minecraft_protocol: foundation + per-version protocol docs 1.7.10->26.2

8 topical docs (overview, data types, lifecycle, handshake, status/ping,
login+encryption, configuration, version-differences) + proxy-forwarding set
+ 16 per-version release-line docs, sourced from minecraft.wiki, ViaVersion
(source + commits), minecraft-data, node-minecraft-protocol, Velocity, BungeeCord.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
claude-timemachine
2026-06-19 14:15:32 +02:00
commit d73c1c9537
35 changed files with 8894 additions and 0 deletions
+380
View File
@@ -0,0 +1,380 @@
# 06 — Configuration State
> **Version gate**: the Configuration state does **not exist** before 1.20.2 (protocol 764).
> Nothing in this document applies to 1.20.1 or earlier.
> Known Packs (§4) requires 1.20.5 (protocol 766) or later.
---
## 1. Why it was added
Before 1.20.2, the `Join Game` / `Login (play)` packet was the kitchen sink: it carried
dimension codec NBT, dimension type, feature flags, and more. Tags arrived as a separate
`Update Tags` play packet immediately after. This meant:
- Registry data (codec NBT) had to be resent in full every time the client changed servers
or re-entered a world.
- There was no clean hook for sending resource packs or feature flags before the client
started rendering the world.
- Server-to-server transfers (dimension changes, datapack reloads) required hacky re-use
of the play state with no protocol-level guarantee the client was in a clean state.
1.20.2 introduced a dedicated **Configuration** phase that sits between Login and Play.
The server can keep the client in Configuration as long as needed, push all setup data
(registries, resource packs, feature flags, tags), then release it into Play. Crucially,
the server can **pull the client back** from Play into Configuration at any time
(`Start Configuration``Finish Configuration` handshake), enabling clean
dimension/datapack changes without a full disconnect.
Source: minecraft.wiki Java Edition protocol (Configuration) [primary]; node-minecraft-protocol `src/client/play.js:39-68`; Velocity `ConfigSessionHandler.java`, `ClientConfigSessionHandler.java`.
---
## 2. Connection-state machine
```
HANDSHAKING ──► STATUS (ping/list)
└──► LOGIN
│ Login Success (CB 0x02)
│ Login Acknowledged (SB 0x03, added 1.20.2)
CONFIGURATION ◄────────────────────────────────┐
│ │
│ Finish Configuration (CB 0x03) │
│ Ack Finish Configuration (SB 0x03) │
▼ │
PLAY ────── Start Configuration (CB) ───────┘
```
State enum source: `node-minecraft-protocol/src/states.js:3-9` — defines `HANDSHAKING`, `STATUS`, `LOGIN`, `CONFIGURATION`, `PLAY`.
Transition packet source: `Velocity/StateRegistry.java:853-854``LoginAcknowledgedPacket` registered at `0x03` from `MINECRAFT_1_20_2`.
---
## 3. Configuration flow
### 3.1 Initial entry (Login → Configuration)
```mermaid
sequenceDiagram
participant C as Client
participant S as Server
Note over C,S: LOGIN state
S->>C: Login Success (Login CB 0x02)
C->>S: Login Acknowledged (Login SB 0x03)
Note over C,S: CONFIGURATION state begins
C->>S: Client Information (Config SB 0x00)
Note right of S: locale, render distance, chat mode, skin parts, main hand
C->>S: Plugin Message minecraft:brand (Config SB 0x02 / 0x01 pre-1.20.5)
Note right of S: client brand string e.g. "vanilla"
alt 1.20.5 and later (protocol 766+)
S->>C: Known Packs (Config CB 0x0E)
C->>S: Known Packs (Config SB 0x07)
Note over C,S: server computes registry diff
end
S->>C: Feature Flags (Config CB 0x0C / 0x07 pre-1.20.5)
Note left of C: enabled experiment identifiers
S->>C: Registry Data x N (Config CB 0x07 / 0x05 pre-1.20.5)
Note left of C: one packet per registry; delta if Known Packs matched
S->>C: Update Tags (Config CB 0x0D / 0x08 pre-1.20.5)
opt Resource packs
S->>C: Add Resource Pack (Config CB 0x09)
C->>S: Resource Pack Response (Config SB 0x06)
end
S->>C: Finish Configuration (Config CB 0x03)
C->>S: Acknowledge Finish Configuration (Config SB 0x03)
Note over C,S: PLAY state begins
S->>C: Login Play (Play CB 0x29 in 1.20.2)
```
The server controls ordering within Configuration. The sequence above reflects
observed Vanilla/Velocity ordering. The client **must not** transition to Play until it
receives `Finish Configuration`.
Source: `node-minecraft-protocol/src/server/login.js:224-239` (server sends registry_data
then finish_configuration); `node-minecraft-protocol/src/client/play.js:49-68` (client
handles select_known_packs → finish_configuration → state=PLAY).
### 3.2 Packet IDs at a glance (Configuration state)
Packet IDs shifted at 1.20.5 when `Cookie Request/Response`, `Store Cookie`, and
`Transfer` were inserted. The table below shows the two main stable points. For 1.21.x+
IDs see `StateRegistry.java:163-261` directly.
#### Clientbound (server → client)
| Packet name | 1.20.21.20.4 | 1.20.5+ | Official name |
|--------------------------|:-------------:|:--------:|---------------------------|
| Cookie Request | — | 0x00 | `cookie_request` |
| Plugin Message | 0x00 | 0x01 | `custom_payload` |
| Disconnect | 0x01 | 0x02 | `disconnect` |
| Finish Configuration | 0x02 | 0x03 | `finish_configuration` |
| Keep Alive | 0x03 | 0x04 | `keep_alive` |
| Ping | 0x04 | 0x05 | `ping` |
| Reset Chat | — | 0x06 | `reset_chat` |
| Registry Data | 0x05 | 0x07 | `registry_data` |
| Remove Resource Pack | 0x06 (1.20.3) | 0x08 | `resource_pack_pop` |
| Add Resource Pack | 0x06 | 0x09 | `resource_pack_push` |
| Store Cookie | — | 0x0A | `store_cookie` |
| Transfer | — | 0x0B | `transfer` |
| Feature Flags | 0x07 | 0x0C | `update_enabled_features` |
| Update Tags | 0x08 | 0x0D | `update_tags` |
| Known Packs (CB) | — | 0x0E | `select_known_packs` |
Source: `StateRegistry.java:202-261` (CONFIG clientbound block).
#### Serverbound (client → server)
| Packet name | 1.20.21.20.4 | 1.20.5+ | Official name |
|----------------------------------|:-------------:|:--------:|---------------------------|
| Client Information | 0x00 | 0x00 | `client_information` |
| Cookie Response | — | 0x01 | `cookie_response` |
| Plugin Message | 0x01 | 0x02 | `custom_payload` |
| Acknowledge Finish Configuration | 0x02 | 0x03 | `finish_configuration` |
| Keep Alive | 0x03 | 0x04 | `keep_alive` |
| Pong | 0x04 | 0x05 | `pong` |
| Resource Pack Response | 0x05 | 0x06 | `resource_pack` |
| Known Packs (SB) | — | 0x07 | `select_known_packs` |
Source: `StateRegistry.java:165-200` (CONFIG serverbound block).
---
## 4. Known Packs handshake (1.20.5 / protocol 766)
**Added in 1.20.5. Not present in 1.20.21.20.4.**
Registry data can be large. If the client already has the vanilla datapack's registry
entries (shipped with the client JAR), the server need not resend them. The Known Packs
exchange lets server and client negotiate which packs each side knows, so Registry Data
only sends the diff.
```mermaid
sequenceDiagram
participant C as Client
participant S as Server
Note over C,S: CONFIGURATION state (1.20.5+)
S->>C: Known Packs CB (0x0E) — server's list of packs
C->>S: Known Packs SB (0x07) — subset client already has locally
Note over C,S: server computes intersection
S->>C: Registry Data — only entries NOT covered by client's known packs
```
**Wire format** — each `KnownPack` entry is three length-prefixed strings read
sequentially: `namespace`, `id`, `version`. VarInt count prefix. Serverbound capped at 64
entries by default.
Source: `KnownPacksPacket.java:68-78` (full record and read/write methods).
```java
// KnownPacksPacket.java:68
public record KnownPack(String namespace, String id, String version) {
private static KnownPack read(ByteBuf buf) {
return new KnownPack(
ProtocolUtils.readString(buf),
ProtocolUtils.readString(buf),
ProtocolUtils.readString(buf));
}
```
### How Velocity uses the Known Packs boundary
`ClientConfigSessionHandler.callConfigurationEvent()` javadoc (lines 305-320):
> "For 1.20.5+ backends this is done when the client responds to the known packs request.
> The response is delayed until the event has been called. For 1.20.21.20.4 servers this
> is done when the client acknowledges the end of the configuration. This is handled
> differently because for 1.20.5+ servers can't keep their connection alive between states
> and older servers don't have the known packs transaction."
`ClientConfigSessionHandler.handle(KnownPacksPacket)` (line 176) fires
`PlayerConfigurationEvent` then forwards the client's response to the backend.
`node-minecraft-protocol/src/client/play.js:56-58`:
```js
client.once('select_known_packs', () => {
client.write('select_known_packs', { packs: [] })
// sends empty list → server sends full registry
})
```
---
## 5. Re-configuration: Play → Configuration loop
The server can return the client to Configuration from Play at any time. Vanilla uses
this for dimension changes, datapack reloads (`/reload`), and server transfers (1.20.5+).
```mermaid
sequenceDiagram
participant C as Client
participant S as Server
Note over C,S: PLAY state (mid-session)
S->>C: Start Configuration (Play CB — see ID table below)
C->>S: Acknowledge Configuration (Play SB 0x0B in 1.20.2)
Note over C,S: CONFIGURATION state (re-entry)
S->>C: Known Packs CB (1.20.5+)
C->>S: Known Packs SB (1.20.5+)
S->>C: Registry Data / Feature Flags / Update Tags / Resource Packs as needed
S->>C: Finish Configuration (Config CB 0x03)
C->>S: Acknowledge Finish Configuration (Config SB 0x03)
Note over C,S: PLAY state resumes
S->>C: Login Play or Respawn
```
**Start Configuration** Play-state clientbound packet IDs:
| Version range | ID |
|-----------------|-------|
| 1.20.2 | 0x65 |
| 1.20.31.20.4 | 0x67 |
| 1.20.51.21.1 | 0x69 |
| 1.21.21.21.4 | 0x70 |
| 1.21.51.21.8 | 0x6F |
Source: `StateRegistry.java:804-813` (PLAY clientbound `StartUpdatePacket` mappings).
`StartUpdatePacket` has zero fields — it is a signal-only packet.
Source: `StartUpdatePacket.java:35-38` — empty `decode`/`encode` bodies.
**node-minecraft-protocol re-entry code** (`src/client/play.js:42-54`):
```js
// Server can tell client to re-enter config state
client.on('start_configuration', () => enterConfigState())
function enterConfigState(finishCb) {
if (client.state === states.CONFIGURATION) return
// If we are returning from the play state, acknowledge it
if (client.state === states.PLAY) {
client.write('configuration_acknowledged', {})
}
client.state = states.CONFIGURATION
// ...
}
```
Players in Configuration are not visible on the tab list. <!-- VERIFY: confirmed for initial entry; verify same applies during mid-session re-configuration -->
### How Velocity bridges re-configuration
Two session handler classes manage each side:
- **`ConfigSessionHandler`** (backend side, `connection/backend/`) — forwards
`RegistrySyncPacket`, `TagsUpdatePacket`, `ActiveFeaturesPacket` directly to the client.
Intercepts `FinishedUpdatePacket` to orchestrate the pipeline switch.
- **`ClientConfigSessionHandler`** (player side, `connection/client/`) — handles packets
from the client during Configuration. `handle(FinishedUpdatePacket)` (line 118) switches
the client connection's active handler to `ClientPlaySessionHandler` and completes
`configSwitchFuture`.
The decoder pipeline is explicitly rewound to `StateRegistry.PLAY` on the backend side
**before** writing `FinishedUpdatePacket` back, ensuring the Netty decoder expects
Play-state packets from that point:
```java
// ConfigSessionHandler.java:240-241 — handle(FinishedUpdatePacket)
smc.getChannel().pipeline().get(MinecraftVarintFrameDecoder.class).setState(StateRegistry.PLAY);
smc.getChannel().pipeline().get(MinecraftDecoder.class).setState(StateRegistry.PLAY);
```
---
## 6. What moved out of Join Game / Login Play
Before 1.20.2, `Join Game` (Play CB) carried a `registryCodec` NBT compound containing
all dimension types, biome registries, damage types, etc. In 1.20.2+ the field is
**absent** from the Join Game packet entirely.
`RegistrySyncPacket.java:34` notes:
```java
// NBT change in 1.20.2 makes it difficult to parse this packet.
```
Velocity treats it as an opaque byte slice and forwards verbatim.
**node-minecraft-protocol** shows the split (`src/server/login.js:226-233`):
```js
if (client.supportFeature('segmentedRegistryCodecData')) {
for (const key in options.registryCodec) {
client.write('registry_data', entry) // one packet per registry
}
} else {
client.write('registry_data', { codec: options.registryCodec || {} }) // pre-1.20.5 monolithic
}
```
<!-- VERIFY: exact version at which registry_data became per-registry (segmented): believed 1.20.5 based on the feature flag name; may be 1.20.3 -->
---
## 7. Notable edge cases
**Resource pack state across re-configuration** — Velocity 1.20.2 clears applied resource
packs on `ConfigSessionHandler.activated()` and re-queues the first previously-applied
pack after `FinishedUpdatePacket` to avoid double-applying.
Source: `ConfigSessionHandler.java:activated()` and `handle(FinishedUpdatePacket):253`.
**Brand forwarding** — the client sends `minecraft:brand` immediately after Login Acknowledged
(before the backend may be ready). Velocity caches the brand string; on
`handleBackendFinishUpdate` it re-writes a brand Plugin Message to the backend *before*
firing finish-config events.
Source: `ClientConfigSessionHandler.java:handleBackendFinishUpdate()` (line 330-337).
**`PlayerConfigurationEvent` timing differs by version** — 1.20.5+ fires when
`KnownPacksPacket` is received; 1.20.21.20.4 fires when `Acknowledge Finish Configuration`
arrives. The comment in `callConfigurationEvent()` (lines 305-320) explains the reason.
**Keep Alive in Configuration** — Keep Alive packets are valid in Configuration state
(same packet, same format). The client can be held in Configuration indefinitely;
Keep Alive prevents timeout.
Source: `StateRegistry.java:179-185``KeepAlivePacket` registered at 0x03 (1.20.2)
and 0x04 (1.20.5+) in the CONFIG serverbound block.
---
## 8. VERIFY flags
<!-- VERIFY: exact packet ordering within Configuration — Client Information before or after brand? Wiki describes expected sequence; confirm against a packet capture. -->
<!-- VERIFY: whether Update Tags is always sent during initial Configuration or only during re-configuration in current Vanilla (1.21.x). Wiki packet list includes it; Velocity passes it through but does not generate it. -->
<!-- VERIFY: whether Known Packs CB is sent before or after Plugin Message (brand) in 1.20.5+ Vanilla — sequence diagram places Known Packs after brand, matching Velocity's event-ordering comment, but confirm against a live trace. -->
<!-- VERIFY: segmentedRegistryCodecData feature flag exact version boundary — believed 1.20.5. -->
<!-- VERIFY: players in Configuration during mid-session re-configuration hidden from tab list the same as during initial Configuration. -->
---
## Sources
| Reference | Used for |
|-----------|----------|
| [minecraft.wiki/w/Java_Edition_protocol/Packets](https://minecraft.wiki/w/Java_Edition_protocol/Packets) | Packet tables, IDs, official names (fetched 2026-06-19) |
| `Velocity/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java` | Authoritative packet ID mapping for all versions (lines 163-261 CONFIG block; 804-813 StartUpdatePacket) |
| `Velocity/…/connection/backend/ConfigSessionHandler.java` | Backend-side Configuration handling; pipeline switch; resource pack state |
| `Velocity/…/connection/client/ClientConfigSessionHandler.java` | Client-side Configuration handling; Known Packs event timing; brand forwarding |
| `Velocity/…/protocol/packet/config/KnownPacksPacket.java` | Known Packs wire format — namespace/id/version record |
| `Velocity/…/protocol/packet/config/RegistrySyncPacket.java` | Opaque-forward note; NBT change comment |
| `Velocity/…/protocol/packet/config/StartUpdatePacket.java` | Zero-length signal packet |
| `Velocity/…/protocol/packet/config/ActiveFeaturesPacket.java` | Feature flags as Key[] array |
| `node-minecraft-protocol/src/states.js:3-9` | State enum: CONFIGURATION alongside HANDSHAKING/STATUS/LOGIN/PLAY |
| `node-minecraft-protocol/src/server/login.js:189-239` | Server-side: Login Acknowledged handler → Configuration state; registry_data loop |
| `node-minecraft-protocol/src/client/play.js:32-68` | Client-side: Login Success → Configuration entry; re-entry from Play; select_known_packs response |