├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── config
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── github
│ └── games647
│ └── scoreboardstats
│ └── config
│ ├── CommentedYaml.java
│ ├── ConfigNode.java
│ ├── Settings.java
│ ├── SidebarConfig.java
│ └── VariableItem.java
├── defaults
├── lib
│ └── BukkitGames-2.3.2.jar
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── github
│ └── games647
│ └── scoreboardstats
│ └── defaults
│ ├── ASkyBlockVariables.java
│ ├── BukkitGamesVariables.java
│ ├── BukkitGlobalVariables.java
│ ├── BukkitVariables.java
│ ├── BungeeCordVariables.java
│ ├── CraftconomyVariables.java
│ ├── FactionsVariables.java
│ ├── GeneralVariables.java
│ ├── GriefPreventionVariables.java
│ ├── HeroesVariables.java
│ ├── McCombatLevelVariables.java
│ ├── McPrisonVariables.java
│ ├── McmmoVariables.java
│ ├── MyPetVariables.java
│ ├── PlaceHolderVariables.java
│ ├── PlayerPingVariable.java
│ ├── PlayerPointsVariables.java
│ ├── SimpleClansVariables.java
│ ├── SkyblockVariables.java
│ ├── TicksPerSecondTask.java
│ └── VaultVariables.java
├── plugin
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── github
│ │ │ └── games647
│ │ │ └── scoreboardstats
│ │ │ ├── PlayerListener.java
│ │ │ ├── RefreshTask.java
│ │ │ ├── SbManager.java
│ │ │ ├── ScoreboardStats.java
│ │ │ ├── commands
│ │ │ ├── CommandHandler.java
│ │ │ ├── SidebarCommands.java
│ │ │ └── ToggleCommand.java
│ │ │ └── scoreboard
│ │ │ ├── Objective.java
│ │ │ ├── PacketListener.java
│ │ │ ├── PacketManager.java
│ │ │ ├── PlayerScoreboard.java
│ │ │ ├── SlotTransformer.java
│ │ │ ├── State.java
│ │ │ ├── Team.java
│ │ │ └── TeamMode.java
│ └── resources
│ │ ├── config.yml
│ │ ├── plugin.yml
│ │ └── sql.yml
│ └── test
│ └── java
│ └── com
│ └── github
│ └── games647
│ └── scoreboardstats
│ └── variables
│ └── VersionTest.java
├── pom.xml
├── pvp
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── github
│ └── games647
│ └── scoreboardstats
│ └── pvp
│ ├── Database.java
│ ├── DatabaseConfiguration.java
│ ├── PlayerStats.java
│ ├── SignListener.java
│ ├── StatsListener.java
│ └── StatsLoader.java
└── variables
├── pom.xml
└── src
├── main
└── java
│ └── com
│ └── github
│ └── games647
│ └── scoreboardstats
│ ├── BoardManager.java
│ ├── Version.java
│ ├── scoreboard
│ └── Score.java
│ └── variables
│ ├── DefaultReplacer.java
│ ├── DefaultReplacers.java
│ ├── EventReplacer.java
│ ├── PluginListener.java
│ ├── ReplaceManager.java
│ ├── Replacer.java
│ ├── ReplacerAPI.java
│ ├── ReplacerException.java
│ ├── UnknownVariableException.java
│ └── UnsupportedPluginException.java
└── test
└── java
└── com
└── github
└── games647
└── scoreboardstats
└── variables
└── ReplaceManagerTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | # Eclipse stuff
2 | /.classpath
3 | /.project
4 | /.settings
5 |
6 | # NetBeans
7 | */nbproject
8 | nb-configuration.xml
9 |
10 | # maven
11 | */target
12 |
13 | # vim
14 | .*.sw[a-p]
15 |
16 | # virtual machine crash logs, see https://www.java.com/en/download/help/error_hotspot.xml
17 | hs_err_pid*
18 |
19 | # various other potential build files
20 | */build/
21 | /bin
22 | /dist
23 | /manifest.mf
24 | *.log
25 |
26 | # Mac filesystem dust
27 | .DS_Store
28 |
29 | # IntelliJ
30 | *.iml
31 | *.ipr
32 | *.iws
33 | .idea/
34 |
35 | # Gradle
36 | .gradle
37 |
38 | # Ignore Gradle GUI config
39 | gradle-app.setting
40 |
41 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
42 | !gradle-wrapper.jar
43 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # Use https://travis-ci.org/ for automatic testing
2 |
3 | # speed up testing https://blog.travis-ci.com/2014-12-17-faster-builds-with-container-based-infrastructure/
4 | sudo: false
5 |
6 | # This is a java project
7 | language: java
8 |
9 | script: mvn test -B
10 |
11 | jdk:
12 | - oraclejdk8
13 | - oraclejdk9
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 games647 and contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ScoreboardStats
2 |
3 | ScoreboardStats is a [Bukkit](https://github.com/Bukkit/Bukkit) plugin
4 | that uses the sidebar from scoreboard feature which was introduced into
5 | [Minecraft](https://minecraft.net) since version
6 | [1.5](https://mcupdate.tumblr.com/post/45267771887/minecraft-1-5).
7 | This plugin simplifies, adds many new features and possible ways to improve
8 | the use of it.
9 |
10 | Project page:
11 |
12 | https://dev.bukkit.org/bukkit-mods/scoreboardstats/
13 |
14 | https://www.curse.com/bukkit-plugins/minecraft/scoreboardstats
15 |
16 | ### For Plugin Developers
17 |
18 | https://github.com/games647/ScoreboardStats/wiki
19 |
20 | Feel free to contribute there too.
21 | Just click the edit button after you login into your Github account
22 |
23 | ### Building
24 |
25 | ScoreboardStats uses Maven 3 to manage building configurations,
26 | general project details and dependencies.
27 | You can compile this project yourself by using Maven.
28 |
29 |
30 | * Just import the project from [Github](https://github.com/).
31 | Your IDE would detect the Maven project.
32 | * If not: Download it from [here](https://maven.apache.org/download.cgi)
33 | You will find executable in the bin folder.
34 | * Run (using IDE, console or something else)
35 |
--------------------------------------------------------------------------------
/config/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 |
6 | com.github.games647
7 | scoreboardstats-parent
8 | 1.0.0
9 |
10 |
11 | scoreboardstats-config
12 | jar
13 |
14 |
--------------------------------------------------------------------------------
/config/src/main/java/com/github/games647/scoreboardstats/config/CommentedYaml.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.config;
2 |
3 | import com.google.common.base.Charsets;
4 | import com.google.common.base.Strings;
5 | import com.google.common.io.CharStreams;
6 |
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.io.InputStreamReader;
10 | import java.lang.reflect.Field;
11 | import java.lang.reflect.Modifier;
12 | import java.nio.file.Files;
13 | import java.nio.file.Path;
14 | import java.util.Collections;
15 | import java.util.List;
16 |
17 | import org.bukkit.ChatColor;
18 | import org.bukkit.configuration.Configuration;
19 | import org.bukkit.configuration.InvalidConfigurationException;
20 | import org.bukkit.configuration.file.FileConfiguration;
21 | import org.bukkit.configuration.file.YamlConfiguration;
22 | import org.slf4j.Logger;
23 |
24 | /**
25 | * Represents an easy way to load and save to yaml configs. Furthermore support
26 | * this class UTF-8 even before Bukkit introduced it and will also support comments
27 | * soon.
28 | */
29 | class CommentedYaml {
30 |
31 | private static final String COMMENT_PREFIX = "# ";
32 | private static final String FILE_NAME = "config.yml";
33 |
34 | protected final transient Logger logger;
35 | protected final transient Path dataFolder;
36 | protected transient FileConfiguration config;
37 |
38 | CommentedYaml(Logger logger, Path dataFolder) {
39 | this.logger = logger;
40 | this.dataFolder = dataFolder;
41 | }
42 |
43 | /**
44 | * Gets the YAML file configuration from the disk while loading it
45 | * explicit with UTF-8. This allows umlauts and other UTF-8 characters
46 | * for all Bukkit versions.
47 | *
48 | * Bukkit add also this feature since
49 | * https://github.com/Bukkit/Bukkit/commit/24883a61704f78a952e948c429f63c4a2ab39912
50 | * To be allow the same feature for all Bukkit version, this method was
51 | * created.
52 | *
53 | * @return the loaded file configuration
54 | */
55 | public FileConfiguration getConfigFromDisk() {
56 | Path file = dataFolder.resolve(FILE_NAME);
57 |
58 | YamlConfiguration newConf = new YamlConfiguration();
59 | newConf.setDefaults(getDefaults());
60 | try {
61 | //UTF-8 should be available on all java running systems
62 | List lines = Files.readAllLines(file);
63 | load(lines, newConf);
64 | } catch (InvalidConfigurationException | IOException ex) {
65 | logger.error("Couldn't load the configuration", ex);
66 | }
67 |
68 | return newConf;
69 | }
70 |
71 | protected void loadConfig() {
72 | config = getConfigFromDisk();
73 |
74 | for (Field field : getClass().getDeclaredFields()) {
75 | if (Modifier.isTransient(field.getModifiers())
76 | || !field.getType().isPrimitive() && field.getType() != String.class) {
77 | continue;
78 | }
79 |
80 | ConfigNode node = field.getAnnotation(ConfigNode.class);
81 | String path = field.getName();
82 | if (node != null && !Strings.isNullOrEmpty(path)) {
83 | path = node.path();
84 | }
85 |
86 | field.setAccessible(true);
87 | setField(path, field);
88 | }
89 | }
90 |
91 | protected void saveConfig() {
92 | if (config == null) {
93 | return;
94 | }
95 |
96 | //todo add comment support
97 | try {
98 | List lines = Collections.singletonList(config.saveToString());
99 | Files.write(dataFolder.resolve(FILE_NAME), lines);
100 | } catch (IOException ex) {
101 | logger.error("Failed to save config", ex);
102 | }
103 | }
104 |
105 | private void setField(String path, Field field) throws IllegalArgumentException {
106 | if (config.isSet(path)) {
107 | try {
108 | if (config.isString(path)) {
109 | field.set(this, ChatColor.translateAlternateColorCodes('&', config.getString(path)));
110 | } else {
111 | field.set(this, config.get(path));
112 | }
113 | } catch (IllegalAccessException ex) {
114 | logger.error("Error setting field", ex);
115 | }
116 | } else {
117 | logger.error("Path not fond {}", path);
118 | }
119 | }
120 |
121 | private void load(Iterable lines, YamlConfiguration newConf) throws InvalidConfigurationException {
122 | StringBuilder builder = new StringBuilder();
123 | for (String line : lines) {
124 | //remove the silly tab error from yaml
125 | builder.append(line.replace("\t", " "));
126 | //indicates a new line
127 | builder.append('\n');
128 | }
129 |
130 | newConf.loadFromString(builder.toString());
131 | }
132 |
133 | /**
134 | * Get the default configuration located in the plugin jar
135 | *
136 | * @return the default configuration
137 | */
138 | private Configuration getDefaults() {
139 | YamlConfiguration defaults = new YamlConfiguration();
140 |
141 | try (InputStream defConfigStream = getClass().getResourceAsStream('/' + FILE_NAME)) {
142 | try {
143 | Readable reader = new InputStreamReader(defConfigStream, Charsets.UTF_8);
144 | //stream will be closed in this method
145 | List lines = CharStreams.readLines(reader);
146 | load(lines, defaults);
147 | return defaults;
148 | } catch (InvalidConfigurationException ex) {
149 | logger.error("Invalid Configuration", ex);
150 | } catch (IOException ex) {
151 | logger.error("Couldn't load the configuration", ex);
152 | }
153 | } catch (IOException ioEx) {
154 | logger.error("Failed to find default", ioEx);
155 | }
156 |
157 | return defaults;
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/config/src/main/java/com/github/games647/scoreboardstats/config/ConfigNode.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.config;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.ElementType;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 | import java.lang.annotation.Target;
8 |
9 | /**
10 | * Specializes a non-default path for config node
11 | */
12 | @Target(ElementType.FIELD)
13 | @Documented
14 | @Retention(RetentionPolicy.RUNTIME)
15 | @interface ConfigNode {
16 |
17 | /**
18 | * Defines the path to the node if it has another as the variable name.
19 | * Every indention is separated with an dot ('.')
20 | *
21 | * @return the path to the node
22 | */
23 | String path() default "";
24 | }
25 |
--------------------------------------------------------------------------------
/config/src/main/java/com/github/games647/scoreboardstats/config/Settings.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.config;
2 |
3 | import com.google.common.collect.ImmutableSet;
4 |
5 | import java.util.Set;
6 |
7 | import org.bukkit.configuration.ConfigurationSection;
8 | import org.bukkit.plugin.Plugin;
9 | import org.slf4j.Logger;
10 |
11 | /**
12 | * Managing all general configurations of this plugin.
13 | */
14 | public class Settings extends CommentedYaml {
15 |
16 | private static final String MIN_ITEMS = "A scoreboard have to display min. 1 item ({})";
17 | private static final String TOO_MANY_ITEMS = "Scoreboard can't have more than 15 items";
18 |
19 | @ConfigNode(path = "enable-pvpstats")
20 | private static boolean pvpStats;
21 |
22 | @ConfigNode(path = "Temp-Scoreboard-enabled")
23 | private static boolean tempScoreboard;
24 |
25 | private static SidebarConfig mainScoreboard;
26 |
27 | @ConfigNode(path = "Temp-Scoreboard.Title")
28 | private static String tempTitle;
29 |
30 | @ConfigNode(path = "Temp-Scoreboard.Color")
31 | private static String tempColor;
32 |
33 | @ConfigNode(path = "Temp-Scoreboard.Type")
34 | private static String topType;
35 |
36 | @ConfigNode(path = "Scoreboard.Update-delay")
37 | private static int interval;
38 |
39 | @ConfigNode(path = "Temp-Scoreboard.Items")
40 | private static int topItems;
41 |
42 | @ConfigNode(path = "Temp-Scoreboard.Interval-show")
43 | private static int tempShow;
44 |
45 | @ConfigNode(path = "Temp-Scoreboard.Interval-Disappear")
46 | private static int tempDisappear;
47 |
48 | private static Set disabledWorlds;
49 |
50 | public Settings(Plugin plugin, Logger logger) {
51 | super(logger, plugin.getDataFolder().toPath());
52 |
53 | plugin.saveDefaultConfig();
54 | }
55 |
56 | /**
57 | * Check if a world is from ScoreboardStats active
58 | *
59 | * @param worldName the checked world
60 | * @return if the world is disabled
61 | */
62 | public static boolean isActiveWorld(String worldName) {
63 | return !disabledWorlds.contains(worldName);
64 | }
65 |
66 | /**
67 | * Check whether tracking of players stats is enabled
68 | *
69 | * @return whether tracking of players stats is enabled
70 | */
71 | public static boolean isPvpStats() {
72 | return pvpStats;
73 | }
74 |
75 | /**
76 | * Check if the temp-scoreboard is enabled
77 | *
78 | * @return if the temp-scoreboard is enabled
79 | */
80 | public static boolean isTempScoreboard() {
81 | return tempScoreboard;
82 | }
83 |
84 | public static SidebarConfig getMainScoreboard() {
85 | return mainScoreboard;
86 | }
87 |
88 | public static String getTempTitle() {
89 | return tempTitle;
90 | }
91 |
92 | public static String getTempColor() {
93 | return tempColor;
94 | }
95 |
96 | /**
97 | * Get the type what the temp-scoreboard should display
98 | *
99 | * @return what the temp-scoreboard should display
100 | */
101 | public static String getTopType() {
102 | return topType;
103 | }
104 |
105 | /**
106 | * Get the interval in which the items being refreshed.
107 | *
108 | * @return the interval in which the items being refreshed.
109 | */
110 | public static int getInterval() {
111 | return interval;
112 | }
113 |
114 | /**
115 | * Get how many items the temp-scoreboard should have
116 | *
117 | * @return how many items the temp-scoreboard should have
118 | */
119 | public static int getTopitems() {
120 | return topItems;
121 | }
122 |
123 | /**
124 | * Get the seconds after the temp-scoreboard should appear.
125 | *
126 | * @return the seconds after the temp-scoreboard should appear
127 | */
128 | public static int getTempAppear() {
129 | return tempShow;
130 | }
131 |
132 | /**
133 | * Get the seconds after the temp-scoreboard should disappear.
134 | *
135 | * @return the seconds after the temp-scoreboard should disappear
136 | */
137 | public static int getTempDisappear() {
138 | return tempDisappear;
139 | }
140 |
141 | /**
142 | * Load the configuration file in memory and convert it into simple variables
143 | */
144 | @Override
145 | public void loadConfig() {
146 | super.loadConfig();
147 |
148 | //This set only changes after another call to loadConfig so this set can be immutable
149 | disabledWorlds = ImmutableSet.copyOf(config.getStringList("disabled-disabledWorlds"));
150 |
151 | tempTitle = trimLength(tempTitle, 32);
152 |
153 | String title = config.getString("Scoreboard.Title");
154 | mainScoreboard = new SidebarConfig(trimLength(title, 32));
155 | //Load all normal scoreboard variables
156 | loadItems(config.getConfigurationSection("Scoreboard.Items"));
157 |
158 | //temp-scoreboard
159 | tempScoreboard = tempScoreboard && pvpStats;
160 | topItems = checkItems(topItems);
161 | topType = topType.replace("%", "");
162 | }
163 |
164 | private String trimLength(String check, int limit) {
165 | //Check if the string is longer, so we don't end up with a IndexOutOfBoundEx
166 | if (check.length() > limit) {
167 | //If the string check is longer cut it down
168 | String cut = check.substring(0, limit);
169 | logger.warn("{} was longer than {} characters. We remove the overlapping characters", cut, limit);
170 |
171 | return cut;
172 | }
173 |
174 | return check;
175 | }
176 |
177 | private int checkItems(int input) {
178 | if (input >= 16) {
179 | //Only 15 items per sidebar objective are allowed
180 | logger.warn(TOO_MANY_ITEMS);
181 | return 16 - 1;
182 | }
183 |
184 | if (input <= 0) {
185 | logger.warn(MIN_ITEMS, "tempscoreboard");
186 | return 5;
187 | }
188 |
189 | return input;
190 | }
191 |
192 | private void loadItems(ConfigurationSection config) {
193 | //clear all existing items
194 | mainScoreboard.clear();
195 |
196 | //not implemented yet in compatibility mode
197 | int maxLength = 40;
198 | for (String key : config.getKeys(false)) {
199 | if (mainScoreboard.size() == 15) {
200 | //Only 15 items per sidebar objective are allowed
201 | logger.warn(TOO_MANY_ITEMS);
202 | break;
203 | }
204 |
205 | String displayName = trimLength(key, maxLength);
206 | String value = config.getString(key);
207 | if (displayName.contains("%")) {
208 | String variable = "";
209 | // mainScoreboard.addVariableItem(true, variable, displayName, value);
210 | } else if (value.charAt(0) == '%' && value.charAt(value.length() - 1) == '%') {
211 | //Prevent case-sensitive mistakes
212 | String variable = value.replace("%", "").toLowerCase();
213 | mainScoreboard.addVariableItem(false, variable, displayName, 0);
214 | } else {
215 | try {
216 | int score = Integer.parseInt(value);
217 | mainScoreboard.addItem(displayName, score);
218 | } catch (NumberFormatException numberFormatException) {
219 | //Prevent user mistakes
220 | logger.info("Replacer {} has to contain % at the beginning and at the end", displayName);
221 | }
222 | }
223 | }
224 |
225 | if (mainScoreboard.size() == 0) {
226 | //It won't show up if there isn't at least one item
227 | logger.info(MIN_ITEMS, "scoreboard");
228 | }
229 | }
230 | }
231 |
--------------------------------------------------------------------------------
/config/src/main/java/com/github/games647/scoreboardstats/config/SidebarConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.config;
2 |
3 | import com.google.common.collect.Maps;
4 |
5 | import java.util.Map;
6 |
7 | import org.bukkit.ChatColor;
8 |
9 | public class SidebarConfig {
10 |
11 | private final Map itemsByName = Maps.newHashMapWithExpectedSize(15);
12 | private final Map itemsByVariable = Maps.newHashMapWithExpectedSize(15);
13 | private String displayName;
14 |
15 | public SidebarConfig(String displayName) {
16 | this.displayName = ChatColor.translateAlternateColorCodes('&', displayName);
17 | }
18 |
19 | public String getTitle() {
20 | return displayName;
21 | }
22 |
23 | public void setTitle(String displayName) {
24 | this.displayName = ChatColor.translateAlternateColorCodes('&', displayName);
25 | }
26 |
27 | public void addItem(String displayName, int score) {
28 | String colorName = ChatColor.translateAlternateColorCodes('&', displayName);
29 |
30 | VariableItem variableItem = new VariableItem(false, null, colorName, score);
31 | itemsByName.put(colorName, variableItem);
32 | }
33 |
34 | public void addVariableItem(boolean textVariable, String variable, String displayText, int defaultScore) {
35 | String coloredDisplay = ChatColor.translateAlternateColorCodes('&', displayText);
36 |
37 | VariableItem variableItem = new VariableItem(textVariable, variable, coloredDisplay, defaultScore);
38 | itemsByName.put(coloredDisplay, variableItem);
39 | itemsByVariable.put(variable, variableItem);
40 | }
41 |
42 | public void remove(VariableItem variableItem) {
43 | itemsByName.remove(variableItem.getDisplayText());
44 | itemsByVariable.remove(variableItem.getVariable());
45 | }
46 |
47 | public Map getItemsByName() {
48 | return itemsByName;
49 | }
50 |
51 | public Map getItemsByVariable() {
52 | return itemsByVariable;
53 | }
54 |
55 | public int size() {
56 | return itemsByName.size();
57 | }
58 |
59 | public void clear() {
60 | itemsByVariable.clear();
61 | }
62 |
63 | @Override
64 | public String toString() {
65 | return "SidebarConfig{" + "displayName="
66 | + displayName + ", itemsByVariable="
67 | + itemsByVariable
68 | + '}';
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/config/src/main/java/com/github/games647/scoreboardstats/config/VariableItem.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.config;
2 |
3 | public class VariableItem {
4 |
5 | private final boolean textVariable;
6 | private final String variable;
7 |
8 | private String displayText;
9 | private int score;
10 |
11 | public VariableItem(boolean textVariable, String variable, String displayText, int defaultScore) {
12 | this(textVariable, variable, displayText);
13 |
14 | this.score = defaultScore;
15 | }
16 |
17 | public VariableItem(boolean textVariable, String variable, String displayText) {
18 | this.textVariable = textVariable;
19 | this.variable = variable;
20 | this.displayText = displayText;
21 | }
22 |
23 | public String getDisplayText() {
24 | return displayText;
25 | }
26 |
27 | public void setDisplayText(String displayText) {
28 | this.displayText = displayText;
29 | }
30 |
31 | public int getScore() {
32 | return score;
33 | }
34 |
35 | public void setScore(int score) {
36 | this.score = score;
37 | }
38 |
39 | public boolean isTextVariable() {
40 | return textVariable;
41 | }
42 |
43 | public String getVariable() {
44 | return variable;
45 | }
46 |
47 | @Override
48 | public String toString() {
49 | return "VariableItem{"
50 | + "textVariable=" + textVariable
51 | + ", variable=" + variable
52 | + ", displayText=" + displayText
53 | + ", score=" + score
54 | + '}';
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/defaults/lib/BukkitGames-2.3.2.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TuxCoding/ScoreboardStats/HEAD/defaults/lib/BukkitGames-2.3.2.jar
--------------------------------------------------------------------------------
/defaults/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 |
6 | com.github.games647
7 | scoreboardstats-parent
8 | 1.0.0
9 |
10 |
11 | scoreboardstats-defaults
12 | jar
13 |
14 |
15 |
16 |
17 | md_5-releases
18 | https://repo.md-5.net/content/groups/public/
19 |
20 |
21 |
22 |
23 | vault-repo
24 | http://nexus.hc.to/content/repositories/pub_releases/
25 |
26 |
27 |
28 | heroes-repo
29 | http://nexus.hc.to/content/repositories/pub_snapshots/
30 |
31 |
32 |
33 | sonatype-repo
34 | https://oss.sonatype.org/content/repositories/snapshots/
35 |
36 |
37 |
38 | xephi-repo
39 | https://ci.xephi.fr/plugin/repository/everything/
40 |
41 |
42 |
43 |
44 | Dakani
45 | Dakani Nexus Repo
46 | https://repo.dakanilabs.com/repository/public/
47 |
48 |
49 |
50 |
51 | simpleclans-repo
52 | http://104.236.246.108:8081/artifactory/plugins-release-local
53 |
54 |
55 |
56 | uSkyBlock-mvn-repo
57 | https://raw.github.com/rlf/uSkyBlock/mvn-repo/
58 |
59 |
60 |
61 |
62 | jitpack.io
63 | https://jitpack.io
64 |
65 |
66 |
67 |
68 | bintray-tastybento-maven-repo
69 | https://dl.bintray.com/tastybento/maven-repo
70 |
71 |
72 |
73 |
74 | mypet-repo
75 | http://repo.keyle.de/
76 |
77 |
78 |
79 |
80 | extendedclip-repo
81 | http://repo.extendedclip.com/content/repositories/placeholderapi/
82 |
83 |
84 |
85 |
86 | dustplanet-repo
87 | https://repo.dustplanet.de/artifactory/ext-release-local/
88 |
89 |
90 |
91 |
92 | enderzone-repo
93 | https://ci.ender.zone/plugin/repository/everything/
94 |
95 |
96 |
97 |
98 |
99 | com.github.games647
100 | scoreboardstats-variables
101 | 1.0.0
102 |
103 |
104 |
105 | com.wasteofplastic
106 | askyblock
107 | 3.0.7
108 |
109 |
110 | *
111 | *
112 |
113 |
114 |
115 |
116 |
117 | me.clip
118 | placeholderapi
119 | 2.8.2
120 |
121 |
122 | *
123 | *
124 |
125 |
126 |
127 |
128 |
129 | de.keyle
130 | mypet
131 | 2.3.2
132 |
133 |
134 | *
135 | *
136 |
137 |
138 |
139 |
140 |
141 |
142 | net.milkbowl.vault
143 | VaultAPI
144 | 1.6
145 |
146 |
147 | *
148 | *
149 |
150 |
151 | true
152 |
153 |
154 |
155 | net.sacredlabyrinth.phaed.simpleclans
156 | SimpleClans
157 | 2.7.5
158 |
159 |
160 | *
161 | *
162 |
163 |
164 | true
165 |
166 |
167 |
168 | org.black_ixx
169 | PlayerPoints
170 | 2.1.5
171 |
172 |
173 | *
174 | *
175 |
176 |
177 | true
178 |
179 |
180 |
181 | com.greatmancode
182 | craftconomy3
183 | 3.3.1
184 |
185 |
186 | *
187 | *
188 |
189 |
190 | true
191 |
192 |
193 |
194 | com.herocraftonline.heroes
195 | Heroes
196 | 1.5.5.7
197 |
198 |
199 | *
200 | *
201 |
202 |
203 | true
204 |
205 |
206 |
207 | com.gmail.nossr50.mcMMO
208 | mcMMO
209 | 1.5.00
210 |
211 |
212 | *
213 | *
214 |
215 |
216 | true
217 |
218 |
219 |
220 | uSkyBlock
221 | uSkyBlock
222 | 2.4.5
223 | true
224 |
225 |
226 | *
227 | *
228 |
229 |
230 |
231 |
232 |
233 | com.github.SirFaizdat
234 | Prison
235 | v2.4.1
236 |
237 |
238 | *
239 | *
240 |
241 |
242 | true
243 |
244 |
245 |
246 | com.github.TechFortress
247 | GriefPrevention
248 | 16.7.1
249 |
250 |
251 | *
252 | *
253 |
254 |
255 |
256 |
257 |
258 | com.github.games647
259 | McCombatLevel
260 | 3ab946cf26
261 |
262 |
263 | *
264 | *
265 |
266 |
267 |
268 |
269 |
270 |
271 | BukkitGames
272 | BukkitGames
273 | 2.3.2
274 | system
275 | ${project.basedir}/lib/BukkitGames-2.3.2.jar
276 | true
277 |
278 |
279 | *
280 | *
281 |
282 |
283 |
284 |
285 |
286 |
287 | com.massivecraft
288 |
289 | Factions
290 | 1.6.9.5-U0.2.2
291 |
292 |
293 | *
294 | *
295 |
296 |
297 | true
298 |
299 |
300 |
301 | com.massivecraft
302 | factions
303 | 2.12.0
304 |
305 |
306 | *
307 | *
308 |
309 |
310 |
311 |
312 |
313 |
314 | com.massivecraft
315 | massivecore
316 | 2.12.0
317 |
318 |
319 | *
320 | *
321 |
322 |
323 |
324 |
325 |
326 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/ASkyBlockVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 | import com.wasteofplastic.askyblock.ASkyBlockAPI;
7 | import com.wasteofplastic.askyblock.events.ChallengeCompleteEvent;
8 | import com.wasteofplastic.askyblock.events.IslandPostLevelEvent;
9 |
10 | import java.util.function.ToIntFunction;
11 | import java.util.stream.Stream;
12 |
13 | import org.bukkit.entity.Player;
14 | import org.bukkit.plugin.Plugin;
15 |
16 | @DefaultReplacer(plugin = "ASkyBlock")
17 | public class ASkyBlockVariables extends DefaultReplacers {
18 |
19 | private final ASkyBlockAPI skyBlockAPI = ASkyBlockAPI.getInstance();
20 |
21 | public ASkyBlockVariables(ReplacerAPI replaceManager, Plugin plugin) {
22 | super(replaceManager, plugin);
23 | }
24 |
25 | @Override
26 | public void register() {
27 | register("island-level")
28 | .scoreSupply(player -> (int) skyBlockAPI.getLongIslandLevel(player.getUniqueId()))
29 | .eventScore(IslandPostLevelEvent.class, event -> (int) event.getLongLevel());
30 |
31 | register("challenge_done")
32 | .scoreSupply(player -> (int) skyBlockAPI.getLongIslandLevel(player.getUniqueId()))
33 | .eventScore(ChallengeCompleteEvent.class, event -> getDoneChallenge(event.getPlayer()));
34 |
35 | register("challenge_incomplete")
36 | .scoreSupply(player -> (int) skyBlockAPI.getLongIslandLevel(player.getUniqueId()))
37 | .eventScore(ChallengeCompleteEvent.class, event -> getIncompleteChallenge(event.getPlayer()));
38 |
39 | register("challenge_total")
40 | .scoreSupply(player -> (int) skyBlockAPI.getLongIslandLevel(player.getUniqueId()));
41 |
42 | register("challenge_unique")
43 | .scoreSupply(player -> (int) skyBlockAPI.getLongIslandLevel(player.getUniqueId()));
44 | }
45 |
46 | private int getDoneChallenge(Player player) {
47 | return mapChallenges(player, challenges -> (int) challenges
48 | .filter(complete -> complete)
49 | .count());
50 | }
51 |
52 | private int getIncompleteChallenge(Player player) {
53 | return mapChallenges(player, challenges -> (int) challenges
54 | .filter(complete -> !complete)
55 | .count());
56 | }
57 |
58 | private int getTotalChallenge(Player player) {
59 | return mapTimes(player, lst -> (int) lst
60 | .filter(times -> times > 0)
61 | .count());
62 | }
63 |
64 | private int getUniqueChallenge(Player player) {
65 | return mapTimes(player, lst -> lst
66 | .mapToInt(time -> time)
67 | .sum());
68 | }
69 |
70 | private int mapTimes(Player player, ToIntFunction> fct) {
71 | return fct.applyAsInt(skyBlockAPI.getChallengeTimes(player.getUniqueId()).values().stream());
72 | }
73 |
74 | private int mapChallenges(Player player, ToIntFunction> fct) {
75 | return fct.applyAsInt(skyBlockAPI.getChallengeStatus(player.getUniqueId()).values().stream());
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/BukkitGamesVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 |
7 | import de.ftbastler.bukkitgames.api.BukkitGamesAPI;
8 | import de.ftbastler.bukkitgames.api.PlayerBuyKitEvent;
9 |
10 | import org.bukkit.plugin.Plugin;
11 |
12 | @DefaultReplacer(plugin = "BukkitGames")
13 | public class BukkitGamesVariables extends DefaultReplacers {
14 |
15 | private final BukkitGamesAPI bukkitGamesAPI = BukkitGamesAPI.getApi();
16 |
17 | public BukkitGamesVariables(ReplacerAPI replaceManager, Plugin plugin) {
18 | super(replaceManager, plugin);
19 | }
20 |
21 | @Override
22 | public void register() {
23 | register("coins")
24 | .scoreSupply(bukkitGamesAPI::getPlayerBalance)
25 | .eventScore(PlayerBuyKitEvent.class, event -> {
26 | int oldBalance = bukkitGamesAPI.getPlayerBalance(event.getPlayer());
27 | return oldBalance - event.getKitCost();
28 | });
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/BukkitGlobalVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 |
7 | import org.bukkit.Bukkit;
8 | import org.bukkit.event.player.PlayerJoinEvent;
9 | import org.bukkit.event.player.PlayerQuitEvent;
10 | import org.bukkit.plugin.Plugin;
11 | import org.bukkit.util.NumberConversions;
12 |
13 | /**
14 | * Replace all Bukkit variables which are the same for players. Currently
15 | * there is no good way to mark variables as global
16 | */
17 | @DefaultReplacer
18 | public class BukkitGlobalVariables extends DefaultReplacers {
19 |
20 | public BukkitGlobalVariables(ReplacerAPI replaceManager, Plugin plugin) {
21 | super(replaceManager, plugin);
22 | }
23 |
24 | @Override
25 | public void register() {
26 | register("tps")
27 | .scoreSupply(() -> NumberConversions.round(TicksPerSecondTask.getLastTicks()));
28 |
29 | register("online")
30 | .scoreSupply(() -> Bukkit.getOnlinePlayers().size())
31 | .eventScore(PlayerJoinEvent.class, () -> Bukkit.getOnlinePlayers().size())
32 | .eventScore(PlayerQuitEvent.class, () -> Bukkit.getOnlinePlayers().size() - 1);
33 |
34 | register("max_player")
35 | .scoreSupply(Bukkit::getMaxPlayers)
36 | .constant();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/BukkitVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 |
7 | import org.bukkit.entity.Player;
8 | import org.bukkit.inventory.ItemStack;
9 | import org.bukkit.plugin.Plugin;
10 | import org.bukkit.util.NumberConversions;
11 |
12 | /**
13 | * Replace all Bukkit variables
14 | */
15 | @DefaultReplacer
16 | public class BukkitVariables extends DefaultReplacers {
17 |
18 | private static final int MINUTE_TO_SECOND = 60;
19 |
20 | public BukkitVariables(ReplacerAPI replaceManager, Plugin plugin) {
21 | super(replaceManager, plugin);
22 | }
23 |
24 | @Override
25 | public void register() {
26 | register("health").scoreSupply(player -> NumberConversions.round(player.getHealth()));
27 | register("lifetime").scoreSupply(player -> player.getTicksLived() / (20 * MINUTE_TO_SECOND));
28 | register("no_damage_ticks").scoreSupply(player -> player.getNoDamageTicks() / (20 * MINUTE_TO_SECOND));
29 | register("last_damage").scoreSupply(player -> (int) (player.getLastDamage()));
30 |
31 | register("exp").scoreSupply(Player::getTotalExperience);
32 | register("xp_to_level").scoreSupply(Player::getExpToLevel);
33 |
34 |
35 | register("helmet").scoreSupply(player -> calculateDurability(player.getInventory().getHelmet()));
36 | register("boots").scoreSupply(player -> calculateDurability(player.getInventory().getBoots()));
37 | register("leggings").scoreSupply(player -> calculateDurability(player.getInventory().getLeggings()));
38 | register("chestplate").scoreSupply(player -> calculateDurability(player.getInventory().getChestplate()));
39 |
40 | register("chestplate").scoreSupply(player -> (int) player.getWorld().getTime());
41 | }
42 |
43 | private int calculateDurability(ItemStack item) {
44 | //Check if the user have an item on the slot and if the item isn't a stone block or something
45 | if (item == null || item.getType().getMaxDurability() == 0) {
46 | return 0;
47 | } else {
48 | //calculate in percent
49 | return item.getDurability() * 100 / item.getType().getMaxDurability();
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/BungeeCordVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 | import com.google.common.collect.Iterables;
7 | import com.google.common.io.ByteArrayDataInput;
8 | import com.google.common.io.ByteArrayDataOutput;
9 | import com.google.common.io.ByteStreams;
10 |
11 | import org.bukkit.Bukkit;
12 | import org.bukkit.entity.Player;
13 | import org.bukkit.plugin.Plugin;
14 | import org.bukkit.plugin.messaging.Messenger;
15 | import org.bukkit.plugin.messaging.PluginMessageListener;
16 |
17 | @DefaultReplacer
18 | public class BungeeCordVariables extends DefaultReplacers implements PluginMessageListener, Runnable {
19 |
20 | private static final int UPDATE_INTERVAL = 20 * 30;
21 | private static final String BUNGEE_CHANNEL = "BungeeCord";
22 |
23 | private int onlinePlayers;
24 |
25 | public BungeeCordVariables(ReplacerAPI replaceManager, Plugin plugin) {
26 | super(replaceManager, plugin);
27 | }
28 |
29 | @Override
30 | public void register() {
31 | Messenger messenger = Bukkit.getMessenger();
32 | messenger.registerOutgoingPluginChannel(plugin, BUNGEE_CHANNEL);
33 | messenger.registerIncomingPluginChannel(plugin, BUNGEE_CHANNEL, this);
34 |
35 | Bukkit.getScheduler().runTaskTimer(plugin, this, UPDATE_INTERVAL, UPDATE_INTERVAL);
36 |
37 | register("bungee-online").scoreSupply(() -> onlinePlayers);
38 | }
39 |
40 | @Override
41 | public void onPluginMessageReceived(String channel, Player player, byte[] message) {
42 | if (!channel.equals(BUNGEE_CHANNEL)) {
43 | return;
44 | }
45 |
46 | ByteArrayDataInput in = ByteStreams.newDataInput(message);
47 | String subChannel = in.readUTF();
48 | if ("PlayerCount".equals(subChannel)) {
49 | try {
50 | String server = in.readUTF();
51 | int count = in.readInt();
52 |
53 | if ("ALL".equals(server)) {
54 | onlinePlayers = count;
55 | }
56 | } catch (Exception eofException) {
57 | //happens if bungeecord doesn't know the server
58 | //ignore the admin should be notified by seeing the -1
59 | }
60 | }
61 | }
62 |
63 | @Override
64 | public void run() {
65 | Player sender = Iterables.getFirst(Bukkit.getOnlinePlayers(), null);
66 | if (sender != null) {
67 | ByteArrayDataOutput out = ByteStreams.newDataOutput();
68 | out.writeUTF("PlayerCount");
69 | out.writeUTF("ALL");
70 |
71 | sender.sendPluginMessage(plugin, BUNGEE_CHANNEL, out.toByteArray());
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/CraftconomyVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 | import com.greatmancode.craftconomy3.Common;
7 | import com.greatmancode.craftconomy3.account.AccountManager;
8 | import com.greatmancode.craftconomy3.currency.Currency;
9 | import com.greatmancode.craftconomy3.currency.CurrencyManager;
10 |
11 | import org.bukkit.plugin.Plugin;
12 | import org.bukkit.util.NumberConversions;
13 |
14 | @DefaultReplacer(plugin = "CraftConomy3")
15 | public class CraftconomyVariables extends DefaultReplacers {
16 |
17 | private final AccountManager accountManager = Common.getInstance().getAccountManager();
18 | private final CurrencyManager currencyManager = Common.getInstance().getCurrencyManager();
19 |
20 | public CraftconomyVariables(ReplacerAPI replaceManager, Plugin plugin) {
21 | super(replaceManager, plugin);
22 | }
23 |
24 | @Override
25 | public void register() {
26 | if (Common.isInitialized()) {
27 | for (String currencyName : currencyManager.getCurrencyNames()) {
28 | register("money_" + currencyName)
29 | .scoreSupply(player -> {
30 | Currency currency = currencyManager.getCurrency(currencyName);
31 | double balance = accountManager.getAccount(player.getWorld().getName(), false)
32 | .getBalance(player.getWorld().getName(), currencyName);
33 | return NumberConversions.round(balance);
34 | });
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/FactionsVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.Version;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
5 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
6 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
7 | import com.github.games647.scoreboardstats.variables.UnsupportedPluginException;
8 | import com.massivecraft.factions.FPlayer;
9 | import com.massivecraft.factions.FPlayers;
10 | import com.massivecraft.factions.entity.MPlayer;
11 |
12 | import java.util.Collection;
13 | import java.util.Optional;
14 |
15 | import org.bukkit.entity.Player;
16 | import org.bukkit.plugin.Plugin;
17 |
18 | /**
19 | * Replace all variables that are associated with the faction plugin
20 | *
21 | * https://dev.bukkit.org/bukkit-plugins/factions/
22 | */
23 | @DefaultReplacer(plugin = "Factions")
24 | public class FactionsVariables extends DefaultReplacers {
25 |
26 | private final boolean newVersion;
27 |
28 | public FactionsVariables(ReplacerAPI replaceManager, Plugin plugin) throws UnsupportedPluginException {
29 | super(replaceManager, plugin);
30 |
31 | String version = plugin.getDescription().getVersion();
32 | newVersion = Version.compare("2", version) >= 0;
33 |
34 | //Version is between 2.0 and 2.7
35 | if (newVersion && Version.compare("2.7", version) < 0) {
36 | throw new UnsupportedPluginException("Due the newest changes from "
37 | + "Factions, you have to upgrade your version to a version above 2.7. "
38 | + "If explicitly want to use this version. Create a ticket on "
39 | + "the project page of this plugin");
40 | }
41 | }
42 |
43 | //faction 2.7+
44 | private void registerNewFactions() {
45 | register("power")
46 | .scoreSupply(player -> getMPlayer(player)
47 | .map(MPlayer::getPowerRounded)
48 | .orElse(-1));
49 |
50 | register("f_power")
51 | .scoreSupply(player -> getNewFaction(player)
52 | .map(com.massivecraft.factions.entity.Faction::getPowerRounded)
53 | .orElse(-1));
54 |
55 | register("members")
56 | .scoreSupply(player -> getNewFaction(player)
57 | .map(com.massivecraft.factions.entity.Faction::getMPlayers)
58 | .map(Collection::size)
59 | .orElse(-1));
60 |
61 | register("members_online")
62 | .scoreSupply(player -> getNewFaction(player)
63 | .map(com.massivecraft.factions.entity.Faction::getMPlayers)
64 | .map(Collection::size)
65 | .orElse(-1));
66 | }
67 |
68 | //factions 1.6.9 and 1.8.2
69 | private void registerOldFactions() {
70 | register("power")
71 | .scoreSupply(player -> getFPlayer(player)
72 | .map(FPlayer::getPowerRounded)
73 | .orElse(-1));
74 |
75 | register("f_power")
76 | .scoreSupply(player -> getOldFaction(player)
77 | .map(com.massivecraft.factions.Faction::getPowerRounded)
78 | .orElse(-1));
79 |
80 | register("members")
81 | .scoreSupply(player -> getOldFaction(player)
82 | .map(com.massivecraft.factions.Faction::getFPlayers)
83 | .map(Collection::size)
84 | .orElse(-1));
85 |
86 | register("members_online")
87 | .scoreSupply(player -> getOldFaction(player)
88 | .map(com.massivecraft.factions.Faction::getFPlayers)
89 | .map(Collection::size)
90 | .orElse(-1));
91 | }
92 |
93 | @Override
94 | public void register() {
95 | if (newVersion) {
96 | registerNewFactions();
97 | } else {
98 | registerOldFactions();
99 | }
100 | }
101 |
102 | private Optional getMPlayer(Player player) {
103 | return Optional.ofNullable(MPlayer.get(player));
104 | }
105 |
106 | private Optional getNewFaction(Player player) {
107 | return getMPlayer(player).map(MPlayer::getFaction);
108 | }
109 |
110 | private Optional getFPlayer(Player player) {
111 | return Optional.ofNullable(FPlayers.getInstance().getByPlayer(player));
112 | }
113 |
114 | private Optional getOldFaction(Player player) {
115 | return getFPlayer(player).map(FPlayer::getFaction);
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/GeneralVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 |
7 | import java.util.Calendar;
8 |
9 | import org.bukkit.plugin.Plugin;
10 |
11 | /**
12 | * Represents a replacer for non Minecraft related variables
13 | */
14 | @DefaultReplacer
15 | public class GeneralVariables extends DefaultReplacers {
16 |
17 | //From bytes to mega bytes
18 | private static final int MB_CONVERSION = 1_024 * 1_024;
19 |
20 | private final Runtime runtime = Runtime.getRuntime();
21 |
22 | public GeneralVariables(ReplacerAPI replaceManager, Plugin plugin) {
23 | super(replaceManager, plugin);
24 | }
25 |
26 | @Override
27 | public void register() {
28 | register("free_ram").scoreSupply(() -> (int) (runtime.freeMemory() / MB_CONVERSION));
29 |
30 | register("max_ram").scoreSupply(() -> (int) (runtime.maxMemory() - runtime.freeMemory() / MB_CONVERSION));
31 |
32 | register("used_ram")
33 | .scoreSupply(() -> (int) ((runtime.maxMemory() - runtime.freeMemory()) * 100 / runtime.maxMemory()));
34 |
35 | register("date").scoreSupply(() -> Calendar.getInstance().get(Calendar.DAY_OF_MONTH));
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/GriefPreventionVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 |
7 | import me.ryanhamshire.GriefPrevention.GriefPrevention;
8 | import me.ryanhamshire.GriefPrevention.PlayerData;
9 |
10 | import org.bukkit.entity.Player;
11 |
12 | @DefaultReplacer(plugin = "GriefPrevention")
13 | public class GriefPreventionVariables extends DefaultReplacers {
14 |
15 | public GriefPreventionVariables(ReplacerAPI replaceManager, GriefPrevention plugin) {
16 | super(replaceManager, plugin);
17 | }
18 |
19 | @Override
20 | public void register() {
21 | register("accrued_claim_block")
22 | .scoreSupply(player -> getData(player).getAccruedClaimBlocks());
23 |
24 | register("bonus_claim_blocks")
25 | .scoreSupply(player -> getData(player).getBonusClaimBlocks());
26 |
27 | register("remaining_blocks")
28 | .scoreSupply(player -> getData(player).getRemainingClaimBlocks());
29 |
30 | register("total_blocks")
31 | .scoreSupply(player -> {
32 | PlayerData playerData = getData(player);
33 | return plugin.dataStore.getGroupBonusBlocks(player.getUniqueId())
34 | + playerData.getAccruedClaimBlocks()
35 | + playerData.getRemainingClaimBlocks()
36 | + playerData.getBonusClaimBlocks();
37 | });
38 |
39 | register("group_bonus_blocks")
40 | .scoreSupply(player -> plugin.dataStore.getGroupBonusBlocks(player.getUniqueId()));
41 | }
42 |
43 | private PlayerData getData(Player player) {
44 | return plugin.dataStore.getPlayerData(player.getUniqueId());
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/HeroesVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 | import com.herocraftonline.heroes.Heroes;
7 | import com.herocraftonline.heroes.api.events.ClassChangeEvent;
8 | import com.herocraftonline.heroes.api.events.HeroChangeLevelEvent;
9 | import com.herocraftonline.heroes.api.events.HeroRegainManaEvent;
10 | import com.herocraftonline.heroes.api.events.SkillUseEvent;
11 | import com.herocraftonline.heroes.characters.CharacterManager;
12 | import com.herocraftonline.heroes.characters.Hero;
13 |
14 | import org.bukkit.entity.Player;
15 |
16 | /**
17 | * Replace all variables that are associated with the heroes plugin
18 | *
19 | * https://dev.bukkit.org/bukkit-plugins/heroes/
20 | */
21 | @DefaultReplacer(plugin = "Heroes")
22 | public class HeroesVariables extends DefaultReplacers {
23 |
24 | private final CharacterManager characterManager;
25 |
26 | public HeroesVariables(ReplacerAPI replaceManager, Heroes plugin) {
27 | super(replaceManager, plugin);
28 |
29 | this.characterManager = plugin.getCharacterManager();
30 | }
31 |
32 | @Override
33 | public void register() {
34 | register("mana").scoreSupply(player -> getHero(player).getMana())
35 | .eventScore(SkillUseEvent.class, event -> event.getHero().getMana() - event.getManaCost())
36 | .eventScore(HeroRegainManaEvent.class, event -> event.getHero().getMana());
37 |
38 | register("max_mana").scoreSupply(player -> getHero(player).getMaxMana())
39 | .eventScore(ClassChangeEvent.class, event -> event.getHero().getMaxMana());
40 |
41 | register("level").scoreSupply(player -> getHero(player).getLevel())
42 | .eventScore(HeroChangeLevelEvent.class, HeroChangeLevelEvent::getTo);
43 | }
44 |
45 | private Hero getHero(Player player) {
46 | return characterManager.getHero(player);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/McCombatLevelVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 | import com.gmail.mrphpfan.mccombatlevel.McCombatLevel;
7 | import com.gmail.mrphpfan.mccombatlevel.PlayerCombatLevelChangeEvent;
8 |
9 | @DefaultReplacer(plugin = "mcCombatLevel")
10 | public class McCombatLevelVariables extends DefaultReplacers {
11 |
12 | public McCombatLevelVariables(ReplacerAPI replaceManager, McCombatLevel plugin) {
13 | super(replaceManager, plugin);
14 | }
15 |
16 | @Override
17 | public void register() {
18 | register("mc_combat_level")
19 | .scoreSupply(plugin::getCombatLevel)
20 | .eventScore(PlayerCombatLevelChangeEvent.class, PlayerCombatLevelChangeEvent::getNewLevel);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/McPrisonVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 | import com.github.games647.scoreboardstats.variables.UnsupportedPluginException;
7 |
8 | import me.sirfaizdat.prison.ranks.Ranks;
9 | import me.sirfaizdat.prison.ranks.UserInfo;
10 | import me.sirfaizdat.prison.ranks.events.BalanceChangeEvent;
11 | import me.sirfaizdat.prison.ranks.events.DemoteEvent;
12 | import me.sirfaizdat.prison.ranks.events.RankupEvent;
13 |
14 | import net.milkbowl.vault.economy.Economy;
15 |
16 | import org.bukkit.Bukkit;
17 | import org.bukkit.entity.Player;
18 | import org.bukkit.plugin.Plugin;
19 | import org.bukkit.plugin.RegisteredServiceProvider;
20 |
21 | /**
22 | * Replace all variables that are associated with the prison plugin
23 | *
24 | * https://dev.bukkit.org/bukkit-plugins/mcprison/
25 | */
26 | @DefaultReplacer(plugin = "Prison")
27 | public class McPrisonVariables extends DefaultReplacers {
28 |
29 | private final Economy eco;
30 |
31 | public McPrisonVariables(ReplacerAPI replaceManager, Plugin plugin) throws UnsupportedPluginException {
32 | super(replaceManager, plugin);
33 |
34 | RegisteredServiceProvider economyProvider = Bukkit.getServicesManager().getRegistration(Economy.class);
35 | if (economyProvider == null) {
36 | throw new UnsupportedPluginException("Couldn't find an economy plugin");
37 | } else {
38 | eco = economyProvider.getProvider();
39 | }
40 | }
41 |
42 | @Override
43 | public void register() {
44 | register("moneyNeeded").scoreSupply(this::getMoneyNeeded)
45 | .eventScore(RankupEvent.class, event -> getMoneyNeeded(event.getPlayer()))
46 | .eventScore(BalanceChangeEvent.class, event -> getMoneyNeeded(event.getPlayer()))
47 | .eventScore(DemoteEvent.class, event -> getMoneyNeeded(event.getPlayer()));
48 | }
49 |
50 | private int getMoneyNeeded(Player player) {
51 | UserInfo userInfo = Ranks.i.getUserInfo(player.getName());
52 | return (int) (userInfo.getNextRank().getPrice() - eco.getBalance(player));
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/McmmoVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 | import com.gmail.nossr50.api.ExperienceAPI;
7 | import com.gmail.nossr50.datatypes.skills.SkillType;
8 | import com.gmail.nossr50.events.experience.McMMOPlayerLevelChangeEvent;
9 | import com.gmail.nossr50.events.experience.McMMOPlayerLevelDownEvent;
10 | import com.gmail.nossr50.events.experience.McMMOPlayerLevelUpEvent;
11 |
12 | import java.util.stream.Stream;
13 |
14 | import org.bukkit.plugin.Plugin;
15 |
16 | /**
17 | * Replace all variables that are associated with the mcMMO plugin
18 | *
19 | * https://dev.bukkit.org/bukkit-plugins/mcmmo/
20 | */
21 | @DefaultReplacer(plugin = "mcMMO")
22 | public class McmmoVariables extends DefaultReplacers {
23 |
24 | public McmmoVariables(ReplacerAPI replaceManager, Plugin plugin) {
25 | super(replaceManager, plugin);
26 | }
27 |
28 | @Override
29 | public void register() {
30 | register("powlvl").scoreSupply(ExperienceAPI::getPowerLevel)
31 | .eventScore(McMMOPlayerLevelUpEvent.class, this::getPowerLevel)
32 | .eventScore(McMMOPlayerLevelDownEvent.class, this::getPowerLevel);
33 |
34 | Stream.of(SkillType.values())
35 | .map(SkillType::name)
36 | .map(String::toUpperCase)
37 | .forEach(skill -> register(skill)
38 | .scoreSupply(player -> ExperienceAPI.getLevel(player, skill))
39 | .eventScore(McMMOPlayerLevelUpEvent.class, event -> ExperienceAPI
40 | .getLevel(event.getPlayer(), skill))
41 | .eventScore(McMMOPlayerLevelDownEvent.class, event -> ExperienceAPI
42 | .getLevel(event.getPlayer(), skill)));
43 | }
44 |
45 | private int getPowerLevel(McMMOPlayerLevelChangeEvent changeEvent) {
46 | return ExperienceAPI.getPowerLevel(changeEvent.getPlayer());
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/MyPetVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 |
7 | import de.Keyle.MyPet.MyPetApi;
8 | import de.Keyle.MyPet.api.event.MyPetLevelUpEvent;
9 | import de.Keyle.MyPet.api.player.MyPetPlayer;
10 |
11 | import org.bukkit.plugin.Plugin;
12 |
13 | @DefaultReplacer(plugin = "MyPet")
14 | public class MyPetVariables extends DefaultReplacers {
15 |
16 | public MyPetVariables(ReplacerAPI replaceManager, Plugin plugin) {
17 | super(replaceManager, plugin);
18 | }
19 |
20 | @Override
21 | public void register() {
22 | register("pet_level").scoreSupply(player -> {
23 | MyPetPlayer myPetPlayer = MyPetApi.getPlayerManager().getMyPetPlayer(player);
24 | return myPetPlayer.getMyPet().getExperience().getLevel();
25 | }).eventScore(MyPetLevelUpEvent.class, MyPetLevelUpEvent::getLevel);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/PlaceHolderVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 | import com.google.common.collect.Sets;
7 |
8 | import java.util.Collection;
9 | import java.util.Set;
10 |
11 | import me.clip.placeholderapi.PlaceholderAPI;
12 | import me.clip.placeholderapi.PlaceholderHook;
13 | import me.clip.placeholderapi.expansion.PlaceholderExpansion;
14 | import me.clip.placeholderapi.external.EZPlaceholderHook;
15 |
16 | import org.bukkit.plugin.Plugin;
17 |
18 | @DefaultReplacer(plugin = "PlaceholderAPI")
19 | public class PlaceHolderVariables extends DefaultReplacers {
20 |
21 | public PlaceHolderVariables(ReplacerAPI replaceManager, Plugin plugin) {
22 | super(replaceManager, plugin);
23 | }
24 |
25 | @Override
26 | public void register() {
27 | Set variables = Sets.newHashSet();
28 |
29 | Collection hooks = PlaceholderAPI.getPlaceholders().values();
30 | for (PlaceholderHook hook : hooks) {
31 | String variablePrefix = null;
32 | if (hook instanceof EZPlaceholderHook) {
33 | variablePrefix = ((EZPlaceholderHook) hook).getPlaceholderName();
34 | } else if (hook instanceof PlaceholderExpansion) {
35 | variablePrefix = ((PlaceholderExpansion) hook).getIdentifier();
36 | }
37 |
38 | if (variablePrefix != null) {
39 | variables.add(variablePrefix + "_*");
40 | }
41 | }
42 |
43 | for (String variable : variables) {
44 | register(variable).supply(player -> PlaceholderAPI.setPlaceholders(player, '%' + variable + '%'));
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/PlayerPingVariable.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 |
7 | import java.lang.reflect.Field;
8 | import java.lang.reflect.InvocationTargetException;
9 | import java.lang.reflect.Method;
10 |
11 | import org.bukkit.entity.Player;
12 | import org.bukkit.plugin.Plugin;
13 |
14 | /**
15 | * Replace the ping variable.
16 | */
17 | @DefaultReplacer
18 | public class PlayerPingVariable extends DefaultReplacers {
19 |
20 | private Method getHandleMethod;
21 | private Field pingField;
22 |
23 | public PlayerPingVariable(ReplacerAPI replaceManager, Plugin plugin) {
24 | super(replaceManager, plugin);
25 | }
26 |
27 | @Override
28 | public void register() {
29 | register("ping").scoreSupply(this::getReflectionPing);
30 | }
31 |
32 | private int getReflectionPing(Player player) {
33 | try {
34 | if (getHandleMethod == null) {
35 | getHandleMethod = player.getClass().getDeclaredMethod("getHandle");
36 | //disable java security check. This will speed it a little
37 | getHandleMethod.setAccessible(true);
38 | }
39 |
40 | Object entityPlayer = getHandleMethod.invoke(player);
41 |
42 | if (pingField == null) {
43 | pingField = entityPlayer.getClass().getDeclaredField("ping");
44 | //disable java security check. This will speed it a little
45 | pingField.setAccessible(true);
46 | }
47 |
48 | //returns the found int value
49 | return pingField.getInt(entityPlayer);
50 | } catch (IllegalAccessException | NoSuchFieldException | NoSuchMethodException | InvocationTargetException ex) {
51 | //Forward the exception to replaceManager
52 | throw new RuntimeException(ex);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/PlayerPointsVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 |
7 | import org.black_ixx.playerpoints.PlayerPoints;
8 | import org.black_ixx.playerpoints.event.PlayerPointsChangeEvent;
9 | import org.black_ixx.playerpoints.event.PlayerPointsResetEvent;
10 |
11 | /**
12 | * Represents a replacer for the Plugin PlayerPoints
13 | *
14 | * https://dev.bukkit.org/bukkit-plugins/playerpoints/
15 | */
16 | @DefaultReplacer(plugin = "PlayerPoints")
17 | public class PlayerPointsVariables extends DefaultReplacers {
18 |
19 | public PlayerPointsVariables(ReplacerAPI replaceManager, PlayerPoints plugin) {
20 | super(replaceManager, plugin);
21 | }
22 |
23 | @Override
24 | public void register() {
25 | register("points")
26 | .scoreSupply(player -> plugin.getAPI().look(player.getUniqueId()))
27 | .eventScore(PlayerPointsResetEvent.class, () -> 0)
28 | .eventScore(PlayerPointsChangeEvent.class, event -> {
29 | int lastBal = plugin.getAPI().look(event.getPlayerId());
30 | return lastBal + event.getChange();
31 | });
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/SimpleClansVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 |
7 | import java.util.Collection;
8 | import java.util.Optional;
9 |
10 | import net.sacredlabyrinth.phaed.simpleclans.Clan;
11 | import net.sacredlabyrinth.phaed.simpleclans.ClanPlayer;
12 | import net.sacredlabyrinth.phaed.simpleclans.SimpleClans;
13 | import net.sacredlabyrinth.phaed.simpleclans.managers.ClanManager;
14 |
15 | import org.bukkit.entity.Player;
16 | import org.bukkit.util.NumberConversions;
17 |
18 | /**
19 | * Replace all variables that are associated with the SimpleClans plugin
20 | *
21 | * https://dev.bukkit.org/bukkit-plugins/simpleclans/
22 | *
23 | * @see SimpleClans
24 | * @see ClanPlayer
25 | * @see Clan
26 | * @see ClanManager
27 | */
28 | @DefaultReplacer(plugin = "SimpleClans")
29 | public class SimpleClansVariables extends DefaultReplacers {
30 |
31 | private final ClanManager clanManager;
32 |
33 | public SimpleClansVariables(ReplacerAPI replaceManager, SimpleClans plugin) {
34 | super(replaceManager, plugin);
35 |
36 | clanManager = plugin.getClanManager();
37 | }
38 |
39 | @Override
40 | public void register() {
41 | register("kills").scoreSupply(player -> getClanPlayer(player)
42 | .map(clanPlayer -> clanPlayer.getCivilianKills()
43 | + clanPlayer.getNeutralKills()
44 | + clanPlayer.getRivalKills())
45 | .orElse(-1));
46 |
47 | register("deaths").scoreSupply(player -> getClanPlayer(player)
48 | .map(ClanPlayer::getDeaths)
49 | .orElse(-1));
50 |
51 | register("kdr").scoreSupply(player -> getClanPlayer(player)
52 | .map(ClanPlayer::getKDR)
53 | .map(NumberConversions::round)
54 | .orElse(-1));
55 |
56 | register("members").scoreSupply(player -> getClan(player)
57 | .map(Clan::getSize)
58 | .orElse(-1));
59 |
60 | register("clan_kdr").scoreSupply(player -> getClan(player)
61 | .map(Clan::getTotalKDR)
62 | .map(NumberConversions::round)
63 | .orElse(-1));
64 |
65 | register("clan_money").scoreSupply(player -> getClan(player)
66 | .map(Clan::getBalance)
67 | .map(NumberConversions::round)
68 | .orElse(-1));
69 |
70 | register("rivals").scoreSupply(player -> getClan(player)
71 | .map(Clan::getRivals)
72 | .map(Collection::size)
73 | .orElse(-1));
74 |
75 | register("allies").scoreSupply(player -> getClan(player)
76 | .map(Clan::getAllies)
77 | .map(Collection::size)
78 | .orElse(-1));
79 |
80 | register("members_online").scoreSupply(player -> getClan(player)
81 | .map(Clan::getOnlineMembers)
82 | .map(Collection::size)
83 | .orElse(-1));
84 |
85 | register("allies_total").scoreSupply(player -> getClan(player)
86 | .map(Clan::getAllAllyMembers)
87 | .map(Collection::size)
88 | .orElse(-1));
89 |
90 | register("clan_kills").scoreSupply(player -> getClan(player)
91 | .map(clan -> clan.getTotalCivilian()
92 | + clan.getTotalNeutral()
93 | + clan.getTotalRival())
94 | .orElse(-1));
95 | }
96 |
97 | private Optional getClanPlayer(Player player) {
98 | return Optional.ofNullable(clanManager.getClanPlayer(player));
99 | }
100 |
101 | private Optional getClan(Player player) {
102 | return getClanPlayer(player).map(ClanPlayer::getClan);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/SkyblockVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 | import com.github.games647.scoreboardstats.variables.UnsupportedPluginException;
7 |
8 | import org.bukkit.plugin.Plugin;
9 | import org.bukkit.util.NumberConversions;
10 | import us.talabrek.ultimateskyblock.api.event.uSkyBlockScoreChangedEvent;
11 | import us.talabrek.ultimateskyblock.api.uSkyBlockAPI;
12 |
13 | /**
14 | * Replace all variables that are associated with the uSkyBlock plugin
15 | *
16 | * https://dev.bukkit.org/bukkit-plugins/uskyblock/
17 | */
18 | @DefaultReplacer(plugin = "uSkyblock")
19 | public class SkyblockVariables extends DefaultReplacers {
20 |
21 | public SkyblockVariables(ReplacerAPI replaceManager, uSkyBlockAPI plugin) {
22 | super(replaceManager, plugin);
23 | }
24 |
25 | private static uSkyBlockAPI getCheckVersion(Plugin plugin) throws UnsupportedPluginException {
26 | if (plugin instanceof uSkyBlockAPI) {
27 | return (uSkyBlockAPI) plugin;
28 | } else {
29 | throw new UnsupportedPluginException("Your uSkyBlock version is outdated");
30 | }
31 | }
32 |
33 | @Override
34 | public void register() {
35 | register("island_level")
36 | .scoreSupply(player -> NumberConversions.round(plugin.getIslandLevel(player)))
37 | .eventScore(uSkyBlockScoreChangedEvent.class, this::getNewScore);
38 | }
39 |
40 | private int getNewScore(uSkyBlockScoreChangedEvent changedEvent) {
41 | return NumberConversions.round(changedEvent.getScore().getScore());
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/TicksPerSecondTask.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | /**
4 | * Tracks how many ticks are passed per second.
5 | */
6 | public class TicksPerSecondTask implements Runnable {
7 |
8 | private static float lastTicks = 20.0F;
9 | //the last time we updated the ticks
10 | private long lastCheck;
11 |
12 | /**
13 | * Get the ticks count of the last check. 20 Ticks should pass per second
14 | *
15 | * @return the ticks count of the last check
16 | */
17 | public static float getLastTicks() {
18 | return lastTicks;
19 | }
20 |
21 | @Override
22 | public void run() {
23 | //nanoTime is more accurate
24 | long currentTime = System.nanoTime();
25 | long timeSpent = currentTime - lastCheck;
26 | //update the last check
27 | lastCheck = currentTime;
28 |
29 | //how many ticks passed since the last check * 1000 to convert to seconds
30 | float tps = 3 * 20 * 1000.0F / (timeSpent / (1_000 * 1_000));
31 | if (tps >= 0.0F && tps < 20.0F) {
32 | //Prevent all invalid values
33 | lastTicks = tps;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/defaults/src/main/java/com/github/games647/scoreboardstats/defaults/VaultVariables.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.defaults;
2 |
3 | import com.github.games647.scoreboardstats.variables.DefaultReplacer;
4 | import com.github.games647.scoreboardstats.variables.DefaultReplacers;
5 | import com.github.games647.scoreboardstats.variables.ReplacerAPI;
6 | import com.github.games647.scoreboardstats.variables.UnsupportedPluginException;
7 |
8 | import net.milkbowl.vault.economy.Economy;
9 |
10 | import org.bukkit.Bukkit;
11 | import org.bukkit.entity.Player;
12 | import org.bukkit.plugin.Plugin;
13 | import org.bukkit.plugin.RegisteredServiceProvider;
14 | import org.bukkit.util.NumberConversions;
15 |
16 | /**
17 | * Replace the economy variable with Vault.
18 | *
19 | * https://dev.bukkit.org/bukkit-plugins/vault/
20 | *
21 | * @see Economy
22 | */
23 | @DefaultReplacer(plugin = "Vault")
24 | public class VaultVariables extends DefaultReplacers {
25 |
26 | private final Economy economy;
27 |
28 | public VaultVariables(ReplacerAPI replaceManager, Plugin plugin) throws UnsupportedPluginException {
29 | super(replaceManager, plugin);
30 |
31 | RegisteredServiceProvider economyProvider = Bukkit.getServicesManager().getRegistration(Economy.class);
32 | if (economyProvider == null) {
33 | //check if an economy plugin is installed otherwise it would throw a exception if the want to replace
34 | throw new UnsupportedPluginException("Cannot find an economy plugin");
35 | } else {
36 | economy = economyProvider.getProvider();
37 | }
38 | }
39 |
40 | @Override
41 | public void register() {
42 | register("money").scoreSupply(this::getBalance);
43 | }
44 |
45 | private int getBalance(Player player) {
46 | return NumberConversions.round(economy.getBalance(player, player.getWorld().getName()));
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/plugin/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 |
6 | com.github.games647
7 | scoreboardstats-parent
8 | 1.0.0
9 |
10 |
11 | scoreboardstats-plugin
12 | jar
13 |
14 |
15 |
16 | ${project.name}
17 |
18 |
19 |
20 | org.apache.maven.plugins
21 | maven-shade-plugin
22 | 3.1.0
23 |
24 | false
25 | false
26 |
27 |
28 | com.github.games647:*
29 |
30 | com.zaxxer:HikariCP
31 |
32 | org.slf4j:slf4j-jdk14
33 | org.slf4j:slf4j-api
34 |
35 |
36 |
37 |
38 |
39 | package
40 |
41 | shade
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | src/main/resources
51 |
52 | true
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | md_5-releases
61 | https://repo.md-5.net/content/groups/public/
62 |
63 |
64 |
65 |
66 | shadowvolt-repo
67 | http://repo.dmulloy2.net/content/groups/public/
68 |
69 |
70 |
71 |
72 |
73 | com.github.games647
74 | scoreboardstats-variables
75 | 1.0.0
76 |
77 |
78 |
79 | com.github.games647
80 | scoreboardstats-defaults
81 | 1.0.0
82 |
83 |
84 |
85 | com.github.games647
86 | scoreboardstats-config
87 | 1.0.0
88 |
89 |
90 |
91 | com.github.games647
92 | scoreboardstats-pvp
93 | 1.0.0
94 |
95 |
96 |
99 |
100 | com.comphenix.protocol
101 | ProtocolLib
102 | 4.4.0-SNAPSHOT
103 | true
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/plugin/src/main/java/com/github/games647/scoreboardstats/PlayerListener.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats;
2 |
3 | import com.github.games647.scoreboardstats.config.Settings;
4 |
5 | import org.bukkit.entity.Player;
6 | import org.bukkit.event.EventHandler;
7 | import org.bukkit.event.EventPriority;
8 | import org.bukkit.event.Listener;
9 | import org.bukkit.event.player.PlayerChangedWorldEvent;
10 | import org.bukkit.event.player.PlayerJoinEvent;
11 | import org.bukkit.event.player.PlayerQuitEvent;
12 |
13 | /**
14 | * Listening to players events.
15 | */
16 | class PlayerListener implements Listener {
17 |
18 | private final ScoreboardStats plugin;
19 |
20 | /**
21 | * Creates a new player listener
22 | *
23 | * @param plugin ScoreboardStats plugin
24 | */
25 | public PlayerListener(ScoreboardStats plugin) {
26 | this.plugin = plugin;
27 | }
28 |
29 | @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
30 | public void onPlayerJoin(PlayerJoinEvent joinEvent) {
31 | Player player = joinEvent.getPlayer();
32 | if (Settings.isActiveWorld(player.getWorld().getName())) {
33 | //add it to the refresh queue
34 | plugin.getRefreshTask().addToQueue(joinEvent.getPlayer());
35 | }
36 | }
37 |
38 | @EventHandler(priority = EventPriority.LOWEST)
39 | public void onPlayerQuit(PlayerQuitEvent quitEvent) {
40 | Player player = quitEvent.getPlayer();
41 | plugin.getRefreshTask().remove(player);
42 | plugin.getScoreboardManager().unregister(player);
43 | }
44 |
45 | /**
46 | * Check if the player moves in a scoreboard disabled world
47 | *
48 | * @param worldChange the teleport event
49 | * @see com.github.games647.scoreboardstats.RefreshTask
50 | */
51 | //ignore cancelled events
52 | @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
53 | public void onWorldChange(PlayerChangedWorldEvent worldChange) {
54 | Player player = worldChange.getPlayer();
55 | //new world
56 | if (Settings.isActiveWorld(player.getWorld().getName())) {
57 | //old world
58 | if (!Settings.isActiveWorld(worldChange.getFrom().getName())) {
59 | //Activate the scoreboard if it was disabled
60 | plugin.getRefreshTask().addToQueue(player);
61 | }
62 | } else {
63 | //Disable the scoreboard if the player goes into a disabled world
64 | plugin.getRefreshTask().remove(player);
65 | plugin.getScoreboardManager().unregister(player);
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/plugin/src/main/java/com/github/games647/scoreboardstats/RefreshTask.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats;
2 |
3 | import com.github.games647.scoreboardstats.config.Settings;
4 | import com.google.common.collect.Maps;
5 |
6 | import java.util.Map;
7 |
8 | import org.apache.commons.lang.mutable.MutableInt;
9 | import org.bukkit.entity.Player;
10 |
11 | /**
12 | * Handling all updates for a player in a performance optimized variant. This
13 | * class split the updates over the ticks much smoother.
14 | *
15 | * @see SbManager
16 | * @see com.github.games647.scoreboardstats.variables.ReplaceManager
17 | */
18 | public class RefreshTask implements Runnable {
19 |
20 | private final ScoreboardStats plugin;
21 |
22 | //Prevent duplicate entries and is faster than the delay queue
23 | private final Map queue = Maps.newHashMapWithExpectedSize(100);
24 |
25 | private int nextGlobalUpdate = 20 * Settings.getInterval();
26 |
27 | /**
28 | * Initialize refresh task
29 | *
30 | * @param instance ScoreboardStats instance
31 | */
32 | public RefreshTask(ScoreboardStats instance) {
33 | this.plugin = instance;
34 | }
35 |
36 | @Override
37 | public void run() {
38 | //let the players update smoother
39 | int remainingUpdates = getNextUpdates();
40 | for (Map.Entry entry : queue.entrySet()) {
41 | Player player = entry.getKey();
42 | MutableInt remainingTicks = entry.getValue();
43 | if (remainingTicks.intValue() == 0) {
44 | if (remainingUpdates != 0) {
45 | //Smoother refreshing; limit the updates
46 | plugin.getScoreboardManager().onUpdate(player);
47 | remainingTicks.setValue(20 * Settings.getInterval());
48 | remainingUpdates--;
49 | }
50 | } else {
51 | remainingTicks.decrement();
52 | }
53 | }
54 |
55 | nextGlobalUpdate--;
56 | if (nextGlobalUpdate == 0) {
57 | nextGlobalUpdate = 20 * Settings.getInterval();
58 | //update globals
59 | plugin.getReplaceManager().updateGlobals();
60 | }
61 | }
62 |
63 | /**
64 | * Add a player to the queue for updating him.
65 | *
66 | * @param request the player that should be added.
67 | */
68 | public void addToQueue(Player request) {
69 | if (queue.containsKey(request)) {
70 | //check if it isn't already in the queue
71 | queue.put(request, new MutableInt(20 * Settings.getInterval()));
72 | }
73 | }
74 |
75 | /**
76 | * Checks if the player is in the refresh queue.
77 | *
78 | * @param request player instance
79 | * @return true if the player is in the refresh queue.
80 | */
81 | public boolean contains(Player request) {
82 | return queue.containsKey(request);
83 | }
84 |
85 | /**
86 | * Explicit removes the player from the refresh queue.
87 | *
88 | * @param request player who should be removed
89 | * @return if the last entry exists
90 | */
91 | public boolean remove(Player request) {
92 | return queue.remove(request) != null;
93 | }
94 |
95 | /**
96 | * Clears the complete queue.
97 | */
98 | public void clear() {
99 | queue.clear();
100 | }
101 |
102 | private int getNextUpdates() {
103 | int nextUpdates = queue.size() / 20;
104 | if (nextUpdates <= 0) {
105 | //just update minimum one player per tick. Otherwise servers with not much players
106 | //won't receive any updates
107 | return 1;
108 | }
109 |
110 | return nextUpdates;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/plugin/src/main/java/com/github/games647/scoreboardstats/SbManager.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats;
2 |
3 | import com.github.games647.scoreboardstats.config.Settings;
4 | import com.github.games647.scoreboardstats.config.VariableItem;
5 |
6 | import java.util.UUID;
7 |
8 | import org.bukkit.Bukkit;
9 | import org.bukkit.entity.Player;
10 |
11 | /**
12 | * Manage the scoreboard access.
13 | */
14 | public abstract class SbManager implements BoardManager {
15 |
16 | protected static final String UNKNOWN_VARIABLE = "Cannot find variable with name: ({}) Maybe you misspelled it " +
17 | "or the replacer isn't available yet";
18 |
19 | protected static final String SB_NAME = "Stats";
20 | protected static final String TEMP_SB_NAME = SB_NAME + 'T';
21 |
22 | private static final int MAX_ITEM_LENGTH = 16;
23 |
24 | protected final ScoreboardStats plugin;
25 |
26 | private final String permission;
27 |
28 | protected SbManager(ScoreboardStats plugin) {
29 | this.plugin = plugin;
30 | this.permission = plugin.getName().toLowerCase() + ".use";
31 | }
32 |
33 | @Override
34 | public void registerAll() {
35 | boolean isPvpStats = Settings.isPvpStats();
36 | //maybe batch this
37 | Bukkit.getOnlinePlayers().stream().filter(Player::isOnline).forEach(player -> {
38 | if (isPvpStats) {
39 | //maybe batch this
40 | player.removeMetadata("player_stats", plugin);
41 | plugin.getStatsDatabase().loadAccountAsync(player);
42 | }
43 |
44 | plugin.getRefreshTask().addToQueue(player);
45 | });
46 | }
47 |
48 | @Override
49 | public void unregisterAll() {
50 | Bukkit.getOnlinePlayers().forEach(this::unregister);
51 | }
52 |
53 | @Override
54 | public void updateVariable(Player player, String variable, int newScore) {
55 | VariableItem variableItem = Settings.getMainScoreboard().getItemsByVariable().get(variable);
56 | if (variableItem != null) {
57 | update(player, variableItem.getDisplayText(), newScore);
58 | }
59 | }
60 |
61 | @Override
62 | public void updateVariable(Player player, String variable, String newScore) {
63 | VariableItem variableItem = Settings.getMainScoreboard().getItemsByName().get(variable);
64 | if (variableItem != null) {
65 | // update(player, variableItem.getDisplayText(), newScore);
66 | }
67 | }
68 |
69 | protected abstract void update(Player player, String title, int newScore);
70 |
71 | protected void scheduleShowTask(Player player, boolean action) {
72 | if (!Settings.isTempScoreboard()) {
73 | return;
74 | }
75 |
76 | int interval = Settings.getTempDisappear();
77 | if (action) {
78 | interval = Settings.getTempAppear();
79 | }
80 |
81 | UUID uuid = player.getUniqueId();
82 | Bukkit.getScheduler().runTaskLater(plugin, () -> {
83 | Player localPlayer = Bukkit.getPlayer(uuid);
84 | if (localPlayer == null) {
85 | return;
86 | }
87 |
88 | if (action) {
89 | createTopListScoreboard(player);
90 | } else {
91 | createScoreboard(player);
92 | }
93 | }, interval * 20L);
94 | }
95 |
96 | protected String stripLength(String check) {
97 | if (check.length() > MAX_ITEM_LENGTH) {
98 | return check.substring(0, MAX_ITEM_LENGTH);
99 | }
100 |
101 | return check;
102 | }
103 |
104 | protected boolean isAllowed(Player player) {
105 | return player.hasPermission(permission) && Settings.isActiveWorld(player.getWorld().getName());
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/plugin/src/main/java/com/github/games647/scoreboardstats/ScoreboardStats.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats;
2 |
3 | import com.github.games647.scoreboardstats.commands.SidebarCommands;
4 | import com.github.games647.scoreboardstats.config.Settings;
5 | import com.github.games647.scoreboardstats.defaults.TicksPerSecondTask;
6 | import com.github.games647.scoreboardstats.pvp.Database;
7 | import com.github.games647.scoreboardstats.scoreboard.PacketManager;
8 | import com.github.games647.scoreboardstats.variables.ReplaceManager;
9 |
10 | import java.lang.reflect.Constructor;
11 | import java.util.logging.Level;
12 |
13 | import org.bukkit.plugin.java.JavaPlugin;
14 | import org.slf4j.Logger;
15 | import org.slf4j.LoggerFactory;
16 | import org.slf4j.impl.JDK14LoggerAdapter;
17 |
18 | /**
19 | * Represents the main class of this plugin.
20 | */
21 | public class ScoreboardStats extends JavaPlugin {
22 |
23 | private final Logger logger = createLoggerFromJDK(getLogger());
24 |
25 | //don't create instances here that accesses the bukkit API - it will be incompatible with older mc versions
26 | private RefreshTask refreshTask;
27 | private Settings settings;
28 | private SbManager scoreboardManager;
29 | private Database database;
30 |
31 | private ReplaceManager replaceManager;
32 |
33 | private static Logger createLoggerFromJDK(java.util.logging.Logger parent) {
34 | try {
35 | parent.setLevel(Level.ALL);
36 |
37 | Class adapterClass = JDK14LoggerAdapter.class;
38 | Constructor cons = adapterClass.getDeclaredConstructor(java.util.logging.Logger.class);
39 | cons.setAccessible(true);
40 | return cons.newInstance(parent);
41 | } catch (ReflectiveOperationException reflectEx) {
42 | parent.log(Level.WARNING, "Cannot create slf4j logging adapter", reflectEx);
43 | parent.log(Level.WARNING, "Creating logger instance manually...");
44 | return LoggerFactory.getLogger(parent.getName());
45 | }
46 | }
47 |
48 | /**
49 | * Get the scoreboard manager.
50 | *
51 | * @return the manager
52 | */
53 | public BoardManager getScoreboardManager() {
54 | return scoreboardManager;
55 | }
56 |
57 | /**
58 | * Get the replace manager.
59 | *
60 | * @return the manager
61 | */
62 | public ReplaceManager getReplaceManager() {
63 | return replaceManager;
64 | }
65 |
66 | /**
67 | * Get the refresh task for updating the scoreboard
68 | *
69 | * @return the refresh task instance
70 | */
71 | public RefreshTask getRefreshTask() {
72 | return refreshTask;
73 | }
74 |
75 | /**
76 | * The database manager for pvp stats
77 | *
78 | * @return pvp stats database manager
79 | */
80 | public Database getStatsDatabase() {
81 | return database;
82 | }
83 |
84 | public Logger getLog() {
85 | return logger;
86 | }
87 |
88 | /**
89 | * Enable the plugin
90 | */
91 | @Override
92 | public void onEnable() {
93 | //Load the config + needs to be initialised to get the configured value for update-checking
94 | settings = new Settings(this, logger);
95 | settings.loadConfig();
96 |
97 | refreshTask = new RefreshTask(this);
98 |
99 | //Register all events
100 | getServer().getPluginManager().registerEvents(new PlayerListener(this), this);
101 |
102 | //register all commands based on the root command of this plugin
103 | getCommand(getName().toLowerCase()).setExecutor(new SidebarCommands(this));
104 |
105 | //start tracking the ticks
106 | getServer().getScheduler().runTaskTimer(this, new TicksPerSecondTask(), 5 * 20L, 3 * 20L);
107 | //Start the refresh task; it should run on every tick, because it's smoothly update the variables with limit
108 | getServer().getScheduler().runTaskTimer(this, refreshTask, 5 * 20L, 1L);
109 |
110 | scoreboardManager = new PacketManager(this);
111 | replaceManager = new ReplaceManager(scoreboardManager, this, logger);
112 |
113 | if (Settings.isPvpStats()) {
114 | database = new Database(this, logger);
115 | database.setupDatabase();
116 | }
117 |
118 | //creates scoreboards for every player that is online
119 | scoreboardManager.registerAll();
120 | }
121 |
122 | /**
123 | * Disable the plugin
124 | */
125 | @Override
126 | public void onDisable() {
127 | if (scoreboardManager != null) {
128 | //Clear all scoreboards
129 | scoreboardManager.unregisterAll();
130 | }
131 |
132 | if (replaceManager != null) {
133 | replaceManager.close();
134 | }
135 |
136 | if (database != null) {
137 | //flush the cache to the database
138 | database.saveAll();
139 | }
140 | }
141 |
142 | /**
143 | * Reload the plugin
144 | */
145 | public void onReload() {
146 | if (settings != null) {
147 | settings.loadConfig();
148 | }
149 |
150 | if (refreshTask != null) {
151 | refreshTask.clear();
152 | }
153 |
154 | if (scoreboardManager != null) {
155 | scoreboardManager.unregisterAll();
156 | }
157 |
158 | scoreboardManager = new PacketManager(this);
159 | if (database == null) {
160 | database = new Database(this, logger);
161 | database.setupDatabase();
162 | } else {
163 | database.setupDatabase();
164 | }
165 |
166 | scoreboardManager.registerAll();
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/plugin/src/main/java/com/github/games647/scoreboardstats/commands/CommandHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.commands;
2 |
3 | import com.github.games647.scoreboardstats.ScoreboardStats;
4 |
5 | import java.util.Arrays;
6 | import java.util.List;
7 |
8 | import org.bukkit.ChatColor;
9 | import org.bukkit.command.CommandSender;
10 |
11 | /**
12 | * Represents a handler for sub commands
13 | */
14 | abstract class CommandHandler {
15 |
16 | protected final ScoreboardStats plugin;
17 |
18 | private final String permission;
19 | private final String description;
20 | private final String subCommand;
21 |
22 | private final List aliases;
23 |
24 | CommandHandler(String command, ScoreboardStats plugin, String... aliases) {
25 | this(command, "&cNo description", plugin, aliases);
26 | }
27 |
28 | CommandHandler(String command, String description, ScoreboardStats plugin, String... aliases) {
29 | this.plugin = plugin;
30 | this.description = ChatColor.translateAlternateColorCodes('&', description);
31 | this.permission = plugin.getName().toLowerCase() + ".command." + command;
32 | this.subCommand = command;
33 |
34 | this.aliases = Arrays.asList(aliases);
35 | }
36 |
37 | /**
38 | * Get the main sub command
39 | *
40 | * @return the main sub command
41 | */
42 | public String getSubCommand() {
43 | return subCommand;
44 | }
45 |
46 | /**
47 | * Get all aliases of this command. All strings in this list can invoke this
48 | * command
49 | *
50 | * @return all aliases
51 | */
52 | public Iterable getAliases() {
53 | return aliases;
54 | }
55 |
56 | /**
57 | * Get the description of this command
58 | *
59 | * @return the description
60 | */
61 | public String getDescription() {
62 | return description;
63 | }
64 |
65 | /**
66 | * Get the permission of this command
67 | *
68 | * @return the permission
69 | */
70 | public String getPermission() {
71 | return permission;
72 | }
73 |
74 | /**
75 | * Check if the player has enough permissions to execute this command. It
76 | * will send a no permission if he doesn't have it.
77 | *
78 | * @param sender the executor
79 | * @return whether the sender has enough permissions
80 | */
81 | public boolean hasPermission(CommandSender sender) {
82 | if (sender.hasPermission(permission)) {
83 | return true;
84 | }
85 |
86 | sender.sendMessage("§4 You don't have enough permissions to do that");
87 | return false;
88 | }
89 |
90 | /**
91 | * Executes the given subcommand
92 | *
93 | * @param sender Source of the command
94 | * @param subCommand Command which was executed
95 | * @param args The arguments passed to the command, including final partial argument to be completed
96 | */
97 | public abstract void onCommand(CommandSender sender, String subCommand, String... args);
98 |
99 | /**
100 | * Requests a list of possible completions for a command argument.
101 | *
102 | * @param sender Source of the command
103 | * @param subCommand Command which was executed
104 | * @param args The arguments passed to the command, including final partial argument to be completed
105 | * @return A List of possible completions for the final argument, or null to default to the command executor
106 | */
107 | public List onTabComplete(CommandSender sender, String subCommand, String... args) {
108 | //default null -> bukkit will handle this with a list of only players
109 | return null;
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/plugin/src/main/java/com/github/games647/scoreboardstats/commands/SidebarCommands.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.commands;
2 |
3 | import com.github.games647.scoreboardstats.ScoreboardStats;
4 | import com.google.common.collect.ImmutableList;
5 | import com.google.common.collect.Lists;
6 | import com.google.common.collect.Maps;
7 |
8 | import java.util.Arrays;
9 | import java.util.List;
10 | import java.util.Map;
11 | import java.util.stream.Collectors;
12 |
13 | import org.bukkit.ChatColor;
14 | import org.bukkit.command.Command;
15 | import org.bukkit.command.CommandSender;
16 | import org.bukkit.command.TabExecutor;
17 | import org.bukkit.util.StringUtil;
18 |
19 | /**
20 | * This class forward all commands to the user commands for a better access
21 | */
22 | public class SidebarCommands implements TabExecutor {
23 |
24 | private final ScoreboardStats plugin;
25 |
26 | private final Map commands = Maps.newHashMap();
27 | private final List subCommands = Lists.newArrayList();
28 |
29 | public SidebarCommands(ScoreboardStats plugin) {
30 | this.plugin = plugin;
31 |
32 | registerSubCommands();
33 | }
34 |
35 | @Override
36 | public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
37 | //default subcommand
38 | String subCommand = "toggle";
39 | String[] newArgs = args;
40 | if (args.length != 0) {
41 | subCommand = args[0];
42 | //remove the subcommand from the args list
43 | newArgs = Arrays.copyOfRange(args, 1, args.length);
44 | }
45 |
46 | CommandHandler commandHandler = commands.get(subCommand);
47 | if (commandHandler == null) {
48 | sender.sendMessage(ChatColor.DARK_RED + "Command not found");
49 | } else {
50 | if (commandHandler.hasPermission(sender)) {
51 | commandHandler.onCommand(sender, subCommand, newArgs);
52 | }
53 | }
54 |
55 | return true;
56 | }
57 |
58 | @Override
59 | public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
60 | if (args.length == 0) {
61 | return ImmutableList.of();
62 | }
63 |
64 | String lastWord = args[args.length - 1];
65 | List suggestion;
66 | if (args.length == 1) {
67 | return subCommands.stream()
68 | .filter(subCommand -> StringUtil.startsWithIgnoreCase(subCommand, lastWord))
69 | .sorted(String.CASE_INSENSITIVE_ORDER)
70 | .collect(Collectors.toList());
71 | }
72 |
73 | String subCommand = args[0];
74 | CommandHandler commandHandler = commands.get(subCommand);
75 | if (commandHandler != null) {
76 | //remove the subcommand from the args list
77 | suggestion = commandHandler.onTabComplete(sender, subCommand, Arrays.copyOfRange(args, 1, args.length));
78 | if (suggestion != null) {
79 | //Prevent NPEs and the usage of this method in nearly every handler
80 | suggestion.sort(String.CASE_INSENSITIVE_ORDER);
81 | }
82 |
83 | return suggestion;
84 | }
85 |
86 | return null;
87 | }
88 |
89 | private void registerSubCommands() {
90 | register(new ToggleCommand(plugin));
91 | }
92 |
93 | private void register(CommandHandler handler) {
94 | for (String alias : handler.getAliases()) {
95 | commands.put(alias, handler);
96 | subCommands.add(alias);
97 | }
98 |
99 | String subCommand = handler.getSubCommand();
100 | commands.put(subCommand, handler);
101 | subCommands.add(subCommand);
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/plugin/src/main/java/com/github/games647/scoreboardstats/commands/ToggleCommand.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.commands;
2 |
3 | import com.github.games647.scoreboardstats.RefreshTask;
4 | import com.github.games647.scoreboardstats.ScoreboardStats;
5 |
6 | import org.bukkit.command.CommandSender;
7 | import org.bukkit.entity.Player;
8 | import org.bukkit.scoreboard.DisplaySlot;
9 |
10 | /**
11 | * Change the visibility of the scoreboard
12 | */
13 | public class ToggleCommand extends CommandHandler {
14 |
15 | private static final String TOGGLE_MSG = "§2Toggling the scoreboard";
16 |
17 | public ToggleCommand(ScoreboardStats plugin) {
18 | super("toggle", "&aToggles the sidebar", plugin, "hide", "show");
19 | }
20 |
21 | @Override
22 | public void onCommand(CommandSender sender, String subCommand, String... args) {
23 | if (!(sender instanceof Player)) {
24 | //the console cannot have a scoreboard
25 | sender.sendMessage("§4This command can only be executed by Players");
26 | return;
27 | }
28 |
29 | //We checked that it can only be players
30 | Player player = (Player) sender;
31 | RefreshTask refreshTask = plugin.getRefreshTask();
32 | if (refreshTask.contains(player)) {
33 | if ("hide".equals(subCommand) || "toggle".equals(subCommand)) {
34 | refreshTask.remove(player);
35 | player.getScoreboard().clearSlot(DisplaySlot.SIDEBAR);
36 | player.sendMessage(TOGGLE_MSG);
37 | }
38 | } else if ("show".equals(subCommand) || "toggle".equals(subCommand)) {
39 | player.sendMessage(TOGGLE_MSG);
40 | refreshTask.addToQueue(player);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/plugin/src/main/java/com/github/games647/scoreboardstats/scoreboard/Objective.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.scoreboard;
2 |
3 | import com.comphenix.protocol.events.PacketContainer;
4 | import com.comphenix.protocol.wrappers.EnumWrappers.ScoreboardAction;
5 | import com.google.common.collect.Lists;
6 | import com.google.common.collect.Maps;
7 |
8 | import java.util.List;
9 | import java.util.Map;
10 | import java.util.Objects;
11 | import java.util.OptionalInt;
12 |
13 | import org.apache.commons.lang.builder.ToStringBuilder;
14 |
15 | import static com.comphenix.protocol.PacketType.Play.Server.SCOREBOARD_DISPLAY_OBJECTIVE;
16 | import static com.comphenix.protocol.PacketType.Play.Server.SCOREBOARD_OBJECTIVE;
17 | import static com.comphenix.protocol.PacketType.Play.Server.SCOREBOARD_SCORE;
18 |
19 | /**
20 | * Represents a sidebar objective
21 | */
22 | public class Objective {
23 |
24 | private static final int MAX_ITEM_SIZE = 15;
25 | private static final int SIDEBAR_SLOT = 1;
26 |
27 | //A scoreboard can only hold < 16 scores
28 | final Map scores = Maps.newHashMapWithExpectedSize(MAX_ITEM_SIZE);
29 |
30 | private final PlayerScoreboard scoreboard;
31 | private final String objectiveId;
32 | String displayName;
33 |
34 | Objective(PlayerScoreboard scoreboard, String objectiveId, String displayName) {
35 | this.scoreboard = scoreboard;
36 |
37 | this.objectiveId = objectiveId;
38 | this.displayName = displayName;
39 | }
40 |
41 | public boolean isShown() {
42 | //Prevents NPE with this ordering
43 | return scoreboard.getSidebarObjective().map(sidebar -> sidebar.equals(this)).orElse(false);
44 | }
45 |
46 | public boolean exists() {
47 | return scoreboard.getObjective(objectiveId).isPresent();
48 | }
49 |
50 | public String getId() {
51 | return objectiveId;
52 | }
53 |
54 | public String getDisplayName() {
55 | return displayName;
56 | }
57 |
58 | public void setDisplayName(String displayName) {
59 | if (this.displayName.equals(displayName)) {
60 | return;
61 | }
62 |
63 | this.displayName = displayName;
64 | sendObjectivePacket(State.UPDATE_DISPLAY_NAME);
65 | }
66 |
67 | public OptionalInt getScore(String name) {
68 | Integer score = scores.get(name);
69 | if (score == null) {
70 | return OptionalInt.empty();
71 | }
72 |
73 | return OptionalInt.of(score);
74 | }
75 |
76 | public int getScore(String name, int def) {
77 | Integer score = scores.get(name);
78 | if (score == null) {
79 | setScores(name, def);
80 | return def;
81 | }
82 |
83 | return score;
84 | }
85 |
86 | public boolean hasScore(String name) {
87 | return scores.containsKey(name);
88 | }
89 |
90 | public void setScores(String name, int value) {
91 | Integer oldVal = scores.put(name, value);
92 | if (oldVal != null && oldVal == value) {
93 | return;
94 | }
95 |
96 | sendScorePacket(name, value, ScoreboardAction.CHANGE);
97 | }
98 |
99 | public List> getScores() {
100 | List> values = Lists.newArrayListWithExpectedSize(scores.size());
101 | values.addAll(scores.entrySet());
102 | values.sort((score1, score2) -> Integer.compare(score2.getValue(), score1.getValue()));
103 | return values;
104 | }
105 |
106 | public void removeScore(String name) {
107 | scores.remove(name);
108 | sendScorePacket(name, 0, ScoreboardAction.REMOVE);
109 | }
110 |
111 | public void clear() {
112 | scores.keySet().forEach(this::removeScore);
113 | }
114 |
115 | public PlayerScoreboard getScoreboard() {
116 | return scoreboard;
117 | }
118 |
119 | @Override
120 | public int hashCode() {
121 | return Objects.hash(objectiveId);
122 | }
123 |
124 | @Override
125 | public boolean equals(Object obj) {
126 | //ignores also null
127 | return obj instanceof Objective && Objects.equals(objectiveId, ((Objective) obj).objectiveId);
128 | }
129 |
130 | @Override
131 | public String toString() {
132 | return ToStringBuilder.reflectionToString(this);
133 | }
134 |
135 | private void sendScorePacket(String name, int score, ScoreboardAction action) {
136 | PacketContainer packet = new PacketContainer(SCOREBOARD_SCORE);
137 | packet.getStrings().write(0, name);
138 | packet.getStrings().write(1, objectiveId);
139 |
140 | packet.getIntegers().write(0, score);
141 |
142 | packet.getScoreboardActions().write(0, action);
143 |
144 | scoreboard.sendPacket(packet);
145 | }
146 |
147 | void sendObjectivePacket(State state) {
148 | PacketContainer packet = new PacketContainer(SCOREBOARD_OBJECTIVE);
149 | packet.getStrings().write(0, objectiveId);
150 |
151 | if (state != State.REMOVE) {
152 | packet.getStrings().write(1, displayName);
153 | packet.getStrings().write(2, "integer");
154 | }
155 |
156 | scoreboard.sendPacket(packet);
157 | }
158 |
159 | void sendShowPacket() {
160 | PacketContainer packet = new PacketContainer(SCOREBOARD_DISPLAY_OBJECTIVE);
161 | packet.getStrings().write(0, objectiveId);
162 |
163 | packet.getIntegers().write(0, SIDEBAR_SLOT);
164 | scoreboard.sendPacket(packet);
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/plugin/src/main/java/com/github/games647/scoreboardstats/scoreboard/PacketListener.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.scoreboard;
2 |
3 | import com.comphenix.net.sf.cglib.proxy.Factory;
4 | import com.comphenix.protocol.PacketType;
5 | import com.comphenix.protocol.events.PacketAdapter;
6 | import com.comphenix.protocol.events.PacketContainer;
7 | import com.comphenix.protocol.events.PacketEvent;
8 | import com.comphenix.protocol.wrappers.EnumWrappers.ScoreboardAction;
9 |
10 | import java.util.Collection;
11 | import java.util.Optional;
12 | import java.util.UUID;
13 |
14 | import org.bukkit.Bukkit;
15 | import org.bukkit.entity.Player;
16 | import org.bukkit.plugin.Plugin;
17 | import org.bukkit.scoreboard.DisplaySlot;
18 |
19 | import static com.comphenix.protocol.PacketType.Play.Server.SCOREBOARD_DISPLAY_OBJECTIVE;
20 | import static com.comphenix.protocol.PacketType.Play.Server.SCOREBOARD_OBJECTIVE;
21 | import static com.comphenix.protocol.PacketType.Play.Server.SCOREBOARD_SCORE;
22 | import static com.comphenix.protocol.PacketType.Play.Server.SCOREBOARD_TEAM;
23 |
24 | /**
25 | * Listening all outgoing packets and check + handle for possibly client crash cases. This Listener should only read and
26 | * listen to relevant packets.
27 | *
28 | * Protocol specifications can be found here http://wiki.vg/Protocol
29 | */
30 | class PacketListener extends PacketAdapter {
31 |
32 | private final PacketManager manager;
33 |
34 | PacketListener(Plugin plugin, PacketManager manager) {
35 | super(plugin, SCOREBOARD_DISPLAY_OBJECTIVE, SCOREBOARD_OBJECTIVE, SCOREBOARD_SCORE, SCOREBOARD_TEAM);
36 |
37 | this.manager = manager;
38 | }
39 |
40 | @Override
41 | public void onPacketSending(PacketEvent packetEvent) {
42 | Player player = packetEvent.getPlayer();
43 | if (packetEvent.isCancelled() || player instanceof Factory) {
44 | return;
45 | }
46 |
47 | PacketContainer packet = packetEvent.getPacket();
48 | if (packet.hasMetadata("ScoreboardStats")) {
49 | //it's our own packet
50 | return;
51 | }
52 |
53 | UUID playerUUID = player.getUniqueId();
54 |
55 | //handle async packets by other plugins
56 | if (Bukkit.isPrimaryThread()) {
57 | ensureMainThread(playerUUID, packet);
58 | } else {
59 | PacketContainer clone = packet.deepClone();
60 | Bukkit.getScheduler().runTask(plugin, () -> ensureMainThread(playerUUID, clone));
61 | }
62 | }
63 |
64 | private void ensureMainThread(UUID uuid, PacketContainer packet) {
65 | Player player = Bukkit.getPlayer(uuid);
66 | if (player == null) {
67 | return;
68 | }
69 |
70 | PacketType packetType = packet.getType();
71 | if (packetType.equals(SCOREBOARD_SCORE)) {
72 | handleScorePacket(player, packet);
73 | } else if (packetType.equals(SCOREBOARD_OBJECTIVE)) {
74 | handleObjectivePacket(player, packet);
75 | } else if (packetType.equals(SCOREBOARD_DISPLAY_OBJECTIVE)) {
76 | handleDisplayPacket(player, packet);
77 | } else if (packetType.equals(SCOREBOARD_TEAM)) {
78 | handleTeamPacket(player, packet);
79 | }
80 | }
81 |
82 | private void handleScorePacket(Player player, PacketContainer packet) {
83 | String scoreName = packet.getStrings().read(0);
84 | String parent = packet.getStrings().read(1);
85 | int score = packet.getIntegers().read(0);
86 |
87 | //state id
88 | ScoreboardAction action = packet.getScoreboardActions().read(0);
89 |
90 | //Packet receiving validation
91 | if (parent.length() > 16) {
92 | //Invalid packet
93 | return;
94 | }
95 |
96 | PlayerScoreboard scoreboard = manager.getScoreboard(player);
97 | //scores actually only have two state id, because these
98 | if (action == ScoreboardAction.CHANGE) {
99 | scoreboard.getObjective(parent).ifPresent(objective -> objective.scores.put(scoreName, score));
100 | } else if (action == ScoreboardAction.REMOVE) {
101 | scoreboard.getObjective(parent).ifPresent(objective -> objective.scores.remove(scoreName, score));
102 | }
103 | }
104 |
105 | private void handleObjectivePacket(Player player, PacketContainer packet) {
106 | String objectiveId = packet.getStrings().read(0);
107 | //Can be empty
108 | String displayName = packet.getStrings().read(1);
109 | State action = State.fromId(packet.getIntegers().read(0));
110 |
111 | //Packet receiving validation
112 | if (objectiveId.length() > 16 || displayName.length() > 32) {
113 | //Invalid packet
114 | return;
115 | }
116 |
117 | PlayerScoreboard scoreboard = manager.getScoreboard(player);
118 | if (action == State.CREATE) {
119 | Objective objective = new Objective(scoreboard, objectiveId, displayName);
120 | scoreboard.objectivesByName.put(objectiveId, objective);
121 | } else {
122 | if (action == State.REMOVE) {
123 | scoreboard.objectivesByName.remove(objectiveId);
124 | } else {
125 | scoreboard.getObjective(objectiveId).ifPresent(obj -> obj.displayName = displayName);
126 | }
127 | }
128 | }
129 |
130 | private void handleDisplayPacket(Player player, PacketContainer packet) {
131 | //Can be empty; if so it would just clear the slot
132 | String objectiveId = packet.getStrings().read(0);
133 | Optional slot = SlotTransformer.fromId(packet.getIntegers().read(0));
134 |
135 | //Packet receiving validation
136 | if (!slot.isPresent() || objectiveId.length() > 16) {
137 | return;
138 | }
139 |
140 | PlayerScoreboard scoreboard = manager.getScoreboard(player);
141 | if (slot.get() == DisplaySlot.SIDEBAR) {
142 | scoreboard.getObjective(objectiveId).ifPresent(obj -> scoreboard.sidebarObjective = obj);
143 | } else {
144 | scoreboard.getSidebarObjective().filter(objective -> objective.getId().equals(objectiveId))
145 | .ifPresent(objective -> scoreboard.sidebarObjective = null);
146 | }
147 | }
148 |
149 | private void handleTeamPacket(Player player, PacketContainer packet) {
150 | String teamId = packet.getStrings().read(0);
151 | Optional optMode = TeamMode.getMode(packet.getIntegers().read(0));
152 |
153 | if (!optMode.isPresent() || teamId.length() > 16) {
154 | return;
155 | }
156 |
157 | TeamMode mode = optMode.get();
158 |
159 | PlayerScoreboard scoreboard = manager.getScoreboard(player);
160 | if (mode == TeamMode.CREATE) {
161 | Collection members = packet.getSpecificModifier(Collection.class).read(0);
162 | scoreboard.teamsByName.put(teamId, new Team(scoreboard, teamId, members));
163 | } else if (mode == TeamMode.REMOVE) {
164 | scoreboard.teamsByName.remove(teamId);
165 | } else if (mode == TeamMode.ADD_MEMBER) {
166 | Collection members = packet.getSpecificModifier(Collection.class).read(0);
167 | scoreboard.getTeam(teamId).ifPresent(team -> team.members.addAll(members));
168 | } else if (mode == TeamMode.REMOVE_MEMBER) {
169 | Collection members = packet.getSpecificModifier(Collection.class).read(0);
170 | scoreboard.getTeam(teamId).ifPresent(team -> team.members.removeAll(members));
171 | }
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/plugin/src/main/java/com/github/games647/scoreboardstats/scoreboard/PacketManager.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.scoreboard;
2 |
3 | import com.comphenix.protocol.ProtocolLibrary;
4 | import com.github.games647.scoreboardstats.SbManager;
5 | import com.github.games647.scoreboardstats.ScoreboardStats;
6 | import com.github.games647.scoreboardstats.config.Settings;
7 | import com.github.games647.scoreboardstats.config.VariableItem;
8 | import com.github.games647.scoreboardstats.variables.ReplaceManager;
9 | import com.github.games647.scoreboardstats.variables.ReplacerException;
10 | import com.github.games647.scoreboardstats.variables.UnknownVariableException;
11 | import com.google.common.collect.Maps;
12 |
13 | import java.util.Iterator;
14 | import java.util.Map;
15 | import java.util.Optional;
16 | import java.util.OptionalInt;
17 | import java.util.UUID;
18 |
19 | import org.bukkit.entity.Player;
20 |
21 | /**
22 | * Manage the scoreboards with packet-use
23 | */
24 | public class PacketManager extends SbManager {
25 |
26 | private final Map scoreboards = Maps.newHashMapWithExpectedSize(50);
27 |
28 | /**
29 | * Creates a new scoreboard manager for the packet system.
30 | *
31 | * @param plugin ScoreboardStats instance
32 | */
33 | public PacketManager(ScoreboardStats plugin) {
34 | super(plugin);
35 |
36 | ProtocolLibrary.getProtocolManager().addPacketListener(new PacketListener(plugin, this));
37 | }
38 |
39 | public PlayerScoreboard getScoreboard(Player player) {
40 | return scoreboards
41 | .computeIfAbsent(player.getUniqueId(), key -> new PlayerScoreboard(plugin, player));
42 | }
43 |
44 | @Override
45 | public void onUpdate(Player player) {
46 | if (getScoreboard(player).getSidebarObjective().isPresent()) {
47 | sendUpdate(player);
48 | } else {
49 | createScoreboard(player);
50 | }
51 | }
52 |
53 | @Override
54 | public void unregisterAll() {
55 | super.unregisterAll();
56 |
57 | scoreboards.clear();
58 | }
59 |
60 | @Override
61 | public void unregister(Player player) {
62 | PlayerScoreboard scoreboard = scoreboards.remove(player.getUniqueId());
63 | if (scoreboard != null) {
64 | scoreboard.getObjectives().stream()
65 | .filter(obj -> obj.getId().startsWith(SB_NAME))
66 | .map(Objective::getId)
67 | .forEach(scoreboard::removeObjective);
68 | }
69 | }
70 |
71 | @Override
72 | public void createScoreboard(Player player) {
73 | PlayerScoreboard scoreboard = getScoreboard(player);
74 | Optional oldObjective = scoreboard.getSidebarObjective();
75 | if (!isAllowed(player) || oldObjective.map(Objective::getId).map(TEMP_SB_NAME::equals).orElse(false)) {
76 | //Check if another scoreboard is showing
77 | return;
78 | }
79 |
80 | Objective objective = scoreboard.getOrCreateObjective(SB_NAME);
81 | objective.setDisplayName(Settings.getTempTitle());
82 |
83 | updateVariables(objective, player, true);
84 |
85 | //Schedule the next temp scoreboard show
86 | scheduleShowTask(player, true);
87 | }
88 |
89 | @Override
90 | public void createTopListScoreboard(Player player) {
91 | PlayerScoreboard scoreboard = getScoreboard(player);
92 | Optional oldObjective = scoreboard.getSidebarObjective();
93 | if (!isAllowed(player) || oldObjective.map(Objective::getId).map(SB_NAME::equals).orElse(false)) {
94 | //Check if another scoreboard is showing
95 | return;
96 | }
97 |
98 | //Unregister objective instead of sending 15 remove score packets
99 | scoreboard.getObjective(TEMP_SB_NAME).map(Objective::getId).ifPresent(scoreboard::removeObjective);
100 |
101 | //We are checking if another object is shown. If it's our scoreboard the code will continue to this
102 | //were the force the replacement, because the scoreboard management in minecraft right now is sync,
103 | //so we don't expect any crashes by other plugins.
104 | Objective objective = scoreboard.getOrCreateObjective(TEMP_SB_NAME);
105 | objective.setDisplayName(Settings.getTempTitle());
106 |
107 | //Colorize and send all elements
108 | for (Map.Entry entry : plugin.getStatsDatabase().getTop()) {
109 | String scoreName = stripLength(Settings.getTempColor() + entry.getKey());
110 | objective.setScores(scoreName, entry.getValue());
111 | }
112 |
113 | //schedule the next normal scoreboard show
114 | scheduleShowTask(player, false);
115 | }
116 |
117 | @Override
118 | public void update(Player player, String title, int newScore) {
119 | getScoreboard(player).getObjective(SB_NAME).ifPresent(objective -> objective.setScores(title, newScore));
120 | }
121 |
122 | @Override
123 | public void sendUpdate(Player player) {
124 | Optional sidebar = getScoreboard(player).getSidebarObjective();
125 | if (sidebar.filter(objective -> SB_NAME.equals(objective.getId())).isPresent()) {
126 | updateVariables(sidebar.get(), player, false);
127 | }
128 | }
129 |
130 | private void updateVariables(Objective objective, Player player, boolean complete) {
131 | Iterator iter = Settings.getMainScoreboard().getItemsByVariable().values().iterator();
132 | while (iter.hasNext()) {
133 | VariableItem variableItem = iter.next();
134 |
135 | String variable = variableItem.getVariable();
136 | String displayText = variableItem.getDisplayText();
137 | int score = variableItem.getScore();
138 |
139 | try {
140 | ReplaceManager replaceManager = plugin.getReplaceManager();
141 | OptionalInt newScore = replaceManager.scoreReplace(player, variable, false);
142 | if (newScore.isPresent()) {
143 | objective.setScores(displayText, newScore.getAsInt());
144 | }
145 | } catch (UnknownVariableException ex) {
146 | //Remove the variable because we can't replace it
147 | iter.remove();
148 | Settings.getMainScoreboard().getItemsByName().remove(variableItem.getDisplayText());
149 |
150 | plugin.getLog().info(UNKNOWN_VARIABLE, variableItem);
151 | } catch (ReplacerException e) {
152 | iter.remove();
153 | plugin.getLog().error("Error on replace", e);
154 | }
155 | }
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/plugin/src/main/java/com/github/games647/scoreboardstats/scoreboard/PlayerScoreboard.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.scoreboard;
2 |
3 | import com.comphenix.protocol.ProtocolLibrary;
4 | import com.comphenix.protocol.events.PacketContainer;
5 | import com.github.games647.scoreboardstats.ScoreboardStats;
6 | import com.google.common.collect.Maps;
7 |
8 | import java.lang.reflect.InvocationTargetException;
9 | import java.util.Collection;
10 | import java.util.Map;
11 | import java.util.Optional;
12 |
13 | import org.bukkit.entity.Player;
14 |
15 | /**
16 | * Represents the scoreboard overview in a server-side perspective.
17 | */
18 | public class PlayerScoreboard {
19 |
20 | private final ScoreboardStats plugin;
21 | private final Player player;
22 |
23 | final Map objectivesByName = Maps.newHashMap();
24 | final Map teamsByName = Maps.newHashMap();
25 |
26 | Objective sidebarObjective;
27 |
28 | public PlayerScoreboard(ScoreboardStats plugin, Player player) {
29 | this.plugin = plugin;
30 | this.player = player;
31 | }
32 |
33 | public Objective getOrCreateObjective(String objectiveId) {
34 | return objectivesByName.computeIfAbsent(objectiveId, this::addObjective);
35 | }
36 |
37 | public Optional getObjective(String objectiveId) {
38 | return Optional.ofNullable(objectivesByName.get(objectiveId));
39 | }
40 |
41 | public Objective addObjective(String objectiveId) {
42 | return addObjective(objectiveId, objectiveId);
43 | }
44 |
45 | public Objective addObjective(String objectiveId, String display) {
46 | Objective objective = new Objective(this, objectiveId, display);
47 | sidebarObjective = objective;
48 | objectivesByName.put(objectiveId, objective);
49 |
50 | objective.sendObjectivePacket(State.CREATE);
51 | objective.sendShowPacket();
52 | return objective;
53 | }
54 |
55 | public Collection getObjectives() {
56 | return objectivesByName.values();
57 | }
58 |
59 | public Optional getSidebarObjective() {
60 | return Optional.ofNullable(sidebarObjective);
61 | }
62 |
63 | public void removeObjective(String objectiveId) {
64 | Objective objective = objectivesByName.remove(objectiveId);
65 | if (objective != null) {
66 | objective.sendObjectivePacket(State.REMOVE);
67 | }
68 | }
69 |
70 | public Optional getTeam(String teamId) {
71 | return Optional.ofNullable(teamsByName.get(teamId));
72 | }
73 |
74 | public Team addTeam(String teamId) {
75 | Team team = new Team(this, teamId);
76 | teamsByName.put(teamId, team);
77 |
78 | team.sendCreatePacket();
79 | return team;
80 | }
81 |
82 | public Collection getTeams() {
83 | return teamsByName.values();
84 | }
85 |
86 | public void removeTeam(String teamId) {
87 | Team team = teamsByName.remove(teamId);
88 | if (team != null) {
89 | team.sendRemovePacket();
90 | }
91 | }
92 |
93 | public Player getOwner() {
94 | return player;
95 | }
96 |
97 | void sendPacket(PacketContainer packet) {
98 | //add metadata that we ignore our packets on the listener
99 | packet.addMetadata("ScoreboardStats", true);
100 |
101 | try {
102 | //false so we don't listen to our own packets
103 | ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet);
104 | } catch (InvocationTargetException ex) {
105 | //just log it for now.
106 | plugin.getLog().info("Failed to send packet", ex);
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/plugin/src/main/java/com/github/games647/scoreboardstats/scoreboard/SlotTransformer.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.scoreboard;
2 |
3 | import java.util.Optional;
4 |
5 | import org.bukkit.scoreboard.DisplaySlot;
6 |
7 | /**
8 | * Represent the three different sides a scoreboard objective can have
9 | *
10 | * Protocol specifications can be found here http://wiki.vg/Protocol
11 | */
12 | class SlotTransformer {
13 |
14 | private SlotTransformer() {
15 | //singleton
16 | }
17 |
18 | /**
19 | * Get the enum from his id
20 | *
21 | * @param slotId the id
22 | * @return the representing enum or null if the id isn't valid
23 | */
24 | public static Optional fromId(int slotId) {
25 | switch (slotId) {
26 | case 0:
27 | return Optional.of(DisplaySlot.PLAYER_LIST);
28 | case 1:
29 | return Optional.of(DisplaySlot.SIDEBAR);
30 | case 2:
31 | return Optional.of(DisplaySlot.BELOW_NAME);
32 | default:
33 | return Optional.empty();
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/plugin/src/main/java/com/github/games647/scoreboardstats/scoreboard/State.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.scoreboard;
2 |
3 | /**
4 | * Represents the state of a scoreboard objective packet
5 | *
6 | * Protocol specifications can be found here http://wiki.vg/Protocol
7 | */
8 | public enum State {
9 |
10 | /**
11 | * The objective was created
12 | */
13 | CREATE,
14 |
15 | /**
16 | * The objective was removed
17 | */
18 | REMOVE,
19 |
20 | /**
21 | * The display name of the objective was changed
22 | */
23 | UPDATE_DISPLAY_NAME;
24 |
25 | /**
26 | * Get the enum from his id
27 | *
28 | * @param stateId the id
29 | * @return the representing enum or null if the id not valid
30 | */
31 | public static State fromId(int stateId) {
32 | return State.values()[stateId];
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/plugin/src/main/java/com/github/games647/scoreboardstats/scoreboard/Team.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.scoreboard;
2 |
3 | import com.comphenix.protocol.PacketType.Play.Server;
4 | import com.comphenix.protocol.events.PacketContainer;
5 | import com.google.common.collect.ImmutableSet;
6 | import com.google.common.collect.Sets;
7 |
8 | import java.util.Collection;
9 | import java.util.Set;
10 |
11 | public class Team {
12 |
13 | private final PlayerScoreboard scoreboard;
14 | private final String teamId;
15 |
16 | final Set members = Sets.newHashSet();
17 | String prefix;
18 | String suffix;
19 |
20 | public Team(PlayerScoreboard scoreboard, String teamId) {
21 | this.scoreboard = scoreboard;
22 | this.teamId = teamId;
23 | }
24 |
25 | public Team(PlayerScoreboard scoreboard, String teamId, Collection members) {
26 | this(scoreboard, teamId);
27 |
28 | this.members.addAll(members);
29 | }
30 |
31 | public String getId() {
32 | return teamId;
33 | }
34 |
35 | public boolean hasMember(String member) {
36 | return members.contains(member);
37 | }
38 |
39 | public boolean addMember(String member) {
40 | return members.add(member);
41 | }
42 |
43 | public boolean removeMember(String member) {
44 | return members.remove(member);
45 | }
46 |
47 | public Set getMembers() {
48 | return ImmutableSet.copyOf(members);
49 | }
50 |
51 | public String getPrefix() {
52 | return prefix;
53 | }
54 |
55 | public void setPrefix(String prefix) {
56 | this.prefix = prefix;
57 | }
58 |
59 | public String getSuffix() {
60 | return suffix;
61 | }
62 |
63 | public void setSuffix(String suffix) {
64 | this.suffix = suffix;
65 | }
66 |
67 | void sendCreatePacket() {
68 | PacketContainer packet = newPacket(TeamMode.CREATE);
69 | packet.getSpecificModifier(Collection.class).write(0, ImmutableSet.copyOf(members));
70 | scoreboard.sendPacket(packet);
71 | }
72 |
73 | void sendMemberUpdatePacket(boolean add) {
74 | PacketContainer packet = newPacket(add ? TeamMode.ADD_MEMBER : TeamMode.REMOVE_MEMBER);
75 | packet.getSpecificModifier(Collection.class).write(0, ImmutableSet.copyOf(members));
76 | scoreboard.sendPacket(packet);
77 | }
78 |
79 | void sendRemovePacket() {
80 | PacketContainer packet = newPacket(TeamMode.REMOVE);
81 | scoreboard.sendPacket(packet);
82 | }
83 |
84 | private PacketContainer newPacket(TeamMode mode) {
85 | PacketContainer packet = new PacketContainer(Server.SCOREBOARD_TEAM);
86 | packet.getStrings().write(0, teamId);
87 |
88 | packet.getIntegers().write(1, mode.ordinal());
89 | return packet;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/plugin/src/main/java/com/github/games647/scoreboardstats/scoreboard/TeamMode.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.scoreboard;
2 |
3 | import java.util.Optional;
4 |
5 | public enum TeamMode {
6 |
7 | CREATE,
8 |
9 | REMOVE,
10 |
11 | UPDATE,
12 |
13 | ADD_MEMBER,
14 |
15 | REMOVE_MEMBER;
16 |
17 | public static Optional getMode(int id) {
18 | TeamMode[] values = TeamMode.values();
19 | if (id < 0 || id >= values.length) {
20 | return Optional.empty();
21 | }
22 |
23 | return Optional.of(values[id]);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/plugin/src/main/resources/config.yml:
--------------------------------------------------------------------------------
1 | # ${project.name} configuration
2 |
3 | disabled-worlds:
4 | - city
5 |
6 | Scoreboard:
7 | Title: '&a&lStats'
8 | # seconds
9 | # For instant updates you can or 1 and it will update every second
10 | Update-delay: 2
11 | Items:
12 | # The Title must have under 48 characters
13 | # Title: Type
14 | '&9Online': '%online%'
15 | '&9Money': '%money%'
16 | # Your can choose your custom score here
17 | '&aHello World': 1337
18 |
19 | # Let ScoreboardStats track stats (kills, deaths, mobkills, killstreak) You need no plugin for this
20 | enable-pvpstats: false
21 |
22 | Temp-Scoreboard-enabled: false
23 |
24 | Temp-Scoreboard:
25 | Title: '&a&lTop Kills'
26 | # %mob% | %kills% | %killstreak%
27 | Type: '%kills%'
28 | Color: '&9'
29 | # How many Players would be displayed
30 | Items: 5
31 | Interval-show: 300
32 | Interval-disappear: 300
33 |
--------------------------------------------------------------------------------
/plugin/src/main/resources/plugin.yml:
--------------------------------------------------------------------------------
1 | # project data for Bukkit in order to register our plugin with all it components
2 | # ${project.name} are variables from Maven (pom.xml) which will be replaced after the build
3 | name: ${project.name}
4 | version: ${project.version}
5 | main: ${project.groupId}.${project.parent.artifactId}.${project.name}
6 |
7 | # meta data for plugin managers
8 | website: ${project.url}
9 | dev-url: ${project.url}
10 | description: |
11 | ${project.description}
12 |
13 | # Required for the packets
14 | depend: [ProtocolLib]
15 |
16 | # depending on them - load after them to make sure they are initialized
17 | softdepend:
18 | - InSigns
19 | # Replacer dependencies
20 | - PlaceholderAPI
21 | - mcMMO
22 | - Vault
23 | - SimpleClans
24 | - Factions
25 | - Heroes
26 | - uSkyBlock
27 | - PlayerPoints
28 | - Craftconomy3
29 | - ASkyBlock
30 |
31 | # Root commands to register automatically to Bukkit
32 | commands:
33 | # choose a unique name in order to register it successfully
34 | ${project.artifactId}:
35 | description: 'Root command for all commands in ${project.name}'
36 | aliases: [side, ss, sb, board, sidebar]
37 |
38 | # Permission management
39 | permissions:
40 | ${project.artifactId}.admin:
41 | children:
42 | ${project.artifactId}.reload: true
43 | ${project.artifactId}.sign: true
44 | ${project.artifactId}.use: true
45 | ${project.artifactId}.hide: true
46 | ${project.artifactId}.member:
47 | default: true
48 | children:
49 | ${project.artifactId}.use: true
50 | ${project.artifactId}.hide: true
51 | ${project.artifactId}.use:
52 | description: 'Sender is allowed to see the scoreboard'
53 | ${project.artifactId}.sign:
54 | description: 'Sender can make signs with variables on it'
55 | ${project.artifactId}.reload:
56 | description: 'Sender can perform a plugin reload'
57 | ${project.artifactId}.hide:
58 | description: 'Sender can toggle the scoreboard'
59 |
--------------------------------------------------------------------------------
/plugin/src/main/resources/sql.yml:
--------------------------------------------------------------------------------
1 | # These settings aren't required if you disable pvpstats.
2 | # Here can you configure your sql setting.
3 | # If you don't have or want to use a mysql database you can also use a sqlite database
4 | # MySQL example
5 | # The default port for a mysql database is 3306
6 | # Username: 'bukkit'
7 | # Password: 'xyz'
8 | # Isolation: 'SERIALIZABLE'
9 | # Driver: 'com.mysql.jdbc.Driver'
10 | # Url: 'jdbc:mysql://IP:PORT/databaseName'
11 | # Timeout: 1000
12 |
13 | SQL-Settings:
14 | Username: 'bukkit'
15 | Password: 'walrus'
16 | Driver: 'org.sqlite.JDBC'
17 | # If you use mysql, delete org.sqlite.JDBC, comment out
18 | # Driver: 'com.mysql.jdbc.Driver'
19 | Url: 'jdbc:sqlite:{DIR}{NAME}.db'
20 | tablePrefix: ''
21 |
--------------------------------------------------------------------------------
/plugin/src/test/java/com/github/games647/scoreboardstats/variables/VersionTest.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.variables;
2 |
3 | import com.github.games647.scoreboardstats.Version;
4 |
5 | import org.bukkit.Bukkit;
6 | import org.bukkit.Server;
7 | import org.junit.Assert;
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 | import org.mockito.Mockito;
11 | import org.powermock.api.mockito.PowerMockito;
12 | import org.powermock.core.classloader.annotations.PrepareForTest;
13 | import org.powermock.modules.junit4.PowerMockRunner;
14 |
15 | /**
16 | * Version Test class
17 | *
18 | * @see Version
19 | */
20 | @PrepareForTest(Bukkit.class)
21 | @RunWith(PowerMockRunner.class)
22 | public class VersionTest {
23 |
24 | private static final String DEFAULT_VERSION = "git-Bukkit-1.5.2-R1.0-1-gf46bd58-b2793jnks (MC: 1.7.9)";
25 |
26 | /**
27 | * Test version parsing
28 | */
29 | @Test
30 | public void testParsing() {
31 | //Bukkit version parsing. Can be found here: META-INF
32 | PowerMockito.mockStatic(Bukkit.class);
33 | Server server = Mockito.mock(Server.class);
34 | Mockito.when(Bukkit.getServer()).thenReturn(server);
35 |
36 | Mockito.when(Bukkit.getVersion()).thenReturn(DEFAULT_VERSION);
37 |
38 | //Plugin Parsing of FactionsUUID; shouldn't fail
39 | Version.parse("1.6.9.5-U0.1.12-SNAPSHOT");
40 | }
41 |
42 | /**
43 | * Test of compareTo method, of class Version.
44 | */
45 | @Test
46 | public void testComparison() {
47 | Version low = new Version(1, 5, 4);
48 | Version high = new Version(1, 8, 5);
49 |
50 | Assert.assertSame("Higher Compare: " + high + ' ' + low, 1, high.compareTo(low));
51 | Assert.assertSame("Lower Compare: " + low + ' ' + high, -1, low.compareTo(high));
52 |
53 | Version higher = new Version(1, 5, 5);
54 | Assert.assertSame("Higher Compare: " + higher + ' ' + low, 1, higher.compareTo(low));
55 | Assert.assertSame("Lower Compare: " + low + ' ' + higher, -1, low.compareTo(higher));
56 |
57 | Version equal = new Version(1, 2, 3);
58 | Version equal1 = new Version(1, 2, 3);
59 | Assert.assertSame("Equal Compare: " + equal + ' ' + equal1, 0, equal.compareTo(equal1));
60 | Assert.assertSame("Equal Compare: " + equal1 + ' ' + equal, 0, equal1.compareTo(equal1));
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | com.github.games647
6 |
7 | scoreboardstats-parent
8 | pom
9 |
10 | ScoreboardStats
11 |
12 | Show the Scoreboard with many custom variables
13 | 1.0.0
14 | https://dev.bukkit.org/bukkit-plugins/scoreboardstats
15 |
16 |
17 | UTF-8
18 |
19 | 2.0.0-beta.5
20 |
21 | 1.8
22 | 1.8
23 |
24 |
25 |
26 | plugin
27 | variables
28 | pvp
29 | config
30 | defaults
31 |
32 |
33 |
34 |
35 |
36 | spigot-repo
37 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/
38 |
39 |
40 |
41 |
42 |
43 | org.spigotmc
44 | spigot-api
45 | 1.12.2-R0.1-SNAPSHOT
46 | provided
47 |
48 |
49 |
50 |
51 | org.slf4j
52 | slf4j-jdk14
53 | 1.7.25
54 |
55 |
56 |
57 |
58 | org.powermock
59 | powermock-core
60 | ${powermock.version}
61 | test
62 |
63 |
64 | org.powermock
65 | powermock-module-junit4
66 | ${powermock.version}
67 | test
68 |
69 |
70 | org.powermock
71 | powermock-api-mockito2
72 | ${powermock.version}
73 | test
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/pvp/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 |
6 | com.github.games647
7 | scoreboardstats-parent
8 | 1.0.0
9 |
10 |
11 | scoreboardstats-pvp
12 | jar
13 |
14 |
15 |
16 |
17 | jitpack.io
18 | https://jitpack.io
19 |
20 |
21 |
22 |
23 |
24 | com.github.games647
25 | scoreboardstats-variables
26 | 1.0.0
27 |
28 |
29 |
30 | com.github.games647
31 | scoreboardstats-config
32 | 1.0.0
33 |
34 |
35 |
36 |
37 | com.zaxxer
38 | HikariCP
39 | 2.7.1
40 |
41 |
42 |
43 |
44 | com.github.blablubbabc
45 | IndividualSigns
46 | 91ea396307
47 |
48 |
49 | *
50 | *
51 |
52 |
53 | true
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/pvp/src/main/java/com/github/games647/scoreboardstats/pvp/Database.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.pvp;
2 |
3 | import com.github.games647.scoreboardstats.config.Settings;
4 | import com.github.games647.scoreboardstats.variables.ReplaceManager;
5 | import com.github.games647.scoreboardstats.variables.Replacer;
6 | import com.google.common.collect.ImmutableMap;
7 | import com.google.common.collect.Lists;
8 | import com.google.common.collect.Maps;
9 | import com.zaxxer.hikari.HikariDataSource;
10 |
11 | import java.sql.Connection;
12 | import java.sql.PreparedStatement;
13 | import java.sql.ResultSet;
14 | import java.sql.SQLException;
15 | import java.sql.Statement;
16 | import java.time.Instant;
17 | import java.util.Collection;
18 | import java.util.Collections;
19 | import java.util.List;
20 | import java.util.Map;
21 | import java.util.Map.Entry;
22 | import java.util.Objects;
23 | import java.util.Optional;
24 | import java.util.UUID;
25 | import java.util.concurrent.CancellationException;
26 | import java.util.concurrent.Future;
27 | import java.util.function.Function;
28 | import java.util.stream.Collectors;
29 |
30 | import org.bukkit.Bukkit;
31 | import org.bukkit.entity.Player;
32 | import org.bukkit.metadata.MetadataValue;
33 | import org.bukkit.plugin.Plugin;
34 | import org.slf4j.Logger;
35 |
36 | /**
37 | * This represents a handler for saving player stats.
38 | */
39 | public class Database {
40 |
41 | private static final String METAKEY = "player_stats";
42 |
43 | private final Plugin plugin;
44 | private final Logger logger;
45 |
46 | private final Map toplist;
47 | private final DatabaseConfiguration dbConfig;
48 | private HikariDataSource dataSource;
49 |
50 | public Database(Plugin plugin, Logger logger) {
51 | this.plugin = plugin;
52 | this.logger = logger;
53 |
54 | this.dbConfig = new DatabaseConfiguration(plugin);
55 | this.toplist = Maps.newHashMapWithExpectedSize(Settings.getTopitems());
56 | }
57 |
58 | /**
59 | * Get the cache player stats if they exists and the arguments are valid.
60 | *
61 | * @param request the associated player
62 | * @return the stats if they are in the cache
63 | */
64 | @Deprecated
65 | public PlayerStats getCachedStats(Player request) {
66 | return getStats(request).orElse(null);
67 | }
68 |
69 | public Optional getStats(Player request) {
70 | if (request != null) {
71 | for (MetadataValue metadata : request.getMetadata(METAKEY)) {
72 | if (metadata.value() instanceof PlayerStats) {
73 | return Optional.of((PlayerStats) metadata.value());
74 | }
75 | }
76 | }
77 |
78 | return Optional.empty();
79 | }
80 |
81 | /**
82 | * Starts loading the stats for a specific player in an external thread.
83 | *
84 | * @param player the associated player
85 | */
86 | public void loadAccountAsync(Player player) {
87 | if (dataSource != null && !getStats(player).isPresent()) {
88 | Bukkit.getScheduler().runTaskAsynchronously(plugin, new StatsLoader(plugin, player, this));
89 | }
90 | }
91 |
92 | /**
93 | * Starts loading the stats for a specific player sync
94 | *
95 | * @param uniqueId the associated playername or uuid
96 | * @return the loaded stats
97 | */
98 | public Optional loadAccount(UUID uniqueId) {
99 | if (dataSource == null) {
100 | return Optional.empty();
101 | } else {
102 | try (Connection conn = dataSource.getConnection();
103 | PreparedStatement stmt = conn.prepareStatement("SELECT * FROM player_stats WHERE uuid=?")) {
104 |
105 | stmt.setString(1, uniqueId.toString());
106 | try (ResultSet resultSet = stmt.executeQuery()) {
107 | return Optional.of(extractPlayerStats(resultSet));
108 | }
109 | } catch (SQLException ex) {
110 | logger.error("Error loading player profile", ex);
111 | }
112 |
113 | return Optional.empty();
114 | }
115 | }
116 |
117 | private PlayerStats extractPlayerStats(ResultSet resultSet) throws SQLException {
118 | int id = resultSet.getInt(1);
119 |
120 | String rawUUID = resultSet.getString(2);
121 | UUID uuid = null;
122 | if (rawUUID != null) {
123 | uuid = UUID.fromString(rawUUID);
124 | }
125 |
126 | String playerName = resultSet.getString(3);
127 |
128 | int kills = resultSet.getInt(4);
129 | int deaths = resultSet.getInt(5);
130 | int mobkills = resultSet.getInt(6);
131 | int killstreak = resultSet.getInt(7);
132 |
133 | Instant lastOnline = resultSet.getTimestamp(8).toInstant();
134 | return new PlayerStats(id, uuid, playerName, kills, deaths, mobkills, killstreak, lastOnline);
135 | }
136 |
137 | /**
138 | * Starts loading the stats for a specific player sync
139 | *
140 | * @param player the associated player
141 | * @return the loaded stats
142 | */
143 | public Optional loadAccount(Player player) {
144 | return loadAccount(player.getUniqueId());
145 | }
146 |
147 | /**
148 | * Save PlayerStats async.
149 | *
150 | * @param stats PlayerStats data
151 | */
152 | public void saveAsync(PlayerStats stats) {
153 | Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> save(Lists.newArrayList(stats)));
154 | }
155 |
156 | /**
157 | * Save the PlayerStats on the current Thread.
158 | *
159 | * @param stats PlayerStats data
160 | */
161 | public void save(Collection stats) {
162 | if (stats != null && dataSource != null) {
163 | update(stats.stream()
164 | .filter(Objects::nonNull)
165 | .filter(stat -> !stat.isNew())
166 | .collect(Collectors.toList()));
167 |
168 | insert(stats.stream()
169 | .filter(Objects::nonNull)
170 | .filter(PlayerStats::isNew)
171 | .collect(Collectors.toList()));
172 | }
173 | }
174 |
175 | private void update(Collection stats) {
176 | if (stats.isEmpty()) {
177 | return;
178 | }
179 |
180 | //Save the stats to the database
181 | try (Connection conn = dataSource.getConnection();
182 | PreparedStatement stmt = conn.prepareStatement("UPDATE player_stats "
183 | + "SET kills=?, deaths=?, killstreak=?, mobkills=?, last_online=CURRENT_TIMESTAMP, playername=? "
184 | + "WHERE id=?")) {
185 | conn.setAutoCommit(false);
186 | for (PlayerStats stat : stats) {
187 | stmt.setInt(1, stat.getKills());
188 | stmt.setInt(2, stat.getDeaths());
189 | stmt.setInt(3, stat.getKillstreak());
190 | stmt.setInt(4, stat.getMobkills());
191 |
192 | stmt.setString(5, stat.getPlayername());
193 |
194 | stmt.setInt(6, stat.getId());
195 | stmt.addBatch();
196 | }
197 |
198 | stmt.executeBatch();
199 | conn.commit();
200 | } catch (Exception ex) {
201 | logger.error("Error updating profiles", ex);
202 | }
203 | }
204 |
205 | private void insert(Collection stats) {
206 | if (stats.isEmpty()) {
207 | return;
208 | }
209 |
210 | try (Connection conn = dataSource.getConnection();
211 | PreparedStatement stmt = conn.prepareStatement("INSERT INTO player_stats "
212 | + "(uuid, playername, kills, deaths, killstreak, mobkills, last_online) VALUES "
213 | + "(?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)", Statement.RETURN_GENERATED_KEYS)) {
214 | conn.setAutoCommit(false);
215 | for (PlayerStats stat : stats) {
216 | stmt.setString(1, stat.getUuid().toString());
217 | stmt.setString(2, stat.getPlayername());
218 |
219 | stmt.setInt(3, stat.getKills());
220 | stmt.setInt(4, stat.getDeaths());
221 | stmt.setInt(5, stat.getKillstreak());
222 | stmt.setInt(6, stat.getMobkills());
223 |
224 | stmt.addBatch();
225 | }
226 |
227 | stmt.executeBatch();
228 | conn.commit();
229 |
230 | try (ResultSet generatedKeys = stmt.getGeneratedKeys()) {
231 | for (PlayerStats stat : stats) {
232 | if (!generatedKeys.next()) {
233 | break;
234 | }
235 |
236 | stat.setId(generatedKeys.getInt(1));
237 | }
238 | }
239 | } catch (Exception ex) {
240 | logger.error("Error inserting profiles", ex);
241 | }
242 | }
243 |
244 | /**
245 | * Starts saving all cache player stats and then clears the cache.
246 | */
247 | public void saveAll() {
248 | try {
249 | logger.info("Now saving the stats to the database. This could take a while.");
250 |
251 | //If pvpstats are enabled save all stats that are in the cache
252 | List toSave = Bukkit.getOnlinePlayers().stream()
253 | .map(this::getStats)
254 | .filter(Optional::isPresent)
255 | .map(Optional::get)
256 | .collect(Collectors.toList());
257 |
258 | if (!toSave.isEmpty()) {
259 | save(toSave);
260 | }
261 |
262 | dataSource.close();
263 | } finally {
264 | //Make rally sure we remove all even on error
265 | Bukkit.getOnlinePlayers()
266 | .forEach(player -> player.removeMetadata(METAKEY, plugin));
267 | }
268 | }
269 |
270 | /**
271 | * Initialize a components and checking for an existing database
272 | */
273 | public void setupDatabase() {
274 | //Check if pvpstats should be enabled
275 | dbConfig.loadConfiguration();
276 | dataSource = new HikariDataSource(dbConfig.getServerConfig());
277 |
278 | try (Connection conn = dataSource.getConnection();
279 | Statement stmt = conn.createStatement()) {
280 | String createTableQuery = "CREATE TABLE IF NOT EXISTS " + dbConfig.getTablePrefix() + "player_stats ( "
281 | + "id integer PRIMARY KEY AUTO_INCREMENT, "
282 | + "uuid varchar(40) NOT NULL, "
283 | + "playername varchar(16) NOT NULL, "
284 | + "kills integer NOT NULL, "
285 | + "deaths integer NOT NULL, "
286 | + "mobkills integer NOT NULL, "
287 | + "killstreak integer NOT NULL, "
288 | + "last_online timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP )";
289 |
290 | if (dbConfig.getServerConfig().getDriverClassName().contains("sqlite")) {
291 | createTableQuery = createTableQuery.replace("AUTO_INCREMENT", "");
292 | dataSource.setMaximumPoolSize(1);
293 | }
294 |
295 | stmt.execute(createTableQuery);
296 | } catch (Exception ex) {
297 | logger.error("Error creating database ", ex);
298 | return;
299 | }
300 |
301 | Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, this::updateTopList, 20 * 60 * 5, 0);
302 | Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, () -> {
303 | Future> syncPlayers = Bukkit.getScheduler()
304 | .callSyncMethod(plugin, Bukkit::getOnlinePlayers);
305 |
306 | try {
307 | Collection extends Player> onlinePlayers = syncPlayers.get();
308 |
309 | List toSave = onlinePlayers.stream()
310 | .map(this::getStats)
311 | .filter(Optional::isPresent)
312 | .map(Optional::get)
313 | .collect(Collectors.toList());
314 |
315 | if (!toSave.isEmpty()) {
316 | save(toSave);
317 | }
318 | } catch (CancellationException cancelEx) {
319 | //ignore it on shutdown
320 | } catch (Exception ex) {
321 | logger.error("Error fetching top list", ex);
322 | }
323 | }, 20 * 60, 20 * 60 * 5);
324 |
325 | registerEvents();
326 | }
327 |
328 | /**
329 | * Get the a map of the best players for a specific category.
330 | *
331 | * @return a iterable of the entries
332 | */
333 | public Iterable> getTop() {
334 | synchronized (toplist) {
335 | return ImmutableMap.copyOf(toplist).entrySet();
336 | }
337 | }
338 |
339 | /**
340 | * Updates the toplist
341 | */
342 | private void updateTopList() {
343 | String type = Settings.getTopType();
344 | Map newToplist;
345 | switch (type) {
346 | case "killstreak":
347 | newToplist = getTopList("killstreak", PlayerStats::getKillstreak);
348 | break;
349 | case "mob":
350 | newToplist = getTopList("mobkills", PlayerStats::getMobkills);
351 | break;
352 | default:
353 | newToplist = getTopList("kills", PlayerStats::getKills);
354 | break;
355 | }
356 |
357 | synchronized (toplist) {
358 | //set it after fetching so it's only blocking for a short time
359 | toplist.clear();
360 | toplist.putAll(newToplist);
361 | }
362 | }
363 |
364 | private Map getTopList(String type, Function valueMapper) {
365 | if (dataSource == null) {
366 | return Collections.emptyMap();
367 | }
368 |
369 | try (Connection conn = dataSource.getConnection();
370 | Statement stmt = conn.createStatement()) {
371 | try (ResultSet resultSet = stmt.executeQuery("SELECT * FROM player_stats ORDER BY " + type + " desc"
372 | + " LIMIT " + Settings.getTopitems())) {
373 | Map result = Maps.newHashMap();
374 | for (int i = 0; i < Settings.getTopitems(); i++) {
375 | if (!resultSet.next()) {
376 | return result;
377 | }
378 |
379 | PlayerStats stats = extractPlayerStats(resultSet);
380 | if (!stats.isNew()) {
381 | String entry = (i + 1) + ". " + stats.getPlayername();
382 | result.put(entry, valueMapper.apply(stats));
383 | }
384 | }
385 |
386 | return result;
387 | }
388 | } catch (SQLException ex) {
389 | logger.error("Error loading top list", ex);
390 | }
391 |
392 | return Collections.emptyMap();
393 | }
394 |
395 | private void registerEvents() {
396 | if (Bukkit.getPluginManager().isPluginEnabled("InSigns")) {
397 | //Register this listener if InSigns is available
398 | Bukkit.getPluginManager().registerEvents(new SignListener(plugin, this), plugin);
399 | }
400 |
401 | ReplaceManager replaceManager = ReplaceManager.getInstance();
402 | replaceManager.register(newVariable("kills", PlayerStats::getKills));
403 | replaceManager.register(newVariable("deaths", PlayerStats::getDeaths));
404 | replaceManager.register(newVariable("kdr", PlayerStats::getKdr));
405 | replaceManager.register(newVariable("mob-kills", PlayerStats::getMobkills));
406 | replaceManager.register(newVariable("killstreak", PlayerStats::getKillstreak));
407 | replaceManager.register(newVariable("current-streak", PlayerStats::getCurrentStreak));
408 |
409 | Bukkit.getPluginManager().registerEvents(new StatsListener(plugin, this), plugin);
410 | }
411 |
412 | private Replacer newVariable(String variable, Function fct) {
413 | return new Replacer(plugin, "kills")
414 | .scoreSupply(player -> getStats(player)
415 | .map(fct)
416 | .orElse(-1));
417 | }
418 | }
419 |
--------------------------------------------------------------------------------
/pvp/src/main/java/com/github/games647/scoreboardstats/pvp/DatabaseConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.pvp;
2 |
3 | import com.zaxxer.hikari.HikariConfig;
4 |
5 | import java.nio.file.Files;
6 | import java.nio.file.Path;
7 |
8 | import org.bukkit.configuration.ConfigurationSection;
9 | import org.bukkit.configuration.file.FileConfiguration;
10 | import org.bukkit.configuration.file.YamlConfiguration;
11 | import org.bukkit.plugin.Plugin;
12 |
13 | /**
14 | * Configuration for the SQL database.
15 | *
16 | * @see Database
17 | */
18 | class DatabaseConfiguration {
19 |
20 | private final Plugin plugin;
21 |
22 | private HikariConfig serverConfig;
23 | private String tablePrefix;
24 |
25 | DatabaseConfiguration(Plugin instance) {
26 | plugin = instance;
27 | }
28 |
29 | /**
30 | * Get the SQL configuration
31 | *
32 | * @return the server configuration
33 | */
34 | public HikariConfig getServerConfig() {
35 | return serverConfig;
36 | }
37 |
38 | public String getTablePrefix() {
39 | return tablePrefix;
40 | }
41 |
42 | /**
43 | * Loads the configuration
44 | */
45 | public void loadConfiguration() {
46 | serverConfig = new HikariConfig();
47 |
48 | Path file = plugin.getDataFolder().toPath().resolve("sql.yml");
49 | //Check if the file exists. If so load the settings form there
50 | if (Files.notExists(file)) {
51 | //Create a new configuration based on the default settings form bukkit.yml
52 | plugin.saveResource("sql.yml", false);
53 | }
54 |
55 | FileConfiguration sqlConfig = YamlConfiguration.loadConfiguration(file.toFile());
56 |
57 | ConfigurationSection sqlSettingSection = sqlConfig.getConfigurationSection("SQL-Settings");
58 | serverConfig.setUsername(sqlSettingSection.getString("Username"));
59 | serverConfig.setPassword(sqlSettingSection.getString("Password"));
60 | serverConfig.setDriverClassName(sqlSettingSection.getString("Driver"));
61 | serverConfig.setJdbcUrl(replaceUrlString(sqlSettingSection.getString("Url")));
62 | if (serverConfig.getDriverClassName().contains("sqlite")) {
63 | serverConfig.setConnectionTestQuery("SELECT 1");
64 | }
65 |
66 | tablePrefix = sqlSettingSection.getString("tablePrefix", "");
67 | }
68 |
69 | private String replaceUrlString(String input) {
70 | //Replace the windows separators ('\') with a '/'; \\ escape regEx --> \\\\ escape java
71 | String result = input.replaceAll("\\{DIR\\}", plugin.getDataFolder().getPath().replaceAll("\\\\", "/") + '/');
72 | result = result.replaceAll("\\{NAME\\}", plugin.getDescription().getName().replaceAll("[^\\w-]", ""));
73 |
74 | return result;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/pvp/src/main/java/com/github/games647/scoreboardstats/pvp/PlayerStats.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.pvp;
2 |
3 | import java.time.Instant;
4 | import java.util.Objects;
5 | import java.util.UUID;
6 |
7 | import org.apache.commons.lang.builder.ToStringBuilder;
8 | import org.bukkit.util.NumberConversions;
9 |
10 | /**
11 | * Represents the stats from a player. The stats are kills, deaths, mobkills and killstreak. All stats are annotated to
12 | * be validated on runtime to be not invalid.
13 | *
14 | * Maybe these stats also have to be validated to be not null, so we can prevent some special cases while using it
15 | * especially for SQL database, but this can also occurs on using it as file database due incorrect format or unexpected
16 | * user modifications.
17 | */
18 | public class PlayerStats {
19 |
20 | private final UUID uuid;
21 | private int id = -1;
22 | private String playername;
23 |
24 | //You can't have negative stats
25 | private int kills;
26 | private int deaths;
27 | private int mobkills;
28 | private int killstreak;
29 |
30 | private Instant lastOnline;
31 |
32 | private int currentStreak;
33 |
34 | public PlayerStats(int id, UUID uuid, String playername,
35 | int kills, int deaths, int mobkills, int killstreak, Instant lastOnline) {
36 | this(uuid, playername);
37 |
38 | this.id = id;
39 | this.kills = kills;
40 | this.deaths = deaths;
41 | this.mobkills = mobkills;
42 | this.killstreak = killstreak;
43 | this.lastOnline = lastOnline;
44 | }
45 |
46 | public PlayerStats(UUID uuid, String playername) {
47 | this.uuid = uuid;
48 | this.playername = playername;
49 | }
50 |
51 | /**
52 | * Get the auto incrementing id
53 | *
54 | * @return the auto incrementing id
55 | */
56 | public int getId() {
57 | return id;
58 | }
59 |
60 | public void setId(int id) {
61 | this.id = id;
62 | }
63 |
64 | /**
65 | * Get the UUID of this player. The database represents this one as string
66 | *
67 | * @return the UUID of the player
68 | */
69 | public UUID getUuid() {
70 | return uuid;
71 | }
72 |
73 | /**
74 | * Get the player name of these stats
75 | *
76 | * @return the player name of these stats
77 | */
78 | public String getPlayername() {
79 | return playername;
80 | }
81 |
82 | /**
83 | * Set the player name of these stats
84 | *
85 | * @param playername the player name of these stats
86 | */
87 | public void setPlayername(String playername) {
88 | this.playername = playername;
89 | }
90 |
91 | /**
92 | * Get the player kills
93 | *
94 | * @return the player kills
95 | */
96 | public int getKills() {
97 | return kills;
98 | }
99 |
100 | /**
101 | * Get the deaths
102 | *
103 | * @return the deaths
104 | */
105 | public int getDeaths() {
106 | return deaths;
107 | }
108 |
109 | /**
110 | * Get the mob kills
111 | *
112 | * @return the mob kills
113 | */
114 | public int getMobkills() {
115 | return mobkills;
116 | }
117 |
118 | /**
119 | * Get the highest killstreak
120 | *
121 | * @return the highest killstreak
122 | */
123 | public int getKillstreak() {
124 | return killstreak;
125 | }
126 |
127 | /**
128 | * Get the current killstreak
129 | *
130 | * @return current killstreak
131 | */
132 | public int getCurrentStreak() {
133 | return currentStreak;
134 | }
135 |
136 | /**
137 | * Get the current kill-death-ratio rounded
138 | *
139 | * @return the kill death ratio rounded to an integer
140 | */
141 | public int getKdr() {
142 | //We can't divide by zero
143 | if (deaths == 0) {
144 | return kills;
145 | } else {
146 | //Triggers float division to have decimals
147 | return NumberConversions.round(kills / (float) deaths);
148 | }
149 | }
150 |
151 | /**
152 | * Get the UNIX timestamp where this entry was last updated.
153 | *
154 | * @return the timestamp this was last saved; can be null
155 | */
156 | @Deprecated
157 | public long getLastOnline() {
158 | return lastOnline.toEpochMilli();
159 | }
160 |
161 | public void setLastOnline(Instant lastOnline) {
162 | this.lastOnline = lastOnline;
163 | }
164 |
165 | /**
166 | * Set the update timestamp value. This value is updated on every save.
167 | *
168 | * @param lastOnline the player was online; can be null
169 | */
170 | @Deprecated
171 | public void setLastOnline(long lastOnline) {
172 | this.lastOnline = Instant.ofEpochMilli(lastOnline);
173 | }
174 |
175 | public Instant getLastOnlineDate() {
176 | return lastOnline;
177 | }
178 |
179 | /**
180 | * Increment the kills
181 | */
182 | public void onKill() {
183 | kills++;
184 |
185 | currentStreak++;
186 | if (currentStreak > killstreak) {
187 | killstreak = currentStreak;
188 | }
189 | }
190 |
191 | /**
192 | * Increment the mob kills
193 | */
194 | public void onMobKill() {
195 | mobkills++;
196 | }
197 |
198 | /**
199 | * Increment the deaths
200 | */
201 | public void onDeath() {
202 | currentStreak = 0;
203 | deaths++;
204 | }
205 |
206 | public boolean isNew() {
207 | return id == -1;
208 | }
209 |
210 | @Override
211 | public int hashCode() {
212 | return Objects.hash(id, uuid, playername);
213 | }
214 |
215 | @Override
216 | public boolean equals(Object obj) {
217 | //ignores also null
218 | if (obj instanceof PlayerStats) {
219 | PlayerStats other = (PlayerStats) obj;
220 | return id == other.id
221 | && Objects.equals(uuid, other.uuid)
222 | && Objects.equals(playername, other.playername);
223 | }
224 |
225 | return false;
226 | }
227 |
228 | @Override
229 | public String toString() {
230 | return ToStringBuilder.reflectionToString(this);
231 | }
232 | }
233 |
--------------------------------------------------------------------------------
/pvp/src/main/java/com/github/games647/scoreboardstats/pvp/SignListener.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.pvp;
2 |
3 | import com.google.common.collect.ImmutableMap;
4 |
5 | import de.blablubbabc.insigns.SignSendEvent;
6 |
7 | import java.util.Map;
8 | import java.util.Optional;
9 | import java.util.function.Function;
10 |
11 | import org.bukkit.entity.Player;
12 | import org.bukkit.event.EventHandler;
13 | import org.bukkit.event.EventPriority;
14 | import org.bukkit.event.Listener;
15 | import org.bukkit.plugin.Plugin;
16 |
17 | /**
18 | * Replace some variables on signs with the player individual stats.
19 | * The variables will be replaced dynamically
20 | *
21 | * @see Database
22 | */
23 | public class SignListener implements Listener {
24 |
25 | private static final char OPENING_TAG = '[';
26 | private static final char CLOSING_TAG = ']';
27 |
28 | private final Database database;
29 | private final Map> variables = ImmutableMap.
30 | >builder()
31 | .put(OPENING_TAG + "Kill" + CLOSING_TAG, PlayerStats::getKills)
32 | .put(OPENING_TAG + "Death" + CLOSING_TAG, PlayerStats::getDeaths)
33 | .put(OPENING_TAG + "KDR" + CLOSING_TAG, PlayerStats::getKdr)
34 | .put(OPENING_TAG + "Streak" + CLOSING_TAG, PlayerStats::getKillstreak)
35 | .build();
36 |
37 | public SignListener(Plugin plugin, Database statsDatabase) {
38 | this.database = statsDatabase;
39 | }
40 |
41 | @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
42 | public void onSignSend(SignSendEvent event) {
43 | for (int i = 0; i < 4; ++i) {
44 | String line = event.getLine(i);
45 | Player player = event.getPlayer();
46 |
47 | Optional replaced = replace(player, line);
48 | if (replaced.isPresent()) {
49 | event.setLine(i, replaced.get());
50 | }
51 | }
52 | }
53 |
54 | private Optional replace(Player player, String line) {
55 | for (Map.Entry> entry : variables.entrySet()) {
56 | if (line.contains(entry.getKey())) {
57 | Function fct = entry.getValue();
58 | return Optional.of(database.getStats(player)
59 | .map(fct)
60 | .map(value -> Integer.toString(value))
61 | .orElse("Not loaded"));
62 | }
63 | }
64 |
65 | return Optional.empty();
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/pvp/src/main/java/com/github/games647/scoreboardstats/pvp/StatsListener.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.pvp;
2 |
3 | import com.github.games647.scoreboardstats.config.Settings;
4 | import com.github.games647.scoreboardstats.variables.ReplaceManager;
5 |
6 | import org.bukkit.entity.EntityType;
7 | import org.bukkit.entity.LivingEntity;
8 | import org.bukkit.entity.Player;
9 | import org.bukkit.event.EventHandler;
10 | import org.bukkit.event.EventPriority;
11 | import org.bukkit.event.Listener;
12 | import org.bukkit.event.entity.EntityDeathEvent;
13 | import org.bukkit.event.entity.PlayerDeathEvent;
14 | import org.bukkit.event.player.PlayerJoinEvent;
15 | import org.bukkit.event.player.PlayerQuitEvent;
16 | import org.bukkit.plugin.Plugin;
17 |
18 | /**
19 | * If enabled this class counts the kills.
20 | */
21 | public class StatsListener implements Listener {
22 |
23 | private final Plugin plugin;
24 | private final Database database;
25 |
26 | public StatsListener(Plugin plugin, Database database) {
27 | this.plugin = plugin;
28 | this.database = database;
29 | }
30 |
31 | /**
32 | * Add the player account from the database in the cache.
33 | *
34 | * @param joinEvent the join event
35 | * @see Database
36 | */
37 | @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
38 | public void onJoin(PlayerJoinEvent joinEvent) {
39 | Player player = joinEvent.getPlayer();
40 | //removing old metadata which wasn't removed (which can lead to memory leaks)
41 | player.removeMetadata("player_stats", plugin);
42 |
43 | //load the pvpstats if activated
44 | database.loadAccountAsync(player);
45 | }
46 |
47 | /**
48 | * Saves the stats to database if the player leaves
49 | *
50 | * @param quitEvent leave event
51 | * @see Database
52 | */
53 | @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
54 | public void onQuit(PlayerQuitEvent quitEvent) {
55 | Player player = quitEvent.getPlayer();
56 |
57 | database.getStats(player).ifPresent(database::saveAsync);
58 |
59 | //just remove our metadata to prevent memory leaks
60 | player.removeMetadata("player_stats", plugin);
61 | }
62 |
63 | /**
64 | * Tracks the mob kills.
65 | *
66 | * @param event the death event
67 | */
68 | @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
69 | public void onMobDeath(EntityDeathEvent event) {
70 | LivingEntity entity = event.getEntity();
71 | //killer is null if it's not a player
72 | Player killer = entity.getKiller();
73 |
74 | //Check if it's not player because we are already handling it
75 | if (entity.getType() != EntityType.PLAYER && Settings.isActiveWorld(entity.getWorld().getName())) {
76 | database.getStats(killer).ifPresent(stats -> {
77 | //If the cache entry is loaded and the player isn't null, increase the mob kills
78 | stats.onMobKill();
79 |
80 | ReplaceManager.getInstance().forceUpdate(killer, "mob", stats.getMobkills());
81 | });
82 | }
83 | }
84 |
85 | /**
86 | * Tracks player deaths and kills
87 | *
88 | * @param deathEvent the death event.
89 | */
90 | @EventHandler
91 | public void onDeath(PlayerDeathEvent deathEvent) {
92 | Player killed = deathEvent.getEntity();
93 | //killer is null if it's not a player
94 | Player killer = killed.getKiller();
95 | if (killed.equals(killer)) {
96 | return;
97 | }
98 |
99 | if (Settings.isActiveWorld(killed.getWorld().getName())) {
100 | ReplaceManager replaceManager = ReplaceManager.getInstance();
101 |
102 | database.getStats(killed).ifPresent(killedCache -> {
103 | killedCache.onDeath();
104 | replaceManager.forceUpdate(killed, "deaths", killedCache.getDeaths());
105 | replaceManager.forceUpdate(killed, "kdr", killedCache.getKdr());
106 | //will reset
107 | replaceManager.forceUpdate(killed, "current_streak", killedCache.getCurrentStreak());
108 | });
109 |
110 |
111 | database.getStats(killer).ifPresent(killercache -> {
112 | killercache.onKill();
113 | replaceManager.forceUpdate(killer, "kills", killercache.getKills());
114 | replaceManager.forceUpdate(killer, "kdr", killercache.getKdr());
115 | //maybe the player reaches a new high score
116 | replaceManager.forceUpdate(killer, "killstreak", killercache.getKillstreak());
117 | replaceManager.forceUpdate(killer, "current_streak", killercache.getCurrentStreak());
118 | });
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/pvp/src/main/java/com/github/games647/scoreboardstats/pvp/StatsLoader.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.pvp;
2 |
3 | import com.github.games647.scoreboardstats.variables.ReplaceManager;
4 |
5 | import java.lang.ref.WeakReference;
6 | import java.time.Instant;
7 |
8 | import org.apache.commons.lang.builder.ToStringBuilder;
9 | import org.bukkit.Bukkit;
10 | import org.bukkit.entity.Player;
11 | import org.bukkit.metadata.FixedMetadataValue;
12 | import org.bukkit.plugin.Plugin;
13 |
14 | /**
15 | * This class is used for loading the player stats.
16 | */
17 | public class StatsLoader implements Runnable {
18 |
19 | private final Plugin plugin;
20 |
21 | private final WeakReference weakPlayer;
22 | private final WeakReference weakDatabase;
23 |
24 | /**
25 | * Creates a new loader for a specific player
26 | *
27 | * @param plugin the owning plugin to reschedule
28 | * @param player player instance
29 | * @param statsDatabase the pvp database
30 | */
31 | public StatsLoader(Plugin plugin, Player player, Database statsDatabase) {
32 | this.plugin = plugin;
33 |
34 | //don't prevent the garbage collection of this player if he logs out
35 | this.weakPlayer = new WeakReference<>(player);
36 | this.weakDatabase = new WeakReference<>(statsDatabase);
37 | }
38 |
39 | @Override
40 | public void run() {
41 | final Player player = weakPlayer.get();
42 | Database statsDatabase = weakDatabase.get();
43 | if (player != null && statsDatabase != null) {
44 | PlayerStats stats = statsDatabase.loadAccount(player)
45 | .orElse(new PlayerStats(player.getUniqueId(), player.getName()));
46 |
47 | //update player name on every load, because it's changeable
48 | stats.setPlayername(player.getName());
49 | stats.setLastOnline(Instant.now());
50 |
51 | Bukkit.getScheduler().runTask(plugin, () -> {
52 | //possible not thread-safe, so reschedule it while setMetadata is thread-safe
53 | if (player.isOnline()) {
54 | //sets it only if the player is only
55 | player.setMetadata("player_stats", new FixedMetadataValue(plugin, stats));
56 | ReplaceManager.getInstance().forceUpdate(player, "deaths", stats.getDeaths());
57 | ReplaceManager.getInstance().forceUpdate(player, "kdr", stats.getKdr());
58 | ReplaceManager.getInstance().forceUpdate(player, "kills", stats.getKills());
59 | ReplaceManager.getInstance().forceUpdate(player, "killstreak", stats.getKillstreak());
60 | ReplaceManager.getInstance().forceUpdate(player, "current_streak", stats.getCurrentStreak());
61 | ReplaceManager.getInstance().forceUpdate(player, "mobkills", stats.getMobkills());
62 | }
63 | });
64 | }
65 | }
66 |
67 | @Override
68 | public String toString() {
69 | return ToStringBuilder.reflectionToString(this);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/variables/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 |
6 | com.github.games647
7 | scoreboardstats-parent
8 | 1.0.0
9 |
10 |
11 | scoreboardstats-variables
12 | jar
13 |
14 |
15 |
16 |
17 | org.apache.maven.plugins
18 | maven-javadoc-plugin
19 | 3.0.0-M1
20 |
21 |
22 | https://hub.spigotmc.org/javadocs/spigot/
23 | https://docs.oracle.com/javase/9/docs/api/
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/variables/src/main/java/com/github/games647/scoreboardstats/BoardManager.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats;
2 |
3 | import org.bukkit.entity.Player;
4 |
5 | public interface BoardManager {
6 |
7 | /**
8 | * Adding all players to the refresh queue and loading the player stats if enabled
9 | */
10 | void registerAll();
11 |
12 | /**
13 | * Clear the scoreboard for all players
14 | */
15 | void unregisterAll();
16 |
17 | /**
18 | * Creates a new scoreboard based on the configuration.
19 | *
20 | * @param player for who should the scoreboard be set.
21 | */
22 | void createScoreboard(Player player);
23 |
24 | void createTopListScoreboard(Player player);
25 |
26 | void onUpdate(Player player);
27 |
28 | void updateVariable(Player player, String variable, int newScore);
29 |
30 | void updateVariable(Player player, String variable, String newScore);
31 |
32 | /**
33 | * Unregister ScoreboardStats from the player
34 | *
35 | * @param player who owns the scoreboard
36 | */
37 | void unregister(Player player);
38 |
39 | /**
40 | * Called if the scoreboard should be updated.
41 | *
42 | * @param player for who should the scoreboard be set.
43 | */
44 | void sendUpdate(Player player);
45 | }
46 |
--------------------------------------------------------------------------------
/variables/src/main/java/com/github/games647/scoreboardstats/Version.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats;
2 |
3 | import com.google.common.collect.ComparisonChain;
4 |
5 | import java.util.Objects;
6 | import java.util.regex.Matcher;
7 | import java.util.regex.Pattern;
8 |
9 | import org.apache.commons.lang.builder.ToStringBuilder;
10 |
11 | /**
12 | * Version class for comparing and detecting Minecraft and other versions
13 | */
14 | public class Version implements Comparable {
15 |
16 | //thanks to the author of ProtocolLib aadnk
17 | private static final String VERSION_REGEX = ".*\\(.*MC.\\s*([a-zA-z0-9\\-\\.]+)\\s*\\)";
18 | private final int major;
19 | private final int minor;
20 | private final int build;
21 |
22 | /**
23 | * Creates a new version based on this string.
24 | *
25 | * @param version the version string
26 | * @throws IllegalArgumentException if the string doesn't match a version format
27 | */
28 | public Version(String version) throws IllegalArgumentException {
29 | int[] versionParts = parse(version);
30 |
31 | major = versionParts[0];
32 | minor = versionParts[1];
33 | build = versionParts[2];
34 | }
35 |
36 | /**
37 | * Creates a new version based on these values.
38 | *
39 | * @param major the major version
40 | * @param minor the minor version
41 | * @param build the build version
42 | */
43 | public Version(int major, int minor, int build) {
44 | this.major = major;
45 | this.minor = minor;
46 | this.build = build;
47 | }
48 |
49 | /**
50 | * Compares the version with checking the first three numbers
51 | *
52 | * @param expected the object to be compared.
53 | * @param version the object to be compared.
54 | * @return 1 version is higher; 0 both are equal; -1 version is lower
55 | * a negative integer, zero, or a positive integer as this object
56 | * is less than, equal to, or greater than the specified object.
57 | */
58 | public static int compare(String expected, String version) {
59 | int[] expectedParts = parse(stripVersionDetails(version));
60 | int[] versionParts = parse(stripVersionDetails(expected));
61 |
62 | return ComparisonChain.start()
63 | .compare(expectedParts[0], versionParts[0])
64 | .compare(expectedParts[1], versionParts[1])
65 | .compare(expectedParts[2], versionParts[2])
66 | .result();
67 | }
68 |
69 | /**
70 | * Separate the version into major, minor, build integers
71 | *
72 | * @param version the version that should be parsed
73 | * @return the version parts
74 | * @throws IllegalArgumentException if the version doesn't contains only positive numbers separated by max. 5 dots.
75 | */
76 | public static int[] parse(String version) throws IllegalArgumentException {
77 | //excludes spaces which could be added by mistake and exclude build suffixes
78 | String trimmedVersion = version.trim().split("(\\-|[a-zA-Z])")[0];
79 | if (!trimmedVersion.matches("\\d+(\\.\\d+){0,5}")) {
80 | //check if it's a format like '1.5'
81 | throw new IllegalArgumentException("Invalid format: " + version);
82 | }
83 |
84 | int[] versionParts = new int[3];
85 |
86 | //escape regEx and split by dots
87 | String[] split = trimmedVersion.split("\\.");
88 | //We check if the length has min 1 entry.
89 | for (int i = 0; i < split.length && i < versionParts.length; i++) {
90 | versionParts[i] = Integer.parseInt(split[i]);
91 | }
92 |
93 | return versionParts;
94 | }
95 |
96 | private static String getVersionStringFromServer(String versionString) {
97 | Pattern versionPattern = Pattern.compile(VERSION_REGEX);
98 | Matcher versionMatcher = versionPattern.matcher(versionString);
99 |
100 | if (versionMatcher.matches() && versionMatcher.group(1) != null) {
101 | return versionMatcher.group(1);
102 | }
103 |
104 | //Couldn't extract the version
105 | throw new IllegalStateException("Cannot parse version String '" + versionString);
106 | }
107 |
108 | private static String stripVersionDetails(String version) {
109 | int end = version.indexOf('-');
110 | if (end == -1) {
111 | end = version.length();
112 | }
113 |
114 | return version.substring(0, end);
115 | }
116 |
117 | /**
118 | * Gets the major value
119 | *
120 | * @return the major value
121 | */
122 | public int getMajor() {
123 | return major;
124 | }
125 |
126 | /**
127 | * Gets the minor value
128 | *
129 | * @return the minor value
130 | */
131 | public int getMinor() {
132 | return minor;
133 | }
134 |
135 | /**
136 | * Gets the build value
137 | *
138 | * @return the build value
139 | */
140 | public int getBuild() {
141 | return build;
142 | }
143 |
144 | @Override
145 | public int compareTo(Version other) {
146 | return ComparisonChain.start()
147 | .compare(major, other.major)
148 | .compare(minor, other.minor)
149 | .compare(build, other.build)
150 | .result();
151 | }
152 |
153 | @Override
154 | public int hashCode() {
155 | return Objects.hash(major, minor, build);
156 | }
157 |
158 | @Override
159 | public boolean equals(Object obj) {
160 | //ignores also null
161 | if (obj instanceof Version) {
162 | Version other = (Version) obj;
163 | return major == other.major && minor == other.minor && build == other.build;
164 | }
165 |
166 | return false;
167 | }
168 |
169 | @Override
170 | public String toString() {
171 | return ToStringBuilder.reflectionToString(this);
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/variables/src/main/java/com/github/games647/scoreboardstats/scoreboard/Score.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.scoreboard;
2 |
3 | /**
4 | * Represents a scoreboard score item or "line"
5 | */
6 | public interface Score {
7 |
8 | /**
9 | * Get the current display name for the scoreboard score
10 | *
11 | * @return display name
12 | */
13 | String getName();
14 |
15 | /**
16 | * Score value that is displayed next to the item
17 | *
18 | * @return the current score
19 | */
20 | int getScore();
21 | }
22 |
--------------------------------------------------------------------------------
/variables/src/main/java/com/github/games647/scoreboardstats/variables/DefaultReplacer.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.variables;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.ElementType;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 | import java.lang.annotation.Target;
8 |
9 | /**
10 | * Represents that a replacer that is added to this plugin by this plugin itself.
11 | */
12 | @Documented
13 | @Target(ElementType.TYPE)
14 | @Retention(RetentionPolicy.RUNTIME)
15 | public @interface DefaultReplacer {
16 |
17 | /**
18 | * Returns the required plugin that is needed to activate this variables. It defaults to
19 | * this scoreboard plugin itself.
20 | *
21 | * @return the required plugin dependency for this variables
22 | */
23 | String plugin() default "ScoreboardStats";
24 |
25 | /**
26 | * Minimum required version to activate the replacers that will be registered. The version format
27 | * have to be in semVer format major.minor.fix with
28 | *
29 | * 1.2.3 is higher than 1.1.3
30 | *
31 | * 2.5 higher than 1.0.0
32 | *
33 | * @return required version or empty for no required version
34 | */
35 | String requiredVersion() default "";
36 | }
37 |
--------------------------------------------------------------------------------
/variables/src/main/java/com/github/games647/scoreboardstats/variables/DefaultReplacers.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.variables;
2 |
3 | import org.bukkit.plugin.Plugin;
4 |
5 | /**
6 | * Represents a default replacers instance that will register default variables.
7 | *
8 | * @param plugin class for easier access to the plugin field without casting
9 | */
10 | public abstract class DefaultReplacers {
11 |
12 | protected final ReplacerAPI replaceManager;
13 | protected final T plugin;
14 |
15 | public DefaultReplacers(ReplacerAPI replaceManager, T plugin) {
16 | this.replaceManager = replaceManager;
17 | this.plugin = plugin;
18 | }
19 |
20 | /**
21 | * Register all variables that this class can manage
22 | */
23 | public abstract void register();
24 |
25 | /**
26 | * Shortcut method to register the variables without the boilerplate of adding the plugin instance and registering
27 | * it to the manager.
28 | *
29 | * @param variable variable name like "online"
30 | * @return the replacer responsible for this single variable
31 | */
32 | protected Replacer register(String variable) {
33 | Replacer replacer = new Replacer(plugin, variable);
34 | replaceManager.register(replacer);
35 | return replacer;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/variables/src/main/java/com/github/games647/scoreboardstats/variables/EventReplacer.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.variables;
2 |
3 | import com.google.common.collect.Sets;
4 |
5 | import java.util.Set;
6 | import java.util.function.Function;
7 |
8 | import org.bukkit.Bukkit;
9 | import org.bukkit.entity.Player;
10 | import org.bukkit.event.Event;
11 | import org.bukkit.event.player.PlayerEvent;
12 |
13 | /**
14 | * Manages the variable updating if an a specific event is fired.
15 | *
16 | * @param event class - this have to the exact type
17 | */
18 | class EventReplacer {
19 |
20 | private final Replacer replacer;
21 | private final Class eventClass;
22 | private final Set> functions = Sets.newHashSet();
23 | private final Set> scoreFunctions = Sets.newHashSet();
24 |
25 | EventReplacer(Replacer replacer, Class eventClass) {
26 | this.replacer = replacer;
27 | this.eventClass = eventClass;
28 | }
29 |
30 | void addFct(Function fct) {
31 | functions.add(fct);
32 | }
33 |
34 | void addScoreFct(Function fct) {
35 | scoreFunctions.add(fct);
36 | }
37 |
38 | public Set> getFunctions() {
39 | return functions;
40 | }
41 |
42 | public Set> getScoreFunctions() {
43 | return scoreFunctions;
44 | }
45 |
46 | void execute(ReplacerAPI replaceManager, Event event) {
47 | executeUnsafe(replaceManager, eventClass.cast(event));
48 | }
49 |
50 | private void executeUnsafe(ReplacerAPI replaceManager, T event) {
51 | String variable = replacer.getVariable();
52 | for (Function function : scoreFunctions) {
53 | int newScore = function.apply(event);
54 | if (replacer.isGlobal()) {
55 | for (Player player : Bukkit.getOnlinePlayers()) {
56 | replaceManager.forceUpdate(player, variable, newScore);
57 | }
58 | } else if (event instanceof PlayerEvent) {
59 | replaceManager.forceUpdate(((PlayerEvent) event).getPlayer(), variable, newScore);
60 | }
61 | }
62 | }
63 |
64 | Class getEventClass() {
65 | return eventClass;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/variables/src/main/java/com/github/games647/scoreboardstats/variables/PluginListener.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.variables;
2 |
3 | import org.bukkit.event.EventHandler;
4 | import org.bukkit.event.Listener;
5 | import org.bukkit.event.server.PluginDisableEvent;
6 | import org.bukkit.plugin.Plugin;
7 |
8 | /**
9 | * Keeps track of plugin disables and enables. It will register default replacers
10 | * back again or removes replacers of disabled plugins.
11 | */
12 | class PluginListener implements Listener {
13 |
14 | private final ReplaceManager replaceManager;
15 |
16 | public PluginListener(ReplaceManager replaceManager) {
17 | this.replaceManager = replaceManager;
18 | }
19 |
20 | /**
21 | * Check for disabled plugin to remove the associated replacer
22 | *
23 | * @param disableEvent the disable event
24 | */
25 | @EventHandler
26 | public void onPluginDisable(PluginDisableEvent disableEvent) {
27 | //Remove the listener if the associated plugin was disabled
28 | Plugin disablePlugin = disableEvent.getPlugin();
29 |
30 | replaceManager.unregisterAll(disablePlugin);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/variables/src/main/java/com/github/games647/scoreboardstats/variables/ReplaceManager.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.variables;
2 |
3 | import com.github.games647.scoreboardstats.BoardManager;
4 | import com.github.games647.scoreboardstats.Version;
5 | import com.google.common.collect.Sets;
6 | import com.google.common.reflect.ClassPath;
7 | import com.google.common.reflect.ClassPath.ClassInfo;
8 |
9 | import java.io.IOException;
10 | import java.lang.reflect.Constructor;
11 | import java.util.Set;
12 | import java.util.stream.Collectors;
13 |
14 | import org.bukkit.Bukkit;
15 | import org.bukkit.entity.Player;
16 | import org.bukkit.plugin.Plugin;
17 | import org.slf4j.Logger;
18 |
19 | /**
20 | * Handling the replace management
21 | */
22 | public class ReplaceManager extends ReplacerAPI {
23 |
24 | private static final String UNSUPPORTED_VERSION = "The Replacer: {} cannot be registered -" +
25 | " the plugin version isn't supported {} {}";
26 |
27 | //todo: only temporarily
28 | @Deprecated
29 | private static ReplaceManager instance;
30 | private final Plugin plugin;
31 | private final BoardManager boardManager;
32 |
33 | /**
34 | * Creates a new replace manager
35 | *
36 | * @param scoreboardManager to manage the scoreboards
37 | * @param plugin ScoreboardStats plugin
38 | */
39 | public ReplaceManager(BoardManager scoreboardManager, Plugin plugin, Logger logger) {
40 | super(logger);
41 |
42 | instance = this;
43 |
44 | this.plugin = plugin;
45 | this.boardManager = scoreboardManager;
46 |
47 | Bukkit.getPluginManager().registerEvents(new PluginListener(this), plugin);
48 | addDefaultReplacers();
49 | }
50 |
51 | @Deprecated
52 | public static ReplaceManager getInstance() {
53 | return instance;
54 | }
55 |
56 | public void close() {
57 | instance = null;
58 | }
59 |
60 | @Override
61 | public void forceUpdate(Player player, String variable, int score) {
62 | boardManager.updateVariable(player, variable, score);
63 | }
64 |
65 | @Override
66 | public void forceUpdate(Player player, String variable, String value) {
67 | boardManager.updateVariable(player, variable, value);
68 | }
69 |
70 | public void updateGlobals() {
71 | replacers.values()
72 | .stream()
73 | .filter(Replacer::isGlobal)
74 | .filter(replacer -> !replacer.isEventVariable())
75 | .forEach(replacer -> {
76 | int score = replacer.scoreReplace(null);
77 | String variable = replacer.getVariable();
78 | Bukkit.getOnlinePlayers().forEach(player -> boardManager.updateVariable(player, variable, score));
79 | });
80 | }
81 |
82 | private void addDefaultReplacers() {
83 | Set defaultReplacers = Sets.newHashSet();
84 | try {
85 | defaultReplacers = ClassPath.from(getClass().getClassLoader())
86 | .getTopLevelClasses("com.github.games647.scoreboardstats.defaults")
87 | .stream()
88 | .map(ClassInfo::load)
89 | .filter(DefaultReplacers.class::isAssignableFrom)
90 | .map(clazz -> (Class>) clazz)
91 | .filter(this::registerDefault)
92 | .map(Class::getSimpleName)
93 | .collect(Collectors.toSet());
94 | } catch (IOException ioEx) {
95 | logger.error("Failed to register replacers", ioEx);
96 | }
97 |
98 | logger.info("Registered default replacers: {}", defaultReplacers);
99 | }
100 |
101 | private boolean registerDefault(Class> replacerClass) {
102 | try {
103 | DefaultReplacer annotation = replacerClass.getAnnotation(DefaultReplacer.class);
104 |
105 | String replacerPluginName = annotation.plugin();
106 | if (replacerPluginName.isEmpty()) {
107 | replacerPluginName = plugin.getName();
108 | }
109 |
110 | Plugin replacerPlugin = Bukkit.getPluginManager().getPlugin(replacerPluginName);
111 | if (replacerPlugin != null) {
112 | String required = annotation.requiredVersion();
113 | String version = replacerPlugin.getDescription().getVersion();
114 | if (!required.isEmpty() && new Version(version).compareTo(new Version(required)) >= 0) {
115 | logger.info(UNSUPPORTED_VERSION, replacerClass.getSimpleName(), version, required);
116 | return false;
117 | }
118 |
119 | Constructor> cons = replacerClass.getConstructor(ReplacerAPI.class, Plugin.class);
120 | cons.newInstance(this, replacerPlugin).register();
121 | }
122 |
123 | return true;
124 | } catch (Exception | LinkageError replacerException) {
125 | //only catch this throwable, because they could probably happened
126 | logger.warn("Cannot register replacer", replacerException);
127 | }
128 |
129 | return false;
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/variables/src/main/java/com/github/games647/scoreboardstats/variables/Replacer.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.variables;
2 |
3 | import com.google.common.base.Converter;
4 | import com.google.common.collect.Maps;
5 | import com.google.common.primitives.Ints;
6 |
7 | import java.util.Map;
8 | import java.util.Optional;
9 | import java.util.function.Function;
10 | import java.util.function.IntSupplier;
11 | import java.util.function.Supplier;
12 |
13 | import org.bukkit.ChatColor;
14 | import org.bukkit.entity.Player;
15 | import org.bukkit.event.Event;
16 | import org.bukkit.plugin.Plugin;
17 |
18 | public class Replacer {
19 |
20 | private final Plugin plugin;
21 | private final String variable;
22 |
23 | private final Converter stringConverter = Ints.stringConverter();
24 | private final Converter intConverter = stringConverter.reverse();
25 |
26 | private final Map> eventsReplacers = Maps.newHashMap();
27 |
28 | private boolean async;
29 | private boolean constant;
30 | private String description;
31 | private boolean global;
32 |
33 | private Function scoreSupplier;
34 | private Function supplier;
35 |
36 | public Replacer(Plugin plugin, String variable) {
37 | this.plugin = plugin;
38 | this.variable = variable;
39 | }
40 |
41 | public Replacer async() {
42 | this.async = true;
43 | return this;
44 | }
45 |
46 | public Replacer constant() {
47 | this.constant = true;
48 | return this;
49 | }
50 |
51 | public Replacer description(String description) {
52 | this.description = ChatColor.translateAlternateColorCodes('&', description);
53 | return this;
54 | }
55 |
56 | public Replacer supply(Supplier supplier) {
57 | this.global = true;
58 | supply(player -> supplier.get());
59 | return this;
60 | }
61 |
62 | public Replacer scoreSupply(IntSupplier supplier) {
63 | this.global = true;
64 | scoreSupply(player -> supplier.getAsInt());
65 | return this;
66 | }
67 |
68 | public Replacer supply(Function supplier) {
69 | this.supplier = supplier;
70 | this.scoreSupplier = player -> stringConverter.convert(supplier.apply(player));
71 |
72 | return this;
73 | }
74 |
75 | public Replacer scoreSupply(Function supplier) {
76 | this.scoreSupplier = supplier;
77 | this.supplier = player -> intConverter.convert(supplier.apply(player));
78 |
79 | return this;
80 | }
81 |
82 | public Replacer event(Class eventClass, Supplier supplier) {
83 | event(eventClass, event -> supplier.get());
84 | return this;
85 | }
86 |
87 | public Replacer eventScore(Class eventClass, IntSupplier supplier) {
88 | eventScore(eventClass, event -> supplier.getAsInt());
89 | return this;
90 | }
91 |
92 | public Replacer event(Class eventClass, Function function) {
93 | EventReplacer checkedReplacer = getEventReplacer(eventClass);
94 | checkedReplacer.addFct(function);
95 | return this;
96 | }
97 |
98 | public Replacer eventScore(Class eventClass, Function function) {
99 | EventReplacer checkedReplacer = getEventReplacer(eventClass);
100 | checkedReplacer.addScoreFct(function);
101 | return this;
102 | }
103 |
104 | private EventReplacer getEventReplacer(Class eventClass) {
105 | String eventName = eventClass.getCanonicalName();
106 | EventReplacer> replacer = eventsReplacers.get(eventName);
107 |
108 | EventReplacer checkedReplacer;
109 | if (replacer == null) {
110 | return new EventReplacer<>(this, eventClass);
111 | }
112 |
113 | return ((EventReplacer) replacer);
114 | }
115 |
116 | public String replace(Player player) {
117 | return supplier.apply(player);
118 | }
119 |
120 | public int scoreReplace(Player player) {
121 | return scoreSupplier.apply(player);
122 | }
123 |
124 | public String getVariable() {
125 | return variable;
126 | }
127 |
128 | public boolean isAsync() {
129 | return async;
130 | }
131 |
132 | public boolean isGlobal() {
133 | return global;
134 | }
135 |
136 | public boolean isConstant() {
137 | return constant;
138 | }
139 |
140 | public Plugin getPlugin() {
141 | return plugin;
142 | }
143 |
144 | public boolean isEventVariable() {
145 | return !eventsReplacers.isEmpty();
146 | }
147 |
148 | public Optional getDescription() {
149 | return Optional.ofNullable(description);
150 | }
151 |
152 | protected Function getScoreSupplier() {
153 | return scoreSupplier;
154 | }
155 |
156 | protected Function getSupplier() {
157 | return supplier;
158 | }
159 |
160 | protected Map> getEventsReplacers() {
161 | return eventsReplacers;
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/variables/src/main/java/com/github/games647/scoreboardstats/variables/ReplacerAPI.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.variables;
2 |
3 | import com.google.common.collect.Maps;
4 |
5 | import java.util.Iterator;
6 | import java.util.Map;
7 | import java.util.Optional;
8 | import java.util.OptionalInt;
9 |
10 | import org.bukkit.Bukkit;
11 | import org.bukkit.entity.Player;
12 | import org.bukkit.event.Event;
13 | import org.bukkit.event.Listener;
14 | import org.bukkit.plugin.EventExecutor;
15 | import org.bukkit.plugin.Plugin;
16 | import org.bukkit.plugin.PluginManager;
17 | import org.slf4j.Logger;
18 |
19 | import static org.bukkit.event.EventPriority.HIGHEST;
20 |
21 | public abstract class ReplacerAPI {
22 |
23 | protected final Logger logger;
24 | protected final Map replacers = Maps.newHashMap();
25 |
26 | public ReplacerAPI(Logger logger) {
27 | this.logger = logger;
28 | }
29 |
30 | public void register(Replacer replacer) {
31 | replacers.put(replacer.getVariable(), replacer);
32 |
33 | for (EventReplacer extends Event> eventReplacer : replacer.getEventsReplacers().values()) {
34 | Class extends Event> eventClass = eventReplacer.getEventClass();
35 | Plugin plugin = replacer.getPlugin();
36 |
37 | EventExecutor executor = (listener, event) -> eventReplacer.execute(this, event);
38 |
39 | Listener listener = new Listener() {};
40 |
41 | PluginManager pluginManager = Bukkit.getPluginManager();
42 | pluginManager.registerEvent(eventClass, listener, HIGHEST, executor, plugin, true);
43 | }
44 | }
45 |
46 | public void unregister(String variable) {
47 | replacers.remove(variable);
48 | }
49 |
50 | public void unregisterAll(Plugin disablePlugin) {
51 | Iterator iterator = replacers.values().iterator();
52 | while (iterator.hasNext()) {
53 | Plugin plugin = iterator.next().getPlugin();
54 | if (plugin == disablePlugin) {
55 | iterator.remove();
56 | }
57 | }
58 | }
59 |
60 | public void forceUpdate(String variable, int score) {
61 | Bukkit.getOnlinePlayers().forEach(player -> forceUpdate(player, variable, score));
62 | }
63 |
64 | public void forceUpdate(String variable, String value) {
65 | Bukkit.getOnlinePlayers().forEach(player -> forceUpdate(player, variable, value));
66 | }
67 |
68 | public abstract void forceUpdate(Player player, String variable, int score);
69 |
70 | public abstract void forceUpdate(Player player, String variable, String value);
71 |
72 | public Optional replace(Player player, String variable, boolean complete) throws ReplacerException {
73 | Replacer replacer = getReplacer(variable);
74 | if (!complete && replacer.isGlobal() || replacer.isEventVariable() || replacer.isConstant()) {
75 | return Optional.empty();
76 | }
77 |
78 | try {
79 | return Optional.of(replacer.replace(player));
80 | } catch (Exception ex) {
81 | throw new ReplacerException(ex);
82 | }
83 | }
84 |
85 | public OptionalInt scoreReplace(Player player, String variable, boolean complete) throws ReplacerException {
86 | Replacer replacer = getReplacer(variable);
87 | if (!complete && replacer.isGlobal() || replacer.isEventVariable() || replacer.isConstant()) {
88 | return OptionalInt.empty();
89 | }
90 |
91 | try {
92 | return OptionalInt.of(replacer.scoreReplace(player));
93 | } catch (Exception ex) {
94 | throw new ReplacerException(ex);
95 | }
96 | }
97 |
98 | private Replacer getReplacer(String variable) throws UnknownVariableException {
99 | Replacer replacer = this.replacers.get(variable);
100 | if (replacer == null) {
101 | throw new UnknownVariableException(variable);
102 | }
103 |
104 | return replacer;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/variables/src/main/java/com/github/games647/scoreboardstats/variables/ReplacerException.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.variables;
2 |
3 | public class ReplacerException extends Exception {
4 |
5 | public ReplacerException(Throwable cause) {
6 | super(cause);
7 | }
8 |
9 | public ReplacerException(String message) {
10 | super(message);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/variables/src/main/java/com/github/games647/scoreboardstats/variables/UnknownVariableException.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.variables;
2 |
3 | /**
4 | * Represents an exception if a used variable can't be replaced by a replacer
5 | * So if no replacer know this variable
6 | */
7 | public class UnknownVariableException extends ReplacerException {
8 |
9 | private static final long serialVersionUID = 1L;
10 |
11 | /**
12 | * Creates a new exception if the variable couldn't be replaced.
13 | *
14 | * @param variable variable
15 | */
16 | public UnknownVariableException(String variable) {
17 | super("Unknown variable " + variable);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/variables/src/main/java/com/github/games647/scoreboardstats/variables/UnsupportedPluginException.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.variables;
2 |
3 | public class UnsupportedPluginException extends ReplacerException {
4 |
5 | public UnsupportedPluginException(String pluginName, String expectedVersion, String currentVersion) {
6 | super(String.format("The version %s of plugin %s version isn't supported. We require at least %s",
7 | currentVersion, pluginName, expectedVersion));
8 | }
9 |
10 | public UnsupportedPluginException(String message) {
11 | super(message);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/variables/src/test/java/com/github/games647/scoreboardstats/variables/ReplaceManagerTest.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.scoreboardstats.variables;
2 |
3 | import org.bukkit.Bukkit;
4 | import org.bukkit.plugin.Plugin;
5 | import org.bukkit.plugin.SimplePluginManager;
6 | import org.bukkit.plugin.messaging.StandardMessenger;
7 | import org.bukkit.scheduler.BukkitScheduler;
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 | import org.mockito.Mockito;
11 | import org.powermock.api.mockito.PowerMockito;
12 | import org.powermock.core.classloader.annotations.PrepareForTest;
13 | import org.powermock.modules.junit4.PowerMockRunner;
14 | import org.slf4j.LoggerFactory;
15 |
16 | @PrepareForTest({Bukkit.class, SimplePluginManager.class, Plugin.class})
17 | @RunWith(PowerMockRunner.class)
18 | public class ReplaceManagerTest {
19 |
20 | private static final String SAMPLE_VARIABLE = "sample";
21 |
22 | @Test
23 | public void testUnregister() throws Exception {
24 | PowerMockito.mockStatic(Bukkit.class);
25 | Mockito.when(Bukkit.getPluginManager()).thenReturn(PowerMockito.mock(SimplePluginManager.class));
26 | Mockito.when(Bukkit.getMessenger()).thenReturn(PowerMockito.mock(StandardMessenger.class));
27 | Mockito.when(Bukkit.getScheduler()).thenReturn(PowerMockito.mock(BukkitScheduler.class));
28 |
29 | Plugin plugin = PowerMockito.mock(Plugin.class);
30 |
31 | ReplaceManager replaceManager = new ReplaceManager(null, plugin, LoggerFactory.getLogger("test"));
32 | replaceManager.register(new Replacer(plugin, "test").scoreSupply(() -> 1));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------