User Tools

Site Tools


tutorial:commands

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
tutorial:commands [2020/11/25 08:20] – [Advanced concepts] i509vcbtutorial:commands [2024/02/23 14:22] (current) – Documenting and first draft allen1210
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.+
  
 ===== What is Brigadier? ===== ===== What is Brigadier? =====
Line 14: Line 11:
 The source code for brigadier can be found here: https://github.com/Mojang/brigadier The source code for brigadier can be found here: https://github.com/Mojang/brigadier
  
-===== What is a command? =====+===== The ''Command'' interface =====
  
-Brigadier requires you specify the ''Command'' to be runA "command" is a fairly loose term within brigadier, but typically it means an exit point of the command tree. This is where the code is executed for your command. +In Minecraft, ''com.mojang.brigadier.Command'' is a functional interface, which runs some specific things, and throw a ''CommandSyntaxException'' in some casesIt has a generic type ''S''which defines the type of the //command source//. The command source provides some context in which a command was ran. In Minecraft, the command source is typically a ''<yarn class_2168>'' which can represent a server, a command block, rcon connection, a player or an entity. In some cases, it can also be a ''<yarn class_637>''.
-A ''Command'' is a functional interface. The command has a generic type of ''S'' which defines the type of the command source. The command source provides some context in which a command was ran. In Minecraft, this is typically a ''ServerCommandSource'' which can represent a server, a command block, rcon connection, a player or an entity.+
  
 The single method in ''Command'', ''run(CommandContext<S>)'' takes a ''CommandContext<S>'' as the sole parameter and returns an integer. The command context holds your command source of ''S'' and allows you to obtain arguments, look at the parsed command nodes and see the input used in this command. The single method in ''Command'', ''run(CommandContext<S>)'' takes a ''CommandContext<S>'' as the sole parameter and returns an integer. The command context holds your command source of ''S'' and allows you to obtain arguments, look at the parsed command nodes and see the input used in this command.
  
-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="true"]+Command<ServerCommandSource> command = context -> {
-Command<Object> command = context -> {+
     return 0;     return 0;
 }; };
 </code> </code>
  
-**__As an anonymous 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 ''0'' means the command has passed. Positive values mean the command was successful and did something.
-<code java [enable_line_numbers="true"]> +
-Command<Object> command = new Command<Object>() { +
-    @Override +
-    public int run(CommandContext<Object> context) { +
-        return 0+
-    } +
-+
-</code>+
  
-**__Implemented as a class__** 
-<code java [enable_line_numbers="true"]> 
-final class XYZCommand implements Command<Object> { 
-    @Override 
-    public int run(CommandContext<Object> context) { 
-        return 0; 
-    } 
-} 
-</code> 
  
-**__As a method reference__** +==== What can the ServerCommandSource do====
-<code java [enable_line_numbers="true"]> +
-void registerCommand() { +
-    // Ignore this for now, we will explain it next. +
-    dispatcher.register(CommandManager.literal("foo")) +
-        .executes(this::execute); // This refers to the "execute" method below. +
-+
- +
-private int execute(CommandContext<Object> context) { +
-    return 0; +
-+
-</code> +
- +
- +
-The ''run(CommandContext)'' method can throw a ''CommandSyntaxException'', but this is covered later in the tutorial.  +
- +
-The integer can be considered the result of the command. In Minecraft, the result can correspond to the power of a redstone comparator feeding from a command block or the value that will be passed the chain command block the command block is facing. Typically negative values mean a command has failed and will do nothing. A result of ''0'' means the command has passed. Positive values mean the command was successful and did something. +
- +
-===== A basic command =====+
  
-Below is a command that contains no arguments:+A ''ServerCommandSource'' provides some additional implementation specific context when a command is run. This includes the ability to get the entity that executed the command, the world the command was ran in or the server the command was run on.
  
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
-dispatcher.register(CommandManager.literal("foo").executes(context -> {  +// Get the sourceThis will always work
-    System.out.println("Called foo with no arguments");+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(); 
-</code>+
  
-''CommandManager.literal("foo")'' tells brigadier this command has one node, a **literal** called ''foo''+// Will throw an exception if the executor of the command was not an Entity.  
-To execute this command, one must type ''/foo''If ''/Foo'', ''/FoO'', ''/FOO'', ''/fOO'' or ''/fooo'' is typed insteadthe command will not run.+// 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 entityany living entity or a player. 
 +final @NotNull Entity sender2 = source.getEntityOrThrow(); 
  
-===== Static Imports ===== +// null if the executor of the command is not player. 
-Typing out ''CommandManager.literal("foo")'' every time you want to create a literal may feel redundant. You can clean up your codebase by use of static imports for the arguments. For literal this would shorten the statement to ''literal("foo")''This also works for getting the value of an argumentThis shortens ''StringArgumentType.getString(ctx, "string")'' to ''getString(ctx, "string")''. This also works for Minecraft's own argument types.+final @Nullable ServerPlayerEntity player = source.getPlayer():
  
-Below is an example of some static imports: +// Will throw an exception if the executor of the command was not explicitly a Player. 
-<code java [enable_line_numbers="true"]> +// Also will send feedback telling the sender of the command that they must be a player
-// getString(ctx, "string"+// 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("foo"+
-import static net.minecraft.server.command.CommandManager.literal; +
- // argument("bar", word()+
-import static net.minecraft.server.command.CommandManager.argument; +
-// Import everything +
-import static net.minecraft.server.command.CommandManager.*; +
-</code>+
  
-Note: Please be sure you use the ''literal'' and ''argument'' from CommandManager or you may have issues with generics when trying to compile.+// Gets the sender's position as a Vec3d when the command was sent. 
 +// This could be the location of the entity/command block or in the case of the console, the world's spawn point. 
 +final Vec3d position = source.getPosition(); 
  
-Brigadier's default arguments are at ''com.mojang.brigadier.arguments''+// Gets the world the sender is within. The console'world is the same as the default spawn world. 
 +final ServerWorld world = source.getWorld(); 
  
-Minecraft'arguments are in ''net.minecraft.command.arguments''+// Gets the sender'rotation as a Vec2f
-CommandManager is in the package ''net.minecraft.server.command''+final Vec2f rotation = source.getRotation(); 
  
-==== A sub command ====+// Access to the instance of the MinecraftServer this command was ran on. 
 +final MinecraftServer server source.getServer(); 
  
-To add a sub command, you register the first literal node of the command normally.+// The name of the command source. This could be the name of the entityplayer, 
 +// the name of a CommandBlock that has been renamed before being placed down, or in the case of the Console, "Console"
 +final String name = source.getName(); 
  
-<code java [enable_line_numbers="true"]> +// Returns true if the source of the command has a certain permission level. 
-dispatcher.register(CommandManager.literal("foo")+// 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)
 </code> </code>
  
-In order to have sub command, one needs to append the next node to the existing node. This is done use the ''then(ArgumentBuilder)'' method which takes in an ''ArgumentBuilder''.+===== Register basic command =====
  
-This creates the command ''foo <bar>'' as shown below.+Commands are registered by registering in ''CommandRegistrationCallback'' in the Fabric API. For information on registering callbacks, please see the [[callbacks]]. 
 + 
 +The event should be registered in your mod's initializer. The callback has three parameters. The ''CommmandDispatcher<S>'' is used to register, parse and execute commands. ''S'' is the type of command source the command dispatcher supports, which is usually ''ServerCommandSource''. The second parameter provides an abstraction to registries which may be passed to certain command argument methods. The third parameter is a ''RegistrationEnvironment'' which identifies the type of server the commands are being registered on.
  
-<code java [enable_line_numbers="true", highlight_lines_extra="2"]+To simplify the code, it is highly recommended to ''static import'' the methods in ''CommandManager'' (see [[#Static Imports]]): 
-dispatcher.register(literal("foo"+<code java> 
-    .then(literal("bar")) +import static net.minecraft.server.command.CommandManager.*;
-);+
 </code> </code>
  
-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 the tree command that are shown later on in this tutorial.+In the mod initializer, we just register the simplest command:
  
-**So let's try running the command** +<code java [enable_line_numbers="true"]> 
- +public class ExampleMod implements ModInitializer { 
-Most likely if you typed ''/foo bar'' in game, the command will fail to run. This is because there is no code for the game to execute when all the required arguments have been met. To fix this, you need to tell the game what to run when the command is being executed using the ''executes(Command)'' method. Below is how the command should look as an example. +  @Override 
- +  public void onInitialize() { 
-<code java [enable_line_numbers="true", highlight_lines_extra="3,4,5,6,7"]> +    CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment-> dispatcher.register(literal("foo")
-dispatcher.register(literal("foo") +
-    .then(literal("bar")+
         .executes(context -> {         .executes(context -> {
-            System.out.println("Called foo with bar");+      // For versions below 1.19, replace "Text.literal" with "new LiteralText"
 +      // For versions below 1.20, remode "() ->" directly. 
 +      context.getSource().sendFeedback(() -> Text.literal("Called /foo with no arguments"), false);
  
-            return 1; +      return 1; 
-        }) +    }))); 
-    ) +  } 
-);+}
 </code> </code>
  
-===== Registering the commands =====+**Please ensure you import the correct static method.** The method ''literal'' is ''CommandManager.literal''. You can also alternatively explicitly write ''CommandManager.literal'' instead of using static imports. ''CommandManager.literal("foo")'' tells brigadier this command has one node, a **literal** called ''foo''.
  
-Registering commands is done by registering a callback using the ''CommandRegistrationCallback''For information on registering callbacks, please see the [[tutorial:callbacks|callbacks article]].+In the ''sendFeedback'' method, the first parameter is the text to be sent, which is a ''Text'' in versions below 1.20or a ''Supplier<Text>'' in 1.20 and above (this is used to avoid instantiating ''Text'' objects when not needed, so please do not use ''Suppliers.ofInstance'' or smiliar methods). The second parameter determines whether to broadcast the feedback to other operators. If the command is to //query// something without actually affecting the world, such as query the current time or some player's score, it should be ''false''. If the command actually //does// something, such as changing the time or modifying someone's score, it should be ''true''. If game rule ''sendCommandFeedback'' is false, you will not accept any feedback. If the sender is modifed through ''/execute as ...'', the feedback is sent to the original sender.
  
-The event should be registered in your mod's initializer. The callback has two parameters. The ''CommmandDispatcher<S>'' is used to registerparse and execute commands. ''S'' is the type of command source the command dispatcher supports. The second parameter is boolean which identifies the type of server the commands are being registered. on is an ''dedicated'' or ''integrated'' (false) server.+If the command fails, instead of calling ''sendFeedback'', you may directly throw a ''CommandSyntaxException'' or ''<yarn class_2164>''. See [[command_exceptions]] for details.
  
 +To execute this command, you must type ''/foo'', which is case-sensitive. If ''/Foo'', ''/FoO'', ''/FOO'', ''/fOO'' or ''/fooo'' is typed instead, the command will not run.
  
-<code java [enable_line_numbers="true"]> +===== Registration environment ===== 
-public class ExampleCommandMod implements ModInitializer { +If desired, you can also make sure a command is only registered under some specific circumstancesfor example, only in the dedicated environment:
-    @Override +
-    public void onInitialize() { +
-        CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> { +
-            ... +
-        }); +
-    } +
-+
-</code> +
- +
-Inside your lambdamethod reference or whatever you have chosenyou will register your commands.+
  
-<code java [enable_line_numbers="true", highlight_lines_extra="5,6,7,8"]>+<yarncode java [enable_line_numbers="true", highlight_lines_extra="5,6,7"]>
 public class ExampleCommandMod implements ModInitializer { public class ExampleCommandMod implements ModInitializer {
     @Override     @Override
     public void onInitialize() {     public void onInitialize() {
-        CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> { +        CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { 
-            dispatcher.register(literal("foo").executes(context -> +            if (environment.field_25423) { 
-                System.out.println("foo"); +                ...;
-                return 1; +
-            })); +
-        }); +
-    } +
-+
-</code> +
- +
-If desired, you can make sure a command is only registered on a dedicated server by checking the ''dedicated'' flag +
- +
- +
-<code java [enable_line_numbers="true", highlight_lines_extra="5,6,7"]> +
-public class ExampleCommandMod implements ModInitializer { +
-    @Override +
-    public void onInitialize() { +
-        CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> { +
-            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 ''literal("foo")''. This also works for getting the value of an argument. This shortens ''StringArgumentType.getString(ctx, "string")'' to ''getString(ctx, "string")''. This also works for Minecraft's own argument types.
  
-<code java [enable_line_numbers="true", highlight_lines_extra="5,6,7"]> +Below is an example of some static imports: 
-public class ExampleCommandMod implements ModInitializer { +<code java [enable_line_numbers="true"]> 
-    @Override +// getString(ctx, "string"
-    public void onInitialize() { +import static com.mojang.brigadier.arguments.StringArgumentType.getString; 
-        CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> { +// word() 
-            if (!dedicated{ +import static com.mojang.brigadier.arguments.StringArgumentType.word; 
-                TestIntegratedCommand.register(dispatcher); + // literal("foo") 
-            } +import static net.minecraft.server.command.CommandManager.literal
-        }); + // argument("bar", word()
-    } +import static net.minecraft.server.command.CommandManager.argument
-}+// Import everything in the CommandManager 
 +import static net.minecraft.server.command.CommandManager.*;
 </code> </code>
  
-===== Arguments =====+Note: Please be sure you use the ''literal'' and ''argument'' from ''CommandManager'' instead of other classes, or you may have issues with generics when trying to compile, because the type parameter ''S'' should be ''ServerCommandSource''. (For client-side commands, use ''ClientCommandManager'' instead.)
  
-Arguments in Brigadier both parse and error check any inputted arguments. +Brigadier'default arguments are at ''com.mojang.brigadier.arguments''
-Minecraft creates some special argument types for it'own use such as the ''EntityArgumentType'' which represents the in-game entity selectors ''@a, @r, @p, @e[type=!player, limit=1, distance=..2]'', or an ''NbtTagArgumentType'' that parses stringified nbt (snbt) and verifies that the input is the correct syntax. +
- +
-**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:commands:requirements|Requirements]]               | Only allow users to execute commands in certain scenarios.                      | +
-| [[tutorials:commands:exceptions  |Exceptions]]                 | Fail execution of a command with a descriptive message and in certain contexts. |                                                                   +
-| [[tutorials:commands:suggestions |Suggestions]]                | Suggesting command input for the client.                                        | +
-| [[tutorials:commands:redirects_aliases|Redirects (Aliases)]]   | Allow use of aliases to execute commands.                                       | +
-| [[tutorials:commands:redirects_chaining|Redirects (Chaining)]] | Allow commands to have repeating elements and flags.                            | +
-| [[tutorials:commands:argument_types|Custom Argument Types]]    | Parse your own arguments into your own objects.                                 | +
- +
-**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 ''CommandSyntaxException''. Brigadier will handle the checked exceptions and forward the proper error message in game for you. +
- +
-==== Issues with generics ==== +
- +
-You may have an issue with generic types once in a while. Verify you are using ''CommandManager.literal(...)'' or ''CommandManager.argument(...)'' instead ''LiteralArgumentBuilder'' or ''RequiredArgumentBuilder'' in your static imports. +
- +
-===== Can I register client side commands? ===== +
- +
-Fabric doesn't currently support client side commands. There is a [[https://github.com/CottonMC/ClientCommands|third-party mod]] by the Cotton team that adds this functionality. +
-There is an open pull request to fabric api which adds thisThat 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 ''CommandManager'' from the server and add anything commands you wish to it's ''CommandDispatcher''+
- +
-After that you need to send the command tree to every player again using ''CommandManager.sendCommandTree(ServerPlayerEntity)''. This is required because the client locally caches the command tree it receives during login (or when operator packets are sent) for local completions rich error messages. +
- +
-==== 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 nodesAfter this, you need to send the command tree to every player again using ''sendCommandTree(ServerPlayerEntity)''. If you don't send the updated command tree, the client may think a command still exists, even though the server will fail execution. +
- +
----- +
- +
-====== 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" licenseThis is the current license of the other wiki articles.+Minecraft's arguments are in ''net.minecraft.command.arguments''. CommandManager is in the package ''net.minecraft.server.command''.
  
-===== Requirements =====+===== Add Requirements =====
  
-Lets say you have a command you only want operators to be able to execute. This is where the ''requires'' method comes into play. The requires method has one argument of a Predicate<ServerCommandSource> which will supply a ServerCommandSource to test with and determine if the CommandSource can execute the command.+Let'say you have a command that you only want operators to be able to execute. This is where the ''requires'' method comes into play. The ''requires'' method has one argument of a ''Predicate<ServerCommandSource>'' which will supply a ''ServerCommandSource'' to test with and determine if the ''CommandSource'' can execute the command.
  
 For example this may look like the following: For example this may look like the following:
Line 278: Line 160:
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
 dispatcher.register(literal("foo") dispatcher.register(literal("foo")
- .requires(source -> source.hasPermissionLevel(4)) +  .requires(source -> source.hasPermissionLevel(2)) 
- .executes(ctx -> { +  .executes(ctx -> { 
- ctx.getSource().sendFeedback(new LiteralText("You are an operator", false)); +    ctx.getSource().sendFeedback(() -> Text.literal("You are an operator"), false); 
- return 1; +    return 1; 
- });+  });
 </code> </code>
  
-This command will only execute if the Source of the command is a level operator at minimum. If the predicate returns falsethen 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 operator.+This command will only execute if the source of the command is a level operator at minimum, //including// command blocksOtherwise, the command is not registered. Also this has the side effect of not showing this command in tab completion to anyone who is not a level operator. This is also why you cannot tab-complete most commands when you did not enable cheating.
  
-Nothing prevents someone from specifying calls to permissions implementations within the ''requires'' block. Just note that if permissions change, you need to re send the command tree.+To create commands that only level 4 operators (//not including// command blocks) can execute, use ''source.hasPermissionLevel(4)''.
  
-===== Exceptions =====+===== Arguments =====
  
-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 executeas well as including richer details of the failure.+Arguments are used in most of commands. Sometimes they can be optional, which means if you do not provide that argumentthe command will also run. One node may have multiple argument typesbut be aware that there is possibility of ambiguity, which should be avoided.
  
-All the exceptions from Brigadier are based on the CommandSyntaxException. The two main types of exceptions Brigadier provides are Dynamic and Simple exception typesof which you must ''create()'' the exception to throw it. These exceptions also allow you to specify the context in which the exception was thrown using ''createWithContext(ImmutableStringReader)''which builds the error message to point to where on the inputted command line the error occured. +In this casewe add one integer argumentand calculate the square of the integer.
-Below is a coin flip command to show an example of exceptions in use.+
  
-<code java [enable_line_numbers="true"]+<code java> 
-dispatcher.register(CommandManager.literal("coinflip") +    dispatcher.register(literal("mul") 
-    .executes(ctx -> { +        .then(argument("value", IntegerArgumentType.integer()) 
-        Random random new Random(); +            .executes(context -> { 
-  +              final int value IntegerArgumentType.getInteger(context, "value"); 
-        if(random.nextBoolean()) { // If heads succeed. +              final int result = value * value; 
-            ctx.getSource().sendMessage(new TranslateableText("coin.flip.heads")) +              context.getSource().sendFeedback(() -> Text.literal("%s × %s = %s".formatted(value, value, result)), false); 
-            return Command.SINGLE_SUCCESS+              return result
-        } +            })));
- +
-        throw new SimpleCommandExceptionType(new TranslateableText("coin.flip.tails")).create(); // Oh no tails, you lose. +
-    }));+
 </code> </code>
  
-Though you are not just limited to a single type of exception as Brigadier also supplies Dynamic exceptions which take additional parameters for context.+In this case, after the word ''/mul'', you should type an integer. For example, if you run ''/mul 3'', you will get the feedback message "3 × 3 = 9". If you type ''/mul'' without arguments, the command cannot be correctly parsed.
  
-<code java [enable_line_numbers="true"]> +Notefor simplicity, ''IntegerArgumentType.integer'' and ''IntegerArgumentType.getInteger'' can be replaced with ''integer'' and ''getInteger'' with static import. This example does not use static imports, in order to be more explicit.
-DynamicCommandExceptionType used_name = new DynamicCommandExceptionType(name -> { +
-    return new LiteralText("The name" + (String) name + " has been used"); +
-}); +
-</code>+
  
-There are more Dynamic exception types which each take a different amount of arguments into account (''Dynamic2CommandExceptionType'', ''Dynamic3CommandExceptionType'', ''Dynamic4CommandExceptionType'', ''DynamicNCommandExceptionType''). +Then we add an optional second argument: 
-You should remember that the Dynamic exceptions takes an object as an argument so you may have to cast the argument for your use. +<code java> 
- +    dispatcher.register(literal("mul") 
-===== Redirects (Aliases) ===== +        .then(argument("value", IntegerArgumentType.integer()) 
- +            .executes(context -> { 
-Redirects are Brigadier's form of aliases. Below is how Minecraft handles /msg have an alias of /tell and /w.  +              final int value = IntegerArgumentType.getInteger(context, "value"); 
- +              final int result = value * value; 
-<code java [enable_line_numbers="true"]> +              context.getSource().sendFeedback(() -> Text.literal("%s × %s = %s".formatted(value, value, result)), false); 
-public static void register(CommandDispatcher<ServerCommandSource> dispatcher) { +              return result; 
-    LiteralCommandNode node = registerMain(dispatcher); // Registers main command +            }) 
-    dispatcher.register(literal("tell") +            .then(argument("value2", IntegerArgumentType.integer()) 
-        .redirect(node)); // Alias 1, redirect to main command +                .executes(context -> { 
-    dispatcher.register(literal("w"+                  final int value = IntegerArgumentType.getInteger(context"value"); 
-        .redirect(node)); // Alias 2, redirect to main command +                  final int value2 = IntegerArgumentType.getInteger(context, "value2")
-+                  final int result = value * value2; 
- +                  context.getSource().sendFeedback(() -> Text.literal("%s × %s = %s".formatted(value, value2, result)), false); 
-public static LiteralCommandNode registerMain(CommandDispatcher<ServerCommandSourcedispatcher) +                  return result; 
-    return dispatcher.register(literal("msg") +                }))));
-    .then(argument("targets", EntityArgumentType.players()) +
-        .then(argument("message", MessageArgumentType.message()) +
-            .executes(ctx -> { +
-                return execute(ctx.getSource()getPlayers(ctx, "targets"), getMessage(ctx, "message")); +
-            })))); +
-}+
 </code> </code>
  
-The redirect tells brigadier to continue parsing the command at another command node.+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 to specify similar executions twice. Therefore, we can create a method that will be used in both executions.
  
-===== Redirects (Chainable Commands===== +<code java> 
-Commands such as ''/execute as @e[type=player] in the_end run tp ~ ~ ~'' are possible because of redirectsBelow is an example of a chainable command:+public class ExampleMod implements ModInitializer { 
 +  @Override 
 +  public void onInitialize() { 
 +    CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> dispatcher.register(literal("mul"
 +        .then(argument("value", IntegerArgumentType.integer()) 
 +            .executes(context -> executeMultiply(IntegerArgumentType.getInteger(context, "value"), IntegerArgumentType.getInteger(context, "value"), context)) 
 +            .then(argument("value2", IntegerArgumentType.integer()) 
 +                .executes(context -> executeMultiply(IntegerArgumentType.getInteger(context, "value"), IntegerArgumentType.getInteger(context, "value2"), context)))))); 
 +  }
  
-<code java [enable_line_numbers="true"]> +  private static int executeMultiply(int valueint value2CommandContext<ServerCommandSource> context
-LiteralCommandNode<ServerCommandSource> root = dispatcher.register(literal("fabric_test")); +    final int result = value * value2
-LiteralCommandNode<ServerCommandSource> root1 = dispatcher.register(literal("fabric_test")  +    context.getSource().sendFeedback(() -> Text.literal("%s × %s = %s".formatted(valuevalue2result)), false); 
-// You can register under the same literal more than onceit 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. +    return result; 
-    .then(literal("extra"+  }
-        .then(literal("long"+
-            .redirect(rootthis::lengthen)) // Return to root for chaining +
-        .then(literal("short"+
-            .redirect(root, this::shorten))) // Return to root for chaining +
-        .then(literal("command"+
-            .executes(ctx -> { +
-                ctx.getSource().sendFeedback(new LiteralText("Chainable Command"), false); +
-                return Command.SINGLE_SUCCESS; +
-}))); +
-</code> +
-The redirect can also modify the CommandSource by use of a ''redirect modifier'' which can be used for builder commands. +
- +
-<code java [enable_line_numbers="true"]> +
-.redirect(rootNode, context -> +
-    return ((ServerCommandSource) context.getSource()).withLookingAt(Vec3ArgumentType.getVec3(context, "pos")); +
-}) +
-</code> +
- +
-===== What can the ServerCommandSource do? ===== +
- +
-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. +
- +
-<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.  This method will require your methods to throw a CommandSyntaxException +
- +
-source.getPosition();  +
-// Get's the sender's position as a Vec3 when the command was sent. This could be the location of the entity/command block or in the case of the console, the world's spawn point. +
- +
-source.getWorld();  +
-// Get's the world the sender is within. The console's world is the same as the default spawn world. +
- +
-source.getRotation();  +
-// Get's the sender's rotation as a Vec2f. +
- +
-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> +
- +
-===== Some example commands examples ===== +
- +
-=== Broadcast a message === +
- +
-<code java [enable_line_numbers="true"]>  +
-public static void register(CommandDispatcher<ServerCommandSource> dispatcher){ +
-    dispatcher.register(literal("broadcast") +
-        .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. +
-            .then(argument("color", ColorArgumentType.color()) +
-                .then(argument("message", greedyString()) +
-                    .executes(ctx -> broadcast(ctx.getSource(), getColor(ctx, "color"), getString(ctx, "message")))))); // You can deal with the arguments out here and pipe them into the command. +
-+
- +
-public static int broadcast(ServerCommandSource sourceFormatting formattingString message+
-    final Text text = new LiteralText(message).formatting(formatting); +
- +
-    source.getMinecraftServer().getPlayerManager().broadcastChatMessage(text, false); +
-    return Command.SINGLE_SUCCESS// Success+
 } }
 </code> </code>
 +===== A sub command =====
  
-==== /giveMeDiamond ====+To add a sub command, you register the first literal node of the command normally.
  
-First the basic code where we register "giveMeDiamond" as a literal and then an executes block to tell the dispatcher which method to run. +<code> 
- +dispatcher.register(literal("foo"))
-<code java [enable_line_numbers="true"]+
-public static LiteralCommandNode register(CommandDispatcher<ServerCommandSource> dispatcher) { // You can also return a LiteralCommandNode for use with possible redirects +
-    return dispatcher.register(literal("giveMeDiamond") +
-        .executes(ctx -> giveDiamond(ctx))); +
-}+
 </code> </code>
  
-Then since we only want to give to players, we check if the CommandSource is a player. But we can use ''getPlayer'' and do both at the same time and throw an error if the source is not a player.+In order to have a sub command, one needs to append the next node to the existing node
  
-<code java [enable_line_numbers="true"]> +This creates the command ''foo <bar>'' as shown below.
-public static int giveDiamond(CommandContext<ServerCommandSource> ctx) throws CommandSyntaxException { +
-    final ServerCommandSource source = ctx.getSource(); +
-  +
-    final PlayerEntity self = source.getPlayer(); // If not a player than the command ends +
-</code> +
- +
-Then we add to the player's inventory, with a check to see if the inventory is full:+
  
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
-    if(!player.inventory.insertStack(new ItemStack(Items.DIAMOND))){ +dispatcher.register(literal("foo"
-        throw new SimpleCommandExceptionType(new TranslatableText("inventory.isfull")).create(); +    .then(literal("bar"
-    }+        .executes(context -> { 
 +            // For versions below 1.19, use ''new LiteralText''
 +            // For versions below 1.20, use directly the ''Text'' object instead of supplier. 
 +            context.getSource().sendFeedback((-> Text.literal("Called foo with bar"), false);
  
-    return 1; +            return 1; 
-}+        }
 +    ) 
 +);
 </code> </code>
  
-==== Antioch ==== +Similar to argumentssub command nodes can also be set optionalIn the following case, both ''/foo'' and ''/foo bar'' will be valid.
-...lobbest thou thy Holy Hand Grenade of Antioch towards thy foe. +
-who being naughty in My sightshall 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="true"]> <code java [enable_line_numbers="true"]>
-public static void register(CommandDispatcher<ServerCommandSource> dispatcher) { +dispatcher.register(literal("foo") 
-    dispatcher.register(literal("antioch") +    .executes(context -> { 
-        .then(required("location", BlockPosArgumentType.blockPos() +        context.getSource().sendFeedback(() -> Text.literal("Called foo without bar"), false); 
-            .executes(ctx -> antioch(ctx.getSource(), BlockPosArgument.getBlockPos(ctx, "location"))))+        return 1; 
-        .executes(ctx -> antioch(ctx.getSource(), null))); +    }) 
-}+    .then(literal("bar") 
 +        .executes(context -> 
 +            context.getSource().sendFeedback((-> Text.literal("Called foo with bar"), false); 
 +            return 1; 
 +        }
 +    ) 
 +);
 </code> </code>
 +====== 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="true"]+^ Page                                                           ^ Description                                                                     ^ 
-public static int antioch(ServerCommandSource source, BlockPos blockPos) throws CommandSyntaxException { +[[command_exceptions  |Exceptions]]                 | Fail execution of a command with a descriptive message and in certain contexts|                                                                   
-    if(blockPos == null) { +| [[command_suggestions|Suggestions]]                | Suggesting command input for the client                                       | 
-        // 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+| [[command_redirects|Redirects]]   | Allow use of aliases or repeating elements to execute commands                                  | 
-        // This class is used as an example and actually doesn't exist yet+| [[command_argument_types|Custom Argument Types]]    | Parse your own arguments into your own objects.                                 | 
-        blockPos = LocationUtil.calculateCursorOrThrow(source, source.getRotation()); +| [[command_examples|Examples]] | Some example commands |
-    }+
  
-    final TntEntity tnt new TntEntity(source.getWorld(), blockPos.getX(), blockPos.getY(), blockPos.getZ(), null); +====== FAQ ======
-    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("who being naughty in My sight, shall snuff it.").formatting(Formatting.RED), false); +
-    source.getWorld().spawnEntity(tnt); +
-    return 1; +
-+
-</code>+
  
-==== More examples coming soon ====+===== Why does my code not compile =====
  
-===== Custom Argument Types =====+There are several immediate possibilities for why this could occur.
  
-Brigadier has support for custom argument types and this section goes into showing how to create simple argument type+  * **Catch or throw a CommandSyntaxException:** ''CommandSyntaxException'' is not a ''RuntimeException''. If you throw it, where it is throwed should be in methods that throws ''CommandSyntaxException'' in method signatures, or be caught. Brigadier will handle the checked exceptions and forward the proper error message in game for you. 
 +  * **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 ''CommandManager.literal(...)'' or ''CommandManager.argument(...)'' instead of ''LiteralArgumentBuilder.literal'' or ''RequiredArgumentBuilder.argument'' in your static imports. 
 +  * **Check ''sendFeedback'' method:** You may have forgotten to provide boolean as the second argument. Also remember that, since 1.20, the first parameter is ''Supplier<Text>'' instead of ''Text''
 +  * **''Command'' should return an integer:** When registering commands, the ''executes'' method accepts a ''Command'' object, which is usually a lambda. The lambda should return an integer, instead of other types.
  
-Warning: 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.+===== Can I register client side commands? =====
  
-For this example we will create UuidArgumentType.+Fabric has ''ClientCommandManager'' that can be used to register client side commandsThe code should exist only in client-side codes. Example:
  
-First create a class which extends ''ArgumentType''. Note that ArgumentType is a generic, so the generic will define what type the ArgumentType will return 
- 
-<code java [enable_line_numbers="true"]> 
-public class UuidArgumentType implements ArgumentType<UUID> { 
-</code> 
- 
-ArgumentType requires you to implement the ''parse'' method, the type it returns will match with the Generic type. 
 <code java> <code java>
-@Override +    ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess-> dispatcher.register(ClientCommandManager.literal("foo_client"
-public UUID parse(StringReader readerthrows CommandSyntaxException {+        .executes(context -> { 
 +              context.getSource().sendFeedback(Text.literal("The command is executed in the client!")); 
 +              return 1; 
 +            } 
 +        )));
 </code> </code>
  
-This method is where all of your parsing will occur. Either this method will return the object based on the arguments provided in the command line or throw a CommandSyntaxException and parsing will fail.+If you need to open a screen in the client command execution, instead of directly calling ''client.setScreen(...)'', you should call ''%%client.execute(() -> client.setScreen(...))%%''. The variable ''client'' can be obtained with ''context.getSource().getClient()''.
  
-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="true"]> +You can do this but it is not recommendedYou would get the ''CommandManager'' from the server and add anything commands you wish to it's ''CommandDispatcher''.
-int argBeginning = reader.getCursor(); // The starting position of the cursor is at the beginning of the argument. +
-if (!reader.canRead()) { +
-    reader.skip(); +
-+
-</code>+
  
-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 ''{'' on the command line will require it to be closed. For a UUID we will just figure out what cursor position the argument ends at.+After that you need to send the command tree to every player again using ''CommandManager.sendCommandTree(ServerPlayerEntity)''. This is required because the client locally caches the command tree it receives during login (or when operator packets are sent) for local completions rich error messages.
  
-<code java [enable_line_numbers="true"]> +===== Can I unregister commands in runtime? =====
-while (reader.canRead() && reader.peek() !' ') { // peek provides the character at the current cursor position. +
-    reader.skip(); // Tells the StringReader to move it's cursor to the next position. +
-+
-</code>+
  
-Then we will ask the StringReader what the current position of the cursor is an substring our argument out of the command line.+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 ''sendCommandTree(ServerPlayerEntity)''. If you don't send the updated command tree, the client may think a command still exists, even though the server will fail execution.
  
-<code java [enable_line_numbers="true"]>String uuidString reader.getString().substring(argBeginningreader.getCursor());</code>+===== Can I execute command without typing in game? ===== 
 +Yes! You canBefore trying the next codetake note it works on Fabric 0.91.6+1.20.2 and it was not tested in other versions.
  
-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.+Here is the code example
  
-<code java [enable_line_numbers="true"]+<code java> 
-try +    private void vanillaCommandByPlayer(World world, BlockPos pos, String command) 
-    UUID uuid UUID.fromString(uuidString); // Now our actual logic. +        PlayerEntity player world.getClosestPlayer(pos.getX(), pos.getY()pos.getZ(), 5, false); 
-    return uuid; // And we return our typein this case the parser will consider this argument to have parsed properly and then move on+        if (player != null) { 
-    } catch (Exception ex) { +            CommandManager commandManager = Objects.requireNonNull(player.getServer()).getCommandManager(); 
-    // UUIDs can throw an exception when made by a string, so we catch the exception and repackage it into a CommandSyntaxException type. +            ServerCommandSource commandSource = player.getServer().getCommandSource()
-    // Create with context tells Brigadier to supply some context to tell the user where the command failed at. +            commandManager.executeWithPrefix(commandSource, command); 
-    // Though normal create method could be used+        } 
-    throw new SimpleCommandExceptionType(new LiteralText(ex.getMessage())).createWithContext(reader); +    }
-}+
 </code> </code>
  
-The ArgumentType is donehowever your client will refuse the parse the argument and throw an errorThis is because the server will tell the client what argument type the command node isAnd the client will not parse any argument types it does not know how to parseTo fix this we need to register an ArgumentSerializer.  +Firstyou need a CommandManager<ServerCommandSource>. 
-Within your ModInitializerFor more complex argument types, you may need to create your own ArgumentSerializer.+Second, you need the ServerCommandSource. 
 +Then, u can call some CommandManager public methods like commandeManader.execute(.execute need ParseResults<ServerCommandSource>) 
 +But commandeManader.executeWithPrefix allow you to use String. You can also put the slash (/).
  
-<code java [enable_line_numbers="true"]> +So... have fun!
-ArgumentTypes.register("mymod:uuid", UuidArgumentType.class, new ConstantArgumentSerializer(UuidArgumentType::uuid));  +
-// The argument should be what will create the ArgumentType. +
-</code> +
- +
-And here is the whole ArgumentType: +
- +
-<file java UuidArgumentType.java [enable_line_numbers="true"]> +
- +
-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<UUID>+
-    public static UuidArgumentType uuid() { +
-        return new UuidArgumentType(); +
-    } +
- +
-    public static <S> UUID getUuid(String name, CommandContext<S> context) { +
-        // 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, UUID.class); +
-    } +
- +
-    private static final Collection<String> EXAMPLES = SystemUtil.consume(new ArrayList<>(), list -> { +
-        list.add("765e5d33-c991-454f-8775-b6a7a394c097"); // i509VCB: Username The_1_gamers +
-        list.add("069a79f4-44e9-4726-a5be-fca90e38aaf5"); // Notch +
-        list.add("61699b2e-d327-4a01-9f1e-0ea8c3f06bc6"); // Dinnerbone +
-    }); +
- +
-    @Override +
-    public UUID parse(StringReader reader) throws CommandSyntaxException { +
-        int argBeginning = reader.getCursor(); // The starting position of the cursor is at the beginning of the argument. +
-        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(); // 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. +
-        String uuidString = reader.getString().substring(argBeginning, reader.getCursor()); +
-        try { +
-            UUID uuid = UUID.fromString(uuidString); // Now our actual logic. +
-            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<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>+
tutorial/commands.1606292402.txt.gz · Last modified: 2020/11/25 08:20 by i509vcb