# 1.12.x — World of Color Update **Protocols:** 335 (1.12), 338 (1.12.1), 340 (1.12.2) **Release dates:** 1.12 — 2017-06-07 · 1.12.1 — 2017-08-03 · 1.12.2 — 2017-09-18 **Minecraft wiki release articles:** - (fetched 2026-06-19) - (fetched 2026-06-19) - (fetched 2026-06-19) **Protocol version numbers confirmed:** `ProtocolVersion.java` lines 57-59: `register(335, "1.12")`, `register(338, "1.12.1")`, `register(340, "1.12.2")` — `/tmp/mcproto-refs/ViaVersion/api/src/main/java/com/viaversion/viaversion/api/protocol/version/ProtocolVersion.java`; cross-checked by `/tmp/mcproto-refs/minecraft-data/data/pc/1.12*/version.json`. **ViaVersion packages:** - `v1_11_1to1_12` — `/tmp/mcproto-refs/ViaVersion/common/src/main/java/com/viaversion/viaversion/protocols/v1_11_1to1_12/` - `v1_12to1_12_1` — `…/protocols/v1_12to1_12_1/` - `v1_12_1to1_12_2` — `…/protocols/v1_12_1to1_12_2/` **minecraft-data sources:** `data/pc/1.12/`, `data/pc/1.12.1/`, `data/pc/1.12.2/` — `/tmp/mcproto-refs/minecraft-data/` --- ## Headline changes 1.12 ("World of Color Update") shipped 2017-06-07. The two largest protocol additions are the **recipe book** and the **advancements system**, each requiring a new cluster of packets. The recipe book is a server-driven crafting UI: the client receives a list of unlocked recipe IDs via `Unlock Recipes` (0x30 in 335; shifted to 0x31 in 338) and can request that a recipe's ingredients be placed in a crafting grid via a new serverbound packet. In 1.12 this was `Prepare Crafting Grid` (0x01 SB); the interaction was redesigned in 1.12.1 into `Place Recipe` (0x12 SB, replaces 0x01) and a new server echo `Craft Recipe Response` (0x2B CB). Advancements replace the pre-1.12 achievement system. Rather than a statistics-based one-time trigger, advancements are a DAG of progress nodes stored on the server and pushed to the client. The `Update Advancements` packet (0x4C in 335; 0x4D in 338) carries a full reset flag, a map of advancement keys to display data and criteria, a list of removed advancements, and a progress map. A companion `Select Advancements Tab` (0x36 in 335; 0x37 in 338) drives the tab UI clientbound. The serverbound `Seen Advancements` (0x19 in both) carries the player's tab-open/tab-close action and current tab ID. Gameplay additions visible at the protocol level: parrots (new entity type 105 in `EntityTypes1_12`, `ABSTRACT_SHOULDER_RIDING`); coloured beds (the bed block entity now requires a colour field — ViaVersion injects a fake NBT tag with `color:14` for red into chunk data sent to 1.11 clients, `Protocol1_11_1To1_12.java:94-106`); and 33 new parrot sounds (handled via numeric remapping in `getNewSoundId`, `Protocol1_11_1To1_12.java:147-167`). A `Knowledge Book` item was added (items 235-252 and 453 are unknown to 1.11 servers; ViaVersion replaces them with stone, `ItemPacketRewriter1_12.java:65-72`). --- ## Protocol changes vs 1.11 (335 vs 315/316) Source: diff of `ClientboundPackets1_9_3` (the enum used through 1.11.x) against `ClientboundPackets1_12` and `ServerboundPackets1_9_3` against `ServerboundPackets1_12`; cross-confirmed by `minecraft-data` diff of `data/pc/1.11/protocol.json` vs `data/pc/1.12/protocol.json` which shows +3 CB and +3 SB net-new packets. ### Clientbound play — new packets (1.12 = protocol 335) | ID | Name (ViaVersion enum / minecraft-data) | Notes | |---|---|---| | `0x30` | `RECIPE` / `unlock_recipes` | Pushes the list of unlocked recipe IDs to the client; controls recipe-book state (open, filter). Fields: action (VarInt), craftingBookOpen (bool), filteringCraftable (bool), recipes1 array (VarInt IDs), optional recipes2 array for "init" action. | | `0x36` | `SELECT_ADVANCEMENTS_TAB` / `select_advancement_tab` | Tells the client which advancement tab to show. Field: optional String tab-id. | | `0x4C` | `UPDATE_ADVANCEMENTS` / `advancements` | Full advancement sync. Fields: reset (bool), advancement-map (key→display+criteria+requirements), list of removed keys, progress-map (key→criterion-timestamps). | All three shift IDs for packets above them: `REMOVE_ENTITIES` moves from 0x30 to 0x31, `SELECT_ADVANCEMENTS_TAB` inserts at 0x36 pushing `SET_BORDER` from 0x36 to 0x37, and `UPDATE_ADVANCEMENTS` appends above `UPDATE_ATTRIBUTES` pushing it to 0x4D and `UPDATE_MOB_EFFECT` to 0x4E. Sources: `ClientboundPackets1_12.java` (full enum with ordinal-derived IDs); `minecraft-data/data/pc/1.12/protocol.json` `.play.toClient`. ### Serverbound play — new packets (1.12 = protocol 335) | ID | Name (ViaVersion enum / minecraft-data) | Notes | |---|---|---| | `0x01` | `CRAFTING_RECIPE_PLACEMENT` / `prepare_crafting_grid` | Client sends items to move into crafting slots. Fields: windowId (u8), actionNumber (u16), array of {item, craftingSlot, playerSlot} for both "return" and "prepare" lists. | | `0x17` | `RECIPE_BOOK_UPDATE` / `crafting_book_data` | Carries two sub-types: type 0 = displayed-recipe (i32 recipe ID); type 1 = crafting-book-open flag + filtering flag (bools). | | `0x19` | `SEEN_ADVANCEMENTS` / `advancement_tab` | Player opened/closed an advancements tab. Fields: action (VarInt: 0=opened tab, 1=closed screen), optional tabId String (only for action 0). | These push `RESOURCE_PACK` from 0x16→0x18, `SET_CARRIED_ITEM` from 0x17→0x1A, `SET_CREATIVE_MODE_SLOT` from 0x18→0x1B, and so on, adding three net slots. Sources: `ServerboundPackets1_12.java`; `minecraft-data/data/pc/1.12/protocol.json` `.play.toServer`. ViaVersion strategy (1.11.1→1.12, `Protocol1_11_1To1_12.java`): - `cancelServerbound(ServerboundPackets1_12.CRAFTING_RECIPE_PLACEMENT)` — drops 0x01 (old server can't handle it) - `cancelServerbound(ServerboundPackets1_12.RECIPE_BOOK_UPDATE)` — drops 0x17 - `cancelServerbound(ServerboundPackets1_12.SEEN_ADVANCEMENTS)` — drops 0x19 - Locale truncation: `CLIENT_INFORMATION` max locale length raised 7→16 in 1.12; ViaVersion truncates back to 7 when downgrading (line 131-134) - Bed chunk injection: injects fake `minecraft:bed` NBT block-entity with `color:14` for every bed block (id 26) in chunk data, allowing old servers (which store no bed colour) to render correctly (lines 94-106) - Sound remapping: 1.12 added 33 parrot sounds + 2 end-portal sounds + block-note sounds + illager sounds; `getNewSoundId` remaps the 1.11 continuous ID space (lines 147-167) ### Packet format notes **`Unlock Recipes` action field:** VarInt with values 0 (init — sends both recipes1 and recipes2 lists, the first being "currently unlocked", the second being "all you've ever unlocked"), 1 (add), 2 (remove). When action = 0 the packet sends two arrays; for 1 or 2 it sends only recipes1. **`Update Advancements` structure:** Each advancement entry carries: parentId (optional String), optional displayData {title String, description String, icon Slot, frameType VarInt, flags VarInt, optional background String, x float, y float}, array of criterion keys, array of requirement arrays (AND of OR). Progress map entries carry criterion key → optional completion timestamp (Long). --- ## Per-patch sub-sections ### 1.12 — protocol 335 (2017-06-07) The initial release. Adds the recipe book, advancements, and all associated packets described above. The three new clientbound and three new serverbound play packets define the 1.12 wire baseline. **ViaVersion package:** `v1_11_1to1_12` **Key commits (git log `-- common/src/main/java/com/viaversion/viaversion/protocols/v1_11_1to1_12/`):** - `8f8f5e72c` — Default rewriter registrations across protocols (includes 1.12 migration) - `721e27eb3` — Rewrite show_item in translation components in 1.11.1->1.12 - `a92e75b8c` — Properly track minecart object entity types in ≤1.12.2 - `e965e9713` — Package/class renames and moves (original package creation) ### 1.12.1 — protocol 338 (2017-08-03) **Net packet change: +1 CB, ±0 SB (swap of one packet).** The recipe-book interaction was redesigned between 1.12 and 1.12.1. The `Prepare Crafting Grid` (0x01 SB) was dropped and replaced by two simpler packets: **New clientbound:** | ID | Name | Notes | |---|---|---| | `0x2B` | `PLACE_GHOST_RECIPE` (`craft_recipe_response` in minecraft-data) | Server echoes back a recipe ID to fill the crafting ghost items in the UI. Fields: windowId (i8), recipe (VarInt). Inserted between `OPEN_SIGN_EDITOR` and `PLAYER_ABILITIES`, shifting all higher IDs by +1 (PLAYER_ABILITIES 0x2B→0x2C, PLAYER_COMBAT 0x2C→0x2D, etc., through UPDATE_MOB_EFFECT 0x4E→0x4F). | **Serverbound change:** | Before (1.12, 335) | After (1.12.1, 338) | Notes | |---|---|---| | `0x01` `CRAFTING_RECIPE_PLACEMENT` (prepare_crafting_grid) | *(removed)* | Complex slot-shuffle packet dropped | | *(absent)* | `0x12` `PLACE_RECIPE` (craft_recipe_request) | Simpler request: just windowId (i8), recipe VarInt, makeAll bool. Client sends this to ask the server to fill the crafting grid with a specific recipe. | `PLACE_RECIPE` slots in at 0x12 between `PADDLE_BOAT` and `PLAYER_ABILITIES`; `RECIPE_BOOK_UPDATE` and `SEEN_ADVANCEMENTS` stay at 0x17 and 0x19. ViaVersion strategy: `Protocol1_12To1_12_1.java` — only `cancelServerbound(ServerboundPackets1_12_1.PLACE_RECIPE)` is registered, i.e. 1.12.1 clients talking to 1.12 servers have their `PLACE_RECIPE` dropped (the old server can't understand it; the UI degrades gracefully). **Other fixes in 1.12.1 (wiki):** security vulnerability in recipe book system (MC-119011); performance fix for `Class.getSimpleName` on large block-entity counts (MC-117087); crafting bug where chat messages moved items into crafting slots (MC-119840). **ViaVersion package:** `v1_12to1_12_1` **Key commits:** - `501f65e21` — Packet and entity type renames (Mojang-mapped name alignment) - `e965e9713` — Package/class renames and moves (original package creation) ### 1.12.2 — protocol 340 (2017-09-18) **Net packet change: 0 CB, 0 SB. One field type change.** The only wire-visible change in 1.12.2 is the **Keep Alive ID type**: both `KEEP_ALIVE` clientbound (0x1F) and `KEEP_ALIVE` serverbound (0x0B) changed their `keepAliveId` field from `VarInt` to `i64` (signed 64-bit little-endian long). Confirmed by: - `Protocol1_12_1To1_12_2.java` lines 34-47: registers explicit mappings for both directions: `map(Types.VAR_INT, Types.LONG)` (CB) and `map(Types.LONG, Types.VAR_INT)` (SB). - `minecraft-data/data/pc/1.12.2/protocol.json`: `packet_keep_alive` CB and SB both show `{name: "keepAliveId", type: "i64"}`, vs VarInt in `data/pc/1.12.1/`. No packets were added or removed. The 80 CB / 33 SB play-packet counts from 1.12.1 are unchanged in 1.12.2 (`minecraft-data` diff: empty). **Why this matters for proxies:** A VarInt keep-alive value was always truncated to the range a VarInt can represent; the server's keep-alive ID generator could now use the full 64-bit space. Any proxy doing keep-alive termination that assumed VarInt must be updated, or it will corrupt the field when the server sends a value > 2^28. ViaVersion: `Protocol1_12_1To1_12_2.java` handles the field-width translation inline without any per-packet enum change; it reuses `ClientboundPackets1_12_1` and `ServerboundPackets1_12_1` unchanged (class declaration line 26). Other 1.12.2 changes (wiki): title screen now shows "Java Edition" subtitle; 12 bug fixes; Log4Shell security note (affects the embedded Log4j in the dedicated server, not the protocol). **ViaVersion package:** `v1_12_1to1_12_2` **Key commits:** - `5286efde1` — Move type instances out of enclosing class (Types refactor) - `e965e9713` — Package/class renames and moves --- ## Why 1.12.2 is a common server baseline 1.12.2 (protocol 340) became the de-facto long-running server version for roughly three years (2017–2020) for two compounding reasons: 1. **The 1.13 "Flattening" was a breaking change.** 1.13 restructured the entire block/item ID space, introduced the text-component registry system, and shifted dozens of packets. The migration cost for modpacks and plugins was enormous. Many server operators held at 1.12.2 rather than upgrade. 2. **Mod ecosystem stability.** Forge support for 1.12.2 matured to a very stable state. The Curse/CurseForge modpack ecosystem had thousands of packs targeting 1.12.2 exclusively. This concentrated a huge portion of the modded playerbase on protocol 340 for years. The combination made 1.12.2 the last version before the Flattening and the most widely deployed Forge version in history. ViaVersion's `v1_12_2to1_13` package is one of the most complex in the codebase precisely because it had to bridge this gap for that entrenched server population. --- ## Proxy and translation impact ### What a ViaVersion-style proxy must do for 1.12.x clients connecting to ≤1.11 servers - **Drop the three new SB packets** that 1.11 servers cannot parse: `CRAFTING_RECIPE_PLACEMENT` (0x01), `RECIPE_BOOK_UPDATE` (0x17), `SEEN_ADVANCEMENTS` (0x19). The client degrades gracefully when it receives no response. - **Remap sound IDs**: 1.12 added 33 parrot sounds, 7 illager sounds, and smaller groups of end-portal and block-note sounds. IDs above each insertion point must be shifted down when forwarding to 1.11 servers. - **Inject fake bed NBT**: 1.12 clients expect a bed block-entity (with `color` field) to accompany every bed block in chunk data; 1.11 servers do not send one. ViaVersion synthesises it from the palette. - **Item replacement**: Items 235-252 and 453 (coloured concrete variants, Knowledge Book) are unknown on 1.11 servers; replace with a safe item (e.g. stone). - **Locale truncation**: `CLIENT_INFORMATION` locale max length is 16 in 1.12 vs 7 in 1.11; truncate to 7 before forwarding. ### What a proxy must do for 1.12.1 clients connecting to 1.12 servers - **Drop `PLACE_RECIPE` (0x12 SB)** — 1.12 servers do not understand it. - **Do not forward `PLACE_GHOST_RECIPE` (0x2B CB)** from 1.12 server to 1.12.1 client — this packet does not exist in 1.12; no action needed on that direction (1.12 server simply never sends it). ### What a proxy must do for 1.12.2 clients connecting to 1.12.1 servers - **Translate Keep Alive ID width** in both directions: `LONG` (i64) ↔ `VAR_INT` on the server side. A 64-bit keep-alive ID from the server must be masked or truncated to VarInt range (max 5 bytes, effectively 28 usable bits) when forwarding to a 1.12.1 server; a VarInt from a 1.12.1 server must be zero-extended to i64 when forwarding to a 1.12.2 client. `Protocol1_12_1To1_12_2.java` does this with `map(Types.VAR_INT, Types.LONG)` (CB) and `map(Types.LONG, Types.VAR_INT)` (SB). ### Connecting ≥1.13 clients to 1.12.2 servers The reverse direction (`v1_12_2to1_13` package) is the large one. 1.13 renamed every block, item, entity, and sound to namespaced strings; abolished numeric IDs for most registries; and overhauled chunk encoding. That is covered in `versions/1.13.md`.