Remove PVS data from demo format, derive during playback
Stop recording currentOrigin, absmin, absmax, linked per entity (44 bytes per moving entity per frame). During playback, G_RunFrame computes currentOrigin via BG_EvaluateTrajectory and calls trap_LinkEntity to register in BSP for PVS. svFlags still recorded (1-bit change flag + 4 bytes when changed). Entity linking moved from SVD_ReadFrame (server, no trajectory eval) to G_RunFrame demo mode (game module, has BG_EvaluateTrajectory). Scan all MAX_GENTITIES during playback since recorded entities may have indices above level.num_entities (game-module-spawned count). Demo format bumped to v3. Significant file size reduction. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5a98ef02cf
commit
fe628a2cc4
2 changed files with 34 additions and 58 deletions
|
|
@ -1792,6 +1792,27 @@ int start, end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// evaluate trajectories and link all recorded entities for PVS.
|
||||||
|
// SVD_ReadFrame injected entityState_t but didn't link because
|
||||||
|
// the server doesn't have BG_EvaluateTrajectory.
|
||||||
|
// scan up to MAX_GENTITIES because recorded entities may exceed
|
||||||
|
// level.num_entities (which only counts game-module-spawned entities).
|
||||||
|
for ( i = 0; i < MAX_GENTITIES; i++ ) {
|
||||||
|
ent = &g_entities[i];
|
||||||
|
if ( ent->s.eType == 0 && !ent->inuse ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// skip spectator slot
|
||||||
|
if ( ent->client && ent->client->pers.connected == CON_CONNECTED
|
||||||
|
&& ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// compute currentOrigin from trajectory
|
||||||
|
BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->r.currentOrigin );
|
||||||
|
ent->r.linked = qtrue;
|
||||||
|
trap_LinkEntity( ent );
|
||||||
|
}
|
||||||
|
|
||||||
// update rankings so follow mode can cycle through players
|
// update rankings so follow mode can cycle through players
|
||||||
CalculateRanks();
|
CalculateRanks();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ snapshot pipeline delivers them to a spectator client.
|
||||||
//
|
//
|
||||||
|
|
||||||
#define SVDEMO_MAGIC (('S') | ('V' << 8) | ('D' << 16) | ('M' << 24))
|
#define SVDEMO_MAGIC (('S') | ('V' << 8) | ('D' << 16) | ('M' << 24))
|
||||||
#define SVDEMO_VERSION 2 // v2: optional LZ4 compression
|
#define SVDEMO_VERSION 3 // v3: removed PVS data, svFlags only
|
||||||
#define SVDEMO_MAX_MAPNAME 64
|
#define SVDEMO_MAX_MAPNAME 64
|
||||||
|
|
||||||
// header flags
|
// header flags
|
||||||
|
|
@ -61,14 +61,10 @@ snapshot pipeline delivers them to a spectator client.
|
||||||
// State
|
// State
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
// per-entity data stored for delta compression (entityState + PVS fields)
|
// per-entity data stored for delta compression
|
||||||
typedef struct {
|
typedef struct {
|
||||||
entityState_t es;
|
entityState_t es;
|
||||||
int svFlags;
|
int svFlags;
|
||||||
qboolean linked;
|
|
||||||
vec3_t currentOrigin;
|
|
||||||
vec3_t absmin;
|
|
||||||
vec3_t absmax;
|
|
||||||
qboolean active; // was this entity present last frame?
|
qboolean active; // was this entity present last frame?
|
||||||
} svdEntityState_t;
|
} svdEntityState_t;
|
||||||
|
|
||||||
|
|
@ -275,38 +271,17 @@ static void SVD_WriteFrame( fileHandle_t f ) {
|
||||||
}
|
}
|
||||||
MSG_WriteDeltaEntity( &msg, from, &ent->s, qtrue );
|
MSG_WriteDeltaEntity( &msg, from, &ent->s, qtrue );
|
||||||
|
|
||||||
// write PVS fields only if changed from previous frame
|
// write svFlags only if changed (rarely changes)
|
||||||
{
|
if ( ent->r.svFlags != demo.prevEntities[i].svFlags ) {
|
||||||
qboolean pvsChanged = !demo.prevEntities[i].active
|
MSG_WriteBits( &msg, 1, 1 );
|
||||||
|| ent->r.svFlags != demo.prevEntities[i].svFlags
|
MSG_WriteLong( &msg, ent->r.svFlags );
|
||||||
|| ent->r.linked != demo.prevEntities[i].linked
|
} else {
|
||||||
|| !VectorCompare( ent->r.currentOrigin, demo.prevEntities[i].currentOrigin )
|
MSG_WriteBits( &msg, 0, 1 );
|
||||||
|| !VectorCompare( ent->r.absmin, demo.prevEntities[i].absmin )
|
|
||||||
|| !VectorCompare( ent->r.absmax, demo.prevEntities[i].absmax );
|
|
||||||
|
|
||||||
MSG_WriteBits( &msg, pvsChanged, 1 );
|
|
||||||
if ( pvsChanged ) {
|
|
||||||
MSG_WriteLong( &msg, ent->r.svFlags );
|
|
||||||
MSG_WriteLong( &msg, ent->r.linked );
|
|
||||||
MSG_WriteFloat( &msg, ent->r.currentOrigin[0] );
|
|
||||||
MSG_WriteFloat( &msg, ent->r.currentOrigin[1] );
|
|
||||||
MSG_WriteFloat( &msg, ent->r.currentOrigin[2] );
|
|
||||||
MSG_WriteFloat( &msg, ent->r.absmin[0] );
|
|
||||||
MSG_WriteFloat( &msg, ent->r.absmin[1] );
|
|
||||||
MSG_WriteFloat( &msg, ent->r.absmin[2] );
|
|
||||||
MSG_WriteFloat( &msg, ent->r.absmax[0] );
|
|
||||||
MSG_WriteFloat( &msg, ent->r.absmax[1] );
|
|
||||||
MSG_WriteFloat( &msg, ent->r.absmax[2] );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update prev state
|
// update prev state
|
||||||
demo.prevEntities[i].es = ent->s;
|
demo.prevEntities[i].es = ent->s;
|
||||||
demo.prevEntities[i].svFlags = ent->r.svFlags;
|
demo.prevEntities[i].svFlags = ent->r.svFlags;
|
||||||
demo.prevEntities[i].linked = ent->r.linked;
|
|
||||||
VectorCopy( ent->r.currentOrigin, demo.prevEntities[i].currentOrigin );
|
|
||||||
VectorCopy( ent->r.absmin, demo.prevEntities[i].absmin );
|
|
||||||
VectorCopy( ent->r.absmax, demo.prevEntities[i].absmax );
|
|
||||||
demo.prevEntities[i].active = qtrue;
|
demo.prevEntities[i].active = qtrue;
|
||||||
} else if ( demo.prevEntities[i].active ) {
|
} else if ( demo.prevEntities[i].active ) {
|
||||||
// entity was removed — write a remove marker
|
// entity was removed — write a remove marker
|
||||||
|
|
@ -734,40 +709,20 @@ static qboolean SVD_ReadFrame( fileHandle_t f ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// read PVS fields
|
// read svFlags
|
||||||
{
|
if ( MSG_ReadBits( &msg, 1 ) ) {
|
||||||
qboolean pvsChanged = MSG_ReadBits( &msg, 1 );
|
demo.playPrevEntities[entNum].svFlags = MSG_ReadLong( &msg );
|
||||||
if ( pvsChanged ) {
|
|
||||||
demo.playPrevEntities[entNum].svFlags = MSG_ReadLong( &msg );
|
|
||||||
demo.playPrevEntities[entNum].linked = MSG_ReadLong( &msg );
|
|
||||||
demo.playPrevEntities[entNum].currentOrigin[0] = MSG_ReadFloat( &msg );
|
|
||||||
demo.playPrevEntities[entNum].currentOrigin[1] = MSG_ReadFloat( &msg );
|
|
||||||
demo.playPrevEntities[entNum].currentOrigin[2] = MSG_ReadFloat( &msg );
|
|
||||||
demo.playPrevEntities[entNum].absmin[0] = MSG_ReadFloat( &msg );
|
|
||||||
demo.playPrevEntities[entNum].absmin[1] = MSG_ReadFloat( &msg );
|
|
||||||
demo.playPrevEntities[entNum].absmin[2] = MSG_ReadFloat( &msg );
|
|
||||||
demo.playPrevEntities[entNum].absmax[0] = MSG_ReadFloat( &msg );
|
|
||||||
demo.playPrevEntities[entNum].absmax[1] = MSG_ReadFloat( &msg );
|
|
||||||
demo.playPrevEntities[entNum].absmax[2] = MSG_ReadFloat( &msg );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
demo.playPrevEntities[entNum].es = newEs;
|
demo.playPrevEntities[entNum].es = newEs;
|
||||||
demo.playPrevEntities[entNum].active = qtrue;
|
demo.playPrevEntities[entNum].active = qtrue;
|
||||||
|
|
||||||
// apply to server entity
|
// apply to server entity (entity linking done in G_RunFrame
|
||||||
|
// which has BG_EvaluateTrajectory for computing currentOrigin)
|
||||||
ent = SV_GentityNum( entNum );
|
ent = SV_GentityNum( entNum );
|
||||||
ent->s = newEs;
|
ent->s = newEs;
|
||||||
ent->s.number = entNum;
|
ent->s.number = entNum;
|
||||||
ent->r.svFlags = demo.playPrevEntities[entNum].svFlags;
|
ent->r.svFlags = demo.playPrevEntities[entNum].svFlags;
|
||||||
ent->r.linked = demo.playPrevEntities[entNum].linked;
|
|
||||||
VectorCopy( demo.playPrevEntities[entNum].currentOrigin, ent->r.currentOrigin );
|
|
||||||
VectorCopy( demo.playPrevEntities[entNum].absmin, ent->r.absmin );
|
|
||||||
VectorCopy( demo.playPrevEntities[entNum].absmax, ent->r.absmax );
|
|
||||||
|
|
||||||
if ( ent->r.linked ) {
|
|
||||||
SV_LinkEntity( ent );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( entNum + 1 > sv.num_entities ) {
|
if ( entNum + 1 > sv.num_entities ) {
|
||||||
sv.num_entities = entNum + 1;
|
sv.num_entities = entNum + 1;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue