"),
/** Wrong captcha, please type "/captcha THE_CAPTCHA" into the chat! */
@@ -143,13 +143,13 @@ public enum MessageKey {
/** The server is full, try again later! */
KICK_FULL_SERVER("kick_fullserver"),
- /** Usage: /email add <email> <confirmEmail> */
+ /** Usage: /email add <email> <confirmEmail> */
USAGE_ADD_EMAIL("usage_email_add"),
- /** Usage: /email change <oldEmail> <newEmail> */
+ /** Usage: /email change <oldEmail> <newEmail> */
USAGE_CHANGE_EMAIL("usage_email_change"),
- /** Usage: /email recovery <Email> */
+ /** Usage: /email recovery <Email> */
USAGE_RECOVER_EMAIL("usage_email_recovery"),
/** Invalid new email, try again! */
@@ -224,7 +224,7 @@ public enum MessageKey {
/** A recovery code to reset your password has been sent to your email. */
RECOVERY_CODE_SENT("recovery_code_sent"),
- /** The recovery code is not correct! Use /email recovery [email] to generate a new one */
+ /** The recovery code is not correct! Use "/email recovery [email]" to generate a new one */
INCORRECT_RECOVERY_CODE("recovery_code_incorrect");
private String key;
diff --git a/src/main/java/fr/xephi/authme/message/Messages.java b/src/main/java/fr/xephi/authme/message/Messages.java
index 86fa2db6..5d777ab8 100644
--- a/src/main/java/fr/xephi/authme/message/Messages.java
+++ b/src/main/java/fr/xephi/authme/message/Messages.java
@@ -107,7 +107,7 @@ public class Messages implements Reloadable {
@Override
public void reload() {
this.messageFileHandler = messageFileHandlerProvider
- .initializeHandler(lang -> "messages/messages_" + lang + ".yml");
+ .initializeHandler(lang -> "messages/messages_" + lang + ".yml", "/authme messages");
}
private static String formatMessage(String message) {
diff --git a/src/main/java/fr/xephi/authme/metrics/Metrics.java b/src/main/java/fr/xephi/authme/metrics/Metrics.java
new file mode 100644
index 00000000..714036ce
--- /dev/null
+++ b/src/main/java/fr/xephi/authme/metrics/Metrics.java
@@ -0,0 +1,1031 @@
+package fr.xephi.authme.metrics;
+
+import org.bukkit.Bukkit;
+import org.bukkit.configuration.file.YamlConfiguration;
+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.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.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 Metrics {
+
+ // 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";
+
+ // 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 Metrics(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(Metrics.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 = Bukkit.getOnlinePlayers().size();
+ 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 :)
+ } catch (NoSuchFieldException ignored) {
+ continue; // Continue "searching"
+ }
+ // Found one!
+ try {
+ pluginData.add(service.getMethod("getPluginData").invoke(Bukkit.getServicesManager().load(service)));
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException 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
+ protected final String chartId;
+
+ /**
+ * Class constructor.
+ *
+ * @param chartId The id of the chart.
+ */
+ public CustomChart(String chartId) {
+ if (chartId == null || chartId.isEmpty()) {
+ throw new IllegalArgumentException("ChartId cannot be null or empty!");
+ }
+ this.chartId = chartId;
+ }
+
+ protected 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();
+
+ }
+
+ /**
+ * Represents a custom simple pie.
+ */
+ public static abstract class SimplePie extends CustomChart {
+
+ /**
+ * Class constructor.
+ *
+ * @param chartId The id of the chart.
+ */
+ public SimplePie(String chartId) {
+ super(chartId);
+ }
+
+ /**
+ * Gets the value of the pie.
+ *
+ * @return The value of the pie.
+ */
+ public abstract String getValue();
+
+ @Override
+ protected JSONObject getChartData() {
+ JSONObject data = new JSONObject();
+ String value = getValue();
+ if (value == null || value.isEmpty()) {
+ // Null = skip the chart
+ return null;
+ }
+ data.put("value", value);
+ return data;
+ }
+ }
+
+ /**
+ * Represents a custom advanced pie.
+ */
+ public static abstract class AdvancedPie extends CustomChart {
+
+ /**
+ * Class constructor.
+ *
+ * @param chartId The id of the chart.
+ */
+ public AdvancedPie(String chartId) {
+ super(chartId);
+ }
+
+ /**
+ * 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() {
+ JSONObject data = new JSONObject();
+ JSONObject values = new JSONObject();
+ HashMap map = getValues(new HashMap());
+ 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 single line chart.
+ */
+ public static abstract class SingleLineChart extends CustomChart {
+
+ /**
+ * Class constructor.
+ *
+ * @param chartId The id of the chart.
+ */
+ public SingleLineChart(String chartId) {
+ super(chartId);
+ }
+
+ /**
+ * Gets the value of the chart.
+ *
+ * @return The value of the chart.
+ */
+ public abstract int getValue();
+
+ @Override
+ protected JSONObject getChartData() {
+ JSONObject data = new JSONObject();
+ int value = getValue();
+ if (value == 0) {
+ // Null = skip the chart
+ return null;
+ }
+ data.put("value", value);
+ return data;
+ }
+
+ }
+
+ /**
+ * Represents a custom multi line chart.
+ */
+ public static abstract class MultiLineChart extends CustomChart {
+
+ /**
+ * Class constructor.
+ *
+ * @param chartId The id of the chart.
+ */
+ public MultiLineChart(String chartId) {
+ super(chartId);
+ }
+
+ /**
+ * 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() {
+ JSONObject data = new JSONObject();
+ JSONObject values = new JSONObject();
+ HashMap map = getValues(new HashMap());
+ 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 abstract class SimpleBarChart extends CustomChart {
+
+ /**
+ * Class constructor.
+ *
+ * @param chartId The id of the chart.
+ */
+ public SimpleBarChart(String chartId) {
+ super(chartId);
+ }
+
+ /**
+ * 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() {
+ JSONObject data = new JSONObject();
+ JSONObject values = new JSONObject();
+ HashMap map = getValues(new HashMap());
+ 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 abstract class AdvancedBarChart extends CustomChart {
+
+ /**
+ * Class constructor.
+ *
+ * @param chartId The id of the chart.
+ */
+ public AdvancedBarChart(String chartId) {
+ super(chartId);
+ }
+
+ /**
+ * 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() {
+ JSONObject data = new JSONObject();
+ JSONObject values = new JSONObject();
+ HashMap map = getValues(new HashMap());
+ 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;
+ }
+
+ }
+
+ /**
+ * Represents a custom simple map chart.
+ */
+ public static abstract class SimpleMapChart extends CustomChart {
+
+ /**
+ * Class constructor.
+ *
+ * @param chartId The id of the chart.
+ */
+ public SimpleMapChart(String chartId) {
+ super(chartId);
+ }
+
+ /**
+ * Gets the value of the chart.
+ *
+ * @return The value of the chart.
+ */
+ public abstract Country getValue();
+
+ @Override
+ protected JSONObject getChartData() {
+ JSONObject data = new JSONObject();
+ Country value = getValue();
+
+ if (value == null) {
+ // Null = skip the chart
+ return null;
+ }
+ data.put("value", value.getCountryIsoTag());
+ return data;
+ }
+
+ }
+
+ /**
+ * Represents a custom advanced map chart.
+ */
+ public static abstract class AdvancedMapChart extends CustomChart {
+
+ /**
+ * Class constructor.
+ *
+ * @param chartId The id of the chart.
+ */
+ public AdvancedMapChart(String chartId) {
+ super(chartId);
+ }
+
+ /**
+ * 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() {
+ JSONObject data = new JSONObject();
+ JSONObject values = new JSONObject();
+ HashMap map = getValues(new HashMap());
+ 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().getCountryIsoTag(), entry.getValue());
+ }
+ if (allSkipped) {
+ // Null = skip the chart
+ return null;
+ }
+ data.put("values", values);
+ return data;
+ }
+
+ }
+
+ /**
+ * A enum which is used for custom maps.
+ */
+ 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;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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;
+ }
+ }
+ 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());
+ }
+ }
+}
diff --git a/src/main/java/fr/xephi/authme/process/quit/ProcessSyncronousPlayerQuit.java b/src/main/java/fr/xephi/authme/process/quit/ProcessSyncronousPlayerQuit.java
index 73db67f8..47bfb912 100644
--- a/src/main/java/fr/xephi/authme/process/quit/ProcessSyncronousPlayerQuit.java
+++ b/src/main/java/fr/xephi/authme/process/quit/ProcessSyncronousPlayerQuit.java
@@ -1,6 +1,6 @@
package fr.xephi.authme.process.quit;
-import fr.xephi.authme.data.backup.LimboPlayerStorage;
+import fr.xephi.authme.data.limbo.LimboPlayerStorage;
import fr.xephi.authme.data.limbo.LimboCache;
import fr.xephi.authme.process.SynchronousProcess;
import org.bukkit.entity.Player;
diff --git a/src/main/java/fr/xephi/authme/service/AntiBotService.java b/src/main/java/fr/xephi/authme/service/AntiBotService.java
index 91f80041..90c62a24 100644
--- a/src/main/java/fr/xephi/authme/service/AntiBotService.java
+++ b/src/main/java/fr/xephi/authme/service/AntiBotService.java
@@ -30,7 +30,6 @@ public class AntiBotService implements SettingsDependent {
// Settings
private int duration;
private int sensibility;
- private int delay;
private int interval;
// Service status
private AntiBotStatus antiBotStatus;
@@ -60,7 +59,6 @@ public class AntiBotService implements SettingsDependent {
// Load settings
duration = settings.getProperty(ProtectionSettings.ANTIBOT_DURATION);
sensibility = settings.getProperty(ProtectionSettings.ANTIBOT_SENSIBILITY);
- delay = settings.getProperty(ProtectionSettings.ANTIBOT_DELAY);
interval = settings.getProperty(ProtectionSettings.ANTIBOT_INTERVAL);
// Stop existing protection
@@ -77,6 +75,7 @@ public class AntiBotService implements SettingsDependent {
// Delay the schedule on first start
if (startup) {
+ int delay = settings.getProperty(ProtectionSettings.ANTIBOT_DELAY);
bukkitService.scheduleSyncDelayedTask(enableTask, delay * TICKS_PER_SECOND);
startup = false;
} else {
diff --git a/src/main/java/fr/xephi/authme/settings/SpawnLoader.java b/src/main/java/fr/xephi/authme/settings/SpawnLoader.java
index ac1742fa..26f7d03b 100644
--- a/src/main/java/fr/xephi/authme/settings/SpawnLoader.java
+++ b/src/main/java/fr/xephi/authme/settings/SpawnLoader.java
@@ -1,10 +1,9 @@
package fr.xephi.authme.settings;
import fr.xephi.authme.ConsoleLogger;
-import fr.xephi.authme.datasource.DataSource;
-import fr.xephi.authme.service.PluginHookService;
import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.initialization.Reloadable;
+import fr.xephi.authme.service.PluginHookService;
import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.util.FileUtils;
@@ -43,11 +42,9 @@ public class SpawnLoader implements Reloadable {
* @param pluginFolder The AuthMe data folder
* @param settings The setting instance
* @param pluginHookService The plugin hooks instance
- * @param dataSource The plugin auth database instance
*/
@Inject
- SpawnLoader(@DataFolder File pluginFolder, Settings settings, PluginHookService pluginHookService,
- DataSource dataSource) {
+ SpawnLoader(@DataFolder File pluginFolder, Settings settings, PluginHookService pluginHookService) {
// TODO ljacqu 20160312: Check if resource could be copied and handle the case if not
File spawnFile = new File(pluginFolder, "spawn.yml");
FileUtils.copyFileFromResource(spawnFile, "spawn.yml");
diff --git a/src/main/java/fr/xephi/authme/util/lazytags/WrappedTagReplacer.java b/src/main/java/fr/xephi/authme/util/lazytags/WrappedTagReplacer.java
index 92c3f70d..ce9487e2 100644
--- a/src/main/java/fr/xephi/authme/util/lazytags/WrappedTagReplacer.java
+++ b/src/main/java/fr/xephi/authme/util/lazytags/WrappedTagReplacer.java
@@ -28,7 +28,7 @@ public class WrappedTagReplacer {
* @param allTags all available tags
* @param items the items to apply the replacements on
* @param stringGetter getter of the String property to adapt on the items
- * @param itemCreator a function of signature (T, String) -> T: the original item and the adapted String are passed
+ * @param itemCreator a function taking (T, String): the original item and the adapted String, returning a new item
*/
public WrappedTagReplacer(Collection> allTags,
Collection items,
diff --git a/src/main/resources/messages/help_br.yml b/src/main/resources/messages/help_br.yml
index 1a143c8e..be4983ab 100644
--- a/src/main/resources/messages/help_br.yml
+++ b/src/main/resources/messages/help_br.yml
@@ -4,6 +4,7 @@
# -------------------------------------------------------
# Lista de textos usados na seção de ajuda:
common:
+ header: '==========[ Ajuda AuthMeReloaded ]=========='
optional: 'Opcional'
hasPermission: 'Você tem permissão'
noPermission: 'Sem Permissão'
diff --git a/src/main/resources/messages/help_de.yml b/src/main/resources/messages/help_de.yml
index 46292e16..b1841ef4 100644
--- a/src/main/resources/messages/help_de.yml
+++ b/src/main/resources/messages/help_de.yml
@@ -1,4 +1,5 @@
common:
+ header: '==========[ AuthMeReloaded Hilfe ]=========='
optional: 'Optional'
hasPermission: 'Du hast Berechtigung'
noPermission: 'Keine Berechtigung'
diff --git a/src/main/resources/messages/help_ru.yml b/src/main/resources/messages/help_ru.yml
new file mode 100644
index 00000000..20d6bd9a
--- /dev/null
+++ b/src/main/resources/messages/help_ru.yml
@@ -0,0 +1,45 @@
+# Translation config for the AuthMe help, e.g. when /authme help or /authme help register is called
+
+# -------------------------------------------------------
+# List of texts used in the help section
+common:
+ header: '==========[ AuthMeReloaded Справка ]=========='
+ optional: 'Опционально'
+ hasPermission: 'У вас есть такие права'
+ noPermission: 'Нет прав'
+ default: 'По-умолчанию'
+ result: 'Результат'
+ defaultPermissions:
+ notAllowed: 'Нет прав'
+ opOnly: 'Только Операторы'
+ allowed: 'Разрешено всем'
+
+# -------------------------------------------------------
+# Titles of the individual help sections
+# Set the translation text to empty text to disable the section, e.g. to hide alternatives:
+# alternatives: ''
+section:
+ command: 'Команда'
+ description: 'Краткое описание'
+ detailedDescription: 'Детальное описание'
+ arguments: 'Аргументы'
+ permissions: 'Разрешения'
+ alternatives: 'Альтернативы'
+ children: 'Команды'
+
+# -------------------------------------------------------
+# You can translate the data for all commands using the below pattern.
+# For example to translate /authme reload, create a section "authme.reload", or "login" for /login
+# If the command has arguments, you can use arg1 as below to translate the first argument, and so forth
+# Translations don't need to be complete; any missing section will be taken from the default silently
+# Important: Put main commands like "authme" before their children (e.g. "authme.reload")
+commands:
+ authme.register:
+ description: 'Регистрация новго игрока'
+ detailedDescription: 'Регистрация игрока с указанным именем и паролем.'
+ arg1:
+ label: 'player'
+ description: 'Имя игрока'
+ arg2:
+ label: 'password'
+ description: 'Пароль'
diff --git a/src/main/resources/messages/messages_zhcn.yml b/src/main/resources/messages/messages_zhcn.yml
index f52f9b41..e6c5e755 100644
--- a/src/main/resources/messages/messages_zhcn.yml
+++ b/src/main/resources/messages/messages_zhcn.yml
@@ -2,7 +2,7 @@
reg_msg: '&8[&6玩家系统&8] &c请输入“/register <密码> <再输入一次以确定密码>”以注册'
usage_reg: '&8[&6玩家系统&8] &c正确用法:“/register <密码> <再输入一次以确定密码>”'
reg_only: '&8[&6玩家系统&8] &f只允许注册过的玩家进服!请到 https://example.cn 注册'
-# TODO kicked_admin_registered: 'An admin just registered you; please log in again'
+kicked_admin_registered: '有一位管理员刚刚为您完成了注册,请重新登录'
registered: '&8[&6玩家系统&8] &c已成功注册!'
reg_disabled: '&8[&6玩家系统&8] &c目前服务器暂时禁止注册,请到服务器论坛以得到更多资讯'
user_regged: '&8[&6玩家系统&8] &c此用户已经在此服务器注册过'
@@ -11,7 +11,7 @@ user_regged: '&8[&6玩家系统&8] &c此用户已经在此服务器注册过'
password_error: '&8[&6玩家系统&8] &f密码不相同'
password_error_nick: '&8[&6玩家系统&8] &f你不能使用你的名字作为密码。 '
password_error_unsafe: '&8[&6玩家系统&8] &f你不能使用安全性过低的码。 '
-# TODO password_error_chars: '&4Your password contains illegal characters. Allowed chars: REG_EX'
+password_error_chars: '&4您的密码包含了非法字符。可使用的字符: REG_EX'
pass_len: '&8[&6玩家系统&8] &你的密码没有达到要求!'
# Login
@@ -23,10 +23,10 @@ timeout: '&8[&6玩家系统&8] &f给你登录的时间已经过了'
# Errors
unknown_user: '&8[&6玩家系统&8] &c此用户名还未注册过'
-# TODO denied_command: '&cIn order to use this command you must be authenticated!'
-# TODO denied_chat: '&cIn order to chat you must be authenticated!'
+denied_command: '&c您需要先通过验证才能使用该命令!'
+denied_chat: '&c您需要先通过验证才能聊天!'
not_logged_in: '&8[&6玩家系统&8] &c你还未登录!'
-# TODO tempban_max_logins: '&cYou have been temporarily banned for failing to log in too many times.'
+tempban_max_logins: '&c由于您登录失败次数过多,已被暂时禁止登录。'
# TODO: Missing tags %reg_count, %max_acc, %reg_names
max_reg: '&8[&6玩家系统&8] &f你不允许再为你的IP在服务器注册更多用户了!'
no_perm: '&8[&6玩家系统&8] &c没有权限'
@@ -41,11 +41,11 @@ antibot_auto_disabled: '&8[&6玩家系统&8] &f防机器人程序由于异常连
# Other messages
unregistered: '&8[&6玩家系统&8] &c成功删除此用户!'
-# TODO accounts_owned_self: 'You own %count accounts:'
-# TODO accounts_owned_other: 'The player %name has %count accounts:'
+accounts_owned_self: '您拥有 %count 个账户:'
+accounts_owned_other: '玩家 %name 拥有 %count 个账户:'
two_factor_create: '&8[&6玩家系统&8] &2你的代码是 %code,你可以使用 %url 来扫描'
-# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.'
-# TODO recovery_code_incorrect: 'The recovery code is not correct! Use /email recovery [email] to generate a new one'
+recovery_code_sent: '一个用于重置您的密码的验证码已发到您的邮箱'
+recovery_code_incorrect: '验证码不正确! 使用 /email recovery [email] 以生成新的验证码'
vb_nonActiv: '&8[&6玩家系统&8] &f你的帐号还未激活,请查看你的邮箱!'
usage_unreg: '&8[&6玩家系统&8] &c正确用法:“/unregister <密码>”'
pwd_changed: '&8[&6玩家系统&8] &c密码已成功修改!'
@@ -66,7 +66,7 @@ not_owner_error: '&8[&6玩家系统&8] &4警告! &c你并不是此帐户持有
kick_fullserver: '&8[&6玩家系统&8] &c抱歉,服务器已满!'
same_nick: '&8[&6玩家系统&8] &f同样的用户名现在在线且已经登录了!'
invalid_name_case: '&8[&6玩家系统&8] &c你应该使用「%valid」而并非「%invalid」登入游戏。 '
-# TODO same_ip_online: 'A player with the same IP is already in game!'
+same_ip_online: '已有一个同IP玩家在游戏中了!'
# Email
usage_email_add: '&8[&6玩家系统&8] &f用法: /email add <邮箱> <确认电子邮件> '
@@ -80,11 +80,11 @@ email_confirm: '&8[&6玩家系统&8] &f确认你的邮箱 !'
email_changed: '&8[&6玩家系统&8] &f邮箱已改变 !'
email_send: '&8[&6玩家系统&8] &f恢复邮件已发送 !'
email_exists: '&8[&6玩家系统&8] &c恢复邮件已发送 ! 你可以丢弃它然後使用以下的指令来发送新的邮件:'
-# TODO email_show: '&2Your current email address is: &f%email'
-# TODO incomplete_email_settings: 'Error: not all required settings are set for sending emails. Please contact an admin.'
+email_show: '&2您当前的电子邮件地址为: &f%email'
+incomplete_email_settings: '错误:并非所有发送邮件需要的设置都已被设置,请联系管理员'
email_already_used: '&8[&6玩家系统&8] &4邮箱已被使用'
-# TODO email_send_failure: 'The email could not be sent. Please contact an administrator.'
-# TODO show_no_email: '&2You currently don''t have email address associated with this account.'
+email_send_failure: '邮件发送失败,请联系管理员'
+show_no_email: '&2您当前并没有任何邮箱与该账号绑定'
add_email: '&8[&6玩家系统&8] &c请输入“/email add <你的邮箱> <再输入一次以确认>”以把你的邮箱添加到此帐号'
recovery_email: '&8[&6玩家系统&8] &c忘了你的密码?请输入:“/email recovery <你的邮箱>”'
diff --git a/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java b/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java
index 292b0ec9..ea0f372a 100644
--- a/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java
+++ b/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java
@@ -39,10 +39,10 @@ public class HelpMessagesServiceTest {
@Mock
private MessageFileHandlerProvider messageFileHandlerProvider;
- @SuppressWarnings("unchecked")
@BeforeInjecting
+ @SuppressWarnings("unchecked")
public void initializeHandler() {
- MessageFileHandler handler = new MessageFileHandler(getJarFile(TEST_FILE), "messages/messages_en.yml");
+ MessageFileHandler handler = new MessageFileHandler(getJarFile(TEST_FILE), "messages/messages_en.yml", null);
given(messageFileHandlerProvider.initializeHandler(any(Function.class))).willReturn(handler);
}
diff --git a/src/test/java/fr/xephi/authme/data/limbo/LimboCacheTest.java b/src/test/java/fr/xephi/authme/data/limbo/LimboCacheTest.java
index 8eb8e959..fcc45451 100644
--- a/src/test/java/fr/xephi/authme/data/limbo/LimboCacheTest.java
+++ b/src/test/java/fr/xephi/authme/data/limbo/LimboCacheTest.java
@@ -1,7 +1,6 @@
package fr.xephi.authme.data.limbo;
import fr.xephi.authme.ReflectionTestUtils;
-import fr.xephi.authme.data.backup.LimboPlayerStorage;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SpawnLoader;
diff --git a/src/test/java/fr/xephi/authme/data/backup/LimboPlayerStorageTest.java b/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerStorageTest.java
similarity index 98%
rename from src/test/java/fr/xephi/authme/data/backup/LimboPlayerStorageTest.java
rename to src/test/java/fr/xephi/authme/data/limbo/LimboPlayerStorageTest.java
index f9c5880c..6d346854 100644
--- a/src/test/java/fr/xephi/authme/data/backup/LimboPlayerStorageTest.java
+++ b/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerStorageTest.java
@@ -1,14 +1,13 @@
-package fr.xephi.authme.data.backup;
+package fr.xephi.authme.data.limbo;
import ch.jalu.injector.testing.BeforeInjecting;
import ch.jalu.injector.testing.DelayedInjectionRunner;
import ch.jalu.injector.testing.InjectDelayed;
import fr.xephi.authme.TestHelper;
-import fr.xephi.authme.data.limbo.LimboPlayer;
import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.permission.PermissionsManager;
-import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.service.BukkitService;
+import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.util.FileUtils;
import org.bukkit.Location;
import org.bukkit.World;
diff --git a/src/test/java/fr/xephi/authme/message/HelpMessageConsistencyTest.java b/src/test/java/fr/xephi/authme/message/HelpMessageConsistencyTest.java
new file mode 100644
index 00000000..399fd93a
--- /dev/null
+++ b/src/test/java/fr/xephi/authme/message/HelpMessageConsistencyTest.java
@@ -0,0 +1,93 @@
+package fr.xephi.authme.message;
+
+import fr.xephi.authme.TestHelper;
+import fr.xephi.authme.command.help.HelpMessage;
+import fr.xephi.authme.command.help.HelpSection;
+import fr.xephi.authme.permission.DefaultPermission;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.hamcrest.Matcher;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import static org.hamcrest.Matchers.both;
+import static org.hamcrest.Matchers.emptyString;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Tests that all help_xx.yml files contain all entries for
+ * {@link HelpSection}, {@link HelpMessage} and {@link DefaultPermission}.
+ */
+public class HelpMessageConsistencyTest {
+
+ private static final String MESSAGES_FOLDER = "/messages";
+ private static final Pattern HELP_MESSAGES_FILE = Pattern.compile("help_[a-z]+\\.yml");
+
+ private List helpFiles;
+
+ @Before
+ public void findHelpMessagesFiles() {
+ File folder = TestHelper.getJarFile(MESSAGES_FOLDER);
+ File[] files = folder.listFiles();
+ if (files == null || files.length == 0) {
+ throw new IllegalStateException("Could not get files from '" + MESSAGES_FOLDER + "'");
+ }
+ helpFiles = Arrays.stream(files)
+ .filter(file -> HELP_MESSAGES_FILE.matcher(file.getName()).matches())
+ .collect(Collectors.toList());
+ }
+
+ @Test
+ public void shouldHaveRequiredEntries() {
+ for (File file : helpFiles) {
+ // given
+ FileConfiguration configuration = YamlConfiguration.loadConfiguration(file);
+
+ // when / then
+ assertHasAllHelpSectionEntries(file.getName(), configuration);
+ }
+ }
+
+ private void assertHasAllHelpSectionEntries(String filename, FileConfiguration configuration) {
+ for (HelpSection section : HelpSection.values()) {
+ assertThat(filename + " should have entry for HelpSection '" + section + "'",
+ configuration.getString(section.getKey()), notEmptyString());
+ }
+
+ for (HelpMessage message : HelpMessage.values()) {
+ assertThat(filename + " should have entry for HelpMessage '" + message + "'",
+ configuration.getString(message.getKey()), notEmptyString());
+ }
+
+ for (DefaultPermission defaultPermission : DefaultPermission.values()) {
+ assertThat(filename + " should have entry for DefaultPermission '" + defaultPermission + "'",
+ configuration.getString(getPathForDefaultPermission(defaultPermission)), notEmptyString());
+ }
+ }
+
+ private static String getPathForDefaultPermission(DefaultPermission defaultPermission) {
+ String path = "common.defaultPermissions.";
+ switch (defaultPermission) {
+ case ALLOWED:
+ return path + "allowed";
+ case NOT_ALLOWED:
+ return path + "notAllowed";
+ case OP_ONLY:
+ return path + "opOnly";
+ default:
+ throw new IllegalStateException("Unknown default permission '" + defaultPermission + "'");
+ }
+ }
+
+ private static Matcher notEmptyString() {
+ return both(not(emptyString())).and(not(nullValue()));
+ }
+}
diff --git a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java b/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java
index 12949c5a..94fe9a0d 100644
--- a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java
+++ b/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java
@@ -233,8 +233,8 @@ public class MessagesIntegrationTest {
@SuppressWarnings("unchecked")
private static MessageFileHandlerProvider providerReturning(File file, String defaultFile) {
MessageFileHandlerProvider handler = mock(MessageFileHandlerProvider.class);
- given(handler.initializeHandler(any(Function.class)))
- .willReturn(new MessageFileHandler(file, defaultFile));
+ given(handler.initializeHandler(any(Function.class), anyString()))
+ .willReturn(new MessageFileHandler(file, defaultFile, "/authme messages"));
return handler;
}
}
diff --git a/src/test/java/fr/xephi/authme/settings/SpawnLoaderTest.java b/src/test/java/fr/xephi/authme/settings/SpawnLoaderTest.java
index 1ca06911..edd56192 100644
--- a/src/test/java/fr/xephi/authme/settings/SpawnLoaderTest.java
+++ b/src/test/java/fr/xephi/authme/settings/SpawnLoaderTest.java
@@ -5,9 +5,8 @@ import ch.jalu.injector.testing.DelayedInjectionRunner;
import ch.jalu.injector.testing.InjectDelayed;
import com.google.common.io.Files;
import fr.xephi.authme.TestHelper;
-import fr.xephi.authme.datasource.DataSource;
-import fr.xephi.authme.service.PluginHookService;
import fr.xephi.authme.initialization.DataFolder;
+import fr.xephi.authme.service.PluginHookService;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import org.bukkit.Location;
import org.bukkit.World;
@@ -38,9 +37,6 @@ public class SpawnLoaderTest {
@Mock
private Settings settings;
- @Mock
- private DataSource dataSource;
-
@Mock
private PluginHookService pluginHookService;
diff --git a/src/test/java/tools/messages/AddJavaDocToMessageEnumTask.java b/src/test/java/tools/messages/AddJavaDocToMessageEnumTask.java
index 30a63244..44306dc8 100644
--- a/src/test/java/tools/messages/AddJavaDocToMessageEnumTask.java
+++ b/src/test/java/tools/messages/AddJavaDocToMessageEnumTask.java
@@ -45,6 +45,7 @@ public class AddJavaDocToMessageEnumTask implements AutoToolTask {
return configuration.getString(key.getKey())
.replaceAll("&[0-9a-f]", "")
.replace("&", "&")
- .replace("<", "<");
+ .replace("<", "<")
+ .replace(">", ">");
}
}