🇬🇧 English
🇬🇧 English
Appearance
🇬🇧 English
🇬🇧 English
Appearance
This page is written for:
1.21
This page is written for:
1.21
Creating commands can allow a mod developer to add functionality that can be used through a command. This tutorial will teach you how to register commands and the general command structure of Brigadier.
INFO
Brigadier is a command parser and dispatcher written by Mojang for Minecraft. It is a tree-based command library where you build a tree of commands and arguments.
Brigadier is open-source: https://github.com/Mojang/brigadier
Command
Interface com.mojang.brigadier.Command
is a functional interface, which runs some specific code, and throws a CommandSyntaxException
in certain cases. It has a generic type S
, which defines the type of the command source. The command source provides some context in which a command was run. In Minecraft, the command source is typically a ServerCommandSource
which can represent a server, a command block, a remote connection (RCON), a player or an entity.
The single method in Command
, run(CommandContext<S>)
takes a CommandContext<S>
as the sole parameter and returns an integer. The command context holds your command source of S
and allows you to obtain arguments, look at the parsed command nodes and see the input used in this command.
Like other functional interfaces, it is usually used as a lambda or a method reference:
Command<ServerCommandSource> command = context -> {
return 0;
};
The integer can be considered the result of the command. Typically values less than or equal to zero mean a command has failed and will do nothing. Positive values mean the command was successful and did something. Brigadier provides a constant to indicate success; Command#SINGLE_SUCCESS
.
ServerCommandSource
Do? A ServerCommandSource
provides some additional implementation-specific context when a command is run. This includes the ability to get the entity that executed the command, the world the command was run in or the server the command was run on.
You can access the command source from a command context by calling getSource()
on the CommandContext
instance.
Command<ServerCommandSource> command = context -> {
ServerCommandSource source = context.getSource();
return 0;
};
Commands are registered within the CommandRegistrationCallback
provided by the Fabric API.
INFO
For information on registering callbacks, please see the Events guide.
The event should be registered in your mod's initializer.
The callback has three parameters:
CommandDispatcher<ServerCommandSource> dispatcher
- Used to register, parse and execute commands. S
is the type of command source the command dispatcher supports.CommandRegistryAccess registryAccess
- Provides an abstraction to registries that may be passed to certain command argument methodsCommandManager.RegistrationEnvironment environment
- Identifies the type of server the commands are being registered on.In the mod initializer, we just register a simple command:
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("test_command").executes(context -> {
context.getSource().sendFeedback(() -> Text.literal("Called /test_command."), false);
return 1;
}));
});
In the sendFeedback()
method, the first parameter is the text to be sent, which is a Supplier<Text>
to avoid instantiating Text objects when not needed.
The second parameter determines whether to broadcast the feedback to other operators. Generally, if the command is to query something without actually affecting the world, such as query the current time or some player's score, it should be false
. If the command does something, such as changing the time or modifying someone's score, it should be true
.
If the command fails, instead of calling sendFeedback()
, you may directly throw any exception and the server or client will handle it appropriately.
CommandSyntaxException
is generally thrown to indicate syntax errors in commands or arguments. You can also implement your own exception.
To execute this command, you must type /test_command
, which is case-sensitive.
INFO
From this point onwards, we will be extracting the logic written within the lambda passed into .execute()
builders into individual methods. We can then pass a method reference to .execute()
. This is done for clarity.
If desired, you can also make sure a command is only registered under some specific circumstances, for example, only in the dedicated environment:
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
if (environment.dedicated) {
dispatcher.register(CommandManager.literal("dedicated_command")
.executes(FabricDocsReferenceCommands::executeDedicatedCommand));
}
});
private static int executeDedicatedCommand(CommandContext<ServerCommandSource> context) {
context.getSource().sendFeedback(() -> Text.literal("Called /dedicated_command."), false);
return 1;
}
Let's say you have a command that you only want operators to be able to execute. This is where the requires()
method comes into play. The requires()
method has one argument of a Predicate<S>
which will supply a ServerCommandSource
to test with and determine if the CommandSource
can execute the command.
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("required_command")
.requires(source -> source.hasPermissionLevel(1))
.executes(FabricDocsReferenceCommands::executeRequiredCommand));
});
private static int executeRequiredCommand(CommandContext<ServerCommandSource> context) {
context.getSource().sendFeedback(() -> Text.literal("Called /required_command."), false);
return 1;
}
This command will only execute if the source of the command is a level 2 operator at a minimum, including command blocks. Otherwise, the command is not registered.
This has the side effect of not showing this command in tab completion to anyone who is not a level 2 operator. This is also why you cannot tab-complete most commands when you do not enable cheats.
To add a sub command, you register the first literal node of the command normally. To have a sub command, you have to append the next literal node to the existing node.
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("command_one")
.then(CommandManager.literal("sub_command_one").executes(FabricDocsReferenceCommands::executeSubCommandOne)));
});
private static int executeSubCommandOne(CommandContext<ServerCommandSource> context) {
context.getSource().sendFeedback(() -> Text.literal("Called /command sub_command_one."), false);
return 1;
}
Similar to arguments, sub command nodes can also be set optional. In the following case, both /command_two
and /command_two sub_command_two
will be valid.
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("command_two")
.executes(FabricDocsReferenceCommands::executeCommandTwo)
.then(CommandManager.literal("sub_command_two").executes(FabricDocsReferenceCommands::executeSubCommandTwo)));
});
private static int executeCommandTwo(CommandContext<ServerCommandSource> context) {
context.getSource().sendFeedback(() -> Text.literal("Called /command_two."), false);
return 1;
}
private static int executeSubCommandTwo(CommandContext<ServerCommandSource> context) {
context.getSource().sendFeedback(() -> Text.literal("Called /sub_command_two."), false);
return 1;
}
Fabric API has a ClientCommandManager
in net.fabricmc.fabric.api.client.command.v2
package that can be used to register client-side commands. The code should exist only in client-side code.
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> {
dispatcher.register(ClientCommandManager.literal("clienttater").executes(context -> {
context.getSource().sendFeedback(Text.literal("Called /clienttater with no arguments."));
return 1;
}));
});
Command redirects - also known as aliases - are a way to redirect the functionality of one command to another. This is useful for when you want to change the name of a command, but still want to support the old name.
WARNING
Brigadier will only redirect command nodes with arguments. If you want to redirect a command node without arguments, provide an .executes()
builder with a reference to the same logic as outlined in the example.
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
var redirectedBy = dispatcher.register(CommandManager.literal("redirected_by").executes(FabricDocsReferenceCommands::executeRedirectedBy));
dispatcher.register(CommandManager.literal("to_redirect").executes(FabricDocsReferenceCommands::executeRedirectedBy).redirect(redirectedBy));
});
private static int executeRedirectedBy(CommandContext<ServerCommandSource> context) {
context.getSource().sendFeedback(() -> Text.literal("Called /redirected_by."), false);
return 1;
}
Catch or throw a CommandSyntaxException
- CommandSyntaxException
is not a RuntimeException
. If you throw it, that should be in methods that throw CommandSyntaxException
in method signatures, or it should be caught. Brigadier will handle the checked exceptions and forward the proper error message in the game for you.
Issues with generics - You may have an issue with generics once in a while. If you are registering server commands (which are most of the case), make sure you are using CommandManager.literal
or CommandManager.argument
instead of LiteralArgumentBuilder.literal
or RequiredArgumentBuilder.argument
.
Check sendFeedback()
method - You may have forgotten to provide a boolean as the second argument. Also remember that, since Minecraft 1.20, the first parameter is Supplier<Text>
instead of Text
.
A Command should return an integer - When registering commands, the executes()
method accepts a Command
object, which is usually a lambda. The lambda should return an integer, instead of other types.
WARNING
You can do this, but it is not recommended. You would get the CommandManager
from the server and add anything commands you wish to its CommandDispatcher
.
After that, you need to send the command tree to every player again using CommandManager.sendCommandTree(ServerPlayerEntity)
.
This is required because the client locally caches the command tree it receives during login (or when operator packets are sent) for local completions-rich error messages.
WARNING
You can also do this, however, it is much less stable than registering commands at runtime and could cause unwanted side effects.
To keep things simple, you need to use reflection on Brigadier and remove nodes. After this, you need to send the command tree to every player again using sendCommandTree(ServerPlayerEntity)
.
If you don't send the updated command tree, the client may think a command still exists, even though the server will fail execution.