tutorial:commands
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
tutorial:commands [2023/08/17 00:27] – Added newlines in ServerCommandSource usage treeway7 | tutorial:commands [2025/03/20 23:37] (current) – added a missing "be" dfnkt | ||
---|---|---|---|
Line 3: | Line 3: | ||
====== Creating Commands ====== | ====== Creating Commands ====== | ||
- | Creating commands can allow a mod developer to add functionality that can used through a command. This tutorial will teach you how to register commands, and the general command structure of Brigadier. | + | 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. |
- | + | ||
- | Note: All code written here was written for 1.19.2. For old versions, some versions and mappings may differ. | + | |
===== What is Brigadier? ===== | ===== What is Brigadier? ===== | ||
Line 15: | Line 13: | ||
===== The '' | ===== The '' | ||
- | In Minecraft, | + | In Minecraft, '' |
The single method in '' | The single method in '' | ||
Line 27: | Line 25: | ||
</ | </ | ||
- | In vanilla Minecraft, they are usually used as method references, such as static methods named '' | + | The integer can be considered the result of the command. Typically negative values mean a command has failed and will do nothing. A result of '' |
- | + | ||
- | The integer can be considered the result of the command. In Minecraft, the result can correspond to the power of a redstone comparator feeding from a command block or the value that will be passed the chain command block the command block is facing. Typically negative values mean a command has failed and will do nothing. A result of '' | + | |
Line 40: | Line 36: | ||
final ServerCommandSource source = ctx.getSource(); | final ServerCommandSource source = ctx.getSource(); | ||
- | // Unchecked, may be null if the sender was the console. | + | // Unchecked, may be null if the sender was the console |
- | final Entity sender = source.getEntity(); | + | final @Nullable |
- | // Will end the command | + | // Will throw an exception |
// The result of this could contain a player. Also will send feedback telling the sender of the command that they must be an entity. | // The result of this could contain a player. Also will send feedback telling the sender of the command that they must be an entity. | ||
// This method will require your methods to throw a CommandSyntaxException. | // This method will require your methods to throw a CommandSyntaxException. | ||
// The entity options in ServerCommandSource could return a CommandBlock entity, any living entity or a player. | // The entity options in ServerCommandSource could return a CommandBlock entity, any living entity or a player. | ||
- | final Entity sender2 = source.getEntityOrThrow(); | + | final @NotNull |
- | // Will end the command if the source | + | // null if the executor of the command |
+ | final @Nullable ServerPlayerEntity player = source.getPlayer(): | ||
+ | |||
+ | // Will throw an exception | ||
// Also will send feedback telling the sender of the command that they must be a player. | // Also will send feedback telling the sender of the command that they must be a player. | ||
// This method will require your methods to throw a CommandSyntaxException. | // This method will require your methods to throw a CommandSyntaxException. | ||
- | final ServerPlayerEntity player = source.getPlayer(); | + | final @NotNull |
- | // Get' | + | // Gets the sender' |
// This could be the location of the entity/ | // This could be the location of the entity/ | ||
- | source.getPosition(); | + | final Vec3d position = source.getPosition(); |
- | // Get' | + | // Gets the world the sender is within. The console' |
- | source.getWorld(); | + | final ServerWorld world = source.getWorld(); |
- | // Get' | + | // Gets the sender' |
- | source.getRotation(); | + | final Vec2f rotation = source.getRotation(); |
// Access to the instance of the MinecraftServer this command was ran on. | // Access to the instance of the MinecraftServer this command was ran on. | ||
- | source.getServer(); | + | final MinecraftServer server = source.getServer(); |
// The name of the command source. This could be the name of the entity, player, | // The name of the command source. This could be the name of the entity, player, | ||
// the name of a CommandBlock that has been renamed before being placed down, or in the case of the Console, " | // the name of a CommandBlock that has been renamed before being placed down, or in the case of the Console, " | ||
- | source.getName(); | + | final String name = source.getName(); |
// Returns true if the source of the command has a certain permission level. | // Returns true if the source of the command has a certain permission level. | ||
// This is based on the operator status of the sender. | // This is based on the operator status of the sender. | ||
// (On an integrated server, the player must have cheats enabled to execute these commands.) | // (On an integrated server, the player must have cheats enabled to execute these commands.) | ||
- | source.hasPermissionLevel(int level); | + | final boolean b = source.hasPermissionLevel(int level); |
</ | </ | ||
Line 97: | Line 96: | ||
.executes(context -> { | .executes(context -> { | ||
// For versions below 1.19, replace " | // For versions below 1.19, replace " | ||
- | | + | // For versions |
- | | + | context.getSource().sendFeedback(() -> Text.literal(" |
- | context.getSource().sendMessage(() -> Text.literal(" | + | |
return 1; | return 1; | ||
Line 107: | Line 105: | ||
</ | </ | ||
- | '' | + | **Please ensure you import the correct static method.** The method '' |
+ | |||
+ | In the '' | ||
+ | |||
+ | If the command fails, instead of calling '' | ||
To execute this command, you must type ''/ | To execute this command, you must type ''/ | ||
+ | ===== Registration environment ===== | ||
If desired, you can also make sure a command is only registered under some specific circumstances, | If desired, you can also make sure a command is only registered under some specific circumstances, | ||
- | <code java [enable_line_numbers=" | + | <yarncode |
public class ExampleCommandMod implements ModInitializer { | public class ExampleCommandMod implements ModInitializer { | ||
@Override | @Override | ||
public void onInitialize() { | public void onInitialize() { | ||
CommandRegistrationCallback.EVENT.register((dispatcher, | CommandRegistrationCallback.EVENT.register((dispatcher, | ||
- | if (environment.dedicated) { | + | if (environment.field_25423) { |
...; | ...; | ||
} | } | ||
Line 124: | Line 127: | ||
} | } | ||
} | } | ||
- | </code> | + | </yarncode> |
===== Static Imports ===== | ===== Static Imports ===== | ||
Line 149: | Line 152: | ||
Minecraft' | Minecraft' | ||
- | ===== Requirements ===== | + | ===== Add Requirements ===== |
Let's say you have a command that you only want operators to be able to execute. This is where the '' | Let's say you have a command that you only want operators to be able to execute. This is where the '' | ||
Line 157: | Line 160: | ||
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
dispatcher.register(literal(" | dispatcher.register(literal(" | ||
- | .requires(source -> source.hasPermissionLevel(4)) | + | |
.executes(ctx -> { | .executes(ctx -> { | ||
- | ctx.getSource().sendFeedback(Text.literal(" | + | ctx.getSource().sendFeedback(() -> Text.literal(" |
return 1; | return 1; | ||
}); | }); | ||
</ | </ | ||
- | This command will only execute if the source of the command is a level 4 operator at minimum. Otherwise, the command is not registered. Also this has the side effect of not showing this command in tab completion to anyone who is not a level 4 operator. This is also why you cannot tab-complete most commands when you did not enable cheating. | + | This command will only execute if the source of the command is a level 2 operator at minimum, // |
+ | |||
+ | To create commands that only level 4 operators (//not including// command blocks) can execute, use '' | ||
===== Arguments ===== | ===== Arguments ===== | ||
- | Arguments in Brigadier both parse and error check any inputted arguments. | + | Arguments |
- | Minecraft creates some special | + | |
- | **TODO:** Go into more detail on how to use arguments | + | In this case, we add one integer argument, and calculate the square of the integer. |
- | ===== A sub command ===== | + | <code java> |
+ | dispatcher.register(literal(" | ||
+ | .then(argument(" | ||
+ | .executes(context -> { | ||
+ | final int value = IntegerArgumentType.getInteger(context, | ||
+ | final int result | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
+ | return result; | ||
+ | }))); | ||
+ | </ | ||
- | To add a sub command, you register | + | In this case, after the word ''/ |
- | <code java [enable_line_numbers="true"]> | + | Note: for simplicity, '' |
- | dispatcher.register(literal(" | + | |
+ | Then we add an optional second argument: | ||
+ | <code java> | ||
+ | dispatcher.register(literal(" | ||
+ | .then(argument(" | ||
+ | .executes(context -> { | ||
+ | final int value = IntegerArgumentType.getInteger(context, | ||
+ | final int result = value * value; | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
+ | | ||
+ | }) | ||
+ | | ||
+ | .executes(context -> { | ||
+ | final int value = IntegerArgumentType.getInteger(context, | ||
+ | final int value2 = IntegerArgumentType.getInteger(context, | ||
+ | final int result = value * value2; | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
+ | return result; | ||
+ | })))); | ||
</ | </ | ||
- | In order to have a sub command, | + | Now you can type one or two integers. If you give one integer, that square of integer will be calculated. If you provide two integers, their product will be calculated. You may find it unnecessary |
- | This creates the command '' | + | <code java> |
+ | public class ExampleMod implements ModInitializer { | ||
+ | @Override | ||
+ | public void onInitialize() { | ||
+ | CommandRegistrationCallback.EVENT.register((dispatcher, | ||
+ | .then(argument(" | ||
+ | .executes(context -> executeMultiply(IntegerArgumentType.getInteger(context, | ||
+ | .then(argument(" | ||
+ | .executes(context -> executeMultiply(IntegerArgumentType.getInteger(context, | ||
+ | } | ||
- | <code java [enable_line_numbers=" | + | private static int executeMultiply(int value, int value2, CommandContext< |
- | dispatcher.register(literal(" | + | final int result = value * value2; |
- | | + | context.getSource().sendFeedback(() -> Text.literal(" |
- | ); | + | return result; |
+ | } | ||
+ | } | ||
</ | </ | ||
+ | ===== A sub command ===== | ||
- | It is advised to indent your code as you add nodes to the command. Usually | + | To add a sub command, you register |
- | **So let's try running the command** | + | < |
+ | dispatcher.register(literal(" | ||
+ | </ | ||
- | Most likely if you typed ''/ | + | In order to have a sub command, one needs to append |
- | <code java [enable_line_numbers=" | + | This creates the command '' |
+ | |||
+ | <code java [enable_line_numbers=" | ||
dispatcher.register(literal(" | dispatcher.register(literal(" | ||
.then(literal(" | .then(literal(" | ||
.executes(context -> { | .executes(context -> { | ||
// For versions below 1.19, use '' | // For versions below 1.19, use '' | ||
- | context.getSource().sendMessage(Text.literal(" | + | |
+ | | ||
return 1; | return 1; | ||
Line 210: | Line 258: | ||
</ | </ | ||
+ | Similar to arguments, sub command nodes can also be set optional. In the following case, both ''/ | ||
+ | <code java [enable_line_numbers=" | ||
+ | dispatcher.register(literal(" | ||
+ | .executes(context -> { | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
+ | return 1; | ||
+ | }) | ||
+ | .then(literal(" | ||
+ | .executes(context -> { | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
+ | return 1; | ||
+ | }) | ||
+ | ) | ||
+ | ); | ||
+ | </ | ||
====== Advanced concepts ====== | ====== Advanced concepts ====== | ||
Line 224: | Line 286: | ||
====== FAQ ====== | ====== FAQ ====== | ||
- | ===== Why does my command | + | ===== Why does my code not compile ===== |
- | There are two immediate possibilities for why this could occur. | + | There are several |
- | ==== Catch or throw a CommandSyntaxException | + | * **Catch or throw a CommandSyntaxException:** '' |
- | + | * **Issues with generics:** You may have an issue with generics | |
- | The solution to this issue is to make the '' | + | * **Check '' |
- | + | * **'' | |
- | ==== Issues with generics | + | |
- | + | ||
- | You may have an issue with generic types once in a while. | + | |
===== Can I register client side commands? ===== | ===== Can I register client side commands? ===== | ||
- | Fabric has a ClientCommandManager that can be used to register client side commands. | + | Fabric has a '' |
- | ===== Dark Arts ===== | + | <code java> |
+ | ClientCommandRegistrationCallback.EVENT.register((dispatcher, | ||
+ | .executes(context -> { | ||
+ | context.getSource().sendFeedback(Text.literal(" | ||
+ | return 1; | ||
+ | } | ||
+ | ))); | ||
+ | </ | ||
- | A few things we don't recommend, but are possible. | + | If you need to open a screen in the client command execution, instead of directly calling '' |
- | ==== Can I register commands in runtime? ==== | + | ===== Can I register commands in runtime? |
You can do this but it is not recommended. You would get the '' | You can do this but it is not recommended. You would get the '' | ||
Line 250: | Line 316: | ||
After that you need to send the command tree to every player again using '' | After that you need to send the command tree to every player again using '' | ||
- | ==== Can I unregister commands in runtime? ==== | + | ===== Can I unregister commands in runtime? |
You can also do this, however it is much less stable than registering commands and could cause unwanted side effects. To keep things simple, you need to use reflection on brigadier and remove the nodes. After this, you need to send the command tree to every player again using '' | You can also do this, however it is much less stable than registering commands and could cause unwanted side effects. To keep things simple, you need to use reflection on brigadier and remove the nodes. After this, you need to send the command tree to every player again using '' | ||
+ | ===== Can I execute command without typing in game? ===== | ||
+ | Yes! You can. Before trying the next code, take note it works on Fabric 0.91.6+1.20.2 and it was not tested in other versions. | ||
+ | |||
+ | Here is the code example | ||
+ | |||
+ | <code java> | ||
+ | private void vanillaCommandByPlayer(World world, BlockPos pos, String command) { | ||
+ | PlayerEntity player = world.getClosestPlayer(pos.getX(), | ||
+ | if (player != null) { | ||
+ | CommandManager commandManager = Objects.requireNonNull(player.getServer()).getCommandManager(); | ||
+ | ServerCommandSource commandSource = player.getServer().getCommandSource(); | ||
+ | commandManager.executeWithPrefix(commandSource, | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | First, you need a CommandManager< | ||
+ | Second, you need the ServerCommandSource. | ||
+ | Then, u can call some CommandManager public methods like commandeManader.execute. (.execute need ParseResults< | ||
+ | But commandeManader.executeWithPrefix allow you to use String. You can also put the slash (/). | ||
+ | |||
+ | So... have fun! |
tutorial/commands.1692232077.txt.gz · Last modified: 2023/08/17 00:27 by treeway7