This page specifies every primitive wire encoding used by the Minecraft Java Edition protocol, version 26.1.2. All multi-byte integers and floats are big-endian. Unless stated otherwise, signed integers use two's complement.
The canonical implementations are net.minecraft.network.FriendlyByteBuf,
net.minecraft.network.VarInt, net.minecraft.network.VarLong,
net.minecraft.network.Utf8String, and the StreamCodec instances in
net.minecraft.network.codec.ByteBufCodecs.
A single byte. 0x00 is false, 0x01 is true. Decoders should treat any
non-zero value as true, but encoders must always emit 0x01 for true.
Signed 8-bit integer. Range: -128..=127.
Unsigned 8-bit integer. Range: 0..=255. Used by client information packets,
container indices, and similar small enumerations.
Signed 16-bit big-endian integer. Range: -32768..=32767.
Unsigned 16-bit big-endian integer. Range: 0..=65535. Used for the port
number in Handshake and a few packet length fields.
Signed 32-bit big-endian integer. Range: -2³¹..=2³¹-1.
Signed 64-bit big-endian integer. Range: -2⁶³..=2⁶³-1.
IEEE-754 single-precision 32-bit big-endian. NaN is preserved bit-exactly.
IEEE-754 double-precision 64-bit big-endian. NaN is preserved bit-exactly.
Variable-length encoding of a signed 32-bit integer (1–5 bytes). Each byte uses the low 7 bits as data and the high bit as a continuation flag. The value is reconstructed by concatenating the 7-bit groups in little-endian order.
A negative value is sent in the same way as its 32-bit two's complement unsigned representation, so it always occupies the maximum 5 bytes. The decoder throws if it has read more than 5 bytes without seeing a cleared continuation bit.
Variable-length encoding of a signed 64-bit integer (1–10 bytes). Encoded the same way as a VarInt, but with up to ten 7-bit groups.
Length-prefixed UTF-8 string:
Length is the byte length of the UTF-8 encoding, not the number of
characters. The default character cap is 32 767; the wire byte cap is therefore
utf8MaxBytes(32767) (≈ 98 304 bytes). Specific fields may impose tighter
caps (e.g. player name is 16 chars, server brand 32, signatures 1024).
A namespaced resource location encoded exactly like a String, of the
form namespace:path. Maximum length 32 767 characters. If namespace: is
omitted, the client substitutes minecraft:.
The namespace matches [a-z0-9_.\-]+ and path matches [a-z0-9_./\-]+.
Refers to a Text Component. On the wire it is the network NBT encoding of the component value (see NBT). The plain form (a String holding a literal) is also accepted.
Legacy form: a String containing a JSON-serialised text component. Used in a small number of packets retained for compatibility (e.g. the disconnect reason during the Login state). Maximum length 262 144 characters.
Two consecutive Longs, most-significant bits first, then least-significant bits.
Encoded into a single signed 64-bit integer. The current layout packs the fields as x (26 bits, signed) || z (26 bits, signed) || y (12 bits, signed):
Decoded as:
Encoded as ((x & 0x3FFFFFF) << 38) | ((z & 0x3FFFFFF) << 12) | (y & 0xFFF).
Range: x, z ∈ [-33_554_432, 33_554_431], y ∈ [-2048, 2047].
NOTE: Older protocol versions (pre-1.14) used a different layout (
x:26 || y:12 || z:26). The order shown above (x || z || y) is the current one and is what 26.1.2 transmits.
Single unsigned byte representing an angle in 1/256 of a full turn. To convert
to degrees: deg = byte * 360 / 256. Wraps modulo 256.
Low-precision 3D vector used since 1.21.7 for entity movement/velocity fields
(net.minecraft.network.LpVec3, exposed as Vec3.LP_STREAM_CODEC). Variable
length:
If |x|, |y|, |z| are all below 3.051944088384301e-5 (ABS_MIN), the
vector is encoded as a single 0x00 byte.
Otherwise the encoder computes scale = ceil(max(|x|,|y|,|z|)), packs each
axis into 15 bits as round((axis/scale * 0.5 + 0.5) * 32766), and emits a
64-bit buffer laid out as:
Wire layout: 1 byte (bits 0..7), 1 byte (bits 8..15), 4 bytes big-endian
(bits 16..47) — 6 bytes total. When the continuation flag is set, a VarInt
carrying the upper bits of the scale (scale >> 2) follows.
Range per axis: ±1.7179869183e10. NaN is sanitised to 0.0.
This format replaces the pre-1.21.7 Velocity X / Y / Z triple of signed
shorts. Encoding velocity as three shorts produces the symptom "packet larger
than I expected, found 5 bytes extra" on the vanilla client.
Length-prefixed raw bytes:
The decoder rejects lengths greater than the receiver-imposed cap (often the remaining readable bytes of the frame).
A length-prefixed array of any element type:
Each element is encoded with its own type's codec. Most array fields impose a documented per-packet maximum size; the decoder throws if exceeded.
A boolean discriminator followed by the value when present:
A length-prefixed array of 64-bit longs holding the BitSet's contents in Java's
BitSet.toLongArray() little-endian-bit ordering (bit n of the set lives in
bit n & 63 of long n >> 6).
A BitSet of statically known size N (agreed out-of-band). No length prefix.
Encoded as ceil(N / 8) raw bytes, again following Java's BitSet.toByteArray()
ordering. The encoder throws if any bit at index ≥ N is set.
NBT (Named Binary Tag) is Mojang's tagged binary serialisation format. It is used both on disk (region files, level data) and on the wire, in a slightly different framing. This section describes the network NBT form used by protocol 26.1.2.
The "network NBT" framing used in modern protocol packets:
0x00 0x00) that
preceded the payload prior to 1.20.2 is omitted on the wire.TAG_End (0x00) byte represents an absent / null tag and is
the only legal "empty" value when a packet field is typed as NBT.The default decoder enforces an NbtAccounter quota (≈ 2 MiB and depth 512).
"Trusted" variants used by some server-built payloads lift the limit.
| ID | Name | Payload |
|---|---|---|
0x00 |
TAG_End |
None. Marks the end of a Compound and is the absent-tag sentinel at the root. |
0x01 |
TAG_Byte |
One signed Byte. |
0x02 |
TAG_Short |
One big-endian signed Short. |
0x03 |
TAG_Int |
One big-endian signed Int. |
0x04 |
TAG_Long |
One big-endian signed Long. |
0x05 |
TAG_Float |
One big-endian IEEE-754 Float. |
0x06 |
TAG_Double |
One big-endian IEEE-754 Double. |
0x07 |
TAG_Byte_Array |
Int length N then N raw bytes. |
0x08 |
TAG_String |
NBT-string: unsigned 16-bit length N (big-endian), then N bytes of modified UTF-8. |
0x09 |
TAG_List |
One element-type byte (TAG_*), then Int length N (big-endian, ≥ 0), then N payloads of the declared element type concatenated without their type bytes or names. An empty list (N == 0) is conventionally encoded with element-type TAG_End. |
0x0A |
TAG_Compound |
Sequence of named entries: Type byte + NBT-string Name + Payload, repeated until a terminating TAG_End (0x00) byte. See Compound. |
0x0B |
TAG_Int_Array |
Int length N then N big-endian signed Ints. |
0x0C |
TAG_Long_Array |
Int length N then N big-endian signed Longs. |
All NBT integers and floats are big-endian regardless of platform. Lengths
inside TAG_*_Array and TAG_List are 32-bit signed Ints and must not be
negative; decoders reject negative lengths.
A Type of 0x00 (TAG_End) terminates the compound and carries no name or
payload. Every other entry inside a compound is named; this is the only
context (along with the legacy disk root) in which NBT names appear.
NBT strings (TAG_String and the Name of named entries) use Java's
modified UTF-8 encoding, not the protocol's standard String
encoding. The differences are:
U+0000) is encoded as the two-byte sequence 0xC0 0x80 so
that no real character produces a 0x00 byte in the stream.U+10000–U+10FFFF) are emitted as two
three-byte sequences encoding the UTF-16 surrogate pair, never as a single
four-byte sequence.Length is a 16-bit unsigned short giving the encoded byte length (max
65 535). Implementations decoding NBT strings as standard UTF-8 will
mishandle these two cases.
A specialisation of NBT where the top-level tag is required to be a
TAG_Compound (0x0A). The wire shape is identical to a generic NBT tag —
one type byte (always 0x0A when present), then the named-entry list, then
a TAG_End (0x00) terminator. Used wherever a packet field is declared as
a structured object, e.g. registry entries, recipe payloads, item data
components, chat-message session metadata.
A field typed Compound may also be encoded as a single TAG_End (0x00)
to signal an absent value when the field is documented as optional.
Three consecutive Floats: x, y, z.
Four consecutive Floats: x, y, z, w — in that order.
Two ints packed into a single Long: high 32 bits = x, low 32 bits = z
(both signed).
A dimension Identifier followed by a Position.
Position, then a Direction enum encoded as a VarInt
(0 = down, 1 = up, 2 = north, 3 = south, 4 = west, 5 = east), then three
floats (cx, cy, cz) representing the click point relative to the block's
minimum corner, then two booleans (inside, worldBorder).
A registry value (block, item, entity type, …) is encoded as a single
VarInt holding its id within the receiver's view of that registry.
The actual id table is established during the Configuration state via
ClientboundRegistryDataPacket — see Registries.
A Holder<T> is encoded with a 1-based id: id 0 means "direct value"
(followed by the value's payload), and id n ≥ 1 means "reference to entry
n - 1 of the registry."
A HolderSet<T> is encoded as a VarInt n. If n == 0, an Identifier
follows naming a tag. Otherwise the set contains n - 1 Holders.