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:
+455
@@ -0,0 +1,455 @@
|
||||
# Minecraft Java Edition 1.7 — Protocol Deep-Dive
|
||||
|
||||
> **Range floor.** This line defines the oldest protocol the modern toolchain
|
||||
> targets. Every proxy, translator, and compatibility layer that claims "1.7+"
|
||||
> speaks the protocol described here.
|
||||
|
||||
---
|
||||
|
||||
## Header
|
||||
|
||||
| Sub-release range | Protocol version | Release dates | Cross-compatible |
|
||||
|---|---|---|---|
|
||||
| 1.7.2 | 4 | 2013-10-25 | — |
|
||||
| 1.7.3 | 4 | 2013-10-26 | yes, with 1.7.2 |
|
||||
| 1.7.4 | 4 | 2013-12-09 <!-- VERIFY exact date --> | yes, with 1.7.2–3 |
|
||||
| 1.7.5 | 4 | 2014-02-26 <!-- VERIFY exact date --> | yes, with 1.7.2–4 |
|
||||
| 1.7.6 | **5** | 2014-04-09 | **incompatible** with 1.7.2–5 |
|
||||
| 1.7.7 | **5** | 2014-04-10 <!-- VERIFY exact date --> | yes, with 1.7.6 |
|
||||
| 1.7.8 | **5** | 2014-06-16 <!-- VERIFY exact date --> | yes, with 1.7.6–7 |
|
||||
| 1.7.9 | **5** | 2014-06-16 <!-- VERIFY exact date --> | yes, with 1.7.6–8 |
|
||||
| 1.7.10 | **5** | 2014-06-26 | yes, with 1.7.6–9 |
|
||||
|
||||
Sources:
|
||||
- minecraft.wiki `/w/Java_Edition_1.7.2` (fetched 2026-06-19) — release date, protocol 4, Netty rewrite
|
||||
- minecraft.wiki `/w/Java_Edition_1.7.6` (fetched 2026-06-19) — release date 2014-04-09, protocol 5 confirmed
|
||||
- minecraft.wiki `/w/Java_Edition_1.7.10` (fetched 2026-06-19) — release date 2014-06-26, protocol 5, compatibility note
|
||||
- `/tmp/mcproto-refs/minecraft-data/data/pc/1.7/version.json` — `{"version":5,"minecraftVersion":"1.7.10","majorVersion":"1.7"}`
|
||||
|
||||
The protocol-version page on minecraft.wiki (fetched 2026-06-19) listed all of
|
||||
1.7.2–1.7.10 as protocol 4; the per-version pages for 1.7.6 and 1.7.10
|
||||
individually confirm protocol 5, and the minecraft-data canonical record for
|
||||
`1.7.10` is version 5. The per-version pages are the higher-fidelity source;
|
||||
<!-- VERIFY --> the exact boundary (whether 1.7.2–1.7.5 = 4 and 1.7.6–1.7.10 = 5
|
||||
is consistent with the protocol-version list being incomplete/erroneous for the
|
||||
1.7 range).
|
||||
|
||||
---
|
||||
|
||||
## 1. Headline changes — the Netty rewrite
|
||||
|
||||
1.7.2 ("The Update that Changed the World", released 2013-10-25) is not just a
|
||||
gameplay release — it is the **architectural reset** for the entire modern
|
||||
Minecraft protocol. Everything that followed is a delta on top of what 1.7.2
|
||||
established.
|
||||
|
||||
### 1.1 Netty and length-prefixed VarInt framing
|
||||
|
||||
The network layer was **completely rewritten** to use [Netty](https://netty.io/),
|
||||
replacing the old hand-rolled byte-stream code. The visible wire change:
|
||||
|
||||
```
|
||||
[length: VarInt] [packet-id: VarInt] [fields...]
|
||||
```
|
||||
|
||||
Before 1.7, each packet began with a fixed-width packet-ID byte; the receiver
|
||||
had to know the exact byte length of every packet to know where the next one
|
||||
started. After 1.7, **every packet is prefixed by its own byte length as a
|
||||
VarInt**, making the stream self-delimiting regardless of packet content.
|
||||
The minecraft.wiki 1.7.2 release article confirms "there is now a packet length
|
||||
header" (fetched 2026-06-19). The `string` type likewise moved to VarInt-length
|
||||
prefix (see `protocol.json` `types.string` entry,
|
||||
`/tmp/mcproto-refs/minecraft-data/data/pc/1.7/protocol.json`).
|
||||
|
||||
### 1.2 State machine: Handshake / Status / Login / Play
|
||||
|
||||
Before 1.7, the connection had two informal phases (pre-login and in-game).
|
||||
1.7.2 formalised this as **four explicit states**, each with its own packet
|
||||
namespace and numeric IDs:
|
||||
|
||||
| State | Initiated by |
|
||||
|---|---|
|
||||
| **Handshaking** | Always the first state; client sends one packet (0x00 Set Protocol) then transitions |
|
||||
| **Status** (next\_state = 1) | Server List Ping path |
|
||||
| **Login** (next\_state = 2) | Authentication + encryption setup path |
|
||||
| **Play** | After Login Success; all gameplay traffic |
|
||||
|
||||
Packet IDs are **per-state**. `0x00` in Status means something completely
|
||||
different from `0x00` in Login. The pre-1.7 protocol had a flat namespace where
|
||||
a single byte identified every packet across all phases — this is the deepest
|
||||
structural break from the legacy protocol.
|
||||
|
||||
### 1.3 JSON chat
|
||||
|
||||
Chat messages on the wire became JSON-encoded `Text` component strings
|
||||
(source: minecraft.wiki 1.7.2 article — "/tellraw enables JSON formatted
|
||||
messages"; fetched 2026-06-19). The `packet_chat` payload (both S→C and C→S) is
|
||||
a VarInt-length-prefixed `string` carrying JSON. This is a breaking change from
|
||||
the old raw-string chat packet.
|
||||
|
||||
### 1.4 New Server List Ping (SLP)
|
||||
|
||||
The Server List Ping was redesigned. The new flow uses the **Status state**:
|
||||
|
||||
1. Client sends Handshake (0x00) with `next_state = 1`.
|
||||
2. Client sends Status Request (0x00, no fields).
|
||||
3. Server responds with Status Response (0x00) carrying a JSON string:
|
||||
`{"version":{"name":"...","protocol":N},"players":{...},"description":{...},"favicon":"data:image/png;base64,..."}`
|
||||
4. Client sends Ping (0x01, i64 timestamp); server echoes it back.
|
||||
|
||||
The old "legacy SLP" (0xFE magic byte) is preserved as
|
||||
`packet_legacy_server_list_ping` in the Handshake state for pre-1.7 client
|
||||
compatibility (see `protocol.json` handshaking.toServer mappings). Server icons
|
||||
in the server list (`favicon` field as a base64 PNG) are new in 1.7.2
|
||||
(minecraft.wiki 1.7.2; fetched 2026-06-19).
|
||||
|
||||
### 1.5 Packet renumbering
|
||||
|
||||
All packet IDs were renumbered. The pre-1.7 flat namespace had IDs like 0x01
|
||||
(Login Request), 0x02 (Handshake), 0xFF (Kick Disconnect) scattered across 256
|
||||
possible values. In 1.7, the Play state alone starts at 0x00 and counts up
|
||||
sequentially within the state; 65 unique packet types were defined (41 S→C + 24
|
||||
C→S in the 1.7.10 data — see §3).
|
||||
|
||||
---
|
||||
|
||||
## 2. Protocol 4 → Protocol 5 delta (1.7.6)
|
||||
|
||||
The bump from protocol 4 (1.7.2–1.7.5) to protocol 5 (1.7.6–1.7.10) coincided
|
||||
with a **skin system overhaul** in 1.7.6 (released 2014-04-09;
|
||||
minecraft.wiki `/w/Java_Edition_1.7.6`, fetched 2026-06-19).
|
||||
|
||||
Key changes at the wire level:
|
||||
|
||||
- **Signed skin URLs**: Skins and capes moved from a single centralised skin
|
||||
server to per-player signed URLs distributed through session servers. The
|
||||
`packet_named_entity_spawn` (Play 0x0C S→C) already carried a `data` array of
|
||||
property `{name, value, signature}` triples in the minecraft-data 1.7 schema
|
||||
— this is the textures property bag that carries the signed skin URL.
|
||||
<!-- VERIFY --> whether this field was added in 1.7.2 or specifically in 1.7.6
|
||||
(it is present in the 1.7.10 protocol.json which covers protocol 5).
|
||||
- **Name-change infrastructure**: Server-side preparation for player name
|
||||
changing (actual service launched 2015-02-04). No packet-schema change, but
|
||||
UUID-keyed identity was reinforced.
|
||||
- **Per-server resource pack option**: `packet_resource_pack_send` <!--
|
||||
VERIFY --> exact packet ID / whether this is a new Play packet in protocol 5 or
|
||||
re-uses an existing custom\_payload channel.
|
||||
- The 1.7.6 client was incompatible with 1.7.2–1.7.5 servers (confirmed by
|
||||
minecraft.wiki 1.7.6; fetched 2026-06-19), making the protocol-5 boundary
|
||||
hard.
|
||||
|
||||
The subsequent 1.7.7–1.7.10 releases did not change the protocol number; they
|
||||
were bug-fix and stability releases (1.7.6 had a crash that was fixed the next
|
||||
day in 1.7.7).
|
||||
|
||||
---
|
||||
|
||||
## 3. Packet inventory (protocol 5 / 1.7.10)
|
||||
|
||||
Source: `/tmp/mcproto-refs/minecraft-data/data/pc/1.7/protocol.json`
|
||||
|
||||
### 3.1 Handshaking state
|
||||
|
||||
| ID | Direction | Name | Key fields |
|
||||
|---|---|---|---|
|
||||
| 0x00 | C→S | Set Protocol | `protocolVersion` (VarInt), `serverHost` (string), `serverPort` (u16), `nextState` (VarInt: 1=Status, 2=Login) |
|
||||
| 0xFE | C→S | Legacy Server List Ping | `payload` (u8, always 0x01) — pre-1.7 compat |
|
||||
|
||||
No S→C packets in this state.
|
||||
|
||||
### 3.2 Status state
|
||||
|
||||
| ID | Direction | Name | Key fields |
|
||||
|---|---|---|---|
|
||||
| 0x00 | C→S | Ping Start | (no fields) |
|
||||
| 0x01 | C→S | Ping | `time` (i64) |
|
||||
| 0x00 | S→C | Server Info | `response` (string — JSON payload) |
|
||||
| 0x01 | S→C | Ping | `time` (i64 echo) |
|
||||
|
||||
### 3.3 Login state
|
||||
|
||||
| ID | Direction | Name | Key fields |
|
||||
|---|---|---|---|
|
||||
| 0x00 | C→S | Login Start | `username` (string) |
|
||||
| 0x01 | C→S | Encryption Begin | `sharedSecret` (i16-length buffer), `verifyToken` (i16-length buffer) |
|
||||
| 0x00 | S→C | Disconnect | `reason` (string — JSON chat) |
|
||||
| 0x01 | S→C | Encryption Request | `serverId` (string), `publicKey` (i16-length buffer), `verifyToken` (i16-length buffer) |
|
||||
| 0x02 | S→C | Login Success | `uuid` (string), `username` (string) |
|
||||
|
||||
Notes:
|
||||
- No Login Compression packet (0x03) — that is a 1.8 addition.
|
||||
- `publicKey` and `verifyToken` use **i16** (signed 16-bit) as the length
|
||||
prefix, not VarInt; this is a known 1.7 quirk that 1.8 also retains
|
||||
<!-- VERIFY --> exact format change in 1.8 if any.
|
||||
- `uuid` in Login Success is transmitted as a **string** (hyphenated UUID text),
|
||||
not as two i64 fields — that encoding came later.
|
||||
|
||||
### 3.4 Play state — Server→Client (0x00–0x40)
|
||||
|
||||
| ID | Name | Key fields / notes |
|
||||
|---|---|---|
|
||||
| 0x00 | Keep Alive | `keepAliveId` (i32) |
|
||||
| 0x01 | Login (Join Game) | `entityId` (i32), `gameMode` (u8), `dimension` (i8), `difficulty` (u8), `maxPlayers` (u8), `levelType` (string) |
|
||||
| 0x02 | Chat Message | `message` (string — JSON) |
|
||||
| 0x03 | Update Time | `age` (i64), `time` (i64) |
|
||||
| 0x04 | Entity Equipment | `entityId` (i32), `slot` (i16), `item` (Slot) |
|
||||
| 0x05 | Spawn Position | `location` (position\_iii: 3×i32) |
|
||||
| 0x06 | Update Health | `health` (f32), `food` (i16), `foodSaturation` (f32) |
|
||||
| 0x07 | Respawn | `dimension` (i32), `difficulty` (u8), `gamemode` (u8), `levelType` (string) |
|
||||
| 0x08 | Player Position And Look | `x/y/z` (f64), `yaw/pitch` (f32), `onGround` (bool) |
|
||||
| 0x09 | Held Item Change | `slot` (i8) |
|
||||
| 0x0A | Use Bed | `entityId` (i32), `location` (position\_ibi: i32/u8/i32) |
|
||||
| 0x0B | Animation | `entityId` (VarInt), `animation` (u8) |
|
||||
| 0x0C | Spawn Named Entity | `entityId` (VarInt), `playerUUID` (string), `playerName` (string), `data` (array of {name,value,signature} property triples), `x/y/z` (i32), `yaw/pitch` (i8), `currentItem` (i16), `metadata` |
|
||||
| 0x0D | Collect Item | `collectedEntityId` (i32), `collectorEntityId` (i32) |
|
||||
| 0x0E | Spawn Object | `entityId` (VarInt), `type` (i8), `x/y/z` (i32), `pitch/yaw` (i8), `objectData` (i32 + conditional i16×3 velocity) |
|
||||
| 0x0F | Spawn Mob | `entityId` (VarInt), `type` (u8), `x/y/z` (i32), `yaw/pitch/headPitch` (i8), `velocity` (vec3i16), `metadata` |
|
||||
| 0x10 | Spawn Painting | `entityId` (VarInt), `title` (string), `location` (position\_iii), `direction` (i32) |
|
||||
| 0x11 | Spawn Experience Orb | `entityId` (VarInt), `x/y/z` (i32), `count` (i16) |
|
||||
| 0x12 | Entity Velocity | `entityId` (i32), `velocity` (vec3i16) |
|
||||
| 0x13 | Destroy Entities | `entityIds` (i8-count array of i32) |
|
||||
| 0x14 | Entity | `entityId` (i32) — no-op movement |
|
||||
| 0x15 | Entity Relative Move | `entityId` (i32), `dX/dY/dZ` (i8) |
|
||||
| 0x16 | Entity Look | `entityId` (i32), `yaw/pitch` (i8) |
|
||||
| 0x17 | Entity Look And Relative Move | `entityId` (i32), `dX/dY/dZ` (i8), `yaw/pitch` (i8) |
|
||||
| 0x18 | Entity Teleport | `entityId` (i32), `x/y/z` (i32), `yaw/pitch` (i8) |
|
||||
| 0x19 | Entity Head Look | `entityId` (i32), `headYaw` (i8) |
|
||||
| 0x1A | Entity Status | `entityId` (i32), `entityStatus` (i8) |
|
||||
| 0x1B | Attach Entity | `entityId` (i32), `vehicleId` (i32), `leash` (bool) |
|
||||
| 0x1C | Entity Metadata | `entityId` (i32), `metadata` (entityMetadata) |
|
||||
| 0x1D | Entity Effect | `entityId` (i32), `effectId/amplifier` (i8), `duration` (i16) |
|
||||
| 0x1E | Remove Entity Effect | `entityId` (i32), `effectId` (i8) |
|
||||
| 0x1F | Set Experience | `experienceBar` (f32), `level/totalExperience` (i16) |
|
||||
| 0x20 | Entity Properties | `entityId` (i32), `properties` (i32-count array of {key:string, value:f64, modifiers:[{uuid,amount:f64,operation:i8}]}) |
|
||||
| 0x21 | Chunk Data | `x/z` (i32), `groundUp` (bool), `bitMap/addBitMap` (u16), `compressedChunkData` (i32-length buffer) |
|
||||
| 0x22 | Multi Block Change | `chunkX/chunkZ` (i32), `recordCount` (i16), `dataLength` (i32), `records` (array of {metadata:4bit, blockId:12bit, y:u8, z:4bit, x:4bit}) |
|
||||
| 0x23 | Block Change | `location` (position\_ibi), `type` (VarInt), `metadata` (u8) |
|
||||
| 0x24 | Block Action | `location` (position\_isi: i32/i16/i32), `byte1/byte2` (u8), `blockId` (VarInt) |
|
||||
| 0x25 | Block Break Animation | `entityId` (VarInt), `location` (position\_iii), `destroyStage` (i8) |
|
||||
| 0x26 | Map Chunk Bulk | `chunkColumnCount` (i16), `dataLength` (i32), `skyLightSent` (bool), `compressedChunkData`, `meta` array of {x/z:i32, bitMap/addBitMap:u16} |
|
||||
| 0x27 | Explosion | `x/y/z/radius` (f32), `affectedBlockOffsets` (i32-count array of i8×3), `playerMotionX/Y/Z` (f32) |
|
||||
| 0x28 | Effect (World Event) | `effectId` (i32), `location` (position\_ibi), `data` (i32), `global` (bool) |
|
||||
| 0x29 | Named Sound Effect | `soundName` (string), `x/y/z` (i32), `volume` (f32), `pitch` (u8) |
|
||||
| 0x2A | World Particles | `particleName` (string), `x/y/z/offsetX/Y/Z/particleData` (f32), `particles` (i32) |
|
||||
| 0x2B | Game State Change | `reason` (u8), `gameMode` (f32) |
|
||||
| 0x2C | Spawn Global Entity (Weather) | `entityId` (VarInt), `type` (i8), `x/y/z` (i32) |
|
||||
| 0x2D | Open Window | `windowId` (u8), `inventoryType` (u8), `windowTitle` (string), `slotCount` (u8), `useProvidedTitle` (bool), `entityId` (i32, only for type 11) |
|
||||
| 0x2E | Close Window | `windowId` (u8) |
|
||||
| 0x2F | Set Slot | `windowId` (i8), `slot` (i16), `item` (Slot) |
|
||||
| 0x30 | Window Items | `windowId` (u8), `items` (i16-count Slot array) |
|
||||
| 0x31 | Window Property | `windowId` (u8), `property/value` (i16) |
|
||||
| 0x32 | Confirm Transaction | `windowId` (u8), `action` (i16), `accepted` (bool) |
|
||||
| 0x33 | Update Sign | `location` (position\_isi), `text1–4` (string×4) |
|
||||
| 0x34 | Maps | `itemDamage` (VarInt), `data` (i16-length buffer) |
|
||||
| 0x35 | Update Block Entity | `location` (position\_isi), `action` (u8), `nbtData` (compressedNbt) |
|
||||
| 0x36 | Open Sign Editor | `location` (position\_iii) |
|
||||
| 0x37 | Statistics | `entries` (VarInt-count array of {name:string, value:VarInt}) |
|
||||
| 0x38 | Player List Item | `playerName` (string), `online` (bool), `ping` (i16) |
|
||||
| 0x39 | Player Abilities | `flags` (i8), `flyingSpeed/walkingSpeed` (f32) |
|
||||
| 0x3A | Tab-Complete | `matches` (VarInt-count string array) |
|
||||
| 0x3B | Scoreboard Objective | `name` (string), `displayText` (string), `action` (i8) |
|
||||
| 0x3C | Update Score | `itemName` (string), `action` (i8), `scoreName` (string, absent if action=1), `value` (i32, absent if action=1) |
|
||||
| 0x3D | Display Scoreboard | `position` (i8), `name` (string) |
|
||||
| 0x3E | Teams | `team` (string), `mode` (i8), conditional fields for name/prefix/suffix/friendlyFire/players depending on mode |
|
||||
| 0x3F | Plugin Message | `channel` (string), `data` (i16-length buffer) |
|
||||
| 0x40 | Disconnect | `reason` (string — JSON chat) |
|
||||
|
||||
**Total S→C Play: 65 packet types (0x00–0x40).**
|
||||
|
||||
### 3.5 Play state — Client→Server (0x00–0x17)
|
||||
|
||||
| ID | Name | Key fields / notes |
|
||||
|---|---|---|
|
||||
| 0x00 | Keep Alive | `keepAliveId` (i32) |
|
||||
| 0x01 | Chat Message | `message` (string) |
|
||||
| 0x02 | Use Entity | `target` (i32), `mouse` (i8: 0=interact, 1=attack, 2=interact\_at), `x/y/z` (f32, only if mouse=2) |
|
||||
| 0x03 | Player (ground status only) | `onGround` (bool) |
|
||||
| 0x04 | Player Position | `x/stance/y/z` (f64), `onGround` (bool) — note `stance` field (eye height) |
|
||||
| 0x05 | Player Look | `yaw/pitch` (f32), `onGround` (bool) |
|
||||
| 0x06 | Player Position And Look | `x/stance/y/z` (f64), `yaw/pitch` (f32), `onGround` (bool) |
|
||||
| 0x07 | Player Digging | `status` (i8), `location` (position\_ibi: i32/u8/i32), `face` (i8) |
|
||||
| 0x08 | Player Block Placement | `location` (position\_ibi), `direction` (i8), `heldItem` (Slot), `cursorX/Y/Z` (i8) |
|
||||
| 0x09 | Held Item Change | `slotId` (i16) |
|
||||
| 0x0A | Animation | `entityId` (i32), `animation` (i8) |
|
||||
| 0x0B | Entity Action | `entityId` (i32), `actionId` (i8), `jumpBoost` (i32) |
|
||||
| 0x0C | Steer Vehicle | `sideways/forward` (f32), `jump/unmount` (bool) |
|
||||
| 0x0D | Close Window | `windowId` (u8) |
|
||||
| 0x0E | Click Window | `windowId` (i8), `slot` (i16), `mouseButton` (i8), `action` (i16), `mode` (i8), `item` (Slot) |
|
||||
| 0x0F | Confirm Transaction | `windowId` (i8), `action` (i16), `accepted` (bool) |
|
||||
| 0x10 | Creative Inventory Action | `slot` (i16), `item` (Slot) |
|
||||
| 0x11 | Enchant Item | `windowId/enchantment` (i8) |
|
||||
| 0x12 | Update Sign | `location` (position\_isi), `text1–4` (string×4) |
|
||||
| 0x13 | Player Abilities | `flags` (i8), `flyingSpeed/walkingSpeed` (f32) |
|
||||
| 0x14 | Tab-Complete | `text` (string) |
|
||||
| 0x15 | Client Settings | `locale` (string), `viewDistance` (i8), `chatFlags` (i8), `chatColors` (bool), `difficulty` (u8), `showCape` (bool) |
|
||||
| 0x16 | Client Status | `payload` (i8: 0=perform\_respawn, 1=request\_stats, 2=open\_inventory) |
|
||||
| 0x17 | Plugin Message | `channel` (string), `data` (i16-length buffer) |
|
||||
|
||||
**Total C→S Play: 24 packet types (0x00–0x17).**
|
||||
|
||||
---
|
||||
|
||||
## 4. Notable data-type details
|
||||
|
||||
### Slot (item stack)
|
||||
```
|
||||
blockId: i16 // -1 = empty (null slot)
|
||||
if blockId != -1:
|
||||
itemCount: i8
|
||||
itemDamage: i16
|
||||
nbtData: compressedNbt // zlib-deflate compressed NBT, or 0x0000 if absent
|
||||
```
|
||||
Source: `protocol.json` `types.slot`.
|
||||
|
||||
### Entity Metadata (1.7 wire format)
|
||||
Each entry is a byte whose high 3 bits are the type and low 5 bits are the key
|
||||
(`entityMetadataItem` bitfield). Types: 0=i8, 1=i16, 2=i32, 3=f32, 4=string,
|
||||
5=Slot, 6=int-triple (x/y/z i32s), 7=float-triple (pitch/yaw/roll). The loop
|
||||
terminates on byte value `0x7F` (127). Source: `protocol.json`
|
||||
`types.entityMetadataLoop` / `entityMetadataItem`.
|
||||
|
||||
### String
|
||||
VarInt-prefixed UTF-8 (`pstring` with `countType: varint`). No null terminator.
|
||||
Source: `protocol.json` `types.string`.
|
||||
|
||||
### Position variants (1.7-era)
|
||||
No packed 64-bit Position type (that is a 1.8 addition). 1.7 uses explicit
|
||||
multi-field combinations:
|
||||
- `position_iii` — x:i32, y:i32, z:i32
|
||||
- `position_isi` — x:i32, y:i16, z:i32
|
||||
- `position_ibi` — x:i32, y:u8, z:i32
|
||||
|
||||
Source: `protocol.json` `types.position_iii` / `position_isi` / `position_ibi`.
|
||||
|
||||
### Player Position — `stance` field
|
||||
The client-to-server Player Position packets include a `stance` field (f64)
|
||||
representing the player's eye height offset above their feet (typically `y + 1.62`).
|
||||
The server uses this for hitbox calculations. The `stance` field was **removed in
|
||||
1.8** when the server began computing it server-side.
|
||||
<!-- VERIFY --> exact removal version.
|
||||
|
||||
### Plugin Message channel data length
|
||||
In 1.7, `packet_custom_payload` (both directions) uses an **i16** length prefix
|
||||
for the `data` buffer, not VarInt. Source: `protocol.json`
|
||||
`types` for `packet_custom_payload.data` in both play.toClient and play.toServer.
|
||||
|
||||
---
|
||||
|
||||
## 5. Wire framing summary
|
||||
|
||||
```
|
||||
Packet (1.7+):
|
||||
[length: VarInt] ← total byte count of the rest of this packet
|
||||
[id: VarInt] ← state-scoped packet ID (always 1 byte in 1.7 range: ≤ 0x7F)
|
||||
[fields: ...]
|
||||
|
||||
String:
|
||||
[byteLength: VarInt]
|
||||
[utf8 bytes: ...]
|
||||
|
||||
No compression in 1.7. Login Compression (0x03) was added in 1.8.
|
||||
No encryption wrapper at the framing level — AES/CFB8 is applied per-byte
|
||||
over the already-framed stream after Login Success.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Why 1.7 is the "oldest viable baseline"
|
||||
|
||||
Modern proxies (Velocity, BungeeCord, mc-router) target 1.7.10 as their floor
|
||||
because:
|
||||
|
||||
1. **State machine is present** — Handshake/Status/Login/Play states let a proxy
|
||||
correctly intercept the login sequence to inject forwarding data (BungeeCord
|
||||
IP-forwarding via serverAddress overload, Velocity login-plugin-message). The
|
||||
pre-1.7 protocol has no Login state; there is no clean injection point.
|
||||
|
||||
2. **VarInt framing is self-delimiting** — a proxy can pipeline and inspect
|
||||
packets without a static byte-length lookup table. The pre-1.7 format
|
||||
required per-packet hardcoded lengths.
|
||||
|
||||
3. **JSON chat and SLP** — modern server-list-ping tools and anti-bot layers
|
||||
depend on the JSON SLP format. The old 0xFE SLP returns a `§`-delimited
|
||||
string that is not JSON.
|
||||
|
||||
4. **UUIDs in Login Success** — the Login Success packet carries a UUID string,
|
||||
enabling player identity to be keyed on UUID rather than name. BungeeCord
|
||||
forwarding injects UUID + skin properties into the Handshake `serverAddress`
|
||||
or (for Velocity) via a Login Plugin Request; both require the Login state.
|
||||
|
||||
5. **Plugin Message channel** — the `custom_payload` packet (C→S 0x17, S→C
|
||||
0x3F) provides a channel-namespaced side-band that Forge uses for FML
|
||||
handshake (`FML|HS`) and that proxies use for capability negotiation.
|
||||
|
||||
The pre-1.7 legacy protocol (Minecraft ≤1.6.4, protocol ≤78) required a
|
||||
completely different code path. Proxies that support it (e.g. BungeeCord legacy
|
||||
mode) maintain a separate parser. The consensus since ~2015 is that 1.7.10 is
|
||||
the floor: old enough to be "legacy" but new enough to share the modern framing
|
||||
and state machine.
|
||||
|
||||
---
|
||||
|
||||
## 7. Proxy / translation impact
|
||||
|
||||
### ViaVersion floor
|
||||
|
||||
[ViaVersion](https://github.com/ViaVersion/ViaVersion) handles protocol
|
||||
translation for **1.8 and above only**. Its lowest supported input protocol is
|
||||
1.8 (protocol 47). There is no `protocols/v4to5/` or `protocols/v5to47/` package
|
||||
in ViaVersion; the 1.7 → 1.8 gap is explicitly out of scope for the ViaVersion
|
||||
project.
|
||||
|
||||
### Bridging below 1.8: ViaLegacy and ViaRewind
|
||||
|
||||
To support 1.7.x clients on modern servers, the translation stack requires:
|
||||
|
||||
- **ViaLegacy** — handles protocol ≤ 1.7.10 (protocol ≤ 5) → modern. It
|
||||
re-implements the full 1.7 state machine, framing parser, and all packet
|
||||
transforms including the `stance`-removal, position packing, and the 1.7
|
||||
entity-metadata format.
|
||||
- **ViaRewind** — handles 1.7 and 1.8 clients against 1.9+ servers. Sits
|
||||
on top of ViaVersion.
|
||||
|
||||
Neither ViaLegacy nor ViaRewind is cloned in this repo's ref set; they are
|
||||
referenced by name only. See their respective GitHub repos for implementation
|
||||
detail.
|
||||
|
||||
### What a 1.7-aware proxy must handle
|
||||
|
||||
| Concern | 1.7 detail | Modern delta |
|
||||
|---|---|---|
|
||||
| Framing | VarInt length + VarInt ID | Same in 1.8+ |
|
||||
| Compression | None | Login Compression added in 1.8 (0x03 Login packet) |
|
||||
| Encryption | RSA-1024 key exchange, AES/CFB8 shared secret, verifyToken — same mechanism as 1.8+ | i16 length prefix on `publicKey`/`verifyToken` buffers (vs VarInt in later versions <!-- VERIFY -->) |
|
||||
| Entity IDs in Play | Mix of i32 and VarInt (e.g. `entity_destroy` uses i32 array, `animation` uses VarInt) | Standardised to VarInt in 1.8 |
|
||||
| Position encoding | Three separate i32/i16/u8 fields | Packed 64-bit Position type added in 1.8 |
|
||||
| Slot NBT | `compressedNbt` (zlib-compressed) | Uncompressed NBT after 1.8 |
|
||||
| Player stance | Client sends `stance` (f64) in position packets | Removed in 1.8 |
|
||||
| Login Success UUID | String (hyphenated text) | Two i64 fields in 1.16+ <!-- VERIFY exact version --> |
|
||||
| Player List Item | Simple {name, online, ping} per-packet | Replaced with action-tagged packet in 1.8 |
|
||||
| Chunk format | zlib-compressed section bitmask + addBitMap; separate MapChunkBulk for multi-chunk | Restructured in 1.9 |
|
||||
|
||||
### BungeeCord / Velocity forwarding with 1.7 clients
|
||||
|
||||
BungeeCord legacy forwarding (IP injection into Handshake `serverAddress`) works
|
||||
unchanged with 1.7 clients because the `packet_set_protocol` (Handshake 0x00)
|
||||
field is a plain `string` that BungeeCord appends to with `\0` delimiters.
|
||||
|
||||
Velocity modern forwarding uses a **Login Plugin Request / Response** exchange in
|
||||
the Login state (added in 1.13). 1.7 clients do not support Login Plugin
|
||||
messages; a Velocity-in-modern-mode proxy cannot forward 1.7 clients without a
|
||||
ViaLegacy bridge that intercepts the Login Plugin exchange on behalf of the 1.7
|
||||
client. <!-- VERIFY --> exact Velocity behaviour when a 1.7 client hits a
|
||||
modern-forwarding backend.
|
||||
|
||||
---
|
||||
|
||||
## 8. Sources summary
|
||||
|
||||
| Source | Used for |
|
||||
|---|---|
|
||||
| minecraft.wiki `/w/Java_Edition_1.7.2` (fetched 2026-06-19) | Release date, Netty rewrite, JSON chat, SLP, packet length header |
|
||||
| minecraft.wiki `/w/Java_Edition_1.7.6` (fetched 2026-06-19) | Protocol 5 confirmation, release date 2014-04-09, skin overhaul, incompatibility note |
|
||||
| minecraft.wiki `/w/Java_Edition_1.7.10` (fetched 2026-06-19) | Protocol 5 confirmation, release date 2014-06-26, 1.7.6–9 compatibility note, Log4j note |
|
||||
| `/tmp/mcproto-refs/minecraft-data/data/pc/1.7/version.json` | Canonical: `{"version":5,"minecraftVersion":"1.7.10","majorVersion":"1.7"}` |
|
||||
| `/tmp/mcproto-refs/minecraft-data/data/pc/1.7/protocol.json` | Full packet inventory by state: all IDs, field names, data types |
|
||||
Reference in New Issue
Block a user