# Entity Metadata Wire Format > **Scope:** Java Edition entity metadata (a.k.a. "entity data") as carried by `Set Entity Metadata` and (historically) inside spawn packets. > **Primary sources:** > - `/tmp/mcproto-refs/minecraft-data/data/pc/*/protocol.json` — `entityMetadata`, `entityMetadataEntry`, `entityMetadataItem` type defs > - `/tmp/mcproto-refs/ViaVersion/api/src/main/java/com/viaversion/viaversion/api/type/types/entitydata/OldEntityDataType.java` — legacy frame reader > - `/tmp/mcproto-refs/ViaVersion/api/src/main/java/com/viaversion/viaversion/api/type/types/entitydata/ModernEntityDataType.java` — modern frame reader > - `/tmp/mcproto-refs/ViaVersion/api/src/main/java/com/viaversion/viaversion/api/minecraft/entitydata/types/EntityDataTypes*.java` — per-version type registries > - Cross-links: [../versions/1.9.md](../versions/1.9.md) · [../versions/1.14.md](../versions/1.14.md) · [../01-data-types.md](../01-data-types.md) (VarInt / NBT / Slot) --- ## 1. Overview Entity metadata is an indexed key/value list sent by the server to describe the current state of an entity: health flags, custom names, riding status, arm poses, and hundreds of entity-class-specific attributes. It travels in two contexts: 1. **`Set Entity Metadata` (Play CB)** — differential update for an already-spawned entity. Only the changed indices are sent. 2. **Spawn packets (historical, ≤ 1.8)** — the full metadata list was embedded in `Spawn Mob` and `Spawn Object`. The format has two incompatible eras: | Era | Versions | Index encoding | Type encoding | Terminator | |-----|----------|----------------|---------------|------------| | **Legacy** | ≤ 1.8 (protocol ≤ 47) | low 5 bits of a single byte | high 3 bits of the same byte | `0x7F` (`127`) | | **Modern** | ≥ 1.9 (protocol ≥ 107) | separate unsigned byte | separate VarInt | `0xFF` (`255`) | --- ## 2. Modern Format (1.9+) Source: `ModernEntityDataType.java:34–38`; `minecraft-data` `data/pc/1.9/protocol.json` (and all later versions). ### 2.1 Frame structure ``` EntityMetadata = (Entry)* Terminator Entry = { Index : u8 -- unsigned byte; 0x00–0xFE are valid indices Type : VarInt -- type ID from the per-version registry (see §3) Value : } Terminator = 0xFF -- single byte; signals end of list ``` Read loop (pseudocode matching `ModernEntityDataType.java`): ``` loop: index = readUnsignedByte() if index == 0xFF: break typeId = readVarInt() type = registry.byId(typeId) value = type.read(buffer) entries.add(Entry(index, type, value)) ``` **Key points:** - Index and Type are always present together; neither is omitted. - The list is unordered within the packet (indices need not be ascending). - An empty metadata list is a legal single byte: `FF`. - The `Type` VarInt is always small (≤ 2 bytes on the wire) but must be read as a full VarInt. > **Note on minecraft-data 1.9–1.12.2:** `minecraft-data` records the `type` field as `i8` (signed byte) in those versions rather than `varint`. This is an artifact of minecraft-data's protocol description — since values 0–12 all fit in one byte, both readings are wire-compatible. ViaVersion's `ModernEntityDataType` reads it as VarInt from 1.9 onward, and the Minecraft wiki specifies VarInt. The `varint` encoding is authoritative; `i8` in minecraft-data is a pragmatic approximation. The minecraft-data source confirms the change to `varint` in its 1.13 JSON: `/tmp/mcproto-refs/minecraft-data/data/pc/1.13/protocol.json`. --- ## 3. Type Registry — 1.21.1 Source: `EntityDataTypes1_21.java` (lines 33–63) + `minecraft-data` `data/pc/1.21.1/protocol.json` `entityMetadataEntry` type mapper. | ID | Name | Wire encoding | |----|------|---------------| | 0 | Byte | `i8` — signed byte | | 1 | VarInt | VarInt | | 2 | VarLong | VarLong (added 1.19.3; see §5) | | 3 | Float | `f32` — big-endian IEEE 754 single | | 4 | String | VarInt length + UTF-8 bytes | | 5 | Chat (TextComponent) | Anonymous NBT tag (1.20.3+); `string` (JSON text) in 1.9–1.20.2 | | 6 | Optional Chat | `bool` present-flag + Chat (if present) | | 7 | Slot | Item stack — see [../01-data-types.md](../01-data-types.md) §Slot; encoding changed at 1.9, 1.13, 1.13.2, 1.20.2, 1.20.5 | | 8 | Boolean | `bool` — single byte `0x00`/`0x01` | | 9 | Rotations | `f32` pitch + `f32` yaw + `f32` roll (3 × 4 bytes) | | 10 | Position | 64-bit packed `Position` — `[x:26][z:26][y:12]` since 1.14; `[x:26][y:12][z:26]` in 1.9–1.13 | | 11 | Optional Position | `bool` present-flag + Position (if present) | | 12 | Direction | VarInt enum: `0`=Down `1`=Up `2`=North `3`=South `4`=West `5`=East | | 13 | Optional UUID | `bool` present-flag + 128-bit UUID big-endian (if present) | | 14 | Block State | VarInt flat block-state ID (0 = air) | | 15 | Optional Block State | VarInt: `0` = absent; non-zero = block-state ID | | 16 | NBT | Anonymous compound NBT (no leading name) — see [../01-data-types.md](../01-data-types.md) §NBT | | 17 | Particle | Typed `Particle` compound (added 1.13) | | 18 | Particles | VarInt count + Particle\[\] (added 1.20.5) | | 19 | Villager Data | VarInt villagerType + VarInt villagerProfession + VarInt level (added 1.14) | | 20 | Optional VarInt | VarInt: `0` = absent; value = stored\_value + 1 | | 21 | Pose | VarInt enum — see §4.3 (added 1.14) | | 22 | Cat Variant | VarInt registry ID (added 1.19) | | 23 | Wolf Variant | `registryEntryHolder`: VarInt registry ID or inline `WolfVariant` struct (added 1.21.2; was plain VarInt in 1.21) | | 24 | Frog Variant | VarInt registry ID (added 1.19) | | 25 | Optional Global Position | `bool` present-flag + dimension String + Position (if present) | | 26 | Painting Variant | `registryEntryHolder`: VarInt registry ID or inline `PaintingVariant` struct (added 1.21; struct encoding differs 1.21 vs 1.21.2+) | | 27 | Sniffer State | VarInt enum (added 1.20) | | 28 | Armadillo State | VarInt enum (added 1.20.5) | | 29 | Vector3 | `f32` x + `f32` y + `f32` z (added 1.20) | | 30 | Quaternion | `f32` x + `f32` y + `f32` z + `f32` w (added 1.20) | **Total: 31 type IDs (0–30) in 1.21.x.** Source: `EntityDataTypes1_21.java` constructor argument `super(31)`. ### 3.1 `registryEntryHolder` encoding (types 23, 26) Used since 1.21 for Wolf Variant and Painting Variant. ``` RegistryEntryHolder = { hasInlineData : bool if hasInlineData: inlineValue : else: registryId : VarInt -- numeric ID within the server's registry } ``` `WolfVariant` inline struct (1.21): wildTexture String + tameTexture String + angryTexture String + biome IDSet. `PaintingVariant` inline struct (1.21): i32 width + i32 height + String assetId + Optional Anonymous NBT title + Optional Anonymous NBT author. Source: `EntityMetadataWolfVariant` / `EntityMetadataPaintingVariant` in `minecraft-data` `data/pc/1.21.1/protocol.json`. --- ## 4. Type Registry History (selected versions) The per-version type classes in ViaVersion are authoritative. Below are the salient snapshots. ### 4.1 1.9–1.12.2 (EntityDataTypes1_9 / EntityDataTypes1_12) Source: `EntityDataTypes1_9.java` (ordinal = ID), `EntityDataTypes1_12.java`. | ID | 1.9 | 1.12 (added) | |----|-----|--------------| | 0 | Byte | same | | 1 | VarInt | same | | 2 | Float | same | | 3 | String | same | | 4 | Component (JSON text) | same | | 5 | Slot (`ITEM1_8`) | same | | 6 | Boolean | same | | 7 | Rotations | same | | 8 | Position (`BLOCK_POSITION1_8`) | same | | 9 | Optional Position | same | | 10 | Direction | same | | 11 | Optional UUID | same | | 12 | Optional Block State | same | | 13 | — | NBT (`NAMED_COMPOUND_TAG`) | Note: no VarLong, no Particle, no Villager Data, no Pose in this era. ### 4.2 1.13–1.13.2 (EntityDataTypes1_13 / EntityDataTypes1_13_2) Source: `EntityDataTypes1_13.java`, `EntityDataTypes1_13_2.java`. Key changes: - **Type field becomes VarInt** (was `i8`-compatible byte in 1.9–1.12.2; explicitly `varint` in minecraft-data from 1.13). - ID 5 becomes `Optional Component` (was absent; Component shifts from id 4 to id 4, Optional Component added at 5; ids 5–13 shift up by 1). - Particle added at ID 15. - Slot encoding updated to `ITEM1_13` / `ITEM1_13_2`. Full 1.13.2 registry (16 entries, IDs 0–15): `Byte · VarInt · Float · String · Component · OptComponent · Slot · Boolean · Rotations · Position · OptPosition · Direction · OptUUID · OptBlockState · NBT · Particle` ### 4.3 1.14 (EntityDataTypes1_14) Source: `EntityDataTypes1_14.java`. Added IDs (shifts all subsequent IDs by +1 relative to 1.12, due to Optional Component insertion in 1.13 and now Villager Data + Opt VarInt + Pose): | New ID | Type | Notes | |--------|------|-------| | 16 | Villager Data | VarInt×3: villagerType, villagerProfession, level | | 17 | Optional VarInt | 0 = absent; otherwise stored\_value + 1 | | 18 | Pose | VarInt enum | **Pose values (1.14):** `0`=Standing `1`=FallFlying `2`=Sleeping `3`=Swimming `4`=SpinAttack `5`=Sneaking _(1.14 original list)_ Position encoding also changes: `BLOCK_POSITION1_14` (`[x:26][z:26][y:12]` bit layout) replaces `BLOCK_POSITION1_8`. See [../versions/1.14.md](../versions/1.14.md) §Position. ### 4.4 1.19 (EntityDataTypes1_19) Source: `EntityDataTypes1_19.java`. Added IDs 19–22: - 19: Cat Variant (VarInt) - 20: Frog Variant (VarInt) - 21: Optional Global Position (`bool` + dimension String + Position) - 22: Painting Variant (VarInt) ### 4.5 1.19.3 (EntityDataTypes1_19_3) Source: `EntityDataTypes1_19_3.java`; confirmed by `minecraft-data` `data/pc/1.19.3/protocol.json`. **VarLong inserted at ID 2.** All existing IDs from 2 onward shift up by 1 (Float moves to 3, String to 4, etc.). This was the largest single-version numbering shift since 1.13. Registry after insertion (24 entries): `Byte(0) · VarInt(1) · VarLong(2) · Float(3) · String(4) · Component(5) · OptComponent(6) · Slot(7) · Boolean(8) · Rotations(9) · Position(10) · OptPosition(11) · Direction(12) · OptUUID(13) · OptBlockState(14) · NBT(15) · Particle(16) · VillagerData(17) · OptVarInt(18) · Pose(19) · CatVariant(20) · FrogVariant(21) · OptGlobalPos(22) · PaintingVariant(23)` ### 4.6 1.19.4 (EntityDataTypes1_19_4) Source: `EntityDataTypes1_19_4.java`. Added: - ID 14: Block State (non-optional, explicit; confirmed new in 1.19.4). ViaVersion `EntityDataTypes1_9` through `EntityDataTypes1_19_3` have only `optionalBlockStateType` (never a separate `blockStateType`); `EntityDataTypes1_19_4.java:45` is the first to add `blockStateType = add(14, Types.VAR_INT)` alongside `optionalBlockStateType = add(15, Types.VAR_INT)`. Source: grep across `EntityDataTypes1_9.java`, `EntityDataTypes1_14.java`, `EntityDataTypes1_19.java`, `EntityDataTypes1_19_3.java`, `EntityDataTypes1_19_4.java`. - ID 25: Sniffer State (VarInt enum, new in 1.20 — but the type ID slot is first defined in ViaVersion's 1.19.4 class) - ID 26: Vector3F (`f32`×3) - ID 27: Quaternion (`f32`×4) ### 4.7 1.20.5 (EntityDataTypes1_20_5) and 1.21 (EntityDataTypes1_21) Source: `EntityDataTypes1_20_5.java`, `EntityDataTypes1_21.java`. Changes vs 1.19.4: - **Particles (plural) added at ID 18.** Particle (singular) stays at 17. All subsequent IDs shift +1. - **Chat/Component now uses anonymous NBT tag** (`TRUSTED_TAG`) instead of JSON string. Source: `Types.TRUSTED_TAG` / `Types.TRUSTED_OPTIONAL_TAG` in both files. - **Wolf Variant (ID 23)**: 1.21 uses `WolfVariant.TYPE` (inline struct holder); 1.20.5 used plain VarInt. - **Armadillo State (ID 28)** added. - **Wolf Variant, Painting Variant** use `registryEntryHolder` shape from 1.21.2 onward. Full 1.21.x registry: see §3 above (31 entries, IDs 0–30). --- ## 5. Evolution Timeline | Version | Change | |---------|--------| | ≤ 1.8 | Legacy single-byte packed format (§6); terminator `0x7F` | | **1.9** | Modern format: separate u8 index + VarInt type + 0xFF terminator; 13 type IDs | | 1.9 | Dual-hand: `HAND_ACTIVE` (Byte, index 5 on LivingEntity) added; off-hand Slot carried in Slot type; old Block/Int types removed | | 1.12 | NBT type added (ID 13); total 14 type IDs | | **1.13** | Type field officially becomes VarInt (was byte-range compatible); Optional Component added (ID 5); Particle added (ID 15); Slot encoding updated; 16 type IDs | | **1.14** | Villager Data, Optional VarInt, Pose added (IDs 16–18); Position bit-layout changed (`[x:26][z:26][y:12]`) | | 1.19 | Cat Variant, Frog Variant, Optional Global Position, Painting Variant added (IDs 19–22); 23 type IDs | | **1.19.3** | VarLong inserted at ID 2 — all subsequent IDs +1; 24 type IDs | | 1.19.4 | Block State (explicit non-optional) ID 14 split from Optional Block State; Sniffer State, Vector3, Quaternion added; 28 type IDs | | 1.20.5 | Particles (plural) added at ID 18 (+1 shift); Chat type switches from JSON string to anonymous NBT; Armadillo State added; Wolf Variant expanded; 31 type IDs | | 1.21 | Wolf Variant becomes `registryEntryHolder`; Painting Variant becomes `registryEntryHolder` | | 1.21.2 | Painting Variant inline struct encoding updated | --- ## 6. Legacy Format (≤ 1.8) Source: `OldEntityDataType.java:36–50`; `minecraft-data` `data/pc/1.8/protocol.json` `entityMetadata`. ### 6.1 Frame structure ``` EntityMetadata = (Entry)* Terminator Entry = { header : u8 -- bits [7:5] = type ID (3 bits); bits [4:0] = index (5 bits) Value : } Terminator = 0x7F -- single byte (127); index field would be 31, type 3 = Float, but 0x7F is the sentinel ``` The header byte encoding: ``` header = (typeId << 5) | (index & 0x1F) ``` This limits index to 0–31 (5 bits) and type to 0–7 (3 bits). ### 6.2 1.8 type registry (8 entries) Source: `EntityDataTypes1_8.java` (ordinal = ID); `minecraft-data` `data/pc/1.8/protocol.json` `entityMetadataItem`. | ID | Name | Wire encoding | |----|------|---------------| | 0 | Byte | `i8` | | 1 | Short | `i16` big-endian | | 2 | Int | `i32` big-endian | | 3 | Float | `f32` IEEE 754 | | 4 | String | VarInt length + UTF-8 | | 5 | Slot | `ITEM1_8` — item id (i16) + count (i8) + damage (i16) + NBT | | 6 | Block Position | `i32` x + `i32` y + `i32` z (three separate ints, NOT the packed Position) | | 7 | Rotations | `f32` pitch + `f32` yaw + `f32` roll | **Key differences from 1.9+:** - No Boolean type (boolean flags used Byte 0/1). - No Optional types (UUID, Position, Block State). - Short (i16) and Int (i32) exist; both removed in 1.9 (replaced by VarInt). - Block Position is three plain `i32` fields, not a packed 64-bit value. - No Component / NBT / Direction / Particle. --- ## 7. Index Assignments Are Per-Entity-Class and Per-Version **The type registry (§3) defines only the wire encoding per type ID. Which type ID and which index value mean what for a given entity class is entirely version-specific and entity-class-specific.** Examples (from `EntityDataIndex1_9.java`): | Entity class | Index (1.8) | Index (1.9) | Type (1.8) | Type (1.9) | Meaning | |---|---|---|---|---|---| | Entity | 0 | 0 | Byte | Byte | Status flags (on fire, crouching, …) | | Entity | 1 | 1 | Short | VarInt | Air supply | | Entity | 2 | 2 | String | String | Custom name | | LivingEntity | 6 | 6 | Float | Float | Health | | LivingEntity | 7 | 7 | Int | VarInt | Potion effect colour | | Player | 10 | 12 | Byte | Byte | Skin flags | | LivingEntity | — | 5 | — | Byte | Hand state (new in 1.9, dual-hand) | Per-version index tables live in the ViaVersion EntityDataIndex files under `/tmp/mcproto-refs/ViaVersion/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/data/EntityDataIndex1_9.java` and in `EntityTypes*` classes for later versions. For 1.21.1 entity data index assignments, refer to [../versions/1.21.md](../versions/1.21.md) or the Minecraft wiki [Entity format](https://minecraft.wiki/w/Entity_format) page which tabulates per-class indices. --- ## 8. Carrying Packet In 1.21.1 (Play state, client-bound): ``` Set Entity Metadata (0x52) = { entityId : VarInt metadata : EntityMetadata -- zero or more entries + 0xFF } ``` Source: `minecraft-data` `data/pc/1.21.1/protocol.json` `play.toClient.types.packet_entity_metadata`. Historically (≤ 1.8) the full metadata list was also embedded in `Spawn Mob` (CB 0x0F) and `Spawn Object` (CB 0x0E). --- ## 9. Open Items - **Block State (ID 14, non-optional) first appeared in 1.19.4**: confirmed — ViaVersion's 1.14, 1.19, and 1.19.3 classes have only `optionalBlockStateType`; 1.19.4 adds both `blockStateType` (14) and `optionalBlockStateType` (15). See §4.6. - **`optional_global_pos` wire encoding**: confirmed — `GlobalBlockPositionType.java` reads `String dimension` (the dimension ResourceLocation, e.g. `minecraft:overworld`) + `BLOCK_POSITION1_14` (packed i64 position). The `minecraft-data` `['option', 'string']` representation is a schema approximation; the actual wire format is `bool` + (if present) String + packed Position. Source: `ViaVersion/.../api/type/types/math/GlobalBlockPositionType.java:41-43`. - Pose enum values beyond the 1.14 original (e.g. `Croaking`, `UsingTongue`, `Roaring`, `Sniffing`, `Emerging`, `Digging` added for 1.17–1.20 mobs) — not enumerated here; the authoritative list is at `minecraft.wiki/w/Entity_format#Pose`.