Skip to content

Commit 9f65bc6

Browse files
Synchronize changes from 1.6 master branch [ci skip]
008eaa7 Add model streaming functions (#3611)
2 parents cdf052b + 008eaa7 commit 9f65bc6

14 files changed

+258
-3
lines changed

Client/game_sa/CModelInfoSA.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,16 @@ void CModelInfoSA::Remove()
440440
}
441441
}
442442

443+
bool CModelInfoSA::UnloadUnused()
444+
{
445+
if (m_pInterface->usNumberOfRefs == 0 && !m_pCustomClump && !m_pCustomColModel)
446+
{
447+
pGame->GetStreaming()->RemoveModel(m_dwModelID);
448+
return true;
449+
}
450+
return false;
451+
}
452+
443453
bool CModelInfoSA::IsLoaded()
444454
{
445455
if (DoIsLoaded())
@@ -1035,7 +1045,7 @@ void CModelInfoSA::StaticFlushPendingRestreamIPL()
10351045
for (it = removedModels.begin(); it != removedModels.end(); it++)
10361046
{
10371047
pGame->GetStreaming()->RemoveModel(*it);
1038-
pGame->GetStreaming()->GetStreamingInfo(*it)->loadState = 0;
1048+
pGame->GetStreaming()->GetStreamingInfo(*it)->loadState = eModelLoadState::LOADSTATE_NOT_LOADED;
10391049
}
10401050
}
10411051

Client/game_sa/CModelInfoSA.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ class CModelInfoSA : public CModelInfo
362362
BYTE GetVehicleType();
363363
void Request(EModelRequestType requestType, const char* szTag);
364364
void Remove();
365+
bool UnloadUnused();
365366
bool IsLoaded();
366367
bool DoIsLoaded();
367368
unsigned short GetFlags();

Client/mods/deathmatch/logic/CClientModel.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ bool CClientModel::Deallocate()
8484
if (!m_bAllocatedByUs)
8585
return false;
8686

87+
if (m_pParentResource)
88+
m_pParentResource->GetResourceModelStreamer()->FullyReleaseModel(m_iModelID);
89+
8790
SetParentResource(nullptr);
8891

8992
CModelInfo* pModelInfo = g_pGame->GetModelInfo(m_iModelID, true);

Client/mods/deathmatch/logic/CResource.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ CResource::CResource(unsigned short usNetID, const char* szResourceName, CClient
9494

9595
CResource::~CResource()
9696
{
97+
// Remove refrences from requested models
98+
m_modelStreamer.ReleaseAll();
99+
97100
// Deallocate all models that this resource allocated earlier
98101
g_pClientGame->GetManager()->GetModelManager()->DeallocateModelsAllocatedByResource(this);
99102

Client/mods/deathmatch/logic/CResource.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "CClientEntity.h"
1616
#include "CResourceConfigItem.h"
1717
#include "CResourceFile.h"
18+
#include "CResourceModelStreamer.h"
1819
#include "CElementGroup.h"
1920
#include <list>
2021

@@ -79,6 +80,8 @@ class CResource
7980
CClientEntity* GetResourceIFPRoot() { return m_pResourceIFPRoot; };
8081
CClientEntity* GetResourceIMGRoot() { return m_pResourceIMGRoot; };
8182

83+
CResourceModelStreamer* GetResourceModelStreamer() { return &m_modelStreamer; };
84+
8285
// This is to delete all the elements created in this resource that are created locally in this client
8386
void DeleteClientChildren();
8487

@@ -145,4 +148,6 @@ class CResource
145148
CFastHashSet<SString> m_exportedFunctions;
146149
CElementGroup* m_pDefaultElementGroup; // stores elements created by scripts in this resource
147150
std::list<SNoClientCacheScript> m_NoClientCacheScriptList;
151+
152+
CResourceModelStreamer m_modelStreamer{};
148153
};
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*****************************************************************************
2+
*
3+
* PROJECT: Multi Theft Auto
4+
* LICENSE: See LICENSE in the top level directory
5+
* FILE: mods/deathmatch/logic/CResourceModelStreamer.cpp
6+
* PURPOSE: Resource model manager
7+
*
8+
* Multi Theft Auto is available from https://www.multitheftauto.com/
9+
*
10+
*****************************************************************************/
11+
12+
#include "StdInc.h"
13+
14+
#include "CResourceModelStreamer.h"
15+
#include "CClientGame.h"
16+
#include <game/CStreaming.h>
17+
18+
bool CResourceModelStreamer::RequestModel(std::uint16_t modelId, bool addRef, bool blocking)
19+
{
20+
CModelInfo* model = g_pGame->GetModelInfo(modelId);
21+
22+
if (!model)
23+
return false;
24+
25+
if (addRef)
26+
{
27+
std::uint16_t refsCount = ++m_requestedModels[modelId];
28+
if (refsCount == 1)
29+
{
30+
model->ModelAddRef(blocking ? EModelRequestType::BLOCKING : EModelRequestType::NON_BLOCKING, "CResourceModelStreamer::RequestModel With reference");
31+
return true;
32+
}
33+
return false;
34+
}
35+
else
36+
{
37+
if (model->IsLoaded())
38+
{
39+
return false;
40+
}
41+
else
42+
{
43+
model->Request(blocking ? EModelRequestType::BLOCKING : EModelRequestType::NON_BLOCKING, "CResourceModelStreamer::RequestModel With out reference");
44+
return true;
45+
}
46+
}
47+
}
48+
49+
// Return true if model was unloaded
50+
bool CResourceModelStreamer::ReleaseModel(std::uint16_t modelId, bool removeRef)
51+
{
52+
if (removeRef)
53+
{
54+
auto refs = m_requestedModels.find(modelId);
55+
if (refs == m_requestedModels.end())
56+
return false;
57+
58+
std::uint16_t& refsCount = (*refs).second;
59+
60+
if (refsCount == 0)
61+
return false;
62+
63+
refsCount--;
64+
65+
if (refsCount != 0)
66+
return false;
67+
68+
CModelInfo* model = g_pGame->GetModelInfo(modelId);
69+
70+
if (!model)
71+
return false;
72+
73+
// Hack
74+
// This check will update models pending references
75+
model->IsLoaded();
76+
77+
// This call can unload the model
78+
model->RemoveRef();
79+
80+
return !model->IsLoaded();
81+
}
82+
else
83+
{
84+
CModelInfo* model = g_pGame->GetModelInfo(modelId);
85+
86+
if (!model)
87+
return false;
88+
89+
return model->UnloadUnused();
90+
}
91+
}
92+
93+
void CResourceModelStreamer::ReleaseAll()
94+
{
95+
for (const auto &modelRefs : m_requestedModels)
96+
{
97+
if (modelRefs.second > 0)
98+
{
99+
CModelInfo* model = g_pGame->GetModelInfo(modelRefs.first);
100+
model->RemoveRef();
101+
}
102+
}
103+
104+
m_requestedModels.clear();
105+
}
106+
107+
void CResourceModelStreamer::FullyReleaseModel(std::uint16_t modelId)
108+
{
109+
std::uint16_t &refsCount = m_requestedModels[modelId];
110+
111+
if (refsCount > 0)
112+
{
113+
refsCount = 0;
114+
115+
CModelInfo* model = g_pGame->GetModelInfo(modelId);
116+
117+
if (!model)
118+
return;
119+
120+
model->RemoveRef();
121+
}
122+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*****************************************************************************
2+
*
3+
* PROJECT: Multi Theft Auto
4+
* LICENSE: See LICENSE in the top level directory
5+
* FILE: mods/deathmatch/logic/CResourceModelStreamer.h
6+
* PURPOSE: Resource model manager
7+
*
8+
* Multi Theft Auto is available from https://www.multitheftauto.com/
9+
*
10+
*****************************************************************************/
11+
12+
#pragma once
13+
14+
#include <unordered_map>
15+
16+
class CResourceModelStreamer
17+
{
18+
public:
19+
CResourceModelStreamer() = default;
20+
~CResourceModelStreamer() = default;
21+
22+
bool RequestModel(std::uint16_t modelId, bool addRef = false, bool blocking = false);
23+
bool ReleaseModel(std::uint16_t modelId, bool removeRef = false);
24+
25+
void ReleaseAll();
26+
void FullyReleaseModel(std::uint16_t modelId);
27+
28+
private:
29+
std::unordered_map<std::uint16_t, std::uint16_t> m_requestedModels;
30+
};

Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,14 @@ ADD_ENUM(WEATHER_SANDSTORM, "Sandstorm")
896896
ADD_ENUM(WEATHER_RAINBOW, "Rainbow")
897897
IMPLEMENT_ENUM_END("world-property")
898898

899+
IMPLEMENT_ENUM_CLASS_BEGIN(eModelLoadState)
900+
ADD_ENUM(eModelLoadState::LOADSTATE_NOT_LOADED, "unloaded")
901+
ADD_ENUM(eModelLoadState::LOADSTATE_LOADED, "loaded")
902+
ADD_ENUM(eModelLoadState::LOADSTATE_REQUESTED, "requested")
903+
ADD_ENUM(eModelLoadState::LOADSTATE_READING, "reading")
904+
ADD_ENUM(eModelLoadState::LOADSTATE_FINISHING, "finishing")
905+
IMPLEMENT_ENUM_CLASS_END("model-load-state")
906+
899907
//
900908
// CResource from userdata
901909
//

Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <game/CAudioEngine.h>
1414
#include <game/CRenderWare.h>
1515
#include <game/CHud.h>
16+
#include <game/CStreaming.h>
1617
#include <type_traits>
1718

1819
enum eLuaType
@@ -85,6 +86,7 @@ DECLARE_ENUM_CLASS(eRenderStage);
8586
DECLARE_ENUM_CLASS(eFxParticleSystems);
8687
DECLARE_ENUM(ePools);
8788
DECLARE_ENUM(eWorldProperty);
89+
DECLARE_ENUM_CLASS(eModelLoadState);
8890

8991
class CRemoteCall;
9092

Client/mods/deathmatch/logic/lua/CLuaMain.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class CLuaMain //: public CClient
5959

6060
void ResetInstructionCount();
6161

62-
class CResource* GetResource() { return m_pResource; }
62+
class CResource* GetResource() const { return m_pResource; }
6363

6464
CXMLFile* CreateXML(const char* szFilename, bool bUseIDs = true, bool bReadOnly = false);
6565
CXMLNode* ParseString(const char* strXmlContent);

Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <game/CObjectGroupPhysicalProperties.h>
1515
#include <game/CStreaming.h>
1616
#include <lua/CLuaFunctionParser.h>
17+
#include "CLuaEngineDefs.h"
1718

1819
//! Set the CModelCacheManager limits
1920
//! By passing `nil`/no value the original values are restored
@@ -135,6 +136,9 @@ void CLuaEngineDefs::LoadFunctions()
135136
{"engineStreamingSetBufferSize", ArgumentParser<EngineStreamingSetBufferSize>},
136137
{"engineStreamingGetBufferSize", ArgumentParser<EngineStreamingGetBufferSize>},
137138
{"engineStreamingRestoreBufferSize", ArgumentParser<EngineStreamingRestoreBufferSize>},
139+
{"engineStreamingRequestModel", ArgumentParser<EngineStreamingRequestModel>},
140+
{"engineStreamingReleaseModel", ArgumentParser<EngineStreamingReleaseModel>},
141+
{"engineStreamingGetModelLoadState", ArgumentParser<EngineStreamingGetModelLoadState>},
138142
{"engineRequestTXD", ArgumentParser<EngineRequestTXD>},
139143
{"engineFreeTXD", ArgumentParser<EngineFreeTXD>},
140144
{"engineGetPoolCapacity", ArgumentParser<EngineGetPoolCapacity>},
@@ -2502,3 +2506,44 @@ bool CLuaEngineDefs::EngineSetPoolCapacity(lua_State* luaVM, ePools pool, size_t
25022506
}
25032507
return true;
25042508
}
2509+
2510+
bool CLuaEngineDefs::EngineStreamingRequestModel(lua_State* const luaVM, std::uint16_t modelId, std::optional<bool> addReference, std::optional<bool> blocking)
2511+
{
2512+
// Grab the lua main and the resource belonging to this script
2513+
CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM);
2514+
2515+
CModelInfo* pModelInfo = g_pGame->GetModelInfo(modelId);
2516+
2517+
if (modelId >= g_pGame->GetBaseIDforCOL() || !pModelInfo)
2518+
throw std::invalid_argument("Expected a valid model ID at argument 1");
2519+
2520+
// Get the resource we belong to
2521+
CResource* pResource = pLuaMain->GetResource();
2522+
2523+
return pResource->GetResourceModelStreamer()->RequestModel(modelId, addReference.value_or(false), blocking.value_or(false));
2524+
}
2525+
2526+
bool CLuaEngineDefs::EngineStreamingReleaseModel(lua_State* const luaVM, std::uint16_t modelId, std::optional<bool> removeReference)
2527+
{
2528+
// Grab the lua main and the resource belonging to this script
2529+
CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM);
2530+
2531+
CModelInfo* pModelInfo = g_pGame->GetModelInfo(modelId);
2532+
2533+
if (modelId >= g_pGame->GetBaseIDforCOL() || !pModelInfo)
2534+
throw std::invalid_argument("Expected a valid model ID at argument 1");
2535+
2536+
// Get the resource we belong to
2537+
CResource* pResource = pLuaMain->GetResource();
2538+
2539+
return pResource->GetResourceModelStreamer()->ReleaseModel(modelId, removeReference.value_or(false));
2540+
}
2541+
2542+
eModelLoadState CLuaEngineDefs::EngineStreamingGetModelLoadState(std::uint16_t modelId)
2543+
{
2544+
const auto allCount = g_pGame->GetCountOfAllFileIDs();
2545+
if (modelId >= g_pGame->GetCountOfAllFileIDs())
2546+
throw std::invalid_argument("Expected a valid model ID at argument 1");
2547+
2548+
return g_pGame->GetStreaming()->GetStreamingInfo(modelId)->loadState;
2549+
}

Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ class CLuaEngineDefs : public CLuaDefs
8888
static uint EngineRequestTXD(lua_State* const luaVM, std::string strTxdName);
8989
static bool EngineFreeTXD(uint txdID);
9090

91+
static bool EngineStreamingRequestModel(lua_State* const luaVM, std::uint16_t modelId, std::optional<bool> addReference, std::optional<bool> blocking);
92+
static bool EngineStreamingReleaseModel(lua_State* const luaVM, std::uint16_t modelId, std::optional<bool> removeReference);
93+
static eModelLoadState EngineStreamingGetModelLoadState(std::uint16_t modelId);
94+
9195
private:
9296
static void AddEngineColClass(lua_State* luaVM);
9397
static void AddEngineTxdClass(lua_State* luaVM);

Client/sdk/game/CModelInfo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ class CModelInfo
179179
virtual void RemoveRef(bool bRemoveExtraGTARef = false) = 0;
180180
virtual int GetRefCount() = 0;
181181
virtual bool ForceUnload() = 0;
182+
virtual bool UnloadUnused() = 0;
182183
virtual void DeallocateModel() = 0;
183184

184185
virtual float GetDistanceFromCentreOfMassToBaseOfModel() = 0;

Client/sdk/game/CStreaming.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,27 @@
1616
#define INVALID_ARCHIVE_ID 0xFF
1717
#define INVALID_STREAM_ID 0xFF
1818

19+
enum class eModelLoadState : std::uint32_t
20+
{
21+
// Model isn't loaded
22+
LOADSTATE_NOT_LOADED = 0,
23+
24+
// Model is loaded
25+
LOADSTATE_LOADED = 1,
26+
27+
// Model in request list, but not yet in loading channel (TODO: Verify this)
28+
LOADSTATE_REQUESTED = 2,
29+
30+
// Model is being read
31+
LOADSTATE_READING = 3,
32+
33+
// If the model is a `big` one this state is used to indicate
34+
// that the model's first half has been loaded and is yet to be
35+
// finished by loading the second half.
36+
// When it has been loaded the state is set to `LOADED`
37+
LOADSTATE_FINISHING = 4
38+
};
39+
1940
struct CStreamingInfo
2041
{
2142
uint16_t prevId = (uint16_t)-1;
@@ -25,7 +46,7 @@ struct CStreamingInfo
2546
uint8_t archiveId = 0u;
2647
uint32_t offsetInBlocks = 0u;
2748
uint32_t sizeInBlocks = 0u;
28-
uint32_t loadState = 0u;
49+
eModelLoadState loadState = eModelLoadState::LOADSTATE_NOT_LOADED;
2950
};
3051
static_assert(sizeof(CStreamingInfo) == 0x14, "Invalid size for CStreamingInfo");
3152

0 commit comments

Comments
 (0)