From dabb98b812e3105f291085961826191ffc68dcff Mon Sep 17 00:00:00 2001 From: tintinhamans <5984296+tintinhamans@users.noreply.github.com> Date: Tue, 29 Sep 2026 23:26:45 +0200 Subject: [PATCH 1/7] fix(auth): Keep login and anticheat tokens out of the release log --- .../GeneralsOnline/OnlineServices_Auth.cpp | 15 ++++++++++----- .../GeneralsOnline/PluginInterfaces.cpp | 4 ++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Auth.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Auth.cpp index e72240f63f7..4bdfb583a4b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Auth.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Auth.cpp @@ -173,9 +173,12 @@ void NGMP_OnlineServices_AuthInterface::SendMiddlewareToken(std::string strMWTok void NGMP_OnlineServices_AuthInterface::OnRefreshTokenFailed(const char* szReason, const std::string& strBody) { - // log the raw response so refresh failures are actually diagnosable - std::string strBodySnippet = strBody.substr(0, 512); - NetworkLog(ELogVerbosity::LOG_RELEASE, "[AUTH]: Refresh response body: %s", strBodySnippet.c_str()); + // the body can hold a rotated refresh token, so release builds log only its size +#if _DEBUG + NetworkLog(ELogVerbosity::LOG_RELEASE, "[AUTH]: Refresh response body: %s", strBody.c_str()); +#else + NetworkLog(ELogVerbosity::LOG_RELEASE, "[AUTH]: Refresh response body was %zu bytes", strBody.size()); +#endif if (m_currentRefreshAttempt < m_maxRefreshAttempts) { @@ -431,9 +434,9 @@ void NGMP_OnlineServices_AuthInterface::DoFullLoginFlow() NetworkLog(ELogVerbosity::LOG_DEBUG, "Login Code is %s", m_strCode.c_str()); #if defined(USE_TEST_ENV) - std::string strURI = std::format("http://www.playgenerals.online/login/?gamecode={}&env=test", m_strCode.c_str()); + std::string strURI = std::format("https://www.playgenerals.online/login/?gamecode={}&env=test", m_strCode.c_str()); #else - std::string strURI = std::format("http://www.playgenerals.online/login/?gamecode={}", m_strCode.c_str()); + std::string strURI = std::format("https://www.playgenerals.online/login/?gamecode={}", m_strCode.c_str()); #endif ClearGSMessageBoxes(); @@ -533,7 +536,9 @@ void NGMP_OnlineServices_AuthInterface::Tick() nlohmann::json jsonObject = nlohmann::json::parse(strBody); AuthResponse authResp = jsonObject.get(); +#if _DEBUG NetworkLog(ELogVerbosity::LOG_RELEASE, "PageBody: %s", strBody.c_str()); +#endif if (authResp.result == EAuthResponseResult::CODE_INVALID) { NetworkLog(ELogVerbosity::LOG_RELEASE, "LOGIN: Code didnt exist, trying again soon"); diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/PluginInterfaces.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/PluginInterfaces.cpp index 92c578d7f0b..f914ae6ef25 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/PluginInterfaces.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/PluginInterfaces.cpp @@ -423,7 +423,11 @@ void AnticheatPlugInterface::Authenticate() // don't trust the plugin to terminate the buffer buf[sizeof(buf) - 1] = '\0'; +#if _DEBUG NetworkLog(ELogVerbosity::LOG_RELEASE, "[AC] Got MW token: %s", buf); +#else + NetworkLog(ELogVerbosity::LOG_RELEASE, "[AC] Got MW token (%zu chars)", strlen(buf)); +#endif // Now we can begin login NGMP_OnlineServices_AuthInterface* pAuthInterface = NGMP_OnlineServicesManager::GetInterface(); From 637ae2680e130aa918d263fc3287100cfcbf9f8a Mon Sep 17 00:00:00 2001 From: tintinhamans <5984296+tintinhamans@users.noreply.github.com> Date: Tue, 29 Sep 2026 23:26:46 +0200 Subject: [PATCH 2/7] fix(lobby): Log the response body as a format argument --- .../GeneralsOnline/OnlineServices_LobbyInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_LobbyInterface.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_LobbyInterface.cpp index 106d2f9621b..4b5b9da9cbc 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_LobbyInterface.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_LobbyInterface.cpp @@ -905,7 +905,7 @@ void NGMP_OnlineServices_LobbyInterface::UpdateRoomDataCache(std::function Date: Tue, 29 Sep 2026 23:27:10 +0200 Subject: [PATCH 3/7] fix(update): Give the updater download a long timeout --- .../Source/GameNetwork/GeneralsOnline/OnlineServices_Init.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Init.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Init.cpp index 47b7bd8318e..7b2e694fe07 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Init.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Init.cpp @@ -536,7 +536,8 @@ void NGMP_OnlineServicesManager::ContinueUpdate() { TheDownloadManager->OnProgressUpdate(bytesReceived, downloadSize, -1, -1); } - } + }, + 30 * 60 * 1000 // the 5s default aborts any real download ); } else if (m_vecFilesToDownload.size() == 0 && m_vecFilesDownloaded.size() > 0) // nothing left but we did download something From f48f85e6604c0ed6c9c06d62e96c619dd110ce8a Mon Sep 17 00:00:00 2001 From: tintinhamans <5984296+tintinhamans@users.noreply.github.com> Date: Tue, 29 Sep 2026 23:27:23 +0200 Subject: [PATCH 4/7] fix(update): Only accept a bare file name for the updater --- .../GeneralsOnline/OnlineServices_Init.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Init.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Init.cpp index 7b2e694fe07..cfe0835efd4 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Init.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Init.cpp @@ -423,6 +423,15 @@ void NGMP_OnlineServicesManager::StartVersionCheck(std::function vecBuffer = pReq->GetBuffer(); size_t bufSize = pReq->GetBufferSize(); From 50addcd159bdcc9ac002a2ccf9857bd7249269b9 Mon Sep 17 00:00:00 2001 From: tintinhamans <5984296+tintinhamans@users.noreply.github.com> Date: Tue, 29 Sep 2026 23:34:48 +0200 Subject: [PATCH 5/7] fix(http): Keep the logged response's case when checking for tokens --- .../Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp index 9e41910c504..02786d7f3d9 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp @@ -216,9 +216,10 @@ void HTTPRequest::Threaded_SetComplete(CURLcode result) seedPos = valueStart; } - std::transform(strResponse.begin(), strResponse.end(), strResponse.begin(), + std::string strResponseLower = strResponse; + std::transform(strResponseLower.begin(), strResponseLower.end(), strResponseLower.begin(), [](unsigned char c) { return std::tolower(c); }); - if (strResponse.find("token") != std::string::npos) + if (strResponseLower.find("token") != std::string::npos) { strResponse = ""; } From 2e3e6b072daa0e6b22b08fda161439814dbbfcdd Mon Sep 17 00:00:00 2001 From: tintinhamans <5984296+tintinhamans@users.noreply.github.com> Date: Tue, 29 Sep 2026 23:49:38 +0200 Subject: [PATCH 6/7] fix(main): Mark the game window active when it takes over from the splash --- GeneralsMD/Code/Main/WinMain.cpp | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/GeneralsMD/Code/Main/WinMain.cpp b/GeneralsMD/Code/Main/WinMain.cpp index d7a7bb6acc1..d68a5c5d989 100644 --- a/GeneralsMD/Code/Main/WinMain.cpp +++ b/GeneralsMD/Code/Main/WinMain.cpp @@ -291,6 +291,21 @@ static const char* messageToString(unsigned int message) } #endif +static void setWinMainActive(Bool active) +{ + isWinMainActive = active; + + if (TheGameEngine) + TheGameEngine->setIsActive(isWinMainActive); + + if (isWinMainActive) + { + //restore mouse cursor to our custom version. + if (TheWin32Mouse) + TheWin32Mouse->setCursor(TheWin32Mouse->getMouseCursor()); + } +} + // WndProc ==================================================================== /** Window Procedure */ //============================================================================= @@ -471,17 +486,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, // of TestCooperativeLevel() == D3DERR_DEVICENOTRESET is not a requirement. There are other code // paths that take care of that. - isWinMainActive = (BOOL)wParam; - - if (TheGameEngine) - TheGameEngine->setIsActive(isWinMainActive); - - if (isWinMainActive) - { - //restore mouse cursor to our custom version. - if (TheWin32Mouse) - TheWin32Mouse->setCursor(TheWin32Mouse->getMouseCursor()); - } + setWinMainActive((BOOL)wParam); } return 0; } @@ -501,6 +506,10 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, if (TheAudio) TheAudio->unmuteAudio(AudioManager::MuteAudioReason_WindowFocus); + // taking over from the splash stays within the app, so no WM_ACTIVATEAPP arrives + if (!isWinMainActive) + setWinMainActive(TRUE); + // Cursor can only be captured after one of the activation events. if (TheMouse) TheMouse->refreshCursorCapture(); From 92289d416c781e283379b74c58b015161daf2b54 Mon Sep 17 00:00:00 2001 From: tintinhamans <5984296+tintinhamans@users.noreply.github.com> Date: Tue, 29 Sep 2026 23:50:16 +0200 Subject: [PATCH 7/7] fix(mesh): Stop re-signalling a peer that has left the match lobby --- .../Source/GameNetwork/GeneralsOnline/NetworkMesh.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/NetworkMesh.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/NetworkMesh.cpp index 8d4c474857c..ee01a428169 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/NetworkMesh.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/NetworkMesh.cpp @@ -112,9 +112,9 @@ void OnSteamNetConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t NGMP_OnlineServices_LobbyInterface* pJoinOrderLobby = NGMP_OnlineServicesManager::GetInterface(); const bool bWeJoinedLater = pJoinOrderLobby == nullptr || !pJoinOrderLobby->IsJoinOrderKnown() || pJoinOrderLobby->JoinedAfter(userID); const bool bPeerLeft = pJoinOrderLobby != nullptr && pJoinOrderLobby->IsJoinOrderKnown() && !pJoinOrderLobby->IsLobbyMember(userID); - // a match can't leave its lobby, so keep repairing the link until the game drops the player + // a match can't leave its lobby, so keep repairing the link until the service drops the player const bool bInMatch = TheGameLogic != nullptr && TheGameLogic->isInInternetGame(); - bool bShouldRetry = serviceConf.retry_signalling && (bInMatch || (!bWeJoinedLater && !bPeerLeft) || signallingAttemptsBeforeDisconnect < numSignallingAttempts); + bool bShouldRetry = serviceConf.retry_signalling && ((bInMatch && !bPeerLeft) || (!bWeJoinedLater && !bPeerLeft) || signallingAttemptsBeforeDisconnect < numSignallingAttempts); bool bWasError = pInfo->m_info.m_eState == k_ESteamNetworkingConnectionState_ProblemDetectedLocally || pInfo->m_info.m_eEndReason != k_ESteamNetConnectionEnd_App_Generic; plrConnection.SetDisconnected(bWasError, pMesh, bShouldRetry && bWasError);