diff --git a/core/src/main/java/fr/traqueur/commands/api/CommandInvoker.java b/core/src/main/java/fr/traqueur/commands/api/CommandInvoker.java new file mode 100644 index 0000000..b608be9 --- /dev/null +++ b/core/src/main/java/fr/traqueur/commands/api/CommandInvoker.java @@ -0,0 +1,187 @@ +package fr.traqueur.commands.api; + +import fr.traqueur.commands.api.arguments.TabCompleter; +import fr.traqueur.commands.api.exceptions.ArgumentIncorrectException; +import fr.traqueur.commands.api.exceptions.TypeArgumentNotExistException; +import fr.traqueur.commands.api.requirements.Requirement; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * CommandInvoker is responsible for invoking commands based on the provided label and arguments. + * It checks permissions, requirements, and executes the command if all conditions are met. + * + * @param The type of the command sender (e.g., Player, Console). + * @param The type of the source (e.g., Player, CommandSender). + */ +public class CommandInvoker { + + /** + * The command manager that holds the commands and their configurations. + */ + private final CommandManager commandManager; + + /** + * Constructor for CommandInvoker. + * @param manager The command manager that holds the commands and their configurations. + */ + public CommandInvoker(CommandManager manager) { + this.commandManager = manager; + } + + /** + * Get the command label from the label and the arguments. + * @param label The label of the command. + * @param args The arguments of the command. + * @param commandLabelSize The size of the command label. + * @return The command label. + */ + private String getCommandLabel(String label, String[] args, int commandLabelSize) { + StringBuilder buffer = new StringBuilder(); + String labelLower = label.toLowerCase(); + buffer.append(labelLower); + for (int x = 0; x < commandLabelSize; x++) { + buffer.append(".").append(args[x].toLowerCase()); + } + return buffer.toString(); + } + + /** + * Invokes a command based on the provided source, base label, and raw arguments. + * It checks for command existence, permissions, requirements, and executes the command if valid. + * + * @param source The source of the command (e.g., Player, Console). + * @param baseLabel The base label of the command. + * @param rawArgs The raw arguments passed to the command. + * @return true if the command was successfully invoked, false otherwise. + */ + public boolean invoke(S source, + String baseLabel, + String[] rawArgs) { + + Map> commands = this.commandManager.getCommands(); + + String cmdLabel = ""; + Command commandFramework = null; + for (int i = rawArgs.length; i >= 0; i--) { + cmdLabel = getCommandLabel(baseLabel, rawArgs, i); + commandFramework = commands.getOrDefault(cmdLabel, null); + if(commandFramework != null) { + break; + } + } + + if (commandFramework == null) { + return false; + } + + if(commandFramework.inGameOnly() && ! this.commandManager.getPlatform().isPlayer(source)) { + this.commandManager.getPlatform().sendMessage(source, this.commandManager.getMessageHandler().getOnlyInGameMessage()); + return true; + } + + if (!commandFramework.getPermission().isEmpty() && !this.commandManager.getPlatform().hasPermission(source, commandFramework.getPermission())) { + this.commandManager.getPlatform().sendMessage(source, this.commandManager.getMessageHandler().getNoPermissionMessage()); + return true; + } + + List> requirements = commandFramework.getRequirements(); + for (Requirement requirement : requirements) { + if (!requirement.check(source)) { + String error = requirement.errorMessage().isEmpty() + ? this.commandManager.getMessageHandler().getRequirementMessage() + : requirement.errorMessage(); + error = error.replace("%requirement%", requirement.getClass().getSimpleName()); + this.commandManager.getPlatform().sendMessage(source, error); + return true; + } + } + + + int subCommand = cmdLabel.split("\\.").length - 1; + String[] modArgs = Arrays.copyOfRange(rawArgs, subCommand, rawArgs.length); + + if ((modArgs.length < commandFramework.getArgs().size()) || (!commandFramework.isInfiniteArgs() && (modArgs.length > commandFramework.getArgs().size() + commandFramework.getOptinalArgs().size()))) { + String usage = commandFramework.getUsage().equalsIgnoreCase("") + ? commandFramework.generateDefaultUsage(this.commandManager.getPlatform(), source, cmdLabel) + : commandFramework.getUsage(); + this.commandManager.getPlatform().sendMessage(source, usage); + return true; + } + + try { + Arguments arguments = this.commandManager.parse(commandFramework, modArgs); + commandFramework.execute(source, arguments); + } catch (TypeArgumentNotExistException e) { + this.commandManager.getPlatform().sendMessage(source, "&cPlease contact the developer of the commands library, its should not happen."); + return false; + } catch (ArgumentIncorrectException e) { + String message = this.commandManager.getMessageHandler().getArgNotRecognized(); + message = message.replace("%arg%", e.getInput()); + this.commandManager.getPlatform().sendMessage(source, message); + } + return true; + } + + /** + * Suggests completions for the command based on the provided source, label, and arguments. + * It filters the suggestions based on the current argument and checks permissions for each suggestion. + * + * @param source The source of the command (e.g., Player, Console). + * @param label The label of the command. + * @param args The arguments passed to the command. + * @return A list of suggested completions. + */ + public List suggest(S source, String label, String[] args) { + String arg = args[args.length-1]; + + + Map>> completers = commandManager.getCompleters(); + Map> commands = this.commandManager.getCommands(); + + String cmdLabel = ""; + Map> map = null; + for (int i = args.length; i >= 0; i--) { + cmdLabel = getCommandLabel(label, args, i); + map = completers.getOrDefault(cmdLabel, null); + if(map != null) { + break; + } + } + + if (map == null || !map.containsKey(args.length)) { + return Collections.emptyList(); + } + + TabCompleter converter = map.get(args.length); + String argsBeforeString = (label + + "." + + String.join(".", Arrays.copyOf(args, args.length - 1))) + .replaceFirst("^" + cmdLabel + "\\.", ""); + + List completer = converter.onCompletion(source, Arrays.asList(argsBeforeString.split("\\."))) + .stream() + .filter(str -> str.toLowerCase().startsWith(arg.toLowerCase()) || str.equalsIgnoreCase(arg)) + .collect(Collectors.toList()); + + String finalCmdLabel = cmdLabel; + return completer.stream().filter(str -> { + String cmdLabelInner = finalCmdLabel + "." + str.toLowerCase(); + if(commands.containsKey(cmdLabelInner)) { + Command frameworkCommand = commands.get(cmdLabelInner); + List> requirements = frameworkCommand.getRequirements(); + for (Requirement requirement : requirements) { + if (!requirement.check(source)) { + return false; + } + } + return frameworkCommand.getPermission().isEmpty() || this.commandManager.getPlatform().hasPermission(source, frameworkCommand.getPermission()); + } + return true; + }).collect(Collectors.toList()); + } +} diff --git a/core/src/main/java/fr/traqueur/commands/api/CommandManager.java b/core/src/main/java/fr/traqueur/commands/api/CommandManager.java index da8426a..c54f21a 100644 --- a/core/src/main/java/fr/traqueur/commands/api/CommandManager.java +++ b/core/src/main/java/fr/traqueur/commands/api/CommandManager.java @@ -57,6 +57,8 @@ public abstract class CommandManager { private final Map>> completers; + private final CommandInvoker invoker; + /** * The message handler of the command manager. */ @@ -87,6 +89,7 @@ public CommandManager(CommandPlatform platform) { this.commands = new HashMap<>(); this.typeConverters = new HashMap<>(); this.completers = new HashMap<>(); + this.invoker = new CommandInvoker<>(this); this.registerInternalConverters(); } @@ -500,6 +503,14 @@ private boolean applyParsing(String[] args, Arguments arguments, List getInvoker() { + return invoker; + } + /** * Register the internal converters of the command manager. */ diff --git a/core/src/main/java/fr/traqueur/commands/api/CommandPlatform.java b/core/src/main/java/fr/traqueur/commands/api/CommandPlatform.java index 7a04b83..331c16a 100644 --- a/core/src/main/java/fr/traqueur/commands/api/CommandPlatform.java +++ b/core/src/main/java/fr/traqueur/commands/api/CommandPlatform.java @@ -40,6 +40,22 @@ public interface CommandPlatform { */ boolean hasPermission(S sender, String permission); + /** + * Checks if the sender is a player. + * + * @param sender The sender to check. + * @return true if the sender is a player, false otherwise. + */ + boolean isPlayer(S sender); + + /** + * Sends a message to the sender. + * + * @param sender The sender to send the message to. + * @param message The message to send. + */ + void sendMessage(S sender, String message); + /** * Adds a command to the platform. * diff --git a/core/src/main/java/fr/traqueur/commands/api/updater/Updater.java b/core/src/main/java/fr/traqueur/commands/api/updater/Updater.java index a8cb5b1..93c11e5 100644 --- a/core/src/main/java/fr/traqueur/commands/api/updater/Updater.java +++ b/core/src/main/java/fr/traqueur/commands/api/updater/Updater.java @@ -26,10 +26,18 @@ public class Updater { } } + /** + * Set the URL to use to check for the latest release + * @param URL_LATEST_RELEASE The URL to use + */ public static void setUrlLatestRelease(URL URL_LATEST_RELEASE) { Updater.URL_LATEST_RELEASE = URL_LATEST_RELEASE; } + /** + * Set the logger to use for logging messages + * @param LOGGER The logger to use + */ public static void setLogger(Logger LOGGER) { Updater.LOGGER = LOGGER; } diff --git a/core/src/main/java/fr/traqueur/commands/impl/arguments/EnumArgument.java b/core/src/main/java/fr/traqueur/commands/impl/arguments/EnumArgument.java index 175c5c9..36e19b9 100644 --- a/core/src/main/java/fr/traqueur/commands/impl/arguments/EnumArgument.java +++ b/core/src/main/java/fr/traqueur/commands/impl/arguments/EnumArgument.java @@ -7,18 +7,45 @@ import java.util.List; import java.util.stream.Collectors; +/** + * An argument converter for enum types, allowing conversion from string to enum and providing tab completion. + * + * @param The type of the enum. + * @param The type of the sender (e.g., player, console). + */ public class EnumArgument, S> implements ArgumentConverter>, TabCompleter { + /** + * Creates a new EnumArgument instance for the specified enum class. + * + * @param enumClass The class of the enum + * @param The type of the enum + * @param The type of the sender (e.g., player, console) + * @return A new instance of EnumArgument + */ public static , S> EnumArgument of(Class enumClass) { return new EnumArgument<>(enumClass); } + /** + * The class of the enum type this argument converter handles. + */ private final Class clazz; + /** + * Constructs a new EnumArgument for the specified enum class. + * + * @param clazz The class of the enum type + */ public EnumArgument(Class clazz) { this.clazz = clazz; } + /** + * Gets the class of the enum type this argument converter handles. + * + * @return The enum class + */ @Override public Enum apply(String s) { if (s == null || s.isEmpty()) { @@ -31,6 +58,11 @@ public Enum apply(String s) { } } + /** + * Gets the class of the enum type this argument converter handles. + * + * @return The enum class + */ @Override public List onCompletion(S sender, List args) { return Arrays.stream(clazz.getEnumConstants()) diff --git a/core/src/test/java/fr/traqueur/commands/api/CommandInvokerTest.java b/core/src/test/java/fr/traqueur/commands/api/CommandInvokerTest.java new file mode 100644 index 0000000..d8385e5 --- /dev/null +++ b/core/src/test/java/fr/traqueur/commands/api/CommandInvokerTest.java @@ -0,0 +1,130 @@ +package fr.traqueur.commands.api; + +import fr.traqueur.commands.api.exceptions.ArgumentIncorrectException; +import fr.traqueur.commands.api.exceptions.TypeArgumentNotExistException; +import fr.traqueur.commands.api.logging.MessageHandler; +import fr.traqueur.commands.api.requirements.Requirement; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class CommandInvokerTest { + + private CommandManager manager; + private CommandPlatform platform; + private MessageHandler messageHandler; + private CommandInvoker invoker; + private DummyCommand cmd; + + @BeforeEach + void setup() { + // Mock platform and manager + platform = mock(CommandPlatform.class); + messageHandler = mock(MessageHandler.class); + manager = mock(CommandManager.class); + when(manager.getPlatform()).thenReturn(platform); + when(manager.getMessageHandler()).thenReturn(messageHandler); + // Default platform behaviors + when(platform.isPlayer(anyString())).thenReturn(true); + when(platform.hasPermission(anyString(), anyString())).thenReturn(true); + // Register single command under key "base" + cmd = new DummyCommand(); + Map> map = new HashMap<>(); + map.put("base", cmd); + when(manager.getCommands()).thenReturn(map); + // Create invoker + invoker = new CommandInvoker<>(manager); + } + + @Test + void invoke_unknownCommand_returnsFalse() { + boolean res = invoker.invoke("user", "other", new String[]{"x"}); + assertFalse(res); + verifyNoInteractions(platform); + } + + @Test + void invoke_inGameOnly_nonPlayer_sendsOnlyInGame() { + cmd.setGameOnly(true); + when(platform.isPlayer("user")).thenReturn(false); + when(messageHandler.getOnlyInGameMessage()).thenReturn("ONLY_IN_GAME"); + + invoker.invoke("user", "base", new String[]{}); + + verify(platform).sendMessage("user", "ONLY_IN_GAME"); + } + + @Test + void invoke_noPermission_sendsNoPermission() { + cmd.setPermission("perm"); + when(platform.hasPermission("user", "perm")).thenReturn(false); + when(messageHandler.getNoPermissionMessage()).thenReturn("NO_PERMISSION"); + + invoker.invoke("user", "base", new String[]{}); + + verify(platform).sendMessage("user", "NO_PERMISSION"); + } + + @Test + void invoke_requirementFails_sendsRequirementError() { + Requirement req = mock(Requirement.class); + when(req.check(anyString())).thenReturn(false); + when(req.errorMessage()).thenReturn("REQ_ERR"); + cmd.addRequirements(req); + + invoker.invoke("user", "base", new String[]{}); + + verify(platform).sendMessage("user", "REQ_ERR"); + } + + @Test + void invoke_wrongArgCount_sendsUsage() { + cmd.addArgs("a", String.class); + cmd.setUsage("/base "); + + invoker.invoke("user", "base", new String[]{}); + + verify(platform).sendMessage("user", "/base "); + } + + @Test + void invoke_parseThrowsArgumentIncorrect_sendsArgNotRecognized() throws Exception { + cmd.addArgs("a", String.class); + when(manager.parse(eq(cmd), any(String[].class))).thenThrow(new ArgumentIncorrectException("bad")); + when(messageHandler.getArgNotRecognized()).thenReturn("ARG_ERR %arg%"); + + invoker.invoke("user", "base", new String[]{"bad"}); + + verify(platform).sendMessage("user", "ARG_ERR bad"); + } + + @Test + void invoke_valid_executesCommand_andReturnsTrue() { + AtomicBoolean executed = new AtomicBoolean(false); + DummyCommand custom = new DummyCommand() { + @Override + public void execute(String sender, Arguments arguments) { + executed.set(true); + } + }; + custom.addArgs("x", String.class); + when(manager.getCommands()).thenReturn(Collections.singletonMap("base", custom)); + + boolean result = invoker.invoke("user", "base", new String[]{"hello"}); + + assertTrue(result); + assertTrue(executed.get()); + } + + static class DummyCommand extends Command { + DummyCommand() { super(null, "base"); } + @Override public void execute(String sender, Arguments args) {} + } +} diff --git a/core/src/test/java/fr/traqueur/commands/api/CommandManagerTest.java b/core/src/test/java/fr/traqueur/commands/api/CommandManagerTest.java index f0eb688..f71e3f1 100644 --- a/core/src/test/java/fr/traqueur/commands/api/CommandManagerTest.java +++ b/core/src/test/java/fr/traqueur/commands/api/CommandManagerTest.java @@ -2,7 +2,6 @@ import fr.traqueur.commands.api.arguments.TabCompleter; import fr.traqueur.commands.api.exceptions.ArgumentIncorrectException; -import fr.traqueur.commands.api.exceptions.TypeArgumentNotExistException; import fr.traqueur.commands.impl.logging.InternalLogger; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -12,7 +11,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.logging.Logger; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.contains; @@ -36,6 +34,8 @@ static class FakePlatform implements CommandPlatform { @Override public void injectManager(CommandManager cm) {} @Override public java.util.logging.Logger getLogger() { return java.util.logging.Logger.getAnonymousLogger(); } @Override public boolean hasPermission(String sender, String permission) { return true; } + @Override public boolean isPlayer(String sender) {return false;} + @Override public void sendMessage(String sender, String message) {} @Override public void addCommand(Command command, String label) { added.add(label); } @Override public void removeCommand(String label, boolean sub) {} } diff --git a/core/src/test/java/fr/traqueur/commands/api/CommandTest.java b/core/src/test/java/fr/traqueur/commands/api/CommandTest.java index 1e6b426..459edaf 100644 --- a/core/src/test/java/fr/traqueur/commands/api/CommandTest.java +++ b/core/src/test/java/fr/traqueur/commands/api/CommandTest.java @@ -2,7 +2,6 @@ package fr.traqueur.commands.api; -import fr.traqueur.commands.api.requirements.Requirement; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -38,6 +37,8 @@ void setUp() { @Override public void injectManager(CommandManager commandManager) {} @Override public java.util.logging.Logger getLogger() { return java.util.logging.Logger.getAnonymousLogger(); } @Override public boolean hasPermission(Object sender, String permission) { return true; } + @Override public boolean isPlayer(Object sender) {return false;} + @Override public void sendMessage(Object sender, String message) {} @Override public void addCommand(Command command, String label) {} @Override public void removeCommand(String label, boolean subcommand) {} }; @@ -94,6 +95,8 @@ void testRegisterDelegatesToManager() { @Override public void injectManager(CommandManager commandManager) {} @Override public java.util.logging.Logger getLogger() { return java.util.logging.Logger.getAnonymousLogger(); } @Override public boolean hasPermission(Object sender, String permission) { return true; } + @Override public boolean isPlayer(Object sender) {return false;} + @Override public void sendMessage(Object sender, String message) {} @Override public void addCommand(Command command, String label) {called.set(true);} @Override public void removeCommand(String label, boolean subcommand) { } }) {}; @@ -110,6 +113,8 @@ void testUnregisterDelegatesToManager() { @Override public void injectManager(CommandManager commandManager) {} @Override public java.util.logging.Logger getLogger() { return java.util.logging.Logger.getAnonymousLogger(); } @Override public boolean hasPermission(Object sender, String permission) { return true; } + @Override public boolean isPlayer(Object sender) {return false;} + @Override public void sendMessage(Object sender, String message) {} @Override public void addCommand(Command command, String label) {} @Override public void removeCommand(String label, boolean subcommand) { called.set(true); } }) {}; diff --git a/core/src/test/java/fr/traqueur/commands/api/updater/UpdaterTest.java b/core/src/test/java/fr/traqueur/commands/api/updater/UpdaterTest.java index 5b93b83..7ad288f 100644 --- a/core/src/test/java/fr/traqueur/commands/api/updater/UpdaterTest.java +++ b/core/src/test/java/fr/traqueur/commands/api/updater/UpdaterTest.java @@ -5,15 +5,13 @@ import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; -import java.net.URL; -import java.net.URLStreamHandler; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mockStatic; class UpdaterTest { diff --git a/core/src/test/java/fr/traqueur/commands/impl/arguments/BooleanArgumentTest.java b/core/src/test/java/fr/traqueur/commands/impl/arguments/BooleanArgumentTest.java index 2b11529..2d1b3ac 100644 --- a/core/src/test/java/fr/traqueur/commands/impl/arguments/BooleanArgumentTest.java +++ b/core/src/test/java/fr/traqueur/commands/impl/arguments/BooleanArgumentTest.java @@ -3,7 +3,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.Arrays; import java.util.Collections; import java.util.List; diff --git a/core/src/test/java/fr/traqueur/commands/impl/arguments/DoubleArgumentTest.java b/core/src/test/java/fr/traqueur/commands/impl/arguments/DoubleArgumentTest.java index b11b3fc..804c8dc 100644 --- a/core/src/test/java/fr/traqueur/commands/impl/arguments/DoubleArgumentTest.java +++ b/core/src/test/java/fr/traqueur/commands/impl/arguments/DoubleArgumentTest.java @@ -3,7 +3,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; class DoubleArgumentTest { private DoubleArgument converter; diff --git a/core/src/test/java/fr/traqueur/commands/impl/arguments/IntegerArgumentTest.java b/core/src/test/java/fr/traqueur/commands/impl/arguments/IntegerArgumentTest.java index be57a99..aeeacb8 100644 --- a/core/src/test/java/fr/traqueur/commands/impl/arguments/IntegerArgumentTest.java +++ b/core/src/test/java/fr/traqueur/commands/impl/arguments/IntegerArgumentTest.java @@ -3,7 +3,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; class IntegerArgumentTest { diff --git a/core/src/test/java/fr/traqueur/commands/impl/arguments/LongArgumentTest.java b/core/src/test/java/fr/traqueur/commands/impl/arguments/LongArgumentTest.java index ab632b1..9c77e65 100644 --- a/core/src/test/java/fr/traqueur/commands/impl/arguments/LongArgumentTest.java +++ b/core/src/test/java/fr/traqueur/commands/impl/arguments/LongArgumentTest.java @@ -3,7 +3,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; class LongArgumentTest { diff --git a/core/src/test/java/fr/traqueur/commands/impl/logging/InternalMessageHandlerTest.java b/core/src/test/java/fr/traqueur/commands/impl/logging/InternalMessageHandlerTest.java index 77f5f1c..ba53090 100644 --- a/core/src/test/java/fr/traqueur/commands/impl/logging/InternalMessageHandlerTest.java +++ b/core/src/test/java/fr/traqueur/commands/impl/logging/InternalMessageHandlerTest.java @@ -3,7 +3,8 @@ import fr.traqueur.commands.impl.logging.InternalMessageHandler; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; + +import static org.junit.jupiter.api.Assertions.assertEquals; class InternalMessageHandlerTest { diff --git a/spigot/src/main/java/fr/traqueur/commands/spigot/Executor.java b/spigot/src/main/java/fr/traqueur/commands/spigot/Executor.java deleted file mode 100644 index b77a0cd..0000000 --- a/spigot/src/main/java/fr/traqueur/commands/spigot/Executor.java +++ /dev/null @@ -1,234 +0,0 @@ -package fr.traqueur.commands.spigot; - -import fr.traqueur.commands.api.Arguments; -import fr.traqueur.commands.api.Command; -import fr.traqueur.commands.api.CommandManager; -import fr.traqueur.commands.api.arguments.TabCompleter; -import fr.traqueur.commands.api.exceptions.ArgumentIncorrectException; -import fr.traqueur.commands.api.exceptions.TypeArgumentNotExistException; -import fr.traqueur.commands.api.requirements.Requirement; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.plugin.Plugin; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * Represents the executor of the commands. - * @param The type of the plugin that owns the executor. - */ -public class Executor implements CommandExecutor, org.bukkit.command.TabCompleter { - - /** - * The plugin that owns the executor. - */ - private final T plugin; - - /** - * The command manager. - */ - private final CommandManager commandManager; - - /** - * The constructor of the executor. - * @param plugin The plugin that owns the executor. - * @param commandManager The command manager. - */ - public Executor(T plugin, CommandManager commandManager) { - this.plugin = plugin; - this.commandManager = commandManager; - } - - - /** - * Get the command label from the label and the arguments. - * @param label The label of the command. - * @param args The arguments of the command. - * @param commandLabelSize The size of the command label. - * @return The command label. - */ - private String getCommandLabel(String label, String[] args, int commandLabelSize) { - StringBuilder buffer = new StringBuilder(); - String labelLower = label.toLowerCase(); - buffer.append(labelLower); - for (int x = 0; x < commandLabelSize; x++) { - buffer.append(".").append(args[x].toLowerCase()); - } - return buffer.toString(); - } - - /** - * Parse the label of the command. - * @param label The label of the command. - * @return The parsed label or null if the label is not valid. - */ - private String parseLabel(String label) { - String labelLower = label.toLowerCase(); - if(labelLower.contains(":")) { - String[] split = labelLower.split(":"); - labelLower = split[1]; - if(!split[0].equalsIgnoreCase(plugin.getName().toLowerCase())) { - return null; - } - } - return labelLower; - } - - /** - * This method is called when a command is executed. - * @param sender The sender of the command. - * @param command The command executed. - * @param label The label of the command. - * @param args The arguments of the command. - * @return If the command is executed. - */ - @Override - public boolean onCommand(CommandSender sender, org.bukkit.command.Command command, String label, String[] args) { - if (!this.plugin.isEnabled()) { - return false; - } - - String labelLower = this.parseLabel(label); - - if(labelLower == null) { - return false; - } - - Map> commands = this.commandManager.getCommands(); - - String cmdLabel = ""; - Command commandFramework = null; - for (int i = args.length; i >= 0; i--) { - cmdLabel = getCommandLabel(labelLower, args, i); - commandFramework = commands.getOrDefault(cmdLabel, null); - if(commandFramework != null) { - break; - } - } - - if (commandFramework == null) { - return false; - } - - if(commandFramework.inGameOnly() && !(sender instanceof org.bukkit.entity.Player)) { - sender.sendMessage(ChatColor.translateAlternateColorCodes('&', this.commandManager.getMessageHandler().getOnlyInGameMessage())); - return true; - } - - if (!commandFramework.getPermission().isEmpty() && !sender.hasPermission(commandFramework.getPermission())) { - sender.sendMessage(ChatColor.translateAlternateColorCodes('&', this.commandManager.getMessageHandler().getNoPermissionMessage())); - return true; - } - - List> requirements = commandFramework.getRequirements(); - for (Requirement requirement : requirements) { - if (!requirement.check(sender)) { - String error = requirement.errorMessage().isEmpty() - ? this.commandManager.getMessageHandler().getRequirementMessage() - : requirement.errorMessage(); - error = error.replace("%requirement%", requirement.getClass().getSimpleName()); - sender.sendMessage(ChatColor.translateAlternateColorCodes('&', error)); - return true; - } - } - - - int subCommand = cmdLabel.split("\\.").length - 1; - String[] modArgs = Arrays.copyOfRange(args, subCommand, args.length); - - if (modArgs.length < commandFramework.getArgs().size()) { - String usage = commandFramework.getUsage().equalsIgnoreCase("") - ? commandFramework.generateDefaultUsage(this.commandManager.getPlatform(), sender, cmdLabel) - : commandFramework.getUsage(); - sender.sendMessage(ChatColor.translateAlternateColorCodes('&', usage)); - return true; - } - - if (!commandFramework.isInfiniteArgs() && (modArgs.length > commandFramework.getArgs().size() + commandFramework.getOptinalArgs().size())) { - String usage = commandFramework.getUsage().equalsIgnoreCase("") - ? commandFramework.generateDefaultUsage(this.commandManager.getPlatform(),sender, cmdLabel) - : commandFramework.getUsage(); - sender.sendMessage(ChatColor.translateAlternateColorCodes('&', usage)); - return true; - } - - try { - Arguments arguments = this.commandManager.parse(commandFramework, modArgs); - commandFramework.execute(sender, arguments); - } catch (TypeArgumentNotExistException e) { - throw new RuntimeException(e); - } catch (ArgumentIncorrectException e) { - String message = this.commandManager.getMessageHandler().getArgNotRecognized(); - message = message.replace("%arg%", e.getInput()); - sender.sendMessage(ChatColor.translateAlternateColorCodes('&', message)); - } - - return true; - } - - /** - * This method is called when a tab is completed. - * @param commandSender The sender of the command. - * @param command The command completed. - * @param label The label of the command. - * @param args The arguments of the command. - * @return The list of completions. - */ - @Override - public List onTabComplete(CommandSender commandSender, org.bukkit.command.Command command, String label, String[] args) { - String arg = args[args.length-1]; - - String labelLower = this.parseLabel(label); - if(labelLower == null) { - return Collections.emptyList(); - } - - Map>> completers = commandManager.getCompleters(); - Map> commands = this.commandManager.getCommands(); - - String cmdLabel = ""; - Map> map = null; - for (int i = args.length; i >= 0; i--) { - cmdLabel = getCommandLabel(labelLower, args, i); - map = completers.getOrDefault(cmdLabel, null); - if(map != null) { - break; - } - } - - if (map == null || !map.containsKey(args.length)) { - return Collections.emptyList(); - } - - TabCompleter converter = map.get(args.length); - String argsBeforeString = (label + - "." + - String.join(".", Arrays.copyOf(args, args.length - 1))) - .replaceFirst("^" + cmdLabel + "\\.", ""); - - List completer = converter.onCompletion(commandSender, Arrays.asList(argsBeforeString.split("\\."))) - .stream() - .filter(str -> str.toLowerCase().startsWith(arg.toLowerCase()) || str.equalsIgnoreCase(arg)) - .collect(Collectors.toList()); - - String finalCmdLabel = cmdLabel; - return completer.stream().filter(str -> { - String cmdLabelInner = finalCmdLabel + "." + str.toLowerCase(); - if(commands.containsKey(cmdLabelInner)) { - Command frameworkCommand = commands.get(cmdLabelInner); - List> requirements = frameworkCommand.getRequirements(); - for (Requirement requirement : requirements) { - if (!requirement.check(commandSender)) { - return false; - } - } - return frameworkCommand.getPermission().isEmpty() || commandSender.hasPermission(frameworkCommand.getPermission()); - } - return true; - }).collect(Collectors.toList()); - - } - -} diff --git a/spigot/src/main/java/fr/traqueur/commands/spigot/SpigotExecutor.java b/spigot/src/main/java/fr/traqueur/commands/spigot/SpigotExecutor.java new file mode 100644 index 0000000..f2cb97a --- /dev/null +++ b/spigot/src/main/java/fr/traqueur/commands/spigot/SpigotExecutor.java @@ -0,0 +1,89 @@ +package fr.traqueur.commands.spigot; + +import fr.traqueur.commands.api.CommandManager; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.Plugin; + +import java.util.Collections; +import java.util.List; + +/** + * Represents the executor of the commands. + * @param The type of the plugin that owns the executor. + */ +public class SpigotExecutor implements CommandExecutor, org.bukkit.command.TabCompleter { + + /** + * The plugin that owns the executor. + */ + private final T plugin; + + /** + * The command manager. + */ + private final CommandManager commandManager; + + /** + * The constructor of the executor. + * @param plugin The plugin that owns the executor. + * @param commandManager The command manager. + */ + public SpigotExecutor(T plugin, CommandManager commandManager) { + this.plugin = plugin; + this.commandManager = commandManager; + } + + /** + * Parse the label of the command. + * @param label The label of the command. + * @return The parsed label or null if the label is not valid. + */ + private String parseLabel(String label) { + if(label.contains(":")) { + String[] split = label.split(":"); + label = split[1]; + if(!split[0].equalsIgnoreCase(plugin.getName())) { + return null; + } + } + return label.toLowerCase(); + } + + /** + * This method is called when a command is executed. + * @param sender The sender of the command. + * @param command The command executed. + * @param label The label of the command. + * @param args The arguments of the command. + * @return If the command is executed. + */ + @Override + public boolean onCommand(CommandSender sender, org.bukkit.command.Command command, String label, String[] args) { + if (!this.plugin.isEnabled()) { + return false; + } + + String labelLower = this.parseLabel(label); + + return this.commandManager.getInvoker().invoke(sender, labelLower, args); + } + + /** + * This method is called when a tab is completed. + * @param commandSender The sender of the command. + * @param command The command completed. + * @param label The label of the command. + * @param args The arguments of the command. + * @return The list of completions. + */ + @Override + public List onTabComplete(CommandSender commandSender, org.bukkit.command.Command command, String label, String[] args) { + String labelLower = this.parseLabel(label); + if(labelLower == null) { + return Collections.emptyList(); + } + return this.commandManager.getInvoker().suggest(commandSender, labelLower, args); + } + +} diff --git a/spigot/src/main/java/fr/traqueur/commands/spigot/SpigotPlatform.java b/spigot/src/main/java/fr/traqueur/commands/spigot/SpigotPlatform.java index d40e352..9940ea2 100644 --- a/spigot/src/main/java/fr/traqueur/commands/spigot/SpigotPlatform.java +++ b/spigot/src/main/java/fr/traqueur/commands/spigot/SpigotPlatform.java @@ -4,6 +4,7 @@ import fr.traqueur.commands.api.CommandManager; import fr.traqueur.commands.api.CommandPlatform; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.command.CommandMap; import org.bukkit.command.CommandSender; import org.bukkit.command.PluginCommand; @@ -46,7 +47,7 @@ public class SpigotPlatform implements CommandPlatform executor; + private SpigotExecutor spigotExecutor; /** * The constructor of the plugin command. @@ -89,7 +90,7 @@ public T getPlugin() { public void injectManager(CommandManager commandManager) { //noinspection unchecked this.commandManager = commandManager; - this.executor = new Executor<>(plugin, this.commandManager); + this.spigotExecutor = new SpigotExecutor<>(plugin, this.commandManager); } /** @@ -111,6 +112,16 @@ public boolean hasPermission(CommandSender sender, String permission) { return false; } + @Override + public boolean isPlayer(CommandSender sender) { + return sender instanceof org.bukkit.entity.Player; + } + + @Override + public void sendMessage(CommandSender sender, String message) { + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', message)); + } + /** * {@inheritDoc} */ @@ -140,8 +151,8 @@ public void addCommand(Command command, String label) { throw new RuntimeException(e); } - cmd.setExecutor(this.executor); - cmd.setTabCompleter(this.executor); + cmd.setExecutor(this.spigotExecutor); + cmd.setTabCompleter(this.spigotExecutor); cmd.setAliases(command.getAliases().stream().map(s -> s.split("\\.")[0]).collect(Collectors.toList())); if(!commandMap.register(originCmdLabel, this.plugin.getName(), cmd)) { diff --git a/spigot/src/test/java/fr/traqueur/commands/spigot/SpigotIntegrationTest.java b/spigot/src/test/java/fr/traqueur/commands/spigot/SpigotIntegrationTest.java index 2006a49..01d206d 100644 --- a/spigot/src/test/java/fr/traqueur/commands/spigot/SpigotIntegrationTest.java +++ b/spigot/src/test/java/fr/traqueur/commands/spigot/SpigotIntegrationTest.java @@ -27,6 +27,8 @@ void setUp() { @Override public void injectManager(fr.traqueur.commands.api.CommandManager cm) {} @Override public java.util.logging.Logger getLogger() { return java.util.logging.Logger.getAnonymousLogger(); } @Override public boolean hasPermission(CommandSender sender, String permission) { return true; } + @Override public boolean isPlayer(CommandSender sender) {return sender instanceof Player;} + @Override public void sendMessage(CommandSender sender, String message) {} @Override public void addCommand(fr.traqueur.commands.api.Command command, String label) {} @Override public void removeCommand(String label, boolean subcommand) {} }) {}; diff --git a/spigot/src/test/java/fr/traqueur/commands/spigot/arguments/OfflinePlayerArgumentTest.java b/spigot/src/test/java/fr/traqueur/commands/spigot/arguments/OfflinePlayerArgumentTest.java index ad6e126..8da726b 100644 --- a/spigot/src/test/java/fr/traqueur/commands/spigot/arguments/OfflinePlayerArgumentTest.java +++ b/spigot/src/test/java/fr/traqueur/commands/spigot/arguments/OfflinePlayerArgumentTest.java @@ -7,7 +7,6 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; -import java.util.Arrays; import java.util.Collections; import java.util.List; diff --git a/spigot/src/test/java/fr/traqueur/commands/spigot/requirements/WorldRequirementTest.java b/spigot/src/test/java/fr/traqueur/commands/spigot/requirements/WorldRequirementTest.java index 5241709..61f9d47 100644 --- a/spigot/src/test/java/fr/traqueur/commands/spigot/requirements/WorldRequirementTest.java +++ b/spigot/src/test/java/fr/traqueur/commands/spigot/requirements/WorldRequirementTest.java @@ -8,8 +8,9 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; class WorldRequirementTest { private World mockWorld; diff --git a/spigot/src/test/java/fr/traqueur/commands/spigot/requirements/ZoneRequirementTest.java b/spigot/src/test/java/fr/traqueur/commands/spigot/requirements/ZoneRequirementTest.java index 848f179..8b727e1 100644 --- a/spigot/src/test/java/fr/traqueur/commands/spigot/requirements/ZoneRequirementTest.java +++ b/spigot/src/test/java/fr/traqueur/commands/spigot/requirements/ZoneRequirementTest.java @@ -9,7 +9,7 @@ import org.mockito.Mockito; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.when; class ZoneRequirementTest { diff --git a/velocity-test-plugin/src/main/java/fr/traqueur/velocityTestPlugin/VelocityTestPlugin.java b/velocity-test-plugin/src/main/java/fr/traqueur/velocityTestPlugin/VelocityTestPlugin.java index 4839298..37a9ed1 100644 --- a/velocity-test-plugin/src/main/java/fr/traqueur/velocityTestPlugin/VelocityTestPlugin.java +++ b/velocity-test-plugin/src/main/java/fr/traqueur/velocityTestPlugin/VelocityTestPlugin.java @@ -1,8 +1,8 @@ package fr.traqueur.velocityTestPlugin; import com.google.inject.Inject; -import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.plugin.Plugin; import com.velocitypowered.api.proxy.ProxyServer; import fr.traqueur.commands.velocity.CommandManager; diff --git a/velocity/src/main/java/fr/traqueur/commands/velocity/Executor.java b/velocity/src/main/java/fr/traqueur/commands/velocity/Executor.java deleted file mode 100644 index 567e803..0000000 --- a/velocity/src/main/java/fr/traqueur/commands/velocity/Executor.java +++ /dev/null @@ -1,226 +0,0 @@ -package fr.traqueur.commands.velocity; - -import com.velocitypowered.api.command.CommandSource; -import com.velocitypowered.api.command.RawCommand; -import com.velocitypowered.api.proxy.Player; -import fr.traqueur.commands.api.Arguments; -import fr.traqueur.commands.api.Command; -import fr.traqueur.commands.api.CommandManager; -import fr.traqueur.commands.api.arguments.TabCompleter; -import fr.traqueur.commands.api.exceptions.ArgumentIncorrectException; -import fr.traqueur.commands.api.exceptions.TypeArgumentNotExistException; -import fr.traqueur.commands.api.requirements.Requirement; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * The command executor for Velocity. - * This class implements the RawCommand interface and handles command execution and suggestions. - * - * @param The type of the command manager. - */ -public class Executor implements RawCommand { - - /** - * The serializer used to convert legacy components to Adventure components. - */ - private static final LegacyComponentSerializer SERIALIZER = LegacyComponentSerializer.legacyAmpersand(); - - /** - * The MiniMessage instance used for parsing messages. - */ - private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); - - /** - * The command manager that this executor uses to manage commands. - */ - private final CommandManager manager; - - /** - * Constructs a new Executor with the given command manager. - * - * @param manager The command manager to use for this executor. - */ - public Executor(CommandManager manager) { - this.manager = manager; - } - - /** - * Executes the command based on the provided invocation. - * It checks permissions, requirements, and executes the command if all conditions are met. - * - * @param invocation The invocation containing the command source and arguments. - */ - @Override - public void execute(Invocation invocation) { - CommandSource source = invocation.source(); - String[] args = invocation.arguments().split(" "); - String label = invocation.alias(); // base command (e.g. "test") - - String labelLower = label.toLowerCase(); - - Map> commands = this.manager.getCommands(); - - String cmdLabel = ""; - Command commandFramework = null; - for (int i = args.length; i >= 0; i--) { - cmdLabel = getCommandLabel(labelLower, args, i); - commandFramework = commands.getOrDefault(cmdLabel, null); - if(commandFramework != null) { - break; - } - } - - if (commandFramework == null) { - return; - } - - if(commandFramework.inGameOnly() && !(source instanceof Player)) { - source.sendMessage(this.parse(this.manager.getMessageHandler().getOnlyInGameMessage())); - return; - } - - if (!commandFramework.getPermission().isEmpty() && !source.hasPermission(commandFramework.getPermission())) { - source.sendMessage(this.parse(this.manager.getMessageHandler().getNoPermissionMessage())); - return; - } - - List> requirements = commandFramework.getRequirements(); - for (Requirement requirement : requirements) { - if (!requirement.check(source)) { - String error = requirement.errorMessage().isEmpty() - ? this.manager.getMessageHandler().getRequirementMessage() - : requirement.errorMessage(); - error = error.replace("%requirement%", requirement.getClass().getSimpleName()); - source.sendMessage(this.parse(error)); - return; - } - } - - int subCommand = cmdLabel.split("\\.").length - 1; - String[] modArgs = Arrays.copyOfRange(args, subCommand, args.length); - - if (modArgs.length < commandFramework.getArgs().size()) { - String usage = commandFramework.getUsage().equalsIgnoreCase("") - ? commandFramework.generateDefaultUsage(this.manager.getPlatform(), source, cmdLabel) - : commandFramework.getUsage(); - source.sendMessage(this.parse(usage)); - return; - } - - if (!commandFramework.isInfiniteArgs() && (modArgs.length > commandFramework.getArgs().size() + commandFramework.getOptinalArgs().size())) { - String usage = commandFramework.getUsage().equalsIgnoreCase("") - ? commandFramework.generateDefaultUsage(this.manager.getPlatform(), source, cmdLabel) - : commandFramework.getUsage(); - source.sendMessage(this.parse(usage)); - return; - } - - try { - Arguments arguments = this.manager.parse(commandFramework, modArgs); - commandFramework.execute(source, arguments); - } catch (TypeArgumentNotExistException e) { - throw new RuntimeException(e); - } catch (ArgumentIncorrectException e) { - String message = this.manager.getMessageHandler().getArgNotRecognized(); - message = message.replace("%arg%", e.getInput()); - source.sendMessage(this.parse(message)); - } - } - - /** - * Suggests completions for the command based on the provided invocation. - * It checks the command label and returns a list of suggestions based on the current arguments. - * - * @param invocation The invocation containing the command source and arguments. - * @return A list of suggested completions for the command. - */ - @Override - public List suggest(Invocation invocation) { - CommandSource source = invocation.source(); - String[] args = invocation.arguments().split(" "); - String label = invocation.alias(); - String arg = args[args.length-1]; - String labelLower = label.toLowerCase(); - - Map>> completers = this.manager.getCompleters(); - Map> commands = this.manager.getCommands(); - - String cmdLabel = ""; - Map> map = null; - for (int i = args.length; i >= 0; i--) { - cmdLabel = getCommandLabel(labelLower, args, i); - map = completers.getOrDefault(cmdLabel, null); - if(map != null) { - break; - } - } - - if (map == null || !map.containsKey(args.length)) { - return Collections.emptyList(); - } - TabCompleter converter = map.get(args.length); - String argsBeforeString = (label + - "." + - String.join(".", Arrays.copyOf(args, args.length - 1))) - .replaceFirst("^" + cmdLabel + "\\.", ""); - - List completer = converter.onCompletion(source, Arrays.asList(argsBeforeString.split("\\."))) - .stream() - .filter(str -> str.toLowerCase().startsWith(arg.toLowerCase()) || str.equalsIgnoreCase(arg)) - .toList(); - - String finalCmdLabel = cmdLabel; - return completer.stream().filter(str -> { - String cmdLabelInner = finalCmdLabel + "." + str.toLowerCase(); - if(commands.containsKey(cmdLabelInner)) { - Command frameworkCommand = commands.get(cmdLabelInner); - List> requirements = frameworkCommand.getRequirements(); - for (Requirement requirement : requirements) { - if (!requirement.check(source)) { - return false; - } - } - return frameworkCommand.getPermission().isEmpty() || source.hasPermission(frameworkCommand.getPermission()); - } - return true; - }).collect(Collectors.toList()); - } - - /** - * Parses a message from legacy format to Adventure format. - * - * @param message The message in legacy format. - * @return The parsed message in Adventure format. - */ - private Component parse(String message) { - Component legacy = SERIALIZER.deserialize(message); - String asMini = MINI_MESSAGE.serialize(legacy); - return MINI_MESSAGE.deserialize(asMini); - } - - /** - * Constructs a command label by appending the arguments to the base label. - * - * @param label The base command label. - * @param args The arguments to append. - * @param commandLabelSize The number of arguments to include in the label. - * @return The constructed command label. - */ - private String getCommandLabel(String label, String[] args, int commandLabelSize) { - StringBuilder buffer = new StringBuilder(); - String labelLower = label.toLowerCase(); - buffer.append(labelLower); - for (int x = 0; x < commandLabelSize; x++) { - buffer.append(".").append(args[x].toLowerCase()); - } - return buffer.toString(); - } -} \ No newline at end of file diff --git a/velocity/src/main/java/fr/traqueur/commands/velocity/VelocityExecutor.java b/velocity/src/main/java/fr/traqueur/commands/velocity/VelocityExecutor.java new file mode 100644 index 0000000..94a4545 --- /dev/null +++ b/velocity/src/main/java/fr/traqueur/commands/velocity/VelocityExecutor.java @@ -0,0 +1,61 @@ +package fr.traqueur.commands.velocity; + +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.command.RawCommand; +import fr.traqueur.commands.api.CommandManager; + +import java.util.List; + +/** + * The command executor for Velocity. + * This class implements the RawCommand interface and handles command execution and suggestions. + * + * @param The type of the command manager. + */ +public class VelocityExecutor implements RawCommand { + + /** + * The command manager that this executor uses to manage commands. + */ + private final CommandManager manager; + + /** + * Constructs a new Executor with the given command manager. + * + * @param manager The command manager to use for this executor. + */ + public VelocityExecutor(CommandManager manager) { + this.manager = manager; + } + + /** + * Executes the command based on the provided invocation. + * It checks permissions, requirements, and executes the command if all conditions are met. + * + * @param invocation The invocation containing the command source and arguments. + */ + @Override + public void execute(Invocation invocation) { + CommandSource source = invocation.source(); + String[] args = invocation.arguments().split(" "); + String label = invocation.alias(); + String labelLower = label.toLowerCase(); + this.manager.getInvoker().invoke(source, labelLower, args); + } + + /** + * Suggests completions for the command based on the provided invocation. + * It checks the command label and returns a list of suggestions based on the current arguments. + * + * @param invocation The invocation containing the command source and arguments. + * @return A list of suggested completions for the command. + */ + @Override + public List suggest(Invocation invocation) { + CommandSource source = invocation.source(); + String[] args = invocation.arguments().split(" "); + String label = invocation.alias(); + String labelLower = label.toLowerCase(); + return this.manager.getInvoker().suggest(source, labelLower, args); + } +} \ No newline at end of file diff --git a/velocity/src/main/java/fr/traqueur/commands/velocity/VelocityPlatform.java b/velocity/src/main/java/fr/traqueur/commands/velocity/VelocityPlatform.java index 481536d..fd07b4d 100644 --- a/velocity/src/main/java/fr/traqueur/commands/velocity/VelocityPlatform.java +++ b/velocity/src/main/java/fr/traqueur/commands/velocity/VelocityPlatform.java @@ -5,6 +5,9 @@ import fr.traqueur.commands.api.Command; import fr.traqueur.commands.api.CommandManager; import fr.traqueur.commands.api.CommandPlatform; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Logger; @@ -17,6 +20,17 @@ */ public class VelocityPlatform implements CommandPlatform { + + /** + * The serializer used to convert legacy components to Adventure components. + */ + private static final LegacyComponentSerializer SERIALIZER = LegacyComponentSerializer.legacyAmpersand(); + + /** + * The MiniMessage instance used for parsing messages. + */ + private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); + /** * The plugin instance associated with this platform. * This is used to access the plugin's methods and properties. @@ -86,6 +100,16 @@ public boolean hasPermission(CommandSource sender, String permission) { return sender.hasPermission(permission); } + @Override + public boolean isPlayer(CommandSource sender) { + return sender instanceof com.velocitypowered.api.proxy.Player; + } + + @Override + public void sendMessage(CommandSource sender, String message) { + sender.sendMessage(this.parse(message)); + } + /** * {@inheritDoc} */ @@ -120,7 +144,7 @@ public void addCommand(Command command, String label) { .aliases(aliases) .plugin(plugin) .build(), - new Executor<>(commandManager) + new VelocityExecutor<>(commandManager) ); } } @@ -136,4 +160,16 @@ public void removeCommand(String label, boolean subcommand) { this.server.getCommandManager().unregister(label); } } + + /** + * Parses a message from legacy format to Adventure format. + * + * @param message The message in legacy format. + * @return The parsed message in Adventure format. + */ + private Component parse(String message) { + Component legacy = SERIALIZER.deserialize(message); + String asMini = MINI_MESSAGE.serialize(legacy); + return MINI_MESSAGE.deserialize(asMini); + } }