Skip to content
Open
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
4 changes: 4 additions & 0 deletions Client/game_sa/CGameSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ CGameSA::CGameSA()
CPtrNodeSingleLinkPoolSA::StaticSetHooks();
CVehicleAudioSettingsManagerSA::StaticSetHooks();
CPointLightsSA::StaticSetHooks();
CVisibilityPluginsSA::StaticSetHooks();
}
catch (const std::bad_alloc& e)
{
Expand Down Expand Up @@ -470,6 +471,9 @@ void CGameSA::Reset()
// Restore changed TXD IDs
CModelInfoSA::StaticResetTextureDictionaries();

// Restore visibility plugin lists
CVisibilityPluginsSA::ResetRenderingEntityLists();

// Restore default world state
RestoreGameWorld();

Expand Down
103 changes: 103 additions & 0 deletions Client/game_sa/CLinkListSA.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*****************************************************************************
*
* PROJECT: Multi Theft Auto
* LICENSE: See LICENSE in the top level directory
* FILE: game_sa/CLinkListSA.h
*
* Multi Theft Auto is available from https://www.multitheftauto.com/
*
*****************************************************************************/
#pragma once

#include "CLinkSA.h"
#include <cstddef>
#include <utility>

template <typename T>
class CLinkListSA
{
public:
CLinkSA<T> usedListHead{};
CLinkSA<T> usedListTail{};
CLinkSA<T> freeListHead{};
CLinkSA<T> freeListTail{};
CLinkSA<T>* entries{};

void* operator new(std::size_t size) { return ((void*(__cdecl*)(std::size_t))0x821195)(size); }
void* operator new[](std::size_t size) { return ((void*(__cdecl*)(std::size_t))0x821195)(size); }
void operator delete(void* ptr) { ((void(__cdecl*)(void*))0x8213AE)(ptr); }
void operator delete[](void* ptr) { ((void(__cdecl*)(void*))0x8213AE)(ptr); }

void Init(std::size_t count)
{
if (count == 0)
return;

usedListHead.next = &usedListTail;
usedListTail.prev = &usedListHead;
freeListHead.next = &freeListTail;
freeListTail.prev = &freeListHead;

entries = new CLinkSA<T>[count];
for (std::int32_t i = count - 1; i >= 0; i--)
entries[i].Insert(&freeListHead);
}

void Shutdown() { delete[] std::exchange(entries, nullptr); }

void Insert(CLinkSA<T>& link)
{
link.Remove();
link.Insert(&usedListHead);
}

CLinkSA<T>* Insert(T const& data)
{
CLinkSA<T>* link = freeListHead.next;
if (link == &freeListTail)
return nullptr;

link->data = data;
Insert(*link);
return link;
}

CLinkSA<T>* InsertSorted(T const& data)
{
CLinkSA<T>* i = nullptr;
for (i = usedListHead.next; i != &usedListTail; i = i->next)
{
if (i->data.distance >= data.distance)
break;
}

CLinkSA<T>* link = freeListHead.next;
if (link == &freeListTail)
return nullptr;

link->data = data;
link->Remove();
link->Insert(i->prev);
return link;
}

void Clear()
{
for (CLinkSA<T>* link = usedListHead.next; link != &usedListTail; link = usedListHead.next)
Remove(link);
}

auto Remove(CLinkSA<T>* l)
{
l->Remove();
l->Insert(&freeListHead);
return l;
}

auto GetTail() { return usedListTail.prev; }
auto& GetTailLink() { return usedListTail; }

auto GetHead() { return usedListHead.next; }
auto& GetHeadLink() { return usedListHead; }
};
static_assert(sizeof(CLinkListSA<int>) == 0x34, "Invalid size for CLinkListSA class!");
42 changes: 42 additions & 0 deletions Client/game_sa/CLinkSA.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*****************************************************************************
*
* PROJECT: Multi Theft Auto
* LICENSE: See LICENSE in the top level directory
* FILE: game_sa/CLinkSA.h
*
* Multi Theft Auto is available from https://www.multitheftauto.com/
*
*****************************************************************************/

#pragma once

template <typename T>
class CLinkSA
{
public:
T data;
CLinkSA<T>* prev;
CLinkSA<T>* next;

void* operator new(std::size_t size) { return ((void*(__cdecl*)(std::size_t))0x821195)(size); }
void* operator new[](std::size_t size) { return ((void*(__cdecl*)(std::size_t))0x821195)(size); }
void operator delete(void* ptr) { ((void(__cdecl*)(void*))0x8213AE)(ptr); }
void operator delete[](void* ptr) { ((void(__cdecl*)(void*))0x8213AE)(ptr); }

void Remove()
{
next->prev = prev;
prev->next = next;
}

// If `this` is already in another list, `Remove()` must first be called! (Not doing so will result in the list (`this` is in) getting corrupted)
void Insert(CLinkSA<T>* after)
{
next = after->next;
next->prev = this;

prev = after;
prev->next = this;
}
};
static_assert(sizeof(CLinkSA<int>) == 0xC, "Invalid size for CLinkSA class!");
73 changes: 69 additions & 4 deletions Client/game_sa/CVisibilityPluginsSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@

#define FUNC_CVisibilityPlugins_InsertEntityIntoEntityList 0x733DD0

// m_alphaEntitiesList
static RenderingListState alphaEntitiesList{(CLinkListSA<AlphaObjectInfoSA>*)0xC88120, (*(std::size_t*)0x733B05) / sizeof(CLinkSA<AlphaObjectInfoSA>), false};

// m_alphaUnderwaterEntityList
static RenderingListState underwaterEntitiesList{(CLinkListSA<AlphaObjectInfoSA>*)0xC88178, (*(std::size_t*)0x733BD5) / sizeof(CLinkSA<AlphaObjectInfoSA>),
false};

void CVisibilityPluginsSA::SetClumpAlpha(RpClump* pClump, int iAlpha)
{
DWORD dwFunc = FUNC_CVisiblityPlugins_SetClumpAlpha;
Expand Down Expand Up @@ -58,15 +65,73 @@ int CVisibilityPluginsSA::GetAtomicId(RwObject* pAtomic)
return iResult;
}

bool CVisibilityPluginsSA::IsAtomicVisible(RpAtomic* atomic) const
{
if (!atomic)
return false;

return ((bool(__cdecl*)(RpAtomic*))0x732990)(atomic);
}

bool CVisibilityPluginsSA::InsertEntityIntoEntityList(void* entity, float distance, void* callback)
{
return ((bool(_cdecl*)(void*, float, void*))FUNC_CVisibilityPlugins_InsertEntityIntoEntityList)(entity, distance, callback);
}

bool CVisibilityPluginsSA::IsAtomicVisible(RpAtomic* atomic) const
void CVisibilityPluginsSA::SetRenderingListSize(RenderingEntityListType listType, std::size_t elementsCount)
{
if (!atomic)
return false;
RenderingListState& state = listType == RenderingEntityListType::ENTITY_LIST_TYPE_ALPHA ? alphaEntitiesList : underwaterEntitiesList;
if (state.size == elementsCount)
return;

return ((bool(__cdecl*)(RpAtomic*))0x732990)(atomic);
state.size = elementsCount;
state.pendingReinit = true;
}

void CVisibilityPluginsSA::CheckRenderingList(RenderingListState& state)
{
if (!state.pendingReinit)
return;

ReInitRenderingList(state.list, state.size);
state.pendingReinit = false;
}

void CVisibilityPluginsSA::ReInitRenderingList(CLinkListSA<AlphaObjectInfoSA>* list, std::size_t count)
{
if (!list || count == 0)
return;

list->Shutdown();
list->Init(count);
}

void* __fastcall CVisibilityPluginsSA::InsertAlphaEntityIntoSortedList(CLinkListSA<AlphaObjectInfoSA>* entitiesList, void*, AlphaObjectInfoSA* info)
{
CheckRenderingList(alphaEntitiesList);
return entitiesList->InsertSorted(*info);
}

void* __fastcall CVisibilityPluginsSA::InsertUnderwaterEntityIntoSortedList(CLinkListSA<AlphaObjectInfoSA>* entitiesList, void*, AlphaObjectInfoSA* info)
{
CheckRenderingList(underwaterEntitiesList);
return entitiesList->InsertSorted(*info);
}

void CVisibilityPluginsSA::ResetRenderingEntityLists()
{
alphaEntitiesList.size = DEFAULT_MAX_ALPHA_ENTITIES;
underwaterEntitiesList.size = DEFAULT_MAX_UNDERWATER_ENTITIES;

ReInitRenderingList(alphaEntitiesList.list, alphaEntitiesList.size);
ReInitRenderingList(underwaterEntitiesList.list, underwaterEntitiesList.size);
}

void CVisibilityPluginsSA::StaticSetHooks()
{
HookInstallCall(0x7345F2, (DWORD)InsertAlphaEntityIntoSortedList); // CVisibilityPlugins::InsertEntityIntoSortedList
HookInstallCall(0x733DF5, (DWORD)InsertAlphaEntityIntoSortedList); // CVisibilityPlugins::InsertObjectIntoSortedList

HookInstallCall(0x7345D9, (DWORD)InsertUnderwaterEntityIntoSortedList); // CVisibilityPlugins::InsertEntityIntoSortedList
HookInstallCall(0x733DB5, (DWORD)InsertUnderwaterEntityIntoSortedList); // CVisibilityPlugins::InsertEntityIntoUnderwaterList
}
28 changes: 27 additions & 1 deletion Client/game_sa/CVisibilityPluginsSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,43 @@
#pragma once

#include <game/CVisibilityPlugins.h>
#include "CLinkListSA.h"

#define FUNC_CVisiblityPlugins_SetClumpAlpha 0x732B00
#define FUNC_CVisibilityPlugins_GetAtomicId 0x732370

struct AlphaObjectInfoSA
{
void* object;
void* callback;
float distance;
};

struct RenderingListState
{
CLinkListSA<AlphaObjectInfoSA>* list;
std::size_t size;
bool pendingReinit;
};

class CVisibilityPluginsSA : public CVisibilityPlugins
{
public:
void SetClumpAlpha(RpClump* pClump, int iAlpha);
int GetAtomicId(RwObject* pAtomic);

bool IsAtomicVisible(RpAtomic* atomic) const override;

bool InsertEntityIntoEntityList(void* entity, float distance, void* callback);
void SetRenderingListSize(RenderingEntityListType listType, std::size_t elementsCount) override;

bool IsAtomicVisible(RpAtomic* atomic) const override;
static void ResetRenderingEntityLists();
static void StaticSetHooks();

private:
static void CheckRenderingList(RenderingListState& state);
static void ReInitRenderingList(CLinkListSA<AlphaObjectInfoSA>* list, std::size_t count);

static void* __fastcall InsertAlphaEntityIntoSortedList(CLinkListSA<AlphaObjectInfoSA>* entitiesList, void*, AlphaObjectInfoSA* info);
static void* __fastcall InsertUnderwaterEntityIntoSortedList(CLinkListSA<AlphaObjectInfoSA>* entitiesList, void*, AlphaObjectInfoSA* info);
};
5 changes: 5 additions & 0 deletions Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -992,6 +992,11 @@ ADD_ENUM(PostFXType::CONTRAST, "contrast")
ADD_ENUM(PostFXType::SATURATION, "saturation")
IMPLEMENT_ENUM_CLASS_END("postfx-type")

IMPLEMENT_ENUM_CLASS_BEGIN(RenderingEntityListType)
ADD_ENUM(RenderingEntityListType::ENTITY_LIST_TYPE_ALPHA, "alpha")
ADD_ENUM(RenderingEntityListType::ENTITY_LIST_TYPE_UNDERWATER, "underwater")
IMPLEMENT_ENUM_CLASS_END("rendering-entity-list-type")

//
// 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 @@ -26,6 +26,7 @@
#include "enums/SoundEffectType.h"
#include "enums/ObjectGroupPhysicalProperties.h"
#include "enums/PostFXType.h"
#include "enums/RenderingEntityListType.h"

enum eLuaType
{
Expand Down Expand Up @@ -104,6 +105,7 @@ DECLARE_ENUM_CLASS(taskType);
DECLARE_ENUM(eEntityType);
DECLARE_ENUM_CLASS(VehicleAudioSettingProperty);
DECLARE_ENUM_CLASS(PostFXType);
DECLARE_ENUM_CLASS(RenderingEntityListType);

class CRemoteCall;

Expand Down
13 changes: 13 additions & 0 deletions Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
#include <game/CObjectGroupPhysicalProperties.h>
#include <game/CStreaming.h>
#include <game/CPtrNodeSingleLinkPool.h>
#include <game/CVisibilityPlugins.h>
#include <lua/CLuaFunctionParser.h>
#include "CLuaEngineDefs.h"
#include <enums/VehicleType.h>
#include <enums/RenderingEntityListType.h>

//! Set the CModelCacheManager limits
//! By passing `nil`/no value the original values are restored
Expand Down Expand Up @@ -163,6 +165,7 @@ void CLuaEngineDefs::LoadFunctions()
{"enginePreloadWorldArea", ArgumentParser<EnginePreloadWorldArea>},
{"engineRestreamModel", ArgumentParser<EngineRestreamModel>},
{"engineRestream", ArgumentParser<EngineRestream>},
{"engineSetRenderingListSize", ArgumentParser<EngineSetRenderingListSize>},

// CLuaCFunctions::AddFunction ( "engineReplaceMatchingAtomics", EngineReplaceMatchingAtomics );
// CLuaCFunctions::AddFunction ( "engineReplaceWheelAtomics", EngineReplaceWheelAtomics );
Expand Down Expand Up @@ -2631,3 +2634,13 @@ void CLuaEngineDefs::EngineRestream(std::optional<RestreamOption> option)
{
g_pClientGame->Restream(option);
}

void CLuaEngineDefs::EngineSetRenderingListSize(RenderingEntityListType listType, std::size_t elementsCount)
{
if (listType == RenderingEntityListType::ENTITY_LIST_TYPE_ALPHA && elementsCount < DEFAULT_MAX_ALPHA_ENTITIES)
throw std::invalid_argument("Alpha entities list size cannot be less than 200");
else if (listType == RenderingEntityListType::ENTITY_LIST_TYPE_UNDERWATER && elementsCount < DEFAULT_MAX_UNDERWATER_ENTITIES)
throw std::invalid_argument("Underwater entities list size cannot be less than 100");

g_pGame->GetVisibilityPlugins()->SetRenderingListSize(listType, elementsCount);
}
2 changes: 2 additions & 0 deletions Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ class CLuaEngineDefs : public CLuaDefs
static bool EngineRestreamModel(std::uint16_t modelId);
static void EngineRestream(std::optional<RestreamOption> option);

static void EngineSetRenderingListSize(RenderingEntityListType listType, std::size_t elementsCount);

private:
static void AddEngineColClass(lua_State* luaVM);
static void AddEngineTxdClass(lua_State* luaVM);
Expand Down
9 changes: 7 additions & 2 deletions Client/sdk/game/CVisibilityPlugins.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@
*****************************************************************************/

#pragma once
#include "enums/RenderingEntityListType.h"

#define ATOMIC_ID_FLAG_TWO_VERSIONS_UNDAMAGED 1
#define ATOMIC_ID_FLAG_TWO_VERSIONS_DAMAGED 2

#define DEFAULT_MAX_ALPHA_ENTITIES 200
#define DEFAULT_MAX_UNDERWATER_ENTITIES 100

struct RpClump;
struct RpAtomic;
struct RwObject;
Expand All @@ -24,7 +28,8 @@ class CVisibilityPlugins
virtual void SetClumpAlpha(RpClump* pClump, int iAlpha) = 0;
virtual int GetAtomicId(RwObject* pAtomic) = 0;

virtual bool InsertEntityIntoEntityList(void* entity, float distance, void* callback) = 0;

virtual bool IsAtomicVisible(RpAtomic* atomic) const = 0;

virtual bool InsertEntityIntoEntityList(void* entity, float distance, void* callback) = 0;
virtual void SetRenderingListSize(RenderingEntityListType listType, std::size_t elementsCount) = 0;
};
Loading
Loading