Files
minecraft_protocol/packets/format-command-graph.md
T
claude-timemachine 90b711d12a packets/: packet model + catalogs + wire-format deep-dives
Control-state catalogs (handshake/status/login/config), categorized Play
catalog (~182 packets), and deep-dives on the four hard formats: chunk data
(paletted containers + light), entity metadata (type registry), slot/structured
components, command graph (Brigadier). Sourced from minecraft-data + ViaVersion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 15:15:26 +02:00

443 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Commands (Declare Commands) Packet
Clientbound · Play state
Added: **1.13** (protocol 393)
Sends the server's Brigadier command graph to the client so it can populate tab-completion and validate syntax client-side before sending the command.
Packet IDs by version:
| Version | Packet ID |
|---------|-----------|
| 1.13 | `0x11` |
| 1.19 | `0x0F` |
| 1.21.1 | `0x11` |
Sources:
- `minecraft-data/data/pc/1.13/protocol.json` — top-level `types.command_node` + `play.toClient` packet list
- `minecraft-data/data/pc/1.19/protocol.json` — same, post-VarInt-parser change
- `minecraft-data/data/pc/1.21.1/protocol.json` — same, 1.21.1 parser table
- `ViaVersion/common/src/main/java/com/viaversion/viaversion/rewriter/CommandRewriter.java``registerDeclareCommands` (String era) and `handle1_19` / `registerDeclareCommands1_19` (VarInt era)
- `ViaVersion/…/protocols/v1_18_2to1_19/Protocol1_18_2To1_19.java` lines 178210 — transition handler (reads `Types.STRING`, writes `Types.VAR_INT`)
See also: [../versions/1.13.md](../versions/1.13.md) · [../versions/1.19.md](../versions/1.19.md)
---
## Packet Layout
```
Count VarInt number of nodes in the flat node array
Nodes[] command_node[] one record per node (see below)
Root Index VarInt index into Nodes[] of the root node
```
The node array is **flat** — nodes reference each other by index, not by nesting. The root node is always type 0 (root); its index is given by `Root Index` at the end of the packet.
---
## Node Record (`command_node`)
Fields are conditional on the `flags` byte:
```
Flags byte (bitfield)
Children VarInt count, then VarInt[] of child node indices
Redirect Node VarInt — only if flags bit 3 (0x08) set
Name String — only for literal (type 1) and argument (type 2) nodes
Parser ID String|VarInt — only for argument nodes (type 2); encoding changed in 1.19
Properties (variable) — only for argument nodes; depends on Parser ID
Suggestions String/Identifier — only if flags bit 4 (0x10) set
```
### Flags Byte (bitfield)
The byte is parsed from **LSB to MSB**:
```
Bit(s) Mask Field
------ ----- -------------------------------------------
01 0x03 node type: 0 = root, 1 = literal, 2 = argument
2 0x04 is_executable — this node itself is a valid end-of-input
3 0x08 has_redirect — a Redirect Node index follows Children
4 0x10 has_suggestions — a Suggestions Type identifier follows Properties
(argument nodes only; ignored on other types)
57 0xE0 unused
```
Note: `minecraft-data` names these `command_node_type` (bits 01), `has_command` (bit 2), `has_redirect_node` (bit 3), `has_custom_suggestions` (bit 4).
### Children
```
ChildCount VarInt number of children
Children[] VarInt[] indices into the flat node array
```
### Redirect Node
Present only when `flags & 0x08 != 0`. A single VarInt index into the flat node array. Used for command aliases — e.g. `/tp` and `/teleport` can share the same subgraph via redirect.
### Name
Present for node types **literal** (1) and **argument** (2). Not present for root (0). A length-prefixed UTF-8 String (max 32767 chars per the standard String encoding).
### Parser ID + Properties
Present only for argument nodes (type 2). The parser encoding **changed in 1.19**.
---
## Parser Encoding: String (1.13) vs VarInt (1.19+)
### Pre-1.19 (1.13 through 1.18.x)
Parser is a plain **String** (identifier, e.g. `"brigadier:string"`, `"minecraft:entity"`).
```
Parser String e.g. "brigadier:integer"
```
Source: `minecraft-data/data/pc/1.13/protocol.json` `types.command_node` extraNodeData type=2 → `parser: "string"`.
ViaVersion `CommandRewriter.registerDeclareCommands()` reads `Types.STRING` for the parser name.
### 1.19+ (protocol 759 onwards)
Parser is a **VarInt** registry index (a compact enum of all known parser names).
```
Parser VarInt e.g. 5 = "brigadier:string"
```
Source: `minecraft-data/data/pc/1.19/protocol.json` `types.command_node` extraNodeData type=2 → `parser: ["mapper", {"type": "varint", ...}]`.
ViaVersion `Protocol1_18_2To1_19.java` lines 194200: reads `Types.STRING`, looks up `MAPPINGS.getArgumentTypeMappings().mappedId(argumentType)`, writes `Types.VAR_INT`.
ViaVersion `CommandRewriter.handle1_19()` line 131: reads `Types.VAR_INT` and resolves it to a name via `argumentType(argumentTypeId)`.
The property format **did not change** — only the parser identifier encoding changed. Properties are still parsed by the same per-parser logic after resolving the VarInt to a name.
---
## Common Parsers and Their Properties
All parsers with no listed properties send nothing (`void`).
### Brigadier parsers (no namespace qualifier needed for these 6)
| Parser name | VarInt ID (1.19+) | Properties |
|---------------------|:-----------------:|------------|
| `brigadier:bool` | 0 | none |
| `brigadier:float` | 1 | flags byte (bit 0 = min present, bit 1 = max present), then optional f32 min, optional f32 max |
| `brigadier:double` | 2 | flags byte (bit 0 = min present, bit 1 = max present), then optional f64 min, optional f64 max |
| `brigadier:integer` | 3 | flags byte (bit 0 = min present, bit 1 = max present), then optional i32 min, optional i32 max |
| `brigadier:long` | 4 | flags byte (bit 0 = min present, bit 1 = max present), then optional i64 min, optional i64 max |
| `brigadier:string` | 5 | VarInt mode: 0 = SINGLE_WORD, 1 = QUOTABLE_PHRASE, 2 = GREEDY_PHRASE |
Numeric range flags detail (same pattern for float/double/integer/long):
```
Bit 0 (0x01) — minimum bound present → read min value
Bit 1 (0x02) — maximum bound present → read max value
Bits 27 — unused
```
### Minecraft parsers (selected; full table below)
| Parser name | VarInt ID (1.19+) | Properties |
|------------------------------|:-----------------:|------------|
| `minecraft:entity` | 6 | flags byte: bit 0 (0x01) = only allow entities (not players), bit 1 (0x02) = only allow players |
| `minecraft:game_profile` | 7 | none |
| `minecraft:block_pos` | 8 | none |
| `minecraft:column_pos` | 9 | none |
| `minecraft:vec3` | 10 | none |
| `minecraft:vec2` | 11 | none |
| `minecraft:block_state` | 12 | none |
| `minecraft:block_predicate` | 13 | none |
| `minecraft:item_stack` | 14 | none |
| `minecraft:item_predicate` | 15 | none |
| `minecraft:color` | 16 | none |
| `minecraft:component` | 17 | none |
| `minecraft:message` | 18 (1.19) / 19 (1.21.1) | none |
| `minecraft:nbt` | 19 (1.19) / 20 (1.21.1) | none |
| `minecraft:nbt_path` | 21 (1.19) / 22 (1.21.1) | none |
| `minecraft:objective` | 22 (1.19) / 23 (1.21.1) | none |
| `minecraft:scoreboard_slot` | 28 (1.19) / 29 (1.21.1) | none |
| `minecraft:score_holder` | 29 (1.19) / 30 (1.21.1) | flags byte: bit 0 (0x01) = allow multiple |
| `minecraft:swizzle` | 30 (1.19) | none |
| `minecraft:team` | 31 (1.19) | none |
| `minecraft:resource_location`| 33 (1.19) | none |
| `minecraft:resource_or_tag` | 43 (1.19) | String registry name |
| `minecraft:resource` | 44 (1.19) | String registry name |
| `minecraft:uuid` | 47 (1.19) / 53 (1.21.1) | none |
Note: IDs shift across versions as new parsers are inserted. The VarInt ID is version-specific. Use the `minecraft-data` protocol.json for the authoritative table for a given protocol version.
#### `minecraft:entity` flags detail
```
Bit 0 (0x01) — onlyAllowEntities (non-player entities only)
Bit 1 (0x02) — onlyAllowPlayers
Bits 27 — unused
```
Both bits 0 and 1 can be 0 simultaneously (allow any selector).
#### `minecraft:score_holder` flags detail
```
Bit 0 (0x01) — allowMultiple (accept `*` and selectors matching multiple holders)
Bits 17 — unused
```
### Full 1.19 VarInt Parser Table
```
0 brigadier:bool
1 brigadier:float
2 brigadier:double
3 brigadier:integer
4 brigadier:long
5 brigadier:string
6 minecraft:entity
7 minecraft:game_profile
8 minecraft:block_pos
9 minecraft:column_pos
10 minecraft:vec3
11 minecraft:vec2
12 minecraft:block_state
13 minecraft:block_predicate
14 minecraft:item_stack
15 minecraft:item_predicate
16 minecraft:color
17 minecraft:component
18 minecraft:message
19 minecraft:nbt
20 minecraft:nbt_tag
21 minecraft:nbt_path
22 minecraft:objective
23 minecraft:objective_criteria
24 minecraft:operation
25 minecraft:particle
26 minecraft:angle
27 minecraft:rotation
28 minecraft:scoreboard_slot
29 minecraft:score_holder
30 minecraft:swizzle
31 minecraft:team
32 minecraft:item_slot
33 minecraft:resource_location
34 minecraft:mob_effect
35 minecraft:function
36 minecraft:entity_anchor
37 minecraft:int_range
38 minecraft:float_range
39 minecraft:item_enchantment
40 minecraft:entity_summon
41 minecraft:dimension
42 minecraft:time
43 minecraft:resource_or_tag
44 minecraft:resource
45 minecraft:template_mirror
46 minecraft:template_rotation
47 minecraft:uuid
```
Source: `minecraft-data/data/pc/1.19/protocol.json` `types.command_node` parser mapper mappings.
### Full 1.21.1 VarInt Parser Table (additions highlighted)
```
0 brigadier:bool
1 brigadier:float
2 brigadier:double
3 brigadier:integer
4 brigadier:long
5 brigadier:string
6 minecraft:entity
7 minecraft:game_profile
8 minecraft:block_pos
9 minecraft:column_pos
10 minecraft:vec3
11 minecraft:vec2
12 minecraft:block_state
13 minecraft:block_predicate
14 minecraft:item_stack
15 minecraft:item_predicate
16 minecraft:color
17 minecraft:component
18 minecraft:style ← new in 1.21.x era
19 minecraft:message
20 minecraft:nbt
21 minecraft:nbt_tag
22 minecraft:nbt_path
23 minecraft:objective
24 minecraft:objective_criteria
25 minecraft:operation
26 minecraft:particle
27 minecraft:angle
28 minecraft:rotation
29 minecraft:scoreboard_slot
30 minecraft:score_holder
31 minecraft:swizzle
32 minecraft:team
33 minecraft:item_slot
34 minecraft:item_slots ← new
35 minecraft:resource_location
36 minecraft:function
37 minecraft:entity_anchor
38 minecraft:int_range
39 minecraft:float_range
40 minecraft:dimension
41 minecraft:gamemode ← new
42 minecraft:time
43 minecraft:resource_or_tag
44 minecraft:resource_or_tag_key ← new
45 minecraft:resource
46 minecraft:resource_key ← new
47 minecraft:template_mirror
48 minecraft:template_rotation
49 minecraft:heightmap ← new
50 minecraft:loot_table ← new
51 minecraft:loot_predicate ← new
52 minecraft:loot_modifier ← new
53 minecraft:uuid
```
Source: `minecraft-data/data/pc/1.21.1/protocol.json` `types.command_node` parser mapper mappings.
---
## Suggestions Type
When flags bit 4 (`0x10`) is set, an extra field follows Properties:
```
Suggestions String an Identifier naming the suggestion provider
```
Known values:
- `minecraft:ask_server` — client sends a Tab-Complete request to the server for this argument
- `minecraft:all_recipes` — client fills from the known recipe list
- `minecraft:available_sounds` — client fills from the sound registry
- `minecraft:summonable_entities` — client fills from entity type registry
<!-- UNCONFIRMED: whether additional built-in suggestion providers exist beyond these four; wiki.vg lists these as the documented set -->
---
## Example: `/teleport <target>`
A minimal `/teleport <target>` command with one argument. Three nodes total:
```
Node 0: type=root, executable=false, children=[1]
Node 1: type=literal, executable=false, name="teleport", children=[2]
Node 2: type=argument, executable=true, name="target",
parser="minecraft:entity" (VarInt 6 in 1.19+),
properties=flags(0x02 = onlyAllowPlayers),
children=[]
Root Index = 0
```
Wire layout (hex, showing field-by-field):
```
Count 03 (3 nodes)
Node 0:
flags 00 (type=root, not executable, no redirect)
childCount 01 (1 child)
children 01 (index 1)
[no name — root node]
Node 1:
flags 04 (type=literal 0x01 | executable=false; wait — not executable here, so 0x01)
-- actually flags = 0x01 (type=literal, not executable)
childCount 01
children 02
name 08 74 65 6c 65 70 6f 72 74 ("teleport", length-prefixed string)
Node 2:
flags 06 (type=argument 0x02 | is_executable 0x04)
childCount 00
name 06 74 61 72 67 65 74 ("target")
parser 06 (VarInt 6 = minecraft:entity, in 1.19+)
properties 02 (onlyAllowPlayers flag)
Root Index 00
```
ASCII graph:
```
[ROOT node=0]
|
v
[LITERAL "teleport" node=1]
|
v
[ARGUMENT "target" node=2] parser=minecraft:entity executable
```
---
## Redirect Example
`/tp` can alias `/teleport` by using a redirect:
```
Node 3: type=literal, executable=false, flags=0x09 (literal | has_redirect),
name="tp", children=[], redirect=1
```
`flags = 0x01 | 0x08 = 0x09`. The redirect points to node 1 (the `teleport` literal), so the client treats `/tp` as if it navigated into the `teleport` subgraph.
---
## Node-Record Pseudocode
```python
def read_command_node(buf):
flags = buf.read_byte()
node_type = flags & 0x03 # 0=root, 1=literal, 2=argument
is_executable = bool(flags & 0x04)
has_redirect = bool(flags & 0x08)
has_suggestions = bool(flags & 0x10)
child_count = buf.read_varint()
children = [buf.read_varint() for _ in range(child_count)]
redirect = buf.read_varint() if has_redirect else None
name = None
if node_type in (1, 2): # literal or argument
name = buf.read_string()
parser = None
properties = None
if node_type == 2: # argument
# Pre-1.19: parser = buf.read_string()
# 1.19+: parser = PARSER_TABLE[buf.read_varint()]
parser = read_parser_id(buf)
properties = read_parser_properties(buf, parser)
suggestions = None
if has_suggestions: # argument nodes only in practice
suggestions = buf.read_string() # identifier
return CommandNode(node_type, is_executable, children, redirect,
name, parser, properties, suggestions)
```
---
## Version History
| Version | Protocol | Change |
|------------|----------|--------|
| 1.13 | 393 | Packet added; parser = String identifier |
| 1.14 | 477 | New argument parsers added (e.g. `minecraft:uuid`, `minecraft:nbt_compound_tag`) |
| 1.19 | 759 | Parser identifier changed from String to VarInt registry index; new parsers added |
| 1.19.3 | 761 | Additional parser entries added |
| 1.21.1 | 767 | `minecraft:style`, `minecraft:item_slots`, `minecraft:gamemode`, `minecraft:resource_or_tag_key`, `minecraft:resource_key`, `minecraft:heightmap`, `minecraft:loot_*` parsers added |
The `minecraft-data` `types.command_node.extraNodeData[type=2].parser` field is `"string"` for 1.131.18, and `["mapper", {"type": "varint", ...}]` for 1.19+.
ViaVersion `Protocol1_18_2To1_19.java:194200` is the canonical protocol transition point: it reads `Types.STRING` from the 1.18 upstream and writes `Types.VAR_INT` to the 1.19 client.
<!-- UNCONFIRMED: whether 1.13-pre versions had a slightly different flag layout before 1.13 release; only release 1.13 data confirmed via minecraft-data -->