svdemo_seek snaps to the nearest keyframe (fast, ±interval accuracy).
svdemo_seekexact reads forward from the keyframe to the exact target
time, giving frame-accurate positioning at the cost of a brief
processing delay.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Recording:
- svdemo_keyframeInterval cvar (default 5 seconds, 0 = disabled)
- Periodic delta state reset produces full-state frames
- Frame flag bit 1 marks keyframes for reader to reset delta state
- Keyframe index (time + file offset) written at end of file
with numKf_copy trailer for efficient playback loading
- Dynamic allocation for unlimited keyframe count
Playback:
- Keyframe index loaded from file footer on playback start
- svdemo_seek <seconds>: relative seek from current time
Finds nearest keyframe at or before target, seeks file position,
resets delta state + svs.time, sets SNAPFLAG_RESET_ENTITIES
via demo.seeked flag (one-shot on next frame read)
- Normal keyframe frames only reset delta state (no visual glitch)
- Map restart frames also set SNAPFLAG_RESET_ENTITIES
Demo format: keyframe flag (bit 1) added to frame flags byte.
Backward compatible — old demos have no keyframes (numKf=0).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
svdemo_pause toggles playback pause. When paused:
- svs.time frozen so entity trajectories freeze correctly
- No demo frames consumed (SVD_PlaybackFrame returns early)
- Game frame still runs at frozen time for spectator movement
- No time jump on unpause — svs.time resumes from where it was
Spectator movement degrades during pause (200ms PmoveSingle cap)
— will be resolved by client-owned camera in a future change.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. File handle leak: SVD_Play_f opened file twice, first handle leaked.
Fix: memset demo state before opening.
2. svdemo_stop now handles both recording and playback via SVD_Stop_f.
Playback stop disconnects client to return to menu.
3. Zombie client timeout: skip SV_CheckTimeouts during playback so
reserved player slots aren't freed.
4. Buffer overflow: increase entity buffer to MAX_GENTITIES*300 and
playerState buffer to MAX_CLIENTS*600 for worst-case first frame.
Made static to avoid stack overflow.
5. svs.time jump: don't overwrite svs.time with recorded time.
Server time advances normally, avoiding timeout/heartbeat issues.
6. map_restart: SVD_ResetDeltaState clears entity/player delta state
so next frame writes full states, preventing corrupt deltas.
7. Demo end and manual stop both disconnect the client.
8. SV_Shutdown cleans up active recording/playback.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Records full entity state array each server frame, enabling
free-camera demo playback from any viewpoint.
Recording:
- svdemo_record <name> / svdemo_stop
- Captures entityState_t + PVS fields (svFlags, linked,
currentOrigin, absmin, absmax) for all active entities
- Records configstring changes per frame
- File format: svdemos/<name>.svdm
Playback:
- svdemo_play <name>
- Loads map with maxclients=64, reserves recorded player slots
(CS_ZOMBIE with safe rate/timing) so spectator gets a high slot
- Injects recorded entities into sv.gentities each frame with
SV_LinkEntity for PVS visibility
- Re-applies demo configstrings (CS_PLAYERS etc.) after map load,
skipping CS_SERVERINFO/CS_SYSTEMINFO to avoid latch restarts
- Game module runs in demo mode (sv_demoplaying cvar): G_RunFrame
only processes spectator movement, skips all entity logic
- Spectator forced to TEAM_SPECTATOR on connect (ClientBegin)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>