# 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]]