# Minecraft Java Edition Protocol — Wire Data Types > **Scope:** Java Edition wire types only. Version-specific changes are called out inline. > > **Primary sources:** > - `node-minecraft-protocol` v1.66.2 (`/tmp/mcproto-refs/node-minecraft-protocol/`) > - minecraft.wiki/w/Java_Edition_protocol/[Data_types](https://minecraft.wiki/w/Java_Edition_protocol/Data_types) --- ## 1. VarInt Variable-length encoding for **signed 32-bit integers**. 1–5 bytes on the wire. **Encoding rules:** - Take 7 bits of the value (least significant group first). - Set the MSB of each byte to `1` if more bytes follow, `0` on the final byte. - Two's complement for negative numbers — no zigzag encoding — so all negative values use the full 5 bytes. **Encoding table:** | Decimal value | Hex bytes | Byte count | |-----------------------|-----------------------------------|------------| | 0 | `00` | 1 | | 1 | `01` | 1 | | 127 | `7F` | 1 | | 128 | `80 01` | 2 | | 255 | `FF 01` | 2 | | 300 | `AC 02` | 2 | | 25565 | `DD C7 01` | 3 | | 2097151 | `FF FF 7F` | 3 | | 2147483647 | `FF FF FF FF 07` | 5 | | -1 | `FF FF FF FF 0F` | 5 | | -2147483648 | `80 80 80 80 08` | 5 | **Max bytes: 5.** Reading more than 5 bytes for a VarInt is a protocol error. VarInt is used for: packet length, packet ID, string prefix, array length, and most integer fields in the protocol. Source: [minecraft.wiki/w/Java_Edition_protocol/Data_types](https://minecraft.wiki/w/Java_Edition_protocol/Data_types); node-minecraft-protocol delegates VarInt to `protodef` (`src/datatypes/minecraft.js:6`). --- ## 2. VarLong Variable-length encoding for **signed 64-bit integers**. Same algorithm as VarInt, extended to 10 bytes. **Encoding rules:** identical to VarInt but processes all 64 bits. | Decimal value | Hex bytes | Byte count | |-----------------------------|---------------------------------------------------|------------| | 0 | `00` | 1 | | 9223372036854775807 | `FF FF FF FF FF FF FF FF 7F` | 9 | | -1 | `FF FF FF FF FF FF FF FF FF 01` | 10 | | -9223372036854775808 | `80 80 80 80 80 80 80 80 80 01` | 10 | **Max bytes: 10.** Reading more than 10 bytes for a VarLong is a protocol error. node-minecraft-protocol delegates VarLong read/write to the same VarInt routines (which handle BigInt): ```js // node-minecraft-protocol/src/datatypes/minecraft.js:20-29 function readVarLong (buffer, offset) { return readVarInt(buffer, offset) } function writeVarLong (value, buffer, offset) { return writeVarInt(value, buffer, offset) } ``` --- ## 3. String UTF-8 encoded string, prefixed by its byte length as a **VarInt**. ``` [VarInt: byte length][UTF-8 bytes] ``` **Length caps:** - The spec defines a per-field maximum of `n` characters (UTF-16 code units, not bytes). - The most common cap is **32,767** characters. - Maximum byte capacity for a String(n): `(n × 3) + 3` bytes (worst-case UTF-8 + VarInt overhead). - Characters above U+FFFF (surrogate pairs in UTF-16) count as **2** units toward the character cap, even though they encode as 4 bytes in UTF-8. **Common caps by field:** | Context | Max chars (UTF-16 units) | |-------------------------------|--------------------------| | General String(n) | up to 32,767 | | Chat message (1.19+) | 256 | | Player username | 16 | | Identifier (Namespace:Path) | 32,767 (see §7) | Source: [minecraft.wiki/w/Java_Edition_protocol/Data_types](https://minecraft.wiki/w/Java_Edition_protocol/Data_types). --- ## 4. UUID 128-bit UUID encoded as **two big-endian unsigned 64-bit integers**: most significant 64 bits first, then least significant 64 bits. Always **16 bytes** on the wire. ```js // node-minecraft-protocol/src/datatypes/minecraft.js:32-37 function readUUID (buffer, offset) { return { value: UUID.stringify(buffer.slice(offset, 16 + offset)), size: 16 } } ``` ```js // node-minecraft-protocol/src/datatypes/compiler-minecraft.js:8-12 UUID: ['native', (buffer, offset) => { return { value: UUID.stringify(buffer.slice(offset, 16 + offset)), size: 16 } }], ``` No dashes on the wire — raw 16 bytes. The `SizeOf` for UUID is the constant `16` (`compiler-minecraft.js:143`). --- ## 5. Boolean Single byte: `0x00` = false, `0x01` = true. Used as the presence flag in `Prefixed Optional` (see §12). --- ## 6. Position (Block Coordinates) Encodes a block position (x, y, z) packed into a single **signed 64-bit integer** (8 bytes). ### Current format (1.14+, protocol 477+) Bit layout (MSB to LSB): ``` Bits 63–38 : x (26 bits, signed) Bits 37–12 : z (26 bits, signed) Bits 11–0 : y (12 bits, signed) ``` **Encode:** ``` value = ((x & 0x3FFFFFF) << 38) | ((z & 0x3FFFFFF) << 12) | (y & 0xFFF) ``` **Decode:** ``` x = val >> 38 z = val << 26 >> 38 y = val << 52 >> 52 ``` All three components are sign-extended from their field widths. Valid ranges: - x: −33,554,432 to 33,554,431 - z: −33,554,432 to 33,554,431 - y: −2,048 to 2,047 ### Pre-1.14 format (historical, protocol ≤ 476) Different bit layout — y occupied the middle 12 bits: ``` Bits 63–38 : x (26 bits) Bits 37–26 : y (12 bits) Bits 25–0 : z (26 bits) ``` **Breaking change in 1.14 (protocol 477)** — any code parsing Position must branch on the protocol version. Source: `minecraft-data/data/pc/1.14/version.json` → `"version": 477`; confirmed by minecraft-data protocol.json comparison: 1.13 (`version: 393`) defines `position` as `x(26)|y(12)|z(26)`; 1.14 (`version: 477`) redefines it as `x(26)|z(26)|y(12)`. Source: [minecraft.wiki/w/Java_Edition_protocol/Data_types](https://minecraft.wiki/w/Java_Edition_protocol/Data_types). --- ## 7. Angle Single **unsigned byte** representing a rotation angle. ``` angle_degrees = byte_value × (360 / 256) ``` One byte covers a full 360° turn in steps of 1/256 (≈ 1.406°). Used for entity facing direction in many packets. --- ## 8. Identifier (Namespaced Location) Encoded as a **String** (§3) with a max length of 32,767 characters, but with a constrained format: ``` namespace:path ``` - `namespace`: lowercase alphanumerics, `.`, `-`, `_` — regex `[a-z0-9.\-_]` - `path`: lowercase alphanumerics, `.`, `-`, `_`, `/` — regex `[a-z0-9.\-_/]` - Default namespace if omitted: `minecraft` Examples: `minecraft:stone`, `my_mod:custom/block`. Invalid Identifiers (wrong characters, missing colon) cause a disconnect. --- ## 9. Fixed-Width Integer Types Standard big-endian integers used in many fields: | Type | Bytes | Signed | Range | |-------|-------|--------|-------------------------------------------| | Byte | 1 | Yes | −128 to 127 | | UByte | 1 | No | 0 to 255 | | Short | 2 | Yes | −32,768 to 32,767 | | UShort| 2 | No | 0 to 65,535 | | Int | 4 | Yes | −2,147,483,648 to 2,147,483,647 | | Long | 8 | Yes | −9,223,372,036,854,775,808 to max | | Float | 4 | — | IEEE 754 single precision | | Double| 8 | — | IEEE 754 double precision | All in big-endian byte order. --- ## 10. NBT (Named Binary Tag) NBT is a typed binary tree format. On the network it appears in two forms: ### 10.1 Standard network NBT (pre-1.20.2) A normal NBT compound, including the root tag's type byte, the compound tag ID (`0x0A`), a length-prefixed name string for the root compound, and then the compound body. Used in item slots, chunk data, etc. ### 10.2 Network NBT (1.20.2+, protocol 764+) **Breaking change in 1.20.2:** the root compound's name string is **omitted** on the wire. The root tag type byte (`0x0A`) is still present, but where the name length + name bytes used to follow, there are now zero bytes before the compound body begins. This affects any field typed as `nbt` in the protocol data for 1.20.2+. Pre-1.20.2 parsers that expect a root name will misparse post-1.20.2 data and vice versa. Source: [minecraft.wiki/w/Java_Edition_protocol/Data_types](https://minecraft.wiki/w/Java_Edition_protocol/Data_types) — "Version 1.20.2 changed network NBT format by removing the root compound tag's name field during transmission." ### 10.3 Compressed NBT (item slots, pre-1.13) Some older item slot fields used a length-prefixed gzip-compressed NBT blob. Length was an `Int16BE`; value of `-1` means empty/no NBT: ```js // node-minecraft-protocol/src/datatypes/minecraft.js:51-70 function readCompressedNbt (buffer, offset) { const length = buffer.readInt16BE(offset) if (length === -1) return { size: 2 } const compressedNbt = buffer.slice(offset + 2, offset + 2 + length) const nbtBuffer = zlib.gunzipSync(compressedNbt) return { size: length + 2, value: nbt.proto.read(nbtBuffer, 0, 'nbt').value } } ``` This format was replaced by uncompressed NBT in later versions. ### 10.4 NBT in text components Network text components use NBT: - **String Tag** (`0x08`): for components containing only plain text. - **Compound Tag** (`0x0A`): for components with formatting, translations, or other structure. --- ## 11. Array of X An undelimited sequence of `X` elements whose count is **known from context** (e.g., a preceding `Length` field or the packet spec). No length prefix is encoded on the wire for a bare array. --- ## 12. Prefixed Array of X A **VarInt** count followed by that many elements of type `X`: ``` [VarInt: count][X][X]...[X] ``` Zero-length is valid (count = 0, zero following bytes). --- ## 13. Optional X A field that is present or absent based on **context** — typically a flag in an earlier field or a feature of the packet variant. When absent, contributes zero bytes. --- ## 14. Prefixed Optional X A **Boolean** (§5) followed by an `X` value if the boolean is `true`: ``` [Boolean: present][X if present] ``` If `present = false`, the field is completely absent (no bytes for X). --- ## 15. Enum Enums are encoded using an **underlying wire type** (almost always VarInt) with values defined per-packet in the protocol spec. Receiving an undefined enum value typically causes the client or server to disconnect. --- ## 16. BitSet Length-prefixed bit array using 64-bit longs. ``` [VarInt: num_longs][Long][Long]...[Long] ``` Bit `i` is set when: ``` (Data[i / 64] & (1L << (i % 64))) != 0 ``` Longs are big-endian. Bit 0 is the LSB of the first long. Source: [minecraft.wiki/w/Java_Edition_protocol/Data_types](https://minecraft.wiki/w/Java_Edition_protocol/Data_types). --- ## 17. Fixed BitSet(n) A bit array of exactly `n` bits, encoded as `⌈n / 8⌉` bytes (no length prefix). Bit `i` is set when: ``` (Data[i / 8] & (1 << (i % 8))) != 0 ``` **Note:** the bit indexing is byte-based here, not long-based as in the prefixed BitSet. The two are **not** interchangeable. --- ## 18. Miscellaneous Types in node-minecraft-protocol | Type name | Encoding | Source | |-------------------------|-------------------------------------------------------------------|--------| | `restBuffer` | Raw bytes from current offset to end of packet buffer | `minecraft.js:100-113` | | `entityMetadataLoop` | Repeated typed entries terminated by a sentinel byte (`endVal`) | `minecraft.js:116-133` | | `topBitSetTerminatedArray` | Array where MSB of the first byte of each element signals continuation (MSB=1) or end (MSB=0) | `minecraft.js:152-169` | | `compressedNbt` | Int16BE length + gzip-compressed NBT; −1 = absent | `minecraft.js:51-97` | | `lpVec3` | Length-prefixed 3D vector (from `lpVec3.js`) | `minecraft.js:7` | | `registryEntryHolder` | VarInt discriminant: 0 = inline entry follows, n>0 = registry ID (n-1) | `compiler-minecraft.js:45-54` | --- ## 19. Version History Cheat Sheet | Version | Protocol | Change | |-------------|----------|--------------------------------------------------------------| | 1.7.x | 4–5 | Baseline modern framing introduced (replaced legacy 0xFE ping) | | 1.8 | 47 | Whole-packet compression (`Set Compression`) added | | 1.9 | 107 | Protocol overhaul; many packet IDs changed | | 1.14 | 477 | `Position` bit layout changed: z and y swapped (now x/z/y). Source: `minecraft-data` 1.13 vs 1.14 `protocol.json` bitfield definitions; `minecraft-data/data/pc/1.14/version.json` | | 1.20.2 | 764 | `Configuration` state added; NBT root compound name dropped from network NBT | | 1.20.5 | 766 | `Transfer` intent (nextState=3) added to Handshake. Source: Velocity `HandshakeIntent.java:16`; ViaVersion `InitialBaseProtocol.java:55,133` | --- *See `00-overview.md` for TCP transport, packet framing pipeline, and the connection state machine.*