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.
This commit is contained in:
Kawe Mazidjatari 2023-08-22 01:11:49 +02:00
parent ee6e1a6cf0
commit befd38bf51
10 changed files with 154 additions and 167 deletions

View File

@ -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<CIVDebugOverlay*>();
g_pDebugOverlay = (CIVDebugOverlay*)g_pFactorySystem->GetFactory(VDEBUG_OVERLAY_INTERFACE_VERSION);
#ifndef CLIENT_DLL
g_pServerGameDLL = g_pFactory->GetFactoryPtr(INTERFACEVERSION_SERVERGAMEDLL, false).RCast<CServerGameDLL*>();
g_pServerGameClients = g_pFactory->GetFactoryPtr(INTERFACEVERSION_SERVERGAMECLIENTS_NEW, false).RCast<CServerGameClients*>();
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<CServerGameClients*>();
g_pServerGameEntities = g_pFactory->GetFactoryPtr(INTERFACEVERSION_SERVERGAMEENTS, false).RCast<CServerGameEnts*>();
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<CClientEntityList*>();
g_pEngineTraceClient = g_pFactory->GetFactoryPtr(INTERFACEVERSION_ENGINETRACE_CLIENT, false).RCast<CEngineTraceClient*>();
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();

View File

@ -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;
};

View File

@ -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<void*(*)()>();
Assert(getFactorySystemFn, "Could not find GetFactorySystem export from gamesdk.dll");
if (!getFactorySystemFn)
InstantiateInterfaceFn factorySystem = m_SDKModule.GetExportedSymbol("GetFactorySystem").RCast<InstantiateInterfaceFn>();
if (!factorySystem)
{
Assert(factorySystem, "factorySystem == NULL; symbol renamed???");
return false;
}
m_FactoryInstance = reinterpret_cast<IFactory*>(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<IPluginSystem*>();
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;
}

View File

@ -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;

View File

@ -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) \

View File

@ -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<interfaceName *>( 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<interfaceName *>( &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

View File

@ -24,6 +24,7 @@ add_sources( SOURCE_GROUP "Private"
"cmd.cpp"
"convar.cpp"
"cvar.cpp"
"interface.cpp"
)
file( GLOB TIER1_PUBLIC_HEADERS

20
r5dev/tier1/interface.cpp Normal file
View File

@ -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;
}

View File

@ -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<uintptr_t>(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<InterfaceGlobals_t*>();
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<uintptr_t>(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();
//---------------------------------------------------------------------------------
// Purpose:
//---------------------------------------------------------------------------------
IFactorySystem* GetFactorySystem()
{
return g_pFactorySystem;
}
CFactorySystem* g_pFactorySystem = new CFactorySystem();

View File

@ -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<FactoryInfo_t> 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<uintptr_t>(s_ppInterfaceRegs));
LogVarAdr("s_pInterfaceRegs", reinterpret_cast<uintptr_t>(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<void*(*)(const char*, int*)>();
}
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<InterfaceReg**>();
}
virtual void GetCon(void) const { }
virtual void Attach(void) const { }