From 3e488465d9346a137891fb5f38683c5f9b47d27a Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Thu, 29 Feb 2024 19:24:24 +0300 Subject: [PATCH 01/24] Remove/restore sa world --- Client/game_sa/CBuildingsPoolSA.cpp | 169 ++++++++++++++++++ Client/game_sa/CBuildingsPoolSA.h | 38 ++++ Client/game_sa/CGameSA.cpp | 20 +++ Client/game_sa/CGameSA.h | 5 + Client/game_sa/CIplSA.h | 37 ++++ Client/game_sa/CIplStoreSA.cpp | 64 +++++++ Client/game_sa/CIplStoreSA.h | 33 ++++ Client/game_sa/CPoolSAInterface.h | 151 ++++++++++++++++ Client/game_sa/CPoolsSA.cpp | 114 ------------ Client/game_sa/CPoolsSA.h | 151 +--------------- Client/game_sa/CPtrNodeSingleListSA.h | 45 +++++ Client/game_sa/CQuadTreeNodeSA.h | 56 ++++++ .../mods/deathmatch/logic/CClientBuilding.cpp | 4 +- .../logic/luadefs/CLuaBuildingDefs.cpp | 14 +- .../logic/luadefs/CLuaBuildingDefs.h | 2 + Client/sdk/game/CBuildingsPool.h | 31 ++++ Client/sdk/game/CGame.h | 4 + Client/sdk/game/CIplStore.h | 18 ++ Client/sdk/game/CPools.h | 10 +- Client/sdk/game/CWorld.h | 1 + 20 files changed, 699 insertions(+), 268 deletions(-) create mode 100644 Client/game_sa/CBuildingsPoolSA.cpp create mode 100644 Client/game_sa/CBuildingsPoolSA.h create mode 100644 Client/game_sa/CIplSA.h create mode 100644 Client/game_sa/CIplStoreSA.cpp create mode 100644 Client/game_sa/CIplStoreSA.h create mode 100644 Client/game_sa/CPoolSAInterface.h create mode 100644 Client/game_sa/CPtrNodeSingleListSA.h create mode 100644 Client/game_sa/CQuadTreeNodeSA.h create mode 100644 Client/sdk/game/CBuildingsPool.h create mode 100644 Client/sdk/game/CIplStore.h diff --git a/Client/game_sa/CBuildingsPoolSA.cpp b/Client/game_sa/CBuildingsPoolSA.cpp new file mode 100644 index 0000000000..0b257a141b --- /dev/null +++ b/Client/game_sa/CBuildingsPoolSA.cpp @@ -0,0 +1,169 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CBuildingsPoolSA.cpp + * PURPOSE: Buildings pool class + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#include "CBuildingsPoolSA.h" + +#include "CFileLoaderSA.h" +#include +#include "CGameSA.h" + +extern CGameSA* pGame; + +class CClientEntity; + +CBuildingsPoolSA::CBuildingsPoolSA() : m_pOriginalBuildingsBackup(nullptr) +{ + m_ppBuildingPoolInterface = (CPoolSAInterface**)0xB74498; +} + +inline bool CBuildingsPoolSA::AddBuildingToPool(CClientBuilding* pClientBuilding, CBuildingSA* pBuilding) +{ + // Grab the new object interface + CBuildingSAInterface* pInterface = pBuilding->GetBuildingInterface(); + + if (!pInterface) + return false; + + uint32_t dwElementIndexInPool = (*m_ppBuildingPoolInterface)->GetObjectIndexSafe(pInterface); + if (dwElementIndexInPool >= -1) + return false; + + m_buildingPool.arrayOfClientEntities[dwElementIndexInPool] = {pBuilding, (CClientEntity*)pClientBuilding}; + + // Increase the count of objects + ++m_buildingPool.ulCount; + + return true; +} + +CBuilding* CBuildingsPoolSA::AddBuilding(CClientBuilding* pClientBuilding, uint16_t modelId, CVector* vPos, CVector4D* vRot, uint8_t interior) +{ + if (!HasFreeBuildingSlot()) + return nullptr; + + // Load building + SFileObjectInstance instance; + instance.modelID = modelId; + instance.lod = -1; + instance.interiorID = interior; + instance.position = *vPos; + instance.rotation = *vRot; + + // Fix strange SA rotation + instance.rotation.fW = -instance.rotation.fW; + + auto pBuilding = static_cast(CFileLoaderSA::LoadObjectInstance(&instance)); + + // Disable lod and ipl + pBuilding->m_pLod = nullptr; + pBuilding->m_iplIndex = 0; + + // Always stream model collosion + // TODO We can setup collison bounding box and use GTA streamer for it + auto modelInfo = pGame->GetModelInfo(modelId); + modelInfo->AddColRef(); + + // Add building in world + auto pBuildingSA = new CBuildingSA(pBuilding); + pGame->GetWorld()->Add(pBuildingSA, CBuildingPool_Constructor); + + // Add CBuildingSA object in pool + AddBuildingToPool(pClientBuilding, pBuildingSA); + + return pBuildingSA; +} + +void CBuildingsPoolSA::RemoveBuilding(CBuilding* pBuilding) +{ + assert(NULL != pBuilding); + + CBuildingSAInterface* pInterface = pBuilding->GetBuildingInterface(); + + uint32_t dwElementIndexInPool = (*m_ppBuildingPoolInterface)->GetObjectIndexSafe(pInterface); + if (dwElementIndexInPool == -1) + return; + + // Remove building from world + pGame->GetWorld()->Remove(pInterface, CBuildingPool_Destructor); + + // Remove col reference + auto modelInfo = pGame->GetModelInfo(pBuilding->GetModelIndex()); + modelInfo->RemoveColRef(); + + // Remove from BuildingSA pool + auto* pBuildingSA = m_buildingPool.arrayOfClientEntities[dwElementIndexInPool].pEntity; + m_buildingPool.arrayOfClientEntities[dwElementIndexInPool] = {nullptr, nullptr}; + + // Delete it from memory + delete pBuildingSA; + + // Remove building from SA pool + (*m_ppBuildingPoolInterface)->Release(dwElementIndexInPool); + + // Decrease the count of elements in the pool + --m_buildingPool.ulCount; +} + +void CBuildingsPoolSA::RemoveAllBuildings() +{ + if (m_pOriginalBuildingsBackup) + return; + + m_pOriginalBuildingsBackup = std::make_unique, MAX_BUILDINGS>>(); + + auto pBuildsingsPool = (*m_ppBuildingPoolInterface); + for (size_t i = 0; i < MAX_BUILDINGS; i++) + { + if (pBuildsingsPool->IsContains(i)) + { + auto building = pBuildsingsPool->GetObject(i); + + pGame->GetWorld()->Remove(building, CBuildingPool_Destructor); + + pBuildsingsPool->Release(i); + + (*m_pOriginalBuildingsBackup)[i].first = true; + (*m_pOriginalBuildingsBackup)[i].second = *building; + } + else + { + (*m_pOriginalBuildingsBackup)[i].first = false; + } + } +} + +void CBuildingsPoolSA::RestoreAllBuildings() +{ + if (!m_pOriginalBuildingsBackup) + return; + + auto& originalData = *m_pOriginalBuildingsBackup; + auto pBuildsingsPool = (*m_ppBuildingPoolInterface); + for (size_t i = 0; i < MAX_BUILDINGS; i++) + { + if (originalData[i].first) + { + pBuildsingsPool->AllocateAt(i); + auto building = pBuildsingsPool->GetObject(i); + *building = originalData[i].second; + + pGame->GetWorld()->Add(building, CBuildingPool_Constructor); + } + } + + m_pOriginalBuildingsBackup.release(); +} + +bool CBuildingsPoolSA::HasFreeBuildingSlot() +{ + return (*m_ppBuildingPoolInterface)->GetFreeSlot() != -1; +} diff --git a/Client/game_sa/CBuildingsPoolSA.h b/Client/game_sa/CBuildingsPoolSA.h new file mode 100644 index 0000000000..25854e3b2e --- /dev/null +++ b/Client/game_sa/CBuildingsPoolSA.h @@ -0,0 +1,38 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CBuildingsPoolSA.h + * PURPOSE: Buildings pool class + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include +#include +#include "CPoolSAInterface.h" +#include "CBuildingSA.h" + +class CBuildingsPoolSA : public CBuildingsPool +{ +public: + CBuildingsPoolSA(); + ~CBuildingsPoolSA() = default; + + CBuilding* AddBuilding(CClientBuilding*, uint16_t modelId, CVector* vPos, CVector4D* vRot, uint8_t interior); + void RemoveBuilding(CBuilding* pBuilding); + bool HasFreeBuildingSlot(); + + void RemoveAllBuildings(); + void RestoreAllBuildings(); + +private: + bool AddBuildingToPool(CClientBuilding* pClientBuilding, CBuildingSA* pBuilding); + +private: + SPoolData m_buildingPool; + CPoolSAInterface** m_ppBuildingPoolInterface; + + std::unique_ptr, MAX_BUILDINGS>> m_pOriginalBuildingsBackup; +}; diff --git a/Client/game_sa/CGameSA.cpp b/Client/game_sa/CGameSA.cpp index 51fc34e1ba..2a496ca361 100644 --- a/Client/game_sa/CGameSA.cpp +++ b/Client/game_sa/CGameSA.cpp @@ -56,6 +56,7 @@ #include "CWeatherSA.h" #include "CWorldSA.h" #include "D3DResourceSystemSA.h" +#include "CIplStoreSA.h" extern CGameSA* pGame; @@ -137,6 +138,7 @@ CGameSA::CGameSA() m_pWeaponStatsManager = new CWeaponStatManagerSA(); m_pPointLights = new CPointLightsSA(); m_collisionStore = new CColStoreSA(); + m_pIplStore = new CIplStoreSA(); // Normal weapon types (WEAPONSKILL_STD) for (int i = 0; i < NUM_WeaponInfosStdSkill; i++) @@ -273,6 +275,7 @@ CGameSA::~CGameSA() delete reinterpret_cast(m_pAudioContainer); delete reinterpret_cast(m_pPointLights); delete static_cast(m_collisionStore); + delete static_cast(m_pIplStore); delete[] ModelInfo; delete[] ObjectGroupsInfo; @@ -425,6 +428,9 @@ void CGameSA::Reset() // Restore changed TXD IDs CModelInfoSA::StaticResetTextureDictionaries(); + + // Restore default world state + RestoreGameBuildings(); } } @@ -882,6 +888,20 @@ void CGameSA::GetShaderReplacementStats(SShaderReplacementStats& outStats) m_pRenderWare->GetShaderReplacementStats(outStats); } +void CGameSA::RemoveAllBuildings() +{ + m_pIplStore->SetDynamicIplStreamingEnabled(false); + + m_pPools->GetBuildingsPool().RemoveAllBuildings(); +} + +void CGameSA::RestoreGameBuildings() +{ + m_pPools->GetBuildingsPool().RestoreAllBuildings(); + + m_pIplStore->SetDynamicIplStreamingEnabled(true); +} + // Ensure models have the default lod distances void CGameSA::ResetModelLodDistances() { diff --git a/Client/game_sa/CGameSA.h b/Client/game_sa/CGameSA.h index e7aa9c3cfd..5a2be9f3ae 100644 --- a/Client/game_sa/CGameSA.h +++ b/Client/game_sa/CGameSA.h @@ -152,6 +152,7 @@ class CGameSA : public CGame CColStore* GetCollisionStore() override { return m_collisionStore; } CRenderWareSA* GetRenderWareSA() { return m_pRenderWare; } CFxManagerSA* GetFxManagerSA() { return m_pFxManager; } + CIplStore* GetIplStore() { return m_pIplStore; }; CWeaponInfo* GetWeaponInfo(eWeaponType weapon, eWeaponSkill skill = WEAPONSKILL_STD); CModelInfo* GetModelInfo(DWORD dwModelID, bool bCanBeInvalid = false); @@ -273,6 +274,9 @@ class CGameSA : public CGame PostWeaponFireHandler* m_pPostWeaponFireHandler; TaskSimpleBeHitHandler* m_pTaskSimpleBeHitHandler; + void RemoveAllBuildings(); + void RestoreGameBuildings(); + private: CPools* m_pPools; CPlayerInfo* m_pPlayerInfo; @@ -320,6 +324,7 @@ class CGameSA : public CGame CGameSettings* m_pSettings; CCarEnterExit* m_pCarEnterExit; CControllerConfigManager* m_pControllerConfigManager; + CIplStore* m_pIplStore; eGameVersion m_eGameVersion; bool m_bAsyncScriptEnabled; diff --git a/Client/game_sa/CIplSA.h b/Client/game_sa/CIplSA.h new file mode 100644 index 0000000000..e230d1caab --- /dev/null +++ b/Client/game_sa/CIplSA.h @@ -0,0 +1,37 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CIplSA.h + * PURPOSE: Header file for game IPL class + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include +#include "CRect.h" + +class CIplSAInterface +{ +public: + CRect rect; + char name[16]; + uint16_t unk; + uint16_t minBuildId; + uint16_t maxBuildId; + uint16_t minBummyId; + uint16_t maxDummyId; + uint16_t relatedIpl; + uint8_t interior; + uint8_t unk2; + uint8_t bLoadReq; + uint8_t bDisabledStreaming; + uint8_t unk3; + uint8_t unk4; + uint8_t unk5; + uint8_t unk6; +}; +static_assert(sizeof(CIplSAInterface) == 0x34, "Wrong CIplSAInterface size"); diff --git a/Client/game_sa/CIplStoreSA.cpp b/Client/game_sa/CIplStoreSA.cpp new file mode 100644 index 0000000000..87abc1dd9c --- /dev/null +++ b/Client/game_sa/CIplStoreSA.cpp @@ -0,0 +1,64 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CIplStore.cpp + * PURPOSE: IPL store class + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" + +#include "CIplStoreSA.h" +#include "CQuadTreeNodeSA.h" + +static auto gIplQuadTree = (CQuadTreeNodesSAInterface**)0x8E3FAC; + +CIplStoreSA::CIplStoreSA(): m_isStreamingEnbabled(true), m_ppIplPoolInterface((CPoolSAInterface**)0x8E3FB0) +{ +} + +void CIplStoreSA::UnloadAndDisableStreaming(int iplId) +{ + typedef void*(__cdecl * Function_EnableStreaming)(int); + ((Function_EnableStreaming)(0x405890))(iplId); +} + +void CIplStoreSA::SetDynamicIplStreamingEnabled(bool state) +{ + if (m_isStreamingEnbabled == state) + return; + + // Ipl with 0 index is generic + // We don't unload this IPL + + auto pPool = *m_ppIplPoolInterface; + if (!state) + { + for (int i = 1; i < pPool->m_nSize; i++) + { + if (pPool->IsContains(i)) + { + UnloadAndDisableStreaming(i); + } + } + (*gIplQuadTree)->RemoveAllItems(); + } + else + { + for (int i = 1; i < pPool->m_nSize; i++) + { + if (pPool->IsContains(i)) + { + auto ipl = pPool->GetObject(i); + ipl->bDisabledStreaming = false; + + (*gIplQuadTree)->AddItem(ipl, &ipl->rect); + } + } + } + + m_isStreamingEnbabled = state; +} diff --git a/Client/game_sa/CIplStoreSA.h b/Client/game_sa/CIplStoreSA.h new file mode 100644 index 0000000000..220dc563c5 --- /dev/null +++ b/Client/game_sa/CIplStoreSA.h @@ -0,0 +1,33 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CIplStore.h + * PURPOSE: IPL store class + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include "CIplSA.h" +#include "CPoolsSA.h" +#include + +class CIplStoreSA : public CIplStore +{ +public: + CIplStoreSA(); + ~CIplStoreSA() = default; + + void SetDynamicIplStreamingEnabled(bool state); + +private: + void UnloadAndDisableStreaming(int iplId); + +private: + CPoolSAInterface** m_ppIplPoolInterface; + + bool m_isStreamingEnbabled; +}; diff --git a/Client/game_sa/CPoolSAInterface.h b/Client/game_sa/CPoolSAInterface.h new file mode 100644 index 0000000000..285b4dda5f --- /dev/null +++ b/Client/game_sa/CPoolSAInterface.h @@ -0,0 +1,151 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CPoolsSA.h + * PURPOSE: Header file for pools interface + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include + +// size of tPoolObjectFlags is 1 byte only +union tPoolObjectFlags +{ + struct + { + unsigned char nId : 7; + bool bEmpty : 1; + }; + +private: + unsigned char nValue; +}; + +template +class CPoolSAInterface +{ +public: + // m_pObjects contains all interfaces. 140 maximum for ped objects. + B* m_pObjects; + tPoolObjectFlags* m_byteMap; + int m_nSize; + int m_nFirstFree; + bool m_bOwnsAllocations; + bool field_11; + + // Default constructor for statically allocated pools + CPoolSAInterface() + { + // Remember to call CPool::Init to fill in the fields! + m_pObjects = nullptr; + m_byteMap = nullptr; + m_nSize = 0; + m_bOwnsAllocations = false; + } + + uint GetFreeSlot() + { + bool bLooped = false; + uint index = m_nFirstFree + 1; + + while (true) + { + if (index >= m_nSize) + { + if (bLooped) + return -1; + + index = 0; + bLooped = true; + } + + if (m_byteMap[index].bEmpty) + { + m_nFirstFree = index; + return index; + } + index++; + } + + return -1; + }; + + B* Allocate() + { + m_nFirstFree++; // Continue after the last allocated slot + const auto sz = m_nSize; // Storing size to avoid reloads from memory - should help out the optimizer + for (auto i = 0u; i < sz; i++) + { + const auto slot = (m_nFirstFree + i) % sz; + const auto e = &m_byteMap[slot]; + if (!e->bEmpty) + { + continue; + } + m_nFirstFree = slot; + e->bEmpty = false; + e->nId++; + return &m_pObjects[slot]; + } + return nullptr; + } + + B* AllocateAt(uint uiSlot) + { + m_pObjects[uiSlot] = B(); + m_byteMap[uiSlot].bEmpty = false; + m_byteMap[uiSlot].nId ^= uiSlot ^ (uiSlot + 1); + + return &m_pObjects[uiSlot]; + } + + void Release(uint index) + { + m_byteMap[index].bEmpty = true; + m_byteMap[index].nId = 0; + if (index == m_nFirstFree) + --m_nFirstFree; + } + + void Delete(uint index) { Release(index); } + + bool IsEmpty(std::int32_t objectIndex) { return m_byteMap[objectIndex].bEmpty; } + bool IsContains(uint index) + { + if (m_nSize <= index) + return false; + return !IsEmpty(index); + } + + B* GetObject(std::int32_t objectIndex) { return &m_pObjects[objectIndex]; } + + uint GetObjectIndex(B* pObject) { return ((DWORD)pObject - (DWORD)m_pObjects) / sizeof(B); } + + uint32_t GetObjectIndexSafe(B* pObject) + { + uint32_t index = GetObjectIndex(pObject); + return index > m_nSize ? -1 : index; + } +}; + +// Generic container for pools +template +struct SPoolData +{ + std::array, MAX> arrayOfClientEntities; + unsigned long ulCount; + +public: + SPoolData() : ulCount(0UL) + { + for (unsigned int i = 0; i < MAX; ++i) + { + arrayOfClientEntities[i] = {nullptr, nullptr}; + } + } +}; diff --git a/Client/game_sa/CPoolsSA.cpp b/Client/game_sa/CPoolsSA.cpp index 6680e82194..1baa2f7ecb 100644 --- a/Client/game_sa/CPoolsSA.cpp +++ b/Client/game_sa/CPoolsSA.cpp @@ -35,7 +35,6 @@ CPoolsSA::CPoolsSA() m_ppObjectPoolInterface = (CPoolSAInterface**)0xB7449C; m_ppVehiclePoolInterface = (CPoolSAInterface**)0xB74494; m_ppTxdPoolInterface = (CPoolSAInterface**)0xC8800C; - m_ppBuildingPoolInterface = (CPoolSAInterface**)0xB74498; m_bGetVehicleEnabled = true; } @@ -332,107 +331,6 @@ void CPoolsSA::DeleteAllObjects() } } -////////////////////////////////////////////////////////////////////////////////////////// -// BUILDINGS POOL // -////////////////////////////////////////////////////////////////////////////////////////// - -inline bool CPoolsSA::AddBuildingToPool(CClientBuilding* pClientBuilding, CBuildingSA* pBuilding) -{ - // Grab the new object interface - CBuildingSAInterface* pInterface = pBuilding->GetBuildingInterface(); - - if (!pInterface) - return false; - - DWORD dwElementIndexInPool = GetBuildingPoolIndex((std::uint8_t*)pInterface); - if (dwElementIndexInPool >= MAX_BUILDINGS) - { - return false; - } - - m_buildingPool.arrayOfClientEntities[dwElementIndexInPool] = {pBuilding, (CClientEntity*)pClientBuilding}; - - // Increase the count of objects - ++m_buildingPool.ulCount; - - return true; -} - -CBuilding* CPoolsSA::AddBuilding(CClientBuilding* pClientBuilding, uint16_t modelId, CVector *vPos, CVector4D *vRot, uint8_t interior) -{ - if (!HasFreeBuildingSlot()) - return nullptr; - - // Load building - SFileObjectInstance instance; - instance.modelID = modelId; - instance.lod = -1; - instance.interiorID = interior; - instance.position = *vPos; - instance.rotation = *vRot; - - // Fix strange SA rotation - instance.rotation.fW = -instance.rotation.fW; - - auto pBuilding = static_cast(CFileLoaderSA::LoadObjectInstance(&instance)); - - // Disable lod and ipl - pBuilding->m_pLod = nullptr; - pBuilding->m_iplIndex = 0; - - // Always stream model collosion - // TODO We can setup collison bounding box and use GTA streamer for it - auto modelInfo = pGame->GetModelInfo(modelId); - modelInfo->AddColRef(); - - // Add building in world - auto pBuildingSA = new CBuildingSA(pBuilding); - pGame->GetWorld()->Add(pBuildingSA, CBuildingPool_Constructor); - - // Add CBuildingSA object in pool - AddBuildingToPool(pClientBuilding, pBuildingSA); - - return pBuildingSA; -} - -void CPoolsSA::RemoveBuilding(CBuilding* pBuilding) -{ - assert(NULL != pBuilding); - - CBuildingSAInterface* pInterface = pBuilding->GetBuildingInterface(); - - DWORD dwElementIndexInPool = GetBuildingPoolIndex((std::uint8_t*)pInterface); - if (dwElementIndexInPool >= MAX_BUILDINGS) - { - return; - } - - // Remove building from world - pGame->GetWorld()->Remove(pInterface, CBuildingPool_Destructor); - - // Remove col reference - auto modelInfo = pGame->GetModelInfo(pBuilding->GetModelIndex()); - modelInfo->RemoveColRef(); - - // Remove from BuildingSA pool - auto* pBuildingSA = m_buildingPool.arrayOfClientEntities[dwElementIndexInPool].pEntity; - m_buildingPool.arrayOfClientEntities[dwElementIndexInPool] = {nullptr, nullptr}; - - // Delete it from memory - delete pBuildingSA; - - // Remove building from SA pool - (*m_ppBuildingPoolInterface)->Release(dwElementIndexInPool); - - // Decrease the count of elements in the pool - --m_buildingPool.ulCount; -} - -bool CPoolsSA::HasFreeBuildingSlot() -{ - return (*m_ppBuildingPoolInterface)->GetFreeSlot() != -1; -} - ////////////////////////////////////////////////////////////////////////////////////////// // PEDS POOL // ////////////////////////////////////////////////////////////////////////////////////////// @@ -807,18 +705,6 @@ DWORD CPoolsSA::GetObjectPoolIndex(std::uint8_t* pInterface) return ((pInterface - pTheObjects) / dwAlignedSize); } -DWORD CPoolsSA::GetBuildingPoolIndex(std::uint8_t* pInterface) -{ - DWORD dwAlignedSize = sizeof(CBuildingSAInterface); - std::uint8_t* pTheObjects = (std::uint8_t*)(*m_ppBuildingPoolInterface)->m_pObjects; - DWORD dwMaxIndex = MAX_BUILDINGS - 1; - if (pInterface < pTheObjects || pInterface > pTheObjects + (dwMaxIndex * dwAlignedSize)) - { - return MAX_BUILDINGS; - } - return ((pInterface - pTheObjects) / dwAlignedSize); -} - uint CPoolsSA::GetModelIdFromClump(RpClump* pRpClump) { // Search our pools for a match diff --git a/Client/game_sa/CPoolsSA.h b/Client/game_sa/CPoolsSA.h index 106846b162..f3b09a4101 100644 --- a/Client/game_sa/CPoolsSA.h +++ b/Client/game_sa/CPoolsSA.h @@ -16,126 +16,12 @@ #include "CObjectSA.h" #include "CBuildingSA.h" #include "CTextureDictonarySA.h" +#include "CBuildingsPoolSA.h" #define INVALID_POOL_ARRAY_ID 0xFFFFFFFF class CClientEntity; -// size of tPoolObjectFlags is 1 byte only -union tPoolObjectFlags -{ - struct - { - unsigned char nId : 7; - bool bEmpty : 1; - }; - -private: - unsigned char nValue; -}; - -template -class CPoolSAInterface -{ -public: - // m_pObjects contains all interfaces. 140 maximum for ped objects. - B* m_pObjects; - tPoolObjectFlags* m_byteMap; - int m_nSize; - int m_nFirstFree; - bool m_bOwnsAllocations; - bool field_11; - - // Default constructor for statically allocated pools - CPoolSAInterface() - { - // Remember to call CPool::Init to fill in the fields! - m_pObjects = nullptr; - m_byteMap = nullptr; - m_nSize = 0; - m_bOwnsAllocations = false; - } - - uint GetFreeSlot() - { - bool bLooped = false; - uint index = m_nFirstFree + 1; - - while (true) - { - if (index >= m_nSize) - { - if (bLooped) - return -1; - - index = 0; - bLooped = true; - } - - if (m_byteMap[index].bEmpty) - { - m_nFirstFree = index; - return index; - } - index++; - } - - return -1; - }; - - B* Allocate() - { - m_nFirstFree++; // Continue after the last allocated slot - const auto sz = m_nSize; // Storing size to avoid reloads from memory - should help out the optimizer - for (auto i = 0u; i < sz; i++) { - const auto slot = (m_nFirstFree + i) % sz; - const auto e = &m_byteMap[slot]; - if (!e->bEmpty) { - continue; - } - m_nFirstFree = slot; - e->bEmpty = false; - e->nId++; - return &m_pObjects[slot]; - } - return nullptr; - } - - B* AllocateAt(uint uiSlot) - { - m_pObjects[uiSlot] = B(); - m_byteMap[uiSlot].bEmpty = false; - m_byteMap[uiSlot].nId ^= uiSlot ^ (uiSlot + 1); - - return &m_pObjects[uiSlot]; - } - - void Release(uint index) - { - m_byteMap[index].bEmpty = true; - m_byteMap[index].nId = 0; - if (index == m_nFirstFree) - --m_nFirstFree; - } - - void Delete(uint index) - { - Release(index); - } - - bool IsEmpty(std::int32_t objectIndex) { return m_byteMap[objectIndex].bEmpty; } - bool IsContains(uint index) - { - if (m_nSize <= index) - return false; - return !IsEmpty(index); - } - - B* GetObject(std::int32_t objectIndex) { return &m_pObjects[objectIndex]; } - - uint GetObjectIndex(B* pObject) { return ((DWORD)pObject - (DWORD)m_pObjects) / sizeof(B); } -}; - class CPoolsSA : public CPools { public: @@ -171,15 +57,6 @@ class CPoolsSA : public CPools unsigned long GetObjectCount() { return m_objectPool.ulCount; } void DeleteAllObjects(); - // Buildings pool -private: - bool AddBuildingToPool(CClientBuilding* pClientBuilding, CBuildingSA* pBuilding); - -public: - CBuilding* AddBuilding(CClientBuilding*, uint16_t modelId, CVector *vPos, CVector4D *vRot, uint8_t interior); - void RemoveBuilding(CBuilding* pBuilding); - bool HasFreeBuildingSlot(); - // Peds pool CPed* AddPed(CClientPed* pClientPed, unsigned int nModelIndex); CPed* AddPed(CClientPed* pClientPed, DWORD* pGameInterface); @@ -205,7 +82,6 @@ class CPoolsSA : public CPools DWORD GetPedPoolIndex(std::uint8_t* pInterface); DWORD GetVehiclePoolIndex(std::uint8_t* pInterfacee); DWORD GetObjectPoolIndex(std::uint8_t* pInterface); - DWORD GetBuildingPoolIndex(std::uint8_t* pInterface); int GetNumberOfUsedSpaces(ePools pools); int GetPoolDefaultCapacity(ePools pool); @@ -221,37 +97,20 @@ class CPoolsSA : public CPools ushort GetFreeTextureDictonarySlot(); -private: - // Generic container for pools - template - struct SPoolData - { - std::array, MAX> arrayOfClientEntities; - unsigned long ulCount; - - private: - friend class CPoolsSA; - - SPoolData() : ulCount(0UL) - { - for (unsigned int i = 0; i < MAX; ++i) - { - arrayOfClientEntities[i] = {nullptr, nullptr}; - } - } - }; + CBuildingsPool& GetBuildingsPool() noexcept override { return m_BuildingsPool; }; +private: // Pools SPoolData m_vehiclePool; SPoolData m_pedPool; SPoolData m_objectPool; - SPoolData m_buildingPool; CPoolSAInterface** m_ppPedPoolInterface; CPoolSAInterface** m_ppObjectPoolInterface; CPoolSAInterface** m_ppVehiclePoolInterface; CPoolSAInterface** m_ppTxdPoolInterface; - CPoolSAInterface** m_ppBuildingPoolInterface; + + CBuildingsPoolSA m_BuildingsPool; bool m_bGetVehicleEnabled; }; diff --git a/Client/game_sa/CPtrNodeSingleListSA.h b/Client/game_sa/CPtrNodeSingleListSA.h new file mode 100644 index 0000000000..67dfe72669 --- /dev/null +++ b/Client/game_sa/CPtrNodeSingleListSA.h @@ -0,0 +1,45 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CPtrNodeSingleListSA.cpp + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +template +struct CPtrNodeSingleLink +{ + T* pItem; + CPtrNodeSingleLink* pNext; +}; + +template +class CPtrNodeSingleListSAInterface +{ +public: + void RemoveItem(T* item); + void RemoveAllItems(); + +private: + CPtrNodeSingleLink* m_pList; +}; + +template +void CPtrNodeSingleListSAInterface::RemoveItem(T* item) +{ + typedef void(__thiscall * CPtrNodeSingleList_RemoveItem_t)(CPtrNodeSingleListSAInterface * pLinkList, void* item); + ((CPtrNodeSingleList_RemoveItem_t)0x533610)(this, item); +} + +template +void CPtrNodeSingleListSAInterface::RemoveAllItems() +{ + while (m_pList) + { + RemoveItem(m_pList->pItem); + } +} diff --git a/Client/game_sa/CQuadTreeNodeSA.h b/Client/game_sa/CQuadTreeNodeSA.h new file mode 100644 index 0000000000..dabb9bdfe5 --- /dev/null +++ b/Client/game_sa/CQuadTreeNodeSA.h @@ -0,0 +1,56 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CQuadTreeNodeSA.h + * PURPOSE: Quad tree node class interface + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include "CPtrNodeSingleListSA.h" +#include "CRect.h" + +template +class CQuadTreeNodesSAInterface +{ +public: + void RemoveAllItems(); + char AddItem(T* item, CRect* boudingBox); + +private: + float m_fX; + float m_fY; + float m_fW; + float m_fH; + CPtrNodeSingleListSAInterface m_pItemList; + CQuadTreeNodesSAInterface* m_childrens[4]; + uint32_t m_level; +}; +static_assert(sizeof(CQuadTreeNodesSAInterface) == 0x28, "Wrong CQuadTreeNodesSAInterface size"); + +template +void CQuadTreeNodesSAInterface::RemoveAllItems() +{ + if (m_level) + { + for (size_t i = 0; i < 4; i++) + { + m_childrens[i]->RemoveAllItems(); + } + } + else + { + m_pItemList.RemoveAllItems(); + } +}; + +template +char CQuadTreeNodesSAInterface::AddItem(T* item, CRect* boudingBox) +{ + typedef char(__thiscall * CQuadTreeNode_AddItem_t)(CQuadTreeNodesSAInterface*, void*, CRect*); + return ((CQuadTreeNode_AddItem_t)(0x552CD0))(this, item, boudingBox); +}; diff --git a/Client/mods/deathmatch/logic/CClientBuilding.cpp b/Client/mods/deathmatch/logic/CClientBuilding.cpp index 393774c20b..6b50476138 100644 --- a/Client/mods/deathmatch/logic/CClientBuilding.cpp +++ b/Client/mods/deathmatch/logic/CClientBuilding.cpp @@ -96,14 +96,14 @@ void CClientBuilding::Create() CVector4D vRot4D; ConvertZXYEulersToQuaternion(m_vRot, vRot4D); - m_pBuilding = g_pGame->GetPools()->AddBuilding(this, m_usModelId, &m_vPos, &vRot4D, m_interior); + m_pBuilding = g_pGame->GetPools()->GetBuildingsPool().AddBuilding(this, m_usModelId, &m_vPos, &vRot4D, m_interior); } void CClientBuilding::Destroy() { if (m_pBuilding) { - g_pGame->GetPools()->RemoveBuilding(m_pBuilding); + g_pGame->GetPools()->GetBuildingsPool().RemoveBuilding(m_pBuilding); m_pBuilding = nullptr; } } diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp index 205d571d80..114f4a9c89 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp @@ -15,6 +15,8 @@ void CLuaBuildingDefs::LoadFunctions() // Backwards compatibility functions constexpr static const std::pair functions[]{ {"createBuilding", ArgumentParser}, + {"removeAllGameBuildings", ArgumentParser}, + {"restoreAllGameBuildings", ArgumentParser}, }; // Add functions @@ -43,7 +45,7 @@ CClientBuilding* CLuaBuildingDefs::CreateBuilding(lua_State* const luaVM, uint16 if (!CClientBuildingManager::IsValidModel(modelId)) throw std::invalid_argument("Invalid building model id"); - if (!g_pGame->GetPools()->HasFreeBuildingSlot()) + if (!g_pGame->GetPools()->GetBuildingsPool().HasFreeBuildingSlot()) throw std::invalid_argument("No free slot in buildings pool"); if (!CClientBuildingManager::IsValidPosition(pos)) @@ -58,3 +60,13 @@ CClientBuilding* CLuaBuildingDefs::CreateBuilding(lua_State* const luaVM, uint16 return pBuilding; } + +void CLuaBuildingDefs::RemoveAllGameBuildings() +{ + g_pGame->RemoveAllBuildings(); +} + +void CLuaBuildingDefs::RestoreGameBuildings() +{ + g_pGame->RestoreGameBuildings(); +} diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.h index 5d4d69c65b..628d320844 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.h @@ -20,4 +20,6 @@ class CLuaBuildingDefs : public CLuaDefs // Buiding create funcs static CClientBuilding* CreateBuilding(lua_State* const luaVM, uint16_t modelId, CVector pos, CVector rot, std::optional interior); + static void RemoveAllGameBuildings(); + static void RestoreGameBuildings(); }; diff --git a/Client/sdk/game/CBuildingsPool.h b/Client/sdk/game/CBuildingsPool.h new file mode 100644 index 0000000000..6a08ee5372 --- /dev/null +++ b/Client/sdk/game/CBuildingsPool.h @@ -0,0 +1,31 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: sdk/game/CBuildingsPool.h + * PURPOSE: Buildings pool interface + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include "Common.h" +#include + +class CBuilding; +class CBuildingSA; + +class CBuildingsPool +{ +public: + + // Buildings pool + virtual CBuilding* AddBuilding(class CClientBuilding*, uint16_t modelId, CVector* vPos, CVector4D* vRot, uint8_t interior) = 0; + virtual void RemoveBuilding(CBuilding* pObject) = 0; + virtual bool HasFreeBuildingSlot() = 0; + + virtual void RemoveAllBuildings() = 0; + virtual void RestoreAllBuildings() = 0; +}; diff --git a/Client/sdk/game/CGame.h b/Client/sdk/game/CGame.h index dee2eaeae8..6df946ad3c 100644 --- a/Client/sdk/game/CGame.h +++ b/Client/sdk/game/CGame.h @@ -66,6 +66,7 @@ class CWeaponStat; class CWeaponStatManager; class CWeather; class CWorld; +class CIplStore; enum eEntityType; enum ePedPieceTypes; @@ -255,4 +256,7 @@ class __declspec(novtable) CGame virtual int32_t GetBaseIDforRRR() = 0; virtual int32_t GetBaseIDforSCM() = 0; virtual int32_t GetCountOfAllFileIDs() = 0; + + virtual void RemoveAllBuildings() = 0; + virtual void RestoreGameBuildings() = 0; }; diff --git a/Client/sdk/game/CIplStore.h b/Client/sdk/game/CIplStore.h new file mode 100644 index 0000000000..2deec6a8c7 --- /dev/null +++ b/Client/sdk/game/CIplStore.h @@ -0,0 +1,18 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: sdk/game/CIplStore.h + * PURPOSE: Game IPL store interface + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +class CIplStore +{ +public: + virtual void SetDynamicIplStreamingEnabled(bool state) = 0; +}; diff --git a/Client/sdk/game/CPools.h b/Client/sdk/game/CPools.h index f758084639..b6374b6f75 100644 --- a/Client/sdk/game/CPools.h +++ b/Client/sdk/game/CPools.h @@ -11,6 +11,9 @@ #pragma once +#include "Common.h" +#include "CBuildingsPool.h" + class CClientEntity; class CEntity; class CEntitySAInterface; @@ -84,11 +87,6 @@ class CPools virtual CPed* GetPedFromRef(DWORD dwGameRef) = 0; virtual unsigned long GetPedCount() = 0; - // Buildings pool - virtual CBuilding* AddBuilding(class CClientBuilding*, uint16_t modelId, CVector *vPos, CVector4D *vRot, uint8_t interior) = 0; - virtual void RemoveBuilding(CBuilding* pObject) = 0; - virtual bool HasFreeBuildingSlot() = 0; - // Others virtual CVehicle* AddTrain(class CClientVehicle* pClientVehicle, CVector* vecPosition, DWORD dwModels[], int iSize, bool iDirection, uchar ucTrackId = 0xFF) = 0; @@ -110,4 +108,6 @@ class CPools virtual bool IsFreeTextureDictonarySlot(uint uiTxdID) = 0; virtual ushort GetFreeTextureDictonarySlot() = 0; + + virtual CBuildingsPool& GetBuildingsPool() noexcept = 0; }; diff --git a/Client/sdk/game/CWorld.h b/Client/sdk/game/CWorld.h index a07d2dbd20..807fa09a36 100644 --- a/Client/sdk/game/CWorld.h +++ b/Client/sdk/game/CWorld.h @@ -327,6 +327,7 @@ class CWorld { public: virtual void Add(CEntity* entity, eDebugCaller CallerId) = 0; + virtual void Add(CEntitySAInterface* entity, eDebugCaller CallerId) = 0; virtual void Remove(CEntity* entity, eDebugCaller CallerId) = 0; virtual void Remove(CEntitySAInterface* entityInterface, eDebugCaller CallerId) = 0; virtual auto ProcessLineAgainstMesh(CEntitySAInterface* e, CVector start, CVector end) -> SProcessLineOfSightMaterialInfoResult = 0; From eb30a3556cd3126719673e23cee23b54e80411a8 Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Sat, 2 Mar 2024 18:17:34 +0300 Subject: [PATCH 02/24] Fix buildings crashes --- Client/game_sa/CBuildingsPoolSA.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Client/game_sa/CBuildingsPoolSA.cpp b/Client/game_sa/CBuildingsPoolSA.cpp index 0b257a141b..b1eb308c4b 100644 --- a/Client/game_sa/CBuildingsPoolSA.cpp +++ b/Client/game_sa/CBuildingsPoolSA.cpp @@ -15,6 +15,7 @@ #include "CFileLoaderSA.h" #include #include "CGameSA.h" +#include "CPtrNodeSingleListSA.h" extern CGameSA* pGame; @@ -95,6 +96,14 @@ void CBuildingsPoolSA::RemoveBuilding(CBuilding* pBuilding) // Remove building from world pGame->GetWorld()->Remove(pInterface, CBuildingPool_Destructor); + // Remove building from cover list + CPtrNodeSingleListSAInterface* coverList = reinterpret_cast*>(0xC1A2B8); + coverList->RemoveItem(pInterface); + + // Remove plant + typedef CEntitySAInterface*(__cdecl *CPlantColEntEntry_Remove)(CEntitySAInterface * item); + ((CPlantColEntEntry_Remove)0x5DBEF0)(pInterface); + // Remove col reference auto modelInfo = pGame->GetModelInfo(pBuilding->GetModelIndex()); modelInfo->RemoveColRef(); From 37ef3790aa7c4b5ad909e2a31aa185de4b6af1d9 Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Sun, 3 Mar 2024 20:22:42 +0300 Subject: [PATCH 03/24] Use using --- Client/game_sa/CBuildingsPoolSA.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Client/game_sa/CBuildingsPoolSA.cpp b/Client/game_sa/CBuildingsPoolSA.cpp index b1eb308c4b..44a033805a 100644 --- a/Client/game_sa/CBuildingsPoolSA.cpp +++ b/Client/game_sa/CBuildingsPoolSA.cpp @@ -101,8 +101,8 @@ void CBuildingsPoolSA::RemoveBuilding(CBuilding* pBuilding) coverList->RemoveItem(pInterface); // Remove plant - typedef CEntitySAInterface*(__cdecl *CPlantColEntEntry_Remove)(CEntitySAInterface * item); - ((CPlantColEntEntry_Remove)0x5DBEF0)(pInterface); + using CPlantColEntry_Remove = CEntitySAInterface* (*)(CEntitySAInterface*); + ((CPlantColEntry_Remove)0x5DBEF0)(pInterface); // Remove col reference auto modelInfo = pGame->GetModelInfo(pBuilding->GetModelIndex()); From 754b096f3773c00f678061dba080165c1710fcfe Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Mon, 4 Mar 2024 18:22:30 +0300 Subject: [PATCH 04/24] using + clangformat --- Client/game_sa/CPtrNodeSingleListSA.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Client/game_sa/CPtrNodeSingleListSA.h b/Client/game_sa/CPtrNodeSingleListSA.h index 67dfe72669..06df6a2575 100644 --- a/Client/game_sa/CPtrNodeSingleListSA.h +++ b/Client/game_sa/CPtrNodeSingleListSA.h @@ -10,14 +10,14 @@ #pragma once -template +template struct CPtrNodeSingleLink { T* pItem; CPtrNodeSingleLink* pNext; }; -template +template class CPtrNodeSingleListSAInterface { public: @@ -31,7 +31,7 @@ class CPtrNodeSingleListSAInterface template void CPtrNodeSingleListSAInterface::RemoveItem(T* item) { - typedef void(__thiscall * CPtrNodeSingleList_RemoveItem_t)(CPtrNodeSingleListSAInterface * pLinkList, void* item); + using CPtrNodeSingleList_RemoveItem_t = void(__thiscall*)(CPtrNodeSingleListSAInterface * pLinkList, void* item); ((CPtrNodeSingleList_RemoveItem_t)0x533610)(this, item); } From 22bff5f7133cfba3b0d6761c7a9ab0364fc83c42 Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Mon, 4 Mar 2024 23:42:32 +0300 Subject: [PATCH 05/24] Disable barriers after restore --- Client/game_sa/CGameSA.cpp | 2 +- Client/game_sa/CIplStoreSA.cpp | 45 +++++++++++++++++++++++++++++++--- Client/game_sa/CIplStoreSA.h | 5 +++- Client/sdk/game/CIplStore.h | 1 + 4 files changed, 48 insertions(+), 5 deletions(-) diff --git a/Client/game_sa/CGameSA.cpp b/Client/game_sa/CGameSA.cpp index 2a496ca361..236fad9fd7 100644 --- a/Client/game_sa/CGameSA.cpp +++ b/Client/game_sa/CGameSA.cpp @@ -899,7 +899,7 @@ void CGameSA::RestoreGameBuildings() { m_pPools->GetBuildingsPool().RestoreAllBuildings(); - m_pIplStore->SetDynamicIplStreamingEnabled(true); + m_pIplStore->SetDynamicIplStreamingEnabled(true, [](CIplSAInterface* ipl) { return memcmp("barriers", ipl->name, 8) != 0; }); } // Ensure models have the default lod distances diff --git a/Client/game_sa/CIplStoreSA.cpp b/Client/game_sa/CIplStoreSA.cpp index 87abc1dd9c..400602db9d 100644 --- a/Client/game_sa/CIplStoreSA.cpp +++ b/Client/game_sa/CIplStoreSA.cpp @@ -26,6 +26,14 @@ void CIplStoreSA::UnloadAndDisableStreaming(int iplId) ((Function_EnableStreaming)(0x405890))(iplId); } +void CIplStoreSA::EnableStreaming(int iplId) +{ + auto ipl = (*m_ppIplPoolInterface)->GetObject(iplId); + ipl->bDisabledStreaming = false; + + (*gIplQuadTree)->AddItem(ipl, &ipl->rect); +} + void CIplStoreSA::SetDynamicIplStreamingEnabled(bool state) { if (m_isStreamingEnbabled == state) @@ -52,10 +60,41 @@ void CIplStoreSA::SetDynamicIplStreamingEnabled(bool state) { if (pPool->IsContains(i)) { - auto ipl = pPool->GetObject(i); - ipl->bDisabledStreaming = false; + EnableStreaming(i); + } + } + } + + m_isStreamingEnbabled = state; +} + +void CIplStoreSA::SetDynamicIplStreamingEnabled(bool state, std::function filer) +{ + if (m_isStreamingEnbabled == state) + return; + + // Ipl with 0 index is generic + // We don't unload this IPL - (*gIplQuadTree)->AddItem(ipl, &ipl->rect); + auto pPool = *m_ppIplPoolInterface; + if (!state) + { + for (int i = 1; i < pPool->m_nSize; i++) + { + if (pPool->IsContains(i) && filer(pPool->GetObject(i))) + { + UnloadAndDisableStreaming(i); + } + } + (*gIplQuadTree)->RemoveAllItems(); + } + else + { + for (int i = 1; i < pPool->m_nSize; i++) + { + if (pPool->IsContains(i) && filer(pPool->GetObject(i))) + { + EnableStreaming(i); } } } diff --git a/Client/game_sa/CIplStoreSA.h b/Client/game_sa/CIplStoreSA.h index 220dc563c5..a9e7ff2c61 100644 --- a/Client/game_sa/CIplStoreSA.h +++ b/Client/game_sa/CIplStoreSA.h @@ -13,7 +13,8 @@ #include "CIplSA.h" #include "CPoolsSA.h" -#include +#include = +#include class CIplStoreSA : public CIplStore { @@ -22,9 +23,11 @@ class CIplStoreSA : public CIplStore ~CIplStoreSA() = default; void SetDynamicIplStreamingEnabled(bool state); + void SetDynamicIplStreamingEnabled(bool state, std::function filer); private: void UnloadAndDisableStreaming(int iplId); + void EnableStreaming(int iplId); private: CPoolSAInterface** m_ppIplPoolInterface; diff --git a/Client/sdk/game/CIplStore.h b/Client/sdk/game/CIplStore.h index 2deec6a8c7..ea5aeafcbd 100644 --- a/Client/sdk/game/CIplStore.h +++ b/Client/sdk/game/CIplStore.h @@ -15,4 +15,5 @@ class CIplStore { public: virtual void SetDynamicIplStreamingEnabled(bool state) = 0; + virtual void SetDynamicIplStreamingEnabled(bool state, std::function filer) = 0; }; From 87b2894564437aadb2e54859cc37720d45a65cdf Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Mon, 4 Mar 2024 23:46:39 +0300 Subject: [PATCH 06/24] clang format --- Client/game_sa/CIplStoreSA.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/game_sa/CIplStoreSA.cpp b/Client/game_sa/CIplStoreSA.cpp index 400602db9d..85f77b0b63 100644 --- a/Client/game_sa/CIplStoreSA.cpp +++ b/Client/game_sa/CIplStoreSA.cpp @@ -16,7 +16,7 @@ static auto gIplQuadTree = (CQuadTreeNodesSAInterface**)0x8E3FAC; -CIplStoreSA::CIplStoreSA(): m_isStreamingEnbabled(true), m_ppIplPoolInterface((CPoolSAInterface**)0x8E3FB0) +CIplStoreSA::CIplStoreSA() : m_isStreamingEnbabled(true), m_ppIplPoolInterface((CPoolSAInterface**)0x8E3FB0) { } From 8f05f6d0b33de643f059a0f3c469409161bc1fde Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Tue, 5 Mar 2024 22:55:41 +0300 Subject: [PATCH 07/24] Fix typos --- Client/game_sa/CBuildingsPoolSA.cpp | 4 ++-- Client/game_sa/CIplStoreSA.cpp | 16 ++++++++-------- Client/game_sa/CIplStoreSA.h | 6 +++--- Client/game_sa/CPoolSAInterface.h | 2 +- Client/sdk/game/CIplStore.h | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Client/game_sa/CBuildingsPoolSA.cpp b/Client/game_sa/CBuildingsPoolSA.cpp index 44a033805a..76e14cd34e 100644 --- a/Client/game_sa/CBuildingsPoolSA.cpp +++ b/Client/game_sa/CBuildingsPoolSA.cpp @@ -35,7 +35,7 @@ inline bool CBuildingsPoolSA::AddBuildingToPool(CClientBuilding* pClientBuilding return false; uint32_t dwElementIndexInPool = (*m_ppBuildingPoolInterface)->GetObjectIndexSafe(pInterface); - if (dwElementIndexInPool >= -1) + if (dwElementIndexInPool == UINT_MAX) return false; m_buildingPool.arrayOfClientEntities[dwElementIndexInPool] = {pBuilding, (CClientEntity*)pClientBuilding}; @@ -90,7 +90,7 @@ void CBuildingsPoolSA::RemoveBuilding(CBuilding* pBuilding) CBuildingSAInterface* pInterface = pBuilding->GetBuildingInterface(); uint32_t dwElementIndexInPool = (*m_ppBuildingPoolInterface)->GetObjectIndexSafe(pInterface); - if (dwElementIndexInPool == -1) + if (dwElementIndexInPool == UINT_MAX) return; // Remove building from world diff --git a/Client/game_sa/CIplStoreSA.cpp b/Client/game_sa/CIplStoreSA.cpp index 85f77b0b63..2069b40c4e 100644 --- a/Client/game_sa/CIplStoreSA.cpp +++ b/Client/game_sa/CIplStoreSA.cpp @@ -16,7 +16,7 @@ static auto gIplQuadTree = (CQuadTreeNodesSAInterface**)0x8E3FAC; -CIplStoreSA::CIplStoreSA() : m_isStreamingEnbabled(true), m_ppIplPoolInterface((CPoolSAInterface**)0x8E3FB0) +CIplStoreSA::CIplStoreSA() : m_isStreamingEnabled(true), m_ppIplPoolInterface((CPoolSAInterface**)0x8E3FB0) { } @@ -36,7 +36,7 @@ void CIplStoreSA::EnableStreaming(int iplId) void CIplStoreSA::SetDynamicIplStreamingEnabled(bool state) { - if (m_isStreamingEnbabled == state) + if (m_isStreamingEnabled == state) return; // Ipl with 0 index is generic @@ -65,12 +65,12 @@ void CIplStoreSA::SetDynamicIplStreamingEnabled(bool state) } } - m_isStreamingEnbabled = state; + m_isStreamingEnabled = state; } -void CIplStoreSA::SetDynamicIplStreamingEnabled(bool state, std::function filer) +void CIplStoreSA::SetDynamicIplStreamingEnabled(bool state, std::function filter) { - if (m_isStreamingEnbabled == state) + if (m_isStreamingEnabled == state) return; // Ipl with 0 index is generic @@ -81,7 +81,7 @@ void CIplStoreSA::SetDynamicIplStreamingEnabled(bool state, std::functionm_nSize; i++) { - if (pPool->IsContains(i) && filer(pPool->GetObject(i))) + if (pPool->IsContains(i) && filter(pPool->GetObject(i))) { UnloadAndDisableStreaming(i); } @@ -92,12 +92,12 @@ void CIplStoreSA::SetDynamicIplStreamingEnabled(bool state, std::functionm_nSize; i++) { - if (pPool->IsContains(i) && filer(pPool->GetObject(i))) + if (pPool->IsContains(i) && filter(pPool->GetObject(i))) { EnableStreaming(i); } } } - m_isStreamingEnbabled = state; + m_isStreamingEnabled = state; } diff --git a/Client/game_sa/CIplStoreSA.h b/Client/game_sa/CIplStoreSA.h index a9e7ff2c61..1751ecd4f7 100644 --- a/Client/game_sa/CIplStoreSA.h +++ b/Client/game_sa/CIplStoreSA.h @@ -13,7 +13,7 @@ #include "CIplSA.h" #include "CPoolsSA.h" -#include = +#include #include class CIplStoreSA : public CIplStore @@ -23,7 +23,7 @@ class CIplStoreSA : public CIplStore ~CIplStoreSA() = default; void SetDynamicIplStreamingEnabled(bool state); - void SetDynamicIplStreamingEnabled(bool state, std::function filer); + void SetDynamicIplStreamingEnabled(bool state, std::function filter); private: void UnloadAndDisableStreaming(int iplId); @@ -32,5 +32,5 @@ class CIplStoreSA : public CIplStore private: CPoolSAInterface** m_ppIplPoolInterface; - bool m_isStreamingEnbabled; + bool m_isStreamingEnabled; }; diff --git a/Client/game_sa/CPoolSAInterface.h b/Client/game_sa/CPoolSAInterface.h index 285b4dda5f..8b653aefc1 100644 --- a/Client/game_sa/CPoolSAInterface.h +++ b/Client/game_sa/CPoolSAInterface.h @@ -129,7 +129,7 @@ class CPoolSAInterface uint32_t GetObjectIndexSafe(B* pObject) { uint32_t index = GetObjectIndex(pObject); - return index > m_nSize ? -1 : index; + return index > m_nSize ? UINT_MAX : index; } }; diff --git a/Client/sdk/game/CIplStore.h b/Client/sdk/game/CIplStore.h index ea5aeafcbd..c3c58f2c95 100644 --- a/Client/sdk/game/CIplStore.h +++ b/Client/sdk/game/CIplStore.h @@ -15,5 +15,5 @@ class CIplStore { public: virtual void SetDynamicIplStreamingEnabled(bool state) = 0; - virtual void SetDynamicIplStreamingEnabled(bool state, std::function filer) = 0; + virtual void SetDynamicIplStreamingEnabled(bool state, std::function filter) = 0; }; From a0f09654e92bcdaf46fc8ea7481b971d5f78df72 Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Fri, 15 Mar 2024 20:08:02 +0300 Subject: [PATCH 08/24] Remove elements too --- Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp index 114f4a9c89..5862d641de 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp @@ -63,6 +63,7 @@ CClientBuilding* CLuaBuildingDefs::CreateBuilding(lua_State* const luaVM, uint16 void CLuaBuildingDefs::RemoveAllGameBuildings() { + m_pBuildingManager->RemoveAll(); g_pGame->RemoveAllBuildings(); } From 944b76295c611f7e25c935f934953f843630c4ba Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Fri, 15 Mar 2024 20:39:04 +0300 Subject: [PATCH 09/24] Don't remove scripted buildings --- .../mods/deathmatch/logic/CClientBuilding.h | 4 ++- .../logic/luadefs/CLuaBuildingDefs.cpp | 35 ++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientBuilding.h b/Client/mods/deathmatch/logic/CClientBuilding.h index f9f423f4a3..e8ef8d3b22 100644 --- a/Client/mods/deathmatch/logic/CClientBuilding.h +++ b/Client/mods/deathmatch/logic/CClientBuilding.h @@ -42,9 +42,11 @@ class CClientBuilding : public CClientEntity eClientEntityType GetType() const noexcept { return CCLIENTBUILDING; } -private: void Create(); void Destroy(); + bool IsValid() const noexcept { return m_pBuilding != nullptr; }; + +private: void Recreate() { diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp index 5862d641de..7c0c31509b 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp @@ -63,11 +63,44 @@ CClientBuilding* CLuaBuildingDefs::CreateBuilding(lua_State* const luaVM, uint16 void CLuaBuildingDefs::RemoveAllGameBuildings() { - m_pBuildingManager->RemoveAll(); + // We do not want to remove scripted buildings + // But we need remove them from the buildings pool for a bit... + for (CClientBuilding* building : m_pBuildingManager->GetBuildings()) + { + building->Destroy(); + } + + // This function makes buildings backup without scripted buildings g_pGame->RemoveAllBuildings(); + + // ... And restore here + for (CClientBuilding* building : m_pBuildingManager->GetBuildings()) + { + building->Create(); + } } void CLuaBuildingDefs::RestoreGameBuildings() { + // We want to restore the game buildings to the same positions as they were before the backup. + // Remove scripted buildings for a bit + for (CClientBuilding* building : m_pBuildingManager->GetBuildings()) + { + building->Destroy(); + } + g_pGame->RestoreGameBuildings(); + + // Restore what we can + for (CClientBuilding* building : m_pBuildingManager->GetBuildings()) + { + building->Create(); + + if (!building->IsValid()) + { + // User creates too much buildings + // We can't restore them all + delete building; + } + } } From 4c9399c273682e06151192ac1353b917117e651d Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Tue, 19 Mar 2024 00:24:50 +0300 Subject: [PATCH 10/24] Engine pool functions --- Client/game_sa/CBuildingsPoolSA.cpp | 117 ++++++++++++++++-- Client/game_sa/CBuildingsPoolSA.h | 10 +- Client/game_sa/CEntitySA.cpp | 15 +++ Client/game_sa/CEntitySA.h | 2 + Client/game_sa/CGameSA.cpp | 11 ++ Client/game_sa/CGameSA.h | 2 + Client/game_sa/CPoolsSA.cpp | 51 +++++++- Client/game_sa/CPoolsSA.h | 1 + Client/game_sa/MemSA.h | 39 ++++++ .../mods/deathmatch/logic/CClientBuilding.h | 4 +- .../logic/CClientBuildingManager.cpp | 23 ++++ .../deathmatch/logic/CClientBuildingManager.h | 3 + .../logic/lua/CLuaFunctionParseHelpers.cpp | 23 ++++ .../logic/lua/CLuaFunctionParseHelpers.h | 1 + .../logic/luadefs/CLuaBuildingDefs.cpp | 27 +--- .../logic/luadefs/CLuaEngineDefs.cpp | 54 +++++++- .../deathmatch/logic/luadefs/CLuaEngineDefs.h | 5 + Client/sdk/game/CBuildingsPool.h | 2 + Client/sdk/game/CGame.h | 2 + Client/sdk/game/CPools.h | 1 + 20 files changed, 350 insertions(+), 43 deletions(-) create mode 100644 Client/game_sa/MemSA.h diff --git a/Client/game_sa/CBuildingsPoolSA.cpp b/Client/game_sa/CBuildingsPoolSA.cpp index 76e14cd34e..68df9a1b50 100644 --- a/Client/game_sa/CBuildingsPoolSA.cpp +++ b/Client/game_sa/CBuildingsPoolSA.cpp @@ -16,6 +16,7 @@ #include #include "CGameSA.h" #include "CPtrNodeSingleListSA.h" +#include "MemSA.h" extern CGameSA* pGame; @@ -93,16 +94,7 @@ void CBuildingsPoolSA::RemoveBuilding(CBuilding* pBuilding) if (dwElementIndexInPool == UINT_MAX) return; - // Remove building from world - pGame->GetWorld()->Remove(pInterface, CBuildingPool_Destructor); - - // Remove building from cover list - CPtrNodeSingleListSAInterface* coverList = reinterpret_cast*>(0xC1A2B8); - coverList->RemoveItem(pInterface); - - // Remove plant - using CPlantColEntry_Remove = CEntitySAInterface* (*)(CEntitySAInterface*); - ((CPlantColEntry_Remove)0x5DBEF0)(pInterface); + RemoveBuildingFromWorld(pInterface); // Remove col reference auto modelInfo = pGame->GetModelInfo(pBuilding->GetModelIndex()); @@ -134,9 +126,9 @@ void CBuildingsPoolSA::RemoveAllBuildings() { if (pBuildsingsPool->IsContains(i)) { - auto building = pBuildsingsPool->GetObject(i); + CBuildingSAInterface* building = pBuildsingsPool->GetObject(i); - pGame->GetWorld()->Remove(building, CBuildingPool_Destructor); + RemoveBuildingFromWorld(building); pBuildsingsPool->Release(i); @@ -172,6 +164,107 @@ void CBuildingsPoolSA::RestoreAllBuildings() m_pOriginalBuildingsBackup.release(); } +void CBuildingsPoolSA::RemoveBuildingFromWorld(CBuildingSAInterface* pBuilding) +{ + // Remove building from world + pGame->GetWorld()->Remove(pBuilding, CBuildingPool_Destructor); + + // Remove building from cover list + CPtrNodeSingleListSAInterface* coverList = reinterpret_cast*>(0xC1A2B8); + coverList->RemoveItem(pBuilding); + + // Remove plant + using CPlantColEntry_Remove = CEntitySAInterface* (*)(CEntitySAInterface*); + ((CPlantColEntry_Remove)0x5DBEF0)(pBuilding); + + if (pBuilding->m_pRwObject != nullptr) + { + CEntitySA entity{}; + entity.SetInterface(pBuilding); + entity.DeleteRwObject(); + } +} + +bool CBuildingsPoolSA::SetSize(int size) +{ + auto* pool = (*m_ppBuildingPoolInterface); + const int curretnSize = pool->m_nSize; + + void* oldPool = pool->m_pObjects; + + if (oldPool != nullptr) + { + //std::free((void*)pool->m_pObjects); + //free(pool->m_pObjects); + MemSA::free(pool->m_pObjects); + pool->m_pObjects = nullptr; + } + + if (pool->m_byteMap != nullptr) + { + MemSA::free(pool->m_byteMap); + pool->m_byteMap = nullptr; + } + + CBuildingSAInterface* newObjects = MemSA::malloc_struct(size); + if (newObjects == nullptr) + { + SetSize(curretnSize); + return false; + } + + tPoolObjectFlags* newBytemap = MemSA::malloc_struct(size); + if (newBytemap == nullptr) + { + MemSA::free(newObjects); + SetSize(curretnSize); + return false; + } + + pool->m_pObjects = newObjects; + pool->m_byteMap = newBytemap; + pool->m_nSize = size; + pool->m_nFirstFree = 0; + + for (auto i = 0; i < size; i++) + { + newBytemap[i].bEmpty = true; + } + + if (oldPool != nullptr) + { + UpdateIplEntrysPointers(oldPool, newObjects); + } + + return true; +} + +void CBuildingsPoolSA::UpdateIplEntrysPointers(void* oldPool, void* newObjects) +{ + uint32_t offset = (uint32_t)newObjects - (uint32_t)oldPool; + + using buildings_array_t = CBuildingSAInterface* [1000]; + using ipl_entry_array_t = buildings_array_t* [40]; + ipl_entry_array_t* iplEntryArray = (ipl_entry_array_t*)0x8E3F08; + + for (auto i = 0; i < 40; i++) + { + buildings_array_t* ppArray = (*iplEntryArray)[i]; + + if (ppArray == nullptr) + { + return; + } + size_t arraySize = MemSA::msize(*ppArray) / sizeof(CBuildingSAInterface*); + for (auto j = 0; j < arraySize; j++) + { + CBuildingSAInterface* object = (*ppArray)[j]; + + (*ppArray)[j] = (CBuildingSAInterface*)((uint32_t)object + offset); + } + } +} + bool CBuildingsPoolSA::HasFreeBuildingSlot() { return (*m_ppBuildingPoolInterface)->GetFreeSlot() != -1; diff --git a/Client/game_sa/CBuildingsPoolSA.h b/Client/game_sa/CBuildingsPoolSA.h index 25854e3b2e..f9e720835e 100644 --- a/Client/game_sa/CBuildingsPoolSA.h +++ b/Client/game_sa/CBuildingsPoolSA.h @@ -24,11 +24,15 @@ class CBuildingsPoolSA : public CBuildingsPool void RemoveBuilding(CBuilding* pBuilding); bool HasFreeBuildingSlot(); - void RemoveAllBuildings(); - void RestoreAllBuildings(); + void RemoveAllBuildings() override; + void RestoreAllBuildings() override; + bool SetSize(int size) override; + int GetSize() const override { return (*m_ppBuildingPoolInterface)->m_nSize; }; private: - bool AddBuildingToPool(CClientBuilding* pClientBuilding, CBuildingSA* pBuilding); + void RemoveBuildingFromWorld(CBuildingSAInterface* pBuilding); + bool AddBuildingToPool(CClientBuilding* pClientBuilding, CBuildingSA* pBuilding); + void UpdateIplEntrysPointers(void* oldPool, void* newObjects); private: SPoolData m_buildingPool; diff --git a/Client/game_sa/CEntitySA.cpp b/Client/game_sa/CEntitySA.cpp index 4466a53fe4..7ac45eaa31 100644 --- a/Client/game_sa/CEntitySA.cpp +++ b/Client/game_sa/CEntitySA.cpp @@ -189,6 +189,21 @@ void CEntitySA::SetupLighting() } } +// REWRITE THIS SHIT +void CEntitySA::DeleteRwObject() +{ + DWORD dwFunc = m_pInterface->vtbl->DeleteRwObject; + DWORD dwThis = (DWORD)m_pInterface; + if (dwFunc) + { + _asm + { + mov ecx, dwThis + call dwFunc + } + } +} + void CEntitySA::Render() { DWORD dwFunc = 0x59F180; // m_pInterface->vtbl->Render; diff --git a/Client/game_sa/CEntitySA.h b/Client/game_sa/CEntitySA.h index 101b88a46a..e9ea04a3cd 100644 --- a/Client/game_sa/CEntitySA.h +++ b/Client/game_sa/CEntitySA.h @@ -309,6 +309,8 @@ class CEntitySA : public virtual CEntity bool SetBoneRotation(eBone boneId, float yaw, float pitch, float roll); bool GetBonePosition(eBone boneId, CVector& position); bool SetBonePosition(eBone boneId, const CVector& position); + + void DeleteRwObject(); // CEntitySA interface virtual void OnChangingPosition(const CVector& vecNewPosition) {} diff --git a/Client/game_sa/CGameSA.cpp b/Client/game_sa/CGameSA.cpp index 236fad9fd7..d953817710 100644 --- a/Client/game_sa/CGameSA.cpp +++ b/Client/game_sa/CGameSA.cpp @@ -902,6 +902,17 @@ void CGameSA::RestoreGameBuildings() m_pIplStore->SetDynamicIplStreamingEnabled(true, [](CIplSAInterface* ipl) { return memcmp("barriers", ipl->name, 8) != 0; }); } +bool CGameSA::SetBuildingPoolSize(size_t size) +{ + RemoveAllBuildings(); + + bool status = m_pPools->GetBuildingsPool().SetSize(size); + + RestoreGameBuildings(); + + return status; +} + // Ensure models have the default lod distances void CGameSA::ResetModelLodDistances() { diff --git a/Client/game_sa/CGameSA.h b/Client/game_sa/CGameSA.h index 5a2be9f3ae..57cfa987ee 100644 --- a/Client/game_sa/CGameSA.h +++ b/Client/game_sa/CGameSA.h @@ -277,6 +277,8 @@ class CGameSA : public CGame void RemoveAllBuildings(); void RestoreGameBuildings(); + bool SetBuildingPoolSize(size_t size); + private: CPools* m_pPools; CPlayerInfo* m_pPlayerInfo; diff --git a/Client/game_sa/CPoolsSA.cpp b/Client/game_sa/CPoolsSA.cpp index 1baa2f7ecb..279599ce15 100644 --- a/Client/game_sa/CPoolsSA.cpp +++ b/Client/game_sa/CPoolsSA.cpp @@ -808,6 +808,54 @@ int CPoolsSA::GetPoolDefaultCapacity(ePools pool) return 0; } +int CPoolsSA::GetPoolDefaultModdedCapacity(ePools pool) +{ + switch (pool) + { + case BUILDING_POOL: + return MAX_BUILDINGS; + case PED_POOL: + return 140; + case OBJECT_POOL: + return MAX_OBJECTS; + case DUMMY_POOL: + return 2500; + case VEHICLE_POOL: + return 110; + case COL_MODEL_POOL: + return 12000; + case TASK_POOL: + return 5000; + case EVENT_POOL: + return 5000; + case TASK_ALLOCATOR_POOL: + return 16; + case PED_INTELLIGENCE_POOL: + return 140; + case PED_ATTRACTOR_POOL: + return 64; + case ENTRY_INFO_NODE_POOL: + return MAX_ENTRY_INFO_NODES; + case NODE_ROUTE_POOL: + return 64; + case PATROL_ROUTE_POOL: + return 32; + case POINT_ROUTE_POOL: + return 64; + case POINTER_DOUBLE_LINK_POOL: + return MAX_POINTER_DOUBLE_LINKS; + case POINTER_SINGLE_LINK_POOL: + return MAX_POINTER_SINGLE_LINKS; + case ENV_MAP_MATERIAL_POOL: + return 16000; + case ENV_MAP_ATOMIC_POOL: + return 4000; + case SPEC_MAP_MATERIAL_POOL: + return 16000; + } + return 0; +} + int CPoolsSA::GetPoolCapacity(ePools pool) { DWORD iPtr = NULL; @@ -815,8 +863,7 @@ int CPoolsSA::GetPoolCapacity(ePools pool) switch (pool) { case BUILDING_POOL: - iPtr = 0x55105F; - break; + return GetBuildingsPool().GetSize(); case PED_POOL: iPtr = 0x550FF2; break; diff --git a/Client/game_sa/CPoolsSA.h b/Client/game_sa/CPoolsSA.h index f3b09a4101..bb656b20ea 100644 --- a/Client/game_sa/CPoolsSA.h +++ b/Client/game_sa/CPoolsSA.h @@ -85,6 +85,7 @@ class CPoolsSA : public CPools int GetNumberOfUsedSpaces(ePools pools); int GetPoolDefaultCapacity(ePools pool); + int GetPoolDefaultModdedCapacity(ePools pool); int GetPoolCapacity(ePools pool); void SetPoolCapacity(ePools pool, int iValue); diff --git a/Client/game_sa/MemSA.h b/Client/game_sa/MemSA.h new file mode 100644 index 0000000000..34d4a949ea --- /dev/null +++ b/Client/game_sa/MemSA.h @@ -0,0 +1,39 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/MemSA.h + * PURPOSE: C memory functions in SA + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +namespace MemSA +{ + size_t msize(const void* p) + { + using gta_msize = size_t (*__cdecl)(const void*); + return ((gta_msize)0x828C4A)(p); + }; + + void* malloc(size_t count) + { + using gta_malloc = void* (*__cdecl)(size_t a1); + return ((gta_malloc)0x824257)(count); + }; + + template + T* malloc_struct(size_t count) + { + return static_cast(MemSA::malloc(sizeof(T) * count)); + } + + void free(void* p) + { + using gta_free = void (*__cdecl)(void* p); + return ((gta_free)0x82413F)(p); + }; +} diff --git a/Client/mods/deathmatch/logic/CClientBuilding.h b/Client/mods/deathmatch/logic/CClientBuilding.h index e8ef8d3b22..17667b3e1d 100644 --- a/Client/mods/deathmatch/logic/CClientBuilding.h +++ b/Client/mods/deathmatch/logic/CClientBuilding.h @@ -42,11 +42,11 @@ class CClientBuilding : public CClientEntity eClientEntityType GetType() const noexcept { return CCLIENTBUILDING; } - void Create(); - void Destroy(); bool IsValid() const noexcept { return m_pBuilding != nullptr; }; private: + void Create(); + void Destroy(); void Recreate() { diff --git a/Client/mods/deathmatch/logic/CClientBuildingManager.cpp b/Client/mods/deathmatch/logic/CClientBuildingManager.cpp index 13a3246497..5fe42baf23 100644 --- a/Client/mods/deathmatch/logic/CClientBuildingManager.cpp +++ b/Client/mods/deathmatch/logic/CClientBuildingManager.cpp @@ -85,3 +85,26 @@ bool CClientBuildingManager::IsValidPosition(const CVector& pos) noexcept return (pos.fX >= -WORLD_DISTANCE_FROM_CENTER && pos.fX <= WORLD_DISTANCE_FROM_CENTER && pos.fY >= -WORLD_DISTANCE_FROM_CENTER && pos.fY <= WORLD_DISTANCE_FROM_CENTER); } + +void CClientBuildingManager::DestroyAllForABit() +{ + for (CClientBuilding* building : GetBuildings()) + { + building->Destroy(); + } +} + +void CClientBuildingManager::RestoreDestroyed() +{ + for (CClientBuilding* building : GetBuildings()) + { + building->Create(); + + if (!building->IsValid()) + { + // User creates too much buildings + // We can't restore them all + delete building; + } + } +} diff --git a/Client/mods/deathmatch/logic/CClientBuildingManager.h b/Client/mods/deathmatch/logic/CClientBuildingManager.h index e47ce1e0eb..9a649dd5dd 100644 --- a/Client/mods/deathmatch/logic/CClientBuildingManager.h +++ b/Client/mods/deathmatch/logic/CClientBuildingManager.h @@ -31,6 +31,9 @@ class CClientBuildingManager static bool IsValidModel(uint16_t modelId); static bool IsValidPosition(const CVector& pos) noexcept; + void DestroyAllForABit(); + void RestoreDestroyed(); + private: void AddToList(CClientBuilding* pBuilding) { m_List.push_back(pBuilding); } void RemoveFromList(CClientBuilding* pBuilding); diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp index ba91b03381..2975971cbd 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp @@ -824,6 +824,29 @@ ADD_ENUM(_D3DFORMAT::D3DFMT_G32R32F, "g32r32f") ADD_ENUM(_D3DFORMAT::D3DFMT_A32B32G32R32F, "a32b32g32r32f") IMPLEMENT_ENUM_CLASS_END("surface-format") +IMPLEMENT_ENUM_BEGIN(ePools) +ADD_ENUM(ePools::BUILDING_POOL, "building") +ADD_ENUM(ePools::PED_POOL, "ped") +ADD_ENUM(ePools::OBJECT_POOL, "object") +ADD_ENUM(ePools::DUMMY_POOL, "dummy") +ADD_ENUM(ePools::VEHICLE_POOL, "vehicle") +ADD_ENUM(ePools::COL_MODEL_POOL, "col-model") +ADD_ENUM(ePools::TASK_POOL, "task") +ADD_ENUM(ePools::EVENT_POOL, "event") +ADD_ENUM(ePools::TASK_ALLOCATOR_POOL, "task-allocator") +ADD_ENUM(ePools::PED_INTELLIGENCE_POOL, "ped-intelligence") +ADD_ENUM(ePools::PED_ATTRACTOR_POOL, "ped-attractor") +ADD_ENUM(ePools::ENTRY_INFO_NODE_POOL, "entry-info-node") +ADD_ENUM(ePools::NODE_ROUTE_POOL, "node-route") +ADD_ENUM(ePools::PATROL_ROUTE_POOL, "patrol-route") +ADD_ENUM(ePools::POINT_ROUTE_POOL, "point-route") +ADD_ENUM(ePools::POINTER_DOUBLE_LINK_POOL, "pointer-double-link-pool") +ADD_ENUM(ePools::POINTER_SINGLE_LINK_POOL, "pointer-single-link-pool") +ADD_ENUM(ePools::ENV_MAP_MATERIAL_POOL, "env-map-material") +ADD_ENUM(ePools::ENV_MAP_ATOMIC_POOL, "env-map-atomic") +ADD_ENUM(ePools::SPEC_MAP_MATERIAL_POOL, "spec-map-material") +IMPLEMENT_ENUM_END("gta-pool") + // // CResource from userdata // diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h index 7f0b98c679..f823a4f5bd 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h @@ -81,6 +81,7 @@ DECLARE_ENUM_CLASS(eSoundEffectParams::ParamEq); DECLARE_ENUM_CLASS(eSoundEffectParams::Reverb); DECLARE_ENUM_CLASS(eModelIdeFlag); DECLARE_ENUM_CLASS(_D3DFORMAT); +DECLARE_ENUM(ePools); class CRemoteCall; diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp index 7c0c31509b..e48121b6ec 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp @@ -65,42 +65,23 @@ void CLuaBuildingDefs::RemoveAllGameBuildings() { // We do not want to remove scripted buildings // But we need remove them from the buildings pool for a bit... - for (CClientBuilding* building : m_pBuildingManager->GetBuildings()) - { - building->Destroy(); - } + m_pBuildingManager->DestroyAllForABit(); // This function makes buildings backup without scripted buildings g_pGame->RemoveAllBuildings(); // ... And restore here - for (CClientBuilding* building : m_pBuildingManager->GetBuildings()) - { - building->Create(); - } + m_pBuildingManager->RestoreDestroyed(); } void CLuaBuildingDefs::RestoreGameBuildings() { // We want to restore the game buildings to the same positions as they were before the backup. // Remove scripted buildings for a bit - for (CClientBuilding* building : m_pBuildingManager->GetBuildings()) - { - building->Destroy(); - } + m_pBuildingManager->DestroyAllForABit(); g_pGame->RestoreGameBuildings(); // Restore what we can - for (CClientBuilding* building : m_pBuildingManager->GetBuildings()) - { - building->Create(); - - if (!building->IsValid()) - { - // User creates too much buildings - // We can't restore them all - delete building; - } - } + m_pBuildingManager->RestoreDestroyed(); } diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index 2111c41195..d97e644810 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -135,9 +135,12 @@ void CLuaEngineDefs::LoadFunctions() {"engineStreamingSetBufferSize", ArgumentParser}, {"engineStreamingGetBufferSize", ArgumentParser}, {"engineStreamingRestoreBufferSize", ArgumentParser}, - {"engineRequestTXD", ArgumentParser}, {"engineFreeTXD", ArgumentParser}, + {"engineGetPoolCapacity", ArgumentParser}, + {"engineGetPoolDefaultCapacity", ArgumentParser}, + {"engineGetPoolUsedCapacity", ArgumentParser}, + {"engineSetPoolCapacity", ArgumentParser}, // CLuaCFunctions::AddFunction ( "engineReplaceMatchingAtomics", EngineReplaceMatchingAtomics ); // CLuaCFunctions::AddFunction ( "engineReplaceWheelAtomics", EngineReplaceWheelAtomics ); @@ -2458,3 +2461,52 @@ bool CLuaEngineDefs::EngineFreeTXD(uint txdID) std::shared_ptr pModel = m_pManager->GetModelManager()->FindModelByID(MAX_MODEL_DFF_ID + txdID); return pModel && pModel->Deallocate(); } + +size_t CLuaEngineDefs::EngineGetPoolCapacity(ePools pool) +{ + return g_pGame->GetPools()->GetPoolCapacity(pool); +} + +size_t CLuaEngineDefs::EngineGetPoolDefaultCapacity(ePools pool) +{ + return g_pGame->GetPools()->GetPoolDefaultModdedCapacity(pool); +} + +size_t CLuaEngineDefs::EngineGetPoolUsedCapacity(ePools pool) +{ + return g_pGame->GetPools()->GetNumberOfUsedSpaces(pool); +} + +bool CLuaEngineDefs::EngineSetPoolCapacity(lua_State* luaVM, ePools pool, size_t newSize) +{ + size_t minSize = g_pGame->GetPools()->GetPoolDefaultModdedCapacity(pool); + if (newSize < minSize) + { + m_pScriptDebugging->LogWarning(luaVM, "Cannot set the pool capacity to less than the default capacity."); + return false; + } + + minSize = g_pGame->GetPools()->GetNumberOfUsedSpaces(pool); + if (newSize < minSize) + { + m_pScriptDebugging->LogWarning(luaVM, "Cannot set the pool capacity to less than the used capacity."); + return false; + } + + switch (pool) + { + case ePools::BUILDING_POOL: + { + m_pBuildingManager->DestroyAllForABit(); + + bool success = g_pGame->SetBuildingPoolSize(newSize); + + m_pBuildingManager->RestoreDestroyed(); + + return success; + } + default: + throw std::invalid_argument("Can not change this pool capacity"); + } + return true; +} diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h index 899d725915..5426c31015 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h @@ -80,6 +80,11 @@ class CLuaEngineDefs : public CLuaDefs static bool EngineSetModelVisibleTime(std::string strModelId, char cHourOn, char cHourOff); static std::variant> EngineGetModelVisibleTime(std::string strModelId); + static size_t EngineGetPoolCapacity(ePools pool); + static size_t EngineGetPoolDefaultCapacity(ePools pool); + static size_t EngineGetPoolUsedCapacity(ePools pool); + static bool EngineSetPoolCapacity(lua_State* luaVM, ePools pool, size_t newSize); + static uint EngineRequestTXD(lua_State* const luaVM, std::string strTxdName); static bool EngineFreeTXD(uint txdID); diff --git a/Client/sdk/game/CBuildingsPool.h b/Client/sdk/game/CBuildingsPool.h index 6a08ee5372..1d315df77d 100644 --- a/Client/sdk/game/CBuildingsPool.h +++ b/Client/sdk/game/CBuildingsPool.h @@ -25,6 +25,8 @@ class CBuildingsPool virtual CBuilding* AddBuilding(class CClientBuilding*, uint16_t modelId, CVector* vPos, CVector4D* vRot, uint8_t interior) = 0; virtual void RemoveBuilding(CBuilding* pObject) = 0; virtual bool HasFreeBuildingSlot() = 0; + virtual bool SetSize(int size) = 0; + virtual int GetSize() const = 0; virtual void RemoveAllBuildings() = 0; virtual void RestoreAllBuildings() = 0; diff --git a/Client/sdk/game/CGame.h b/Client/sdk/game/CGame.h index 6df946ad3c..1d1499acc3 100644 --- a/Client/sdk/game/CGame.h +++ b/Client/sdk/game/CGame.h @@ -259,4 +259,6 @@ class __declspec(novtable) CGame virtual void RemoveAllBuildings() = 0; virtual void RestoreGameBuildings() = 0; + + virtual bool SetBuildingPoolSize(size_t size) = 0; }; diff --git a/Client/sdk/game/CPools.h b/Client/sdk/game/CPools.h index b6374b6f75..72a53b69bd 100644 --- a/Client/sdk/game/CPools.h +++ b/Client/sdk/game/CPools.h @@ -97,6 +97,7 @@ class CPools virtual int GetNumberOfUsedSpaces(ePools pool) = 0; virtual int GetPoolDefaultCapacity(ePools pool) = 0; + virtual int GetPoolDefaultModdedCapacity(ePools pool) = 0; virtual int GetPoolCapacity(ePools pool) = 0; virtual void SetPoolCapacity(ePools pool, int iValue) = 0; From 56bb1b0dad79b4e44f9bad8f9c8f501574870b96 Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Tue, 19 Mar 2024 19:05:28 +0300 Subject: [PATCH 11/24] fix load crash --- Client/game_sa/CBuildingsPoolSA.cpp | 29 +++++++++++++++++++++++++---- Client/game_sa/CBuildingsPoolSA.h | 3 ++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Client/game_sa/CBuildingsPoolSA.cpp b/Client/game_sa/CBuildingsPoolSA.cpp index 68df9a1b50..6667fa3327 100644 --- a/Client/game_sa/CBuildingsPoolSA.cpp +++ b/Client/game_sa/CBuildingsPoolSA.cpp @@ -231,18 +231,22 @@ bool CBuildingsPoolSA::SetSize(int size) newBytemap[i].bEmpty = true; } + const uint32_t offset = (uint32_t)newObjects - (uint32_t)oldPool; if (oldPool != nullptr) { - UpdateIplEntrysPointers(oldPool, newObjects); + UpdateIplEntrysPointers(offset); + } + + if (m_pOriginalBuildingsBackup) + { + UpdateBackupLodPointers(offset); } return true; } -void CBuildingsPoolSA::UpdateIplEntrysPointers(void* oldPool, void* newObjects) +void CBuildingsPoolSA::UpdateIplEntrysPointers(uint32_t offset) { - uint32_t offset = (uint32_t)newObjects - (uint32_t)oldPool; - using buildings_array_t = CBuildingSAInterface* [1000]; using ipl_entry_array_t = buildings_array_t* [40]; ipl_entry_array_t* iplEntryArray = (ipl_entry_array_t*)0x8E3F08; @@ -265,6 +269,23 @@ void CBuildingsPoolSA::UpdateIplEntrysPointers(void* oldPool, void* newObjects) } } +void CBuildingsPoolSA::UpdateBackupLodPointers(uint32_t offset) +{ + std::array, MAX_BUILDINGS> *arr = m_pOriginalBuildingsBackup.get(); + for (auto i = 0; i < MAX_BUILDINGS; i++) + { + std::pair* data = &(*arr)[i]; + if (data->first) + { + CBuildingSAInterface* building = &data->second; + if (building->m_pLod != nullptr) + { + building->m_pLod = (CBuildingSAInterface*)((uint32_t)building->m_pLod + offset); + } + } + } +} + bool CBuildingsPoolSA::HasFreeBuildingSlot() { return (*m_ppBuildingPoolInterface)->GetFreeSlot() != -1; diff --git a/Client/game_sa/CBuildingsPoolSA.h b/Client/game_sa/CBuildingsPoolSA.h index f9e720835e..c47da30689 100644 --- a/Client/game_sa/CBuildingsPoolSA.h +++ b/Client/game_sa/CBuildingsPoolSA.h @@ -32,7 +32,8 @@ class CBuildingsPoolSA : public CBuildingsPool private: void RemoveBuildingFromWorld(CBuildingSAInterface* pBuilding); bool AddBuildingToPool(CClientBuilding* pClientBuilding, CBuildingSA* pBuilding); - void UpdateIplEntrysPointers(void* oldPool, void* newObjects); + void UpdateIplEntrysPointers(uint32_t offset); + void UpdateBackupLodPointers(uint32_t offset); private: SPoolData m_buildingPool; From cf89ee4298d0b04be0b303353f3f9076145130aa Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Tue, 19 Mar 2024 19:55:33 +0300 Subject: [PATCH 12/24] Fix crash --- Client/game_sa/CBuildingsPoolSA.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Client/game_sa/CBuildingsPoolSA.cpp b/Client/game_sa/CBuildingsPoolSA.cpp index 6667fa3327..4f4cb854da 100644 --- a/Client/game_sa/CBuildingsPoolSA.cpp +++ b/Client/game_sa/CBuildingsPoolSA.cpp @@ -140,6 +140,9 @@ void CBuildingsPoolSA::RemoveAllBuildings() (*m_pOriginalBuildingsBackup)[i].first = false; } } + + CPtrNodeSingleListSAInterface* coverList = reinterpret_cast*>(0xC1A2B8); + coverList->RemoveAllItems(); } void CBuildingsPoolSA::RestoreAllBuildings() From 85cd88330ea393fce4a9dcbe21a8b9800f429f03 Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Mon, 25 Mar 2024 21:27:08 +0300 Subject: [PATCH 13/24] Add cover manager and plant manager --- Client/game_sa/CBuildingsPoolSA.cpp | 10 ++++------ Client/game_sa/CCoverManagerSA.cpp | 18 ++++++++++++++++++ Client/game_sa/CCoverManagerSA.h | 17 +++++++++++++++++ Client/game_sa/CGameSA.cpp | 4 ++++ Client/game_sa/CGameSA.h | 6 ++++++ Client/game_sa/CPlantManagerSA.cpp | 15 +++++++++++++++ Client/game_sa/CPlantManagerSA.h | 18 ++++++++++++++++++ 7 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 Client/game_sa/CCoverManagerSA.cpp create mode 100644 Client/game_sa/CCoverManagerSA.h create mode 100644 Client/game_sa/CPlantManagerSA.cpp create mode 100644 Client/game_sa/CPlantManagerSA.h diff --git a/Client/game_sa/CBuildingsPoolSA.cpp b/Client/game_sa/CBuildingsPoolSA.cpp index 4f4cb854da..7009b88889 100644 --- a/Client/game_sa/CBuildingsPoolSA.cpp +++ b/Client/game_sa/CBuildingsPoolSA.cpp @@ -141,8 +141,8 @@ void CBuildingsPoolSA::RemoveAllBuildings() } } - CPtrNodeSingleListSAInterface* coverList = reinterpret_cast*>(0xC1A2B8); - coverList->RemoveAllItems(); + pGame->GetCoverManager()->RemoveAllCovers(); + pGame->GetPlantManager()->RemoveAllPlants(); } void CBuildingsPoolSA::RestoreAllBuildings() @@ -173,12 +173,10 @@ void CBuildingsPoolSA::RemoveBuildingFromWorld(CBuildingSAInterface* pBuilding) pGame->GetWorld()->Remove(pBuilding, CBuildingPool_Destructor); // Remove building from cover list - CPtrNodeSingleListSAInterface* coverList = reinterpret_cast*>(0xC1A2B8); - coverList->RemoveItem(pBuilding); + pGame->GetCoverManager()->RemoveCover(pBuilding); // Remove plant - using CPlantColEntry_Remove = CEntitySAInterface* (*)(CEntitySAInterface*); - ((CPlantColEntry_Remove)0x5DBEF0)(pBuilding); + pGame->GetPlantManager()->RemovePlant(pBuilding); if (pBuilding->m_pRwObject != nullptr) { diff --git a/Client/game_sa/CCoverManagerSA.cpp b/Client/game_sa/CCoverManagerSA.cpp new file mode 100644 index 0000000000..d56ef67b1e --- /dev/null +++ b/Client/game_sa/CCoverManagerSA.cpp @@ -0,0 +1,18 @@ + +#include "StdInc.h" +#include "CCoverManagerSA.h" + +CCoverManagerSA::CCoverManagerSA() +{ + m_pCoverList = reinterpret_cast*>(0xC1A2B8); +} + +void CCoverManagerSA::RemoveAllCovers() +{ + m_pCoverList->RemoveAllItems(); +} + +void CCoverManagerSA::RemoveCover(CEntitySAInterface* entity) +{ + m_pCoverList->RemoveItem(entity); +} diff --git a/Client/game_sa/CCoverManagerSA.h b/Client/game_sa/CCoverManagerSA.h new file mode 100644 index 0000000000..209cfb44c8 --- /dev/null +++ b/Client/game_sa/CCoverManagerSA.h @@ -0,0 +1,17 @@ +#pragma once + +#include "CEntitySA.h" +#include "CPtrNodeSingleListSA.h" + +class CCoverManagerSA +{ +public: + CCoverManagerSA(); + ~CCoverManagerSA() = default; + + void RemoveAllCovers(); + void RemoveCover(CEntitySAInterface* entity); + +private: + CPtrNodeSingleListSAInterface* m_pCoverList; +}; diff --git a/Client/game_sa/CGameSA.cpp b/Client/game_sa/CGameSA.cpp index d953817710..22a5dc1787 100644 --- a/Client/game_sa/CGameSA.cpp +++ b/Client/game_sa/CGameSA.cpp @@ -139,6 +139,8 @@ CGameSA::CGameSA() m_pPointLights = new CPointLightsSA(); m_collisionStore = new CColStoreSA(); m_pIplStore = new CIplStoreSA(); + m_pCoverManager = new CCoverManagerSA(); + m_pPlantManager = new CPlantManagerSA(); // Normal weapon types (WEAPONSKILL_STD) for (int i = 0; i < NUM_WeaponInfosStdSkill; i++) @@ -276,6 +278,8 @@ CGameSA::~CGameSA() delete reinterpret_cast(m_pPointLights); delete static_cast(m_collisionStore); delete static_cast(m_pIplStore); + delete m_pCoverManager; + delete m_pPlantManager; delete[] ModelInfo; delete[] ObjectGroupsInfo; diff --git a/Client/game_sa/CGameSA.h b/Client/game_sa/CGameSA.h index 57cfa987ee..3c50501798 100644 --- a/Client/game_sa/CGameSA.h +++ b/Client/game_sa/CGameSA.h @@ -15,6 +15,8 @@ #include "CFxManagerSA.h" #include "CModelInfoSA.h" #include "CStreamingSA.h" +#include "CCoverManagerSA.h" +#include "CPlantManagerSA.h" class CAnimBlendClumpDataSAInterface; class CObjectGroupPhysicalPropertiesSA; @@ -153,6 +155,8 @@ class CGameSA : public CGame CRenderWareSA* GetRenderWareSA() { return m_pRenderWare; } CFxManagerSA* GetFxManagerSA() { return m_pFxManager; } CIplStore* GetIplStore() { return m_pIplStore; }; + CCoverManagerSA* GetCoverManager() const noexcept { return m_pCoverManager; }; + CPlantManagerSA* GetPlantManager() const noexcept { return m_pPlantManager; }; CWeaponInfo* GetWeaponInfo(eWeaponType weapon, eWeaponSkill skill = WEAPONSKILL_STD); CModelInfo* GetModelInfo(DWORD dwModelID, bool bCanBeInvalid = false); @@ -313,6 +317,8 @@ class CGameSA : public CGame CPointLights* m_pPointLights; CColStore* m_collisionStore; CObjectGroupPhysicalProperties* m_pObjectGroupPhysicalProperties; + CCoverManagerSA* m_pCoverManager; + CPlantManagerSA* m_pPlantManager; CPad* m_pPad; CAERadioTrackManager* m_pCAERadioTrackManager; diff --git a/Client/game_sa/CPlantManagerSA.cpp b/Client/game_sa/CPlantManagerSA.cpp new file mode 100644 index 0000000000..cbbd6e4bea --- /dev/null +++ b/Client/game_sa/CPlantManagerSA.cpp @@ -0,0 +1,15 @@ + +#include "StdInc.h" +#include "CPlantManagerSA.h" + +void CPlantManagerSA::RemoveAllPlants() +{ + int removedCount = 0; + void* list = *(void**)0xC0399C; + while (list != nullptr) + { + RemovePlant((CEntitySAInterface*)list); + removedCount++; + } + int a = removedCount; +} diff --git a/Client/game_sa/CPlantManagerSA.h b/Client/game_sa/CPlantManagerSA.h new file mode 100644 index 0000000000..4ade022684 --- /dev/null +++ b/Client/game_sa/CPlantManagerSA.h @@ -0,0 +1,18 @@ +#pragma once + +#include "CEntitySA.h" + +class CPlantManagerSA +{ +public: + CPlantManagerSA() = default; + ~CPlantManagerSA() = default; + + void RemovePlant(CEntitySAInterface* enity) + { + using CPlantColEntry_Remove = CEntitySAInterface* (*)(CEntitySAInterface*); + ((CPlantColEntry_Remove)0x5DBEF0)(enity); + }; + + void RemoveAllPlants(); +}; From 4a7a5fa2930141dde64bc4e1c8a101e83bea4e55 Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Tue, 26 Mar 2024 21:11:46 +0300 Subject: [PATCH 14/24] Fix plant crash --- Client/game_sa/CPlantManagerSA.cpp | 33 ++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/Client/game_sa/CPlantManagerSA.cpp b/Client/game_sa/CPlantManagerSA.cpp index cbbd6e4bea..aef1849aae 100644 --- a/Client/game_sa/CPlantManagerSA.cpp +++ b/Client/game_sa/CPlantManagerSA.cpp @@ -1,15 +1,36 @@ #include "StdInc.h" #include "CPlantManagerSA.h" +#include "CPtrNodeSingleListSA.h" + +class CPlantLocTri; + +class CPlantColEntEntry +{ +public: + CEntitySAInterface* m_Entity; + CPlantLocTri** m_Objects; + uint16 m_numTriangles; + CPlantColEntEntry* m_NextEntry; + CPlantColEntEntry* m_PrevEntry; + +public: + void ReleaseEntry() { + using CPlantColEntEntry_ReleaseEntry = void* ( __thiscall *)(CPlantColEntEntry*); + ((CPlantColEntEntry_ReleaseEntry)0x5DB8A0)(this); + }; +}; void CPlantManagerSA::RemoveAllPlants() { - int removedCount = 0; - void* list = *(void**)0xC0399C; - while (list != nullptr) + while (true) { - RemovePlant((CEntitySAInterface*)list); - removedCount++; + auto list = reinterpret_cast(0xC0399C); + if (*list == nullptr) + { + break; + } + + (*list)->ReleaseEntry(); } - int a = removedCount; } From 8ed4a56ab48c554108a337a967b7e17039e58129 Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Thu, 11 Apr 2024 00:58:20 +0300 Subject: [PATCH 15/24] Fix dummies crash --- Client/game_sa/CBuildingsPoolSA.cpp | 36 +++++++++----- Client/game_sa/CCoverManagerSA.cpp | 8 ++- Client/game_sa/CDummyPoolSA.cpp | 76 +++++++++++++++++++++++++++++ Client/game_sa/CDummyPoolSA.h | 33 +++++++++++++ Client/game_sa/CGameSA.cpp | 2 + Client/game_sa/CPoolsSA.h | 3 ++ Client/sdk/game/CDummyPool.h | 22 +++++++++ Client/sdk/game/CPools.h | 2 + Client/sdk/game/Common.h | 1 + 9 files changed, 170 insertions(+), 13 deletions(-) create mode 100644 Client/game_sa/CDummyPoolSA.cpp create mode 100644 Client/game_sa/CDummyPoolSA.h create mode 100644 Client/sdk/game/CDummyPool.h diff --git a/Client/game_sa/CBuildingsPoolSA.cpp b/Client/game_sa/CBuildingsPoolSA.cpp index 7009b88889..f7f8aa3a5d 100644 --- a/Client/game_sa/CBuildingsPoolSA.cpp +++ b/Client/game_sa/CBuildingsPoolSA.cpp @@ -119,6 +119,9 @@ void CBuildingsPoolSA::RemoveAllBuildings() if (m_pOriginalBuildingsBackup) return; + pGame->GetCoverManager()->RemoveAllCovers(); + pGame->GetPlantManager()->RemoveAllPlants(); + m_pOriginalBuildingsBackup = std::make_unique, MAX_BUILDINGS>>(); auto pBuildsingsPool = (*m_ppBuildingPoolInterface); @@ -128,7 +131,16 @@ void CBuildingsPoolSA::RemoveAllBuildings() { CBuildingSAInterface* building = pBuildsingsPool->GetObject(i); - RemoveBuildingFromWorld(building); + //RemoveBuildingFromWorld(building); + + pGame->GetWorld()->Remove(building, CBuildingPool_Destructor); + + CEntitySA entity{}; + entity.SetInterface(building); + entity.DeleteRwObject(); + + using CEntity_ResolveReferences = void*(__thiscall*)(CEntitySAInterface*); + ((CEntity_ResolveReferences)0x571A40)(building); pBuildsingsPool->Release(i); @@ -140,9 +152,6 @@ void CBuildingsPoolSA::RemoveAllBuildings() (*m_pOriginalBuildingsBackup)[i].first = false; } } - - pGame->GetCoverManager()->RemoveAllCovers(); - pGame->GetPlantManager()->RemoveAllPlants(); } void CBuildingsPoolSA::RestoreAllBuildings() @@ -178,12 +187,15 @@ void CBuildingsPoolSA::RemoveBuildingFromWorld(CBuildingSAInterface* pBuilding) // Remove plant pGame->GetPlantManager()->RemovePlant(pBuilding); - if (pBuilding->m_pRwObject != nullptr) - { - CEntitySA entity{}; - entity.SetInterface(pBuilding); - entity.DeleteRwObject(); - } + CEntitySA entity{}; + entity.SetInterface(pBuilding); + entity.DeleteRwObject(); + + using CEntity_ResolveReferences = void * (__thiscall*)(CEntitySAInterface*); + ((CEntity_ResolveReferences)0x571A40)(pBuilding); + + //using CPlaceable_destructor = void*(__thiscall*)(CEntitySAInterface*); + //((CPlaceable_destructor)0x0054F490)(pBuilding); } bool CBuildingsPoolSA::SetSize(int size) @@ -195,8 +207,6 @@ bool CBuildingsPoolSA::SetSize(int size) if (oldPool != nullptr) { - //std::free((void*)pool->m_pObjects); - //free(pool->m_pObjects); MemSA::free(pool->m_pObjects); pool->m_pObjects = nullptr; } @@ -243,6 +253,8 @@ bool CBuildingsPoolSA::SetSize(int size) UpdateBackupLodPointers(offset); } + pGame->GetPools()->GetDummyPool().UpdateBuildingLods(oldPool, newObjects); + return true; } diff --git a/Client/game_sa/CCoverManagerSA.cpp b/Client/game_sa/CCoverManagerSA.cpp index d56ef67b1e..2c15262e5c 100644 --- a/Client/game_sa/CCoverManagerSA.cpp +++ b/Client/game_sa/CCoverManagerSA.cpp @@ -9,10 +9,16 @@ CCoverManagerSA::CCoverManagerSA() void CCoverManagerSA::RemoveAllCovers() { - m_pCoverList->RemoveAllItems(); + while (m_pCoverList->m_pList) + { + RemoveCover(m_pCoverList->m_pList->pItem); + } } void CCoverManagerSA::RemoveCover(CEntitySAInterface* entity) { m_pCoverList->RemoveItem(entity); + + //using CCover_RemoveCoverPointsForThisEntity = char (__cdecl*)(CEntitySAInterface*); + //((CCover_RemoveCoverPointsForThisEntity)0x698740)(entity); } diff --git a/Client/game_sa/CDummyPoolSA.cpp b/Client/game_sa/CDummyPoolSA.cpp new file mode 100644 index 0000000000..7f9b539a40 --- /dev/null +++ b/Client/game_sa/CDummyPoolSA.cpp @@ -0,0 +1,76 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CDummyPoolSA.cpp + * PURPOSE: Dummy pool class + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include "StdInc.h" +#include "CDummyPoolSA.h" + +CDummyPoolSA::CDummyPoolSA() +{ + m_ppDummyPoolInterface = (CPoolSAInterface**)0xB744A0; +} + +void CDummyPoolSA::RemoveAllBuildingLods() +{ + if (m_pLodBackup) + return; + + m_pLodBackup = std::make_unique>(); + + for (int i = 0; i < MAX_DUMMIES; i++) + { + CEntitySAInterface* object = (*m_ppDummyPoolInterface)->GetObject(i); + (*m_pLodBackup)[i] = object->m_pLod; + object->m_pLod = nullptr; + } +} + +void CDummyPoolSA::RestoreAllBuildingsLods() +{ + if (!m_pLodBackup) + return; + + for (int i = 0; i < MAX_DUMMIES; i++) + { + CEntitySAInterface* object = (*m_ppDummyPoolInterface)->GetObject(i); + object->m_pLod = (*m_pLodBackup)[i]; + } + + m_pLodBackup.release(); +} + +void CDummyPoolSA::UpdateBuildingLods(void* oldPool, void* newPool) +{ + const uint32_t offset = (uint32_t)newPool - (uint32_t)oldPool; + + if (m_pLodBackup) + { + for (int i = 0; i < MAX_DUMMIES; i++) + { + if ((*m_pLodBackup)[i] != nullptr) + { + (*m_pLodBackup)[i] = (CEntitySAInterface*)((uint32_t)(*m_pLodBackup)[i] + offset); + } + } + } + else + { + for (int i = 0; i < MAX_DUMMIES; i++) + { + CEntitySAInterface* object = (*m_ppDummyPoolInterface)->GetObject(i); + if (object->m_pLod) + { + object->m_pLod = (CEntitySAInterface*)((uint32_t)object->m_pLod + offset); + } + } + } +} diff --git a/Client/game_sa/CDummyPoolSA.h b/Client/game_sa/CDummyPoolSA.h new file mode 100644 index 0000000000..95d00b19f3 --- /dev/null +++ b/Client/game_sa/CDummyPoolSA.h @@ -0,0 +1,33 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CDummyPoolSA.h + * PURPOSE: Dummy pool class + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include +#include "CEntitySA.h" +#include "CPoolSAInterface.h" +#include + +class CDummyPoolSA : public CDummyPool +{ +public: + CDummyPoolSA(); + ~CDummyPoolSA() = default; + + void RemoveAllBuildingLods(); + void RestoreAllBuildingsLods(); + void UpdateBuildingLods(void* oldPool, void* newPool); + +private: + CPoolSAInterface** m_ppDummyPoolInterface; + + std::unique_ptr> m_pLodBackup; +}; diff --git a/Client/game_sa/CGameSA.cpp b/Client/game_sa/CGameSA.cpp index 22a5dc1787..192d194585 100644 --- a/Client/game_sa/CGameSA.cpp +++ b/Client/game_sa/CGameSA.cpp @@ -896,12 +896,14 @@ void CGameSA::RemoveAllBuildings() { m_pIplStore->SetDynamicIplStreamingEnabled(false); + m_pPools->GetDummyPool().RemoveAllBuildingLods(); m_pPools->GetBuildingsPool().RemoveAllBuildings(); } void CGameSA::RestoreGameBuildings() { m_pPools->GetBuildingsPool().RestoreAllBuildings(); + m_pPools->GetDummyPool().RestoreAllBuildingsLods(); m_pIplStore->SetDynamicIplStreamingEnabled(true, [](CIplSAInterface* ipl) { return memcmp("barriers", ipl->name, 8) != 0; }); } diff --git a/Client/game_sa/CPoolsSA.h b/Client/game_sa/CPoolsSA.h index bb656b20ea..7c7a536ccf 100644 --- a/Client/game_sa/CPoolsSA.h +++ b/Client/game_sa/CPoolsSA.h @@ -17,6 +17,7 @@ #include "CBuildingSA.h" #include "CTextureDictonarySA.h" #include "CBuildingsPoolSA.h" +#include "CDummyPoolSA.h" #define INVALID_POOL_ARRAY_ID 0xFFFFFFFF @@ -99,6 +100,7 @@ class CPoolsSA : public CPools ushort GetFreeTextureDictonarySlot(); CBuildingsPool& GetBuildingsPool() noexcept override { return m_BuildingsPool; }; + CDummyPool& GetDummyPool() noexcept { return m_DummyPool; }; private: // Pools @@ -112,6 +114,7 @@ class CPoolsSA : public CPools CPoolSAInterface** m_ppTxdPoolInterface; CBuildingsPoolSA m_BuildingsPool; + CDummyPoolSA m_DummyPool; bool m_bGetVehicleEnabled; }; diff --git a/Client/sdk/game/CDummyPool.h b/Client/sdk/game/CDummyPool.h new file mode 100644 index 0000000000..46308c304f --- /dev/null +++ b/Client/sdk/game/CDummyPool.h @@ -0,0 +1,22 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: sdk/game/CDummyPool.h + * PURPOSE: Dummy pool interface + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include "Common.h" + +class CDummyPool +{ +public: + virtual void RemoveAllBuildingLods() = 0; + virtual void RestoreAllBuildingsLods() = 0; + virtual void UpdateBuildingLods(void* oldPool, void* newPool) = 0; +}; diff --git a/Client/sdk/game/CPools.h b/Client/sdk/game/CPools.h index 72a53b69bd..7b99d9795a 100644 --- a/Client/sdk/game/CPools.h +++ b/Client/sdk/game/CPools.h @@ -13,6 +13,7 @@ #include "Common.h" #include "CBuildingsPool.h" +#include "CDummyPool.h" class CClientEntity; class CEntity; @@ -111,4 +112,5 @@ class CPools virtual ushort GetFreeTextureDictonarySlot() = 0; virtual CBuildingsPool& GetBuildingsPool() noexcept = 0; + virtual CDummyPool& GetDummyPool() noexcept = 0; }; diff --git a/Client/sdk/game/Common.h b/Client/sdk/game/Common.h index 503f4c9021..14108f5210 100644 --- a/Client/sdk/game/Common.h +++ b/Client/sdk/game/Common.h @@ -27,6 +27,7 @@ #define MAX_PEDS ( MAX_PEDS_MTA + 30 ) // 140 #define MAX_OBJECTS ( MAX_OBJECTS_MTA + 200 ) // 1200 #define MAX_BUILDINGS 13000 +#define MAX_DUMMIES 2500 #define MAX_ENTRY_INFO_NODES ( MAX_ENTRY_INFO_NODES_MTA + 600 ) // 72600 #define MAX_POINTER_SINGLE_LINKS ( MAX_POINTER_SINGLE_LINKS_MTA + 5000 ) // 90000 #define MAX_POINTER_DOUBLE_LINKS ( MAX_POINTER_DOUBLE_LINKS_MTA + 800 ) // 74800 From 34b22d38fffbdd4cfaf9aae0ee9de18c95e75346 Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Sun, 14 Apr 2024 18:33:00 +0300 Subject: [PATCH 16/24] Fix building --- Client/game_sa/CCoverManagerSA.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Client/game_sa/CCoverManagerSA.cpp b/Client/game_sa/CCoverManagerSA.cpp index 2c15262e5c..5e625cdad6 100644 --- a/Client/game_sa/CCoverManagerSA.cpp +++ b/Client/game_sa/CCoverManagerSA.cpp @@ -9,10 +9,7 @@ CCoverManagerSA::CCoverManagerSA() void CCoverManagerSA::RemoveAllCovers() { - while (m_pCoverList->m_pList) - { - RemoveCover(m_pCoverList->m_pList->pItem); - } + m_pCoverList->RemoveAllItems(); } void CCoverManagerSA::RemoveCover(CEntitySAInterface* entity) From 083b448b70994f5b9dd5be19a71a0ddd55bc0bb5 Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Sun, 14 Apr 2024 18:55:48 +0300 Subject: [PATCH 17/24] Fix shadows --- Client/game_sa/CBuildingsPoolSA.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Client/game_sa/CBuildingsPoolSA.cpp b/Client/game_sa/CBuildingsPoolSA.cpp index f7f8aa3a5d..05566e3831 100644 --- a/Client/game_sa/CBuildingsPoolSA.cpp +++ b/Client/game_sa/CBuildingsPoolSA.cpp @@ -122,6 +122,10 @@ void CBuildingsPoolSA::RemoveAllBuildings() pGame->GetCoverManager()->RemoveAllCovers(); pGame->GetPlantManager()->RemoveAllPlants(); + // Remove all shadows + using CStencilShadowObjects_dtorAll = void* (*)(); + ((CStencilShadowObjects_dtorAll)0x711390)(); + m_pOriginalBuildingsBackup = std::make_unique, MAX_BUILDINGS>>(); auto pBuildsingsPool = (*m_ppBuildingPoolInterface); @@ -196,6 +200,10 @@ void CBuildingsPoolSA::RemoveBuildingFromWorld(CBuildingSAInterface* pBuilding) //using CPlaceable_destructor = void*(__thiscall*)(CEntitySAInterface*); //((CPlaceable_destructor)0x0054F490)(pBuilding); + + // Remove shadows + using CStencilShadow_dtorByOwner = void* (__cdecl*)(CEntitySAInterface * a2); + ((CStencilShadow_dtorByOwner)0x711730)(pBuilding); } bool CBuildingsPoolSA::SetSize(int size) From a192fe36e7073b453b6231f5189653327a959906 Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Sun, 14 Apr 2024 19:32:58 +0300 Subject: [PATCH 18/24] Fix vehicle crash --- Client/game_sa/CBuildingsPoolSA.cpp | 15 +++++++++++++++ Client/game_sa/CBuildingsPoolSA.h | 3 +++ Client/game_sa/CPoolsSA.h | 7 ++----- Client/sdk/game/CPools.h | 1 + 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Client/game_sa/CBuildingsPoolSA.cpp b/Client/game_sa/CBuildingsPoolSA.cpp index 05566e3831..fea06c76fe 100644 --- a/Client/game_sa/CBuildingsPoolSA.cpp +++ b/Client/game_sa/CBuildingsPoolSA.cpp @@ -17,6 +17,7 @@ #include "CGameSA.h" #include "CPtrNodeSingleListSA.h" #include "MemSA.h" +#include "CVehicleSA.h" extern CGameSA* pGame; @@ -263,6 +264,8 @@ bool CBuildingsPoolSA::SetSize(int size) pGame->GetPools()->GetDummyPool().UpdateBuildingLods(oldPool, newObjects); + RemoveVehicleDamageLinks(); + return true; } @@ -307,6 +310,18 @@ void CBuildingsPoolSA::UpdateBackupLodPointers(uint32_t offset) } } +void CBuildingsPoolSA::RemoveVehicleDamageLinks() +{ + const int count = pGame->GetPools()->GetVehicleCount(); + for (int i = 0; i < count; i++) + { + auto* vehicle = pGame->GetPools()->GetVehicle(i); + { + vehicle->pEntity->GetVehicleInterface()->m_pCollidedEntity = nullptr; + } + } +} + bool CBuildingsPoolSA::HasFreeBuildingSlot() { return (*m_ppBuildingPoolInterface)->GetFreeSlot() != -1; diff --git a/Client/game_sa/CBuildingsPoolSA.h b/Client/game_sa/CBuildingsPoolSA.h index c47da30689..43a2646b73 100644 --- a/Client/game_sa/CBuildingsPoolSA.h +++ b/Client/game_sa/CBuildingsPoolSA.h @@ -9,6 +9,8 @@ * *****************************************************************************/ +#pragma once + #include #include #include "CPoolSAInterface.h" @@ -34,6 +36,7 @@ class CBuildingsPoolSA : public CBuildingsPool bool AddBuildingToPool(CClientBuilding* pClientBuilding, CBuildingSA* pBuilding); void UpdateIplEntrysPointers(uint32_t offset); void UpdateBackupLodPointers(uint32_t offset); + void RemoveVehicleDamageLinks(); private: SPoolData m_buildingPool; diff --git a/Client/game_sa/CPoolsSA.h b/Client/game_sa/CPoolsSA.h index 7c7a536ccf..2e0ba7427e 100644 --- a/Client/game_sa/CPoolsSA.h +++ b/Client/game_sa/CPoolsSA.h @@ -38,11 +38,8 @@ class CPoolsSA : public CPools public: void RemoveVehicle(CVehicle* pVehicle, bool bDelete = true); SClientEntity* GetVehicle(DWORD* pGameInterface); - unsigned long GetVehicleCount() - { - return m_vehiclePool.ulCount; - ; - } + SClientEntity* GetVehicle(size_t pos) { return &m_vehiclePool.arrayOfClientEntities[pos]; }; + unsigned long GetVehicleCount() { return m_vehiclePool.ulCount; }; void DeleteAllVehicles(); // Objects pool diff --git a/Client/sdk/game/CPools.h b/Client/sdk/game/CPools.h index 7b99d9795a..3319f71e32 100644 --- a/Client/sdk/game/CPools.h +++ b/Client/sdk/game/CPools.h @@ -69,6 +69,7 @@ class CPools virtual void RemoveVehicle(CVehicle* pVehicle, bool bDelete = true) = 0; virtual SClientEntity* GetVehicle(DWORD* pGameInterface) = 0; + virtual SClientEntity* GetVehicle(size_t pos) = 0; virtual unsigned long GetVehicleCount() = 0; // Objects pool From 5d366b9bf75edff3a470bf3f8feb2c4a98036bad Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Sun, 14 Apr 2024 20:44:02 +0300 Subject: [PATCH 19/24] Fix more vehicle crash --- Client/game_sa/CBuildingsPoolSA.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Client/game_sa/CBuildingsPoolSA.cpp b/Client/game_sa/CBuildingsPoolSA.cpp index fea06c76fe..021eeb188c 100644 --- a/Client/game_sa/CBuildingsPoolSA.cpp +++ b/Client/game_sa/CBuildingsPoolSA.cpp @@ -315,9 +315,16 @@ void CBuildingsPoolSA::RemoveVehicleDamageLinks() const int count = pGame->GetPools()->GetVehicleCount(); for (int i = 0; i < count; i++) { - auto* vehicle = pGame->GetPools()->GetVehicle(i); + auto* vehLinks = pGame->GetPools()->GetVehicle(i); + if (vehLinks->pEntity) { - vehicle->pEntity->GetVehicleInterface()->m_pCollidedEntity = nullptr; + CVehicleSAInterface* vehicle = vehLinks->pEntity->GetVehicleInterface(); + vehicle->m_pCollidedEntity = nullptr; + vehicle->pLastContactedEntity[0] = nullptr; + vehicle->pLastContactedEntity[1] = nullptr; + vehicle->pLastContactedEntity[2] = nullptr; + vehicle->pLastContactedEntity[3] = nullptr; + vehicle->m_ucCollisionState = 0; } } } From 4569a7200cea94a4965e5ed221f5f4b65d67b0c4 Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Sun, 28 Apr 2024 22:25:26 +0300 Subject: [PATCH 20/24] Fix cover crashes --- Client/game_sa/CCoverManagerSA.cpp | 26 +++++++++++++++-- Client/game_sa/CCoverManagerSA.h | 17 +++++++++-- Client/game_sa/CPhysicalSA.h | 3 +- Client/game_sa/CPtrNodeDoubleListSA.h | 41 +++++++++++++++++++++++++++ Client/game_sa/CPtrNodeSingleListSA.h | 2 +- 5 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 Client/game_sa/CPtrNodeDoubleListSA.h diff --git a/Client/game_sa/CCoverManagerSA.cpp b/Client/game_sa/CCoverManagerSA.cpp index 5e625cdad6..dee7782ffb 100644 --- a/Client/game_sa/CCoverManagerSA.cpp +++ b/Client/game_sa/CCoverManagerSA.cpp @@ -1,21 +1,41 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CCoverManagerSA.cpp + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ #include "StdInc.h" #include "CCoverManagerSA.h" CCoverManagerSA::CCoverManagerSA() { - m_pCoverList = reinterpret_cast*>(0xC1A2B8); + m_pCoverList = reinterpret_cast*>(0xC1A2B8); } void CCoverManagerSA::RemoveAllCovers() { + CPtrNodeDoubleLink* pNode = m_pCoverList->m_pNode; + while (pNode) + { + RemoveCoverFromArray(pNode->pItem); + pNode = pNode->pNext; + } m_pCoverList->RemoveAllItems(); } void CCoverManagerSA::RemoveCover(CEntitySAInterface* entity) { + RemoveCoverFromArray(entity); + m_pCoverList->RemoveItem(entity); +} - //using CCover_RemoveCoverPointsForThisEntity = char (__cdecl*)(CEntitySAInterface*); - //((CCover_RemoveCoverPointsForThisEntity)0x698740)(entity); +void CCoverManagerSA::RemoveCoverFromArray(CEntitySAInterface* entity) +{ + using CCover_RemoveCoverPointsForThisEntity = char(__cdecl*)(CEntitySAInterface*); + ((CCover_RemoveCoverPointsForThisEntity)0x698740)(entity); } diff --git a/Client/game_sa/CCoverManagerSA.h b/Client/game_sa/CCoverManagerSA.h index 209cfb44c8..6944b43604 100644 --- a/Client/game_sa/CCoverManagerSA.h +++ b/Client/game_sa/CCoverManagerSA.h @@ -1,7 +1,17 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CCoverManagerSA.h + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + #pragma once #include "CEntitySA.h" -#include "CPtrNodeSingleListSA.h" +#include "CPtrNodeDoubleListSA.h" class CCoverManagerSA { @@ -13,5 +23,8 @@ class CCoverManagerSA void RemoveCover(CEntitySAInterface* entity); private: - CPtrNodeSingleListSAInterface* m_pCoverList; + void RemoveCoverFromArray(CEntitySAInterface* entity); + +private: + CPtrNodeDoubleListSAInterface* m_pCoverList; }; diff --git a/Client/game_sa/CPhysicalSA.h b/Client/game_sa/CPhysicalSA.h index 7ac72168f1..2b1234150c 100644 --- a/Client/game_sa/CPhysicalSA.h +++ b/Client/game_sa/CPhysicalSA.h @@ -14,6 +14,7 @@ #include #include "CEntitySA.h" #include +#include "CPtrNodeDoubleListSA.h" #define FUNC_GetMoveSpeed 0x404460 #define FUNC_GetTurnSpeed 0x470030 @@ -102,7 +103,7 @@ class CPhysicalSAInterface : public CEntitySAInterface CVector m_vecAttachedRotation; // 268 CVector m_vecUnk; // 280 uint32 m_pad4; // 292 - class CPtrNodeDoubleLink* m_pControlCodeNodeLink; // 296 + CPtrNodeDoubleLink* m_pControlCodeNodeLink; // 296 float m_fLighting; // 300 float m_fLighting2; // 304 class CShadowDataSA* m_pShadowData; // 308 diff --git a/Client/game_sa/CPtrNodeDoubleListSA.h b/Client/game_sa/CPtrNodeDoubleListSA.h new file mode 100644 index 0000000000..fb6f6c9978 --- /dev/null +++ b/Client/game_sa/CPtrNodeDoubleListSA.h @@ -0,0 +1,41 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CPtrNodeDoubleListSA.h + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +template +class CPtrNodeDoubleLink +{ +public: + T* pItem; + CPtrNodeDoubleLink* pNext; + CPtrNodeDoubleLink* pPrev; +}; + +template +class CPtrNodeDoubleListSAInterface +{ +public: + CPtrNodeDoubleLink* m_pNode; + + void RemoveItem(T* pItem) + { + using CPtrListDoubleLinkSAInterface_RemoveItem = void(__thiscall*)(CPtrNodeDoubleListSAInterface * pLinkList, void* item); + ((CPtrListDoubleLinkSAInterface_RemoveItem)0x5336B0)(this, pItem); + }; + + void RemoveAllItems() + { + while (m_pNode) + { + RemoveItem(m_pNode->pItem); + } + }; +}; diff --git a/Client/game_sa/CPtrNodeSingleListSA.h b/Client/game_sa/CPtrNodeSingleListSA.h index 06df6a2575..f083614421 100644 --- a/Client/game_sa/CPtrNodeSingleListSA.h +++ b/Client/game_sa/CPtrNodeSingleListSA.h @@ -2,7 +2,7 @@ * * PROJECT: Multi Theft Auto v1.0 * LICENSE: See LICENSE in the top level directory - * FILE: game_sa/CPtrNodeSingleListSA.cpp + * FILE: game_sa/CPtrNodeSingleListSA.h * * Multi Theft Auto is available from http://www.multitheftauto.com/ * From 17eb1a832ecd572ae76bc3667ca909093a0e7835 Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Sun, 28 Apr 2024 22:53:21 +0300 Subject: [PATCH 21/24] Fix some style --- Client/game_sa/CBuildingsPoolSA.cpp | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/Client/game_sa/CBuildingsPoolSA.cpp b/Client/game_sa/CBuildingsPoolSA.cpp index 021eeb188c..6f8203a4a9 100644 --- a/Client/game_sa/CBuildingsPoolSA.cpp +++ b/Client/game_sa/CBuildingsPoolSA.cpp @@ -95,6 +95,12 @@ void CBuildingsPoolSA::RemoveBuilding(CBuilding* pBuilding) if (dwElementIndexInPool == UINT_MAX) return; + // Remove building from cover list + pGame->GetCoverManager()->RemoveCover(pInterface); + + // Remove plant + pGame->GetPlantManager()->RemovePlant(pInterface); + RemoveBuildingFromWorld(pInterface); // Remove col reference @@ -136,16 +142,7 @@ void CBuildingsPoolSA::RemoveAllBuildings() { CBuildingSAInterface* building = pBuildsingsPool->GetObject(i); - //RemoveBuildingFromWorld(building); - - pGame->GetWorld()->Remove(building, CBuildingPool_Destructor); - - CEntitySA entity{}; - entity.SetInterface(building); - entity.DeleteRwObject(); - - using CEntity_ResolveReferences = void*(__thiscall*)(CEntitySAInterface*); - ((CEntity_ResolveReferences)0x571A40)(building); + RemoveBuildingFromWorld(building); pBuildsingsPool->Release(i); @@ -186,12 +183,6 @@ void CBuildingsPoolSA::RemoveBuildingFromWorld(CBuildingSAInterface* pBuilding) // Remove building from world pGame->GetWorld()->Remove(pBuilding, CBuildingPool_Destructor); - // Remove building from cover list - pGame->GetCoverManager()->RemoveCover(pBuilding); - - // Remove plant - pGame->GetPlantManager()->RemovePlant(pBuilding); - CEntitySA entity{}; entity.SetInterface(pBuilding); entity.DeleteRwObject(); From 6e39ad2e6455c2d9e127e88338400ae2da175e80 Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Mon, 29 Apr 2024 22:59:57 +0300 Subject: [PATCH 22/24] Small refactor --- Client/game_sa/CBuildingsPoolSA.cpp | 22 ++++++---------------- Client/game_sa/CEntitySA.cpp | 15 --------------- Client/game_sa/CEntitySA.h | 20 ++++++++++++++++++-- 3 files changed, 24 insertions(+), 33 deletions(-) diff --git a/Client/game_sa/CBuildingsPoolSA.cpp b/Client/game_sa/CBuildingsPoolSA.cpp index 6f8203a4a9..a3cfe80e6b 100644 --- a/Client/game_sa/CBuildingsPoolSA.cpp +++ b/Client/game_sa/CBuildingsPoolSA.cpp @@ -168,10 +168,10 @@ void CBuildingsPoolSA::RestoreAllBuildings() if (originalData[i].first) { pBuildsingsPool->AllocateAt(i); - auto building = pBuildsingsPool->GetObject(i); - *building = originalData[i].second; + auto pBuilding = pBuildsingsPool->GetObject(i); + *pBuilding = originalData[i].second; - pGame->GetWorld()->Add(building, CBuildingPool_Constructor); + pGame->GetWorld()->Add(pBuilding, CBuildingPool_Constructor); } } @@ -183,19 +183,9 @@ void CBuildingsPoolSA::RemoveBuildingFromWorld(CBuildingSAInterface* pBuilding) // Remove building from world pGame->GetWorld()->Remove(pBuilding, CBuildingPool_Destructor); - CEntitySA entity{}; - entity.SetInterface(pBuilding); - entity.DeleteRwObject(); - - using CEntity_ResolveReferences = void * (__thiscall*)(CEntitySAInterface*); - ((CEntity_ResolveReferences)0x571A40)(pBuilding); - - //using CPlaceable_destructor = void*(__thiscall*)(CEntitySAInterface*); - //((CPlaceable_destructor)0x0054F490)(pBuilding); - - // Remove shadows - using CStencilShadow_dtorByOwner = void* (__cdecl*)(CEntitySAInterface * a2); - ((CStencilShadow_dtorByOwner)0x711730)(pBuilding); + pBuilding->DeleteRwObject(); + pBuilding->ResolveReferences(); + pBuilding->RemoveShadows(); } bool CBuildingsPoolSA::SetSize(int size) diff --git a/Client/game_sa/CEntitySA.cpp b/Client/game_sa/CEntitySA.cpp index 7ac45eaa31..4466a53fe4 100644 --- a/Client/game_sa/CEntitySA.cpp +++ b/Client/game_sa/CEntitySA.cpp @@ -189,21 +189,6 @@ void CEntitySA::SetupLighting() } } -// REWRITE THIS SHIT -void CEntitySA::DeleteRwObject() -{ - DWORD dwFunc = m_pInterface->vtbl->DeleteRwObject; - DWORD dwThis = (DWORD)m_pInterface; - if (dwFunc) - { - _asm - { - mov ecx, dwThis - call dwFunc - } - } -} - void CEntitySA::Render() { DWORD dwFunc = 0x59F180; // m_pInterface->vtbl->Render; diff --git a/Client/game_sa/CEntitySA.h b/Client/game_sa/CEntitySA.h index e9ea04a3cd..9de57edc0a 100644 --- a/Client/game_sa/CEntitySA.h +++ b/Client/game_sa/CEntitySA.h @@ -224,6 +224,24 @@ class CEntitySAInterface return numLodChildrenRendered & 0x1f; return -1; } + + void ResolveReferences() + { + using CEntity_ResolveReferences = void*(__thiscall*)(CEntitySAInterface*); + ((CEntity_ResolveReferences)0x571A40)(this); + }; + + void RemoveShadows() + { + using CStencilShadow_dtorByOwner = void*(__cdecl*)(CEntitySAInterface * pEntity); + ((CStencilShadow_dtorByOwner)0x711730)(this); + }; + + void DeleteRwObject() + { + using vtbl_DeleteRwObject = void(__thiscall*)(CEntitySAInterface * pEntity); + ((vtbl_DeleteRwObject)this->vtbl->DeleteRwObject)(this); + }; }; static_assert(sizeof(CEntitySAInterface) == 0x38, "Invalid size for CEntitySAInterface"); @@ -309,8 +327,6 @@ class CEntitySA : public virtual CEntity bool SetBoneRotation(eBone boneId, float yaw, float pitch, float roll); bool GetBonePosition(eBone boneId, CVector& position); bool SetBonePosition(eBone boneId, const CVector& position); - - void DeleteRwObject(); // CEntitySA interface virtual void OnChangingPosition(const CVector& vecNewPosition) {} From c9c8dd8fe2d0b85f309d190d4d006c5e98a71c47 Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Mon, 29 Apr 2024 23:44:17 +0300 Subject: [PATCH 23/24] Fix memory leak --- Client/game_sa/CBuildingsPoolSA.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/game_sa/CBuildingsPoolSA.cpp b/Client/game_sa/CBuildingsPoolSA.cpp index a3cfe80e6b..38100631bd 100644 --- a/Client/game_sa/CBuildingsPoolSA.cpp +++ b/Client/game_sa/CBuildingsPoolSA.cpp @@ -175,7 +175,7 @@ void CBuildingsPoolSA::RestoreAllBuildings() } } - m_pOriginalBuildingsBackup.release(); + m_pOriginalBuildingsBackup = nullptr; } void CBuildingsPoolSA::RemoveBuildingFromWorld(CBuildingSAInterface* pBuilding) From 1829272e035ff150a8a0a7eb01bc43f53341f5cf Mon Sep 17 00:00:00 2001 From: Uladzislau Nikalayevich Date: Mon, 29 Apr 2024 23:49:58 +0300 Subject: [PATCH 24/24] Rename SetSize to resize --- Client/game_sa/CBuildingsPoolSA.cpp | 6 +++--- Client/game_sa/CBuildingsPoolSA.h | 2 +- Client/game_sa/CGameSA.cpp | 2 +- Client/sdk/game/CBuildingsPool.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Client/game_sa/CBuildingsPoolSA.cpp b/Client/game_sa/CBuildingsPoolSA.cpp index 38100631bd..b0f5812a9d 100644 --- a/Client/game_sa/CBuildingsPoolSA.cpp +++ b/Client/game_sa/CBuildingsPoolSA.cpp @@ -188,7 +188,7 @@ void CBuildingsPoolSA::RemoveBuildingFromWorld(CBuildingSAInterface* pBuilding) pBuilding->RemoveShadows(); } -bool CBuildingsPoolSA::SetSize(int size) +bool CBuildingsPoolSA::Resize(int size) { auto* pool = (*m_ppBuildingPoolInterface); const int curretnSize = pool->m_nSize; @@ -210,7 +210,7 @@ bool CBuildingsPoolSA::SetSize(int size) CBuildingSAInterface* newObjects = MemSA::malloc_struct(size); if (newObjects == nullptr) { - SetSize(curretnSize); + Resize(curretnSize); return false; } @@ -218,7 +218,7 @@ bool CBuildingsPoolSA::SetSize(int size) if (newBytemap == nullptr) { MemSA::free(newObjects); - SetSize(curretnSize); + Resize(curretnSize); return false; } diff --git a/Client/game_sa/CBuildingsPoolSA.h b/Client/game_sa/CBuildingsPoolSA.h index 43a2646b73..0a2be522d5 100644 --- a/Client/game_sa/CBuildingsPoolSA.h +++ b/Client/game_sa/CBuildingsPoolSA.h @@ -28,7 +28,7 @@ class CBuildingsPoolSA : public CBuildingsPool void RemoveAllBuildings() override; void RestoreAllBuildings() override; - bool SetSize(int size) override; + bool Resize(int size) override; int GetSize() const override { return (*m_ppBuildingPoolInterface)->m_nSize; }; private: diff --git a/Client/game_sa/CGameSA.cpp b/Client/game_sa/CGameSA.cpp index 192d194585..fa0142228b 100644 --- a/Client/game_sa/CGameSA.cpp +++ b/Client/game_sa/CGameSA.cpp @@ -912,7 +912,7 @@ bool CGameSA::SetBuildingPoolSize(size_t size) { RemoveAllBuildings(); - bool status = m_pPools->GetBuildingsPool().SetSize(size); + bool status = m_pPools->GetBuildingsPool().Resize(size); RestoreGameBuildings(); diff --git a/Client/sdk/game/CBuildingsPool.h b/Client/sdk/game/CBuildingsPool.h index 1d315df77d..d490c162f7 100644 --- a/Client/sdk/game/CBuildingsPool.h +++ b/Client/sdk/game/CBuildingsPool.h @@ -25,7 +25,7 @@ class CBuildingsPool virtual CBuilding* AddBuilding(class CClientBuilding*, uint16_t modelId, CVector* vPos, CVector4D* vRot, uint8_t interior) = 0; virtual void RemoveBuilding(CBuilding* pObject) = 0; virtual bool HasFreeBuildingSlot() = 0; - virtual bool SetSize(int size) = 0; + virtual bool Resize(int size) = 0; virtual int GetSize() const = 0; virtual void RemoveAllBuildings() = 0;