diff --git a/Client/mods/deathmatch/logic/CSingularFileDownload.cpp b/Client/mods/deathmatch/logic/CSingularFileDownload.cpp index bb606d0be52..927de266aba 100644 --- a/Client/mods/deathmatch/logic/CSingularFileDownload.cpp +++ b/Client/mods/deathmatch/logic/CSingularFileDownload.cpp @@ -12,7 +12,7 @@ #include CSingularFileDownload::CSingularFileDownload(CResource* pResource, const char* szName, const char* szNameShort, SString strHTTPURL, CResource* pRequestResource, - CChecksum serverChecksum) + CChecksum serverChecksum, std::uint32_t handlerId) { // Store the name m_strName = szName; @@ -21,13 +21,17 @@ CSingularFileDownload::CSingularFileDownload(CResource* pResource, const char* s m_strNameShort = szNameShort; // store resources - m_pResource = pResource; - m_pRequestResource = pRequestResource; + resource = pResource; + requestResource = pRequestResource; // Store the server checksum m_ServerChecksum = serverChecksum; - m_bBeingDeleted = false; + beingDeleted = false; + cancelled = false; + handlerId = handlerId; + httpManager = nullptr; + downloadMode = EDownloadMode::NONE; GenerateClientChecksum(); @@ -35,9 +39,10 @@ CSingularFileDownload::CSingularFileDownload(CResource* pResource, const char* s { SHttpRequestOptions options; options.bCheckContents = true; - CNetHTTPDownloadManagerInterface* pHTTP = g_pCore->GetNetwork()->GetHTTPDownloadManager(EDownloadMode::RESOURCE_SINGULAR_FILES); - pHTTP->QueueFile(strHTTPURL.c_str(), szName, this, DownloadFinishedCallBack, options); - m_bComplete = false; + httpManager = g_pCore->GetNetwork()->GetHTTPDownloadManager(EDownloadMode::RESOURCE_SINGULAR_FILES); + downloadMode = EDownloadMode::RESOURCE_SINGULAR_FILES; + httpManager->QueueFile(strHTTPURL.c_str(), szName, this, DownloadFinishedCallBack, options); + complete = false; g_pClientGame->SetTransferringSingularFiles(true); } else @@ -61,33 +66,46 @@ void CSingularFileDownload::CallFinished(bool bSuccess) // Flag file as loadable g_pClientGame->GetResourceManager()->OnDownloadedResourceFile(GetName()); - if (!m_bBeingDeleted && m_pResource) + if (!beingDeleted && resource) { - // Call the onClientbFileDownloadComplete event + // Call the onClientFileDownloadComplete event CLuaArguments Arguments; Arguments.PushString(GetShortName()); // file name Arguments.PushBoolean(bSuccess); // Completed successfully? - if (m_pRequestResource) + if (requestResource) { - Arguments.PushResource(m_pRequestResource); // Resource that called downloadFile + Arguments.PushResource(requestResource); // Resource that called downloadFile } else { Arguments.PushBoolean(false); // or false } - m_pResource->GetResourceEntity()->CallEvent("onClientFileDownloadComplete", Arguments, false); + resource->GetResourceEntity()->CallEvent("onClientFileDownloadComplete", Arguments, false); } SetComplete(); } -void CSingularFileDownload::Cancel() +bool CSingularFileDownload::Cancel() { - m_bBeingDeleted = true; - m_pResource = NULL; - m_pRequestResource = NULL; + if (cancelled || complete) + return false; - // TODO: Cancel also in Net + cancelled = true; + beingDeleted = true; + resource = nullptr; + requestResource = nullptr; + + if (!httpManager || downloadMode == EDownloadMode::NONE) + return true; + + const bool httpCancelSuccess = httpManager->CancelDownload(this, DownloadFinishedCallBack); + return httpCancelSuccess; +} + +void CSingularFileDownload::MarkForDeletion() +{ + beingDeleted = true; } bool CSingularFileDownload::DoesClientAndServerChecksumMatch() diff --git a/Client/mods/deathmatch/logic/CSingularFileDownload.h b/Client/mods/deathmatch/logic/CSingularFileDownload.h index b3686c14d85..0bb8b0dce93 100644 --- a/Client/mods/deathmatch/logic/CSingularFileDownload.h +++ b/Client/mods/deathmatch/logic/CSingularFileDownload.h @@ -20,28 +20,33 @@ #include #include "CChecksum.h" +#include "net/CNetHTTPDownloadManagerInterface.h" +#include class CSingularFileDownload { public: CSingularFileDownload(CResource* pResource, const char* szName, const char* szNameShort, SString strHTTPURL, CResource* pRequestResource, - CChecksum serverChecksum); + CChecksum serverChecksum, std::uint32_t handlerId); ~CSingularFileDownload(); static void DownloadFinishedCallBack(const SHttpDownloadResult& result); bool DoesClientAndServerChecksumMatch(); - const char* GetName() { return m_strName; }; - const char* GetShortName() { return m_strNameShort; }; + const char* GetName() const noexcept { return m_strName; } + const char* GetShortName() const noexcept { return m_strNameShort; } - CResource* GetResource() { return m_pResource; }; + CResource* GetResource() const noexcept { return resource; } + std::uint32_t GetHandlerId() const noexcept { return handlerId; } - void SetComplete() { m_bComplete = true; }; - bool GetComplete() { return m_bComplete; }; + void SetComplete() noexcept { complete = true; } + bool GetComplete() const noexcept { return complete; } + bool IsCancelled() const noexcept { return cancelled; } void CallFinished(bool bSuccess); - void Cancel(); + bool Cancel(); + void MarkForDeletion(); CChecksum GenerateClientChecksum(); @@ -49,11 +54,16 @@ class CSingularFileDownload SString m_strName; SString m_strNameShort; - CResource* m_pResource; - CResource* m_pRequestResource; + CResource* resource; + CResource* requestResource; - bool m_bComplete; - bool m_bBeingDeleted; + bool complete; + bool beingDeleted; + bool cancelled; + + std::uint32_t handlerId; + CNetHTTPDownloadManagerInterface* httpManager; + EDownloadMode::EDownloadModeType downloadMode; CChecksum m_LastClientChecksum; CChecksum m_ServerChecksum; diff --git a/Client/mods/deathmatch/logic/CSingularFileDownloadManager.cpp b/Client/mods/deathmatch/logic/CSingularFileDownloadManager.cpp index 97be6ba0dd2..7cbfbdfa96b 100644 --- a/Client/mods/deathmatch/logic/CSingularFileDownloadManager.cpp +++ b/Client/mods/deathmatch/logic/CSingularFileDownloadManager.cpp @@ -12,7 +12,7 @@ #include using std::list; -CSingularFileDownloadManager::CSingularFileDownloadManager() +CSingularFileDownloadManager::CSingularFileDownloadManager() : m_nextHandlerId(1) { } @@ -25,38 +25,68 @@ CSingularFileDownloadManager::~CSingularFileDownloadManager() CSingularFileDownload* CSingularFileDownloadManager::AddFile(CResource* pResource, const char* szName, const char* szNameShort, SString strHTTPURL, CResource* pRequestResource, CChecksum checksum) { - CSingularFileDownload* pFile = new CSingularFileDownload(pResource, szName, szNameShort, strHTTPURL, pRequestResource, checksum); + const std::uint32_t handlerId = m_nextHandlerId++; + auto* pFile = new CSingularFileDownload(pResource, szName, szNameShort, strHTTPURL, pRequestResource, checksum, handlerId); m_Downloads.push_back(pFile); - return NULL; + m_HandlerMap[handlerId] = pFile; + return pFile; } void CSingularFileDownloadManager::CancelResourceDownloads(CResource* pResource) { - list::const_iterator iter = m_Downloads.begin(); - for (; iter != m_Downloads.end(); ++iter) + for (const auto& pDownload : m_Downloads) { - if ((*iter)->GetResource() == pResource) - (*iter)->Cancel(); + if (pDownload->GetResource() == pResource) + pDownload->Cancel(); } } void CSingularFileDownloadManager::ClearList() { - list::const_iterator iter = m_Downloads.begin(); - for (; iter != m_Downloads.end(); ++iter) + for (const auto& pDownload : m_Downloads) { - delete *iter; + delete pDownload; } m_Downloads.clear(); + m_HandlerMap.clear(); } bool CSingularFileDownloadManager::AllComplete() { - list::const_iterator iter = m_Downloads.begin(); - for (; iter != m_Downloads.end(); ++iter) + for (const auto& pDownload : m_Downloads) { - if (!(*iter)->GetComplete()) + if (!pDownload->GetComplete()) return false; } return true; +} + +CSingularFileDownload* CSingularFileDownloadManager::FindDownloadByHandler(std::uint32_t handlerId) const +{ + const auto it = m_HandlerMap.find(handlerId); + return (it != m_HandlerMap.end()) ? it->second : nullptr; +} + +bool CSingularFileDownloadManager::AbortDownload(std::uint32_t handlerId) +{ + auto* pDownload = FindDownloadByHandler(handlerId); + if (!pDownload) + return false; + + if (pDownload->GetComplete() || pDownload->IsCancelled()) + return false; + + const bool success = pDownload->Cancel(); + RemoveDownload(pDownload); + return success; +} + +void CSingularFileDownloadManager::RemoveDownload(CSingularFileDownload* pDownload) +{ + if (!pDownload) + return; + + m_HandlerMap.erase(pDownload->GetHandlerId()); + m_Downloads.remove(pDownload); + pDownload->MarkForDeletion(); } \ No newline at end of file diff --git a/Client/mods/deathmatch/logic/CSingularFileDownloadManager.h b/Client/mods/deathmatch/logic/CSingularFileDownloadManager.h index 9b861b5dce8..c75bdf78c2a 100644 --- a/Client/mods/deathmatch/logic/CSingularFileDownloadManager.h +++ b/Client/mods/deathmatch/logic/CSingularFileDownloadManager.h @@ -21,6 +21,8 @@ #include #include "CChecksum.h" #include "CSingularFileDownload.h" +#include +#include class CSingularFileDownloadManager { @@ -36,6 +38,12 @@ class CSingularFileDownloadManager bool AllComplete(); + CSingularFileDownload* FindDownloadByHandler(std::uint32_t handlerId) const; + bool AbortDownload(std::uint32_t handlerId); + void RemoveDownload(CSingularFileDownload* pDownload); + protected: std::list m_Downloads; + std::map m_HandlerMap; + std::uint32_t m_nextHandlerId; }; diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 18430ed7f92..e3007a856f4 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -233,7 +233,7 @@ bool CStaticFunctionDefinitions::WasEventCancelled() return m_pEvents->WasEventCancelled(); } -bool CStaticFunctionDefinitions::DownloadFile(CResource* pResource, const char* szFile, CResource* pRequestResource, CChecksum checksum) +CSingularFileDownload* CStaticFunctionDefinitions::DownloadFile(CResource* pResource, const char* szFile, CResource* pRequestResource, CChecksum checksum) { SString strHTTPDownloadURLFull("%s/%s/%s", g_pClientGame->GetHTTPURL().c_str(), pResource->GetName(), szFile); SString strPath("%s\\resources\\%s\\%s", g_pClientGame->GetFileCacheRoot(), pResource->GetName(), szFile); @@ -241,10 +241,9 @@ bool CStaticFunctionDefinitions::DownloadFile(CResource* pResource, const char* // Call SingularFileDownloadManager if (g_pClientGame->GetSingularFileDownloadManager()) { - g_pClientGame->GetSingularFileDownloadManager()->AddFile(pResource, strPath.c_str(), szFile, strHTTPDownloadURLFull, pRequestResource, checksum); - return true; + return g_pClientGame->GetSingularFileDownloadManager()->AddFile(pResource, strPath.c_str(), szFile, strHTTPDownloadURLFull, pRequestResource, checksum); } - return false; + return nullptr; } bool CStaticFunctionDefinitions::OutputConsole(const char* szText) diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h index 75999cf0882..f3f0b7ea816 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h @@ -20,6 +20,9 @@ class CStaticFunctionDefinitions; #include "enums/WeaponProperty.h" #include "enums/ObjectProperty.h" +// Forward declarations +class CSingularFileDownload; + class CStaticFunctionDefinitions { public: @@ -39,7 +42,7 @@ class CStaticFunctionDefinitions static bool WasEventCancelled(); // Misc funcs - static bool DownloadFile(CResource* pResource, const char* szFile, CResource* pRequestResource, CChecksum checksum = CChecksum()); + static CSingularFileDownload* DownloadFile(CResource* pResource, const char* szFile, CResource* pRequestResource, CChecksum checksum = CChecksum()); // Output funcs static bool OutputConsole(const char* szText); diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionDefs.Util.cpp b/Client/mods/deathmatch/logic/lua/CLuaFunctionDefs.Util.cpp index 2df7db7cea2..daea5696762 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionDefs.Util.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionDefs.Util.cpp @@ -372,6 +372,14 @@ int CLuaFunctionDefs::DownloadFile(lua_State* luaVM) if (!argStream.HasErrors()) { + // Validate that the file input is not empty + if (strFileInput.empty()) + { + m_pScriptDebugging->LogCustom(luaVM, "Expected non-empty string, got empty string"); + lua_pushboolean(luaVM, false); + return 1; + } + // Grab our VM CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM); if (pLuaMain) @@ -390,10 +398,12 @@ int CLuaFunctionDefs::DownloadFile(lua_State* luaVM) { if (strcmp(strMetaPath, (*iter)->GetShortName()) == 0) { - if (CStaticFunctionDefinitions::DownloadFile(pOtherResource, strMetaPath, pThisResource, (*iter)->GetServerChecksum())) + CSingularFileDownload* pDownload = CStaticFunctionDefinitions::DownloadFile(pOtherResource, strMetaPath, pThisResource, (*iter)->GetServerChecksum()); + if (pDownload) { - lua_pushboolean(luaVM, true); - return 1; + lua_pushuserdata(luaVM, pDownload); + lua_pushnumber(luaVM, pDownload->GetHandlerId()); + return 2; } } } @@ -409,6 +419,48 @@ int CLuaFunctionDefs::DownloadFile(lua_State* luaVM) return 1; } +int CLuaFunctionDefs::AbortDownload(lua_State* luaVM) +{ + // bool abortDownload(number handlerId) + std::uint32_t handlerId = 0; + CScriptArgReader argStream(luaVM); + + // Check if argument is a number first + if (!argStream.NextIsNumber()) + { + m_pScriptDebugging->LogCustom(luaVM, SString("Expected number, got %s", lua_typename(luaVM, lua_type(luaVM, 1)))); + lua_pushboolean(luaVM, false); + return 1; + } + + argStream.ReadNumber(handlerId); + + if (!argStream.HasErrors()) + { + // Validate that handlerId is positive + if (handlerId <= 0) + { + m_pScriptDebugging->LogCustom(luaVM, SString("Expected positive value, got %d", handlerId)); + lua_pushboolean(luaVM, false); + return 1; + } + + if (g_pClientGame->GetSingularFileDownloadManager()) + { + const bool success = g_pClientGame->GetSingularFileDownloadManager()->AbortDownload(handlerId); + lua_pushboolean(luaVM, success); + return 1; + } + } + else + { + m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + } + + lua_pushboolean(luaVM, false); + return 1; +} + int CLuaFunctionDefs::AddDebugHook(lua_State* luaVM) { // bool AddDebugHook ( string hookType, function callback[, table allowedNames ] ) diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionDefs.h b/Client/mods/deathmatch/logic/lua/CLuaFunctionDefs.h index a6592f9df04..364ab06e335 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionDefs.h +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionDefs.h @@ -43,6 +43,7 @@ class CLuaFunctionDefs // Misc functions LUA_DECLARE(DownloadFile); + LUA_DECLARE(AbortDownload); // Output functions LUA_DECLARE(OutputConsole); diff --git a/Client/mods/deathmatch/logic/lua/CLuaManager.cpp b/Client/mods/deathmatch/logic/lua/CLuaManager.cpp index 89dadab3325..38adfc5a2b9 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaManager.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaManager.cpp @@ -199,6 +199,7 @@ void CLuaManager::LoadCFunctions() // Util functions {"getValidPedModels", CLuaFunctionDefs::GetValidPedModels}, {"downloadFile", CLuaFunctionDefs::DownloadFile}, + {"abortDownload", CLuaFunctionDefs::AbortDownload}, // Input functions {"bindKey", CLuaFunctionDefs::BindKey},