diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/AnarchyExploitFixes.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/AnarchyExploitFixes.java index 24668dea1..3e810cf30 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/AnarchyExploitFixes.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/AnarchyExploitFixes.java @@ -131,9 +131,6 @@ public void onEnable() { prefixedLogger.info("Loading Translations"); reloadLang(); - prefixedLogger.info("Registering Commands"); - AEFCommand.registerCommands(); - prefixedLogger.info("Loading NBT-API"); // Hide all messages with a log level lower than WARNING because of the same reason as Reflections logging. Logger.getLogger("NBTAPI").setLevel(java.util.logging.Level.WARNING); @@ -150,6 +147,7 @@ public void onDisable() { AEFPermission.unregisterAll(); if (isPacketEventsInstalled) { AEFModule.disableAll(); + AEFCommand.disableAll(); } if (languageCacheMap != null) { languageCacheMap.clear(); @@ -221,6 +219,7 @@ private void reloadConfiguration() { if (tickReporter != null) tickReporter.disable(); tickReporter = TickReporter.create(this, config.tickData_cache_duration); AEFModule.reloadModules(); + AEFCommand.reloadCommands(); config.saveConfig(); } catch (Throwable t) { prefixedLogger.error("Failed while loading config!", t); diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/AEFCommand.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/AEFCommand.java index d7ca97d44..842f60760 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/AEFCommand.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/AEFCommand.java @@ -1,24 +1,100 @@ package me.xginko.aef.commands; -import me.xginko.aef.commands.aef.AEFCmd; +import com.google.common.collect.ImmutableSet; +import me.xginko.aef.AnarchyExploitFixes; +import me.xginko.aef.utils.models.ConditionalEnableable; +import me.xginko.aef.utils.models.Disableable; import me.xginko.aef.utils.models.Enableable; +import org.bukkit.command.Command; +import org.bukkit.command.PluginIdentifiableCommand; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.reflections.Reflections; +import org.reflections.scanners.Scanners; +import java.lang.reflect.Modifier; +import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; -public interface AEFCommand extends Enableable { +public abstract class AEFCommand extends Command implements PluginIdentifiableCommand, ConditionalEnableable, Disableable { - boolean shouldEnable(); + protected static final Set> AVAILABLE_COMMANDS; + protected static final Set ENABLED_COMMANDS; - static void registerCommands() { - for (AEFCommand command : Set.of( - new AEFCmd(), - new ToggleConnectionMsgsCmd(), - new SayCmd(), - new HelpCmd() - )) { - if (command.shouldEnable()) { - command.enable(); + static { + AVAILABLE_COMMANDS = new Reflections(AEFCommand.class.getPackage().getName()) + .get(Scanners.SubTypes.of(AEFCommand.class).asClass()) + .stream() + .filter(clazz -> !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers())) + .map(clazz -> (Class) clazz) + .collect(Collectors.collectingAndThen(Collectors.toList(), ImmutableSet::copyOf)); + ENABLED_COMMANDS = new HashSet<>(AVAILABLE_COMMANDS.size()); + } + + private final AnarchyExploitFixes plugin; + + protected AEFCommand(@NotNull String name, @NotNull String description, @NotNull String usageMessage, @NotNull List aliases) { + super(name, description, usageMessage, aliases); + this.plugin = AnarchyExploitFixes.getInstance(); + } + + @Override + public @NotNull Plugin getPlugin() { + return plugin; + } + + @Override + public boolean shouldEnable() { + return true; + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void enable() { + plugin.getServer().getCommandMap().register(plugin.getPluginMeta().getName().toLowerCase(), this); + } + + @Override + public void disable() { + Map knownCommands = new ConcurrentHashMap<>(plugin.getServer().getCommandMap().getKnownCommands()); + for (Map.Entry entry : knownCommands.entrySet()) { + if (entry.getValue() == this) { + entry.getValue().unregister(plugin.getServer().getCommandMap()); + BukkitCommandWrap.getInstance().unwrap(entry.getKey()); + knownCommands.remove(entry.getKey()); } } + BukkitCommandWrap.getInstance().setKnownCommands(knownCommands); + BukkitCommandWrap.getInstance().sync(); + } + + public static void disableAll() { + ENABLED_COMMANDS.forEach(Disableable::disable); + ENABLED_COMMANDS.clear(); + } + + public static void reloadCommands() { + disableAll(); + + for (Class cmdClass : AVAILABLE_COMMANDS) { + try { + AEFCommand aefCommand = cmdClass.getDeclaredConstructor().newInstance(); + if (aefCommand.shouldEnable()) { + ENABLED_COMMANDS.add(aefCommand); + } + } catch (Throwable t) { + if (t.getCause() instanceof NoClassDefFoundError) { + AnarchyExploitFixes.prefixedLogger().info("Dependencies for command class {} missing, not enabling.", cmdClass.getSimpleName()); + } else { + AnarchyExploitFixes.prefixedLogger().warn("Failed initialising command class '{}'.", cmdClass.getSimpleName(), t); + } + } + } + + ENABLED_COMMANDS.forEach(Enableable::enable); } } \ No newline at end of file diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/HelpCmd.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/HelpCmd.java index 12920da9c..80451944e 100644 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/HelpCmd.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/HelpCmd.java @@ -2,7 +2,6 @@ import me.xginko.aef.AnarchyExploitFixes; import me.xginko.aef.utils.permissions.AEFPermission; -import org.bukkit.command.Command; import org.bukkit.command.CommandException; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.NotNull; @@ -10,7 +9,7 @@ import java.util.Collections; import java.util.List; -public class HelpCmd extends Command implements AEFCommand { +public class HelpCmd extends AEFCommand { public HelpCmd() { super("help", "Command help overview", "/help", Collections.emptyList()); @@ -21,13 +20,6 @@ public boolean shouldEnable() { return AnarchyExploitFixes.config().cmd_help_enabled; } - @Override - @SuppressWarnings("UnstableApiUsage") - public void enable() { - AnarchyExploitFixes plugin = AnarchyExploitFixes.getInstance(); - plugin.getServer().getCommandMap().register(plugin.getPluginMeta().getName().toLowerCase(), this); - } - @Override public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws CommandException, IllegalArgumentException diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/SayCmd.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/SayCmd.java index 41017fa07..60d0b3068 100644 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/SayCmd.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/SayCmd.java @@ -1,11 +1,10 @@ package me.xginko.aef.commands; import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.utils.permissions.AEFPermission; import me.xginko.aef.utils.CommandUtil; +import me.xginko.aef.utils.permissions.AEFPermission; import net.kyori.adventure.text.TextReplacementConfig; import net.kyori.adventure.text.minimessage.MiniMessage; -import org.bukkit.command.Command; import org.bukkit.command.CommandException; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.NotNull; @@ -13,7 +12,7 @@ import java.util.Collections; import java.util.List; -public class SayCmd extends Command implements AEFCommand { +public class SayCmd extends AEFCommand { public SayCmd() { super("say", "Custom say command", "/say", Collections.emptyList()); @@ -24,13 +23,6 @@ public boolean shouldEnable() { return AnarchyExploitFixes.config().cmd_say_enabled; } - @Override - @SuppressWarnings("UnstableApiUsage") - public void enable() { - AnarchyExploitFixes plugin = AnarchyExploitFixes.getInstance(); - plugin.getServer().getCommandMap().register(plugin.getPluginMeta().getName().toLowerCase(), this); - } - @Override public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws CommandException, IllegalArgumentException diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/ToggleConnectionMsgsCmd.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/ToggleConnectionMsgsCmd.java index dda8ef895..660ae3f57 100644 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/ToggleConnectionMsgsCmd.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/ToggleConnectionMsgsCmd.java @@ -5,7 +5,6 @@ import me.xginko.aef.utils.permissions.AEFPermission; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.command.Command; import org.bukkit.command.CommandException; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -16,7 +15,7 @@ import java.util.Collections; import java.util.List; -public class ToggleConnectionMsgsCmd extends Command implements AEFCommand { +public class ToggleConnectionMsgsCmd extends AEFCommand { public ToggleConnectionMsgsCmd() { super("toggleconnectionmsgs", "Toggle connection messages", "/toggleconnectionmsgs", Collections.singletonList("tcmsgs")); @@ -27,13 +26,6 @@ public boolean shouldEnable() { return AnarchyExploitFixes.config().cmd_toggleConMsgs_enabled; } - @Override - @SuppressWarnings("UnstableApiUsage") - public void enable() { - AnarchyExploitFixes plugin = AnarchyExploitFixes.getInstance(); - plugin.getServer().getCommandMap().register(plugin.getPluginMeta().getName().toLowerCase(), this); - } - @Override public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws CommandException, IllegalArgumentException diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/aef/AEFCmd.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/aef/AEFCmd.java index 70325fa00..e8e79dd2e 100644 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/aef/AEFCmd.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/commands/aef/AEFCmd.java @@ -2,7 +2,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import me.xginko.aef.AnarchyExploitFixes; import me.xginko.aef.commands.AEFCommand; import me.xginko.aef.commands.SubCommand; import me.xginko.aef.commands.aef.subcommands.DisableSubCmd; @@ -13,7 +12,6 @@ import me.xginko.aef.commands.aef.subcommands.VersionSubCmd; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; -import org.bukkit.command.Command; import org.bukkit.command.CommandException; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.NotNull; @@ -25,7 +23,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -public class AEFCmd extends Command implements AEFCommand { +public class AEFCmd extends AEFCommand { private final @NotNull Set subCommands; private final @NotNull List tabCompletes; @@ -64,18 +62,6 @@ public AEFCmd() { .collect(Collectors.collectingAndThen(Collectors.toList(), ImmutableList::copyOf)); } - @Override - public boolean shouldEnable() { - return true; - } - - @Override - @SuppressWarnings("UnstableApiUsage") - public void enable() { - AnarchyExploitFixes plugin = AnarchyExploitFixes.getInstance(); - plugin.getServer().getCommandMap().register(plugin.getPluginMeta().getName().toLowerCase(), this); - } - @Override public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws CommandException, IllegalArgumentException diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/AnarchyExploitFixes.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/AnarchyExploitFixes.java index df604445c..4a226c7d0 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/AnarchyExploitFixes.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/AnarchyExploitFixes.java @@ -8,9 +8,9 @@ import me.xginko.aef.config.Datastore; import me.xginko.aef.config.LanguageCache; import me.xginko.aef.modules.AEFModule; +import me.xginko.aef.utils.PlatformUtil; import me.xginko.aef.utils.permissions.AEFPermission; import me.xginko.aef.utils.permissions.PermissionHandler; -import me.xginko.aef.utils.PlatformUtil; import me.xginko.aef.utils.tickdata.TickReporter; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; @@ -141,9 +141,6 @@ public void onEnable() { prefixedLogger.info("Loading Translations"); reloadLang(); - prefixedLogger.info("Registering Commands"); - AEFCommand.registerCommands(); - prefixedLogger.info("Loading NBT-API"); // Hide all messages with a log level lower than WARNING because of the same reason as Reflections logging. java.util.logging.Logger.getLogger("NBTAPI").setLevel(java.util.logging.Level.WARNING); @@ -160,6 +157,7 @@ public void onDisable() { AEFPermission.unregisterAll(); if (isPacketEventsInstalled) { AEFModule.disableAll(); + AEFCommand.disableAll(); } if (languageCacheMap != null) { languageCacheMap.clear(); @@ -239,6 +237,7 @@ private void reloadConfiguration() { if (tickReporter != null) tickReporter.disable(); tickReporter = TickReporter.create(this, config.tps_cache_duration); AEFModule.reloadModules(); + AEFCommand.reloadCommands(); config.saveConfig(); } catch (Throwable t) { prefixedLogger.error("Failed while loading config!", t); diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/AEFCommand.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/AEFCommand.java index 1a2e5c686..aed2d35d2 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/AEFCommand.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/AEFCommand.java @@ -1,23 +1,99 @@ package me.xginko.aef.commands; -import com.google.common.collect.Sets; -import me.xginko.aef.commands.aef.AEFCmd; +import com.google.common.collect.ImmutableSet; +import me.xginko.aef.AnarchyExploitFixes; +import me.xginko.aef.utils.models.ConditionalEnableable; +import me.xginko.aef.utils.models.Disableable; import me.xginko.aef.utils.models.Enableable; +import org.bukkit.command.Command; +import org.bukkit.command.PluginIdentifiableCommand; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.reflections.Reflections; +import org.reflections.scanners.Scanners; -public interface AEFCommand extends Enableable { +import java.lang.reflect.Modifier; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; - boolean shouldEnable(); +public abstract class AEFCommand extends Command implements PluginIdentifiableCommand, ConditionalEnableable, Disableable { - static void registerCommands() { - for (AEFCommand command : Sets.newHashSet( - new AEFCmd(), - new ToggleConnectionMsgsCmd(), - new SayCmd(), - new HelpCmd() - )) { - if (command.shouldEnable()) { - command.enable(); + protected static final Set> AVAILABLE_COMMANDS; + protected static final Set ENABLED_COMMANDS; + + static { + AVAILABLE_COMMANDS = new Reflections(AEFCommand.class.getPackage().getName()) + .get(Scanners.SubTypes.of(AEFCommand.class).asClass()) + .stream() + .filter(clazz -> !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers())) + .map(clazz -> (Class) clazz) + .collect(Collectors.collectingAndThen(Collectors.toList(), ImmutableSet::copyOf)); + ENABLED_COMMANDS = new HashSet<>(AVAILABLE_COMMANDS.size()); + } + + private final AnarchyExploitFixes plugin; + + protected AEFCommand(@NotNull String name, @NotNull String description, @NotNull String usageMessage, @NotNull List aliases) { + super(name, description, usageMessage, aliases); + this.plugin = AnarchyExploitFixes.getInstance(); + } + + @Override + public @NotNull Plugin getPlugin() { + return plugin; + } + + @Override + public boolean shouldEnable() { + return true; + } + + @Override + public void enable() { + plugin.getServer().getCommandMap().register(plugin.getDescription().getName().toLowerCase(), this); + } + + @Override + public void disable() { + Map knownCommands = new ConcurrentHashMap<>(plugin.getServer().getCommandMap().getKnownCommands()); + for (Map.Entry entry : knownCommands.entrySet()) { + if (entry.getValue() == this) { + entry.getValue().unregister(plugin.getServer().getCommandMap()); + BukkitCommandWrap.getInstance().unwrap(entry.getKey()); + knownCommands.remove(entry.getKey()); + } + } + BukkitCommandWrap.getInstance().setKnownCommands(knownCommands); + BukkitCommandWrap.getInstance().sync(); + } + + public static void disableAll() { + ENABLED_COMMANDS.forEach(Disableable::disable); + ENABLED_COMMANDS.clear(); + } + + public static void reloadCommands() { + disableAll(); + + for (Class cmdClass : AVAILABLE_COMMANDS) { + try { + AEFCommand aefCommand = cmdClass.getDeclaredConstructor().newInstance(); + if (aefCommand.shouldEnable()) { + ENABLED_COMMANDS.add(aefCommand); + } + } catch (Throwable t) { + if (t.getCause() instanceof NoClassDefFoundError) { + AnarchyExploitFixes.prefixedLogger().info("Dependencies for command class {} missing, not enabling.", cmdClass.getSimpleName()); + } else { + AnarchyExploitFixes.prefixedLogger().warn("Failed initialising command class '{}'.", cmdClass.getSimpleName(), t); + } } } + + ENABLED_COMMANDS.forEach(Enableable::enable); } } \ No newline at end of file diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/HelpCmd.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/HelpCmd.java index 6635414fc..01267c85f 100644 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/HelpCmd.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/HelpCmd.java @@ -2,7 +2,6 @@ import me.xginko.aef.AnarchyExploitFixes; import me.xginko.aef.utils.permissions.AEFPermission; -import org.bukkit.command.Command; import org.bukkit.command.CommandException; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.NotNull; @@ -10,7 +9,7 @@ import java.util.Collections; import java.util.List; -public class HelpCmd extends Command implements AEFCommand { +public class HelpCmd extends AEFCommand { public HelpCmd() { super("help", "Command help overview", "/help", Collections.emptyList()); @@ -21,12 +20,6 @@ public boolean shouldEnable() { return AnarchyExploitFixes.config().cmd_help_enabled; } - @Override - public void enable() { - AnarchyExploitFixes plugin = AnarchyExploitFixes.getInstance(); - plugin.getServer().getCommandMap().register(plugin.getDescription().getName().toLowerCase(), this); - } - @Override public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws CommandException, IllegalArgumentException diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/SayCmd.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/SayCmd.java index cc1493c0f..e42bb9c40 100644 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/SayCmd.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/SayCmd.java @@ -1,10 +1,9 @@ package me.xginko.aef.commands; import me.xginko.aef.AnarchyExploitFixes; -import me.xginko.aef.utils.permissions.AEFPermission; import me.xginko.aef.utils.CommandUtil; +import me.xginko.aef.utils.permissions.AEFPermission; import org.bukkit.ChatColor; -import org.bukkit.command.Command; import org.bukkit.command.CommandException; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.NotNull; @@ -12,7 +11,7 @@ import java.util.Collections; import java.util.List; -public class SayCmd extends Command implements AEFCommand { +public class SayCmd extends AEFCommand { public SayCmd() { super("say", "Custom say command", "/say", Collections.emptyList()); @@ -23,12 +22,6 @@ public boolean shouldEnable() { return AnarchyExploitFixes.config().cmd_say_enabled; } - @Override - public void enable() { - AnarchyExploitFixes plugin = AnarchyExploitFixes.getInstance(); - plugin.getServer().getCommandMap().register(plugin.getDescription().getName().toLowerCase(), this); - } - @Override public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws CommandException, IllegalArgumentException diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/ToggleConnectionMsgsCmd.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/ToggleConnectionMsgsCmd.java index 91d399bd3..90f729597 100644 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/ToggleConnectionMsgsCmd.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/ToggleConnectionMsgsCmd.java @@ -3,7 +3,6 @@ import me.xginko.aef.AnarchyExploitFixes; import me.xginko.aef.utils.permissions.AEFPermission; import org.bukkit.ChatColor; -import org.bukkit.command.Command; import org.bukkit.command.CommandException; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -12,7 +11,7 @@ import java.util.Collections; import java.util.List; -public class ToggleConnectionMsgsCmd extends Command implements AEFCommand { +public class ToggleConnectionMsgsCmd extends AEFCommand { public ToggleConnectionMsgsCmd() { super("toggleconnectionmsgs", "Toggle connection messages", @@ -24,12 +23,6 @@ public boolean shouldEnable() { return AnarchyExploitFixes.config().cmd_toggleConMsgs_enabled; } - @Override - public void enable() { - AnarchyExploitFixes plugin = AnarchyExploitFixes.getInstance(); - plugin.getServer().getCommandMap().register(plugin.getDescription().getName().toLowerCase(), this); - } - @Override public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws CommandException, IllegalArgumentException diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/aef/AEFCmd.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/aef/AEFCmd.java index 05441e07e..ad29f3c45 100644 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/aef/AEFCmd.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/commands/aef/AEFCmd.java @@ -2,7 +2,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import me.xginko.aef.AnarchyExploitFixes; import me.xginko.aef.commands.AEFCommand; import me.xginko.aef.commands.SubCommand; import me.xginko.aef.commands.aef.subcommands.DisableSubCmd; @@ -11,7 +10,6 @@ import me.xginko.aef.commands.aef.subcommands.LagSubCmd; import me.xginko.aef.commands.aef.subcommands.ReloadSubCmd; import me.xginko.aef.commands.aef.subcommands.VersionSubCmd; -import org.bukkit.command.Command; import org.bukkit.command.CommandException; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.NotNull; @@ -23,7 +21,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -public class AEFCmd extends Command implements AEFCommand { +public class AEFCmd extends AEFCommand { private final Set subCommands; private final List tabCompletes, overview; @@ -61,17 +59,6 @@ public AEFCmd() { .collect(Collectors.collectingAndThen(Collectors.toList(), ImmutableList::copyOf)); } - @Override - public boolean shouldEnable() { - return true; - } - - @Override - public void enable() { - AnarchyExploitFixes plugin = AnarchyExploitFixes.getInstance(); - plugin.getServer().getCommandMap().register(plugin.getDescription().getName().toLowerCase(), this); - } - @Override public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws CommandException, IllegalArgumentException diff --git a/build-logic/src/main/kotlin/me.xginko.aef.wrapper.gradle.kts b/build-logic/src/main/kotlin/me.xginko.aef.wrapper.gradle.kts index 41f5266fe..60dbba5cc 100755 --- a/build-logic/src/main/kotlin/me.xginko.aef.wrapper.gradle.kts +++ b/build-logic/src/main/kotlin/me.xginko.aef.wrapper.gradle.kts @@ -36,6 +36,11 @@ repositories { url = uri("https://repo.aikar.co/content/groups/aikar/") } + maven { + name = "mojang" + url = uri("https://libraries.minecraft.net") + } + maven { name = "jitpack.io" url = uri("https://jitpack.io") @@ -44,6 +49,7 @@ repositories { dependencies { compileOnly("com.github.retrooper:packetevents-spigot:2.7.0") // PacketEvents to patch packet based exploits + compileOnly("com.mojang:brigadier:1.0.18") api("com.github.cryptomorin:XSeries:13.0.0") // Crossversion entitytype and material support api("com.github.thatsmusic99:ConfigurationMaster-API:v2.0.0-rc.3") // ConfigurationMaster for enhanced config management api("de.tr7zw:item-nbt-api:2.14.1") // NBT API for cross version nbt tag handling diff --git a/shared/src/main/java/me/xginko/aef/commands/BukkitCommandWrap.java b/shared/src/main/java/me/xginko/aef/commands/BukkitCommandWrap.java new file mode 100644 index 000000000..d68a53552 --- /dev/null +++ b/shared/src/main/java/me/xginko/aef/commands/BukkitCommandWrap.java @@ -0,0 +1,321 @@ +package me.xginko.aef.commands; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.tree.CommandNode; +import com.mojang.brigadier.tree.RootCommandNode; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.SimpleCommandMap; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Map; +/* +* Straight up yoinked from https://github.com/Test-Account666/PlugManX +* (Stinky reflections, but it does the job) +* */ +public final class BukkitCommandWrap { + private static BukkitCommandWrap instance; + + private final boolean nmsVersioning; + private String nmsVersion; + private Class minecraftServerClass; + private Method aMethod, getServerMethod, registerMethod, syncCommandsMethod, removeCommandMethod; + private Field bField, vanillaCommandDispatcherField, commandMapField, knownCommandsField; + private Constructor bukkitcommandWrapperConstructor; + + public BukkitCommandWrap() { + String prefix = getCraftBukkitPrefix(); + String[] packageParts = prefix.split("\\."); + String versionPart = packageParts[packageParts.length - 1]; + if (versionPart.startsWith("v1_")) { + this.nmsVersion = versionPart; + } + boolean nmsVers = true; + try { + Class.forName("net.minecraft.server.MinecraftServer"); + nmsVers = false; + } catch (ClassNotFoundException ignore) {} + this.nmsVersioning = nmsVers; + } + + public static BukkitCommandWrap getInstance() { + if (instance == null) { + instance = new BukkitCommandWrap(); + } + return instance; + } + + private static @NotNull String getCraftBukkitPrefix() { + return Bukkit.getServer().getClass().getPackage().getName(); + } + + public static @NotNull String getCraftBukkitPrefix(String cbClassName) { + return getCraftBukkitPrefix() + "." + cbClassName; + } + + private @NotNull String getNetMinecraftServerPrefix(String nmsClassName) { + if (this.nmsVersioning) return "net.minecraft.server." + this.nmsVersion + "." + nmsClassName; + return "net.minecraft.server." + nmsClassName; + } + + private boolean resolveSyncCommandsMethod() { + if (this.syncCommandsMethod != null) return true; + + try { + this.syncCommandsMethod = Class.forName(getCraftBukkitPrefix("CraftServer")).getDeclaredMethod("syncCommands"); + this.syncCommandsMethod.setAccessible(true); + return true; + } catch (NoSuchMethodException | ClassNotFoundException e) { + e.printStackTrace(); + return false; + } + } + + private boolean fetchCommandMapField() { + if (this.commandMapField != null) return true; + try { + this.commandMapField = Class.forName(getCraftBukkitPrefix("CraftServer")).getDeclaredField("commandMap"); + this.commandMapField.setAccessible(true); + return true; + } catch (NoSuchFieldException | ClassNotFoundException e) { + e.printStackTrace(); + return false; + } + } + + private boolean fetchKnownCommandsField() { + if (this.knownCommandsField != null) return true; + + try { + this.knownCommandsField = SimpleCommandMap.class.getDeclaredField("knownCommands"); + this.knownCommandsField.setAccessible(true); + return true; + } catch (NoSuchFieldException e) { + e.printStackTrace(); + return false; + } + } + + public void setKnownCommands(Map knownCommands) { + if (!this.fetchCommandMapField()) + throw new RuntimeException("Cannot find command map"); + + SimpleCommandMap commandMap; + try { + commandMap = (SimpleCommandMap) this.commandMapField.get(Bukkit.getServer()); + } catch (Exception e) { + e.printStackTrace(); + return; + } + + if (!this.fetchKnownCommandsField()) + throw new RuntimeException("Unable to find known commands"); + + try { + this.knownCommandsField.set(commandMap, knownCommands); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + public void sync() { + if (!this.resolveSyncCommandsMethod()) return; + + try { + this.syncCommandsMethod.invoke(Bukkit.getServer()); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + + if (Bukkit.getOnlinePlayers().isEmpty()) return; + + Bukkit.getOnlinePlayers().forEach(Player::updateCommands); + } + + public void unwrap(String command) { + if (this.nmsVersion == null) return; + + if (!this.resolveMinecraftServerClass()) return; + + if (!this.resolveGetServerMethod()) return; + Object server = this.getServerInstance(); + + if (!this.resolveVanillaCommandDispatcherField()) return; + Object commandDispatcher = this.getCommandDispatcher(server); + + if (!this.resolveBField()) return; + + CommandDispatcher b = this.getDispatcher(commandDispatcher); + if (b == null) return; + + if (!this.resolveRemoveCommandMethod()) return; + + try { + this.removeCommandMethod.invoke(b.getRoot(), command); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + } + + private boolean resolveRemoveCommandMethod() { + if (this.removeCommandMethod == null) try { + try { + this.removeCommandMethod = RootCommandNode.class.getDeclaredMethod("removeCommand", String.class); + } catch (NoSuchMethodException | NoSuchMethodError ex) { + this.removeCommandMethod = CommandNode.class.getDeclaredMethod("removeCommand", String.class); + } + return true; + } catch (NoSuchMethodException e) { + e.printStackTrace(); + return false; + } + return true; + } + + private @Nullable CommandDispatcher getDispatcher(Object commandDispatcher) { + try { + return (CommandDispatcher) this.bField.get(commandDispatcher); + } catch (IllegalAccessException e) { + e.printStackTrace(); + return null; + } + } + + private @Nullable Object getCommandDispatcher(Object server) { + try { + return this.vanillaCommandDispatcherField.get(server); + } catch (IllegalAccessException e) { + e.printStackTrace(); + return null; + } + } + + private Object getServerInstance() { + try { + return this.getServerMethod.invoke(this.minecraftServerClass); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + return null; + } + } + + private boolean resolveMinecraftServerClass() { + if (this.minecraftServerClass != null) return true; + try { + this.minecraftServerClass = Class.forName(this.getNetMinecraftServerPrefix("MinecraftServer")); + return true; + } catch (ClassNotFoundException ex) { + ex.printStackTrace(); + return false; + } + } + + private boolean resolveGetServerMethod() { + if (this.getServerMethod != null) return true; + try { + this.getServerMethod = this.minecraftServerClass.getMethod("getServer"); + this.getServerMethod.setAccessible(true); + return true; + } catch (NoSuchMethodException e) { + e.printStackTrace(); + return false; + } + } + + private boolean resolveVanillaCommandDispatcherField() { + if (this.vanillaCommandDispatcherField != null) return true; + try { + this.vanillaCommandDispatcherField = this.minecraftServerClass.getDeclaredField("vanillaCommandDispatcher"); + this.vanillaCommandDispatcherField.setAccessible(true); + return true; + } catch (NoSuchFieldException e) { + e.printStackTrace(); + return false; + } + } + + private boolean resolveBField() { + if (this.bField != null) return true; + try { + this.bField = Class.forName(this.getNetMinecraftServerPrefix("CommandDispatcher")).getDeclaredField("b"); + this.bField.setAccessible(true); + return true; + } catch (NoSuchFieldException | ClassNotFoundException e) { + try { + Class clazz = Class.forName("net.minecraft.commands.CommandDispatcher"); + Field gField = clazz.getDeclaredField("g"); + if (gField.getType() == CommandDispatcher.class) + this.bField = gField; + else + this.bField = clazz.getDeclaredField("h"); + this.bField.setAccessible(true); + return true; + } catch (NoSuchFieldException | ClassNotFoundException ex) { + ex.addSuppressed(e); + e.printStackTrace(); + return false; + } + } + } + + private boolean resolveRegisterCommandMethod() { + if (this.registerMethod != null) return true; + try { + this.registerMethod = Class.forName(getCraftBukkitPrefix("command.BukkitCommandWrapper")) + .getMethod("register", CommandDispatcher.class, String.class); + this.registerMethod.setAccessible(true); + return true; + } catch (NoSuchMethodException | ClassNotFoundException e) { + e.printStackTrace(); + return false; + } + } + + private Object getAInstance(Object commandDispatcher) { + try { + return this.aMethod.invoke(commandDispatcher); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + return null; + } + } + + private @Nullable Object getCommandWrapper(Command command) { + try { + return this.bukkitcommandWrapperConstructor.newInstance(Bukkit.getServer(), command); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + return null; + } + } + + private boolean resolveBukkitCmdWrapperConstructor() { + if (this.bukkitcommandWrapperConstructor != null) return true; + try { + this.bukkitcommandWrapperConstructor = Class.forName(getCraftBukkitPrefix("command.BukkitCommandWrapper")).getDeclaredConstructor(Class.forName("org.bukkit.craftbukkit." + this.nmsVersion + ".CraftServer"), Command.class); + this.bukkitcommandWrapperConstructor.setAccessible(true); + return true; + } catch (NoSuchMethodException | ClassNotFoundException e) { + e.printStackTrace(); + return false; + } + } + + private boolean resolveAMethod(Object commandDispatcher) { + if (this.aMethod != null) return true; + try { + this.aMethod = commandDispatcher.getClass().getDeclaredMethod("a"); + this.aMethod.setAccessible(true); + return true; + } catch (NoSuchMethodException e) { + e.printStackTrace(); + return false; + } + } +}