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>
14 KiB
Minecraft Java Edition Protocol — Wire Data Types
Scope: Java Edition wire types only. Version-specific changes are called out inline.
Primary sources:
node-minecraft-protocolv1.66.2 (/tmp/mcproto-refs/node-minecraft-protocol/)- 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
1if more bytes follow,0on 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; 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):
// 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
ncharacters (UTF-16 code units, not bytes). - The most common cap is 32,767 characters.
- Maximum byte capacity for a String(n):
(n × 3) + 3bytes (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.
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.
// node-minecraft-protocol/src/datatypes/minecraft.js:32-37
function readUUID (buffer, offset) {
return {
value: UUID.stringify(buffer.slice(offset, 16 + offset)),
size: 16
}
}
// 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.
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 — "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:
// 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.
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.