Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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 @@ -253,6 +253,7 @@ CGameSA::CGameSA()
CVehicleAudioSettingsManagerSA::StaticSetHooks();
CPointLightsSA::StaticSetHooks();
CBuildingRemovalSA::StaticSetHooks();
CVisibilityPluginsSA::StaticSetHooks();
}
catch (const std::bad_alloc& e)
{
Expand Down Expand Up @@ -479,6 +480,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!");
65 changes: 65 additions & 0 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 @@ -62,3 +69,61 @@ bool CVisibilityPluginsSA::InsertEntityIntoEntityList(void* entity, float distan
{
return ((bool(_cdecl*)(void*, float, void*))FUNC_CVisibilityPlugins_InsertEntityIntoEntityList)(entity, distance, callback);
}

void CVisibilityPluginsSA::SetRenderingListSize(RenderingEntityListType listType, std::size_t elementsCount)
{
RenderingListState& state = listType == RenderingEntityListType::ENTITY_LIST_TYPE_ALPHA ? alphaEntitiesList : underwaterEntitiesList;
if (state.size == elementsCount)
return;

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
}
26 changes: 26 additions & 0 deletions Client/game_sa/CVisibilityPluginsSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,41 @@
#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 InsertEntityIntoEntityList(void* entity, float distance, void* callback);
void SetRenderingListSize(RenderingEntityListType listType, std::size_t elementsCount) 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 @@ -167,6 +169,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 @@ -2792,3 +2795,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
5 changes: 5 additions & 0 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 RwObject;

Expand All @@ -24,4 +28,5 @@ class CVisibilityPlugins
virtual int GetAtomicId(RwObject* pAtomic) = 0;

virtual bool InsertEntityIntoEntityList(void* entity, float distance, void* callback) = 0;
virtual void SetRenderingListSize(RenderingEntityListType listType, std::size_t elementsCount) = 0;
};
18 changes: 18 additions & 0 deletions Shared/sdk/enums/RenderingEntityListType.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*****************************************************************************
*
* PROJECT: Multi Theft Auto
* LICENSE: See LICENSE in the top level directory
* FILE: sdk/RenderingEntityListType.h
* PURPOSE: Header for common definitions
*
* Multi Theft Auto is available from https://www.multitheftauto.com/
*
*****************************************************************************/

#pragma once

enum class RenderingEntityListType
{
ENTITY_LIST_TYPE_ALPHA,
ENTITY_LIST_TYPE_UNDERWATER,
};
Loading