Brigadier Suggestions
As described in The ArgumentSuggestions interface, the ArgumentSuggestions
interface has the following default method:
@FunctionalInterface
public interface ArgumentSuggestions {
/**
* Create a {@link CompletableFuture} resolving onto a brigadier {@link Suggestions} object.
* @param info The suggestions info
* @param builder The Brigadier {@link SuggestionsBuilder} object
* @return a {@link CompletableFuture} resolving onto a brigadier {@link Suggestions} object.
*
* @throws CommandSyntaxException if there is an error making suggestions
*/
CompletableFuture<Suggestions> suggest(SuggestionInfo info, SuggestionsBuilder builder)
throws CommandSyntaxException;
}
This allows you to use Brigadier's SuggestionsBuilder
and Suggestions
classes to create more powerful suggestions beyond the basic capabilities of the CommandAPI.
In order to use this, you will need the Brigadier dependency, which you can find under the Brigadier installation instructions.
Example - Using a Minecraft command as an argument
Courtesy of 469512345, the following example shows how using Brigadier's suggestions and parser can be combined with the CommandAPI to create an argument which suggests valid Minecraft commands. This could be used for example as a sudo
command, to run a command as another player.
For this command, we'll use a GreedyStringArgument
because that allows users to enter any combination of characters (which therefore, allows users to enter any command). First, we start by defining the suggestions that we'll use for the GreedyStringArgument
. We'll use the ArgumentSuggestions
functional interface described above:
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()
);
});
};
There's a lot to unpack there, but it's generally split up into 4 key sections:
-
Finding the start of the argument. We find the start of the argument so we know where the beginning of our command suggestion is. This is done easily using
builder.getStart()
, but we also have to take into account any spaces if our command argument contains spaces. -
Parsing the command argument. We make use of Brigadier's
parse()
method to parse the argument and generate someParseResults
. -
Reporting parsing errors. This is actually an optional step, but in general it's good practice to handle exceptions stored in
ParseResults
. While Brigadier doesn't actually handle suggestion exceptions, this has been included in this example to showcase exception handling. -
Generating suggestions from parse results. We use our parse results with Brigadier's
getCompletionSuggestions()
method to generate some suggestions based on the parse results and the suggestion string range.
Now that we've declared our arguments suggestions, we can then create our simple command with the following syntax:
/commandargument <command>
We use the command suggestions declared above by using the replaceSuggestions
method in our GreedyStringArgument
, and write a simple executor which runs the command that the user provided:
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();