Files
minecraft_protocol/versions/1.16.md
T
claude-timemachine a3d5f64ef5 verify pass: resolve VERIFY flags (corrections + citations + honest UNCONFIRMED)
Corrected real errors: several 1.7.x release dates, resource_pack_send version,
config packet ordering, structured-component count (56), PLAYER_LOADED (1.21.4),
entity_sound_effect field order. Confirmed+cited the rest; remaining ~19 items
re-marked UNCONFIRMED (third-party/ViaLegacy/26.2 internals unreachable from refs).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 15:03:44 +02:00

315 lines
19 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.16 — Nether Update
| Release | Protocol | Release Date | Data Version |
|---------|----------|--------------|--------------|
| 1.16 | 735 | 2020-06-23 | 2566 |
| 1.16.1 | 736 | 2020-06-24 | 2567 |
| 1.16.2 | 751 | 2020-08-11 | 2578 |
| 1.16.3 | 753 | 2020-09-10 | 2580 |
| 1.16.4 | 754 | 2020-11-02 | 2584 |
| 1.16.5 | 754 | 2021-01-15 | 2586 |
Sources: minecraft.wiki/w/Java_Edition_1.16 (fetched 2026-06-19), minecraft.wiki/w/Java_Edition_1.16.2 (fetched 2026-06-19), minecraft.wiki/w/Java_Edition_1.16.4 (fetched 2026-06-19); `/tmp/mcproto-refs/minecraft-data/data/pc/*/version.json`; `ViaVersion/api/.../ProtocolVersion.java:7175`.
---
## Headline Changes (Nether Update)
1.16 overhauled the Nether completely: four new biomes (Crimson Forest, Warped Forest, Soul Sand Valley, Basalt Deltas), four new mobs (Piglin, Hoglin, Zoglin, Strider), and the Netherite equipment tier. The most consequential **protocol change** was replacing the dimension system's hardcoded `int` enum with a **Codec NBT tag** sent at login — the server now declares all available dimensions at runtime, enabling data-pack-defined custom dimensions. The chat text-component format gained **full RGB hex colour** support (`"color":"#rrggbb"`), and the Login Success packet changed the UUID wire type from a plain String to a proper binary UUID.
---
## Protocol Changes vs 1.15.2
### Login state
**Login Success (0x02 C→client)**
In 1.15.2 the `uuid` field was `STRING` — a plain text UUID like `"550e8400-e29b-41d4-a716-446655440000"`.
In 1.16 it became a binary `UUID` (two `Long`s, big-endian, 16 bytes). ViaVersion translates the type on downgrade by parsing the string with `UUID.fromString()` and writing the binary form.
Source: `Protocol1_15_2To1_16.java:8387` (ViaVersion) — `UUID uuid = UUID.fromString(wrapper.read(Types.STRING)); wrapper.write(Types.UUID, uuid);`; minecraft-data `data/pc/1.15.2/protocol.json` (uuid type: `string`) vs `data/pc/1.16/protocol.json` (uuid type: `UUID`).
Also: ViaVersion introduced `ClientboundBaseProtocol1_16.java` which overrides `passthroughUUID` to use `Types.UUID` instead of a string form, affecting all base login handling from 1.16 onwards.
### Play state — clientbound
#### 0x25 Login (Join Game) — major restructure
The Login/Join Game packet grew substantially. Diff against 1.15.2:
| Field | 1.15.2 | 1.16 |
|-------|--------|------|
| `entityId` | Int | Int |
| `gameMode` | UnsignedByte | UnsignedByte |
| `previousGameMode` | *(absent)* | Byte (1 = none) |
| `worldNames` | *(absent)* | Array\<String\> — list of all registered world identifiers |
| `dimensionCodec` | *(absent)* | **NBT CompoundTag** — full registry of dimension types and biomes |
| `dimension` | **Int** (1/0/1 enum) | **String** resource key (`"minecraft:overworld"` etc.) |
| `worldName` | *(absent)* | String — current world identifier |
| `hashedSeed` | Long | Long |
| `maxPlayers` | UnsignedByte | UnsignedByte |
| `levelType` | String | *(removed)* |
| `viewDistance` | VarInt | VarInt |
| `reducedDebugInfo` | Boolean | Boolean |
| `enableRespawnScreen` | Boolean | Boolean |
| `isDebug` | *(absent)* | Boolean |
| `isFlat` | *(absent)* | Boolean |
The `dimensionCodec` NBT tag has the key `"dimension"` mapping to a `ListTag<CompoundTag>`. Each dimension entry contains fields such as `piglin_safe`, `natural`, `ambient_light`, `infiniburn`, `respawn_anchor_works`, `has_skylight`, `bed_works`, `has_raids`, `logical_height`, `shrunk`, `ultrawarm`, `has_ceiling`, and optionally `fixed_time`. ViaVersion synthesizes this tag when downgrading from 1.16 to 1.15.2 via `DimensionRegistries1_16.java` (four entries: overworld, overworld_caves, the_nether, the_end).
Sources: `EntityPacketRewriter1_16.java:133157` (ViaVersion); `minecraft-data/data/pc/1.16/protocol.json` (packet_login); `DimensionRegistries1_16.java` (full NBT structure).
#### 0x3A Respawn — dimension field changed
| Field | 1.15.2 | 1.16 |
|-------|--------|------|
| `dimension` | Int (1/0/1 enum) | String (resource key) |
| `worldName` | *(absent)* | String |
| `hashedSeed` | Long | Long |
| `gamemode` | UnsignedByte | UnsignedByte |
| `previousGamemode` | *(absent)* | Byte |
| `levelType` | String | *(removed)* |
| `isDebug` | *(absent)* | Boolean |
| `isFlat` | *(absent)* | Boolean |
| `copyMetadata` | *(absent)* | Boolean (keep player attributes on respawn) |
Source: `EntityPacketRewriter1_16.java:113131`; `minecraft-data/data/pc/1.16/protocol.json` (packet_respawn vs 1.15.2).
#### 0x0E Chat (clientbound) — sender UUID added
The Chat packet in 1.16 gained a `sender` UUID field (after the existing `message` string and `position` byte). ViaVersion fills it with the nil UUID `00000000-0000-0000-0000-000000000000` for system messages when downgrading.
Source: `Protocol1_15_2To1_16.java:125135`; `minecraft-data/data/pc/1.16/protocol.json` (packet_chat sender field type: UUID).
#### 0x00 Spawn Entity — absorbed lightning bolt (ADD_GLOBAL_ENTITY removed)
`packet_spawn_entity_weather` (0x2C in 1.15.2) was removed. Lightning bolts are now spawned via the regular `Spawn Entity` (0x00) packet with an entity type of `minecraft:lightning_bolt`. ViaVersion handles this by converting the old `ADD_GLOBAL_ENTITY` packet (type byte `1` = lightning) to `ADD_ENTITY` with a synthesised UUID and zero velocity.
Source: `EntityPacketRewriter1_16.java:85108`; `minecraft-data` packet list diff (packet_spawn_entity_weather absent in 1.16).
#### Light Update (0x24) — new boolean field
A new Boolean field "take neighbour's light into account" was added to the Light Update packet. ViaVersion writes `true` for this field when downgrading.
Source: `WorldPacketRewriter1_16.java:5965`.
#### Chunk data — heightmap padding changed
The heightmap `LongArray` encoding in Chunk Data changed: 1.15 used compact arrays without per-element padding (values could span two longs), but 1.16 added per-element padding (each value is contained within a single long, with padding bits at the end of each long). ViaVersion re-packs the heightmap when downgrading.
Source: `WorldPacketRewriter1_16.java:4657`.
#### Block entities — UUID fields changed from String to int\[\]
Two block-entity NBT format changes:
- `minecraft:conduit`: `target_uuid` (String) → `Target` (IntArray of 4 ints)
- `minecraft:skull`: `Owner.Id` (String) → `Owner.Id` (IntArray); key `Owner` renamed to `SkullOwner`
Source: `WorldPacketRewriter1_16.java:76118`.
### Play state — serverbound
#### 0x0F Generate Structure (new)
New packet for generating Jigsaw structures in-game. Fields: `location` (BlockPosition), `levels` (VarInt), `keepJigsaws` (Boolean). ViaVersion cancels it when downgrading to 1.15.2.
Source: `minecraft-data/data/pc/1.16/protocol.json` (packet_generate_structure); `Protocol1_15_2To1_16.java:202203`.
#### 0x05 Client Settings — new fields
`CLIENT_INFORMATION` in 1.16 added fields (specifically `mainHand`); ViaVersion cancels `JIGSAW_GENERATE` and `SET_JIGSAW_BLOCK` packets on downgrade.
#### 0x0E Interact Entity — new sneak field
The Interact packet gained a trailing Boolean field indicating whether the player is sneaking. ViaVersion reads and discards it on downgrade.
Source: `Protocol1_15_2To1_16.java:137152`.
#### 0x1A Player Abilities (serverbound) — fields removed
In 1.16 the client no longer sends `flyingSpeed` and `walkingSpeed` in the Player Abilities packet — only the flags byte remains. ViaVersion re-adds them from a `PlayerAbilitiesProvider` when downgrading.
Source: `Protocol1_15_2To1_16.java:194199`.
### Status state
**Status Response** — player-sample name line-break no longer allowed. Line-break characters in player sample names are split into separate entries.
Source: `Protocol1_15_2To1_16.java:89122`.
### Chat component — RGB hex colour support
In snapshot 20w17a (part of the 1.16 development cycle) the text-component `"color"` field was extended to accept 24-bit RGB hex strings of the form `"#rrggbb"` in addition to the 16 named colours (`"red"`, `"blue"`, etc.). This is a JSON-level change; no wire-format packet change accompanies it — the component is still serialised as a JSON string in the `Chat` packet. Older clients that don't understand `"#rrggbb"` will ignore or error on the colour.
Source: minecraft.wiki/w/Raw_JSON_text_format (fetched 2026-06-19); `ComponentRewriter1_16.java` processes component text but does not translate RGB colours, confirming ViaVersion does not downgrade them. Confirmed: `ComponentRewriter1_16.java` contains no hex-colour or RGB mapping logic; neither does its parent `JsonNBTComponentRewriter.java` — the `"color"` field is passed through unmodified on downgrade.
### Attribute identifiers renamed
All attribute keys were renamed from camelCase to snake_case namespaced identifiers (e.g. `generic.maxHealth``minecraft:generic.max_health`). ViaVersion maps these in `AttributeMappings1_16.java` and `UPDATE_ATTRIBUTES` rewriter.
Source: `EntityPacketRewriter1_16.java:159197`; `AttributeMappings1_16.java`.
---
## Per-Patch Sub-sections
### 1.16 — Protocol 735 (2020-06-23)
Initial release of the Nether Update. All changes above (dimension codec, RGB, sender UUID, lightning bolt, heightmap padding) introduced here. The release was reuploaded ~6 hours after initial publication to address a Realms connectivity regression (MC-191138), but the hotfix failed and 1.16.1 followed the next day.
Source: minecraft.wiki/w/Java_Edition_1.16.
### 1.16.1 — Protocol 736 (2020-06-24)
Hotfix release. **No protocol-level packet changes.** The protocol number incremented from 735→736, making 1.16 and 1.16.1 mutually incompatible even though no packet structure changed. ViaVersion's `Protocol1_16To1_16_1` is an essentially empty protocol class that registers no packet remappers (inherits the same `ClientboundPackets1_16`/`ServerboundPackets1_16` enums for both sides).
Fixes: Slime block arrow launch (MC-110792), crossbow third-person animation (MC-146743), Endermen hostility (MC-186546), Realms crash (MC-191138).
Source: minecraft.wiki/w/Java_Edition_1.16.1; `Protocol1_16To1_16_1.java` (empty class body).
### 1.16.2 — Protocol 751 (2020-08-11)
Significant protocol update despite sharing the same major version. Key changes:
#### Join Game — isHardcore extracted + dimension field type changed
The `isHardcore` flag was separated from the `gameMode` byte (previously hardcoded as bit 0x08 of the gamemode value) into its own Boolean field. `maxPlayers` also changed from `UnsignedByte` to `VarInt`. Most critically, the `dimension` field changed again:
| Field | 1.16 | 1.16.2 |
|-------|------|--------|
| `isHardcore` | *(packed in gameMode byte)* | **Boolean** (separate) |
| `gameMode` | UnsignedByte (incl. hardcore bit) | Byte (without hardcore bit) |
| `dimensionCodec` | NBT (registry) | NBT (registry, updated format) |
| `dimension` | **String** (resource key) | **NBT CompoundTag** (full dimension data inline) |
| `maxPlayers` | UnsignedByte | **VarInt** |
In 1.16.2 the current dimension's full data is sent inline in the `dimension` field (a `CompoundTag`) rather than just a resource-key string referencing the registry. The `dimensionCodec` NBT registry was also updated.
ViaVersion converts from 1.16.2 back to 1.16 by reading the NBT dimension data from the registry mapping (`MappingData1_16_2.getDimensionDataMap()`), discarding the 1.16.2-style NBT, and writing the resource-key string.
Source: `EntityPacketRewriter1_16_2.java:4170`; `minecraft-data/data/pc/1.16.2/protocol.json` (packet_login); `WorldPacketRewriter1_16_2.java`.
#### Respawn — dimension field also became NBT
The `dimension` field in Respawn changed from String to CompoundTag, parallel to Login.
Source: `EntityPacketRewriter1_16_2.java:7379`.
#### Section Blocks Update (0x3B) — replaces Multi Block Change (0x0F)
`multi_block_change` was replaced by `section_blocks_update` and moved from packet ID 0x0F to 0x3B, causing all packets from 0x10 through 0x3A in 1.16 to shift down by one slot (0x0F0x3A in 1.16.2).
Old format (1.16): `chunkX` (Int) + `chunkZ` (Int) + array of records per block `{horizontalPos:u8, y:u8, blockId:VarInt}`.
New format (1.16.2): A single packed Long encoding `chunkX:22 | chunkZ:22 | chunkY:20` (the chunk section rather than chunk column), a Boolean `notTrustEdges`, then an array of packed VarLongs each encoding `blockStateId:52 | localX:4 | localZ:4 | localY:4`. This is more efficient and per-section rather than per-column.
ViaVersion converts by splitting the old per-column records into per-section `SECTION_BLOCKS_UPDATE` packets.
Source: `WorldPacketRewriter1_16_2.java:3974`; `ClientboundPackets1_16_2.java:83` (SECTION_BLOCKS_UPDATE at 0x3B); `minecraft-data/data/pc/1.16.2/protocol.json` (packet_section_blocks_update vs packet_multi_block_change).
#### Recipe Book — split into two serverbound packets
`crafting_book_data` (0x1E) was split into two packets:
- `RECIPE_BOOK_CHANGE_SETTINGS` (0x1E) — book type, open state, filter state
- `RECIPE_BOOK_SEEN_RECIPE` (0x1F) — single recipe identifier
All serverbound packets at 0x1F and above shifted up by one slot.
Source: `Protocol1_16_1To1_16_2.java:5978`; `ServerboundPackets1_16_2.java:5455`.
#### Entity type additions
New entity type `Piglin Brute` added. Piglin entity metadata indices 15 and 16 swapped between 1.16 and 1.16.2.
Source: `EntityPacketRewriter1_16_2.java:8693`.
#### Tags — `furnace_materials` removed
The block/item tag `minecraft:furnace_materials` was removed.
Source: `Protocol1_16_1To1_16_2.java:85`.
### 1.16.3 — Protocol 753 (2020-09-10)
Minimal release. The wiki states "the only difference between the two [1.16.3-rc1 and 1.16.3] is a change in the data and protocol versions." No packet-structure changes. ViaVersion's `Protocol1_16_2To1_16_3` is an empty class that registers no remappers.
Fixes: Nether mob pathfinding, baby piglin item duplication.
Source: minecraft.wiki/w/Java_Edition_1.16.3; `Protocol1_16_2To1_16_3.java` (empty class body).
### 1.16.4 — Protocol 754 (2020-11-02)
#### Edit Book serverbound — hand field changed
The `Edit Book` serverbound packet (0x0C/0x1E depending on version) changed how the hand is encoded. In 1.16.4 the hand is now a VarInt slot index (40 = offhand, otherwise mainhand), whereas before it was a hand enum VarInt (0=main, 1=off). ViaVersion converts slot 40 → hand 1 (offhand), everything else → 0 (mainhand).
Source: `Protocol1_16_3To1_16_4.java:3344`.
#### Social Interactions screen
A new in-game social interactions screen was added (opens with P by default) allowing players to suppress chat from specific players. This is a client-side UI feature with no new protocol packets; it operates on the existing Chat packet sender UUID field (added in 1.16).
Source: minecraft.wiki/w/Java_Edition_1.16.4.
#### Snapshot high-bit scheme introduced
Starting with **1.16.4-pre1**, Minecraft development releases began using protocol version numbers with bit 30 set (`1 << 30 = 0x40000000`). The release version of a snapshot's corresponding full release keeps the same protocol number as the previous compatible release in cases of wire compatibility.
For example, snapshots leading to 1.16.4 used versions like `0x40000001`, `0x40000002`, etc. (incrementing by 1 per snapshot), while the full 1.16.4 release used 754. The ViaVersion implementation encodes this as `(1 << 30) | snapshotVersion` in `ProtocolVersion.java:305`.
This convention has been used for all snapshot/pre-release versions ever since.
Source: minecraft.wiki/w/Java_Edition_1.16.4 ("New network protocol scheme, with a high bit (bit 30) set for snapshots"); `ProtocolVersion.java:305` (`return (1 << 30) | snapshotVersion`); minecraft.wiki/w/Protocol_version (fetched 2026-06-19).
### 1.16.5 — Protocol 754 (2021-01-15)
Shares protocol 754 with 1.16.4; 1.16.4 clients can connect to 1.16.5 servers and vice versa. The update addressed server crash bugs (MC-203337: `IllegalStateException` in POI data). No protocol-level changes; ViaVersion has no separate protocol class for 1.16.5 and registers it as a sub-version of 754.
Source: minecraft.wiki/w/Java_Edition_1.16.5; `ProtocolVersion.java:75` (`register(754, "1.16.4-1.16.5", new SubVersionRange("1.16", 4, 5))`).
---
## Summary: Packet ID Renumbering in 1.16.2
The removal of `multi_block_change` from slot 0x0F and its replacement with `section_blocks_update` at 0x3B meant that all 1.16 clientbound play packets between 0x10 and 0x3A shifted down by one ID in 1.16.2. Any hardcoded packet ID table for 1.16 is incorrect for 1.16.2.
Notable shifts (1.16 ID → 1.16.2 ID):
| Packet | 1.16 | 1.16.2 |
|--------|------|--------|
| `multi_block_change` | 0x0F | *(removed / renamed to section_blocks_update at 0x3B)* |
| `tab_complete` | 0x10 | 0x0F |
| `declare_commands` | 0x11 | 0x10 |
| `login` (Join Game) | 0x25 | 0x24 |
| `respawn` | 0x3A | 0x39 |
| `section_blocks_update` | *(absent)* | 0x3B |
Source: `minecraft-data/data/pc/1.16/protocol.json` vs `data/pc/1.16.2/protocol.json` packet ID mappings; `ClientboundPackets1_16.java` vs `ClientboundPackets1_16_2.java`.
---
## Proxy / Translation Impact
### Dimension codec — proxies must maintain per-session registry
Before 1.16, proxies could route players between servers without caring about dimension state in the Join Game packet (dimension was a fixed enum). From 1.16 onwards:
1. The proxy must read the `dimensionCodec` NBT from every backend's Login response and cache it per player.
2. On dimension change (Respawn packet), the proxy must update its cached notion of the current dimension.
3. If a ViaVersion-managed proxy downgrades a 1.16 client to a pre-1.16 backend, it must synthesise the dimension codec from scratch (using `DimensionRegistries1_16`).
4. In 1.16.2 the current dimension's NBT data is sent inline in both Login and Respawn; the proxy must parse and relay that tag accurately or player loading breaks.
### Login Success UUID type change
Pre-1.16 proxies that parse Login Success as a plain string UUID will misread the packet after 1.16. The 16-byte binary UUID is not a valid UTF-8 string; the proxy must switch UUID handling based on protocol version (≥735 = binary UUID).
### Section Blocks Update (1.16.2+) — packet format incompatibility
The new section-relative, VarLong-packed format of `section_blocks_update` is completely incompatible with the old `multi_block_change` format. Any protocol-translation layer must reconstruct the records from scratch when crossing the 1.16/1.16.2 boundary.
### Snapshot high-bit (1.16.4+ clients)
Development clients connecting to proxies send protocol versions with bit 30 set. A proxy that expects protocol versions ≤ 1000 will misidentify a snapshot client as an unknown/future version. Proxies must mask out bit 30 to identify the base release version, then handle snapshot clients as the corresponding release version or reject them gracefully.