A chunk is a 16×384×16 column of blocks divided into 24 vertical sections of 16×16×16. The server sends a chunk to the client as one Level Chunk With Light packet, which is the concatenation of two well-defined sub-payloads: chunk data (heightmaps + block/biome contents + block entities) and light data (sky/block light bitmasks + arrays). Light updates outside of chunk loads are sent independently as Update Light and reuse the same light sub-payload.
The packet field tables (with packet IDs) live on the play-clientbound page; this page specifies the two sub-payloads they share.
| Field | Type | Notes |
|---|---|---|
| Heightmaps | NBT/map | Map of heightmap type → packed long array. |
| Data length | VarInt | Length in bytes of the section blob that follows. |
| Data | byte[] | Concatenation of all 24 chunk sections (see Chunk section). |
| Block entity count | VarInt | Number of block entities to follow. |
| Block entities | Array | Repeats count times, see Block entity entry. |
Sent as a map keyed by heightmap type (an enum identifier serialized as VarInt) to a packed long array of height values. Two types are sent to the client:
| Type | Notes |
|---|---|
MOTION_BLOCKING |
Y of the highest block that blocks motion or contains a fluid (used by mob spawning, AI). |
WORLD_SURFACE |
Y of the highest non-air block (used by lighting). |
Each heightmap stores 256 entries (one per (x,z) column) packed into longs at 9 bits per entry (enough to address a 384-block tall world).
A section is written sequentially into the Data byte buffer. There are always exactly 24 sections, even if some are entirely empty.
| Field | Type | Notes |
|---|---|---|
| Non-air block count | Short | Number of non-air block states in the section; used to short-circuit physics/render. |
| Fluid count | Short | Number of blocks containing a fluid. |
| Block states | Paletted container | 16×16×16 entries, palette over the global block-state registry. |
| Biomes | Paletted container | 4×4×4 entries (biomes are 4-block-aligned), palette over the dynamic biome registry. |
A paletted container compresses a 3D grid of registry references by carrying a small palette of values plus a packed-long-array of indices into that palette. The wire layout is:
| Field | Type | Notes |
|---|---|---|
| Bits per entry | UByte | Selects the palette format (see below). |
| Palette | format-dependent | Empty/single/list of registry IDs. |
| Data length | VarInt | Length of the packed long array (sometimes omitted — see below). |
| Data | long[] | Packed indices, fixed length determined by entry count and bits per entry. |
In 26.1.2 the data array is sent as a fixed-size long array — its length is implied by the section's entry count and the bits-per-entry, so no length prefix is written in front of it (the implementation reads entryCount * bits / 64 longs, rounded up).
The palette format is selected by the Bits per entry byte:
Thresholds (T_low, T_dir) are container-specific:
bits ∈ [1, 4], hash palette for [5, 8], direct (currently 15 bits to address every block state) for bits ≥ 9.bits ∈ [1, 3], hash palette unused, direct for bits ≥ 4.The packed long array uses the standard 1.16+ "no spanning" layout: each long contains floor(64 / bits) entries, and entries are not split across two longs (any leftover bits are padding).
| Field | Type | Notes |
|---|---|---|
| Packed XZ | Byte | High nibble = X (0–15), low nibble = Z (0–15) within the chunk. |
| Y | Short | Absolute world Y coordinate. |
| Type | VarInt | Numeric ID into the minecraft:block_entity_type registry. |
| Data | NBT (optional) | Network NBT compound; may be empty/absent (the network NBT marker TAG_End indicates "no data"). |
The light sub-payload is also the body of the standalone Update Light packet. It uses BitSets (sent as long[] with a length prefix) to indicate which sections include data.
| Field | Type | Notes |
|---|---|---|
| Sky light mask | BitSet | One bit per section index (0..lightSectionCount-1); set if a non-empty sky-light array follows for that section. |
| Block light mask | BitSet | Same, for block light. |
| Empty sky light mask | BitSet | Sections that are explicitly all-dark for sky light. |
| Empty block light mask | BitSet | Same, for block light. |
| Sky light arrays | Array | One byte[2048] per bit set in Sky light mask, in ascending section order. |
| Block light arrays | Array | One byte[2048] per bit set in Block light mask, in ascending section order. |
A section index runs from 0 (one section below the bottom of the world for cross-border lighting) to lightSectionCount - 1 (one above the top), so a 24-section world emits up to 26 light bits per layer.
Each light array stores 4 bits per block (one nibble per cell, two cells per byte), totalling 4096 cells × 4 bits = 16384 bits = 2048 bytes. The lower nibble is the cell at the lower index.
A bit set in the empty mask means "this section exists but its values are all zero, save bandwidth"; the client should treat it as fully dark for that layer. A section absent from both the data mask and the empty mask is unchanged.
Source: net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java, net/minecraft/network/protocol/game/ClientboundLightUpdatePacketData.java, net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java, net/minecraft/world/level/chunk/LevelChunkSection.java, net/minecraft/world/level/chunk/PalettedContainer.java, net/minecraft/world/level/chunk/SingleValuePalette.java, net/minecraft/world/level/chunk/LinearPalette.java, net/minecraft/world/level/chunk/HashMapPalette.java, net/minecraft/world/level/chunk/GlobalPalette.java, net/minecraft/util/SimpleBitStorage.java, net/minecraft/world/level/levelgen/Heightmap.java.