SNAPFLAG_RESET_ENTITIES: no-interpolation reset without gamestate resend
Replace the gamestate resend approach for map_restart with a custom snapshot flag (SNAPFLAG_RESET_ENTITIES, bit 4 in snapFlags byte). Server side (sv_netdemo.c): - On restart frame, OR the flag into svs.snapFlagServerBit (one-shot) - Cleared at the start of the next playback frame Client side (cg_snapshot.c): - CG_SetNextSnap: clear currentValid for all entities when flag is set, making them all "new" — existing interpolation check at line 228 sets interpolate=qfalse, CG_TransitionEntity calls CG_ResetEntity - Also set cg.nextFrameTeleport=qtrue to prevent playerstate interpolation during follow mode No loading screen, no lost frames, no gamestate resend. Entities and playerstate both snap to correct positions on map_restart. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0b4eb7b69f
commit
c6af63ae42
4 changed files with 22 additions and 13 deletions
|
|
@ -203,6 +203,15 @@ static void CG_SetNextSnap( snapshot_t *snap ) {
|
||||||
|
|
||||||
cg.nextSnap = snap;
|
cg.nextSnap = snap;
|
||||||
|
|
||||||
|
// SNAPFLAG_RESET_ENTITIES: invalidate all entities so they are
|
||||||
|
// treated as new (no interpolation from old positions).
|
||||||
|
// Must happen before the entity loop below.
|
||||||
|
if ( snap->snapFlags & SNAPFLAG_RESET_ENTITIES ) {
|
||||||
|
for ( num = 0 ; num < MAX_GENTITIES ; num++ ) {
|
||||||
|
cg_entities[ num ].currentValid = qfalse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].nextState, qfalse );
|
BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].nextState, qfalse );
|
||||||
cg_entities[ cg.snap->ps.clientNum ].interpolate = qtrue;
|
cg_entities[ cg.snap->ps.clientNum ].interpolate = qtrue;
|
||||||
|
|
||||||
|
|
@ -241,6 +250,11 @@ static void CG_SetNextSnap( snapshot_t *snap ) {
|
||||||
cg.nextFrameTeleport = qtrue;
|
cg.nextFrameTeleport = qtrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// entity reset also prevents playerstate interpolation
|
||||||
|
if ( cg.nextSnap->snapFlags & SNAPFLAG_RESET_ENTITIES ) {
|
||||||
|
cg.nextFrameTeleport = qtrue;
|
||||||
|
}
|
||||||
|
|
||||||
// sort out solid entities
|
// sort out solid entities
|
||||||
CG_BuildSolidList();
|
CG_BuildSolidList();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1085,6 +1085,7 @@ typedef enum {
|
||||||
#define SNAPFLAG_RATE_DELAYED 1
|
#define SNAPFLAG_RATE_DELAYED 1
|
||||||
#define SNAPFLAG_NOT_ACTIVE 2 // snapshot used during connection and for zombies
|
#define SNAPFLAG_NOT_ACTIVE 2 // snapshot used during connection and for zombies
|
||||||
#define SNAPFLAG_SERVERCOUNT 4 // toggled every map_restart so transitions can be detected
|
#define SNAPFLAG_SERVERCOUNT 4 // toggled every map_restart so transitions can be detected
|
||||||
|
#define SNAPFLAG_RESET_ENTITIES 16 // snap all entities to current position, no interpolation
|
||||||
|
|
||||||
//
|
//
|
||||||
// per-level limits
|
// per-level limits
|
||||||
|
|
|
||||||
|
|
@ -289,7 +289,6 @@ void SV_AuthorizeIpPacket( netadr_t from );
|
||||||
void SV_ExecuteClientMessage( client_t *cl, msg_t *msg );
|
void SV_ExecuteClientMessage( client_t *cl, msg_t *msg );
|
||||||
void SV_UserinfoChanged( client_t *cl );
|
void SV_UserinfoChanged( client_t *cl );
|
||||||
|
|
||||||
void SV_SendClientGameState( client_t *client );
|
|
||||||
void SV_ClientEnterWorld( client_t *client, usercmd_t *cmd );
|
void SV_ClientEnterWorld( client_t *client, usercmd_t *cmd );
|
||||||
void SV_DropClient( client_t *drop, const char *reason );
|
void SV_DropClient( client_t *drop, const char *reason );
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -677,22 +677,14 @@ static qboolean SVD_ReadFrame( fileHandle_t f ) {
|
||||||
byte frameFlags;
|
byte frameFlags;
|
||||||
FS_Read( &frameFlags, 1, f );
|
FS_Read( &frameFlags, 1, f );
|
||||||
if ( frameFlags & 1 ) {
|
if ( frameFlags & 1 ) {
|
||||||
int j;
|
|
||||||
// map was restarted — reset playback delta state to match
|
// map was restarted — reset playback delta state to match
|
||||||
// the recording's reset (both now decode from zero baseline).
|
// the recording's reset (both now decode from zero baseline).
|
||||||
// Also toggle server bit and force non-delta snapshot so the
|
|
||||||
// client clears old entity state and doesn't interpolate.
|
|
||||||
Com_Memset( demo.playPrevEntities, 0, sizeof(demo.playPrevEntities) );
|
Com_Memset( demo.playPrevEntities, 0, sizeof(demo.playPrevEntities) );
|
||||||
Com_Memset( demo.playPrevPlayers, 0, sizeof(demo.playPrevPlayers) );
|
Com_Memset( demo.playPrevPlayers, 0, sizeof(demo.playPrevPlayers) );
|
||||||
svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT;
|
// set one-shot SNAPFLAG_RESET_ENTITIES so cgame snaps all
|
||||||
// send a full gamestate to all active clients — this makes
|
// entities to current position without interpolation.
|
||||||
// CL_ParseGamestate → CL_ClearState wipe all snapshot/entity
|
// OR'd into snapFlagServerBit, cleared after one frame.
|
||||||
// history, exactly like a real map_restart does.
|
svs.snapFlagServerBit |= SNAPFLAG_RESET_ENTITIES;
|
||||||
for ( j = 0; j < sv_maxclients->integer; j++ ) {
|
|
||||||
if ( svs.clients[j].state >= CS_PRIMED ) {
|
|
||||||
SV_SendClientGameState( &svs.clients[j] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1020,6 +1012,9 @@ qboolean SVD_PlaybackFrame( void ) {
|
||||||
demo.needConfigstrings = qfalse;
|
demo.needConfigstrings = qfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear one-shot reset flag from previous frame before reading new one
|
||||||
|
svs.snapFlagServerBit &= ~SNAPFLAG_RESET_ENTITIES;
|
||||||
|
|
||||||
if ( !SVD_ReadFrame( demo.playFile ) ) {
|
if ( !SVD_ReadFrame( demo.playFile ) ) {
|
||||||
Com_Printf( "Server demo playback finished.\n" );
|
Com_Printf( "Server demo playback finished.\n" );
|
||||||
SVD_CleanupPlayback();
|
SVD_CleanupPlayback();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue