From 9bb234169dedba755c4e2ec1a277e79923cabcae Mon Sep 17 00:00:00 2001 From: EpicKnarvik97 Date: Wed, 2 Mar 2022 00:37:00 +0100 Subject: [PATCH] Adds various improvements, fixes and a new feature Adds and option to match any paid sign condition instead of all conditions Adds checks for whether line indices are outside the allowed range Disallows any invalid regular expressions in sign conditions --- README.md | 5 ++- .../paidsigns/command/AddCommand.java | 23 ++++++---- .../command/AddConditionCommand.java | 27 ++++++++++++ .../paidsigns/command/AddTabCompleter.java | 4 ++ .../paidsigns/command/ListCommand.java | 21 ++++++---- .../command/RemoveConditionCommand.java | 14 ++++--- .../paidsigns/container/PaidSign.java | 42 ++++++++++++------- .../formatting/TranslatableMessage.java | 5 +++ .../paidsigns/manager/PaidSignManager.java | 4 +- src/main/resources/plugin.yml | 2 +- src/main/resources/strings.yml | 5 ++- 11 files changed, 110 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 9ef0e25..99892b8 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ As this plugin only listens to sign change events, there are some limitations: ## Commands -* /addpaidsign \[permission] \[ignore case] \[ignore color] +* /addpaidsign \[permission] \[ignore case] \[ignore color] \[match any condition] * /addpaidsigncondition \[executeRegEx] \[ignoreCase] \[ignoreColor] * /listpaidsigns \[name (of a paid sign)] \[line number] @@ -35,6 +35,8 @@ This command adds a new paid sign that does nothing until a condition is added. default uses the config file value). * ignore color - Whether any condition of the paid sign should ignore color by default, when matching against text ( default uses the config file value). +* match any condition - Whether to trigger a paid sign match if a single one of the sign's conditions is true. This is + mainly useful if several lines may contain the match string, or if trying to match a word. ### /addpaidsigncondition @@ -79,6 +81,7 @@ Removes a registered paid sign ## Configuration options +* language - The language to use for all messages displayed to players * ignoreCase - Whether to ignore the case (lowercase/uppercase) of the paid sign text. The option can be set on a per-sign basis, but this value is used if not specified. The correct value depends on whether the plugin signs it should match are case-sensitive or not. diff --git a/src/main/java/net/knarcraft/paidsigns/command/AddCommand.java b/src/main/java/net/knarcraft/paidsigns/command/AddCommand.java index 82f2fd5..5a89fbf 100644 --- a/src/main/java/net/knarcraft/paidsigns/command/AddCommand.java +++ b/src/main/java/net/knarcraft/paidsigns/command/AddCommand.java @@ -45,26 +45,31 @@ public class AddCommand extends TokenizedCommand { if (argumentSize > 4) { ignoreColor = OptionState.fromString(arguments.get(4)); } + boolean matchAnyCondition = false; + if (argumentSize > 5) { + matchAnyCondition = Boolean.parseBoolean(arguments.get(5)); + } - return createPaidSign(sender, signName, cost, permission, ignoreCase, ignoreColor); + return createPaidSign(sender, signName, cost, permission, ignoreCase, ignoreColor, matchAnyCondition); } /** * Creates a new paid sign with the given user input * - * @param sender

The command sender that called the add command

- * @param signName

The name of the new paid sign

- * @param cost

The cost of the new paid sign

- * @param permission

The permission required for creating the sign represented by the paid sign

- * @param ignoreCase

Whether to ignore case for the paid sign's conditions

- * @param ignoreColor

Whether to ignore color for the paid sign's conditions

+ * @param sender

The command sender that called the add command

+ * @param signName

The name of the new paid sign

+ * @param cost

The cost of the new paid sign

+ * @param permission

The permission required for creating the sign represented by the paid sign

+ * @param ignoreCase

Whether to ignore case for the paid sign's conditions

+ * @param ignoreColor

Whether to ignore color for the paid sign's conditions

+ * @param matchAnyCondition

Whether to treat any matching condition as a sign match

* @return

True if the paid sign was successfully created and registered

*/ private boolean createPaidSign(CommandSender sender, String signName, double cost, String permission, - OptionState ignoreCase, OptionState ignoreColor) { + OptionState ignoreCase, OptionState ignoreColor, boolean matchAnyCondition) { PaidSignManager manager = PaidSigns.getInstance().getSignManager(); try { - PaidSign sign = new PaidSign(signName, cost, permission, ignoreCase, ignoreColor); + PaidSign sign = new PaidSign(signName, cost, permission, ignoreCase, ignoreColor, matchAnyCondition); if (manager.getPaidSign(signName) != null) { sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_NAME_DUPLICATE)); return false; diff --git a/src/main/java/net/knarcraft/paidsigns/command/AddConditionCommand.java b/src/main/java/net/knarcraft/paidsigns/command/AddConditionCommand.java index 0ea04fe..f42febc 100644 --- a/src/main/java/net/knarcraft/paidsigns/command/AddConditionCommand.java +++ b/src/main/java/net/knarcraft/paidsigns/command/AddConditionCommand.java @@ -11,6 +11,8 @@ import org.bukkit.command.CommandSender; import org.jetbrains.annotations.NotNull; import java.io.IOException; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; /** * A representation of the command for adding a new match condition for a sign @@ -29,6 +31,10 @@ public class AddConditionCommand extends TokenizedCommand { short lineNumber; try { lineNumber = (short) (Short.parseShort(arguments.get(1)) - 1); + if (lineNumber < 0 || lineNumber > 3) { + sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_INVALID_NUMBER)); + return false; + } } catch (NumberFormatException exception) { sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_INVALID_NUMBER)); return false; @@ -37,6 +43,9 @@ public class AddConditionCommand extends TokenizedCommand { boolean executeRegEx = false; if (argumentSize > 3) { executeRegEx = Boolean.parseBoolean(arguments.get(3)); + if (executeRegEx && !testRegEx(sender, stringToMatch)) { + return false; + } } OptionState ignoreCase = OptionState.DEFAULT; if (argumentSize > 4) { @@ -49,6 +58,24 @@ public class AddConditionCommand extends TokenizedCommand { return addCondition(name, lineNumber, stringToMatch, executeRegEx, ignoreCase, ignoreColor, sender); } + /** + * Tests whether the given regular expression is valid + * + * @param sender

The command sender to notify if the regular expression is invalid

+ * @param regularExpression

The regular expression to test

+ * @return

True if the regular expression is valid

+ */ + private boolean testRegEx(CommandSender sender, String regularExpression) { + try { + Pattern.compile(regularExpression); + return true; + } catch (PatternSyntaxException exception) { + sender.sendMessage(StringFormatter.getTranslatedErrorMessage( + TranslatableMessage.ERROR_INVALID_REGULAR_EXPRESSION)); + return false; + } + } + /** * Uses the given input to add a paid sign condition * diff --git a/src/main/java/net/knarcraft/paidsigns/command/AddTabCompleter.java b/src/main/java/net/knarcraft/paidsigns/command/AddTabCompleter.java index fa984c7..d37b45c 100644 --- a/src/main/java/net/knarcraft/paidsigns/command/AddTabCompleter.java +++ b/src/main/java/net/knarcraft/paidsigns/command/AddTabCompleter.java @@ -24,6 +24,7 @@ public class AddTabCompleter extends TokenizedTabCompleter { private static List plugins; private static Map> permissions; private static List options; + private static List booleans; @Nullable @Override @@ -45,6 +46,8 @@ public class AddTabCompleter extends TokenizedTabCompleter { return TabCompleteHelper.filterMatchingStartsWith(options, arguments.get(3)); } else if (argumentSize == 5) { return TabCompleteHelper.filterMatchingStartsWith(options, arguments.get(4)); + } else if (argumentSize == 6) { + return TabCompleteHelper.filterMatchingStartsWith(booleans, arguments.get(5)); } return new ArrayList<>(); } @@ -131,6 +134,7 @@ public class AddTabCompleter extends TokenizedTabCompleter { names = TabCompleteHelper.getExamplePaidSignNames(); costs = TabCompleteHelper.getCosts(); options = TabCompleteHelper.getOptionStates(); + booleans = TabCompleteHelper.getBooleans(); } } diff --git a/src/main/java/net/knarcraft/paidsigns/command/ListCommand.java b/src/main/java/net/knarcraft/paidsigns/command/ListCommand.java index da173a1..0fdef4e 100644 --- a/src/main/java/net/knarcraft/paidsigns/command/ListCommand.java +++ b/src/main/java/net/knarcraft/paidsigns/command/ListCommand.java @@ -65,20 +65,24 @@ public class ListCommand extends TokenizedCommand { if (argumentSize < 2) { displayPaidSign(sender, paidSign); } else { - short signLine; + short lineNumber; try { - signLine = (short) (Short.parseShort(arguments.get(1)) - 1); + lineNumber = (short) (Short.parseShort(arguments.get(1)) - 1); + if (lineNumber < 0 || lineNumber > 3) { + sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_INVALID_NUMBER)); + return false; + } } catch (NumberFormatException exception) { sender.sendMessage(getTranslatedErrorMessage(TranslatableMessage.ERROR_INVALID_NUMBER)); return false; } - if (!paidSign.getConditions().containsKey(signLine)) { + if (!paidSign.getConditions().containsKey(lineNumber)) { sender.sendMessage(replacePlaceholder(getTranslatedErrorMessage( - TranslatableMessage.ERROR_NO_SUCH_CONDITION), "{line}", String.valueOf(signLine))); + TranslatableMessage.ERROR_NO_SUCH_CONDITION), "{line}", String.valueOf(lineNumber))); return false; } - PaidSignCondition condition = paidSign.getConditions().get(signLine); - displayPaidSignCondition(sender, paidSign.getName(), signLine, condition); + PaidSignCondition condition = paidSign.getConditions().get(lineNumber); + displayPaidSignCondition(sender, paidSign.getName(), lineNumber, condition); } return true; } @@ -117,9 +121,10 @@ public class ListCommand extends TokenizedCommand { sender.sendMessage(replacePlaceholders(Translator.getTranslatedMessage( TranslatableMessage.PAID_SIGN_INFO), new String[]{"{name}", "{cost}", "{permission}", "{case}", - "{color}", "{conditions}"}, new String[]{paidSign.getName(), String.valueOf(paidSign.getCost()), + "{color}", "{any}", "{conditions}"}, new String[]{paidSign.getName(), String.valueOf(paidSign.getCost()), paidSign.getPermission(), translateBoolean(paidSign.getIgnoreCase()), - translateBoolean(paidSign.getIgnoreColor()), conditions.toString()})); + translateBoolean(paidSign.getIgnoreColor()), translateBoolean(paidSign.matchAnyCondition()), + conditions.toString()})); } } diff --git a/src/main/java/net/knarcraft/paidsigns/command/RemoveConditionCommand.java b/src/main/java/net/knarcraft/paidsigns/command/RemoveConditionCommand.java index fc04247..133ebb5 100644 --- a/src/main/java/net/knarcraft/paidsigns/command/RemoveConditionCommand.java +++ b/src/main/java/net/knarcraft/paidsigns/command/RemoveConditionCommand.java @@ -24,9 +24,13 @@ public class RemoveConditionCommand extends TokenizedCommand { } String name = arguments.get(0); - short line; + short lineNumber; try { - line = (short) (Short.parseShort(arguments.get(1)) - 1); + lineNumber = (short) (Short.parseShort(arguments.get(1)) - 1); + if (lineNumber < 0 || lineNumber > 3) { + sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_INVALID_NUMBER)); + return false; + } } catch (NumberFormatException exception) { sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_INVALID_NUMBER)); return false; @@ -38,13 +42,13 @@ public class RemoveConditionCommand extends TokenizedCommand { return false; } - if (!sign.getConditions().containsKey(line)) { + if (!sign.getConditions().containsKey(lineNumber)) { sender.sendMessage(StringFormatter.replacePlaceholder(StringFormatter.getTranslatedErrorMessage( - TranslatableMessage.ERROR_NO_SUCH_CONDITION), "{line}", String.valueOf((line + 1)))); + TranslatableMessage.ERROR_NO_SUCH_CONDITION), "{line}", String.valueOf((lineNumber + 1)))); return false; } - sign.removeCondition(line); + sign.removeCondition(lineNumber); try { signManager.saveSigns(); } catch (IOException e) { diff --git a/src/main/java/net/knarcraft/paidsigns/container/PaidSign.java b/src/main/java/net/knarcraft/paidsigns/container/PaidSign.java index 4b95d58..18a3223 100644 --- a/src/main/java/net/knarcraft/paidsigns/container/PaidSign.java +++ b/src/main/java/net/knarcraft/paidsigns/container/PaidSign.java @@ -5,8 +5,6 @@ import net.knarcraft.paidsigns.property.OptionState; import java.util.HashMap; import java.util.Map; -import java.util.logging.Level; -import java.util.regex.PatternSyntaxException; /** * A representation of a paid sign @@ -18,18 +16,21 @@ public class PaidSign { private final String permission; private final OptionState ignoreCase; private final OptionState ignoreColor; + private final boolean matchAnyCondition; private final Map conditions = new HashMap<>(); /** * Instantiates a new paid sign * - * @param name

A recognizable, unique, name used to identify this paid sign

- * @param cost

The cost of creating this paid sign

- * @param permission

The permission required to create the sign this paid sign matches

- * @param ignoreCase

Whether to ignore case when looking for this permission sign

- * @param ignoreColor

Whether to ignore color when looking for this permission sign

+ * @param name

A recognizable, unique, name used to identify this paid sign

+ * @param cost

The cost of creating this paid sign

+ * @param permission

The permission required to create the sign this paid sign matches

+ * @param ignoreCase

Whether to ignore case when looking for this permission sign

+ * @param ignoreColor

Whether to ignore color when looking for this permission sign

+ * @param matchAnyCondition

Whether to treat a match for any condition as a sign match

*/ - public PaidSign(String name, double cost, String permission, OptionState ignoreCase, OptionState ignoreColor) { + public PaidSign(String name, double cost, String permission, OptionState ignoreCase, OptionState ignoreColor, + boolean matchAnyCondition) { if (name == null || name.trim().isBlank()) { throw new IllegalArgumentException("Name cannot be empty"); } @@ -44,6 +45,7 @@ public class PaidSign { this.permission = permission; this.ignoreCase = ignoreCase; this.ignoreColor = ignoreColor; + this.matchAnyCondition = matchAnyCondition; } /** @@ -100,6 +102,15 @@ public class PaidSign { return OptionState.getBooleanValue(this.ignoreColor, PaidSigns.getInstance().ignoreColor()); } + /** + * Gets whether to treat a match for any condition as a sign match + * + * @return

Whether to treat a match for any condition as a sign match

+ */ + public boolean matchAnyCondition() { + return this.matchAnyCondition; + } + /** * Checks whether this paid sign matches the given set of sign lines * @@ -111,17 +122,18 @@ public class PaidSign { if (this.conditions.isEmpty()) { return false; } - boolean success = true; + boolean matchAny = matchAnyCondition(); + boolean success = !matchAny; for (short i = 0; i < 4; i++) { PaidSignCondition condition = this.conditions.get(i); if (condition != null) { - try { - success = success && condition.test(lines[i]); - } catch (PatternSyntaxException exception) { - PaidSigns.getInstance().getLogger().log(Level.SEVERE, "The paid sign condition match string:" + - " " + condition.getStringToMatch() + " belonging to sign: " + this.getName() + - " is not a valid regular expression."); + boolean conditionMatches = condition.test(lines[i]); + if (matchAny) { + success |= conditionMatches; + } else { + success &= conditionMatches; } + } } return success; diff --git a/src/main/java/net/knarcraft/paidsigns/formatting/TranslatableMessage.java b/src/main/java/net/knarcraft/paidsigns/formatting/TranslatableMessage.java index 7a18f34..b0219d5 100644 --- a/src/main/java/net/knarcraft/paidsigns/formatting/TranslatableMessage.java +++ b/src/main/java/net/knarcraft/paidsigns/formatting/TranslatableMessage.java @@ -115,4 +115,9 @@ public enum TranslatableMessage { */ ERROR_CANNOT_AFFORD, + /** + * The error to display if an invalid regular expression is provided + */ + ERROR_INVALID_REGULAR_EXPRESSION, + } diff --git a/src/main/java/net/knarcraft/paidsigns/manager/PaidSignManager.java b/src/main/java/net/knarcraft/paidsigns/manager/PaidSignManager.java index 68972ed..359f3d8 100644 --- a/src/main/java/net/knarcraft/paidsigns/manager/PaidSignManager.java +++ b/src/main/java/net/knarcraft/paidsigns/manager/PaidSignManager.java @@ -98,7 +98,8 @@ public final class PaidSignManager { String permission = signSection.getString(name + ".permission"); OptionState ignoreCase = OptionState.getFromBoolean(signSection.getBoolean(name + ".ignoreCase")); OptionState ignoreColor = OptionState.getFromBoolean(signSection.getBoolean(name + ".ignoreColor")); - PaidSign sign = new PaidSign(name, cost, permission, ignoreCase, ignoreColor); + boolean matchAnyCondition = signSection.getBoolean(name + ".matchAnyCondition"); + PaidSign sign = new PaidSign(name, cost, permission, ignoreCase, ignoreColor, matchAnyCondition); loadConditions(signSection, sign); paidSigns.put(name, sign); } @@ -150,6 +151,7 @@ public final class PaidSignManager { signSection.set(name + ".permission", sign.getPermission()); signSection.set(name + ".ignoreCase", sign.getIgnoreCase()); signSection.set(name + ".ignoreColor", sign.getIgnoreColor()); + signSection.set(name + ".matchAnyCondition", sign.matchAnyCondition()); ConfigurationSection conditionsSection = signSection.createSection(name + ".conditions"); Map signConditions = sign.getConditions(); for (short lineIndex : signConditions.keySet()) { diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 88afa1e..5fbd018 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -10,7 +10,7 @@ website: https://git.knarcraft.net/EpicKnarvik97/PaidSigns commands: addpaidsign: description: Used to add a new paid sign - usage: / [permission] [ignore case] [ignore color] + usage: / [permission] [ignore case] [ignore color] [match any condition] permission: paidsigns.manage addpaidsigncondition: description: Used to add a new match condition for a paid sign diff --git a/src/main/resources/strings.yml b/src/main/resources/strings.yml index e068abf..28dbac3 100644 --- a/src/main/resources/strings.yml +++ b/src/main/resources/strings.yml @@ -9,7 +9,7 @@ en: SUCCESS_REFUNDED: "&bYou were refunded &3{cost} {unit} &bfor your broken sign" PAID_SIGNS_INFO: "&f---&3Paid signs&f---{signs}\n&f-----------" PAID_SIGNS_INFO_FORMAT: "\n&f| &3{name}" - PAID_SIGN_INFO: "&f---&3Paid sign&f---\n&f| &bName: &3{name}\n&f| &bCost: &3{cost}\n&f| &bPermission: &3{permission}\n&f| &bIgnore case: &3{case}\n&f| &bIgnore color: &3{color}\n&f| &bSign conditions: &3{conditions}\n&f---------------" + PAID_SIGN_INFO: "&f---&3Paid sign&f---\n&f| &bName: &3{name}\n&f| &bCost: &3{cost}\n&f| &bPermission: &3{permission}\n&f| &bIgnore case: &3{case}\n&f| &bIgnore color: &3{color}\n&f| &bMatch any condition: &3{any}\n&f| &bSign conditions: &3{conditions}\n&f---------------" PAID_SIGN_INFO_CONDITION_FORMAT: "\n&f| &b{line}: &3{condition}" PAID_SIGN_CONDITION_INFO: "&f---&3Paid sign condition&f---\n&f| &bPaid sign name: &3{name}\n&f| &bCondition line: &3{line}\n&f| &bMatch string: &3{match}\n&f| &bExecute RegEx: &3{regex}\n&f| &bIgnore case: &3{case}\n&f| &bIgnore color: &3{color}\n&f---------------" BOOLEAN_TRUE: "&2true" @@ -20,4 +20,5 @@ en: ERROR_INVALID_INPUT: "&bInvalid input: {input}" ERROR_PAID_SIGN_NOT_FOUND: "&bNo such paid sign exists" ERROR_NO_SUCH_CONDITION: "&bThe paid sign you specified has no condition for line {line}" - ERROR_CANNOT_AFFORD: "&bYou cannot afford to create this sign" \ No newline at end of file + ERROR_CANNOT_AFFORD: "&bYou cannot afford to create this sign" + ERROR_INVALID_REGULAR_EXPRESSION: "&bThe provided regular expression is invalid" \ No newline at end of file