From edf013952ca2d110f190dc8cd16e5529846656e4 Mon Sep 17 00:00:00 2001 From: uniboi <64006268+uniboi@users.noreply.github.com> Date: Sun, 4 Feb 2024 02:14:46 +0100 Subject: Plugin interfaces (plugins v4) (#615) 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. --- primedev/plugins/interfaces/IPluginCallbacks.h | 36 ++++++++++++++ primedev/plugins/interfaces/IPluginId.h | 31 ++++++++++++ primedev/plugins/interfaces/interface.cpp | 36 ++++++++++++++ primedev/plugins/interfaces/interface.h | 39 +++++++++++++++ primedev/plugins/interfaces/sys/ISys.cpp | 66 ++++++++++++++++++++++++++ primedev/plugins/interfaces/sys/ISys.h | 21 ++++++++ 6 files changed, 229 insertions(+) create mode 100644 primedev/plugins/interfaces/IPluginCallbacks.h create mode 100644 primedev/plugins/interfaces/IPluginId.h create mode 100644 primedev/plugins/interfaces/interface.cpp create mode 100644 primedev/plugins/interfaces/interface.h create mode 100644 primedev/plugins/interfaces/sys/ISys.cpp create mode 100644 primedev/plugins/interfaces/sys/ISys.h (limited to 'primedev/plugins/interfaces') diff --git a/primedev/plugins/interfaces/IPluginCallbacks.h b/primedev/plugins/interfaces/IPluginCallbacks.h new file mode 100644 index 00000000..c02ce8a6 --- /dev/null +++ b/primedev/plugins/interfaces/IPluginCallbacks.h @@ -0,0 +1,36 @@ +#ifndef IPLUGIN_CALLBACKS_H +#define IPLUGIN_CALLBACKS_H + +#include +#include +#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 diff --git a/primedev/plugins/interfaces/IPluginId.h b/primedev/plugins/interfaces/IPluginId.h new file mode 100644 index 00000000..dc4c548b --- /dev/null +++ b/primedev/plugins/interfaces/IPluginId.h @@ -0,0 +1,31 @@ +#ifndef IPLUGIN_ID_H +#define IPLUGIN_ID_H + +#include +#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 diff --git a/primedev/plugins/interfaces/interface.cpp b/primedev/plugins/interfaces/interface.cpp new file mode 100644 index 00000000..4c006f2c --- /dev/null +++ b/primedev/plugins/interfaces/interface.cpp @@ -0,0 +1,36 @@ +#include +#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; +} diff --git a/primedev/plugins/interfaces/interface.h b/primedev/plugins/interfaces/interface.h new file mode 100644 index 00000000..440db5b2 --- /dev/null +++ b/primedev/plugins/interfaces/interface.h @@ -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(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(&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 diff --git a/primedev/plugins/interfaces/sys/ISys.cpp b/primedev/plugins/interfaces/sys/ISys.cpp new file mode 100644 index 00000000..6b0b41dd --- /dev/null +++ b/primedev/plugins/interfaces/sys/ISys.cpp @@ -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 = 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(handle)); + } + } + + void Unload(HMODULE handle) + { + std::optional plugin = g_pPluginManager->GetPlugin(handle); + if (plugin) + { + plugin->Unload(); + } + else + { + NS::log::PLUGINSYS->warn("Attempted to unload plugin with invalid handle {}", static_cast(handle)); + } + } + + void Reload(HMODULE handle) + { + std::optional plugin = g_pPluginManager->GetPlugin(handle); + if (plugin) + { + plugin->Reload(); + } + else + { + NS::log::PLUGINSYS->warn("Attempted to reload plugin with invalid handle {}", static_cast(handle)); + } + } +}; + +EXPOSE_SINGLE_INTERFACE(CSys, ISys, SYS_VERSION); diff --git a/primedev/plugins/interfaces/sys/ISys.h b/primedev/plugins/interfaces/sys/ISys.h new file mode 100644 index 00000000..3e55a6d9 --- /dev/null +++ b/primedev/plugins/interfaces/sys/ISys.h @@ -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 -- cgit v1.2.3