From befd38bf512164c28c0d4aa1a9d6dc73c68ce7e1 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Tue, 22 Aug 2023 01:11:49 +0200 Subject: [PATCH] Interface factory system rewrite Removed all extraneous copies by adding the class 'InterfaceReg' which will construct a new interface, and link it to the engine's static register. The Source Engine macro 'EXPOSE_INTERFACE_FN' will help utilizing this. The game module from the plugin is not obtained through the process environment block, so the executable is no longer sensitive to names. --- r5dev/engine/sys_dll.cpp | 21 +++--- r5dev/pluginsdk/ifactory.h | 16 ++-- r5dev/pluginsdk/pluginsdk.cpp | 37 ++++------ r5dev/pluginsdk/pluginsdk.h | 4 +- r5dev/pluginsystem/pluginsystem.h | 5 ++ r5dev/public/tier1/interface.h | 59 +++++++++++---- r5dev/tier1/CMakeLists.txt | 1 + r5dev/tier1/interface.cpp | 20 +++++ r5dev/vpc/interfaces.cpp | 118 ++++++++---------------------- r5dev/vpc/interfaces.h | 40 +++++----- 10 files changed, 154 insertions(+), 167 deletions(-) create mode 100644 r5dev/tier1/interface.cpp diff --git a/r5dev/engine/sys_dll.cpp b/r5dev/engine/sys_dll.cpp index 85a10d5d..dac12f72 100644 --- a/r5dev/engine/sys_dll.cpp +++ b/r5dev/engine/sys_dll.cpp @@ -102,28 +102,27 @@ bool CModAppSystemGroup::StaticCreate(CModAppSystemGroup* pModAppSystemGroup) pModAppSystemGroup->SetServerOnly(); *m_bIsDedicated = true; #endif // DEDICATED - g_pFactory->GetFactoriesFromRegister(); - g_pFactory->AddFactory(FACTORY_INTERFACE_VERSION, g_pFactory); - g_pFactory->AddFactory(INTERFACEVERSION_PLUGINSYSTEM, g_pPluginSystem); - g_pFactory->AddFactory(KEYVALUESSYSTEM_INTERFACE_VERSION, g_pKeyValuesSystem); + + EXPOSE_INTERFACE_FN((InstantiateInterfaceFn)PluginSystem, CPluginSystem, INTERFACEVERSION_PLUGINSYSTEM); + EXPOSE_INTERFACE_FN((InstantiateInterfaceFn)KeyValuesSystem, CKeyValuesSystem, KEYVALUESSYSTEM_INTERFACE_VERSION); InitPluginSystem(pModAppSystemGroup); CALL_PLUGIN_CALLBACKS(g_pPluginSystem->GetCreateCallbacks(), pModAppSystemGroup); g_pModSystem->Init(); - g_pDebugOverlay = g_pFactory->GetFactoryPtr(VDEBUG_OVERLAY_INTERFACE_VERSION, false).RCast(); + g_pDebugOverlay = (CIVDebugOverlay*)g_pFactorySystem->GetFactory(VDEBUG_OVERLAY_INTERFACE_VERSION); #ifndef CLIENT_DLL - g_pServerGameDLL = g_pFactory->GetFactoryPtr(INTERFACEVERSION_SERVERGAMEDLL, false).RCast(); - g_pServerGameClients = g_pFactory->GetFactoryPtr(INTERFACEVERSION_SERVERGAMECLIENTS_NEW, false).RCast(); + g_pServerGameDLL = (CServerGameDLL*)g_pFactorySystem->GetFactory(INTERFACEVERSION_SERVERGAMEDLL); + g_pServerGameClients = (CServerGameClients*)g_pFactorySystem->GetFactory(INTERFACEVERSION_SERVERGAMECLIENTS_NEW); if (!g_pServerGameClients) - g_pServerGameClients = g_pFactory->GetFactoryPtr(INTERFACEVERSION_SERVERGAMECLIENTS, false).RCast(); - g_pServerGameEntities = g_pFactory->GetFactoryPtr(INTERFACEVERSION_SERVERGAMEENTS, false).RCast(); + g_pServerGameClients = (CServerGameClients*)g_pFactorySystem->GetFactory(INTERFACEVERSION_SERVERGAMECLIENTS); + g_pServerGameEntities = (CServerGameEnts*)g_pFactorySystem->GetFactory(INTERFACEVERSION_SERVERGAMEENTS); #endif // !CLIENT_DLL #ifndef DEDICATED - g_pClientEntityList = g_pFactory->GetFactoryPtr(VCLIENTENTITYLIST_INTERFACE_VERSION, false).RCast(); - g_pEngineTraceClient = g_pFactory->GetFactoryPtr(INTERFACEVERSION_ENGINETRACE_CLIENT, false).RCast(); + g_pClientEntityList = (CClientEntityList*)g_pFactorySystem->GetFactory(VCLIENTENTITYLIST_INTERFACE_VERSION); + g_pEngineTraceClient = (CEngineTraceClient*)g_pFactorySystem->GetFactory(INTERFACEVERSION_ENGINETRACE_CLIENT); g_pImGuiConfig->Load(); // Load ImGui configs. DirectX_Init(); diff --git a/r5dev/pluginsdk/ifactory.h b/r5dev/pluginsdk/ifactory.h index 4acc4833..862cbc30 100644 --- a/r5dev/pluginsdk/ifactory.h +++ b/r5dev/pluginsdk/ifactory.h @@ -1,16 +1,10 @@ #pragma once +#include "tier1/interface.h" -struct FactoryInfo_t; -class CMemory; - -// TODO: Make this abstract and make it base class of CFactory. -class IFactory +abstract_class IFactorySystem { public: - virtual void AddFactory(const string& svFactoryName, void* pFactory) = 0; - virtual void AddFactory(FactoryInfo_t factoryInfo) = 0; - virtual size_t GetVersionIndex(const string& svInterfaceName) const = 0; - virtual void GetFactoriesFromRegister(void) = 0; - virtual CMemory GetFactoryPtr(const string& svFactoryName, bool versionLess = true) const = 0; - virtual const char* GetFactoryFullName(const string& svFactoryName) const = 0; + virtual void AddFactory(InstantiateInterfaceFn createFn, const char* pName) const = 0; + virtual void* GetFactory(const char* pName) const = 0; + virtual const char* GetVersion(void) const = 0; }; diff --git a/r5dev/pluginsdk/pluginsdk.cpp b/r5dev/pluginsdk/pluginsdk.cpp index ab541d44..cd677130 100644 --- a/r5dev/pluginsdk/pluginsdk.cpp +++ b/r5dev/pluginsdk/pluginsdk.cpp @@ -20,9 +20,7 @@ CPluginSDK::CPluginSDK(const char* pszSelfModule) : m_FactoryInstance(nullptr), m_PluginSystem(nullptr) { m_SelfModule.InitFromName(pszSelfModule); - - // !TODO: Use PEB! - m_GameModule.InitFromName("r5apex.exe"); + m_GameModule.InitFromBase(CModule::GetProcessEnvironmentBlock()->ImageBaseAddress); } //--------------------------------------------------------------------------------- @@ -37,33 +35,30 @@ CPluginSDK::~CPluginSDK() //--------------------------------------------------------------------------------- bool CPluginSDK::InitSDK() { - auto getFactorySystemFn = m_SDKModule.GetExportedSymbol("GetFactorySystem").RCast(); - - Assert(getFactorySystemFn, "Could not find GetFactorySystem export from gamesdk.dll"); - if (!getFactorySystemFn) + InstantiateInterfaceFn factorySystem = m_SDKModule.GetExportedSymbol("GetFactorySystem").RCast(); + if (!factorySystem) + { + Assert(factorySystem, "factorySystem == NULL; symbol renamed???"); return false; + } - m_FactoryInstance = reinterpret_cast(getFactorySystemFn()); - Assert(getFactorySystemFn, "m_FactoryInstace was nullptr."); - if (!m_FactoryInstance) - return false; + m_FactoryInstance = (IFactorySystem*)factorySystem(); // Let's make sure the factory version matches, else we unload. - bool isFactoryVersionOk = strcmp(m_FactoryInstance->GetFactoryFullName("VFactorySystem"), FACTORY_INTERFACE_VERSION) == 0; - Assert(isFactoryVersionOk, "Version mismatch between IFactory and CFactory."); + bool isFactoryVersionOk = strcmp(m_FactoryInstance->GetVersion(), FACTORY_INTERFACE_VERSION) == 0; if (!isFactoryVersionOk) + { + Assert(isFactoryVersionOk, "Version mismatch!"); return false; + } - // Let's make sure the SDK version matches with the PluginSystem, else we unload - bool isPluginVersionOk = strcmp(m_FactoryInstance->GetFactoryFullName("VPluginSystem"), INTERFACEVERSION_PLUGINSYSTEM) == 0; - Assert(isPluginVersionOk, "Version mismatch between CPluginSDK and CPluginSystem."); - if (!isPluginVersionOk) - return false; - - m_PluginSystem = m_FactoryInstance->GetFactoryPtr(INTERFACEVERSION_PLUGINSYSTEM, false).RCast(); - Assert(m_PluginSystem, "m_PluginSystem was nullptr."); + // Unload if + m_PluginSystem = (IPluginSystem*)m_FactoryInstance->GetFactory(INTERFACEVERSION_PLUGINSYSTEM); if (!m_PluginSystem) + { + Assert(m_PluginSystem, "CPluginSDK::m_PluginSystem == NULL"); return false; + } return true; } diff --git a/r5dev/pluginsdk/pluginsdk.h b/r5dev/pluginsdk/pluginsdk.h index fa6a0e22..5ef7ebc8 100644 --- a/r5dev/pluginsdk/pluginsdk.h +++ b/r5dev/pluginsdk/pluginsdk.h @@ -1,6 +1,6 @@ #pragma once -class IFactory; +class IFactorySystem; class IPluginSystem; //-----------------------------------------------------------------------------// @@ -15,7 +15,7 @@ public: inline void SetSDKModule(const CModule& sdkModule) { m_SDKModule = sdkModule; }; private: - IFactory* m_FactoryInstance; + IFactorySystem* m_FactoryInstance; IPluginSystem* m_PluginSystem; CModule m_SelfModule; CModule m_GameModule; diff --git a/r5dev/pluginsystem/pluginsystem.h b/r5dev/pluginsystem/pluginsystem.h index 943d0660..da8dda72 100644 --- a/r5dev/pluginsystem/pluginsystem.h +++ b/r5dev/pluginsystem/pluginsystem.h @@ -117,6 +117,11 @@ private: }; extern CPluginSystem* g_pPluginSystem; +FORCEINLINE CPluginSystem* PluginSystem() +{ + return g_pPluginSystem; +} + // Monitor this and performance profile this if fps drops are detected. #define CALL_PLUGIN_CALLBACKS(callback, ...) \ for (auto& cb : !callback) \ diff --git a/r5dev/public/tier1/interface.h b/r5dev/public/tier1/interface.h index 91418523..70fe4359 100644 --- a/r5dev/public/tier1/interface.h +++ b/r5dev/public/tier1/interface.h @@ -14,26 +14,53 @@ typedef void* (*CreateInterfaceFn)(const char* pName, int* pReturnCode); typedef void* (*InstantiateInterfaceFn)(); typedef HINSTANCE CSysModule; -struct InterfaceGlobals_t +class InterfaceReg { - InstantiateInterfaceFn m_pInterfacePtr; - const char* m_pInterfaceName; - InterfaceGlobals_t* m_pNextInterfacePtr; +public: + InterfaceReg(InstantiateInterfaceFn fn, const char* pName); + +public: + InstantiateInterfaceFn m_CreateFn; + const char* m_pName; + InterfaceReg* m_pNext; }; +extern InterfaceReg** s_ppInterfaceRegs; + //----------------------------------------------------------------------------- +// Use this to expose an interface that can have multiple instances. +// e.g.: +// EXPOSE_INTERFACE( CInterfaceImp, IInterface, "MyInterface001" ) +// This will expose a class called CInterfaceImp that implements IInterface (a pure class) +// clients can receive a pointer to this class by calling CreateInterface( "MyInterface001" ) +// +// In practice, the shared header file defines the interface (IInterface) and version name ("MyInterface001") +// so that each component can use these names/vtables to communicate +// +// A single class can support multiple interfaces through multiple inheritance +// +// Use this if you want to write the factory function. +#define EXPOSE_INTERFACE_FN(functionName, interfaceName, versionName) \ + { \ + static InterfaceReg __g_Create##interfaceName##_reg(functionName, versionName); \ + } +#define EXPOSE_INTERFACE(className, interfaceName, versionName) \ + { \ + static void* __Create##className##_interface() {return static_cast( new className );} \ + static InterfaceReg __g_Create##className##_reg(__Create##className##_interface, versionName ); \ + } -struct FactoryInfo_t -{ - CMemory m_pFactoryPtr; - string m_szFactoryFullName; - string m_szFactoryName; - string m_szFactoryVersion; +// Use this to expose a singleton interface with a global variable you've created. +#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, globalVarName) \ + { \ + static void* __Create##className##interfaceName##_interface() {return static_cast( &globalVarName );} \ + static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName); \ + } - FactoryInfo_t() : m_pFactoryPtr(nullptr) {} - FactoryInfo_t(const uintptr_t factoryPtr, const string& factoryFullName, const string& factoryName, const string& factoryVersion) : - m_pFactoryPtr(factoryPtr), m_szFactoryFullName(factoryFullName), m_szFactoryName(factoryName), m_szFactoryVersion(factoryVersion) {} - FactoryInfo_t(const uintptr_t factoryPtr, const string& factoryFullName) : - m_pFactoryPtr(factoryPtr), m_szFactoryFullName(factoryFullName) {} -}; +// Use this to expose a singleton interface. This creates the global variable for you automatically. +#define EXPOSE_SINGLE_INTERFACE(className, interfaceName, versionName) \ + { \ + static className __g_##className##_singleton; \ + } \ + EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, __g_##className##_singleton) #endif // INTERFACE_H diff --git a/r5dev/tier1/CMakeLists.txt b/r5dev/tier1/CMakeLists.txt index d10a8eb0..283ae2b2 100644 --- a/r5dev/tier1/CMakeLists.txt +++ b/r5dev/tier1/CMakeLists.txt @@ -24,6 +24,7 @@ add_sources( SOURCE_GROUP "Private" "cmd.cpp" "convar.cpp" "cvar.cpp" + "interface.cpp" ) file( GLOB TIER1_PUBLIC_HEADERS diff --git a/r5dev/tier1/interface.cpp b/r5dev/tier1/interface.cpp new file mode 100644 index 00000000..fcda892d --- /dev/null +++ b/r5dev/tier1/interface.cpp @@ -0,0 +1,20 @@ +//===========================================================================// +// +// Purpose: +// +//===========================================================================// +#include "tier1/interface.h" + +// ------------------------------------------------------------------------- // +// InterfaceReg. +// ------------------------------------------------------------------------- // + +InterfaceReg** s_ppInterfaceRegs; + +InterfaceReg::InterfaceReg(InstantiateInterfaceFn fn, const char* pName) : + m_pName(pName) +{ + m_CreateFn = fn; + m_pNext = *s_ppInterfaceRegs; + *s_ppInterfaceRegs = this; +} diff --git a/r5dev/vpc/interfaces.cpp b/r5dev/vpc/interfaces.cpp index 898e3d36..9c3be91b 100644 --- a/r5dev/vpc/interfaces.cpp +++ b/r5dev/vpc/interfaces.cpp @@ -6,114 +6,58 @@ #include "core/stdafx.h" #include "vpc/interfaces.h" +#include "tier1/interface.h" //--------------------------------------------------------------------------------- -// Purpose: add a factory to the factories vector -// Input : svFactoryName - -// pFactory - +// Purpose: register a new factory +// Input : createFn - +// *pName - //--------------------------------------------------------------------------------- -void CFactory::AddFactory(const string& svFactoryName, void* pFactory) +void CFactorySystem::AddFactory(InstantiateInterfaceFn createFn, const char* pName) const { - size_t nVersionIndex = GetVersionIndex(svFactoryName); - FactoryInfo_t factoryInfo(reinterpret_cast(pFactory), svFactoryName, - svFactoryName.substr(0, nVersionIndex), svFactoryName.substr(nVersionIndex)); - - m_vFactories.push_back(factoryInfo); // Push factory info back into the vector. + InterfaceReg(createFn, pName); } //--------------------------------------------------------------------------------- -// Purpose: add a factory to the factories vector -// Input : factoryInfo - +// Purpose: get a factory by name +// Input : *pName - //--------------------------------------------------------------------------------- -void CFactory::AddFactory(FactoryInfo_t factoryInfo) +void* CFactorySystem::GetFactory(const char* pName) const { - m_vFactories.push_back(factoryInfo); // Push factory info back into the vector. -} - -//--------------------------------------------------------------------------------- -// Purpose: get the version index from interface name -// Input : svInterfaceName - -// Output : index of version in input interface string -//--------------------------------------------------------------------------------- -size_t CFactory::GetVersionIndex(const string& svInterfaceName) const -{ - size_t nVersionIndex = 0; - for (size_t i = 0; i < svInterfaceName.length(); i++) // Loop through each character to find the start of interface version. + for (InterfaceReg* it = *s_ppInterfaceRegs; + it; it = it->m_pNext) // Loop till we go out of scope. { - if (std::isdigit(svInterfaceName[i])) - { - nVersionIndex = i; - break; - } - } - return nVersionIndex; -} - -//--------------------------------------------------------------------------------- -// Purpose: get all factory registered in the global s_pInterfacesRegs -//--------------------------------------------------------------------------------- -void CFactory::GetFactoriesFromRegister(void) -{ - for (InterfaceGlobals_t* it = s_pInterfacesRegs.GetValue(); - it; it = it->m_pNextInterfacePtr) // Loop till we go out of scope. - { - string svInterfaceName = it->m_pInterfaceName; // Get copy of the name. - size_t nVersionIndex = GetVersionIndex(svInterfaceName); - - // Push back the interface. - AddFactory(FactoryInfo_t(reinterpret_cast(it->m_pInterfacePtr()), svInterfaceName, - svInterfaceName.substr(0, nVersionIndex), svInterfaceName.substr(nVersionIndex))); - } -} - -//--------------------------------------------------------------------------------- -// Purpose: get factory pointer with factoryname input from factories vector -// Input : svFactoryName - -// bVersionLess - -// Output : CMemory -//--------------------------------------------------------------------------------- -CMemory CFactory::GetFactoryPtr(const string& svFactoryName, bool bVersionLess) const -{ - for (const FactoryInfo_t& it : m_vFactories) // Loop through the whole vector. - { - if (bVersionLess) - { - if (it.m_szFactoryName == svFactoryName) - return it.m_pFactoryPtr; - } - else - { - if (it.m_szFactoryFullName == svFactoryName) - return it.m_pFactoryPtr; - } + if (V_strcmp(it->m_pName, pName) == NULL) + return it->m_CreateFn(); } - return CMemory(); + // No dice. + return nullptr; } - //--------------------------------------------------------------------------------- -// Purpose: get full factory string from versionless string -// Input : svFactoryName - -// Output : const char* +// Purpose: get the factory system's interface version +// Input : *pName - //--------------------------------------------------------------------------------- -const char* CFactory::GetFactoryFullName(const string& svFactoryName) const +const char* CFactorySystem::GetVersion(void) const { - for (const FactoryInfo_t& it : m_vFactories) - { - if (it.m_szFactoryName == svFactoryName) - return it.m_szFactoryFullName.c_str(); - } - - return ""; + return FACTORY_INTERFACE_VERSION; } //--------------------------------------------------------------------------------- -// Purpose: expose factory system to other dlls +// Purpose: //--------------------------------------------------------------------------------- -CFactory* GetFactorySystem() +void* CreateInterface(const char* pName, int* pReturnCode) { - return g_pFactory; + return CreateInterfaceInternal(pName, pReturnCode); } -CFactory* g_pFactory = new CFactory(); \ No newline at end of file +//--------------------------------------------------------------------------------- +// Purpose: +//--------------------------------------------------------------------------------- +IFactorySystem* GetFactorySystem() +{ + return g_pFactorySystem; +} + +CFactorySystem* g_pFactorySystem = new CFactorySystem(); diff --git a/r5dev/vpc/interfaces.h b/r5dev/vpc/interfaces.h index 6d248fc2..3fb201ce 100644 --- a/r5dev/vpc/interfaces.h +++ b/r5dev/vpc/interfaces.h @@ -1,5 +1,6 @@ #pragma once #include "tier1/interface.h" +#include "pluginsdk/ifactory.h" /*----------------------------------------------------------------------------- * _interfaces.h @@ -38,7 +39,7 @@ #define SOUNDCARD_INTERFACE_VERSION "ISoundC002" #define SHADERSYSTEM_INTERFACE_VERSION "ShaderSystem002" -#define FACTORY_INTERFACE_VERSION "VFactorySystem001" +#define FACTORY_INTERFACE_VERSION "VFactorySystem002" #define FILESYSTEM_INTERFACE_VERSION "VFileSystem017" #define BASEFILESYSTEM_INTERFACE_VERSION "VBaseFileSystem011" #define KEYVALUESSYSTEM_INTERFACE_VERSION "VKeyValuesSystem001" @@ -46,37 +47,38 @@ //----------------------------------------------------------------------------- // Class to hold all factories (interfaces) //----------------------------------------------------------------------------- -class CFactory +class CFactorySystem : public IFactorySystem { public: - virtual void AddFactory(const string& svFactoryName, void* pFactory); - virtual void AddFactory(FactoryInfo_t factoryInfo); - virtual size_t GetVersionIndex(const string& svInterfaceName) const; - virtual void GetFactoriesFromRegister(void); - virtual CMemory GetFactoryPtr(const string& svFactoryName, bool versionLess = true) const; - virtual const char* GetFactoryFullName(const string& svFactoryName) const; - -private: - vector m_vFactories; + virtual void AddFactory(InstantiateInterfaceFn createFn, const char* pName) const override; + virtual void* GetFactory(const char* pName) const override; + virtual const char* GetVersion(void) const override; }; -extern CFactory* g_pFactory; -PLATFORM_INTERFACE CFactory* GetFactorySystem(); - -/* ==== s_pInterfaceRegs ==================================================================================================================================================== */ -inline CMemory s_pInterfacesRegs; +extern CFactorySystem* g_pFactorySystem; +PLATFORM_INTERFACE IFactorySystem* GetFactorySystem(); /////////////////////////////////////////////////////////////////////////////// + +inline CMemory p_CreateInterfaceInternal; +inline void*(*CreateInterfaceInternal)(const char* pName, int* pReturnCode); + class VFactory : public IDetour { virtual void GetAdr(void) const { - LogVarAdr("s_pInterfacesRegs", s_pInterfacesRegs.GetPtr()); + LogFunAdr("CreateInterfaceInternal", reinterpret_cast(s_ppInterfaceRegs)); + LogVarAdr("s_pInterfaceRegs", reinterpret_cast(s_ppInterfaceRegs)); + } + virtual void GetFun(void) const + { + p_CreateInterfaceInternal = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B 1D ?? ?? ?? ?? 48 8B FA"); + CreateInterfaceInternal = p_CreateInterfaceInternal.RCast(); } - virtual void GetFun(void) const { } virtual void GetVar(void) const { - s_pInterfacesRegs = g_GameDll.FindPatternSIMD("E9 ?? ?? ?? ?? CC CC 89 91 ?? ?? ?? ??").FollowNearCallSelf().FindPatternSelf("48 8B 1D", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7); + s_ppInterfaceRegs = g_GameDll.FindPatternSIMD("E9 ?? ?? ?? ?? CC CC 89 91 ?? ?? ?? ??") + .FollowNearCallSelf().FindPatternSelf("48 8B 1D", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } virtual void Attach(void) const { }