# 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) | | `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). 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 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 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.