Files
commands/brigadier/src/main/java/co.aikar.commands/ACFBrigadierManager.java
T
chickeneer 0f75b89a2e Fix brigadier command registration with no required arguments
This fixes issues with optional arguments and Default subcommands
2022-08-01 02:01:05 -05:00

182 lines
7.8 KiB
Java

package co.aikar.commands;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.arguments.BoolArgumentType;
import com.mojang.brigadier.arguments.DoubleArgumentType;
import com.mojang.brigadier.arguments.FloatArgumentType;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
/**
* Handles registering of commands into brigadier
*
* @param <S>
* @author MiniDigger
* @deprecated Unstable API
*/
@Deprecated
@UnstableAPI
public class ACFBrigadierManager<S> {
protected final CommandManager<?, ?, ?, ?, ?, ?> manager;
private final Map<Class<?>, ArgumentType<?>> arguments = new HashMap<>();
/**
* Constructs a new brigadier manager, utilizing the currently active command manager
*
* @param manager
*/
ACFBrigadierManager(CommandManager<?, ?, ?, ?, ?, ?> manager) {
manager.verifyUnstableAPI("brigadier");
this.manager = manager;
// TODO support stuff like min max via brigadier?
registerArgument(String.class, StringArgumentType.word());
registerArgument(float.class, FloatArgumentType.floatArg());
registerArgument(Float.class, FloatArgumentType.floatArg());
registerArgument(double.class, DoubleArgumentType.doubleArg());
registerArgument(Double.class, DoubleArgumentType.doubleArg());
registerArgument(boolean.class, BoolArgumentType.bool());
registerArgument(Boolean.class, BoolArgumentType.bool());
registerArgument(int.class, IntegerArgumentType.integer());
registerArgument(Integer.class, IntegerArgumentType.integer());
// We use integer for long due to Bungee bug, plus should really be considered same on client
registerArgument(long.class, IntegerArgumentType.integer());
registerArgument(Long.class, IntegerArgumentType.integer());
}
<T> void registerArgument(Class<T> clazz, ArgumentType<?> type) {
arguments.put(clazz, type);
}
ArgumentType<Object> getArgumentTypeByClazz(CommandParameter param) {
if (param.consumesRest) {
//noinspection unchecked
return (ArgumentType<Object>) (ArgumentType<?>) StringArgumentType.greedyString();
}
//noinspection unchecked
return (ArgumentType<Object>) arguments.getOrDefault(param.getType(), StringArgumentType.string());
}
/**
* Registers the given RootCommand into the given brigadir command node, utilizing the provided suggestion provider, executor and permission predicate.<br>
* <p>
* It recreates the root command node!
*/
LiteralCommandNode<S> register(RootCommand rootCommand,
LiteralCommandNode<S> root,
SuggestionProvider<S> suggestionProvider,
Command<S> executor,
BiPredicate<RootCommand, S> permCheckerRoot,
BiPredicate<RegisteredCommand, S> permCheckerSub) {
// recreate root to get rid of bukkits default arg
LiteralArgumentBuilder<S> rootBuilder = LiteralArgumentBuilder.<S>literal(root.getLiteral())
.requires(sender -> permCheckerRoot.test(rootCommand, sender));
RegisteredCommand defaultCommand = rootCommand.getDefaultRegisteredCommand();
if (defaultCommand != null) {
if (defaultCommand.requiredResolvers == 0) {
rootBuilder.executes(executor);
}
}
root = rootBuilder.build();
boolean isForwardingCommand = rootCommand.getDefCommand() instanceof ForwardingCommand;
if (defaultCommand != null) {
registerParameters(defaultCommand, root, suggestionProvider, executor, permCheckerSub);
}
for (Map.Entry<String, RegisteredCommand> subCommand : rootCommand.getSubCommands().entries()) {
if ((BaseCommand.isSpecialSubcommand(subCommand.getKey()) && !isForwardingCommand) || (!subCommand.getKey().equals("help") && subCommand.getValue().prefSubCommand.equals("help"))) {
// don't register stuff like __catchunknown and don't help command aliases
continue;
}
// handle sub sub commands
String commandName = subCommand.getKey();
CommandNode<S> currentParent = root;
CommandNode<S> subCommandNode;
Predicate<S> subPermChecker = sender -> permCheckerSub.test(subCommand.getValue(), sender);
if (!isForwardingCommand) {
if (commandName.contains(" ")) {
String[] split = ACFPatterns.SPACE.split(commandName);
for (int i = 0; i < split.length - 1; i++) {
if (currentParent.getChild(split[i]) == null) {
LiteralCommandNode<S> sub = LiteralArgumentBuilder.<S>literal(split[i])
.requires(subPermChecker).build();
currentParent.addChild(sub);
currentParent = sub;
} else {
currentParent = currentParent.getChild(split[i]);
}
}
commandName = split[split.length - 1];
}
subCommandNode = currentParent.getChild(commandName);
if (subCommandNode == null) {
LiteralArgumentBuilder<S> argumentBuilder = LiteralArgumentBuilder.<S>literal(commandName)
.requires(subPermChecker);
// if we have no required params, this command is actually executable
if (subCommand.getValue().requiredResolvers == 0) {
argumentBuilder.executes(executor);
}
subCommandNode = argumentBuilder.build();
}
} else {
subCommandNode = root;
}
registerParameters(subCommand.getValue(), subCommandNode, suggestionProvider, executor, permCheckerSub);
if (!isForwardingCommand) {
currentParent.addChild(subCommandNode);
}
}
return root;
}
void registerParameters(RegisteredCommand command,
CommandNode<S> node,
SuggestionProvider<S> suggestionProvider,
Command<S> executor,
BiPredicate<RegisteredCommand, S> permChecker) {
for (int i = 0; i < command.parameters.length; i++) {
CommandParameter param = command.parameters[i];
CommandParameter nextParam = param.getNextParam();
if (param.isCommandIssuer() || (param.canExecuteWithoutInput() && nextParam != null && !nextParam.canExecuteWithoutInput())) {
continue;
}
RequiredArgumentBuilder<S, Object> builder = RequiredArgumentBuilder
.<S, Object>argument(param.getName(), getArgumentTypeByClazz(param))
.suggests(suggestionProvider)
.requires(sender -> permChecker.test(command, sender));
if (nextParam == null || nextParam.canExecuteWithoutInput()) {
builder.executes(executor);
}
CommandNode<S> subSubCommand = builder.build();
node.addChild(subSubCommand);
node = subSubCommand;
}
}
}