Record and replay broadcast server commands (chat, prints)
Capture broadcast server commands (chat, print, cp, etc.) from SV_SendServerCommand when cl==NULL. Buffer up to 64 commands per frame. Written after configstrings in the demo file, replayed to the spectator client during playback. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
60b50ce224
commit
4f0d46024b
3 changed files with 53 additions and 0 deletions
|
|
@ -349,6 +349,7 @@ void SVD_StopRecord_f( void );
|
||||||
void SVD_RecordFrame( void );
|
void SVD_RecordFrame( void );
|
||||||
void SVD_ResetDeltaState( void );
|
void SVD_ResetDeltaState( void );
|
||||||
void SVD_AutoRecord( void );
|
void SVD_AutoRecord( void );
|
||||||
|
void SVD_CaptureServerCommand( const char *cmd );
|
||||||
void SVD_Play_f( void );
|
void SVD_Play_f( void );
|
||||||
void SVD_StopPlay_f( void );
|
void SVD_StopPlay_f( void );
|
||||||
void SVD_Stop_f( void );
|
void SVD_Stop_f( void );
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,9 @@ void QDECL SV_SendServerCommand(client_t *cl, const char *fmt, ...) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// capture broadcast commands for demo recording
|
||||||
|
SVD_CaptureServerCommand( (char *)message );
|
||||||
|
|
||||||
// hack to echo broadcast prints to console
|
// hack to echo broadcast prints to console
|
||||||
if ( com_dedicated->integer && !strncmp( (char *)message, "print", 5) ) {
|
if ( com_dedicated->integer && !strncmp( (char *)message, "print", 5) ) {
|
||||||
Com_Printf ("broadcast: %s\n", SV_ExpandNewlines((char *)message) );
|
Com_Printf ("broadcast: %s\n", SV_ExpandNewlines((char *)message) );
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,9 @@ typedef struct {
|
||||||
qboolean active;
|
qboolean active;
|
||||||
} svdPlayerState_t;
|
} svdPlayerState_t;
|
||||||
|
|
||||||
|
#define SVD_MAX_SERVERCMDS 64
|
||||||
|
#define SVD_MAX_SERVERCMD_LEN 1024
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
// recording
|
// recording
|
||||||
fileHandle_t recordFile;
|
fileHandle_t recordFile;
|
||||||
|
|
@ -87,6 +90,10 @@ typedef struct {
|
||||||
svdEntityState_t prevEntities[MAX_GENTITIES]; // previous frame for delta
|
svdEntityState_t prevEntities[MAX_GENTITIES]; // previous frame for delta
|
||||||
svdPlayerState_t prevPlayers[MAX_CLIENTS]; // previous frame player states
|
svdPlayerState_t prevPlayers[MAX_CLIENTS]; // previous frame player states
|
||||||
|
|
||||||
|
// buffered server commands for current frame
|
||||||
|
char serverCmds[SVD_MAX_SERVERCMDS][SVD_MAX_SERVERCMD_LEN];
|
||||||
|
int numServerCmds;
|
||||||
|
|
||||||
// playback
|
// playback
|
||||||
fileHandle_t playFile;
|
fileHandle_t playFile;
|
||||||
qboolean playing;
|
qboolean playing;
|
||||||
|
|
@ -368,6 +375,15 @@ static void SVD_WriteFrame( fileHandle_t f ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// write buffered server commands (chat, prints, etc.)
|
||||||
|
SVD_WriteShort( f, (short)demo.numServerCmds );
|
||||||
|
for ( i = 0; i < demo.numServerCmds; i++ ) {
|
||||||
|
short len = (short)( strlen( demo.serverCmds[i] ) + 1 );
|
||||||
|
SVD_WriteShort( f, len );
|
||||||
|
FS_Write( demo.serverCmds[i], len, f );
|
||||||
|
}
|
||||||
|
demo.numServerCmds = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
|
|
@ -488,6 +504,21 @@ Called from SV_Frame() after the game has run its frame.
|
||||||
Reset delta compression state. Call on map_restart so the next
|
Reset delta compression state. Call on map_restart so the next
|
||||||
recorded frame writes full entity/player states from baseline.
|
recorded frame writes full entity/player states from baseline.
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
|
Capture a broadcast server command for the current frame.
|
||||||
|
Called from SV_SendServerCommand when cl == NULL (broadcast).
|
||||||
|
*/
|
||||||
|
void SVD_CaptureServerCommand( const char *cmd ) {
|
||||||
|
if ( !demo.recording ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( demo.numServerCmds >= SVD_MAX_SERVERCMDS ) {
|
||||||
|
return; // overflow, drop command
|
||||||
|
}
|
||||||
|
Q_strncpyz( demo.serverCmds[demo.numServerCmds], cmd, SVD_MAX_SERVERCMD_LEN );
|
||||||
|
demo.numServerCmds++;
|
||||||
|
}
|
||||||
|
|
||||||
void SVD_ResetDeltaState( void ) {
|
void SVD_ResetDeltaState( void ) {
|
||||||
if ( !demo.recording ) {
|
if ( !demo.recording ) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -770,6 +801,24 @@ static qboolean SVD_ReadFrame( fileHandle_t f ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read server commands (chat, prints, etc.) and replay to spectator
|
||||||
|
{
|
||||||
|
short numCmds = SVD_ReadShort( f );
|
||||||
|
for ( i = 0; i < numCmds; i++ ) {
|
||||||
|
short len = SVD_ReadShort( f );
|
||||||
|
char buf[SVD_MAX_SERVERCMD_LEN];
|
||||||
|
if ( len > 0 && len < (short)sizeof(buf) ) {
|
||||||
|
FS_Read( buf, len, f );
|
||||||
|
buf[len - 1] = '\0';
|
||||||
|
// send to the spectator client
|
||||||
|
if ( demo.spectatorClientNum < sv_maxclients->integer
|
||||||
|
&& svs.clients[demo.spectatorClientNum].state >= CS_PRIMED ) {
|
||||||
|
SV_SendServerCommand( &svs.clients[demo.spectatorClientNum], "%s", buf );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return qtrue;
|
return qtrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue