Skip to content

Commit

Permalink
Make single link node pool unlimited (#3986)
Browse files Browse the repository at this point in the history
  • Loading branch information
TheNormalnij authored Feb 6, 2025
1 parent 13c35f8 commit 12d8bbf
Show file tree
Hide file tree
Showing 16 changed files with 321 additions and 78 deletions.
178 changes: 178 additions & 0 deletions Client/game_sa/CDynamicPool.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*****************************************************************************
*
* PROJECT: Multi Theft Auto v1.0
* LICENSE: See LICENSE in the top level directory
* FILE: game_sa/CDynamicPool.h
* PURPOSE: Custom implementation for SA pools
*
* Multi Theft Auto is available from http://www.multitheftauto.com/
*
*****************************************************************************/

#pragma once

#include <vector>
#include <array>
#include <memory>

template <typename PoolObjT>
class CDynamicPoolPart
{
public:
explicit CDynamicPoolPart(std::size_t size) : m_size{size}
{
m_items = std::make_unique<PoolObjT[]>(size);
m_unusedIndices.reserve(size);
for (std::size_t i = 0; i < size; i++)
m_unusedIndices.push_back(i);
}

PoolObjT* AllocateItem()
{
std::size_t index = m_unusedIndices.back();
m_unusedIndices.pop_back();
return &m_items[index];
}

void RemoveItem(PoolObjT* item)
{
auto pos = item - m_items.get();
m_unusedIndices.push_back(pos);
}

bool OwnsItem(PoolObjT* item) const noexcept { return item >= m_items.get() && item < m_items.get() + m_size; }
bool HasFreeSize() const noexcept { return m_unusedIndices.size() != 0; }
std::size_t GetCapacity() const noexcept { return m_size; }
std::size_t GetUsedSize() const noexcept { return m_size - m_unusedIndices.size(); }

private:
std::unique_ptr<PoolObjT[]> m_items;
std::vector<std::size_t> m_unusedIndices;
const std::size_t m_size;
};

template <std::size_t InitialSize, std::size_t AddSize>
struct PoolGrowAddStrategy
{
static constexpr std::size_t GetInitialSize() { return InitialSize; }
static constexpr std::size_t GetNextSize(std::size_t index) { return AddSize; }
};

template <typename PoolObjT, typename GrowStrategy>
class CDynamicPool
{
public:
CDynamicPool()
{
constexpr size_t initialSize = GrowStrategy::GetInitialSize();
m_poolParts.emplace_back(initialSize);
}

PoolObjT* AllocateItem()
{
for (auto& pool : m_poolParts)
{
if (pool.HasFreeSize())
return pool.AllocateItem();
}

try
{
return AllocateNewPart().AllocateItem();
}
catch (const std::bad_alloc&)
{
assert(false && "Could not allocate a memory for CDynamicPoolPart");
}
}

void RemoveItem(PoolObjT* item)
{
for (auto& pool : m_poolParts)
{
if (pool.OwnsItem(item))
{
pool.RemoveItem(item);
return;
}
}

assert(false && "Invalid item for CDynamicPool::RemoveItem");
}

std::size_t GetCapacity() const noexcept
{
std::size_t size = 0;
for (auto& pool : m_poolParts)
size += pool.GetCapacity();

return size;
}

std::size_t GetUsedSize() const noexcept
{
std::size_t size = 0;
for (auto& pool : m_poolParts)
size += pool.GetUsedSize();

return size;
}

bool SetCapacity(std::size_t newSize) {
if (newSize == 0)
return false;

std::size_t currentSize = GetCapacity();

if (currentSize == newSize)
return false;
else if (currentSize < newSize)
{
// Grow
while (currentSize < newSize)
{
try
{
auto& nextPart = AllocateNewPart();
currentSize += nextPart.GetCapacity();
}
catch (const std::bad_alloc&)
{
return false;
}
}
}
else
{
// Shrink
while (true)
{
auto& part = m_poolParts.back();
if (part.GetUsedSize() != 0)
return false;

currentSize -= part.GetCapacity();
if (currentSize < newSize)
return false;

m_poolParts.pop_back();

if (currentSize == newSize)
return true;
}
}

return true;
}

private:
CDynamicPoolPart<PoolObjT>& AllocateNewPart()
{
const std::size_t nextSize = GrowStrategy::GetNextSize(m_poolParts.size());
m_poolParts.emplace_back(nextSize);
return m_poolParts.back();
}

private:
std::list<CDynamicPoolPart<PoolObjT>> m_poolParts;
};
2 changes: 2 additions & 0 deletions Client/game_sa/CGameSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
#include "CIplStoreSA.h"
#include "CBuildingRemovalSA.h"
#include "CCheckpointSA.h"
#include "CPtrNodeSingleLinkPoolSA.h"

extern CGameSA* pGame;

Expand Down Expand Up @@ -245,6 +246,7 @@ CGameSA::CGameSA()
CVehicleSA::StaticSetHooks();
CCheckpointSA::StaticSetHooks();
CHudSA::StaticSetHooks();
CPtrNodeSingleLinkPoolSA::StaticSetHooks();
}
catch (const std::bad_alloc& e)
{
Expand Down
7 changes: 2 additions & 5 deletions Client/game_sa/CPoolsSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -900,8 +900,7 @@ int CPoolsSA::GetPoolCapacity(ePools pool)
iPtr = 0x550F82;
break;
case POINTER_SINGLE_LINK_POOL:
iPtr = 0x550F46;
break;
return GetPtrNodeSingleLinkPool().GetCapacity();
case ENV_MAP_MATERIAL_POOL:
iPtr = 0x5DA08E;
break;
Expand Down Expand Up @@ -1067,9 +1066,7 @@ int CPoolsSA::GetNumberOfUsedSpaces(ePools pool)
dwThis = CLASS_CPtrNodeDoubleLinkPool;
break;
case POINTER_SINGLE_LINK_POOL:
dwFunc = FUNC_CPtrNodeSingleLinkPool_GetNoOfUsedSpaces;
dwThis = CLASS_CPtrNodeSingleLinkPool;
break;
return GetPtrNodeSingleLinkPool().GetUsedSize();
default:
return -1;
}
Expand Down
3 changes: 3 additions & 0 deletions Client/game_sa/CPoolsSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "CBuildingsPoolSA.h"
#include "CDummyPoolSA.h"
#include "CTxdPoolSA.h"
#include "CPtrNodeSingleLinkPoolSA.h"

#define INVALID_POOL_ARRAY_ID 0xFFFFFFFF

Expand Down Expand Up @@ -97,6 +98,7 @@ class CPoolsSA : public CPools
CBuildingsPool& GetBuildingsPool() noexcept override { return m_BuildingsPool; };
CDummyPool& GetDummyPool() noexcept { return m_DummyPool; };
CTxdPool& GetTxdPool() noexcept { return m_TxdPool; };
CPtrNodeSingleLinkPool& GetPtrNodeSingleLinkPool() noexcept override { return m_PtrNodeSingleLinkPool; };

private:
// Pools
Expand All @@ -111,6 +113,7 @@ class CPoolsSA : public CPools
CBuildingsPoolSA m_BuildingsPool;
CDummyPoolSA m_DummyPool;
CTxdPoolSA m_TxdPool;
CPtrNodeSingleLinkPoolSA m_PtrNodeSingleLinkPool;

bool m_bGetVehicleEnabled;
};
Expand Down
68 changes: 68 additions & 0 deletions Client/game_sa/CPtrNodeSingleLinkPoolSA.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*****************************************************************************
*
* PROJECT: Multi Theft Auto v1.0
* LICENSE: See LICENSE in the top level directory
* FILE: game_sa/CPtrNodeSingleLinkPoolSA.cpp
* PURPOSE: Custom implementation for the CPtrNodeSingleLinkPool pool
*
* Multi Theft Auto is available from http://www.multitheftauto.com/
*
*****************************************************************************/

#include "StdInc.h"
#include "CPtrNodeSingleLinkPoolSA.h"

CPtrNodeSingleLinkPoolSA::pool_t* CPtrNodeSingleLinkPoolSA::m_customPool = nullptr;

CPtrNodeSingleLinkPoolSA::CPtrNodeSingleLinkPoolSA()
{
if (!m_customPool)
m_customPool = new CPtrNodeSingleLinkPoolSA::pool_t();
}

constexpr std::uint32_t HOOKPOS_SingleLinkNodeConstructor = 0x552380;
constexpr std::size_t HOOKSIZE_SingleLinkNodeConstructor = 6;
static CPtrNodeSingleLinkPoolSA::pool_item_t* __cdecl HOOK_SingleLinkNodeConstructor()
{
return CPtrNodeSingleLinkPoolSA::GetPoolInstance()->AllocateItem();
}

constexpr std::uint32_t HOOKPOS_SingleLinkNodeDestructor = 0x552390;
constexpr std::size_t HOOKSIZE_SingleLinkNodeDestructor = 6;
static CPtrNodeSingleLinkPoolSA::pool_item_t* __cdecl HOOK_SingleLinkNodeDestructor(CPtrNodeSingleLinkPoolSA::pool_item_t* item)
{
CPtrNodeSingleLinkPoolSA::GetPoolInstance()->RemoveItem(item);
// The game doesen't use the return value
return item;
}

// Replace pool->RemoveItem here
constexpr std::uint32_t HOOKPOS_CPtrListSingleLink_Flush = 0x55243B;
constexpr std::size_t HOOKSIZE_CPtrListSingleLink_Flush = 6;
constexpr std::uint32_t CONTINUE_CPtrListSingleLink_Flush = 0x55245B;
static void _declspec(naked) HOOK_CPtrListSingleLink_Flush()
{
__asm {
mov edi, ecx ; save register

; CPtrNodeSingleLinkPoolSA::m_customPool->RemoveItem(eax)

mov ecx, CPtrNodeSingleLinkPoolSA::m_customPool
push eax
call CPtrNodeSingleLinkPoolSA::pool_t::RemoveItem

mov ecx, edi ; restore
jmp CONTINUE_CPtrListSingleLink_Flush
}
}

void CPtrNodeSingleLinkPoolSA::StaticSetHooks()
{
EZHookInstall(SingleLinkNodeConstructor);
EZHookInstall(SingleLinkNodeDestructor);
EZHookInstall(CPtrListSingleLink_Flush);

// Skip the original pool initialization
MemCpy((void*)0x550F26, "\xEB\x2D", 2);
}

37 changes: 37 additions & 0 deletions Client/game_sa/CPtrNodeSingleLinkPoolSA.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*****************************************************************************
*
* PROJECT: Multi Theft Auto v1.0
* LICENSE: See LICENSE in the top level directory
* FILE: game_sa/CPtrNodeSingleLinkPoolSA.h
* PURPOSE: Custom implementation for the CPtrNodeSingleLinkPool pool
*
* Multi Theft Auto is available from http://www.multitheftauto.com/
*
*****************************************************************************/

#pragma once

#include "CPoolSAInterface.h"
#include "CDynamicPool.h"
#include "CPtrNodeSingleListSA.h"
#include <game/CPtrNodeSingleLinkPool.h>

class CPtrNodeSingleLinkPoolSA final : public CPtrNodeSingleLinkPool
{
public:
using pool_item_t = CPtrNodeSingleLink<void*>;
using pool_t = CDynamicPool<pool_item_t, PoolGrowAddStrategy<MAX_POINTER_SINGLE_LINKS, MAX_POINTER_SINGLE_LINKS / 2>>;

CPtrNodeSingleLinkPoolSA();

std::size_t GetCapacity() const override { return m_customPool->GetCapacity(); }
std::size_t GetUsedSize() const override { return m_customPool->GetUsedSize(); }

bool Resize(std::size_t newSize) override { return m_customPool->SetCapacity(newSize); };
void ResetCapacity() override { m_customPool->SetCapacity(MAX_POINTER_SINGLE_LINKS); };

static auto* GetPoolInstance() { return m_customPool; }
static void StaticSetHooks();
private:
static pool_t* m_customPool;
};
1 change: 1 addition & 0 deletions Client/mods/deathmatch/logic/CClientGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,7 @@ CClientGame::~CClientGame()
g_pGame->SetPreWeaponFireHandler(NULL);
g_pGame->SetPostWeaponFireHandler(NULL);
g_pGame->SetTaskSimpleBeHitHandler(NULL);
g_pGame->GetPools()->GetPtrNodeSingleLinkPool().ResetCapacity();
g_pGame->GetAudioEngine()->SetWorldSoundHandler(NULL);
g_pCore->SetMessageProcessor(NULL);
g_pCore->GetKeyBinds()->SetKeyStrokeHandler(NULL);
Expand Down
21 changes: 2 additions & 19 deletions Client/mods/deathmatch/logic/CClientObjectManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,22 +230,7 @@ void CClientObjectManager::OnDestruction(CClientObject* pObject)
void CClientObjectManager::UpdateLimitInfo()
{
m_iEntryInfoNodeEntries = g_pMultiplayer->EntryInfoNodePool_NoOfUsedSpaces();
m_iPointerNodeSingleLinkEntries = g_pMultiplayer->PtrNodeSingleLinkPool_NoOfUsedSpaces();
m_iPointerNodeDoubleLinkEntries = g_pMultiplayer->PtrNodeDoubleLinkPool_NoOfUsedSpaces();

/*
CPools* pPools = g_pGame->GetPools();
unsigned int nEntryInfoNodeEntries = pPools->GetEntryInfoNodePool()->GetNumberOfUsedSpaces();
unsigned int nPointerNodeSingleLinkEntries = pPools->GetPointerNodeSingleLinkPool()->GetNumberOfUsedSpaces();
unsigned int nPointerNodeDoubleLinkEntries = pPools->GetPointerNodeDoubleLinkPool()->GetNumberOfUsedSpaces();
g_pCore->ChatPrintf("%d = %d ### %d = %d ### %d = %d", false, nEntryInfoNodeEntries, m_iEntryInfoNodeEntries, nPointerNodeSingleLinkEntries,
m_iPointerNodeSingleLinkEntries, nPointerNodeDoubleLinkEntries, m_iPointerNodeDoubleLinkEntries);
assert(nEntryInfoNodeEntries == m_iEntryInfoNodeEntries);
assert(nPointerNodeSingleLinkEntries == m_iPointerNodeSingleLinkEntries);
assert(nPointerNodeDoubleLinkEntries == m_iPointerNodeDoubleLinkEntries);
*/
}

bool CClientObjectManager::StaticIsObjectLimitReached()
Expand Down Expand Up @@ -278,18 +263,16 @@ bool CClientObjectManager::IsHardObjectLimitReached()
return true;

// If we've run out of either of these limit, don't allow more objects
if (m_iEntryInfoNodeEntries >= MAX_ENTRY_INFO_NODES_MTA || m_iPointerNodeSingleLinkEntries >= MAX_POINTER_SINGLE_LINKS_MTA ||
m_iPointerNodeDoubleLinkEntries >= MAX_POINTER_DOUBLE_LINKS_MTA)
if (m_iEntryInfoNodeEntries >= MAX_ENTRY_INFO_NODES_MTA || m_iPointerNodeDoubleLinkEntries >= MAX_POINTER_DOUBLE_LINKS_MTA)
{
if (!m_bDoneLimitWarning)
{
m_bDoneLimitWarning = true;
SString strMessage(
"CClientObjectManager reached limit -"
" ENTRY_INFO_NODES:%d/%d"
" POINTER_SINGLE_LINKS:%d/%d"
" POINTER_DOUBLE_LINKS:%d/%d",
m_iEntryInfoNodeEntries, MAX_ENTRY_INFO_NODES_MTA, m_iPointerNodeSingleLinkEntries, MAX_POINTER_SINGLE_LINKS_MTA,
m_iEntryInfoNodeEntries, MAX_ENTRY_INFO_NODES_MTA,
m_iPointerNodeDoubleLinkEntries, MAX_POINTER_DOUBLE_LINKS_MTA);
g_pCore->GetConsole()->Echo(strMessage);
AddReportLog(7430, strMessage);
Expand Down
Loading

0 comments on commit 12d8bbf

Please sign in to comment.