Files
minecraft_protocol/versions/1.9.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

410 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.31.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, 14=armor); 1.9 uses VarInt with expanded enum (0=main hand, 1=off hand, 25=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,382384`; `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:102116` (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, 14=armor) → 1.9 VarInt (0=main hand, 1=off hand, 25=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:157205`.
### 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 243273) 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:96130`.
- 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:6062` (`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:332340`.
---
## 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.51.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:4048
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:4048`.
**2. `SOUND` — Elytra sound ID insertion.**
```java
// Protocol1_9To1_9_1.java:5163
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:5859`.
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:89113` 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:89113`; 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:6187` 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:6187`.
**Sound pitch scaling change**
The `SOUND` packet pitch field encoding changed:
```java
// Protocol1_9_1To1_9_3.java:4751
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:143154`. 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:115140`.
**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, 14=armor) ↔ new VarInt (0=main, 1=off, 25=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.