Skip to content

Commit

Permalink
Initial rewrite of command building system
Browse files Browse the repository at this point in the history
Precursor work for #483

Notable changes:
- No more flattening everything into a CommandAPICommand
  - CommandTrees are not flattened
    - CommandMetaData and Execution removed (only used to flatten CommandTrees)
  - MultiLiteralArgument and subcommands are no longer converted into LiteralArguments
  - Optional arguments are not chopped up into multiple arrays
  - In general, a single call to `ExecutableCommand#register` now creates the entire Brigadier tree for that command instance
    - Should only be converting Arguments to Brigadier nodes once instead of multiple times

- CommandAPIHandler no longer creates Brigadier nodes
  - Delegated to AbstractCommandAPICommand, AbstractCommandTree, AbstractArgumentTree.java, and AbstractArgument

- Problems that prevent a command from being registered have been unified under CommandRegistrationException
  - Exception messages tweaked

- Bukkit-specific features removed from `commandapi-core`
  - Removed `isConverted` property of CommandAPICommand
  - Moved logic for flattening EntitySelectorArgument from `CommandAPIHandler#generateBrigadierCommand` to Converter
  - Moved special Bukkit command permission stuff into CommandAPIBukkit

- Taking advantage of Brigadier redirects
  - Command alias nodes redirect to main command name
  - MultiLiteralArgument literal nodes use redirects
  - Instead of duplicating the argument node structure after these arguments, they can reference the same path

TODO:
- The checks performed by `CommandAPIHandler#hasCommandConflict` have not been reimplemented (I'm not sure if they did anything?)
- MultiLiteralArgument is currently broken. See Mojang/brigadier#137
- Many changes not covered by tests (could be sneaky behavioral differences)
  • Loading branch information
willkroboth committed Mar 19, 2024
1 parent 19247bb commit afe246d
Show file tree
Hide file tree
Showing 35 changed files with 1,831 additions and 1,487 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package dev.jorel.commandapi;

import com.mojang.brigadier.tree.CommandNode;
import dev.jorel.commandapi.arguments.AbstractArgument;
import dev.jorel.commandapi.arguments.GreedyArgument;
import dev.jorel.commandapi.exceptions.GreedyArgumentException;
import dev.jorel.commandapi.exceptions.MissingCommandExecutorException;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -30,10 +34,10 @@ public abstract class AbstractArgumentTree<Impl
*/
@SuppressWarnings("unchecked")
protected AbstractArgumentTree() {
if (this instanceof AbstractArgument<?, ?, Argument, CommandSender>) {
if (this instanceof AbstractArgument<?, ?, ?, ?>) {
this.argument = (Argument) this;
} else {
throw new IllegalArgumentException("Implicit inherited constructor must be from Argument");
throw new IllegalArgumentException("Implicit inherited constructor must be from AbstractArgument");
}
}

Expand All @@ -60,19 +64,73 @@ public Impl then(final AbstractArgumentTree<?, Argument, CommandSender> tree) {
return instance();
}

List<Execution<CommandSender, Argument>> getExecutions() {
List<Execution<CommandSender, Argument>> executions = new ArrayList<>();
// If this is executable, add its execution
if (this.executor.hasAnyExecutors()) {
executions.add(new Execution<>(List.of(this.argument), this.executor));
/**
* Builds the Brigadier {@link CommandNode} structure for this argument tree.
*
* @param previousNode The {@link CommandNode} to add this argument tree onto.
* @param previousArguments A List of CommandAPI arguments that came before this argument tree.
* @param previousNonLiteralArgumentNames A List of Strings containing the node names that came before this argument.
* @param <Source> The Brigadier Source object for running commands.
*/
public <Source> void buildBrigadierNode(CommandNode<Source> previousNode, List<Argument> previousArguments, List<String> previousNonLiteralArgumentNames) {
// Check preconditions
if (argument instanceof GreedyArgument && !arguments.isEmpty()) {
throw new GreedyArgumentException(previousArguments, argument, getBranchesAsList());
}
if (!executor.hasAnyExecutors() && arguments.isEmpty()) {
throw new MissingCommandExecutorException(previousArguments, argument);
}

// Create node for this argument
CommandNode<Source> rootNode = argument.addArgumentNodes(previousNode, previousArguments, previousNonLiteralArgumentNames, executor);

// Add our branches as children to the node
for (AbstractArgumentTree<?, Argument, CommandSender> child : arguments) {
// We need a new list for each branch of the tree
List<Argument> newPreviousArguments = new ArrayList<>(previousArguments);
List<String> newPreviousArgumentNames = new ArrayList<>(previousNonLiteralArgumentNames);

child.buildBrigadierNode(rootNode, newPreviousArguments, newPreviousArgumentNames);
}
}

/**
* @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<List<String>> getBranchesAsStrings() {
List<List<String>> baseArgumentPaths = new ArrayList<>();
baseArgumentPaths.add(new ArrayList<>());
argument.appendToCommandPaths(baseArgumentPaths);

List<List<String>> argumentStrings = new ArrayList<>();
for (AbstractArgumentTree<?, Argument, CommandSender> child : arguments) {
for (List<String> subArgs : child.getBranchesAsStrings()) {
for (List<String> basePath : baseArgumentPaths) {
List<String> mergedPaths = new ArrayList<>();
mergedPaths.addAll(basePath);
mergedPaths.addAll(subArgs);
argumentStrings.add(mergedPaths);
}
}
}
// Add all executions from all arguments
for (AbstractArgumentTree<?, Argument, CommandSender> tree : arguments) {
for (Execution<CommandSender, Argument> execution : tree.getExecutions()) {
// Prepend this argument to the arguments of the executions
executions.add(execution.prependedBy(this.argument));

return argumentStrings;
}

/**
* @return A list of paths that represent the possible branches of this argument tree as Argument objects.
*/
protected List<List<Argument>> getBranchesAsList() {
List<List<Argument>> branchesList = new ArrayList<>();
for (AbstractArgumentTree<?, Argument, CommandSender> branch : arguments) {
for (List<Argument> subBranchList : branch.getBranchesAsList()) {
List<Argument> newBranchList = new ArrayList<>();
newBranchList.add(branch.argument);
newBranchList.addAll(subBranchList);
branchesList.add(newBranchList);
}
}
return executions;
return branchesList;
}
}
Loading

0 comments on commit afe246d

Please sign in to comment.