From f791c08cc084e28fb4a1bf603852da511e065286 Mon Sep 17 00:00:00 2001 From: MrFawkes1337 <80531493+MrFawkes1337@users.noreply.github.com> Date: Sun, 6 Mar 2022 19:29:13 +0000 Subject: [PATCH] Update MetricsLite Updates MetricsLite to the latest Version. --- .../java/org/dynmap/mobs/MetricsLite.java | 288 ++++++++++++++---- target/classes/8x8/vanillagoat.png | Bin 0 -> 324 bytes target/classes/dynmap-mobs - Shortcut.lnk | Bin 0 -> 1024 bytes target/classes/vanillagoat.png | Bin 0 -> 778 bytes 4 files changed, 235 insertions(+), 53 deletions(-) create mode 100644 target/classes/8x8/vanillagoat.png create mode 100644 target/classes/dynmap-mobs - Shortcut.lnk create mode 100644 target/classes/vanillagoat.png diff --git a/src/main/java/org/dynmap/mobs/MetricsLite.java b/src/main/java/org/dynmap/mobs/MetricsLite.java index 013f4db..f6533d8 100644 --- a/src/main/java/org/dynmap/mobs/MetricsLite.java +++ b/src/main/java/org/dynmap/mobs/MetricsLite.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 Tyler Blair. All rights reserved. + * Copyright 2011-2013 Tyler Blair. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -29,45 +29,52 @@ package org.dynmap.mobs; import org.bukkit.Bukkit; +import org.bukkit.Server; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.scheduler.BukkitTask; import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; -import java.io.OutputStreamWriter; +import java.io.OutputStream; import java.io.UnsupportedEncodingException; +import java.lang.reflect.Method; import java.net.Proxy; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; +import java.util.Collection; import java.util.UUID; import java.util.logging.Level; +import java.util.zip.GZIPOutputStream; public class MetricsLite { /** * The current revision number */ - private final static int REVISION = 5; + private final static int REVISION = 7; /** * The base url of the metrics domain */ - private static final String BASE_URL = "http://mcstats.org"; + private static final String BASE_URL = "http://report.mcstats.org"; /** * The url used to report a server's status */ - private static final String REPORT_URL = "/report/%s"; + private static final String REPORT_URL = "/plugin/%s"; /** * Interval of time to ping (in minutes) */ - private final static int PING_INTERVAL = 10; + private final static int PING_INTERVAL = 15; /** * The plugin this metrics submits for @@ -89,6 +96,11 @@ public class MetricsLite { */ private final String guid; + /** + * Debug mode + */ + private final boolean debug; + /** * Lock for synchronization */ @@ -97,7 +109,7 @@ public class MetricsLite { /** * Id of the scheduled task */ - private volatile int taskId = -1; + private volatile BukkitTask task = null; public MetricsLite(Plugin plugin) throws IOException { if (plugin == null) { @@ -113,6 +125,7 @@ public MetricsLite(Plugin plugin) throws IOException { // add some defaults configuration.addDefault("opt-out", false); configuration.addDefault("guid", UUID.randomUUID().toString()); + configuration.addDefault("debug", false); // Do we need to create the file? if (configuration.get("guid", null) == null) { @@ -122,6 +135,7 @@ public MetricsLite(Plugin plugin) throws IOException { // Load the guid then guid = configuration.getString("guid"); + debug = configuration.getBoolean("debug", false); } /** @@ -139,12 +153,12 @@ public boolean start() { } // Is metrics already running? - if (taskId >= 0) { + if (task != null) { return true; } // Begin hitting the server with glorious data - taskId = plugin.getServer().getScheduler().scheduleAsyncRepeatingTask(plugin, new Runnable() { + task = plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, new Runnable() { private boolean firstPost = true; @@ -153,9 +167,9 @@ public void run() { // This has to be synchronized or it can collide with the disable method. synchronized (optOutLock) { // Disable Task, if it is running and the server owner decided to opt-out - if (isOptOut() && taskId > 0) { - plugin.getServer().getScheduler().cancelTask(taskId); - taskId = -1; + if (isOptOut() && task != null) { + task.cancel(); + task = null; } } @@ -168,7 +182,9 @@ public void run() { // Each post thereafter will be a ping firstPost = false; } catch (IOException e) { - //Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage()); + if (debug) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage()); + } } } }, 0, PING_INTERVAL * 1200); @@ -183,15 +199,19 @@ public void run() { * @return true if metrics should be opted out of it */ public boolean isOptOut() { - synchronized(optOutLock) { + synchronized (optOutLock) { try { // Reload the metrics file configuration.load(getConfigFile()); } catch (IOException ex) { - Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + if (debug) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + } return true; } catch (InvalidConfigurationException ex) { - Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + if (debug) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + } return true; } return configuration.getBoolean("opt-out", false); @@ -201,7 +221,7 @@ public boolean isOptOut() { /** * Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task. * - * @throws IOException + * @throws java.io.IOException */ public void enable() throws IOException { // This has to be synchronized or it can collide with the check in the task. @@ -213,7 +233,7 @@ public void enable() throws IOException { } // Enable Task, if it is not running - if (taskId < 0) { + if (task == null) { start(); } } @@ -222,7 +242,7 @@ public void enable() throws IOException { /** * Disables metrics for the server by setting "opt-out" to true in the config file and canceling the metrics task. * - * @throws IOException + * @throws java.io.IOException */ public void disable() throws IOException { // This has to be synchronized or it can collide with the check in the task. @@ -234,9 +254,9 @@ public void disable() throws IOException { } // Disable Task, if it is running - if (taskId > 0) { - this.plugin.getServer().getScheduler().cancelTask(taskId); - taskId = -1; + if (task != null) { + task.cancel(); + task = null; } } } @@ -257,29 +277,82 @@ public File getConfigFile() { // return => base/plugins/PluginMetrics/config.yml return new File(new File(pluginsFolder, "PluginMetrics"), "config.yml"); } + + /** + * Gets the online player (backwards compatibility) + * + * @return online player amount + */ + private int getOnlinePlayers() { + try { + Method onlinePlayerMethod = Server.class.getMethod("getOnlinePlayers"); + if(onlinePlayerMethod.getReturnType().equals(Collection.class)) { + return ((Collection)onlinePlayerMethod.invoke(Bukkit.getServer())).size(); + } else { + return ((Player[])onlinePlayerMethod.invoke(Bukkit.getServer())).length; + } + } catch (Exception ex) { + if (debug) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + } + } + + return 0; + } /** * Generic method that posts a plugin to the metrics website */ private void postPlugin(boolean isPing) throws IOException { - // The plugin's description file containg all of the plugin data such as name, version, author, etc - final PluginDescriptionFile description = plugin.getDescription(); + // Server software specific section + PluginDescriptionFile description = plugin.getDescription(); + String pluginName = description.getName(); + boolean onlineMode = Bukkit.getServer().getOnlineMode(); // TRUE if online mode is enabled + String pluginVersion = description.getVersion(); + String serverVersion = Bukkit.getVersion(); + int playersOnline = this.getOnlinePlayers(); + + // END server software specific section -- all code below does not use any code outside of this class / Java // Construct the post data - final StringBuilder data = new StringBuilder(); - data.append(encode("guid")).append('=').append(encode(guid)); - encodeDataPair(data, "version", description.getVersion()); - encodeDataPair(data, "server", Bukkit.getVersion()); - encodeDataPair(data, "players", Integer.toString(Bukkit.getServer().getOnlinePlayers().size())); - encodeDataPair(data, "revision", String.valueOf(REVISION)); + StringBuilder json = new StringBuilder(1024); + json.append('{'); + + // The plugin's description file containg all of the plugin data such as name, version, author, etc + appendJSONPair(json, "guid", guid); + appendJSONPair(json, "plugin_version", pluginVersion); + appendJSONPair(json, "server_version", serverVersion); + appendJSONPair(json, "players_online", Integer.toString(playersOnline)); + + // New data as of R6 + String osname = System.getProperty("os.name"); + String osarch = System.getProperty("os.arch"); + String osversion = System.getProperty("os.version"); + String java_version = System.getProperty("java.version"); + int coreCount = Runtime.getRuntime().availableProcessors(); + + // normalize os arch .. amd64 -> x86_64 + if (osarch.equals("amd64")) { + osarch = "x86_64"; + } + + appendJSONPair(json, "osname", osname); + appendJSONPair(json, "osarch", osarch); + appendJSONPair(json, "osversion", osversion); + appendJSONPair(json, "cores", Integer.toString(coreCount)); + appendJSONPair(json, "auth_mode", onlineMode ? "1" : "0"); + appendJSONPair(json, "java_version", java_version); // If we're pinging, append it if (isPing) { - encodeDataPair(data, "ping", "true"); + appendJSONPair(json, "ping", "1"); } + // close json + json.append('}'); + // Create the url - URL url = new URL(BASE_URL + String.format(REPORT_URL, encode(plugin.getDescription().getName()))); + URL url = new URL(BASE_URL + String.format(REPORT_URL, urlEncode(pluginName))); // Connect to the website URLConnection connection; @@ -292,25 +365,71 @@ private void postPlugin(boolean isPing) throws IOException { connection = url.openConnection(); } + + byte[] uncompressed = json.toString().getBytes(); + byte[] compressed = gzip(json.toString()); + + // Headers + connection.addRequestProperty("User-Agent", "MCStats/" + REVISION); + connection.addRequestProperty("Content-Type", "application/json"); + connection.addRequestProperty("Content-Encoding", "gzip"); + connection.addRequestProperty("Content-Length", Integer.toString(compressed.length)); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + connection.setDoOutput(true); + if (debug) { + System.out.println("[Metrics] Prepared request for " + pluginName + " uncompressed=" + uncompressed.length + " compressed=" + compressed.length); + } + // Write the data - final OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream()); - writer.write(data.toString()); - writer.flush(); + OutputStream os = connection.getOutputStream(); + os.write(compressed); + os.flush(); // Now read the response final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); - final String response = reader.readLine(); + String response = reader.readLine(); // close resources - writer.close(); + os.close(); reader.close(); - if (response == null || response.startsWith("ERR")) { - throw new IOException(response); //Throw the exception + if (response == null || response.startsWith("ERR") || response.startsWith("7")) { + if (response == null) { + response = "null"; + } else if (response.startsWith("7")) { + response = response.substring(response.startsWith("7,") ? 2 : 1); + } + + throw new IOException(response); } - //if (response.startsWith("OK")) - We should get "OK" followed by an optional description if everything goes right + } + + /** + * GZip compress a string of bytes + * + * @param input + * @return + */ + public static byte[] gzip(String input) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream gzos = null; + + try { + gzos = new GZIPOutputStream(baos); + gzos.write(input.getBytes("UTF-8")); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (gzos != null) try { + gzos.close(); + } catch (IOException ignore) { + } + } + + return baos.toByteArray(); } /** @@ -328,20 +447,83 @@ private boolean isMineshafterPresent() { } /** - *

Encode a key/value data pair to be used in a HTTP post request. This INCLUDES a & so the first - * key/value pair MUST be included manually, e.g:

- * - * StringBuffer data = new StringBuffer(); - * data.append(encode("guid")).append('=').append(encode(guid)); - * encodeDataPair(data, "version", description.getVersion()); - * + * Appends a json encoded key/value pair to the given string builder. + * + * @param json + * @param key + * @param value + * @throws UnsupportedEncodingException + */ + private static void appendJSONPair(StringBuilder json, String key, String value) throws UnsupportedEncodingException { + boolean isValueNumeric = false; + + try { + if (value.equals("0") || !value.endsWith("0")) { + Double.parseDouble(value); + isValueNumeric = true; + } + } catch (NumberFormatException e) { + isValueNumeric = false; + } + + if (json.charAt(json.length() - 1) != '{') { + json.append(','); + } + + json.append(escapeJSON(key)); + json.append(':'); + + if (isValueNumeric) { + json.append(value); + } else { + json.append(escapeJSON(value)); + } + } + + /** + * Escape a string to create a valid JSON string * - * @param buffer the stringbuilder to append the data pair onto - * @param key the key value - * @param value the value + * @param text + * @return */ - private static void encodeDataPair(final StringBuilder buffer, final String key, final String value) throws UnsupportedEncodingException { - buffer.append('&').append(encode(key)).append('=').append(encode(value)); + private static String escapeJSON(String text) { + StringBuilder builder = new StringBuilder(); + + builder.append('"'); + for (int index = 0; index < text.length(); index++) { + char chr = text.charAt(index); + + switch (chr) { + case '"': + case '\\': + builder.append('\\'); + builder.append(chr); + break; + case '\b': + builder.append("\\b"); + break; + case '\t': + builder.append("\\t"); + break; + case '\n': + builder.append("\\n"); + break; + case '\r': + builder.append("\\r"); + break; + default: + if (chr < ' ') { + String t = "000" + Integer.toHexString(chr); + builder.append("\\u" + t.substring(t.length() - 4)); + } else { + builder.append(chr); + } + break; + } + } + builder.append('"'); + + return builder.toString(); } /** @@ -350,7 +532,7 @@ private static void encodeDataPair(final StringBuilder buffer, final String key, * @param text the text to encode * @return the encoded text, as UTF-8 */ - private static String encode(final String text) throws UnsupportedEncodingException { + private static String urlEncode(final String text) throws UnsupportedEncodingException { return URLEncoder.encode(text, "UTF-8"); } diff --git a/target/classes/8x8/vanillagoat.png b/target/classes/8x8/vanillagoat.png new file mode 100644 index 0000000000000000000000000000000000000000..74f4643f2d79b8ef069bdd4f9652608f5542cb3f GIT binary patch literal 324 zcmV-K0lWT*P)1}ZYDaZFkwi*n5ct4VUXXz?~tF69Vf#C$Uwk`!P&tC5fW%?U!g#& zE0$ClJmTOEJf`Y?OuP zNlnvuYBca&hucz_neEbQxAEozO;73Y^G&q!NbjZT4_+AzOs3N*Cz&kj=V-!cY|-iV zh@uG7G?6G6y9UW_6&$vB2#5KklsR)V!}Q!M0BbEi2L?uhf^8A&~fWsR9l zhJ`=ykgc-``MUF)pvJX*HYNl*(v|qOhZ<*grU=(|IP7&2V)555O-y Wl8#`4I>|Bs0000Qx zB^pgwvUZZ1H29tym@FY7uG+RoaKuXEs+_s)?;EfuobaoX@qcWpDvR=*AFIan-%OHb zAg(5ZAoQ8Yc%s{&o$y^~xAru3%cd9Vn+4z&(9EMua7P@wHa{~>sye#v?%0qWS2;e+ zKYv*ghnAa46WU()u|5siq^P~6o@h6vQ7X9O)!re$yF@Y~Ch$1}c10q3(PZ4ZO*WD! zN_J`?P)SR*zcqdC+(r#q4p#2p>pZ@d5Rp{nTz>b@0IP=F++ko*F!5!0+)7?*qYwqD zj$|sv9)`LCuMY45YXRo+UIy^;0(|Bwe`t%UdBWy!M5&V^dY8*WW|LT>_#%p^$WlZS zEn!bf&=-m-VzP13+ZGDe$;H8NJu<1uh@ZH`)+VL<4`Aoz^(jr!uw2|6C<`2I2-6co z`-3vJc;v)vrjnsGGg~J@eX>J9fzWAoef+1o}xM>Tia2smtk_79PGc(^=c_ z{Med7#h+ipIXQ72kPj@Kyq*flOePdugE{#q0xJSCUMB*GWWfwQc%>k`A-tGEMJ$HZ z3vfbVcGJBXTdwBnY)Wm{m8t9OPv-zmxDr_IQL^D%4NwIeSEs=LX=bPLd4X+38382M zlOLr7pvGkqF?$sURsgRrolHLuHlI%eT-VSMPhok_c;=lqG+OmmHzwhJJqNJz7T|vy z7ylZaxNKjXV<*5xjA(lbhL)-7nP_GPjH4K*)*y|RWMjcL6_tXZB2sWy5G|!3h%SVxtFGdvt_tl& z|Al}>#VkUb8Wiy(#!@jT~!&-*^ew^)`X zuYP}3PEAe8|I4a5pD)PFw)f@m(6GERIw~E zdJ`$4(Ks!u#j9+V?A)I`n|;du0|(Ik${RMUr(DcIGJ%=PGO}lo`m#lDQV{Kl^W@QE z{>l|G7fNWVN>5LmSS*GvrJ!C}BpeRYtXCMjF~)Lzfs$WgAf8;QB)GiG-z;dasmgPjp=P@s$_clA^H3~sZTDTDXo&yKT|GInChUZuFtyA^bD?-DSF-0@7h?|1LuBg>YRPMNx3Q zHl39Qj%!nIEOF+{1-1`-$ZAMHP@aFz$fx^Br?;})s0G9EJcmDKr>WHz_+;;Aq*9yE z!;!U+_#8ZRm=j-~X2;HtaGfrl)-ugzl}62EWZ&oXZO$+=^MnTv9#XB=0#6VU|I%7% z5RI>AV*C!xMwR}|0KeXyz%UHH{^o2TzJC20nx=95_!q&hgOK?6Mjxx&;g`EpeC!HZ zjRvg6pHrt!ul@4hZNKBl(PNyya2dz-criD}2Ln6*EBOcDZ#1x$aj7U+F#rGn07*qo IM6N<$g3U#IIRF3v literal 0 HcmV?d00001