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>
This commit is contained in:
@@ -0,0 +1,442 @@
|
||||
# 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 178–210 — 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
|
||||
------ ----- -------------------------------------------
|
||||
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 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.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.
|
||||
|
||||
<!-- 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 -->
|
||||
Reference in New Issue
Block a user