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

34 KiB
Raw Blame History

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.json files and ViaVersion's MappingDataBase(...) constructor arguments in each Protocol*.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.md and ../02-connection-lifecycle.md §5b7 for the topical treatment.
  • Structured item data components (1.20.5 / protocol 766). The free-form tag NBT compound that had ridden along with every item stack since the dawn of the protocol was replaced by typed structured components — a map of minecraft:<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-ID VarInt field was dropped from each (the duration/killer fields shifted). ViaVersion handles this in-place. Source: Protocol1_19_4To1_20.java:5666.
  • 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_20 plus the 1.19.41.20 mapping 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_20 only re-touches existing packets (OPEN_SIGN_EDITOR, SIGN_UPDATE, LEVEL_CHUNK_WITH_LIGHT, LIGHT_UPDATE, SECTION_BLOCKS_UPDATE, UPDATE_RECIPES) — source ItemPacketRewriter1_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):

  1. Server sends Login Success (ClientboundLoginPackets.LOGIN_FINISHED, login ID 0x02; aliased GAME_PROFILE. Source: protocols/base/ClientboundLoginPackets.java:26,32).
  2. Client replies Login Acknowledged (ServerboundLoginPackets.LOGIN_ACKNOWLEDGED, login ID 0x03. Source: protocols/base/ServerboundLoginPackets.java:27) — this is the new packet that ends the Login state.
  3. 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.
  4. 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 stateClientboundConfigurationPackets1_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:2432 (IDs are enum ordinal()).

Serverbound (client → server), Configuration stateServerboundConfigurationPackets1_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:2530.

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:167169
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 sets BridgePhase.PROFILE_SENT and forces the server-side tracked state to PLAY (the old server is about to start sending Play packets). Source: Protocol1_20To1_20_2.java:141144.
  • The client then sends LOGIN_ACKNOWLEDGED. Via cancels it (the old server can't parse it), sets BridgePhase.CONFIGURATION, and flushes any packets it had queued. Source: Protocol1_20To1_20_2.java:146155.
  • 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 in transform(...) at Protocol1_20To1_20_2.java:247327, 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 a REGISTRY_DATA packet built from the dimension registry it captured, replays tags (or an empty UPDATE_TAGS so later protocols can append), optionally re-sends the last resource pack, then sends FINISH_CONFIGURATION and flips server state back to PLAY. Source: Protocol1_20To1_20_2.java:329373.
  • 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:167184 + ConfigurationState.java:171187.
  • The client's Configuration-state CUSTOM_PAYLOAD / KEEP_ALIVE / PONG are mapped to their Play equivalents and queued until the server's listener is in PLAY. Source: Protocol1_20To1_20_2.java:187189.
  • Login HELLO (serverbound name) gained an OPTIONAL_UUID field in 1.20.2; Via converts the plain UUID a 1.20 client sends. Source: Protocol1_20To1_20_2.java:128133.
  • The config-phase serverbound packet queue is bounded (default 1000 packets / 1 MiB) and over-budget connections are disconnected. Source: ConfigurationState.java:3738,8191.

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 ByteVarInt (Protocol1_20To1_20_2.java:123126); CUSTOM_PAYLOAD content sanitised softly (:84,231245); 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:330336
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:229262, 98254.

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,312328. 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 (:297310). 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_SCORE split — a SET_SCORE with action 1 ("reset") becomes a dedicated RESET_SCORE packet; scores gain an optional display component + number-format fields. Source: Protocol1_20_2To1_20_3.java:7997. Number formats (styled / fixed / blank) are new scoreboard display options (wiki 1.20.3).
  • SET_JIGSAW_BLOCK gained selection-priority + placement-priority VarInts (Via strips them downward). Source: :113122.
  • CONTAINER_SLOT_STATE_CHANGED is a new serverbound packet (Via cancels it downward). Source: :77.
  • The Configuration-state packet set is shared with 764 except resource-pack: createPacketTypesProvider() maps ClientboundConfigurationPackets1_20_2ClientboundConfigurationPackets1_20_3. Source: :380388.

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) {  }
// :111115 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:282302 plus 3 from StructuredDataKeys1_20_5container, chargedProjectiles, bundleContents). Source: Protocol1_20_3To1_20_5.java:282302; 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:2658. 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:2438.

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:2835.

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:98101
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:98230.

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 + transferSTORE_COOKIE / COOKIE_REQUEST / COOKIE_RESPONSE and TRANSFER packets (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:256259.
  • CHAT_COMMAND split — signed vs unsigned: CHAT_COMMAND_SIGNED (mapped to old CHAT_COMMAND) is separate from the new unsigned CHAT_COMMAND ([wiki 1.20.5]; source :139,175). Chat-signature handling moved behind a secureChatEnforced flag that now arrives in SERVER_DATA and is replayed via AcknowledgedMessagesStorage (:106198, 263276).
  • Login HELLO gained a trailing Authenticate boolean (:99104); LOGIN_FINISHED gained a trailing strict-error-handling boolean (:202207).
  • Strict error handling — invalid packet data now disconnects the client by default; Via exposes viaversion.strict-error-handling1_20_5 to opt out for modded servers (:7476). Matches the wiki's "invalid packet data now causes client disconnection (opt-out for modded)".
  • DEBUG_SAMPLE_SUBSCRIPTION new 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:

  1. Recognise the Login Acknowledged packet as the end of Login and switch its own state tracking to Configuration (not Play).
  2. Track which Configuration-state packet IDs apply (the tables above) — they are not the Play IDs.
  3. 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 §67.)

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:167169) 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:98101) 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 in Protocol1_20_3To1_20_5.java:282302 makes 53 explicit .add(StructuredDataKey.*) calls, plus .add(VersionedTypes.V1_20_5.structuredDataKeys().keys()) which adds 3 more (container, chargedProjectiles, bundleContents from StructuredDataKeys1_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/ — packages v1_19_4to1_20, v1_20to1_20_2, v1_20_2to1_20_3, v1_20_3to1_20_5, plus protocols/base/ login enums and api/.../minecraft/data/StructuredDataKey.java. Commits via git -C /tmp/mcproto-refs/ViaVersion log --oneline -- <package>.
  • minecraft-data /tmp/mcproto-refs/minecraft-data/data/pc/version.json (protocol numbers) and protocol.json (state sets / packet counts) for 1.19.4, 1.20, 1.20.11.20.6.
  • Cross-references: ../02-connection-lifecycle.md, ../06-configuration.md, INDEX.md, ../07-version-differences.md.