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 [2020/11/25 08:18] – [Advanced concepts] Reword description of arg types i509vcb | tutorial:commands [2023/11/18 10:55] – [Register a basic command] solidblock | ||
---|---|---|---|
Line 3: | Line 3: | ||
====== Creating Commands ====== | ====== Creating Commands ====== | ||
- | Creating commands can allow a mod developer to add functionality that can used through a command. | + | 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. |
- | 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.14.4. Some mappings may have changed in yarn, but all code should still be applicable. | + | 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 14: | Line 13: | ||
The source code for brigadier can be found here: https:// | The source code for brigadier can be found here: https:// | ||
- | ===== What is a command? | + | ===== The '' |
- | Brigadier requires you specify the '' | + | In Minecraft, |
- | A '' | + | |
The single method in '' | The single method in '' | ||
- | A command can be implemented in several ways as shown below: | + | Like other functional interfaces, it is usually used as a lambda or a method reference: |
- | **__As a lambda__** | + | <code java> |
- | <code java [enable_line_numbers=" | + | Command<ServerCommandSource> command = context -> { |
- | Command<Object> command = context -> { | + | |
return 0; | return 0; | ||
}; | }; | ||
</ | </ | ||
- | **__As an anonymous class__** | + | In vanilla Minecraft, they are usually used as method references, such as static methods named '' |
- | <code java [enable_line_numbers=" | + | |
- | Command< | + | |
- | @Override | + | |
- | public int run(CommandContext< | + | |
- | return 0; | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | **__Implemented as a class__** | + | 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 '' |
- | <code java [enable_line_numbers=" | + | |
- | final class XYZCommand implements Command< | + | |
- | @Override | + | |
- | public int run(CommandContext< | + | |
- | return | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | **__As a method reference__** | ||
- | <code java [enable_line_numbers=" | ||
- | void registerCommand() { | ||
- | // Ignore this for now, we will explain it next. | ||
- | dispatcher.register(CommandManager.literal(" | ||
- | .executes(this:: | ||
- | } | ||
- | private int execute(CommandContext< | + | ==== What can the ServerCommandSource do? ==== |
- | return 0; | + | |
- | } | + | |
- | </ | + | |
- | + | A '' | |
- | The '' | + | |
- | + | ||
- | The integer can be considered | + | |
- | + | ||
- | ===== A basic command ===== | + | |
- | + | ||
- | Below is a command that contains no arguments: | + | |
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | dispatcher.register(CommandManager.literal(" | + | // Get the source. This will always work. |
- | | + | final ServerCommandSource source = ctx.getSource(); |
- | return 1; | + | // Unchecked, may be null if the sender was the console or the command block. |
- | })); | + | final @Nullable Entity sender = source.getEntity(); |
- | </ | + | |
- | '' | + | // Will throw an exception if the executor of the command |
- | To execute | + | // The result of this could contain a player. Also will send feedback telling the sender of the command |
+ | // 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 = source.getEntityOrThrow(); | ||
- | ===== Static Imports ===== | + | // null if the executor |
- | Typing out '' | + | final @Nullable ServerPlayerEntity player = source.getPlayer(): |
- | Below is an example | + | // Will throw an exception if the executor |
- | <code java [enable_line_numbers=" | + | // Also will send feedback telling the sender of the command that they must be a player. |
- | // getString(ctx, | + | // This method will require your methods to throw a CommandSyntaxException. |
- | import static com.mojang.brigadier.arguments.StringArgumentType.getString; | + | final @NotNull ServerPlayerEntity player = source.getPlayerOrThrow(); |
- | // word() | + | |
- | import static com.mojang.brigadier.arguments.StringArgumentType.word; | + | |
- | // literal(" | + | |
- | import static net.minecraft.server.command.CommandManager.literal; | + | |
- | // argument(" | + | |
- | import static net.minecraft.server.command.CommandManager.argument; | + | |
- | // Import everything | + | |
- | import static net.minecraft.server.command.CommandManager.*; | + | |
- | </ | + | |
- | Note: Please be sure you use the '' | + | // Gets the sender's position as a Vec3d when the command was sent. |
+ | // This could be the location of the entity/ | ||
+ | final Vec3d position = source.getPosition(); | ||
- | Brigadier's default | + | // Gets the world the sender is within. The console' |
+ | final ServerWorld world = source.getWorld(); | ||
- | Minecraft' | + | // Gets the sender' |
- | CommandManager is in the package '' | + | final Vec2f rotation = source.getRotation(); |
- | ==== A sub command ==== | + | // Access to the instance of the MinecraftServer this command |
+ | final MinecraftServer server | ||
- | To add a sub 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, " | ||
+ | final String name = source.getName(); | ||
- | <code java [enable_line_numbers=" | + | // Returns |
- | dispatcher.register(CommandManager.literal(" | + | // This is based on the operator status of the sender. |
+ | // (On an integrated server, the player must have cheats enabled to execute these commands.) | ||
+ | final boolean b = source.hasPermissionLevel(int level); | ||
</ | </ | ||
- | In order to have a sub command, one needs to append the next node to the existing node. This is done use the '' | + | ===== Register |
- | This creates the command | + | Commands are registered by registering in '' |
- | <code java [enable_line_numbers=" | + | The event should be registered in your mod's initializer. The callback has three parameters. The '' |
- | dispatcher.register(literal(" | + | |
- | | + | To simplify the code, it is highly recommended to '' |
- | ); | + | <code java> |
+ | import static net.minecraft.server.command.CommandManager.*; | ||
</ | </ | ||
- | It is advised to indent your code as you add nodes to the command. Usually the indentation corresponds to how many nodes deep one is on the command tree. The new line also makes it visible that another node is being added. There are alternative styles to formatting | + | In the mod initializer, |
- | **So let's try running the command** | + | <code java [enable_line_numbers=" |
- | + | public class ExampleMod implements ModInitializer { | |
- | Most likely if you typed ''/ | + | @Override |
- | + | public void onInitialize() { | |
- | <code java [enable_line_numbers=" | + | CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, |
- | dispatcher.register(literal(" | + | |
- | | + | |
.executes(context -> { | .executes(context -> { | ||
- | System.out.println(" | + | // For versions below 1.19, replace " |
+ | context.getSource().sendFeedback(Text.literal(" | ||
+ | // For versions since 1.20, please use the following, which is intended to avoid creating Text objects if no feedback is needed. | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
- | | + | |
- | }) | + | }))); |
- | | + | } |
- | ); | + | } |
</ | </ | ||
- | ===== Registering | + | **Please ensure you import |
- | Registering commands | + | In the '' |
- | The event should be registered in your mod's initializer. The callback has two parameters. The '' | + | If the command fails, instead of calling |
+ | To execute this command, you must type ''/ | ||
- | <code java [enable_line_numbers=" | + | ===== Registration environment ===== |
- | public class ExampleCommandMod implements ModInitializer { | + | If desired, you can also make sure a command is only registered under some specific circumstances, |
- | | + | |
- | public void onInitialize() { | + | |
- | CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> { | + | |
- | ... | + | |
- | }); | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | Inside your lambda, method reference or whatever you have chosen, you will register your commands. | + | <yarncode |
- | + | ||
- | <code java [enable_line_numbers=" | + | |
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, |
- | | + | |
- | | + | ...; |
- | return 1; | + | |
- | })); | + | |
- | }); | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | If desired, you can make sure a command is only registered on a dedicated server by checking the '' | + | |
- | + | ||
- | + | ||
- | <code java [enable_line_numbers=" | + | |
- | public class ExampleCommandMod implements ModInitializer { | + | |
- | @Override | + | |
- | public void onInitialize() { | + | |
- | CommandRegistrationCallback.EVENT.register((dispatcher, | + | |
- | if (dedicated) { | + | |
- | TestDedicatedCommand.register(dispatcher); | + | |
} | } | ||
}); | }); | ||
} | } | ||
} | } | ||
- | </code> | + | </yarncode> |
- | And vice versa | + | ===== Static Imports ===== |
+ | In the example above, the use of static imports is used for code simplifying. For a literal this would shorten the statement to '' | ||
- | <code java [enable_line_numbers=" | + | Below is an example of some static imports: |
- | public class ExampleCommandMod implements ModInitializer { | + | <code java [enable_line_numbers=" |
- | @Override | + | // getString(ctx, " |
- | public void onInitialize() { | + | import static com.mojang.brigadier.arguments.StringArgumentType.getString; |
- | | + | // word() |
- | | + | import static com.mojang.brigadier.arguments.StringArgumentType.word; |
- | | + | // literal(" |
- | } | + | import static net.minecraft.server.command.CommandManager.literal; |
- | }); | + | // argument(" |
- | } | + | import static net.minecraft.server.command.CommandManager.argument; |
- | } | + | // Import everything in the CommandManager |
+ | import static net.minecraft.server.command.CommandManager.*; | ||
</ | </ | ||
- | ===== Arguments ===== | + | Note: Please be sure you use the '' |
- | Arguments in Brigadier | + | Brigadier' |
- | Minecraft creates some special argument types for it' | + | |
- | + | ||
- | **TODO:** Go into more detail on how to use arguments | + | |
- | + | ||
- | ====== Advanced concepts ====== | + | |
- | + | ||
- | Below are links to the articles about more complex concepts used in brigadier. | + | |
- | + | ||
- | ^ Page ^ Description | + | |
- | | [[tutorials: | + | |
- | | [[tutorials: | + | |
- | | [[tutorials: | + | |
- | | [[tutorials: | + | |
- | | [[tutorials: | + | |
- | | [[tutorials: | + | |
- | + | ||
- | **TODO:** Sections are being moved to sub categories and will be added to their respective articles as they are migrated. | + | |
- | + | ||
- | ====== FAQ ====== | + | |
- | + | ||
- | ===== Why does my command not compile ===== | + | |
- | + | ||
- | There are two immediate possibilities for why this could occur. | + | |
- | + | ||
- | ==== Catch or throw a CommandSyntaxException ==== | + | |
- | + | ||
- | The solution to this issue is to make the run or suggest methods throw a '' | + | |
- | + | ||
- | ==== Issues with generics ==== | + | |
- | + | ||
- | You may have an issue with generic types once in a while. Verify you are using '' | + | |
- | + | ||
- | ===== Can I register client side commands? ===== | + | |
- | + | ||
- | Fabric doesn' | + | |
- | There is an open pull request to fabric api which adds this. That will be documented on this page in the future. | + | |
- | + | ||
- | ===== Dark Arts ===== | + | |
- | + | ||
- | A few things we don't recommend, but are possible. | + | |
- | + | ||
- | ==== Can I register commands in runtime? ==== | + | |
- | + | ||
- | You can do this but it is not recommended. You would get the '' | + | |
- | + | ||
- | After that you need to send the command tree to every player again using '' | + | |
- | + | ||
- | ==== 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 | + | |
- | + | ||
- | ---- | + | |
- | + | ||
- | ====== Sorry for the mess ====== | + | |
- | + | ||
- | **__Currently this article is being migrated, so things may be a mess. Below is are the parts of the article that are yet to be migrated to the new format.__** | + | |
- | Licensing from below onwards is available under the "CC Attribution-Noncommercial-Share Alike 4.0 International" | + | Minecraft' |
- | ===== Requirements ===== | + | ===== Add Requirements ===== |
- | Lets say you have a command you only want operators to be able to execute. This is where the '' | + | Let' |
For example this may look like the following: | For example this may look like the following: | ||
Line 278: | Line 165: | ||
<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(new LiteralText("You are an operator", | + | ctx.getSource().sendFeedback(() -> Text.literal("You are an operator" |
- | return 1; | + | return 1; |
- | }); | + | }); |
</ | </ | ||
- | This command will only execute if the Source | + | This command will only execute if the source |
- | Nothing prevents someone from specifying calls to permissions implementations within the '' | + | To create commands that only level 4 operators (//not including// command blocks) can execute, use '' |
- | ===== Exceptions | + | ===== Arguments |
- | Brigadier supports command exceptions which can be used to end a command such as if an argument | + | Arguments are used in most of commands. Sometimes they can be optional, which means if you do not provide that argument, 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 '' | + | In this case, we add one integer argument, and calculate |
- | Below is a coin flip command to show an example of exceptions in use. | + | |
- | <code java [enable_line_numbers=" | + | <code java> |
- | dispatcher.register(CommandManager.literal(" | + | dispatcher.register(literal(" |
- | .executes(ctx -> { | + | |
- | | + | |
- | + | final int value = IntegerArgumentType.getInteger(context, " | |
- | | + | final int result = value * value; |
- | ctx.getSource().sendMessage(new TranslateableText("coin.flip.heads")) | + | |
- | return | + | return |
- | } | + | }))); |
- | + | ||
- | throw new SimpleCommandExceptionType(new TranslateableText(" | + | |
- | })); | + | |
</ | </ | ||
- | Though | + | In this case, after the word ''/ |
- | <code java [enable_line_numbers=" | + | Note: for simplicity, '' |
- | DynamicCommandExceptionType used_name = new DynamicCommandExceptionType(name -> { | + | |
- | return new LiteralText(" | + | |
- | }); | + | |
- | </ | + | |
- | There are more Dynamic exception types which each take a different amount of arguments into account ('' | + | Then we add an optional second |
- | You should remember that the Dynamic exceptions takes an object as an argument | + | <code java> |
- | + | dispatcher.register(literal(" | |
- | ===== Redirects (Aliases) ===== | + | .then(argument("value", IntegerArgumentType.integer()) |
- | + | | |
- | Redirects are Brigadier' | + | final int value = IntegerArgumentType.getInteger(context, |
- | + | final int result = value * value; | |
- | <code java [enable_line_numbers=" | + | context.getSource().sendFeedback(() -> Text.literal("%s × %s = %s".formatted(value, value, result)), false); |
- | public static void register(CommandDispatcher< | + | |
- | LiteralCommandNode node = registerMain(dispatcher); | + | }) |
- | dispatcher.register(literal(" | + | |
- | .redirect(node)); // Alias 1, redirect to main command | + | .executes(context |
- | dispatcher.register(literal("w") | + | final int value = IntegerArgumentType.getInteger(context, " |
- | | + | final int value2 = IntegerArgumentType.getInteger(context, "value2"); |
- | } | + | final int result = value * value2; |
- | + | context.getSource().sendFeedback(() -> Text.literal("%s × %s = %s".formatted(value, | |
- | public static LiteralCommandNode registerMain(CommandDispatcher< | + | |
- | | + | |
- | .then(argument("targets", EntityArgumentType.players()) | + | |
- | .then(argument(" | + | |
- | .executes(ctx -> { | + | |
- | | + | |
- | })))); | + | |
- | } | + | |
</ | </ | ||
- | The redirect tells brigadier | + | Now you can type one or two integers. If you give one integer, that integer will be self-multiplied. If you provide two integers, they will be multipled. You may find it unnecessary |
- | ===== Redirects (Chainable Commands) ===== | + | <code java> |
- | Commands such as ''/ | + | public class ExampleMod implements ModInitializer { |
- | + | @Override | |
- | <code java [enable_line_numbers=" | + | public void onInitialize() { |
- | LiteralCommandNode< | + | CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, |
- | LiteralCommandNode< | + | .then(argument("value", IntegerArgumentType.integer()) |
- | // You can register under the same literal more than once, it will just register new parts of the branch as shown below if you register a duplicate branch an error will popup in console warning of conflicting commands but one will still work. | + | .executes(context -> executeMultiply(IntegerArgumentType.getInteger(context, |
- | | + | .then(argument("value2", IntegerArgumentType.integer()) |
- | | + | .executes(context -> executeMultiply(IntegerArgumentType.getInteger(context, "value"), IntegerArgumentType.getInteger(context, "value2" |
- | .redirect(root, this:: | + | } |
- | | + | |
- | | + | |
- | .then(literal("command") | + | |
- | | + | |
- | ctx.getSource().sendFeedback(new LiteralText(" | + | |
- | | + | |
- | }))); | + | |
- | </ | + | |
- | The redirect can also modify the CommandSource by use of a '' | + | |
- | + | ||
- | <code java [enable_line_numbers=" | + | |
- | .redirect(rootNode, | + | |
- | return | + | |
- | }) | + | |
- | </ | + | |
- | + | ||
- | ===== What can the ServerCommandSource do? ===== | + | |
- | + | ||
- | A server command source provides some additional implementation specific | + | |
- | + | ||
- | <code java [enable_line_numbers="true"]> | + | |
- | final ServerCommandSource source = ctx.getSource(); | + | |
- | // Get the source. This will always work. | + | |
- | + | ||
- | final Entity sender = source.getEntity(); | + | |
- | // Unchecked, may be null if the sender was the console. | + | |
- | + | ||
- | final Entity sender2 = source.getEntityOrThrow(); | + | |
- | // Will end the command if the source 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 ServerPlayerEntity player = source.getPlayer(); | + | |
- | // 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. | + | |
- | + | ||
- | source.getPosition(); | + | |
- | // Get's the sender' | + | |
- | + | ||
- | 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 example commands examples ===== | + | |
- | + | ||
- | === Broadcast a message === | + | |
- | <code java [enable_line_numbers=" | + | private |
- | public | + | |
- | | + | |
- | .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 operators or any operator that is permission level 1. | + | return |
- | | + | } |
- | .then(argument(" | + | |
- | .executes(ctx | + | |
- | } | + | |
- | + | ||
- | public static int broadcast(ServerCommandSource source, Formatting formatting, String message) { | + | |
- | final Text text = new LiteralText(message).formatting(formatting); | + | |
- | + | ||
- | source.getMinecraftServer().getPlayerManager().broadcastChatMessage(text, false); | + | |
- | return | + | |
} | } | ||
</ | </ | ||
+ | ===== A sub command ===== | ||
- | ==== / | + | To add a sub command, you register the first literal node of the command normally. |
- | First the basic code where we register " | + | < |
- | + | dispatcher.register(literal(" | |
- | < | + | |
- | public static LiteralCommandNode register(CommandDispatcher< | + | |
- | return | + | |
- | .executes(ctx -> giveDiamond(ctx))); | + | |
- | } | + | |
</ | </ | ||
- | Then since we only want to give to players, we check if the CommandSource is a player. But we can use '' | + | In order to have a sub command, one needs to append |
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | public static int giveDiamond(CommandContext< | + | dispatcher.register(literal(" |
- | | + | .then(literal(" |
- | + | .executes(context -> { | |
- | final PlayerEntity self = source.getPlayer(); // If not a player than the command ends | + | |
- | </code> | + | // For versions below 1.20, use directly the '' |
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
- | Then we add to the player' | + | return 1; |
- | + | }) | |
- | <code java [enable_line_numbers=" | + | ) |
- | | + | ); |
- | throw new SimpleCommandExceptionType(new TranslatableText(" | + | |
- | } | + | |
- | + | ||
- | return 1; | + | |
- | } | + | |
</ | </ | ||
- | ==== Antioch ==== | + | Similar to arguments, sub command nodes can also be set optional. In the following cases, both ''/ |
- | ...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 summons a primed TNT to a specified location or the location of the sender's cursor. | + | |
- | + | ||
- | First create an entry into the CommandDispatcher that takes a literal of antioch with an optional argument of the location to summon the entity at. | + | |
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | public static void register(CommandDispatcher< | + | dispatcher.register(literal(" |
- | | + | .executes(context -> { |
- | .then(required(" | + | context.getSource().sendFeedback(() -> Text.literal(" |
- | | + | return 1; |
- | .executes(ctx -> antioch(ctx.getSource(), null))); | + | }) |
- | } | + | |
+ | .executes(context | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
+ | | ||
+ | | ||
+ | ) | ||
+ | ); | ||
</ | </ | ||
+ | ====== Advanced concepts ====== | ||
- | Then the creation and messages behind the joke. | + | Below are links to the articles about more complex concepts used in brigadier. |
- | <code java [enable_line_numbers=" | + | ^ Page ^ Description |
- | public static int antioch(ServerCommandSource source, BlockPos blockPos) throws CommandSyntaxException { | + | | [[command_exceptions |
- | if(blockPos == null) { | + | | [[command_suggestions|Suggestions]] |
- | // For the case of no inputted argument we calculate the cursor position | + | | [[command_redirects|Redirects]] |
- | // This class is used as an example and actually doesn' | + | | [[command_argument_types|Custom Argument Types]] |
- | | + | | [[command_examples|Examples]] | Some example commands | |
- | | + | |
- | final TntEntity tnt = new TntEntity(source.getWorld(), | + | ====== FAQ ====== |
- | tnt.setFuse(3); | + | |
- | + | ||
- | source.getMinecraftServer().getPlayerManager().broadcastChatMessage(new LiteralText(" | + | |
- | source.getMinecraftServer().getPlayerManager().broadcastChatMessage(new LiteralText(" | + | |
- | source.getWorld().spawnEntity(tnt); | + | |
- | return 1; | + | |
- | } | + | |
- | </ | + | |
- | ==== More examples coming soon ==== | + | ===== Why does my code not compile ===== |
- | ===== Custom Argument Types ===== | + | There are two immediate possibilities for why this could occur. |
- | Brigadier has support for custom argument types and this section goes into showing how to create | + | * **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 '' | ||
- | Warning: Custom arguments require | + | ===== Can I register |
- | For this example we will create | + | Fabric has a '' |
- | First create a class which extends '' | ||
- | |||
- | <code java [enable_line_numbers=" | ||
- | public class UuidArgumentType implements ArgumentType< | ||
- | </ | ||
- | |||
- | ArgumentType requires you to implement the '' | ||
<code java> | <code java> | ||
- | @Override | + | ClientCommandRegistrationCallback.EVENT.register((dispatcher, |
- | public UUID parse(StringReader reader) throws CommandSyntaxException | + | .executes(context -> { |
+ | context.getSource().sendFeedback(Text.literal(" | ||
+ | return 1; | ||
+ | } | ||
+ | ))); | ||
</ | </ | ||
- | This method is where all of your parsing will occur. Either this method will return the object based on the arguments provided | + | If you need to open a screen |
- | Next you will store the current position of the cursor, this is so you can substring out only the specific argument. This will always be at the beginning of where your argument appears on the command line. | + | ===== Can I register commands in runtime? ===== |
- | <code java [enable_line_numbers=" | + | You can do this but it is not recommended. You would get the '' |
- | int argBeginning = reader.getCursor(); | + | |
- | if (!reader.canRead()) { | + | |
- | reader.skip(); | + | |
- | } | + | |
- | </ | + | |
- | Now we grab the entire argument. Depending on your argument type, you may have a different criteria or be similar | + | After that you need to send the command tree to every player again using '' |
- | <code java [enable_line_numbers=" | + | ===== Can I unregister commands in runtime? ===== |
- | while (reader.canRead() && reader.peek() != ' ') { // peek provides the character at the current cursor position. | + | |
- | reader.skip(); | + | |
- | } | + | |
- | </ | + | |
- | Then we will ask the StringReader what the current position of the cursor is an substring our argument out of the command | + | 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 |
- | <code java [enable_line_numbers=" | ||
- | |||
- | Now finally we check if our argument is correct and parse the specific argument to our liking, and throwing an exception if the parsing fails. | ||
- | |||
- | <code java [enable_line_numbers=" | ||
- | try { | ||
- | UUID uuid = UUID.fromString(uuidString); | ||
- | return uuid; // And we return our type, in this case the parser will consider this argument to have parsed properly and then move on. | ||
- | } catch (Exception ex) { | ||
- | // UUIDs can throw an exception when made by a string, so we catch the exception and repackage it into a CommandSyntaxException type. | ||
- | // Create with context tells Brigadier to supply some context to tell the user where the command failed at. | ||
- | // Though normal create method could be used. | ||
- | throw new SimpleCommandExceptionType(new LiteralText(ex.getMessage())).createWithContext(reader); | ||
- | } | ||
- | </ | ||
- | |||
- | The ArgumentType is done, however your client will refuse the parse the argument and throw an error. This is because the server will tell the client what argument type the command node is. And the client will not parse any argument types it does not know how to parse. To fix this we need to register an ArgumentSerializer. | ||
- | Within your ModInitializer. For more complex argument types, you may need to create your own ArgumentSerializer. | ||
- | |||
- | <code java [enable_line_numbers=" | ||
- | ArgumentTypes.register(" | ||
- | // The argument should be what will create the ArgumentType. | ||
- | </ | ||
- | |||
- | And here is the whole ArgumentType: | ||
- | |||
- | <file java UuidArgumentType.java [enable_line_numbers=" | ||
- | |||
- | import com.mojang.brigadier.StringReader; | ||
- | import com.mojang.brigadier.arguments.ArgumentType; | ||
- | import com.mojang.brigadier.context.CommandContext; | ||
- | import com.mojang.brigadier.exceptions.CommandSyntaxException; | ||
- | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; | ||
- | import net.minecraft.text.LiteralText; | ||
- | import net.minecraft.util.SystemUtil; | ||
- | |||
- | import java.util.ArrayList; | ||
- | import java.util.Collection; | ||
- | import java.util.UUID; | ||
- | |||
- | /** | ||
- | * Represents an ArgumentType that will return a UUID. | ||
- | */ | ||
- | public class UuidArgumentType implements ArgumentType< | ||
- | public static UuidArgumentType uuid() { | ||
- | return new UuidArgumentType(); | ||
- | } | ||
- | |||
- | public static <S> UUID getUuid(String name, CommandContext< | ||
- | // Note that you should assume the CommandSource wrapped inside of the CommandContext will always be a generic type. | ||
- | // If you need to access the ServerCommandSource make sure you verify the source is a server command source before casting. | ||
- | return context.getArgument(name, | ||
- | } | ||
- | |||
- | private static final Collection< | ||
- | list.add(" | ||
- | list.add(" | ||
- | list.add(" | ||
- | }); | ||
- | |||
- | @Override | ||
- | public UUID parse(StringReader reader) throws CommandSyntaxException { | ||
- | int argBeginning = reader.getCursor(); | ||
- | if (!reader.canRead()) { | ||
- | reader.skip(); | ||
- | } | ||
- | |||
- | // Now we check the contents of the argument till either we hit the end of the command line (When canRead becomes false) | ||
- | // Otherwise we go till reach reach a space, which signifies the next argument | ||
- | while (reader.canRead() && reader.peek() != ' ') { // peek provides the character at the current cursor position. | ||
- | reader.skip(); | ||
- | } | ||
- | |||
- | // Now we substring the specific part we want to see using the starting cursor position and the ends where the next argument starts. | ||
- | String uuidString = reader.getString().substring(argBeginning, | ||
- | try { | ||
- | UUID uuid = UUID.fromString(uuidString); | ||
- | return uuid; // And we return our type, in this case the parser will consider this argument to have parsed properly and then move on. | ||
- | } catch (Exception ex) { | ||
- | // UUIDs can throw an exception when made by a string, so we catch the exception and repackage it into a CommandSyntaxException type. | ||
- | // Create with context tells Brigadier to supply some context to tell the user where the command failed at. | ||
- | // Though normal create method could be used. | ||
- | throw new SimpleCommandExceptionType(new LiteralText(ex.getMessage())).createWithContext(reader); | ||
- | } | ||
- | } | ||
- | |||
- | @Override | ||
- | public Collection< | ||
- | return EXAMPLES; | ||
- | } | ||
- | } | ||
- | </ |
tutorial/commands.txt · Last modified: 2024/02/23 14:22 by allen1210