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>
34 KiB
Java Edition 1.20.x — Protocol Deep-Dive
| Release | Protocol # | Release date | ViaVersion package (bump into this) | minecraft-data dir |
|---|---|---|---|---|
| 1.20 | 763 | 2023-06-07 | v1_19_4to1_20 (762→763) |
data/pc/1.20 |
| 1.20.1 | 763 | 2023-06-12 | (same protocol as 1.20; no bump) | data/pc/1.20.1 |
| 1.20.2 | 764 | 2023-09-21 | v1_20to1_20_2 (763→764) — adds Configuration state |
data/pc/1.20.2 |
| 1.20.3 | 765 | 2023-12-05 | v1_20_2to1_20_3 (764→765) |
data/pc/1.20.3 |
| 1.20.4 | 765 | 2023-12-07 | (same protocol as 1.20.3; no bump) | data/pc/1.20.4 |
| 1.20.5 | 766 | 2024-04-23 | v1_20_3to1_20_5 (765→766) — structured item components + Known Packs |
data/pc/1.20.5 |
| 1.20.6 | 766 | 2024-04-29 | (same protocol as 1.20.5; no bump) | data/pc/1.20.6 |
Sources: minecraft.wiki release articles (1.20 fetched 2026-06-19, 1.20.2, 1.20.3, 1.20.4, 1.20.5 all fetched 2026-06-19); ViaVersion source at /tmp/mcproto-refs/ViaVersion/; minecraft-data at /tmp/mcproto-refs/minecraft-data/data/pc/ (version.json numbers cross-checked: 1.20/1.20.1=763, 1.20.2=764, 1.20.3/1.20.4=765, 1.20.5/1.20.6=766).
Protocol numbers verified directly from minecraft-data
version.jsonfiles and ViaVersion'sMappingDataBase(...)constructor arguments in eachProtocol*.java.
Headline — Trails & Tales, plus the two biggest connection-layer changes since the Netty rewrite
1.20 the release ("Trails & Tales", 2023-06-07) is a content update — cherry wood, the archaeology system (brush + suspicious sand/gravel + pottery sherds), the Sniffer and Camel mobs, bamboo wood set, hanging signs, smithing-template armor trims, and the chiseled bookshelf (minecraft.wiki/w/Java_Edition_1.20, fetched 2026-06-19). But protocol-wise, base 1.20 (763) is a small bump. The 1.20 line is remembered instead for two structural protocol mechanics that landed in its patch releases and reshaped how every proxy on the planet works:
- The Configuration connection state (1.20.2 / protocol 764). A brand-new network state was inserted between Login and Play. Registry data, tags, feature flags and resource-pack negotiation all moved out of the Play-state "Join Game" packet into this dedicated phase. Login no longer flows straight into Play — the client sends a Login Acknowledged packet to end Login, runs the Configuration handshake, then sends Finish Configuration to enter Play. See
../06-configuration.mdand../02-connection-lifecycle.md§5b–7 for the topical treatment. - Structured item data components (1.20.5 / protocol 766). The free-form
tagNBT compound that had ridden along with every item stack since the dawn of the protocol was replaced by typed structured components — a map ofminecraft:<component>keys, each with its own wire type, e.g.wooden_pickaxe[damage=23](minecraft.wiki/w/Java_Edition_1.20.5, fetched 2026-06-19). The same release added the Known Packs handshake to Configuration so server and client can agree on which registry entries are already known client-side.
The other two bumps are smaller: 763 (cherry/archaeology content, minimal wire change), and 765 (text components serialised as NBT instead of JSON strings; multi-resource-pack push/pop).
sequenceDiagram
participant C as Client
participant S as Server
Note over C,S: protocol < 764 (1.20 / 1.20.1) — Login flows straight into Play
C->>S: Login Start
S-->>C: Login Success (Game Profile)
Note over C,S: client switches to PLAY immediately
S-->>C: Join Game (registries + tags + features inline)
Note over C,S: protocol >= 764 (1.20.2+) — Configuration inserted between Login and Play
C->>S: Login Start
S-->>C: Login Success (Game Profile)
C->>S: Login Acknowledged
Note over C,S: state = CONFIGURATION
S-->>C: Registry Data / Update Tags / Update Enabled Features
S-->>C: Resource Pack (optional) / Custom Payload (brand)
S-->>C: Finish Configuration
C->>S: Finish Configuration (ack)
Note over C,S: state = PLAY
S-->>C: Join Game (now lean — IDs/dimensions only)
763 — 1.20 / 1.20.1 (2023-06-07)
ViaVersion package: protocols/v1_19_4to1_20/ — Protocol1_19_4To1_20.java.
The single most telling fact: this package reuses the 1.19.4 packet enums on both sides. Its class signature is
// Protocol1_19_4To1_20.java:39
public final class Protocol1_19_4To1_20 extends
AbstractProtocol<ClientboundPackets1_19_4, ClientboundPackets1_19_4,
ServerboundPackets1_19_4, ServerboundPackets1_19_4>
i.e. clientbound-unmapped == clientbound-mapped == ClientboundPackets1_19_4, and likewise serverbound. There is no ClientboundPackets1_20 / ServerboundPackets1_20 enum in the package at all (ls v1_19_4to1_20/ → only Protocol1_19_4To1_20.java + rewriter/). So 763 introduced zero packet additions, removals, or ID renumberings in any state. minecraft-data agrees: data/pc/1.20/protocol.json has the same top-level state set as 1.19.4 (handshaking/status/login/play, no configuration) and a near-identical packet count.
What 763 did change is content-side mapping data and a few in-place field handlers:
PLAYER_COMBAT_END/PLAYER_COMBAT_KILL— a leading entity-IDVarIntfield was dropped from each (the duration/killer fields shifted). ViaVersion handles this in-place. Source:Protocol1_19_4To1_20.java:56–66.- Block/item/entity mapping refresh — new cherry & bamboo blocks, the Sniffer entity, suspicious sand/gravel ("brushable") block entities, hanging-sign and decorated-pot block entities, and the armor-trim smithing items are all handled purely as remapped registry IDs through
BlockPacketRewriter1_20,ItemPacketRewriter1_20,EntityPacketRewriter1_20plus the1.19.4→1.20mapping files (MappingDataBase("1.19.4", "1.20"),Protocol1_19_4To1_20.java:41). No new packet types were needed for any of this content. - The
ItemPacketRewriter1_20only re-touches existing packets (OPEN_SIGN_EDITOR,SIGN_UPDATE,LEVEL_CHUNK_WITH_LIGHT,LIGHT_UPDATE,SECTION_BLOCKS_UPDATE,UPDATE_RECIPES) — sourceItemPacketRewriter1_20.java:72,76,84,95,101,115.
ViaVersion git log for v1_19_4to1_20 (git -C /tmp/mcproto-refs/ViaVersion log --oneline -- common/…/v1_19_4to1_20):
| Commit | Subject |
|---|---|
0121534e7 |
Deduplicate particle type fillers |
8f8f5e72c |
Default rewriter registrations across protocols |
bfaf7a58e |
Move block entity handling to block rewriter (fixes new-instance pass-through on type-id change) |
ac3362f95 |
Don't add damage types twice in 1.19.4→1.20 (#4389) |
c13b40a37 |
Add ParticleRewriter base (#4203) |
501f65e21 |
Packet and entity type renames (Mojang-mapped names) |
e965e9713 |
Package/class renames and moves |
1.20.1 carried the same protocol number 763 with no ViaVersion package of its own — it was a server-side bugfix release. Packet set identical to 1.20. (data/pc/1.20.1/version.json → {"minecraftVersion":"1.20.1","version":763,"majorVersion":"1.20"}.)
764 — 1.20.2 (2023-09-21) — the Configuration state (the headline)
ViaVersion package: protocols/v1_20to1_20_2/ — Protocol1_20To1_20_2.java (+ storage/ConfigurationState.java, the config packet enums). This bump finally gives the package its own packet enums (ClientboundPackets1_20_2 / ServerboundPackets1_20_2) and a brand-new set of Configuration-state enums.
What the Configuration state is
Mojang inserted a fourth long-lived connection state. From the wiki: "Configuration phase automatically starts after login phase (i.e. after client account has been verified) and lasts until the player joins the world (play phase)… Clients can stay in configuration phase indefinitely — it's up to server to release it to the world… Servers can also request clients to re-enter the configuration phase after it has entered the play phase." (minecraft.wiki/w/Java_Edition_1.20.2, fetched 2026-06-19.)
minecraft-data captures the structural shift exactly: data/pc/1.20/protocol.json top-level states are handshaking/status/login/play, while data/pc/1.20.2/protocol.json adds a fifth: handshaking/status/login/configuration/play. Verified by reading the top-level keys of each file.
How Login → Config → Play changed
The old flow was: server sends LOGIN_FINISHED (a.k.a. Game Profile / Login Success), client flips straight to Play, server sends a fat Join Game packet carrying the dimension registry, tags, and enabled features inline.
The new flow (protocol ≥ 764):
- Server sends Login Success (
ClientboundLoginPackets.LOGIN_FINISHED, login ID0x02; aliasedGAME_PROFILE. Source:protocols/base/ClientboundLoginPackets.java:26,32). - Client replies Login Acknowledged (
ServerboundLoginPackets.LOGIN_ACKNOWLEDGED, login ID0x03. Source:protocols/base/ServerboundLoginPackets.java:27) — this is the new packet that ends the Login state. - Connection enters CONFIGURATION. Server pushes registry data, tags, features and (optionally) a resource pack, then Finish Configuration; the client acks with its own Finish Configuration.
- Only then does the connection enter PLAY, and the now-lean Join Game packet arrives.
Per the wiki, the following moved out of Play and into Configuration: registry-data configuration, enabled-features setup, and server resource-pack negotiation ("the player is no longer in world when answering prompts"). Custom-payload, tag updates, and ping/keep-alive exist in both states. (minecraft.wiki/w/Java_Edition_1.20.2, fetched 2026-06-19; cross-checked Java Edition protocol/Registry data, accessed 2026-06-19.)
Configuration-state packet tables (protocol 764)
Clientbound (server → client), Configuration state — ClientboundConfigurationPackets1_20_2.java:
| ID | Name |
|---|---|
| 0x00 | CUSTOM_PAYLOAD |
| 0x01 | DISCONNECT |
| 0x02 | FINISH_CONFIGURATION |
| 0x03 | KEEP_ALIVE |
| 0x04 | PING |
| 0x05 | REGISTRY_DATA |
| 0x06 | RESOURCE_PACK |
| 0x07 | UPDATE_ENABLED_FEATURES |
| 0x08 | UPDATE_TAGS |
Source: v1_20to1_20_2/packet/ClientboundConfigurationPackets1_20_2.java:24–32 (IDs are enum ordinal()).
Serverbound (client → server), Configuration state — ServerboundConfigurationPackets1_20_2.java:
| ID | Name |
|---|---|
| 0x00 | CLIENT_INFORMATION |
| 0x01 | CUSTOM_PAYLOAD |
| 0x02 | FINISH_CONFIGURATION |
| 0x03 | KEEP_ALIVE |
| 0x04 | PONG |
| 0x05 | RESOURCE_PACK |
Source: v1_20to1_20_2/packet/ServerboundConfigurationPackets1_20_2.java:25–30.
How ViaVersion fakes the Configuration state for a 1.20 (763) server
This is the most instructive part of the whole release for proxy authors. A 1.20.2 client expects the Configuration handshake; a 1.20 server knows nothing about it. ViaVersion bridges the gap with a small state machine in ConfigurationState (storage/ConfigurationState.java) whose BridgePhase enum has four values:
// ConfigurationState.java:167–169
public enum BridgePhase {
NONE, PROFILE_SENT, CONFIGURATION, REENTERING_CONFIGURATION
}
The bridge works like this (Protocol1_20To1_20_2.java):
- On the server's
LOGIN_FINISHED(Game Profile), Via setsBridgePhase.PROFILE_SENTand forces the server-side tracked state to PLAY (the old server is about to start sending Play packets). Source:Protocol1_20To1_20_2.java:141–144. - The client then sends
LOGIN_ACKNOWLEDGED. Via cancels it (the old server can't parse it), setsBridgePhase.CONFIGURATION, and flushes any packets it had queued. Source:Protocol1_20To1_20_2.java:146–155. - While in
PROFILE_SENT, every clientbound Play packet the old server emits is queued, not forwarded, until the client has transitioned into Configuration — see the override intransform(...)atProtocol1_20To1_20_2.java:247–327, which queues packets (addClientboundPacketToQueue) or remaps a handful (CUSTOM_PAYLOAD,DISCONNECT,KEEP_ALIVE,PING,UPDATE_ENABLED_FEATURES,UPDATE_TAGS) to their Configuration-state counterparts. - Via synthesises the whole Configuration sequence itself in
sendConfigurationPackets(...): it sends aREGISTRY_DATApacket built from the dimension registry it captured, replays tags (or an emptyUPDATE_TAGSso later protocols can append), optionally re-sends the last resource pack, then sendsFINISH_CONFIGURATIONand flips server state back to PLAY. Source:Protocol1_20To1_20_2.java:329–373. - The client's serverbound
CLIENT_INFORMATION(settings) is captured during Configuration and re-sent later as the Play-state settings packet, because the client only sends it once per connection. Source:Protocol1_20To1_20_2.java:167–184+ConfigurationState.java:171–187. - The client's Configuration-state
CUSTOM_PAYLOAD/KEEP_ALIVE/PONGare mapped to their Play equivalents and queued until the server's listener is in PLAY. Source:Protocol1_20To1_20_2.java:187–189. - Login
HELLO(serverbound name) gained anOPTIONAL_UUIDfield in 1.20.2; Via converts the plainUUIDa 1.20 client sends. Source:Protocol1_20To1_20_2.java:128–133. - The config-phase serverbound packet queue is bounded (default 1000 packets / 1 MiB) and over-budget connections are disconnected. Source:
ConfigurationState.java:37–38,81–91.
ViaVersion git log for v1_20to1_20_2 (selected, from git -C … log --oneline -- common/…/v1_20to1_20_2):
| Commit | Subject |
|---|---|
9a8a01b75 |
Make config limits configurable via system properties |
3e8682f94 |
Limit config phase packet queue |
6ce21135f |
Make AbstractProtocol#registerFinishConfiguration obsolete (#4853) |
8d3c36de0 |
Send empty tags packet if not sent early enough when leaving config stage |
5772ee4a9 |
Only send tags early for 1.20.5+ clients, track early send properly in 1.20→1.20.2 |
7f06b0345 |
Remove StorableObject#clearOnServerSwitch (#4583) |
ceb1cffb0 |
Cancel "message not delivered" messages (fixes #3438) |
32e51b52a |
Cleanup LOGIN/STATUS packet handlers (#4113) |
Other 764 Play-state field changes handled in the same file: SET_DISPLAY_OBJECTIVE slot widened Byte→VarInt (Protocol1_20To1_20_2.java:123–126); CUSTOM_PAYLOAD content sanitised softly (:84,231–245); UPDATE_ENABLED_FEATURES is cancelled in Play and re-emitted in Config (:194,310).
765 — 1.20.3 / 1.20.4 (2023-12-05 / 2023-12-07)
ViaVersion package: protocols/v1_20_2to1_20_3/ — Protocol1_20_2To1_20_3.java, with its own ClientboundPackets1_20_3 / ServerboundPackets1_20_3 and ClientboundConfigurationPackets1_20_3.
This is a medium bump, dominated by two themes:
1. Text components serialise as NBT, not JSON strings
The headline 765 change: text/chat components are now sent over the wire as NBT rather than JSON strings (minecraft.wiki/w/Java_Edition_1.20.3, fetched 2026-06-19 — "Chat components now serialize to NBT when sent over network"; plain-text components serialise as a bare string instead of {"text":"…"}). ViaVersion does this conversion in convertComponent / convertOptionalComponent, which read a Types.COMPONENT (JSON) and write a Types.TRUSTED_TAG (NBT):
// Protocol1_20_2To1_20_3.java:330–336
private void convertComponent(final PacketWrapper wrapper) {
wrapper.write(Types.TRUSTED_TAG, ComponentUtil.jsonToTag(wrapper.read(Types.COMPONENT)));
}
It is applied across a long list of packets carrying components: DISCONNECT (both Play and Configuration states — note ClientboundConfigurationPackets1_20_2.DISCONNECT at :229), SERVER_DATA, SET_ACTION_BAR_TEXT, SET_TITLE_TEXT, SET_SUBTITLE_TEXT, DISGUISED_CHAT, SYSTEM_CHAT, OPEN_SCREEN, TAB_LIST, PLAYER_COMBAT_KILL, PLAYER_INFO_UPDATE (display name), BOSS_EVENT, PLAYER_CHAT, SET_OBJECTIVE, SET_PLAYER_TEAM, UPDATE_ADVANCEMENTS, COMMAND_SUGGESTIONS, MAP_ITEM_DATA. Source: Protocol1_20_2To1_20_3.java:229–262, 98–254.
2. Multi-resource-pack push/pop
Servers can now apply multiple resource packs, each identified by a UUID, and un-apply them individually. The old single RESOURCE_PACK packet (Play and Config) splits into RESOURCE_PACK_PUSH + RESOURCE_PACK_POP. ViaVersion maps the old packet to a push, synthesising a UUID from the URL via UUID.nameUUIDFromBytes(url), and prepends a pop with OPTIONAL_UUID = null to drop prior packs. Source: Protocol1_20_2To1_20_3.java:231,294,312–328. The serverbound resource-pack status enum gained new action values (downloaded / invalid-url / failed-reload / discarded); Via folds the new statuses back onto the old set (:297–310). Resource packs are also no longer dropped on entering Configuration (wiki 1.20.3, fetched 2026-06-19).
Other 765 changes (from source)
SET_SCORE/RESET_SCOREsplit — aSET_SCOREwith action 1 ("reset") becomes a dedicatedRESET_SCOREpacket; scores gain an optional display component + number-format fields. Source:Protocol1_20_2To1_20_3.java:79–97. Number formats (styled / fixed / blank) are new scoreboard display options (wiki 1.20.3).SET_JIGSAW_BLOCKgained selection-priority + placement-priorityVarInts (Via strips them downward). Source::113–122.CONTAINER_SLOT_STATE_CHANGEDis a new serverbound packet (Via cancels it downward). Source::77.- The Configuration-state packet set is shared with 764 except resource-pack:
createPacketTypesProvider()mapsClientboundConfigurationPackets1_20_2↔ClientboundConfigurationPackets1_20_3. Source::380–388.
1.20.4 shares protocol 765 with 1.20.3 — it was a one-bug hotfix (MC-267185, decorated pots deleting items on reload) with no protocol change (minecraft.wiki/w/Java_Edition_1.20.4, fetched 2026-06-19). No separate ViaVersion package.
ViaVersion git log for v1_20_2to1_20_3 (selected):
| Commit | Subject |
|---|---|
ab3927dff |
Implement our own hash writing |
bf84eb014 |
Add separate config option for text component conversion errors |
6ad9a7190 |
Print book conversion errors to default logger in 1.20.2→1.20.3 (#4158) |
2841bf304 |
Add option to hide scoreboard numbers (#4122) |
2e91b841b |
Automatically call mapTypes in entity rewriter |
766 — 1.20.5 / 1.20.6 (2024-04-23 / 2024-04-29) — structured item components + Known Packs
ViaVersion package: protocols/v1_20_3to1_20_5/ — Protocol1_20_3To1_20_5.java, with data/, storage/, the *Packets1_20_5 enums and the big StructuredDataConverter.java. This is by far the heaviest bump of the 1.20 line (minecraft-data packet-name count jumps from ~911 at 1.20.3 to ~1066 at 1.20.5).
1. Structured item data components (typed StructuredDataKey)
Mojang replaced the single free-form tag NBT compound on every item stack with a map of typed components. From the wiki: "Unstructured NBT data attached to stacks of items (tag field) has been replaced with structured 'components'." Components are minecraft:<component_name> and render as wooden_pickaxe[damage=23]; legacy {...} custom NBT survives only inside minecraft:custom_data (minecraft.wiki/w/Java_Edition_1.20.5, fetched 2026-06-19).
ViaVersion models each component as a StructuredDataKey<T> — a typed key pairing an identifier string with a wire Type<T>:
// api/.../minecraft/data/StructuredDataKey.java:109
public record StructuredDataKey<T>(String identifier, Type<T> type) { … }
// :111–115 examples
public static final StructuredDataKey<CompoundTag> CUSTOM_DATA = new StructuredDataKey<>("custom_data", Types.COMPOUND_TAG);
public static final StructuredDataKey<Integer> MAX_STACK_SIZE = new StructuredDataKey<>("max_stack_size", Types.VAR_INT);
public static final StructuredDataKey<Integer> DAMAGE = new StructuredDataKey<>("damage", Types.VAR_INT);
public static final StructuredDataKey<Unbreakable> UNBREAKABLE1_20_5 = new StructuredDataKey<>("unbreakable", Unbreakable.TYPE);
The full 1.20.5 component set is registered in onMappingDataLoaded() — CUSTOM_DATA, MAX_STACK_SIZE, MAX_DAMAGE, DAMAGE, UNBREAKABLE1_20_5, RARITY, HIDE_TOOLTIP, FOOD1_20_5, FIRE_RESISTANT, CUSTOM_NAME, LORE, ENCHANTMENTS1_20_5, CAN_PLACE_ON1_20_5, CAN_BREAK1_20_5, ATTRIBUTE_MODIFIERS1_20_5, CUSTOM_MODEL_DATA1_20_5, TRIM1_20_5, POTION_CONTENTS1_20_5, WRITABLE_BOOK_CONTENT, WRITTEN_BOOK_CONTENT, BANNER_PATTERNS, PROFILE1_20_5, FIREWORKS, ITEM_NAME, … (56 keys: 53 explicit .add() calls in Protocol1_20_3To1_20_5.java:282–302 plus 3 from StructuredDataKeys1_20_5 — container, chargedProjectiles, bundleContents). Source: Protocol1_20_3To1_20_5.java:282–302; api/.../data/version/StructuredDataKeys1_20_5.java.
Downgrade to NBT (the proxy-critical bit): to serve a 1.20.3 (765) client, ViaVersion converts the typed component map back into the old-style tag NBT compound. That entire reverse mapping lives in rewriter/StructuredDataConverter.java (a large per-component switch importing every …item.data.* class). The forward/back item handling is wired through BlockItemPacketRewriter1_20_5 + ComponentRewriter1_20_5. Source: v1_20_3to1_20_5/rewriter/StructuredDataConverter.java (class at :74), Protocol1_20_3To1_20_5.java:78,88.
By 1.20.5 the armor-trim material/pattern registries (the smithing-template feature from base 1.20) are themselves data-driven, so Via keeps a default-registry fallback in storage/ArmorTrimStorage.java — the 10 vanilla trim materials (amethyst…redstone) and 16 patterns (coast…wild) as KeyMappings, updatable from the live registry. Source: storage/ArmorTrimStorage.java:26–58. The TRIM1_20_5 structured key resolves trim material/pattern indices against this storage during conversion.
2. The Known Packs handshake (Configuration state)
1.20.5 adds SELECT_KNOWN_PACKS to the Configuration state, in both directions. The server clientbound-pushes the packs it intends to drive the registries from; the client serverbound-replies with the packs it already has bundled, letting the server skip re-sending registry entries the client can reconstruct locally.
Clientbound Configuration (1.20.5) — ClientboundConfigurationPackets1_20_5.java. Note the Configuration table grew from 9 to 15 entries (cookies + transfer + reset-chat + pack push/pop all live here now):
| ID | Name | ID | Name | |
|---|---|---|---|---|
| 0x00 | COOKIE_REQUEST |
0x08 | RESOURCE_PACK_POP |
|
| 0x01 | CUSTOM_PAYLOAD |
0x09 | RESOURCE_PACK_PUSH |
|
| 0x02 | DISCONNECT |
0x0A | STORE_COOKIE |
|
| 0x03 | FINISH_CONFIGURATION |
0x0B | TRANSFER |
|
| 0x04 | KEEP_ALIVE |
0x0C | UPDATE_ENABLED_FEATURES |
|
| 0x05 | PING |
0x0D | UPDATE_TAGS |
|
| 0x06 | RESET_CHAT |
0x0E | SELECT_KNOWN_PACKS |
|
| 0x07 | REGISTRY_DATA |
Source: v1_20_3to1_20_5/packet/ClientboundConfigurationPackets1_20_5.java:24–38.
Serverbound Configuration (1.20.5) — ServerboundConfigurationPackets1_20_5.java:
| ID | Name | ID | Name | |
|---|---|---|---|---|
| 0x00 | CLIENT_INFORMATION |
0x04 | KEEP_ALIVE |
|
| 0x01 | COOKIE_RESPONSE |
0x05 | PONG |
|
| 0x02 | CUSTOM_PAYLOAD |
0x06 | RESOURCE_PACK |
|
| 0x03 | FINISH_CONFIGURATION |
0x07 | SELECT_KNOWN_PACKS |
Source: v1_20_3to1_20_5/packet/ServerboundConfigurationPackets1_20_5.java:28–35.
How Via bridges it: a 765 server never sends SELECT_KNOWN_PACKS, but a 766 client expects it before REGISTRY_DATA. So Via intercepts the server's clientbound REGISTRY_DATA and synthesises a SELECT_KNOWN_PACKS with an empty list ("no known packs, everything is sent here") immediately before it — forcing the client to take all registry entries from the wire rather than from local packs:
// EntityPacketRewriter1_20_5.java:98–101
protocol.registerClientbound(ClientboundConfigurationPackets1_20_3.REGISTRY_DATA, wrapper -> {
final PacketWrapper knownPacksPacket = wrapper.create(ClientboundConfigurationPackets1_20_5.SELECT_KNOWN_PACKS);
knownPacksPacket.write(Types.VAR_INT, 0); // No known packs, everything is sent here
knownPacksPacket.send(Protocol1_20_3To1_20_5.class);
…
Conversely the client's serverbound SELECT_KNOWN_PACKS reply is cancelled (the old server wouldn't understand it). Source: Protocol1_20_3To1_20_5.java:258. The same REGISTRY_DATA handler also splits the single monolithic registry blob into per-registry packets and injects extra registries (wolf variants, banner patterns) that 1.20.5 expects but 1.20.3 didn't send — EntityPacketRewriter1_20_5.java:98–230.
See
../06-configuration.md§4 for the topical Known-Packs treatment and how Velocity uses the Known-Packs boundary to bridge registries on backend switches.
Other 766 changes (from source)
- Cookies + transfer —
STORE_COOKIE/COOKIE_REQUEST/COOKIE_RESPONSEandTRANSFERpackets (server can ask a client to connect elsewhere, carrying cookie state up to 5 KiB) (wiki 1.20.5, fetched 2026-06-19). Via simply cancels the serverbound cookie responses downward —Protocol1_20_3To1_20_5.java:256–259. CHAT_COMMANDsplit — signed vs unsigned:CHAT_COMMAND_SIGNED(mapped to oldCHAT_COMMAND) is separate from the new unsignedCHAT_COMMAND([wiki 1.20.5]; source:139,175). Chat-signature handling moved behind asecureChatEnforcedflag that now arrives inSERVER_DATAand is replayed viaAcknowledgedMessagesStorage(:106–198, 263–276).- Login
HELLOgained a trailingAuthenticateboolean (:99–104);LOGIN_FINISHEDgained a trailing strict-error-handling boolean (:202–207). - Strict error handling — invalid packet data now disconnects the client by default; Via exposes
viaversion.strict-error-handling1_20_5to opt out for modded servers (:74–76). Matches the wiki's "invalid packet data now causes client disconnection (opt-out for modded)". DEBUG_SAMPLE_SUBSCRIPTIONnew serverbound packet (cancelled downward) —:260.
1.20.6 shares protocol 766 with 1.20.5 (a stability hotfix); no separate ViaVersion package. data/pc/1.20.6/version.json → {"version":766,"minecraftVersion":"1.20.6"}.
ViaVersion git log for v1_20_3to1_20_5 (selected):
| Commit | Subject |
|---|---|
a09fa97f4 |
Send new entries for fully missing registries |
b3d560bd3 |
Track tags for adventure-mode predicates again in 1.20.3→1.20.5 |
a66dbf77a |
Fix profile with multiple properties of the same key in 1.20.5→1.20.3 (#4909) |
31f257fee |
Register empty handlers for transient data components in 1.20.5→1.20.3 |
c267a754e |
Always send extra attributes in 1.20.5 (#4742) |
7657a59b0 |
Add step_height and version-dependent interaction-range 1.20.5 attributes (#4741) |
b54a87f46 |
Re-shuffle mapping files to reduce size, include sound identifiers |
e63c806d6 |
Fix custom potion effects translation in 1.20.3→1.20.5 (#4858) |
Proxy / forwarding & translation impact
The Configuration state is non-optional for proxies (764+)
This is the load-bearing takeaway of the whole 1.20 line. Any proxy that wants to support 1.20.2+ clients MUST implement the Configuration state. It is not a passthrough nicety — the connection genuinely sits in a distinct state between Login and Play, with its own packet ID space, its own FINISH_CONFIGURATION boundary, and a server-initiated re-configuration loop (START_CONFIGURATION Play→Config and back). A proxy must:
- Recognise the Login Acknowledged packet as the end of Login and switch its own state tracking to Configuration (not Play).
- Track which Configuration-state packet IDs apply (the tables above) — they are not the Play IDs.
- Bridge backend server switches through Configuration: on a backend swap, Velocity/BungeeCord push the player back into Configuration, swap the registries/tags, then re-finish into Play. (Velocity's bridge logic is covered in
../06-configuration.md§5; lifecycle in../02-connection-lifecycle.md§6–7.)
ViaVersion, which is a translation layer rather than a state-aware proxy, has to go further still: when the backend speaks 763 (no Configuration), it fabricates the entire Configuration exchange toward the client — queueing Play packets from the old server, synthesising REGISTRY_DATA + tags + FINISH_CONFIGURATION, replaying the client's settings, and bounding the queue. The four-value BridgePhase state machine (ConfigurationState.java:167–169) is the canonical reference implementation of "fake a Configuration state for a server that doesn't have one."
Structured-data must be downgraded to NBT (766↔765)
For item stacks, a proxy/translation layer bridging a 766 server to a ≤765 client must convert the typed structured-component map back to the legacy tag NBT compound (and the reverse when going up). This is lossy/complex enough that ViaVersion devotes an entire class to it (StructuredDataConverter.java) plus per-version data tables for attributes, enchantments, potions, banner patterns, map decorations, armor-trim materials/patterns, instruments, and max-stack-size (v1_20_3to1_20_5/data/*). A proxy that only forwards raw bytes will hand a 765 client component-format items it cannot parse. The armor-trim registries in particular must be tracked from REGISTRY_DATA (or fall back to the vanilla defaults in ArmorTrimStorage) so trim indices resolve correctly.
Known Packs must be answered, even if trivially (766)
A 766 client will wait for SELECT_KNOWN_PACKS before finalising its registries. A proxy bridging from a server that doesn't send it must synthesise an empty Known-Packs selection (as ViaVersion does at EntityPacketRewriter1_20_5.java:98–101) so the client falls back to taking all registry data off the wire. Failing to do so leaves the client stuck in Configuration.
Summary table — what changed per bump
| Protocol | Release(s) | Headline wire change | New connection mechanic | Proxy must… |
|---|---|---|---|---|
| 763 | 1.20 / 1.20.1 | Content remap only (reuses 1.19.4 enums) | — | Refresh block/item/entity ID maps; drop combat-packet leading entity-ID |
| 764 | 1.20.2 | Configuration state inserted Login→Play; registry/tags/features/pack move there | Configuration state; Login Acknowledged ends Login; Finish Configuration enters Play; re-configuration loop | Implement Configuration state; recognise Login Acknowledged; bridge backend switches through Config |
| 765 | 1.20.3 / 1.20.4 | Components serialised as NBT not JSON; multi-pack push/pop; score reset/number-format | — | Convert JSON↔NBT components; map single resource pack ↔ push/pop with UUIDs |
| 766 | 1.20.5 / 1.20.6 | Structured item components replace tag NBT; cookies/transfer; signed-command split; strict errors |
Known Packs handshake in Configuration; cookies persist across transfer | Downgrade structured components ↔ NBT; answer Known Packs (empty selection ok); handle cookies/transfer |
Verification notes
- 1.20.1 release date (2023-06-12): CONFIRMED — minecraft.wiki/w/Java_Edition_1.20.1 infobox, fetched 2026-06-19. Protocol 763 confirmed from minecraft-data
version.json. - 1.20.6 release date (2024-04-29): CONFIRMED — minecraft.wiki/w/Java_Edition_1.20.6 infobox, fetched 2026-06-19. Protocol 766 confirmed from minecraft-data
version.json. - 1.20.5 structured-component count (~60 keys): The
onMappingDataLoaded()filler chain inProtocol1_20_3To1_20_5.java:282–302makes 53 explicit.add(StructuredDataKey.*)calls, plus.add(VersionedTypes.V1_20_5.structuredDataKeys().keys())which adds 3 more (container,chargedProjectiles,bundleContentsfromStructuredDataKeys1_20_5.java) — total 56 keys in ViaVersion's registry. The "~60" approximation is slightly high; corrected to 56. (Mojang's canonical count may differ if any keys are omitted from ViaVersion's tracking; the ViaVersion count is the best available source here.)
Sources
- minecraft.wiki release articles (all fetched 2026-06-19): 1.20, 1.20.2, 1.20.3, 1.20.4, 1.20.5.
- minecraft.wiki protocol pages (accessed 2026-06-19): Registry Data, Registries.
- ViaVersion source
/tmp/mcproto-refs/ViaVersion/— packagesv1_19_4to1_20,v1_20to1_20_2,v1_20_2to1_20_3,v1_20_3to1_20_5, plusprotocols/base/login enums andapi/.../minecraft/data/StructuredDataKey.java. Commits viagit -C /tmp/mcproto-refs/ViaVersion log --oneline -- <package>. - minecraft-data
/tmp/mcproto-refs/minecraft-data/data/pc/—version.json(protocol numbers) andprotocol.json(state sets / packet counts) for 1.19.4, 1.20, 1.20.1–1.20.6. - Cross-references:
../02-connection-lifecycle.md,../06-configuration.md,INDEX.md,../07-version-differences.md.