archive.org Wayback was network-blocked; pivoted to live GitHub. Key fix: FML host token is \0FML\0 (not \0FML2/3\0 — those are FMLNETVERSION ints on the fml:handshake channel); modern Forge uses \0FORGE/\0FORGEn. Confirmed BungeeGuard backend token check (lucko source), 4 cmd suggestion providers, 26.2 interval_select worldgen-only, corrected 1.14 villager metadata layout. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
16 KiB
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-leveltypes.command_node+play.toClientpacket listminecraft-data/data/pc/1.19/protocol.json— same, post-VarInt-parser changeminecraft-data/data/pc/1.21.1/protocol.json— same, 1.21.1 parser tableViaVersion/common/src/main/java/com/viaversion/viaversion/rewriter/CommandRewriter.java—registerDeclareCommands(String era) andhandle1_19/registerDeclareCommands1_19(VarInt era)ViaVersion/…/protocols/v1_18_2to1_19/Protocol1_18_2To1_19.javalines 178–210 — transition handler (readsTypes.STRING, writesTypes.VAR_INT)
See also: ../versions/1.13.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
------ ----- -------------------------------------------
0–1 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)
5–7 0xE0 unused
Note: minecraft-data names these command_node_type (bits 0–1), 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 194–200: 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 2–7 — 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 2–7 — 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 1–7 — 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 argumentminecraft:all_recipes— client fills from the known recipe listminecraft:available_sounds— client fills from the sound registryminecraft:summonable_entities— client fills from entity type registry
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
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.13–1.18, and ["mapper", {"type": "varint", ...}] for 1.19+.
ViaVersion Protocol1_18_2To1_19.java:194–200 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.