From e4b6f186e563af3ed9b9357e87ed6323da559ae8 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sat, 22 Mar 2025 21:12:08 +0100 Subject: [PATCH 01/42] 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 41f4242bd6..3b428d43f5 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/42] 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 cab9f0b73a..4d98c6bd69 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/42] 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 3b428d43f5..abf4ca4f4a 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/42] 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 91d18bc9bb..0b9836effd 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 2e2719bde1..b47234fe7d 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/42] 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 99408dad6b..2d9cbe2efe 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 46c781f656..b225639523 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 abf4ca4f4a..310daa959a 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 0a90b9cefc..5d5905e3ef 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 b47234fe7d..0a2455a3a1 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/42] 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 0182d5695d..f21e76eb37 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 0b9836effd..dd89756b72 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 e1d33961f4..4ed23eab8b 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/42] 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 dd89756b72..a52c234d3d 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/42] 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 a52c234d3d..923a6b2ef7 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/42] 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 f21e76eb37..55e9dd5bcc 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/42] 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 b225639523..4595942ce6 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/42] 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 4d98c6bd69..d758d92848 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 310daa959a..f479b1b983 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/42] 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 923a6b2ef7..5b11aa8d91 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 0a2455a3a1..2e2719bde1 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/42] 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 55e9dd5bcc..c10523fee2 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/42] 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 2d9cbe2efe..a43f8e1172 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 4595942ce6..ff777cb600 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 5d5905e3ef..a86e76725d 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/42] 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 d0fcc0071c..95c7d0b651 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/42] 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 df3350f6e0..15808d1b6d 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 83e7ec193302d83554a61ab0703a495d9465072d Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Fri, 11 Apr 2025 00:58:25 +0200 Subject: [PATCH 17/42] Add m_headless global flag to optionally run the game in headless mode. --- .../GameEngine/Include/Common/GlobalData.h | 5 + .../Code/GameEngine/Include/Common/Radar.h | 24 ++ .../GameEngine/Source/Common/GameEngine.cpp | 2 +- .../GameEngine/Source/Common/GlobalData.cpp | 2 + .../GameEngine/Source/Common/RandomValue.cpp | 2 +- .../GameEngine/Source/Common/Recorder.cpp | 2 +- .../GameEngine/Source/Common/System/Radar.cpp | 7 +- .../GameClient/GUI/ControlBar/ControlBar.cpp | 22 +- .../GUI/ControlBar/ControlBarScheme.cpp | 2 + .../ControlBarPopupDescription.cpp | 2 +- .../GameClient/GUI/GUICallbacks/Diplomacy.cpp | 2 + .../GUI/GUICallbacks/InGameChat.cpp | 2 +- .../Source/GameClient/GameClient.cpp | 87 ++++-- .../GameEngine/Source/GameClient/InGameUI.cpp | 88 +++--- .../GameLogic/ScriptEngine/ScriptActions.cpp | 23 +- .../Source/GameLogic/System/GameLogic.cpp | 21 +- .../GameLogic/System/GameLogicDispatch.cpp | 12 +- .../W3DDevice/GameClient/BaseHeightMap.cpp | 40 +-- .../Drawable/Draw/W3DDebrisDraw.cpp | 6 +- .../GameClient/Drawable/Draw/W3DLaserDraw.cpp | 6 +- .../GameClient/Drawable/Draw/W3DModelDraw.cpp | 6 +- .../Source/W3DDevice/GameClient/HeightMap.cpp | 4 +- .../W3DDevice/GameClient/W3DAssetManager.cpp | 7 + .../W3DDevice/GameClient/W3DDisplay.cpp | 265 ++++++++++-------- .../W3DDevice/GameClient/W3DInGameUI.cpp | 3 +- .../W3DDevice/GameClient/W3DTerrainVisual.cpp | 55 ++-- .../Source/W3DDevice/GameClient/W3DView.cpp | 4 +- .../W3DDevice/GameLogic/W3DGhostObject.cpp | 5 +- GeneralsMD/Code/Main/WinMain.cpp | 4 +- 29 files changed, 436 insertions(+), 274 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h index 8dd24109ca..b3babd2b12 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h @@ -100,6 +100,11 @@ class GlobalData : public SubsystemInterface Bool m_dumpAssetUsage; Int m_framesPerSecondLimit; Int m_chipSetType; ///m_headless ? NEW RadarHeadless : createRadar(), NULL); initSubsystem(TheVictoryConditions,"TheVictoryConditions", createVictoryConditions(), NULL); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp index b641acf4e7..437d122268 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp @@ -607,6 +607,8 @@ GlobalData::GlobalData() m_dumpAssetUsage = FALSE; m_framesPerSecondLimit = 0; m_chipSetType = 0; + //m_headless = FALSE; + m_headless = TRUE; m_windowed = 0; m_xResolution = 800; m_yResolution = 600; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/RandomValue.cpp b/GeneralsMD/Code/GameEngine/Source/Common/RandomValue.cpp index 289eb1f10a..11989e118c 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/RandomValue.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/RandomValue.cpp @@ -249,7 +249,7 @@ Int GetGameClientRandomValue( int lo, int hi, const char *file, int line ) /**/ #ifdef DEBUG_RANDOM_CLIENT DEBUG_LOG(( "%d: GetGameClientRandomValue = %d (%d - %d), %s line %d\n", - TheGameLogic->getFrame(), rval, lo, hi, file, line )); + TheGameLogic ? TheGameLogic->getFrame() : -1, rval, lo, hi, file, line )); #endif /**/ diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp index 46ebcaa78e..8ede28f0b5 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp @@ -1610,7 +1610,7 @@ RecorderModeType RecorderClass::getMode() { void RecorderClass::initControls() { NameKeyType parentReplayControlID = TheNameKeyGenerator->nameToKey( AsciiString("ReplayControl.wnd:ParentReplayControl") ); - GameWindow *parentReplayControl = TheWindowManager->winGetWindowFromId( NULL, parentReplayControlID ); + GameWindow *parentReplayControl = TheWindowManager ? TheWindowManager->winGetWindowFromId( NULL, parentReplayControlID ) : NULL; Bool show = (getMode() != RECORDERMODETYPE_PLAYBACK); if (parentReplayControl) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/System/Radar.cpp b/GeneralsMD/Code/GameEngine/Source/Common/System/Radar.cpp index 1b792fabbf..5d15adb588 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/System/Radar.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/System/Radar.cpp @@ -335,8 +335,11 @@ void Radar::newMap( TerrainLogic *terrain ) // keep a pointer for our radar window Int id = NAMEKEY( "ControlBar.wnd:LeftHUD" ); - m_radarWindow = TheWindowManager->winGetWindowFromId( NULL, id ); - DEBUG_ASSERTCRASH( m_radarWindow, ("Radar::newMap - Unable to find radar game window\n") ); + if (TheWindowManager != NULL) + { + m_radarWindow = TheWindowManager->winGetWindowFromId( NULL, id ); + DEBUG_ASSERTCRASH( m_radarWindow, ("Radar::newMap - Unable to find radar game window\n") ); + } // reset all the data in the radar reset(); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp index 4d9a8774fa..f680558e86 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp @@ -1382,6 +1382,8 @@ void ControlBar::reset( void ) //------------------------------------------------------------------------------------------------- void ControlBar::update( void ) { + if (TheGlobalData->m_headless) + return; getStarImage(); updateRadarAttackGlow(); if(m_controlBarSchemeManager) @@ -1652,7 +1654,7 @@ const Image *ControlBar::getStarImage(void ) else m_lastFlashedAtPointValue = ThePlayerList->getLocalPlayer()->getSciencePurchasePoints(); - GameWindow *win= TheWindowManager->winGetWindowFromId( NULL, TheNameKeyGenerator->nameToKey( "ControlBar.wnd:ButtonGeneral" ) ); + GameWindow *win= TheWindowManager ? TheWindowManager->winGetWindowFromId( NULL, TheNameKeyGenerator->nameToKey( "ControlBar.wnd:ButtonGeneral" ) ) : NULL; if(!win) return NULL; if(!m_genStarFlash) @@ -2141,6 +2143,12 @@ void ControlBar::switchToContext( ControlBarContext context, Drawable *draw ) // save a pointer for the currently selected drawable m_currentSelectedDrawable = draw; + if (TheWindowManager == NULL) + { + m_currContext = context; + return; + } + if (IsInGameChatActive() == FALSE && TheGameLogic && !TheGameLogic->isInShellGame()) { TheWindowManager->winSetFocus( NULL ); } @@ -2593,6 +2601,8 @@ void ControlBar::setPortraitByImage( const Image *image ) //------------------------------------------------------------------------------------------------- void ControlBar::setPortraitByObject( Object *obj ) { + if (TheWindowManager == NULL) + return; if( obj ) { @@ -2754,7 +2764,8 @@ void ControlBar::setControlBarSchemeByPlayer(Player *p) { if(m_controlBarSchemeManager) m_controlBarSchemeManager->setControlBarSchemeByPlayer(p); - + if (TheWindowManager == NULL) + return; static NameKeyType buttonPlaceBeaconID = NAMEKEY( "ControlBar.wnd:ButtonPlaceBeacon" ); static NameKeyType buttonIdleWorkerID = NAMEKEY("ControlBar.wnd:ButtonIdleWorker"); static NameKeyType buttonGeneralID = NAMEKEY("ControlBar.wnd:ButtonGeneral"); @@ -2961,7 +2972,7 @@ void ControlBar::showPurchaseScience( void ) void ControlBar::hidePurchaseScience( void ) { - if(m_contextParent[ CP_PURCHASE_SCIENCE ]->winIsHidden()) + if(m_contextParent[ CP_PURCHASE_SCIENCE ] == NULL || m_contextParent[ CP_PURCHASE_SCIENCE ]->winIsHidden()) return; if( m_contextParent[ CP_PURCHASE_SCIENCE ] ) @@ -2986,6 +2997,8 @@ void ControlBar::hidePurchaseScience( void ) void ControlBar::togglePurchaseScience( void ) { + if (m_contextParent[ CP_PURCHASE_SCIENCE ] == NULL) + return; if(m_contextParent[ CP_PURCHASE_SCIENCE ]->winIsHidden()) showPurchaseScience(); else @@ -3244,7 +3257,8 @@ void ControlBar::initSpecialPowershortcutBar( Player *player) if(!player || !pt|| !player->isLocalPlayer() || pt->getSpecialPowerShortcutButtonCount() == 0 || pt->getSpecialPowerShortcutWinName().isEmpty() - || !player->isPlayerActive()) + || !player->isPlayerActive() + || TheWindowManager == NULL) return; m_currentlyUsedSpecialPowersButtons = pt->getSpecialPowerShortcutButtonCount(); AsciiString layoutName, tempName, windowName, parentName; diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarScheme.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarScheme.cpp index 2d8ea77453..7baa7ed756 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarScheme.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarScheme.cpp @@ -1162,6 +1162,8 @@ void ControlBarSchemeManager::setControlBarSchemeByPlayerTemplate( const PlayerT //----------------------------------------------------------------------------- void ControlBarSchemeManager::setControlBarSchemeByPlayer(Player *p) { + if (TheWindowManager == NULL) + return; GameWindow *communicatorButton = TheWindowManager->winGetWindowFromId( NULL, NAMEKEY("ControlBar.wnd:PopupCommunicator") ); if (communicatorButton && TheControlBar) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/ControlBarPopupDescription.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/ControlBarPopupDescription.cpp index f54463db74..b674e2d613 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/ControlBarPopupDescription.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/ControlBarPopupDescription.cpp @@ -702,7 +702,7 @@ void ControlBar::deleteBuildTooltipLayout( void ) { m_showBuildToolTipLayout = FALSE; prevWindow= NULL; - m_buildToolTipLayout->hide(TRUE); + if (m_buildToolTipLayout) m_buildToolTipLayout->hide(TRUE); // if(!m_buildToolTipLayout) // return; // diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp index 2f29ed4e25..3f20a3fbba 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp @@ -169,6 +169,8 @@ BriefingList* GetBriefingTextList(void) //------------------------------------------------------------------------------------------------- void UpdateDiplomacyBriefingText(AsciiString newText, Bool clear) { + if (TheWindowManager == NULL) + return; GameWindow *listboxSolo = TheWindowManager->winGetWindowFromId(theWindow, NAMEKEY("Diplomacy.wnd:ListboxSolo")); if (clear) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/InGameChat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/InGameChat.cpp index c71f8c0f93..6ae73663a5 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/InGameChat.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/InGameChat.cpp @@ -114,7 +114,7 @@ void HideInGameChat( Bool immediate ) chatTextEntry->winEnable(FALSE); TheWindowManager->winSetFocus( NULL ); } - TheWindowManager->winSetFocus( NULL ); + if (TheWindowManager) TheWindowManager->winSetFocus( NULL ); } // ------------------------------------------------------------------------------------------------ diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp index 21be7ea196..b567ff5f87 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp @@ -270,10 +270,13 @@ void GameClient::init( void ) TheDisplayStringManager->setName("TheDisplayStringManager"); } - // create the keyboard - TheKeyboard = createKeyboard(); - TheKeyboard->init(); - TheKeyboard->setName("TheKeyboard"); + if (!TheGlobalData->m_headless) + { + // create the keyboard + TheKeyboard = createKeyboard(); + TheKeyboard->init(); + TheKeyboard->setName("TheKeyboard"); + } // allocate and load image collection for the GUI and just load the 256x256 ones for now TheMappedImageCollection = MSGNEW("GameClientSubsystem") ImageCollection; @@ -321,11 +324,14 @@ void GameClient::init( void ) if( TheFontLibrary ) TheFontLibrary->init(); - // create the mouse - TheMouse = createMouse(); - TheMouse->parseIni(); - TheMouse->initCursorResources(); - TheMouse->setName("TheMouse"); + if (!TheGlobalData->m_headless) + { + // create the mouse + TheMouse = createMouse(); + TheMouse->parseIni(); + TheMouse->initCursorResources(); + TheMouse->setName("TheMouse"); + } // instantiate the display TheDisplay = createGameDisplay(); @@ -339,23 +345,25 @@ void GameClient::init( void ) TheHeaderTemplateManager->init(); } - // create the window manager - TheWindowManager = createWindowManager(); - if( TheWindowManager ) + if (!TheGlobalData->m_headless) { + // create the window manager + TheWindowManager = createWindowManager(); + if( TheWindowManager ) + { - TheWindowManager->init(); - TheWindowManager->setName("TheWindowManager"); -// TheWindowManager->initTestGUI(); - - } // end if + TheWindowManager->init(); + TheWindowManager->setName("TheWindowManager"); + // TheWindowManager->initTestGUI(); - // create the IME manager - TheIMEManager = CreateIMEManagerInterface(); - if ( TheIMEManager ) - { - TheIMEManager->init(); - TheIMEManager->setName("TheIMEManager"); + } // end if + // create the IME manager + TheIMEManager = CreateIMEManagerInterface(); + if ( TheIMEManager ) + { + TheIMEManager->init(); + TheIMEManager->setName("TheIMEManager"); + } } // create the shell @@ -397,11 +405,10 @@ void GameClient::init( void ) TheRayEffects->setName("TheRayEffects"); } - TheMouse->init(); //finish initializing the mouse. - // set the limits of the mouse now that we've created the display and such if( TheMouse ) { + TheMouse->init(); //finish initializing the mouse. TheMouse->setPosition( 0, 0 ); TheMouse->setMouseLimits(); TheMouse->setName("TheMouse"); @@ -633,6 +640,7 @@ void GameClient::update( void ) } // update the window system itself + if (TheWindowManager) { TheWindowManager->UPDATE(); } @@ -1612,3 +1620,32 @@ void GameClient::crc( Xfer *xfer ) { } // end crc + +/* +g_headless disable: +GameEngine: + TheGameClient + TheKeyboard = NULL + TheMouse = NULL + TheDisplay is partially disabled: + m_3DInterfaceScene = NULL + m_2DScene = NULL + m_3DScene = NULL + m_assetManager remains! + TheWindowManager = NULL + TheIMEManager = NULL + TheTerrainVisual is partially disabled: + TheTerrainTracksRenderObjClassSystem = NULL + TheW3DShadowManager = NULL + TheWaterRenderObj = NULL + TheSmudgeManager = NULL + TheTerrainRenderObject is partially disabled: + m_treeBuffer = NULL + m_propBuffer = NULL + m_bibBuffer = NULL + m_bridgeBuffer = NULL + m_waypointBuffer = NULL + m_roadBuffer = NULL + m_shroud = NULL + TheRadar set to RadarHeadless +*/ \ No newline at end of file diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp index 1fa290fa39..87da7589a3 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp @@ -1298,7 +1298,7 @@ void InGameUI::setRadiusCursor(RadiusCursorType cursorType, const SpecialPowerTe //------------------------------------------------------------------------------------------------- void InGameUI::handleRadiusCursor() { - if (!m_curRadiusCursor.isEmpty()) + if (!m_curRadiusCursor.isEmpty() && TheMouse) { const MouseIO* mouseIO = TheMouse->getMouseStatus(); Coord3D pos; @@ -1337,6 +1337,8 @@ void InGameUI::handleRadiusCursor() void InGameUI::triggerDoubleClickAttackMoveGuardHint( void ) { + if (TheMouse == NULL) + return; m_duringDoubleClickAttackMoveGuardHintTimer = 11; const MouseIO* mouseIO = TheMouse->getMouseStatus(); TheTacticalView->screenToTerrain( &mouseIO->pos, &m_duringDoubleClickAttackMoveGuardHintStashedPosition ); @@ -1803,39 +1805,42 @@ void InGameUI::update( void ) static NameKeyType moneyWindowKey = TheNameKeyGenerator->nameToKey( "ControlBar.wnd:MoneyDisplay" ); static NameKeyType powerWindowKey = TheNameKeyGenerator->nameToKey( "ControlBar.wnd:PowerWindow" ); - GameWindow *moneyWin = TheWindowManager->winGetWindowFromId( NULL, moneyWindowKey ); - GameWindow *powerWin = TheWindowManager->winGetWindowFromId( NULL, powerWindowKey ); -// if( moneyWin == NULL ) -// { -// NameKeyType moneyWindowKey = TheNameKeyGenerator->nameToKey( "ControlBar.wnd:MoneyDisplay" ); -// -// moneyWin = TheWindowManager->winGetWindowFromId( NULL, moneyWindowKey ); -// -// } // end if - Player *moneyPlayer = NULL; - if( TheControlBar->isObserverControlBarOn()) - moneyPlayer = TheControlBar->getObserverLookAtPlayer(); - else - moneyPlayer = ThePlayerList->getLocalPlayer(); - if( moneyPlayer) + if (TheWindowManager != NULL) { - Int currentMoney = moneyPlayer->getMoney()->countMoney(); - if( lastMoney != currentMoney ) + GameWindow *moneyWin = TheWindowManager->winGetWindowFromId( NULL, moneyWindowKey ); + GameWindow *powerWin = TheWindowManager->winGetWindowFromId( NULL, powerWindowKey ); + // if( moneyWin == NULL ) + // { + // NameKeyType moneyWindowKey = TheNameKeyGenerator->nameToKey( "ControlBar.wnd:MoneyDisplay" ); + // + // moneyWin = TheWindowManager->winGetWindowFromId( NULL, moneyWindowKey ); + // + // } // end if + Player *moneyPlayer = NULL; + if( TheControlBar->isObserverControlBarOn()) + moneyPlayer = TheControlBar->getObserverLookAtPlayer(); + else + moneyPlayer = ThePlayerList->getLocalPlayer(); + if( moneyPlayer) { - UnicodeString buffer; + Int currentMoney = moneyPlayer->getMoney()->countMoney(); + if( lastMoney != currentMoney ) + { + UnicodeString buffer; - buffer.format( TheGameText->fetch( "GUI:ControlBarMoneyDisplay" ), currentMoney ); - GadgetStaticTextSetText( moneyWin, buffer ); - lastMoney = currentMoney; + buffer.format( TheGameText->fetch( "GUI:ControlBarMoneyDisplay" ), currentMoney ); + GadgetStaticTextSetText( moneyWin, buffer ); + lastMoney = currentMoney; - } // end if - moneyWin->winHide(FALSE); - powerWin->winHide(FALSE); - } - else - { - moneyWin->winHide(TRUE); - powerWin->winHide(TRUE); + } // end if + moneyWin->winHide(FALSE); + powerWin->winHide(FALSE); + } + else + { + moneyWin->winHide(TRUE); + powerWin->winHide(TRUE); + } } // Update the floating Text; @@ -2936,8 +2941,9 @@ void InGameUI::setGUICommand( const CommandButton *command ) } setRadiusCursorNone(); } - - m_mouseModeCursor = TheMouse->getMouseCursor(); + + if (TheMouse != NULL) + m_mouseModeCursor = TheMouse->getMouseCursor(); } // end setGUICommand @@ -3863,8 +3869,8 @@ void InGameUI::expireHint( HintType type, UnsignedInt hintIndex ) //------------------------------------------------------------------------------------------------- void InGameUI::createControlBar( void ) { - - TheWindowManager->winCreateFromScript( AsciiString("ControlBar.wnd") ); + if (TheWindowManager != NULL) + TheWindowManager->winCreateFromScript( AsciiString("ControlBar.wnd") ); HideControlBar(); /* // hide all windows created from this layout @@ -3880,8 +3886,8 @@ void InGameUI::createControlBar( void ) //------------------------------------------------------------------------------------------------- void InGameUI::createReplayControl( void ) { - - m_replayWindow = TheWindowManager->winCreateFromScript( AsciiString("ReplayControl.wnd") ); + if (TheWindowManager != NULL) + m_replayWindow = TheWindowManager->winCreateFromScript( AsciiString("ReplayControl.wnd") ); /* // hide all windows created from this layout @@ -3983,6 +3989,8 @@ void InGameUI::playCameoMovie( const AsciiString& movieName ) void InGameUI::stopCameoMovie( void ) { //RightHUD + if (TheWindowManager == NULL) + return; //GameWindow *window = TheWindowManager->winGetWindowFromId(NULL,TheNameKeyGenerator->nameToKey( AsciiString("ControlBar.wnd:CameoMovieWindow") )); GameWindow *window = TheWindowManager->winGetWindowFromId(NULL,TheNameKeyGenerator->nameToKey( AsciiString("ControlBar.wnd:RightHUD") )); // window->winHide(FALSE); @@ -4978,6 +4986,8 @@ void InGameUI::buildRegion( const ICoord2D *anchor, const ICoord2D *dest, IRegio //------------------------------------------------------------------------------------------------- void InGameUI::addFloatingText(const UnicodeString& text,const Coord3D *pos, Color color) { + if (TheWindowManager == NULL) + return; if( TheGameLogic->getDrawIconUI() ) { FloatingTextData *newFTD = newInstance( FloatingTextData ); @@ -5011,6 +5021,8 @@ inline Bool isClose(const Coord3D& a, const Coord3D& b) } void InGameUI::DEBUG_addFloatingText(const AsciiString& text, const Coord3D * pos, Color color) { + if (TheWindowManager == NULL) + return; const Int POINTSIZE = 8; const Int LEADING = 0; @@ -5168,6 +5180,8 @@ void InGameUI::popupMessage( const AsciiString& message, Int x, Int y, Int width //------------------------------------------------------------------------------------------------- void InGameUI::popupMessage( const AsciiString& identifier, Int x, Int y, Int width, Color textColor, Bool pause, Bool pauseMusic) { + if (TheWindowManager == NULL) + return; if(m_popupMessageData) clearPopupMessageData(); @@ -5210,7 +5224,7 @@ void InGameUI::popupMessage( const AsciiString& identifier, Int x, Int y, Int wi //------------------------------------------------------------------------------------------------- void InGameUI::clearPopupMessageData( void ) { - if(!m_popupMessageData) + if(!m_popupMessageData || TheWindowManager == NULL) return; if(m_popupMessageData->layout) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp index fdc4613e73..37ef972ecc 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp @@ -217,7 +217,7 @@ void ScriptActions::doVictory( void ) closeWindows(FALSE); TheGameLogic->closeWindows(); doDisableInput(); - if (!m_suppressNewWindows) + if (!m_suppressNewWindows && TheWindowManager) { const Player *localPlayer = ThePlayerList->getLocalPlayer(); Bool showObserverWindow = localPlayer->isPlayerObserver() || TheScriptEngine->hasShownMPLocalDefeatWindow(); @@ -241,7 +241,7 @@ void ScriptActions::doDefeat( void ) closeWindows(FALSE); TheGameLogic->closeWindows(); doDisableInput(); - if (!m_suppressNewWindows) + if (!m_suppressNewWindows && TheWindowManager != NULL) { const Player *localPlayer = ThePlayerList->getLocalPlayer(); Bool showObserverWindow = localPlayer->isPlayerObserver() || TheScriptEngine->hasShownMPLocalDefeatWindow(); @@ -3201,17 +3201,16 @@ void ScriptActions::doMergeTeamIntoTeam(const AsciiString& teamSrcName, const As void ScriptActions::doDisableInput() { #if defined(_DEBUG) || defined(_INTERNAL) - if (!TheGlobalData->m_disableScriptedInputDisabling) + if (TheGlobalData->m_disableScriptedInputDisabling) + return; #endif - { - TheInGameUI->setInputEnabled(false); - TheMouse->setVisibility(false); - TheInGameUI->deselectAllDrawables(); - TheInGameUI->clearAttackMoveToMode(); - TheInGameUI->setWaypointMode( FALSE ); - TheControlBar->deleteBuildTooltipLayout(); - TheLookAtTranslator->resetModes(); - } + TheInGameUI->setInputEnabled(false); + if (TheMouse) TheMouse->setVisibility(false); + TheInGameUI->deselectAllDrawables(); + TheInGameUI->clearAttackMoveToMode(); + TheInGameUI->setWaypointMode( FALSE ); + TheControlBar->deleteBuildTooltipLayout(); + TheLookAtTranslator->resetModes(); } //------------------------------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index d64f523b4b..34a7d4417b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -736,6 +736,9 @@ static void populateRandomSideAndColor( GameInfo *game ) // Prevent from selecting the disabled Generals. // This is also enforced at GUI setup (GUIUtil.cpp). // @todo: unlock these when something rad happens + + // TheSuperHackers @logic-client-separation helmutbuhler 04/11/2025 + // TheChallengeGenerals belongs to client, we shouldn't depend on that here. Bool disallowLockedGenerals = TRUE; const GeneralPersona *general = TheChallengeGenerals->getGeneralByTemplateName(ptTest->getName()); Bool startsLocked = general ? !general->isStartingEnabled() : FALSE; @@ -1981,6 +1984,9 @@ void GameLogic::startNewGame( Bool loadingSaveGame ) // The game will start, but the cheater will be instantly defeated because he has no troops. // This is also enforced at GUI setup (GUIUtil.cpp and UserPreferences.cpp). // @todo: unlock these when something rad happens + + // TheSuperHackers @logic-client-separation helmutbuhler 04/11/2025 + // TheChallengeGenerals belongs to client, we shouldn't depend on that here. Bool disallowLockedGenerals = TRUE; const GeneralPersona *general = TheChallengeGenerals->getGeneralByTemplateName(pt->getName()); Bool startsLocked = general ? !general->isStartingEnabled() : FALSE; @@ -2226,14 +2232,17 @@ void GameLogic::startNewGame( Bool loadingSaveGame ) if(m_gameMode == GAME_SHELL) { - if(TheShell->getScreenCount() == 0) - TheShell->push( AsciiString("Menus/MainMenu.wnd") ); - else if (TheShell->top()) + if (!TheGlobalData->m_headless) { - TheShell->top()->hide(FALSE); - TheShell->top()->bringForward(); + if(TheShell->getScreenCount() == 0) + TheShell->push( AsciiString("Menus/MainMenu.wnd") ); + else if (TheShell->top()) + { + TheShell->top()->hide(FALSE); + TheShell->top()->bringForward(); + } + HideControlBar(); } - HideControlBar(); } else { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp index 2434afa6e6..b96552e37b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp @@ -235,11 +235,13 @@ void GameLogic::closeWindows( void ) // hide the options menu NameKeyType buttonID = TheNameKeyGenerator->nameToKey( "OptionsMenu.wnd:ButtonBack" ); - GameWindow *button = TheWindowManager->winGetWindowFromId( NULL, buttonID ); - GameWindow *window = TheWindowManager->winGetWindowFromId( NULL, TheNameKeyGenerator->nameToKey("OptionsMenu.wnd:OptionsMenuParent") ); - if(window) - TheWindowManager->winSendSystemMsg( window, GBM_SELECTED, - (WindowMsgData)button, buttonID ); + if (TheWindowManager) + { + GameWindow *button = TheWindowManager->winGetWindowFromId( NULL, buttonID ); + GameWindow *window = TheWindowManager->winGetWindowFromId( NULL, TheNameKeyGenerator->nameToKey("OptionsMenu.wnd:OptionsMenuParent") ); + if(window) + TheWindowManager->winSendSystemMsg( window, GBM_SELECTED, (WindowMsgData)button, buttonID ); + } } // ------------------------------------------------------------------------------------------------ diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp index 0a56f49a91..22f7517c53 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp @@ -288,36 +288,41 @@ BaseHeightMapRenderObjClass::BaseHeightMapRenderObjClass(void) m_useDepthFade = false; m_disableTextures = false; TheTerrainRenderObject = this; - m_treeBuffer = NULL; - - m_treeBuffer = NEW W3DTreeBuffer; + m_treeBuffer = NULL; m_propBuffer = NULL; - - m_propBuffer = NEW W3DPropBuffer; - - m_bibBuffer = NULL; - m_bibBuffer = NEW W3DBibBuffer; - m_curImpassableSlope = 45.0f; // default to 45 degrees. m_bridgeBuffer = NULL; - m_bridgeBuffer = NEW W3DBridgeBuffer; - m_waypointBuffer = NEW W3DWaypointBuffer; + m_waypointBuffer = NULL; #ifdef DO_ROADS m_roadBuffer = NULL; - m_roadBuffer = NEW W3DRoadBuffer; #endif #ifdef DO_SCORCH m_vertexScorch = NULL; m_indexScorch = NULL; m_scorchTexture = NULL; clearAllScorches(); + m_shroud = NULL; +#endif + + if (TheGlobalData->m_headless) + return; + + m_treeBuffer = NEW W3DTreeBuffer; + + m_propBuffer = NEW W3DPropBuffer; + + m_bibBuffer = NEW W3DBibBuffer; + + m_curImpassableSlope = 45.0f; // default to 45 degrees. + m_bridgeBuffer = NEW W3DBridgeBuffer; + m_waypointBuffer = NEW W3DWaypointBuffer; +#ifdef DO_ROADS + m_roadBuffer = NEW W3DRoadBuffer; #endif #if defined(_DEBUG) || defined(_INTERNAL) if (TheGlobalData->m_shroudOn) m_shroud = NEW W3DShroud; - else - m_shroud = NULL; #else m_shroud = NEW W3DShroud; #endif @@ -1791,7 +1796,8 @@ Int BaseHeightMapRenderObjClass::initHeightData(Int x, Int y, WorldHeightMap *pM if (m_shroud) m_shroud->init(m_map,TheGlobalData->m_partitionCellSize,TheGlobalData->m_partitionCellSize); #ifdef DO_ROADS - m_roadBuffer->setMap(m_map); + if (m_roadBuffer) + m_roadBuffer->setMap(m_map); #endif HeightSampleType *data = NULL; if (pMap) { @@ -1846,7 +1852,7 @@ Int BaseHeightMapRenderObjClass::initHeightData(Int x, Int y, WorldHeightMap *pM m_curNumScorchIndices=0; // If the textures aren't allocated (usually because of a hardware reset) need to allocate. Bool needToAllocate = false; - if (m_stageTwoTexture == NULL) { + if (m_stageTwoTexture == NULL && m_treeBuffer) { needToAllocate = true; } if (data && needToAllocate) @@ -2368,7 +2374,7 @@ void BaseHeightMapRenderObjClass::staticLightingChanged( void ) m_scorchesInBuffer = 0; // If we just allocated the buffers, we got no scorches in the buffer. m_curNumScorchVertices=0; m_curNumScorchIndices=0; - m_roadBuffer->updateLighting(); + if (m_roadBuffer) m_roadBuffer->updateLighting(); } diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DDebrisDraw.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DDebrisDraw.cpp index 1e709a3c97..f4fca38dc7 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DDebrisDraw.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DDebrisDraw.cpp @@ -73,7 +73,8 @@ W3DDebrisDraw::~W3DDebrisDraw(void) } if (m_renderObject) { - W3DDisplay::m_3DScene->Remove_Render_Object(m_renderObject); + if (W3DDisplay::m_3DScene != NULL) + W3DDisplay::m_3DScene->Remove_Render_Object(m_renderObject); REF_PTR_RELEASE(m_renderObject); m_renderObject = NULL; } @@ -111,7 +112,8 @@ void W3DDebrisDraw::setModelName(AsciiString name, Color color, ShadowType t) DEBUG_ASSERTCRASH(m_renderObject, ("Debris model %s not found!\n",name.str())); if (m_renderObject) { - W3DDisplay::m_3DScene->Add_Render_Object(m_renderObject); + if (W3DDisplay::m_3DScene != NULL) + W3DDisplay::m_3DScene->Add_Render_Object(m_renderObject); m_renderObject->Set_User_Data(getDrawable()->getDrawableInfo()); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DLaserDraw.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DLaserDraw.cpp index 7306011ab8..93349f000c 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DLaserDraw.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DLaserDraw.cpp @@ -205,7 +205,8 @@ W3DLaserDraw::W3DLaserDraw( Thing *thing, const ModuleData* moduleData ) : } // add to scene - W3DDisplay::m_3DScene->Add_Render_Object( line ); //add it to our scene so it gets rendered with other objects. + if (W3DDisplay::m_3DScene != NULL) + W3DDisplay::m_3DScene->Add_Render_Object( line ); //add it to our scene so it gets rendered with other objects. // hide the render object until the first time we come to draw it and // set the correct position @@ -229,7 +230,8 @@ W3DLaserDraw::~W3DLaserDraw( void ) { // remove line from scene - W3DDisplay::m_3DScene->Remove_Render_Object( m_line3D[ i ] ); + if (W3DDisplay::m_3DScene != NULL) + W3DDisplay::m_3DScene->Remove_Render_Object( m_line3D[ i ] ); // delete line REF_PTR_RELEASE( m_line3D[ i ] ); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp index cbf09f7a72..fdf20e54e8 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp @@ -2806,7 +2806,8 @@ void W3DModelDraw::nukeCurrentRender(Matrix3D* xform) // save the transform for the new model if (xform) *xform = m_renderObject->Get_Transform(); - W3DDisplay::m_3DScene->Remove_Render_Object(m_renderObject); + if (W3DDisplay::m_3DScene != NULL) + W3DDisplay::m_3DScene->Remove_Render_Object(m_renderObject); REF_PTR_RELEASE(m_renderObject); m_renderObject = NULL; } @@ -3133,7 +3134,8 @@ void W3DModelDraw::setModelState(const ModelConditionInfo* newState) } // add render object to our scene - W3DDisplay::m_3DScene->Add_Render_Object(m_renderObject); + if (W3DDisplay::m_3DScene != NULL) + W3DDisplay::m_3DScene->Add_Render_Object(m_renderObject); // tie in our drawable as the user data pointer in the render object m_renderObject->Set_User_Data(draw->getDrawableInfo()); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp index 6fd292f62a..cecf9e44dc 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp @@ -1026,7 +1026,7 @@ Int HeightMapRenderObjClass::updateBlock(Int x0, Int y0, Int x1, Int y1, WorldH DEBUG_ASSERTCRASH(y0<=y1, ("HeightMapRenderObjClass::UpdateBlock parameters have inside-out rectangle (on Y).")); #endif Invalidate_Cached_Bounding_Volumes(); - if (pMap) { + if (pMap && m_treeBuffer != NULL) { REF_PTR_SET(m_stageZeroTexture, pMap->getTerrainTexture()); REF_PTR_SET(m_stageOneTexture, pMap->getAlphaTerrainTexture()); } @@ -1326,7 +1326,7 @@ Int HeightMapRenderObjClass::initHeightData(Int x, Int y, WorldHeightMap *pMap, if (m_stageOneTexture == NULL) { needToAllocate = true; } - if (data && needToAllocate) + if (data && needToAllocate && m_treeBuffer != NULL) { //requested heightmap different from old one. freeIndexVertexBuffers(); //Create static index buffers. These will index the vertex buffers holding the map. diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp index 53b7171272..ca7f49ae9e 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp @@ -904,6 +904,9 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( */ int W3DAssetManager::Recolor_Asset(RenderObjClass *robj, const int color) { + if (TheGlobalData->m_headless) + return 0; + switch (robj->Class_ID()) { case RenderObjClass::CLASSID_MESH: return Recolor_Mesh(robj,color); @@ -923,6 +926,8 @@ int W3DAssetManager::Recolor_Mesh(RenderObjClass *robj, const int color) int i; int didRecolor=0; const char *meshName; + if (TheGlobalData->m_headless) + return 0; MeshClass *mesh=(MeshClass*) robj; MeshModelClass * model = mesh->Get_Model(); @@ -966,6 +971,8 @@ int W3DAssetManager::Recolor_Mesh(RenderObjClass *robj, const int color) int W3DAssetManager::Recolor_HLOD(RenderObjClass *robj, const int color) { int didRecolor=0; + if (TheGlobalData->m_headless) + return 0; int num_sub = robj->Get_Num_Sub_Objects(); for(int i = 0; i < num_sub; i++) { diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp index f551296241..b315dddcd6 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp @@ -675,49 +675,54 @@ void W3DDisplay::init( void ) // init the Westwood math library WWMath::Init(); - // create our 3D interface scene - m_3DInterfaceScene = NEW_REF( RTS3DInterfaceScene, () ); - m_3DInterfaceScene->Set_Ambient_Light( Vector3( 1, 1, 1 ) ); + if (!TheGlobalData->m_headless) + { - // create our 2D scene - m_2DScene = NEW_REF( RTS2DScene, () ); - m_2DScene->Set_Ambient_Light( Vector3( 1, 1, 1 ) ); + // create our 3D interface scene + m_3DInterfaceScene = NEW_REF( RTS3DInterfaceScene, () ); + m_3DInterfaceScene->Set_Ambient_Light( Vector3( 1, 1, 1 ) ); - // create our 3D scene - m_3DScene =NEW_REF( RTS3DScene, () ); -#if defined(_DEBUG) || defined(_INTERNAL) - if( TheGlobalData->m_wireframe ) - m_3DScene->Set_Polygon_Mode( SceneClass::LINE ); -#endif -//============================================================================ - // m_myLight = NEW_REF -//============================================================================ - Int lindex; - for (lindex=0; lindexm_numGlobalLights; lindex++) - { m_myLight[lindex] = NEW_REF( LightClass, (LightClass::DIRECTIONAL) ); - } + // create our 2D scene + m_2DScene = NEW_REF( RTS2DScene, () ); + m_2DScene->Set_Ambient_Light( Vector3( 1, 1, 1 ) ); + + // create our 3D scene + m_3DScene =NEW_REF( RTS3DScene, () ); + #if defined(_DEBUG) || defined(_INTERNAL) + if( TheGlobalData->m_wireframe ) + m_3DScene->Set_Polygon_Mode( SceneClass::LINE ); + #endif + //============================================================================ + // m_myLight = NEW_REF + //============================================================================ + Int lindex; + for (lindex=0; lindexm_numGlobalLights; lindex++) + { m_myLight[lindex] = NEW_REF( LightClass, (LightClass::DIRECTIONAL) ); + } - setTimeOfDay( TheGlobalData->m_timeOfDay ); //set each light to correct values for given time + setTimeOfDay( TheGlobalData->m_timeOfDay ); //set each light to correct values for given time - for (lindex=0; lindexm_numGlobalLights; lindex++) - { m_3DScene->setGlobalLight( m_myLight[lindex], lindex ); - } + for (lindex=0; lindexm_numGlobalLights; lindex++) + { m_3DScene->setGlobalLight( m_myLight[lindex], lindex ); + } -#ifdef SAMPLE_DYNAMIC_LIGHT - theDynamicLight = NEW_REF(W3DDynamicLight, ()); - Real red = 1; - Real green = 1; - Real blue = 0; - if(red==0 && blue==0 && green==0) { - red = green = blue = 1; - } - theDynamicLight->Set_Ambient( Vector3( red, green, blue ) ); - theDynamicLight->Set_Diffuse( Vector3( red, green, blue) ); - theDynamicLight->Set_Position(Vector3(0, 0, 4)); - theDynamicLight->Set_Far_Attenuation_Range(1, 8); - // Note: Don't Add_Render_Object dynamic lights. - m_3DScene->addDynamicLight( theDynamicLight ); -#endif + #ifdef SAMPLE_DYNAMIC_LIGHT + theDynamicLight = NEW_REF(W3DDynamicLight, ()); + Real red = 1; + Real green = 1; + Real blue = 0; + if(red==0 && blue==0 && green==0) { + red = green = blue = 1; + } + theDynamicLight->Set_Ambient( Vector3( red, green, blue ) ); + theDynamicLight->Set_Diffuse( Vector3( red, green, blue) ); + theDynamicLight->Set_Position(Vector3(0, 0, 4)); + theDynamicLight->Set_Far_Attenuation_Range(1, 8); + // Note: Don't Add_Render_Object dynamic lights. + m_3DScene->addDynamicLight( theDynamicLight ); + #endif + + } // create a new asset manager m_assetManager = NEW W3DAssetManager; @@ -725,43 +730,35 @@ void W3DDisplay::init( void ) m_assetManager->Register_Prototype_Loader(&_AggregateLoader); m_assetManager->Set_WW3D_Load_On_Demand( true ); - - if (TheGlobalData->m_incrementalAGPBuf) + if (!TheGlobalData->m_headless) { - SortingRendererClass::SetMinVertexBufferSize(1); - } - if (WW3D::Init( ApplicationHWnd ) != WW3D_ERROR_OK) - throw ERROR_INVALID_D3D; //failed to initialize. User probably doesn't have DX 8.1 - WW3D::Set_Prelit_Mode( WW3D::PRELIT_MODE_LIGHTMAP_MULTI_PASS ); - WW3D::Set_Collision_Box_Display_Mask(0x00); ///m_incrementalAGPBuf) + { + SortingRendererClass::SetMinVertexBufferSize(1); + } + if (WW3D::Init( ApplicationHWnd ) != WW3D_ERROR_OK) + throw ERROR_INVALID_D3D; //failed to initialize. User probably doesn't have DX 8.1 + + WW3D::Set_Prelit_Mode( WW3D::PRELIT_MODE_LIGHTMAP_MULTI_PASS ); + WW3D::Set_Collision_Box_Display_Mask(0x00); ///m_windowed ); - - // create a 2D renderer helper - m_2DRender = NEW Render2DClass; - DEBUG_ASSERTCRASH( m_2DRender, ("Cannot create Render2DClass") ); - - // set our default width and height and bit depth - /// @todo we should set this according to options read from a file - setWidth( TheGlobalData->m_xResolution ); - setHeight( TheGlobalData->m_yResolution ); - setBitDepth( W3D_DISPLAY_DEFAULT_BIT_DEPTH ); - - if( WW3D::Set_Render_Device( 0, - getWidth(), - getHeight(), - getBitDepth(), - getWindowed(), - true ) != WW3D_ERROR_OK ) - { - // Getting the device at the default bit depth (32) didn't work, so try - // getting a 16 bit display. (Voodoo 1-3 only supported 16 bit.) jba. - setBitDepth( 16 ); + setWindowed( TheGlobalData->m_windowed ); + + // create a 2D renderer helper + m_2DRender = NEW Render2DClass; + DEBUG_ASSERTCRASH( m_2DRender, ("Cannot create Render2DClass") ); + + // set our default width and height and bit depth + /// @todo we should set this according to options read from a file + setWidth( TheGlobalData->m_xResolution ); + setHeight( TheGlobalData->m_yResolution ); + setBitDepth( W3D_DISPLAY_DEFAULT_BIT_DEPTH ); + if( WW3D::Set_Render_Device( 0, getWidth(), getHeight(), @@ -769,63 +766,78 @@ void W3DDisplay::init( void ) getWindowed(), true ) != WW3D_ERROR_OK ) { + // Getting the device at the default bit depth (32) didn't work, so try + // getting a 16 bit display. (Voodoo 1-3 only supported 16 bit.) jba. + setBitDepth( 16 ); + if( WW3D::Set_Render_Device( 0, + getWidth(), + getHeight(), + getBitDepth(), + getWindowed(), + true ) != WW3D_ERROR_OK ) + { - WW3D::Shutdown(); - WWMath::Shutdown(); - throw ERROR_INVALID_D3D; //failed to initialize. User probably doesn't have DX 8.1 - DEBUG_ASSERTCRASH( 0, ("Unable to set render device\n") ); - return; - } + WW3D::Shutdown(); + WWMath::Shutdown(); + throw ERROR_INVALID_D3D; //failed to initialize. User probably doesn't have DX 8.1 + DEBUG_ASSERTCRASH( 0, ("Unable to set render device\n") ); + return; + } - } // end if + } // end if - //Check if level was never set and default to setting most suitable for system. - if (TheGameLODManager->getStaticLODLevel() == STATIC_GAME_LOD_UNKNOWN) - TheGameLODManager->setStaticLODLevel(TheGameLODManager->findStaticLODLevel()); - else - { //Static LOD level was applied during GameLOD manager init except for texture reduction - //which needs to be applied here. - Int txtReduction=TheWritableGlobalData->m_textureReductionFactor; - if (txtReduction > 0) - { WW3D::Set_Texture_Reduction(txtReduction,32); - //Tell LOD manager that texture reduction was applied. - TheGameLODManager->setCurrentTextureReduction(txtReduction); + //Check if level was never set and default to setting most suitable for system. + if (TheGameLODManager->getStaticLODLevel() == STATIC_GAME_LOD_UNKNOWN) + TheGameLODManager->setStaticLODLevel(TheGameLODManager->findStaticLODLevel()); + else + { //Static LOD level was applied during GameLOD manager init except for texture reduction + //which needs to be applied here. + Int txtReduction=TheWritableGlobalData->m_textureReductionFactor; + if (txtReduction > 0) + { WW3D::Set_Texture_Reduction(txtReduction,32); + //Tell LOD manager that texture reduction was applied. + TheGameLODManager->setCurrentTextureReduction(txtReduction); + } } - } - if (TheGlobalData->m_displayGamma != 1.0f) - setGamma(TheGlobalData->m_displayGamma,0.0f,1.0f,FALSE); + if (TheGlobalData->m_displayGamma != 1.0f) + setGamma(TheGlobalData->m_displayGamma,0.0f,1.0f,FALSE); + } initAssets(); - init2DScene(); - init3DScene(); - W3DShaderManager::init(); - // Create and initialize the debug display - m_nativeDebugDisplay = NEW W3DDebugDisplay(); - m_debugDisplay = m_nativeDebugDisplay; - if ( m_nativeDebugDisplay ) + if (!TheGlobalData->m_headless) { - m_nativeDebugDisplay->init(); - GameFont *font; + init2DScene(); + init3DScene(); + W3DShaderManager::init(); - if (TheGlobalLanguageData && TheGlobalLanguageData->m_nativeDebugDisplay.name.isNotEmpty()) + // Create and initialize the debug display + m_nativeDebugDisplay = NEW W3DDebugDisplay(); + m_debugDisplay = m_nativeDebugDisplay; + if ( m_nativeDebugDisplay ) { - font=TheFontLibrary->getFont( - TheGlobalLanguageData->m_nativeDebugDisplay.name, - TheGlobalLanguageData->m_nativeDebugDisplay.size, - TheGlobalLanguageData->m_nativeDebugDisplay.bold); + m_nativeDebugDisplay->init(); + GameFont *font; + + if (TheGlobalLanguageData && TheGlobalLanguageData->m_nativeDebugDisplay.name.isNotEmpty()) + { + font=TheFontLibrary->getFont( + TheGlobalLanguageData->m_nativeDebugDisplay.name, + TheGlobalLanguageData->m_nativeDebugDisplay.size, + TheGlobalLanguageData->m_nativeDebugDisplay.bold); + } + else + font=TheFontLibrary->getFont( AsciiString("FixedSys"), 8, FALSE ); + + m_nativeDebugDisplay->setFont( font ); + m_nativeDebugDisplay->setFontHeight( 13 ); + m_nativeDebugDisplay->setFontWidth( 9 ); } - else - font=TheFontLibrary->getFont( AsciiString("FixedSys"), 8, FALSE ); - m_nativeDebugDisplay->setFont( font ); - m_nativeDebugDisplay->setFontHeight( 13 ); - m_nativeDebugDisplay->setFontWidth( 9 ); + DX8WebBrowser::Initialize(); } - DX8WebBrowser::Initialize(); - // we're now online m_initialized = true; if( TheGlobalData->m_displayDebug ) @@ -845,16 +857,19 @@ void W3DDisplay::reset( void ) // Remove all render objects. - SceneIterator *sceneIter = m_3DScene->Create_Iterator(); - sceneIter->First(); - while(!sceneIter->Is_Done()) { - RenderObjClass * robj = sceneIter->Current_Item(); - robj->Add_Ref(); - m_3DScene->Remove_Render_Object(robj); - robj->Release_Ref(); - sceneIter->Next(); + if (m_3DScene != NULL) + { + SceneIterator *sceneIter = m_3DScene->Create_Iterator(); + sceneIter->First(); + while(!sceneIter->Is_Done()) { + RenderObjClass * robj = sceneIter->Current_Item(); + robj->Add_Ref(); + m_3DScene->Remove_Render_Object(robj); + robj->Release_Ref(); + sceneIter->Next(); + } + m_3DScene->Destroy_Iterator(sceneIter); } - m_3DScene->Destroy_Iterator(sceneIter); m_isClippedEnabled = FALSE; @@ -1671,6 +1686,8 @@ void W3DDisplay::draw( void ) return; } + if (TheGlobalData->m_headless) + return; updateAverageFPS(); if (TheGlobalData->m_enableDynamicLOD && TheGameLogic->getShowDynamicLOD()) @@ -2082,7 +2099,7 @@ void W3DDisplay::createLightPulse( const Coord3D *pos, const RGBColor *color, UnsignedInt decayFrameTime//, Bool donut ) { - if (innerRadius+attenuationWidth<2.0*PATHFIND_CELL_SIZE_F + 1.0f) { + if (innerRadius+attenuationWidth<2.0*PATHFIND_CELL_SIZE_F + 1.0f || m_3DScene == NULL) { return; // it basically won't make any visual difference. jba. } W3DDynamicLight * theDynamicLight = m_3DScene->getADynamicLight(); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DInGameUI.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DInGameUI.cpp index 747512e025..83b7e66914 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DInGameUI.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DInGameUI.cpp @@ -436,7 +436,8 @@ void W3DInGameUI::draw( void ) postDraw(); - TheWindowManager->winRepaint(); + if (TheWindowManager) + TheWindowManager->winRepaint(); #ifdef EXTENDED_STATS } diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp index 907d5e6dcb..dc39ecea92 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp @@ -242,27 +242,29 @@ void W3DTerrainVisual::init( void ) m_terrainRenderObject->Set_Collision_Type( PICK_TYPE_TERRAIN ); TheTerrainRenderObject = m_terrainRenderObject; - // initialize track drawing system - TheTerrainTracksRenderObjClassSystem = NEW TerrainTracksRenderObjClassSystem; - TheTerrainTracksRenderObjClassSystem->init(W3DDisplay::m_3DScene); + if (!TheGlobalData->m_headless) + { + // initialize track drawing system + TheTerrainTracksRenderObjClassSystem = NEW TerrainTracksRenderObjClassSystem; + TheTerrainTracksRenderObjClassSystem->init(W3DDisplay::m_3DScene); #ifdef INCLUDE_GRANNY_IN_BUILD - // initialize Granny model drawing system - TheGrannyRenderObjSystem = NEW GrannyRenderObjSystem; + // initialize Granny model drawing system + TheGrannyRenderObjSystem = NEW GrannyRenderObjSystem; #endif - // initialize object shadow drawing system - TheW3DShadowManager = NEW W3DShadowManager; - TheW3DShadowManager->init(); + // initialize object shadow drawing system + TheW3DShadowManager = NEW W3DShadowManager; + TheW3DShadowManager->init(); - // create a water plane render object - TheWaterRenderObj=m_waterRenderObject = NEW_REF( WaterRenderObjClass, () ); - m_waterRenderObject->init(TheGlobalData->m_waterPositionZ, TheGlobalData->m_waterExtentX, TheGlobalData->m_waterExtentY, W3DDisplay::m_3DScene, (WaterRenderObjClass::WaterType)TheGlobalData->m_waterType); //create a water plane that's 128x128 units - m_waterRenderObject->Set_Position(Vector3(TheGlobalData->m_waterPositionX,TheGlobalData->m_waterPositionY,TheGlobalData->m_waterPositionZ)); //place water in world + // create a water plane render object + TheWaterRenderObj=m_waterRenderObject = NEW_REF( WaterRenderObjClass, () ); + m_waterRenderObject->init(TheGlobalData->m_waterPositionZ, TheGlobalData->m_waterExtentX, TheGlobalData->m_waterExtentY, W3DDisplay::m_3DScene, (WaterRenderObjClass::WaterType)TheGlobalData->m_waterType); //create a water plane that's 128x128 units + m_waterRenderObject->Set_Position(Vector3(TheGlobalData->m_waterPositionX,TheGlobalData->m_waterPositionY,TheGlobalData->m_waterPositionZ)); //place water in world - // create smudge rendering system. - TheSmudgeManager = NEW(W3DSmudgeManager); - TheSmudgeManager->init(); + // create smudge rendering system. + TheSmudgeManager = NEW(W3DSmudgeManager); + TheSmudgeManager->init(); #ifdef DO_UNIT_TIMINGS #pragma MESSAGE("********************* WARNING- Doing UNIT TIMINGS. ") @@ -276,10 +278,11 @@ void W3DTerrainVisual::init( void ) W3DDisplay::m_3DScene->Add_Render_Object( m_waterRenderObject); } #endif - if (TheGlobalData->m_useCloudPlane) - m_waterRenderObject->toggleCloudLayer(true); - else - m_waterRenderObject->toggleCloudLayer(false); + if (TheGlobalData->m_useCloudPlane) + m_waterRenderObject->toggleCloudLayer(true); + else + m_waterRenderObject->toggleCloudLayer(false); + } // set the vertex animated water properties Int waterSettingIndex = 0; // use index 0 settings by default @@ -649,7 +652,7 @@ Bool W3DTerrainVisual::load( AsciiString filename ) } - RefRenderObjListIterator *it = W3DDisplay::m_3DScene->createLightsIterator(); + RefRenderObjListIterator *it = W3DDisplay::m_3DScene ? W3DDisplay::m_3DScene->createLightsIterator() : NULL; // apply the heightmap to the terrain render object #ifdef DO_SEISMIC_SIMULATIONS @@ -670,13 +673,17 @@ Bool W3DTerrainVisual::load( AsciiString filename ) it = NULL; } // add our terrain render object to the scene - W3DDisplay::m_3DScene->Add_Render_Object( m_terrainRenderObject ); + if (W3DDisplay::m_3DScene != NULL) + W3DDisplay::m_3DScene->Add_Render_Object( m_terrainRenderObject ); #if defined _DEBUG || defined _INTERNAL // Icon drawing utility object for pathfinding. - W3DDebugIcons *icons = NEW W3DDebugIcons; - W3DDisplay::m_3DScene->Add_Render_Object( icons ); - icons->Release_Ref(); // belongs to scene. + if (W3DDisplay::m_3DScene != NULL) + { + W3DDebugIcons *icons = NEW W3DDebugIcons; + W3DDisplay::m_3DScene->Add_Render_Object( icons ); + icons->Release_Ref(); // belongs to scene. + } #endif #ifdef DO_UNIT_TIMINGS diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp index 906ebb2c1a..bbbba8b442 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp @@ -616,7 +616,7 @@ void W3DView::setCameraTransform( void ) buildCameraTransform( &cameraTransform ); m_3DCamera->Set_Transform( cameraTransform ); - if (TheTerrainRenderObject) + if (TheTerrainRenderObject && W3DDisplay::m_3DScene != NULL) { RefRenderObjListIterator *it = W3DDisplay::m_3DScene->createLightsIterator(); TheTerrainRenderObject->updateCenter(m_3DCamera, it); @@ -1538,6 +1538,8 @@ void W3DView::draw( void ) CustomScenePassModes customScenePassMode = SCENE_PASS_DEFAULT; Bool preRenderResult = false; + if (TheGlobalData->m_headless) + return; if (m_viewFilterMode && m_viewFilter > FT_NULL_FILTER && m_viewFilter < FT_MAX) diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DGhostObject.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DGhostObject.cpp index 0af9735af9..645ae89227 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DGhostObject.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DGhostObject.cpp @@ -155,7 +155,8 @@ void W3DRenderObjectSnapshot::update(RenderObjClass *robj, DrawableInfo *drawInf // ------------------------------------------------------------------------------------------------ void W3DRenderObjectSnapshot::addToScene(void) { - ((SimpleSceneClass *)W3DDisplay::m_3DScene)->Add_Render_Object(m_robj); + if (W3DDisplay::m_3DScene != NULL) + ((SimpleSceneClass *)W3DDisplay::m_3DScene)->Add_Render_Object(m_robj); } // ------------------------------------------------------------------------------------------------ @@ -457,7 +458,7 @@ void W3DGhostObject::restoreParentObject(void) //removed by the ghost object manager, so restore it. If we have a render //object that is in the scene, then it was probably added because the model //changed while the object was ghosted (for damage states, garrison, etc.). - if (robj->Peek_Scene() == NULL) + if (robj->Peek_Scene() == NULL && W3DDisplay::m_3DScene != NULL) ((SimpleSceneClass *)W3DDisplay::m_3DScene)->Add_Render_Object(robj); } } diff --git a/GeneralsMD/Code/Main/WinMain.cpp b/GeneralsMD/Code/Main/WinMain.cpp index 716fe8536b..44b3162666 100644 --- a/GeneralsMD/Code/Main/WinMain.cpp +++ b/GeneralsMD/Code/Main/WinMain.cpp @@ -984,9 +984,11 @@ Int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, gLoadScreenBitmap = (HBITMAP)LoadImage(hInstance, "Install_Final.bmp", IMAGE_BITMAP, 0, 0, LR_SHARED|LR_LOADFROMFILE); #endif + //TheWritableGlobalData->m_headless = true; + Bool headless = true; // register windows class and create application window - if( initializeAppWindows( hInstance, nCmdShow, ApplicationIsWindowed) == false ) + if(!headless && initializeAppWindows(hInstance, nCmdShow, ApplicationIsWindowed) == false) return 0; if (gLoadScreenBitmap!=NULL) { From 16aa073ea98853c06e6624b8c18141d4583b5b4b Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Fri, 11 Apr 2025 01:20:30 +0200 Subject: [PATCH 18/42] Add some comments where logic-client-separation is broken. --- .../Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp | 2 ++ .../Code/GameEngine/Source/GameLogic/Object/Object.cpp | 3 +++ .../Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp | 4 ++++ .../Code/GameEngine/Source/GameLogic/Object/Weapon.cpp | 2 ++ .../Source/W3DDevice/GameLogic/W3DTerrainLogic.cpp | 5 ++++- 5 files changed, 15 insertions(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp index c18e65726f..38192a4a86 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp @@ -2218,6 +2218,8 @@ const WaterHandle* TerrainLogic::getWaterHandle( Real x, Real y ) /**@todo: Remove this after we have all water types included in water triggers. For now do special check for water grid mesh. */ + // TheSuperHackers @logic-client-separation helmutbuhler 04/11/2025 + // We shouldn't depend on TerrainVisual here. Real meshZ; if( TheTerrainVisual->getWaterGridHeight( x, y, &meshZ ) ) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index 37a44e14bd..ad1c7de212 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -6096,6 +6096,9 @@ const AsciiString& Object::getCommandSetString() const //============================================================================= Bool Object::canProduceUpgrade( const UpgradeTemplate *upgrade ) { + // TheSuperHackers @logic-client-separation helmutbuhler 04/11/2025 + // TheControlBar belongs to the client, we shouldn't depend on that to check this. + // We need to have the button to make the upgrade. CommandSets are a weird Logic/Client hybrid. const CommandSet *set = TheControlBar->findCommandSet(getCommandSetString()); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp index b3571f6463..53ad8a5230 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp @@ -510,6 +510,10 @@ void DockUpdate::loadDockPositions() if( m_numberApproachPositions != DYNAMIC_APPROACH_VECTOR_FLAG ) { // Dynamic means no bones + + // TheSuperHackers @logic-client-separation helmutbuhler 04/11/2025 + // We shouldn't depend on bones of a drawable here! + Coord3D approachBones[DEFAULT_APPROACH_VECTOR_SIZE]; m_numberApproachPositionBones = myDrawable->getPristineBonePositions( "DockWaiting", 1, approachBones, NULL, m_numberApproachPositions); if( m_numberApproachPositions == m_approachPositions.size() )//safeguard: will always be true diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp index 0f22d5b3a7..11b8c88cf6 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp @@ -2598,6 +2598,8 @@ Bool Weapon::privateFireWeapon( Bool reloaded = false; if (m_ammoInClip > 0) { + // TheSuperHackers @logic-client-separation helmutbuhler 04/11/2025 + // barrelCount shouln't depend on Drawable, which belongs to client. Int barrelCount = sourceObj->getDrawable()->getBarrelCount(m_wslot); if (m_curBarrel >= barrelCount) { diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DTerrainLogic.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DTerrainLogic.cpp index 8682b13c2d..e3d44ce007 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DTerrainLogic.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DTerrainLogic.cpp @@ -281,6 +281,8 @@ Real W3DTerrainLogic::getGroundHeight( Real x, Real y, Coord3D* normal ) const { #define USE_THE_TERRAIN_OBJECT #ifdef USE_THE_TERRAIN_OBJECT + // TheSuperHackers @logic-client-separation helmutbuhler 04/11/2025 + // W3DTerrainLogic shouldn't depend on TheTerrainRenderObject! if (TheTerrainRenderObject) { return TheTerrainRenderObject->getHeightMapHeight(x,y,normal); @@ -358,7 +360,8 @@ Real W3DTerrainLogic::getLayerHeight( Real x, Real y, PathfindLayerEnum layer, C //------------------------------------------------------------------------------------------------- Bool W3DTerrainLogic::isCliffCell( Real x, Real y) const { - + // TheSuperHackers @logic-client-separation helmutbuhler 04/11/2025 + // W3DTerrainLogic shouldn't depend on TheTerrainRenderObject! return TheTerrainRenderObject->isCliffCell(x,y); } // end isCliffCell From 4229f1dc9588fd12ec9577c6fc3e67db83ce4c5f Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Fri, 11 Apr 2025 01:30:19 +0200 Subject: [PATCH 19/42] Add some more checks for headless --- .../Code/GameEngine/Source/GameLogic/System/GameLogic.cpp | 4 ++-- .../GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index 34a7d4417b..f4dbf89954 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -1269,7 +1269,7 @@ void GameLogic::startNewGame( Bool loadingSaveGame ) //****************************// // Get the m_loadScreen for this kind of game - if(!m_loadScreen) + if(!m_loadScreen && !TheGlobalData->m_headless) { m_loadScreen = getLoadScreen( loadingSaveGame ); if(m_loadScreen) @@ -2192,7 +2192,7 @@ void GameLogic::startNewGame( Bool loadingSaveGame ) } // if we're in a load game, don't fade yet - if( loadingSaveGame == FALSE ) + if(loadingSaveGame == FALSE && TheTransitionHandler != NULL) { TheTransitionHandler->setGroup("FadeWholeScreen"); while(!TheTransitionHandler->isFinished()) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp index b96552e37b..08abc68385 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp @@ -320,14 +320,15 @@ void GameLogic::prepareNewGame( Int gameMode, GameDifficulty diff, Int rankPoint TheScriptEngine->setGlobalDifficulty(diff); - if(!m_background) + if(!m_background && TheWindowManager != NULL) { m_background = TheWindowManager->winCreateLayout("Menus/BlankWindow.wnd"); DEBUG_ASSERTCRASH(m_background,("We Couldn't Load Menus/BlankWindow.wnd")); m_background->hide(FALSE); m_background->bringForward(); } - m_background->getFirstWindow()->winClearStatus(WIN_STATUS_IMAGE); + if (m_background != NULL) + m_background->getFirstWindow()->winClearStatus(WIN_STATUS_IMAGE); TheGameLogic->setGameMode( gameMode ); if (!TheGlobalData->m_pendingFile.isEmpty()) { From c7c8b1da984947c49ae1044a131688cb441232a0 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Fri, 11 Apr 2025 01:49:45 +0200 Subject: [PATCH 20/42] Add comment about logic-client architecture and headless mode --- .../Include/GameClient/GameClient.h | 40 +++++++++++++++++++ .../Source/GameClient/GameClient.cpp | 29 -------------- 2 files changed, 40 insertions(+), 29 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h b/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h index 33c0fa2627..e90c444568 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h @@ -259,4 +259,44 @@ inline Drawable* GameClient::findDrawableByID( const DrawableID id ) // the singleton extern GameClient *TheGameClient; + +// TheSuperHackers @logic-client-separation helmutbuhler 04/11/2025 +// Some information about the architecture and headless mode: +// The game is structurally separated into GameLogic and GameClient. +// The Logic is responsible for everything that affects the game mechanic and what is synchronized over +// the network. The Client is responsible for rendering, input, audio and similar stuff. +// +// Unfortunately there are some places in the code that make the Logic depend on the Client. +// (Search for @logic-client-separation) +// That means if we want to run the game headless, we cannot just disable the Client. We need to disable +// the parts in the Client that don't work in headless mode and need to keep the parts that are needed +// to run the Logic. +// The following describes which parts we disable in headless mode: +// +// GameEngine: +// TheGameClient is partially disabled: +// TheKeyboard = NULL +// TheMouse = NULL +// TheDisplay is partially disabled: +// m_3DInterfaceScene = NULL +// m_2DScene = NULL +// m_3DScene = NULL +// (m_assetManager remains!) +// TheWindowManager = NULL +// TheIMEManager = NULL +// TheTerrainVisual is partially disabled: +// TheTerrainTracksRenderObjClassSystem = NULL +// TheW3DShadowManager = NULL +// TheWaterRenderObj = NULL +// TheSmudgeManager = NULL +// TheTerrainRenderObject is partially disabled: +// m_treeBuffer = NULL +// m_propBuffer = NULL +// m_bibBuffer = NULL +// m_bridgeBuffer = NULL +// m_waypointBuffer = NULL +// m_roadBuffer = NULL +// m_shroud = NULL +// TheRadar = RadarHeadless + #endif // _GAME_INTERFACE_H_ diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp index b567ff5f87..39aad4c652 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp @@ -1620,32 +1620,3 @@ void GameClient::crc( Xfer *xfer ) { } // end crc - -/* -g_headless disable: -GameEngine: - TheGameClient - TheKeyboard = NULL - TheMouse = NULL - TheDisplay is partially disabled: - m_3DInterfaceScene = NULL - m_2DScene = NULL - m_3DScene = NULL - m_assetManager remains! - TheWindowManager = NULL - TheIMEManager = NULL - TheTerrainVisual is partially disabled: - TheTerrainTracksRenderObjClassSystem = NULL - TheW3DShadowManager = NULL - TheWaterRenderObj = NULL - TheSmudgeManager = NULL - TheTerrainRenderObject is partially disabled: - m_treeBuffer = NULL - m_propBuffer = NULL - m_bibBuffer = NULL - m_bridgeBuffer = NULL - m_waypointBuffer = NULL - m_roadBuffer = NULL - m_shroud = NULL - TheRadar set to RadarHeadless -*/ \ No newline at end of file From f69e41fd8b1f9fe9e3e321edd4993079be11e27d Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Fri, 11 Apr 2025 12:57:53 +0200 Subject: [PATCH 21/42] Add -headless commandline option --- .../Code/GameEngine/Include/Common/GlobalData.h | 2 +- .../Code/GameEngine/Source/Common/CommandLine.cpp | 13 +++++++++++++ .../Code/GameEngine/Source/Common/GlobalData.cpp | 3 +-- .../Source/GameClient/GUI/Shell/Shell.cpp | 2 +- GeneralsMD/Code/Main/WinMain.cpp | 15 ++++++++++----- 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h index b3babd2b12..94c6e30e22 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h @@ -102,7 +102,7 @@ class GlobalData : public SubsystemInterface Int m_chipSetType; ///m_headless = TRUE; + } + return 1; +} + Int parseConstantDebug( char *args[], int num ) { if (TheWritableGlobalData) @@ -1204,6 +1213,10 @@ static CommandLineParam params[] = { "-noshaders", parseNoShaders }, { "-quickstart", parseQuickStart }, + // TheSuperHackers @feature helmutbuhler 04/11/2025 + // This runs the game without a window, graphics, input and audio. Used for testing. + { "-headless", parseHeadless }, + #if (defined(_DEBUG) || defined(_INTERNAL)) { "-noaudio", parseNoAudio }, { "-map", parseMapName }, diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp index 437d122268..1382639059 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp @@ -607,8 +607,7 @@ GlobalData::GlobalData() m_dumpAssetUsage = FALSE; m_framesPerSecondLimit = 0; m_chipSetType = 0; - //m_headless = FALSE; - m_headless = TRUE; + m_headless = FALSE; m_windowed = 0; m_xResolution = 800; m_yResolution = 600; diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp index 4fb7455338..ad48d234ad 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp @@ -496,7 +496,7 @@ void Shell::showShellMap(Bool useShellMap ) TheMessageStream->appendMessage( GameMessage::MSG_CLEAR_GAME_DATA ); // if the shell is active,we need a background - if(!m_isShellActive) + if(!m_isShellActive || TheWindowManager == NULL) return; if(!m_background) m_background = TheWindowManager->winCreateLayout("Menus/BlankWindow.wnd"); diff --git a/GeneralsMD/Code/Main/WinMain.cpp b/GeneralsMD/Code/Main/WinMain.cpp index 44b3162666..76794c5078 100644 --- a/GeneralsMD/Code/Main/WinMain.cpp +++ b/GeneralsMD/Code/Main/WinMain.cpp @@ -919,13 +919,21 @@ Int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, char * argv[20]; argv[0] = NULL; + Bool headless = false; + char *token; token = nextParam(lpCmdLine, "\" "); while (argc < 20 && token != NULL) { argv[argc++] = strtrim(token); + //added a preparse step for this flag because it affects window creation style - if (stricmp(token,"-win")==0) - ApplicationIsWindowed=true; + if (stricmp(token, "-win") == 0) + ApplicationIsWindowed = true; + + // preparse for headless as well. We need to know about this before we create the window. + if (stricmp(token, "-headless") == 0) + headless = true; + token = nextParam(NULL, "\" "); } @@ -984,9 +992,6 @@ Int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, gLoadScreenBitmap = (HBITMAP)LoadImage(hInstance, "Install_Final.bmp", IMAGE_BITMAP, 0, 0, LR_SHARED|LR_LOADFROMFILE); #endif - //TheWritableGlobalData->m_headless = true; - Bool headless = true; - // register windows class and create application window if(!headless && initializeAppWindows(hInstance, nCmdShow, ApplicationIsWindowed) == false) return 0; From 7498de07ca08f0fa6a1939c5628a5ebbc55a3700 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sun, 13 Apr 2025 17:16:27 +0200 Subject: [PATCH 22/42] Fix headless when shutting down the engine. --- .../Code/GameEngine/Source/Common/GameEngine.cpp | 14 +++++++++----- .../Source/GameLogic/System/GameLogicDispatch.cpp | 5 +++-- .../Source/W3DDevice/GameClient/W3DDisplay.cpp | 9 ++++++--- GeneralsMD/Code/Main/WinMain.cpp | 6 ++++-- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp index c5ddc32a12..da75e80b89 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp @@ -709,11 +709,15 @@ void GameEngine::init( int argc, char *argv[] ) void GameEngine::reset( void ) { - WindowLayout *background = TheWindowManager->winCreateLayout("Menus/BlankWindow.wnd"); - DEBUG_ASSERTCRASH(background,("We Couldn't Load Menus/BlankWindow.wnd")); - background->hide(FALSE); - background->bringForward(); - background->getFirstWindow()->winClearStatus(WIN_STATUS_IMAGE); + WindowLayout *background = TheWindowManager ? TheWindowManager->winCreateLayout("Menus/BlankWindow.wnd") : NULL; + if (background != NULL) + { + DEBUG_ASSERTCRASH(background,("We Couldn't Load Menus/BlankWindow.wnd")); + background->hide(FALSE); + background->bringForward(); + background->getFirstWindow()->winClearStatus(WIN_STATUS_IMAGE); + } + Bool deleteNetwork = false; if (TheGameLogic->isInMultiplayerGame()) deleteNetwork = true; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp index 08abc68385..8af03e8b37 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp @@ -267,7 +267,7 @@ void GameLogic::clearGameData( Bool showScoreScreen ) TheScriptActions->closeWindows(FALSE); // Close victory or defeat windows. Bool shellGame = FALSE; - if ((!isInShellGame() || !isInGame()) && showScoreScreen) + if ((!isInShellGame() || !isInGame()) && showScoreScreen && !TheGlobalData->m_headless) { shellGame = TRUE; TheTransitionHandler->setGroup("FadeWholeScreen"); @@ -293,7 +293,8 @@ void GameLogic::clearGameData( Bool showScoreScreen ) HideControlBar(); closeWindows(); - TheMouse->setVisibility(TRUE); + if (TheMouse != NULL) + TheMouse->setVisibility(TRUE); if(m_background) { diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp index b315dddcd6..0674d70151 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp @@ -457,12 +457,15 @@ W3DDisplay::~W3DDisplay() // shutdown Debug_Statistics::Shutdown_Statistics(); - W3DShaderManager::shutdown(); + if (!TheGlobalData->m_headless) + W3DShaderManager::shutdown(); m_assetManager->Free_Assets(); delete m_assetManager; - WW3D::Shutdown(); + if (!TheGlobalData->m_headless) + WW3D::Shutdown(); WWMath::Shutdown(); - DX8WebBrowser::Shutdown(); + if (!TheGlobalData->m_headless) + DX8WebBrowser::Shutdown(); delete TheW3DFileSystem; TheW3DFileSystem = NULL; diff --git a/GeneralsMD/Code/Main/WinMain.cpp b/GeneralsMD/Code/Main/WinMain.cpp index 76794c5078..6723cde469 100644 --- a/GeneralsMD/Code/Main/WinMain.cpp +++ b/GeneralsMD/Code/Main/WinMain.cpp @@ -744,8 +744,7 @@ static Bool initializeAppWindows( HINSTANCE hInstance, Int nCmdShow, Bool runWin ShowWindow( hWnd, nCmdShow ); UpdateWindow( hWnd ); - // save our application instance and window handle for future use - ApplicationHInstance = hInstance; + // save our application window handle for future use ApplicationHWnd = hWnd; gInitializing = false; if (!runWindowed) { @@ -995,6 +994,9 @@ Int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, // register windows class and create application window if(!headless && initializeAppWindows(hInstance, nCmdShow, ApplicationIsWindowed) == false) return 0; + + // save our application instance for future use + ApplicationHInstance = hInstance; if (gLoadScreenBitmap!=NULL) { ::DeleteObject(gLoadScreenBitmap); From bf0a2e5ec9e86451069e52d60042e946b6832786 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sun, 13 Apr 2025 19:41:29 +0200 Subject: [PATCH 23/42] Rename RadarHeadless to RadarDummy --- GeneralsMD/Code/GameEngine/Include/Common/Radar.h | 6 +++--- GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h | 2 +- GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/Radar.h b/GeneralsMD/Code/GameEngine/Include/Common/Radar.h index 50ef569674..2d294dd6c5 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/Radar.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/Radar.h @@ -295,11 +295,11 @@ extern Radar *TheRadar; ///< the radar singleton extern // TheSuperHackers @feature helmutbuhler 04/10/2025 // Radar that does nothing -class RadarHeadless : public Radar +class RadarDummy : public Radar { public: - RadarHeadless(){} - virtual ~RadarHeadless() {} + RadarDummy(){} + virtual ~RadarDummy() {} virtual void init() { } virtual void reset() { Radar::reset(); } diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h b/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h index e90c444568..28044fc3b8 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h @@ -297,6 +297,6 @@ extern GameClient *TheGameClient; // m_waypointBuffer = NULL // m_roadBuffer = NULL // m_shroud = NULL -// TheRadar = RadarHeadless +// TheRadar = RadarDummy #endif // _GAME_INTERFACE_H_ diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp index da75e80b89..42a4a719ad 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp @@ -507,7 +507,7 @@ void GameEngine::init( int argc, char *argv[] ) initSubsystem(TheCrateSystem,"TheCrateSystem", MSGNEW("GameEngineSubsystem") CrateSystem(), &xferCRC, "Data\\INI\\Default\\Crate.ini", "Data\\INI\\Crate.ini"); initSubsystem(ThePlayerList,"ThePlayerList", MSGNEW("GameEngineSubsystem") PlayerList(), NULL); initSubsystem(TheRecorder,"TheRecorder", createRecorder(), NULL); - initSubsystem(TheRadar,"TheRadar", TheGlobalData->m_headless ? NEW RadarHeadless : createRadar(), NULL); + initSubsystem(TheRadar,"TheRadar", TheGlobalData->m_headless ? NEW RadarDummy : createRadar(), NULL); initSubsystem(TheVictoryConditions,"TheVictoryConditions", createVictoryConditions(), NULL); From 7df3c688b9aab9edc8ff009fb9b78e25a6ca0548 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sun, 13 Apr 2025 19:51:07 +0200 Subject: [PATCH 24/42] Simplify RadarDummy --- .../Code/GameEngine/Include/Common/Radar.h | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/Radar.h b/GeneralsMD/Code/GameEngine/Include/Common/Radar.h index 2d294dd6c5..ba9629d866 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/Radar.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/Radar.h @@ -294,27 +294,13 @@ class Radar : public Snapshot, extern Radar *TheRadar; ///< the radar singleton extern // TheSuperHackers @feature helmutbuhler 04/10/2025 -// Radar that does nothing +// Radar that does nothing. Used for Headless Mode. class RadarDummy : public Radar { public: - RadarDummy(){} - virtual ~RadarDummy() {} - - virtual void init() { } - virtual void reset() { Radar::reset(); } - virtual void update() { Radar::update(); } - - virtual void refreshTerrain(TerrainLogic *terrain) { Radar::refreshTerrain(terrain); } - virtual void queueTerrainRefresh() { Radar::queueTerrainRefresh(); } - virtual void newMap(TerrainLogic *terrain) { Radar::newMap(terrain); } virtual void draw(Int pixelX, Int pixelY, Int width, Int height) { } virtual void clearShroud() { } virtual void setShroudLevel(Int x, Int y, CellShroudStatus setting) { } - - virtual void crc(Xfer *xfer) { } - virtual void xfer(Xfer *xfer) { } - virtual void loadPostProcess() { Radar::loadPostProcess(); } }; #endif // __RADAR_H_ From 8d325d9440ba2e5060089d9fcf6b2bac0d9a7f7e Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sun, 13 Apr 2025 19:54:39 +0200 Subject: [PATCH 25/42] Change TheWindowManager Check in ControlBar::getStarImage --- .../Source/GameClient/GUI/ControlBar/ControlBar.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp index f680558e86..3c2551ed7e 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp @@ -1654,7 +1654,9 @@ const Image *ControlBar::getStarImage(void ) else m_lastFlashedAtPointValue = ThePlayerList->getLocalPlayer()->getSciencePurchasePoints(); - GameWindow *win= TheWindowManager ? TheWindowManager->winGetWindowFromId( NULL, TheNameKeyGenerator->nameToKey( "ControlBar.wnd:ButtonGeneral" ) ) : NULL; + if (TheWindowManager == NULL) + return NULL; + GameWindow *win= TheWindowManager->winGetWindowFromId( NULL, TheNameKeyGenerator->nameToKey( "ControlBar.wnd:ButtonGeneral" ) ); if(!win) return NULL; if(!m_genStarFlash) From bb744e7a2797408a21990cca67e35369f8d4e494 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sun, 13 Apr 2025 20:23:59 +0200 Subject: [PATCH 26/42] 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 0000000000..e8273b304a --- /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 f1c52f1652..475c9136c1 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 27/42] 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 e8273b304a..0000000000 --- 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 28/42] 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 1d52400bab..a350825c69 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 29/42] 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 ad61481918..fc7252ad6a 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 a350825c69..3fe4b1746f 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 30/42] 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 fc7252ad6a..40c29ed405 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 3fe4b1746f..36458f131b 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 From e50eec4a7cf224e4016fb5846b3f2b573628279a Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Thu, 17 Apr 2025 18:47:44 +0200 Subject: [PATCH 31/42] Add null checks for W3DDisplay::m_3DScene --- .../W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp index d4684d871c..4b44b10c05 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp @@ -107,8 +107,8 @@ void W3DRopeDraw::buildSegments() m_color.blue, // blue 0.5f ); // transparency - W3DDisplay::m_3DScene->Add_Render_Object( info.line ); - W3DDisplay::m_3DScene->Add_Render_Object( info.softLine ); + if (W3DDisplay::m_3DScene) W3DDisplay::m_3DScene->Add_Render_Object( info.line ); + if (W3DDisplay::m_3DScene) W3DDisplay::m_3DScene->Add_Render_Object( info.softLine ); m_segments.push_back(info); } } @@ -122,12 +122,12 @@ void W3DRopeDraw::tossSegments() { if (it->line) { - W3DDisplay::m_3DScene->Remove_Render_Object(it->line); + if (W3DDisplay::m_3DScene) W3DDisplay::m_3DScene->Remove_Render_Object(it->line); REF_PTR_RELEASE((it->line)); } if (it->softLine) { - W3DDisplay::m_3DScene->Remove_Render_Object(it->softLine); + if (W3DDisplay::m_3DScene) W3DDisplay::m_3DScene->Remove_Render_Object(it->softLine); REF_PTR_RELEASE((it->softLine)); } } From d6c8339929e2a0d66b8acad49acf713d4a390877 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Mon, 21 Apr 2025 18:09:38 +0200 Subject: [PATCH 32/42] Move m_headless check for W3DView to GameClient --- GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp | 5 +++-- .../GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp index 39aad4c652..cf6c31c600 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp @@ -630,10 +630,9 @@ void GameClient::update( void ) if(TheGlobalData->m_playIntro || TheGlobalData->m_afterIntro) { // redraw all views, update the GUI + if (!TheGlobalData->m_headless) { TheDisplay->DRAW(); - } - { TheDisplay->UPDATE(); } return; @@ -754,10 +753,12 @@ void GameClient::update( void ) } // update display + if (!TheGlobalData->m_headless) { TheDisplay->UPDATE(); } + if (!TheGlobalData->m_headless) { USE_PERF_TIMER(GameClient_draw) diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp index bbbba8b442..332947c69c 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp @@ -1538,8 +1538,6 @@ void W3DView::draw( void ) CustomScenePassModes customScenePassMode = SCENE_PASS_DEFAULT; Bool preRenderResult = false; - if (TheGlobalData->m_headless) - return; if (m_viewFilterMode && m_viewFilter > FT_NULL_FILTER && m_viewFilter < FT_MAX) From c26971ef7c6f02ab5b98b3f743bcc237647b35a8 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Mon, 21 Apr 2025 18:10:42 +0200 Subject: [PATCH 33/42] Some minor code moving around --- .../Source/W3DDevice/GameClient/BaseHeightMap.cpp | 3 ++- .../Source/W3DDevice/GameClient/W3DAssetManager.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp index 9db2876354..767554f3d6 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp @@ -2374,7 +2374,8 @@ void BaseHeightMapRenderObjClass::staticLightingChanged( void ) m_scorchesInBuffer = 0; // If we just allocated the buffers, we got no scorches in the buffer. m_curNumScorchVertices=0; m_curNumScorchIndices=0; - if (m_roadBuffer) m_roadBuffer->updateLighting(); + if (m_roadBuffer) + m_roadBuffer->updateLighting(); } diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp index 89355672c5..20553b40ce 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp @@ -874,12 +874,12 @@ int W3DAssetManager::Recolor_Asset(RenderObjClass *robj, const int color) */ int W3DAssetManager::Recolor_Mesh(RenderObjClass *robj, const int color) { - int i; - int didRecolor=0; - const char *meshName; if (TheGlobalData->m_headless) return 0; + int i; + int didRecolor=0; + const char *meshName; MeshClass *mesh=(MeshClass*) robj; MeshModelClass * model = mesh->Get_Model(); MaterialInfoClass *material = mesh->Get_Material_Info(); @@ -921,10 +921,10 @@ int W3DAssetManager::Recolor_Mesh(RenderObjClass *robj, const int color) int W3DAssetManager::Recolor_HLOD(RenderObjClass *robj, const int color) { - int didRecolor=0; if (TheGlobalData->m_headless) return 0; + int didRecolor=0; int num_sub = robj->Get_Num_Sub_Objects(); for(int i = 0; i < num_sub; i++) { RenderObjClass *sub_obj = robj->Get_Sub_Object(i); From 8eb9730d3bab692d2ac02e56f4df10490b1a8e14 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Tue, 22 Apr 2025 23:27:40 +0200 Subject: [PATCH 34/42] Add missing headless check for Sizzle Video --- GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp index cf6c31c600..41f87d3a8f 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp @@ -539,7 +539,7 @@ void GameClient::update( void ) //Initial Game Codition. We must show the movie first and then we can display the shell if(TheGlobalData->m_afterIntro && !TheDisplay->isMoviePlaying()) { - if( playSizzle && TheGlobalData->m_playSizzle ) + if( playSizzle && TheGlobalData->m_playSizzle && !TheGlobalData->m_headless ) { TheWritableGlobalData->m_allowExitOutOfMovies = TRUE; if(TheGameLODManager && TheGameLODManager->didMemPass()) From 5951bc8a26735786cd37c1faa535f8fac7eab66a Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Tue, 22 Apr 2025 23:40:01 +0200 Subject: [PATCH 35/42] Add missing headless checks for m_bibBuffer in BaseHeightMapRenderObjClass --- .../W3DDevice/GameClient/BaseHeightMap.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp index 767554f3d6..3b2ec477b5 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp @@ -2306,7 +2306,8 @@ void BaseHeightMapRenderObjClass::notifyShroudChanged(void) void BaseHeightMapRenderObjClass::addTerrainBib(Vector3 corners[4], ObjectID id, Bool highlight) { - m_bibBuffer->addBib(corners, id, highlight); + if (m_bibBuffer) + m_bibBuffer->addBib(corners, id, highlight); }; //============================================================================= @@ -2317,7 +2318,8 @@ void BaseHeightMapRenderObjClass::addTerrainBib(Vector3 corners[4], void BaseHeightMapRenderObjClass::addTerrainBibDrawable(Vector3 corners[4], DrawableID id, Bool highlight) { - m_bibBuffer->addBibDrawable(corners, id, highlight); + if (m_bibBuffer) + m_bibBuffer->addBibDrawable(corners, id, highlight); }; //============================================================================= @@ -2327,7 +2329,8 @@ void BaseHeightMapRenderObjClass::addTerrainBibDrawable(Vector3 corners[4], //============================================================================= void BaseHeightMapRenderObjClass::removeTerrainBibHighlighting() { - m_bibBuffer->removeHighlighting( ); + if (m_bibBuffer) + m_bibBuffer->removeHighlighting( ); }; //============================================================================= @@ -2337,7 +2340,8 @@ void BaseHeightMapRenderObjClass::removeTerrainBibHighlighting() //============================================================================= void BaseHeightMapRenderObjClass::removeAllTerrainBibs() { - m_bibBuffer->clearAllBibs( ); + if (m_bibBuffer) + m_bibBuffer->clearAllBibs( ); }; //============================================================================= @@ -2347,7 +2351,8 @@ void BaseHeightMapRenderObjClass::removeAllTerrainBibs() //============================================================================= void BaseHeightMapRenderObjClass::removeTerrainBib(ObjectID id) { - m_bibBuffer->removeBib( id ); + if (m_bibBuffer) + m_bibBuffer->removeBib( id ); }; //============================================================================= @@ -2357,7 +2362,8 @@ void BaseHeightMapRenderObjClass::removeTerrainBib(ObjectID id) //============================================================================= void BaseHeightMapRenderObjClass::removeTerrainBibDrawable(DrawableID id) { - m_bibBuffer->removeBibDrawable( id ); + if (m_bibBuffer) + m_bibBuffer->removeBibDrawable( id ); }; //============================================================================= From 39ae00cbb7c6e813b33c7c68008ac820202b4041 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Wed, 23 Apr 2025 23:25:46 +0200 Subject: [PATCH 36/42] Add DummyGameWindowManager to avoid null checks --- .../Include/GameClient/GameWindowManager.h | 38 +++++++++++++++++++ .../Source/Common/System/MemoryInit.cpp | 1 + .../GameClient/GUI/GameWindowManager.cpp | 33 ++++++++++++++++ .../Source/GameClient/GameClient.cpp | 30 +++++++-------- 4 files changed, 85 insertions(+), 17 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/GameWindowManager.h b/GeneralsMD/Code/GameEngine/Include/GameClient/GameWindowManager.h index 7965895b8d..8016172378 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/GameWindowManager.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/GameWindowManager.h @@ -379,7 +379,45 @@ extern WindowMsgHandledType PassMessagesToParentSystem( GameWindow *window, WindowMsgData mData1, WindowMsgData mData2 ); +class DummyGameWindow : public GameWindow +{ + MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DummyGameWindow, "DummyGameWindow") +public: + //virtual ~DummyGameWindow(); + virtual void winDrawBorder() { } +}; +class DummyGameWindowManager : public GameWindowManager +{ +public: + virtual GameWindow *winGetWindowFromId(GameWindow *window, Int id); + virtual GameWindow *winCreateFromScript(AsciiString filenameString, WindowLayoutInfo *info); + + virtual GameWindow *allocateNewWindow() { return newInstance(DummyGameWindow); } + + virtual GameWinDrawFunc getPushButtonImageDrawFunc() { return NULL; } + virtual GameWinDrawFunc getPushButtonDrawFunc() { return NULL; } + virtual GameWinDrawFunc getCheckBoxImageDrawFunc() { return NULL; } + virtual GameWinDrawFunc getCheckBoxDrawFunc() { return NULL; } + virtual GameWinDrawFunc getRadioButtonImageDrawFunc() { return NULL; } + virtual GameWinDrawFunc getRadioButtonDrawFunc() { return NULL; } + virtual GameWinDrawFunc getTabControlImageDrawFunc() { return NULL; } + virtual GameWinDrawFunc getTabControlDrawFunc() { return NULL; } + virtual GameWinDrawFunc getListBoxImageDrawFunc() { return NULL; } + virtual GameWinDrawFunc getListBoxDrawFunc() { return NULL; } + virtual GameWinDrawFunc getComboBoxImageDrawFunc() { return NULL; } + virtual GameWinDrawFunc getComboBoxDrawFunc() { return NULL; } + virtual GameWinDrawFunc getHorizontalSliderImageDrawFunc() { return NULL; } + virtual GameWinDrawFunc getHorizontalSliderDrawFunc() { return NULL; } + virtual GameWinDrawFunc getVerticalSliderImageDrawFunc() { return NULL; } + virtual GameWinDrawFunc getVerticalSliderDrawFunc() { return NULL; } + virtual GameWinDrawFunc getProgressBarImageDrawFunc() { return NULL; } + virtual GameWinDrawFunc getProgressBarDrawFunc() { return NULL; } + virtual GameWinDrawFunc getStaticTextImageDrawFunc() { return NULL; } + virtual GameWinDrawFunc getStaticTextDrawFunc() { return NULL; } + virtual GameWinDrawFunc getTextEntryImageDrawFunc() { return NULL; } + virtual GameWinDrawFunc getTextEntryDrawFunc() { return NULL; } +}; #endif // __GAMEWINDOWMANAGER_H_ diff --git a/GeneralsMD/Code/GameEngine/Source/Common/System/MemoryInit.cpp b/GeneralsMD/Code/GameEngine/Source/Common/System/MemoryInit.cpp index 032121e26d..90e3b0199e 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/System/MemoryInit.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/System/MemoryInit.cpp @@ -416,6 +416,7 @@ static PoolSizeRec sizes[] = { "Overridable", 32, 32 }, { "W3DGameWindow", 700, 256 }, + { "DummyGameWindow", 700, 256 }, { "SuccessState", 32, 32 }, { "FailureState", 32, 32 }, { "ContinueState", 32, 32 }, diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp index 93c40ade6f..a8a0c8b0ff 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp @@ -4097,3 +4097,36 @@ void GameWindowManager::clearTabList( void ) { m_tabList.clear(); } + + +GameWindow *DummyGameWindowManager::winGetWindowFromId(GameWindow *window, Int id) +{ + window = GameWindowManager::winGetWindowFromId(window, id); + if (window != NULL) + return window; + + // Just return any window, callers expect this to be non-null + return m_windowList; +} + +WindowMsgHandledType DummyWindowSystem(GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2) +{ + return MSG_IGNORED; +} + +GameWindow *DummyGameWindowManager::winCreateFromScript(AsciiString filenameString, WindowLayoutInfo *info) +{ + WindowLayoutInfo scriptInfo; + GameWindow* dummyWindow = winCreate(NULL, 0, 0, 0, 100, 100, DummyWindowSystem, NULL); + scriptInfo.windows.push_back(dummyWindow); + if (info) + *info = scriptInfo; + return dummyWindow; +} + +DummyGameWindow::~DummyGameWindow() +{ +} + + + diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp index 41f87d3a8f..7a4a0c23d0 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp @@ -345,25 +345,21 @@ void GameClient::init( void ) TheHeaderTemplateManager->init(); } - if (!TheGlobalData->m_headless) + // create the window manager + TheWindowManager = TheGlobalData->m_headless ? NEW DummyGameWindowManager : createWindowManager(); + if( TheWindowManager ) { - // create the window manager - TheWindowManager = createWindowManager(); - if( TheWindowManager ) - { - - TheWindowManager->init(); - TheWindowManager->setName("TheWindowManager"); - // TheWindowManager->initTestGUI(); + TheWindowManager->init(); + TheWindowManager->setName("TheWindowManager"); +// TheWindowManager->initTestGUI(); + } // end if - } // end if - // create the IME manager - TheIMEManager = CreateIMEManagerInterface(); - if ( TheIMEManager ) - { - TheIMEManager->init(); - TheIMEManager->setName("TheIMEManager"); - } + // create the IME manager + TheIMEManager = CreateIMEManagerInterface(); + if ( TheIMEManager ) + { + TheIMEManager->init(); + TheIMEManager->setName("TheIMEManager"); } // create the shell From 8759b91e3282e813496867a3280a1cb5fa49f747 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Thu, 24 Apr 2025 00:30:33 +0200 Subject: [PATCH 37/42] Remove TheWindowManager NULL checks --- .../GameEngine/Include/GameClient/GameClient.h | 2 +- .../Code/GameEngine/Source/Common/GameEngine.cpp | 2 +- .../Code/GameEngine/Source/Common/Recorder.cpp | 2 +- .../GameClient/GUI/ControlBar/ControlBar.cpp | 16 +--------------- .../GUI/ControlBar/ControlBarScheme.cpp | 2 -- .../GameClient/GUI/GUICallbacks/Diplomacy.cpp | 2 -- .../Source/GameClient/GUI/Shell/Shell.cpp | 2 +- .../GameEngine/Source/GameClient/GameClient.cpp | 1 - .../GameEngine/Source/GameClient/InGameUI.cpp | 10 +--------- 9 files changed, 6 insertions(+), 33 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h b/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h index b394cc8acf..55d1bdc07f 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h @@ -282,7 +282,7 @@ extern GameClient *TheGameClient; // m_2DScene = NULL // m_3DScene = NULL // (m_assetManager remains!) -// TheWindowManager = NULL +// TheWindowManager = DummyGameWindowManager // TheIMEManager = NULL // TheTerrainVisual is partially disabled: // TheTerrainTracksRenderObjClassSystem = NULL diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp index 2514dfe71e..85e551e251 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp @@ -709,7 +709,7 @@ void GameEngine::init( int argc, char *argv[] ) void GameEngine::reset( void ) { - WindowLayout *background = TheWindowManager ? TheWindowManager->winCreateLayout("Menus/BlankWindow.wnd") : NULL; + WindowLayout *background = TheWindowManager->winCreateLayout("Menus/BlankWindow.wnd"); if (background != NULL) { DEBUG_ASSERTCRASH(background,("We Couldn't Load Menus/BlankWindow.wnd")); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp index 8ede28f0b5..46ebcaa78e 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp @@ -1610,7 +1610,7 @@ RecorderModeType RecorderClass::getMode() { void RecorderClass::initControls() { NameKeyType parentReplayControlID = TheNameKeyGenerator->nameToKey( AsciiString("ReplayControl.wnd:ParentReplayControl") ); - GameWindow *parentReplayControl = TheWindowManager ? TheWindowManager->winGetWindowFromId( NULL, parentReplayControlID ) : NULL; + GameWindow *parentReplayControl = TheWindowManager->winGetWindowFromId( NULL, parentReplayControlID ); Bool show = (getMode() != RECORDERMODETYPE_PLAYBACK); if (parentReplayControl) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp index 3c2551ed7e..2079652dc1 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp @@ -1654,8 +1654,6 @@ const Image *ControlBar::getStarImage(void ) else m_lastFlashedAtPointValue = ThePlayerList->getLocalPlayer()->getSciencePurchasePoints(); - if (TheWindowManager == NULL) - return NULL; GameWindow *win= TheWindowManager->winGetWindowFromId( NULL, TheNameKeyGenerator->nameToKey( "ControlBar.wnd:ButtonGeneral" ) ); if(!win) return NULL; @@ -2145,12 +2143,6 @@ void ControlBar::switchToContext( ControlBarContext context, Drawable *draw ) // save a pointer for the currently selected drawable m_currentSelectedDrawable = draw; - if (TheWindowManager == NULL) - { - m_currContext = context; - return; - } - if (IsInGameChatActive() == FALSE && TheGameLogic && !TheGameLogic->isInShellGame()) { TheWindowManager->winSetFocus( NULL ); } @@ -2603,9 +2595,6 @@ void ControlBar::setPortraitByImage( const Image *image ) //------------------------------------------------------------------------------------------------- void ControlBar::setPortraitByObject( Object *obj ) { - if (TheWindowManager == NULL) - return; - if( obj ) { if( obj->isKindOf( KINDOF_SHOW_PORTRAIT_WHEN_CONTROLLED ) && !obj->isLocallyControlled() ) @@ -2766,8 +2755,6 @@ void ControlBar::setControlBarSchemeByPlayer(Player *p) { if(m_controlBarSchemeManager) m_controlBarSchemeManager->setControlBarSchemeByPlayer(p); - if (TheWindowManager == NULL) - return; static NameKeyType buttonPlaceBeaconID = NAMEKEY( "ControlBar.wnd:ButtonPlaceBeacon" ); static NameKeyType buttonIdleWorkerID = NAMEKEY("ControlBar.wnd:ButtonIdleWorker"); static NameKeyType buttonGeneralID = NAMEKEY("ControlBar.wnd:ButtonGeneral"); @@ -3259,8 +3246,7 @@ void ControlBar::initSpecialPowershortcutBar( Player *player) if(!player || !pt|| !player->isLocalPlayer() || pt->getSpecialPowerShortcutButtonCount() == 0 || pt->getSpecialPowerShortcutWinName().isEmpty() - || !player->isPlayerActive() - || TheWindowManager == NULL) + || !player->isPlayerActive()) return; m_currentlyUsedSpecialPowersButtons = pt->getSpecialPowerShortcutButtonCount(); AsciiString layoutName, tempName, windowName, parentName; diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarScheme.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarScheme.cpp index 7baa7ed756..2d8ea77453 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarScheme.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarScheme.cpp @@ -1162,8 +1162,6 @@ void ControlBarSchemeManager::setControlBarSchemeByPlayerTemplate( const PlayerT //----------------------------------------------------------------------------- void ControlBarSchemeManager::setControlBarSchemeByPlayer(Player *p) { - if (TheWindowManager == NULL) - return; GameWindow *communicatorButton = TheWindowManager->winGetWindowFromId( NULL, NAMEKEY("ControlBar.wnd:PopupCommunicator") ); if (communicatorButton && TheControlBar) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp index 3f20a3fbba..2f29ed4e25 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp @@ -169,8 +169,6 @@ BriefingList* GetBriefingTextList(void) //------------------------------------------------------------------------------------------------- void UpdateDiplomacyBriefingText(AsciiString newText, Bool clear) { - if (TheWindowManager == NULL) - return; GameWindow *listboxSolo = TheWindowManager->winGetWindowFromId(theWindow, NAMEKEY("Diplomacy.wnd:ListboxSolo")); if (clear) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp index ad48d234ad..4fb7455338 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp @@ -496,7 +496,7 @@ void Shell::showShellMap(Bool useShellMap ) TheMessageStream->appendMessage( GameMessage::MSG_CLEAR_GAME_DATA ); // if the shell is active,we need a background - if(!m_isShellActive || TheWindowManager == NULL) + if(!m_isShellActive) return; if(!m_background) m_background = TheWindowManager->winCreateLayout("Menus/BlankWindow.wnd"); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp index 7a4a0c23d0..030922a9c9 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp @@ -635,7 +635,6 @@ void GameClient::update( void ) } // update the window system itself - if (TheWindowManager) { TheWindowManager->UPDATE(); } diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp index 87da7589a3..07480af288 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp @@ -3989,8 +3989,6 @@ void InGameUI::playCameoMovie( const AsciiString& movieName ) void InGameUI::stopCameoMovie( void ) { //RightHUD - if (TheWindowManager == NULL) - return; //GameWindow *window = TheWindowManager->winGetWindowFromId(NULL,TheNameKeyGenerator->nameToKey( AsciiString("ControlBar.wnd:CameoMovieWindow") )); GameWindow *window = TheWindowManager->winGetWindowFromId(NULL,TheNameKeyGenerator->nameToKey( AsciiString("ControlBar.wnd:RightHUD") )); // window->winHide(FALSE); @@ -4986,8 +4984,6 @@ void InGameUI::buildRegion( const ICoord2D *anchor, const ICoord2D *dest, IRegio //------------------------------------------------------------------------------------------------- void InGameUI::addFloatingText(const UnicodeString& text,const Coord3D *pos, Color color) { - if (TheWindowManager == NULL) - return; if( TheGameLogic->getDrawIconUI() ) { FloatingTextData *newFTD = newInstance( FloatingTextData ); @@ -5021,8 +5017,6 @@ inline Bool isClose(const Coord3D& a, const Coord3D& b) } void InGameUI::DEBUG_addFloatingText(const AsciiString& text, const Coord3D * pos, Color color) { - if (TheWindowManager == NULL) - return; const Int POINTSIZE = 8; const Int LEADING = 0; @@ -5180,8 +5174,6 @@ void InGameUI::popupMessage( const AsciiString& message, Int x, Int y, Int width //------------------------------------------------------------------------------------------------- void InGameUI::popupMessage( const AsciiString& identifier, Int x, Int y, Int width, Color textColor, Bool pause, Bool pauseMusic) { - if (TheWindowManager == NULL) - return; if(m_popupMessageData) clearPopupMessageData(); @@ -5224,7 +5216,7 @@ void InGameUI::popupMessage( const AsciiString& identifier, Int x, Int y, Int wi //------------------------------------------------------------------------------------------------- void InGameUI::clearPopupMessageData( void ) { - if(!m_popupMessageData || TheWindowManager == NULL) + if(!m_popupMessageData) return; if(m_popupMessageData->layout) { From cf8963daefcf39d86b564fddf447b22827229e2f Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Thu, 24 Apr 2025 12:06:16 +0200 Subject: [PATCH 38/42] Remove more TheWindowManager NULL checks --- .../GameEngine/Source/Common/GameEngine.cpp | 12 ++-- .../GameEngine/Source/Common/System/Radar.cpp | 7 +- .../GameClient/GUI/ControlBar/ControlBar.cpp | 6 +- .../ControlBarPopupDescription.cpp | 2 +- .../GUI/GUICallbacks/InGameChat.cpp | 2 +- .../Source/GameClient/GameClient.cpp | 2 + .../GameEngine/Source/GameClient/InGameUI.cpp | 69 +++++++++---------- .../GameLogic/ScriptEngine/ScriptActions.cpp | 24 ++++--- .../GameLogic/System/GameLogicDispatch.cpp | 17 ++--- 9 files changed, 66 insertions(+), 75 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp index 85e551e251..15ea12e47e 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp @@ -710,14 +710,10 @@ void GameEngine::reset( void ) { WindowLayout *background = TheWindowManager->winCreateLayout("Menus/BlankWindow.wnd"); - if (background != NULL) - { - DEBUG_ASSERTCRASH(background,("We Couldn't Load Menus/BlankWindow.wnd")); - background->hide(FALSE); - background->bringForward(); - background->getFirstWindow()->winClearStatus(WIN_STATUS_IMAGE); - } - + DEBUG_ASSERTCRASH(background,("We Couldn't Load Menus/BlankWindow.wnd")); + background->hide(FALSE); + background->bringForward(); + background->getFirstWindow()->winClearStatus(WIN_STATUS_IMAGE); Bool deleteNetwork = false; if (TheGameLogic->isInMultiplayerGame()) deleteNetwork = true; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/System/Radar.cpp b/GeneralsMD/Code/GameEngine/Source/Common/System/Radar.cpp index 5d15adb588..1b792fabbf 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/System/Radar.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/System/Radar.cpp @@ -335,11 +335,8 @@ void Radar::newMap( TerrainLogic *terrain ) // keep a pointer for our radar window Int id = NAMEKEY( "ControlBar.wnd:LeftHUD" ); - if (TheWindowManager != NULL) - { - m_radarWindow = TheWindowManager->winGetWindowFromId( NULL, id ); - DEBUG_ASSERTCRASH( m_radarWindow, ("Radar::newMap - Unable to find radar game window\n") ); - } + m_radarWindow = TheWindowManager->winGetWindowFromId( NULL, id ); + DEBUG_ASSERTCRASH( m_radarWindow, ("Radar::newMap - Unable to find radar game window\n") ); // reset all the data in the radar reset(); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp index 2079652dc1..8164ee45ff 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp @@ -2595,6 +2595,7 @@ void ControlBar::setPortraitByImage( const Image *image ) //------------------------------------------------------------------------------------------------- void ControlBar::setPortraitByObject( Object *obj ) { + if( obj ) { if( obj->isKindOf( KINDOF_SHOW_PORTRAIT_WHEN_CONTROLLED ) && !obj->isLocallyControlled() ) @@ -2755,6 +2756,7 @@ void ControlBar::setControlBarSchemeByPlayer(Player *p) { if(m_controlBarSchemeManager) m_controlBarSchemeManager->setControlBarSchemeByPlayer(p); + static NameKeyType buttonPlaceBeaconID = NAMEKEY( "ControlBar.wnd:ButtonPlaceBeacon" ); static NameKeyType buttonIdleWorkerID = NAMEKEY("ControlBar.wnd:ButtonIdleWorker"); static NameKeyType buttonGeneralID = NAMEKEY("ControlBar.wnd:ButtonGeneral"); @@ -2961,7 +2963,7 @@ void ControlBar::showPurchaseScience( void ) void ControlBar::hidePurchaseScience( void ) { - if(m_contextParent[ CP_PURCHASE_SCIENCE ] == NULL || m_contextParent[ CP_PURCHASE_SCIENCE ]->winIsHidden()) + if(m_contextParent[ CP_PURCHASE_SCIENCE ]->winIsHidden()) return; if( m_contextParent[ CP_PURCHASE_SCIENCE ] ) @@ -2986,8 +2988,6 @@ void ControlBar::hidePurchaseScience( void ) void ControlBar::togglePurchaseScience( void ) { - if (m_contextParent[ CP_PURCHASE_SCIENCE ] == NULL) - return; if(m_contextParent[ CP_PURCHASE_SCIENCE ]->winIsHidden()) showPurchaseScience(); else diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/ControlBarPopupDescription.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/ControlBarPopupDescription.cpp index b674e2d613..f54463db74 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/ControlBarPopupDescription.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/ControlBarPopupDescription.cpp @@ -702,7 +702,7 @@ void ControlBar::deleteBuildTooltipLayout( void ) { m_showBuildToolTipLayout = FALSE; prevWindow= NULL; - if (m_buildToolTipLayout) m_buildToolTipLayout->hide(TRUE); + m_buildToolTipLayout->hide(TRUE); // if(!m_buildToolTipLayout) // return; // diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/InGameChat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/InGameChat.cpp index 6ae73663a5..c71f8c0f93 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/InGameChat.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/InGameChat.cpp @@ -114,7 +114,7 @@ void HideInGameChat( Bool immediate ) chatTextEntry->winEnable(FALSE); TheWindowManager->winSetFocus( NULL ); } - if (TheWindowManager) TheWindowManager->winSetFocus( NULL ); + TheWindowManager->winSetFocus( NULL ); } // ------------------------------------------------------------------------------------------------ diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp index 030922a9c9..103721042f 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp @@ -349,9 +349,11 @@ void GameClient::init( void ) TheWindowManager = TheGlobalData->m_headless ? NEW DummyGameWindowManager : createWindowManager(); if( TheWindowManager ) { + TheWindowManager->init(); TheWindowManager->setName("TheWindowManager"); // TheWindowManager->initTestGUI(); + } // end if // create the IME manager diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp index 07480af288..022e976842 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp @@ -1805,42 +1805,39 @@ void InGameUI::update( void ) static NameKeyType moneyWindowKey = TheNameKeyGenerator->nameToKey( "ControlBar.wnd:MoneyDisplay" ); static NameKeyType powerWindowKey = TheNameKeyGenerator->nameToKey( "ControlBar.wnd:PowerWindow" ); - if (TheWindowManager != NULL) + GameWindow *moneyWin = TheWindowManager->winGetWindowFromId( NULL, moneyWindowKey ); + GameWindow *powerWin = TheWindowManager->winGetWindowFromId( NULL, powerWindowKey ); +// if( moneyWin == NULL ) +// { +// NameKeyType moneyWindowKey = TheNameKeyGenerator->nameToKey( "ControlBar.wnd:MoneyDisplay" ); +// +// moneyWin = TheWindowManager->winGetWindowFromId( NULL, moneyWindowKey ); +// +// } // end if + Player *moneyPlayer = NULL; + if( TheControlBar->isObserverControlBarOn()) + moneyPlayer = TheControlBar->getObserverLookAtPlayer(); + else + moneyPlayer = ThePlayerList->getLocalPlayer(); + if( moneyPlayer) { - GameWindow *moneyWin = TheWindowManager->winGetWindowFromId( NULL, moneyWindowKey ); - GameWindow *powerWin = TheWindowManager->winGetWindowFromId( NULL, powerWindowKey ); - // if( moneyWin == NULL ) - // { - // NameKeyType moneyWindowKey = TheNameKeyGenerator->nameToKey( "ControlBar.wnd:MoneyDisplay" ); - // - // moneyWin = TheWindowManager->winGetWindowFromId( NULL, moneyWindowKey ); - // - // } // end if - Player *moneyPlayer = NULL; - if( TheControlBar->isObserverControlBarOn()) - moneyPlayer = TheControlBar->getObserverLookAtPlayer(); - else - moneyPlayer = ThePlayerList->getLocalPlayer(); - if( moneyPlayer) + Int currentMoney = moneyPlayer->getMoney()->countMoney(); + if( lastMoney != currentMoney ) { - Int currentMoney = moneyPlayer->getMoney()->countMoney(); - if( lastMoney != currentMoney ) - { - UnicodeString buffer; + UnicodeString buffer; - buffer.format( TheGameText->fetch( "GUI:ControlBarMoneyDisplay" ), currentMoney ); - GadgetStaticTextSetText( moneyWin, buffer ); - lastMoney = currentMoney; + buffer.format( TheGameText->fetch( "GUI:ControlBarMoneyDisplay" ), currentMoney ); + GadgetStaticTextSetText( moneyWin, buffer ); + lastMoney = currentMoney; - } // end if - moneyWin->winHide(FALSE); - powerWin->winHide(FALSE); - } - else - { - moneyWin->winHide(TRUE); - powerWin->winHide(TRUE); - } + } // end if + moneyWin->winHide(FALSE); + powerWin->winHide(FALSE); + } + else + { + moneyWin->winHide(TRUE); + powerWin->winHide(TRUE); } // Update the floating Text; @@ -3869,8 +3866,8 @@ void InGameUI::expireHint( HintType type, UnsignedInt hintIndex ) //------------------------------------------------------------------------------------------------- void InGameUI::createControlBar( void ) { - if (TheWindowManager != NULL) - TheWindowManager->winCreateFromScript( AsciiString("ControlBar.wnd") ); + + TheWindowManager->winCreateFromScript( AsciiString("ControlBar.wnd") ); HideControlBar(); /* // hide all windows created from this layout @@ -3886,8 +3883,8 @@ void InGameUI::createControlBar( void ) //------------------------------------------------------------------------------------------------- void InGameUI::createReplayControl( void ) { - if (TheWindowManager != NULL) - m_replayWindow = TheWindowManager->winCreateFromScript( AsciiString("ReplayControl.wnd") ); + + m_replayWindow = TheWindowManager->winCreateFromScript( AsciiString("ReplayControl.wnd") ); /* // hide all windows created from this layout diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp index c4cafaa441..7eae3e86d5 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp @@ -217,7 +217,7 @@ void ScriptActions::doVictory( void ) closeWindows(FALSE); TheGameLogic->closeWindows(); doDisableInput(); - if (!m_suppressNewWindows && TheWindowManager) + if (!m_suppressNewWindows) { const Player *localPlayer = ThePlayerList->getLocalPlayer(); Bool showObserverWindow = localPlayer->isPlayerObserver() || TheScriptEngine->hasShownMPLocalDefeatWindow(); @@ -241,7 +241,7 @@ void ScriptActions::doDefeat( void ) closeWindows(FALSE); TheGameLogic->closeWindows(); doDisableInput(); - if (!m_suppressNewWindows && TheWindowManager != NULL) + if (!m_suppressNewWindows) { const Player *localPlayer = ThePlayerList->getLocalPlayer(); Bool showObserverWindow = localPlayer->isPlayerObserver() || TheScriptEngine->hasShownMPLocalDefeatWindow(); @@ -3201,16 +3201,18 @@ void ScriptActions::doMergeTeamIntoTeam(const AsciiString& teamSrcName, const As void ScriptActions::doDisableInput() { #if defined(_DEBUG) || defined(_INTERNAL) - if (TheGlobalData->m_disableScriptedInputDisabling) - return; + if (!TheGlobalData->m_disableScriptedInputDisabling) #endif - TheInGameUI->setInputEnabled(false); - if (TheMouse) TheMouse->setVisibility(false); - TheInGameUI->deselectAllDrawables(); - TheInGameUI->clearAttackMoveToMode(); - TheInGameUI->setWaypointMode( FALSE ); - TheControlBar->deleteBuildTooltipLayout(); - TheLookAtTranslator->resetModes(); + { + TheInGameUI->setInputEnabled(false); + if (TheMouse) + TheMouse->setVisibility(false); + TheInGameUI->deselectAllDrawables(); + TheInGameUI->clearAttackMoveToMode(); + TheInGameUI->setWaypointMode( FALSE ); + TheControlBar->deleteBuildTooltipLayout(); + TheLookAtTranslator->resetModes(); + } } //------------------------------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp index 8af03e8b37..808a8b1e80 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp @@ -235,13 +235,11 @@ void GameLogic::closeWindows( void ) // hide the options menu NameKeyType buttonID = TheNameKeyGenerator->nameToKey( "OptionsMenu.wnd:ButtonBack" ); - if (TheWindowManager) - { - GameWindow *button = TheWindowManager->winGetWindowFromId( NULL, buttonID ); - GameWindow *window = TheWindowManager->winGetWindowFromId( NULL, TheNameKeyGenerator->nameToKey("OptionsMenu.wnd:OptionsMenuParent") ); - if(window) - TheWindowManager->winSendSystemMsg( window, GBM_SELECTED, (WindowMsgData)button, buttonID ); - } + GameWindow *button = TheWindowManager->winGetWindowFromId( NULL, buttonID ); + GameWindow *window = TheWindowManager->winGetWindowFromId( NULL, TheNameKeyGenerator->nameToKey("OptionsMenu.wnd:OptionsMenuParent") ); + if(window) + TheWindowManager->winSendSystemMsg( window, GBM_SELECTED, + (WindowMsgData)button, buttonID ); } // ------------------------------------------------------------------------------------------------ @@ -321,15 +319,14 @@ void GameLogic::prepareNewGame( Int gameMode, GameDifficulty diff, Int rankPoint TheScriptEngine->setGlobalDifficulty(diff); - if(!m_background && TheWindowManager != NULL) + if(!m_background) { m_background = TheWindowManager->winCreateLayout("Menus/BlankWindow.wnd"); DEBUG_ASSERTCRASH(m_background,("We Couldn't Load Menus/BlankWindow.wnd")); m_background->hide(FALSE); m_background->bringForward(); } - if (m_background != NULL) - m_background->getFirstWindow()->winClearStatus(WIN_STATUS_IMAGE); + m_background->getFirstWindow()->winClearStatus(WIN_STATUS_IMAGE); TheGameLogic->setGameMode( gameMode ); if (!TheGlobalData->m_pendingFile.isEmpty()) { From d371c7654e054d74881e5a7768a688e4aaa5df36 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Thu, 24 Apr 2025 12:42:43 +0200 Subject: [PATCH 39/42] Fix stupid dates in comments --- GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h | 2 +- GeneralsMD/Code/GameEngine/Include/Common/Radar.h | 2 +- GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h | 2 +- GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp | 2 +- .../Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp | 2 +- GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp | 2 +- .../Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp | 2 +- GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp | 2 +- .../Code/GameEngine/Source/GameLogic/System/GameLogic.cpp | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h index 886d54367b..ba3b9f750b 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h @@ -101,7 +101,7 @@ class GlobalData : public SubsystemInterface Int m_framesPerSecondLimit; Int m_chipSetType; ///getWaterGridHeight( x, y, &meshZ ) ) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index ad1c7de212..7ad6c38542 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -6096,7 +6096,7 @@ const AsciiString& Object::getCommandSetString() const //============================================================================= Bool Object::canProduceUpgrade( const UpgradeTemplate *upgrade ) { - // TheSuperHackers @logic-client-separation helmutbuhler 04/11/2025 + // TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025 // TheControlBar belongs to the client, we shouldn't depend on that to check this. // We need to have the button to make the upgrade. CommandSets are a weird Logic/Client hybrid. diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp index 53ad8a5230..dfa7399a90 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp @@ -511,7 +511,7 @@ void DockUpdate::loadDockPositions() { // Dynamic means no bones - // TheSuperHackers @logic-client-separation helmutbuhler 04/11/2025 + // TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025 // We shouldn't depend on bones of a drawable here! Coord3D approachBones[DEFAULT_APPROACH_VECTOR_SIZE]; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp index 11b8c88cf6..66e2298826 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp @@ -2598,7 +2598,7 @@ Bool Weapon::privateFireWeapon( Bool reloaded = false; if (m_ammoInClip > 0) { - // TheSuperHackers @logic-client-separation helmutbuhler 04/11/2025 + // TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025 // barrelCount shouln't depend on Drawable, which belongs to client. Int barrelCount = sourceObj->getDrawable()->getBarrelCount(m_wslot); if (m_curBarrel >= barrelCount) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index dc5ac13e2b..77ff8271c9 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -738,7 +738,7 @@ static void populateRandomSideAndColor( GameInfo *game ) // This is also enforced at GUI setup (GUIUtil.cpp). // @todo: unlock these when something rad happens - // TheSuperHackers @logic-client-separation helmutbuhler 04/11/2025 + // TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025 // TheChallengeGenerals belongs to client, we shouldn't depend on that here. Bool disallowLockedGenerals = TRUE; const GeneralPersona *general = TheChallengeGenerals->getGeneralByTemplateName(ptTest->getName()); @@ -1986,7 +1986,7 @@ void GameLogic::startNewGame( Bool loadingSaveGame ) // This is also enforced at GUI setup (GUIUtil.cpp and UserPreferences.cpp). // @todo: unlock these when something rad happens - // TheSuperHackers @logic-client-separation helmutbuhler 04/11/2025 + // TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025 // TheChallengeGenerals belongs to client, we shouldn't depend on that here. Bool disallowLockedGenerals = TRUE; const GeneralPersona *general = TheChallengeGenerals->getGeneralByTemplateName(pt->getName()); From 9513645337f002ae3cac9d7a459ada782d9210c9 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Thu, 24 Apr 2025 12:43:00 +0200 Subject: [PATCH 40/42] Remove one more TheWindowManager NULL check --- .../Source/W3DDevice/GameClient/W3DInGameUI.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DInGameUI.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DInGameUI.cpp index 83b7e66914..747512e025 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DInGameUI.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DInGameUI.cpp @@ -436,8 +436,7 @@ void W3DInGameUI::draw( void ) postDraw(); - if (TheWindowManager) - TheWindowManager->winRepaint(); + TheWindowManager->winRepaint(); #ifdef EXTENDED_STATS } From 1cf0c697d5de29744f0029590e06d0dac9513c29 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Thu, 24 Apr 2025 12:43:32 +0200 Subject: [PATCH 41/42] Fix some white space stuff --- .../Code/GameEngine/Source/GameClient/InGameUI.cpp | 2 +- .../Source/GameLogic/ScriptEngine/ScriptActions.cpp | 2 +- .../GameClient/Drawable/Draw/W3DRopeDraw.cpp | 13 +++++++++---- .../Source/W3DDevice/GameClient/W3DAssetManager.cpp | 2 ++ 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp index 022e976842..dab86e26a5 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp @@ -1298,7 +1298,7 @@ void InGameUI::setRadiusCursor(RadiusCursorType cursorType, const SpecialPowerTe //------------------------------------------------------------------------------------------------- void InGameUI::handleRadiusCursor() { - if (!m_curRadiusCursor.isEmpty() && TheMouse) + if (!m_curRadiusCursor.isEmpty() && TheMouse != NULL) { const MouseIO* mouseIO = TheMouse->getMouseStatus(); Coord3D pos; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp index 7eae3e86d5..2e9c8da0ac 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp @@ -3205,7 +3205,7 @@ void ScriptActions::doDisableInput() #endif { TheInGameUI->setInputEnabled(false); - if (TheMouse) + if (TheMouse != NULL) TheMouse->setVisibility(false); TheInGameUI->deselectAllDrawables(); TheInGameUI->clearAttackMoveToMode(); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp index 4b44b10c05..e133e6fadd 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp @@ -107,8 +107,11 @@ void W3DRopeDraw::buildSegments() m_color.blue, // blue 0.5f ); // transparency - if (W3DDisplay::m_3DScene) W3DDisplay::m_3DScene->Add_Render_Object( info.line ); - if (W3DDisplay::m_3DScene) W3DDisplay::m_3DScene->Add_Render_Object( info.softLine ); + if (W3DDisplay::m_3DScene) + { + W3DDisplay::m_3DScene->Add_Render_Object( info.line ); + W3DDisplay::m_3DScene->Add_Render_Object( info.softLine ); + } m_segments.push_back(info); } } @@ -122,12 +125,14 @@ void W3DRopeDraw::tossSegments() { if (it->line) { - if (W3DDisplay::m_3DScene) W3DDisplay::m_3DScene->Remove_Render_Object(it->line); + if (W3DDisplay::m_3DScene) + W3DDisplay::m_3DScene->Remove_Render_Object(it->line); REF_PTR_RELEASE((it->line)); } if (it->softLine) { - if (W3DDisplay::m_3DScene) W3DDisplay::m_3DScene->Remove_Render_Object(it->softLine); + if (W3DDisplay::m_3DScene) + W3DDisplay::m_3DScene->Remove_Render_Object(it->softLine); REF_PTR_RELEASE((it->softLine)); } } diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp index 20553b40ce..90950e4316 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp @@ -880,6 +880,7 @@ int W3DAssetManager::Recolor_Mesh(RenderObjClass *robj, const int color) int i; int didRecolor=0; const char *meshName; + MeshClass *mesh=(MeshClass*) robj; MeshModelClass * model = mesh->Get_Model(); MaterialInfoClass *material = mesh->Get_Material_Info(); @@ -925,6 +926,7 @@ int W3DAssetManager::Recolor_HLOD(RenderObjClass *robj, const int color) return 0; int didRecolor=0; + int num_sub = robj->Get_Num_Sub_Objects(); for(int i = 0; i < num_sub; i++) { RenderObjClass *sub_obj = robj->Get_Sub_Object(i); From d8bcd57e762f6456629faa1d7c424d9e0903b5e8 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Thu, 24 Apr 2025 12:48:57 +0200 Subject: [PATCH 42/42] Add comments for DummyGameWindowManager --- .../Code/GameEngine/Include/GameClient/GameWindowManager.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/GameWindowManager.h b/GeneralsMD/Code/GameEngine/Include/GameClient/GameWindowManager.h index 8016172378..1b4db1ecdd 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/GameWindowManager.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/GameWindowManager.h @@ -379,6 +379,9 @@ extern WindowMsgHandledType PassMessagesToParentSystem( GameWindow *window, WindowMsgData mData1, WindowMsgData mData2 ); + +// TheSuperHackers @feature helmutbuhler 24/04/2025 +// GameWindow that does nothing. Used for Headless Mode. class DummyGameWindow : public GameWindow { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DummyGameWindow, "DummyGameWindow") @@ -387,6 +390,8 @@ class DummyGameWindow : public GameWindow virtual void winDrawBorder() { } }; +// TheSuperHackers @feature helmutbuhler 24/04/2025 +// GameWindowManager that does nothing. Used for Headless Mode. class DummyGameWindowManager : public GameWindowManager { public: