Files
minecraft_protocol/versions/1.8.md
T
claude-timemachine d73c1c9537 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>
2026-06-19 14:15:32 +02:00

24 KiB
Raw Blame History

1.8.x — The Bountiful Update

Protocol: 47
Versions: 1.8 (2014-09-02) through 1.8.9 (2015-12-09) — all share the same protocol number
Minecraft wiki release article: https://minecraft.wiki/w/Java_Edition_1.8 (fetched 2026-06-19)
minecraft-data sources: /tmp/mcproto-refs/minecraft-data/data/pc/1.8/protocol.json, version.json; diff against /tmp/mcproto-refs/minecraft-data/data/pc/1.7/protocol.json
ViaVersion source: /tmp/mcproto-refs/ViaVersion/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/packet/ClientboundPackets1_8.java, ServerboundPackets1_8.java
Protocol version confirmed: version.json (/tmp/mcproto-refs/minecraft-data/data/pc/1.8/version.json) = {"version":47,"minecraftVersion":"1.8.8","majorVersion":"1.8"}; 1.7.10 = {"version":5}.


Headline changes

1.8 ("The Bountiful Update") shipped 2014-09-02. On the wire it made three structural additions that defined the protocol for years:

Packet compression. A new Set Compression packet was inserted into the login flow (login-state, clientbound 0x03). From that point forward, each packet carries a second VarInt (Data Length) between the existing Packet Length VarInt and the payload, enabling optional zlib deflate per-packet. This is the biggest framing change since the Netty rewrite in 1.7. See § Compression detail below.

Palette-based block and chunk encoding. The Map Chunk (0x21) and Multi Block Change (0x22) packets switched from the 1.7 format (separate 8-bit block-ID + 4-bit metadata nibble-plane) to a single unified block-state integer. The Multi Block Change record went from a packed {12-bit blockId, 4-bit metadata} bitfield (separate words) to a single VarInt block-state ID per record. The block_change packet's payload similarly changed from {type:varint, metadata:u8} (two fields) to a single {type:varint} block-state ID.

New packed-integer Position type. Block coordinates moved from three separate i32/i16/i32 fields in a position_iii/position_isi/position_ibi struct to a single i64 packed as {x:26-bit signed, y:12-bit signed, z:26-bit signed}. This is the position bitfield type that appears throughout the 1.8 protocol.

Why protocol 47 stayed so long. 1.8 was the last major version for ~17 months (1.9 shipped 2016-02-29). Server operators preferred it for PvP because 1.9 introduced a weapon cooldown mechanic that most competitive networks rejected. Combined with Mojang's slow adoption of forced upgrades, protocol 47 remained the dominant live-traffic version until at least 2017 — the era that spawned the entire ViaVersion ecosystem.


Compression in detail

Cross-reference: ../05-login-encryption.md covers the full login flow; this section focuses on the 1.8 addition.

The Set Compression packet

Sent by the server during the login state, after Login Success is in the pipeline but before it is actually sent:

State Direction ID Name Field Type
login clientbound 0x03 Set Compression threshold VarInt

Source: minecraft-data/data/pc/1.8/protocol.json login.toClient mappings "0x03": "compress" and packet_compress definition [{"name":"threshold","type":"varint"}].

The 1.7 login state had only three clientbound packets (0x00 disconnect, 0x01 encryption_begin, 0x02 success). 1.8 added 0x03 compress.

A negative threshold value disables compression. A value ≥ 0 means: packets whose uncompressed payload is ≥ threshold bytes must be zlib-compressed; smaller packets may be sent uncompressed (Data Length = 0).

Framing change: the Data Length VarInt

Before compression is enabled the packet format is identical to 1.7:

[Packet Length : VarInt] [Packet ID : VarInt] [Payload...]

After Set Compression is received, all subsequent packets (in both directions) add a second VarInt:

[Packet Length : VarInt] [Data Length : VarInt] [Packet ID + Payload : zlib or raw]
  • Packet Length = byte-count of everything that follows, i.e. len(Data Length varint) + len(compressed-or-raw-data).
  • Data Length = 0 → the following bytes are not compressed (uncompressed payload was below threshold).
  • Data Length > 0 → the following bytes are zlib-deflated; Data Length is the uncompressed byte count (used to size the decompression buffer).

This second VarInt is invisible in the 1.7 framing; adding it is the entire framing change. The rest of the packet (ID + payload) is structurally unchanged — compression is a wrapper layer only.

Login flow with compression (1.8)

sequenceDiagram
    participant C as Client
    participant S as Server
    C->>S: Login Start (0x00) [username]
    S->>C: Encryption Request (0x01) [serverId, pubKey, verifyToken]
    C->>S: Encryption Response (0x02) [encSharedSecret, encVerifyToken]
    Note over C,S: AES/CFB8 encryption begins (both directions)
    S->>C: Set Compression (0x03) [threshold] — NEW in 1.8
    Note over C,S: Framing switches to compressed format (both directions)
    S->>C: Login Success (0x02) [uuid, username]
    Note over C,S: Transition to PLAY state

Note: Set Compression is sent encrypted (after the AES stream is established) but before the state transitions to PLAY. The compression threshold is server-controlled; vanilla default is 256 bytes.


Protocol changes vs 1.7 (from minecraft-data diff)

The 1.7 and 1.8 protocol.json files share the same top-level packet names and IDs for nearly all play-state packets (0x000x40 clientbound, 0x000x17 serverbound). The protocol-breaking changes are in field types and the new packets. Key diffs:

Handshake state

Identical. Both: 0x00 set_protocol (protocolVersion:varint, serverHost:string, serverPort:u16, nextState:varint).

Login state (clientbound)

ID 1.7 packet 1.8 change
0x00 disconnect unchanged
0x01 encryption_begin field type change: publicKey and verifyToken lengths changed from i16-prefixed buffers (1.7) to varint-prefixed buffers (1.8)
0x02 success unchanged (uuid:string, username:string)
0x03 NEW: compress (threshold:varint)

Source: compare login.toClient in both protocol.json files. In 1.7, packet_encryption_begin uses {"countType":"i16"} for both fields; in 1.8 they use {"countType":"varint"}.

Login state (serverbound)

Same change: 1.8 encryption_begin uses varint-prefixed sharedSecret and verifyToken buffers instead of i16-prefixed.

Play state — new/changed clientbound packets

ID Name 1.7 → 1.8 change
0x01 (login) Login Added reducedDebugInfo:bool field in 1.8
0x02 (chat) Chat Added position:i8 field in 1.8 (chat box=0, system=1, game info/action bar=2)
0x04 (entity_equipment) Entity Equipment Entity ID: i32varint
0x05 (spawn_position) Spawn Position location: position_iii (3×i32) → packed position (single i64 bitfield)
0x06 (update_health) Update Health food: i16varint
0x0a (bed) Use Bed Entity ID i32varint; location: position_ibi → packed position
0x0c (named_entity_spawn) Named Entity Spawn Major: removed inline playerName:string and data[] properties array (1.7 sent these inline); added playerUUID:UUID binary type; removed currentItem field; 1.7 had string UUID + full profile inline
0x0d (collect) Collect Both entity IDs i32varint
0x0e (spawn_entity) Spawn Entity objectData refactored: was inline {intField:i32, velocityX/Y/Z:i16-conditional}; same semantics, slightly different schema
0x12 (entity_velocity) Entity Velocity Entity ID i32varint
0x13 (entity_destroy) Entity Destroy Count prefix i8+entity i32 array → varint count + varint array
0x140x19 (entity move/look/teleport) Entity movement Entity ID i32varint; onGround:bool added to rel_entity_move, entity_look, entity_move_look, entity_teleport
0x1a (entity_status) Entity Status Entity ID unchanged (i32)
0x1c (entity_metadata) Entity Metadata Entity ID i32varint
0x1d (entity_effect) Entity Effect duration: i16varint; added hideParticles:bool
0x1e (remove_entity_effect) Remove Entity Effect Entity ID i32varint
0x1f (experience) Set Experience level and totalExperience: i16varint
0x20 (update_attributes) Update Attributes Modifier count prefix: i16varint
0x21 (map_chunk) Map Chunk Major: removed addBitMap:u16 and compressedChunkData (zlib-in-packet); replaced with raw chunkData:ByteArray(varint-prefixed); blocks now use palette block-state IDs
0x22 (multi_block_change) Multi Block Change Major: record format changed from separate {metadata+blockId bitfield, y:u8, x+z bitfield} to {horizontalPos:u8, y:u8, blockId:varint}; count now varint, removed dataLength:i32
0x23 (block_change) Block Change location: position_ibi → packed position; removed metadata:u8 field — block-state ID in type:varint encodes both
0x24 (block_action) Block Action location: position_isi → packed position
0x25 (block_break_animation) Block Break Animation location: position_iii → packed position
0x26 (map_chunk_bulk) Map Chunk Bulk Major: removed per-chunk addBitMap:u16; removed compressedChunkData bulk buffer; switched to skyLightSent:bool + varint-prefixed meta array + raw data buffer
0x28 (world_event) World Event location: position_ibi → packed position
0x2a (world_particles) World Particles Switched from particleName:string to particleId:i32; added longDistance:bool; added extra data[] varint array for item/block particles
0x2c (spawn_entity_weather) Spawn Weather Entity Entity ID unchanged (varint)
0x2d (open_window) Open Window inventoryType: u8 numeric → string identifier; removed useProvidedTitle:bool
0x33 (update_sign) Update Sign location: position_isi → packed position
0x34 (map) Map Item Data Major reformat: 1.7 had just {itemDamage:varint, data:buffer(i16)}; 1.8 expanded to {itemDamage:varint, scale:i8, icons:[...], columns:i8, rows:i8, x:i8, y:i8, data:ByteArray}
0x35 (tile_entity_data) Tile Entity Data location: position_isi → packed position; nbtData: compressedNbt → uncompressed optionalNbt
0x36 (open_sign_entity) Open Sign Editor location: position_iii → packed position
0x38 (player_info) Player Info Major: 1.7 was a flat {playerName:string, online:bool, ping:i16}; 1.8 became an action-based structure with {action:varint, data:[{uuid:UUID, ...}]} supporting add/remove/update-gamemode/update-latency/update-displayname actions
0x41 (difficulty) Change Difficulty NEW in 1.8 (absent from 1.7)
0x42 (combat_event) Player Combat NEW in 1.8
0x43 (camera) Set Camera NEW in 1.8
0x44 (world_border) World Border NEW in 1.8
0x45 (title) Title NEW in 1.8
0x46 (set_compression) Set Compression NEW in 1.8 (play-state duplicate; also exists in login state as 0x03)
0x47 (playerlist_header) Tab List Header/Footer NEW in 1.8
0x48 (resource_pack_send) Resource Pack NEW in 1.8
0x49 (update_entity_nbt) Update Entity NBT NEW in 1.8

Source: packet maps in minecraft-data/data/pc/1.8/protocol.json play.toClient vs data/pc/1.7/protocol.json play.toClient. 1.7 clientbound play topped at 0x40 (kick_disconnect); 1.8 extends to 0x49.

Play state — changed serverbound packets

ID Name 1.7 → 1.8 change
0x02 (use_entity) Interact target:i32varint; mouse:i8varint; added optional {x,y,z}:f32 fields when mouse=2 (interact-at)
0x04 (position) Player Position Removed stance:f64 field (1.7 had x,stance,y,z; 1.8 has x,y,z)
0x06 (position_look) Player Pos+Look Same: removed stance:f64
0x07 (block_dig) Player Action status:i8varint; location: position_ibi → packed position
0x08 (block_place) Use Item On location: position_ibi → packed position
0x0a (arm_animation) Swing Arm 1.7 had {entityId:i32, animation:i8}; 1.8 is empty (no fields)
0x0b (entity_action) Player Command entityId:i32varint; actionId:i8varint; jumpBoost:i32varint
0x0c (steer_vehicle) Player Input jump:bool+unmount:bool → single jump:u8 flags byte
0x12 (update_sign) Sign Update location: position_isi → packed position
0x14 (tab_complete) Command Suggestion Added optional block:option<position> field
0x15 (settings) Client Information Removed difficulty:u8 and showCape:bool; added skinParts:u8 bitmask
0x18 (spectate) Teleport To Entity NEW in 1.8
0x19 (resource_pack_receive) Resource Pack Status NEW in 1.8

Source: minecraft-data/data/pc/1.8/protocol.json play.toServer vs data/pc/1.7/protocol.json play.toServer. 1.7 serverbound play topped at 0x17 (custom_payload); 1.8 extends to 0x19.


Entity metadata format

Both 1.7 and 1.8 use the same sentinel-terminated metadata loop structure:

  • Each entry is a header byte with {type:3 bits, key:5 bits}.
  • End of stream signalled by byte 0x7F (127).
  • Entry values by type: 0=i8, 1=i16, 2=i32, 3=f32, 4=string, 5=slot, 6={x:i32,y:i32,z:i32}, 7={pitch:f32,yaw:f32,roll:f32}.

The type table is identical in both versions (verified from entityMetadataItem in both protocol.json files). However, the set of actual metadata indices and their meanings changed for several entity types in 1.8 to accommodate new entity properties (guardians, rabbits, armor stands).

The container types entityMetadata and entityMetadataItem in 1.8's protocol.json are structurally identical to 1.7 — confirming that the metadata encoding format did not change between protocol 5 and 47; the changes were in which indices are used, not the framing.


Chunk and block encoding

1.7 chunk format (for comparison)

In 1.7, Map Chunk (0x21) contained:

  • bitMap:u16 — which of the 16 vertical sections are present
  • addBitMap:u16 — additional-data sections for blocks requiring >8 bits of block ID (extended blocks)
  • compressedChunkData:buffer(i32-count) — zlib-deflated payload containing raw nibble arrays: 8-bit block IDs + 4-bit metadata nibbles + 4-bit block light + optional 4-bit sky light + optional 4-bit add-data

The 1.7 format encoded block type as (blockID << 4) | metadata using two separate nibble planes.

1.8 chunk format

In 1.8, Map Chunk (0x21) contains:

  • bitMap:u16 — which sections are present
  • chunkData:ByteArray (varint-prefixed) — raw uncompressed per-section data

The per-section data layout in 1.8 encodes blocks as block-state IDs — a single integer combining what was previously blockId and metadata. Each section now stores:

  • 4096 block-state values as 16-bit entries (2 bytes per block, stored little-endian in a flat array of 4096 × 2 bytes)
  • 2048 bytes block light nibble array
  • 2048 bytes sky light nibble array (overworld only)

The addBitMap field and the separate metadata nibble plane are gone entirely. Block state IDs are the canonical 1.8+ block representation: e.g., oak log facing north is a single integer distinct from oak log facing east, rather than id=17, meta=4 vs id=17, meta=0.

The Multi Block Change record format reflects this too: in 1.7, each record packed {metadata:4bits, blockId:12bits} in one i16 plus a separate y:u8 and {x:4bits,z:4bits} nibble. In 1.8, each record is {horizontalPos:u8, y:u8, blockId:varint} where blockId is the full block-state ID.


Packet inventory by state — 1.8 (protocol 47)

Source: minecraft-data/data/pc/1.8/protocol.json packet mapper sections. ViaVersion ClientboundPackets1_8.java and ServerboundPackets1_8.java confirm the same IDs.

Handshake (C→S)

ID Name
0x00 Set Protocol (Handshake)
0xFE Legacy Server List Ping

Status

ID Direction Name
0x00 S→C Server Info (JSON response)
0x01 S→C Ping (i64 payload echo)
0x00 C→S Request (empty)
0x01 C→S Ping (i64 timestamp)

Login

ID Direction Name
0x00 S→C Disconnect
0x01 S→C Encryption Request
0x02 S→C Login Success
0x03 S→C Set Compression ← new in 1.8
0x00 C→S Login Start
0x01 C→S Encryption Response

Play — Clientbound (S→C)

ID Name
0x00 Keep Alive
0x01 Login (Join Game)
0x02 Chat Message
0x03 Time Update
0x04 Entity Equipment
0x05 Spawn Position
0x06 Update Health
0x07 Respawn
0x08 Player Position and Look
0x09 Held Item Change
0x0A Use Bed
0x0B Animation
0x0C Spawn Named Entity
0x0D Collect Item
0x0E Spawn Object
0x0F Spawn Mob
0x10 Spawn Painting
0x11 Spawn Experience Orb
0x12 Entity Velocity
0x13 Destroy Entities
0x14 Entity (no-op move)
0x15 Entity Relative Move
0x16 Entity Look
0x17 Entity Look and Relative Move
0x18 Entity Teleport
0x19 Entity Head Look
0x1A Entity Status
0x1B Attach Entity
0x1C Entity Metadata
0x1D Entity Effect
0x1E Remove Entity Effect
0x1F Set Experience
0x20 Entity Properties
0x21 Chunk Data
0x22 Multi Block Change
0x23 Block Change
0x24 Block Action
0x25 Block Break Animation
0x26 Map Chunk Bulk
0x27 Explosion
0x28 Effect
0x29 Sound Effect
0x2A Particle
0x2B Change Game State
0x2C Spawn Global Entity
0x2D Open Window
0x2E Close Window
0x2F Set Slot
0x30 Window Items
0x31 Window Property
0x32 Confirm Transaction
0x33 Update Sign
0x34 Maps
0x35 Update Block Entity
0x36 Open Sign Editor
0x37 Statistics
0x38 Player List Item
0x39 Player Abilities
0x3A Tab-Complete
0x3B Scoreboard Objective
0x3C Update Score
0x3D Display Scoreboard
0x3E Teams
0x3F Plugin Message
0x40 Disconnect
0x41 Server Difficulty ← new in 1.8
0x42 Combat Event ← new in 1.8
0x43 Camera ← new in 1.8
0x44 World Border ← new in 1.8
0x45 Title ← new in 1.8
0x46 Set Compression (play-state) ← new in 1.8
0x47 Player List Header and Footer ← new in 1.8
0x48 Resource Pack Send ← new in 1.8
0x49 Update Entity NBT ← new in 1.8

Play — Serverbound (C→S)

ID Name
0x00 Keep Alive
0x01 Chat Message
0x02 Use Entity
0x03 Player (ground status only)
0x04 Player Position
0x05 Player Look
0x06 Player Position and Look
0x07 Player Digging
0x08 Player Block Placement
0x09 Held Item Change
0x0A Animation (arm swing — empty payload in 1.8)
0x0B Entity Action
0x0C Steer Vehicle
0x0D Close Window
0x0E Click Window
0x0F Confirm Transaction
0x10 Creative Inventory Action
0x11 Enchant Item
0x12 Update Sign
0x13 Player Abilities
0x14 Tab-Complete
0x15 Client Settings
0x16 Client Status
0x17 Plugin Message
0x18 Spectate ← new in 1.8
0x19 Resource Pack Status ← new in 1.8

Proxy and translation impact

ViaVersion floor. Protocol 47 is ViaVersion's oldest natively supported client. There is no v1_7to1_8 package in ViaVersion — 1.7 support requires ViaLegacy/ViaRewind. The v1_8to1_9 package (/tmp/mcproto-refs/ViaVersion/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/) is the FROM-1.8 direction and serves as the canonical definition of the 1.8 packet set.

Compression negotiation at the proxy. A proxy operating in front of a 1.8+ server must intercept Set Compression (login 0x03) from the backend and decide whether to relay, modify, or suppress it. The compression threshold may differ between the proxy's own connection to the client and the proxy-to-server leg. BungeeCord and Velocity both intercept this packet and re-issue their own Set Compression to the client with their own threshold, then maintain separate compression state for each half of the connection.

Position type translation. Any proxy accepting 1.7 clients and forwarding to a 1.8+ server must translate between the old triple-i32/i16 position types and the new packed i64 position type for every block-coordinate field (spawn_position, bed, block_change, block_action, block_break_animation, update_sign, tile_entity_data, open_sign_entity, world_event, and on the serverbound side: block_dig, block_place, update_sign).

block_change metadata removal. A 1.7-to-1.8 translator must combine the 1.7 {type:varint, metadata:u8} pair into a single 1.8 block-state VarInt on block_change, and split in the reverse direction.

Stance removal. The 1.7 serverbound position packet had a stance:f64 field (eye-height offset for collision) that was removed in 1.8. A proxy bridging 1.7→1.8 must strip this field; a 1.8→1.7 bridge must synthesise it (typically y + 1.62).

Player Info restructure. The flat single-player player_info from 1.7 (playerName, online, ping) was replaced in 1.8 with the action-based multi-player structure. Proxies doing tab-list passthrough must understand the 1.8 structure.

New packets requiring proxy awareness. World Border (0x44), Title (0x45), Combat Event (0x42), and Set Compression (0x46 in play-state) are all 1.8-new and unknown to 1.7 clients. A proxy connecting a 1.7 client to a 1.8 server must either translate or drop these; ViaLegacy/ViaRewind handle this for the compatibility layer.


Sub-version notes (1.8.1 1.8.9)

All 1.8.x patches share protocol 47. Patch releases addressed server-side exploits (combat, enchantment mechanics, item duplication), server crash vectors, and connectivity bugs but made no wire-format changes. A 1.8 client connects to a 1.8.9 server without negotiation issues, and vice versa.


Sources summary

Claim Source
Protocol 47; version 1.8.8 as representative /tmp/mcproto-refs/minecraft-data/data/pc/1.8/version.json
Release date 2014-09-02 https://minecraft.wiki/w/Java_Edition_1.8 (fetched 2026-06-19)
Login 0x03 compress packet field def /tmp/mcproto-refs/minecraft-data/data/pc/1.8/protocol.json login.toClient
1.7 login had no 0x03 /tmp/mcproto-refs/minecraft-data/data/pc/1.7/protocol.json login.toClient (0x000x02 only)
Encryption buffer countType i16→varint Both protocol.json files, packet_encryption_begin
Play packet IDs 0x000x49 (1.8) /tmp/mcproto-refs/minecraft-data/data/pc/1.8/protocol.json play.toClient mapper
Play packet IDs 0x000x40 (1.7) /tmp/mcproto-refs/minecraft-data/data/pc/1.7/protocol.json play.toClient mapper
1.8 clientbound packet enum with IDs /tmp/mcproto-refs/ViaVersion/…/v1_8to1_9/packet/ClientboundPackets1_8.java
1.8 serverbound packet enum with IDs /tmp/mcproto-refs/ViaVersion/…/v1_8to1_9/packet/ServerboundPackets1_8.java
Compression framing (Data Length VarInt) https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Protocol (fetched 2026-06-19)
chunk_data addBitMap present in 1.7, absent in 1.8 diff of packet_map_chunk in both protocol.json
multi_block_change record format change diff of packet_multi_block_change in both protocol.json
stance field in 1.7 position, absent in 1.8 packet_position in both protocol.json
metadata loop structure identical 1.7 and 1.8 entityMetadata/entityMetadataItem in both protocol.json