Files
minecraft_protocol/01-data-types.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

14 KiB
Raw Blame History

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

1. VarInt

Variable-length encoding for signed 32-bit integers. 15 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; 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 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.


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 6338 : x (26 bits, signed)
Bits 3712 : z (26 bits, signed)
Bits 110  : 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 6338 : x (26 bits)
Bits 3726 : y (12 bits)
Bits 250  : 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 45 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.