# Minecraft Java Edition 1.7 — Protocol Deep-Dive > **Range floor.** This line defines the oldest protocol the modern toolchain > targets. Every proxy, translator, and compatibility layer that claims "1.7+" > speaks the protocol described here. --- ## Header | Sub-release range | Protocol version | Release dates | Cross-compatible | |---|---|---|---| | 1.7.2 | 4 | 2013-10-25 | — | | 1.7.3 | 4 | 2013-10-26 | yes, with 1.7.2 | | 1.7.4 | 4 | 2013-12-09 | yes, with 1.7.2–3 | | 1.7.5 | 4 | 2014-02-26 | yes, with 1.7.2–4 | | 1.7.6 | **5** | 2014-04-09 | **incompatible** with 1.7.2–5 | | 1.7.7 | **5** | 2014-04-10 | yes, with 1.7.6 | | 1.7.8 | **5** | 2014-06-16 | yes, with 1.7.6–7 | | 1.7.9 | **5** | 2014-06-16 | yes, with 1.7.6–8 | | 1.7.10 | **5** | 2014-06-26 | yes, with 1.7.6–9 | Sources: - minecraft.wiki `/w/Java_Edition_1.7.2` (fetched 2026-06-19) — release date, protocol 4, Netty rewrite - minecraft.wiki `/w/Java_Edition_1.7.6` (fetched 2026-06-19) — release date 2014-04-09, protocol 5 confirmed - minecraft.wiki `/w/Java_Edition_1.7.10` (fetched 2026-06-19) — release date 2014-06-26, protocol 5, compatibility note - `/tmp/mcproto-refs/minecraft-data/data/pc/1.7/version.json` — `{"version":5,"minecraftVersion":"1.7.10","majorVersion":"1.7"}` The protocol-version page on minecraft.wiki (fetched 2026-06-19) listed all of 1.7.2–1.7.10 as protocol 4; the per-version pages for 1.7.6 and 1.7.10 individually confirm protocol 5, and the minecraft-data canonical record for `1.7.10` is version 5. The per-version pages are the higher-fidelity source; the exact boundary (whether 1.7.2–1.7.5 = 4 and 1.7.6–1.7.10 = 5 is consistent with the protocol-version list being incomplete/erroneous for the 1.7 range). --- ## 1. Headline changes — the Netty rewrite 1.7.2 ("The Update that Changed the World", released 2013-10-25) is not just a gameplay release — it is the **architectural reset** for the entire modern Minecraft protocol. Everything that followed is a delta on top of what 1.7.2 established. ### 1.1 Netty and length-prefixed VarInt framing The network layer was **completely rewritten** to use [Netty](https://netty.io/), replacing the old hand-rolled byte-stream code. The visible wire change: ``` [length: VarInt] [packet-id: VarInt] [fields...] ``` Before 1.7, each packet began with a fixed-width packet-ID byte; the receiver had to know the exact byte length of every packet to know where the next one started. After 1.7, **every packet is prefixed by its own byte length as a VarInt**, making the stream self-delimiting regardless of packet content. The minecraft.wiki 1.7.2 release article confirms "there is now a packet length header" (fetched 2026-06-19). The `string` type likewise moved to VarInt-length prefix (see `protocol.json` `types.string` entry, `/tmp/mcproto-refs/minecraft-data/data/pc/1.7/protocol.json`). ### 1.2 State machine: Handshake / Status / Login / Play Before 1.7, the connection had two informal phases (pre-login and in-game). 1.7.2 formalised this as **four explicit states**, each with its own packet namespace and numeric IDs: | State | Initiated by | |---|---| | **Handshaking** | Always the first state; client sends one packet (0x00 Set Protocol) then transitions | | **Status** (next\_state = 1) | Server List Ping path | | **Login** (next\_state = 2) | Authentication + encryption setup path | | **Play** | After Login Success; all gameplay traffic | Packet IDs are **per-state**. `0x00` in Status means something completely different from `0x00` in Login. The pre-1.7 protocol had a flat namespace where a single byte identified every packet across all phases — this is the deepest structural break from the legacy protocol. ### 1.3 JSON chat Chat messages on the wire became JSON-encoded `Text` component strings (source: minecraft.wiki 1.7.2 article — "/tellraw enables JSON formatted messages"; fetched 2026-06-19). The `packet_chat` payload (both S→C and C→S) is a VarInt-length-prefixed `string` carrying JSON. This is a breaking change from the old raw-string chat packet. ### 1.4 New Server List Ping (SLP) The Server List Ping was redesigned. The new flow uses the **Status state**: 1. Client sends Handshake (0x00) with `next_state = 1`. 2. Client sends Status Request (0x00, no fields). 3. Server responds with Status Response (0x00) carrying a JSON string: `{"version":{"name":"...","protocol":N},"players":{...},"description":{...},"favicon":"data:image/png;base64,..."}` 4. Client sends Ping (0x01, i64 timestamp); server echoes it back. The old "legacy SLP" (0xFE magic byte) is preserved as `packet_legacy_server_list_ping` in the Handshake state for pre-1.7 client compatibility (see `protocol.json` handshaking.toServer mappings). Server icons in the server list (`favicon` field as a base64 PNG) are new in 1.7.2 (minecraft.wiki 1.7.2; fetched 2026-06-19). ### 1.5 Packet renumbering All packet IDs were renumbered. The pre-1.7 flat namespace had IDs like 0x01 (Login Request), 0x02 (Handshake), 0xFF (Kick Disconnect) scattered across 256 possible values. In 1.7, the Play state alone starts at 0x00 and counts up sequentially within the state; 65 unique packet types were defined (41 S→C + 24 C→S in the 1.7.10 data — see §3). --- ## 2. Protocol 4 → Protocol 5 delta (1.7.6) The bump from protocol 4 (1.7.2–1.7.5) to protocol 5 (1.7.6–1.7.10) coincided with a **skin system overhaul** in 1.7.6 (released 2014-04-09; minecraft.wiki `/w/Java_Edition_1.7.6`, fetched 2026-06-19). Key changes at the wire level: - **Signed skin URLs**: Skins and capes moved from a single centralised skin server to per-player signed URLs distributed through session servers. The `packet_named_entity_spawn` (Play 0x0C S→C) already carried a `data` array of property `{name, value, signature}` triples in the minecraft-data 1.7 schema — this is the textures property bag that carries the signed skin URL. whether this field was added in 1.7.2 or specifically in 1.7.6 (it is present in the 1.7.10 protocol.json which covers protocol 5). - **Name-change infrastructure**: Server-side preparation for player name changing (actual service launched 2015-02-04). No packet-schema change, but UUID-keyed identity was reinforced. - **Per-server resource pack option**: `packet_resource_pack_send` exact packet ID / whether this is a new Play packet in protocol 5 or re-uses an existing custom\_payload channel. - The 1.7.6 client was incompatible with 1.7.2–1.7.5 servers (confirmed by minecraft.wiki 1.7.6; fetched 2026-06-19), making the protocol-5 boundary hard. The subsequent 1.7.7–1.7.10 releases did not change the protocol number; they were bug-fix and stability releases (1.7.6 had a crash that was fixed the next day in 1.7.7). --- ## 3. Packet inventory (protocol 5 / 1.7.10) Source: `/tmp/mcproto-refs/minecraft-data/data/pc/1.7/protocol.json` ### 3.1 Handshaking state | ID | Direction | Name | Key fields | |---|---|---|---| | 0x00 | C→S | Set Protocol | `protocolVersion` (VarInt), `serverHost` (string), `serverPort` (u16), `nextState` (VarInt: 1=Status, 2=Login) | | 0xFE | C→S | Legacy Server List Ping | `payload` (u8, always 0x01) — pre-1.7 compat | No S→C packets in this state. ### 3.2 Status state | ID | Direction | Name | Key fields | |---|---|---|---| | 0x00 | C→S | Ping Start | (no fields) | | 0x01 | C→S | Ping | `time` (i64) | | 0x00 | S→C | Server Info | `response` (string — JSON payload) | | 0x01 | S→C | Ping | `time` (i64 echo) | ### 3.3 Login state | ID | Direction | Name | Key fields | |---|---|---|---| | 0x00 | C→S | Login Start | `username` (string) | | 0x01 | C→S | Encryption Begin | `sharedSecret` (i16-length buffer), `verifyToken` (i16-length buffer) | | 0x00 | S→C | Disconnect | `reason` (string — JSON chat) | | 0x01 | S→C | Encryption Request | `serverId` (string), `publicKey` (i16-length buffer), `verifyToken` (i16-length buffer) | | 0x02 | S→C | Login Success | `uuid` (string), `username` (string) | Notes: - No Login Compression packet (0x03) — that is a 1.8 addition. - `publicKey` and `verifyToken` use **i16** (signed 16-bit) as the length prefix, not VarInt; this is a known 1.7 quirk that 1.8 also retains exact format change in 1.8 if any. - `uuid` in Login Success is transmitted as a **string** (hyphenated UUID text), not as two i64 fields — that encoding came later. ### 3.4 Play state — Server→Client (0x00–0x40) | ID | Name | Key fields / notes | |---|---|---| | 0x00 | Keep Alive | `keepAliveId` (i32) | | 0x01 | Login (Join Game) | `entityId` (i32), `gameMode` (u8), `dimension` (i8), `difficulty` (u8), `maxPlayers` (u8), `levelType` (string) | | 0x02 | Chat Message | `message` (string — JSON) | | 0x03 | Update Time | `age` (i64), `time` (i64) | | 0x04 | Entity Equipment | `entityId` (i32), `slot` (i16), `item` (Slot) | | 0x05 | Spawn Position | `location` (position\_iii: 3×i32) | | 0x06 | Update Health | `health` (f32), `food` (i16), `foodSaturation` (f32) | | 0x07 | Respawn | `dimension` (i32), `difficulty` (u8), `gamemode` (u8), `levelType` (string) | | 0x08 | Player Position And Look | `x/y/z` (f64), `yaw/pitch` (f32), `onGround` (bool) | | 0x09 | Held Item Change | `slot` (i8) | | 0x0A | Use Bed | `entityId` (i32), `location` (position\_ibi: i32/u8/i32) | | 0x0B | Animation | `entityId` (VarInt), `animation` (u8) | | 0x0C | Spawn Named Entity | `entityId` (VarInt), `playerUUID` (string), `playerName` (string), `data` (array of {name,value,signature} property triples), `x/y/z` (i32), `yaw/pitch` (i8), `currentItem` (i16), `metadata` | | 0x0D | Collect Item | `collectedEntityId` (i32), `collectorEntityId` (i32) | | 0x0E | Spawn Object | `entityId` (VarInt), `type` (i8), `x/y/z` (i32), `pitch/yaw` (i8), `objectData` (i32 + conditional i16×3 velocity) | | 0x0F | Spawn Mob | `entityId` (VarInt), `type` (u8), `x/y/z` (i32), `yaw/pitch/headPitch` (i8), `velocity` (vec3i16), `metadata` | | 0x10 | Spawn Painting | `entityId` (VarInt), `title` (string), `location` (position\_iii), `direction` (i32) | | 0x11 | Spawn Experience Orb | `entityId` (VarInt), `x/y/z` (i32), `count` (i16) | | 0x12 | Entity Velocity | `entityId` (i32), `velocity` (vec3i16) | | 0x13 | Destroy Entities | `entityIds` (i8-count array of i32) | | 0x14 | Entity | `entityId` (i32) — no-op movement | | 0x15 | Entity Relative Move | `entityId` (i32), `dX/dY/dZ` (i8) | | 0x16 | Entity Look | `entityId` (i32), `yaw/pitch` (i8) | | 0x17 | Entity Look And Relative Move | `entityId` (i32), `dX/dY/dZ` (i8), `yaw/pitch` (i8) | | 0x18 | Entity Teleport | `entityId` (i32), `x/y/z` (i32), `yaw/pitch` (i8) | | 0x19 | Entity Head Look | `entityId` (i32), `headYaw` (i8) | | 0x1A | Entity Status | `entityId` (i32), `entityStatus` (i8) | | 0x1B | Attach Entity | `entityId` (i32), `vehicleId` (i32), `leash` (bool) | | 0x1C | Entity Metadata | `entityId` (i32), `metadata` (entityMetadata) | | 0x1D | Entity Effect | `entityId` (i32), `effectId/amplifier` (i8), `duration` (i16) | | 0x1E | Remove Entity Effect | `entityId` (i32), `effectId` (i8) | | 0x1F | Set Experience | `experienceBar` (f32), `level/totalExperience` (i16) | | 0x20 | Entity Properties | `entityId` (i32), `properties` (i32-count array of {key:string, value:f64, modifiers:[{uuid,amount:f64,operation:i8}]}) | | 0x21 | Chunk Data | `x/z` (i32), `groundUp` (bool), `bitMap/addBitMap` (u16), `compressedChunkData` (i32-length buffer) | | 0x22 | Multi Block Change | `chunkX/chunkZ` (i32), `recordCount` (i16), `dataLength` (i32), `records` (array of {metadata:4bit, blockId:12bit, y:u8, z:4bit, x:4bit}) | | 0x23 | Block Change | `location` (position\_ibi), `type` (VarInt), `metadata` (u8) | | 0x24 | Block Action | `location` (position\_isi: i32/i16/i32), `byte1/byte2` (u8), `blockId` (VarInt) | | 0x25 | Block Break Animation | `entityId` (VarInt), `location` (position\_iii), `destroyStage` (i8) | | 0x26 | Map Chunk Bulk | `chunkColumnCount` (i16), `dataLength` (i32), `skyLightSent` (bool), `compressedChunkData`, `meta` array of {x/z:i32, bitMap/addBitMap:u16} | | 0x27 | Explosion | `x/y/z/radius` (f32), `affectedBlockOffsets` (i32-count array of i8×3), `playerMotionX/Y/Z` (f32) | | 0x28 | Effect (World Event) | `effectId` (i32), `location` (position\_ibi), `data` (i32), `global` (bool) | | 0x29 | Named Sound Effect | `soundName` (string), `x/y/z` (i32), `volume` (f32), `pitch` (u8) | | 0x2A | World Particles | `particleName` (string), `x/y/z/offsetX/Y/Z/particleData` (f32), `particles` (i32) | | 0x2B | Game State Change | `reason` (u8), `gameMode` (f32) | | 0x2C | Spawn Global Entity (Weather) | `entityId` (VarInt), `type` (i8), `x/y/z` (i32) | | 0x2D | Open Window | `windowId` (u8), `inventoryType` (u8), `windowTitle` (string), `slotCount` (u8), `useProvidedTitle` (bool), `entityId` (i32, only for type 11) | | 0x2E | Close Window | `windowId` (u8) | | 0x2F | Set Slot | `windowId` (i8), `slot` (i16), `item` (Slot) | | 0x30 | Window Items | `windowId` (u8), `items` (i16-count Slot array) | | 0x31 | Window Property | `windowId` (u8), `property/value` (i16) | | 0x32 | Confirm Transaction | `windowId` (u8), `action` (i16), `accepted` (bool) | | 0x33 | Update Sign | `location` (position\_isi), `text1–4` (string×4) | | 0x34 | Maps | `itemDamage` (VarInt), `data` (i16-length buffer) | | 0x35 | Update Block Entity | `location` (position\_isi), `action` (u8), `nbtData` (compressedNbt) | | 0x36 | Open Sign Editor | `location` (position\_iii) | | 0x37 | Statistics | `entries` (VarInt-count array of {name:string, value:VarInt}) | | 0x38 | Player List Item | `playerName` (string), `online` (bool), `ping` (i16) | | 0x39 | Player Abilities | `flags` (i8), `flyingSpeed/walkingSpeed` (f32) | | 0x3A | Tab-Complete | `matches` (VarInt-count string array) | | 0x3B | Scoreboard Objective | `name` (string), `displayText` (string), `action` (i8) | | 0x3C | Update Score | `itemName` (string), `action` (i8), `scoreName` (string, absent if action=1), `value` (i32, absent if action=1) | | 0x3D | Display Scoreboard | `position` (i8), `name` (string) | | 0x3E | Teams | `team` (string), `mode` (i8), conditional fields for name/prefix/suffix/friendlyFire/players depending on mode | | 0x3F | Plugin Message | `channel` (string), `data` (i16-length buffer) | | 0x40 | Disconnect | `reason` (string — JSON chat) | **Total S→C Play: 65 packet types (0x00–0x40).** ### 3.5 Play state — Client→Server (0x00–0x17) | ID | Name | Key fields / notes | |---|---|---| | 0x00 | Keep Alive | `keepAliveId` (i32) | | 0x01 | Chat Message | `message` (string) | | 0x02 | Use Entity | `target` (i32), `mouse` (i8: 0=interact, 1=attack, 2=interact\_at), `x/y/z` (f32, only if mouse=2) | | 0x03 | Player (ground status only) | `onGround` (bool) | | 0x04 | Player Position | `x/stance/y/z` (f64), `onGround` (bool) — note `stance` field (eye height) | | 0x05 | Player Look | `yaw/pitch` (f32), `onGround` (bool) | | 0x06 | Player Position And Look | `x/stance/y/z` (f64), `yaw/pitch` (f32), `onGround` (bool) | | 0x07 | Player Digging | `status` (i8), `location` (position\_ibi: i32/u8/i32), `face` (i8) | | 0x08 | Player Block Placement | `location` (position\_ibi), `direction` (i8), `heldItem` (Slot), `cursorX/Y/Z` (i8) | | 0x09 | Held Item Change | `slotId` (i16) | | 0x0A | Animation | `entityId` (i32), `animation` (i8) | | 0x0B | Entity Action | `entityId` (i32), `actionId` (i8), `jumpBoost` (i32) | | 0x0C | Steer Vehicle | `sideways/forward` (f32), `jump/unmount` (bool) | | 0x0D | Close Window | `windowId` (u8) | | 0x0E | Click Window | `windowId` (i8), `slot` (i16), `mouseButton` (i8), `action` (i16), `mode` (i8), `item` (Slot) | | 0x0F | Confirm Transaction | `windowId` (i8), `action` (i16), `accepted` (bool) | | 0x10 | Creative Inventory Action | `slot` (i16), `item` (Slot) | | 0x11 | Enchant Item | `windowId/enchantment` (i8) | | 0x12 | Update Sign | `location` (position\_isi), `text1–4` (string×4) | | 0x13 | Player Abilities | `flags` (i8), `flyingSpeed/walkingSpeed` (f32) | | 0x14 | Tab-Complete | `text` (string) | | 0x15 | Client Settings | `locale` (string), `viewDistance` (i8), `chatFlags` (i8), `chatColors` (bool), `difficulty` (u8), `showCape` (bool) | | 0x16 | Client Status | `payload` (i8: 0=perform\_respawn, 1=request\_stats, 2=open\_inventory) | | 0x17 | Plugin Message | `channel` (string), `data` (i16-length buffer) | **Total C→S Play: 24 packet types (0x00–0x17).** --- ## 4. Notable data-type details ### Slot (item stack) ``` blockId: i16 // -1 = empty (null slot) if blockId != -1: itemCount: i8 itemDamage: i16 nbtData: compressedNbt // zlib-deflate compressed NBT, or 0x0000 if absent ``` Source: `protocol.json` `types.slot`. ### Entity Metadata (1.7 wire format) Each entry is a byte whose high 3 bits are the type and low 5 bits are the key (`entityMetadataItem` bitfield). Types: 0=i8, 1=i16, 2=i32, 3=f32, 4=string, 5=Slot, 6=int-triple (x/y/z i32s), 7=float-triple (pitch/yaw/roll). The loop terminates on byte value `0x7F` (127). Source: `protocol.json` `types.entityMetadataLoop` / `entityMetadataItem`. ### String VarInt-prefixed UTF-8 (`pstring` with `countType: varint`). No null terminator. Source: `protocol.json` `types.string`. ### Position variants (1.7-era) No packed 64-bit Position type (that is a 1.8 addition). 1.7 uses explicit multi-field combinations: - `position_iii` — x:i32, y:i32, z:i32 - `position_isi` — x:i32, y:i16, z:i32 - `position_ibi` — x:i32, y:u8, z:i32 Source: `protocol.json` `types.position_iii` / `position_isi` / `position_ibi`. ### Player Position — `stance` field The client-to-server Player Position packets include a `stance` field (f64) representing the player's eye height offset above their feet (typically `y + 1.62`). The server uses this for hitbox calculations. The `stance` field was **removed in 1.8** when the server began computing it server-side. exact removal version. ### Plugin Message channel data length In 1.7, `packet_custom_payload` (both directions) uses an **i16** length prefix for the `data` buffer, not VarInt. Source: `protocol.json` `types` for `packet_custom_payload.data` in both play.toClient and play.toServer. --- ## 5. Wire framing summary ``` Packet (1.7+): [length: VarInt] ← total byte count of the rest of this packet [id: VarInt] ← state-scoped packet ID (always 1 byte in 1.7 range: ≤ 0x7F) [fields: ...] String: [byteLength: VarInt] [utf8 bytes: ...] No compression in 1.7. Login Compression (0x03) was added in 1.8. No encryption wrapper at the framing level — AES/CFB8 is applied per-byte over the already-framed stream after Login Success. ``` --- ## 6. Why 1.7 is the "oldest viable baseline" Modern proxies (Velocity, BungeeCord, mc-router) target 1.7.10 as their floor because: 1. **State machine is present** — Handshake/Status/Login/Play states let a proxy correctly intercept the login sequence to inject forwarding data (BungeeCord IP-forwarding via serverAddress overload, Velocity login-plugin-message). The pre-1.7 protocol has no Login state; there is no clean injection point. 2. **VarInt framing is self-delimiting** — a proxy can pipeline and inspect packets without a static byte-length lookup table. The pre-1.7 format required per-packet hardcoded lengths. 3. **JSON chat and SLP** — modern server-list-ping tools and anti-bot layers depend on the JSON SLP format. The old 0xFE SLP returns a `§`-delimited string that is not JSON. 4. **UUIDs in Login Success** — the Login Success packet carries a UUID string, enabling player identity to be keyed on UUID rather than name. BungeeCord forwarding injects UUID + skin properties into the Handshake `serverAddress` or (for Velocity) via a Login Plugin Request; both require the Login state. 5. **Plugin Message channel** — the `custom_payload` packet (C→S 0x17, S→C 0x3F) provides a channel-namespaced side-band that Forge uses for FML handshake (`FML|HS`) and that proxies use for capability negotiation. The pre-1.7 legacy protocol (Minecraft ≤1.6.4, protocol ≤78) required a completely different code path. Proxies that support it (e.g. BungeeCord legacy mode) maintain a separate parser. The consensus since ~2015 is that 1.7.10 is the floor: old enough to be "legacy" but new enough to share the modern framing and state machine. --- ## 7. Proxy / translation impact ### ViaVersion floor [ViaVersion](https://github.com/ViaVersion/ViaVersion) handles protocol translation for **1.8 and above only**. Its lowest supported input protocol is 1.8 (protocol 47). There is no `protocols/v4to5/` or `protocols/v5to47/` package in ViaVersion; the 1.7 → 1.8 gap is explicitly out of scope for the ViaVersion project. ### Bridging below 1.8: ViaLegacy and ViaRewind To support 1.7.x clients on modern servers, the translation stack requires: - **ViaLegacy** — handles protocol ≤ 1.7.10 (protocol ≤ 5) → modern. It re-implements the full 1.7 state machine, framing parser, and all packet transforms including the `stance`-removal, position packing, and the 1.7 entity-metadata format. - **ViaRewind** — handles 1.7 and 1.8 clients against 1.9+ servers. Sits on top of ViaVersion. Neither ViaLegacy nor ViaRewind is cloned in this repo's ref set; they are referenced by name only. See their respective GitHub repos for implementation detail. ### What a 1.7-aware proxy must handle | Concern | 1.7 detail | Modern delta | |---|---|---| | Framing | VarInt length + VarInt ID | Same in 1.8+ | | Compression | None | Login Compression added in 1.8 (0x03 Login packet) | | Encryption | RSA-1024 key exchange, AES/CFB8 shared secret, verifyToken — same mechanism as 1.8+ | i16 length prefix on `publicKey`/`verifyToken` buffers (vs VarInt in later versions ) | | Entity IDs in Play | Mix of i32 and VarInt (e.g. `entity_destroy` uses i32 array, `animation` uses VarInt) | Standardised to VarInt in 1.8 | | Position encoding | Three separate i32/i16/u8 fields | Packed 64-bit Position type added in 1.8 | | Slot NBT | `compressedNbt` (zlib-compressed) | Uncompressed NBT after 1.8 | | Player stance | Client sends `stance` (f64) in position packets | Removed in 1.8 | | Login Success UUID | String (hyphenated text) | Two i64 fields in 1.16+ | | Player List Item | Simple {name, online, ping} per-packet | Replaced with action-tagged packet in 1.8 | | Chunk format | zlib-compressed section bitmask + addBitMap; separate MapChunkBulk for multi-chunk | Restructured in 1.9 | ### BungeeCord / Velocity forwarding with 1.7 clients BungeeCord legacy forwarding (IP injection into Handshake `serverAddress`) works unchanged with 1.7 clients because the `packet_set_protocol` (Handshake 0x00) field is a plain `string` that BungeeCord appends to with `\0` delimiters. Velocity modern forwarding uses a **Login Plugin Request / Response** exchange in the Login state (added in 1.13). 1.7 clients do not support Login Plugin messages; a Velocity-in-modern-mode proxy cannot forward 1.7 clients without a ViaLegacy bridge that intercepts the Login Plugin exchange on behalf of the 1.7 client. exact Velocity behaviour when a 1.7 client hits a modern-forwarding backend. --- ## 8. Sources summary | Source | Used for | |---|---| | minecraft.wiki `/w/Java_Edition_1.7.2` (fetched 2026-06-19) | Release date, Netty rewrite, JSON chat, SLP, packet length header | | minecraft.wiki `/w/Java_Edition_1.7.6` (fetched 2026-06-19) | Protocol 5 confirmation, release date 2014-04-09, skin overhaul, incompatibility note | | minecraft.wiki `/w/Java_Edition_1.7.10` (fetched 2026-06-19) | Protocol 5 confirmation, release date 2014-06-26, 1.7.6–9 compatibility note, Log4j note | | `/tmp/mcproto-refs/minecraft-data/data/pc/1.7/version.json` | Canonical: `{"version":5,"minecraftVersion":"1.7.10","majorVersion":"1.7"}` | | `/tmp/mcproto-refs/minecraft-data/data/pc/1.7/protocol.json` | Full packet inventory by state: all IDs, field names, data types |