diff --git a/commandapi-core/src/main/java/dev/jorel/commandapi/AbstractArgumentTree.java b/commandapi-core/src/main/java/dev/jorel/commandapi/AbstractArgumentTree.java index 0b32375f0d..acaa7bb0af 100644 --- a/commandapi-core/src/main/java/dev/jorel/commandapi/AbstractArgumentTree.java +++ b/commandapi-core/src/main/java/dev/jorel/commandapi/AbstractArgumentTree.java @@ -2,6 +2,7 @@ import com.mojang.brigadier.tree.CommandNode; import dev.jorel.commandapi.arguments.AbstractArgument; +import dev.jorel.commandapi.arguments.AbstractArgument.NodeInformation; import dev.jorel.commandapi.exceptions.MissingCommandExecutorException; import java.util.ArrayList; @@ -89,47 +90,16 @@ public void setArguments(List> ////////////////// // Registration // ////////////////// - - /** - * @return A list of paths that represent the possible branches of this argument tree as Strings, starting with the - * base argument held by this tree. - */ - public List> getBranchesAsStrings() { - List> argumentStrings = new ArrayList<>(); - - // Create paths for the argument at this node - List> baseArgumentPaths = new ArrayList<>(); - baseArgumentPaths.add(new ArrayList<>()); - argument.appendToCommandPaths(baseArgumentPaths); - - // If this node is executable, add it as a valid path - if (this.executor.hasAnyExecutors()) argumentStrings.addAll(baseArgumentPaths); - - // Add paths for the branches - for (AbstractArgumentTree child : arguments) { - for (List subArgs : child.getBranchesAsStrings()) { - for (List basePath : baseArgumentPaths) { - List mergedPaths = new ArrayList<>(); - mergedPaths.addAll(basePath); - mergedPaths.addAll(subArgs); - argumentStrings.add(mergedPaths); - } - } - } - - return argumentStrings; - } - /** * Builds the Brigadier {@link CommandNode} structure for this argument tree. * - * @param previousNodes A List of {@link CommandNode}s to add this argument onto. - * @param previousArguments A List of CommandAPI arguments that came before this argument tree. - * @param previousArgumentNames A List of Strings containing the node names that came before this argument. - * @param The Brigadier Source object for running commands. + * @param previousNodeInformation The {@link NodeInformation} of the argument this argument is being added to. + * @param previousArguments A List of CommandAPI arguments that came before this argument tree. + * @param previousArgumentNames A List of Strings containing the node names that came before this argument. + * @param The Brigadier Source object for running commands. */ public void buildBrigadierNode( - List> previousNodes, + NodeInformation previousNodeInformation, List previousArguments, List previousArgumentNames ) { CommandAPIHandler handler = CommandAPIHandler.getInstance(); @@ -141,16 +111,28 @@ public void buildBrigadierNode( } // Create node for this argument - previousNodes = argument.addArgumentNodes(previousNodes, previousArguments, previousArgumentNames, + previousNodeInformation = argument.addArgumentNodes(previousNodeInformation, previousArguments, previousArgumentNames, executor.hasAnyExecutors() ? args -> handler.generateBrigadierCommand(args, executor) : null); + // Collect children into our own list + List childrenNodeInformation = new ArrayList<>(); + // Add our branches as children to the node for (AbstractArgumentTree child : arguments) { - // We need a new list for each branch of the tree + // Collect children into our own list + NodeInformation newPreviousNodeInformation = new NodeInformation<>( + previousNodeInformation.lastCommandNodes(), + children -> childrenNodeInformation.addAll(children) + ); + + // We need a new list so each branch acts independently List newPreviousArguments = new ArrayList<>(previousArguments); List newPreviousArgumentNames = new ArrayList<>(previousArgumentNames); - child.buildBrigadierNode(previousNodes, newPreviousArguments, newPreviousArgumentNames); + child.buildBrigadierNode(newPreviousNodeInformation, newPreviousArguments, newPreviousArgumentNames); } + + // Create registered nodes now that all children are generated + previousNodeInformation.childrenConsumer().createNodeWithChildren(childrenNodeInformation); } } diff --git a/commandapi-core/src/main/java/dev/jorel/commandapi/AbstractCommandAPICommand.java b/commandapi-core/src/main/java/dev/jorel/commandapi/AbstractCommandAPICommand.java index f9df1d633c..3fede09760 100644 --- a/commandapi-core/src/main/java/dev/jorel/commandapi/AbstractCommandAPICommand.java +++ b/commandapi-core/src/main/java/dev/jorel/commandapi/AbstractCommandAPICommand.java @@ -21,9 +21,9 @@ package dev.jorel.commandapi; import com.mojang.brigadier.Command; -import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.LiteralCommandNode; import dev.jorel.commandapi.arguments.AbstractArgument; +import dev.jorel.commandapi.arguments.AbstractArgument.NodeInformation; import dev.jorel.commandapi.exceptions.MissingCommandExecutorException; import dev.jorel.commandapi.exceptions.OptionalArgumentException; @@ -222,65 +222,6 @@ public void setSubcommands(List subcommands) { ////////////////// // Registration // ////////////////// - - @Override - public List> getArgumentsAsStrings() { - // Return an empty list if we have no arguments - if (subcommands.isEmpty() && !hasAnyArguments()) { - return List.of(List.of()); - } - - List> argumentStrings = new ArrayList<>(); - - // Build main path, if it is executable - if (executor.hasAnyExecutors()) { - // Required arguments represent one path - List> mainPath = new ArrayList<>(); - mainPath.add(new ArrayList<>()); - for (Argument argument : requiredArguments) { - argument.appendToCommandPaths(mainPath); - } - - List slicePositions = new ArrayList<>(); - // Note: Assumption that all paths are the same length - slicePositions.add(mainPath.get(0).size()); - - // Each optional argument is a potential stopping point - for (Argument argument : optionalArguments) { - argument.appendToCommandPaths(mainPath); - slicePositions.add(mainPath.get(0).size()); - } - - // Return each path as sublists of the main path - for (List path : mainPath) { - for (int slicePos : slicePositions) { - argumentStrings.add(path.subList(0, slicePos)); - } - } - } - - // Build subcommand paths - for (Impl subCommand : subcommands) { - String[] subCommandArguments = new String[subCommand.aliases.length + 1]; - subCommandArguments[0] = subCommand.name; - System.arraycopy(subCommand.aliases, 0, subCommandArguments, 1, subCommand.aliases.length); - for (int i = 0; i < subCommandArguments.length; i++) { - subCommandArguments[i] += ":LiteralArgument"; - } - - for (List subArgs : subCommand.getArgumentsAsStrings()) { - for (String subCommandArgument : subCommandArguments) { - List newPath = new ArrayList<>(); - newPath.add(subCommandArgument); - newPath.addAll(subArgs); - argumentStrings.add(newPath); - } - } - } - - return argumentStrings; - } - @Override protected void checkPreconditions() { if (!executor.hasAnyExecutors() && (subcommands.isEmpty() || hasAnyArguments())) { @@ -297,12 +238,14 @@ protected boolean isRootExecutable() { } @Override - protected void createArgumentNodes(LiteralCommandNode rootNode) { + protected List createArgumentNodes(LiteralCommandNode rootNode) { CommandAPIHandler handler = CommandAPIHandler.getInstance(); + List childrenNodes = new ArrayList<>(); + // Create arguments if (hasAnyArguments()) { - List> previousNodes = List.of(rootNode); + NodeInformation previousNodeInformation = new NodeInformation<>(List.of(rootNode), children -> childrenNodes.addAll(children)); List previousArguments = new ArrayList<>(); List previousArgumentNames = new ArrayList<>(); @@ -316,26 +259,50 @@ protected void createArgumentNodes(LiteralCommandNode rootNode) previousArguments.add(commandNames); - Function, Command> executorCreator = executor.hasAnyExecutors() ? - args -> handler.generateBrigadierCommand(args, executor) : null; + // Note that `executor#hasAnyExecutors` must be true here + // If not, then `checkPreconditions` would have thrown a `MissingCommandExecutorException` + Function, Command> executorCreator = args -> handler.generateBrigadierCommand(args, executor); + // Stack required arguments // Only the last required argument is executable - previousNodes = AbstractArgument.stackArguments(requiredArguments, previousNodes, previousArguments, previousArgumentNames, executorCreator); + previousNodeInformation = AbstractArgument.stackArguments(requiredArguments, previousNodeInformation, previousArguments, previousArgumentNames, executorCreator); // Add optional arguments for (Argument argument : optionalArguments) { // All optional arguments are executable - previousNodes = argument.addArgumentNodes(previousNodes, previousArguments, previousArgumentNames, executorCreator); + previousNodeInformation = argument.addArgumentNodes(previousNodeInformation, previousArguments, previousArgumentNames, executorCreator); } + + // Create registered nodes now that all children are generated + previousNodeInformation.childrenConsumer().createNodeWithChildren(List.of()); } // Add subcommands for (Impl subCommand : subcommands) { - Nodes nodes = subCommand.createCommandNodes(); + CommandInformation nodes = subCommand.createCommandInformation(""); + + // Add root node rootNode.addChild(nodes.rootNode()); + + RegisteredCommand.Node rootNodeInformation = nodes.command().rootNode(); + childrenNodes.add(rootNodeInformation); + + // Add aliases for (LiteralCommandNode aliasNode : nodes.aliasNodes()) { rootNode.addChild(aliasNode); + + // Create node information for the alias + String aliasName = aliasNode.getLiteral(); + childrenNodes.add( + new RegisteredCommand.Node( + aliasName, rootNodeInformation.className(), aliasName, + rootNodeInformation.executable(), rootNodeInformation.children() + ) + ); } } + + // Return node information + return childrenNodes; } } diff --git a/commandapi-core/src/main/java/dev/jorel/commandapi/AbstractCommandTree.java b/commandapi-core/src/main/java/dev/jorel/commandapi/AbstractCommandTree.java index add75565e2..c2520e39c1 100644 --- a/commandapi-core/src/main/java/dev/jorel/commandapi/AbstractCommandTree.java +++ b/commandapi-core/src/main/java/dev/jorel/commandapi/AbstractCommandTree.java @@ -2,6 +2,7 @@ import com.mojang.brigadier.tree.LiteralCommandNode; import dev.jorel.commandapi.arguments.AbstractArgument; +import dev.jorel.commandapi.arguments.AbstractArgument.NodeInformation; import dev.jorel.commandapi.exceptions.MissingCommandExecutorException; import java.util.ArrayList; @@ -72,25 +73,6 @@ public void setArguments(List> ////////////////// // Registration // ////////////////// - - @Override - public List> getArgumentsAsStrings() { - // Return an empty list if we have no arguments - if (arguments.isEmpty()) return List.of(List.of()); - - List> argumentStrings = new ArrayList<>(); - - // If this node is executable, no arguments is a valid path - if (this.executor.hasAnyExecutors()) argumentStrings.add(List.of()); - - // Add branching paths - for (AbstractArgumentTree argument : arguments) { - argumentStrings.addAll(argument.getBranchesAsStrings()); - } - - return argumentStrings; - } - @Override protected void checkPreconditions() { if (!executor.hasAnyExecutors() && arguments.isEmpty()) { @@ -105,9 +87,11 @@ protected boolean isRootExecutable() { } @Override - protected void createArgumentNodes(LiteralCommandNode rootNode) { + protected List createArgumentNodes(LiteralCommandNode rootNode) { CommandAPIHandler handler = CommandAPIHandler.getInstance(); + List childrenNodes = new ArrayList<>(); + // The previous arguments include an unlisted MultiLiteral representing the command name and aliases // This doesn't affect how the command acts, but it helps represent the command path in exceptions String[] literals = new String[aliases.length + 1]; @@ -119,11 +103,16 @@ protected void createArgumentNodes(LiteralCommandNode rootNode) // Build branches for (AbstractArgumentTree argument : arguments) { // We need new previousArguments lists for each branch so they don't interfere + NodeInformation previousNodeInformation = new NodeInformation<>(List.of(rootNode), children -> childrenNodes.addAll(children)); List previousArguments = new ArrayList<>(); List previousArgumentNames = new ArrayList<>(); + previousArguments.add(commandNames); - argument.buildBrigadierNode(List.of(rootNode), previousArguments, previousArgumentNames); + argument.buildBrigadierNode(previousNodeInformation, previousArguments, previousArgumentNames); } + + // Return children + return childrenNodes; } } diff --git a/commandapi-core/src/main/java/dev/jorel/commandapi/CommandAPI.java b/commandapi-core/src/main/java/dev/jorel/commandapi/CommandAPI.java index 9c63f73e1c..85bd740ebe 100644 --- a/commandapi-core/src/main/java/dev/jorel/commandapi/CommandAPI.java +++ b/commandapi-core/src/main/java/dev/jorel/commandapi/CommandAPI.java @@ -289,6 +289,6 @@ public static void registerCommand(Class commandClass) { * registered by the CommandAPI so far. The returned list is immutable. */ public static List getRegisteredCommands() { - return Collections.unmodifiableList(CommandAPIHandler.getInstance().registeredCommands); + return Collections.unmodifiableList(new ArrayList<>(CommandAPIHandler.getInstance().registeredCommands.values())); } } diff --git a/commandapi-core/src/main/java/dev/jorel/commandapi/CommandAPIHandler.java b/commandapi-core/src/main/java/dev/jorel/commandapi/CommandAPIHandler.java index eea07642a8..8bd4179e5f 100644 --- a/commandapi-core/src/main/java/dev/jorel/commandapi/CommandAPIHandler.java +++ b/commandapi-core/src/main/java/dev/jorel/commandapi/CommandAPIHandler.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.BiFunction; import java.util.function.Predicate; import java.util.regex.Pattern; @@ -91,7 +92,7 @@ public class CommandAPIHandler platform; - final List registeredCommands; // Keep track of what has been registered for type checking + final Map registeredCommands; // Keep track of what has been registered for type checking final Map, Previewable> previewableArguments; // Arguments with previewable chat static final Pattern NAMESPACE_PATTERN = Pattern.compile("[0-9a-z_.-]+"); @@ -103,7 +104,7 @@ public class CommandAPIHandler platform) { this.platform = platform; - this.registeredCommands = new ArrayList<>(); + this.registeredCommands = new LinkedHashMap<>(); // This should be a LinkedHashMap to preserve insertion order this.previewableArguments = new HashMap<>(); CommandAPIHandler.instance = this; @@ -172,18 +173,17 @@ public void registerCommand(ExecutableCommand command, String // Do plaform-specific pre-registration tasks platform.preCommandRegistration(command.getName()); - // Record the commands that are registered - List registeredCommandInformation = RegisteredCommand.fromExecutableCommand(command, namespace); - registeredCommands.addAll(registeredCommandInformation); + // Generate command information + ExecutableCommand.CommandInformation commandInformation = command.createCommandInformation(namespace); - for (RegisteredCommand singleCommand : registeredCommandInformation) { - CommandAPI.logInfo("Registering command /" + command.getName() + " " + String.join(" ", singleCommand.argsAsStr())); - } + LiteralCommandNode resultantNode = commandInformation.rootNode(); + List> aliasNodes = commandInformation.aliasNodes(); + RegisteredCommand registeredCommand = commandInformation.command(); - // Create command nodes - ExecutableCommand.Nodes nodes = command.createCommandNodes(); - LiteralCommandNode resultantNode = nodes.rootNode(); - List> aliasNodes = nodes.aliasNodes(); + // Log the commands being registered + for (List argsAsStr : registeredCommand.rootNode().argsAsStr()) { + CommandAPI.logInfo("Registering command /" + String.join(" ", argsAsStr)); + } // Handle command conflicts ensureNoCommandConflict(resultantNode); @@ -211,8 +211,21 @@ public void registerCommand(ExecutableCommand command, String // partial) command registration. Generate the dispatcher file! writeDispatcherToFile(); + // Merge RegisteredCommand into map + BiFunction mergeRegisteredCommands = (key, value) -> value == null ? registeredCommand : value.mergeCommandInformation(registeredCommand); + + if (registeredCommand.namespace().isEmpty()) { + registeredCommands.compute(registeredCommand.commandName(), mergeRegisteredCommands); + } else { + registeredCommands.compute(registeredCommand.commandName(), (key, value) -> value == null ? + registeredCommand.copyWithEmptyNamespace() : + value.mergeCommandInformation(registeredCommand) + ); + registeredCommands.compute(registeredCommand.namespace() + ":" + registeredCommand.commandName(), mergeRegisteredCommands); + } + // Do platform-specific post-registration tasks - platform.postCommandRegistration(registeredCommandInformation, resultantNode, aliasNodes); + platform.postCommandRegistration(registeredCommand, resultantNode, aliasNodes); } /** @@ -635,12 +648,18 @@ public void addPreviewableArgument(List previousArguments, Argument pr // Generate all paths to the argument List> paths = new ArrayList<>(); paths.add(new ArrayList<>()); - for (Argument argument : previousArguments) { - argument.appendToCommandPaths(paths); - } - previewableArgument.appendToCommandPaths(paths); - // Insert paths to out map + // TODO: Fix this, the `appendToCommandPaths` method was removed + // A smarter way to get this information should exist + // It probably makes sense to make a custom CommandNode for PreviewableArgument + if(true) throw new IllegalStateException("TODO: Fix this method"); + + // for (Argument argument : previousArguments) { + // argument.appendToCommandPaths(paths); + // } + // previewableArgument.appendToCommandPaths(paths); + + // Insert paths to our map for (List path : paths) { previewableArguments.put(path, previewable); } diff --git a/commandapi-core/src/main/java/dev/jorel/commandapi/CommandAPIPlatform.java b/commandapi-core/src/main/java/dev/jorel/commandapi/CommandAPIPlatform.java index 0709a5a026..e7134e3cf5 100644 --- a/commandapi-core/src/main/java/dev/jorel/commandapi/CommandAPIPlatform.java +++ b/commandapi-core/src/main/java/dev/jorel/commandapi/CommandAPIPlatform.java @@ -104,11 +104,11 @@ public interface CommandAPIPlatform registeredCommands, LiteralCommandNode resultantNode, List> aliasNodes); + public abstract void postCommandRegistration(RegisteredCommand registeredCommand, LiteralCommandNode resultantNode, List> aliasNodes); /** * Builds and registers a Brigadier command node. diff --git a/commandapi-core/src/main/java/dev/jorel/commandapi/ExecutableCommand.java b/commandapi-core/src/main/java/dev/jorel/commandapi/ExecutableCommand.java index 46417462ef..34a62566af 100644 --- a/commandapi-core/src/main/java/dev/jorel/commandapi/ExecutableCommand.java +++ b/commandapi-core/src/main/java/dev/jorel/commandapi/ExecutableCommand.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.function.Predicate; import com.mojang.brigadier.builder.LiteralArgumentBuilder; @@ -298,9 +299,6 @@ public Object getHelpTopic() { ////////////////// // Registration // ////////////////// - - public abstract List> getArgumentsAsStrings(); - /** * Overrides a command. Effectively the same as unregistering the command using * CommandAPI.unregister() and then registering the command using .register() @@ -328,19 +326,28 @@ public void register(String namespace) { ((CommandAPIHandler) CommandAPIHandler.getInstance()).registerCommand(this, namespace); } - record Nodes(LiteralCommandNode rootNode, List> aliasNodes) { + protected static record CommandInformation(LiteralCommandNode rootNode, List> aliasNodes, RegisteredCommand command) { } - Nodes createCommandNodes() { + protected CommandInformation createCommandInformation(String namespace) { checkPreconditions(); + // Create rootNode LiteralCommandNode rootNode = this.createCommandNodeBuilder(name).build(); - createArgumentNodes(rootNode); + List children = createArgumentNodes(rootNode); + // Create aliaseNodes List> aliasNodes = createAliasNodes(rootNode); - return new Nodes<>(rootNode, aliasNodes); + // Create command information + RegisteredCommand command = new RegisteredCommand( + name, aliases, namespace, permission, + Optional.ofNullable(shortDescription), Optional.ofNullable(fullDescription), Optional.ofNullable(usageDescription), Optional.ofNullable(helpTopic), + new RegisteredCommand.Node(name, getClass().getSimpleName(), name, isRootExecutable(), children) + ); + + return new CommandInformation<>(rootNode, aliasNodes, command); } protected LiteralArgumentBuilder createCommandNodeBuilder(String nodeName) { @@ -380,5 +387,5 @@ protected List> createAliasNodes(LiteralComm protected abstract boolean isRootExecutable(); - protected abstract void createArgumentNodes(LiteralCommandNode rootNode); + protected abstract List createArgumentNodes(LiteralCommandNode rootNode); } diff --git a/commandapi-core/src/main/java/dev/jorel/commandapi/RegisteredCommand.java b/commandapi-core/src/main/java/dev/jorel/commandapi/RegisteredCommand.java index 2aa91dcb60..0243d52599 100644 --- a/commandapi-core/src/main/java/dev/jorel/commandapi/RegisteredCommand.java +++ b/commandapi-core/src/main/java/dev/jorel/commandapi/RegisteredCommand.java @@ -1,19 +1,25 @@ package dev.jorel.commandapi; -import dev.jorel.commandapi.arguments.AbstractArgument; - import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; /** - * Class to store a registered command which has its command name and a list of - * arguments as a string. The arguments are expected to be of the form - * {@code node_name:class_name}, for example {@code value:IntegerArgument}. This - * class also contains the information required to construct a meaningful help - * topic for a command + * Class to store a registered command which has its command name and a tree representing its arguments. + * This class also contains the information required to construct a meaningful help topic for a command. + * + * @param commandName The name of this command, without any leading {@code /} characters + * @param aliases A {@link String}{@code []} of aliases for this command + * @param namespace The namespace for this command + * @param permission The {@link CommandPermission} required to run this command + * @param shortDescription An {@link Optional} containing this command's help's short description + * @param fullDescription An {@link Optional} containing this command's help's full description + * @param fullDescription An {@link Optional} containing this command's help's usage + * @param rootNode The root {@link Node} in the tree structure that holds the arguments of this command */ public record RegisteredCommand( @@ -23,34 +29,34 @@ public record RegisteredCommand( String commandName, /** - * @return The list of node names and argument class simple names in the form - * {@code node_name:class_name}, for example - * {@code value:}{@link IntegerArgument} + * @return A {@link String}{@code []} of aliases for this command + */ + String[] aliases, + + /** + * @return The namespace for this command */ - List argsAsStr, + String namespace, /** - * @return An unmodifiable list of arguments for this command + * @return The {@link CommandPermission} required to run this command */ - List> arguments, + CommandPermission permission, /** - * @return An {@link Optional} containing this command's help's short - * descriptions + * @return An {@link Optional} containing this command's help's short description */ Optional shortDescription, /** - * @return An {@link Optional} containing this command's help's full - * descriptions + * @return An {@link Optional} containing this command's help's full description */ Optional fullDescription, /** - * @return An {@link Optional} containing this command's help's - * usage + * @return An {@link Optional} containing this command's help's usage */ - Optional usageDescription, + Optional usageDescription, // TODO: Bukkit specific fields probably should not be in platform agnostic classes // Either make HelpTopic platform agnostic or move this field into bukkit-core @@ -60,46 +66,125 @@ public record RegisteredCommand( Optional helpTopic, /** - * @return a {@link String}{@code []} of aliases for this command + * @return The root {@link Node} in the tree structure that holds the arguments of this command */ - String[] aliases, + Node rootNode) { /** - * @return The {@link CommandPermission} required to run this command + * Class to store information about each argument in a command's tree. + * + * @param nodeName The name of this argument node + * @param className The name of the CommandAPI object that this node represents + * @param helpString The string that should be used to represent this node when automatically generating help usage. + * @param executable True if this node can be executed, and false otherwise + * @param children A {@link List} of nodes that are children to this node */ - CommandPermission permission, + public static record Node( + + /** + * @return The name of this argument node + */ + String nodeName, + + /** + * @return The name of the CommandAPI object that this node represents + */ + String className, + + /** + * @return The string that should be used to represent this node when automatically generating help usage. + */ + String helpString, + + /** + * @return True if this node can be executed, and false otherwise + */ + boolean executable, + + /** + * @return A {@link List} of nodes that are children to this node + */ + List children) { + + /** + * @return A {@link List} of each executable path starting at this node. Each path is represented as a {@link List} of + * strings, where each string represents a node with the form {@code node_name:class_name}, for example {@code value:IntegerArgument}. + */ + public List> argsAsStr() { + List> paths = new ArrayList<>(); + + // Add this node + String nodeString = nodeName + ":" + className; + if (executable) { + // This should be an ArrayList so parent nodes can insert themselves at the start + paths.add(new ArrayList<>(List.of(nodeString))); + } + + // Add children nodes + for (Node node : children) { + List> subPaths = node.argsAsStr(); + + for (List subPath : subPaths) { + subPath.add(0, nodeString); + } + + paths.addAll(subPaths); + } + + return paths; + } + + private Node merge(Node other) { + // Merge executable status + boolean mergeExecutable = this.executable || other.executable; + + // Merge children + Map childrenByName = new HashMap<>(); + for (Node child : this.children) { + childrenByName.put(child.nodeName, child); + } + for (Node child : other.children) { + childrenByName.compute(child.nodeName, (key, value) -> value == null ? child : value.merge(child)); + } + List mergeChildren = new ArrayList<>(childrenByName.values()); + + // Other information defaults to the node that was registered first (this) + return new Node(nodeName, className, helpString, mergeExecutable, mergeChildren); + } + } /** - * @return The namespace for this command + * Merges the given command into this command, returning the result. The result should accurately represent + * the command that would result from registering the commands in order. For example, registering two commands + * with different permissions results in the first command's permission being used, and so the result of this + * method will have the first command's permission. + * + * @param other The {@link RegisteredCommand} that was registered after this one. + * @return The {@link RegisteredCommand} that results from merging this and the other command. */ - String namespace) { - public static List fromExecutableCommand(ExecutableCommand command, String namespace) { - // Unpack command parameters - String commandName = command.getName(); - List> argumentsAsStrings = command.getArgumentsAsStrings(); - - Optional shortDescription = Optional.ofNullable(command.getShortDescription()); - Optional fullDescription = Optional.ofNullable(command.getFullDescription()); - Optional usageDescription = Optional.ofNullable(command.getUsage()); - Optional helpTopic = Optional.ofNullable(command.getHelpTopic()); - - String[] aliases = command.getAliases(); - CommandPermission permission = command.getPermission(); - - List result = new ArrayList<>(argumentsAsStrings.size()); - for (List argumentString : argumentsAsStrings) { - result.add(new RegisteredCommand( - commandName, argumentString, null, // TODO: Need to fix this. Trying to rebase changes from https://github.com/JorelAli/CommandAPI/pull/537 into `dev/command-build-rewrite` - shortDescription, fullDescription, usageDescription, - helpTopic, aliases, permission, namespace - )); - } + public RegisteredCommand mergeCommandInformation(RegisteredCommand other) { + // Merge aliases + String[] mergedAliases = new String[this.aliases.length + other.aliases.length]; + System.arraycopy(this.aliases, 0, mergedAliases, 0, this.aliases.length); + System.arraycopy(other.aliases, 0, mergedAliases, this.aliases.length, other.aliases.length); - return result; + // Merge arguments + Node mergedRootNode = this.rootNode.merge(other.rootNode); + + // Other information defaults to the command that was registered first (this) + return new RegisteredCommand(commandName, mergedAliases, namespace, permission, shortDescription, fullDescription, usageDescription, helpTopic, mergedRootNode); + } + + /** + * @return A copy of this {@link RegisteredCommand}, but with {@link RegisteredCommand#namespace()} as {@code ""}. + */ + public RegisteredCommand copyWithEmptyNamespace() { + return new RegisteredCommand(commandName, aliases, "", permission, shortDescription, fullDescription, usageDescription, helpTopic, rootNode); } - // As https://stackoverflow.com/a/32083420 mentions, Optional's hashCode, equals, and toString method don't work if the - // Optional wraps an array, like `Optional usageDescription`, so we have to use the Arrays methods ourselves + // The default implementation of `hashCode`, `equals`, and `toString` don't work for arrays, + // so we need to override and specifically use the Arrays methods for `String[] aliases` + // As https://stackoverflow.com/a/32083420 mentions, the same thing happens when Optionals wrap an array, like `Optional usageDescription` @Override public int hashCode() { @@ -107,7 +192,7 @@ public int hashCode() { int result = 1; result = prime * result + Arrays.hashCode(aliases); result = prime * result + Arrays.hashCode(usageDescription.orElse(null)); - result = prime * result + Objects.hash(argsAsStr, commandName, fullDescription, permission, shortDescription, helpTopic, namespace); + result = prime * result + Objects.hash(commandName, namespace, permission, shortDescription, fullDescription, helpTopic, rootNode); return result; } @@ -120,7 +205,7 @@ public boolean equals(Object obj) { return false; } RegisteredCommand other = (RegisteredCommand) obj; - return Arrays.equals(aliases, other.aliases) && Objects.equals(argsAsStr, other.argsAsStr) && Objects.equals(commandName, other.commandName) + return Arrays.equals(aliases, other.aliases) && Objects.equals(rootNode, other.rootNode) && Objects.equals(commandName, other.commandName) && Objects.equals(namespace, other.namespace) && Arrays.equals(usageDescription.orElse(null), other.usageDescription.orElse(null)) && Objects.equals(fullDescription, other.fullDescription) && Objects.equals(permission, other.permission) && Objects.equals(shortDescription, other.shortDescription) && Objects.equals(helpTopic, other.helpTopic); @@ -128,8 +213,10 @@ public boolean equals(Object obj) { @Override public String toString() { - return "RegisteredCommand [commandName=" + commandName + ", argsAsStr=" + argsAsStr + ", shortDescription=" + shortDescription + ", fullDescription=" + fullDescription + return "RegisteredCommand [commandName=" + commandName + ", aliases=" + Arrays.toString(aliases) + + ", namespace=" + namespace + ", permission=" + permission + + ", shortDescription=" + shortDescription + ", fullDescription=" + fullDescription + ", usageDescription=" + (usageDescription.isPresent() ? "Optional[" + Arrays.toString(usageDescription.get()) + "]" : "Optional.empty") - + ", aliases=" + Arrays.toString(aliases) + ", permission=" + permission + ", namespace=" + namespace + ", helpTopic=" + helpTopic + "]"; + + ", helpTopic=" + helpTopic + ", rootNode=" + rootNode + "]"; } } diff --git a/commandapi-core/src/main/java/dev/jorel/commandapi/arguments/AbstractArgument.java b/commandapi-core/src/main/java/dev/jorel/commandapi/arguments/AbstractArgument.java index fcbc6c68a2..c8b4e05d86 100644 --- a/commandapi-core/src/main/java/dev/jorel/commandapi/arguments/AbstractArgument.java +++ b/commandapi-core/src/main/java/dev/jorel/commandapi/arguments/AbstractArgument.java @@ -30,6 +30,7 @@ import dev.jorel.commandapi.AbstractArgumentTree; import dev.jorel.commandapi.CommandAPIHandler; import dev.jorel.commandapi.CommandPermission; +import dev.jorel.commandapi.RegisteredCommand; import dev.jorel.commandapi.commandnodes.UnnamedRequiredArgumentBuilder; import dev.jorel.commandapi.exceptions.DuplicateNodeNameException; import dev.jorel.commandapi.exceptions.GreedyArgumentException; @@ -321,33 +322,24 @@ public final Impl combineWith(Argument... combinedArguments) { ////////////////////// // Command Building // ////////////////////// - - public String getHelpString() { - return "<" + this.getNodeName() + ">"; - } - @Override public String toString() { return this.getNodeName() + "<" + this.getClass().getSimpleName() + ">"; } /** - * Adds this argument to the end of the all the current possible paths given. The added entry is built as {code nodeName:argumentClass}. - * Any arguments combined with this one are also added. - * - * @param argumentStrings A list of possible paths up to this argument so far. + * A record of the information needed to stack one argument onto another. + * + * @param lastCommandNodes A {@link List} of {@link CommandNode}s that were the last nodes in the structure of the previous argument. + * @param childrenConsumer A callback that accepts a {@link List} of all the {@link RegisteredCommand.Node}s that represent the nodes + * added as children to the previous argument. */ - public void appendToCommandPaths(List> argumentStrings) { - // Create paths for this argument - String argumentString = nodeName + ":" + getClass().getSimpleName(); - for (List path : argumentStrings) { - path.add(argumentString); - } + public static record NodeInformation(List> lastCommandNodes, ChildrenConsumer childrenConsumer) { + } - // Add combined arguments - for (Argument subArgument : combinedArguments) { - subArgument.appendToCommandPaths(argumentStrings); - } + @FunctionalInterface + public static interface ChildrenConsumer { + public void createNodeWithChildren(List children); } /** @@ -358,13 +350,13 @@ public void appendToCommandPaths(List> argumentStrings) { * This process is broken up into 4 other methods for the convenience of defining special node structures for specific * arguments. Those methods are: *
    - *
  • {@link #checkPreconditions(List, List, List)}
  • + *
  • {@link #checkPreconditions(NodeInformation, List, List)}
  • *
  • {@link #createArgumentBuilder(List, List)}
  • *
  • {@link #finishBuildingNode(ArgumentBuilder, List, Function)}
  • - *
  • {@link #linkNode(List, CommandNode, List, List, Function)}
  • + *
  • {@link #linkNode(NodeInformation, CommandNode, List, List, Function)}
  • *
* - * @param previousNodes A List of {@link CommandNode}s to add this argument onto. + * @param previousNodeInformation The {@link NodeInformation} of the argument this argument is being added to. * @param previousArguments A List of CommandAPI arguments that came before this argument. * @param previousArgumentNames A List of Strings containing the node names that came before this argument. * @param terminalExecutorCreator A function that transforms the list of {@code previousArguments} into an @@ -374,15 +366,15 @@ public void appendToCommandPaths(List> argumentStrings) { * @param The Brigadier Source object for running commands. * @return The list of last nodes in the Brigadier node structure for this argument. */ - public List> addArgumentNodes( - List> previousNodes, + public NodeInformation addArgumentNodes( + NodeInformation previousNodeInformation, List previousArguments, List previousArgumentNames, Function, Command> terminalExecutorCreator ) { CommandAPIHandler handler = CommandAPIHandler.getInstance(); // Check preconditions - checkPreconditions(previousNodes, previousArguments, previousArgumentNames); + checkPreconditions(previousNodeInformation, previousArguments, previousArgumentNames); // Handle previewable argument if (this instanceof Previewable) { @@ -396,23 +388,23 @@ public List> addArgumentNodes( CommandNode rootNode = finishBuildingNode(rootBuilder, previousArguments, terminalExecutorCreator); // Link node to previous - previousNodes = linkNode(previousNodes, rootNode, previousArguments, previousArgumentNames, terminalExecutorCreator); + previousNodeInformation = linkNode(previousNodeInformation, rootNode, previousArguments, previousArgumentNames, terminalExecutorCreator); // Return last nodes - return previousNodes; + return previousNodeInformation; } /** * Checks for any conditions that mean this argument cannot be built properly. * - * @param previousNodes A List of {@link CommandNode}s to add this argument onto. - * @param previousArguments A List of CommandAPI arguments that came before this argument. - * @param previousArgumentNames A List of Strings containing the node names that came before this argument. + * @param previousNodeInformation The {@link NodeInformation} of the argument this argument is being added to. + * @param previousArguments A List of CommandAPI arguments that came before this argument. + * @param previousArgumentNames A List of Strings containing the node names that came before this argument. */ public void checkPreconditions( - List> previousNodes, List previousArguments, List previousArgumentNames + NodeInformation previousNodeInformation, List previousArguments, List previousArgumentNames ) { - if(previousNodes.isEmpty()) { + if(previousNodeInformation.lastCommandNodes().isEmpty()) { throw new GreedyArgumentException(previousArguments, (Argument) this); } if (isListed && previousArgumentNames.contains(nodeName)) { @@ -489,7 +481,7 @@ public CommandNode finishBuildingNode(ArgumentBuilder CommandNode finishBuildingNode(ArgumentBuilder List> linkNode( - List> previousNodes, CommandNode rootNode, + public NodeInformation linkNode( + NodeInformation previousNodeInformation, CommandNode rootNode, List previousArguments, List previousArgumentNames, Function, Command> terminalExecutorCreator ) { // Add rootNode to the previous nodes - for(CommandNode previousNode : previousNodes) { + for(CommandNode previousNode : previousNodeInformation.lastCommandNodes()) { previousNode.addChild(rootNode); } - // A GreedyArgument cannot have arguments after it - previousNodes = this instanceof GreedyArgument ? List.of() : List.of(rootNode); - // Stack on combined arguments - previousNodes = stackArguments(combinedArguments, previousNodes, previousArguments, previousArgumentNames, terminalExecutorCreator); - - // Return last nodes - return previousNodes; + // Create information for this node + NodeInformation nodeInformation = new NodeInformation<>( + // A GreedyArgument cannot have arguments after it + this instanceof GreedyArgument ? List.of() : List.of(rootNode), + // Create registered node information once children are created + children -> previousNodeInformation.childrenConsumer().createNodeWithChildren(List.of( + new RegisteredCommand.Node( + nodeName, getClass().getSimpleName(), "<" + nodeName + ">", + combinedArguments.isEmpty() && terminalExecutorCreator != null, + children + ) + )) + ); + + // Stack on combined arguments and return last nodes + return stackArguments(combinedArguments, nodeInformation, previousArguments, previousArgumentNames, terminalExecutorCreator); } /** @@ -527,7 +528,7 @@ public List> linkNode( * CommandAPI arguments. Only the last argument will be executable. * * @param argumentsToStack A List of CommandAPI arguments to put in sequence - * @param previousNodes A List of {@link CommandNode}s that start the stack. + * @param previousNodeInformation The {@link NodeInformation} of the argument this stack is being added to. * @param previousArguments A List of CommandAPI arguments that came before the {@code argumentsToStack}. * @param previousArgumentNames A List of Strings containing the node names that came before the {@code argumentsToStack}. * @param terminalExecutorCreator A function that transforms the list of {@code previousArguments} into an @@ -537,18 +538,21 @@ public List> linkNode( * @param The Brigadier Source object for running commands. * @return The list of last nodes in the Brigadier {@link CommandNode} structure representing the built argument stack. */ - public static , Source> List> stackArguments( - List argumentsToStack, List> previousNodes, + public static , Source> NodeInformation stackArguments( + List argumentsToStack, NodeInformation previousNodeInformation, List previousArguments, List previousArgumentNames, Function, Command> terminalExecutorCreator ) { int lastIndex = argumentsToStack.size() - 1; for (int i = 0; i < argumentsToStack.size(); i++) { Argument subArgument = argumentsToStack.get(i); - previousNodes = subArgument.addArgumentNodes(previousNodes, previousArguments, previousArgumentNames, + + previousNodeInformation = subArgument.addArgumentNodes(previousNodeInformation, previousArguments, previousArgumentNames, // Only apply the `terminalExecutor` to the last argument i == lastIndex ? terminalExecutorCreator : null); } - return previousNodes; + + // Return information + return previousNodeInformation; } } diff --git a/commandapi-core/src/main/java/dev/jorel/commandapi/arguments/FlagsArgumentCommon.java b/commandapi-core/src/main/java/dev/jorel/commandapi/arguments/FlagsArgumentCommon.java index 28ee81c000..2792155117 100644 --- a/commandapi-core/src/main/java/dev/jorel/commandapi/arguments/FlagsArgumentCommon.java +++ b/commandapi-core/src/main/java/dev/jorel/commandapi/arguments/FlagsArgumentCommon.java @@ -10,6 +10,8 @@ import com.mojang.brigadier.tree.LiteralCommandNode; import com.mojang.brigadier.tree.RootCommandNode; import dev.jorel.commandapi.CommandAPIHandler; +import dev.jorel.commandapi.RegisteredCommand; +import dev.jorel.commandapi.arguments.AbstractArgument.NodeInformation; import dev.jorel.commandapi.commandnodes.FlagsArgumentEndingNode; import dev.jorel.commandapi.commandnodes.FlagsArgumentRootNode; import dev.jorel.commandapi.executors.CommandArguments; @@ -52,10 +54,10 @@ public interface FlagsArgumentCommon getCombinedArguments(); /** - * Links to {@link AbstractArgument#checkPreconditions(List, List, List)}. + * Links to {@link AbstractArgument#checkPreconditions(NodeInformation, List, List)}. */ void checkPreconditions( - List> previousNodes, List previousArguments, List previousArgumentNames + NodeInformation previousNodes, List previousArguments, List previousArgumentNames ); /** @@ -93,18 +95,18 @@ default List parseArgument(CommandContext cmd } /** - * Overrides {@link AbstractArgument#addArgumentNodes(List, List, List, Function)}. + * Overrides {@link AbstractArgument#addArgumentNodes(NodeInformation, List, List, Function)}. *

* A FlagsArgument works completely differently from a typical argument, so we need to completely * override the usual logic. */ - default List> addArgumentNodes( - List> previousNodes, + default NodeInformation addArgumentNodes( + NodeInformation previousNodeInformation, List previousArguments, List previousArgumentNames, Function, Command> terminalExecutorCreator ) { // Typical preconditions still apply - checkPreconditions(previousNodes, previousArguments, previousArgumentNames); + checkPreconditions(previousNodeInformation, previousArguments, previousArgumentNames); // Add this argument to the lists String nodeName = getNodeName(); @@ -116,7 +118,7 @@ default List> addArgumentNodes( finishBuildingNode(rootBuilder, previousArguments, null); FlagsArgumentRootNode rootNode = new FlagsArgumentRootNode<>(rootBuilder.build()); - for(CommandNode previousNode : previousNodes) { + for(CommandNode previousNode : previousNodeInformation.lastCommandNodes()) { previousNode.addChild(rootNode); } @@ -133,18 +135,28 @@ default List> addArgumentNodes( Function, Command> terminalBranchExecutor = terminalBranchesExecutable ? terminalExecutorCreator : null; // The last nodes here will be our final nodes - previousNodes = new ArrayList<>(); + List> newNodes = new ArrayList<>(); for(List terminalBranch : getTerminalBranches()) { - previousNodes.addAll( + newNodes.addAll( setupBranch(terminalBranch, rootNode, previousArguments, previousArgumentNames, terminalBranchExecutor, this::wrapTerminalBranchNodes) ); } - // Stack on combined arguments - previousNodes = AbstractArgument.stackArguments(getCombinedArguments(), previousNodes, previousArguments, previousArgumentNames, terminalExecutorCreator); - - // Return last nodes - return previousNodes; + // Create information for this node + NodeInformation nodeInformation = new NodeInformation<>( + newNodes, + // Create registered node information once children are created + children -> previousNodeInformation.childrenConsumer().createNodeWithChildren(List.of( + new RegisteredCommand.Node( + nodeName, getClass().getSimpleName(), "<" + nodeName + ">", + loopingBranchesExecutable || terminalBranchesExecutable, + children + ) + )) + ); + + // Stack on combined arguments and return last nodes + return AbstractArgument.stackArguments(getCombinedArguments(), nodeInformation, previousArguments, previousArgumentNames, terminalExecutorCreator); } private static , Source> List> setupBranch( @@ -158,10 +170,10 @@ default List> addArgumentNodes( List branchPreviousArgumentNames = new ArrayList<>(previousArgumentNames); RootCommandNode branchRoot = new RootCommandNode<>(); - List> branchPreviousNodes = List.of(branchRoot); + NodeInformation branchNodeInformation = new NodeInformation<>(List.of(branchRoot), null); // Stack branch nodes - branchPreviousNodes = AbstractArgument.stackArguments(branchArguments, branchPreviousNodes, branchPreviousArguments, branchPreviousArgumentNames, terminalExecutorCreator); + branchNodeInformation = AbstractArgument.stackArguments(branchArguments, branchNodeInformation, branchPreviousArguments, branchPreviousArgumentNames, terminalExecutorCreator); // Find second-to-last nodes so their children can be modified // Unfortunately, we can't get this while stacking since arguments may (and may not) unpack to multiple layers @@ -187,7 +199,7 @@ default List> addArgumentNodes( CommandAPIHandler.getCommandNodeArguments(rootNode).putAll(CommandAPIHandler.getCommandNodeArguments(branchRoot)); // Return the last nodes in the tree - return branchPreviousNodes; + return branchNodeInformation.lastCommandNodes(); } private void wrapTerminalBranchNodes( diff --git a/commandapi-core/src/main/java/dev/jorel/commandapi/arguments/Literal.java b/commandapi-core/src/main/java/dev/jorel/commandapi/arguments/Literal.java index f91dc86661..f9347166c5 100644 --- a/commandapi-core/src/main/java/dev/jorel/commandapi/arguments/Literal.java +++ b/commandapi-core/src/main/java/dev/jorel/commandapi/arguments/Literal.java @@ -1,10 +1,16 @@ package dev.jorel.commandapi.arguments; +import com.mojang.brigadier.Command; import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.tree.CommandNode; + +import dev.jorel.commandapi.RegisteredCommand; +import dev.jorel.commandapi.arguments.AbstractArgument.NodeInformation; import dev.jorel.commandapi.commandnodes.NamedLiteralArgumentBuilder; import java.util.List; +import java.util.function.Function; /** * An interface representing literal-based arguments @@ -38,6 +44,11 @@ public interface Literal getCombinedArguments(); + /////////////////////////////////////////////////////////////////////////////////////////////// // OVERRIDING METHODS // // A Literal has special logic that should override the implementations in AbstractArgument // @@ -62,4 +73,36 @@ public interface Literal + * Normally, Arguments use thier node name as their help string. However, a Literal uses its literal as the help string. + */ + default NodeInformation linkNode( + NodeInformation previousNodeInformation, CommandNode rootNode, + List previousArguments, List previousArgumentNames, + Function, Command> terminalExecutorCreator + ) { + // Add rootNode to the previous nodes + for(CommandNode previousNode : previousNodeInformation.lastCommandNodes()) { + previousNode.addChild(rootNode); + } + + // Create information for this node + NodeInformation nodeInformation = new NodeInformation<>( + List.of(rootNode), + // Create registered node information once children are created + children -> previousNodeInformation.childrenConsumer().createNodeWithChildren(List.of( + new RegisteredCommand.Node( + getNodeName(), getClass().getSimpleName(), getLiteral(), + getCombinedArguments().isEmpty() && terminalExecutorCreator != null, + children + ) + )) + ); + + // Stack on combined arguments and return last nodes + return AbstractArgument.stackArguments(getCombinedArguments(), nodeInformation, previousArguments, previousArgumentNames, terminalExecutorCreator); + } } diff --git a/commandapi-core/src/main/java/dev/jorel/commandapi/arguments/MultiLiteral.java b/commandapi-core/src/main/java/dev/jorel/commandapi/arguments/MultiLiteral.java index ab0ea3cbd5..cc4e84c284 100644 --- a/commandapi-core/src/main/java/dev/jorel/commandapi/arguments/MultiLiteral.java +++ b/commandapi-core/src/main/java/dev/jorel/commandapi/arguments/MultiLiteral.java @@ -4,6 +4,9 @@ import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.tree.CommandNode; + +import dev.jorel.commandapi.RegisteredCommand; +import dev.jorel.commandapi.arguments.AbstractArgument.NodeInformation; import dev.jorel.commandapi.commandnodes.NamedLiteralArgumentBuilder; import java.util.ArrayList; @@ -60,41 +63,6 @@ public interface MultiLiteral - * Normally, Arguments only represent a single branching path. However, a MultiLiteral represents multiple literal - * node paths, so we need to branch out the current paths for each literal. - */ - default void appendToCommandPaths(List> argumentStrings) { - // Create paths for this argument - Iterator literals = Arrays.asList(getLiterals()).iterator(); - String firstLiteralArgumentString = literals.next() + ":LiteralArgument"; - - // Copy each path for the other literals - List> newPaths = new ArrayList<>(); - while (literals.hasNext()) { - String literalArgumentString = literals.next() + ":LiteralArgument"; - for (List path : argumentStrings) { - List newPath = new ArrayList<>(path); - newPath.add(literalArgumentString); - newPaths.add(newPath); - } - } - - // Add first literal to the original paths - for (List path : argumentStrings) { - path.add(firstLiteralArgumentString); - } - argumentStrings.addAll(newPaths); - - // Add combined arguments - for (Argument argument : getCombinedArguments()) { - argument.appendToCommandPaths(argumentStrings); - } - } - /** * Overrides {@link AbstractArgument#createArgumentBuilder(List, List)}. *

@@ -113,19 +81,19 @@ default void appendToCommandPaths(List> argumentStrings) { } /** - * Overrides {@link AbstractArgument#linkNode(List, CommandNode, List, List, Function)}. + * Overrides {@link AbstractArgument#linkNode(NodeInformation, CommandNode, List, List, Function)}. *

* Normally, Arguments are only represented by a single node, and so only need to link one node. However, a MultiLiteral * represents multiple literal node paths, which also need to be generated and inserted into the node structure. */ - default List> linkNode( - List> previousNodes, CommandNode rootNode, + default NodeInformation linkNode( + NodeInformation previousNodeInformation, CommandNode rootNode, List previousArguments, List previousArgumentNames, Function, Command> terminalExecutorCreator ) { List> newNodes = new ArrayList<>(); // Add root node to previous - for(CommandNode previousNode : previousNodes) { + for(CommandNode previousNode : previousNodeInformation.lastCommandNodes()) { previousNode.addChild(rootNode); } newNodes.add(rootNode); @@ -145,16 +113,27 @@ default List> linkNode( CommandNode literalNode = finishBuildingNode(literalBuilder, previousArguments, terminalExecutorCreator); // Add node to previous - for(CommandNode previousNode : previousNodes) { + for(CommandNode previousNode : previousNodeInformation.lastCommandNodes()) { previousNode.addChild(literalNode); } newNodes.add(literalNode); } - // Stack on combined arguments - previousNodes = AbstractArgument.stackArguments(getCombinedArguments(), newNodes, previousArguments, previousArgumentNames, terminalExecutorCreator); - - // Return last nodes - return previousNodes; + // Create information for this node + NodeInformation nodeInformation = new NodeInformation<>( + newNodes, + // Create registered node information once children are created + children -> previousNodeInformation.childrenConsumer().createNodeWithChildren(List.of( + new RegisteredCommand.Node( + nodeName, getClass().getSimpleName(), + "(" + String.join("|", getLiterals())+ ")", + getCombinedArguments().isEmpty() && terminalExecutorCreator != null, + children + ) + )) + ); + + // Stack on combined arguments and return last nodes + return AbstractArgument.stackArguments(getCombinedArguments(), nodeInformation, previousArguments, previousArgumentNames, terminalExecutorCreator); } } diff --git a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/CommandAPIBukkit.java b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/CommandAPIBukkit.java index 277a57cfe6..4322815312 100644 --- a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/CommandAPIBukkit.java +++ b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/CommandAPIBukkit.java @@ -45,7 +45,6 @@ import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.tree.LiteralCommandNode; -import dev.jorel.commandapi.arguments.AbstractArgument; import dev.jorel.commandapi.arguments.Argument; import dev.jorel.commandapi.arguments.LiteralArgument; import dev.jorel.commandapi.arguments.MultiLiteralArgument; @@ -242,35 +241,34 @@ private void generateHelpUsage(StringBuilder sb, RegisteredCommand command) { } private String[] getUsageList(RegisteredCommand currentCommand) { - List commandsWithIdenticalNames = new ArrayList<>(); + // TODO: I don't think the changes I made here are well tested, so this could have changed behavior + final Optional usageDescription = currentCommand.usageDescription(); + if (usageDescription.isPresent()) return usageDescription.get(); // Usage was overriden - // Collect every command with the same name - for (RegisteredCommand registeredCommand : CommandAPIHandler.getInstance().registeredCommands) { - if (registeredCommand.commandName().equals(currentCommand.commandName())) { - commandsWithIdenticalNames.add(registeredCommand); - } - } + // Generate command usage + // TODO: Should default usage generation be updated? https://github.com/JorelAli/CommandAPI/issues/363 + List usages = new ArrayList<>(); + StringBuilder usageSoFar = new StringBuilder("/"); + addUsageForNode(currentCommand.rootNode(), usages, usageSoFar); + return usages.toArray(String[]::new); + } - // Generate command usage or fill it with a user provided one - final String[] usages; - final Optional usageDescription = currentCommand.usageDescription(); - if (usageDescription.isPresent()) { - usages = usageDescription.get(); - } else { - // TODO: Figure out if default usage generation should be updated - final int numCommandsWithIdenticalNames = commandsWithIdenticalNames.size(); - usages = new String[numCommandsWithIdenticalNames]; - for (int i = 0; i < numCommandsWithIdenticalNames; i++) { - final RegisteredCommand command = commandsWithIdenticalNames.get(i); - StringBuilder usageString = new StringBuilder(); - usageString.append("/").append(command.commandName()).append(" "); - for (AbstractArgument arg : command.arguments()) { - usageString.append(arg.getHelpString()).append(" "); - } - usages[i] = usageString.toString().trim(); - } + private void addUsageForNode(RegisteredCommand.Node node, List usages, StringBuilder usageSoFar) { + // Add node to usage + usageSoFar.append(node.helpString()); + + // Add usage to the list if this is executable + if (node.executable()) usages.add(usageSoFar.toString()); + + // Add children + usageSoFar.append(" "); + int currentLength = usageSoFar.length(); + for (RegisteredCommand.Node child : node.children()) { + // Reset the string builder to the usage up to and including this node + usageSoFar.delete(currentLength, usageSoFar.length()); + + addUsageForNode(child, usages, usageSoFar); } - return usages; } void updateHelpForCommands(List commands) { @@ -410,6 +408,15 @@ public BukkitCommandSender wrapCommandSender(CommandSen throw new RuntimeException("Failed to wrap CommandSender " + sender + " to a CommandAPI-compatible BukkitCommandSender"); } + public void registerPermission(String string) { + try { + Bukkit.getPluginManager().addPermission(new Permission(string)); + } catch (IllegalArgumentException ignored) { + // Exception is thrown if we attempt to register a permission that already exists + // If it already exists, that's totally fine, so just ignore the exception + } + } + @Override @Unimplemented(because = REQUIRES_MINECRAFT_SERVER) public abstract SuggestionProvider getSuggestionProvider(SuggestionProviders suggestionProvider); @@ -454,28 +461,16 @@ public void preCommandRegistration(String commandName) { } @Override - public void postCommandRegistration(List registeredCommands, LiteralCommandNode resultantNode, List> aliasNodes) { - commandRegistrationStrategy.postCommandRegistration(registeredCommands, resultantNode, aliasNodes); + public void postCommandRegistration(RegisteredCommand registeredCommand, LiteralCommandNode resultantNode, List> aliasNodes) { + commandRegistrationStrategy.postCommandRegistration(registeredCommand, resultantNode, aliasNodes); - // Using registeredCommands.get(0) as representation for most command features. - // This is fine, because the only difference between the commands in the list is their argument strings. - RegisteredCommand commonCommandInformation = registeredCommands.get(0); - - // Register the command's permission node to Bukkit's manager, if it exists - CommandPermission permission = commonCommandInformation.permission(); - Optional wrappedPermissionString = permission.getPermission(); - if (wrappedPermissionString.isPresent()) { - try { - Bukkit.getPluginManager().addPermission(new Permission(wrappedPermissionString.get())); - } catch (IllegalArgumentException ignored) { - // Exception is thrown if we attempt to register a permission that already exists - // If it already exists, that's totally fine, so just ignore the exception - } - } + // Register the command's permission string (if it exists) to Bukkit's manager + CommandPermission permission = registeredCommand.permission(); + permission.getPermission().ifPresent(this::registerPermission); if (!CommandAPI.canRegister()) { // Adding the command to the help map usually happens in `CommandAPIBukkit#onEnable` - updateHelpForCommands(registeredCommands); + updateHelpForCommands(List.of(registeredCommand)); // Sending command dispatcher packets usually happens when Players join the server for (Player p : Bukkit.getOnlinePlayers()) { diff --git a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/CommandRegistrationStrategy.java b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/CommandRegistrationStrategy.java index 477d8a24b7..59f7892cc2 100644 --- a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/CommandRegistrationStrategy.java +++ b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/CommandRegistrationStrategy.java @@ -58,7 +58,7 @@ protected static boolean isThisTheCommandButNamespaced(String commandName, Strin public abstract void runTasksAfterServerStart(); - public abstract void postCommandRegistration(List registeredCommands, LiteralCommandNode resultantNode, List> aliasNodes); + public abstract void postCommandRegistration(RegisteredCommand registeredCommand, LiteralCommandNode resultantNode, List> aliasNodes); public abstract void registerCommandNode(LiteralCommandNode node, String namespace); diff --git a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/PaperCommandRegistration.java b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/PaperCommandRegistration.java index fce5f7fe18..e501f88c3a 100644 --- a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/PaperCommandRegistration.java +++ b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/PaperCommandRegistration.java @@ -49,7 +49,7 @@ public void runTasksAfterServerStart() { } @Override - public void postCommandRegistration(List registeredCommands, LiteralCommandNode resultantNode, List> aliasNodes) { + public void postCommandRegistration(RegisteredCommand registeredCommand, LiteralCommandNode resultantNode, List> aliasNodes) { // Nothing to do } diff --git a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/SpigotCommandRegistration.java b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/SpigotCommandRegistration.java index 3bf6ffc8b4..47bff20693 100644 --- a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/SpigotCommandRegistration.java +++ b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/SpigotCommandRegistration.java @@ -183,11 +183,7 @@ private void fixPermissions() { } @Override - public void postCommandRegistration(List registeredCommands, LiteralCommandNode resultantNode, List> aliasNodes) { - // Using registeredCommands.get(0) as representation for most command features. - // This is fine, because the only difference between the commands in the list is their argument strings. - RegisteredCommand commonCommandInformation = registeredCommands.get(0); - + public void postCommandRegistration(RegisteredCommand registeredCommand, LiteralCommandNode resultantNode, List> aliasNodes) { if (!CommandAPI.canRegister()) { // Usually, when registering commands during server startup, we can just put our commands into the // `net.minecraft.server.MinecraftServer#vanillaCommandDispatcher` and leave it. As the server finishes setup, @@ -201,8 +197,8 @@ public void postCommandRegistration(List registeredCommands, RootCommandNode root = getResourcesDispatcher.get().getRoot(); String name = resultantNode.getLiteral(); - String namespace = commonCommandInformation.namespace(); - String permNode = unpackInternalPermissionNodeString(commonCommandInformation.permission()); + String namespace = registeredCommand.namespace(); + String permNode = unpackInternalPermissionNodeString(registeredCommand.permission()); registerCommand(knownCommands, root, name, permNode, namespace, resultantNode); @@ -228,20 +224,20 @@ public void postCommandRegistration(List registeredCommands, minecraftCommandNamespaces = new RootCommandNode<>(); } } else { - CommandPermission permission = commonCommandInformation.permission(); + CommandPermission permission = registeredCommand.permission(); // Since the VanillaCommandWrappers aren't created yet, we need to remember to // fix those permissions once the server is enabled. Using `putIfAbsent` to // default to the first permission associated with this command. - String commandName = commonCommandInformation.commandName().toLowerCase(); + String commandName = registeredCommand.commandName().toLowerCase(); permissionsToFix.putIfAbsent(commandName, permission); // Do the same for the namespaced version of the command (which is never empty on Bukkit forks) - String namespace = commonCommandInformation.namespace().toLowerCase(); + String namespace = registeredCommand.namespace().toLowerCase(); permissionsToFix.putIfAbsent(namespace + ":" + commandName, permission); // Do the same for the aliases - for (String alias : commonCommandInformation.aliases()) { + for (String alias : registeredCommand.aliases()) { alias = alias.toLowerCase(); permissionsToFix.putIfAbsent(alias, permission); permissionsToFix.putIfAbsent(namespace + ":" + alias, permission); diff --git a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/arguments/CustomArgument.java b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/arguments/CustomArgument.java index ca0663f7b0..5e5d73539b 100644 --- a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/arguments/CustomArgument.java +++ b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/arguments/CustomArgument.java @@ -26,7 +26,6 @@ import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; -import com.mojang.brigadier.tree.CommandNode; import dev.jorel.commandapi.BukkitTooltip; import dev.jorel.commandapi.CommandAPIBukkit; import dev.jorel.commandapi.CommandAPIHandler; @@ -200,13 +199,13 @@ public Argument combineWith(List> combinedArguments) { } @Override - public List> addArgumentNodes( - List> previousNodes, + public NodeInformation addArgumentNodes( + NodeInformation previousNodeInformation, List> previousArguments, List previousArgumentNames, Function>, Command> terminalExecutorCreator ) { // Node structure is determined by the base argument - previousNodes = base.addArgumentNodes(previousNodes, previousArguments, previousArgumentNames, + previousNodeInformation = base.addArgumentNodes(previousNodeInformation, previousArguments, previousArgumentNames, // Replace the base argument with this argument when executing the command terminalExecutorCreator == null ? null : args -> { @@ -219,7 +218,7 @@ public List> addArgumentNodes( // Replace the base argument with this argument when executing the command previousArguments.set(previousArguments.indexOf(base), this); - return previousNodes; + return previousNodeInformation; } /** diff --git a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/arguments/FlagsArgument.java b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/arguments/FlagsArgument.java index 5642becf89..b1cb0155bc 100644 --- a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/arguments/FlagsArgument.java +++ b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/arguments/FlagsArgument.java @@ -3,7 +3,6 @@ import com.mojang.brigadier.Command; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.tree.CommandNode; import dev.jorel.commandapi.executors.CommandArguments; import org.bukkit.command.CommandSender; @@ -74,7 +73,7 @@ public List parseArgument(CommandContext cmdC } @Override - public List> addArgumentNodes(List> previousNodes, List> previousArguments, List previousArgumentNames, Function>, Command> terminalExecutorCreator) { - return FlagsArgumentCommon.super.addArgumentNodes(previousNodes, previousArguments, previousArgumentNames, terminalExecutorCreator); + public NodeInformation addArgumentNodes(NodeInformation previousNodeInformation, List> previousArguments, List previousArgumentNames, Function>, Command> terminalExecutorCreator) { + return FlagsArgumentCommon.super.addArgumentNodes(previousNodeInformation, previousArguments, previousArgumentNames, terminalExecutorCreator); } } diff --git a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/arguments/LiteralArgument.java b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/arguments/LiteralArgument.java index 1740f54b2f..45c3dac01c 100644 --- a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/arguments/LiteralArgument.java +++ b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/arguments/LiteralArgument.java @@ -20,14 +20,18 @@ *******************************************************************************/ package dev.jorel.commandapi.arguments; +import com.mojang.brigadier.Command; import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.tree.CommandNode; + import dev.jorel.commandapi.exceptions.BadLiteralException; import dev.jorel.commandapi.executors.CommandArguments; import org.bukkit.command.CommandSender; import java.util.List; +import java.util.function.Function; /** * A pseudo-argument representing a single literal string @@ -136,11 +140,6 @@ public CommandAPIArgumentType getArgumentType() { return CommandAPIArgumentType.LITERAL; } - @Override - public String getHelpString() { - return literal; - } - @Override public String parseArgument(CommandContext cmdCtx, String key, CommandArguments previousArgs) throws CommandSyntaxException { return literal; @@ -156,4 +155,9 @@ public String parseArgument(CommandContext cmdCtx, String key, public ArgumentBuilder createArgumentBuilder(List> previousArguments, List previousArgumentNames) { return Literal.super.createArgumentBuilder(previousArguments, previousArgumentNames); } + + @Override + public NodeInformation linkNode(NodeInformation previousNodeInformation, CommandNode rootNode, List> previousArguments, List previousArgumentNames, Function>, Command> terminalExecutorCreator) { + return Literal.super.linkNode(previousNodeInformation, rootNode, previousArguments, previousArgumentNames, terminalExecutorCreator); + } } diff --git a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/arguments/MultiLiteralArgument.java b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/arguments/MultiLiteralArgument.java index 42f0b93caf..b3d95b1e60 100644 --- a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/arguments/MultiLiteralArgument.java +++ b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/arguments/MultiLiteralArgument.java @@ -106,18 +106,13 @@ public String parseArgument(CommandContext cmdCtx, String key, // method by default. However, we want to use the implementations found in the MultiLiteral interface. // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - @Override - public void appendToCommandPaths(List> argumentStrings) { - MultiLiteral.super.appendToCommandPaths(argumentStrings); - } - @Override public ArgumentBuilder createArgumentBuilder(List> previousArguments, List previousArgumentNames) { return MultiLiteral.super.createArgumentBuilder(previousArguments, previousArgumentNames); } @Override - public List> linkNode(List> previousNodes, CommandNode rootNode, List> previousArguments, List previousArgumentNames, Function>, Command> terminalExecutorCreator) { - return MultiLiteral.super.linkNode(previousNodes, rootNode, previousArguments, previousArgumentNames, terminalExecutorCreator); + public NodeInformation linkNode(NodeInformation previousNodeInformation, CommandNode rootNode, List> previousArguments, List previousArgumentNames, Function>, Command> terminalExecutorCreator) { + return MultiLiteral.super.linkNode(previousNodeInformation, rootNode, previousArguments, previousArgumentNames, terminalExecutorCreator); } } diff --git a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-test/commandapi-bukkit-test-tests/src/test/java/dev/jorel/commandapi/test/CommandAPICommandRegisteredCommandTests.java b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-test/commandapi-bukkit-test-tests/src/test/java/dev/jorel/commandapi/test/CommandAPICommandRegisteredCommandTests.java index fbe4ea7b49..ac053a096c 100644 --- a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-test/commandapi-bukkit-test-tests/src/test/java/dev/jorel/commandapi/test/CommandAPICommandRegisteredCommandTests.java +++ b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-test/commandapi-bukkit-test-tests/src/test/java/dev/jorel/commandapi/test/CommandAPICommandRegisteredCommandTests.java @@ -1,9 +1,9 @@ package dev.jorel.commandapi.test; -import dev.jorel.commandapi.CommandAPI; import dev.jorel.commandapi.CommandAPICommand; import dev.jorel.commandapi.CommandPermission; import dev.jorel.commandapi.RegisteredCommand; +import dev.jorel.commandapi.RegisteredCommand.Node; import dev.jorel.commandapi.arguments.IntegerArgument; import dev.jorel.commandapi.arguments.LiteralArgument; import dev.jorel.commandapi.arguments.MultiLiteralArgument; @@ -12,17 +12,16 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.Arrays; import java.util.List; -import java.util.Objects; import java.util.Optional; -import static org.junit.jupiter.api.Assertions.fail; +import static dev.jorel.commandapi.test.RegisteredCommandTestBase.NodeBuilder.node; +import static dev.jorel.commandapi.test.RegisteredCommandTestBase.NodeBuilder.children; /** * Tests for making sure the {@link RegisteredCommand} information is correct when registering {@link CommandAPICommand}s */ -class CommandAPICommandRegisteredCommandTests extends TestBase { +class CommandAPICommandRegisteredCommandTests extends RegisteredCommandTestBase { /********* * Setup * @@ -38,64 +37,8 @@ public void tearDown() { super.tearDown(); } - private RegisteredCommand registeredCommandNoHelpOrPermission(String name, List argsAsStr, String... aliases) { - return new RegisteredCommand(name, argsAsStr, - Optional.empty(), Optional.empty(), Optional.empty(), - aliases, - CommandPermission.NONE, "minecraft" - ); - } - - private void assertCreatedRegisteredCommands(RegisteredCommand... commands) { - List expectedCommands = Arrays.asList(commands); - List actualCommands = CommandAPI.getRegisteredCommands(); - - if (expectedCommands.size() != actualCommands.size()) { - StringBuilder builder = new StringBuilder(); - builder.append("Expected ").append(expectedCommands.size()).append(" command(s), found ").append(actualCommands.size()).append(" command(s)."); - - builder.append("\nExpected: "); - addRegisteredCommandList(builder, expectedCommands); - builder.append("\nActual: "); - addRegisteredCommandList(builder, actualCommands); - - fail(builder.toString()); - } - - for (int i = 0; i < expectedCommands.size(); i++) { - RegisteredCommand expectedCommand = expectedCommands.get(i); - RegisteredCommand actualCommand = actualCommands.get(i); - - if (!Objects.equals(expectedCommand, actualCommand)) { - StringBuilder builder = new StringBuilder(); - builder.append("Command #").append(i + 1).append(" differed. Expected:\n"); - builder.append(expectedCommand); - builder.append("\nActual:\n"); - builder.append(actualCommand); - - builder.append("\nExpected list: "); - addRegisteredCommandList(builder, expectedCommands); - builder.append("\nActual list: "); - addRegisteredCommandList(builder, actualCommands); - - fail(builder.toString()); - } - } - } - - private void addRegisteredCommandList(StringBuilder builder, List commands) { - if (commands.isEmpty()) { - builder.append("[]"); - return; - } - - builder.append("[\n"); - for (RegisteredCommand command : commands) { - builder.append("\t"); - builder.append(command); - builder.append("\n"); - } - builder.append("]"); + private NodeBuilder commandNode(String nodeName, boolean executable) { + return node(nodeName, CommandAPICommand.class, executable).helpString(nodeName); } /********* @@ -108,7 +51,11 @@ void testRegister() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands(registeredCommandNoHelpOrPermission("command", List.of())); + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", true), + List.of("command:CommandAPICommand") + ); } @Test @@ -123,13 +70,13 @@ void testRegisterHelpInformation() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands( - new RegisteredCommand( - "command", List.of(), - Optional.of("short description"), Optional.of("full description"), Optional.of(new String[]{"usage 1", "usage 2", "usage 3"}), - new String[0], CommandPermission.NONE, "minecraft" - ) + RegisteredCommand expectedCommand = new RegisteredCommand( + "command", new String[0], "minecraft", CommandPermission.NONE, + Optional.of("short description"), Optional.of("full description"), Optional.of(new String[]{"usage 1", "usage 2", "usage 3"}), + commandNode("command", true).build() ); + + assertCreatedRegisteredCommands(expectedCommand.copyWithEmptyNamespace(), expectedCommand); } @Test @@ -139,13 +86,13 @@ void testRegisterOpPermission() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands( - new RegisteredCommand( - "command", List.of(), - Optional.empty(), Optional.empty(), Optional.empty(), - new String[0], CommandPermission.OP, "minecraft" - ) + RegisteredCommand expectedCommand = new RegisteredCommand( + "command", new String[0], "minecraft", CommandPermission.OP, + Optional.empty(), Optional.empty(), Optional.empty(), + commandNode("command", true).build() ); + + assertCreatedRegisteredCommands(expectedCommand.copyWithEmptyNamespace(), expectedCommand); } @Test @@ -155,13 +102,13 @@ void testRegisterStringPermission() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands( - new RegisteredCommand( - "command", List.of(), - Optional.empty(), Optional.empty(), Optional.empty(), - new String[0], CommandPermission.fromString("permission"), "minecraft" - ) + RegisteredCommand expectedCommand = new RegisteredCommand( + "command", new String[0], "minecraft", CommandPermission.fromString("permission"), + Optional.empty(), Optional.empty(), Optional.empty(), + commandNode("command", true).build() ); + + assertCreatedRegisteredCommands(expectedCommand.copyWithEmptyNamespace(), expectedCommand); } @Test @@ -171,7 +118,9 @@ void testRegisterOneAlias() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands(registeredCommandNoHelpOrPermission("command", List.of(), "alias1")); + RegisteredCommand expectedCommand = simpleRegisteredCommand("command", "minecraft", commandNode("command", true), "alias1"); + + assertCreatedRegisteredCommands(expectedCommand.copyWithEmptyNamespace(), expectedCommand); } @Test @@ -181,7 +130,20 @@ void testRegisterTwoAliases() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands(registeredCommandNoHelpOrPermission("command", List.of(), "alias1", "alias2")); + RegisteredCommand expectedCommand = simpleRegisteredCommand("command", "minecraft", commandNode("command", true), "alias1", "alias2"); + + assertCreatedRegisteredCommands(expectedCommand.copyWithEmptyNamespace(), expectedCommand); + } + + @Test + void testRegisterNamespace() { + new CommandAPICommand("command") + .executesPlayer(P_EXEC) + .register("custom"); + + RegisteredCommand expectedCommand = simpleRegisteredCommand("command", "custom", commandNode("command", true)); + + assertCreatedRegisteredCommands(expectedCommand.copyWithEmptyNamespace(), expectedCommand); } @Test @@ -191,7 +153,12 @@ void testRegisterOneArgument() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands(registeredCommandNoHelpOrPermission("command", List.of("string:StringArgument"))); + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false) + .withChildren(node("string", StringArgument.class, true)), + List.of("command:CommandAPICommand", "string:StringArgument") + ); } @Test @@ -204,7 +171,14 @@ void testRegisterTwoArguments() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands(registeredCommandNoHelpOrPermission("command", List.of("string:StringArgument", "integer:IntegerArgument"))); + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false) + .withChildren(node("string", StringArgument.class, false) + .withChildren(node("integer", IntegerArgument.class, true)) + ), + List.of("command:CommandAPICommand", "string:StringArgument", "integer:IntegerArgument") + ); } @Test @@ -217,16 +191,14 @@ void testRegisterMultiLiteralArguments() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands( - registeredCommandNoHelpOrPermission("command", List.of("a:LiteralArgument", "d:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("b:LiteralArgument", "d:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("c:LiteralArgument", "d:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("a:LiteralArgument", "e:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("b:LiteralArgument", "e:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("c:LiteralArgument", "e:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("a:LiteralArgument", "f:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("b:LiteralArgument", "f:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("c:LiteralArgument", "f:LiteralArgument")) + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false).withChildren( + node("literal1", MultiLiteralArgument.class, false).helpString("(a|b|c)").withChildren( + node("literal2", MultiLiteralArgument.class, true).helpString("(d|e|f)") + ) + ), + List.of("command:CommandAPICommand", "literal1:MultiLiteralArgument", "literal2:MultiLiteralArgument") ); } @@ -237,9 +209,12 @@ void testRegisterOneOptionalArgument() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands( - registeredCommandNoHelpOrPermission("command", List.of()), - registeredCommandNoHelpOrPermission("command", List.of("string:StringArgument")) + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", true) + .withChildren(node("string", StringArgument.class, true)), + List.of("command:CommandAPICommand"), + List.of("command:CommandAPICommand", "string:StringArgument") ); } @@ -253,10 +228,15 @@ void testRegisterTwoOptionalArguments() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands( - registeredCommandNoHelpOrPermission("command", List.of()), - registeredCommandNoHelpOrPermission("command", List.of("string:StringArgument")), - registeredCommandNoHelpOrPermission("command", List.of("string:StringArgument", "integer:IntegerArgument")) + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", true) + .withChildren(node("string", StringArgument.class, true) + .withChildren(node("integer", IntegerArgument.class, true)) + ), + List.of("command:CommandAPICommand"), + List.of("command:CommandAPICommand", "string:StringArgument"), + List.of("command:CommandAPICommand", "string:StringArgument", "integer:IntegerArgument") ); } @@ -270,10 +250,17 @@ void testRegisterCombinedOptionalArguments() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands( - registeredCommandNoHelpOrPermission("command", List.of()), - registeredCommandNoHelpOrPermission("command", List.of("1:LiteralArgument", "2:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("1:LiteralArgument", "2:LiteralArgument", "3:LiteralArgument", "4:LiteralArgument")) + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", true) + .withChildren(node("1", LiteralArgument.class, false).helpString("1") + .withChildren(node("2", LiteralArgument.class, true).helpString("2") + .withChildren(node("3", LiteralArgument.class, false).helpString("3") + .withChildren(node("4", LiteralArgument.class, true).helpString("4") + )))), + List.of("command:CommandAPICommand"), + List.of("command:CommandAPICommand", "1:LiteralArgument", "2:LiteralArgument"), + List.of("command:CommandAPICommand", "1:LiteralArgument", "2:LiteralArgument", "3:LiteralArgument", "4:LiteralArgument") ); } @@ -291,19 +278,30 @@ void testRegisterCombinedRequiredAndOptionalArguments() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands( - registeredCommandNoHelpOrPermission("command", List.of( - "1:LiteralArgument", "2:LiteralArgument", "3:LiteralArgument", "4:LiteralArgument" - )), - registeredCommandNoHelpOrPermission("command", List.of( - "1:LiteralArgument", "2:LiteralArgument", "3:LiteralArgument", "4:LiteralArgument", - "5:LiteralArgument", "6:LiteralArgument" - )), - registeredCommandNoHelpOrPermission("command", List.of( - "1:LiteralArgument", "2:LiteralArgument", "3:LiteralArgument", "4:LiteralArgument", - "5:LiteralArgument", "6:LiteralArgument", "7:LiteralArgument", "8:LiteralArgument" - )) - ); + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false) + .withChildren(node("1", LiteralArgument.class, false).helpString("1") + .withChildren(node("2", LiteralArgument.class, false).helpString("2") + .withChildren(node("3", LiteralArgument.class, false).helpString("3") + .withChildren(node("4", LiteralArgument.class, true).helpString("4") + .withChildren(node("5", LiteralArgument.class, false).helpString("5") + .withChildren(node("6", LiteralArgument.class, true).helpString("6") + .withChildren(node("7", LiteralArgument.class, false).helpString("7") + .withChildren(node("8", LiteralArgument.class, true).helpString("8") + )))))))), + List.of("command:CommandAPICommand", + "1:LiteralArgument", "2:LiteralArgument", "3:LiteralArgument", "4:LiteralArgument" + ), + List.of("command:CommandAPICommand", + "1:LiteralArgument", "2:LiteralArgument", "3:LiteralArgument", "4:LiteralArgument", + "5:LiteralArgument", "6:LiteralArgument" + ), + List.of("command:CommandAPICommand", + "1:LiteralArgument", "2:LiteralArgument", "3:LiteralArgument", "4:LiteralArgument", + "5:LiteralArgument", "6:LiteralArgument", "7:LiteralArgument", "8:LiteralArgument" + ) + ); } ////////////////////////////////////// @@ -320,7 +318,12 @@ void testRegisterOneSubcommand() { ) .register(); - assertCreatedRegisteredCommands(registeredCommandNoHelpOrPermission("command", List.of("subcommand:LiteralArgument"))); + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false) + .withChildren(commandNode("subcommand", true)), + List.of("command:CommandAPICommand", "subcommand:CommandAPICommand") + ); } @Test @@ -334,9 +337,14 @@ void testRegisterTwoSubcommands() { ) .register(); - assertCreatedRegisteredCommands( - registeredCommandNoHelpOrPermission("command", List.of("subcommand1:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand2:LiteralArgument")) + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false).withChildren( + commandNode("subcommand1", true), + commandNode("subcommand2", true) + ), + List.of("command:CommandAPICommand", "subcommand1:CommandAPICommand"), + List.of("command:CommandAPICommand", "subcommand2:CommandAPICommand") ); } @@ -350,9 +358,12 @@ void testRegisterOneSubcommandAndBaseExecutable() { ) .register(); - assertCreatedRegisteredCommands( - registeredCommandNoHelpOrPermission("command", List.of()), - registeredCommandNoHelpOrPermission("command", List.of("subcommand:LiteralArgument")) + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", true) + .withChildren(commandNode("subcommand", true)), + List.of("command:CommandAPICommand"), + List.of("command:CommandAPICommand", "subcommand:CommandAPICommand") ); } @@ -366,10 +377,16 @@ void testRegisterSubcommandWithAliases() { ) .register(); - assertCreatedRegisteredCommands( - registeredCommandNoHelpOrPermission("command", List.of("subcommand:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("alias1:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("alias2:LiteralArgument")) + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false).withChildren( + commandNode("subcommand", true), + commandNode("alias1", true), + commandNode("alias2", true) + ), + List.of("command:CommandAPICommand", "subcommand:CommandAPICommand"), + List.of("command:CommandAPICommand", "alias1:CommandAPICommand"), + List.of("command:CommandAPICommand", "alias2:CommandAPICommand") ); } @@ -392,9 +409,22 @@ void testRegisterSubcommandsWithArguments() { ) .register(); - assertCreatedRegisteredCommands( - registeredCommandNoHelpOrPermission("command", List.of("subcommand1:LiteralArgument", "string1:StringArgument", "integer1:IntegerArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand2:LiteralArgument", "string2:StringArgument", "integer2:IntegerArgument")) + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false).withChildren( + commandNode("subcommand1", false).withChildren( + node("string1", StringArgument.class, false).withChildren( + node("integer1", IntegerArgument.class, true) + ) + ), + commandNode("subcommand2", false).withChildren( + node("string2", StringArgument.class, false).withChildren( + node("integer2", IntegerArgument.class, true) + ) + ) + ), + List.of("command:CommandAPICommand", "subcommand1:CommandAPICommand", "string1:StringArgument", "integer1:IntegerArgument"), + List.of("command:CommandAPICommand", "subcommand2:CommandAPICommand", "string2:StringArgument", "integer2:IntegerArgument") ); } @@ -412,19 +442,26 @@ void testRegisterSubcommandWithAliasesAndMultiLiteralArgument() { ) .register(); - assertCreatedRegisteredCommands( - registeredCommandNoHelpOrPermission("command", List.of("subcommand:LiteralArgument", "a:LiteralArgument", "c:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("alias1:LiteralArgument", "a:LiteralArgument", "c:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("alias2:LiteralArgument", "a:LiteralArgument", "c:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand:LiteralArgument", "b:LiteralArgument", "c:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("alias1:LiteralArgument", "b:LiteralArgument", "c:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("alias2:LiteralArgument", "b:LiteralArgument", "c:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand:LiteralArgument", "a:LiteralArgument", "d:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("alias1:LiteralArgument", "a:LiteralArgument", "d:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("alias2:LiteralArgument", "a:LiteralArgument", "d:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand:LiteralArgument", "b:LiteralArgument", "d:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("alias1:LiteralArgument", "b:LiteralArgument", "d:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("alias2:LiteralArgument", "b:LiteralArgument", "d:LiteralArgument")) + List literal2 = children( + node("literal2", MultiLiteralArgument.class, true).helpString("(c|d)") + ); + + List literal1 = children( + node("literal1", MultiLiteralArgument.class, false).helpString("(a|b)").withChildren(literal2) + ); + + List subcommands = children( + commandNode("subcommand", false).withChildren(literal1), + commandNode("alias1", false).withChildren(literal1), + commandNode("alias2", false).withChildren(literal1) + ); + + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false).withChildren(subcommands), + List.of("command:CommandAPICommand", "subcommand:CommandAPICommand", "literal1:MultiLiteralArgument", "literal2:MultiLiteralArgument"), + List.of("command:CommandAPICommand", "alias1:CommandAPICommand", "literal1:MultiLiteralArgument", "literal2:MultiLiteralArgument"), + List.of("command:CommandAPICommand", "alias2:CommandAPICommand", "literal1:MultiLiteralArgument", "literal2:MultiLiteralArgument") ); } @@ -449,13 +486,26 @@ void testRegisterSubcommandsWithOptionalArguments() { ) .register(); - assertCreatedRegisteredCommands( - registeredCommandNoHelpOrPermission("command", List.of("subcommand1:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand1:LiteralArgument", "a:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand1:LiteralArgument", "a:LiteralArgument", "b:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand2:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand2:LiteralArgument", "c:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand2:LiteralArgument", "c:LiteralArgument", "d:LiteralArgument")) + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false).withChildren( + commandNode("subcommand1", true).withChildren( + node("a", LiteralArgument.class, true).helpString("a").withChildren( + node("b", LiteralArgument.class, true).helpString("b") + ) + ), + commandNode("subcommand2", true).withChildren( + node("c", LiteralArgument.class, true).helpString("c").withChildren( + node("d", LiteralArgument.class, true).helpString("d") + ) + ) + ), + List.of("command:CommandAPICommand", "subcommand1:CommandAPICommand"), + List.of("command:CommandAPICommand", "subcommand1:CommandAPICommand", "a:LiteralArgument"), + List.of("command:CommandAPICommand", "subcommand1:CommandAPICommand", "a:LiteralArgument", "b:LiteralArgument"), + List.of("command:CommandAPICommand", "subcommand2:CommandAPICommand"), + List.of("command:CommandAPICommand", "subcommand2:CommandAPICommand", "c:LiteralArgument"), + List.of("command:CommandAPICommand", "subcommand2:CommandAPICommand", "c:LiteralArgument", "d:LiteralArgument") ); } @@ -484,11 +534,109 @@ void testRegisterSubcommandsWithCombinedRequiredAndOptionalArguments() { ) .register(); + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false).withChildren( + commandNode("subcommand1", false) + .withChildren(node("1a", LiteralArgument.class, false).helpString("1a") + .withChildren(node("1b", LiteralArgument.class, true).helpString("1b") + .withChildren(node("1c", LiteralArgument.class, false).helpString("1c") + .withChildren(node("1d", LiteralArgument.class, true).helpString("1d") + )))), + commandNode("subcommand2", false) + .withChildren(node("2a", LiteralArgument.class, false).helpString("2a") + .withChildren(node("2b", LiteralArgument.class, true).helpString("2b") + .withChildren(node("2c", LiteralArgument.class, false).helpString("2c") + .withChildren(node("2d", LiteralArgument.class, true).helpString("2d") + )))) + ), + List.of("command:CommandAPICommand", "subcommand1:CommandAPICommand", "1a:LiteralArgument", "1b:LiteralArgument"), + List.of("command:CommandAPICommand", "subcommand1:CommandAPICommand", "1a:LiteralArgument", "1b:LiteralArgument", "1c:LiteralArgument", "1d:LiteralArgument"), + List.of("command:CommandAPICommand", "subcommand2:CommandAPICommand", "2a:LiteralArgument", "2b:LiteralArgument"), + List.of("command:CommandAPICommand", "subcommand2:CommandAPICommand", "2a:LiteralArgument", "2b:LiteralArgument", "2c:LiteralArgument", "2d:LiteralArgument") + ); + } + + ///////////////////////// + // Information merging // + ///////////////////////// + + @Test + void testRegisterTwoSeparateCommands() { + new CommandAPICommand("command1") + .executesPlayer(P_EXEC) + .register(); + + new CommandAPICommand("command2") + .executesPlayer(P_EXEC) + .register(); + + RegisteredCommand command1 = simpleRegisteredCommand("command1", "minecraft", commandNode("command1", true)); + RegisteredCommand command2 = simpleRegisteredCommand("command2", "minecraft", commandNode("command2", true)); + assertCreatedRegisteredCommands( - registeredCommandNoHelpOrPermission("command", List.of("subcommand1:LiteralArgument", "1a:LiteralArgument", "1b:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand1:LiteralArgument", "1a:LiteralArgument", "1b:LiteralArgument", "1c:LiteralArgument", "1d:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand2:LiteralArgument", "2a:LiteralArgument", "2b:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand2:LiteralArgument", "2a:LiteralArgument", "2b:LiteralArgument", "2c:LiteralArgument", "2d:LiteralArgument")) + command1.copyWithEmptyNamespace(), command1, + command2.copyWithEmptyNamespace(), command2 + ); + } + + @Test + void testRegisterMergeArguments() { + new CommandAPICommand("command") + .withArguments(new StringArgument("string")) + .executesPlayer(P_EXEC) + .register(); + + new CommandAPICommand("command") + .withArguments(new IntegerArgument("integer")) + .executesPlayer(P_EXEC) + .register(); + + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false).withChildren( + node("string", StringArgument.class, true), + node("integer", IntegerArgument.class, true) + ), + List.of("command:CommandAPICommand", "string:StringArgument"), + List.of("command:CommandAPICommand", "integer:IntegerArgument") ); } + + @Test + void testRegisterMergeNamespaces() { + new CommandAPICommand("command") + .withArguments(new LiteralArgument("first")) + .executesPlayer(P_EXEC) + .register("first"); + + new CommandAPICommand("command") + .withArguments(new LiteralArgument("second")) + .executesPlayer(P_EXEC) + .register("second"); + + RegisteredCommand first = simpleRegisteredCommand( + "command", "first", + commandNode("command", false).withChildren( + node("first", LiteralArgument.class, true).helpString("first") + ) + ); + + RegisteredCommand second = simpleRegisteredCommand( + "command", "second", + commandNode("command", false).withChildren( + node("second", LiteralArgument.class, true).helpString("second") + ) + ); + + RegisteredCommand merged = simpleRegisteredCommand( + "command", "", + commandNode("command", false).withChildren( + node("first", LiteralArgument.class, true).helpString("first"), + node("second", LiteralArgument.class, true).helpString("second") + ) + ); + + assertCreatedRegisteredCommands(merged, first, second); + } } diff --git a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-test/commandapi-bukkit-test-tests/src/test/java/dev/jorel/commandapi/test/CommandTreeRegisteredCommandTests.java b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-test/commandapi-bukkit-test-tests/src/test/java/dev/jorel/commandapi/test/CommandTreeRegisteredCommandTests.java index 1c3b5764b0..9e772b2003 100644 --- a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-test/commandapi-bukkit-test-tests/src/test/java/dev/jorel/commandapi/test/CommandTreeRegisteredCommandTests.java +++ b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-test/commandapi-bukkit-test-tests/src/test/java/dev/jorel/commandapi/test/CommandTreeRegisteredCommandTests.java @@ -1,6 +1,5 @@ package dev.jorel.commandapi.test; -import dev.jorel.commandapi.CommandAPI; import dev.jorel.commandapi.CommandTree; import dev.jorel.commandapi.CommandPermission; import dev.jorel.commandapi.RegisteredCommand; @@ -12,17 +11,15 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.Arrays; import java.util.List; -import java.util.Objects; import java.util.Optional; -import static org.junit.jupiter.api.Assertions.fail; +import static dev.jorel.commandapi.test.RegisteredCommandTestBase.NodeBuilder.node; /** * Tests for making sure the {@link RegisteredCommand} information is correct when registering {@link CommandTree}s */ -class CommandTreeRegisteredCommandTests extends TestBase { +class CommandTreeRegisteredCommandTests extends RegisteredCommandTestBase { /********* * Setup * @@ -38,64 +35,8 @@ public void tearDown() { super.tearDown(); } - private RegisteredCommand registeredCommandNoHelpOrPermission(String name, List argsAsStr, String... aliases) { - return new RegisteredCommand(name, argsAsStr, - Optional.empty(), Optional.empty(), Optional.empty(), - aliases, - CommandPermission.NONE, "minecraft" - ); - } - - private void assertCreatedRegisteredCommands(RegisteredCommand... commands) { - List expectedCommands = Arrays.asList(commands); - List actualCommands = CommandAPI.getRegisteredCommands(); - - if (expectedCommands.size() != actualCommands.size()) { - StringBuilder builder = new StringBuilder(); - builder.append("Expected ").append(expectedCommands.size()).append(" command(s), found ").append(actualCommands.size()).append(" command(s)."); - - builder.append("\nExpected: "); - addRegisteredCommandList(builder, expectedCommands); - builder.append("\nActual: "); - addRegisteredCommandList(builder, actualCommands); - - fail(builder.toString()); - } - - for (int i = 0; i < expectedCommands.size(); i++) { - RegisteredCommand expectedCommand = expectedCommands.get(i); - RegisteredCommand actualCommand = actualCommands.get(i); - - if (!Objects.equals(expectedCommand, actualCommand)) { - StringBuilder builder = new StringBuilder(); - builder.append("Command #").append(i + 1).append(" differed. Expected:\n"); - builder.append(expectedCommand); - builder.append("\nActual:\n"); - builder.append(actualCommand); - - builder.append("\nExpected list: "); - addRegisteredCommandList(builder, expectedCommands); - builder.append("\nActual list: "); - addRegisteredCommandList(builder, actualCommands); - - fail(builder.toString()); - } - } - } - - private void addRegisteredCommandList(StringBuilder builder, List commands) { - if (commands.isEmpty()) { - builder.append("[]"); - return; - } - - builder.append("[\n"); - for (RegisteredCommand command : commands) { - builder.append("\t"); - builder.append(command); - builder.append("\n"); - } - builder.append("]"); + private NodeBuilder commandNode(String nodeName, boolean executable) { + return node(nodeName, CommandTree.class, executable).helpString(nodeName); } /********* @@ -108,7 +49,11 @@ void testRegister() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands(registeredCommandNoHelpOrPermission("command", List.of())); + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", true), + List.of("command:CommandTree") + ); } @Test @@ -123,13 +68,13 @@ void testRegisterHelpInformation() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands( - new RegisteredCommand( - "command", List.of(), - Optional.of("short description"), Optional.of("full description"), Optional.of(new String[]{"usage 1", "usage 2", "usage 3"}), - new String[0], CommandPermission.NONE, "minecraft" - ) + RegisteredCommand expectedCommand = new RegisteredCommand( + "command", new String[0], "minecraft", CommandPermission.NONE, + Optional.of("short description"), Optional.of("full description"), Optional.of(new String[]{"usage 1", "usage 2", "usage 3"}), + commandNode("command", true).build() ); + + assertCreatedRegisteredCommands(expectedCommand.copyWithEmptyNamespace(), expectedCommand); } @Test @@ -139,13 +84,13 @@ void testRegisterOpPermission() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands( - new RegisteredCommand( - "command", List.of(), - Optional.empty(), Optional.empty(), Optional.empty(), - new String[0], CommandPermission.OP, "minecraft" - ) + RegisteredCommand expectedCommand = new RegisteredCommand( + "command", new String[0], "minecraft", CommandPermission.OP, + Optional.empty(), Optional.empty(), Optional.empty(), + commandNode("command", true).build() ); + + assertCreatedRegisteredCommands(expectedCommand.copyWithEmptyNamespace(), expectedCommand); } @Test @@ -155,13 +100,13 @@ void testRegisterStringPermission() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands( - new RegisteredCommand( - "command", List.of(), - Optional.empty(), Optional.empty(), Optional.empty(), - new String[0], CommandPermission.fromString("permission"), "minecraft" - ) + RegisteredCommand expectedCommand = new RegisteredCommand( + "command", new String[0], "minecraft", CommandPermission.fromString("permission"), + Optional.empty(), Optional.empty(), Optional.empty(), + commandNode("command", true).build() ); + + assertCreatedRegisteredCommands(expectedCommand.copyWithEmptyNamespace(), expectedCommand); } @Test @@ -171,7 +116,9 @@ void testRegisterOneAlias() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands(registeredCommandNoHelpOrPermission("command", List.of(), "alias1")); + RegisteredCommand expectedCommand = simpleRegisteredCommand("command", "minecraft", commandNode("command", true), "alias1"); + + assertCreatedRegisteredCommands(expectedCommand.copyWithEmptyNamespace(), expectedCommand); } @Test @@ -181,7 +128,20 @@ void testRegisterTwoAliases() { .executesPlayer(P_EXEC) .register(); - assertCreatedRegisteredCommands(registeredCommandNoHelpOrPermission("command", List.of(), "alias1", "alias2")); + RegisteredCommand expectedCommand = simpleRegisteredCommand("command", "minecraft", commandNode("command", true), "alias1", "alias2"); + + assertCreatedRegisteredCommands(expectedCommand.copyWithEmptyNamespace(), expectedCommand); + } + + @Test + void testRegisterNamespace() { + new CommandTree("command") + .executesPlayer(P_EXEC) + .register("custom"); + + RegisteredCommand expectedCommand = simpleRegisteredCommand("command", "custom", commandNode("command", true)); + + assertCreatedRegisteredCommands(expectedCommand.copyWithEmptyNamespace(), expectedCommand); } @Test @@ -190,7 +150,12 @@ void testRegisterOneBranch() { .then(new StringArgument("string").executesPlayer(P_EXEC)) .register(); - assertCreatedRegisteredCommands(registeredCommandNoHelpOrPermission("command", List.of("string:StringArgument"))); + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false) + .withChildren(node("string", StringArgument.class, true)), + List.of("command:CommandTree", "string:StringArgument") + ); } @Test @@ -200,9 +165,14 @@ void testRegisterTwoBranches() { .then(new IntegerArgument("integer").executesPlayer(P_EXEC)) .register(); - assertCreatedRegisteredCommands( - registeredCommandNoHelpOrPermission("command", List.of("string:StringArgument")), - registeredCommandNoHelpOrPermission("command", List.of("integer:IntegerArgument")) + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false).withChildren( + node("string", StringArgument.class, true), + node("integer", IntegerArgument.class, true) + ), + List.of("command:CommandTree", "string:StringArgument"), + List.of("command:CommandTree", "integer:IntegerArgument") ); } @@ -215,16 +185,14 @@ void testRegisterMultiLiteralArguments() { ) .register(); - assertCreatedRegisteredCommands( - registeredCommandNoHelpOrPermission("command", List.of("a:LiteralArgument", "d:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("b:LiteralArgument", "d:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("c:LiteralArgument", "d:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("a:LiteralArgument", "e:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("b:LiteralArgument", "e:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("c:LiteralArgument", "e:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("a:LiteralArgument", "f:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("b:LiteralArgument", "f:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("c:LiteralArgument", "f:LiteralArgument")) + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false).withChildren( + node("literal1", MultiLiteralArgument.class, false).helpString("(a|b|c)").withChildren( + node("literal2", MultiLiteralArgument.class, true).helpString("(d|e|f)") + ) + ), + List.of("command:CommandTree", "literal1:MultiLiteralArgument", "literal2:MultiLiteralArgument") ); } @@ -248,18 +216,29 @@ void testRegisterCombinedArguments() { ) .register(); - assertCreatedRegisteredCommands( - registeredCommandNoHelpOrPermission("command", List.of( + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false) + .withChildren(node("1", LiteralArgument.class, false).helpString("1") + .withChildren(node("2", LiteralArgument.class, false).helpString("2") + .withChildren(node("3", LiteralArgument.class, false).helpString("3") + .withChildren(node("4", LiteralArgument.class, true).helpString("4") + .withChildren(node("5", LiteralArgument.class, false).helpString("5") + .withChildren(node("6", LiteralArgument.class, true).helpString("6") + .withChildren(node("7", LiteralArgument.class, false).helpString("7") + .withChildren(node("8", LiteralArgument.class, true).helpString("8") + )))))))), + List.of("command:CommandTree", "1:LiteralArgument", "2:LiteralArgument", "3:LiteralArgument", "4:LiteralArgument" - )), - registeredCommandNoHelpOrPermission("command", List.of( + ), + List.of("command:CommandTree", "1:LiteralArgument", "2:LiteralArgument", "3:LiteralArgument", "4:LiteralArgument", "5:LiteralArgument", "6:LiteralArgument" - )), - registeredCommandNoHelpOrPermission("command", List.of( + ), + List.of("command:CommandTree", "1:LiteralArgument", "2:LiteralArgument", "3:LiteralArgument", "4:LiteralArgument", "5:LiteralArgument", "6:LiteralArgument", "7:LiteralArgument", "8:LiteralArgument" - )) + ) ); } @@ -278,9 +257,12 @@ void testRegisterOneBranchAndBaseExecutable() { ) .register(); - assertCreatedRegisteredCommands( - registeredCommandNoHelpOrPermission("command", List.of()), - registeredCommandNoHelpOrPermission("command", List.of("subcommand:LiteralArgument")) + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", true) + .withChildren(node("subcommand", LiteralArgument.class, true).helpString("subcommand")), + List.of("command:CommandTree"), + List.of("command:CommandTree", "subcommand:LiteralArgument") ); } @@ -299,11 +281,22 @@ void testRegisterBranchesWithBranches() { ) .register(); - assertCreatedRegisteredCommands( - registeredCommandNoHelpOrPermission("command", List.of("subcommand1:LiteralArgument", "string1:StringArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand1:LiteralArgument", "integer1:IntegerArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand2:LiteralArgument", "string2:StringArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand2:LiteralArgument", "integer2:IntegerArgument")) + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false).withChildren( + node("subcommand1", LiteralArgument.class, false).helpString("subcommand1").withChildren( + node("string1", StringArgument.class, true), + node("integer1", IntegerArgument.class, true) + ), + node("subcommand2", LiteralArgument.class, false).helpString("subcommand2").withChildren( + node("string2", StringArgument.class, true), + node("integer2", IntegerArgument.class, true) + ) + ), + List.of("command:CommandTree", "subcommand1:LiteralArgument", "string1:StringArgument"), + List.of("command:CommandTree", "subcommand1:LiteralArgument", "integer1:IntegerArgument"), + List.of("command:CommandTree", "subcommand2:LiteralArgument", "string2:StringArgument"), + List.of("command:CommandTree", "subcommand2:LiteralArgument", "integer2:IntegerArgument") ); } @@ -334,11 +327,150 @@ void testRegisterBranchesWithCombinedArguments() { ) .register(); + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false).withChildren( + node("subcommand1", LiteralArgument.class, false).helpString("subcommand1") + .withChildren(node("1a", LiteralArgument.class, false).helpString("1a") + .withChildren(node("1b", LiteralArgument.class, true).helpString("1b") + .withChildren(node("1c", LiteralArgument.class, false).helpString("1c") + .withChildren(node("1d", LiteralArgument.class, true).helpString("1d") + )))), + node("subcommand2", LiteralArgument.class, false).helpString("subcommand2") + .withChildren(node("2a", LiteralArgument.class, false).helpString("2a") + .withChildren(node("2b", LiteralArgument.class, true).helpString("2b") + .withChildren(node("2c", LiteralArgument.class, false).helpString("2c") + .withChildren(node("2d", LiteralArgument.class, true).helpString("2d") + )))) + ), + List.of("command:CommandTree", "subcommand1:LiteralArgument", "1a:LiteralArgument", "1b:LiteralArgument"), + List.of("command:CommandTree", "subcommand1:LiteralArgument", "1a:LiteralArgument", "1b:LiteralArgument", "1c:LiteralArgument", "1d:LiteralArgument"), + List.of("command:CommandTree", "subcommand2:LiteralArgument", "2a:LiteralArgument", "2b:LiteralArgument"), + List.of("command:CommandTree", "subcommand2:LiteralArgument", "2a:LiteralArgument", "2b:LiteralArgument", "2c:LiteralArgument", "2d:LiteralArgument") + ); + } + + ///////////////////////// + // Information merging // + ///////////////////////// + + ///////////////////////// + // Information merging // + ///////////////////////// + + @Test + void testRegisterTwoSeparateCommands() { + new CommandTree("command1") + .executesPlayer(P_EXEC) + .register(); + + new CommandTree("command2") + .executesPlayer(P_EXEC) + .register(); + + RegisteredCommand command1 = simpleRegisteredCommand("command1", "minecraft", commandNode("command1", true)); + RegisteredCommand command2 = simpleRegisteredCommand("command2", "minecraft", commandNode("command2", true)); + assertCreatedRegisteredCommands( - registeredCommandNoHelpOrPermission("command", List.of("subcommand1:LiteralArgument", "1a:LiteralArgument", "1b:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand1:LiteralArgument", "1a:LiteralArgument", "1b:LiteralArgument", "1c:LiteralArgument", "1d:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand2:LiteralArgument", "2a:LiteralArgument", "2b:LiteralArgument")), - registeredCommandNoHelpOrPermission("command", List.of("subcommand2:LiteralArgument", "2a:LiteralArgument", "2b:LiteralArgument", "2c:LiteralArgument", "2d:LiteralArgument")) + command1.copyWithEmptyNamespace(), command1, + command2.copyWithEmptyNamespace(), command2 ); } + + @Test + void testRegisterMergeArguments() { + new CommandTree("command") + .then(new StringArgument("string").executesPlayer(P_EXEC)) + .register(); + + new CommandTree("command") + .then(new IntegerArgument("integer").executesPlayer(P_EXEC)) + .register(); + + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false).withChildren( + node("string", StringArgument.class, true), + node("integer", IntegerArgument.class, true) + ), + List.of("command:CommandTree", "string:StringArgument"), + List.of("command:CommandTree", "integer:IntegerArgument") + ); + } + + @Test + void testRegisterMergeDifferentLengthBranches() { + new CommandTree("command") + .then( + new LiteralArgument("first").then( + new StringArgument("argument").executesPlayer(P_EXEC) + ) + ) + .then( + new LiteralArgument("second").executesPlayer(P_EXEC) + ) + .register(); + + new CommandTree("command") + .then( + new LiteralArgument("first").executesPlayer(P_EXEC) + ) + .then( + new LiteralArgument("second").then( + new IntegerArgument("argument").executesPlayer(P_EXEC) + ) + ) + .register(); + + assertCreatedSimpleRegisteredCommand( + "command", + commandNode("command", false).withChildren( + node("first", LiteralArgument.class, true).helpString("first").withChildren( + node("argument", StringArgument.class, true) + ), + node("second", LiteralArgument.class, true).helpString("second").withChildren( + node("argument", IntegerArgument.class, true) + ) + ), + List.of("command:CommandTree", "first:LiteralArgument"), + List.of("command:CommandTree", "first:LiteralArgument", "argument:StringArgument"), + List.of("command:CommandTree", "second:LiteralArgument"), + List.of("command:CommandTree", "second:LiteralArgument", "argument:IntegerArgument") + ); + } + + @Test + void testRegisterMergeNamespaces() { + new CommandTree("command") + .then(new LiteralArgument("first").executesPlayer(P_EXEC)) + .register("first"); + + new CommandTree("command") + .then(new LiteralArgument("second").executesPlayer(P_EXEC)) + .register("second"); + + RegisteredCommand first = simpleRegisteredCommand( + "command", "first", + commandNode("command", false).withChildren( + node("first", LiteralArgument.class, true).helpString("first") + ) + ); + + RegisteredCommand second = simpleRegisteredCommand( + "command", "second", + commandNode("command", false).withChildren( + node("second", LiteralArgument.class, true).helpString("second") + ) + ); + + RegisteredCommand merged = simpleRegisteredCommand( + "command", "", + commandNode("command", false).withChildren( + node("first", LiteralArgument.class, true).helpString("first"), + node("second", LiteralArgument.class, true).helpString("second") + ) + ); + + assertCreatedRegisteredCommands(merged, first, second); + } } diff --git a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-test/commandapi-bukkit-test-tests/src/test/java/dev/jorel/commandapi/test/RegisteredCommandTestBase.java b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-test/commandapi-bukkit-test-tests/src/test/java/dev/jorel/commandapi/test/RegisteredCommandTestBase.java new file mode 100644 index 0000000000..a0a1bc5105 --- /dev/null +++ b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-test/commandapi-bukkit-test-tests/src/test/java/dev/jorel/commandapi/test/RegisteredCommandTestBase.java @@ -0,0 +1,164 @@ +package dev.jorel.commandapi.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +import dev.jorel.commandapi.CommandAPI; +import dev.jorel.commandapi.CommandPermission; +import dev.jorel.commandapi.RegisteredCommand; +import dev.jorel.commandapi.RegisteredCommand.Node; + +public abstract class RegisteredCommandTestBase extends TestBase { + + /********* + * Setup * + *********/ + + @BeforeEach + public void setUp() { + super.setUp(); + } + + @AfterEach + public void tearDown() { + super.tearDown(); + } + + /******************* + * Utility methods * + *******************/ + + @SafeVarargs + public final void assertCreatedSimpleRegisteredCommand(String name, NodeBuilder args, List... argsAsStr) { + RegisteredCommand expectedCommand = simpleRegisteredCommand(name, "minecraft", args); + + assertCreatedRegisteredCommands(expectedCommand.copyWithEmptyNamespace(), expectedCommand); + + // `.get(0)` makes sense since we just verified `CommandAPI#getRegisteredCommands` has elements + assertEquals(Arrays.asList(argsAsStr), CommandAPI.getRegisteredCommands().get(0).rootNode().argsAsStr()); + } + + public RegisteredCommand simpleRegisteredCommand(String name, String namespace, NodeBuilder args, String... aliases) { + return new RegisteredCommand( + name, aliases, namespace, CommandPermission.NONE, + Optional.empty(), Optional.empty(), Optional.empty(), + args.build() + ); + } + + public static class NodeBuilder { + public static NodeBuilder node(String name, Class clazz, boolean executable) { + return new NodeBuilder(name, clazz, executable); + } + + public static List children(NodeBuilder... children) { + List result = new ArrayList<>(children.length); + for (NodeBuilder child : children) { + result.add(child.build()); + } + return result; + } + + private final String nodeName; + private final String className; + private final boolean executable; + + private String helpString; + private final List children; + + public NodeBuilder(String nodeName, Class clazz, boolean executable) { + this.nodeName = nodeName; + this.className = clazz.getSimpleName(); + this.executable = executable; + + this.helpString = "<" + nodeName + ">"; + this.children = new ArrayList<>(); + } + + public NodeBuilder helpString(String helpString) { + this.helpString = helpString; + return this; + } + + public NodeBuilder withChildren(NodeBuilder... children) { + for (NodeBuilder child : children) { + this.children.add(child.build()); + } + return this; + } + + public NodeBuilder withChildren(Node... children) { + return withChildren(Arrays.asList(children)); + } + + public NodeBuilder withChildren(List children) { + this.children.addAll(children); + return this; + } + + public Node build() { + return new Node(nodeName, className, helpString, executable, children); + } + } + + public void assertCreatedRegisteredCommands(RegisteredCommand... commands) { + List expectedCommands = Arrays.asList(commands); + List actualCommands = CommandAPI.getRegisteredCommands(); + + if (expectedCommands.size() != actualCommands.size()) { + StringBuilder builder = new StringBuilder(); + builder.append("Expected ").append(expectedCommands.size()).append(" command(s), found ").append(actualCommands.size()).append(" command(s)."); + + builder.append("\nExpected: "); + addRegisteredCommandList(builder, expectedCommands); + builder.append("\nActual: "); + addRegisteredCommandList(builder, actualCommands); + + fail(builder.toString()); + } + + for (int i = 0; i < expectedCommands.size(); i++) { + RegisteredCommand expectedCommand = expectedCommands.get(i); + RegisteredCommand actualCommand = actualCommands.get(i); + + if (!Objects.equals(expectedCommand, actualCommand)) { + StringBuilder builder = new StringBuilder(); + builder.append("Command #").append(i + 1).append(" differed. Expected:\n"); + builder.append(expectedCommand); + builder.append("\nActual:\n"); + builder.append(actualCommand); + + builder.append("\nExpected list: "); + addRegisteredCommandList(builder, expectedCommands); + builder.append("\nActual list: "); + addRegisteredCommandList(builder, actualCommands); + + fail(builder.toString()); + } + } + } + + private void addRegisteredCommandList(StringBuilder builder, List commands) { + if (commands.isEmpty()) { + builder.append("[]"); + return; + } + + builder.append("[\n"); + for (RegisteredCommand command : commands) { + builder.append("\t"); + builder.append(command); + builder.append("\n"); + } + builder.append("]"); + } +} diff --git a/commandapi-platforms/commandapi-velocity/commandapi-velocity-core/src/main/java/dev/jorel/commandapi/CommandAPIVelocity.java b/commandapi-platforms/commandapi-velocity/commandapi-velocity-core/src/main/java/dev/jorel/commandapi/CommandAPIVelocity.java index 2f7deee7c6..8da4c35299 100644 --- a/commandapi-platforms/commandapi-velocity/commandapi-velocity-core/src/main/java/dev/jorel/commandapi/CommandAPIVelocity.java +++ b/commandapi-platforms/commandapi-velocity/commandapi-velocity-core/src/main/java/dev/jorel/commandapi/CommandAPIVelocity.java @@ -219,7 +219,7 @@ public void preCommandRegistration(String commandName) { } @Override - public void postCommandRegistration(List registeredCommands, LiteralCommandNode resultantNode, List> aliasNodes) { + public void postCommandRegistration(RegisteredCommand registeredCommand, LiteralCommandNode resultantNode, List> aliasNodes) { // Nothing to do } diff --git a/commandapi-platforms/commandapi-velocity/commandapi-velocity-core/src/main/java/dev/jorel/commandapi/arguments/LiteralArgument.java b/commandapi-platforms/commandapi-velocity/commandapi-velocity-core/src/main/java/dev/jorel/commandapi/arguments/LiteralArgument.java index 4cc2bd8cfa..a49151f55e 100644 --- a/commandapi-platforms/commandapi-velocity/commandapi-velocity-core/src/main/java/dev/jorel/commandapi/arguments/LiteralArgument.java +++ b/commandapi-platforms/commandapi-velocity/commandapi-velocity-core/src/main/java/dev/jorel/commandapi/arguments/LiteralArgument.java @@ -20,14 +20,18 @@ *******************************************************************************/ package dev.jorel.commandapi.arguments; +import com.mojang.brigadier.Command; import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.tree.CommandNode; import com.velocitypowered.api.command.CommandSource; + import dev.jorel.commandapi.exceptions.BadLiteralException; import dev.jorel.commandapi.executors.CommandArguments; import java.util.List; +import java.util.function.Function; /** * A pseudo-argument representing a single literal string @@ -132,11 +136,6 @@ public CommandAPIArgumentType getArgumentType() { return CommandAPIArgumentType.LITERAL; } - @Override - public String getHelpString() { - return literal; - } - @Override public String parseArgument(CommandContext cmdCtx, String key, CommandArguments previousArgs) throws CommandSyntaxException { return literal; @@ -152,4 +151,9 @@ public String parseArgument(CommandContext cmdCtx, String key, public ArgumentBuilder createArgumentBuilder(List> previousArguments, List previousArgumentNames) { return Literal.super.createArgumentBuilder(previousArguments, previousArgumentNames); } + + @Override + public NodeInformation linkNode(NodeInformation previousNodeInformation, CommandNode rootNode, List> previousArguments, List previousArgumentNames, Function>, Command> terminalExecutorCreator) { + return Literal.super.linkNode(previousNodeInformation, rootNode, previousArguments, previousArgumentNames,terminalExecutorCreator); + } } diff --git a/commandapi-platforms/commandapi-velocity/commandapi-velocity-core/src/main/java/dev/jorel/commandapi/arguments/MultiLiteralArgument.java b/commandapi-platforms/commandapi-velocity/commandapi-velocity-core/src/main/java/dev/jorel/commandapi/arguments/MultiLiteralArgument.java index 10ba0e5c25..cef66a147b 100644 --- a/commandapi-platforms/commandapi-velocity/commandapi-velocity-core/src/main/java/dev/jorel/commandapi/arguments/MultiLiteralArgument.java +++ b/commandapi-platforms/commandapi-velocity/commandapi-velocity-core/src/main/java/dev/jorel/commandapi/arguments/MultiLiteralArgument.java @@ -104,18 +104,13 @@ public String parseArgument(CommandContext cmdCtx, String key, // method by default. However, we want to use the implementations found in the MultiLiteral interface. // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - @Override - public void appendToCommandPaths(List> argumentStrings) { - MultiLiteral.super.appendToCommandPaths(argumentStrings); - } - @Override public ArgumentBuilder createArgumentBuilder(List> previousArguments, List previousArgumentNames) { return MultiLiteral.super.createArgumentBuilder(previousArguments, previousArgumentNames); } @Override - public List> linkNode(List> previousNodes, CommandNode rootNode, List> previousArguments, List previousArgumentNames, Function>, Command> terminalExecutorCreator) { - return MultiLiteral.super.linkNode(previousNodes, rootNode, previousArguments, previousArgumentNames, terminalExecutorCreator); + public NodeInformation linkNode(NodeInformation previousNodeInformation, CommandNode rootNode, List> previousArguments, List previousArgumentNames, Function>, Command> terminalExecutorCreator) { + return MultiLiteral.super.linkNode(previousNodeInformation, rootNode, previousArguments, previousArgumentNames, terminalExecutorCreator); } }