Skip to content

Commit

Permalink
Plugin interfaces (plugins v4) (#615)
Browse files Browse the repository at this point in the history
Replaces the current plugin api with source interfaces.

- backwards compatible
- no more json in binaries (wtf)
- does not rely on structs from third party libraries (wtf)
- actually initializes variables
- no more basically unused classes

The launcher exposes almost everything required by plugins in interfaces that allow for backwards compatibility.
The only thing that's passed to a plugin directly is the northstar dll HWND and a struct of data that's different for each plugin.
  • Loading branch information
uniboi committed Feb 4, 2024
1 parent 6ad955a commit edf0139
Show file tree
Hide file tree
Showing 24 changed files with 674 additions and 689 deletions.
11 changes: 8 additions & 3 deletions primedev/Northstar.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,16 @@ add_library(
"mods/modmanager.h"
"mods/modsavefiles.cpp"
"mods/modsavefiles.h"
"plugins/plugin_abi.h"
"plugins/pluginbackend.cpp"
"plugins/pluginbackend.h"
"plugins/interfaces/interface.h"
"plugins/interfaces/interface.cpp"
"plugins/interfaces/sys/ISys.h"
"plugins/interfaces/sys/ISys.cpp"
"plugins/interfaces/IPluginId.h"
"plugins/interfaces/IPluginCallbacks.h"
"plugins/plugins.cpp"
"plugins/plugins.h"
"plugins/pluginmanager.h"
"plugins/pluginmanager.cpp"
"scripts/client/clientchathooks.cpp"
"scripts/client/cursorposition.cpp"
"scripts/client/scriptbrowserhooks.cpp"
Expand Down
6 changes: 0 additions & 6 deletions primedev/core/convar/concommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
#include "shared/misccommands.h"
#include "engine/r2engine.h"

#include "plugins/pluginbackend.h"
#include "plugins/plugin_abi.h"

#include <iostream>

//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -152,7 +149,4 @@ ON_DLL_LOAD("engine.dll", ConCommand, (CModule module))
{
ConCommandConstructor = module.Offset(0x415F60).RCast<ConCommandConstructorType>();
AddMiscConCommands();

g_pPluginCommunicationhandler->m_sEngineData.ConCommandConstructor =
reinterpret_cast<PluginConCommandConstructorType>(ConCommandConstructor);
}
9 changes: 0 additions & 9 deletions primedev/core/convar/convar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
#include "convar.h"
#include "core/sourceinterface.h"

#include "plugins/pluginbackend.h"
#include "plugins/plugin_abi.h"

#include <float.h>

typedef void (*ConVarRegisterType)(
Expand Down Expand Up @@ -40,12 +37,6 @@ ON_DLL_LOAD("engine.dll", ConVar, (CModule module))

g_pCVarInterface = new SourceInterface<CCvar>("vstdlib.dll", "VEngineCvar007");
g_pCVar = *g_pCVarInterface;

g_pPluginCommunicationhandler->m_sEngineData.conVarMalloc = reinterpret_cast<PluginConVarMallocType>(conVarMalloc);
g_pPluginCommunicationhandler->m_sEngineData.conVarRegister = reinterpret_cast<PluginConVarRegisterType>(conVarRegister);
g_pPluginCommunicationhandler->m_sEngineData.ConVar_Vtable = reinterpret_cast<void*>(g_pConVar_Vtable);
g_pPluginCommunicationhandler->m_sEngineData.IConVar_Vtable = reinterpret_cast<void*>(g_pIConVar_Vtable);
g_pPluginCommunicationhandler->m_sEngineData.g_pCVar = reinterpret_cast<void*>(g_pCVar);
}

//-----------------------------------------------------------------------------
Expand Down
6 changes: 3 additions & 3 deletions primedev/core/hooks.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include "dedicated/dedicated.h"
#include "plugins/pluginbackend.h"
#include "plugins/pluginmanager.h"

#include <iostream>
#include <wchar.h>
Expand Down Expand Up @@ -417,7 +417,7 @@ HMODULE, WINAPI, (LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags))
if (moduleAddress)
{
CallLoadLibraryACallbacks(lpLibFileName, moduleAddress);
InformPluginsDLLLoad(fs::path(lpLibFileName), moduleAddress);
g_pPluginManager->InformDllLoad(moduleAddress, fs::path(lpLibFileName));
}

return moduleAddress;
Expand Down Expand Up @@ -459,7 +459,7 @@ HMODULE, WINAPI, (LPCWSTR lpLibFileName))
if (moduleAddress)
{
CallLoadLibraryWCallbacks(lpLibFileName, moduleAddress);
InformPluginsDLLLoad(fs::path(lpLibFileName), moduleAddress);
g_pPluginManager->InformDllLoad(moduleAddress, fs::path(lpLibFileName));
}

return moduleAddress;
Expand Down
7 changes: 7 additions & 0 deletions primedev/core/sourceinterface.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
#pragma once
#include <string>

// interface return status
enum class InterfaceStatus : int
{
IFACE_OK = 0,
IFACE_FAILED,
};

// literally just copied from ttf2sdk definition
typedef void* (*CreateInterfaceFn)(const char* pName, int* pReturnCode);

Expand Down
7 changes: 4 additions & 3 deletions primedev/dllmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
#include "core/memalloc.h"
#include "core/vanilla.h"
#include "config/profile.h"
#include "plugins/plugin_abi.h"
#include "plugins/plugins.h"
#include "plugins/pluginbackend.h"
#include "plugins/pluginmanager.h"
#include "util/version.h"
#include "util/wininfo.h"
#include "squirrel/squirrel.h"
#include "server/serverpresence.h"

Expand All @@ -23,6 +23,8 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_NorthstarModule = hModule;
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
Expand Down Expand Up @@ -65,7 +67,6 @@ bool InitialiseNorthstar()
g_pServerPresence = new ServerPresenceManager();

g_pPluginManager = new PluginManager();
g_pPluginCommunicationhandler = new PluginCommunicationHandler();
g_pPluginManager->LoadPlugins();

InitialiseSquirrelManagers();
Expand Down
3 changes: 1 addition & 2 deletions primedev/engine/hoststate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
#include "engine/r2engine.h"
#include "shared/exploit_fixes/ns_limits.h"
#include "squirrel/squirrel.h"
#include "plugins/plugins.h"
#include "plugins/pluginbackend.h"
#include "plugins/pluginmanager.h"

AUTOHOOK_INIT()

Expand Down
5 changes: 3 additions & 2 deletions primedev/logging/crashhandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "util/version.h"
#include "mods/modmanager.h"
#include "plugins/plugins.h"
#include "plugins/pluginmanager.h"

#include <minidumpapiset.h>

Expand Down Expand Up @@ -524,9 +525,9 @@ void CCrashHandler::FormatLoadedPlugins()
if (g_pPluginManager)
{
spdlog::error("Loaded Plugins:");
for (const Plugin& plugin : g_pPluginManager->m_vLoadedPlugins)
for (const Plugin& plugin : g_pPluginManager->GetLoadedPlugins())
{
spdlog::error("\t{}", plugin.name);
spdlog::error("\t{}", plugin.GetName());
}
}
}
Expand Down
36 changes: 36 additions & 0 deletions primedev/plugins/interfaces/IPluginCallbacks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#ifndef IPLUGIN_CALLBACKS_H
#define IPLUGIN_CALLBACKS_H

#include <windows.h>
#include <stdint.h>
#include "squirrel/squirrel.h"

// can't use bitwise ops on enum classes but I don't want these in the global namespace (user defined operators suck)
namespace PluginContext
{
enum : uint64_t
{
DEDICATED = 0x1,
CLIENT = 0x2,
};
}

struct PluginNorthstarData
{
HMODULE pluginHandle;
};

class IPluginCallbacks
{
public:
virtual void
Init(HMODULE northstarModule, const PluginNorthstarData* initData, bool reloaded) = 0; // runs after the plugin is loaded and validated
virtual void Finalize() = 0; // runs after all plugins have been loaded
virtual bool Unload() = 0; // runs just before the library is freed
virtual void OnSqvmCreated(CSquirrelVM* sqvm) = 0;
virtual void OnSqvmDestroying(CSquirrelVM* sqvm) = 0;
virtual void OnLibraryLoaded(HMODULE module, const char* name) = 0;
virtual void RunFrame() = 0;
};

#endif
31 changes: 31 additions & 0 deletions primedev/plugins/interfaces/IPluginId.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef IPLUGIN_ID_H
#define IPLUGIN_ID_H

#include <stdint.h>
#include "squirrel/squirrelclasstypes.h"

#define PLUGIN_ID_VERSION "PluginId001"

// an identifier for the type of string data requested from the plugin
enum class PluginString : int
{
NAME = 0,
LOG_NAME = 1,
DEPENDENCY_NAME = 2,
};

// an identifier for the type of bitflag requested from the plugin
enum class PluginField : int
{
CONTEXT = 0,
};

// an interface that is required from every plugin to query data about it
class IPluginId
{
public:
virtual const char* GetString(PluginString prop) = 0;
virtual int64_t GetField(PluginField prop) = 0;
};

#endif
36 changes: 36 additions & 0 deletions primedev/plugins/interfaces/interface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <string.h>
#include "interface.h"

InterfaceReg* s_pInterfaceRegs;

InterfaceReg::InterfaceReg(InstantiateInterfaceFn fn, const char* pName) : m_pName(pName)
{
m_CreateFn = fn;
m_pNext = s_pInterfaceRegs;
s_pInterfaceRegs = this;
}

void* CreateInterface(const char* pName, InterfaceStatus* pReturnCode)
{
for (InterfaceReg* pCur = s_pInterfaceRegs; pCur; pCur = pCur->m_pNext)
{
if (strcmp(pCur->m_pName, pName) == 0)
{
if (pReturnCode)
{
*pReturnCode = InterfaceStatus::IFACE_OK;
}

NS::log::PLUGINSYS->info("creating interface {}", pName);
return pCur->m_CreateFn();
}
}

if (pReturnCode)
{
*pReturnCode = InterfaceStatus::IFACE_FAILED;
}

NS::log::PLUGINSYS->error("could not find interface {}", pName);
return NULL;
}
39 changes: 39 additions & 0 deletions primedev/plugins/interfaces/interface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#ifndef INTERFACE_H
#define INTERFACE_H

typedef void* (*InstantiateInterfaceFn)();

// Used internally to register classes.
class InterfaceReg
{
public:
InterfaceReg(InstantiateInterfaceFn fn, const char* pName);

InstantiateInterfaceFn m_CreateFn;
const char* m_pName;
InterfaceReg* m_pNext;
};

// Use this to expose an interface that can have multiple instances.
#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);

#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);

// 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)

EXPORT void* CreateInterface(const char* pName, InterfaceStatus* pReturnCode);

#endif
66 changes: 66 additions & 0 deletions primedev/plugins/interfaces/sys/ISys.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include "plugins/interfaces/interface.h"
#include "ISys.h"
#include "plugins/plugins.h"
#include "plugins/pluginmanager.h"

class CSys : public ISys
{
public:
void Log(HMODULE handle, LogLevel level, char* msg)
{
spdlog::level::level_enum spdLevel;

switch (level)
{
case LogLevel::WARN:
spdLevel = spdlog::level::level_enum::warn;
break;
case LogLevel::ERR:
spdLevel = spdlog::level::level_enum::err;
break;
default:
NS::log::PLUGINSYS->warn("Attempted to log with invalid level {}. Defaulting to info", (int)level);
case LogLevel::INFO:
spdLevel = spdlog::level::level_enum::info;
break;
}

std::optional<Plugin> plugin = g_pPluginManager->GetPlugin(handle);
if (plugin)
{
plugin->Log(spdLevel, msg);
}
else
{
NS::log::PLUGINSYS->warn("Attempted to log message '{}' with invalid plugin handle {}", msg, static_cast<void*>(handle));
}
}

void Unload(HMODULE handle)
{
std::optional<Plugin> plugin = g_pPluginManager->GetPlugin(handle);
if (plugin)
{
plugin->Unload();
}
else
{
NS::log::PLUGINSYS->warn("Attempted to unload plugin with invalid handle {}", static_cast<void*>(handle));
}
}

void Reload(HMODULE handle)
{
std::optional<Plugin> plugin = g_pPluginManager->GetPlugin(handle);
if (plugin)
{
plugin->Reload();
}
else
{
NS::log::PLUGINSYS->warn("Attempted to reload plugin with invalid handle {}", static_cast<void*>(handle));
}
}
};

EXPOSE_SINGLE_INTERFACE(CSys, ISys, SYS_VERSION);
21 changes: 21 additions & 0 deletions primedev/plugins/interfaces/sys/ISys.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef ILOGGING_H
#define ILOGGING_H

#define SYS_VERSION "NSSys001"

enum class LogLevel : int
{
INFO = 0,
WARN,
ERR,
};

class ISys
{
public:
virtual void Log(HMODULE handle, LogLevel level, char* msg) = 0;
virtual void Unload(HMODULE handle) = 0;
virtual void Reload(HMODULE handle) = 0;
};

#endif
Loading

0 comments on commit edf0139

Please sign in to comment.