getEnglishPlural(String word) {
+ assert word != null;
+ if (word.isEmpty())
return new NonNullPair<>("", Boolean.FALSE);
- for (final String[] p : plurals) {
- if (s.endsWith(p[1]))
- return new NonNullPair<>(s.substring(0, s.length() - p[1].length()) + p[0], Boolean.TRUE);
- if (s.endsWith(p[1].toUpperCase(Locale.ENGLISH)))
- return new NonNullPair<>(s.substring(0, s.length() - p[1].length()) + p[0].toUpperCase(Locale.ENGLISH), Boolean.TRUE);
+ for (final WordEnding ending : plurals) {
+ if (word.endsWith(ending.plural()))
+ return new NonNullPair<>(word.substring(0, word.length() - ending.plural().length()) + ending.singular(), Boolean.TRUE);
+ if (word.endsWith(ending.plural().toUpperCase(Locale.ENGLISH)))
+ return new NonNullPair<>(word.substring(0, word.length() - ending.plural().length()) + ending.singular().toUpperCase(Locale.ENGLISH), Boolean.TRUE);
}
- return new NonNullPair<>(s, Boolean.FALSE);
+ return new NonNullPair<>(word, Boolean.FALSE);
}
-
+
/**
* Gets the english plural of a word.
- *
- * @param s
+ *
+ * @param word
* @return The english plural of the given word
*/
- public static String toEnglishPlural(final String s) {
- assert s != null && s.length() != 0;
- for (final String[] p : plurals) {
- if (s.endsWith(p[0]))
- return s.substring(0, s.length() - p[0].length()) + p[1];
+ public static String toEnglishPlural(String word) {
+ assert word != null && word.length() != 0;
+ for (WordEnding ending : plurals) {
+ if (word.endsWith(ending.singular()))
+ return word.substring(0, word.length() - ending.singular().length()) + ending.plural();
}
assert false;
- return s + "s";
+ return word + "s";
}
-
+
/**
* Gets the plural of a word (or not if p is false)
- *
+ *
* @param s
* @param p
* @return The english plural of the given word, or the word itself if p is false.
@@ -322,10 +323,10 @@ public static String toEnglishPlural(final String s, final boolean p) {
return toEnglishPlural(s);
return s;
}
-
+
/**
* Adds 'a' or 'an' to the given string, depending on the first character of the string.
- *
+ *
* @param s The string to add the article to
* @return The given string with an appended a/an and a space at the beginning
* @see #A(String)
@@ -334,10 +335,10 @@ public static String toEnglishPlural(final String s, final boolean p) {
public static String a(final String s) {
return a(s, false);
}
-
+
/**
* Adds 'A' or 'An' to the given string, depending on the first character of the string.
- *
+ *
* @param s The string to add the article to
* @return The given string with an appended A/An and a space at the beginning
* @see #a(String)
@@ -346,10 +347,10 @@ public static String a(final String s) {
public static String A(final String s) {
return a(s, true);
}
-
+
/**
* Adds 'a' or 'an' to the given string, depending on the first character of the string.
- *
+ *
* @param s The string to add the article to
* @param capA Whether to use a capital a or not
* @return The given string with an appended a/an (or A/An if capA is true) and a space at the beginning
@@ -367,14 +368,14 @@ public static String a(final String s, final boolean capA) {
return "a " + s;
}
}
-
+
/**
* Gets the collision height of solid or partially-solid blocks at the center of the block.
* This is mostly for use in the {@link EffTeleport teleport effect}.
*
* This version operates on numeric ids, thus only working on
* Minecraft 1.12 or older.
- *
+ *
* @param type
* @return The block's height at the center
*/
@@ -530,14 +531,14 @@ public static CompletableFuture sendPluginMessage(Player pla
return completableFuture;
}
-
+
final static ChatColor[] styles = {ChatColor.BOLD, ChatColor.ITALIC, ChatColor.STRIKETHROUGH, ChatColor.UNDERLINE, ChatColor.MAGIC, ChatColor.RESET};
final static Map chat = new HashMap<>();
final static Map englishChat = new HashMap<>();
-
+
public final static boolean HEX_SUPPORTED = Skript.isRunningMinecraft(1, 16);
public final static boolean COPY_SUPPORTED = Skript.isRunningMinecraft(1, 15);
-
+
static {
Language.addListener(new LanguageChangeListener() {
@Override
@@ -554,21 +555,21 @@ public void onLanguageChange() {
}
});
}
-
+
@Nullable
public static String getChatStyle(final String s) {
SkriptColor color = SkriptColor.fromName(s);
-
+
if (color != null)
return color.getFormattedChat();
return chat.get(s);
}
-
+
private final static Pattern stylePattern = Pattern.compile("<([^<>]+)>");
-
+
/**
* Replaces <chat styles> in the message
- *
+ *
* @param message
* @return message with localised chat styles converted to Minecraft's format
*/
@@ -602,11 +603,11 @@ public String run(final Matcher m) {
m = ChatColor.translateAlternateColorCodes('&', "" + m);
return "" + m;
}
-
+
/**
* Replaces english <chat styles> in the message. This is used for messages in the language file as the language of colour codes is not well defined while the language is
* changing, and for some hardcoded messages.
- *
+ *
* @param message
* @return message with english chat styles converted to Minecraft's format
*/
@@ -653,7 +654,7 @@ public String run(final Matcher m) {
public static ChatColor parseHexColor(String hex) {
if (!HEX_SUPPORTED || !HEX_PATTERN.matcher(hex).matches()) // Proper hex code validation
return null;
-
+
hex = hex.replace("#", "");
try {
return ChatColor.of('#' + hex.substring(0, 6));
@@ -661,10 +662,10 @@ public static ChatColor parseHexColor(String hex) {
return null;
}
}
-
+
/**
* Gets a random value between start (inclusive) and end (exclusive)
- *
+ *
* @param start
* @param end
* @return start + random.nextInt(end - start)
@@ -733,12 +734,12 @@ public static Class highestDenominator(Class<
// See #1747 to learn how it broke returning items from functions
return (Class) (chosen == Cloneable.class ? bestGuess : chosen == Object.class ? bestGuess : chosen);
}
-
+
/**
* Parses a number that was validated to be an integer but might still result in a {@link NumberFormatException} when parsed with {@link Integer#parseInt(String)} due to
* overflow.
* This method will return {@link Integer#MIN_VALUE} or {@link Integer#MAX_VALUE} respectively if that happens.
- *
+ *
* @param s
* @return The parsed integer, {@link Integer#MIN_VALUE} or {@link Integer#MAX_VALUE} respectively
*/
@@ -750,12 +751,12 @@ public static int parseInt(final String s) {
return s.startsWith("-") ? Integer.MIN_VALUE : Integer.MAX_VALUE;
}
}
-
+
/**
* Parses a number that was validated to be an integer but might still result in a {@link NumberFormatException} when parsed with {@link Long#parseLong(String)} due to
* overflow.
* This method will return {@link Long#MIN_VALUE} or {@link Long#MAX_VALUE} respectively if that happens.
- *
+ *
* @param s
* @return The parsed long, {@link Long#MIN_VALUE} or {@link Long#MAX_VALUE} respectively
*/
@@ -767,7 +768,7 @@ public static long parseLong(final String s) {
return s.startsWith("-") ? Long.MIN_VALUE : Long.MAX_VALUE;
}
}
-
+
/**
* Gets class for name. Throws RuntimeException instead of checked one.
* Use this only when absolutely necessary.
@@ -783,7 +784,7 @@ public static Class> classForName(String name) {
throw new RuntimeException("Class not found!");
}
}
-
+
/**
* Finds the index of the last in a {@link List} that matches the given {@link Checker}.
*
@@ -807,5 +808,37 @@ public static boolean isInteger(Number... numbers) {
}
return true;
}
-
+
+ protected static class WordEnding { // To be a record in 2.10
+
+ private final String singular, plural;
+
+ private WordEnding(String singular, String plural) {
+ this.singular = singular;
+ this.plural = plural;
+ }
+
+ public String singular() {
+ return singular;
+ }
+
+ public String plural() {
+ return plural;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) return true;
+ if (!(object instanceof WordEnding)) return false;
+ WordEnding ending = (WordEnding) object;
+ return Objects.equals(singular, ending.singular) && Objects.equals(plural, ending.plural);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(singular, plural);
+ }
+
+ }
+
}
diff --git a/src/main/java/org/skriptlang/skript/bukkit/SkriptMetrics.java b/src/main/java/org/skriptlang/skript/bukkit/SkriptMetrics.java
new file mode 100644
index 00000000000..e57c4ec7700
--- /dev/null
+++ b/src/main/java/org/skriptlang/skript/bukkit/SkriptMetrics.java
@@ -0,0 +1,268 @@
+package org.skriptlang.skript.bukkit;
+
+import ch.njol.skript.Skript;
+import ch.njol.skript.SkriptConfig;
+import ch.njol.skript.config.Option;
+import ch.njol.skript.localization.Language;
+import ch.njol.skript.update.Updater;
+import ch.njol.skript.util.Version;
+import ch.njol.skript.util.chat.ChatMessages;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Table;
+import org.bstats.bukkit.Metrics;
+import org.bstats.charts.DrilldownPie;
+import org.bstats.charts.SimplePie;
+import org.jetbrains.annotations.Nullable;
+
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * helper class to handle bstats metrics
+ */
+public class SkriptMetrics {
+
+ /**
+ * Helper method to set up bstats charts on the supplied Metrics object
+ * @param metrics The Metrics object to which charts will be added.
+ */
+ public static void setupMetrics(Metrics metrics) {
+ // Enable metrics and register custom charts
+
+ // sets up the old charts to prevent data splitting due to various user version
+ setupLegacyMetrics(metrics);
+
+ // add custom version charts for easier reading:
+ metrics.addCustomChart(new DrilldownPie("drilldownPluginVersion", () -> {
+ Version version = Skript.getVersion();
+ Table table = HashBasedTable.create(1,1);
+ table.put(
+ version.getMajor() + "." + version.getMinor(), // upper label
+ version.toString(), // lower label
+ 1 // weight
+ );
+ return table.rowMap();
+ }));
+
+ metrics.addCustomChart(new DrilldownPie("drilldownMinecraftVersion", () -> {
+ Version version = Skript.getMinecraftVersion();
+ Table table = HashBasedTable.create(1,1);
+ table.put(
+ version.getMajor() + "." + version.getMinor(), // upper label
+ version.toString(), // lower label
+ 1 // weight
+ );
+ return table.rowMap();
+ }));
+
+ metrics.addCustomChart(new SimplePie("buildFlavor", () -> {
+ Updater updater = Skript.getInstance().getUpdater();
+ if (updater != null)
+ return updater.getCurrentRelease().flavor;
+ return "unknown";
+ }));
+
+ //
+ // config options
+ //
+
+ metrics.addCustomChart(new DrilldownPie("drilldownPluginLanguage", () -> {
+ String lang = Language.getName();
+ return isDefaultMap(lang, SkriptConfig.language.defaultValue());
+ }));
+
+ metrics.addCustomChart(new DrilldownPie("drilldownUpdateChecker", () -> {
+ Table table = HashBasedTable.create(1,1);
+ table.put(
+ SkriptConfig.checkForNewVersion.value().toString(), // upper label
+ SkriptConfig.updateCheckInterval.value().toString(), // lower label
+ 1 // weight
+ );
+ return table.rowMap();
+ }));
+ metrics.addCustomChart(new SimplePie("releaseChannel", SkriptConfig.releaseChannel::value));
+
+ // effect commands
+ metrics.addCustomChart(new DrilldownPie("drilldownEffectCommands", () -> {
+ Table table = HashBasedTable.create(1,1);
+ table.put(
+ SkriptConfig.enableEffectCommands.value().toString(), // upper label
+ SkriptConfig.effectCommandToken.value(), // lower label
+ 1 // weight
+ );
+ return table.rowMap();
+ }));
+ metrics.addCustomChart(new SimplePie("effectCommandsOps", () ->
+ SkriptConfig.allowOpsToUseEffectCommands.value().toString()
+ ));
+ metrics.addCustomChart(new SimplePie("logEffectCommands", () ->
+ SkriptConfig.logEffectCommands.value().toString()
+ ));
+
+ metrics.addCustomChart(new SimplePie("loadDefaultAliases", () ->
+ SkriptConfig.loadDefaultAliases.value().toString()
+ ));
+
+ metrics.addCustomChart(new SimplePie("playerVariableFix", () ->
+ SkriptConfig.enablePlayerVariableFix.value().toString()
+ ));
+ metrics.addCustomChart(new SimplePie("uuidsWithPlayers", () ->
+ SkriptConfig.usePlayerUUIDsInVariableNames.value().toString()
+ ));
+
+ metrics.addCustomChart(new DrilldownPie("drilldownDateFormat", () -> {
+ String value = ((SimpleDateFormat) SkriptConfig.dateFormat.value()).toPattern();
+ String defaultValue = ((SimpleDateFormat) SkriptConfig.dateFormat.defaultValue()).toPattern();
+ return isDefaultMap(value, defaultValue, "default");
+ }));
+
+ metrics.addCustomChart(new DrilldownPie("drilldownLogVerbosity", () -> {
+ String verbosity = SkriptConfig.verbosity.value().name().toLowerCase(Locale.ENGLISH).replace('_', ' ');
+ return isDefaultMap(verbosity, SkriptConfig.verbosity.defaultValue());
+ }));
+
+ metrics.addCustomChart(new DrilldownPie("drilldownPluginPriority", () -> {
+ String priority = SkriptConfig.defaultEventPriority.value().name().toLowerCase(Locale.ENGLISH).replace('_', ' ');
+ return isDefaultMap(priority, SkriptConfig.defaultEventPriority.defaultValue());
+ }));
+ metrics.addCustomChart(new SimplePie("cancelledByDefault", () ->
+ SkriptConfig.listenCancelledByDefault.value().toString()
+ ));
+
+ metrics.addCustomChart(new DrilldownPie("drilldownNumberAccuracy", () ->
+ isDefaultMap(SkriptConfig.numberAccuracy)
+ ));
+
+ metrics.addCustomChart(new DrilldownPie("drilldownMaxTargetDistance", () ->
+ isDefaultMap(SkriptConfig.maxTargetBlockDistance)
+ ));
+
+ metrics.addCustomChart(new SimplePie("caseSensitiveFunctions", () ->
+ SkriptConfig.caseSensitive.value().toString()
+ ));
+ metrics.addCustomChart(new SimplePie("caseSensitiveVariables", () ->
+ SkriptConfig.caseInsensitiveVariables.value().toString()
+ ));
+ metrics.addCustomChart(new SimplePie("caseSensitiveCommands", () ->
+ SkriptConfig.caseInsensitiveCommands.value().toString()
+ ));
+
+ metrics.addCustomChart(new SimplePie("disableSaveWarnings", () ->
+ SkriptConfig.disableObjectCannotBeSavedWarnings.value().toString()
+ ));
+ metrics.addCustomChart(new SimplePie("disableAndOrWarnings", () ->
+ SkriptConfig.disableMissingAndOrWarnings.value().toString()
+ ));
+ metrics.addCustomChart(new SimplePie("disableStartsWithWarnings", () ->
+ SkriptConfig.disableVariableStartingWithExpressionWarnings.value().toString()
+ ));
+
+ metrics.addCustomChart(new SimplePie("softApiExceptions", () ->
+ SkriptConfig.apiSoftExceptions.value().toString()
+ ));
+
+ metrics.addCustomChart(new SimplePie("timingsStatus", () -> {
+ if (!Skript.classExists("co.aikar.timings.Timings"))
+ return "unsupported";
+ return SkriptConfig.enableTimings.value().toString();
+ }));
+
+ metrics.addCustomChart(new SimplePie("parseLinks", () ->
+ ChatMessages.linkParseMode.name().toLowerCase(Locale.ENGLISH)
+ ));
+
+ metrics.addCustomChart(new SimplePie("colorResetCodes", () ->
+ SkriptConfig.colorResetCodes.value().toString()
+ ));
+
+ metrics.addCustomChart(new SimplePie("keepLastUsage", () ->
+ SkriptConfig.keepLastUsageDates.value().toString()
+ ));
+
+ metrics.addCustomChart(new DrilldownPie("drilldownParsetimeWarningThreshold", () ->
+ isDefaultMap(SkriptConfig.longParseTimeWarningThreshold, "disabled")
+ ));
+ }
+
+ /**
+ * Helper method to set up legacy charts (pre 2.9.2)
+ * @param metrics The Metrics object to which charts will be added.
+ */
+ private static void setupLegacyMetrics(Metrics metrics) {
+ // Enable metrics and register legacy charts
+ metrics.addCustomChart(new SimplePie("pluginLanguage", Language::getName));
+ metrics.addCustomChart(new SimplePie("updateCheckerEnabled", () ->
+ SkriptConfig.checkForNewVersion.value().toString()
+ ));
+ metrics.addCustomChart(new SimplePie("logVerbosity", () ->
+ SkriptConfig.verbosity.value().name().toLowerCase(Locale.ENGLISH).replace('_', ' ')
+ ));
+ metrics.addCustomChart(new SimplePie("pluginPriority", () ->
+ SkriptConfig.defaultEventPriority.value().name().toLowerCase(Locale.ENGLISH).replace('_', ' ')
+ ));
+ metrics.addCustomChart(new SimplePie("effectCommands", () ->
+ SkriptConfig.enableEffectCommands.value().toString()
+ ));
+ metrics.addCustomChart(new SimplePie("maxTargetDistance", () ->
+ SkriptConfig.maxTargetBlockDistance.value().toString()
+ ));
+ }
+
+ /**
+ * Provides a Map for use with a {@link DrilldownPie} chart. Meant to be used in cases where a single default option has majority share, with many or custom alternative options.
+ * Creates a chart where the default option is presented against "other", then clicking on "other" shows the alternative options.
+ * @param value The option the user chose.
+ * @param defaultValue The default option for this chart.
+ * @return A Map that can be returned directly to a {@link DrilldownPie}.
+ * @param The type of the option.
+ */
+ private static Map> isDefaultMap(@Nullable T value, T defaultValue) {
+ return isDefaultMap(value, defaultValue ,defaultValue.toString());
+ }
+
+ /**
+ * Provides a Map for use with a {@link DrilldownPie} chart. Meant to be used in cases where a single default option has majority share, with many or custom alternative options.
+ * Creates a chart where the default option is presented against "other", then clicking on "other" shows the alternative options.
+ * @param value The option the user chose.
+ * @param defaultValue The default option for this chart.
+ * @param defaultLabel The label to use as the default option for this chart
+ * @return A Map that can be returned directly to a {@link DrilldownPie}.
+ * @param The type of the option.
+ */
+ private static Map> isDefaultMap(@Nullable T value, @Nullable T defaultValue, String defaultLabel) {
+ Table table = HashBasedTable.create(1,1);
+ table.put(
+ Objects.equals(value, defaultValue) ? defaultLabel : "other", // upper label
+ String.valueOf(value), // lower label
+ 1 // weight
+ );
+ return table.rowMap();
+ }
+
+ /**
+ * Provides a Map for use with a {@link DrilldownPie} chart. Meant to be used in cases where a single default option has majority share, with many or custom alternative options.
+ * Creates a chart where the default option is presented against "other", then clicking on "other" shows the alternative options.
+ * @param option The {@link Option} from which to pull the current and default values
+ * @return A Map that can be returned directly to a {@link DrilldownPie}.
+ * @param The type of the option.
+ */
+ private static Map> isDefaultMap(Option option) {
+ return isDefaultMap(option.value(), option.defaultValue(), option.defaultValue().toString());
+
+ }
+
+ /**
+ * Provides a Map for use with a {@link DrilldownPie} chart. Meant to be used in cases where a single default option has majority share, with many or custom alternative options.
+ * Creates a chart where the default option is presented against "other", then clicking on "other" shows the alternative options.
+ * @param option The {@link Option} from which to pull the current and default values
+ * @param defaultLabel The label to use as the default option for this chart
+ * @return A Map that can be returned directly to a {@link DrilldownPie}.
+ * @param The type of the option.
+ */
+ private static Map> isDefaultMap(Option option, String defaultLabel) {
+ return isDefaultMap(option.value(), option.defaultValue(), defaultLabel);
+ }
+
+}
diff --git a/src/main/resources/config.sk b/src/main/resources/config.sk
index ff5e1e9e40a..eb0b295c288 100644
--- a/src/main/resources/config.sk
+++ b/src/main/resources/config.sk
@@ -91,9 +91,9 @@ load default aliases: true
player variable fix: true
-# Whether to enable the player variable fix if a player has rejoined and was reciding inside a variable.
-# Player objects inside a variable(list or normal) are not updated to the new player object
-# A server creates whenever a player rejoins.
+# Whether to enable the player variable fix if a player has rejoined and was residing inside a variable.
+# Player objects inside a variable (list or normal) are not updated to the new player object
+# that a server creates whenever a player rejoins.
# Basically the variable holds the old player object when a player has rejoined thus rendering the variable kinda broken.
# This fix should work around that and whenever a invalid(old) player object is attempted to be get through a variable
# It will check if the player is online and then get the valid(new) player object and update the variable object to that one.
diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang
index 59fa474fec6..fec454c2ac5 100644
--- a/src/main/resources/lang/default.lang
+++ b/src/main/resources/lang/default.lang
@@ -2227,7 +2227,7 @@ attribute types:
generic.scale: generic scale, scale
generic.step_height: generic step height, step height
generic.water_movement_efficiency: generic water movement efficiency, water movement efficiency
- horse_jump_strength: horse jump strength
+ horse.jump_strength: horse jump strength
player.block_break_speed: player block break speed, block break speed
player.block_interaction_range: player block interaction range, block interaction range
player.entity_interaction_range: player entity interaction range, entity interaction range
diff --git a/src/main/resources/lang/dutch.lang b/src/main/resources/lang/dutch.lang
new file mode 100644
index 00000000000..8169cc1286b
--- /dev/null
+++ b/src/main/resources/lang/dutch.lang
@@ -0,0 +1,191 @@
+# Default Dutch language file
+
+# Which version of Skript this language file was written for
+version: @version@
+
+# What null (nothing) should show up as in a string/text
+none:
+
+# -- Skript --
+skript:
+ copyright: ~ gemaakt door & © Peter Güttinger oftewel Njol ~
+ prefix: [Skript]
+ quotes error: Ongeldig gebruik van aanhalingstekens ("). Als je aanhalingstekens wil gebruiken in "aangehaalde tekst", verdubbel ze dan: "".
+ brackets error: Ongeldige hoeveelheid of plaatsing van haakjes. Zorg ervoor dat elke openend haakje een bijbehorend sluitend haakje heeft.
+ invalid reload: Skript mag alleen worden herladen door Bukkits '/reload' of Skripts '/skript reload' commando.
+ no scripts: Er zijn geen scripts gevonden, misschien moet je er een paar schrijven ;)
+ no errors: Alle scripts zijn zonder fouten geladen.
+ scripts loaded: %s scripts zijn geladen met in totaal %s structuren in %s
+ finished loading: Klaar met laden.
+
+# -- Skript command --
+skript command:
+ usage: Gebruiksaanwijzing:
+ help:
+ description: Skripts hoofdcommando
+ help: Toont deze hulpmelding. Gebruik '/skript reload/enable/disable/update' voor meer informatie.
+ reload:
+ description: Herlaadt een specifiek script, alle scripts, de configuratie of alles
+ all: Herlaadt de configuratie, alle alliassenconfiguraties en alle scripts
+ config: Herlaadt de hoofdconfiguratie
+ aliases: Herlaadt de alliassenconfiguratie (aliases-english.zip of plugin jar)
+ scripts: Herlaadt alle scripts
+