diff --git a/code/client/cl_cgame.cpp b/code/client/cl_cgame.cpp index 97acc6a..c842b0d 100644 --- a/code/client/cl_cgame.cpp +++ b/code/client/cl_cgame.cpp @@ -1031,11 +1031,11 @@ qbool CL_CGNDP_IsConfigStringNeeded( int csIndex ) } -void CL_CGNDP_AnalyzeSnapshot( int progress ) +qbool CL_CGNDP_AnalyzeSnapshot( int progress ) { Q_assert(cls.cgameNewDemoPlayer); Q_assert(progress >= 0 && progress < 100); - VM_Call(cgvm, cls.cgvmCalls[CGVM_NDP_ANALYZE_SNAPSHOT], progress); + return (qbool)VM_Call(cgvm, cls.cgvmCalls[CGVM_NDP_ANALYZE_SNAPSHOT], progress); } diff --git a/code/client/cl_demo.cpp b/code/client/cl_demo.cpp index e54c803..dcdef99 100644 --- a/code/client/cl_demo.cpp +++ b/code/client/cl_demo.cpp @@ -104,7 +104,7 @@ There's no need to make the player more complex for such an edge case. #define FULL_SNAPSHOT_INTERVAL_MS (8 * 1000) -#define MAX_COMMANDS ARRAY_LEN(demo.commands) +#define MAX_COMMANDS 256 #define VERBOSE_DEBUGGING 0 @@ -155,6 +155,7 @@ struct ndpSnapshot_t { int snapFlags; int ping; qbool isFullSnap; + qbool isServerPaused; }; struct parser_t { @@ -198,7 +199,7 @@ struct command_t { struct demo_t { ndpSnapshot_t snapshots[2]; // current one and next one for CGame requests demoIndex_t indices[4096]; - command_t commands[256]; + command_t commands[MAX_COMMANDS]; memoryBuffer_t buffer; ndpSnapshot_t* currSnap; ndpSnapshot_t* nextSnap; @@ -213,6 +214,9 @@ struct demo_t { struct newDemoPlayer_t { jmp_buf abortLoad; + qbool trackServerPause; + qbool isServerPaused; + int serverPauseDelay; // total duration in server pause since the full snapshot }; @@ -307,13 +311,14 @@ static void MB_Seek( memoryBuffer_t* mb, int position ) } -static void WriteNDPSnapshot( msg_t* outMsg, const ndpSnapshot_t* prevSnap, ndpSnapshot_t* currSnap ) +static void WriteNDPSnapshot( msg_t* outMsg, const ndpSnapshot_t* prevSnap, ndpSnapshot_t* currSnap, qbool isServerPaused ) { const qbool isFullSnap = prevSnap == NULL; // header MSG_WriteBits(outMsg, isFullSnap, 1); MSG_WriteLong(outMsg, currSnap->serverTime); + MSG_WriteBits(outMsg, !!isServerPaused, 1); // player state const playerState_t* const oldPS = !prevSnap ? &nullPlayerState : &prevSnap->ps; @@ -660,7 +665,7 @@ static void ParseSnapshot() // let CGame analyze the snapshot so it can do cool stuff // e.g. store events of interest for the timeline overlay demo.currSnap = currNDPSnap; - CL_CGNDP_AnalyzeSnapshot(parser.progress); + const qbool isServerPaused = CL_CGNDP_AnalyzeSnapshot(parser.progress); // draw the current progress once in a while... static int lastRefreshTime = Sys_Milliseconds(); @@ -676,7 +681,7 @@ static void ParseSnapshot() MSG_Clear(&writeMsg); MSG_Bitstream(&writeMsg); ndpSnapshot_t* const prevNDPSnap = parser.prevSnap; - WriteNDPSnapshot(&writeMsg, isFullSnap ? NULL : prevNDPSnap, currNDPSnap); + WriteNDPSnapshot(&writeMsg, isFullSnap ? NULL : prevNDPSnap, currNDPSnap, isServerPaused); MB_Write(&demo.buffer, &writeMsg.cursize, 4); MB_Write(&demo.buffer, writeMsg.data, writeMsg.cursize); @@ -835,6 +840,7 @@ static void ReadNextSnapshot() } currSnap->isFullSnap = isFullSnap; currSnap->serverTime = MSG_ReadLong(&inMsg); + currSnap->isServerPaused = MSG_ReadBits(&inMsg, 1); // player state MSG_ReadDeltaPlayerstate(&inMsg, prevSnap ? &prevSnap->ps : &nullPlayerState, &currSnap->ps); @@ -925,6 +931,14 @@ static void ReadNextSnapshot() } } + if (ndp.trackServerPause) { + if (currSnap->isServerPaused && ndp.isServerPaused && prevSnap != NULL) { + const int delta = currSnap->serverTime - prevSnap->serverTime; + ndp.serverPauseDelay += delta; + } + ndp.isServerPaused = currSnap->isServerPaused; + } + demo.snapshotIndex++; } @@ -1056,7 +1070,9 @@ after_parse: CL_CGNDP_EndAnalysis(clc.demoName, demo.firstServerTime, demo.lastServerTime, videoRestart); // make sure we don't execute commands from the end of the demo when starting up - demo.numCommands = 0; + demo.commands[0].command[0] = '\0'; + demo.commands[1].command[0] = '\0'; + demo.numCommands = 1; const int duration = Sys_Milliseconds() - startTime; Com_Printf("New Demo Player: loaded demo in %d.%03d seconds\n", duration / 1000, duration % 1000); @@ -1200,6 +1216,10 @@ int CL_NDP_Seek( int serverTime ) demo.commands[i].command[0] = '\0'; } + ndp.trackServerPause = qtrue; + ndp.isServerPaused = qfalse; + ndp.serverPauseDelay = 0; + SeekToIndex(index); // read more snapshots until we're close to the target time for more precise jumps @@ -1218,6 +1238,11 @@ int CL_NDP_Seek( int serverTime ) numSnapshotsRead++; } + if (ndp.serverPauseDelay > 0) { + AddCommand(va("server_pause_delay %d", ndp.serverPauseDelay)); + } + ndp.trackServerPause = qfalse; + Com_DPrintf("New Demo Player: sought and read %d snaps in %d ms\n", numSnapshotsRead, Sys_Milliseconds() - seekStartTime); diff --git a/code/client/client.h b/code/client/client.h index 623a542..dd3ae53 100644 --- a/code/client/client.h +++ b/code/client/client.h @@ -522,7 +522,7 @@ void CL_CGameRendering( stereoFrame_t stereo ); void CL_SetCGameTime(); void CL_ConfigstringModified(); void CL_CGNDP_EndAnalysis( const char* filePath, int firstServerTime, int lastServerTime, qbool videoRestart ); -void CL_CGNDP_AnalyzeSnapshot( int progress ); +qbool CL_CGNDP_AnalyzeSnapshot( int progress ); // qtrue when a server pause is active void CL_CGNDP_AnalyzeCommand( int serverTime ); void CL_CGNDP_GenerateCommands( const char** commands, int* numCommandBytes ); qbool CL_CGNDP_IsConfigStringNeeded( int csIndex );