Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a greedy "command argument" #307

Closed
willkroboth opened this issue Jul 25, 2022 · 3 comments
Closed

Add a greedy "command argument" #307

willkroboth opened this issue Jul 25, 2022 · 3 comments
Labels
enhancement New feature or request implemented for next release This has been implemented in the current dev build for the next public release

Comments

@willkroboth
Copy link
Collaborator

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.

@willkroboth willkroboth added the enhancement New feature or request label Jul 25, 2022
@JorelAli
Copy link
Owner

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.

@JorelAli
Copy link
Owner

JorelAli commented Nov 3, 2022

Merged to dev/dev for 8.6.0's release.

@JorelAli JorelAli added the implemented for next release This has been implemented in the current dev build for the next public release label Nov 3, 2022
@JorelAli
Copy link
Owner

JorelAli commented Dec 4, 2022

Implemented in release 8.6.0.

@JorelAli JorelAli closed this as completed Dec 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request implemented for next release This has been implemented in the current dev build for the next public release
Projects
None yet
Development

No branches or pull requests

2 participants