diff --git a/.gitignore b/.gitignore index 44ea237797a..58847f48f6d 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ /Essentials/usermap.csv /Essentials/usermap.bin /Essentials/uuids.bin +/Essentials/command-filters.yml # Build files .gradle/ diff --git a/Essentials/src/main/java/com/earth2me/essentials/AsyncTeleport.java b/Essentials/src/main/java/com/earth2me/essentials/AsyncTeleport.java index 19ab4581815..469eb3d4efe 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/AsyncTeleport.java +++ b/Essentials/src/main/java/com/earth2me/essentials/AsyncTeleport.java @@ -266,6 +266,7 @@ private void teleport(final IUser teleportee, final ITarget target, final Trade delay = event.getDelay(); Trade cashCharge = chargeFor; + String cooldownCommand = null; if (chargeFor != null) { chargeFor.isAffordableFor(teleportOwner, future); @@ -273,6 +274,9 @@ private void teleport(final IUser teleportee, final ITarget target, final Trade return; } + // When cashCharge is being reassigned below, ensure the charge knows the command we should apply cooldown on + cooldownCommand = chargeFor.getCommand(); + //This code is to make sure that commandcosts are checked in the initial world, and not in the resulting world. if (!chargeFor.getCommandCost(teleportOwner).equals(BigDecimal.ZERO)) { //By converting a command cost to a regular cost, the command cost permission isn't checked when executing the charge after teleport. @@ -291,7 +295,7 @@ private void teleport(final IUser teleportee, final ITarget target, final Trade } nowAsync(teleportee, target, cause, future); if (cashCharge != null) { - cashCharge.charge(teleportOwner, future); + cashCharge.charge(teleportOwner, cooldownCommand, future); if (future.isCompletedExceptionally()) { return; } @@ -316,6 +320,7 @@ private void teleportOther(final IUser teleporter, final IUser teleportee, final delay = event.getDelay(); Trade cashCharge = chargeFor; + String cooldownCommand = null; if (teleporter != null && chargeFor != null) { chargeFor.isAffordableFor(teleporter, future); @@ -323,6 +328,9 @@ private void teleportOther(final IUser teleporter, final IUser teleportee, final return; } + // When cashCharge is being reassigned below, ensure the charge knows the command we should apply cooldown on + cooldownCommand = chargeFor.getCommand(); + //This code is to make sure that commandcosts are checked in the initial world, and not in the resulting world. if (!chargeFor.getCommandCost(teleporter).equals(BigDecimal.ZERO)) { //By converting a command cost to a regular cost, the command cost permission isn't checked when executing the charge after teleport. @@ -343,7 +351,7 @@ private void teleportOther(final IUser teleporter, final IUser teleportee, final nowAsync(teleportee, target, cause, future); if (teleporter != null && cashCharge != null) { - cashCharge.charge(teleporter, future); + cashCharge.charge(teleporter, cooldownCommand, future); if (future.isCompletedExceptionally()) { return; } diff --git a/Essentials/src/main/java/com/earth2me/essentials/CommandFilter.java b/Essentials/src/main/java/com/earth2me/essentials/CommandFilter.java new file mode 100644 index 00000000000..7466abd04f2 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/CommandFilter.java @@ -0,0 +1,66 @@ +package com.earth2me.essentials; + +import com.google.common.base.Preconditions; +import net.ess3.api.IUser; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.regex.Pattern; + +public abstract class CommandFilter { + + public enum Type { + REGEX, + ESS + } + + private final String name; + private final Pattern pattern; + private final Integer cooldown; + private final boolean persistentCooldown; + private final BigDecimal cost; + + public CommandFilter(String name, Pattern pattern, Integer cooldown, boolean persistentCooldown, BigDecimal cost) { + Preconditions.checkNotNull(pattern); + this.name = name; + this.pattern = pattern; + this.cooldown = cooldown; + this.persistentCooldown = persistentCooldown; + this.cost = cost; + } + + public String getName() { + return name; + } + + public Pattern getPattern() { + return pattern; + } + + public boolean hasCooldown() { + return cooldown != null; + } + + public Integer getCooldown() { + return cooldown; + } + + public boolean applyCooldownTo(IUser user) { + if (!hasCooldown()) return false; + final Date expiry = new Date(System.currentTimeMillis() + cooldown); + user.addCommandCooldown(pattern, expiry, persistentCooldown); + return true; + } + + public boolean isPersistentCooldown() { + return persistentCooldown; + } + + public boolean hasCost() { + return cost != null; + } + + public BigDecimal getCost() { + return cost; + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/CommandFilters.java b/Essentials/src/main/java/com/earth2me/essentials/CommandFilters.java new file mode 100644 index 00000000000..8e1e58f561b --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/CommandFilters.java @@ -0,0 +1,125 @@ +package com.earth2me.essentials; + +import com.earth2me.essentials.config.ConfigurateUtil; +import com.earth2me.essentials.config.EssentialsConfiguration; +import net.ess3.api.IUser; +import org.spongepowered.configurate.CommentedConfigurationNode; + +import java.io.File; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +public class CommandFilters implements IConf { + + private final IEssentials ess; + private final EssentialsConfiguration config; + private Map> commandFilters; + + public CommandFilters(final IEssentials ess) { + this.ess = ess; + config = new EssentialsConfiguration(new File(ess.getDataFolder(), "command-filters.yml"), "/command-filters.yml"); + + reloadConfig(); + } + + @Override + public void reloadConfig() { + config.load(); + commandFilters = parseCommandFilters(config); + } + + private Map> parseCommandFilters(EssentialsConfiguration config) { + final Map> commandFilters = new EnumMap<>(CommandFilter.Type.class); + final CommentedConfigurationNode filterSection = config.getSection("filters"); + for (final String filterItem : ConfigurateUtil.getKeys(filterSection)) { + final CommentedConfigurationNode section = filterSection.node(filterItem); + final String patternString = section.node("pattern").getString(); + final Pattern pattern = patternString == null ? null : compileRegex(patternString); + final String command = section.node("command").getString(); + + if (pattern == null && command == null) { + ess.getLogger().warning("Invalid command filter '" + filterItem + "', filter must either define 'pattern' or 'command'!"); + continue; + } + + if (pattern != null && command != null) { + ess.getLogger().warning("Invalid command filter '" + filterItem + "', filter can't have both 'pattern' and 'command'!"); + continue; + } + + Integer cooldown = section.node("cooldown").getInt(-1); + if (cooldown < 0) { + cooldown = null; + } else { + cooldown *= 1000; // Convert to milliseconds + } + + final boolean persistentCooldown = section.node("persistent-cooldown").getBoolean(true); + final BigDecimal cost = ConfigurateUtil.toBigDecimal(section.node("cost").getString(), null); + + final String filterItemName = filterItem.toLowerCase(Locale.ENGLISH); + + if (pattern == null) { + commandFilters.computeIfAbsent(CommandFilter.Type.ESS, k -> new ArrayList<>()).add(new EssCommandFilter(filterItemName, command, compileRegex(command), cooldown, persistentCooldown, cost)); + } else { + commandFilters.computeIfAbsent(CommandFilter.Type.REGEX, k -> new ArrayList<>()).add(new RegexCommandFilter(filterItemName, pattern, cooldown, persistentCooldown, cost)); + } + } + return commandFilters; + } + + private Pattern compileRegex(String regex) { + if (regex.startsWith("^")) { + try { + return Pattern.compile(regex.substring(1)); + } catch (final PatternSyntaxException e) { + ess.getLogger().warning("Command cooldown error: " + e.getMessage()); + return null; + } + } else { + // Escape above Regex + if (regex.startsWith("\\^")) { + regex = regex.substring(1); + } + final String cmd = regex.replaceAll("\\*", ".*"); // Wildcards are accepted as asterisk * as known universally. + return Pattern.compile(cmd + "( .*)?"); // This matches arguments, if present, to "ignore" them from the feature. + } + } + + public EssentialsConfiguration getConfig() { + return config; + } + + public CommandFilter getCommandCooldown(final IUser user, final String label, CommandFilter.Type type) { + if (user.isAuthorized("essentials.commandcooldowns.bypass")) return null; + return getFilter(label, type, filter -> filter.hasCooldown() && !user.isAuthorized("essentials.commandcooldowns.bypass." + filter.getName())); + } + + public CommandFilter getCommandCost(final IUser user, final String label, CommandFilter.Type type) { + if (user.isAuthorized("essentials.nocommandcost.all")) return null; + return getFilter(label, type, filter -> filter.hasCost() && !user.isAuthorized("essentials.nocommandcost." + filter.getName())); + } + + private CommandFilter getFilter(final String label, CommandFilter.Type type, Predicate filterPredicate) { + for (CommandFilter filter : commandFilters.get(type)) { + if (!filterPredicate.test(filter)) continue; + + final boolean matches = filter.getPattern().matcher(label).matches(); + if (ess.getSettings().isDebug()) { + ess.getLogger().info(String.format("Checking command '%s' against filter '%s': %s", label, filter.getName(), matches)); + } + + if (matches) { + return filter; + } + } + return null; + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/EssCommandFilter.java b/Essentials/src/main/java/com/earth2me/essentials/EssCommandFilter.java new file mode 100644 index 00000000000..b2e2e943b4f --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/EssCommandFilter.java @@ -0,0 +1,18 @@ +package com.earth2me.essentials; + +import java.math.BigDecimal; +import java.util.regex.Pattern; + +public class EssCommandFilter extends CommandFilter { + + private final String command; + + public EssCommandFilter(String name, String command, Pattern pattern, Integer cooldown, boolean persistentCooldown, BigDecimal cost) { + super(name, pattern, cooldown, persistentCooldown, cost); + this.command = command; + } + + public String getCommand() { + return command; + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/Essentials.java b/Essentials/src/main/java/com/earth2me/essentials/Essentials.java index c36da635b34..e382889337e 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Essentials.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Essentials.java @@ -183,6 +183,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials { private transient WorldInfoProvider worldInfoProvider; private transient SignDataProvider signDataProvider; private transient Kits kits; + private transient CommandFilters commandFilters; private transient RandomTeleport randomTeleport; private transient UpdateChecker updateChecker; private transient Map commandMap = new HashMap<>(); @@ -233,6 +234,7 @@ public void setupForTesting(final Server server) throws IOException, InvalidDesc jails = new Jails(this); registerListeners(server.getPluginManager()); kits = new Kits(this); + commandFilters = new CommandFilters(this); } @Override @@ -322,6 +324,11 @@ public void onEnable() { upgrade.convertKits(); execTimer.mark("Kits"); + commandFilters = new CommandFilters(this); + confList.add(commandFilters); + upgrade.convertCommandFilters(); + execTimer.mark("CommandFilters"); + upgrade.afterSettings(); execTimer.mark("Upgrade3"); @@ -911,6 +918,11 @@ public Kits getKits() { return kits; } + @Override + public CommandFilters getCommandFilters() { + return commandFilters; + } + @Override public RandomTeleport getRandomTeleport() { return randomTeleport; diff --git a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java index e6f368a4776..f9e89a41c8e 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java +++ b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java @@ -10,6 +10,7 @@ import com.earth2me.essentials.utils.FormatUtil; import com.earth2me.essentials.utils.LocationUtil; import com.earth2me.essentials.utils.MaterialUtil; +import com.earth2me.essentials.utils.NumberUtil; import com.earth2me.essentials.utils.VersionUtil; import io.papermc.lib.PaperLib; import net.ess3.api.IEssentials; @@ -59,6 +60,7 @@ import java.io.IOException; import java.lang.management.ManagementFactory; +import java.math.BigDecimal; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Date; @@ -674,9 +676,7 @@ public void handlePlayerCommandPreprocess(final PlayerCommandPreprocessEvent eve user.updateActivityOnInteract(broadcast); } - if (ess.getSettings().isCommandCooldownsEnabled() - && !user.isAuthorized("essentials.commandcooldowns.bypass") - && (pluginCommand == null || !user.isAuthorized("essentials.commandcooldowns.bypass." + pluginCommand.getName()))) { + if (pluginCommand != null) { final int argStartIndex = effectiveCommand.indexOf(" "); final String args = argStartIndex == -1 ? "" // No arguments present : " " + effectiveCommand.substring(argStartIndex); // arguments start at argStartIndex; substring from there. @@ -705,14 +705,27 @@ public void handlePlayerCommandPreprocess(final PlayerCommandPreprocessEvent eve } if (!cooldownFound) { - final Entry cooldownEntry = ess.getSettings().getCommandCooldownEntry(fullCommand); + final CommandFilter cooldownFilter = ess.getCommandFilters().getCommandCooldown(user, fullCommand, CommandFilter.Type.REGEX); + if (cooldownFilter != null) { + if (ess.getSettings().isDebug()) { + ess.getLogger().info("Applying " + cooldownFilter.getCooldown() + "ms cooldown on /" + fullCommand + " for " + user.getName() + "."); + } + cooldownFilter.applyCooldownTo(user); + } - if (cooldownEntry != null) { + final CommandFilter costFilter = ess.getCommandFilters().getCommandCost(user, fullCommand, CommandFilter.Type.REGEX); + if (costFilter != null) { if (ess.getSettings().isDebug()) { - ess.getLogger().info("Applying " + cooldownEntry.getValue() + "ms cooldown on /" + fullCommand + " for" + user.getName() + "."); + ess.getLogger().info("Applying a cost of " + costFilter.getCost() + " on /" + fullCommand + " for " + user.getName() + "."); + } + + final BigDecimal cost = costFilter.getCost(); + if (!user.canAfford(cost) && cost.signum() > 0) { + player.sendMessage(tl("notEnoughMoney", NumberUtil.displayCurrency(cost, ess))); + event.setCancelled(true); + return; } - final Date expiry = new Date(System.currentTimeMillis() + cooldownEntry.getValue()); - user.addCommandCooldown(cooldownEntry.getKey(), expiry, ess.getSettings().isCommandCooldownPersistent(fullCommand)); + user.takeMoney(cost); } } } diff --git a/Essentials/src/main/java/com/earth2me/essentials/EssentialsUpgrade.java b/Essentials/src/main/java/com/earth2me/essentials/EssentialsUpgrade.java index 33fc06fb438..96f4b83a2d6 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/EssentialsUpgrade.java +++ b/Essentials/src/main/java/com/earth2me/essentials/EssentialsUpgrade.java @@ -463,6 +463,61 @@ public void convertKits() { ess.getLogger().info("Done converting kits."); } + public void convertCommandFilters() { + final CommandFilters commandFilters = ess.getCommandFilters(); + final EssentialsConfiguration config = commandFilters.getConfig(); + if (doneFile.getBoolean("commandFiltersYml", false)) { + return; + } + + ess.getLogger().info("Attempting to convert old command-cooldowns and -costs in config.yml to new command-filters.yml"); + + final CommentedConfigurationNode commandCooldowns = ess.getSettings().getCommandCooldowns(); + if (commandCooldowns != null) { + convertCommandCooldowns(commandCooldowns, config); + } else { + ess.getLogger().info("No command cooldowns found to migrate."); + } + + final Map commandCosts = ess.getSettings().getCommandCosts(); + if (commandCosts != null) { + convertCommandCosts(commandCosts, config); + } else { + ess.getLogger().info("No command costs found to migrate."); + } + + config.save(); + doneFile.setProperty("commandFiltersYml", true); + doneFile.save(); + ess.getLogger().info("Done converting command filters."); + } + + private void convertCommandCooldowns(CommentedConfigurationNode commandCooldowns, EssentialsConfiguration config) { + final boolean persistent = ess.getSettings().isCommandCooldownPersistent("dummy"); + for (Map.Entry entry : ConfigurateUtil.getRawMap(commandCooldowns).entrySet()) { + ess.getLogger().info("Converting cooldown \"" + entry.getKey() + "\""); + + final String key = entry.getKey().replace("\\.", "{dot}"); // Convert periods + config.setProperty("filters." + key + ".pattern", entry.getKey()); + final String cooldownKey = "filters." + key + ".cooldown"; + if (entry.getValue() instanceof Double) { + config.setProperty(cooldownKey, (double) entry.getValue()); + } else if (entry.getValue() instanceof Integer) { + config.setProperty(cooldownKey, (int) entry.getValue()); + } + config.setProperty("filters." + key + ".persistent-cooldown", persistent); + } + } + + private void convertCommandCosts(Map commandCosts, EssentialsConfiguration config) { + for (Map.Entry entry : commandCosts.entrySet()) { + ess.getLogger().info("Converting cost \"" + entry.getKey() + "\""); + + config.setProperty("filters." + entry.getKey() + ".command", entry.getKey()); + config.setProperty("filters." + entry.getKey() + ".cost", entry.getValue().toString()); + } + } + private void moveMotdRulesToFile(final String name) { if (doneFile.getBoolean("move" + name + "ToFile", false)) { return; diff --git a/Essentials/src/main/java/com/earth2me/essentials/IEssentials.java b/Essentials/src/main/java/com/earth2me/essentials/IEssentials.java index d8916a958b6..9b5f197d6eb 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/IEssentials.java +++ b/Essentials/src/main/java/com/earth2me/essentials/IEssentials.java @@ -94,6 +94,8 @@ public interface IEssentials extends Plugin { Kits getKits(); + CommandFilters getCommandFilters(); + RandomTeleport getRandomTeleport(); UpdateChecker getUpdateChecker(); diff --git a/Essentials/src/main/java/com/earth2me/essentials/ISettings.java b/Essentials/src/main/java/com/earth2me/essentials/ISettings.java index ae7814153c8..18b8ac763d9 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/ISettings.java +++ b/Essentials/src/main/java/com/earth2me/essentials/ISettings.java @@ -46,6 +46,10 @@ public interface ISettings extends IConf { char getChatQuestion(); + @Deprecated + Map getCommandCosts(); + + @Deprecated boolean isShoutDefault(); boolean isPersistShout(); @@ -54,6 +58,7 @@ public interface ISettings extends IConf { BigDecimal getCommandCost(IEssentialsCommand cmd); + @Deprecated BigDecimal getCommandCost(String label); String getCurrencySymbol(); @@ -328,16 +333,23 @@ public interface ISettings extends IConf { boolean isTeleportToCenterLocation(); + @Deprecated boolean isCommandCooldownsEnabled(); boolean isWorldChangeFlyResetEnabled(); boolean isWorldChangeSpeedResetEnabled(); + @Deprecated + CommentedConfigurationNode getCommandCooldowns(); + + @Deprecated long getCommandCooldownMs(String label); + @Deprecated Entry getCommandCooldownEntry(String label); + @Deprecated boolean isCommandCooldownPersistent(String label); boolean isNpcsInBalanceRanking(); diff --git a/Essentials/src/main/java/com/earth2me/essentials/RegexCommandFilter.java b/Essentials/src/main/java/com/earth2me/essentials/RegexCommandFilter.java new file mode 100644 index 00000000000..81674a87dce --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/RegexCommandFilter.java @@ -0,0 +1,11 @@ +package com.earth2me.essentials; + +import java.math.BigDecimal; +import java.util.regex.Pattern; + +public class RegexCommandFilter extends CommandFilter { + + public RegexCommandFilter(String name, Pattern pattern, Integer cooldown, boolean persistentCooldown, BigDecimal cost) { + super(name, pattern, cooldown, persistentCooldown, cost); + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/Settings.java b/Essentials/src/main/java/com/earth2me/essentials/Settings.java index 628271bc969..72d29a038d4 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Settings.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Settings.java @@ -377,6 +377,11 @@ public BigDecimal getCommandCost(final IEssentialsCommand cmd) { return getCommandCost(cmd.getName()); } + @Override + public Map getCommandCosts() { + return commandCosts; + } + private Map _getCommandCosts() { final Map section = ConfigurateUtil.getMap(config.getSection("command-costs")); if (!section.isEmpty()) { @@ -1561,6 +1566,11 @@ public boolean isTeleportToCenterLocation() { return config.getBoolean("teleport-to-center", true); } + @Override + public CommentedConfigurationNode getCommandCooldowns() { + return config.getSection("command-cooldowns"); + } + private Map _getCommandCooldowns() { final CommentedConfigurationNode section = config.getSection("command-cooldowns"); if (section == null) { @@ -1645,7 +1655,6 @@ public Entry getCommandCooldownEntry(final String label) { @Override public boolean isCommandCooldownPersistent(final String label) { - // TODO: enable per command cooldown specification for persistence. return config.getBoolean("command-cooldown-persistence", true); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/Trade.java b/Essentials/src/main/java/com/earth2me/essentials/Trade.java index c82bf51cbb6..9e527062618 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Trade.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Trade.java @@ -279,6 +279,10 @@ public void charge(final IUser user) throws ChargeException { } public void charge(final IUser user, final CompletableFuture future) { + charge(user, this.command, future); + } + + public void charge(final IUser user, final String cooldownCommand, final CompletableFuture future) { if (ess.getSettings().isDebug()) { ess.getLogger().log(Level.INFO, "attempting to charge user " + user.getName()); } @@ -325,6 +329,20 @@ public void charge(final IUser user, final CompletableFuture future) { if (ess.getSettings().isDebug()) { ess.getLogger().log(Level.INFO, "charge user " + user.getName() + " completed"); } + + if (cooldownCommand != null && !cooldownCommand.isEmpty()) { + final CommandFilter cooldownFilter = ess.getCommandFilters().getCommandCooldown(user, cooldownCommand, CommandFilter.Type.ESS); + if (cooldownFilter != null) { + if (ess.getSettings().isDebug()) { + ess.getLogger().info("Applying " + cooldownFilter.getCooldown() + "ms cooldown on /" + cooldownCommand + " for " + user.getName() + "."); + } + cooldownFilter.applyCooldownTo(user); + } + } + } + + public String getCommand() { + return command; } public BigDecimal getMoney() { @@ -354,8 +372,10 @@ public TradeType getType() { public BigDecimal getCommandCost(final IUser user) { BigDecimal cost = BigDecimal.ZERO; if (command != null && !command.isEmpty()) { - cost = ess.getSettings().getCommandCost(command.charAt(0) == '/' ? command.substring(1) : command); - if (cost.signum() == 0 && fallbackTrade != null) { + final CommandFilter filter = ess.getCommandFilters().getCommandCost(user, command.charAt(0) == '/' ? command.substring(1) : command, CommandFilter.Type.ESS); + if (filter != null && filter.getCost().signum() != 0) { + cost = filter.getCost(); + } else if (fallbackTrade != null) { cost = fallbackTrade.getCommandCost(user); } @@ -363,9 +383,6 @@ public BigDecimal getCommandCost(final IUser user) { ess.getLogger().log(Level.INFO, "calculated command (" + command + ") cost for " + user.getName() + " as " + cost); } } - if (cost.signum() != 0 && (user.isAuthorized("essentials.nocommandcost.all") || user.isAuthorized("essentials.nocommandcost." + command))) { - return BigDecimal.ZERO; - } return cost; } diff --git a/Essentials/src/main/resources/command-filters.yml b/Essentials/src/main/resources/command-filters.yml new file mode 100644 index 00000000000..00926a21100 --- /dev/null +++ b/Essentials/src/main/resources/command-filters.yml @@ -0,0 +1,49 @@ +# This file stores cooldowns and costs for commands. + +# Each filter must follow the following template keys: + +# [command] - the name of an EssentialsX command; these filters will ensure that the command succeeds before charging the player or starting the cooldown +# All commands do not start with a Forward Slash (/). Instead of /msg, write msg +# Sub-commands are supported: for example, "kit tools" would be look like this: + +# example: +# command: "kit-tools" +# cost: 1000 +# which would result in a 1000 cost for "/kit tools". + +# [pattern] - a regex to match any plugin's command against; these filters will not check whether the command succeeds +# Wildcards are also supported. E.g. +# - '*i*': 50 +# adds a 50 second cooldown to all commands that include the letter i + +# EssentialsX supports regex by starting the command with a caret ^ +# For example, to target commands starting with ban and not banip the following would be used: + +# example: +# pattern: "^ban([^ip])( .*)?" +# cooldown: 60 +# which would result in a 60 seconds /ban cooldown. +# Note: If you have a command that starts with ^, then you can escape it using backslash (\). e.g. \^command: 123 + +# [cooldown] - the length of time that the player will have to wait until they can use a command matching this filter again, in seconds + +# [cost] - the amount that will be deducted from the player's economy balance when they use the command + +# You need to set at least the pattern or command. + +# The "warp" example will charge 100 bucks for "/warp examplewarp", and have a cooldown of 10 seconds. +# The cooldown can be bypassed with "essentials.commandcooldowns.bypass.warp", or if you want to bypass all cooldowns: "essentials.commandcooldowns.bypass". +# The cost can be bypassed with "essentials.nocommandcost.warp", or if you want to bypass all costs: "essentials.nocommandcost.all". + +# The "commands" example will charge 10000 for "/example" and "/othercommand". +# Bypass permissions are the same as above, but now with "commands" instead of "warp". + +filters: + warp: + command: "warp-examplewarp" + cost: 100 + cooldown: 10 + + commands: + pattern: "(example|othercommand)" + cost: 10000 \ No newline at end of file diff --git a/Essentials/src/main/resources/config.yml b/Essentials/src/main/resources/config.yml index 9ce30622d36..2de881941c1 100644 --- a/Essentials/src/main/resources/config.yml +++ b/Essentials/src/main/resources/config.yml @@ -649,23 +649,7 @@ send-fly-enable-on-join: true # Give someone permission to teleport to a world with essentials.time.world.. world-time-permissions: false -# Specify cooldown for both Essentials commands and external commands as well. -# All commands do not start with a Forward Slash (/). Instead of /msg, write msg -# -# Wildcards are supported. E.g. -# - '*i*': 50 -# adds a 50 second cooldown to all commands that include the letter i -# -# EssentialsX supports regex by starting the command with a caret ^ -# For example, to target commands starting with ban and not banip the following would be used: -# '^ban([^ip])( .*)?': 60 # 60 seconds /ban cooldown. -# Note: If you have a command that starts with ^, then you can escape it using backslash (\). e.g. \^command: 123 -command-cooldowns: -# feed: 100 # 100 second cooldown on /feed command -# '*': 5 # 5 Second cooldown on all commands - -# Whether command cooldowns should be persistent past server shutdowns -command-cooldown-persistence: true +# command-cooldowns are now in command-filters.yml # Whether NPC balances should be listed in balance ranking features such as /balancetop. # NPC balances can include features like factions from FactionsUUID plugin. @@ -798,13 +782,7 @@ confirm-home-overwrite: false # Defines the balance with which new players begin. Defaults to 0. starting-balance: 0 -# Defines the cost to use the given commands PER USE. -# Some commands like /repair have sub-costs, check the wiki for more information. -command-costs: - # /example costs $1000 PER USE - #example: 1000 - # /kit tools costs $1500 PER USE - #kit-tools: 1500 +# command-costs are now in command-filters.yml # Set this to a currency symbol you want to use. # Remember, if you want to use special characters in this document,