# Recording Format
Recap stores recordings as GZIP-compressed binary streams. Format is designed for high write throughput (per-tick streaming) and small disk footprint (delta encoding, bit-flag packing, skip-tick optimization).
This page documents the wire format for plugin authors who want to read `.recap` files directly. The official API exposes recordings via `IRecordingSession` / `IRecordingManager`.
---
## File layout
```
[4 bytes] Magic "RCAP"
[1 byte] Format version
[2 bytes] Header length
--- Header ---
[string] Recording name
[string] Player name
[string] Player UUID
[string] World name
[8 bytes] Origin X (double)
[8 bytes] Origin Y (double)
[8 bytes] Origin Z (double)
[8 bytes] Start timestamp (long)
[8 bytes] End timestamp (long)
[4 bytes] Total frame count
[4 bytes] Weather (int) — v2+
[8 bytes] Time of day (long) — v2+
--- Frame data (GZIP compressed) ---
Sequence of (action_tag byte) + (variable-length payload)
```
Strings use a 1-byte length prefix.
---
## Action tags
Each action is one byte, optionally followed by a payload:
| Tag | Hex | Meaning |
|---|---|---|
| FULL_STATE | 0x01 | Full snapshot of all player state |
| MOVEMENT | 0x02 | Position + rotation delta |
| STATE_FLAGS | 0x03 | Packed entity flags (sneak/sprint/fly/...) |
| EQUIPMENT | 0x04 | One slot changed |
| SWING_ARM | 0x05 | Arm swing this tick |
| USE_ITEM | 0x06 | Item-use start |
| BLOCK_PLACE | 0x07 | Block placement |
| BLOCK_BREAK | 0x08 | Block break |
| DAMAGE | 0x09 | Took damage |
| HEALTH | 0x0A | Health changed |
| NEXT_TICK | 0x0B | Advance one tick |
| SKIP_TICKS | 0x0C | Repeat current state N more ticks |
| BLOCK_DAMAGE_START | 0x0D | Start mining |
| BLOCK_DAMAGE_ABORT | 0x0E | Abort mining |
| SIGN_CHANGE | 0x0F | Sign text change |
| GAME_MODE | 0x10 | Gamemode swap |
| CONTAINER_OPEN | 0x11 | Container opened |
| CONTAINER_UPDATE | 0x12 | Container updated |
| HAND_STATE | 0x13 | Active hand state |
| PROJECTILE | 0x14 | Projectile launch |
| DEATH | 0x15 | Death |
| POSE | 0x16 | Pose change |
| RIPTIDE | 0x17 | Riptide spin |
| BED_ENTER | 0x18 | Enter bed |
| BED_LEAVE | 0x19 | Leave bed |
| REDSTONE_INTERACTION | 0x1A | Redstone interaction |
| CRITICAL_HIT | 0x1B | Critical hit particles |
| ENCHANT_CRIT | 0x1C | Enchanted critical hit |
| CHAT_MESSAGE | 0x1D | Chat message |
| ENTITY_INTERACTION | 0x1E | Item frame / armor stand interaction |
| VEHICLE_ENTER | 0x1F | Enter vehicle |
| VEHICLE_LEAVE | 0x20 | Leave vehicle |
| MOB_STATES | 0x21 | Tracked mob deltas |
| POTION_EFFECTS | 0x22 | Potion effect particles |
| TOTEM | 0x23 | Totem of undying |
| ARROWS_IN_BODY | 0x24 | Arrows stuck in player |
| EXPLOSION | 0x25 | Explosion blocks |
| PARTICLES | 0x26 | Server-emitted particle packets |
| SIGN_EXTRAS | 0x27 | Sign dye + glowing-text (optional, old files omit) |
| ITEM_DROP | 0x28 | Player dropped an item this tick |
| CAPTURED_PACKETS | 0x29 | Allowlisted server→client packets to replay |
| FULL_INVENTORY | 0x2A | 41-slot ItemStack snapshot |
| BLOCK_CHANGES | 0x2B | World-driven block changes near the player — v4+ (`--environment` capture) |
---
## Format versions
Current `Format version` byte is **4**.
| Version | Added |
|---|---|
| v2 | Weather + time-of-day in the header |
| v3 | (interim) |
| v4 | `BLOCK_CHANGES` (0x2B) for `--environment` capture |
Older readers reject a higher version byte rather than misparsing it.
---
## Movement encoding
Each coordinate uses one of four modes:
| Mode | Bytes | Range |
|---|---|---|
| UNCHANGED | 0 | Delta is zero |
| SHORT | 2 | Delta fits in ±32 768 fixed-point (×100) |
| FLOAT | 4 | Single precision |
| DOUBLE | 8 | Full precision |
The encoder picks the smallest mode that preserves the value.
---
## Delta encoding
Equipment, full inventory, and most state fields are written only when they differ from the previous frame's value. Frames with no changes are coalesced into `SKIP_TICKS` runs (one byte of count after a single state).
This is why a 60-second idle recording can be ~1 KiB and an active combat recording with NBT-heavy items is ~100 KiB.
---
## Capture buffers
Two streams are interleaved per tick:
- **Allowlisted packets** — netty-thread captures of outbound packets matching `Recap.capturePacketType` or `capturePluginMessageChannel`. Queued onto the recorder; drained into the next frame as `CAPTURED_PACKETS`. See [[Recap/Developers/Packet Capture]].
- **Bukkit events** — drop, swing, sign change, container open, etc., emitted via Spigot listeners and consumed at tick boundary.
---
## See Also
- [[Recap/Developers/Packet Capture]]
- [[Recap/API/IRecordingSession]]