d73c1c9537
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>
410 lines
28 KiB
Markdown
410 lines
28 KiB
Markdown
# Java Edition 1.9.x — Protocol Deep-Dive
|
||
|
||
| Release | Protocol # | Release date | minecraft-data dir |
|
||
|---|---|---|---|
|
||
| 1.9 | **107** | 2016-02-29 | `data/pc/1.9` |
|
||
| 1.9.1 | **108** | 2016-03-30 | `data/pc/1.9.1-pre2` |
|
||
| 1.9.2 | **109** | 2016-03-30 | `data/pc/1.9.2` |
|
||
| 1.9.3 | **110** | 2016-05-10 | *(no separate mc-data dir; same packets as 1.9.4)* |
|
||
| 1.9.4 | **110** | 2016-05-10 | `data/pc/1.9.4` |
|
||
|
||
Sources: minecraft.wiki release articles ([1.9](https://minecraft.wiki/w/Java_Edition_1.9) fetched 2026-06-19, [1.9.1](https://minecraft.wiki/w/Java_Edition_1.9.1), [1.9.2](https://minecraft.wiki/w/Java_Edition_1.9.2), [1.9.3](https://minecraft.wiki/w/Java_Edition_1.9.3), [1.9.4](https://minecraft.wiki/w/Java_Edition_1.9.4)); ViaVersion source at `/tmp/mcproto-refs/ViaVersion/`; minecraft-data at `/tmp/mcproto-refs/minecraft-data/data/pc/`.
|
||
|
||
---
|
||
|
||
## Headline — the Combat Update
|
||
|
||
1.9 is the **Combat Update**, shipping 2016-02-29 after a development stretch that touched nearly every Play-state subsystem. The protocol impact was the largest single-version Play-packet reorganisation since the Netty rewrite in 1.7:
|
||
|
||
- 77 Play clientbound packets in 1.9 vs 74 in 1.8 (net +3; the real story is that almost all IDs shifted).
|
||
- 30 Play serverbound packets in 1.9 vs 26 in 1.8 (net +4).
|
||
- The entire CB packet list was renumbered wholesale — no packet kept its 1.8 ID except by coincidence.
|
||
- Four major protocol subsystems arrived at once: **dual-hand** (off-hand slot), **Boss Bar** promoted to dedicated packets, **teleport-confirm** handshake, and **vehicle movement** packets.
|
||
|
||
The patch line (108 / 109 / 110) is comparatively quiet: a Login dimension-type fix (108), a connectivity hotfix (109), and a chunk-format extension bundling block-entity NBT into the chunk packet (110 / 1.9.3–1.9.4).
|
||
|
||
---
|
||
|
||
## Protocol changes vs 1.8 — Play state
|
||
|
||
All IDs below are **Play** state only. Handshake / Status / Login state packets were unchanged structurally between 1.8 and 1.9 (the Login state `SET_COMPRESSION` 0x03 packet was moved exclusively into the Play path — see below).
|
||
|
||
Sources for this section:
|
||
- ViaVersion packet enums: `common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/packet/ClientboundPackets1_8.java` and `ClientboundPackets1_9.java`, `ServerboundPackets1_8.java`, `ServerboundPackets1_9.java`.
|
||
- minecraft-data `data/pc/1.8/protocol.json` and `data/pc/1.9/protocol.json` (cross-check).
|
||
|
||
### Clientbound Play — 1.8 vs 1.9
|
||
|
||
The table below lists every 1.8 packet by name, its 1.8 ID, and what happened to it in 1.9.
|
||
|
||
| 1.8 name | 1.8 ID | 1.9 name | 1.9 ID | Notes |
|
||
|---|---|---|---|---|
|
||
| `keep_alive` | 0x00 | `keep_alive` | 0x1F | renumbered |
|
||
| `login` | 0x01 | `login` | 0x23 | renumbered |
|
||
| `chat` | 0x02 | `chat` | 0x0F | renumbered |
|
||
| `update_time` | 0x03 | `update_time` | 0x44 | renumbered |
|
||
| `entity_equipment` | 0x04 | `entity_equipment` | 0x3C | renumbered; slot field changed: 1.8 used Short (0=hand, 1–4=armor); 1.9 uses VarInt with expanded enum (0=main hand, 1=off hand, 2–5=armor) |
|
||
| `spawn_position` | 0x05 | `spawn_position` | 0x43 | renumbered |
|
||
| `update_health` | 0x06 | `update_health` | 0x3E | renumbered |
|
||
| `respawn` | 0x07 | `respawn` | 0x33 | renumbered |
|
||
| `position` | 0x08 | `position` | 0x2E | renumbered; **+VarInt teleport ID** appended — the new teleport-confirm field (see below) |
|
||
| `held_item_slot` | 0x09 | `held_item_slot` | 0x37 | renumbered |
|
||
| `bed` | 0x0A | `bed` | 0x2F | renumbered |
|
||
| `animation` | 0x0B | `animation` | 0x06 | renumbered |
|
||
| `named_entity_spawn` | 0x0C | `named_entity_spawn` | 0x05 | renumbered; +UUID field inserted after entity ID |
|
||
| `collect` | 0x0D | `collect` | 0x49 | renumbered |
|
||
| `spawn_entity` | 0x0E | `spawn_entity` | 0x00 | renumbered; +UUID after entity ID; position coords changed from fixed-point Int×(1/32) to Double |
|
||
| `spawn_entity_living` | 0x0F | `spawn_entity_living` | 0x03 | renumbered; +UUID; coords to Double |
|
||
| `spawn_entity_painting` | 0x10 | `spawn_entity_painting` | 0x04 | renumbered; +UUID |
|
||
| `spawn_entity_experience_orb` | 0x11 | `spawn_entity_experience_orb` | 0x01 | renumbered |
|
||
| `entity_velocity` | 0x12 | `entity_velocity` | 0x3B | renumbered |
|
||
| `entity_destroy` | 0x13 | `entity_destroy` | 0x30 | renumbered |
|
||
| `entity` (no-move) | 0x14 | `entity` | 0x28 | renumbered |
|
||
| `rel_entity_move` | 0x15 | `rel_entity_move` | 0x25 | renumbered; delta values changed from Byte to Short (larger range) |
|
||
| `entity_look` | 0x16 | `entity_look` | 0x27 | renumbered |
|
||
| `entity_move_look` | 0x17 | `entity_move_look` | 0x26 | renumbered; delta values Byte→Short |
|
||
| `entity_teleport` | 0x18 | `entity_teleport` | 0x4A | renumbered; position changed from Int×(1/32) to Double; +on-ground Boolean |
|
||
| `entity_head_rotation` | 0x19 | `entity_head_rotation` | 0x34 | renumbered |
|
||
| `entity_status` | 0x1A | `entity_status` | 0x1B | renumbered |
|
||
| `attach_entity` | 0x1B | `attach_entity` | 0x3A | renumbered; leash Boolean field removed (1.9 uses `set_passengers` for riding) <!-- VERIFY: leash field drop --> |
|
||
| `entity_metadata` | 0x1C | `entity_metadata` | 0x39 | renumbered; metadata format revised (new EntityDataTypes1_9) |
|
||
| `entity_effect` | 0x1D | `entity_effect` | 0x4C | renumbered |
|
||
| `remove_entity_effect` | 0x1E | `remove_entity_effect` | 0x31 | renumbered |
|
||
| `experience` | 0x1F | `experience` | 0x3D | renumbered |
|
||
| `update_attributes` | 0x20 | `entity_update_attributes` | 0x4B | renumbered |
|
||
| `map_chunk` | 0x21 | `map_chunk` | 0x20 | renumbered; chunk wire format changed to `ChunkType1_9_1` (direct length-prefixed sections, no bulk; light separate) |
|
||
| `multi_block_change` | 0x22 | `multi_block_change` | 0x10 | renumbered |
|
||
| `block_change` | 0x23 | `block_change` | 0x0B | renumbered |
|
||
| `block_action` | 0x24 | `block_action` | 0x0A | renumbered |
|
||
| `block_break_animation` | 0x25 | `block_break_animation` | 0x08 | renumbered |
|
||
| `map_chunk_bulk` | 0x26 | *(removed)* | — | eliminated; servers send individual `map_chunk` per column instead |
|
||
| `explosion` | 0x27 | `explosion` | 0x1C | renumbered |
|
||
| `world_event` | 0x28 | `world_event` | 0x21 | renumbered; effect ID values remapped |
|
||
| `named_sound_effect` | 0x29 | `named_sound_effect` | 0x19 | renumbered; +sound category VarInt inserted after name; position encoding changed to Int×8 |
|
||
| `world_particles` | 0x2A | `world_particles` | 0x22 | renumbered |
|
||
| `game_state_change` | 0x2B | `game_state_change` | 0x1E | renumbered |
|
||
| `spawn_entity_weather` | 0x2C | `spawn_entity_weather` | 0x02 | renumbered (lightning bolt) |
|
||
| `open_window` | 0x2D | `open_window` | 0x13 | renumbered |
|
||
| `close_window` | 0x2E | `close_window` | 0x12 | renumbered |
|
||
| `set_slot` | 0x2F | `set_slot` | 0x16 | renumbered |
|
||
| `window_items` | 0x30 | `window_items` | 0x14 | renumbered |
|
||
| `craft_progress_bar` | 0x31 | `craft_progress_bar` | 0x15 | renumbered |
|
||
| `transaction` | 0x32 | `transaction` | 0x11 | renumbered |
|
||
| `update_sign` | 0x33 | `update_sign` | 0x46 | renumbered (removed in 1.9.4 — replaced by block entity data; see §110) |
|
||
| `map` | 0x34 | `map` | 0x24 | renumbered |
|
||
| `tile_entity_data` | 0x35 | `tile_entity_data` | 0x09 | renumbered |
|
||
| `open_sign_entity` | 0x36 | `open_sign_entity` | 0x2A | renumbered |
|
||
| `statistics` | 0x37 | `statistics` | 0x07 | renumbered |
|
||
| `player_info` | 0x38 | `player_info` | 0x2D | renumbered; display name field type changed String→optional JSON component |
|
||
| `abilities` | 0x39 | `abilities` | 0x2B | renumbered |
|
||
| `tab_complete` | 0x3A | `tab_complete` | 0x0E | renumbered |
|
||
| `scoreboard_objective` | 0x3B | `scoreboard_objective` | 0x3F | renumbered |
|
||
| `scoreboard_score` | 0x3C | `scoreboard_score` | 0x42 | renumbered |
|
||
| `scoreboard_display_objective` | 0x3D | `scoreboard_display_objective` | 0x38 | renumbered |
|
||
| `scoreboard_team` | 0x3E | `teams` | 0x41 | renumbered; +collision rule field added to mode 0/2 |
|
||
| `custom_payload` | 0x3F | `custom_payload` | 0x18 | renumbered |
|
||
| `kick_disconnect` | 0x40 | `kick_disconnect` | 0x1A | renumbered; reason field changed String→JSON component |
|
||
| `difficulty` | 0x41 | `difficulty` | 0x0D | renumbered |
|
||
| `combat_event` | 0x42 | `combat_event` | 0x2C | renumbered |
|
||
| `camera` | 0x43 | `camera` | 0x36 | renumbered |
|
||
| `world_border` | 0x44 | `world_border` | 0x35 | renumbered |
|
||
| `title` | 0x45 | `title` | 0x45 | same ID |
|
||
| `set_compression` | 0x46 | *(removed from Play)* | — | absorbed: 1.9 handles compression entirely in Login state; 1.8 servers sent this during Play, ViaVersion intercepts and discards it |
|
||
| `playerlist_header` | 0x47 | `playerlist_header` | 0x48 | renumbered |
|
||
| `resource_pack_send` | 0x48 | `resource_pack_send` | 0x32 | renumbered |
|
||
| `update_entity_nbt` | 0x49 | *(removed)* | — | dropped; ViaVersion cancels it |
|
||
|
||
**New in 1.9 (no 1.8 equivalent):**
|
||
|
||
| 1.9 name | 1.9 ID | Description |
|
||
|---|---|---|
|
||
| `boss_bar` | 0x0C | First-class boss bar packet with UUID + action enum (add/remove/update health/update title/update style/update flags). In 1.8 boss bar was emulated via entity data on WitherBoss/EnderDragon entities. ViaVersion provides a `BossBarProvider` to translate old entity-data-based bars to the new packet for downstream 1.9 clients. |
|
||
| `set_cooldown` | 0x17 | Item use cooldown — VarInt item ID + VarInt cooldown ticks. New with the attack-speed system. |
|
||
| `sound_effect` | 0x47 | Numeric sound ID (VarInt) + category + fixed-point position + volume + pitch. Complements `named_sound_effect` (0x19) which keeps the string-name form. |
|
||
| `unload_chunk` | 0x1D | Explicit chunk unload (Int chunkX, Int chunkZ). In 1.8 unload was signalled by a `map_chunk` with bitmask=0; ViaVersion converts the old form to this packet. |
|
||
| `vehicle_move` | 0x29 | Server→client vehicle position sync (Double x/y/z + Float yaw/pitch). |
|
||
| `set_passengers` | 0x40 | Replaces the old riding field in `attach_entity`; carries a VarInt[] of passenger entity IDs. |
|
||
|
||
Sources: ViaVersion `ClientboundPackets1_8.java` and `ClientboundPackets1_9.java` (ordinal = packet ID); `WorldPacketRewriter1_9.java:197` (MAP_BULK_CHUNK cancel), `WorldPacketRewriter1_9.java:140` (unload-chunk emit), `PlayerPacketRewriter1_9.java:332` (SET_COMPRESSION cancel), `EntityPacketRewriter1_9.java:258` (UPDATE_ENTITY_NBT cancel), `EntityPacketRewriter1_9.java:83` (SET_PASSENGERS emit).
|
||
|
||
---
|
||
|
||
### Serverbound Play — 1.8 vs 1.9
|
||
|
||
| 1.8 name | 1.8 ID | 1.9 name | 1.9 ID | Notes |
|
||
|---|---|---|---|---|
|
||
| `keep_alive` | 0x00 | `keep_alive` | 0x0B | renumbered |
|
||
| `chat` | 0x01 | `chat` | 0x02 | renumbered |
|
||
| `use_entity` | 0x02 | `use_entity` | 0x0A | renumbered; +hand VarInt appended |
|
||
| `flying` | 0x03 | `flying` | 0x0F | renumbered |
|
||
| `position` | 0x04 | `position` | 0x0C | renumbered |
|
||
| `look` | 0x05 | `look` | 0x0E | renumbered |
|
||
| `position_look` | 0x06 | `position_look` | 0x0D | renumbered |
|
||
| `block_dig` | 0x07 | `block_dig` | 0x13 | renumbered |
|
||
| `block_place` | 0x08 | `block_place` / `use_item` | 0x1C / 0x1D | **split into two packets**: `use_item_on` (0x1C, block interaction) and `use_item` (0x1D, general item use in hand). Each now carries a hand VarInt (0=main, 1=off). |
|
||
| `held_item_slot` | 0x09 | `held_item_slot` | 0x17 | renumbered |
|
||
| `arm_animation` | 0x0A | `arm_animation` | 0x1A | renumbered; +hand VarInt |
|
||
| `entity_action` | 0x0B | `entity_action` | 0x14 | renumbered |
|
||
| `steer_vehicle` | 0x0C | `steer_vehicle` | 0x15 | renumbered |
|
||
| `close_window` | 0x0D | `close_window` | 0x08 | renumbered |
|
||
| `window_click` | 0x0E | `window_click` | 0x07 | renumbered |
|
||
| `transaction` | 0x0F | `transaction` | 0x05 | renumbered |
|
||
| `set_creative_slot` | 0x10 | `set_creative_slot` | 0x18 | renumbered |
|
||
| `enchant_item` | 0x11 | `enchant_item` | 0x06 | renumbered |
|
||
| `update_sign` | 0x12 | `update_sign` | 0x19 | renumbered |
|
||
| `abilities` | 0x13 | `abilities` | 0x12 | renumbered |
|
||
| `tab_complete` | 0x14 | `tab_complete` | 0x01 | renumbered; **+Boolean** "is command block" field removed in 1.9 (ViaVersion reads and discards it from 1.9 clients before forwarding to 1.8 servers) |
|
||
| `settings` | 0x15 | `settings` | 0x04 | renumbered; +hand VarInt appended (main-hand preference) |
|
||
| `client_command` | 0x16 | `client_command` | 0x03 | renumbered |
|
||
| `custom_payload` | 0x17 | `custom_payload` | 0x09 | renumbered |
|
||
| `spectate` | 0x18 | `spectate` | 0x1B | renumbered |
|
||
| `resource_pack_receive` | 0x19 | `resource_pack_receive` | 0x16 | renumbered |
|
||
|
||
**New in 1.9 (no 1.8 equivalent):**
|
||
|
||
| 1.9 name | 1.9 ID | Description |
|
||
|---|---|---|
|
||
| `teleport_confirm` | 0x00 | Client acknowledges a `PLAYER_POSITION` (0x2E) by echoing its VarInt teleport ID. 1.8 servers do not send or expect this; ViaVersion cancels it when translating down. Source: `PlayerPacketRewriter1_9.java:382` (`protocol.cancelServerbound(ServerboundPackets1_9.ACCEPT_TELEPORTATION)`). |
|
||
| `vehicle_move` | 0x10 | Client→server vehicle position update (Double x/y/z + Float yaw/pitch). Cancelled by ViaVersion when translating down to 1.8. Source: `PlayerPacketRewriter1_9.java:383`. |
|
||
| `steer_boat` | 0x11 | Two Booleans: left paddle turning, right paddle turning. New for boats redesign. Cancelled by ViaVersion. Source: `PlayerPacketRewriter1_9.java:384` (`cancelServerbound(PADDLE_BOAT)`). |
|
||
| `use_item` | 0x1D | Use held item without targeting a block (e.g. eat food, throw ender pearl). Carries hand VarInt. ViaVersion translates to a synthetic `block_place` at position −1,−1,−1 face 255. Source: `WorldPacketRewriter1_9.java:314`. |
|
||
|
||
Sources: ViaVersion `ServerboundPackets1_8.java`, `ServerboundPackets1_9.java`; `PlayerPacketRewriter1_9.java:375,382–384`; `WorldPacketRewriter1_9.java:314,370`.
|
||
|
||
---
|
||
|
||
## Key data-format changes
|
||
|
||
### Teleport-confirm round-trip
|
||
|
||
`PLAYER_POSITION` (CB 0x2E) gains a trailing `VarInt teleportId` field. The client must reply with `ACCEPT_TELEPORTATION` (SB 0x00) carrying the same ID before the server will accept further movement packets. This prevents the classic 1.8 rubber-band exploit where a lagging client could ignore server position corrections.
|
||
|
||
ViaVersion approach for 1.8 servers: inject a fake teleport ID of 0 into every `PLAYER_POSITION` sent downward; cancel all `ACCEPT_TELEPORTATION` packets from 1.9 clients (they mean nothing to a 1.8 server). Source: `PlayerPacketRewriter1_9.java:102–116` (add `create(Types.VAR_INT, 0)` to PLAYER_POSITION), line 382 (cancel ACCEPT_TELEPORTATION).
|
||
|
||
### Dual-hand (off-hand slot)
|
||
|
||
Several packets gained a `hand` VarInt discriminator (0=main, 1=off):
|
||
|
||
- SB `use_entity` — which hand interacted.
|
||
- SB `arm_animation` — which arm swings.
|
||
- SB `use_item_on` / `use_item` — which hand placed / used item.
|
||
- SB `settings` (CLIENT_INFORMATION) — new VarInt for main-hand preference (0=left, 1=right).
|
||
|
||
CB `entity_equipment` slot encoding expanded: 1.8 Short (0=hand, 1–4=armor) → 1.9 VarInt (0=main hand, 1=off hand, 2–5=armor). ViaVersion translates the old Short to the new VarInt, remapping armor slots by +1 and handling the self-entity edge case. Source: `EntityPacketRewriter1_9.java:157–205`.
|
||
|
||
### Boss Bar
|
||
|
||
CB `boss_bar` (0x0C) is a new multiplexed packet with action=VarInt:
|
||
- 0 = add (UUID + title JSON + health float + color VarInt + division VarInt + flags Byte)
|
||
- 1 = remove (UUID)
|
||
- 2 = update health
|
||
- 3 = update title
|
||
- 4 = update style
|
||
- 5 = update flags
|
||
|
||
In 1.8, boss-bar appearance was achieved by spawning WitherBoss / EnderDragon entities whose health controlled the bar. ViaVersion's `EntityTracker1_9.java` (lines 243–273) reverse-engineers entity metadata updates for those entity types and synthesises the new `BOSS_EVENT` packet for 1.9+ clients connecting to 1.8 servers, using the `BossBarProvider` abstraction.
|
||
|
||
### Sound system split
|
||
|
||
1.9 introduces two parallel sound mechanisms:
|
||
- CB `named_sound_effect` (0x19) — string name (namespaced resource location) + category VarInt + fixed-point position (Int×8) + volume + pitch. Wire format changed from 1.8 (no category, string-only position). ViaVersion injects category and reformats position. Source: `WorldPacketRewriter1_9.java:96–130`.
|
||
- CB `sound_effect` (0x47) — numeric sound ID (VarInt) + same remaining fields as named. New packet type absent from 1.8 entirely.
|
||
|
||
### Chunk format — ChunkType1_9_1
|
||
|
||
The 1.8 `map_chunk_bulk` (0x26) packet is gone; all chunk data is sent as individual `map_chunk` (0x20) packets. The chunk data section encoding changed to `ChunkType1_9_1`:
|
||
- Sections are length-prefixed in a single VAR_INT-prefixed byte array.
|
||
- Block light and sky light are appended per section directly after palette+data.
|
||
- Biome array (256 bytes) follows all sections for full-chunk packets.
|
||
- No embedded block-entity NBT (that arrives in a separate `tile_entity_data` 0x09 packet or via `FakeTileEntities` during translation). <!-- VERIFY: exact 1.9 wire layout vs wiki -->
|
||
|
||
Source: `ViaVersion/api/src/main/java/com/viaversion/viaversion/api/type/types/chunk/ChunkType1_9_1.java`.
|
||
|
||
### Entity coordinate precision
|
||
|
||
Entity spawn positions changed from 1.8 fixed-point integer (value/32 = world coord) to IEEE 754 Double. ViaVersion applies the `/32` transform when translating `ADD_ENTITY` / `ADD_MOB` down to 1.8. Source: `SpawnPacketRewriter1_9.java:60–62` (`map(Types.INT, toNewDouble)` where `toNewDouble = inputValue / 32D`).
|
||
|
||
### Entity metadata format
|
||
|
||
`ENTITY_DATA_LIST1_8` → `ENTITY_DATA_LIST1_9`: the index/type packing changed, and the type tag set was revised (new types for optional UUID, VarInt enum, etc.). ViaVersion performs full metadata translation in `EntityPacketRewriter1_9.java`. The `EntityDataTypes1_9` type set is referenced in `SpawnPacketRewriter1_9.java:26`.
|
||
|
||
### Removed: `update_entity_nbt` (CB 0x49)
|
||
|
||
The 1.8 packet that sent arbitrary NBT for an entity (used by command blocks to sync state) was removed. ViaVersion cancels it: `EntityPacketRewriter1_9.java:258`. Functionality moved to the block-entity / custom-payload path.
|
||
|
||
### Removed: Play-state `set_compression`
|
||
|
||
In 1.8, the server could send `set_compression` (0x46) during the **Play** state. In 1.9 compression is negotiated exclusively in Login state (as it had always been intended). ViaVersion intercepts the 1.8 Play-state `set_compression`, sets the compression threshold on the connection, and cancels the packet so the 1.9 client never sees it. Source: `PlayerPacketRewriter1_9.java:332–340`.
|
||
|
||
---
|
||
|
||
## Per-patch sub-sections
|
||
|
||
### Protocol 107 — 1.9 (2016-02-29)
|
||
|
||
The full 1.8→1.9 translation lives in:
|
||
|
||
```
|
||
common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/
|
||
Protocol1_8To1_9.java — entry point; registers rewriters + providers
|
||
rewriter/PlayerPacketRewriter1_9.java
|
||
rewriter/EntityPacketRewriter1_9.java
|
||
rewriter/ItemPacketRewriter1_9.java
|
||
rewriter/SpawnPacketRewriter1_9.java
|
||
rewriter/WorldPacketRewriter1_9.java
|
||
packet/ClientboundPackets1_9.java
|
||
packet/ServerboundPackets1_9.java
|
||
```
|
||
|
||
Selected ViaVersion commits for this package (from `git -C /tmp/mcproto-refs/ViaVersion log --oneline -- common/…/v1_8to1_9`):
|
||
|
||
| Commit | Subject |
|
||
|---|---|
|
||
| `0b5fa37f9` | Put title directly into component in 1.8→1.9 bossbar emulation |
|
||
| `73286fd94` | Send op permission level 4 in 1.8→1.9 |
|
||
| `08b921729` | Fix entity defaults across all protocols |
|
||
| `75c666266` | Sword blocking: consumables for 1.21.4+, back to shields for 1.20.5–1.21.3 |
|
||
| `e0a7b70e3` | Fix 1.8/1.9 thrown potion entity data handling |
|
||
| `e436bbe37` | Refactor dimension switch handling across all protocols |
|
||
| `501f65e21` | Packet and entity type renames (Mojang-mapped names) |
|
||
|
||
Key protocol facts established at 107:
|
||
- 77 CB / 30 SB Play packets.
|
||
- Teleport-confirm round-trip mandatory.
|
||
- `boss_bar` 0x0C, `set_cooldown` 0x17, `sound_effect` 0x47, `unload_chunk` 0x1D, `vehicle_move` 0x29, `set_passengers` 0x40 all new.
|
||
- `map_chunk_bulk`, `update_entity_nbt`, Play-state `set_compression` all removed.
|
||
|
||
### Protocol 108 — 1.9.1 (2016-03-30)
|
||
|
||
ViaVersion package: `protocols/v1_9to1_9_1/Protocol1_9To1_9_1.java`
|
||
|
||
This package is a single file with two packet handlers:
|
||
|
||
**1. `LOGIN` — dimension field type change.**
|
||
|
||
```java
|
||
// Protocol1_9To1_9_1.java:40–48
|
||
map(Types.INT); // 0 - Player ID
|
||
map(Types.UNSIGNED_BYTE); // 1 - Player Gamemode
|
||
// 1.9.1 PRE 2 Changed this
|
||
map(Types.BYTE, Types.INT); // 2 - Player Dimension ← Byte in 1.9, Int in 1.9.1
|
||
```
|
||
|
||
The `LOGIN` packet's dimension field widened from `Byte` to `Int`. ViaVersion automatically converts the Int sent by a 1.9.1 server back to a Byte for any 1.9 client still connecting. Source: `Protocol1_9To1_9_1.java:40–48`.
|
||
|
||
**2. `SOUND` — Elytra sound ID insertion.**
|
||
|
||
```java
|
||
// Protocol1_9To1_9_1.java:51–63
|
||
if (sound >= 415) // Add 1 since there is no Elytra sound on a 1.9 server
|
||
wrapper.set(Types.VAR_INT, 0, sound + 1);
|
||
```
|
||
|
||
1.9.1 inserted the Elytra wing-flap sound into the sound registry at ID 415, shifting all higher numeric IDs up by one. Any `sound_effect` packet from a 1.9.1+ server with ID ≥ 415 must have its ID decremented by one when sent to a 1.9 client, and incremented when the reverse applies. Source: `Protocol1_9To1_9_1.java:58–59`.
|
||
|
||
All other Play packets (IDs, fields, format) are identical between 107 and 108 — the packet enum used by `Protocol1_9To1_9_1` is still `ClientboundPackets1_9` / `ServerboundPackets1_9` with no new packet types. Source: `Protocol1_9To1_9_1.java:26`.
|
||
|
||
mc-wiki: 1.9.1 was superseded by 1.9.2 the same day due to a multiplayer bug; historically short-lived. ([minecraft.wiki/w/Java_Edition_1.9.1](https://minecraft.wiki/w/Java_Edition_1.9.1), fetched 2026-06-19.)
|
||
|
||
ViaVersion git log for `v1_9to1_9_1` (shallow clone — limited history): `cff9a8715` (copyright), `9f6e7fa4e` (copyright), `75d86851c` (IJ reformat + rewriter renames), `501f65e21` (packet/entity renames), `e965e9713` (package renames).
|
||
|
||
### Protocol 109 — 1.9.2 (2016-03-30)
|
||
|
||
1.9.2 was a same-day connectivity hotfix replacing 1.9.1. No new ViaVersion package exists for the 1.9.1→1.9.2 bump — `Protocol1_9To1_9_1.java` covers "1.9.1 and 1.9.2" (comment at line 34: `"// Currently supports 1.9.1 and 1.9.2"`). The packet set at protocol 109 is identical to 108: same 77 CB / 30 SB Play packets, same IDs.
|
||
|
||
minecraft-data: `data/pc/1.9.2/version.json` → `{"version":109,"minecraftVersion":"1.9.2","majorVersion":"1.9"}`. Packet list identical to 1.9 (`protocol.json` diff produces no changes). Source: minecraft-data `data/pc/1.9.2/`.
|
||
|
||
mc-wiki notes a connectivity issue with 1.9.1 servers was fixed; 7 bugs resolved total. No wire-protocol changes. ([minecraft.wiki/w/Java_Edition_1.9.2](https://minecraft.wiki/w/Java_Edition_1.9.2), fetched 2026-06-19.)
|
||
|
||
### Protocol 110 — 1.9.3 and 1.9.4 (2016-05-10)
|
||
|
||
ViaVersion package: `protocols/v1_9_1to1_9_3/`
|
||
|
||
```
|
||
Protocol1_9_1To1_9_3.java
|
||
packet/ClientboundPackets1_9_3.java
|
||
packet/ServerboundPackets1_9_3.java
|
||
```
|
||
|
||
1.9.3 and 1.9.4 share protocol number 110. 1.9.3 shipped and was replaced on the same day because it incorrectly showed a "you're running a snapshot" warning for old world saves. The actual protocol changes are in 1.9.3/1.9.4 together.
|
||
|
||
**Chunk format: ChunkType1_9_1 → ChunkType1_9_3**
|
||
|
||
The single largest change: the `map_chunk` (0x20) payload now **appends a `CompoundTag[]` (NBT array) of block entities** after the existing section data. This means the receiver no longer needs to request separate `tile_entity_data` packets for blocks in newly loaded chunks.
|
||
|
||
```
|
||
// ChunkType1_9_3.java:89
|
||
List<CompoundTag> nbtData = new ArrayList<>(
|
||
Arrays.asList(Types.NAMED_COMPOUND_TAG_ARRAY.read(input)));
|
||
return new BaseChunk(chunkX, chunkZ, fullChunk, false,
|
||
primaryBitmask, sections, biomeData, nbtData);
|
||
|
||
// write side: ChunkType1_9_3.java:127
|
||
Types.NAMED_COMPOUND_TAG_ARRAY.write(output,
|
||
chunk.getBlockEntities().toArray(new CompoundTag[0]));
|
||
```
|
||
|
||
ViaVersion's `Protocol1_9_1To1_9_3.java:89–113` translates this: when receiving a `LEVEL_CHUNK` from a 1.9/1.9.2 server (no embedded NBT), it walks the block palette looking for blocks that are tile entities (`FakeTileEntities1_9_1.isTileEntity(id)`) and synthesises placeholder `CompoundTag` entries so the 1.9.3+ client gets the required array. Source: `Protocol1_9_1To1_9_3.java:89–113`; chunk type classes `ChunkType1_9_1.java` and `ChunkType1_9_3.java`.
|
||
|
||
**`update_sign` removed from Play packet list**
|
||
|
||
In 1.9 the CB `update_sign` packet existed at 0x46. In 1.9.3/1.9.4 it is absent from the packet list (76 CB packets vs 77 in 1.9). Sign data is instead conveyed via `tile_entity_data` (0x09) with action=9. ViaVersion `Protocol1_9_1To1_9_3.java:61–87` translates an `UPDATE_SIGN` from a 1.9 server into a `BLOCK_ENTITY_DATA` packet with the sign lines serialised as NBT `Text1`–`Text4` strings. Source: `Protocol1_9_1To1_9_3.java:61–87`.
|
||
|
||
**Sound pitch scaling change**
|
||
|
||
The `SOUND` packet pitch field encoding changed:
|
||
|
||
```java
|
||
// Protocol1_9_1To1_9_3.java:47–51
|
||
public static final ValueTransformer<Short, Short> ADJUST_PITCH =
|
||
new ValueTransformer<>(Types.UNSIGNED_BYTE, Types.UNSIGNED_BYTE) {
|
||
public Short transform(PacketWrapper wrapper, Short inputValue) {
|
||
return (short) Math.round(inputValue / 63.5F * 63.0F);
|
||
}
|
||
};
|
||
```
|
||
|
||
Applied to every `SOUND` packet at `Protocol1_9_1To1_9_3.java:143–154`. The denominator changed from 63.5 to 63.0 (a subtle range normalisation).
|
||
|
||
**`LOGIN` and `RESPAWN` — dimension tracking**
|
||
|
||
Both packets are handled to keep the proxy's `ClientWorld` dimension-tracking in sync, since dimension determines sky-light presence in chunk packets. No field changes. Source: `Protocol1_9_1To1_9_3.java:115–140`.
|
||
|
||
**Serverbound: no changes**
|
||
|
||
`ServerboundPackets1_9_3` is byte-for-byte identical to `ServerboundPackets1_9` (same 30 packets, same IDs). Source: `v1_9_1to1_9_3/packet/ServerboundPackets1_9_3.java`.
|
||
|
||
ViaVersion git log for `v1_9_1to1_9_3` (shallow): `cff9a8715` (copyright), `fd5dadbe0` (clean imports), `e436bbe37` (dimension switch refactor), `c5756fe45` (BlockPosition rename), `75d86851c` (reformat), `501f65e21` (packet/entity renames), `e965e9713` (package renames).
|
||
|
||
mc-wiki: 1.9.3 introduced Netty blockedservers check and memory-use fixes for pathfinding/biome caching; 1.9.4 fixed the snapshot warning and 5 additional bugs. ([minecraft.wiki/w/Java_Edition_1.9.3](https://minecraft.wiki/w/Java_Edition_1.9.3), [1.9.4](https://minecraft.wiki/w/Java_Edition_1.9.4), fetched 2026-06-19.)
|
||
|
||
---
|
||
|
||
## Proxy and translation impact
|
||
|
||
### What a 1.9-aware proxy must handle at protocol 107 (largest workload)
|
||
|
||
1. **Full play-state packet ID remapping** — every packet from a 1.8 server must be renumbered before forwarding to a 1.9 client, and vice versa.
|
||
2. **Teleport-confirm injection** — proxy must synthesise a teleport ID (e.g. 0) and append it to every `PLAYER_POSITION` forwarded to 1.9 clients; must swallow all `ACCEPT_TELEPORTATION` from those clients before they reach a 1.8 server.
|
||
3. **Boss Bar emulation** — for 1.9 clients on 1.8 servers: track Wither/EnderDragon entity metadata and synthesise `BOSS_EVENT` packets. Requires an entity tracker.
|
||
4. **Chunk format translation** — 1.8 sends `MAP_BULK_CHUNK`; proxy must split into individual `MAP_CHUNK` packets encoded as `ChunkType1_9_1`. 1.8 unload-via-bitmask=0 must become `FORGET_LEVEL_CHUNK`.
|
||
5. **Dual-hand shim** — `USE_ITEM` (SB 0x1D) has no 1.8 equivalent; proxy synthesises a `BLOCK_PLACE` at pos −1,−1,−1 face 255; off-hand `USE_ITEM_ON` packets are cancelled. `SWING` loses its hand argument.
|
||
6. **Sound category insertion** — 1.8's `NAMED_SOUND_EFFECT` lacks the sound category field; proxy must inject a category VarInt.
|
||
7. **Entity coordinate conversion** — Double → Int×32 for spawn packets going toward 1.8 servers; Int/32 → Double for spawn packets coming from 1.8 servers.
|
||
8. **Entity-equipment slot remapping** — old Short (0=hand, 1–4=armor) ↔ new VarInt (0=main, 1=off, 2–5=armor).
|
||
9. **Component type upgrades** — disconnect reason, title text, player_info display name, tab-list header/footer: String → JSON component.
|
||
10. **`SET_COMPRESSION` absorption** — Play-state compression packet from 1.8 servers must not reach 1.9 clients.
|
||
|
||
### Additional at protocol 108/109
|
||
|
||
- Rewrite `LOGIN` dimension field: Int (1.9.1 server) ↔ Byte (1.9 client).
|
||
- Remap `SOUND` IDs ≥ 415 (+1 when going server→1.9 client against a 1.9.1+ server).
|
||
|
||
### Additional at protocol 110 (1.9.3/1.9.4)
|
||
|
||
- **Chunk format upgrade**: strip embedded `CompoundTag[]` from incoming 1.9.3 chunk packets when forwarding to 1.9/1.9.2 clients; or synthesise fake tile-entity NBT when forwarding 1.9-server chunks to 1.9.3+ clients.
|
||
- **`UPDATE_SIGN` → `BLOCK_ENTITY_DATA`**: translate sign update packet in either direction depending on server/client version.
|
||
- **Sound pitch rescaling**: apply `round(p / 63.5 × 63.0)` when bridging 1.9.1/1.9.2 ↔ 1.9.3/1.9.4.
|
||
|
||
ViaVersion handles all of the above via the three packages `v1_8to1_9`, `v1_9to1_9_1`, `v1_9_1to1_9_3` described throughout this document.
|