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