Skip to content

Add model streaming functions #3611

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion Client/game_sa/CModelInfoSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,16 @@ void CModelInfoSA::Remove()
}
}

bool CModelInfoSA::UnloadUnused()
{
if (m_pInterface->usNumberOfRefs == 0 && !m_pCustomClump && !m_pCustomColModel)
{
pGame->GetStreaming()->RemoveModel(m_dwModelID);
return true;
}
return false;
}

bool CModelInfoSA::IsLoaded()
{
if (DoIsLoaded())
Expand Down Expand Up @@ -1035,7 +1045,7 @@ void CModelInfoSA::StaticFlushPendingRestreamIPL()
for (it = removedModels.begin(); it != removedModels.end(); it++)
{
pGame->GetStreaming()->RemoveModel(*it);
pGame->GetStreaming()->GetStreamingInfo(*it)->loadState = 0;
pGame->GetStreaming()->GetStreamingInfo(*it)->loadState = eModelLoadState::LOADSTATE_NOT_LOADED;
}
}

Expand Down
1 change: 1 addition & 0 deletions Client/game_sa/CModelInfoSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ class CModelInfoSA : public CModelInfo
BYTE GetVehicleType();
void Request(EModelRequestType requestType, const char* szTag);
void Remove();
bool UnloadUnused();
bool IsLoaded();
bool DoIsLoaded();
unsigned short GetFlags();
Expand Down
3 changes: 3 additions & 0 deletions Client/mods/deathmatch/logic/CClientModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ bool CClientModel::Deallocate()
if (!m_bAllocatedByUs)
return false;

if (m_pParentResource)
m_pParentResource->GetResourceModelStreamer()->FullyReleaseModel(m_iModelID);

SetParentResource(nullptr);

CModelInfo* pModelInfo = g_pGame->GetModelInfo(m_iModelID, true);
Expand Down
3 changes: 3 additions & 0 deletions Client/mods/deathmatch/logic/CResource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ CResource::CResource(unsigned short usNetID, const char* szResourceName, CClient

CResource::~CResource()
{
// Remove refrences from requested models
m_modelStreamer.ReleaseAll();

// Deallocate all models that this resource allocated earlier
g_pClientGame->GetManager()->GetModelManager()->DeallocateModelsAllocatedByResource(this);

Expand Down
5 changes: 5 additions & 0 deletions Client/mods/deathmatch/logic/CResource.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "CClientEntity.h"
#include "CResourceConfigItem.h"
#include "CResourceFile.h"
#include "CResourceModelStreamer.h"
#include "CElementGroup.h"
#include <list>

Expand Down Expand Up @@ -79,6 +80,8 @@ class CResource
CClientEntity* GetResourceIFPRoot() { return m_pResourceIFPRoot; };
CClientEntity* GetResourceIMGRoot() { return m_pResourceIMGRoot; };

CResourceModelStreamer* GetResourceModelStreamer() { return &m_modelStreamer; };

// This is to delete all the elements created in this resource that are created locally in this client
void DeleteClientChildren();

Expand Down Expand Up @@ -145,4 +148,6 @@ class CResource
CFastHashSet<SString> m_exportedFunctions;
CElementGroup* m_pDefaultElementGroup; // stores elements created by scripts in this resource
std::list<SNoClientCacheScript> m_NoClientCacheScriptList;

CResourceModelStreamer m_modelStreamer{};
};
122 changes: 122 additions & 0 deletions Client/mods/deathmatch/logic/CResourceModelStreamer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*****************************************************************************
*
* PROJECT: Multi Theft Auto
* LICENSE: See LICENSE in the top level directory
* FILE: mods/deathmatch/logic/CResourceModelStreamer.cpp
* PURPOSE: Resource model manager
*
* Multi Theft Auto is available from https://www.multitheftauto.com/
*
*****************************************************************************/

#include "StdInc.h"

#include "CResourceModelStreamer.h"
#include "CClientGame.h"
#include <game/CStreaming.h>

bool CResourceModelStreamer::RequestModel(std::uint16_t modelId, bool addRef, bool blocking)
{
CModelInfo* model = g_pGame->GetModelInfo(modelId);

if (!model)
return false;

if (addRef)
{
std::uint16_t refsCount = ++m_requestedModels[modelId];
if (refsCount == 1)
{
model->ModelAddRef(blocking ? EModelRequestType::BLOCKING : EModelRequestType::NON_BLOCKING, "CResourceModelStreamer::RequestModel With reference");
return true;
}
return false;
}
else
{
if (model->IsLoaded())
{
return false;
}
else
{
model->Request(blocking ? EModelRequestType::BLOCKING : EModelRequestType::NON_BLOCKING, "CResourceModelStreamer::RequestModel With out reference");
return true;
}
}
}

// Return true if model was unloaded
bool CResourceModelStreamer::ReleaseModel(std::uint16_t modelId, bool removeRef)
{
if (removeRef)
{
auto refs = m_requestedModels.find(modelId);
if (refs == m_requestedModels.end())
return false;

std::uint16_t& refsCount = (*refs).second;

if (refsCount == 0)
return false;

refsCount--;

if (refsCount != 0)
return false;

CModelInfo* model = g_pGame->GetModelInfo(modelId);

if (!model)
return false;

// Hack
// This check will update models pending references
model->IsLoaded();

// This call can unload the model
model->RemoveRef();

return !model->IsLoaded();
}
else
{
CModelInfo* model = g_pGame->GetModelInfo(modelId);

if (!model)
return false;

return model->UnloadUnused();
}
}

void CResourceModelStreamer::ReleaseAll()
{
for (const auto &modelRefs : m_requestedModels)
{
if (modelRefs.second > 0)
{
CModelInfo* model = g_pGame->GetModelInfo(modelRefs.first);
model->RemoveRef();
}
}

m_requestedModels.clear();
}

void CResourceModelStreamer::FullyReleaseModel(std::uint16_t modelId)
{
std::uint16_t &refsCount = m_requestedModels[modelId];

if (refsCount > 0)
{
refsCount = 0;

CModelInfo* model = g_pGame->GetModelInfo(modelId);

if (!model)
return;

model->RemoveRef();
}
}
30 changes: 30 additions & 0 deletions Client/mods/deathmatch/logic/CResourceModelStreamer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*****************************************************************************
*
* PROJECT: Multi Theft Auto
* LICENSE: See LICENSE in the top level directory
* FILE: mods/deathmatch/logic/CResourceModelStreamer.h
* PURPOSE: Resource model manager
*
* Multi Theft Auto is available from https://www.multitheftauto.com/
*
*****************************************************************************/

#pragma once

#include <unordered_map>

class CResourceModelStreamer
{
public:
CResourceModelStreamer() = default;
~CResourceModelStreamer() = default;

bool RequestModel(std::uint16_t modelId, bool addRef = false, bool blocking = false);
bool ReleaseModel(std::uint16_t modelId, bool removeRef = false);

void ReleaseAll();
void FullyReleaseModel(std::uint16_t modelId);

private:
std::unordered_map<std::uint16_t, std::uint16_t> m_requestedModels;
};
8 changes: 8 additions & 0 deletions Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,14 @@ ADD_ENUM(WEATHER_SANDSTORM, "Sandstorm")
ADD_ENUM(WEATHER_RAINBOW, "Rainbow")
IMPLEMENT_ENUM_END("world-property")

IMPLEMENT_ENUM_CLASS_BEGIN(eModelLoadState)
ADD_ENUM(eModelLoadState::LOADSTATE_NOT_LOADED, "unloaded")
ADD_ENUM(eModelLoadState::LOADSTATE_LOADED, "loaded")
ADD_ENUM(eModelLoadState::LOADSTATE_REQUESTED, "requested")
ADD_ENUM(eModelLoadState::LOADSTATE_READING, "reading")
ADD_ENUM(eModelLoadState::LOADSTATE_FINISHING, "finishing")
IMPLEMENT_ENUM_CLASS_END("model-load-state")

//
// CResource from userdata
//
Expand Down
2 changes: 2 additions & 0 deletions Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <game/CAudioEngine.h>
#include <game/CRenderWare.h>
#include <game/CHud.h>
#include <game/CStreaming.h>
#include <type_traits>

enum eLuaType
Expand Down Expand Up @@ -85,6 +86,7 @@ DECLARE_ENUM_CLASS(eRenderStage);
DECLARE_ENUM_CLASS(eFxParticleSystems);
DECLARE_ENUM(ePools);
DECLARE_ENUM(eWorldProperty);
DECLARE_ENUM_CLASS(eModelLoadState);

class CRemoteCall;

Expand Down
2 changes: 1 addition & 1 deletion Client/mods/deathmatch/logic/lua/CLuaMain.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class CLuaMain //: public CClient

void ResetInstructionCount();

class CResource* GetResource() { return m_pResource; }
class CResource* GetResource() const { return m_pResource; }

CXMLFile* CreateXML(const char* szFilename, bool bUseIDs = true, bool bReadOnly = false);
CXMLNode* ParseString(const char* strXmlContent);
Expand Down
45 changes: 45 additions & 0 deletions Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <game/CObjectGroupPhysicalProperties.h>
#include <game/CStreaming.h>
#include <lua/CLuaFunctionParser.h>
#include "CLuaEngineDefs.h"

//! Set the CModelCacheManager limits
//! By passing `nil`/no value the original values are restored
Expand Down Expand Up @@ -135,6 +136,9 @@ void CLuaEngineDefs::LoadFunctions()
{"engineStreamingSetBufferSize", ArgumentParser<EngineStreamingSetBufferSize>},
{"engineStreamingGetBufferSize", ArgumentParser<EngineStreamingGetBufferSize>},
{"engineStreamingRestoreBufferSize", ArgumentParser<EngineStreamingRestoreBufferSize>},
{"engineStreamingRequestModel", ArgumentParser<EngineStreamingRequestModel>},
{"engineStreamingReleaseModel", ArgumentParser<EngineStreamingReleaseModel>},
{"engineStreamingGetModelLoadState", ArgumentParser<EngineStreamingGetModelLoadState>},
{"engineRequestTXD", ArgumentParser<EngineRequestTXD>},
{"engineFreeTXD", ArgumentParser<EngineFreeTXD>},
{"engineGetPoolCapacity", ArgumentParser<EngineGetPoolCapacity>},
Expand Down Expand Up @@ -2502,3 +2506,44 @@ bool CLuaEngineDefs::EngineSetPoolCapacity(lua_State* luaVM, ePools pool, size_t
}
return true;
}

bool CLuaEngineDefs::EngineStreamingRequestModel(lua_State* const luaVM, std::uint16_t modelId, std::optional<bool> addReference, std::optional<bool> blocking)
{
// Grab the lua main and the resource belonging to this script
CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM);

CModelInfo* pModelInfo = g_pGame->GetModelInfo(modelId);

if (modelId >= g_pGame->GetBaseIDforCOL() || !pModelInfo)
throw std::invalid_argument("Expected a valid model ID at argument 1");

// Get the resource we belong to
CResource* pResource = pLuaMain->GetResource();

return pResource->GetResourceModelStreamer()->RequestModel(modelId, addReference.value_or(false), blocking.value_or(false));
}

bool CLuaEngineDefs::EngineStreamingReleaseModel(lua_State* const luaVM, std::uint16_t modelId, std::optional<bool> removeReference)
{
// Grab the lua main and the resource belonging to this script
CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM);

CModelInfo* pModelInfo = g_pGame->GetModelInfo(modelId);

if (modelId >= g_pGame->GetBaseIDforCOL() || !pModelInfo)
throw std::invalid_argument("Expected a valid model ID at argument 1");

// Get the resource we belong to
CResource* pResource = pLuaMain->GetResource();

return pResource->GetResourceModelStreamer()->ReleaseModel(modelId, removeReference.value_or(false));
}

eModelLoadState CLuaEngineDefs::EngineStreamingGetModelLoadState(std::uint16_t modelId)
{
const auto allCount = g_pGame->GetCountOfAllFileIDs();
if (modelId >= g_pGame->GetCountOfAllFileIDs())
throw std::invalid_argument("Expected a valid model ID at argument 1");

return g_pGame->GetStreaming()->GetStreamingInfo(modelId)->loadState;
}
4 changes: 4 additions & 0 deletions Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ class CLuaEngineDefs : public CLuaDefs
static uint EngineRequestTXD(lua_State* const luaVM, std::string strTxdName);
static bool EngineFreeTXD(uint txdID);

static bool EngineStreamingRequestModel(lua_State* const luaVM, std::uint16_t modelId, std::optional<bool> addReference, std::optional<bool> blocking);
static bool EngineStreamingReleaseModel(lua_State* const luaVM, std::uint16_t modelId, std::optional<bool> removeReference);
static eModelLoadState EngineStreamingGetModelLoadState(std::uint16_t modelId);

private:
static void AddEngineColClass(lua_State* luaVM);
static void AddEngineTxdClass(lua_State* luaVM);
Expand Down
1 change: 1 addition & 0 deletions Client/sdk/game/CModelInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ class CModelInfo
virtual void RemoveRef(bool bRemoveExtraGTARef = false) = 0;
virtual int GetRefCount() = 0;
virtual bool ForceUnload() = 0;
virtual bool UnloadUnused() = 0;
virtual void DeallocateModel() = 0;

virtual float GetDistanceFromCentreOfMassToBaseOfModel() = 0;
Expand Down
23 changes: 22 additions & 1 deletion Client/sdk/game/CStreaming.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,27 @@
#define INVALID_ARCHIVE_ID 0xFF
#define INVALID_STREAM_ID 0xFF

enum class eModelLoadState : std::uint32_t
{
// Model isn't loaded
LOADSTATE_NOT_LOADED = 0,

// Model is loaded
LOADSTATE_LOADED = 1,

// Model in request list, but not yet in loading channel (TODO: Verify this)
LOADSTATE_REQUESTED = 2,

// Model is being read
LOADSTATE_READING = 3,

// If the model is a `big` one this state is used to indicate
// that the model's first half has been loaded and is yet to be
// finished by loading the second half.
// When it has been loaded the state is set to `LOADED`
LOADSTATE_FINISHING = 4
};

struct CStreamingInfo
{
uint16_t prevId = (uint16_t)-1;
Expand All @@ -25,7 +46,7 @@ struct CStreamingInfo
uint8_t archiveId = 0u;
uint32_t offsetInBlocks = 0u;
uint32_t sizeInBlocks = 0u;
uint32_t loadState = 0u;
eModelLoadState loadState = eModelLoadState::LOADSTATE_NOT_LOADED;
};
static_assert(sizeof(CStreamingInfo) == 0x14, "Invalid size for CStreamingInfo");

Expand Down
Loading