tutorial:commands
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionLast revisionBoth sides next revision | ||
tutorial:commands [2020/05/16 01:57] – Reword some parts of the tutorial, includes a few gramatical corrections i509vcb | tutorial:commands [2024/02/23 14:09] – Adding one question in FAQ section allen1210 | ||
---|---|---|---|
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. | + | |
- | 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. | + | ===== What is Brigadier? ===== |
- | ===== Registering Commands ===== | + | 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 by registering a new listener in the '' | + | The source code for brigadier can be found here: https:// |
- | The event should | + | |
- | The dedicated parameter if true will tell event listeners that the server commands are being registered on is a '' | + | ===== The '' |
- | Below are a few examples of how the commands can be registered. | + | |
- | <code java [enable_line_numbers=" | + | In Minecraft, '' |
- | // Method reference | + | |
- | CommandRegistrationCallback.EVENT.register(TutorialCommands:: | + | |
- | // Using a lambda | + | The single method in '' |
- | CommandRegistrationCallback.EVENT.register((dispatcher, | + | |
- | // This command will be registered regardless of the server being dedicated or integrated | + | |
- | TutorialCommand.register(dispatcher); | + | |
- | if (dedicated) { | + | |
- | // This command | + | |
- | TutorialHelpCommand.register(dispatcher); | + | |
- | } else { | + | |
- | // This command | + | |
- | // Commands which call client only classes | + | |
- | IntegratedTutorialHelpCommand.register(dispatcher); | + | |
- | } | + | |
- | }); | + | |
- | // Or register directly | + | Like other functional interfaces, it is usually used as a lambda or a method reference: |
- | CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> { | + | |
- | dispatcher.register(LiteralArgumentBuilder.literal(" | + | |
- | }); | + | |
- | </ | + | |
- | ==== A very basic command ==== | + | <code java> |
- | + | 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. | + | return |
- | + | }; | |
- | <code java [enable_line_numbers=" | + | |
- | // The root of the command. This must be a literal argument. | + | |
- | dispatcher.register(CommandManager.literal(" | + | |
- | // Then add an argument named bar that is an integer | + | |
- | .then(CommandManager.argument(" | + | |
- | // The command to be executed if the command " | + | |
- | .executes(ctx | + | |
- | System.out.println(" | + | |
- | // Return a result. Typically -1 is failure, 0 is pass and 1 is success. | + | |
- | return 1; | + | |
- | })) | + | |
- | | + | |
- | .executes(ctx -> { | + | |
- | System.out.println(" | + | |
- | | + | |
- | }) | + | |
- | ); | + | |
</ | </ | ||
- | The main process registers | + | The integer can be considered the result of the command. Typically negative values mean a command has failed and will do nothing. A result |
- | Since the root node must be literal, The sender must enter the exact same sequence of letters to execute | + | |
- | ===== Brigadier Explained ===== | ||
- | Brigadier starts with the '' | + | ==== What can the ServerCommandSource do? ==== |
- | Command nodes are similar | + | A '' |
- | The execute blocks specify the command to be ran. As Brigadier' | ||
- | |||
- | ==== CommandContexts ==== | ||
- | |||
- | When a command is ran, Brigadier provides a '' | ||
- | The CommandContext contains all arguments and other objects such as the inputted String and the '' | ||
- | |||
- | ==== Arguments ==== | ||
- | |||
- | Arguments in Brigadier both parse and error check any inputted arguments. | ||
- | Minecraft creates some special argument types for it's own use such as the '' | ||
- | |||
- | ==== Static Imports ==== | ||
- | You could do the long method of typing '' | ||
- | This also works for getting arguments, which shortens the already long '' | ||
- | This also works for Minecraft' | ||
- | |||
- | And your imports would look something like this: | ||
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | import static com.mojang.brigadier.arguments.StringArgumentType.getString; | + | // Get the source. This will always work. |
- | import static com.mojang.brigadier.arguments.StringArgumentType.word; | + | final ServerCommandSource source = ctx.getSource(); |
- | import static net.minecraft.server.command.CommandManager.literal; | + | |
- | import static net.minecraft.server.command.CommandManager.argument; // argument(" | + | |
- | import static net.minecraft.server.command.CommandManager.*; | + | |
- | </ | + | |
- | Note: Please | + | // Unchecked, may be null if the sender was the console |
+ | final @Nullable Entity sender = source.getEntity(); | ||
- | Brigadier' | + | // 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 = source.getEntityOrThrow(); | ||
- | Minecraft' | + | // null if the executor of the command is not a player. |
+ | final @Nullable ServerPlayerEntity player = source.getPlayer(): | ||
- | ==== Suggestions ==== | + | // Will throw an exception if the executor of the command was not explicitly 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. | ||
+ | final @NotNull ServerPlayerEntity player | ||
- | Suggestions can be provided to the client to recommend what to input into the command. | + | // Gets the sender' |
- | < | + | // This could be the location of the entity/ |
- | SUMMONABLE_ENTITIES | + | final Vec3d position = source.getPosition(); |
- | AVAILIBLE_SOUNDS | + | |
- | ALL_RECIPES | + | |
- | ASK_SERVER | + | |
- | </ | + | |
- | Loot tables specify their own SuggestionProvider inside | + | // Gets the world the sender is within. The console's world is the same as the default spawn world. |
+ | final ServerWorld world = source.getWorld(); | ||
- | The example below is a dynamically changing SuggestionProvider that lists several words for a StringArgumentType to demonstrate how it works: | + | // Gets the sender' |
- | <code java [enable_line_numbers=" | + | final Vec2f rotation = source.getRotation(); |
- | public static SuggestionProvider< | + | |
- | return (ctx, builder) -> getSuggestionsBuilder(builder, | + | |
- | } | + | |
- | + | ||
- | private static CompletableFuture< | + | |
- | String remaining = builder.getRemaining().toLowerCase(Locale.ROOT); | + | |
- | + | ||
- | if(list.isEmpty()) { // If the list is empty then return no suggestions | + | |
- | return Suggestions.empty(); // No suggestions | + | |
- | } | + | |
- | + | ||
- | for (String str : list) { // Iterate through the supplied list | + | |
- | if (str.toLowerCase(Locale.ROOT).startsWith(remaining)) { | + | |
- | builder.suggest(str); // Add every single entry to suggestions list. | + | |
- | } | + | |
- | } | + | |
- | return builder.buildFuture(); | + | |
- | } | + | |
- | </ | + | |
- | The SuggestionProvider is a functional interface that returns a CompletableFuture containing a list of suggestions. These suggestions are sent to client as a command is typed and can be changed while server is running. The SuggestionProvider provides a CommandContext and a SuggestionBuilder to allow determination | + | // Access |
+ | final MinecraftServer server = source.getServer(); | ||
- | Though remember these are suggestions. | + | // 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(); | ||
- | To use the suggestion you would append it right after the argument you want to suggest possible arguments for. This can be any argument and the normal client side exception popups will still work. Note this cannot be applied to literals. | + | // Returns true if the source of the command has a certain permission level. |
- | + | // This is based on the operator status of the sender. | |
- | <code java [enable_line_numbers=" | + | // (On an integrated server, the player must have cheats enabled to execute these commands.) |
- | argument(argumentName, word()) | + | final boolean b = source.hasPermissionLevel(int level); |
- | .suggests(CompletionProviders.suggestedStrings()) | + | |
- | .then(/*Rest of the command*/)); | + | |
</ | </ | ||
- | ==== Requiring Permissions | + | ===== Register a basic command ===== |
- | Lets say you have a command you only want operators to be able to execute. This is where the '' | + | Commands are registered by registering in '' |
- | For example this may look like the following: | + | The event should be registered in your mod's initializer. The callback has three parameters. The '' |
- | <code java [enable_line_numbers=" | + | To simplify the code, it is highly recommended to '' |
- | dispatcher.register(literal(" | + | <code java> |
- | .requires(source -> source.hasPermissionLevel(4)) | + | import static net.minecraft.server.command.CommandManager.*; |
- | .executes(ctx -> { | + | |
- | ctx.getSource().sendFeedback(new LiteralText(" | + | |
- | return 1; | + | |
- | }); | + | |
</ | </ | ||
- | This command will only execute if the Source of the command is a level 4 operator at minimum. If the predicate returns false, then the command will not execute. Also this has the side effect of not showing this command in tab completion to anyone who is not a level 4 operator. | + | In the mod initializer, we just register |
- | + | ||
- | Nothing prevents someone from specifying calls to permissions implementations within the '' | + | |
- | + | ||
- | ==== Exceptions ==== | + | |
- | + | ||
- | Brigadier supports command exceptions which can be used to end a command such as if an argument didn't parse properly or the command failed to execute, as well as including richer details of the failure. | + | |
- | + | ||
- | 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 '' | + | |
- | Below is a coin flip command | + | |
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | dispatcher.register(CommandManager.literal(" | + | public class ExampleMod implements ModInitializer { |
- | .executes(ctx -> { | + | @Override |
- | | + | public void onInitialize() { |
- | + | CommandRegistrationCallback.EVENT.register((dispatcher, | |
- | if(random.nextBoolean()) { // If heads succeed. | + | .executes(context |
- | ctx.getSource().sendMessage(new TranslateableText("coin.flip.heads")) | + | // For versions below 1.19, replace " |
- | return Command.SINGLE_SUCCESS; | + | // For versions below 1.20, remode "() ->" directly. |
- | } | + | |
- | throw new SimpleCommandExceptionType(new TranslateableText(" | + | |
- | })); | + | }))); |
- | </ | + | } |
- | + | ||
- | Though you are not just limited to a single type of exception as Brigadier also supplies Dynamic exceptions which take additional parameters for context. | + | |
- | + | ||
- | <code java [enable_line_numbers=" | + | |
- | DynamicCommandExceptionType used_name = new DynamicCommandExceptionType(name -> { | + | |
- | | + | |
- | }); | + | |
- | </ | + | |
- | + | ||
- | 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 (Aliases) ==== | + | |
- | + | ||
- | Redirects are Brigadier' | + | |
- | + | ||
- | <code java [enable_line_numbers=" | + | |
- | public static void register(CommandDispatcher< | + | |
- | | + | |
- | dispatcher.register(literal(" | + | |
- | .redirect(node)); | + | |
- | dispatcher.register(literal(" | + | |
- | .redirect(node)); | + | |
- | } | + | |
- | + | ||
- | public static LiteralCommandNode registerMain(CommandDispatcher< | + | |
- | return dispatcher.register(literal(" | + | |
- | .then(argument(" | + | |
- | .then(argument(" | + | |
- | .executes(ctx -> { | + | |
- | return execute(ctx.getSource(), | + | |
- | })))); | + | |
} | } | ||
</ | </ | ||
- | The redirect | + | **Please ensure you import the correct static method.** |
- | ==== Redirects | + | In the '' |
- | Commands | + | |
- | <code java [enable_line_numbers=" | + | If the command fails, instead |
- | LiteralCommandNode< | + | |
- | LiteralCommandNode< | + | |
- | // 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. | + | |
- | .then(literal(" | + | |
- | .then(literal(" | + | |
- | .redirect(root, | + | |
- | .then(literal(" | + | |
- | .redirect(root, | + | |
- | .then(literal(" | + | |
- | .executes(ctx -> { | + | |
- | ctx.getSource().sendFeedback(new LiteralText(" | + | |
- | return Command.SINGLE_SUCCESS; | + | |
- | }))); | + | |
- | </ | + | |
- | The redirect can also modify the CommandSource by use of a '' | + | |
- | <code java [enable_line_numbers=" | + | To execute this command, you must type ''/ |
- | .redirect(rootNode, context | + | |
- | return ((ServerCommandSource) context.getSource()).withLookingAt(Vec3ArgumentType.getVec3(context, " | + | |
- | }) | + | |
- | </code> | + | |
- | ===== ServerCommandSource | + | ===== Registration environment |
+ | If desired, you can also make sure a command is only registered under some specific circumstances, | ||
- | A server command source 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 ran in or the server the command was run on. | + | <yarncode |
- | + | public class ExampleCommandMod implements ModInitializer { | |
- | <code java [enable_line_numbers=" | + | |
- | final ServerCommandSource source = ctx.getSource(); | + | public void onInitialize() { |
- | // 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 | + | } |
- | // 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, "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) | + | |
- | </code> | + | |
- | + | ||
- | ===== A few examples ===== | + | |
- | + | ||
- | === Broadcast a message === | + | |
- | + | ||
- | <code java [enable_line_numbers=" | + | |
- | public | + | |
- | | + | |
- | .requires(source | + | |
- | | + | |
- | .then(argument(" | + | |
- | | + | |
} | } | ||
+ | </ | ||
- | public static int broadcast(ServerCommandSource source, Formatting formatting, String message) { | + | ===== Static Imports ===== |
- | final Text text = new LiteralText(message).formatting(formatting); | + | In the example above, the use of static imports is used for code simplifying. For a literal |
- | + | ||
- | source.getMinecraftServer().getPlayerManager().broadcastChatMessage(text, | + | |
- | return Command.SINGLE_SUCCESS; | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | === / | + | |
- | + | ||
- | First the basic code where we register " | + | |
+ | Below is an example of some static imports: | ||
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | public static LiteralCommandNode register(CommandDispatcher< | + | // getString(ctx, " |
- | | + | import static com.mojang.brigadier.arguments.StringArgumentType.getString; |
- | .executes(ctx -> giveDiamond(ctx))); | + | // word() |
- | } | + | import static com.mojang.brigadier.arguments.StringArgumentType.word; |
+ | // | ||
+ | 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.*; | ||
</ | </ | ||
- | Then since we only want to give to players, we check if the CommandSource is a player. But we can use '' | + | Note: Please be sure you use the '' |
- | <code java [enable_line_numbers=" | + | Brigadier' |
- | public static int giveDiamond(CommandContext< | + | |
- | final ServerCommandSource source = ctx.getSource(); | + | |
- | + | ||
- | final PlayerEntity self = source.getPlayer(); | + | |
- | </ | + | |
- | Then we add to the player' | + | Minecraft' |
- | <code java [enable_line_numbers=" | + | ===== Add Requirements ===== |
- | if(!player.inventory.insertStack(new ItemStack(Items.DIAMOND))){ | + | |
- | throw new SimpleCommandExceptionType(new TranslatableText(" | + | |
- | } | + | |
- | return 1; | + | Let's say you have a command that you only want operators to be able to execute. This is where the '' |
- | } | + | |
- | </code> | + | |
- | === Antioch === | + | For example |
- | ...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' | + | |
- | + | ||
- | First create an entry into the CommandDispatcher that takes a literal of antioch with an optional argument of the location to summon | + | |
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | public static void register(CommandDispatcher< | + | dispatcher.register(literal(" |
- | | + | .requires(source |
- | .then(required(" | + | .executes(ctx -> { |
- | .executes(ctx | + | |
- | .executes(ctx -> antioch(ctx.getSource(), | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | Then the creation and messages behind the joke. | + | |
- | + | ||
- | <code java [enable_line_numbers=" | + | |
- | public static int antioch(ServerCommandSource source, BlockPos blockPos) throws CommandSyntaxException | + | |
- | | + | |
- | // For the case of no inputted argument we calculate the cursor position of the player or throw an error if the nearest position is too far or is outside of the world. | + | |
- | // This class is used as an example and actually doesn' | + | |
- | blockPos = LocationUtil.calculateCursorOrThrow(source, | + | |
- | } | + | |
- | + | ||
- | final TntEntity tnt = new TntEntity(source.getWorld(), blockPos.getX(), blockPos.getY(), blockPos.getZ(), | + | |
- | tnt.setFuse(3); | + | |
- | + | ||
- | source.getMinecraftServer().getPlayerManager().broadcastChatMessage(new LiteralText("...lobbest thou thy Holy Hand Grenade of Antioch towards thy foe").formatting(Formatting.RED), false); | + | |
- | source.getMinecraftServer().getPlayerManager().broadcastChatMessage(new LiteralText(" | + | |
- | source.getWorld().spawnEntity(tnt); | + | |
return 1; | return 1; | ||
- | } | + | |
</ | </ | ||
- | === Finding Biomes via Command === | + | This command will only execute if the source of the command is a level 2 operator at minimum, // |
- | This example shows examples of redirects, exceptions, suggestions and a tiny bit of text. Note this command when used works but can take a bit of time to work similarly to '' | + | To create commands that only level 4 operators (//not including// command blocks) can execute, |
- | <code java [enable_line_numbers=" | + | |
- | public class CommandLocateBiome { | + | |
- | | + | |
- | public static void register(CommandDispatcher< | + | |
- | LiteralCommandNode< | + | |
- | .then(argument(" | + | |
- | .then(argument(" | + | |
- | .executes(ctx -> execute(ctx.getSource(), getIdentifier(ctx, | + | |
- | | + | |
- | // Register a redirect for /biome alias | + | |
- | dispatcher.register(literal(" | + | |
- | .redirect(basenode)); | + | |
- | } | + | |
- | private static int execute(ServerCommandSource source, Identifier biomeId, int range) throws CommandSyntaxException { | + | ===== Arguments ===== |
- | Biome biome = Registry.BIOME.get(biomeId); | + | |
- | + | ||
- | if(biome | + | |
- | // Since the argument is an Identifier we need to check if the identifier actually exists in the registry | + | |
- | throw new SimpleCommandExceptionType(new TranslatableText(" | + | |
- | } | + | |
- | + | ||
- | List< | + | |
- | bio.add(biome); | + | |
- | ServerWorld world = source.getWorld(); | + | Arguments are used in most of commands. Sometimes they can be optional, which means if you do not provide that argument, the command will also run. One node may have multiple argument types, but be aware that there is possibility of ambiguity, which should be avoided. |
- | BiomeSource bsource = world.getChunkManager().getChunkGenerator().getBiomeSource(); | + | |
- | BlockPos loc = new BlockPos(source.getPosition()); | + | |
- | // This call will likely block the main thread | + | In this case, we add one integer argument, and calculate the square of the integer. |
- | BlockPos pos = bsource.locateBiome(loc.getX(), loc.getZ(), range, bio, new Random(world.getSeed())); | + | |
- | + | ||
- | // If null, a biome was not found | + | |
- | if(pos == null) { | + | |
- | throw new SimpleCommandExceptionType(new TranslatableText(" | + | |
- | } | + | |
- | int distance = MathHelper.floor(getDistance(loc.getX(), loc.getZ(), pos.getX(), pos.getZ())); | + | <code java> |
- | | + | dispatcher.register(literal(" |
- | Text teleportButtonPopup = Texts.bracketed(new TranslatableText("chat.coordinates", | + | .then(argument("value", |
- | style.setColor(Formatting.GREEN).setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/tp @s " | + | |
- | }); | + | final int value = IntegerArgumentType.getInteger(context, "value"); |
- | + | | |
- | source.sendFeedback(new TranslatableText(" | + | |
- | + | return | |
- | return 1; | + | }))); |
- | } | + | |
- | // Just a normal old 2d distance method. | + | |
- | private static float getDistance(int int_1, int int_2, int int_3, int int_4) { | + | |
- | int int_5 = int_3 - int_1; | + | |
- | int int_6 = int_4 - int_2; | + | |
- | + | ||
- | return MathHelper.sqrt((float) (int_5 | + | |
- | } | + | |
- | + | ||
- | + | ||
- | + | ||
- | public static class BiomeCompletionProvider { | + | |
- | // This provides suggestions of what biomes can be selected. Since this uses the registry, mods that add new biomes will work without any modifications. | + | |
- | public static final SuggestionProvider< | + | |
- | Registry.BIOME.getIds().stream().forEach(identifier | + | |
- | return | + | |
- | }); | + | |
- | + | ||
- | } | + | |
</ | </ | ||
- | ===== Custom Argument Types ===== | + | In this case, after the word ''/ |
- | Brigadier has support | + | Note: for simplicity, '' |
- | Warning: Custom arguments require client mod installation to be registered correctly! If you are making a server plugin, consider using existing argument type and a custom suggestions provider instead. | + | Then we add an optional second argument: |
- | + | ||
- | For this example | + | |
- | + | ||
- | 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 | + | dispatcher.register(literal(" |
- | public UUID parse(StringReader reader) throws CommandSyntaxException | + | |
+ | .executes(context -> { | ||
+ | final int value = IntegerArgumentType.getInteger(context, | ||
+ | final int result = value * value; | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
+ | return result; | ||
+ | }) | ||
+ | .then(argument(" | ||
+ | .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; | ||
+ | })))); | ||
</ | </ | ||
- | This method is where all of your parsing | + | Now you can type one or two integers. If you give one integer, that square |
- | Next you will store the current position of the cursor, this is so you can substring out only the specific | + | <code java> |
+ | public class ExampleMod implements ModInitializer { | ||
+ | @Override | ||
+ | public void onInitialize() { | ||
+ | CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, | ||
+ | .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<ServerCommandSource> context) { |
- | int argBeginning | + | |
- | if (!reader.canRead()) { | + | context.getSource().sendFeedback(() -> Text.literal("%s × %s = %s" |
- | | + | |
+ | } | ||
} | } | ||
</ | </ | ||
+ | ===== A sub command ===== | ||
- | Now we grab the entire argument. Depending on your argument type, you may have a different criteria or be similar to some arguments where detecting a '' | + | To add a sub command, you register the first literal node of the command |
- | < | + | < |
- | while (reader.canRead() && reader.peek() != ' ') { // peek provides the character at the current cursor position. | + | dispatcher.register(literal(" |
- | reader.skip(); | + | |
- | } | + | |
</ | </ | ||
- | Then we will ask the StringReader what the current position of the cursor is an substring our argument out of the command line. | + | In order to have a sub command, one needs to append |
- | <code java [enable_line_numbers=" | + | This creates the command '' |
- | + | ||
- | 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=" | <code java [enable_line_numbers=" | ||
- | try { | + | dispatcher.register(literal(" |
- | | + | .then(literal(" |
- | | + | .executes(context -> { |
- | } catch (Exception ex) { | + | // For versions below 1.19, use '' |
- | // UUIDs can throw an exception when made by a string, so we catch the exception and repackage it into a CommandSyntaxException type. | + | // For versions below 1.20, use directly |
- | // 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); | + | return 1; |
- | } | + | }) |
+ | ) | ||
+ | ); | ||
</ | </ | ||
- | 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 | + | Similar to arguments, sub command |
- | Within your ModInitializer. For more complex argument types, you may need to create your own ArgumentSerializer. | + | |
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | ArgumentTypes.register(" | + | dispatcher.register(literal("foo") |
- | // The argument should be what will create the ArgumentType. | + | |
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
+ | | ||
+ | }) | ||
+ | | ||
+ | .executes(context -> { | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
+ | return 1; | ||
+ | }) | ||
+ | ) | ||
+ | ); | ||
</ | </ | ||
+ | ====== Advanced concepts ====== | ||
- | And here is the whole ArgumentType: | + | Below are links to the articles about more complex concepts used in brigadier. |
- | <file java UuidArgumentType.java [enable_line_numbers=" | + | ^ Page ^ Description |
+ | | [[command_exceptions | ||
+ | | [[command_suggestions|Suggestions]] | ||
+ | | [[command_redirects|Redirects]] | ||
+ | | [[command_argument_types|Custom Argument Types]] | ||
+ | | [[command_examples|Examples]] | Some example commands | | ||
- | import com.mojang.brigadier.StringReader; | + | ====== FAQ ====== |
- | 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; | + | ===== Why does my code not compile ===== |
- | import java.util.Collection; | + | |
- | import java.util.UUID; | + | |
- | /** | + | There are several immediate possibilities for why this could occur. |
- | * 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< | + | * **Catch or throw a CommandSyntaxException: |
- | // Note that you should | + | * **Issues with generics:** You may have an issue with generics once in a while. |
- | // | + | * **Check '' |
- | return | + | * **'' |
- | } | + | |
- | private static final Collection< | + | ===== Can I register client side commands? ===== |
- | list.add(" | + | |
- | list.add(" | + | |
- | list.add(" | + | |
- | }); | + | |
- | @Override | + | Fabric has a '' |
- | 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) | + | <code java> |
- | // 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. | + | .executes(context -> { |
- | reader.skip(); // Tells the StringReader to move it's cursor to the next position. | + | |
- | | + | |
- | + | | |
- | // Now we substring the specific part we want to see using the starting cursor position and the ends where the next argument starts. | + | ))); |
- | | + | </code> |
- | try { | + | |
- | UUID uuid = UUID.fromString(uuidString); // Now our actual logic. | + | |
- | | + | |
- | | + | |
- | // 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<String> getExamples() { // Brigadier has support to show examples for what the argument should look like, this should contain a Collection of only the argument this type will return. This is mainly used to calculate ambiguous commands which share the exact same | + | |
- | return EXAMPLES; | + | |
- | } | + | |
- | } | + | |
- | </file> | + | |
- | ===== FAQ ===== | + | |
- | + | ||
- | === What else can I send feedback to the CommandSource? | + | |
- | + | ||
- | You use the Text classes (LiteralText, | + | |
- | + | ||
- | === Why does my IDE complain saying that a method executed by my command needs to catch or throw a CommandSyntaxException === | + | |
- | + | ||
- | The solution to this is just to make the methods throw a CommandSyntaxException down the whole chain as the executes block handles the exceptions. | + | |
- | === Can I register commands runtime? === | + | If you need to open a screen in the client command execution, instead of directly calling '' |
- | 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 commands in runtime? ===== |
- | After that you will need to send the command tree to every player again using '' | + | You can do this but it is not recommended. You would get the '' |
- | === Can I unregister commands runtime? === | + | After that you need to send the command tree to every player again using '' |
- | 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. | + | ===== Can I unregister commands in runtime? ===== |
- | Once again you will 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 register client side commands? === | + | ===== Can I execute command without typing in game? ===== |
+ | Yes! You can. | ||
- | Well Fabric currently doesn' | ||
- | https:// |
tutorial/commands.txt · Last modified: 2024/02/23 14:22 by allen1210