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
|
||||
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_VERSION 2 // v2: optional LZ4 compression
|
||||
#define SVDEMO_VERSION 3 // v3: removed PVS data, svFlags only
|
||||
#define SVDEMO_MAX_MAPNAME 64
|
||||
|
||||
// header flags
|
||||
|
|
@ -61,14 +61,10 @@ snapshot pipeline delivers them to a spectator client.
|
|||
// State
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
// per-entity data stored for delta compression (entityState + PVS fields)
|
||||
// per-entity data stored for delta compression
|
||||
typedef struct {
|
||||
entityState_t es;
|
||||
int svFlags;
|
||||
qboolean linked;
|
||||
vec3_t currentOrigin;
|
||||
vec3_t absmin;
|
||||
vec3_t absmax;
|
||||
qboolean active; // was this entity present last frame?
|
||||
} svdEntityState_t;
|
||||
|
||||
|
|
@ -275,38 +271,17 @@ static void SVD_WriteFrame( fileHandle_t f ) {
|
|||
}
|
||||
MSG_WriteDeltaEntity( &msg, from, &ent->s, qtrue );
|
||||
|
||||
// write PVS fields only if changed from previous frame
|
||||
{
|
||||
qboolean pvsChanged = !demo.prevEntities[i].active
|
||||
|| ent->r.svFlags != demo.prevEntities[i].svFlags
|
||||
|| ent->r.linked != demo.prevEntities[i].linked
|
||||
|| !VectorCompare( ent->r.currentOrigin, demo.prevEntities[i].currentOrigin )
|
||||
|| !VectorCompare( ent->r.absmin, demo.prevEntities[i].absmin )
|
||||
|| !VectorCompare( ent->r.absmax, demo.prevEntities[i].absmax );
|
||||
|
||||
MSG_WriteBits( &msg, pvsChanged, 1 );
|
||||
if ( pvsChanged ) {
|
||||
// write svFlags only if changed (rarely changes)
|
||||
if ( ent->r.svFlags != demo.prevEntities[i].svFlags ) {
|
||||
MSG_WriteBits( &msg, 1, 1 );
|
||||
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] );
|
||||
}
|
||||
} else {
|
||||
MSG_WriteBits( &msg, 0, 1 );
|
||||
}
|
||||
|
||||
// update prev state
|
||||
demo.prevEntities[i].es = ent->s;
|
||||
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;
|
||||
} else if ( demo.prevEntities[i].active ) {
|
||||
// entity was removed — write a remove marker
|
||||
|
|
@ -734,40 +709,20 @@ static qboolean SVD_ReadFrame( fileHandle_t f ) {
|
|||
continue;
|
||||
}
|
||||
|
||||
// read PVS fields
|
||||
{
|
||||
qboolean pvsChanged = MSG_ReadBits( &msg, 1 );
|
||||
if ( pvsChanged ) {
|
||||
// read svFlags
|
||||
if ( MSG_ReadBits( &msg, 1 ) ) {
|
||||
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].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->s = newEs;
|
||||
ent->s.number = entNum;
|
||||
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 ) {
|
||||
sv.num_entities = entNum + 1;
|
||||
|
|
|
|||
Loading…
Reference in a new issue