JorelAli / CommandAPI

A Bukkit/Spigot API for the command UI introduced in Minecraft 1.13

Home Page:https://commandapi.jorel.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add a greedy "command argument"

willkroboth opened this issue · comments

Description

It is currently possible to import Brigadier and create a GreedyStringArgument that lets users input a Minecraft command as an argument like so:

From https://commandapi.jorel.dev/8.4.0/brigadiersuggestions.html

ArgumentSuggestions commandSuggestions = (info, builder) -> {
    // The current argument, which is a full command
    String arg = info.currentArg();

    // Identify the position of the current argument
    int start;
    if(arg.contains(" ")) {
        // Current argument contains spaces - it starts after the last space and after the start of this argument.
        start = builder.getStart() + arg.lastIndexOf(' ') + 1;
    } else {
        // Input starts at the start of this argument
        start = builder.getStart();
    }
    
    // Parse command using brigadier
    ParseResults<?> parseResults = Brigadier.getCommandDispatcher()
        .parse(info.currentArg(), Brigadier.getBrigadierSourceFromCommandSender(info.sender()));
    
    // Intercept any parsing errors indicating an invalid command
    for(CommandSyntaxException exception : parseResults.getExceptions().values()) {
        // Raise the error, with the cursor offset to line up with the argument
        throw new CommandSyntaxException(exception.getType(), exception.getRawMessage(), exception.getInput(), exception.getCursor() + start);
    }

    return Brigadier
        .getCommandDispatcher()
        .getCompletionSuggestions(parseResults)
        .thenApply((suggestionsObject) -> {
            // Brigadier's suggestions
            Suggestions suggestions = (Suggestions) suggestionsObject;

            return new Suggestions(
                // Offset the index range of the suggestions by the start of the current argument
                new StringRange(start, start + suggestions.getRange().getLength()),
                // Copy the suggestions
                suggestions.getList()
            );
        });
};

new CommandAPICommand("commandargument")
    .withArguments(new GreedyStringArgument("command").replaceSuggestions(commandSuggestions))
    .executes((sender, args) -> {
        // Run the command using Bukkit.dispatchCommand()
        Bukkit.dispatchCommand(sender, (String) args[0]);
    }).register();

Similar to how the ListArgument is a GreedyStringArgument that automatically applies a special ArgumentSuggesstions rule (#275), it would be useful if there was a CommandArgument that implemented this special ArgumentSuggestions for you. This would let users create commands like the vanilla /execute run [another command] without having to import Brigadier or figuring out the complicated ArgumentSuggestions.

Expected code

The same example from before, but using the CommandArgument (much simpler)

new CommandAPICommand("commandargument")
                .withArguments(new CommandArgument("command"))
                .executes((sender, args) -> {
                    // Run the command using Bukkit.dispatchCommand()
                    Bukkit.dispatchCommand(sender, (String) args[0]);
                }).register();

A sudo command for running a command as another player

new CommandAPICommand("sudo")
                .withArguments(
                        new PlayerArgument("target"),
                        new CommandArgument("command")
                ).executes(
                        (sender, args) -> {
                            Player target = (Player) args[0];
                            String command = (String) args[1];
                            Bukkit.dispatchCommand(target, command);
                        }
                ).register();

Extra details

I don't know if the Brigadier methods used already do this, but it would be nice if the CommandArgument could also receive plugin commands. Maybe this can only work after converting the plugin commands using the CommandAPI.

The Brigadier methods only use Minecraft-registered commands. It may be possible to use the TabCompleteEvent to generate plugin command completions and shove them into the argument if tab completion produces any results.

Merged to dev/dev for 8.6.0's release.

Implemented in release 8.6.0.