Skip to main content

Command API

Paper's command system is built on top of Minecraft's Brigadier command system. This system is a powerful and flexible way to define commands and arguments.

Experimental

Paper's command system is still experimental and may change in the future.

Defining commands

note

This uses the LifecycleEventManager to register the command. See the Lifecycle Events page for more information.

JavaPlugin

Commands can be registered inside the onEnable method of the plugin. Commands registered here will not be available to datapack functions because functions are loaded by the server before plugins are loaded. To make commands available to datapacks, register them via the PluginBootstrap.

public class YourPluginClass extends JavaPlugin {

@Override
public void onEnable() {
LifecycleEventManager<Plugin> manager = this.getLifecycleManager();
manager.registerEventHandler(LifecycleEvents.COMMANDS, event -> {
final Commands commands = event.registrar();
commands.register(
Commands.literal("new-command")
.executes(ctx -> {
ctx.getSource().getSender().sendPlainMessage("some message");
return Command.SINGLE_SUCCESS;
})
.build(),
"some bukkit help description string",
List.of("an-alias")
);
});
}
}

PluginBootstrap

note

If any plugin on the server registers a handler for the LifecycleEvents.COMMANDS event, the server will disable Bukkit's plugin reload functionality. This is a limitation with how the plugin reload system works in conjunction with the plugin bootstrapper system. The /reload command should never be used regardless.

Commands are registered in the same way in a plugin's bootstrapper. The benefit of registering commands here is that they will be available to datapack functions because the command registration happens early enough.

public class YourPluginBootstrap implements PluginBootstrap {

@Override
public void bootstrap(BootstrapContext context) {
LifecycleEventManager<BootstrapContext> manager = context.getLifecycleManager();
// Same as for JavaPlugin
}
}

BasicCommand

Paper provides a simple BasicCommand interface which can be used to define a command in a similar way to Bukkit's CommandExecutor. There are methods on the Commands interface to register such commands.

Example registration and implementation of a BasicCommand:

LifecycleEventManager<BootstrapContext> manager = context.getLifecycleManager();
manager.registerEventHandler(LifecycleEvents.COMMANDS, event -> {
final Commands commands = event.registrar();
commands.register("fun", "some help description string", new FunCommand());
});
class FunCommand implements BasicCommand {

@Override
public void execute(@NotNull CommandSourceStack stack, @NotNull String[] args) {
if (args.length == 1 && args[0].equalsIgnoreCase("start")) {
stack.getSender().sendRichMessage("<rainbow>Fun activated!");
}
}
}

Arguments

Built-in arguments

Vanilla has lots of built-in arguments that the client also supports. These can provide better syntax and error-checking before even executing the commands. ArgumentTypes has all the argument types available to the API.

For now, you can find specific examples of arguments, among other things, on the Fabric Wiki.

Custom arguments

Custom arguments can be created by implementing the CustomArgumentType interface. See the Javadocs for more information on how they work.

Command suggestions

Custom command suggestions can be supplied by either overriding the listSuggestions method in CustomArgumentType, or using the suggests(SuggestionProvider) method to define a SuggestionProvider for the argument.

An example suggestion provider might look like this:

Commands.argument("count", IntegerArgumentType.integer())
.suggests((ctx, builder) -> {
builder.suggest(1);
builder.suggest(10);
return builder.buildFuture();
})

Suggestions can also be contextual since they have access to the CommandContext.

note

To access other arguments when building suggestions, make sure the correct CommandContext is obtained with CommandContext.getLastChild().

An example contextual suggestion provider:

Commands.argument("min", IntegerArgumentType.integer())
.then(Commands.argument("max", IntegerArgumentType.integer())
.suggests((ctx, builder) -> {
int min = ctx.getLastChild().getArgument("min", Integer.class);
builder.suggest(min + 1);
return builder.buildFuture();
}))

Lifecycle

Commands are not just registered once at the start, but anytime a reload happens. This can be either via Bukkit's reload command (which should never be used and may not always be available) or Minecraft's /reload command (which can be used), the commands are re-registered by having the event handlers called again.