From e4b6f186e563af3ed9b9357e87ed6323da559ae8 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sat, 22 Mar 2025 21:12:08 +0100 Subject: [PATCH 01/21] Make CRC-related commandline arguments work when compiling Release with logging enabled (if you define RELEASE_DEBUG_LOGGING in Debug.h) --- .../Code/GameEngine/Source/Common/CommandLine.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp index 41f4242bd63..3b428d43f58 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp @@ -163,6 +163,7 @@ Int parseFPUPreserve(char *args[], int argc) } #if defined(_DEBUG) || defined(_INTERNAL) + //============================================================================= //============================================================================= Int parseUseCSF(char *args[], int) @@ -203,6 +204,7 @@ Int parseNoMilCap(char *args[], int) } return 1; } +#endif // _DEBUG || _INTERNAL //============================================================================= //============================================================================= @@ -329,6 +331,8 @@ Int parseNoDraw(char *args[], int argc) return 1; } +#if defined(_DEBUG) || defined(_INTERNAL) + //============================================================================= //============================================================================= Int parseLogToConsole(char *args[], int) @@ -1161,6 +1165,8 @@ static CommandLineParam params[] = { "-localMOTD", parseLocalMOTD }, { "-UseCSF", parseUseCSF }, { "-NoInputDisable", parseNoInputDisable }, +#endif +#ifdef DEBUG_CRC { "-DebugCRCFromFrame", parseDebugCRCFromFrame }, { "-DebugCRCUntilFrame", parseDebugCRCUntilFrame }, { "-KeepCRCSaves", parseKeepCRCSave }, @@ -1169,9 +1175,11 @@ static CommandLineParam params[] = { "-ClientDeepCRC", parseClientDeepCRC }, { "-VerifyClientCRC", parseVerifyClientCRC }, { "-LogObjectCRCs", parseLogObjectCRCs }, - { "-saveAllStats", parseSaveAllStats }, { "-NetCRCInterval", parseNetCRCInterval }, { "-ReplayCRCInterval", parseReplayCRCInterval }, +#endif +#if (defined(_DEBUG) || defined(_INTERNAL)) + { "-saveAllStats", parseSaveAllStats }, { "-noDraw", parseNoDraw }, { "-nomilcap", parseNoMilCap }, { "-nofade", parseNoFade }, From fb4881f79f1258230bdc0df3045fb52d94bf5705 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sat, 22 Mar 2025 23:42:33 +0100 Subject: [PATCH 02/21] Add comments to Xfer.h --- GeneralsMD/Code/GameEngine/Include/Common/Xfer.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/Xfer.h b/GeneralsMD/Code/GameEngine/Include/Common/Xfer.h index cab9f0b73a8..4d98c6bd69c 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/Xfer.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/Xfer.h @@ -27,6 +27,11 @@ // Desc: The Xfer system is capable of setting up operations to work with blocks of data // from other subsystems. It can work things such as file reading, file writing, // CRC computations etc +// The baseclass Xfer has 3 implementations: +// - XferLoad: Load gamestate +// - XferSave: Save gamestate +// - XferCRC: Calculate gamestate CRC +// - XferDeepCRC: This derives from XferCRC and also writes the gamestate data relevant to crc calculation to a file (only used in developer builds) /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once From 10991d64f99f29c1127b004ad69224b4cedfda7b Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sat, 22 Mar 2025 23:46:57 +0100 Subject: [PATCH 03/21] Add some comments to crc command line arguments --- .../GameEngine/Source/Common/CommandLine.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp index 3b428d43f58..abf4ca4f4aa 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp @@ -1167,15 +1167,30 @@ static CommandLineParam params[] = { "-NoInputDisable", parseNoInputDisable }, #endif #ifdef DEBUG_CRC + // After which frame to log crc logging. Call with 0 to log all frames and with -1 to log none (default). { "-DebugCRCFromFrame", parseDebugCRCFromFrame }, + + // Last frame to log { "-DebugCRCUntilFrame", parseDebugCRCUntilFrame }, + + // Save data involving crc calculation to binary file (This isn't that useful). { "-KeepCRCSaves", parseKeepCRCSave }, { "-CRCLogicModuleData", parseCRCLogicModuleData }, { "-CRCClientModuleData", parseCRCClientModuleData }, - { "-ClientDeepCRC", parseClientDeepCRC }, + + // Verify that Game Logic CRC doesn't change during client update. Client update is only for visuals and not supposed to change the crc. (This is implemented using CRCVerification class in GameEngine::update) { "-VerifyClientCRC", parseVerifyClientCRC }, + + // Write out binary crc data pre and post client update to "clientPre.crc" and "clientPost.crc" + { "-ClientDeepCRC", parseClientDeepCRC }, + + // Log CRC of Objects and Weapons (See Object::crc and Weapon::crc) { "-LogObjectCRCs", parseLogObjectCRCs }, + + // Number of frames between each CRC check between all players in multiplayer games (if not all crcs are equal, mismatch occurs). { "-NetCRCInterval", parseNetCRCInterval }, + + // Number of frames between each CRC that is written to replay files in singleplayer games. { "-ReplayCRCInterval", parseReplayCRCInterval }, #endif #if (defined(_DEBUG) || defined(_INTERNAL)) From 320cd7c5ba8b4eb39f33a298acbfbc5a70d171f3 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sun, 23 Mar 2025 19:08:42 +0100 Subject: [PATCH 04/21] Resest Frame Counter earlier in GameLogic::prepareNewGame instead of in GameLogic::startNewGame to improve CRC Logging --- .../Code/GameEngine/Source/GameLogic/System/GameLogic.cpp | 3 +-- .../GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index 91d18bc9bbf..0b9836effd3 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -1284,8 +1284,7 @@ void GameLogic::startNewGame( Bool loadingSaveGame ) if(m_loadScreen) updateLoadProgress(LOAD_PROGRESS_POST_PARTICLE_INI_LOAD); - // reset the frame counter - m_frame = 0; + DEBUG_ASSERTCRASH(m_frame == 0, ("framecounter expected to be 0 here\n")); // before loading the map, load the map.ini file in the same directory. loadMapINI( TheGlobalData->m_mapName ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp index 2e2719bde10..b47234fe7d3 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp @@ -333,6 +333,9 @@ void GameLogic::prepareNewGame( Int gameMode, GameDifficulty diff, Int rankPoint TheWritableGlobalData->m_pendingFile.clear(); } + // reset the frame counter + m_frame = 0; + m_rankPointsToAddAtGameStart = rankPoints; DEBUG_LOG(("GameLogic::prepareNewGame() - m_rankPointsToAddAtGameStart = %d\n", m_rankPointsToAddAtGameStart)); From 2e0a25c6615ed7bd57b388bdfa996fa98630c121 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sat, 29 Mar 2025 17:56:59 +0100 Subject: [PATCH 05/21] Improved CRC Debugging: Optionally save crc data into a file per frame with -SaveDebugCRCPerFrame. Also remove Bool dumped (wasn't really used and was in the way for this new code) --- .../Code/GameEngine/Include/Common/CRCDebug.h | 6 +- .../GameEngine/Source/Common/CRCDebug.cpp | 115 ++++++++++++++---- .../GameEngine/Source/Common/CommandLine.cpp | 32 +++++ .../GameEngine/Source/Common/System/Debug.cpp | 4 + .../GameLogic/System/GameLogicDispatch.cpp | 4 + 5 files changed, 138 insertions(+), 23 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/CRCDebug.h b/GeneralsMD/Code/GameEngine/Include/Common/CRCDebug.h index 99408dad6b6..2d9cbe2efe1 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/CRCDebug.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/CRCDebug.h @@ -68,9 +68,11 @@ void dumpReal(Real r, AsciiString name, AsciiString fname, Int line); void outputCRCDebugLines( void ); + void CRCDebugStartNewGame( void ); void outputCRCDumpLines( void ); void addCRCDebugLine(const char *fmt, ...); + void addCRCDebugLineNoCounter(const char *str); void addCRCDumpLine(const char *fmt, ...); void addCRCGenLine(const char *fmt, ...); #define CRCDEBUG_LOG(x) addCRCDebugLine x @@ -97,7 +99,9 @@ extern Bool g_crcModuleDataFromLogic; extern Bool g_keepCRCSaves; - + extern Bool g_saveDebugCRCPerFrame; + extern AsciiString g_saveDebugCRCPerFrameDir; + extern Bool g_logObjectCRCs; #else // DEBUG_CRC diff --git a/GeneralsMD/Code/GameEngine/Source/Common/CRCDebug.cpp b/GeneralsMD/Code/GameEngine/Source/Common/CRCDebug.cpp index 46c781f6563..b2256395234 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/CRCDebug.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/CRCDebug.cpp @@ -91,12 +91,8 @@ CRCVerification::~CRCVerification() #endif } -static Bool dumped = FALSE; void outputCRCDebugLines( void ) { - if (dumped) - return; - dumped = TRUE; IPEnumeration ips; AsciiString fname; fname.format("crcDebug%s.txt", ips.getMachineName().str()); @@ -116,6 +112,57 @@ void outputCRCDebugLines( void ) if (fp) fclose(fp); } +Int lastCRCDebugFrame = 0; +Int lastCRCDebugIndex = 0; +extern Bool inCRCGen; + +void CRCDebugStartNewGame() +{ + if (g_saveDebugCRCPerFrame) + { + CreateDirectory(g_saveDebugCRCPerFrameDir.str(), NULL); + for (int i = 0; ; i++) + { + AsciiString fname; + fname.format("%s/DebugFrame_%06d.txt", g_saveDebugCRCPerFrameDir.str(), i); + FILE *fp = fopen(fname.str(), "rt"); + if (!fp) + break; + fclose(fp); + DeleteFile(fname.str()); + } + } + nextDebugString = 0; + numDebugStrings = 0; + lastCRCDebugFrame = 0; + lastCRCDebugIndex = 0; +} + +void outputCRCDebugLinesPerFrame() +{ + if (!g_saveDebugCRCPerFrame || numDebugStrings == 0) + return; + AsciiString fname; + fname.format("%s/DebugFrame_%06d.txt", g_saveDebugCRCPerFrameDir.str(), lastCRCDebugFrame); + FILE *fp = fopen(fname.str(), "wt"); + int start = 0; + int end = nextDebugString; + if (numDebugStrings >= MaxStrings) + start = nextDebugString - MaxStrings; + + for (Int i=start; iisInGameLogicUpdate()*/) + if (TheGameLogic == NULL)// || inCRCGen /*|| !TheGameLogic->isInGameLogicUpdate()*/) return; if (IS_FRAME_OK_TO_LOG) @@ -150,11 +194,12 @@ void addCRCDebugLine(const char *fmt, ...) if (lastCRCDebugFrame != TheGameLogic->getFrame()) { + outputCRCDebugLinesPerFrame(); lastCRCDebugFrame = TheGameLogic->getFrame(); lastCRCDebugIndex = 0; } - sprintf(DebugStrings[nextDebugString], "%d:%d ", TheGameLogic->getFrame(), lastCRCDebugIndex++); + sprintf(DebugStrings[nextDebugString], "%d:%05d ", TheGameLogic->getFrame(), lastCRCDebugIndex++); //DebugStrings[nextDebugString][0] = 0; Int len = strlen(DebugStrings[nextDebugString]); @@ -184,9 +229,47 @@ void addCRCDebugLine(const char *fmt, ...) } } +void addCRCDebugLineNoCounter(const char *str) +{ + // This version doesn't increase the lastCRCDebugIndex counter + // and can be used for logging lines that don't necessarily match up on all peers. + // (Otherwise the numbers would not longer match up and the diff would be very difficult to read) + if (TheGameLogic == NULL)// || inCRCGen /*|| !TheGameLogic->isInGameLogicUpdate()*/) + return; + + if (IS_FRAME_OK_TO_LOG) + { + + if (lastCRCDebugFrame != TheGameLogic->getFrame()) + { + outputCRCDebugLinesPerFrame(); + lastCRCDebugFrame = TheGameLogic->getFrame(); + lastCRCDebugIndex = 0; + } + + strncpy(DebugStrings[nextDebugString], str, 1024); + DebugStrings[nextDebugString][1023] = 0; + + char *tmp = DebugStrings[nextDebugString]; + while (tmp && *tmp) + { + if (*tmp == '\r' || *tmp == '\n') + { + *tmp = ' '; + } + ++tmp; + } + + ++nextDebugString; + ++numDebugStrings; + if (nextDebugString == MaxStrings) + nextDebugString = 0; + } +} + void addCRCGenLine(const char *fmt, ...) { - if (dumped || !(IS_FRAME_OK_TO_LOG)) + if (!(IS_FRAME_OK_TO_LOG)) return; static char buf[1024]; @@ -218,9 +301,6 @@ void addCRCDumpLine(const char *fmt, ...) void dumpVector3(const Vector3 *v, AsciiString name, AsciiString fname, Int line) { - if (dumped) - return; - if (!(IS_FRAME_OK_TO_LOG)) return; fname.toLower(); fname = getFname(fname); @@ -231,9 +311,6 @@ void dumpVector3(const Vector3 *v, AsciiString name, AsciiString fname, Int line void dumpCoord3D(const Coord3D *c, AsciiString name, AsciiString fname, Int line) { - if (dumped) - return; - if (!(IS_FRAME_OK_TO_LOG)) return; fname.toLower(); fname = getFname(fname); @@ -244,9 +321,6 @@ void dumpCoord3D(const Coord3D *c, AsciiString name, AsciiString fname, Int line void dumpMatrix3D(const Matrix3D *m, AsciiString name, AsciiString fname, Int line) { - if (dumped) - return; - if (!(IS_FRAME_OK_TO_LOG)) return; fname.toLower(); fname = getFname(fname); @@ -260,9 +334,6 @@ void dumpMatrix3D(const Matrix3D *m, AsciiString name, AsciiString fname, Int li void dumpReal(Real r, AsciiString name, AsciiString fname, Int line) { - if (dumped) - return; - if (!(IS_FRAME_OK_TO_LOG)) return; fname.toLower(); fname = getFname(fname); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp index abf4ca4f4aa..310daa959a3 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp @@ -48,6 +48,8 @@ extern Int DX8Wrapper_PreserveFPU; Int TheCRCFirstFrameToLog = -1; UnsignedInt TheCRCLastFrameToLog = 0xffffffff; Bool g_keepCRCSaves = FALSE; +Bool g_saveDebugCRCPerFrame = FALSE; +AsciiString g_saveDebugCRCPerFrameDir; Bool g_crcModuleDataFromLogic = FALSE; Bool g_crcModuleDataFromClient = FALSE; Bool g_verifyClientCRC = FALSE; // verify that GameLogic CRC doesn't change from client @@ -242,6 +244,22 @@ Int parseKeepCRCSave(char *args[], int argc) return 1; } +//============================================================================= +//============================================================================= +Int parseSaveDebugCRCPerFrame(char* args[], int num) +{ +#ifdef DEBUG_CRC + if (num > 1) + { + g_saveDebugCRCPerFrame = TRUE; + g_saveDebugCRCPerFrameDir = args[1]; + if (TheCRCFirstFrameToLog == -1) + TheCRCFirstFrameToLog = 0; + } +#endif + return 2; +} + //============================================================================= //============================================================================= Int parseCRCLogicModuleData(char *args[], int argc) @@ -1167,6 +1185,16 @@ static CommandLineParam params[] = { "-NoInputDisable", parseNoInputDisable }, #endif #ifdef DEBUG_CRC + // The following arguments are useful for CRC debugging. + // Note that you need to have a debug or internal configuration build in order to use this. + // Release configuration also works if RELEASE_DEBUG_LOGGING is defined in Debug.h + // Also note that all players need to play in the same configuration, otherwise mismatch will + // occur almost immediately. + // If you want to play the game and have useful debuginformation in case mismatch orrcurs, I suggest this: + // -ignoreAsserts -DebugCRCFromFrame 0 -VerifyClientCRC -LogObjectCRCs -NetCRCInterval 1 + // After mismatch occurs, you can examine the logfile and also reproduce the crc from the replay with this (and diff that with the log): + // -ignoreAsserts -DebugCRCFromFrame xxx -LogObjectCRCs -SaveDebugCRCPerFrame crc + // After which frame to log crc logging. Call with 0 to log all frames and with -1 to log none (default). { "-DebugCRCFromFrame", parseDebugCRCFromFrame }, @@ -1175,6 +1203,10 @@ static CommandLineParam params[] = // Save data involving crc calculation to binary file (This isn't that useful). { "-KeepCRCSaves", parseKeepCRCSave }, + + // Store CRC Debug Logging into a separate file for each frame. Pass the foldername after this. This is useful for replay analysis. + { "-SaveDebugCRCPerFrame", parseSaveDebugCRCPerFrame }, + { "-CRCLogicModuleData", parseCRCLogicModuleData }, { "-CRCClientModuleData", parseCRCClientModuleData }, diff --git a/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp b/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp index 0a90b9cefc9..5d5905e3ef7 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp @@ -52,6 +52,7 @@ #include "Common/CriticalSection.h" #endif #include "Common/Debug.h" +#include "Common/CRCDebug.h" #include "Common/SystemInfo.h" #include "Common/UnicodeString.h" #include "GameClient/GameText.h" @@ -229,6 +230,9 @@ static void doLogOutput(const char *buffer) { ::OutputDebugString(buffer); } + + // Uncomment this to show normal logging stuff in the crc logging. + //addCRCDebugLineNoCounter(buffer); } #endif diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp index b47234fe7d3..0a2455a3a1a 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp @@ -336,6 +336,10 @@ void GameLogic::prepareNewGame( Int gameMode, GameDifficulty diff, Int rankPoint // reset the frame counter m_frame = 0; +#ifdef DEBUG_CRC + CRCDebugStartNewGame(); +#endif + m_rankPointsToAddAtGameStart = rankPoints; DEBUG_LOG(("GameLogic::prepareNewGame() - m_rankPointsToAddAtGameStart = %d\n", m_rankPointsToAddAtGameStart)); From adfd7218df6e228564eb1e68c3926440a458a443 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sat, 29 Mar 2025 18:08:00 +0100 Subject: [PATCH 06/21] Improved logging for mismatch debugging. --- .../Code/GameEngine/Source/GameLogic/Object/Object.cpp | 9 ++++----- .../GameEngine/Source/GameLogic/System/GameLogic.cpp | 4 ++-- .../Code/GameEngine/Source/GameNetwork/Network.cpp | 3 ++- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index 0182d5695d1..f21e76eb37a 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -3884,10 +3884,6 @@ void Object::onDisabledEdge(Bool becomingDisabled) //------------------------------------------------------------------------------------------------- void Object::crc( Xfer *xfer ) { - // This is evil - we cast the const Matrix3D * to a Matrix3D * because the XferCRC class must use - // the same interface as the XferLoad class for save game restore. This only works because - // XferCRC does not modify its data. - #ifdef DEBUG_CRC // g_logObjectCRCs = TRUE; // Bool g_logAllObjects = TRUE; @@ -3896,7 +3892,7 @@ void Object::crc( Xfer *xfer ) Bool doLogging = g_logObjectCRCs /* && getControllingPlayer()->getPlayerType() == PLAYER_HUMAN */; if (doLogging) { - tmp.format("CRC of Object %d (%s), owned by player %d, ", m_id, getTemplate()->getName().str(), getControllingPlayer()->getPlayerIndex()); + tmp.format("CRC of Object %d (%s), owned by player %d, team: %d, ", m_id, getTemplate()->getName().str(), getControllingPlayer()->getPlayerIndex(), this->getTeam() ? this->getTeam()->getID() : 0); logString.concat(tmp); } #endif DEBUG_CRC @@ -3910,6 +3906,9 @@ void Object::crc( Xfer *xfer ) } #endif // DEBUG_CRC + // This is evil - we cast the const Matrix3D * to a Matrix3D * because the XferCRC class must use + // the same interface as the XferLoad class for save game restore. This only works because + // XferCRC does not modify its data. xfer->xferUser((Matrix3D *)getTransformMatrix(), sizeof(Matrix3D)); #ifdef DEBUG_CRC if (doLogging) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index 0b9836effd3..dd89756b723 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -3665,14 +3665,14 @@ void GameLogic::update( void ) GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_LOGIC_CRC ); msg->appendIntegerArgument( m_CRC ); msg->appendBooleanArgument( (TheRecorder && TheRecorder->getMode() == RECORDERMODETYPE_PLAYBACK) ); // playback CRC - //DEBUG_LOG(("Appended CRC of %8.8X on frame %d\n", m_CRC, m_frame)); + DEBUG_LOG(("Appended CRC on frame %d: %8.8X\n", m_frame, m_CRC)); } else { GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_LOGIC_CRC ); msg->appendIntegerArgument( m_CRC ); msg->appendBooleanArgument( (TheRecorder && TheRecorder->getMode() == RECORDERMODETYPE_PLAYBACK) ); // playback CRC - //DEBUG_LOG(("Appended Playback CRC of %8.8X on frame %d\n", m_CRC, m_frame)); + DEBUG_LOG(("Appended Playback CRC on frame %d: %8.8X\n", m_frame, m_CRC)); } } diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/Network.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/Network.cpp index e1d33961f44..4ed23eab8b4 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/Network.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/Network.cpp @@ -380,7 +380,8 @@ void Network::setSawCRCMismatch( void ) TheRecorder->logCRCMismatch(); // dump GameLogic random seed - DEBUG_LOG(("GameLogic frame = %d\n", TheGameLogic->getFrame())); + DEBUG_LOG(("Latest frame for mismatch = %d GameLogic frame = %d\n", + TheGameLogic->getFrame()-m_runAhead-1, TheGameLogic->getFrame())); DEBUG_LOG(("GetGameLogicRandomSeedCRC() = %d\n", GetGameLogicRandomSeedCRC())); // dump CRCs From ba139025b82ab6d6b7565794a5be38a0f6c62928 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sat, 29 Mar 2025 18:09:26 +0100 Subject: [PATCH 07/21] Make condition for checking TheCRCFirstFrameToLog consistent. --- .../Code/GameEngine/Source/GameLogic/System/GameLogic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index dd89756b723..a52c234d3df 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -3652,7 +3652,7 @@ void GameLogic::update( void ) Bool generateForMP = (isMPGameOrReplay && (m_frame % TheGameInfo->getCRCInterval()) == 0); #if defined(_DEBUG) || defined(_INTERNAL) Bool generateForSolo = isSoloGameOrReplay && ((m_frame && (m_frame%100 == 0)) || - (getFrame() > TheCRCFirstFrameToLog && getFrame() < TheCRCLastFrameToLog && ((m_frame % REPLAY_CRC_INTERVAL) == 0))); + (getFrame() >= TheCRCFirstFrameToLog && getFrame() < TheCRCLastFrameToLog && ((m_frame % REPLAY_CRC_INTERVAL) == 0))); #else Bool generateForSolo = isSoloGameOrReplay && ((m_frame % REPLAY_CRC_INTERVAL) == 0); #endif // defined(_DEBUG) || defined(_INTERNAL) From 1b2f5a839a8e79b1b47fc452c77e102de4634213 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sat, 29 Mar 2025 18:10:09 +0100 Subject: [PATCH 08/21] Add comment regarding g_keepCRCSaves --- .../Code/GameEngine/Source/GameLogic/System/GameLogic.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index a52c234d3df..923a6b2ef74 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -4028,6 +4028,11 @@ UnsignedInt GameLogic::getCRC( Int mode, AsciiString deepCRCFileName ) { AsciiString crcName; #ifdef DEBUG_CRC + // This allows you to save the binary data that is involved in the crc calculation + // to a binary file per frame. + // This was apparently used early in development and isn't that useful, because diffing + // that binary data is very difficult. The CRC logging is much easier to diff and also more + // granular than this because it can capture changes between two frames. if (isInGameLogicUpdate() && g_keepCRCSaves && m_frame < 5) { xferCRC = NEW XferDeepCRC; From fb5d9778ae47c139147065024c00317d7bf0570f Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sat, 29 Mar 2025 18:12:50 +0100 Subject: [PATCH 09/21] Remove transform crclogging in Object::crc. This was likely copypasted and not intended because it spams the crclogging with mostly useless data. --- .../Source/GameLogic/Object/Object.cpp | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index f21e76eb37a..55e9dd5bcc4 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -3923,25 +3923,6 @@ void Object::crc( Xfer *xfer ) #endif DEBUG_CRC -#ifdef DEBUG_CRC - if (doLogging) - { - const Matrix3D *mtx = getTransformMatrix(); - CRCDEBUG_LOG(("CRC of Object %d (%s), owned by player %d, ", m_id, getTemplate()->getName().str(), getControllingPlayer()->getPlayerIndex())); - DUMPMATRIX3D(mtx); - } -#endif DEBUG_CRC - - - - - - - - - - - xfer->xferUser(&m_id, sizeof(m_id)); #ifdef DEBUG_CRC if (doLogging) From 46a4ab469d83f6b682050287d09bcab0cb346848 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sat, 29 Mar 2025 18:50:47 +0100 Subject: [PATCH 10/21] Delete CRC Frame files properly --- .../GameEngine/Source/Common/CRCDebug.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/CRCDebug.cpp b/GeneralsMD/Code/GameEngine/Source/Common/CRCDebug.cpp index b2256395234..4595942ce68 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/CRCDebug.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/CRCDebug.cpp @@ -28,6 +28,7 @@ #include "Common/CRCDebug.h" #include "Common/Debug.h" #include "Common/PerfTimer.h" +#include "Common/LocalFileSystem.h" #include "GameClient/InGameUI.h" #include "GameNetwork/IPEnumeration.h" #include @@ -120,16 +121,18 @@ void CRCDebugStartNewGame() { if (g_saveDebugCRCPerFrame) { + // Create folder for frame data, if it doesn't exist yet. CreateDirectory(g_saveDebugCRCPerFrameDir.str(), NULL); - for (int i = 0; ; i++) + + // Delete existing files + FilenameList files; + AsciiString dir = g_saveDebugCRCPerFrameDir; + dir.concat("/"); + TheLocalFileSystem->getFileListInDirectory(dir.str(), "", "DebugFrame_*.txt", files, FALSE); + FilenameList::iterator it; + for (it = files.begin(); it != files.end(); ++it) { - AsciiString fname; - fname.format("%s/DebugFrame_%06d.txt", g_saveDebugCRCPerFrameDir.str(), i); - FILE *fp = fopen(fname.str(), "rt"); - if (!fp) - break; - fclose(fp); - DeleteFile(fname.str()); + DeleteFile(it->str()); } } nextDebugString = 0; From 9f7287e77e3791e88a6a39f3c065e5d31a7134a3 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Thu, 10 Apr 2025 12:36:22 +0200 Subject: [PATCH 11/21] Improve comments --- .../Code/GameEngine/Include/Common/Xfer.h | 5 ++++- .../GameEngine/Source/Common/CommandLine.cpp | 22 +++++++++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/Xfer.h b/GeneralsMD/Code/GameEngine/Include/Common/Xfer.h index 4d98c6bd69c..d758d92848b 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/Xfer.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/Xfer.h @@ -27,11 +27,14 @@ // Desc: The Xfer system is capable of setting up operations to work with blocks of data // from other subsystems. It can work things such as file reading, file writing, // CRC computations etc +// +// TheSuperHackers @info helmutbuhler 04/09/2025 // The baseclass Xfer has 3 implementations: // - XferLoad: Load gamestate // - XferSave: Save gamestate // - XferCRC: Calculate gamestate CRC -// - XferDeepCRC: This derives from XferCRC and also writes the gamestate data relevant to crc calculation to a file (only used in developer builds) +// - XferDeepCRC: This derives from XferCRC and also writes the gamestate data relevant +// to crc calculation to a file (only used in developer builds) /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once diff --git a/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp index 310daa959a3..f479b1b983a 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp @@ -246,10 +246,10 @@ Int parseKeepCRCSave(char *args[], int argc) //============================================================================= //============================================================================= -Int parseSaveDebugCRCPerFrame(char* args[], int num) +Int parseSaveDebugCRCPerFrame(char* args[], int argc) { #ifdef DEBUG_CRC - if (num > 1) + if (argc > 1) { g_saveDebugCRCPerFrame = TRUE; g_saveDebugCRCPerFrameDir = args[1]; @@ -1185,12 +1185,13 @@ static CommandLineParam params[] = { "-NoInputDisable", parseNoInputDisable }, #endif #ifdef DEBUG_CRC + // TheSuperHackers @info helmutbuhler 04/09/2025 // The following arguments are useful for CRC debugging. // Note that you need to have a debug or internal configuration build in order to use this. // Release configuration also works if RELEASE_DEBUG_LOGGING is defined in Debug.h // Also note that all players need to play in the same configuration, otherwise mismatch will // occur almost immediately. - // If you want to play the game and have useful debuginformation in case mismatch orrcurs, I suggest this: + // Try this if you want to play the game and have useful debug information in case mismatch occurs: // -ignoreAsserts -DebugCRCFromFrame 0 -VerifyClientCRC -LogObjectCRCs -NetCRCInterval 1 // After mismatch occurs, you can examine the logfile and also reproduce the crc from the replay with this (and diff that with the log): // -ignoreAsserts -DebugCRCFromFrame xxx -LogObjectCRCs -SaveDebugCRCPerFrame crc @@ -1201,16 +1202,22 @@ static CommandLineParam params[] = // Last frame to log { "-DebugCRCUntilFrame", parseDebugCRCUntilFrame }, - // Save data involving crc calculation to binary file (This isn't that useful). + // Save data involving CRC calculation to a binary file. (This isn't that useful.) { "-KeepCRCSaves", parseKeepCRCSave }, - // Store CRC Debug Logging into a separate file for each frame. Pass the foldername after this. This is useful for replay analysis. + // TheSuperHackers @feature helmutbuhler 04/09/2025 + // Store CRC Debug Logging into a separate file for each frame. + // Pass the foldername after this where those files are to be stored. + // This is useful for replay analysis. + // Note that the passed folder is deleted if it already exists for every started game. { "-SaveDebugCRCPerFrame", parseSaveDebugCRCPerFrame }, { "-CRCLogicModuleData", parseCRCLogicModuleData }, { "-CRCClientModuleData", parseCRCClientModuleData }, - // Verify that Game Logic CRC doesn't change during client update. Client update is only for visuals and not supposed to change the crc. (This is implemented using CRCVerification class in GameEngine::update) + // Verify that Game Logic CRC doesn't change during client update. + // Client update is only for visuals and not supposed to change the crc. + // (This is implemented using CRCVerification class in GameEngine::update) { "-VerifyClientCRC", parseVerifyClientCRC }, // Write out binary crc data pre and post client update to "clientPre.crc" and "clientPost.crc" @@ -1219,7 +1226,8 @@ static CommandLineParam params[] = // Log CRC of Objects and Weapons (See Object::crc and Weapon::crc) { "-LogObjectCRCs", parseLogObjectCRCs }, - // Number of frames between each CRC check between all players in multiplayer games (if not all crcs are equal, mismatch occurs). + // Number of frames between each CRC check between all players in multiplayer games + // (if not all crcs are equal, mismatch occurs). { "-NetCRCInterval", parseNetCRCInterval }, // Number of frames between each CRC that is written to replay files in singleplayer games. From 4a22c73fb2fb3c2980032e995bbc4ceae60a14f1 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Thu, 10 Apr 2025 12:39:30 +0200 Subject: [PATCH 12/21] Reset framecounter and call CRCDebugStartNewGame in GameLogic::startNewGame instead of GameLogic::prepareNewGame so it works when loading a game. --- .../GameEngine/Source/GameLogic/System/GameLogic.cpp | 10 ++++++++++ .../Source/GameLogic/System/GameLogicDispatch.cpp | 7 ------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index 923a6b2ef74..5b11aa8d916 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -1112,6 +1112,15 @@ void GameLogic::startNewGame( Bool loadingSaveGame ) GetPrecisionTimer(&startTime64); #endif + // reset the frame counter + m_frame = 0; + +#ifdef DEBUG_CRC + // TheSuperHackers @info helmutbuhler 04/09/2025 + // Let CRC Logger know that a new game was started. + CRCDebugStartNewGame(); +#endif + setLoadingMap( TRUE ); if( loadingSaveGame == FALSE ) @@ -4028,6 +4037,7 @@ UnsignedInt GameLogic::getCRC( Int mode, AsciiString deepCRCFileName ) { AsciiString crcName; #ifdef DEBUG_CRC + // TheSuperHackers @info helmutbuhler 04/09/2025 // This allows you to save the binary data that is involved in the crc calculation // to a binary file per frame. // This was apparently used early in development and isn't that useful, because diffing diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp index 0a2455a3a1a..2e2719bde10 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp @@ -333,13 +333,6 @@ void GameLogic::prepareNewGame( Int gameMode, GameDifficulty diff, Int rankPoint TheWritableGlobalData->m_pendingFile.clear(); } - // reset the frame counter - m_frame = 0; - -#ifdef DEBUG_CRC - CRCDebugStartNewGame(); -#endif - m_rankPointsToAddAtGameStart = rankPoints; DEBUG_LOG(("GameLogic::prepareNewGame() - m_rankPointsToAddAtGameStart = %d\n", m_rankPointsToAddAtGameStart)); From e536826f9e6788849925f76b7767c552199f3611 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Thu, 10 Apr 2025 12:44:26 +0200 Subject: [PATCH 13/21] Fix TEAM_ID_INVALID --- GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index 55e9dd5bcc4..c10523fee25 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -3892,7 +3892,7 @@ void Object::crc( Xfer *xfer ) Bool doLogging = g_logObjectCRCs /* && getControllingPlayer()->getPlayerType() == PLAYER_HUMAN */; if (doLogging) { - tmp.format("CRC of Object %d (%s), owned by player %d, team: %d, ", m_id, getTemplate()->getName().str(), getControllingPlayer()->getPlayerIndex(), this->getTeam() ? this->getTeam()->getID() : 0); + tmp.format("CRC of Object %d (%s), owned by player %d, team: %d, ", m_id, getTemplate()->getName().str(), getControllingPlayer()->getPlayerIndex(), this->getTeam() ? this->getTeam()->getID() : TEAM_ID_INVALID); logString.concat(tmp); } #endif DEBUG_CRC From 21b48516025ff5fd34f9bfdd4625cb53186a39d6 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Thu, 10 Apr 2025 12:48:02 +0200 Subject: [PATCH 14/21] Some code cleanup in CRCDebug --- .../Code/GameEngine/Include/Common/CRCDebug.h | 2 +- .../GameEngine/Source/Common/CRCDebug.cpp | 132 ++++++++---------- .../GameEngine/Source/Common/System/Debug.cpp | 2 +- 3 files changed, 57 insertions(+), 79 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/CRCDebug.h b/GeneralsMD/Code/GameEngine/Include/Common/CRCDebug.h index 2d9cbe2efe1..a43f8e11726 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/CRCDebug.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/CRCDebug.h @@ -72,7 +72,7 @@ void outputCRCDumpLines( void ); void addCRCDebugLine(const char *fmt, ...); - void addCRCDebugLineNoCounter(const char *str); + void addCRCDebugLineNoCounter(const char *fmt, ...); void addCRCDumpLine(const char *fmt, ...); void addCRCGenLine(const char *fmt, ...); #define CRCDEBUG_LOG(x) addCRCDebugLine x diff --git a/GeneralsMD/Code/GameEngine/Source/Common/CRCDebug.cpp b/GeneralsMD/Code/GameEngine/Source/Common/CRCDebug.cpp index 4595942ce68..ff777cb6004 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/CRCDebug.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/CRCDebug.cpp @@ -42,11 +42,12 @@ #ifdef DEBUG_CRC static const Int MaxStrings = 64000; +static const Int MaxStringLen = 1024; -static char DebugStrings[MaxStrings][1024]; +static char DebugStrings[MaxStrings][MaxStringLen]; static Int nextDebugString = 0; static Int numDebugStrings = 0; -//static char DumpStrings[MaxStrings][1024]; +//static char DumpStrings[MaxStrings][MaxStringLen]; //static Int nextDumpString = 0; //static Int numDumpStrings = 0; @@ -141,7 +142,7 @@ void CRCDebugStartNewGame() lastCRCDebugIndex = 0; } -void outputCRCDebugLinesPerFrame() +static void outputCRCDebugLinesPerFrame() { if (!g_saveDebugCRCPerFrame || numDebugStrings == 0) return; @@ -152,18 +153,19 @@ void outputCRCDebugLinesPerFrame() int end = nextDebugString; if (numDebugStrings >= MaxStrings) start = nextDebugString - MaxStrings; + nextDebugString = 0; + numDebugStrings = 0; + if (!fp) + return; for (Int i=start; iisInGameLogicUpdate()*/) + if (TheGameLogic == NULL || !(IS_FRAME_OK_TO_LOG)) return; - if (IS_FRAME_OK_TO_LOG) + if (lastCRCDebugFrame != TheGameLogic->getFrame()) { + outputCRCDebugLinesPerFrame(); + lastCRCDebugFrame = TheGameLogic->getFrame(); + lastCRCDebugIndex = 0; + } - if (lastCRCDebugFrame != TheGameLogic->getFrame()) - { - outputCRCDebugLinesPerFrame(); - lastCRCDebugFrame = TheGameLogic->getFrame(); - lastCRCDebugIndex = 0; - } - - sprintf(DebugStrings[nextDebugString], "%d:%05d ", TheGameLogic->getFrame(), lastCRCDebugIndex++); - //DebugStrings[nextDebugString][0] = 0; - Int len = strlen(DebugStrings[nextDebugString]); + if (count) + sprintf(DebugStrings[nextDebugString], "%d:%05d ", TheGameLogic->getFrame(), lastCRCDebugIndex++); + else + DebugStrings[nextDebugString][0] = 0; + Int len = strlen(DebugStrings[nextDebugString]); - va_list va; - va_start( va, fmt ); - _vsnprintf(DebugStrings[nextDebugString]+len, 1024-len, fmt, va ); - DebugStrings[nextDebugString][1023] = 0; - va_end( va ); + _vsnprintf(DebugStrings[nextDebugString]+len, MaxStringLen-len, fmt, args); + DebugStrings[nextDebugString][MaxStringLen-1] = 0; - char *tmp = DebugStrings[nextDebugString]; - while (tmp && *tmp) + char *tmp = DebugStrings[nextDebugString]; + while (tmp && *tmp) + { + if (*tmp == '\r' || *tmp == '\n') { - if (*tmp == '\r' || *tmp == '\n') - { - *tmp = ' '; - } - ++tmp; + *tmp = ' '; } + ++tmp; + } - //DEBUG_LOG(("%s\n", DebugStrings[nextDebugString])); + //DEBUG_LOG(("%s\n", DebugStrings[nextDebugString])); - ++nextDebugString; - ++numDebugStrings; - if (nextDebugString == MaxStrings) - nextDebugString = 0; + ++nextDebugString; + ++numDebugStrings; + if (nextDebugString == MaxStrings) + nextDebugString = 0; +} - } +void addCRCDebugLine(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + addCRCDebugLineInternal(true, fmt, args); + va_end(args); } -void addCRCDebugLineNoCounter(const char *str) +void addCRCDebugLineNoCounter(const char *fmt, ...) { + // TheSuperHackers @feature helmutbuhler 04/09/2025 // This version doesn't increase the lastCRCDebugIndex counter // and can be used for logging lines that don't necessarily match up on all peers. - // (Otherwise the numbers would not longer match up and the diff would be very difficult to read) - if (TheGameLogic == NULL)// || inCRCGen /*|| !TheGameLogic->isInGameLogicUpdate()*/) - return; - - if (IS_FRAME_OK_TO_LOG) - { - - if (lastCRCDebugFrame != TheGameLogic->getFrame()) - { - outputCRCDebugLinesPerFrame(); - lastCRCDebugFrame = TheGameLogic->getFrame(); - lastCRCDebugIndex = 0; - } - - strncpy(DebugStrings[nextDebugString], str, 1024); - DebugStrings[nextDebugString][1023] = 0; - - char *tmp = DebugStrings[nextDebugString]; - while (tmp && *tmp) - { - if (*tmp == '\r' || *tmp == '\n') - { - *tmp = ' '; - } - ++tmp; - } - - ++nextDebugString; - ++numDebugStrings; - if (nextDebugString == MaxStrings) - nextDebugString = 0; - } + // (Otherwise the numbers would no longer match up and the diff would be very difficult to read) + va_list args; + va_start(args, fmt); + addCRCDebugLineInternal(false, fmt, args); + va_end(args); } void addCRCGenLine(const char *fmt, ...) @@ -275,12 +253,12 @@ void addCRCGenLine(const char *fmt, ...) if (!(IS_FRAME_OK_TO_LOG)) return; - static char buf[1024]; + static char buf[MaxStringLen]; va_list va; va_start( va, fmt ); - _vsnprintf(buf, 1024, fmt, va ); + _vsnprintf(buf, MaxStringLen, fmt, va ); va_end( va ); - buf[1023] = 0; + buf[MaxStringLen-1] = 0; addCRCDebugLine("%s", buf); //DEBUG_LOG(("%s", buf)); @@ -291,8 +269,8 @@ void addCRCDumpLine(const char *fmt, ...) /* va_list va; va_start( va, fmt ); - _vsnprintf(DumpStrings[nextDumpString], 1024, fmt, va ); - DumpStrings[nextDumpString][1023] = 0; + _vsnprintf(DumpStrings[nextDumpString], MaxStringLen, fmt, va ); + DumpStrings[nextDumpString][MaxStringLen-1] = 0; va_end( va ); ++nextDumpString; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp b/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp index 5d5905e3ef7..a86e76725d3 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp @@ -232,7 +232,7 @@ static void doLogOutput(const char *buffer) } // Uncomment this to show normal logging stuff in the crc logging. - //addCRCDebugLineNoCounter(buffer); + //addCRCDebugLineNoCounter("%s", buffer); } #endif From 2be0870221f79ed24813962597c5be49a1e423f3 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Thu, 10 Apr 2025 22:22:51 +0200 Subject: [PATCH 15/21] Add define NORMAL_LOG_IN_CRC_LOG to control normal logging in crc logs. --- .../Code/GameEngine/Source/Common/System/Debug.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp b/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp index d0fcc0071c2..95c7d0b6514 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp @@ -46,7 +46,14 @@ #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine -// USER INCLUDES +// USER INCLUDES + +// TheSuperHackers @feature helmutbuhler 04/10/2025 +// Uncomment this to show normal logging stuff in the crc logging. +// This can be helpful for context, but can also clutter diffs because normal logs arn't necessarily +// deterministic or the same on all peers in multiplayer games. +//#define NORMAL_LOG_IN_CRC_LOG + #define DEBUG_THREADSAFE #ifdef DEBUG_THREADSAFE #include "Common/CriticalSection.h" @@ -237,8 +244,9 @@ static void doLogOutput(const char *buffer) ::OutputDebugString(buffer); } - // Uncomment this to show normal logging stuff in the crc logging. - //addCRCDebugLineNoCounter("%s", buffer); +#ifdef NORMAL_LOG_IN_CRC_LOG + addCRCDebugLineNoCounter("%s", buffer); +#endif } #endif From 3813f097fd4b24fd0c50fd6fa51d205cec932bd0 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Thu, 10 Apr 2025 22:23:52 +0200 Subject: [PATCH 16/21] Add cmake options to control logging --- cmake/config.cmake | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/cmake/config.cmake b/cmake/config.cmake index df3350f6e00..15808d1b6d9 100644 --- a/cmake/config.cmake +++ b/cmake/config.cmake @@ -21,6 +21,17 @@ add_feature_info(DebugBuild RTS_BUILD_OPTION_DEBUG "Building as a \"Debug\" buil add_feature_info(AddressSanitizer RTS_BUILD_OPTION_ASAN "Building with address sanitizer") +### LOGGING ### + +option(RTS_LOGGING_RELEASE_DEBUG_LOGGING "Enable logging in Release build" OFF) +option(RTS_LOGGING_DISABLE_DEBUG_LOGGING "Disable logging in Debug build" OFF) +option(RTS_LOGGING_NORMAL_LOG_IN_CRC_LOG "Enable normal logging in crc logging" OFF) + +add_feature_info(LoggingReleaseDebugLogging RTS_LOGGING_RELEASE_DEBUG_LOGGING "Force logging on in all builds, even Release. Note that enabling logging can alter Game Logic and cause mismatches.") +add_feature_info(LoggingDisableDebugLogging RTS_LOGGING_DISABLE_DEBUG_LOGGING "Force disable logging in all builds.") +add_feature_info(LoggingNormalLoggingInCRCLog RTS_LOGGING_NORMAL_LOG_IN_CRC_LOG "Capture normal logs in crc logs.") + + ### GAME MEMORY OPTIONS ### # Game Memory features @@ -88,6 +99,20 @@ else() endif() +# Logging +if(RTS_LOGGING_RELEASE_DEBUG_LOGGING) + target_compile_definitions(core_config INTERFACE RELEASE_DEBUG_LOGGING) +endif() + +if(RTS_LOGGING_DISABLE_DEBUG_LOGGING) + target_compile_definitions(core_config INTERFACE DISABLE_DEBUG_LOGGING) +endif() + +if(RTS_LOGGING_NORMAL_LOG_IN_CRC_LOG) + target_compile_definitions(core_config INTERFACE NORMAL_LOG_IN_CRC_LOG) +endif() + + # Game Memory features if(NOT RTS_GAMEMEMORY_ENABLE) target_compile_definitions(core_config INTERFACE DISABLE_GAMEMEMORY=1) From bb744e7a2797408a21990cca67e35369f8d4e494 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sun, 13 Apr 2025 20:23:59 +0200 Subject: [PATCH 17/21] Readd logging configuration to cmake files --- cmake/config-logging.cmake | 21 +++++++++++++++++++++ cmake/config.cmake | 1 + 2 files changed, 22 insertions(+) create mode 100644 cmake/config-logging.cmake diff --git a/cmake/config-logging.cmake b/cmake/config-logging.cmake new file mode 100644 index 00000000000..e8273b304a3 --- /dev/null +++ b/cmake/config-logging.cmake @@ -0,0 +1,21 @@ + +option(RTS_LOGGING_RELEASE_DEBUG_LOGGING "Enable logging in Release build" OFF) +option(RTS_LOGGING_DISABLE_DEBUG_LOGGING "Disable logging in Debug build" OFF) +option(RTS_LOGGING_NORMAL_LOG_IN_CRC_LOG "Enable normal logging in crc logging" OFF) + +add_feature_info(LoggingReleaseDebugLogging RTS_LOGGING_RELEASE_DEBUG_LOGGING "Force logging on in all builds, even Release. Note that enabling logging can alter Game Logic and cause mismatches.") +add_feature_info(LoggingDisableDebugLogging RTS_LOGGING_DISABLE_DEBUG_LOGGING "Force disable logging in all builds.") +add_feature_info(LoggingNormalLoggingInCRCLog RTS_LOGGING_NORMAL_LOG_IN_CRC_LOG "Capture normal logs in crc logs.") + +if(RTS_LOGGING_RELEASE_DEBUG_LOGGING) + target_compile_definitions(core_config INTERFACE RELEASE_DEBUG_LOGGING) +endif() + +if(RTS_LOGGING_DISABLE_DEBUG_LOGGING) + target_compile_definitions(core_config INTERFACE DISABLE_DEBUG_LOGGING) +endif() + +if(RTS_LOGGING_NORMAL_LOG_IN_CRC_LOG) + target_compile_definitions(core_config INTERFACE NORMAL_LOG_IN_CRC_LOG) +endif() + diff --git a/cmake/config.cmake b/cmake/config.cmake index f1c52f1652f..475c9136c1e 100644 --- a/cmake/config.cmake +++ b/cmake/config.cmake @@ -1,4 +1,5 @@ add_library(core_config INTERFACE) include(${CMAKE_CURRENT_LIST_DIR}/config-build.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/config-logging.cmake) include(${CMAKE_CURRENT_LIST_DIR}/config-memory.cmake) From adfbe8b4605ead189ac79c6282601b36058b0c8e Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Mon, 14 Apr 2025 23:22:04 +0200 Subject: [PATCH 18/21] Remove config-logging.cmake --- cmake/config-logging.cmake | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 cmake/config-logging.cmake diff --git a/cmake/config-logging.cmake b/cmake/config-logging.cmake deleted file mode 100644 index e8273b304a3..00000000000 --- a/cmake/config-logging.cmake +++ /dev/null @@ -1,21 +0,0 @@ - -option(RTS_LOGGING_RELEASE_DEBUG_LOGGING "Enable logging in Release build" OFF) -option(RTS_LOGGING_DISABLE_DEBUG_LOGGING "Disable logging in Debug build" OFF) -option(RTS_LOGGING_NORMAL_LOG_IN_CRC_LOG "Enable normal logging in crc logging" OFF) - -add_feature_info(LoggingReleaseDebugLogging RTS_LOGGING_RELEASE_DEBUG_LOGGING "Force logging on in all builds, even Release. Note that enabling logging can alter Game Logic and cause mismatches.") -add_feature_info(LoggingDisableDebugLogging RTS_LOGGING_DISABLE_DEBUG_LOGGING "Force disable logging in all builds.") -add_feature_info(LoggingNormalLoggingInCRCLog RTS_LOGGING_NORMAL_LOG_IN_CRC_LOG "Capture normal logs in crc logs.") - -if(RTS_LOGGING_RELEASE_DEBUG_LOGGING) - target_compile_definitions(core_config INTERFACE RELEASE_DEBUG_LOGGING) -endif() - -if(RTS_LOGGING_DISABLE_DEBUG_LOGGING) - target_compile_definitions(core_config INTERFACE DISABLE_DEBUG_LOGGING) -endif() - -if(RTS_LOGGING_NORMAL_LOG_IN_CRC_LOG) - target_compile_definitions(core_config INTERFACE NORMAL_LOG_IN_CRC_LOG) -endif() - From 6fdf9ed0a3ef70d5bd348e62dd6e5e5409275b3f Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Mon, 14 Apr 2025 23:23:32 +0200 Subject: [PATCH 19/21] Fix wrong cmake feature names --- cmake/config-debug.cmake | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cmake/config-debug.cmake b/cmake/config-debug.cmake index 1d52400bab1..a350825c69c 100644 --- a/cmake/config-debug.cmake +++ b/cmake/config-debug.cmake @@ -11,12 +11,14 @@ set(RTS_DEBUG_PROFILE "DEFAULT" CACHE STRING "Enables debug profiling. When DEFA set_property(CACHE RTS_DEBUG_PROFILE PROPERTY STRINGS DEFAULT ON OFF) -add_feature_info(DebugLogging RTS_DEBUG_LOGGING "Build with Debug Logging") -add_feature_info(DebugLogging RTS_DEBUG_CRASHING "Build with Debug Crashing") -add_feature_info(DebugLogging RTS_DEBUG_STACKTRACE "Build with Debug Stacktracing") -add_feature_info(DebugLogging RTS_DEBUG_PROFILE "Build with Debug Profiling") +add_feature_info(DebugLogging RTS_DEBUG_LOGGING "Build with Debug Logging") +add_feature_info(DebugCrashing RTS_DEBUG_CRASHING "Build with Debug Crashing") +add_feature_info(DebugStacktrace RTS_DEBUG_STACKTRACE "Build with Debug Stacktracing") +add_feature_info(DebugProfile RTS_DEBUG_PROFILE "Build with Debug Profiling") + +# Helper macro that handles DEFAULT ON OFF options macro(define_debug_option OptionName OptionEnabledCompileDef OptionDisabledCompileDef) if(${OptionName} STREQUAL "DEFAULT") # Does nothing From c26a4fa8a399d63d3047f23601fc93d8402dd037 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Mon, 14 Apr 2025 23:23:56 +0200 Subject: [PATCH 20/21] Add RTS_DEBUG_INCLUDE_DEBUG_LOG_IN_CRC_LOG option (again) --- GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp | 4 ++-- cmake/config-debug.cmake | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp b/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp index ad614819180..fc7252ad6ac 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp @@ -52,7 +52,7 @@ // Uncomment this to show normal logging stuff in the crc logging. // This can be helpful for context, but can also clutter diffs because normal logs arn't necessarily // deterministic or the same on all peers in multiplayer games. -//#define NORMAL_LOG_IN_CRC_LOG +//#define INCLUDE_DEBUG_LOG_IN_CRC_LOG #define DEBUG_THREADSAFE #ifdef DEBUG_THREADSAFE @@ -246,7 +246,7 @@ static void doLogOutput(const char *buffer) ::OutputDebugString(buffer); } -#ifdef NORMAL_LOG_IN_CRC_LOG +#ifdef INCLUDE_DEBUG_LOG_IN_CRC_LOG addCRCDebugLineNoCounter("%s", buffer); #endif } diff --git a/cmake/config-debug.cmake b/cmake/config-debug.cmake index a350825c69c..3fe4b1746fd 100644 --- a/cmake/config-debug.cmake +++ b/cmake/config-debug.cmake @@ -10,12 +10,14 @@ set_property(CACHE RTS_DEBUG_STACKTRACE PROPERTY STRINGS DEFAULT ON OFF) set(RTS_DEBUG_PROFILE "DEFAULT" CACHE STRING "Enables debug profiling. When DEFAULT, this option is enabled with DEBUG or INTERNAL") set_property(CACHE RTS_DEBUG_PROFILE PROPERTY STRINGS DEFAULT ON OFF) +option(RTS_DEBUG_INCLUDE_DEBUG_LOG_IN_CRC_LOG "Include normal debug log in crc log" OFF) add_feature_info(DebugLogging RTS_DEBUG_LOGGING "Build with Debug Logging") add_feature_info(DebugCrashing RTS_DEBUG_CRASHING "Build with Debug Crashing") add_feature_info(DebugStacktrace RTS_DEBUG_STACKTRACE "Build with Debug Stacktracing") add_feature_info(DebugProfile RTS_DEBUG_PROFILE "Build with Debug Profiling") +add_feature_info(DebugIncludeDebugLogInCrcLog RTS_DEBUG_INCLUDE_DEBUG_LOG_IN_CRC_LOG "Include debug logs in crc logs") # Helper macro that handles DEFAULT ON OFF options @@ -36,3 +38,7 @@ define_debug_option(RTS_DEBUG_LOGGING DEBUG_LOGGING DISABLE_DEBUG_LOGGING define_debug_option(RTS_DEBUG_CRASHING DEBUG_CRASHING DISABLE_DEBUG_CRASHING ) define_debug_option(RTS_DEBUG_STACKTRACE DEBUG_STACKTRACE DISABLE_DEBUG_STACKTRACE) define_debug_option(RTS_DEBUG_PROFILE DEBUG_PROFILE DISABLE_DEBUG_PROFILE ) + +if(RTS_DEBUG_INCLUDE_DEBUG_LOG_IN_CRC_LOG) + target_compile_definitions(core_config INTERFACE INCLUDE_DEBUG_LOG_IN_CRC_LOG) +endif() From 49173621ad1ada2dd74fac742c64b27c398f1215 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Tue, 15 Apr 2025 22:20:50 +0200 Subject: [PATCH 21/21] Fix comments --- GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp | 2 +- cmake/config-debug.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp b/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp index fc7252ad6ac..40c29ed4055 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/System/Debug.cpp @@ -50,7 +50,7 @@ // TheSuperHackers @feature helmutbuhler 04/10/2025 // Uncomment this to show normal logging stuff in the crc logging. -// This can be helpful for context, but can also clutter diffs because normal logs arn't necessarily +// This can be helpful for context, but can also clutter diffs because normal logs aren't necessarily // deterministic or the same on all peers in multiplayer games. //#define INCLUDE_DEBUG_LOG_IN_CRC_LOG diff --git a/cmake/config-debug.cmake b/cmake/config-debug.cmake index 3fe4b1746fd..36458f131b0 100644 --- a/cmake/config-debug.cmake +++ b/cmake/config-debug.cmake @@ -17,7 +17,7 @@ add_feature_info(DebugLogging RTS_DEBUG_LOGGING add_feature_info(DebugCrashing RTS_DEBUG_CRASHING "Build with Debug Crashing") add_feature_info(DebugStacktrace RTS_DEBUG_STACKTRACE "Build with Debug Stacktracing") add_feature_info(DebugProfile RTS_DEBUG_PROFILE "Build with Debug Profiling") -add_feature_info(DebugIncludeDebugLogInCrcLog RTS_DEBUG_INCLUDE_DEBUG_LOG_IN_CRC_LOG "Include debug logs in crc logs") +add_feature_info(DebugIncludeDebugLogInCrcLog RTS_DEBUG_INCLUDE_DEBUG_LOG_IN_CRC_LOG "Build with Debug Logging in CRC log") # Helper macro that handles DEFAULT ON OFF options