diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/OnlineServices_Init.h b/GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/OnlineServices_Init.h index 26fc183227..b0dc617381 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/OnlineServices_Init.h +++ b/GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/OnlineServices_Init.h @@ -571,6 +571,7 @@ class NGMP_OnlineServicesManager // Screenshot thread management std::vector m_vecScreenshotThreads; std::mutex m_mutexScreenshotThreads; + std::atomic m_bShuttingDown = false; ServiceConfig m_ServiceConfig; diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Init.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Init.cpp index bc46b0f359..43af96ac07 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Init.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Init.cpp @@ -56,20 +56,27 @@ void NGMP_OnlineServicesManager::GetAndParseServiceConfig(std::function mapHeaders; - NGMP_OnlineServicesManager::GetInstance()->GetHTTPManager()->SendGETRequest(strURI.c_str(), EIPProtocolVersion::DONT_CARE, mapHeaders, [=](bool bSuccess, int statusCode, std::string strBody, HTTPRequest* pReq) + NGMP_OnlineServicesManager::GetInstance()->GetHTTPManager()->SendGETRequest(strURI.c_str(), EIPProtocolVersion::DONT_CARE, mapHeaders, [cbOnDone](bool bSuccess, int statusCode, std::string strBody, HTTPRequest* pReq) { + // Get the manager instance safely + NGMP_OnlineServicesManager* pMgr = NGMP_OnlineServicesManager::GetInstance(); + if (pMgr == nullptr) + { + return; + } + try { if (bSuccess && statusCode == 200) { nlohmann::json jsonObject = nlohmann::json::parse(strBody); - m_ServiceConfig = jsonObject.get(); + pMgr->m_ServiceConfig = jsonObject.get(); } else { // It's OK to fail, we'll just use the sensible defaults NetworkLog(ELogVerbosity::LOG_RELEASE, "[NGMP] Failed to get service config, using defaults. Status code: %d", statusCode); - m_ServiceConfig = ServiceConfig(); + pMgr->m_ServiceConfig = ServiceConfig(); } } @@ -77,7 +84,7 @@ void NGMP_OnlineServicesManager::GetAndParseServiceConfig(std::functionm_ServiceConfig = ServiceConfig(); } if (cbOnDone != nullptr) @@ -292,6 +299,12 @@ void NGMP_OnlineServicesManager::WaitForScreenshotThreads() void NGMP_OnlineServicesManager::Shutdown() { + // Signal that we're shutting down + m_bShuttingDown = true; + + // Wait for all screenshot threads to complete before shutting down other components + WaitForScreenshotThreads(); + if (m_pWebSocket) { m_pWebSocket->Shutdown(); @@ -332,9 +345,21 @@ void NGMP_OnlineServicesManager::StartVersionCheck(std::function mapHeaders; - NGMP_OnlineServicesManager::GetInstance()->GetHTTPManager()->SendPOSTRequest(strURI.c_str(), EIPProtocolVersion::DONT_CARE, mapHeaders, strPostData.c_str(), [=](bool bSuccess, int statusCode, std::string strBody, HTTPRequest* pReq) + NGMP_OnlineServicesManager::GetInstance()->GetHTTPManager()->SendPOSTRequest(strURI.c_str(), EIPProtocolVersion::DONT_CARE, mapHeaders, strPostData.c_str(), [fnCallback](bool bSuccess, int statusCode, std::string strBody, HTTPRequest* pReq) { NetworkLog(ELogVerbosity::LOG_RELEASE, "Version Check: Response code was %d and body was %s", statusCode, strBody.c_str()); + + // Get the manager instance safely + NGMP_OnlineServicesManager* pMgr = NGMP_OnlineServicesManager::GetInstance(); + if (pMgr == nullptr) + { + if (fnCallback != nullptr) + { + fnCallback(false, false); + } + return; + } + try { NetworkLog(ELogVerbosity::LOG_RELEASE, "VERSION CHECK: Up To Date"); @@ -344,29 +369,41 @@ void NGMP_OnlineServicesManager::StartVersionCheck(std::functionm_patcher_name = authResp.patcher_name; + pMgr->m_patcher_path = authResp.patcher_path; + pMgr->m_patcher_size = authResp.patcher_size; - fnCallback(true, true); + if (fnCallback != nullptr) + { + fnCallback(true, true); + } } else { NetworkLog(ELogVerbosity::LOG_RELEASE, "VERSION CHECK: Failed"); - fnCallback(false, false); + if (fnCallback != nullptr) + { + fnCallback(false, false); + } } } catch (...) { NetworkLog(ELogVerbosity::LOG_RELEASE, "VERSION CHECK: Failed to parse response"); - fnCallback(false, false); + if (fnCallback != nullptr) + { + fnCallback(false, false); + } } }, nullptr, -1); } @@ -389,7 +426,7 @@ void NGMP_OnlineServicesManager::ContinueUpdate() // this isnt a super nice way of doing this, lets make a download manager std::map mapHeaders; - m_pHTTPManager->SendGETRequest(strDownloadPath.c_str(), EIPProtocolVersion::DONT_CARE, mapHeaders, [=](bool bSuccess, int statusCode, std::string strBody, HTTPRequest* pReq) + m_pHTTPManager->SendGETRequest(strDownloadPath.c_str(), EIPProtocolVersion::DONT_CARE, mapHeaders, [strDownloadPath, downloadSize](bool bSuccess, int statusCode, std::string strBody, HTTPRequest* pReq) { if (statusCode != 200) { @@ -403,15 +440,22 @@ void NGMP_OnlineServicesManager::ContinueUpdate() } else { + // Get the manager instance safely + NGMP_OnlineServicesManager* pMgr = NGMP_OnlineServicesManager::GetInstance(); + if (pMgr == nullptr) + { + return; + } + // set done if (TheDownloadManager != nullptr) { TheDownloadManager->OnProgressUpdate(downloadSize, downloadSize, 0, 0); } - m_vecFilesDownloaded.push_back(strDownloadPath); + pMgr->m_vecFilesDownloaded.push_back(strDownloadPath); - std::string strPatchDir = GetPatcherDirectoryPath(); + std::string strPatchDir = pMgr->GetPatcherDirectoryPath(); // Extract the filename with extension from strDownloadPath std::string strFileName = strDownloadPath.substr(strDownloadPath.find_last_of('/') + 1); @@ -430,12 +474,12 @@ void NGMP_OnlineServicesManager::ContinueUpdate() fclose(pFile); // call continue update again, thisll check if we're done or have more work to do - ContinueUpdate(); + pMgr->ContinueUpdate(); NetworkLog(ELogVerbosity::LOG_RELEASE, "GOT FILE: %s", strDownloadPath.c_str()); } }, - [=](size_t bytesReceived) + [downloadSize](size_t bytesReceived) { //m_bytesReceivedSoFar += bytesReceived; @@ -509,92 +553,102 @@ void NGMP_OnlineServicesManager::CaptureScreenshot(bool bResizeForTransmit, std: if (pBits != nullptr) { - int width = surfaceDesc.Width; - int height = surfaceDesc.Height; - - // process on thread - track the thread so we can join it during shutdown - std::thread* pNewThread = new std::thread([cbOnDataAvailable, width, height, pBits, pDXsurf, pitch, bResizeForTransmit]() - { - CHECK_WORKER_THREAD; - - unsigned char* rgbData = new unsigned char[width * height * 3]; - - std::vector vecData; + // Check if we're shutting down before creating a new thread + if (m_pOnlineServicesManager != nullptr && m_pOnlineServicesManager->m_bShuttingDown) + { + // Don't create new threads during shutdown + NetworkLog(ELogVerbosity::LOG_RELEASE, "[NGMP] Screenshot capture skipped - manager is shutting down"); + bSucceeded = false; + } + else + { + int width = surfaceDesc.Width; + int height = surfaceDesc.Height; - int finalWidth = width; - int finalHeight = height; + // process on thread - track the thread so we can join it during shutdown + std::thread* pNewThread = new std::thread([cbOnDataAvailable, width, height, pBits, pDXsurf, pitch, bResizeForTransmit]() + { + CHECK_WORKER_THREAD; + + unsigned char* rgbData = new unsigned char[width * height * 3]; + + std::vector vecData; + + int finalWidth = width; + int finalHeight = height; + + for (int y = 0; y < height; ++y) { + uint8_t* row = static_cast(pBits) + y * pitch; + int rowOffset = y * width * 3; + int srcOffset = 0; + for (int x = 0; x < width; ++x, srcOffset += 4) + { + int dstIndex = rowOffset + x * 3; + rgbData[dstIndex + 0] = row[srcOffset + 2]; // R + rgbData[dstIndex + 1] = row[srcOffset + 1]; // G + rgbData[dstIndex + 2] = row[srcOffset + 0]; // B + } + } - for (int y = 0; y < height; ++y) { - uint8_t* row = static_cast(pBits) + y * pitch; - int rowOffset = y * width * 3; - int srcOffset = 0; - for (int x = 0; x < width; ++x, srcOffset += 4) + // resize + unsigned char* pBufferToWrite = rgbData; + if (bResizeForTransmit) { - int dstIndex = rowOffset + x * 3; - rgbData[dstIndex + 0] = row[srcOffset + 2]; // R - rgbData[dstIndex + 1] = row[srcOffset + 1]; // G - rgbData[dstIndex + 2] = row[srcOffset + 0]; // B + int new_width = 557; + int new_height = 333; + int channels = 3; + unsigned char* resized = new unsigned char[new_width * new_height * channels]; + + stbir_resize_uint8(rgbData, width, height, 0, + resized, new_width, new_height, 0, + channels + ); + + // update data + finalWidth = new_width; + finalHeight = new_height; + pBufferToWrite = resized; } - } + // end resize - // resize - unsigned char* pBufferToWrite = rgbData; - if (bResizeForTransmit) - { - int new_width = 557; - int new_height = 333; - int channels = 3; - unsigned char* resized = new unsigned char[new_width * new_height * channels]; - - stbir_resize_uint8(rgbData, width, height, 0, - resized, new_width, new_height, 0, - channels - ); - - // update data - finalWidth = new_width; - finalHeight = new_height; - pBufferToWrite = resized; - } - // end resize + stbi_write_jpg_to_func([](void* context, void* data, int size) + { + std::vector* buffer = static_cast*>(context); + buffer->insert(buffer->end(), (unsigned char*)data, (unsigned char*)data + size); + }, &vecData, finalWidth, finalHeight, 3, pBufferToWrite, bResizeForTransmit ? 0 : 90); - stbi_write_jpg_to_func([](void* context, void* data, int size) + // cleanup + if (bResizeForTransmit) { - std::vector* buffer = static_cast*>(context); - buffer->insert(buffer->end(), (unsigned char*)data, (unsigned char*)data + size); - }, &vecData, finalWidth, finalHeight, 3, pBufferToWrite, bResizeForTransmit ? 0 : 90); + delete[] pBufferToWrite; // This is 'resized' + pBufferToWrite = nullptr; + } - // cleanup - if (bResizeForTransmit) - { - delete[] pBufferToWrite; // This is 'resized' - pBufferToWrite = nullptr; - } + delete[] rgbData; + rgbData = nullptr; - delete[] rgbData; - rgbData = nullptr; + if (pDXsurf != nullptr) + { + pDXsurf->Release(); + } - if (pDXsurf != nullptr) - { - pDXsurf->Release(); + // invoke cb + if (cbOnDataAvailable != nullptr) + { + cbOnDataAvailable(vecData); + } } + ); - // invoke cb - if (cbOnDataAvailable != nullptr) - { - cbOnDataAvailable(vecData); - } + // Store the thread so we can join it during shutdown + if (m_pOnlineServicesManager != nullptr) + { + std::scoped_lock lock(m_pOnlineServicesManager->m_mutexScreenshotThreads); + m_pOnlineServicesManager->m_vecScreenshotThreads.push_back(pNewThread); } - ); - // Store the thread so we can join it during shutdown - if (m_pOnlineServicesManager != nullptr) - { - std::scoped_lock lock(m_pOnlineServicesManager->m_mutexScreenshotThreads); - m_pOnlineServicesManager->m_vecScreenshotThreads.push_back(pNewThread); + bSucceeded = true; } - - bSucceeded = true; } } }