From e7df266ea4fa7e856597d064864612a690b0ac0b Mon Sep 17 00:00:00 2001 From: Magnus Jensen Date: Sat, 11 Jul 2026 18:39:18 +0200 Subject: [PATCH 1/4] Add simple commands docs --- docs/advanced/_category_.json | 4 +- docs/commands/_category_.json | 4 ++ docs/commands/index.md | 110 ++++++++++++++++++++++++++++++++++ docs/misc/_category_.json | 4 +- 4 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 docs/commands/_category_.json create mode 100644 docs/commands/index.md diff --git a/docs/advanced/_category_.json b/docs/advanced/_category_.json index 4a462c5f9..c3dd22baf 100644 --- a/docs/advanced/_category_.json +++ b/docs/advanced/_category_.json @@ -1,4 +1,4 @@ { - "label": "Advanced Topics", - "position": 13 + "label": "Advanced Topics", + "position": 14 } \ No newline at end of file diff --git a/docs/commands/_category_.json b/docs/commands/_category_.json new file mode 100644 index 000000000..5a7b6159d --- /dev/null +++ b/docs/commands/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Commands", + "position": 13 +} \ No newline at end of file diff --git a/docs/commands/index.md b/docs/commands/index.md new file mode 100644 index 000000000..8a161b1c8 --- /dev/null +++ b/docs/commands/index.md @@ -0,0 +1,110 @@ +# Commands + +Commands are text-based actions triggered from chat by prefixing a message with a slash (`/`), from the server console, from command blocks, or from [functions]. A command performs some effect in the game, such as modifying the world, querying state, or exposing debug utilities. A mod can define its own commands to run dynamic actions when invoked. + +Command definitions are hierarchical. Minecraft builds commands on Mojang's [Brigadier] library, which models each command as a tree of nodes rooted at a dispatcher. A **literal** node matches a fixed keyword, such as `mymod` or `reload` in `/mymod reload`; an **argument** node matches a typed, named input, such as a number or an entity selector. Nodes are joined through `#then` and made executable through `#executes`, and a node may hold execution logic, child nodes, or both. This nesting produces the tree of subcommands. + +An argument is used in two places. It is **declared** while building the tree, by supplying a name and an `ArgumentType`, and its parsed value is **accessed** inside the execution logic from the `CommandContext`, using that same name. The name is the contract between the two. The sections below cover each half in turn. + +## Registering Commands + +A command tree is passed to `CommandDispatcher#register` during the [`RegisterCommandsEvent`][event] like so: + +```java +@SubscribeEvent // on the game event bus +public static void registerCommands(RegisterCommandsEvent event) { + event.getDispatcher().register( + Commands.literal("mymod") + .then(Commands.literal("reload") + .executes(context -> { + // Perform the command logic here + return Command.SINGLE_SUCCESS; + }) + .then(Commands.literal("force") + .executes(context -> { + // Perform the command logic here + return Command.SINGLE_SUCCESS; + }) + ) + ) + ); +} +``` + +`Commands#literal` starts a literal builder, `#then` attaches a child node, and `#executes` supplies a `Command` callback whose returned `int` is the result count reported back to the caller. The example above registers `/mymod reload` and `/mymod reload force`. + +:::note +For more advanced command trees that requires registry access, you can access `RegisterCommandsEvent#getBuildContext` to get a `CommandBuildContext` that can be passed to argument types that require it. +::: + +## Arguments + +An argument node is added with `Commands#argument`, supplying the argument name and an `ArgumentType`. The parsed value is read back inside `#executes` through the static getter that pairs with the chosen type, keyed by the same name. + +```java +Commands.literal("give") + .then(Commands.argument("count", IntegerArgumentType.integer(1)) + .executes(context -> { + // Access the value declared above as "count" + int count = IntegerArgumentType.getInteger(context, "count"); + // Use count + return count; + })) +``` + +Every `ArgumentType` provides a builder used when declaring the argument and most often a static getter used when accessing the parsed value. The getter throws if the supplied name does not match a declared argument on the current path, which is why the declaration name and the access name must agree. + +_If an `ArgumentType` does not provide a static getter, the parsed value can be accessed through `CommandContext#getArgument` with the argument name and the expected class._ + +:::note +An argument is not made optional through a flag. Instead, `#executes` is attached at more than one depth of the tree: once on the parent node for the case where the argument is absent, and again on the argument node for the case where it is provided. +::: + +## Argument Types + +The following tables list common argument types. Each row pairs the builder used to declare the argument with the static getter used to access the parsed value. + +### Brigadier + +Brigadier provides the primitive types, found in `com.mojang.brigadier.arguments`. These are synchronized to the client automatically. + +| Argument Type | Declares (builder) | Accesses (getter) | Description | +| --------------------- | -------------------------------------- | ----------------- | -------------------------------------------------------- | +| `BoolArgumentType` | `#bool` | `#getBool` | A boolean. | +| `IntegerArgumentType` | `#integer` (optional min and max) | `#getInteger` | A 32-bit integer. | +| `LongArgumentType` | `#longArg` (optional min and max) | `#getLong` | A 64-bit integer. | +| `FloatArgumentType` | `#floatArg` (optional min and max) | `#getFloat` | A single-precision decimal. | +| `DoubleArgumentType` | `#doubleArg` (optional min and max) | `#getDouble` | A double-precision decimal. | +| `StringArgumentType` | `#word`, `#string`, or `#greedyString` | `#getString` | A single word, a quotable string, or the remaining text. | + +### Built-in Minecraft + +Minecraft adds game-specific argument types, found in `net.minecraft.commands.arguments`. + +_This list is not exhaustive._ + +| Argument Type | Declares (builder) | Accesses (getter) | Description | +| --------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | +| `EntityArgument` | `#entity`, `#entities`, `#player`, `#players` | `#getEntity`, `#getEntities`, `#getOptionalEntities`, `#getPlayer`, `#getPlayers`, `#getOptionalPlayers` | A single entity, player, or multiple entities, and players via a selector. | +| `BlockPosArgument` | `#blockPos` | `#getLoadedBlockPos`, `#getBlockPos` | A block position. | +| `Vec3Argument` | `#vec3` | `#getVec3` | A position in the world. | +| `ItemArgument` | `#item` | `#getItem` | An item stack, including data components. | +| `BlockStateArgument` | `#block` | `#getBlock` | A block state, including block entity data. | +| `ResourceArgument` | `#resource` | `#getResource` | A namespaced identifier for a given registry. | +| `ResourceKeyArgument` | `#key` | `#getRegistryKey` | A resource key for a given registry. | + +### NeoForge + +NeoForge adds additional argument types in `net.neoforged.neoforge.server.command`. These are synchronized to the client automatically. + +| Argument Type | Declares (builder) | Accesses (getter) | Description | +| --------------- | ------------------ | ------------------------------------------------------------------- | -------------- | +| `EnumArgument` | `#enumArgument` | `CommandContext#getArgument` with the keyed name and the enum class | An enum value. | +| `ModIdArgument` | `#modIdArgument` | `CommandContext#getArgument` with the keyed name and `String.class` | A mod ID. | + +[Brigadier]: https://github.com/Mojang/brigadier +[NeoForge]: https://github.com/neoforged/NeoForge/ +[Parchment]: https://github.com/ParchmentMC/Parchment +[registries]: ../concepts/registries.md +[event]: ../concepts/events.md +[functions]: https://minecraft.wiki/w/Function_(Java_Edition) diff --git a/docs/misc/_category_.json b/docs/misc/_category_.json index 78da091bd..eb4a357ef 100644 --- a/docs/misc/_category_.json +++ b/docs/misc/_category_.json @@ -1,4 +1,4 @@ { - "label": "Miscellaneous", - "position": 14 + "label": "Miscellaneous", + "position": 15 } \ No newline at end of file From 342cbc1c1852b55e9aa16d6ea176ef606480fb49 Mon Sep 17 00:00:00 2001 From: Magnus Jensen Date: Sat, 11 Jul 2026 18:59:23 +0200 Subject: [PATCH 2/4] small tweaks --- docs/commands/index.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/docs/commands/index.md b/docs/commands/index.md index 8a161b1c8..85beff73f 100644 --- a/docs/commands/index.md +++ b/docs/commands/index.md @@ -34,7 +34,7 @@ public static void registerCommands(RegisterCommandsEvent event) { `Commands#literal` starts a literal builder, `#then` attaches a child node, and `#executes` supplies a `Command` callback whose returned `int` is the result count reported back to the caller. The example above registers `/mymod reload` and `/mymod reload force`. :::note -For more advanced command trees that requires registry access, you can access `RegisterCommandsEvent#getBuildContext` to get a `CommandBuildContext` that can be passed to argument types that require it. +For more advanced command trees that require registry access, `RegisterCommandsEvent#getBuildContext` provides a `CommandBuildContext` that can be passed to argument types that require it. ::: ## Arguments @@ -54,7 +54,7 @@ Commands.literal("give") Every `ArgumentType` provides a builder used when declaring the argument and most often a static getter used when accessing the parsed value. The getter throws if the supplied name does not match a declared argument on the current path, which is why the declaration name and the access name must agree. -_If an `ArgumentType` does not provide a static getter, the parsed value can be accessed through `CommandContext#getArgument` with the argument name and the expected class._ +If an `ArgumentType` does not provide a static getter, the parsed value can be accessed through `CommandContext#getArgument` with the argument name and the expected class. :::note An argument is not made optional through a flag. Instead, `#executes` is attached at more than one depth of the tree: once on the parent node for the case where the argument is absent, and again on the argument node for the case where it is provided. @@ -66,7 +66,7 @@ The following tables list common argument types. Each row pairs the builder used ### Brigadier -Brigadier provides the primitive types, found in `com.mojang.brigadier.arguments`. These are synchronized to the client automatically. +Brigadier provides the primitive types, found in `com.mojang.brigadier.arguments`. These types are synchronized to the client automatically. | Argument Type | Declares (builder) | Accesses (getter) | Description | | --------------------- | -------------------------------------- | ----------------- | -------------------------------------------------------- | @@ -79,9 +79,7 @@ Brigadier provides the primitive types, found in `com.mojang.brigadier.arguments ### Built-in Minecraft -Minecraft adds game-specific argument types, found in `net.minecraft.commands.arguments`. - -_This list is not exhaustive._ +Minecraft adds game-specific argument types, found in `net.minecraft.commands.arguments`. This is not an exhaustive list. | Argument Type | Declares (builder) | Accesses (getter) | Description | | --------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | @@ -103,8 +101,5 @@ NeoForge adds additional argument types in `net.neoforged.neoforge.server.comman | `ModIdArgument` | `#modIdArgument` | `CommandContext#getArgument` with the keyed name and `String.class` | A mod ID. | [Brigadier]: https://github.com/Mojang/brigadier -[NeoForge]: https://github.com/neoforged/NeoForge/ -[Parchment]: https://github.com/ParchmentMC/Parchment -[registries]: ../concepts/registries.md [event]: ../concepts/events.md [functions]: https://minecraft.wiki/w/Function_(Java_Edition) From a8248d1ead5ec0b22cbaf5ca6cf8751e5a428fed Mon Sep 17 00:00:00 2001 From: Magnus Jensen Date: Sun, 26 Jul 2026 18:26:01 +0200 Subject: [PATCH 3/4] review tweaks --- docs/commands/index.md | 49 ++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/docs/commands/index.md b/docs/commands/index.md index 85beff73f..69405387b 100644 --- a/docs/commands/index.md +++ b/docs/commands/index.md @@ -54,11 +54,11 @@ Commands.literal("give") Every `ArgumentType` provides a builder used when declaring the argument and most often a static getter used when accessing the parsed value. The getter throws if the supplied name does not match a declared argument on the current path, which is why the declaration name and the access name must agree. +:::note If an `ArgumentType` does not provide a static getter, the parsed value can be accessed through `CommandContext#getArgument` with the argument name and the expected class. +::: -:::note An argument is not made optional through a flag. Instead, `#executes` is attached at more than one depth of the tree: once on the parent node for the case where the argument is absent, and again on the argument node for the case where it is provided. -::: ## Argument Types @@ -66,34 +66,38 @@ The following tables list common argument types. Each row pairs the builder used ### Brigadier -Brigadier provides the primitive types, found in `com.mojang.brigadier.arguments`. These types are synchronized to the client automatically. +Brigadier provides the primitive types, found in `com.mojang.brigadier.arguments`. -| Argument Type | Declares (builder) | Accesses (getter) | Description | -| --------------------- | -------------------------------------- | ----------------- | -------------------------------------------------------- | -| `BoolArgumentType` | `#bool` | `#getBool` | A boolean. | -| `IntegerArgumentType` | `#integer` (optional min and max) | `#getInteger` | A 32-bit integer. | -| `LongArgumentType` | `#longArg` (optional min and max) | `#getLong` | A 64-bit integer. | -| `FloatArgumentType` | `#floatArg` (optional min and max) | `#getFloat` | A single-precision decimal. | -| `DoubleArgumentType` | `#doubleArg` (optional min and max) | `#getDouble` | A double-precision decimal. | -| `StringArgumentType` | `#word`, `#string`, or `#greedyString` | `#getString` | A single word, a quotable string, or the remaining text. | +| Argument Type | Declares (builder) | Accesses (getter) | Description | +| --------------------- | -------------------------------------- | ----------------- | ------------------------------------ | +| `BoolArgumentType` | `#bool` | `#getBool` | A boolean. | +| `IntegerArgumentType` | `#integer` (optional min and max) | `#getInteger` | A 32-bit integer. | +| `LongArgumentType` | `#longArg` (optional min and max) | `#getLong` | A 64-bit integer. | +| `FloatArgumentType` | `#floatArg` (optional min and max) | `#getFloat` | A single-precision decimal. | +| `DoubleArgumentType` | `#doubleArg` (optional min and max) | `#getDouble` | A double-precision decimal. | +| `StringArgumentType` | `#word`, `#string`, or `#greedyString` | `#getString` | A piece of text, see the note below. | + +:::note +The three `StringArgumentType` builders differ in how much text they consume. `#word` reads a single unquoted word, ending at the first space. `#string` reads a single word as well, unless the input is wrapped in double quotes (`"hello world"`), in which case the whole quoted text is read. `#greedyString` reads everything after the argument, spaces included, and therefore cannot be followed by further nodes. +::: ### Built-in Minecraft Minecraft adds game-specific argument types, found in `net.minecraft.commands.arguments`. This is not an exhaustive list. -| Argument Type | Declares (builder) | Accesses (getter) | Description | -| --------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | -| `EntityArgument` | `#entity`, `#entities`, `#player`, `#players` | `#getEntity`, `#getEntities`, `#getOptionalEntities`, `#getPlayer`, `#getPlayers`, `#getOptionalPlayers` | A single entity, player, or multiple entities, and players via a selector. | -| `BlockPosArgument` | `#blockPos` | `#getLoadedBlockPos`, `#getBlockPos` | A block position. | -| `Vec3Argument` | `#vec3` | `#getVec3` | A position in the world. | -| `ItemArgument` | `#item` | `#getItem` | An item stack, including data components. | -| `BlockStateArgument` | `#block` | `#getBlock` | A block state, including block entity data. | -| `ResourceArgument` | `#resource` | `#getResource` | A namespaced identifier for a given registry. | -| `ResourceKeyArgument` | `#key` | `#getRegistryKey` | A resource key for a given registry. | +| Argument Type | Declares (builder) | Accesses (getter) | Description | +| --------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | +| `EntityArgument` | `#entity`, `#entities`, `#player`, `#players` | `#getEntity`, `#getEntities`, `#getOptionalEntities`, `#getPlayer`, `#getPlayers`, `#getOptionalPlayers` | One or multiple entities or players, selected by name, UUID, or a selector such as `@p` or `@e`. | +| `BlockPosArgument` | `#blockPos` | `#getLoadedBlockPos`, `#getBlockPos` | A block position. | +| `Vec3Argument` | `#vec3` | `#getVec3` | A position in the world. | +| `ItemArgument` | `#item` | `#getItem` | An item stack, including data components. | +| `BlockStateArgument` | `#block` | `#getBlock` | A block state, including block entity data. | +| `ResourceArgument` | `#resource` | `#getResource` | An [`Identifier`][identifier] pointing to an entry of a given registry. | +| `ResourceKeyArgument` | `#key` | `#getRegistryKey` | A [`ResourceKey`][resourcekey] for a given registry. | ### NeoForge -NeoForge adds additional argument types in `net.neoforged.neoforge.server.command`. These are synchronized to the client automatically. +NeoForge adds additional argument types in `net.neoforged.neoforge.server.command`. | Argument Type | Declares (builder) | Accesses (getter) | Description | | --------------- | ------------------ | ------------------------------------------------------------------- | -------------- | @@ -103,3 +107,6 @@ NeoForge adds additional argument types in `net.neoforged.neoforge.server.comman [Brigadier]: https://github.com/Mojang/brigadier [event]: ../concepts/events.md [functions]: https://minecraft.wiki/w/Function_(Java_Edition) +[identifier]: ../misc/identifier.md +[registration]: ../concepts/registries.md#methods-for-registering +[resourcekey]: ../misc/identifier.md#resourcekeys From 238c11e0dee05f89f86d3d38f9b5ca8c7fdbf3e1 Mon Sep 17 00:00:00 2001 From: Magnus Jensen Date: Sun, 26 Jul 2026 18:33:38 +0200 Subject: [PATCH 4/4] add custom argument type --- docs/commands/index.md | 76 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/docs/commands/index.md b/docs/commands/index.md index 69405387b..110ee823c 100644 --- a/docs/commands/index.md +++ b/docs/commands/index.md @@ -104,6 +104,82 @@ NeoForge adds additional argument types in `net.neoforged.neoforge.server.comman | `EnumArgument` | `#enumArgument` | `CommandContext#getArgument` with the keyed name and the enum class | An enum value. | | `ModIdArgument` | `#modIdArgument` | `CommandContext#getArgument` with the keyed name and `String.class` | A mod ID. | +## Custom Argument Types + +A value that none of the existing types can parse is handled by implementing a custom argument type via. `ArgumentType`, which reads the raw command text from a `StringReader` and returns a value of type `T`. + +```java +public class SpellArgument implements ArgumentType { + private static final DynamicCommandExceptionType ERROR_UNKNOWN_SPELL = new DynamicCommandExceptionType( + name -> Component.translatableEscape("commands.examplemod.spell.unknown", name) + ); + + // Follows the naming scheme of the vanilla argument types, used when declaring the argument + public static SpellArgument spell() { + return new SpellArgument(); + } + + // Consumes as much of the input as the argument needs + @Override + public Spell parse(StringReader reader) throws CommandSyntaxException { + String name = reader.readUnquotedString(); + // Look up the spell, throwing ERROR_UNKNOWN_SPELL#createWithContext if there is none + } + + // Optional, defaults to no suggestions: the completions offered while typing the argument + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + // Suggest the names of the available spells or none + return Suggestions.empty(); + } + + // Optional, defaults to an empty list: example inputs used by Brigadier to detect + // ambiguities between sibling nodes + @Override + public Collection getExamples() { + return List.of("fireball", "heal"); + } +} +``` + +As no static getter exists for a new type, one is usually added next to the builder, so that the value can be accessed like that of a built-in type: + +```java +public static Spell getSpell(CommandContext context, String name) { + return context.getArgument(name, Spell.class); +} +``` + +:::tip +If an existing argument type already parses the desired value and only the suggestions should differ, `#suggests` on the argument node replaces them without a custom type. Suggestions added this way are requested from the server as the player types. +::: + +### Synchronization + +The server sends its command tree to every client, allowing commands to be parsed and completed locally while being typed. Each argument node is described by an `ArgumentTypeInfo`, looked up from the `COMMAND_ARGUMENT_TYPE` registry by the class of the `ArgumentType`. The types listed in the argument types tables above already have one. A custom type without one cannot be sent to the client. + +`ArgumentTypeInfos#registerByClass` associates the `ArgumentType` class with its info and returns the info, so it can be [registered][registration] in the same statement: + +```java +public static final DeferredRegister> COMMAND_ARGUMENT_TYPES = + DeferredRegister.create(Registries.COMMAND_ARGUMENT_TYPE, ExampleMod.MOD_ID); + +public static final DeferredHolder, SingletonArgumentInfo> SPELL = COMMAND_ARGUMENT_TYPES.register( + // The registry name of the argument type. + "spell", + () -> ArgumentTypeInfos.registerByClass( + // The class of the argument type. + SpellArgument.class, + // The info describing it. 'contextFree' is used for argument types + // constructed without parameters, 'contextAware' for those requiring + // a CommandBuildContext. + SingletonArgumentInfo.contextFree(SpellArgument::spell) + ) +); +``` + +An argument type that holds parameters of its own, such as the bounds of `IntegerArgumentType`, needs those parameters on the client as well and therefore cannot use `SingletonArgumentInfo`. Instead, `ArgumentTypeInfo` is implemented directly, along with an `ArgumentTypeInfo.Template` holding the parameters: `#serializeToNetwork` and `#deserializeFromNetwork` transfer the template, `#serializeToJson` writes it into the JSON representation of the command tree, `#unpack` creates a template from an argument type, and `Template#instantiate` creates an argument type from a template. `EnumArgument.Info` is a small example of such an implementation. + [Brigadier]: https://github.com/Mojang/brigadier [event]: ../concepts/events.md [functions]: https://minecraft.wiki/w/Function_(Java_Edition)