Fix seeking: snapshot timing, backwards time, entity cleanup
Server: - Reset nextSnapshotTime for active clients after seek so SV_SendClientMessages doesn't skip sending (was comparing future nextSnapshotTime against past svs.time) - First frame always a keyframe (beginning seekable) - Keyframes during normal playback only reset delta state, no SNAPFLAG_RESET_ENTITIES (no visual glitch every 5 sec) Engine client: - Reset cl.oldFrameServerTime on SERVERCOUNT toggle to prevent "time went backwards" error in CL_SetCGameTime cgame: - Handle backwards time in CG_ProcessSnapshots for demo seek: accept the time jump, reset cg.time, clear all entity state (currentValid, interpolate, time-dependent fields), clear local entities (particles, gibs, smoke), wait for next snapshot - Prevents CG_InterpolateEntityPosition NULL nextSnap error Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
628007ec57
commit
a8bfa738b0
3 changed files with 46 additions and 6 deletions
|
|
@ -402,8 +402,34 @@ void CG_ProcessSnapshots( void ) {
|
|||
CG_SetNextSnap( snap );
|
||||
|
||||
|
||||
// if time went backwards, we have a level restart
|
||||
// if time went backwards, we have a level restart or demo seek
|
||||
if ( cg.nextSnap->serverTime < cg.snap->serverTime ) {
|
||||
if ( cg.svDemoPlayback ) {
|
||||
// demo seek — discard old snap, use nextSnap as current,
|
||||
// and wait for another snapshot before rendering
|
||||
cg.snap = cg.nextSnap;
|
||||
cg.nextSnap = NULL;
|
||||
cg.time = cg.snap->serverTime;
|
||||
// reset all entity state and time-dependent fields
|
||||
{
|
||||
int e;
|
||||
for ( e = 0; e < MAX_GENTITIES; e++ ) {
|
||||
cg_entities[e].currentValid = qfalse;
|
||||
cg_entities[e].interpolate = qfalse;
|
||||
cg_entities[e].muzzleFlashTime = 0;
|
||||
cg_entities[e].trailTime = 0;
|
||||
cg_entities[e].dustTrailTime = 0;
|
||||
cg_entities[e].miscTime = 0;
|
||||
cg_entities[e].snapShotTime = 0;
|
||||
cg_entities[e].previousEvent = 0;
|
||||
cg_entities[e].teleportFlag = 0;
|
||||
}
|
||||
}
|
||||
// clear local entities (particles, gibs, etc.)
|
||||
// they reference old times and would render incorrectly
|
||||
CG_InitLocalEntities();
|
||||
break; // exit loop, wait for next snapshot
|
||||
}
|
||||
CG_Error( "CG_ProcessSnapshots: Server time went backwards" );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -308,6 +308,7 @@ void CL_ParseSnapshot( msg_t *msg ) {
|
|||
cl.serverTimeDelta = cl.snap.serverTime - cls.realtime;
|
||||
cl.oldServerTime = cl.snap.serverTime;
|
||||
cl.serverTime = cl.snap.serverTime;
|
||||
cl.oldFrameServerTime = cl.snap.serverTime; // prevent backwards time error on seek
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -396,7 +396,8 @@ static qboolean SVD_StartRecording( const char *demoname ) {
|
|||
} else {
|
||||
demo.keyframeInterval = 0;
|
||||
}
|
||||
demo.framesSinceKeyframe = 0;
|
||||
// first frame is always a keyframe (makes beginning seekable)
|
||||
demo.framesSinceKeyframe = demo.keyframeInterval;
|
||||
demo.numKeyframes = 0;
|
||||
}
|
||||
|
||||
|
|
@ -1083,13 +1084,24 @@ void SVD_Seek_f( void ) {
|
|||
// toggle SERVERCOUNT to reset client time delta
|
||||
svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT;
|
||||
|
||||
// mark that we seeked — next SVD_ReadFrame sets SNAPFLAG_RESET_ENTITIES
|
||||
demo.seeked = qtrue;
|
||||
// on the next SV_Frame. SVD_ReadFrame will detect the keyframe flag
|
||||
// and set SNAPFLAG_RESET_ENTITIES + reset delta state.
|
||||
demo.endOfDemo = qfalse;
|
||||
|
||||
Com_Printf( "Seeked to keyframe at time %d.\n", demo.keyframeTimes[bestKf] );
|
||||
// reset client snapshot timing so SV_SendClientMessages doesn't
|
||||
// skip sending (nextSnapshotTime was in the future, now svs.time is past)
|
||||
{
|
||||
int j;
|
||||
for ( j = 0; j < sv_maxclients->integer; j++ ) {
|
||||
if ( svs.clients[j].state >= CS_ACTIVE ) {
|
||||
svs.clients[j].nextSnapshotTime = svs.time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ensure one frame runs on next SV_Frame
|
||||
sv.timeResidual = 1000 / sv_fps->integer;
|
||||
|
||||
Com_Printf( "Seeked to time %d.\n", svs.time );
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1101,6 +1113,7 @@ qboolean SVD_PlaybackFrame( void ) {
|
|||
return qfalse;
|
||||
}
|
||||
|
||||
|
||||
// manual pause — don't consume demo data
|
||||
if ( demo.paused ) {
|
||||
return qfalse;
|
||||
|
|
|
|||
Loading…
Reference in a new issue