001package co.aikar.commands;
002
003import com.mojang.brigadier.Command;
004import com.mojang.brigadier.arguments.ArgumentType;
005import com.mojang.brigadier.arguments.BoolArgumentType;
006import com.mojang.brigadier.arguments.DoubleArgumentType;
007import com.mojang.brigadier.arguments.FloatArgumentType;
008import com.mojang.brigadier.arguments.IntegerArgumentType;
009import com.mojang.brigadier.arguments.StringArgumentType;
010import com.mojang.brigadier.builder.LiteralArgumentBuilder;
011import com.mojang.brigadier.builder.RequiredArgumentBuilder;
012import com.mojang.brigadier.suggestion.SuggestionProvider;
013import com.mojang.brigadier.tree.CommandNode;
014import com.mojang.brigadier.tree.LiteralCommandNode;
015
016import java.util.HashMap;
017import java.util.Map;
018import java.util.function.BiPredicate;
019import java.util.function.Predicate;
020
021/**
022 * Handles registering of commands into brigadier
023 *
024 * @param <S>
025 * @author MiniDigger
026 * @deprecated Unstable API
027 */
028@Deprecated
029@UnstableAPI
030public class ACFBrigadierManager<S> {
031
032    protected final CommandManager<?, ?, ?, ?, ?, ?> manager;
033
034    private final Map<Class<?>, ArgumentType<?>> arguments = new HashMap<>();
035
036    /**
037     * Constructs a new brigadier manager, utilizing the currently active command manager
038     *
039     * @param manager
040     */
041    ACFBrigadierManager(CommandManager<?, ?, ?, ?, ?, ?> manager) {
042        manager.verifyUnstableAPI("brigadier");
043
044        this.manager = manager;
045
046        // TODO support stuff like min max via brigadier?
047        registerArgument(String.class, StringArgumentType.word());
048        registerArgument(float.class, FloatArgumentType.floatArg());
049        registerArgument(Float.class, FloatArgumentType.floatArg());
050        registerArgument(double.class, DoubleArgumentType.doubleArg());
051        registerArgument(Double.class, DoubleArgumentType.doubleArg());
052        registerArgument(boolean.class, BoolArgumentType.bool());
053        registerArgument(Boolean.class, BoolArgumentType.bool());
054        registerArgument(int.class, IntegerArgumentType.integer());
055        registerArgument(Integer.class, IntegerArgumentType.integer());
056        // We use integer for long due to Bungee bug, plus should really be considered same on client
057        registerArgument(long.class, IntegerArgumentType.integer());
058        registerArgument(Long.class, IntegerArgumentType.integer());
059    }
060
061    <T> void registerArgument(Class<T> clazz, ArgumentType<?> type) {
062        arguments.put(clazz, type);
063    }
064
065    ArgumentType<Object> getArgumentTypeByClazz(CommandParameter param) {
066        if (param.consumesRest) {
067            //noinspection unchecked
068            return (ArgumentType<Object>) (ArgumentType<?>) StringArgumentType.greedyString();
069        }
070        //noinspection unchecked
071        return (ArgumentType<Object>) arguments.getOrDefault(param.getType(), StringArgumentType.string());
072    }
073
074    /**
075     * Registers the given RootCommand into the given brigadir command node, utilizing the provided suggestion provider, executor and permission predicate.<br>
076     * <p>
077     * It recreates the root command node!
078     */
079    LiteralCommandNode<S> register(RootCommand rootCommand,
080                                   LiteralCommandNode<S> root,
081                                   SuggestionProvider<S> suggestionProvider,
082                                   Command<S> executor,
083                                   BiPredicate<RootCommand, S> permCheckerRoot,
084                                   BiPredicate<RegisteredCommand, S> permCheckerSub) {
085        // recreate root to get rid of bukkits default arg
086        LiteralArgumentBuilder<S> rootBuilder = LiteralArgumentBuilder.<S>literal(root.getLiteral())
087                .requires(sender -> permCheckerRoot.test(rootCommand, sender));
088
089        RegisteredCommand defaultCommand = rootCommand.getDefaultRegisteredCommand();
090        if (defaultCommand != null) {
091            if (defaultCommand.requiredResolvers == 0) {
092                rootBuilder.executes(executor);
093            }
094        }
095
096        root = rootBuilder.build();
097        boolean isForwardingCommand = rootCommand.getDefCommand() instanceof ForwardingCommand;
098
099        if (defaultCommand != null) {
100            registerParameters(defaultCommand, root, suggestionProvider, executor, permCheckerSub);
101        }
102
103        for (Map.Entry<String, RegisteredCommand> subCommand : rootCommand.getSubCommands().entries()) {
104            if ((BaseCommand.isSpecialSubcommand(subCommand.getKey()) && !isForwardingCommand) || (!subCommand.getKey().equals("help") && subCommand.getValue().prefSubCommand.equals("help"))) {
105                // don't register stuff like __catchunknown and don't help command aliases
106                continue;
107            }
108
109            // handle sub sub commands
110            String commandName = subCommand.getKey();
111            CommandNode<S> currentParent = root;
112            CommandNode<S> subCommandNode;
113            Predicate<S> subPermChecker = sender -> permCheckerSub.test(subCommand.getValue(), sender);
114            if (!isForwardingCommand) {
115                if (commandName.contains(" ")) {
116                    String[] split = ACFPatterns.SPACE.split(commandName);
117                    for (int i = 0; i < split.length - 1; i++) {
118                        if (currentParent.getChild(split[i]) == null) {
119                            LiteralCommandNode<S> sub = LiteralArgumentBuilder.<S>literal(split[i])
120                                    .requires(subPermChecker).build();
121                            currentParent.addChild(sub);
122                            currentParent = sub;
123                        } else {
124                            currentParent = currentParent.getChild(split[i]);
125                        }
126                    }
127                    commandName = split[split.length - 1];
128                }
129
130                subCommandNode = currentParent.getChild(commandName);
131                if (subCommandNode == null) {
132                    LiteralArgumentBuilder<S> argumentBuilder = LiteralArgumentBuilder.<S>literal(commandName)
133                            .requires(subPermChecker);
134
135                    // if we have no required params, this command is actually executable
136                    if (subCommand.getValue().requiredResolvers == 0) {
137                        argumentBuilder.executes(executor);
138                    }
139                    subCommandNode = argumentBuilder.build();
140                }
141            } else {
142                subCommandNode = root;
143            }
144
145            registerParameters(subCommand.getValue(), subCommandNode, suggestionProvider, executor, permCheckerSub);
146
147            if (!isForwardingCommand) {
148                currentParent.addChild(subCommandNode);
149            }
150        }
151
152        return root;
153    }
154
155    void registerParameters(RegisteredCommand command,
156                            CommandNode<S> node,
157                            SuggestionProvider<S> suggestionProvider,
158                            Command<S> executor,
159                            BiPredicate<RegisteredCommand, S> permChecker) {
160        for (int i = 0; i < command.parameters.length; i++) {
161            CommandParameter param = command.parameters[i];
162            CommandParameter nextParam = param.getNextParam();
163            if (param.isCommandIssuer() || (param.canExecuteWithoutInput() && nextParam != null && !nextParam.canExecuteWithoutInput())) {
164                continue;
165            }
166            RequiredArgumentBuilder<S, Object> builder = RequiredArgumentBuilder
167                    .<S, Object>argument(param.getName(), getArgumentTypeByClazz(param))
168                    .suggests(suggestionProvider)
169                    .requires(sender -> permChecker.test(command, sender));
170
171            if (nextParam == null || nextParam.canExecuteWithoutInput()) {
172                builder.executes(executor);
173            }
174
175            CommandNode<S> subSubCommand = builder.build();
176            node.addChild(subSubCommand);
177            node = subSubCommand;
178        }
179    }
180
181}