Fix follow mode switching and PVS during pause

Three issues fixed:

1. ClientThink called ClientThink_real during demo playback, which
   ran server-side PmoveSingle and overwrote the client-owned
   ps.origin. Now ClientThink returns early in demo mode after
   updating pers.cmd (for button access).

2. Buttons never reached the server while paused: SV_UserMove
   discards usercmds with duplicate serverTime (line 1421). During
   pause, all usercmds have frozen serverTime. Fix: always process
   the last usercmd in the packet during demo playback.

3. Spectator button handling in G_RunFrame demo mode processes
   MOUSE1 (attack) to cycle follow targets via Cmd_FollowCycle_f.
   Removed debug print.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
serge_shubin 2026-03-24 04:31:39 +08:00
parent c4dca5f950
commit 2ce4aa88f2
3 changed files with 23 additions and 1 deletions

View file

@ -1034,6 +1034,12 @@ void ClientThink( int clientNum ) {
// phone jack if they don't get any for a while
ent->client->lastCmdTime = level.time;
// demo playback: don't run ClientThink_real — cgame owns
// the camera movement, G_RunFrame handles buttons and PVS origin
if ( g_svDemoPlaying.integer ) {
return;
}
if ( !(ent->r.svFlags & SVF_BOT) && !g_synchronousClients.integer ) {
ClientThink_real( ent );
}

View file

@ -1761,6 +1761,17 @@ int start, end;
VectorCopy( cl->pers.cmd.origin, e->s.pos.trBase );
VectorCopy( cl->pers.cmd.origin, e->r.currentOrigin );
}
// process spectator buttons for follow mode switching
// (pers.cmd is updated by ClientThink which still runs)
{
int oldButtons = cl->buttons;
cl->oldbuttons = cl->buttons;
cl->buttons = cl->pers.cmd.buttons;
// attack cycles follow targets
if ( ( cl->buttons & BUTTON_ATTACK ) && !( oldButtons & BUTTON_ATTACK ) ) {
Cmd_FollowCycle_f( e, 1 );
}
}
specEnt = e;
continue;
}

View file

@ -1419,8 +1419,13 @@ static void SV_UserMove( client_t *cl, msg_t *msg, qboolean delta ) {
// don't execute if this is an old cmd which is already executed
// these old cmds are included when cl_packetdup > 0
if ( cmds[i].serverTime <= cl->lastUsercmd.serverTime ) {
// demo playback: still process the LAST cmd even with duplicate
// serverTime (paused = frozen time, all cmds have same time).
// Need buttons and origin from fresh usercmds.
if ( !SVD_IsPlaying() || i != cmdCount - 1 ) {
continue;
}
}
SV_ClientThink (cl, &cmds[ i ]);
}
}