Commit graph

18 commits

Author SHA1 Message Date
5a18130ba0 Force non-delta snapshot on map_restart during playback
snapFlagServerBit toggle alone doesn't clear client entity
interpolation state. Also reset deltaMessage=-1 for all active
clients, forcing the next snapshot to be full (non-delta). The
client receives deltaNum<=0, clears old entity state, and
renders all entities at their new positions without interpolation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 06:08:34 +08:00
479443513a Toggle snapFlagServerBit on map_restart during demo playback
Record a per-frame restart flag (1 byte) set by SVD_ResetDeltaState
when map_restart occurs. During playback, toggle svs.snapFlagServerBit
so the client treats the next snapshot as a fresh baseline — no
interpolation of entities from pre-restart positions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 06:03:45 +08:00
cc081ddee4 Fix chat capture: capture any target, deduplicate in buffer
Per-client chat is sent to every connected client in a loop.
Capture chat/tchat commands regardless of target clientNum, and
deduplicate by checking if the exact string is already buffered
for the current frame.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 05:55:19 +08:00
90b26dc2a1 Capture per-client chat commands for demo recording
Chat messages use per-client trap_SendServerCommand (not broadcast),
so they weren't captured. Move capture hook to SV_GameSendServerCommand
which is the game module boundary. Capture broadcasts (clientNum=-1)
and chat/tchat commands sent to client 0 (first in the per-client
loop, deduplicated by only capturing once).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 05:53:53 +08:00
8b7ec11034 Fix server command replay: broadcast instead of targeting slot 63
spectatorClientNum was hardcoded to MAX_CLIENTS-1 (63) but the
spectator actually connects at the first free slot after zombie
reservations. Broadcasting with SV_SendServerCommand(NULL, ...)
reaches the spectator regardless of their actual slot number.
Zombie clients (< CS_PRIMED) are skipped by the broadcast loop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 05:50:15 +08:00
0d1f1d515e Restore svs.time sync for trajectory interpolation
Reverting the svs.time change from the bugfix commit — entity
trajectories (rockets, grenades, bobbing items) need svs.time to
match recorded time for client-side interpolation. The zombie
timeout issue is already handled by skipping SV_CheckTimeouts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 05:47:51 +08:00
4f0d46024b Record and replay broadcast server commands (chat, prints)
Capture broadcast server commands (chat, print, cp, etc.) from
SV_SendServerCommand when cl==NULL. Buffer up to 64 commands per
frame. Written after configstrings in the demo file, replayed to
the spectator client during playback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 05:41:18 +08:00
330cc30ae7 Optional LZ4 compression for demo files
Per-frame entity and playerState blocks are compressed with LZ4
when svdemo_compress is set (default: 1). The block format writes
[original_size][compressed_size][data] — compressed_size=0 means
uncompressed. Playback auto-detects based on header flags.

Demo format bumped to version 2 with SVDEMO_FLAG_COMPRESSED flag.
Version 1 (uncompressed) demos are no longer compatible.

Uses the lz4.c/lz4.h library already in the server code directory.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 05:22:41 +08:00
e58414f564 Auto-record demos with svdemo_autorecord cvar
Set svdemo_autorecord 1 to automatically record a demo on every
map load. Demo files are named <mapname>_YYYYMMDD_HHMMSS.svdm
in the svdemos/ directory.

Refactored SVD_Record_f to use SVD_StartRecording helper so both
manual and auto recording share the same code path. Also fixed
prevPlayers delta state not being cleared on recording start.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 05:11:27 +08:00
6fc96e7070 Auto-stop demo recording on map change
SV_SpawnServer reinitializes the game module, invalidating all
entity and configstring state. Recording across a map boundary
would produce corrupt deltas. Stop recording automatically at
the start of SV_SpawnServer (before game shutdown).

map_restart is unaffected — it has its own path (SV_MapRestart_f)
which doesn't call SV_SpawnServer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 05:08:38 +08:00
5987109014 Filter CS_SERVERINFO/SYSTEMINFO in per-frame configstring changes
The per-frame configstring path in SVD_ReadFrame was not filtering
dangerous configstrings like SVD_ApplyConfigstrings does. When a
recorded map_restart changed CS_SERVERINFO (containing maxclients),
applying it overwrote maxclients=64 with the recorded value,
triggering a latched restart loop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 05:06:27 +08:00
a1167ff398 Fix 8 netdemo bugs found in code review
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>
2026-03-23 05:03:33 +08:00
b48a0575f1 Proper demo playback cleanup and state reset
- Extract SVD_CleanupPlayback for shared cleanup logic
- Called on demo end (SVD_PlaybackFrame) and manual stop
- Frees zombie client slots, saved configstrings, file handle
- Clears sv_demoplaying cvar so game module exits demo mode
- Hook into SV_Shutdown to clean up on server shutdown/map change
- Allows playing another demo after one finishes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 04:55:12 +08:00
1a186eeb81 Fix player disconnect handling during demo playback
Clear the game module's playerState when a recorded player
disconnects, so G_RunFrame sees commandTime=0 and marks them
as CON_DISCONNECTED. Without this, disconnected players stayed
visible on the scoreboard indefinitely.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 04:48:35 +08:00
1f8c8aea1c Record playerStates for scoreboard and future follow mode
Record delta-compressed playerState_t for each active player per
frame using MSG_WriteDeltaPlayerstate. During playback, inject
into game module via SV_GameClientNum and mark players as
CON_CONNECTED with correct team. CalculateRanks and the
scoreboard now show recorded players with scores.

This also lays the groundwork for player-follow spectating.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 04:46:40 +08:00
de9863da57 Delta-compress netdemo entity states
Use MSG_WriteDeltaEntity/MSG_ReadDeltaEntity for entity state
serialization. Only changed fields are written per frame. PVS
fields (svFlags, linked, origin, absmin, absmax) also use a
1-bit change flag to skip when unchanged.

Reduces a 10-second demo from ~1400KB to ~52KB (27x smaller).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 04:36:02 +08:00
a8044aad8b Server-side demo recording and playback (netdemo)
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>
2026-03-23 04:28:55 +08:00
Sergei Shubin
4c57221941 Initial commit: Quake 3 1.32b GPL source 2026-03-18 13:32:24 +08:00