From a99c7d39ba9558d0b4f8163795e47dbf9c2fed11 Mon Sep 17 00:00:00 2001 From: xjboss Date: Sun, 30 Jul 2017 09:48:36 +0800 Subject: [PATCH 1/5] fix yumc statistics --- src/main/java/org/bstats/BMetrics.java | 669 +++++++++++++++++++++++ src/main/java/pw/yumc/KCXStatistics.java | 12 +- 2 files changed, 670 insertions(+), 11 deletions(-) create mode 100644 src/main/java/org/bstats/BMetrics.java diff --git a/src/main/java/org/bstats/BMetrics.java b/src/main/java/org/bstats/BMetrics.java new file mode 100644 index 0000000..6a1218f --- /dev/null +++ b/src/main/java/org/bstats/BMetrics.java @@ -0,0 +1,669 @@ +package org.bstats; + +import org.bukkit.plugin.java.JavaPlugin; +import org.json.simple.JSONArray; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.bukkit.plugin.ServicePriority; +import org.bukkit.plugin.java.JavaPlugin; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import javax.net.ssl.HttpsURLConnection; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.logging.Level; +import java.util.zip.GZIPOutputStream; + +/** + * bStats collects some data for plugin authors. + * + * Check out https://bStats.org/ to learn more about bStats! + */ +public class BMetrics { + + static { + // You can use the property to disable the check in your test environment + if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) { + // Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D + final String defaultPackage = new String( + new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'}); + final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); + // We want to make sure nobody just copy & pastes the example and use the wrong package names + if (BMetrics.class.getPackage().getName().equals(defaultPackage) || BMetrics.class.getPackage().getName().equals(examplePackage)) { + throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); + } + } + } + + // The version of this bStats class + public static final int B_STATS_VERSION = 1; + + // The url to which the data is sent + private static final String URL = "https://bStats.org/submitData/bukkit"; + + // Should failed requests be logged? + private static boolean logFailedRequests; + + // The uuid of the server + private static String serverUUID; + + // The plugin + private final JavaPlugin plugin; + + // A list with all custom charts + private final List charts = new ArrayList<>(); + + /** + * Class constructor. + * + * @param plugin The plugin which stats should be submitted. + */ + public BMetrics(JavaPlugin plugin) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null!"); + } + this.plugin = plugin; + + // Get the config file + File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); + File configFile = new File(bStatsFolder, "config.yml"); + YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); + + // Check if the config file exists + if (!config.isSet("serverUuid")) { + + // Add default values + config.addDefault("enabled", true); + // Every server gets it's unique random id. + config.addDefault("serverUuid", UUID.randomUUID().toString()); + // Should failed request be logged? + config.addDefault("logFailedRequests", false); + + // Inform the server owners about bStats + config.options().header( + "bStats collects some data for plugin authors like how many servers are using their plugins.\n" + + "To honor their work, you should not disable it.\n" + + "This has nearly no effect on the server performance!\n" + + "Check out https://bStats.org/ to learn more :)" + ).copyDefaults(true); + try { + config.save(configFile); + } catch (IOException ignored) { } + } + + // Load the data + serverUUID = config.getString("serverUuid"); + logFailedRequests = config.getBoolean("logFailedRequests", false); + if (config.getBoolean("enabled", true)) { + boolean found = false; + // Search for all other bStats Metrics classes to see if we are the first one + for (Class service : Bukkit.getServicesManager().getKnownServices()) { + try { + service.getField("B_STATS_VERSION"); // Our identifier :) + found = true; // We aren't the first + break; + } catch (NoSuchFieldException ignored) { } + } + // Register our service + Bukkit.getServicesManager().register(BMetrics.class, this, plugin, ServicePriority.Normal); + if (!found) { + // We are the first! + startSubmitting(); + } + } + } + + /** + * Adds a custom chart. + * + * @param chart The chart to add. + */ + public void addCustomChart(CustomChart chart) { + if (chart == null) { + throw new IllegalArgumentException("Chart cannot be null!"); + } + charts.add(chart); + } + + /** + * Starts the Scheduler which submits our data every 30 minutes. + */ + private void startSubmitting() { + final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + if (!plugin.isEnabled()) { // Plugin was disabled + timer.cancel(); + return; + } + // Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler + // Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;) + Bukkit.getScheduler().runTask(plugin, new Runnable() { + @Override + public void run() { + submitData(); + } + }); + } + }, 1000*60*5, 1000*60*30); + // Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start + // WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted! + // WARNING: Just don't do it! + } + + /** + * Gets the plugin specific data. + * This method is called using Reflection. + * + * @return The plugin specific data. + */ + public JSONObject getPluginData() { + JSONObject data = new JSONObject(); + + String pluginName = plugin.getDescription().getName(); + String pluginVersion = plugin.getDescription().getVersion(); + + data.put("pluginName", pluginName); // Append the name of the plugin + data.put("pluginVersion", pluginVersion); // Append the version of the plugin + JSONArray customCharts = new JSONArray(); + for (CustomChart customChart : charts) { + // Add the data of the custom charts + JSONObject chart = customChart.getRequestJsonObject(); + if (chart == null) { // If the chart is null, we skip it + continue; + } + customCharts.add(chart); + } + data.put("customCharts", customCharts); + + return data; + } + + /** + * Gets the server specific data. + * + * @return The server specific data. + */ + private JSONObject getServerData() { + // Minecraft specific data + int playerAmount; + try { + // Around MC 1.8 the return type was changed to a collection from an array, + // This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; + Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); + playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class) + ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() + : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; + } catch (Exception e) { + playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed + } + int onlineMode = Bukkit.getOnlineMode() ? 1 : 0; + String bukkitVersion = org.bukkit.Bukkit.getVersion(); + bukkitVersion = bukkitVersion.substring(bukkitVersion.indexOf("MC: ") + 4, bukkitVersion.length() - 1); + + // OS/Java specific data + String javaVersion = System.getProperty("java.version"); + String osName = System.getProperty("os.name"); + String osArch = System.getProperty("os.arch"); + String osVersion = System.getProperty("os.version"); + int coreCount = Runtime.getRuntime().availableProcessors(); + + JSONObject data = new JSONObject(); + + data.put("serverUUID", serverUUID); + + data.put("playerAmount", playerAmount); + data.put("onlineMode", onlineMode); + data.put("bukkitVersion", bukkitVersion); + + data.put("javaVersion", javaVersion); + data.put("osName", osName); + data.put("osArch", osArch); + data.put("osVersion", osVersion); + data.put("coreCount", coreCount); + + return data; + } + + /** + * Collects the data and sends it afterwards. + */ + private void submitData() { + final JSONObject data = getServerData(); + + JSONArray pluginData = new JSONArray(); + // Search for all other bStats Metrics classes to get their plugin data + for (Class service : Bukkit.getServicesManager().getKnownServices()) { + try { + service.getField("B_STATS_VERSION"); // Our identifier :) + + for (RegisteredServiceProvider provider : Bukkit.getServicesManager().getRegistrations(service)) { + try { + pluginData.add(provider.getService().getMethod("getPluginData").invoke(provider.getProvider())); + } catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { } + } + } catch (NoSuchFieldException ignored) { } + } + + data.put("plugins", pluginData); + + // Create a new thread for the connection to the bStats server + new Thread(new Runnable() { + @Override + public void run() { + try { + // Send the data + sendData(data); + } catch (Exception e) { + // Something went wrong! :( + if (logFailedRequests) { + plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e); + } + } + } + }).start(); + } + + /** + * Sends the data to the bStats server. + * + * @param data The data to send. + * @throws Exception If the request failed. + */ + private static void sendData(JSONObject data) throws Exception { + if (data == null) { + throw new IllegalArgumentException("Data cannot be null!"); + } + if (Bukkit.isPrimaryThread()) { + throw new IllegalAccessException("This method must not be called from the main thread!"); + } + HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); + + // Compress the data to save bandwidth + byte[] compressedData = compress(data.toString()); + + // Add headers + connection.setRequestMethod("POST"); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request + connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); + connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format + connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION); + + // Send data + connection.setDoOutput(true); + DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); + outputStream.write(compressedData); + outputStream.flush(); + outputStream.close(); + + connection.getInputStream().close(); // We don't care about the response - Just send our data :) + } + + /** + * Gzips the given String. + * + * @param str The string to gzip. + * @return The gzipped String. + * @throws IOException If the compression failed. + */ + private static byte[] compress(final String str) throws IOException { + if (str == null) { + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + GZIPOutputStream gzip = new GZIPOutputStream(outputStream); + gzip.write(str.getBytes("UTF-8")); + gzip.close(); + return outputStream.toByteArray(); + } + + /** + * Represents a custom chart. + */ + public static abstract class CustomChart { + + // The id of the chart + final String chartId; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + */ + CustomChart(String chartId) { + if (chartId == null || chartId.isEmpty()) { + throw new IllegalArgumentException("ChartId cannot be null or empty!"); + } + this.chartId = chartId; + } + + private JSONObject getRequestJsonObject() { + JSONObject chart = new JSONObject(); + chart.put("chartId", chartId); + try { + JSONObject data = getChartData(); + if (data == null) { + // If the data is null we don't send the chart. + return null; + } + chart.put("data", data); + } catch (Throwable t) { + if (logFailedRequests) { + Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t); + } + return null; + } + return chart; + } + + protected abstract JSONObject getChartData() throws Exception; + + } + + /** + * Represents a custom simple pie. + */ + public static class SimplePie extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimplePie(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + String value = callable.call(); + if (value == null || value.isEmpty()) { + // Null = skip the chart + return null; + } + data.put("value", value); + return data; + } + } + + /** + * Represents a custom advanced pie. + */ + public static class AdvancedPie extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedPie(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + continue; // Skip this invalid + } + allSkipped = false; + values.put(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + } + + /** + * Represents a custom drilldown pie. + */ + public static class DrilldownPie extends CustomChart { + + private final Callable>> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public DrilldownPie(String chartId, Callable>> callable) { + super(chartId); + this.callable = callable; + } + + @Override + public JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map> map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean reallyAllSkipped = true; + for (Map.Entry> entryValues : map.entrySet()) { + JSONObject value = new JSONObject(); + boolean allSkipped = true; + for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { + value.put(valueEntry.getKey(), valueEntry.getValue()); + allSkipped = false; + } + if (!allSkipped) { + reallyAllSkipped = false; + values.put(entryValues.getKey(), value); + } + } + if (reallyAllSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + } + + /** + * Represents a custom single line chart. + */ + public static class SingleLineChart extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SingleLineChart(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + int value = callable.call(); + if (value == 0) { + // Null = skip the chart + return null; + } + data.put("value", value); + return data; + } + + } + + /** + * Represents a custom multi line chart. + */ + public static class MultiLineChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public MultiLineChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + continue; // Skip this invalid + } + allSkipped = false; + values.put(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + + } + + /** + * Represents a custom simple bar chart. + */ + public static class SimpleBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimpleBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + for (Map.Entry entry : map.entrySet()) { + JSONArray categoryValues = new JSONArray(); + categoryValues.add(entry.getValue()); + values.put(entry.getKey(), categoryValues); + } + data.put("values", values); + return data; + } + + } + + /** + * Represents a custom advanced bar chart. + */ + public static class AdvancedBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().length == 0) { + continue; // Skip this invalid + } + allSkipped = false; + JSONArray categoryValues = new JSONArray(); + for (int categoryValue : entry.getValue()) { + categoryValues.add(categoryValue); + } + values.put(entry.getKey(), categoryValues); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + + } +} \ No newline at end of file diff --git a/src/main/java/pw/yumc/KCXStatistics.java b/src/main/java/pw/yumc/KCXStatistics.java index 53e312a..f97029f 100644 --- a/src/main/java/pw/yumc/KCXStatistics.java +++ b/src/main/java/pw/yumc/KCXStatistics.java @@ -210,16 +210,6 @@ public class KCXStatistics { task.start(); return true; } - - /** - * 获得在线玩家人数 - * - * @return 在线玩家人数 - */ - private int getOnlinePlayerNumber() { - return Bukkit.getOnlinePlayers().size(); - } - /** * 发送服务器数据到统计网页 */ @@ -234,7 +224,7 @@ public class KCXStatistics { data.put("server_port", Bukkit.getServer().getPort()); data.put("server_tps", FMLCommonHandler.instance().getMinecraftServerInstance().recentTps[1]); data.put("plugin_version", KCauldron.getCurrentVersion()); - data.put("players_online", getOnlinePlayerNumber()); + data.put("players_online", Bukkit.getServer().getOnlinePlayers().size()); data.put("os_name", System.getProperty("os.name")); data.put("os_arch", tmposarch.equalsIgnoreCase("amd64") ? "x86_64" : tmposarch); data.put("os_version", System.getProperty("os.version")); From 823599a9ec454f862f5b8097febeb0f39fa326eb Mon Sep 17 00:00:00 2001 From: xjboss Date: Sun, 30 Jul 2017 10:21:01 +0800 Subject: [PATCH 2/5] move statistics class to kcauldronx package --- .../dedicated/DedicatedServer.java.patch | 34 ++++--- src/main/java/kcauldron/KCauldron.java | 2 +- .../{org/bstats => kcauldronx}/BMetrics.java | 94 ++++++------------- .../YUMCStatistics.java} | 10 +- src/main/java/org/spigotmc/Metrics.java | 5 +- src/main/java/org/spigotmc/SpigotConfig.java | 14 ++- 6 files changed, 70 insertions(+), 89 deletions(-) rename src/main/java/{org/bstats => kcauldronx}/BMetrics.java (86%) rename src/main/java/{pw/yumc/KCXStatistics.java => kcauldronx/YUMCStatistics.java} (97%) diff --git a/patches/net/minecraft/server/dedicated/DedicatedServer.java.patch b/patches/net/minecraft/server/dedicated/DedicatedServer.java.patch index 557fc68..0dc781f 100644 --- a/patches/net/minecraft/server/dedicated/DedicatedServer.java.patch +++ b/patches/net/minecraft/server/dedicated/DedicatedServer.java.patch @@ -8,7 +8,16 @@ import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import java.io.BufferedReader; -@@ -34,9 +35,24 @@ +@@ -14,6 +15,8 @@ + import java.util.List; + import java.util.Random; + import java.util.concurrent.Callable; ++ ++import kcauldronx.YUMCStatistics; + import net.minecraft.command.ICommandSender; + import net.minecraft.command.ServerCommand; + import net.minecraft.crash.CrashReport; +@@ -34,9 +37,23 @@ import net.minecraft.world.World; import net.minecraft.world.WorldSettings; import net.minecraft.world.WorldType; @@ -27,13 +36,12 @@ +import org.bukkit.craftbukkit.SpigotTimings; // Spigot +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.server.ServerCommandEvent; -+import pw.yumc.KCXStatistics; +// CraftBukkit end + @SideOnly(Side.SERVER) public class DedicatedServer extends MinecraftServer implements IServer { -@@ -44,18 +60,21 @@ +@@ -44,18 +61,21 @@ public final List pendingCommandList = Collections.synchronizedList(new ArrayList()); private RConThreadQuery theRConThreadQuery; private RConThreadMain theRConThreadMain; @@ -47,7 +55,7 @@ private static final String __OBFID = "CL_00001784"; - - public DedicatedServer(File p_i1508_1_) -+ private KCXStatistics yumc_statistics; ++ private YUMCStatistics yumc_statistics; + // CraftBukkit start - Signature changed + public DedicatedServer(joptsimple.OptionSet options) { @@ -60,7 +68,7 @@ { private static final String __OBFID = "CL_00001787"; { -@@ -82,31 +101,77 @@ +@@ -82,31 +102,77 @@ }; } @@ -145,7 +153,7 @@ field_155771_h.info("Starting minecraft server version 1.7.10"); if (Runtime.getRuntime().maxMemory() / 1024L / 1024L < 512L) -@@ -117,7 +182,7 @@ +@@ -117,7 +183,7 @@ FMLCommonHandler.instance().onServerStart(this); field_155771_h.info("Loading properties"); @@ -154,7 +162,7 @@ this.field_154332_n = new ServerEula(new File("eula.txt")); if (!this.field_154332_n.func_154346_a()) -@@ -172,6 +237,18 @@ +@@ -172,6 +238,18 @@ this.setServerPort(this.settings.getIntProperty("server-port", 25565)); } @@ -173,7 +181,7 @@ field_155771_h.info("Generating keypair"); this.setKeyPair(CryptManager.createNewKeyPair()); field_155771_h.info("Starting Minecraft server on " + (this.getServerHostname().length() == 0 ? "*" : this.getServerHostname()) + ":" + this.getServerPort()); -@@ -180,7 +257,7 @@ +@@ -180,7 +258,7 @@ { this.func_147137_ag().addLanEndpoint(inetaddress, this.getServerPort()); } @@ -182,7 +190,7 @@ { field_155771_h.warn("**** FAILED TO BIND TO PORT!"); field_155771_h.warn("The exception was: {}", new Object[] {ioexception.toString()}); -@@ -196,10 +273,17 @@ +@@ -196,10 +274,17 @@ field_155771_h.warn("To change this, set \"online-mode\" to \"true\" in the server.properties file."); } @@ -202,7 +210,7 @@ if (!PreYggdrasilConverter.func_152714_a(this.settings)) { -@@ -208,7 +292,8 @@ +@@ -208,7 +293,8 @@ else { FMLCommonHandler.instance().onServerStarted(); @@ -212,7 +220,7 @@ long j = System.nanoTime(); if (this.getFolderName() == null) -@@ -274,11 +359,40 @@ +@@ -274,11 +360,40 @@ this.theRConThreadMain.startThread(); } @@ -234,7 +242,7 @@ + FakePlayer.fakePlayers=null; + FakePlayer.BukkitInited=true; + //Yumc Statistcs start -+ yumc_statistics=new KCXStatistics(); ++ yumc_statistics=new YUMCStatistics(); + yumc_statistics.start(); + //Yumc Statistcs end + // KCauldronX end @@ -253,7 +261,7 @@ public boolean canStructuresSpawn() { return this.canSpawnStructures; -@@ -364,11 +478,19 @@ +@@ -364,11 +479,19 @@ public void executePendingCommands() { diff --git a/src/main/java/kcauldron/KCauldron.java b/src/main/java/kcauldron/KCauldron.java index 6561386..6ba9aa3 100644 --- a/src/main/java/kcauldron/KCauldron.java +++ b/src/main/java/kcauldron/KCauldron.java @@ -14,7 +14,7 @@ import cpw.mods.fml.common.FMLLog; public class KCauldron { public static final ThreadGroup sKCauldronThreadGroup = new ThreadGroup("KCauldron"); - + public static final String name="KCauldronX"; private static boolean sManifestParsed = false; private static void parseManifest() { diff --git a/src/main/java/org/bstats/BMetrics.java b/src/main/java/kcauldronx/BMetrics.java similarity index 86% rename from src/main/java/org/bstats/BMetrics.java rename to src/main/java/kcauldronx/BMetrics.java index 6a1218f..1dd6980 100644 --- a/src/main/java/org/bstats/BMetrics.java +++ b/src/main/java/kcauldronx/BMetrics.java @@ -1,14 +1,13 @@ -package org.bstats; +package kcauldronx; -import org.bukkit.plugin.java.JavaPlugin; +import cpw.mods.fml.common.FMLCommonHandler; +import kcauldron.KCauldron; import org.json.simple.JSONArray; import org.bukkit.Bukkit; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.ServicePriority; -import org.bukkit.plugin.java.JavaPlugin; -import org.json.simple.JSONArray; import org.json.simple.JSONObject; import javax.net.ssl.HttpsURLConnection; @@ -62,26 +61,33 @@ public class BMetrics { // The uuid of the server private static String serverUUID; - - // The plugin - private final JavaPlugin plugin; - // A list with all custom charts private final List charts = new ArrayList<>(); + /** + * Gets the File object of the config file that should be used to store data such as the GUID and opt-out status + * + * @return the File object for the config file + */ + public File getConfigFile() { + // I believe the easiest way to get the base folder (e.g craftbukkit set via -P) for plugins to use + // is to abuse the plugin object we already have + // plugin.getDataFolder() => base/plugins/PluginA/ + // pluginsFolder => base/plugins/ + // The base is not necessarily relative to the startup directory. + // File pluginsFolder = plugin.getDataFolder().getParentFile(); + + // return => base/plugins/PluginMetrics/config.yml + return new File(new File((File) net.minecraft.server.MinecraftServer.getServer().options.valueOf("plugins"), "PluginMetrics"), "config.yml"); + } /** * Class constructor. * * @param plugin The plugin which stats should be submitted. */ - public BMetrics(JavaPlugin plugin) { - if (plugin == null) { - throw new IllegalArgumentException("Plugin cannot be null!"); - } - this.plugin = plugin; - + public BMetrics() { // Get the config file - File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); + File bStatsFolder = new File(getConfigFile(), "bStats"); File configFile = new File(bStatsFolder, "config.yml"); YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); @@ -111,21 +117,8 @@ public class BMetrics { serverUUID = config.getString("serverUuid"); logFailedRequests = config.getBoolean("logFailedRequests", false); if (config.getBoolean("enabled", true)) { - boolean found = false; - // Search for all other bStats Metrics classes to see if we are the first one - for (Class service : Bukkit.getServicesManager().getKnownServices()) { - try { - service.getField("B_STATS_VERSION"); // Our identifier :) - found = true; // We aren't the first - break; - } catch (NoSuchFieldException ignored) { } - } - // Register our service - Bukkit.getServicesManager().register(BMetrics.class, this, plugin, ServicePriority.Normal); - if (!found) { - // We are the first! - startSubmitting(); - } + Bukkit.getServicesManager().getKnownServices().add(BMetrics.class); + startSubmitting(); } } @@ -149,13 +142,9 @@ public class BMetrics { timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { - if (!plugin.isEnabled()) { // Plugin was disabled - timer.cancel(); - return; - } // Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler // Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;) - Bukkit.getScheduler().runTask(plugin, new Runnable() { + FMLCommonHandler.instance().getMinecraftServerInstance().processQueue.add(new Runnable() { @Override public void run() { submitData(); @@ -177,8 +166,8 @@ public class BMetrics { public JSONObject getPluginData() { JSONObject data = new JSONObject(); - String pluginName = plugin.getDescription().getName(); - String pluginVersion = plugin.getDescription().getVersion(); + String pluginName = KCauldron.name; + String pluginVersion = KCauldron.getCurrentVersion(); data.put("pluginName", pluginName); // Append the name of the plugin data.put("pluginVersion", pluginVersion); // Append the version of the plugin @@ -203,17 +192,7 @@ public class BMetrics { */ private JSONObject getServerData() { // Minecraft specific data - int playerAmount; - try { - // Around MC 1.8 the return type was changed to a collection from an array, - // This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; - Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); - playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class) - ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() - : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; - } catch (Exception e) { - playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed - } + int playerAmount=Bukkit.getServer().getOnlinePlayers().size(); int onlineMode = Bukkit.getOnlineMode() ? 1 : 0; String bukkitVersion = org.bukkit.Bukkit.getVersion(); bukkitVersion = bukkitVersion.substring(bukkitVersion.indexOf("MC: ") + 4, bukkitVersion.length() - 1); @@ -247,23 +226,8 @@ public class BMetrics { */ private void submitData() { final JSONObject data = getServerData(); - JSONArray pluginData = new JSONArray(); - // Search for all other bStats Metrics classes to get their plugin data - for (Class service : Bukkit.getServicesManager().getKnownServices()) { - try { - service.getField("B_STATS_VERSION"); // Our identifier :) - - for (RegisteredServiceProvider provider : Bukkit.getServicesManager().getRegistrations(service)) { - try { - pluginData.add(provider.getService().getMethod("getPluginData").invoke(provider.getProvider())); - } catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { } - } - } catch (NoSuchFieldException ignored) { } - } - data.put("plugins", pluginData); - // Create a new thread for the connection to the bStats server new Thread(new Runnable() { @Override @@ -272,10 +236,6 @@ public class BMetrics { // Send the data sendData(data); } catch (Exception e) { - // Something went wrong! :( - if (logFailedRequests) { - plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e); - } } } }).start(); diff --git a/src/main/java/pw/yumc/KCXStatistics.java b/src/main/java/kcauldronx/YUMCStatistics.java similarity index 97% rename from src/main/java/pw/yumc/KCXStatistics.java rename to src/main/java/kcauldronx/YUMCStatistics.java index f97029f..f977d36 100644 --- a/src/main/java/pw/yumc/KCXStatistics.java +++ b/src/main/java/kcauldronx/YUMCStatistics.java @@ -1,7 +1,7 @@ /* * Copyright 2011-2015 喵♂呜. All rights reserved. */ -package pw.yumc; +package kcauldronx; import java.io.BufferedReader; import java.io.File; @@ -43,7 +43,7 @@ import org.json.simple.JSONValue; * @since 2015年12月14日 下午1:36:42 * @author 喵♂呜 */ -public class KCXStatistics { +public class YUMCStatistics { /** * 统计系统版本 */ @@ -85,7 +85,7 @@ public class KCXStatistics { * @throws IOException * IO异常 */ - public KCXStatistics() { + public YUMCStatistics() { try { if (!configfile.exists()) { configfile.getParentFile().mkdirs(); @@ -179,7 +179,7 @@ public class KCXStatistics { */ public void print(final String msg) { if (debug) { - System.out.println("[KCXStatistics] " + msg); + System.out.println("[YUMCStatistics] " + msg); } } @@ -215,7 +215,7 @@ public class KCXStatistics { */ private void postPlugin() throws IOException { // 服务器数据获取 - final String pluginname = "KCauldronX"; + final String pluginname = KCauldron.name; final String tmposarch = System.getProperty("os.arch"); final Map data = new HashMap(); diff --git a/src/main/java/org/spigotmc/Metrics.java b/src/main/java/org/spigotmc/Metrics.java index 1dc2852..fb4d1bc 100644 --- a/src/main/java/org/spigotmc/Metrics.java +++ b/src/main/java/org/spigotmc/Metrics.java @@ -28,6 +28,7 @@ package org.spigotmc; import kcauldron.KCauldron; +import net.minecraft.server.MinecraftServer; import org.bukkit.Bukkit; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.InvalidConfigurationException; @@ -336,7 +337,7 @@ public class Metrics { // File pluginsFolder = plugin.getDataFolder().getParentFile(); // return => base/plugins/PluginMetrics/config.yml - return new File(new File((File) net.minecraft.server.MinecraftServer.getServer().options.valueOf("plugins"), "PluginMetrics"), "config.yml"); + return new File(new File((File) MinecraftServer.getServer().options.valueOf("plugins"), "PluginMetrics"), "config.yml"); } /** @@ -344,7 +345,7 @@ public class Metrics { */ private void postPlugin(final boolean isPing) throws IOException { // Server software specific section - String pluginName = "KCauldronX"; + String pluginName = KCauldron.name; boolean onlineMode = Bukkit.getServer().getOnlineMode(); // TRUE if online mode is enabled String pluginVersion = (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown"; String serverVersion = KCauldron.getCurrentVersion(); diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java index 904ddf5..f30cd04 100644 --- a/src/main/java/org/spigotmc/SpigotConfig.java +++ b/src/main/java/org/spigotmc/SpigotConfig.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Map; import java.util.logging.Level; +import kcauldronx.BMetrics; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.Command; @@ -20,7 +21,6 @@ import com.google.common.base.Throwables; import gnu.trove.map.hash.TObjectIntHashMap; import net.minecraft.server.MinecraftServer; -import pw.yumc.KCXStatistics; public class SpigotConfig { @@ -43,6 +43,7 @@ public class SpigotConfig static Map commands; /*========================================================================*/ private static Metrics metrics; + private static BMetrics bstats; public static void init() { @@ -75,6 +76,17 @@ public class SpigotConfig Bukkit.getServer().getLogger().log( Level.SEVERE, "Could not start metrics service", ex ); } } + if ( bstats == null ) + { + try + { + bstats = new BMetrics(); + bstats.start(); + } catch ( IOException ex ) + { + Bukkit.getServer().getLogger().log( Level.SEVERE, "Could not start bstats metrics service", ex ); + } + } } static void readConfig(Class clazz, Object instance) From 2e9c649e3db87ab82a2a49e41771c603bdf811bd Mon Sep 17 00:00:00 2001 From: xjboss Date: Sun, 30 Jul 2017 10:24:36 +0800 Subject: [PATCH 3/5] Remove diamond operator --- src/main/java/kcauldronx/BMetrics.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/kcauldronx/BMetrics.java b/src/main/java/kcauldronx/BMetrics.java index 1dd6980..9760ea3 100644 --- a/src/main/java/kcauldronx/BMetrics.java +++ b/src/main/java/kcauldronx/BMetrics.java @@ -62,7 +62,7 @@ public class BMetrics { // The uuid of the server private static String serverUUID; // A list with all custom charts - private final List charts = new ArrayList<>(); + private final List charts = new ArrayList(); /** * Gets the File object of the config file that should be used to store data such as the GUID and opt-out status From c91798691bba453c41400c61d68b8ec0f3d1421e Mon Sep 17 00:00:00 2001 From: xjboss Date: Sun, 30 Jul 2017 10:27:22 +0800 Subject: [PATCH 4/5] fix --- src/main/java/org/spigotmc/SpigotConfig.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java index f30cd04..bfe70e0 100644 --- a/src/main/java/org/spigotmc/SpigotConfig.java +++ b/src/main/java/org/spigotmc/SpigotConfig.java @@ -78,14 +78,7 @@ public class SpigotConfig } if ( bstats == null ) { - try - { - bstats = new BMetrics(); - bstats.start(); - } catch ( IOException ex ) - { - Bukkit.getServer().getLogger().log( Level.SEVERE, "Could not start bstats metrics service", ex ); - } + bstats = new BMetrics(); } } From cd7c75d1ed0fab11fe3b7da7c5f09c1ca0c6a7ff Mon Sep 17 00:00:00 2001 From: xjboss Date: Sun, 30 Jul 2017 10:42:46 +0800 Subject: [PATCH 5/5] fix --- src/main/java/kcauldronx/BMetrics.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/kcauldronx/BMetrics.java b/src/main/java/kcauldronx/BMetrics.java index 9760ea3..52f67ed 100644 --- a/src/main/java/kcauldronx/BMetrics.java +++ b/src/main/java/kcauldronx/BMetrics.java @@ -117,7 +117,6 @@ public class BMetrics { serverUUID = config.getString("serverUuid"); logFailedRequests = config.getBoolean("logFailedRequests", false); if (config.getBoolean("enabled", true)) { - Bukkit.getServicesManager().getKnownServices().add(BMetrics.class); startSubmitting(); } }