tutorial:commands
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision | ||
tutorial:commands [2019/07/29 19:19] – started fixing english errors but it's way too long lmao fudge | tutorial:commands [2023/11/18 11:21] – solidblock | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | Licensing: The code in this article is licensed under the " | ||
+ | |||
====== Creating Commands ====== | ====== Creating Commands ====== | ||
- | Creating commands can allow a mod developer to add functionality that a player | + | 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 |
- | This tutorial will teach you how to register commands, and the command structure of Brigadier | + | |
- | ===== Registering Commands | + | ===== What is Brigadier? |
- | If you just want to see how to register | + | Brigadier is a command parser & dispatcher written by Mojang for use in Minecraft. Brigadier is a tree based command library where you build a tree of arguments and commands. |
- | Registering commands is done through '' | + | The source code for brigadier can be found here: https:// |
- | The '' | + | ===== The '' |
- | The dedicated flag if set to true will tell Fabric to only register the command on a '' | + | In Minecraft, '' |
- | Below are a few examples | + | The single method in '' |
- | <code java> | + | Like other functional interfaces, it is usually used as a lambda or a method reference: |
- | CommandRegistry.INSTANCE.register(false, dispatcher -> TutorialCommands.register(dispatcher)); | + | |
- | + | ||
- | CommandRegistry.INSTANCE.register(false, | + | |
- | TutorialCommand.register(dispatcher); | + | |
- | TutorialHelpCommand.register(dispatcher); | + | |
- | }); | + | |
- | + | ||
- | CommandRegistry.INSTANCE.register(true, | + | |
- | dispatcher.register(LiteralArgumentBuilder.literal(" | + | |
- | }); | + | |
- | </ | + | |
- | + | ||
- | ==== A very basic command ==== | + | |
- | + | ||
- | Wait isn't this the exact same command from the Brigadier tutorial? Well yes it is but it is here to help explain the structure of a command. | + | |
<code java> | <code java> | ||
- | // The root of the command. This must be a literal argument. | + | Command< |
- | dispatcher.register(LiteralArgumentBuilder.literal(" | + | return |
- | // Then add an argument named bar that is an integer | + | }; |
- | .then(RequiredArgumentBuilder.argument(" | + | |
- | // The command to be executed if the command " | + | |
- | .executes(ctx | + | |
- | System.out.println(" | + | |
- | // Return a result. -1 is failure, 0 is a pass and 1 is success. | + | |
- | return 1; | + | |
- | })) | + | |
- | | + | |
- | .executes(ctx -> { | + | |
- | System.out.println(" | + | |
- | | + | |
- | }) | + | |
- | ); | + | |
</ | </ | ||
- | The main process registers the command " | + | The integer can be considered |
- | Since the root node must be literal, The sender must enter the exact same sequence | + | |
- | ===== Brigadier Explained ===== | ||
- | Brigadier starts with the '' | + | ==== What can the ServerCommandSource do? ==== |
- | The trunk of the tree is the CommandDispatcher. | + | |
- | The register(LiteralArgumentBuilder) methods specify the beginning of branches with the following then methods specifying the shape of length of the branches. | + | |
- | The executes blocks | + | |
- | The execute blocks specify the command to be ran. As Brigadier's Command is a FunctionalInterface you can use lambdas | + | A '' |
- | ==== CommandContexts ==== | + | <code java [enable_line_numbers=" |
+ | // Get the source. This will always work. | ||
+ | final ServerCommandSource source | ||
- | When a command is ran, Brigadier provides a CommandContext to the command | + | // Unchecked, may be null if the sender was the console or the command |
- | The CommandContext contains all arguments and other objects such as the inputted String and the '' | + | final @Nullable Entity sender = source.getEntity(); |
- | ==== Arguments ==== | + | // Will throw an exception if the executor of the command was not 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. | ||
+ | // The entity options in ServerCommandSource could return a CommandBlock entity, any living entity or a player. | ||
+ | final @NotNull Entity sender2 | ||
- | The arguments in Brigadier both parse and error check any inputted arguments. | + | // null if the executor of the command is not a player. |
- | Minecraft creates some special arguments for it's own use such as the '' | + | final @Nullable ServerPlayerEntity |
- | You could do the long method | + | // Will throw an exception if the executor |
- | This also works for getting arguments, which shortens the already long '' | + | // Also will send feedback telling |
- | This also works for Minecraft' | + | // This method will require your methods |
+ | final @NotNull ServerPlayerEntity player = source.getPlayerOrThrow(); | ||
- | And your imports would look something like this: | + | // Gets the sender' |
- | <code java> | + | // This could be the location of the entity/ |
- | import static com.mojang.brigadier.arguments.StringArgumentType.getString; | + | final Vec3d position = source.getPosition(); |
- | import static com.mojang.brigadier.arguments.StringArgumentType.word; | + | |
- | import static net.minecraft.server.command.CommandManager.literal; | + | |
- | import static net.minecraft.server.command.CommandManager.argument; | + | |
- | import static net.minecraft.server.command.CommandManager.*; // Import everything | + | |
- | </ | + | |
- | Brigadier's default | + | // Gets the world the sender is within. The console' |
+ | final ServerWorld world = source.getWorld(); | ||
- | Minecraft' | + | // Gets the sender' |
+ | final Vec2f rotation = source.getRotation(); | ||
- | ==== Suggestions ==== | + | // Access to the instance of the MinecraftServer this command was ran on. |
+ | final MinecraftServer server | ||
- | Suggestions can be provided to the client to recommend what to input into the command. | + | // The name of the command |
- | < | + | // the name of a CommandBlock that has been renamed before being placed down, or in the case of the Console, " |
- | SUMMONABLE_ENTITIES | + | final String name = source.getName(); |
- | AVAILIBLE_SOUNDS | + | |
- | ALL_RECIPES | + | |
- | ASK_SERVER | + | |
- | </code> | + | |
- | Loot tables specify their own SuggestionProvider inside LootCommand for example. | + | // Returns true if the source of the command has a certain permission level. |
- | + | // This is based on the operator status of the sender. | |
- | The example below is a dynamically changing SuggestionProvider that lists several words for a StringArgumentType to demonstrate how it works: | + | // (On an integrated server, the player must have cheats enabled |
- | <code java> | + | final boolean b = source.hasPermissionLevel(int level); |
- | public static SuggestionProvider< | + | |
- | return (ctx, builder) -> getSuggestionsBuilder(builder, | + | |
- | } | + | |
- | + | ||
- | private static CompletableFuture< | + | |
- | String remaining = builder.getRemaining().toLowerCase(Locale.ROOT); | + | |
- | + | ||
- | | + | |
- | return Suggestions.empty(); // No suggestions | + | |
- | } | + | |
- | + | ||
- | for (String str : list) { // Iterate through | + | |
- | if (str.toLowerCase(Locale.ROOT).startsWith(remaining)) { | + | |
- | | + | |
- | } | + | |
- | } | + | |
- | return builder.buildFuture(); // Create the CompletableFuture containing all the suggestions | + | |
- | } | + | |
</ | </ | ||
- | The SuggestionProvider is a FunctionalInterface that returns a CompletableFuture containing a list of suggestions. These suggestions are given to client as a command | + | ===== Register |
- | Though remember these are suggestions. The inputted command may not contain an argument you suggested so you still have to parse check inside | + | Commands |
- | To use the suggestion you would append it right after the argument you want to recommend arguments for. This can be any argument | + | The event should |
+ | To simplify the code, it is highly recommended to '' | ||
<code java> | <code java> | ||
- | argument(argumentName, | + | import static net.minecraft.server.command.CommandManager.*; |
- | .suggests(CompletionProviders.suggestedStrings()) | + | |
- | | + | |
</ | </ | ||
- | ==== Requires ==== | + | In the mod initializer, |
- | Lets say you have a command you only want operators to be able to execute. This is where the '' | + | <code java [enable_line_numbers=" |
+ | public class ExampleMod implements ModInitializer { | ||
+ | @Override | ||
+ | public void onInitialize() { | ||
+ | CommandRegistrationCallback.EVENT.register((dispatcher, | ||
+ | .executes(context -> { | ||
+ | // For versions below 1.19, replace " | ||
+ | // For versions below 1.20, remode "() ->" directly. | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
- | For example this may look like the following: | + | return 1; |
- | + | }))); | |
- | <code java> | + | } |
- | dispatcher.register(literal(" | + | } |
- | .requires(source -> source.hasPermissionLevel(4)) | + | |
- | .executes(ctx -> { | + | |
- | ctx.getSource().sendFeedback(new LiteralText(" | + | |
- | return 1; | + | |
- | }); | + | |
</ | </ | ||
- | This command will only execute if the Source of the command | + | **Please ensure you import |
- | ==== Exceptions ==== | + | In the '' |
- | Brigadier supports | + | If the command |
- | All the exceptions from Brigadier are based on the CommandSyntaxException. The two main types of exceptions Brigadier provides are Dynamic and Simple exception types, of which you must '' | + | To execute this command, you must type '' |
- | Below is a coin flip command | + | |
- | <code java> | + | ===== Registration environment ===== |
- | dispatcher.register(CommandManager.literal(" | + | If desired, you can also make sure a command is only registered under some specific circumstances, |
- | .executes(ctx -> { | + | |
- | Random random | + | |
- | + | ||
- | if(random.nextBoolean()) { // If heads succeed. | + | |
- | ctx.getSource().sendMessage(new TranslateableText(" | + | |
- | return Command.SINGLE_SUCCESS; | + | |
- | } | + | |
- | throw new SimpleCommandExceptionType(new TranslateableText(" | + | |
- | })); | + | |
- | </ | + | |
- | Though you are not just limited to a single type of exception as Brigadier also supplies Dynamic exceptions. | + | <yarncode |
- | + | public class ExampleCommandMod implements ModInitializer { | |
- | <code java> | + | |
- | DynamicCommandExceptionType used_name | + | |
- | return new LiteralText("The name: " | + | |
- | }); | + | |
- | </code> | + | ...; |
- | + | } | |
- | There are more Dynamic exception types which each take a different amount of arguments into account ('' | + | }); |
- | You should remember that the Dynamic exceptions takes an object as an argument so you may have to cast the argument for your use. | + | } |
- | + | ||
- | ==== Redirects | + | |
- | + | ||
- | Redirects are Brigadier' | + | |
- | + | ||
- | <code java> | + | |
- | public static void register(CommandDispatcher< | + | |
- | | + | |
- | dispatcher.register(literal(" | + | |
- | .redirect(node)); // Alias 1, redirect to main command | + | |
- | | + | |
- | .redirect(node)); // Alias 2, redirect to main command | + | |
} | } | ||
+ | </ | ||
- | public | + | ===== Static Imports ===== |
- | return dispatcher.register(literal(" | + | In the example above, the use of static |
- | .then(argument(" | + | |
- | | + | |
- | .executes(ctx -> { | + | |
- | return execute(ctx.getSource(), | + | |
- | })))); | + | |
- | } | + | |
- | </ | + | |
- | The redirect registers a branch into the command tree, where the dispatcher | + | Below is an example |
- | + | <code java [enable_line_numbers="true"]> | |
- | Redirects also work in shortened aliases such as ''/ | + | // getString(ctx, "string") |
- | + | import static com.mojang.brigadier.arguments.StringArgumentType.getString; | |
- | <code java> | + | // word() |
- | public static void register(CommandDispatcher< | + | import static com.mojang.brigadier.arguments.StringArgumentType.word; |
- | LiteralCommandNode node = registerShortened(dispatcher); | + | // literal(" |
- | dispatcher.register(literal("mod") | + | import static net.minecraft.server.command.CommandManager.literal; |
- | .then(literal("thing") | + | // |
- | | + | import static net.minecraft.server.command.CommandManager.argument; |
- | } | + | // Import everything in the CommandManager |
- | + | import static net.minecraft.server.command.CommandManager.*; | |
- | public static LiteralCommandNode registerShortened(CommandDispatcher< | + | |
- | return dispatcher.register(literal(" | + | |
- | .then(argument(" | + | |
- | | + | |
- | } | + | |
</ | </ | ||
- | ===== ServerCommandSource | + | Note: Please be sure you use the '' |
- | What if you wanted a command that the CommandSource must be an entity to execute? The ServerCommandSource provides this option with a couple of methods | + | Brigadier' |
- | <code java> | + | Minecraft' |
- | ServerCommandSource source = ctx.getSource(); | + | |
- | // Get the source. This will always work. | + | |
- | Entity sender | + | ===== Add Requirements ===== |
- | // Unchecked, may be null if the sender was the console. | + | |
- | Entity sender2 = source.getEntityOrThrow(); | + | Let's say you have a command that you only want operators to be able to execute. This is where the '' |
- | // Will end the command if the source of the command was not an Entity. | + | |
- | // The result of this could contain | + | |
- | // This method | + | |
- | // The entity options in ServerCommandSource | + | |
- | ServerPlayerEntity player = source.getPlayer(); | + | For example this may look like the following: |
- | // Will end the command if the source of the command was not explicitly a Player. Also will send feedback telling the sender of the command that they must be a player. | + | |
- | </ | + | |
- | The ServerCommandSource also provides other information about the sender of the command. | + | <code java [enable_line_numbers=" |
- | + | dispatcher.register(literal(" | |
- | <code java> | + | .requires(source -> source.hasPermissionLevel(2)) |
- | source.getPosition(); | + | .executes(ctx -> { |
- | // Get's the sender' | + | ctx.getSource().sendFeedback(() -> Text.literal("You are an operator" |
- | + | | |
- | source.getWorld(); | + | }); |
- | // Get's the world the sender is within. The console' | + | |
- | + | ||
- | source.getRotation(); | + | |
- | // Get's the sender' | + | |
- | + | ||
- | source.getMinecraftServer(); | + | |
- | // Access to the instance of the MinecraftServer this command was ran on. | + | |
- | + | ||
- | source.getName(); | + | |
- | // 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, " | + | |
- | + | ||
- | source.hasPermissionLevel(int level); | + | |
- | // Returns true if the source of the command has a certain permission level. This is based on the operator status of the sender. (On an integrated server, the player must have cheats enabled to execute these commands) | + | |
</ | </ | ||
- | ===== Some actual examples ===== | + | This command will only execute if the source of the command is a level 2 operator at minimum, // |
- | Just a few to show: | + | To create commands that only level 4 operators (//not including// command blocks) can execute, use '' |
- | === Broadcast a message | + | ===== Arguments ===== |
- | <code java> | + | Arguments are used in most of commands. Sometimes they can be optional, which means if you do not provide |
- | public static void register(CommandDispatcher< | + | |
- | dispatcher.register(literal(" | + | |
- | .requires(source -> source.hasPermissionLevel(2)) // Must be a game master to use the command. Command will not show up in tab completion or execute to non op's or any op that is permission level 1. | + | |
- | .then(argument(" | + | |
- | .then(argument(" | + | |
- | .executes(ctx -> broadcast(ctx.getSource(), getColor(ctx, | + | |
- | } | + | |
- | public static int broadcast(ServerCommandSource source, Formatting formatting, String message) { | + | In this case, we add one integer argument, and calculate the square of the integer. |
- | Text text = new LiteralText(message).formatting(formatting); | + | |
- | source.getMinecraftServer().getPlayerManager().broadcastChatMessage(text, false); | + | <code java> |
- | return | + | dispatcher.register(literal(" |
- | } | + | |
+ | | ||
+ | final int value = IntegerArgumentType.getInteger(context, | ||
+ | final int result = value * value; | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
+ | return | ||
+ | }))); | ||
</ | </ | ||
- | === /giveMeDiamond === | + | In this case, after the word ''/ |
- | First the basic code where we register " | + | Note: for simplicity, '' |
+ | Then we add an optional second argument: | ||
<code java> | <code java> | ||
- | public static LiteralCommandNode | + | dispatcher.register(literal(" |
- | | + | .then(argument(" |
- | .executes(ctx -> giveDiamond(ctx))); | + | .executes(context -> { |
- | } | + | final int value = IntegerArgumentType.getInteger(context, " |
+ | final int result = value * value; | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
+ | | ||
+ | }) | ||
+ | .then(argument(" | ||
+ | | ||
+ | final int value = IntegerArgumentType.getInteger(context, " | ||
+ | final int value2 = IntegerArgumentType.getInteger(context, | ||
+ | final int result = value * value2; | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
+ | | ||
+ | | ||
</ | </ | ||
- | Then since we only want to give to players, we check if the CommandSource is a player. But we can use '' | + | 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 |
<code java> | <code java> | ||
- | public static int giveDiamond(CommandContext< | + | public |
- | | + | @Override |
- | + | public void onInitialize() { | |
- | PlayerEntity self = source.getPlayer(); // If not a player than the command ends | + | CommandRegistrationCallback.EVENT.register((dispatcher, |
+ | .then(argument(" | ||
+ | .executes(context -> executeMultiply(IntegerArgumentType.getInteger(context, | ||
+ | .then(argument(" | ||
+ | .executes(context -> executeMultiply(IntegerArgumentType.getInteger(context, | ||
+ | } | ||
+ | |||
+ | private | ||
+ | | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
+ | return result; | ||
+ | } | ||
+ | } | ||
</ | </ | ||
+ | ===== A sub command ===== | ||
- | Then we add to the player' | + | To add a sub command, you register the first literal node of the command normally. |
- | < | + | < |
- | | + | dispatcher.register(literal("foo")) |
- | throw new SimpleCommandExceptionType(new TranslateableText("inventory.isfull")).create(); | + | |
- | } | + | |
- | return 1; | + | |
- | } | + | |
</ | </ | ||
- | === Antioch === | + | In order to have a sub command, one needs to append the next node to the existing node. |
- | ...lobbest thou thy Holy Hand Grenade of Antioch towards thy foe. | + | |
- | who being naughty in My sight, shall snuff it. | + | |
- | Aside from the joke this command | + | This creates |
- | First create an entry into the CommandDispatcher that takes a literal | + | <code java [enable_line_numbers=" |
+ | dispatcher.register(literal(" | ||
+ | .then(literal(" | ||
+ | .executes(context -> { | ||
+ | // For versions below 1.19, use '' | ||
+ | // For versions below 1.20, use directly | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
- | <code java> | + | return 1; |
- | public static void register(CommandDispatcher< | + | }) |
- | | + | ) |
- | | + | ); |
- | .executes(ctx -> antioch(ctx.getSource(), | + | |
- | .executes(ctx -> antioch(ctx.getSource(), | + | |
- | } | + | |
</ | </ | ||
- | Then the creation | + | Similar to arguments, sub command nodes can also be set optional. In the following case, both ''/ |
- | + | <code java [enable_line_numbers=" | |
- | <code java> | + | dispatcher.register(literal(" |
- | public static int antioch(ServerCommandSource source, BlockPos blockPos) throws CommandSyntaxException { | + | |
- | + | | |
- | | + | |
- | | + | |
- | } | + | |
- | + | | |
- | TntEntity tnt = new TntEntity(source.getWorld(), blockPos.getX(), blockPos.getY(), | + | |
- | + | return 1; | |
- | | + | }) |
- | | + | ) |
- | | + | ); |
- | | + | |
- | } | + | |
</ | </ | ||
+ | ====== Advanced concepts ====== | ||
- | ===== FAQ ===== | + | Below are links to the articles about more complex concepts used in brigadier. |
- | === What else can I send feedback to the CommandSource? | + | ^ Page ^ Description |
+ | | [[command_exceptions | ||
+ | | [[command_suggestions|Suggestions]] | ||
+ | | [[command_redirects|Redirects]] | ||
+ | | [[command_argument_types|Custom Argument Types]] | ||
+ | | [[command_examples|Examples]] | Some example commands | | ||
- | You can choose between Brigadier' | + | ====== FAQ ====== |
- | === Why does my IDE complain saying that a method executed by my command needs to catch or throw a CommandSyntaxException | + | ===== Why does my code not compile ===== |
- | The solution to this is just to make the methods throw a CommandSyntaxException down the whole chain as the executes block handles the exceptions. | + | There are several immediate possibilities for why this could occur. |
- | === Can I register commands | + | * **Catch or throw a CommandSyntaxException: |
+ | * **Issues with generics:** You may have an issue with generics once in a while. If you are registering server command (which is most of the case), make sure you are using '' | ||
+ | * **Check '' | ||
+ | * **'' | ||
- | You can do this but it is not reccomended. You would get the instance of the CommandManager and add anything you wish to the CommandDispatcher within it. | + | ===== Can I register client side commands? ===== |
- | After that you will need to send the command tree to every player again using '' | + | Fabric has a '' |
- | + | ||
- | === Can I unregister commands in run time? === | + | |
- | + | ||
- | You can also do this but it is very unstable and could cause unwanted side effects. Lets just say it involves a bunch of Reflection. | + | |
- | + | ||
- | Once again you will need to send the command tree to every player again using '' | + | |
- | + | ||
- | === Can I register client side commands? === | + | |
- | + | ||
- | Well Fabric currently doesn' | + | |
- | https:// | + | |
- | + | ||
- | If you only want the command to only be visible on the integrated server like ''/ | + | |
<code java> | <code java> | ||
- | dispatcher.register(literal(" | + | ClientCommandRegistrationCallback.EVENT.register((dispatcher, |
- | // The permission level 4 on integrated server is the equivalent of having cheats enabled. | + | .executes(context |
- | .requires(source | + | context.getSource().sendFeedback(Text.literal("The command is executed in the client!" |
+ | return 1; | ||
+ | } | ||
+ | | ||
</ | </ | ||
- | === I want to access X from my mod when a command | + | If you need to open a screen in the client |
- | This is going to require a way to statically access your mod with a '' | + | ===== Can I register commands |
- | <code java> | + | You can do this but it is not recommended. You would get the '' |
- | private static Type instance; | + | |
- | static { // Static option on class initalize for seperate API class for example | + | After that you need to send the command tree to every player again using '' |
- | | + | |
- | } | + | |
- | public void onInitalize() { // If within your mod initalizer | + | ===== 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 '' |
- | public static Type getInstance() { | ||
- | return instance; | ||
- | } | ||
- | </ |
tutorial/commands.txt · Last modified: 2024/02/23 14:22 by allen1210