Skip to content

Commit 7739d94

Browse files
committed
Implementation of CommandFilters, replacing cooldowns and costs
1 parent 318df64 commit 7739d94

File tree

11 files changed

+395
-37
lines changed

11 files changed

+395
-37
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.earth2me.essentials;
2+
3+
import net.ess3.api.IUser;
4+
5+
import java.math.BigDecimal;
6+
import java.util.Date;
7+
import java.util.regex.Pattern;
8+
9+
public class CommandFilter {
10+
11+
private final String name;
12+
private final String command;
13+
private final Pattern pattern;
14+
private final Integer cooldown;
15+
private final boolean persistentCooldown;
16+
private final BigDecimal cost;
17+
18+
public CommandFilter(String name, String command, Pattern pattern, Integer cooldown, boolean persistentCooldown, BigDecimal cost) {
19+
this.name = name;
20+
this.command = command;
21+
this.pattern = pattern;
22+
this.cooldown = cooldown;
23+
this.persistentCooldown = persistentCooldown;
24+
this.cost = cost;
25+
}
26+
27+
public String getName() {
28+
return name;
29+
}
30+
31+
public String getCommand() {
32+
return command;
33+
}
34+
35+
public boolean hasCommand() {
36+
return command != null;
37+
}
38+
39+
public Pattern getPattern() {
40+
return pattern;
41+
}
42+
43+
public boolean hasCooldown() {
44+
return cooldown != null;
45+
}
46+
47+
public Integer getCooldown() {
48+
return cooldown;
49+
}
50+
51+
public boolean applyCooldownTo(IUser user) {
52+
if (!hasCooldown()) return false;
53+
final Date expiry = new Date(System.currentTimeMillis() + cooldown);
54+
user.addCommandCooldown(pattern, expiry, persistentCooldown);
55+
return true;
56+
}
57+
58+
public boolean isPersistentCooldown() {
59+
return persistentCooldown;
60+
}
61+
62+
public boolean hasCost() {
63+
return cost != null;
64+
}
65+
66+
public BigDecimal getCost() {
67+
return cost;
68+
}
69+
}
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package com.earth2me.essentials;
2+
3+
import net.ess3.api.IUser;
4+
import org.bukkit.configuration.ConfigurationSection;
5+
import org.bukkit.configuration.MemoryConfiguration;
6+
7+
import java.io.File;
8+
import java.math.BigDecimal;
9+
import java.util.HashMap;
10+
import java.util.Locale;
11+
import java.util.Map;
12+
import java.util.Objects;
13+
import java.util.function.Predicate;
14+
import java.util.regex.Pattern;
15+
import java.util.regex.PatternSyntaxException;
16+
17+
public class CommandFilters implements IConf {
18+
19+
private final IEssentials essentials;
20+
private final EssentialsConf config;
21+
private ConfigurationSection filters;
22+
private Map<String, CommandFilter> commandFilters;
23+
24+
public CommandFilters(final IEssentials essentials) {
25+
this.essentials = essentials;
26+
config = new EssentialsConf(new File(essentials.getDataFolder(), "command-filters.yml"));
27+
config.setTemplateName("/command-filters.yml");
28+
29+
reloadConfig();
30+
}
31+
32+
@Override
33+
public void reloadConfig() {
34+
config.load();
35+
filters = _getCommandFilterSection();
36+
commandFilters = _getCommandFilters();
37+
}
38+
39+
private ConfigurationSection _getCommandFilterSection() {
40+
if (config.isConfigurationSection("filters")) {
41+
final ConfigurationSection section = config.getConfigurationSection("filters");
42+
final ConfigurationSection newSection = new MemoryConfiguration();
43+
for (final String filterItem : section.getKeys(false)) {
44+
if (section.isConfigurationSection(filterItem)) {
45+
newSection.set(filterItem.toLowerCase(Locale.ENGLISH), section.getConfigurationSection(filterItem));
46+
}
47+
}
48+
return newSection;
49+
}
50+
return null;
51+
}
52+
53+
private Map<String, CommandFilter> _getCommandFilters() {
54+
final Map<String, CommandFilter> commandFilters = new HashMap<>();
55+
for (final String name : filters.getKeys(false)) {
56+
if (!filters.isConfigurationSection(name)) {
57+
EssentialsConf.LOGGER.warning("Invalid command filter '" + name + "'");
58+
continue;
59+
}
60+
61+
final ConfigurationSection section = Objects.requireNonNull(filters.getConfigurationSection(name));
62+
Pattern pattern = section.isString("pattern") ? compileRegex(section.getString("pattern")) : null;
63+
final String command = section.getString("command");
64+
65+
if (pattern == null && command == null) {
66+
EssentialsConf.LOGGER.warning("Invalid command filter '" + name + "', filter must either define 'pattern' or 'command'!");
67+
continue;
68+
}
69+
70+
if (pattern != null && command != null) {
71+
EssentialsConf.LOGGER.warning("Invalid command filter '" + name + "', filter can't have both 'pattern' and 'command'!");
72+
continue;
73+
}
74+
75+
// Compile the command as a regex if the pattern hasn't been set, so pattern is always available.
76+
if (pattern == null) {
77+
pattern = compileRegex(command);
78+
}
79+
80+
Integer cooldown = section.getInt("cooldown", -1);
81+
if (cooldown < 0) {
82+
cooldown = null;
83+
} else {
84+
cooldown *= 1000; // Convert to milliseconds
85+
}
86+
87+
final boolean persistentCooldown = section.getBoolean("persistent-cooldown", true);
88+
final BigDecimal cost = EssentialsConf.toBigDecimal(section.getString("cost"), null);
89+
90+
final String lowerName = name.toLowerCase(Locale.ENGLISH);
91+
commandFilters.put(lowerName, new CommandFilter(lowerName, command, pattern, cooldown, persistentCooldown, cost));
92+
}
93+
config.save();
94+
return commandFilters;
95+
}
96+
97+
private Pattern compileRegex(String regex) {
98+
if (regex.startsWith("^")) {
99+
try {
100+
return Pattern.compile(regex.substring(1));
101+
} catch (final PatternSyntaxException e) {
102+
essentials.getLogger().warning("Command cooldown error: " + e.getMessage());
103+
return null;
104+
}
105+
} else {
106+
// Escape above Regex
107+
if (regex.startsWith("\\^")) {
108+
regex = regex.substring(1);
109+
}
110+
final String cmd = regex.replaceAll("\\*", ".*"); // Wildcards are accepted as asterisk * as known universally.
111+
return Pattern.compile(cmd + "( .*)?"); // This matches arguments, if present, to "ignore" them from the feature.
112+
}
113+
}
114+
115+
public EssentialsConf getConfig() {
116+
return config;
117+
}
118+
119+
public CommandFilter getFilterByName(final String name) {
120+
return commandFilters.get(name.toLowerCase(Locale.ENGLISH));
121+
}
122+
123+
public CommandFilter getCommandCooldown(final IUser user, final String label, boolean essCommand) {
124+
if (user.isAuthorized("essentials.commandcooldowns.bypass")) return null;
125+
return getFilter(label, essCommand, filter -> filter.hasCooldown() && !user.isAuthorized("essentials.commandcooldowns.bypass." + filter.getName()));
126+
}
127+
128+
public CommandFilter getCommandCost(final IUser user, final String label, boolean essCommand) {
129+
if (user.isAuthorized("essentials.nocommandcost.all")) return null;
130+
return getFilter(label, essCommand, filter -> filter.hasCost() && !user.isAuthorized("essentials.nocommandcost." + filter.getName()));
131+
}
132+
133+
private CommandFilter getFilter(final String label, boolean essCommand, Predicate<CommandFilter> filterPredicate) {
134+
for (CommandFilter filter : commandFilters.values()) {
135+
// When the label is an ess command, the filter must define a command entry.
136+
if (essCommand && !filter.hasCommand()) continue;
137+
138+
// Same vice versa.
139+
if (!essCommand && filter.hasCommand()) continue;
140+
141+
if (!filterPredicate.test(filter)) continue;
142+
143+
final boolean matches = filter.getPattern().matcher(label).matches();
144+
if (essentials.getSettings().isDebug()) {
145+
essentials.getLogger().info(String.format("Checking command '%s' against filter '%s': %s", label, filter.getName(), matches));
146+
}
147+
148+
if (matches) {
149+
return filter;
150+
}
151+
}
152+
return null;
153+
}
154+
}

Essentials/src/main/java/com/earth2me/essentials/Essentials.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
138138
private transient KnownCommandsProvider knownCommandsProvider;
139139
private transient ProviderListener recipeBookEventProvider;
140140
private transient Kits kits;
141+
private transient CommandFilters commandFilters;
141142
private transient RandomTeleport randomTeleport;
142143

143144
static {
@@ -184,6 +185,7 @@ public void setupForTesting(final Server server) throws IOException, InvalidDesc
184185
jails = new Jails(this);
185186
registerListeners(server.getPluginManager());
186187
kits = new Kits(this);
188+
commandFilters = new CommandFilters(this);
187189
}
188190

189191
@Override
@@ -238,6 +240,11 @@ public void onEnable() {
238240
upgrade.convertKits();
239241
execTimer.mark("Kits");
240242

243+
commandFilters = new CommandFilters(this);
244+
confList.add(commandFilters);
245+
upgrade.convertCommandFilters();
246+
execTimer.mark("CommandFilters");
247+
241248
upgrade.afterSettings();
242249
execTimer.mark("Upgrade2");
243250

@@ -738,6 +745,11 @@ public Kits getKits() {
738745
return kits;
739746
}
740747

748+
@Override
749+
public CommandFilters getCommandFilters() {
750+
return commandFilters;
751+
}
752+
741753
@Override
742754
public RandomTeleport getRandomTeleport() {
743755
return randomTeleport;

Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.earth2me.essentials.utils.DateUtil;
99
import com.earth2me.essentials.utils.LocationUtil;
1010
import com.earth2me.essentials.utils.MaterialUtil;
11+
import com.earth2me.essentials.utils.NumberUtil;
1112
import com.earth2me.essentials.utils.VersionUtil;
1213
import io.papermc.lib.PaperLib;
1314
import net.ess3.api.IEssentials;
@@ -51,6 +52,7 @@
5152

5253
import java.io.IOException;
5354
import java.lang.management.ManagementFactory;
55+
import java.math.BigDecimal;
5456
import java.text.NumberFormat;
5557
import java.util.ArrayList;
5658
import java.util.Date;
@@ -572,8 +574,7 @@ public void onPlayerCommandPreprocess(final PlayerCommandPreprocessEvent event)
572574
user.updateActivityOnInteract(broadcast);
573575
}
574576

575-
if (ess.getSettings().isCommandCooldownsEnabled() && pluginCommand != null
576-
&& !user.isAuthorized("essentials.commandcooldowns.bypass")) {
577+
if (pluginCommand != null) {
577578
final int argStartIndex = event.getMessage().indexOf(" ");
578579
final String args = argStartIndex == -1 ? "" // No arguments present
579580
: " " + event.getMessage().substring(argStartIndex); // arguments start at argStartIndex; substring from there.
@@ -602,14 +603,27 @@ public void onPlayerCommandPreprocess(final PlayerCommandPreprocessEvent event)
602603
}
603604

604605
if (!cooldownFound) {
605-
final Entry<Pattern, Long> cooldownEntry = ess.getSettings().getCommandCooldownEntry(fullCommand);
606+
final CommandFilter cooldownFilter = ess.getCommandFilters().getCommandCooldown(user, fullCommand, false);
607+
if (cooldownFilter != null) {
608+
if (ess.getSettings().isDebug()) {
609+
ess.getLogger().info("Applying " + cooldownFilter.getCooldown() + "ms cooldown on /" + fullCommand + " for" + user.getName() + ".");
610+
}
611+
cooldownFilter.applyCooldownTo(user);
612+
}
606613

607-
if (cooldownEntry != null) {
614+
final CommandFilter costFilter = ess.getCommandFilters().getCommandCost(user, fullCommand, false);
615+
if (costFilter != null) {
608616
if (ess.getSettings().isDebug()) {
609-
ess.getLogger().info("Applying " + cooldownEntry.getValue() + "ms cooldown on /" + fullCommand + " for" + user.getName() + ".");
617+
ess.getLogger().info("Applying a cost of " + costFilter.getCost() + " on /" + fullCommand + " for" + user.getName() + ".");
618+
}
619+
620+
final BigDecimal cost = costFilter.getCost();
621+
if (!user.canAfford(cost) && cost.signum() > 0) {
622+
player.sendMessage(tl("notEnoughMoney", NumberUtil.displayCurrency(cost, ess)));
623+
event.setCancelled(true);
624+
return;
610625
}
611-
final Date expiry = new Date(System.currentTimeMillis() + cooldownEntry.getValue());
612-
user.addCommandCooldown(cooldownEntry.getKey(), expiry, ess.getSettings().isCommandCooldownPersistent(fullCommand));
626+
user.takeMoney(cost);
613627
}
614628
}
615629
}

Essentials/src/main/java/com/earth2me/essentials/EssentialsUpgrade.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,56 @@ public void convertKits() {
224224
LOGGER.info("Done converting kits.");
225225
}
226226

227+
public void convertCommandFilters() {
228+
final CommandFilters commandFilters = ess.getCommandFilters();
229+
final EssentialsConf config = commandFilters.getConfig();
230+
if (doneFile.getBoolean("commandFiltersYml", false)) {
231+
return;
232+
}
233+
234+
LOGGER.info("Attempting to convert old command-cooldowns and -costs in config.yml to new command-filters.yml");
235+
236+
final ConfigurationSection commandCooldowns = ess.getSettings().getCommandCooldowns();
237+
if (commandCooldowns != null) {
238+
convertCommandCooldowns(commandCooldowns, config);
239+
} else {
240+
LOGGER.info("No command cooldowns found to migrate.");
241+
}
242+
243+
final ConfigurationSection commandCosts = ess.getSettings().getCommandCosts();
244+
if (commandCosts != null) {
245+
convertCommandCosts(commandCosts, config);
246+
} else {
247+
LOGGER.info("No command costs found to migrate.");
248+
}
249+
250+
config.save();
251+
doneFile.setProperty("commandFiltersYml", true);
252+
doneFile.save();
253+
LOGGER.info("Done converting command filters.");
254+
}
255+
256+
private void convertCommandCooldowns(ConfigurationSection commandCooldowns, EssentialsConf config) {
257+
final boolean persistent = ess.getSettings().isCommandCooldownPersistent("dummy");
258+
for (Map.Entry<String, Object> entry : commandCooldowns.getValues(false).entrySet()) {
259+
LOGGER.info("Converting cooldown \"" + entry.getKey() + "\"");
260+
261+
final String key = entry.getKey().replace("\\.", "{dot}"); // Convert periods
262+
config.set("filters." + key + ".pattern", entry.getKey());
263+
config.set("filters." + key + ".cooldown", entry.getValue());
264+
config.set("filters." + key + ".persistent-cooldown", persistent);
265+
}
266+
}
267+
268+
private void convertCommandCosts(ConfigurationSection commandCosts, EssentialsConf config) {
269+
for (Map.Entry<String, Object> entry : commandCosts.getValues(false).entrySet()) {
270+
LOGGER.info("Converting cost \"" + entry.getKey() + "\"");
271+
272+
config.set("filters." + entry.getKey() + ".pattern", entry.getKey());
273+
config.set("filters." + entry.getKey() + ".cost", entry.getValue());
274+
}
275+
}
276+
227277
private void moveMotdRulesToFile(final String name) {
228278
if (doneFile.getBoolean("move" + name + "ToFile", false)) {
229279
return;

Essentials/src/main/java/com/earth2me/essentials/IEssentials.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ public interface IEssentials extends Plugin {
6868

6969
Kits getKits();
7070

71+
CommandFilters getCommandFilters();
72+
7173
RandomTeleport getRandomTeleport();
7274

7375
BukkitTask runTaskAsynchronously(Runnable run);

0 commit comments

Comments
 (0)