Add support for commands.yml aliases in command cooldowns (#3744)

Fixes #2058.
This commit is contained in:
Frank van der Heijden 2021-02-20 17:30:07 +01:00 committed by GitHub
parent f3c347424b
commit f806409d80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 149 additions and 5 deletions

View File

@ -43,11 +43,13 @@ import net.ess3.api.IEssentials;
import net.ess3.api.IItemDb; import net.ess3.api.IItemDb;
import net.ess3.api.IJails; import net.ess3.api.IJails;
import net.ess3.api.ISettings; import net.ess3.api.ISettings;
import net.ess3.nms.refl.providers.ReflFormattedCommandAliasProvider;
import net.ess3.nms.refl.providers.ReflKnownCommandsProvider; import net.ess3.nms.refl.providers.ReflKnownCommandsProvider;
import net.ess3.nms.refl.providers.ReflServerStateProvider; import net.ess3.nms.refl.providers.ReflServerStateProvider;
import net.ess3.nms.refl.providers.ReflSpawnEggProvider; import net.ess3.nms.refl.providers.ReflSpawnEggProvider;
import net.ess3.nms.refl.providers.ReflSpawnerBlockProvider; import net.ess3.nms.refl.providers.ReflSpawnerBlockProvider;
import net.ess3.provider.ContainerProvider; import net.ess3.provider.ContainerProvider;
import net.ess3.provider.FormattedCommandAliasProvider;
import net.ess3.provider.KnownCommandsProvider; import net.ess3.provider.KnownCommandsProvider;
import net.ess3.provider.MaterialTagProvider; import net.ess3.provider.MaterialTagProvider;
import net.ess3.provider.PotionMetaProvider; import net.ess3.provider.PotionMetaProvider;
@ -140,6 +142,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
private transient ServerStateProvider serverStateProvider; private transient ServerStateProvider serverStateProvider;
private transient ContainerProvider containerProvider; private transient ContainerProvider containerProvider;
private transient KnownCommandsProvider knownCommandsProvider; private transient KnownCommandsProvider knownCommandsProvider;
private transient FormattedCommandAliasProvider formattedCommandAliasProvider;
private transient ProviderListener recipeBookEventProvider; private transient ProviderListener recipeBookEventProvider;
private transient MaterialTagProvider materialTagProvider; private transient MaterialTagProvider materialTagProvider;
private transient Kits kits; private transient Kits kits;
@ -348,6 +351,9 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
knownCommandsProvider = new ReflKnownCommandsProvider(); knownCommandsProvider = new ReflKnownCommandsProvider();
} }
// Command aliases provider
formattedCommandAliasProvider = new ReflFormattedCommandAliasProvider(PaperLib.isPaper());
//Material Tag Providers //Material Tag Providers
if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_13_0_R01)) { if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_13_0_R01)) {
materialTagProvider = PaperLib.isPaper() ? new PaperMaterialTagProvider() : new BukkitMaterialTagProvider(); materialTagProvider = PaperLib.isPaper() ? new PaperMaterialTagProvider() : new BukkitMaterialTagProvider();
@ -1057,6 +1063,11 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
return knownCommandsProvider; return knownCommandsProvider;
} }
@Override
public FormattedCommandAliasProvider getFormattedCommandAliasProvider() {
return formattedCommandAliasProvider;
}
private AbstractItemDb getItemDbFromConfig() { private AbstractItemDb getItemDbFromConfig() {
final String setting = settings.getItemDbType(); final String setting = settings.getItemDbType();

View File

@ -18,6 +18,8 @@ import org.bukkit.GameMode;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.command.FormattedCommandAlias;
import org.bukkit.command.PluginCommand; import org.bukkit.command.PluginCommand;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
@ -520,9 +522,30 @@ public class EssentialsPlayerListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerCommandPreprocess(final PlayerCommandPreprocessEvent event) { public void onPlayerCommandPreprocess(final PlayerCommandPreprocessEvent event) {
final Player player = event.getPlayer();
final String cmd = event.getMessage().toLowerCase(Locale.ENGLISH).split(" ")[0].replace("/", "").toLowerCase(Locale.ENGLISH); final String cmd = event.getMessage().toLowerCase(Locale.ENGLISH).split(" ")[0].replace("/", "").toLowerCase(Locale.ENGLISH);
final int argStartIndex = event.getMessage().indexOf(" ");
final String args = argStartIndex == -1 ? "" // No arguments present
: event.getMessage().substring(argStartIndex); // arguments start at argStartIndex; substring from there.
// If the plugin command does not exist, check if it is an alias from commands.yml
if (ess.getServer().getPluginCommand(cmd) == null) {
final Command knownCommand = ess.getKnownCommandsProvider().getKnownCommands().get(cmd);
if (knownCommand instanceof FormattedCommandAlias) {
final FormattedCommandAlias command = (FormattedCommandAlias) knownCommand;
for (String fullCommand : ess.getFormattedCommandAliasProvider().createCommands(command, event.getPlayer(), args.split(" "))) {
handlePlayerCommandPreprocess(event, fullCommand);
}
return;
}
}
// Handle the command given from the event.
handlePlayerCommandPreprocess(event, cmd + args);
}
public void handlePlayerCommandPreprocess(final PlayerCommandPreprocessEvent event, final String effectiveCommand) {
final Player player = event.getPlayer();
final String cmd = effectiveCommand.toLowerCase(Locale.ENGLISH).split(" ")[0].replace("/", "").toLowerCase(Locale.ENGLISH);
final PluginCommand pluginCommand = ess.getServer().getPluginCommand(cmd); final PluginCommand pluginCommand = ess.getServer().getPluginCommand(cmd);
if (ess.getSettings().getSocialSpyCommands().contains(cmd) || ess.getSettings().getSocialSpyCommands().contains("*")) { if (ess.getSettings().getSocialSpyCommands().contains(cmd) || ess.getSettings().getSocialSpyCommands().contains("*")) {
@ -575,12 +598,12 @@ public class EssentialsPlayerListener implements Listener {
user.updateActivityOnInteract(broadcast); user.updateActivityOnInteract(broadcast);
} }
if (ess.getSettings().isCommandCooldownsEnabled() && pluginCommand != null if (ess.getSettings().isCommandCooldownsEnabled()
&& !user.isAuthorized("essentials.commandcooldowns.bypass")) { && !user.isAuthorized("essentials.commandcooldowns.bypass")) {
final int argStartIndex = event.getMessage().indexOf(" "); final int argStartIndex = effectiveCommand.indexOf(" ");
final String args = argStartIndex == -1 ? "" // No arguments present final String args = argStartIndex == -1 ? "" // No arguments present
: " " + event.getMessage().substring(argStartIndex); // arguments start at argStartIndex; substring from there. : " " + effectiveCommand.substring(argStartIndex); // arguments start at argStartIndex; substring from there.
final String fullCommand = pluginCommand.getName() + args; final String fullCommand = pluginCommand == null ? effectiveCommand : pluginCommand.getName() + args;
// Used to determine whether a user already has an existing cooldown // Used to determine whether a user already has an existing cooldown
// If so, no need to check for (and write) new ones. // If so, no need to check for (and write) new ones.

View File

@ -6,6 +6,7 @@ import com.earth2me.essentials.api.IWarps;
import com.earth2me.essentials.perm.PermissionsHandler; import com.earth2me.essentials.perm.PermissionsHandler;
import net.ess3.provider.MaterialTagProvider; import net.ess3.provider.MaterialTagProvider;
import net.ess3.provider.ContainerProvider; import net.ess3.provider.ContainerProvider;
import net.ess3.provider.FormattedCommandAliasProvider;
import net.ess3.provider.KnownCommandsProvider; import net.ess3.provider.KnownCommandsProvider;
import net.ess3.provider.ServerStateProvider; import net.ess3.provider.ServerStateProvider;
import net.ess3.provider.SpawnerBlockProvider; import net.ess3.provider.SpawnerBlockProvider;
@ -124,4 +125,6 @@ public interface IEssentials extends Plugin {
ContainerProvider getContainerProvider(); ContainerProvider getContainerProvider();
KnownCommandsProvider getKnownCommandsProvider(); KnownCommandsProvider getKnownCommandsProvider();
FormattedCommandAliasProvider getFormattedCommandAliasProvider();
} }

View File

@ -0,0 +1,15 @@
package net.ess3.provider;
import org.bukkit.command.CommandSender;
import org.bukkit.command.FormattedCommandAlias;
import java.util.List;
public interface FormattedCommandAliasProvider extends Provider {
List<String> createCommands(FormattedCommandAlias command, CommandSender sender, String[] args);
String[] getFormatStrings(FormattedCommandAlias command);
String buildCommand(FormattedCommandAlias command, CommandSender sender, String formatString, String[] args);
}

View File

@ -0,0 +1,92 @@
package net.ess3.nms.refl.providers;
import net.ess3.nms.refl.ReflUtil;
import net.ess3.provider.FormattedCommandAliasProvider;
import org.bukkit.command.CommandSender;
import org.bukkit.command.FormattedCommandAlias;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class ReflFormattedCommandAliasProvider implements FormattedCommandAliasProvider {
private final boolean paper;
private final Field formatStringsField;
private final MethodHandle buildCommandMethodHandle;
public ReflFormattedCommandAliasProvider(boolean paper) {
this.paper = paper;
final Class<? extends FormattedCommandAlias> formattedCommandAliasClass;
Field formatStringsField = null;
MethodHandle buildCommandMethodHandle = null;
try {
formattedCommandAliasClass = FormattedCommandAlias.class;
formatStringsField = ReflUtil.getFieldCached(formattedCommandAliasClass, "formatStrings");
final Class<?>[] parameterTypes;
if (paper) {
parameterTypes = new Class[] {CommandSender.class, String.class, String[].class};
} else {
parameterTypes = new Class[] {String.class, String[].class};
}
final Method buildCommandMethod = ReflUtil.getMethodCached(formattedCommandAliasClass, "buildCommand", parameterTypes);
buildCommandMethod.setAccessible(true);
buildCommandMethodHandle = MethodHandles.lookup().unreflect(buildCommandMethod);
} catch (final Exception ex) {
ex.printStackTrace();
} finally {
this.formatStringsField = formatStringsField;
this.buildCommandMethodHandle = buildCommandMethodHandle;
}
}
@Override
public List<String> createCommands(FormattedCommandAlias command, CommandSender sender, String[] args) {
final List<String> commands = new ArrayList<>();
for (String formatString : getFormatStrings(command)) {
final String cmd;
try {
cmd = buildCommand(command, sender, formatString, args);
} catch (Throwable th) {
continue; // Ignore, let server handle this.
}
if (cmd == null) continue;
commands.add(cmd.trim());
}
return commands;
}
@Override
public String[] getFormatStrings(FormattedCommandAlias command) {
try {
return (String[]) formatStringsField.get(command);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex); // If this happens we have bigger problems...
}
}
@Override
public String buildCommand(FormattedCommandAlias command, CommandSender sender, String formatString, String[] args) {
try {
if (paper) {
return (String) buildCommandMethodHandle.invoke(command, sender, formatString, args);
} else {
return (String) buildCommandMethodHandle.invoke(command, formatString, args);
}
} catch (Throwable ex) {
throw new RuntimeException(ex); // If this happens we have bigger problems...
}
}
@Override
public String getDescription() {
return "NMS Reflection Provider for FormattedCommandAlias methods";
}
}