Fix spectator display and recorded spectator handling
- Set sv_demoplaying before devmap so game module knows during ClientConnect/ClientBegin - Call ClientUserinfoChanged after forcing spectator team so CS_PLAYERS configstring has correct team value for cgame - Record sanitized playerState for spectators (pm_type=PM_SPECTATOR, PERS_TEAM=TEAM_SPECTATOR) so they show correctly on scoreboard instead of appearing as regular players from follow-mode corruption Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d78f5c7aaf
commit
fcd5fc6ce8
2 changed files with 23 additions and 5 deletions
|
|
@ -1013,10 +1013,10 @@ void ClientBegin( int clientNum ) {
|
||||||
client->pers.teamState.state = TEAM_BEGIN;
|
client->pers.teamState.state = TEAM_BEGIN;
|
||||||
|
|
||||||
// demo playback: force all clients to spectator
|
// demo playback: force all clients to spectator
|
||||||
|
// demo playback: force to spectator and update configstring
|
||||||
if ( g_svDemoPlaying.integer ) {
|
if ( g_svDemoPlaying.integer ) {
|
||||||
client->sess.sessionTeam = TEAM_SPECTATOR;
|
client->sess.sessionTeam = TEAM_SPECTATOR;
|
||||||
client->ps.pm_type = PM_SPECTATOR;
|
ClientUserinfoChanged( ent->client - level.clients );
|
||||||
client->ps.persistant[PERS_TEAM] = TEAM_SPECTATOR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// save eflags around this, because changing teams will
|
// save eflags around this, because changing teams will
|
||||||
|
|
|
||||||
|
|
@ -336,6 +336,23 @@ static void SVD_WriteFrame( fileHandle_t f ) {
|
||||||
playerState_t *ps = SV_GameClientNum( i );
|
playerState_t *ps = SV_GameClientNum( i );
|
||||||
client_t *cl = &svs.clients[i];
|
client_t *cl = &svs.clients[i];
|
||||||
qboolean active = ( cl->state >= CS_ACTIVE );
|
qboolean active = ( cl->state >= CS_ACTIVE );
|
||||||
|
qboolean isSpectator;
|
||||||
|
playerState_t specPs;
|
||||||
|
|
||||||
|
// detect spectators: free cam or follow mode
|
||||||
|
isSpectator = active && ( ps->pm_type == PM_SPECTATOR || (ps->pm_flags & PMF_FOLLOW) );
|
||||||
|
|
||||||
|
// for spectators, record a sanitized ps so they appear on
|
||||||
|
// the scoreboard as spectators (follow mode corrupts their ps
|
||||||
|
// with the followed player's data)
|
||||||
|
if ( isSpectator ) {
|
||||||
|
Com_Memset( &specPs, 0, sizeof(specPs) );
|
||||||
|
specPs.commandTime = ps->commandTime;
|
||||||
|
specPs.pm_type = PM_SPECTATOR;
|
||||||
|
specPs.persistant[PERS_TEAM] = TEAM_SPECTATOR;
|
||||||
|
specPs.clientNum = i;
|
||||||
|
ps = &specPs;
|
||||||
|
}
|
||||||
|
|
||||||
if ( active ) {
|
if ( active ) {
|
||||||
playerState_t *from = demo.prevPlayers[i].active ? &demo.prevPlayers[i].ps : NULL;
|
playerState_t *from = demo.prevPlayers[i].active ? &demo.prevPlayers[i].ps : NULL;
|
||||||
|
|
@ -897,14 +914,15 @@ void SVD_Play_f( void ) {
|
||||||
Com_Printf( "Playing server demo: map=%s maxclients=%d fps=%d\n",
|
Com_Printf( "Playing server demo: map=%s maxclients=%d fps=%d\n",
|
||||||
demo.playMapName, demo.playMaxClients, demo.playFps );
|
demo.playMapName, demo.playMaxClients, demo.playFps );
|
||||||
|
|
||||||
|
// Signal demo mode BEFORE map load so the game module knows
|
||||||
|
// during ClientConnect/ClientBegin to force spectator team.
|
||||||
|
Cvar_Set( "sv_demoplaying", "1" );
|
||||||
|
|
||||||
// Load the map with maxclients = MAX_CLIENTS to avoid entity slot collisions.
|
// Load the map with maxclients = MAX_CLIENTS to avoid entity slot collisions.
|
||||||
Cbuf_ExecuteText( EXEC_NOW, va("set sv_maxclients %d\n", MAX_CLIENTS) );
|
Cbuf_ExecuteText( EXEC_NOW, va("set sv_maxclients %d\n", MAX_CLIENTS) );
|
||||||
Cbuf_ExecuteText( EXEC_NOW, va("set sv_fps %d\n", demo.playFps) );
|
Cbuf_ExecuteText( EXEC_NOW, va("set sv_fps %d\n", demo.playFps) );
|
||||||
Cbuf_ExecuteText( EXEC_NOW, va("devmap %s\n", demo.playMapName) );
|
Cbuf_ExecuteText( EXEC_NOW, va("devmap %s\n", demo.playMapName) );
|
||||||
|
|
||||||
// Signal demo mode to the game module AFTER map load
|
|
||||||
Cvar_Set( "sv_demoplaying", "1" );
|
|
||||||
|
|
||||||
// Reserve recorded player slots so the connecting spectator
|
// Reserve recorded player slots so the connecting spectator
|
||||||
// doesn't land in slot 0 (which collides with recorded player 0).
|
// doesn't land in slot 0 (which collides with recorded player 0).
|
||||||
// Mark as CS_ZOMBIE (non-free, won't be reused by SV_DirectConnect).
|
// Mark as CS_ZOMBIE (non-free, won't be reused by SV_DirectConnect).
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue