Skip to content

Commit 12d8bbf

Browse files
authored
Make single link node pool unlimited (#3986)
1 parent 13c35f8 commit 12d8bbf

16 files changed

+321
-78
lines changed

Client/game_sa/CDynamicPool.h

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*****************************************************************************
2+
*
3+
* PROJECT: Multi Theft Auto v1.0
4+
* LICENSE: See LICENSE in the top level directory
5+
* FILE: game_sa/CDynamicPool.h
6+
* PURPOSE: Custom implementation for SA pools
7+
*
8+
* Multi Theft Auto is available from http://www.multitheftauto.com/
9+
*
10+
*****************************************************************************/
11+
12+
#pragma once
13+
14+
#include <vector>
15+
#include <array>
16+
#include <memory>
17+
18+
template <typename PoolObjT>
19+
class CDynamicPoolPart
20+
{
21+
public:
22+
explicit CDynamicPoolPart(std::size_t size) : m_size{size}
23+
{
24+
m_items = std::make_unique<PoolObjT[]>(size);
25+
m_unusedIndices.reserve(size);
26+
for (std::size_t i = 0; i < size; i++)
27+
m_unusedIndices.push_back(i);
28+
}
29+
30+
PoolObjT* AllocateItem()
31+
{
32+
std::size_t index = m_unusedIndices.back();
33+
m_unusedIndices.pop_back();
34+
return &m_items[index];
35+
}
36+
37+
void RemoveItem(PoolObjT* item)
38+
{
39+
auto pos = item - m_items.get();
40+
m_unusedIndices.push_back(pos);
41+
}
42+
43+
bool OwnsItem(PoolObjT* item) const noexcept { return item >= m_items.get() && item < m_items.get() + m_size; }
44+
bool HasFreeSize() const noexcept { return m_unusedIndices.size() != 0; }
45+
std::size_t GetCapacity() const noexcept { return m_size; }
46+
std::size_t GetUsedSize() const noexcept { return m_size - m_unusedIndices.size(); }
47+
48+
private:
49+
std::unique_ptr<PoolObjT[]> m_items;
50+
std::vector<std::size_t> m_unusedIndices;
51+
const std::size_t m_size;
52+
};
53+
54+
template <std::size_t InitialSize, std::size_t AddSize>
55+
struct PoolGrowAddStrategy
56+
{
57+
static constexpr std::size_t GetInitialSize() { return InitialSize; }
58+
static constexpr std::size_t GetNextSize(std::size_t index) { return AddSize; }
59+
};
60+
61+
template <typename PoolObjT, typename GrowStrategy>
62+
class CDynamicPool
63+
{
64+
public:
65+
CDynamicPool()
66+
{
67+
constexpr size_t initialSize = GrowStrategy::GetInitialSize();
68+
m_poolParts.emplace_back(initialSize);
69+
}
70+
71+
PoolObjT* AllocateItem()
72+
{
73+
for (auto& pool : m_poolParts)
74+
{
75+
if (pool.HasFreeSize())
76+
return pool.AllocateItem();
77+
}
78+
79+
try
80+
{
81+
return AllocateNewPart().AllocateItem();
82+
}
83+
catch (const std::bad_alloc&)
84+
{
85+
assert(false && "Could not allocate a memory for CDynamicPoolPart");
86+
}
87+
}
88+
89+
void RemoveItem(PoolObjT* item)
90+
{
91+
for (auto& pool : m_poolParts)
92+
{
93+
if (pool.OwnsItem(item))
94+
{
95+
pool.RemoveItem(item);
96+
return;
97+
}
98+
}
99+
100+
assert(false && "Invalid item for CDynamicPool::RemoveItem");
101+
}
102+
103+
std::size_t GetCapacity() const noexcept
104+
{
105+
std::size_t size = 0;
106+
for (auto& pool : m_poolParts)
107+
size += pool.GetCapacity();
108+
109+
return size;
110+
}
111+
112+
std::size_t GetUsedSize() const noexcept
113+
{
114+
std::size_t size = 0;
115+
for (auto& pool : m_poolParts)
116+
size += pool.GetUsedSize();
117+
118+
return size;
119+
}
120+
121+
bool SetCapacity(std::size_t newSize) {
122+
if (newSize == 0)
123+
return false;
124+
125+
std::size_t currentSize = GetCapacity();
126+
127+
if (currentSize == newSize)
128+
return false;
129+
else if (currentSize < newSize)
130+
{
131+
// Grow
132+
while (currentSize < newSize)
133+
{
134+
try
135+
{
136+
auto& nextPart = AllocateNewPart();
137+
currentSize += nextPart.GetCapacity();
138+
}
139+
catch (const std::bad_alloc&)
140+
{
141+
return false;
142+
}
143+
}
144+
}
145+
else
146+
{
147+
// Shrink
148+
while (true)
149+
{
150+
auto& part = m_poolParts.back();
151+
if (part.GetUsedSize() != 0)
152+
return false;
153+
154+
currentSize -= part.GetCapacity();
155+
if (currentSize < newSize)
156+
return false;
157+
158+
m_poolParts.pop_back();
159+
160+
if (currentSize == newSize)
161+
return true;
162+
}
163+
}
164+
165+
return true;
166+
}
167+
168+
private:
169+
CDynamicPoolPart<PoolObjT>& AllocateNewPart()
170+
{
171+
const std::size_t nextSize = GrowStrategy::GetNextSize(m_poolParts.size());
172+
m_poolParts.emplace_back(nextSize);
173+
return m_poolParts.back();
174+
}
175+
176+
private:
177+
std::list<CDynamicPoolPart<PoolObjT>> m_poolParts;
178+
};

Client/game_sa/CGameSA.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
#include "CIplStoreSA.h"
6060
#include "CBuildingRemovalSA.h"
6161
#include "CCheckpointSA.h"
62+
#include "CPtrNodeSingleLinkPoolSA.h"
6263

6364
extern CGameSA* pGame;
6465

@@ -245,6 +246,7 @@ CGameSA::CGameSA()
245246
CVehicleSA::StaticSetHooks();
246247
CCheckpointSA::StaticSetHooks();
247248
CHudSA::StaticSetHooks();
249+
CPtrNodeSingleLinkPoolSA::StaticSetHooks();
248250
}
249251
catch (const std::bad_alloc& e)
250252
{

Client/game_sa/CPoolsSA.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -900,8 +900,7 @@ int CPoolsSA::GetPoolCapacity(ePools pool)
900900
iPtr = 0x550F82;
901901
break;
902902
case POINTER_SINGLE_LINK_POOL:
903-
iPtr = 0x550F46;
904-
break;
903+
return GetPtrNodeSingleLinkPool().GetCapacity();
905904
case ENV_MAP_MATERIAL_POOL:
906905
iPtr = 0x5DA08E;
907906
break;
@@ -1067,9 +1066,7 @@ int CPoolsSA::GetNumberOfUsedSpaces(ePools pool)
10671066
dwThis = CLASS_CPtrNodeDoubleLinkPool;
10681067
break;
10691068
case POINTER_SINGLE_LINK_POOL:
1070-
dwFunc = FUNC_CPtrNodeSingleLinkPool_GetNoOfUsedSpaces;
1071-
dwThis = CLASS_CPtrNodeSingleLinkPool;
1072-
break;
1069+
return GetPtrNodeSingleLinkPool().GetUsedSize();
10731070
default:
10741071
return -1;
10751072
}

Client/game_sa/CPoolsSA.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "CBuildingsPoolSA.h"
1919
#include "CDummyPoolSA.h"
2020
#include "CTxdPoolSA.h"
21+
#include "CPtrNodeSingleLinkPoolSA.h"
2122

2223
#define INVALID_POOL_ARRAY_ID 0xFFFFFFFF
2324

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

101103
private:
102104
// Pools
@@ -111,6 +113,7 @@ class CPoolsSA : public CPools
111113
CBuildingsPoolSA m_BuildingsPool;
112114
CDummyPoolSA m_DummyPool;
113115
CTxdPoolSA m_TxdPool;
116+
CPtrNodeSingleLinkPoolSA m_PtrNodeSingleLinkPool;
114117

115118
bool m_bGetVehicleEnabled;
116119
};
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*****************************************************************************
2+
*
3+
* PROJECT: Multi Theft Auto v1.0
4+
* LICENSE: See LICENSE in the top level directory
5+
* FILE: game_sa/CPtrNodeSingleLinkPoolSA.cpp
6+
* PURPOSE: Custom implementation for the CPtrNodeSingleLinkPool pool
7+
*
8+
* Multi Theft Auto is available from http://www.multitheftauto.com/
9+
*
10+
*****************************************************************************/
11+
12+
#include "StdInc.h"
13+
#include "CPtrNodeSingleLinkPoolSA.h"
14+
15+
CPtrNodeSingleLinkPoolSA::pool_t* CPtrNodeSingleLinkPoolSA::m_customPool = nullptr;
16+
17+
CPtrNodeSingleLinkPoolSA::CPtrNodeSingleLinkPoolSA()
18+
{
19+
if (!m_customPool)
20+
m_customPool = new CPtrNodeSingleLinkPoolSA::pool_t();
21+
}
22+
23+
constexpr std::uint32_t HOOKPOS_SingleLinkNodeConstructor = 0x552380;
24+
constexpr std::size_t HOOKSIZE_SingleLinkNodeConstructor = 6;
25+
static CPtrNodeSingleLinkPoolSA::pool_item_t* __cdecl HOOK_SingleLinkNodeConstructor()
26+
{
27+
return CPtrNodeSingleLinkPoolSA::GetPoolInstance()->AllocateItem();
28+
}
29+
30+
constexpr std::uint32_t HOOKPOS_SingleLinkNodeDestructor = 0x552390;
31+
constexpr std::size_t HOOKSIZE_SingleLinkNodeDestructor = 6;
32+
static CPtrNodeSingleLinkPoolSA::pool_item_t* __cdecl HOOK_SingleLinkNodeDestructor(CPtrNodeSingleLinkPoolSA::pool_item_t* item)
33+
{
34+
CPtrNodeSingleLinkPoolSA::GetPoolInstance()->RemoveItem(item);
35+
// The game doesen't use the return value
36+
return item;
37+
}
38+
39+
// Replace pool->RemoveItem here
40+
constexpr std::uint32_t HOOKPOS_CPtrListSingleLink_Flush = 0x55243B;
41+
constexpr std::size_t HOOKSIZE_CPtrListSingleLink_Flush = 6;
42+
constexpr std::uint32_t CONTINUE_CPtrListSingleLink_Flush = 0x55245B;
43+
static void _declspec(naked) HOOK_CPtrListSingleLink_Flush()
44+
{
45+
__asm {
46+
mov edi, ecx ; save register
47+
48+
; CPtrNodeSingleLinkPoolSA::m_customPool->RemoveItem(eax)
49+
50+
mov ecx, CPtrNodeSingleLinkPoolSA::m_customPool
51+
push eax
52+
call CPtrNodeSingleLinkPoolSA::pool_t::RemoveItem
53+
54+
mov ecx, edi ; restore
55+
jmp CONTINUE_CPtrListSingleLink_Flush
56+
}
57+
}
58+
59+
void CPtrNodeSingleLinkPoolSA::StaticSetHooks()
60+
{
61+
EZHookInstall(SingleLinkNodeConstructor);
62+
EZHookInstall(SingleLinkNodeDestructor);
63+
EZHookInstall(CPtrListSingleLink_Flush);
64+
65+
// Skip the original pool initialization
66+
MemCpy((void*)0x550F26, "\xEB\x2D", 2);
67+
}
68+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*****************************************************************************
2+
*
3+
* PROJECT: Multi Theft Auto v1.0
4+
* LICENSE: See LICENSE in the top level directory
5+
* FILE: game_sa/CPtrNodeSingleLinkPoolSA.h
6+
* PURPOSE: Custom implementation for the CPtrNodeSingleLinkPool pool
7+
*
8+
* Multi Theft Auto is available from http://www.multitheftauto.com/
9+
*
10+
*****************************************************************************/
11+
12+
#pragma once
13+
14+
#include "CPoolSAInterface.h"
15+
#include "CDynamicPool.h"
16+
#include "CPtrNodeSingleListSA.h"
17+
#include <game/CPtrNodeSingleLinkPool.h>
18+
19+
class CPtrNodeSingleLinkPoolSA final : public CPtrNodeSingleLinkPool
20+
{
21+
public:
22+
using pool_item_t = CPtrNodeSingleLink<void*>;
23+
using pool_t = CDynamicPool<pool_item_t, PoolGrowAddStrategy<MAX_POINTER_SINGLE_LINKS, MAX_POINTER_SINGLE_LINKS / 2>>;
24+
25+
CPtrNodeSingleLinkPoolSA();
26+
27+
std::size_t GetCapacity() const override { return m_customPool->GetCapacity(); }
28+
std::size_t GetUsedSize() const override { return m_customPool->GetUsedSize(); }
29+
30+
bool Resize(std::size_t newSize) override { return m_customPool->SetCapacity(newSize); };
31+
void ResetCapacity() override { m_customPool->SetCapacity(MAX_POINTER_SINGLE_LINKS); };
32+
33+
static auto* GetPoolInstance() { return m_customPool; }
34+
static void StaticSetHooks();
35+
private:
36+
static pool_t* m_customPool;
37+
};

Client/mods/deathmatch/logic/CClientGame.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,7 @@ CClientGame::~CClientGame()
515515
g_pGame->SetPreWeaponFireHandler(NULL);
516516
g_pGame->SetPostWeaponFireHandler(NULL);
517517
g_pGame->SetTaskSimpleBeHitHandler(NULL);
518+
g_pGame->GetPools()->GetPtrNodeSingleLinkPool().ResetCapacity();
518519
g_pGame->GetAudioEngine()->SetWorldSoundHandler(NULL);
519520
g_pCore->SetMessageProcessor(NULL);
520521
g_pCore->GetKeyBinds()->SetKeyStrokeHandler(NULL);

Client/mods/deathmatch/logic/CClientObjectManager.cpp

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -230,22 +230,7 @@ void CClientObjectManager::OnDestruction(CClientObject* pObject)
230230
void CClientObjectManager::UpdateLimitInfo()
231231
{
232232
m_iEntryInfoNodeEntries = g_pMultiplayer->EntryInfoNodePool_NoOfUsedSpaces();
233-
m_iPointerNodeSingleLinkEntries = g_pMultiplayer->PtrNodeSingleLinkPool_NoOfUsedSpaces();
234233
m_iPointerNodeDoubleLinkEntries = g_pMultiplayer->PtrNodeDoubleLinkPool_NoOfUsedSpaces();
235-
236-
/*
237-
CPools* pPools = g_pGame->GetPools();
238-
unsigned int nEntryInfoNodeEntries = pPools->GetEntryInfoNodePool()->GetNumberOfUsedSpaces();
239-
unsigned int nPointerNodeSingleLinkEntries = pPools->GetPointerNodeSingleLinkPool()->GetNumberOfUsedSpaces();
240-
unsigned int nPointerNodeDoubleLinkEntries = pPools->GetPointerNodeDoubleLinkPool()->GetNumberOfUsedSpaces();
241-
242-
g_pCore->ChatPrintf("%d = %d ### %d = %d ### %d = %d", false, nEntryInfoNodeEntries, m_iEntryInfoNodeEntries, nPointerNodeSingleLinkEntries,
243-
m_iPointerNodeSingleLinkEntries, nPointerNodeDoubleLinkEntries, m_iPointerNodeDoubleLinkEntries);
244-
245-
assert(nEntryInfoNodeEntries == m_iEntryInfoNodeEntries);
246-
assert(nPointerNodeSingleLinkEntries == m_iPointerNodeSingleLinkEntries);
247-
assert(nPointerNodeDoubleLinkEntries == m_iPointerNodeDoubleLinkEntries);
248-
*/
249234
}
250235

251236
bool CClientObjectManager::StaticIsObjectLimitReached()
@@ -278,18 +263,16 @@ bool CClientObjectManager::IsHardObjectLimitReached()
278263
return true;
279264

280265
// If we've run out of either of these limit, don't allow more objects
281-
if (m_iEntryInfoNodeEntries >= MAX_ENTRY_INFO_NODES_MTA || m_iPointerNodeSingleLinkEntries >= MAX_POINTER_SINGLE_LINKS_MTA ||
282-
m_iPointerNodeDoubleLinkEntries >= MAX_POINTER_DOUBLE_LINKS_MTA)
266+
if (m_iEntryInfoNodeEntries >= MAX_ENTRY_INFO_NODES_MTA || m_iPointerNodeDoubleLinkEntries >= MAX_POINTER_DOUBLE_LINKS_MTA)
283267
{
284268
if (!m_bDoneLimitWarning)
285269
{
286270
m_bDoneLimitWarning = true;
287271
SString strMessage(
288272
"CClientObjectManager reached limit -"
289273
" ENTRY_INFO_NODES:%d/%d"
290-
" POINTER_SINGLE_LINKS:%d/%d"
291274
" POINTER_DOUBLE_LINKS:%d/%d",
292-
m_iEntryInfoNodeEntries, MAX_ENTRY_INFO_NODES_MTA, m_iPointerNodeSingleLinkEntries, MAX_POINTER_SINGLE_LINKS_MTA,
275+
m_iEntryInfoNodeEntries, MAX_ENTRY_INFO_NODES_MTA,
293276
m_iPointerNodeDoubleLinkEntries, MAX_POINTER_DOUBLE_LINKS_MTA);
294277
g_pCore->GetConsole()->Echo(strMessage);
295278
AddReportLog(7430, strMessage);

0 commit comments

Comments
 (0)