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/06/14 02:01] – i509vcb | tutorial:commands [2020/11/25 08:07] – Move static imports up and some rewords i509vcb | ||
---|---|---|---|
Line 6: | Line 6: | ||
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.14.4. Some mappings may have changed in yarn, but all code should still be applicable. | ||
- | ===== Registering Commands | + | ===== What is Brigadier? |
- | Registering commands | + | Brigadier |
- | The event should be registered in your mod's initializer. | + | |
- | The dedicated parameter if true will tell event listeners that the server commands are being registered on is a '' | + | The source code for brigadier |
- | Below are a few examples of how the commands | + | |
+ | ===== What is a command? ===== | ||
+ | |||
+ | Brigadier requires you specify the '' | ||
+ | A '' | ||
+ | |||
+ | The single method in '' | ||
+ | |||
+ | A command can be implemented in several ways as shown below: | ||
+ | |||
+ | **__As a lambda__** | ||
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | // Method reference | + | Command< |
- | CommandRegistrationCallback.EVENT.register(TutorialCommands:: | + | |
+ | }; | ||
+ | </ | ||
- | // Using a lambda | + | **__As an anonymous class__** |
- | CommandRegistrationCallback.EVENT.register((dispatcher, | + | <code java [enable_line_numbers=" |
- | // This command | + | Command< |
- | TutorialCommand.register(dispatcher); | + | |
- | | + | |
- | // This command will only be registered on a dedicated server | + | |
- | TutorialHelpCommand.register(dispatcher); | + | |
- | } else { | + | |
- | | + | |
- | // Commands which call client only classes and methods should be registered in your ClientModInitializer | + | |
- | IntegratedTutorialHelpCommand.register(dispatcher); | + | |
} | } | ||
- | }); | + | } |
+ | </ | ||
- | // Or register directly | + | **__Implemented as a class__** |
- | CommandRegistrationCallback.EVENT.register((dispatcher, | + | <code java [enable_line_numbers=" |
- | | + | final class XYZCommand implements Command< |
- | }); | + | |
+ | public int run(CommandContext< | ||
+ | return 0; | ||
+ | } | ||
+ | } | ||
</ | </ | ||
- | ===== Some basic commands | + | **__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< | ||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | The '' | ||
+ | |||
+ | 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 '' | ||
+ | |||
+ | ===== A basic command | ||
Below is a command that contains no arguments: | Below is a command that contains no arguments: | ||
Line 52: | Line 80: | ||
'' | '' | ||
To execute this command, one must type ''/ | To execute this command, one must type ''/ | ||
+ | |||
+ | ===== Static Imports ===== | ||
+ | Typing out '' | ||
+ | |||
+ | Below is an example of some static imports: | ||
+ | <code java [enable_line_numbers=" | ||
+ | // getString(ctx, | ||
+ | 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 | ||
+ | import static net.minecraft.server.command.CommandManager.*; | ||
+ | </ | ||
+ | |||
+ | Note: Please be sure you use the '' | ||
+ | |||
+ | Brigadier' | ||
+ | |||
+ | Minecraft' | ||
+ | CommandManager is in the package '' | ||
==== A sub command ==== | ==== A sub command ==== | ||
Line 66: | Line 118: | ||
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | dispatcher.register(CommandManager.literal(" | + | dispatcher.register(literal(" |
- | .then(CommandManager.literal(" | + | .then(literal(" |
); | ); | ||
</ | </ | ||
- | 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 | + | 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 |
**So let's try running the command** | **So let's try running the command** | ||
- | Most likely if you typed ''/ | + | Most likely if you typed ''/ |
- | Brigadier requires you specify the '' | + | <code java [enable_line_numbers=" |
- | To specify the command to be run, you must use the '' | + | dispatcher.register(literal(" |
- | + | .then(literal(" | |
- | The executes method takes a '' | + | |
- | + | ||
- | 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 '' | + | |
- | + | ||
- | <code java [enable_line_numbers=" | + | |
- | dispatcher.register(CommandManager.literal(" | + | |
- | .then(CommandManager.literal(" | + | |
.executes(context -> { | .executes(context -> { | ||
System.out.println(" | System.out.println(" | ||
Line 96: | Line 141: | ||
</ | </ | ||
- | The command trees above one builds specify the arguments and paths to these '' | + | ===== Registering the commands |
- | ===== Brigadier Explained | + | |
- | Brigadier starts with the '' | + | Registering commands is done by registering a callback using the '' |
- | Command nodes are similar | + | The event should be registered in your mod's initializer. The callback has two parameters. The '' |
- | The execute blocks specify the command to be ran. As Brigadier' | ||
- | ==== Command Context ==== | + | <code java [enable_line_numbers=" |
+ | public class ExampleCommandMod implements ModInitializer { | ||
+ | @Override | ||
+ | public void onInitialize() { | ||
+ | CommandRegistrationCallback.EVENT.register((dispatcher, | ||
+ | ... | ||
+ | }); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
- | When a command is ran, Brigadier provides a '' | + | Inside your lambda, method reference or whatever you have chosen, you will register your commands. |
- | The command context contains all arguments and other objects such as the inputted String and the '' | + | |
- | ==== Arguments ==== | + | <code java [enable_line_numbers=" |
+ | public class ExampleCommandMod implements ModInitializer { | ||
+ | @Override | ||
+ | public void onInitialize() { | ||
+ | CommandRegistrationCallback.EVENT.register((dispatcher, | ||
+ | dispatcher.register(literal(" | ||
+ | System.out.println(" | ||
+ | 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); | ||
+ | } | ||
+ | }); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | And vice versa | ||
+ | |||
+ | <code java [enable_line_numbers=" | ||
+ | public class ExampleCommandMod implements ModInitializer { | ||
+ | @Override | ||
+ | public void onInitialize() { | ||
+ | CommandRegistrationCallback.EVENT.register((dispatcher, | ||
+ | if (!dedicated) { | ||
+ | TestIntegratedCommand.register(dispatcher); | ||
+ | } | ||
+ | }); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Arguments | ||
Arguments in Brigadier both parse and error check any inputted 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 '' | Minecraft creates some special argument types for it's own use such as the '' | ||
- | ==== Static Imports ==== | + | **TODO:** Go into more detail on how to use arguments |
- | You could type out '' | + | |
- | This also works for Minecraft' | + | |
- | And your imports would look something like this: | + | ====== Advanced concepts ====== |
- | <code java [enable_line_numbers=" | + | |
- | // getString(ctx, | + | |
- | 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 | + | |
- | import static net.minecraft.server.command.CommandManager.*; | + | |
- | </ | + | |
- | Note: Please be sure you use the '' | + | Below are links to the articles about more complex concepts used in brigadier. |
- | Brigadier' | + | ^ Page ^ Description |
+ | | [[tutorials: | ||
+ | | [[tutorials: | ||
+ | | [[tutorials: | ||
+ | | [[tutorials: | ||
+ | | [[tutorials: | ||
+ | | [[tutorials: | ||
- | Minecraft' | + | **TODO:** Sections |
- | CommandManager is in '' | + | |
- | ==== Suggestions | + | ====== FAQ ====== |
- | Suggestions can be provided to the client to recommend what to input into the command. This is used for Scoreboards and Loot Tables ingame. The game stores these in the SuggestionProviders. A few examples of Minecraft' | + | ===== Why does my command |
- | < | + | |
- | SUMMONABLE_ENTITIES | + | |
- | AVAILIBLE_SOUNDS | + | |
- | ALL_RECIPES | + | |
- | ASK_SERVER | + | |
- | </ | + | |
- | Loot tables specify their own SuggestionProvider inside '' | + | There are two immediate possibilities |
- | The example below is a dynamically changing SuggestionProvider that lists several words for a StringArgumentType to demonstrate how it works: | + | ==== Catch or throw a CommandSyntaxException ==== |
- | <code java [enable_line_numbers=" | + | |
- | public static SuggestionProvider< | + | |
- | return (ctx, builder) -> getSuggestionsBuilder(builder, | + | |
- | } | + | |
- | + | ||
- | private static CompletableFuture< | + | |
- | String remaining | + | |
- | + | ||
- | if(list.isEmpty()) { // If the list is empty then return no suggestions | + | |
- | return Suggestions.empty(); | + | |
- | } | + | |
- | + | ||
- | for (String str : list) { // Iterate through the supplied list | + | |
- | if (str.toLowerCase(Locale.ROOT).startsWith(remaining)) { | + | |
- | builder.suggest(str); | + | |
- | } | + | |
- | } | + | |
- | 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 | + | The solution |
- | Though remember these are suggestions. The inputted command may not contain an argument you suggested so arguments are parsed without consideration for suggestions. | + | ==== Issues with generics ==== |
- | To use the suggestion | + | You may have an issue with generic types once in a while. Verify |
- | <code java [enable_line_numbers=" | + | ===== Can I register client side commands? ===== |
- | argument(argumentName, word()) | + | |
- | .suggests(CompletionProviders.suggestedStrings()) | + | Fabric doesn' |
- | .then(/*Rest of the command*/)); | + | 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 and remove the nodes. After this, you need to send the command tree to every player again using '' | ||
+ | |||
+ | ---- | ||
+ | |||
+ | ====== 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.__** | ||
- | ==== Requiring Permissions | + | ===== Requirements ===== |
Lets say you have a command you only want operators to be able to execute. This is where the '' | Lets say you have a command you only want operators to be able to execute. This is where the '' | ||
Line 205: | Line 285: | ||
Nothing prevents someone from specifying calls to permissions implementations within the '' | Nothing prevents someone from specifying calls to permissions implementations within the '' | ||
- | ==== Exceptions ==== | + | ===== 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. | 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. | ||
Line 237: | Line 317: | ||
You should remember that the Dynamic exceptions takes an object as an argument so you may have to cast the argument for your use. | 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 (Aliases) |
Redirects are Brigadier' | Redirects are Brigadier' | ||
Line 262: | Line 342: | ||
The redirect tells brigadier to continue parsing the command at another command node. | The redirect tells brigadier to continue parsing the command at another command node. | ||
- | ==== Redirects (Chainable Commands) ==== | + | ===== Redirects (Chainable Commands) |
Commands such as ''/ | Commands such as ''/ | ||
Line 288: | Line 368: | ||
</ | </ | ||
- | ===== ServerCommandSource ===== | + | ===== What can the ServerCommandSource |
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. | 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. | ||
Line 327: | Line 407: | ||
</ | </ | ||
- | ===== A few examples ===== | + | ===== Some example commands |
=== Broadcast a message === | === Broadcast a message === | ||
Line 348: | Line 428: | ||
</ | </ | ||
- | === / | + | ==== / |
First the basic code where we register " | First the basic code where we register " | ||
Line 379: | Line 459: | ||
</ | </ | ||
- | === Antioch === | + | ==== Antioch |
...lobbest thou thy Holy Hand Grenade of Antioch towards thy foe. | ...lobbest thou thy Holy Hand Grenade of Antioch towards thy foe. | ||
who being naughty in My sight, shall snuff it. | who being naughty in My sight, shall snuff it. | ||
Line 416: | Line 496: | ||
</ | </ | ||
- | === Finding Biomes via Command | + | ==== More examples |
- | + | ||
- | This example shows examples | + | |
- | <code java [enable_line_numbers=" | + | |
- | public class CommandLocateBiome { | + | |
- | // First make method to register | + | |
- | public static void register(CommandDispatcher< | + | |
- | LiteralCommandNode< | + | |
- | .then(argument(" | + | |
- | .then(argument(" | + | |
- | .executes(ctx -> execute(ctx.getSource(), | + | |
- | .executes(ctx -> execute(ctx.getSource(), | + | |
- | // Register a redirect for /biome alias | + | |
- | dispatcher.register(literal(" | + | |
- | .redirect(basenode)); | + | |
- | } | + | |
- | + | ||
- | private static int execute(ServerCommandSource source, Identifier biomeId, int range) throws CommandSyntaxException { | + | |
- | Biome biome = Registry.BIOME.get(biomeId); | + | |
- | + | ||
- | if(biome == null) { | + | |
- | // 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(); | + | |
- | BiomeSource bsource = world.getChunkManager().getChunkGenerator().getBiomeSource(); | + | |
- | BlockPos loc = new BlockPos(source.getPosition()); | + | |
- | + | ||
- | // This call will likely block the main thread | + | |
- | BlockPos pos = bsource.locateBiome(loc.getX(), | + | |
- | + | ||
- | // If null, a biome was not found | + | |
- | if(pos == null) { | + | |
- | throw new SimpleCommandExceptionType(new TranslatableText(" | + | |
- | } | + | |
- | + | ||
- | int distance = MathHelper.floor(getDistance(loc.getX(), | + | |
- | // Popup text that can suggest commands. This is the exact same system that /locate uses. | + | |
- | Text teleportButtonPopup | + | |
- | style.setColor(Formatting.GREEN).setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, | + | |
- | }); | + | |
- | + | ||
- | source.sendFeedback(new TranslatableText(" | + | |
- | + | ||
- | 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 * int_5 + int_6 * int_6)); | + | |
- | } | + | |
- | + | ||
- | + | ||
- | + | ||
- | 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 -> builder.suggest(identifier.toString(), | + | |
- | return builder.buildFuture(); | + | |
- | }); | + | |
- | + | ||
- | } | + | |
- | </ | + | |
===== Custom Argument Types ===== | ===== Custom Argument Types ===== | ||
Line 619: | Line 631: | ||
} | } | ||
</ | </ | ||
- | ===== 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? === | ||
- | |||
- | 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. | ||
- | |||
- | After that you will need to send the command tree to every player again using '' | ||
- | |||
- | === Can I unregister commands runtime? === | ||
- | |||
- | You can also do this but it is very unstable and could cause unwanted side effects. Lets just say it involves a bunch of Reflection. | ||
- | |||
- | Once again you will need to send the command tree to every player again using '' | ||
- | |||
- | === Can I register client side commands? === | ||
- | |||
- | Well Fabric currently doesn' | ||
- | https:// |
tutorial/commands.txt · Last modified: 2024/02/23 14:22 by allen1210