Demo pause: freeze svs.time, hold demo data consumption
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>
This commit is contained in:
parent
9799952b34
commit
7141d941a3
4 changed files with 1087 additions and 1056 deletions
|
|
@ -353,9 +353,11 @@ void SVD_CaptureServerCommand( const char *cmd );
|
||||||
void SVD_Play_f( void );
|
void SVD_Play_f( void );
|
||||||
void SVD_StopPlay_f( void );
|
void SVD_StopPlay_f( void );
|
||||||
void SVD_Stop_f( void );
|
void SVD_Stop_f( void );
|
||||||
|
void SVD_Pause_f( void );
|
||||||
qboolean SVD_PlaybackFrame( void );
|
qboolean SVD_PlaybackFrame( void );
|
||||||
qboolean SVD_IsRecording( void );
|
qboolean SVD_IsRecording( void );
|
||||||
qboolean SVD_IsPlaying( void );
|
qboolean SVD_IsPlaying( void );
|
||||||
|
qboolean SVD_IsPaused( void );
|
||||||
qboolean SVD_ShouldPause( void );
|
qboolean SVD_ShouldPause( void );
|
||||||
|
|
||||||
//============================================================
|
//============================================================
|
||||||
|
|
|
||||||
|
|
@ -744,6 +744,7 @@ void SV_AddOperatorCommands( void ) {
|
||||||
Cmd_AddCommand ("svdemo_record", SVD_Record_f);
|
Cmd_AddCommand ("svdemo_record", SVD_Record_f);
|
||||||
Cmd_AddCommand ("svdemo_stop", SVD_Stop_f);
|
Cmd_AddCommand ("svdemo_stop", SVD_Stop_f);
|
||||||
Cmd_AddCommand ("svdemo_play", SVD_Play_f);
|
Cmd_AddCommand ("svdemo_play", SVD_Play_f);
|
||||||
|
Cmd_AddCommand ("svdemo_pause", SVD_Pause_f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -831,6 +831,15 @@ void SV_Frame( int msec ) {
|
||||||
// run the game simulation in chunks
|
// run the game simulation in chunks
|
||||||
while ( sv.timeResidual >= frameMsec ) {
|
while ( sv.timeResidual >= frameMsec ) {
|
||||||
sv.timeResidual -= frameMsec;
|
sv.timeResidual -= frameMsec;
|
||||||
|
|
||||||
|
if ( SVD_IsPaused() ) {
|
||||||
|
// demo paused: freeze svs.time so trajectories freeze
|
||||||
|
// and client doesn't see time jumps on unpause.
|
||||||
|
// still run game frame for spectator movement (at frozen time).
|
||||||
|
VM_Call( gvm, GAME_RUN_FRAME, svs.time );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
svs.time += frameMsec;
|
svs.time += frameMsec;
|
||||||
|
|
||||||
if ( SVD_IsPlaying() ) {
|
if ( SVD_IsPlaying() ) {
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@ typedef struct {
|
||||||
char playMapName[SVDEMO_MAX_MAPNAME];
|
char playMapName[SVDEMO_MAX_MAPNAME];
|
||||||
qboolean endOfDemo;
|
qboolean endOfDemo;
|
||||||
qboolean needConfigstrings; // apply saved configstrings on first frame
|
qboolean needConfigstrings; // apply saved configstrings on first frame
|
||||||
|
qboolean paused;
|
||||||
svdEntityState_t playPrevEntities[MAX_GENTITIES]; // previous frame for delta read
|
svdEntityState_t playPrevEntities[MAX_GENTITIES]; // previous frame for delta read
|
||||||
svdPlayerState_t playPrevPlayers[MAX_CLIENTS]; // previous frame player states
|
svdPlayerState_t playPrevPlayers[MAX_CLIENTS]; // previous frame player states
|
||||||
} svDemo_t;
|
} svDemo_t;
|
||||||
|
|
@ -987,6 +988,15 @@ void SVD_Stop_f( void ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SVD_Pause_f( void ) {
|
||||||
|
if ( !demo.playing ) {
|
||||||
|
Com_Printf( "Not playing a server demo.\n" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
demo.paused = !demo.paused;
|
||||||
|
Com_Printf( "Demo playback %s.\n", demo.paused ? "paused" : "resumed" );
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Called from SV_Frame() to advance playback by one frame.
|
Called from SV_Frame() to advance playback by one frame.
|
||||||
Returns qtrue if a frame was read, qfalse if demo ended.
|
Returns qtrue if a frame was read, qfalse if demo ended.
|
||||||
|
|
@ -996,6 +1006,11 @@ qboolean SVD_PlaybackFrame( void ) {
|
||||||
return qfalse;
|
return qfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// manual pause — don't consume demo data
|
||||||
|
if ( demo.paused ) {
|
||||||
|
return qfalse;
|
||||||
|
}
|
||||||
|
|
||||||
// wait for a spectator to be fully in-game before starting playback.
|
// wait for a spectator to be fully in-game before starting playback.
|
||||||
// the server keeps running frames (so the connection handshake completes)
|
// the server keeps running frames (so the connection handshake completes)
|
||||||
// but no demo data is consumed until someone is CS_ACTIVE.
|
// but no demo data is consumed until someone is CS_ACTIVE.
|
||||||
|
|
@ -1034,6 +1049,10 @@ qboolean SVD_IsPlaying( void ) {
|
||||||
return demo.playing;
|
return demo.playing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qboolean SVD_IsPaused( void ) {
|
||||||
|
return demo.playing && demo.paused;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Returns qtrue if demo playback should pause (no active spectators).
|
Returns qtrue if demo playback should pause (no active spectators).
|
||||||
Controlled by svdemo_pauseEmpty cvar.
|
Controlled by svdemo_pauseEmpty cvar.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue