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>
28 KiB
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 fetched 2026-06-19, 1.9.1, 1.9.2, 1.9.3, 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.javaandClientboundPackets1_9.java,ServerboundPackets1_8.java,ServerboundPackets1_9.java. - minecraft-data
data/pc/1.8/protocol.jsonanddata/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_data0x09 packet or viaFakeTileEntitiesduring 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_bar0x0C,set_cooldown0x17,sound_effect0x47,unload_chunk0x1D,vehicle_move0x29,set_passengers0x40 all new.map_chunk_bulk,update_entity_nbt, Play-stateset_compressionall 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.
// 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.
// 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, 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, 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:
// 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, 1.9.4, fetched 2026-06-19.)
Proxy and translation impact
What a 1.9-aware proxy must handle at protocol 107 (largest workload)
- 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.
- Teleport-confirm injection — proxy must synthesise a teleport ID (e.g. 0) and append it to every
PLAYER_POSITIONforwarded to 1.9 clients; must swallow allACCEPT_TELEPORTATIONfrom those clients before they reach a 1.8 server. - Boss Bar emulation — for 1.9 clients on 1.8 servers: track Wither/EnderDragon entity metadata and synthesise
BOSS_EVENTpackets. Requires an entity tracker. - Chunk format translation — 1.8 sends
MAP_BULK_CHUNK; proxy must split into individualMAP_CHUNKpackets encoded asChunkType1_9_1. 1.8 unload-via-bitmask=0 must becomeFORGET_LEVEL_CHUNK. - Dual-hand shim —
USE_ITEM(SB 0x1D) has no 1.8 equivalent; proxy synthesises aBLOCK_PLACEat pos −1,−1,−1 face 255; off-handUSE_ITEM_ONpackets are cancelled.SWINGloses its hand argument. - Sound category insertion — 1.8's
NAMED_SOUND_EFFECTlacks the sound category field; proxy must inject a category VarInt. - 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.
- Entity-equipment slot remapping — old Short (0=hand, 1–4=armor) ↔ new VarInt (0=main, 1=off, 2–5=armor).
- Component type upgrades — disconnect reason, title text, player_info display name, tab-list header/footer: String → JSON component.
SET_COMPRESSIONabsorption — Play-state compression packet from 1.8 servers must not reach 1.9 clients.
Additional at protocol 108/109
- Rewrite
LOGINdimension field: Int (1.9.1 server) ↔ Byte (1.9 client). - Remap
SOUNDIDs ≥ 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.