From 861644566df7f379dd9f252220d909324b160575 Mon Sep 17 00:00:00 2001 From: okx-code Date: Tue, 27 Nov 2018 17:14:11 +0000 Subject: [PATCH] add locales --- build.gradle | 2 +- src/main/java/sh/okx/rankup/Metrics.java | 729 ++++++------------ src/main/java/sh/okx/rankup/Rankup.java | 76 +- .../java/sh/okx/rankup/messages/Variable.java | 11 +- .../requirement/PlaceholderRequirement.java | 29 +- src/main/resources/config.yml | 8 +- .../resources/{messages.yml => locale/en.yml} | 4 +- src/main/resources/plugin.yml | 2 +- 8 files changed, 313 insertions(+), 548 deletions(-) rename src/main/resources/{messages.yml => locale/en.yml} (94%) diff --git a/build.gradle b/build.gradle index 64d61fb..8b626ef 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group 'sh.okx' -version '3.3.1' +version '3.3.2-beta' repositories { mavenCentral() diff --git a/src/main/java/sh/okx/rankup/Metrics.java b/src/main/java/sh/okx/rankup/Metrics.java index 29075ff..ed89c6e 100644 --- a/src/main/java/sh/okx/rankup/Metrics.java +++ b/src/main/java/sh/okx/rankup/Metrics.java @@ -2,26 +2,21 @@ package sh.okx.rankup; import org.bukkit.Bukkit; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +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.io.*; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Timer; -import java.util.TimerTask; -import java.util.UUID; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.Callable; import java.util.logging.Level; import java.util.zip.GZIPOutputStream; @@ -30,22 +25,46 @@ import java.util.zip.GZIPOutputStream; *

* Check out https://bStats.org/ to learn more about bStats! */ +@SuppressWarnings({"WeakerAccess", "unused"}) public class Metrics { + 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 (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.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"; + private static final String URL = "https://bStats.org/submitData/bukkit"; + + // Is bStats enabled on this server? + private boolean enabled; // Should failed requests be logged? private static boolean logFailedRequests; + // Should the sent data be logged? + private static boolean logSentData; + + // Should the response text be logged? + private static boolean logResponseStatusText; + // The uuid of the server private static String serverUUID; // The plugin - private final JavaPlugin plugin; + private final Plugin plugin; // A list with all custom charts private final List charts = new ArrayList<>(); @@ -55,7 +74,7 @@ public class Metrics { * * @param plugin The plugin which stats should be submitted. */ - public Metrics(JavaPlugin plugin) { + public Metrics(Plugin plugin) { if (plugin == null) { throw new IllegalArgumentException("Plugin cannot be null!"); } @@ -75,6 +94,10 @@ public class Metrics { config.addDefault("serverUuid", UUID.randomUUID().toString()); // Should failed request be logged? config.addDefault("logFailedRequests", false); + // Should the sent data be logged? + config.addDefault("logSentData", false); + // Should the response text be logged? + config.addDefault("logResponseStatusText", false); // Inform the server owners about bStats config.options().header( @@ -85,14 +108,17 @@ public class Metrics { ).copyDefaults(true); try { config.save(configFile); - } catch (IOException ignored) { - } + } catch (IOException ignored) { } } // Load the data + enabled = config.getBoolean("enabled", true); serverUUID = config.getString("serverUuid"); logFailedRequests = config.getBoolean("logFailedRequests", false); - if (config.getBoolean("enabled", true)) { + logSentData = config.getBoolean("logSentData", false); + logResponseStatusText = config.getBoolean("logResponseStatusText", false); + + if (enabled) { boolean found = false; // Search for all other bStats Metrics classes to see if we are the first one for (Class service : Bukkit.getServicesManager().getKnownServices()) { @@ -100,8 +126,7 @@ public class Metrics { service.getField("B_STATS_VERSION"); // Our identifier :) found = true; // We aren't the first break; - } catch (NoSuchFieldException ignored) { - } + } catch (NoSuchFieldException ignored) { } } // Register our service Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal); @@ -113,58 +138,12 @@ public class Metrics { } /** - * Sends the data to the bStats server. + * Checks if bStats is enabled. * - * @param data The data to send. - * @throws Exception If the request failed. + * @return Whether bStats is enabled or not. */ - 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(); + public boolean isEnabled() { + return enabled; } /** @@ -193,12 +172,7 @@ public class Metrics { } // 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(); - } - }); + Bukkit.getScheduler().runTask(plugin, () -> 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 @@ -241,10 +215,19 @@ public class Metrics { */ private JSONObject getServerData() { // Minecraft specific data - int playerAmount = Bukkit.getOnlinePlayers().size(); + 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); + String bukkitVersion = Bukkit.getVersion(); // OS/Java specific data String javaVersion = System.getProperty("java.version"); @@ -281,14 +264,13 @@ public class Metrics { for (Class service : Bukkit.getServicesManager().getKnownServices()) { try { service.getField("B_STATS_VERSION"); // Our identifier :) - } catch (NoSuchFieldException ignored) { - continue; // Continue "searching" - } - // Found one! - try { - pluginData.add(service.getMethod("getPluginData").invoke(Bukkit.getServicesManager().load(service))); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { - } + + 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); @@ -299,7 +281,7 @@ public class Metrics { public void run() { try { // Send the data - sendData(data); + sendData(plugin, data); } catch (Exception e) { // Something went wrong! :( if (logFailedRequests) { @@ -308,322 +290,76 @@ public class Metrics { } } }).start(); - } /** - * A enum which is used for custom maps. + * Sends the data to the bStats server. + * + * @param plugin Any plugin. It's just used to get a logger instance. + * @param data The data to send. + * @throws Exception If the request failed. */ - public enum Country { - - /** - * bStats will use the country of the server. - */ - AUTO_DETECT("AUTO", "Auto Detected"), - - ANDORRA("AD", "Andorra"), - UNITED_ARAB_EMIRATES("AE", "United Arab Emirates"), - AFGHANISTAN("AF", "Afghanistan"), - ANTIGUA_AND_BARBUDA("AG", "Antigua and Barbuda"), - ANGUILLA("AI", "Anguilla"), - ALBANIA("AL", "Albania"), - ARMENIA("AM", "Armenia"), - NETHERLANDS_ANTILLES("AN", "Netherlands Antilles"), - ANGOLA("AO", "Angola"), - ANTARCTICA("AQ", "Antarctica"), - ARGENTINA("AR", "Argentina"), - AMERICAN_SAMOA("AS", "American Samoa"), - AUSTRIA("AT", "Austria"), - AUSTRALIA("AU", "Australia"), - ARUBA("AW", "Aruba"), - ÅLAND_ISLANDS("AX", "Åland Islands"), - AZERBAIJAN("AZ", "Azerbaijan"), - BOSNIA_AND_HERZEGOVINA("BA", "Bosnia and Herzegovina"), - BARBADOS("BB", "Barbados"), - BANGLADESH("BD", "Bangladesh"), - BELGIUM("BE", "Belgium"), - BURKINA_FASO("BF", "Burkina Faso"), - BULGARIA("BG", "Bulgaria"), - BAHRAIN("BH", "Bahrain"), - BURUNDI("BI", "Burundi"), - BENIN("BJ", "Benin"), - SAINT_BARTHÉLEMY("BL", "Saint Barthélemy"), - BERMUDA("BM", "Bermuda"), - BRUNEI("BN", "Brunei"), - BOLIVIA("BO", "Bolivia"), - BONAIRE_SINT_EUSTATIUS_AND_SABA("BQ", "Bonaire, Sint Eustatius and Saba"), - BRAZIL("BR", "Brazil"), - BAHAMAS("BS", "Bahamas"), - BHUTAN("BT", "Bhutan"), - BOUVET_ISLAND("BV", "Bouvet Island"), - BOTSWANA("BW", "Botswana"), - BELARUS("BY", "Belarus"), - BELIZE("BZ", "Belize"), - CANADA("CA", "Canada"), - COCOS_ISLANDS("CC", "Cocos Islands"), - THE_DEMOCRATIC_REPUBLIC_OF_CONGO("CD", "The Democratic Republic Of Congo"), - CENTRAL_AFRICAN_REPUBLIC("CF", "Central African Republic"), - CONGO("CG", "Congo"), - SWITZERLAND("CH", "Switzerland"), - CÔTE_D_IVOIRE("CI", "Côte d'Ivoire"), - COOK_ISLANDS("CK", "Cook Islands"), - CHILE("CL", "Chile"), - CAMEROON("CM", "Cameroon"), - CHINA("CN", "China"), - COLOMBIA("CO", "Colombia"), - COSTA_RICA("CR", "Costa Rica"), - CUBA("CU", "Cuba"), - CAPE_VERDE("CV", "Cape Verde"), - CURAÇAO("CW", "Curaçao"), - CHRISTMAS_ISLAND("CX", "Christmas Island"), - CYPRUS("CY", "Cyprus"), - CZECH_REPUBLIC("CZ", "Czech Republic"), - GERMANY("DE", "Germany"), - DJIBOUTI("DJ", "Djibouti"), - DENMARK("DK", "Denmark"), - DOMINICA("DM", "Dominica"), - DOMINICAN_REPUBLIC("DO", "Dominican Republic"), - ALGERIA("DZ", "Algeria"), - ECUADOR("EC", "Ecuador"), - ESTONIA("EE", "Estonia"), - EGYPT("EG", "Egypt"), - WESTERN_SAHARA("EH", "Western Sahara"), - ERITREA("ER", "Eritrea"), - SPAIN("ES", "Spain"), - ETHIOPIA("ET", "Ethiopia"), - FINLAND("FI", "Finland"), - FIJI("FJ", "Fiji"), - FALKLAND_ISLANDS("FK", "Falkland Islands"), - MICRONESIA("FM", "Micronesia"), - FAROE_ISLANDS("FO", "Faroe Islands"), - FRANCE("FR", "France"), - GABON("GA", "Gabon"), - UNITED_KINGDOM("GB", "United Kingdom"), - GRENADA("GD", "Grenada"), - GEORGIA("GE", "Georgia"), - FRENCH_GUIANA("GF", "French Guiana"), - GUERNSEY("GG", "Guernsey"), - GHANA("GH", "Ghana"), - GIBRALTAR("GI", "Gibraltar"), - GREENLAND("GL", "Greenland"), - GAMBIA("GM", "Gambia"), - GUINEA("GN", "Guinea"), - GUADELOUPE("GP", "Guadeloupe"), - EQUATORIAL_GUINEA("GQ", "Equatorial Guinea"), - GREECE("GR", "Greece"), - SOUTH_GEORGIA_AND_THE_SOUTH_SANDWICH_ISLANDS("GS", "South Georgia And The South Sandwich Islands"), - GUATEMALA("GT", "Guatemala"), - GUAM("GU", "Guam"), - GUINEA_BISSAU("GW", "Guinea-Bissau"), - GUYANA("GY", "Guyana"), - HONG_KONG("HK", "Hong Kong"), - HEARD_ISLAND_AND_MCDONALD_ISLANDS("HM", "Heard Island And McDonald Islands"), - HONDURAS("HN", "Honduras"), - CROATIA("HR", "Croatia"), - HAITI("HT", "Haiti"), - HUNGARY("HU", "Hungary"), - INDONESIA("ID", "Indonesia"), - IRELAND("IE", "Ireland"), - ISRAEL("IL", "Israel"), - ISLE_OF_MAN("IM", "Isle Of Man"), - INDIA("IN", "India"), - BRITISH_INDIAN_OCEAN_TERRITORY("IO", "British Indian Ocean Territory"), - IRAQ("IQ", "Iraq"), - IRAN("IR", "Iran"), - ICELAND("IS", "Iceland"), - ITALY("IT", "Italy"), - JERSEY("JE", "Jersey"), - JAMAICA("JM", "Jamaica"), - JORDAN("JO", "Jordan"), - JAPAN("JP", "Japan"), - KENYA("KE", "Kenya"), - KYRGYZSTAN("KG", "Kyrgyzstan"), - CAMBODIA("KH", "Cambodia"), - KIRIBATI("KI", "Kiribati"), - COMOROS("KM", "Comoros"), - SAINT_KITTS_AND_NEVIS("KN", "Saint Kitts And Nevis"), - NORTH_KOREA("KP", "North Korea"), - SOUTH_KOREA("KR", "South Korea"), - KUWAIT("KW", "Kuwait"), - CAYMAN_ISLANDS("KY", "Cayman Islands"), - KAZAKHSTAN("KZ", "Kazakhstan"), - LAOS("LA", "Laos"), - LEBANON("LB", "Lebanon"), - SAINT_LUCIA("LC", "Saint Lucia"), - LIECHTENSTEIN("LI", "Liechtenstein"), - SRI_LANKA("LK", "Sri Lanka"), - LIBERIA("LR", "Liberia"), - LESOTHO("LS", "Lesotho"), - LITHUANIA("LT", "Lithuania"), - LUXEMBOURG("LU", "Luxembourg"), - LATVIA("LV", "Latvia"), - LIBYA("LY", "Libya"), - MOROCCO("MA", "Morocco"), - MONACO("MC", "Monaco"), - MOLDOVA("MD", "Moldova"), - MONTENEGRO("ME", "Montenegro"), - SAINT_MARTIN("MF", "Saint Martin"), - MADAGASCAR("MG", "Madagascar"), - MARSHALL_ISLANDS("MH", "Marshall Islands"), - MACEDONIA("MK", "Macedonia"), - MALI("ML", "Mali"), - MYANMAR("MM", "Myanmar"), - MONGOLIA("MN", "Mongolia"), - MACAO("MO", "Macao"), - NORTHERN_MARIANA_ISLANDS("MP", "Northern Mariana Islands"), - MARTINIQUE("MQ", "Martinique"), - MAURITANIA("MR", "Mauritania"), - MONTSERRAT("MS", "Montserrat"), - MALTA("MT", "Malta"), - MAURITIUS("MU", "Mauritius"), - MALDIVES("MV", "Maldives"), - MALAWI("MW", "Malawi"), - MEXICO("MX", "Mexico"), - MALAYSIA("MY", "Malaysia"), - MOZAMBIQUE("MZ", "Mozambique"), - NAMIBIA("NA", "Namibia"), - NEW_CALEDONIA("NC", "New Caledonia"), - NIGER("NE", "Niger"), - NORFOLK_ISLAND("NF", "Norfolk Island"), - NIGERIA("NG", "Nigeria"), - NICARAGUA("NI", "Nicaragua"), - NETHERLANDS("NL", "Netherlands"), - NORWAY("NO", "Norway"), - NEPAL("NP", "Nepal"), - NAURU("NR", "Nauru"), - NIUE("NU", "Niue"), - NEW_ZEALAND("NZ", "New Zealand"), - OMAN("OM", "Oman"), - PANAMA("PA", "Panama"), - PERU("PE", "Peru"), - FRENCH_POLYNESIA("PF", "French Polynesia"), - PAPUA_NEW_GUINEA("PG", "Papua New Guinea"), - PHILIPPINES("PH", "Philippines"), - PAKISTAN("PK", "Pakistan"), - POLAND("PL", "Poland"), - SAINT_PIERRE_AND_MIQUELON("PM", "Saint Pierre And Miquelon"), - PITCAIRN("PN", "Pitcairn"), - PUERTO_RICO("PR", "Puerto Rico"), - PALESTINE("PS", "Palestine"), - PORTUGAL("PT", "Portugal"), - PALAU("PW", "Palau"), - PARAGUAY("PY", "Paraguay"), - QATAR("QA", "Qatar"), - REUNION("RE", "Reunion"), - ROMANIA("RO", "Romania"), - SERBIA("RS", "Serbia"), - RUSSIA("RU", "Russia"), - RWANDA("RW", "Rwanda"), - SAUDI_ARABIA("SA", "Saudi Arabia"), - SOLOMON_ISLANDS("SB", "Solomon Islands"), - SEYCHELLES("SC", "Seychelles"), - SUDAN("SD", "Sudan"), - SWEDEN("SE", "Sweden"), - SINGAPORE("SG", "Singapore"), - SAINT_HELENA("SH", "Saint Helena"), - SLOVENIA("SI", "Slovenia"), - SVALBARD_AND_JAN_MAYEN("SJ", "Svalbard And Jan Mayen"), - SLOVAKIA("SK", "Slovakia"), - SIERRA_LEONE("SL", "Sierra Leone"), - SAN_MARINO("SM", "San Marino"), - SENEGAL("SN", "Senegal"), - SOMALIA("SO", "Somalia"), - SURINAME("SR", "Suriname"), - SOUTH_SUDAN("SS", "South Sudan"), - SAO_TOME_AND_PRINCIPE("ST", "Sao Tome And Principe"), - EL_SALVADOR("SV", "El Salvador"), - SINT_MAARTEN_DUTCH_PART("SX", "Sint Maarten (Dutch part)"), - SYRIA("SY", "Syria"), - SWAZILAND("SZ", "Swaziland"), - TURKS_AND_CAICOS_ISLANDS("TC", "Turks And Caicos Islands"), - CHAD("TD", "Chad"), - FRENCH_SOUTHERN_TERRITORIES("TF", "French Southern Territories"), - TOGO("TG", "Togo"), - THAILAND("TH", "Thailand"), - TAJIKISTAN("TJ", "Tajikistan"), - TOKELAU("TK", "Tokelau"), - TIMOR_LESTE("TL", "Timor-Leste"), - TURKMENISTAN("TM", "Turkmenistan"), - TUNISIA("TN", "Tunisia"), - TONGA("TO", "Tonga"), - TURKEY("TR", "Turkey"), - TRINIDAD_AND_TOBAGO("TT", "Trinidad and Tobago"), - TUVALU("TV", "Tuvalu"), - TAIWAN("TW", "Taiwan"), - TANZANIA("TZ", "Tanzania"), - UKRAINE("UA", "Ukraine"), - UGANDA("UG", "Uganda"), - UNITED_STATES_MINOR_OUTLYING_ISLANDS("UM", "United States Minor Outlying Islands"), - UNITED_STATES("US", "United States"), - URUGUAY("UY", "Uruguay"), - UZBEKISTAN("UZ", "Uzbekistan"), - VATICAN("VA", "Vatican"), - SAINT_VINCENT_AND_THE_GRENADINES("VC", "Saint Vincent And The Grenadines"), - VENEZUELA("VE", "Venezuela"), - BRITISH_VIRGIN_ISLANDS("VG", "British Virgin Islands"), - U_S__VIRGIN_ISLANDS("VI", "U.S. Virgin Islands"), - VIETNAM("VN", "Vietnam"), - VANUATU("VU", "Vanuatu"), - WALLIS_AND_FUTUNA("WF", "Wallis And Futuna"), - SAMOA("WS", "Samoa"), - YEMEN("YE", "Yemen"), - MAYOTTE("YT", "Mayotte"), - SOUTH_AFRICA("ZA", "South Africa"), - ZAMBIA("ZM", "Zambia"), - ZIMBABWE("ZW", "Zimbabwe"); - - private String isoTag; - private String name; - - Country(String isoTag, String name) { - this.isoTag = isoTag; - this.name = name; + private static void sendData(Plugin plugin, 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!"); + } + if (logSentData) { + plugin.getLogger().info("Sending data to bStats: " + data.toString()); + } + HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); - /** - * Gets a country by it's iso tag. - * - * @param isoTag The iso tag of the county. - * @return The country with the given iso tag or null if unknown. - */ - public static Country byIsoTag(String isoTag) { - for (Country country : Country.values()) { - if (country.getCountryIsoTag().equals(isoTag)) { - return country; - } - } + // 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(); + + InputStream inputStream = connection.getInputStream(); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + + StringBuilder builder = new StringBuilder(); + String line; + while ((line = bufferedReader.readLine()) != null) { + builder.append(line); + } + bufferedReader.close(); + if (logResponseStatusText) { + plugin.getLogger().info("Sent data to bStats and received response: " + builder.toString()); + } + } + + /** + * 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; } - - /** - * Gets a country by a locale. - * - * @param locale The locale. - * @return The country from the giben locale or null if unknown country or - * if the locale does not contain a country. - */ - public static Country byLocale(Locale locale) { - return byIsoTag(locale.getCountry()); - } - - /** - * Gets the name of the country. - * - * @return The name of the country. - */ - public String getCountryName() { - return name; - } - - /** - * Gets the iso tag of the country. - * - * @return The iso tag of the country. - */ - public String getCountryIsoTag() { - return isoTag; - } - + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + GZIPOutputStream gzip = new GZIPOutputStream(outputStream); + gzip.write(str.getBytes(StandardCharsets.UTF_8)); + gzip.close(); + return outputStream.toByteArray(); } /** @@ -632,21 +368,21 @@ public class Metrics { public static abstract class CustomChart { // The id of the chart - protected final String chartId; + final String chartId; /** * Class constructor. * * @param chartId The id of the chart. */ - public CustomChart(String chartId) { + CustomChart(String chartId) { if (chartId == null || chartId.isEmpty()) { throw new IllegalArgumentException("ChartId cannot be null or empty!"); } this.chartId = chartId; } - protected JSONObject getRequestJsonObject() { + private JSONObject getRequestJsonObject() { JSONObject chart = new JSONObject(); chart.put("chartId", chartId); try { @@ -665,35 +401,32 @@ public class Metrics { return chart; } - protected abstract JSONObject getChartData(); + protected abstract JSONObject getChartData() throws Exception; } /** * Represents a custom simple pie. */ - public static abstract class SimplePie extends CustomChart { + 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) { + public SimplePie(String chartId, Callable callable) { super(chartId); + this.callable = callable; } - /** - * Gets the value of the pie. - * - * @return The value of the pie. - */ - public abstract String getValue(); - @Override - protected JSONObject getChartData() { + protected JSONObject getChartData() throws Exception { JSONObject data = new JSONObject(); - String value = getValue(); + String value = callable.call(); if (value == null || value.isEmpty()) { // Null = skip the chart return null; @@ -706,31 +439,26 @@ public class Metrics { /** * Represents a custom advanced pie. */ - public static abstract class AdvancedPie extends CustomChart { + 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) { + public AdvancedPie(String chartId, Callable> callable) { super(chartId); + this.callable = callable; } - /** - * Gets the values of the pie. - * - * @param valueMap Just an empty map. The only reason it exists is to make your life easier. - * You don't have to create a map yourself! - * @return The values of the pie. - */ - public abstract HashMap getValues(HashMap valueMap); - @Override - protected JSONObject getChartData() { + protected JSONObject getChartData() throws Exception { JSONObject data = new JSONObject(); JSONObject values = new JSONObject(); - HashMap map = getValues(new HashMap()); + Map map = callable.call(); if (map == null || map.isEmpty()) { // Null = skip the chart return null; @@ -753,30 +481,76 @@ public class Metrics { } /** - * Represents a custom single line chart. + * Represents a custom drilldown pie. */ - public static abstract class SingleLineChart extends CustomChart { + 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 SingleLineChart(String chartId) { + 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; + /** - * Gets the value of the chart. + * Class constructor. * - * @return The value of the chart. + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. */ - public abstract int getValue(); + public SingleLineChart(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } @Override - protected JSONObject getChartData() { + protected JSONObject getChartData() throws Exception { JSONObject data = new JSONObject(); - int value = getValue(); + int value = callable.call(); if (value == 0) { // Null = skip the chart return null; @@ -790,31 +564,26 @@ public class Metrics { /** * Represents a custom multi line chart. */ - public static abstract class MultiLineChart extends CustomChart { + 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) { + public MultiLineChart(String chartId, Callable> callable) { super(chartId); + this.callable = callable; } - /** - * Gets the values of the chart. - * - * @param valueMap Just an empty map. The only reason it exists is to make your life easier. - * You don't have to create a map yourself! - * @return The values of the chart. - */ - public abstract HashMap getValues(HashMap valueMap); - @Override - protected JSONObject getChartData() { + protected JSONObject getChartData() throws Exception { JSONObject data = new JSONObject(); JSONObject values = new JSONObject(); - HashMap map = getValues(new HashMap()); + Map map = callable.call(); if (map == null || map.isEmpty()) { // Null = skip the chart return null; @@ -838,80 +607,81 @@ public class Metrics { } /** - * Represents a custom simple map chart. + * Represents a custom simple bar chart. */ - public static abstract class SimpleMapChart extends CustomChart { + 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 SimpleMapChart(String chartId) { + public SimpleBarChart(String chartId, Callable> callable) { super(chartId); + this.callable = callable; } - /** - * Gets the value of the chart. - * - * @return The value of the chart. - */ - public abstract Country getValue(); - @Override - protected JSONObject getChartData() { + protected JSONObject getChartData() throws Exception { JSONObject data = new JSONObject(); - Country value = getValue(); - - if (value == null) { + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { // Null = skip the chart return null; } - data.put("value", value.getCountryIsoTag()); + 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 map chart. + * Represents a custom advanced bar chart. */ - public static abstract class AdvancedMapChart extends CustomChart { + 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 AdvancedMapChart(String chartId) { + public AdvancedBarChart(String chartId, Callable> callable) { super(chartId); + this.callable = callable; } - /** - * Gets the value of the chart. - * - * @param valueMap Just an empty map. The only reason it exists is to make your life easier. - * You don't have to create a map yourself! - * @return The value of the chart. - */ - public abstract HashMap getValues(HashMap valueMap); - @Override - protected JSONObject getChartData() { + protected JSONObject getChartData() throws Exception { JSONObject data = new JSONObject(); JSONObject values = new JSONObject(); - HashMap map = getValues(new HashMap()); + 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) { + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().length == 0) { continue; // Skip this invalid } allSkipped = false; - values.put(entry.getKey().getCountryIsoTag(), entry.getValue()); + JSONArray categoryValues = new JSONArray(); + for (int categoryValue : entry.getValue()) { + categoryValues.add(categoryValue); + } + values.put(entry.getKey(), categoryValues); } if (allSkipped) { // Null = skip the chart @@ -920,7 +690,6 @@ public class Metrics { data.put("values", values); return data; } - } -} \ No newline at end of file +} diff --git a/src/main/java/sh/okx/rankup/Rankup.java b/src/main/java/sh/okx/rankup/Rankup.java index 2ec6cbf..8ec13d2 100644 --- a/src/main/java/sh/okx/rankup/Rankup.java +++ b/src/main/java/sh/okx/rankup/Rankup.java @@ -1,6 +1,7 @@ package sh.okx.rankup; import com.gmail.nossr50.datatypes.skills.SkillType; +import com.google.common.base.Preconditions; import lombok.Getter; import net.milkbowl.vault.economy.Economy; import net.milkbowl.vault.permission.Permission; @@ -14,11 +15,7 @@ import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.InventoryView; import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.java.JavaPlugin; -import sh.okx.rankup.commands.InfoCommand; -import sh.okx.rankup.commands.PrestigeCommand; -import sh.okx.rankup.commands.PrestigesCommand; -import sh.okx.rankup.commands.RanksCommand; -import sh.okx.rankup.commands.RankupCommand; +import sh.okx.rankup.commands.*; import sh.okx.rankup.gui.Gui; import sh.okx.rankup.gui.GuiListener; import sh.okx.rankup.messages.EmptyMessageBuilder; @@ -33,25 +30,22 @@ import sh.okx.rankup.ranks.Rankups; import sh.okx.rankup.requirements.OperationRegistry; import sh.okx.rankup.requirements.Requirement; import sh.okx.rankup.requirements.RequirementRegistry; -import sh.okx.rankup.requirements.operation.AllOperation; -import sh.okx.rankup.requirements.operation.AnyOperation; -import sh.okx.rankup.requirements.operation.NoneOperation; -import sh.okx.rankup.requirements.operation.OneOperation; -import sh.okx.rankup.requirements.requirement.GroupRequirement; -import sh.okx.rankup.requirements.requirement.advancedachievements.AdvancedAchievementsAchievementRequirement; -import sh.okx.rankup.requirements.requirement.advancedachievements.AdvancedAchievementsTotalRequirement; -import sh.okx.rankup.requirements.requirement.mcmmo.McMMOPowerLevelRequirement; -import sh.okx.rankup.requirements.requirement.mcmmo.McMMOSkillRequirement; -import sh.okx.rankup.requirements.requirement.MoneyRequirement; -import sh.okx.rankup.requirements.requirement.PermissionRequirement; -import sh.okx.rankup.requirements.requirement.PlaceholderRequirement; -import sh.okx.rankup.requirements.requirement.PlaytimeMinutesRequirement; -import sh.okx.rankup.requirements.requirement.XpLevelRequirement; +import sh.okx.rankup.requirements.operation.*; +import sh.okx.rankup.requirements.requirement.*; +import sh.okx.rankup.requirements.requirement.advancedachievements.*; +import sh.okx.rankup.requirements.requirement.mcmmo.*; import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; import java.text.DecimalFormat; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.ResourceBundle; import java.util.WeakHashMap; import java.util.function.Supplier; @@ -90,12 +84,16 @@ public class Rankup extends JavaPlugin { reload(); Metrics metrics = new Metrics(this); - metrics.addCustomChart(new Metrics.SimplePie("confirmation") { - @Override - public String getValue() { - return getConfig().getString("confirmation.type"); + metrics.addCustomChart(new Metrics.SimplePie("confirmation", + () -> config.getString("confirmation.type"))); + metrics.addCustomChart(new Metrics.AdvancedPie("requirements", () -> { + Map map = new HashMap<>(); + addAll(map, rankups); + if (prestiges != null) { + addAll(map, prestiges); } - }); + return map; + })); if (config.getBoolean("ranks")) { getCommand("ranks").setExecutor(new RanksCommand(this)); @@ -136,7 +134,7 @@ public class Rankup extends JavaPlugin { autoRankup.runTaskTimer(this, time, time); } - if (config.getInt("version") < 2) { + if (config.getInt("version") < 3) { getLogger().severe("You are using an outdated config!"); getLogger().severe("This means that some things might not work!"); getLogger().severe("To update, please rename ALL your config files (or the folder they are in),"); @@ -147,6 +145,15 @@ public class Rankup extends JavaPlugin { } } + private void addAll(Map map, RankList ranks) { + for (Rank rank : ranks.ranks) { + for (Requirement requirement : rank.getRequirements()) { + String name = requirement.getName(); + map.put(name, map.getOrDefault(name, 0) + 1); + } + } + } + /** * Closes all rankup inventories on disable * so players cannot grab items from the inventory @@ -163,8 +170,13 @@ public class Rankup extends JavaPlugin { } private void loadConfigs() { - messages = loadConfig("messages.yml"); + saveLocales(); + config = loadConfig("config.yml"); + String locale = config.getString("locale"); + File localeFile = new File(new File(getDataFolder(), "locale"), locale + ".yml"); + messages = YamlConfiguration.loadConfiguration(localeFile); + Bukkit.getScheduler().scheduleSyncDelayedTask(this, this::refreshRanks); } @@ -178,6 +190,18 @@ public class Rankup extends JavaPlugin { } } + private void saveLocales() { + saveLocale("en"); + } + + private void saveLocale(String locale) { + String name = "locale/" + locale + ".yml"; + File file = new File(getDataFolder(), name); + if (!file.exists()) { + saveResource("locale/" + locale + ".yml", false); + } + } + private FileConfiguration loadConfig(String name) { File file = new File(getDataFolder(), name); if (!file.exists()) { diff --git a/src/main/java/sh/okx/rankup/messages/Variable.java b/src/main/java/sh/okx/rankup/messages/Variable.java index aba7f6c..506487c 100644 --- a/src/main/java/sh/okx/rankup/messages/Variable.java +++ b/src/main/java/sh/okx/rankup/messages/Variable.java @@ -1,8 +1,5 @@ package sh.okx.rankup.messages; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - public enum Variable { PLAYER, OLD_RANK, @@ -18,11 +15,5 @@ public enum Variable { PERCENT_DONE, PERCENT_LEFT, SECONDS, - SECONDS_LEFT; - - public String replace(String message, String value, String type) { - Pattern pattern = Pattern.compile("\\{" + type + "_" + this + "}", Pattern.CASE_INSENSITIVE); - Matcher matcher = pattern.matcher(message); - return matcher.replaceAll(value); - } + SECONDS_LEFT } diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/PlaceholderRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/PlaceholderRequirement.java index ae6ea6e..7a21add 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/PlaceholderRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/PlaceholderRequirement.java @@ -14,39 +14,14 @@ public class PlaceholderRequirement extends Requirement { super(clone); } - private double getValue(Player player) { - String[] parts = getValueString().split(" ", 2); - String parsed = PlaceholderAPI.setPlaceholders(player, parts[0]); - if(!PlaceholderAPI.containsPlaceholders(parts[0]) || parsed.equals(parts[0])) { - plugin.getLogger().severe(parts[0] + " is not a PlaceholderAPI placeholder!"); - return -1; - } - double value; - try { - value = Double.parseDouble(parsed); - } catch(NumberFormatException ex) { - plugin.getLogger().severe("Parsed placeholder '" +parsed + "' is not a valid number"); - return -1; - } - return value; - } - - private double getNeeded() { - String needed = getValueString().split(" ", 2)[1]; - try { - return Double.parseDouble(needed); - } catch(NumberFormatException e) { - plugin.getLogger().severe("Needed '" + needed + "' is not a valid number!"); - return -1; - } - } - @Override public boolean check(Player player) { String[] parts = getValueString().split(" "); String parsed = PlaceholderAPI.setPlaceholders(player, parts[0]); if(!PlaceholderAPI.containsPlaceholders(parts[0]) || parsed.equals(parts[0])) { throw new IllegalArgumentException(parts[0] + " is not a PlaceholderAPI placeholder!"); + } else if (parts.length < 3) { + throw new IllegalArgumentException("Placeholder requirements must be in the form %placeholder% string"); } String value = parts[2]; diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 53c475a..059a750 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,5 +1,11 @@ # this is used for letting you know that you need to update/change your config file -version: 2 +version: 3 + +# the ISO-639-1 locale to use for messages +# all messages can be customised but this allows you to +# choose messages that are already translated +# locales can be found in the locale/ folder +locale: en # interval (in minutes) to check to autorankup players # ranking up manually wil always be enabled diff --git a/src/main/resources/messages.yml b/src/main/resources/locale/en.yml similarity index 94% rename from src/main/resources/messages.yml rename to src/main/resources/locale/en.yml index 0e0d85a..87d6cff 100644 --- a/src/main/resources/messages.yml +++ b/src/main/resources/locale/en.yml @@ -56,8 +56,8 @@ prestige: header: "" footer: "" cooldown: - singular: "&cYou must wait {SECONDS_LEFT} second to prestige again." - plural: "&cYou must wait {SECONDS_LEFT} more seconds to prestige again." + singular: "&cYou must wait {SECONDS_LEFT} second to prestige again." + plural: "&cYou must wait {SECONDS_LEFT} more seconds to prestige again." not-high-enough: "&cYou cannot prestige at your rank!" not-in-ladder: "&cSorry, but we could not find any rankups for the group(s) you are in." diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index b502005..df3904c 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: Rankup -version: 3.3.1 +version: 3.3.2-beta main: sh.okx.rankup.Rankup author: Okx depend: [Vault]