Add subcommand support for fast parsing & proper separation between logics

This commit is contained in:
themode 2021-02-20 08:59:15 +01:00
parent 06abbcb7fb
commit a419d7eb3f
3 changed files with 96 additions and 42 deletions

View File

@ -306,38 +306,7 @@ public final class CommandManager {
// Brigadier-like commands
for (Command command : dispatcher.getCommands()) {
// Check if player should see this command
final CommandCondition commandCondition = command.getCondition();
if (commandCondition != null) {
// Do not show command if return false
if (!commandCondition.canUse(player, null)) {
continue;
}
}
// The main root of this command
IntList cmdChildren = new IntArrayList();
final Collection<CommandSyntax> syntaxes = command.getSyntaxes();
// Create command for main name
final int mainNodeIndex = createCommand(player, nodes, cmdChildren,
command.getName(), syntaxes, rootChildren);
// Use redirection to hook aliases with the command
final String[] aliases = command.getAliases();
if (aliases == null)
continue;
for (String alias : aliases) {
DeclareCommandsPacket.Node aliasNode = new DeclareCommandsPacket.Node();
aliasNode.flags = DeclareCommandsPacket.getFlag(DeclareCommandsPacket.NodeType.LITERAL,
false, true, false);
aliasNode.name = alias;
aliasNode.redirectedNode = mainNodeIndex;
addCommandNameNode(aliasNode, rootChildren, nodes);
}
serializeCommand(player, command, nodes, rootChildren);
}
// Pair<CommandName,EnabledTracking>
@ -394,6 +363,52 @@ public final class CommandManager {
return declareCommandsPacket;
}
private int serializeCommand(CommandSender sender, Command command,
List<DeclareCommandsPacket.Node> nodes,
IntList rootChildren) {
// Check if player should see this command
final CommandCondition commandCondition = command.getCondition();
if (commandCondition != null) {
// Do not show command if return false
if (!commandCondition.canUse(sender, null)) {
return -1;
}
}
// The main root of this command
IntList cmdChildren = new IntArrayList();
final Collection<CommandSyntax> syntaxes = command.getSyntaxes();
// Create command for main name
final DeclareCommandsPacket.Node mainNode = createCommand(sender, nodes, cmdChildren,
command.getName(), syntaxes, rootChildren);
final int mainNodeIndex = nodes.indexOf(mainNode);
// Serialize all the subcommands
for (Command subcommand : command.getSubcommands()) {
final int subNodeIndex = serializeCommand(sender, subcommand, nodes, cmdChildren);
if (subNodeIndex != -1) {
mainNode.children = ArrayUtils.concatenateIntArrays(mainNode.children, new int[]{subNodeIndex});
}
}
// Use redirection to hook aliases with the command
final String[] aliases = command.getAliases();
if (aliases != null) {
for (String alias : aliases) {
DeclareCommandsPacket.Node aliasNode = new DeclareCommandsPacket.Node();
aliasNode.flags = DeclareCommandsPacket.getFlag(DeclareCommandsPacket.NodeType.LITERAL,
false, true, false);
aliasNode.name = alias;
aliasNode.redirectedNode = mainNodeIndex;
addCommandNameNode(aliasNode, rootChildren, nodes);
}
}
return mainNodeIndex;
}
/**
* Adds the command's syntaxes to the nodes list.
*
@ -405,12 +420,12 @@ public final class CommandManager {
* @param rootChildren the children of the main node (all commands name)
* @return The index of the main node for alias redirection
*/
private int createCommand(@NotNull CommandSender sender,
@NotNull List<DeclareCommandsPacket.Node> nodes,
@NotNull IntList cmdChildren,
@NotNull String name,
@NotNull Collection<CommandSyntax> syntaxes,
@NotNull IntList rootChildren) {
private DeclareCommandsPacket.Node createCommand(@NotNull CommandSender sender,
@NotNull List<DeclareCommandsPacket.Node> nodes,
@NotNull IntList cmdChildren,
@NotNull String name,
@NotNull Collection<CommandSyntax> syntaxes,
@NotNull IntList rootChildren) {
DeclareCommandsPacket.Node literalNode = createMainNode(name, syntaxes.isEmpty());
@ -511,8 +526,7 @@ public final class CommandManager {
}
literalNode.children = ArrayUtils.toArray(cmdChildren);
return nodes.indexOf(literalNode);
return literalNode;
}

View File

@ -45,6 +45,7 @@ public class Command {
private CommandExecutor defaultExecutor;
private CommandCondition condition;
private final List<Command> subcommands;
private final List<CommandSyntax> syntaxes;
/**
@ -58,6 +59,7 @@ public class Command {
this.name = name;
this.aliases = aliases;
this.subcommands = new ArrayList<>();
this.syntaxes = new ArrayList<>();
}
@ -108,6 +110,15 @@ public class Command {
argument.setCallback(callback);
}
public void addSubcommand(@NotNull Command command) {
this.subcommands.add(command);
}
@NotNull
public List<Command> getSubcommands() {
return Collections.unmodifiableList(subcommands);
}
/**
* Adds a new syntax in the command.
* <p>
@ -278,4 +289,17 @@ public class Command {
public void globalListener(@NotNull CommandSender sender, @NotNull Arguments arguments, @NotNull String command) {
}
public static boolean isValidName(@NotNull Command command, @NotNull String name) {
if (command.getName().equals(name))
return true;
final String[] aliases = command.getAliases();
if (aliases == null)
return false;
for (String alias : aliases) {
if (alias.equals(name))
return true;
}
return false;
}
}

View File

@ -110,7 +110,10 @@ public class CommandDispatcher {
}
// Removes the command's name + the space after
final String[] args = commandString.replaceFirst(Pattern.quote(commandName), "").trim().split(StringUtils.SPACE);
String[] args = commandString.replaceFirst(Pattern.quote(commandName), "").trim().split(StringUtils.SPACE);
if (args.length == 1 && args[0].length() == 0) {
args = new String[0];
}
// Find the used syntax
ParsedCommand parsedCommand = findParsedCommand(command, args);
@ -133,13 +136,26 @@ public class CommandDispatcher {
@Nullable
private ParsedCommand findParsedCommand(@NotNull Command command, @NotNull String[] args) {
final boolean hasArgument = args.length > 0;
// Search for subcommand
if (hasArgument) {
final String firstArgument = args[0];
for (Command subcommand : command.getSubcommands()) {
if (Command.isValidName(subcommand, firstArgument)) {
return findParsedCommand(subcommand, Arrays.copyOfRange(args, 1, args.length));
}
}
}
ParsedCommand parsedCommand = new ParsedCommand();
parsedCommand.command = command;
// The default executor should be used if no argument is provided
{
final CommandExecutor defaultExecutor = command.getDefaultExecutor();
if (defaultExecutor != null && args[0].length() == 0) {
if (defaultExecutor != null && !hasArgument) {
parsedCommand.executor = defaultExecutor;
parsedCommand.arguments = new Arguments();
return parsedCommand;