# Packet Capture Recap intercepts outbound packets matching a plugin-registered allowlist, stores the raw bytes in the active recording, and re-sends them during playback at the same tick. This is how lightning, sounds, and client-mod plugin messages (e.g. Lectern effects) replay correctly without per-plugin glue. --- ## Allowlist API ```java IRecap recap = RecapAPI.get(); // By packet type name (PacketEvents PacketType constants) recap.capturePacketType("SOUND_EFFECT"); recap.capturePacketType("ENTITY_SOUND_EFFECT"); recap.capturePacketType("ENTITY_ANIMATION"); // By plugin-message channel recap.capturePluginMessageChannel("yourplugin:effect_channel"); // Inspect / remove recap.getCapturedPacketTypes(); recap.getCapturedPluginMessageChannels(); recap.uncapturePacketType("SOUND_EFFECT"); recap.uncapturePluginMessageChannel("yourplugin:effect_channel"); ``` Defaults active at plugin enable: - `SOUND_EFFECT` - `ENTITY_SOUND_EFFECT` - `lectern:receive_event` (Lectern Paper plugin integration) --- ## How it works on the wire 1. **Recording side** — a high-priority PacketEvents listener inspects every outbound packet. If the packet type or plugin-message channel is in the allowlist AND the recipient has an active recorder, the raw bytes are queued on the recorder's pending-packet list. 2. **Tick boundary** — the recorder drains the queue into the current frame's `capturedPackets` list and serializes it to the `.recap` stream as an `ACTION_CAPTURED_PACKETS` action. 3. **Playback side** — every actor's `applyFrame` reads `frame.capturedPackets` and re-sends each payload to every viewer via `User.sendPacket(ByteBuf)`. --- ## Rate limiting Replay is capped at **32 packets per actor per tick**, overflow queued to a deferred list and bled out across subsequent ticks. Without this guard a single frame with many captures (e.g. an explosion's burst of sound packets) replayed all at once and pushed the per-tick outbound count past the client's 4096-packet bundle limit. --- ## Playback-only entries skip replay Scene entries flagged `playbackOnly` (e.g. hijacker paths) skip captured-packet replay so per-lap recordings don't stack their sound / particle bytes over the NPC reactive's identical events. See [[Recap/Server Owners/Features/Hijack]]. --- ## What NOT to allowlist - **Bundle delimiters** — would nest delimiters in the replay stream and crash clients. - **Entity-data / spawn / despawn packets** — those refer to entity IDs that don't exist at playback time. - **Position / movement packets** — duplicates Recap's own movement broadcasts. - **Anything entity-ID-bound** — replayed bytes have the original recording's entity ID, not the playback NPC's. Best fits: per-location effects (sounds, lightning), plugin-message channels carrying opaque payloads to a client mod, particle bursts that are world-space not entity-space. --- ## Integration example: Lectern Lectern (a Lodestone client mod) dispatches camera, cutscene, screen-shake, and audio effects to clients via a plugin-message channel `lectern:receive_event`. Recap allowlists this channel by default — any effect Lectern fires during an active recording is captured and replays automatically when the recording plays back. If you're building a similar client-mod, allowlist your channel at plugin enable: ```java @Override public void onEnable() { var recap = RecapAPI.get(); if (recap != null) { recap.capturePluginMessageChannel("yourmod:effects"); } } ``` --- ## See Also - [[Recap/Developers/Recording Format]] — ACTION_CAPTURED_PACKETS layout - [[Recap/API/IRecap]] — full method list