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:
@@ -0,0 +1,287 @@
|
||||
# Connection Lifecycle — Java Edition Protocol State Machine
|
||||
|
||||
> **Scope**: 1.7.10 through current (26.1 / protocol 775). Version-specific divergences are called out inline.
|
||||
> **Connective-tissue doc**: per-phase detail lives in `03-handshake.md`, `04-status.md`, `05-login.md`, `06-configuration.md`, `07-play.md`. This doc covers the state machine, transition triggers, and the ordered exchange that takes a raw TCP socket to in-game.
|
||||
|
||||
---
|
||||
|
||||
## 1. States
|
||||
|
||||
The protocol defines five named states. Each state carries its own packet-ID namespace (IDs are reused across states):
|
||||
|
||||
| State | Integer ID | Direction of entry | Purpose |
|
||||
|---|---|---|---|
|
||||
| **Handshaking** | (implicit, initial) | TCP connect | One packet; chooses the next state |
|
||||
| **Status** | 1 | via Handshake intent=1 | Server-list ping/pong; no auth |
|
||||
| **Login** | 2 | via Handshake intent=2 (or 3) | Auth, encryption, compression, identity |
|
||||
| **Configuration** | — (no integer) | after Login Acknowledged (≥1.20.2) | Registry sync, feature flags, resource packs |
|
||||
| **Play** | — | after Finish Configuration (or Login Success <1.20.2) | Gameplay |
|
||||
|
||||
Sources:
|
||||
- State names: `node-minecraft-protocol/src/states.js:3-9`
|
||||
- Velocity enum: `Velocity/proxy/…/StateRegistry.java:140-263` (`HANDSHAKE`, `STATUS`, `CONFIG`, `PLAY`, `LOGIN`)
|
||||
- Integer IDs (STATUS_ID=1, LOGIN_ID=2, TRANSFER_ID=3): `StateRegistry.java:881-883`
|
||||
- Intent=3 (Transfer login, ≥1.20.5): `minecraft.wiki/w/Java_Edition_protocol/Packets`
|
||||
|
||||
---
|
||||
|
||||
## 2. State Machine Diagram
|
||||
|
||||
```
|
||||
TCP connect
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ HANDSHAKING │ (one packet: set_protocol / Handshake 0x00)
|
||||
└──────┬───────┘
|
||||
│ intent field
|
||||
├─ 1 ──────────────────────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────┐ ┌─────────┐
|
||||
│ STATUS │ │ LOGIN │
|
||||
└────┬────┘ └────┬────┘
|
||||
│ ping done / disconnect │ Login Success + LoginAcknowledged
|
||||
▼ │ (≥1.20.2 only)
|
||||
[close] ┌───┴──────────────┐
|
||||
│ CONFIGURATION │ (≥1.20.2 / protocol 764)
|
||||
└───┬──────────────┘
|
||||
│ Finish Configuration
|
||||
│ ┌─────────────────────┐
|
||||
▼ ◄─────────────┤ Start Configuration │
|
||||
┌────────┐ │ (Play→Config re-cfg) │
|
||||
│ PLAY ├────────────►┘ │
|
||||
└────────┘ └─────────────────────┘
|
||||
```
|
||||
|
||||
**Pre-1.20.2 path**: Login Success → PLAY directly (no Configuration state, no Login Acknowledged).
|
||||
|
||||
---
|
||||
|
||||
## 3. Handshake: How next-state is chosen
|
||||
|
||||
The very first packet sent on a new TCP connection is the serverbound **Handshake** (packet ID `0x00` in the HANDSHAKING state, stable since 1.7.2).
|
||||
|
||||
Fields:
|
||||
- `protocolVersion` — VarInt, the client's protocol number
|
||||
- `serverHost` — String (max 255), the address the client typed
|
||||
- `serverPort` — Unsigned Short
|
||||
- `nextState` — VarInt enum:
|
||||
- `1` → STATUS
|
||||
- `2` → LOGIN
|
||||
- `3` → LOGIN after server transfer (≥1.20.5 / protocol 766) <!-- VERIFY exact version of Transfer -->
|
||||
|
||||
The server reads `nextState` and immediately switches its decoder to the corresponding state. There is no server response in the Handshaking state.
|
||||
|
||||
Source: `node-minecraft-protocol/src/server/handshake.js:34-38`
|
||||
```js
|
||||
if (packet.nextState === 1) {
|
||||
client.state = states.STATUS
|
||||
} else if (packet.nextState === 2) {
|
||||
client.state = states.LOGIN
|
||||
}
|
||||
```
|
||||
|
||||
The client side always sends `nextState: 2` for normal login:
|
||||
`node-minecraft-protocol/src/client/setProtocol.js:21-27`
|
||||
```js
|
||||
client.write('set_protocol', {
|
||||
protocolVersion: options.protocolVersion,
|
||||
serverHost: taggedHost,
|
||||
serverPort: options.port,
|
||||
nextState: 2
|
||||
})
|
||||
client.state = states.LOGIN
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Status Path (Server List Ping)
|
||||
|
||||
Used by the client (and external tools) to read MOTD, player count, and favicon without authenticating.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as Client
|
||||
participant S as Server
|
||||
|
||||
C->>S: "Handshake (0x00, nextState=1)"
|
||||
Note over C,S: Both switch to STATUS state
|
||||
C->>S: "Status Request (0x00)"
|
||||
S->>C: "Status Response (0x00) — JSON payload"
|
||||
C->>S: "Ping Request (0x01) — Long timestamp"
|
||||
S->>C: "Pong Response (0x01) — echo timestamp"
|
||||
Note over C,S: Server closes connection
|
||||
```
|
||||
|
||||
Notes:
|
||||
- The Ping Request / Pong Response round-trip is optional but used by vanilla to measure latency.
|
||||
- No encryption, no compression, no auth in this path.
|
||||
- Server handler: `node-minecraft-protocol/src/server/ping.js:7-64` — writes `server_info` then echoes `ping` then ends.
|
||||
- Velocity STATUS registration: `StateRegistry.java:148-161` — `StatusRequestPacket` 0x00, `StatusPingPacket` 0x01 (both directions).
|
||||
|
||||
---
|
||||
|
||||
## 5. Login Path
|
||||
|
||||
### 5a. Pre-1.20.2 (protocol < 764): Login → Play directly
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as Client
|
||||
participant S as Server
|
||||
|
||||
C->>S: "Handshake (0x00, nextState=2)"
|
||||
Note over C,S: Both switch to LOGIN state
|
||||
C->>S: "Login Start (0x00) — username + UUID"
|
||||
Note over S: online-mode check
|
||||
S->>C: "Encryption Request (0x01) — serverId, publicKey, verifyToken [online-mode only]"
|
||||
Note over C: Mojang auth (joinServer call)
|
||||
C->>S: "Encryption Response (0x01) — encrypted sharedSecret + verifyToken"
|
||||
Note over C,S: Both enable AES-128-CFB8 encryption immediately
|
||||
Note over S: Yggdrasil hasJoined verification
|
||||
S->>C: "Set Compression (0x03) — threshold [optional, >=1.8]"
|
||||
Note over C,S: All subsequent packets use compressed format if enabled
|
||||
S->>C: "Login Success (0x02) — UUID + username + properties"
|
||||
Note over C,S: Both switch to PLAY state
|
||||
S->>C: "Join Game (Play 0x01/varies) — game state, dimension, etc."
|
||||
```
|
||||
|
||||
Key sequencing rules (pre-1.20.2):
|
||||
- **Set Compression must precede Login Success.** Anything sent after Set Compression (including Login Success itself) uses the compressed packet format. Source: minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Protocol
|
||||
- **Encryption activates immediately** after the server receives Encryption Response — both sides flip the AES cipher before the next byte. Source: `node-minecraft-protocol/src/client/encrypt.js:73` (`client.setEncryption(sharedSecret)` called before returning from handler); `node-minecraft-protocol/src/server/login.js:150` (server side, same).
|
||||
- Offline-mode servers skip Encryption Request/Response entirely (`node-minecraft-protocol/src/server/login.js:88-106`).
|
||||
- Set Compression was added in 1.8 (protocol 47). Prior versions (1.7.x) have no compression packet; per-packet compression existed in very early snapshots but was dropped. Source: `StateRegistry.java:869-870` (`SetCompressionPacket` mapped from `MINECRAFT_1_8`).
|
||||
|
||||
### 5b. 1.20.2+ (protocol ≥ 764): Login → Configuration → Play
|
||||
|
||||
The big change in 1.20.2 inserted the **Configuration** state between Login and Play. The gate is `LoginAcknowledged` — the client must explicitly acknowledge Login Success before Configuration begins.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as Client
|
||||
participant S as Server
|
||||
|
||||
C->>S: "Handshake (0x00, nextState=2)"
|
||||
Note over C,S: Both switch to LOGIN state
|
||||
C->>S: "Login Start (0x00) — username + UUID"
|
||||
S->>C: "Encryption Request (0x01) [online-mode only]"
|
||||
Note over C: Mojang auth
|
||||
C->>S: "Encryption Response (0x01)"
|
||||
Note over C,S: AES-128-CFB8 encryption enabled
|
||||
S->>C: "Set Compression (0x03) [optional]"
|
||||
Note over C,S: Compressed format enabled if threshold >= 0
|
||||
S->>C: "Login Success (0x02) — UUID + username + properties"
|
||||
C->>S: "Login Acknowledged (0x03) [NEW in 1.20.2]"
|
||||
Note over C,S: Both switch to CONFIGURATION state
|
||||
S->>C: "Registry Data (0x05/0x07) — codec entries"
|
||||
S->>C: "Feature Flags (0x0C) — enabled experiments"
|
||||
S->>C: "Known Packs (0x0E) [>=1.20.5]"
|
||||
C->>S: "Known Packs (0x07) — client's pack list [>=1.20.5]"
|
||||
S->>C: "Tags (0x0D/varies) — tag registry"
|
||||
S->>C: "Finish Configuration (0x02/0x03)"
|
||||
C->>S: "Acknowledge Finish Configuration (0x02/0x03)"
|
||||
Note over C,S: Both switch to PLAY state
|
||||
S->>C: "Login / Join Game (Play 0x29/varies)"
|
||||
```
|
||||
|
||||
Sources:
|
||||
- `LoginAcknowledgedPacket` registered serverbound at `0x03` from `MINECRAFT_1_20_2`: `StateRegistry.java:853-854`
|
||||
- Server side transition: `node-minecraft-protocol/src/server/login.js:189-239` — on `login_acknowledged`, server switches to `states.CONFIGURATION`, sends `registry_data`, then `finish_configuration`; client acks with `finish_configuration`, server switches to `states.PLAY`
|
||||
- Client side: `node-minecraft-protocol/src/client/play.js:40-43` — on `success`, writes `login_acknowledged` and calls `enterConfigState()`
|
||||
- `KnownPacksPacket` registered in CONFIG from `MINECRAFT_1_20_5`: `StateRegistry.java:193-194, 249-250`
|
||||
- Velocity CONFIG state registration starts at `MINECRAFT_1_20_2` throughout: `StateRegistry.java:163-261`
|
||||
|
||||
---
|
||||
|
||||
## 6. Configuration State
|
||||
|
||||
**Introduced**: 1.20.2, protocol 764. Source: `StateRegistry.java:163` (first CONFIG packet mapped from `MINECRAFT_1_20_2`); minecraft.wiki/w/Java_Edition_protocol/Packets.
|
||||
|
||||
Purpose: synchronise server-side data (registry codec, feature flags, resource packs, tags, known data packs) to the client before any gameplay packets flow. Replacing what was previously stuffed into the Login sequence or early Play packets.
|
||||
|
||||
Key packets (clientbound unless noted):
|
||||
- `Plugin Message` — channel-based custom data, same as Play
|
||||
- `Disconnect` — kick during config
|
||||
- `Keep Alive` — connection liveness check
|
||||
- `Registry Data` — serialised registry codec entries (dimension types, biomes, damage types, etc.)
|
||||
- `Feature Flags` — experimental feature toggles
|
||||
- `Resource Pack Request` — push a resource pack
|
||||
- `Tags Update` — tag data (formerly in Play)
|
||||
- `Known Packs` (≥1.20.5) — bidirectional; server asks, client responds with list of known data packs to skip redundant sync
|
||||
- `Finish Configuration` (clientbound) → `Acknowledge Finish Configuration` (serverbound) — the handshake that exits Configuration
|
||||
|
||||
Full CONFIG packet table: `StateRegistry.java:163-261`
|
||||
|
||||
---
|
||||
|
||||
## 7. Re-configuration: Play → Configuration
|
||||
|
||||
**Since 1.20.2.** A connected player can be sent back to Configuration state without disconnecting. This allows servers to push updated registries (e.g., when switching between sub-servers with different dimensions).
|
||||
|
||||
Trigger: server sends **Start Configuration** (`StartUpdatePacket`) in the Play state.
|
||||
- Velocity: `StateRegistry.java:805-813`, registered from `MINECRAFT_1_20_2`
|
||||
|
||||
Client responds: **Acknowledge Configuration** (serverbound Play packet, Velocity name `FinishedUpdatePacket`)
|
||||
- Velocity: `StateRegistry.java:407-413`, registered from `MINECRAFT_1_20_2`
|
||||
|
||||
Both sides switch back to Configuration state. The full Configuration exchange runs again (registry data, finish handshake). On `Acknowledge Finish Configuration`, both switch back to Play.
|
||||
|
||||
Client-side handling in node-minecraft-protocol:
|
||||
```js
|
||||
// node-minecraft-protocol/src/client/play.js:43
|
||||
client.on('start_configuration', () => enterConfigState())
|
||||
// ...
|
||||
// play.js:51-53: if state===PLAY, write 'configuration_acknowledged' before switching
|
||||
if (client.state === states.PLAY) {
|
||||
client.write('configuration_acknowledged', {})
|
||||
}
|
||||
client.state = states.CONFIGURATION
|
||||
```
|
||||
Source: `node-minecraft-protocol/src/client/play.js:43-55`
|
||||
|
||||
---
|
||||
|
||||
## 8. Encryption Details
|
||||
|
||||
- **Algorithm**: AES-128-CFB8, symmetric, both directions use the same 16-byte shared secret.
|
||||
- **Key exchange**: RSA (server's ephemeral keypair, ≥1024-bit); client encrypts shared secret and verify token with server's public key.
|
||||
- **Activation point**: immediately after Encryption Response is sent/received — before the next byte on the wire. Encryption applies to all subsequent data including Set Compression and Login Success.
|
||||
- **Online-mode only** (vanilla): offline-mode servers skip the Encryption Request/Response entirely. As of 1.21, vanilla offline-mode servers do not use encryption. <!-- VERIFY: whether any version change altered this for 1.21 specifically -->
|
||||
- Source: `node-minecraft-protocol/src/client/encrypt.js` (client), `node-minecraft-protocol/src/server/login.js:88-155` (server)
|
||||
|
||||
---
|
||||
|
||||
## 9. Compression Details
|
||||
|
||||
- **Algorithm**: zlib deflate, wrapped in the compressed packet format (prepended with uncompressed data length as VarInt).
|
||||
- **Threshold**: packets with uncompressed size ≥ threshold are compressed; smaller packets are sent uncompressed (length VarInt = 0).
|
||||
- **Activation point**: Set Compression (Login 0x03) must arrive before Login Success. All subsequent packets — including Login Success — use the compressed format. Source: minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Protocol
|
||||
- **Version gating**: added in 1.8 (protocol 47). Not present in 1.7.x. Source: `StateRegistry.java:869-870`
|
||||
- Default threshold used by node-minecraft-protocol: 256 bytes. Source: `node-minecraft-protocol/src/server/login.js:179`
|
||||
- Client receives `compress` / `set_compression` event: `node-minecraft-protocol/src/client/compress.js:3-8`
|
||||
|
||||
---
|
||||
|
||||
## 10. Protocol Version Reference
|
||||
|
||||
| Minecraft version | Protocol number | Notable state-machine change |
|
||||
|---|---|---|
|
||||
| 1.7.2 | 4 | Baseline modern protocol; Handshake/Status/Login/Play |
|
||||
| 1.8 | 47 | Set Compression (0x03 in Login) added |
|
||||
| 1.13 | 393 | Login Plugin Message (0x04) added |
|
||||
| 1.20.2 | 764 | **Configuration state inserted**; Login Acknowledged; re-configuration |
|
||||
| 1.20.5 | 766 | Known Packs exchange in Configuration; Cookie packets; Transfer login intent=3 |
|
||||
| 1.21 | 767 | (Config/Play packet renumbering) |
|
||||
| 1.21.2 | 769 | (further renumbering) |
|
||||
| 26.1 | 775 | Current as of 2026-06 |
|
||||
|
||||
Sources: Velocity `StateRegistry.java` import block (lines 21-50); minecraft.wiki/w/Java_Edition_protocol/Packets (protocol 773/775 noted).
|
||||
|
||||
---
|
||||
|
||||
## VERIFY flags
|
||||
|
||||
<!-- VERIFY: intent=3 (Transfer) exact minimum version — wiki says ≥1.20.5 (protocol 766); Velocity TRANSFER_ID=3 at StateRegistry.java:883 consistent, but the Transfer login packet path vs. the Transfer clientbound packet in Play/Config should be distinguished. -->
|
||||
<!-- VERIFY: "offline-mode servers do not use encryption as of 1.21" — wiki claim; node-minecraft-protocol skips encryption if !needToVerify regardless of version, which is correct, but the "as of 1.21" qualifier may be wiki-specific. -->
|
||||
<!-- VERIFY: exact packet IDs for Configuration-state packets shift between 1.20.2, 1.20.3, 1.20.5 — the table at StateRegistry.java:163-261 is authoritative for Velocity's mapping but wiki IDs may differ for vanilla. -->
|
||||
Reference in New Issue
Block a user