diff options
Diffstat (limited to 'NorthstarDLL/plugins')
-rw-r--r-- | NorthstarDLL/plugins/plugin_abi.h | 151 | ||||
-rw-r--r-- | NorthstarDLL/plugins/pluginbackend.cpp | 50 | ||||
-rw-r--r-- | NorthstarDLL/plugins/pluginbackend.h | 40 | ||||
-rw-r--r-- | NorthstarDLL/plugins/plugins.cpp | 340 | ||||
-rw-r--r-- | NorthstarDLL/plugins/plugins.h | 59 |
5 files changed, 0 insertions, 640 deletions
diff --git a/NorthstarDLL/plugins/plugin_abi.h b/NorthstarDLL/plugins/plugin_abi.h deleted file mode 100644 index 16b26a1c..00000000 --- a/NorthstarDLL/plugins/plugin_abi.h +++ /dev/null @@ -1,151 +0,0 @@ -#pragma once -#include "squirrel/squirrelclasstypes.h" - -#define ABI_VERSION 3 - -enum PluginLoadDLL -{ - ENGINE = 0, - CLIENT, - SERVER -}; - -enum ObjectType -{ - CONCOMMANDS = 0, - CONVAR = 1, -}; - -struct SquirrelFunctions -{ - RegisterSquirrelFuncType RegisterSquirrelFunc; - sq_defconstType __sq_defconst; - - sq_compilebufferType __sq_compilebuffer; - sq_callType __sq_call; - sq_raiseerrorType __sq_raiseerror; - sq_compilefileType __sq_compilefile; - - sq_newarrayType __sq_newarray; - sq_arrayappendType __sq_arrayappend; - - sq_newtableType __sq_newtable; - sq_newslotType __sq_newslot; - - sq_pushroottableType __sq_pushroottable; - sq_pushstringType __sq_pushstring; - sq_pushintegerType __sq_pushinteger; - sq_pushfloatType __sq_pushfloat; - sq_pushboolType __sq_pushbool; - sq_pushassetType __sq_pushasset; - sq_pushvectorType __sq_pushvector; - sq_pushobjectType __sq_pushobject; - - sq_getstringType __sq_getstring; - sq_getintegerType __sq_getinteger; - sq_getfloatType __sq_getfloat; - sq_getboolType __sq_getbool; - sq_getType __sq_get; - sq_getassetType __sq_getasset; - sq_getuserdataType __sq_getuserdata; - sq_getvectorType __sq_getvector; - sq_getthisentityType __sq_getthisentity; - sq_getobjectType __sq_getobject; - - sq_stackinfosType __sq_stackinfos; - - sq_createuserdataType __sq_createuserdata; - sq_setuserdatatypeidType __sq_setuserdatatypeid; - sq_getfunctionType __sq_getfunction; - - sq_schedule_call_externalType __sq_schedule_call_external; - - sq_getentityfrominstanceType __sq_getentityfrominstance; - sq_GetEntityConstantType __sq_GetEntityConstant_CBaseEntity; - - sq_pushnewstructinstanceType __sq_pushnewstructinstance; - sq_sealstructslotType __sq_sealstructslot; -}; - -struct MessageSource -{ - const char* file; - const char* func; - int line; -}; - -// This is a modified version of spdlog::details::log_msg -// This is so that we can make it cross DLL boundaries -struct LogMsg -{ - int level; - uint64_t timestamp; - const char* msg; - MessageSource source; - int pluginHandle; -}; - -extern "C" -{ - typedef void (*loggerfunc_t)(LogMsg* msg); - typedef void (*PLUGIN_RELAY_INVITE_TYPE)(const char* invite); - typedef void* (*CreateObjectFunc)(ObjectType type); - - typedef void (*PluginFnCommandCallback_t)(void* command); - typedef void (*PluginConCommandConstructorType)( - void* newCommand, const char* name, PluginFnCommandCallback_t callback, const char* helpString, int flags, void* parent); - typedef void (*PluginConVarRegisterType)( - void* pConVar, - const char* pszName, - const char* pszDefaultValue, - int nFlags, - const char* pszHelpString, - bool bMin, - float fMin, - bool bMax, - float fMax, - void* pCallback); - typedef void (*PluginConVarMallocType)(void* pConVarMaloc, int a2, int a3); -} - -struct PluginNorthstarData -{ - const char* version; - HMODULE northstarModule; - int pluginHandle; -}; - -struct PluginInitFuncs -{ - loggerfunc_t logger; - PLUGIN_RELAY_INVITE_TYPE relayInviteFunc; - CreateObjectFunc createObject; -}; - -struct PluginEngineData -{ - PluginConCommandConstructorType ConCommandConstructor; - PluginConVarMallocType conVarMalloc; - PluginConVarRegisterType conVarRegister; - void* ConVar_Vtable; - void* IConVar_Vtable; - void* g_pCVar; -}; - -/// <summary> Async communication within the plugin system -/// Due to the asynchronous nature of plugins, combined with the limitations of multi-compiler support -/// and the custom memory allocator used by r2, is it difficult to safely get data across DLL boundaries -/// from Northstar to plugin unless Northstar can own that memory. -/// This means that plugins should manage their own memory and can only receive data from northstar using one of the functions below. -/// These should be exports of the plugin DLL. If they are not exported, they will not be called. -/// Note that it is not required to have these exports if you do not use them. -/// </summary> - -// Northstar -> Plugin -typedef void (*PLUGIN_INIT_TYPE)(PluginInitFuncs* funcs, PluginNorthstarData* data); -typedef void (*PLUGIN_INIT_SQVM_TYPE)(SquirrelFunctions* funcs); -typedef void (*PLUGIN_INFORM_SQVM_CREATED_TYPE)(ScriptContext context, CSquirrelVM* sqvm); -typedef void (*PLUGIN_INFORM_SQVM_DESTROYED_TYPE)(ScriptContext context); - -typedef void (*PLUGIN_INFORM_DLL_LOAD_TYPE)(const char* dll, PluginEngineData* data, void* dllPtr); -typedef void (*PLUGIN_RUNFRAME)(); diff --git a/NorthstarDLL/plugins/pluginbackend.cpp b/NorthstarDLL/plugins/pluginbackend.cpp deleted file mode 100644 index 850394ac..00000000 --- a/NorthstarDLL/plugins/pluginbackend.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "pluginbackend.h" -#include "plugin_abi.h" -#include "server/serverpresence.h" -#include "masterserver/masterserver.h" -#include "squirrel/squirrel.h" -#include "plugins.h" - -#include "core/convar/concommand.h" - -#include <filesystem> - -#define EXPORT extern "C" __declspec(dllexport) - -AUTOHOOK_INIT() - -PluginCommunicationHandler* g_pPluginCommunicationhandler; - -static PluginDataRequest storedRequest {PluginDataRequestType::END, (PluginRespondDataCallable) nullptr}; - -void PluginCommunicationHandler::RunFrame() -{ - std::lock_guard<std::mutex> lock(requestMutex); - if (!requestQueue.empty()) - { - storedRequest = requestQueue.front(); - switch (storedRequest.type) - { - default: - spdlog::error("{} was called with invalid request type '{}'", __FUNCTION__, static_cast<int>(storedRequest.type)); - } - requestQueue.pop(); - } -} - -void PluginCommunicationHandler::PushRequest(PluginDataRequestType type, PluginRespondDataCallable func) -{ - std::lock_guard<std::mutex> lock(requestMutex); - requestQueue.push(PluginDataRequest {type, func}); -} - -void InformPluginsDLLLoad(fs::path dllPath, void* address) -{ - std::string dllName = dllPath.filename().string(); - - void* data = NULL; - if (strncmp(dllName.c_str(), "engine.dll", 10) == 0) - data = &g_pPluginCommunicationhandler->m_sEngineData; - - g_pPluginManager->InformDLLLoad(dllName.c_str(), data, address); -} diff --git a/NorthstarDLL/plugins/pluginbackend.h b/NorthstarDLL/plugins/pluginbackend.h deleted file mode 100644 index 45cd42f3..00000000 --- a/NorthstarDLL/plugins/pluginbackend.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once -#include "plugin_abi.h" - -#include <queue> -#include <mutex> - -enum PluginDataRequestType -{ - END = 0, -}; - -union PluginRespondDataCallable -{ - // Empty for now - void* UNUSED; -}; - -class PluginDataRequest -{ -public: - PluginDataRequestType type; - PluginRespondDataCallable func; - PluginDataRequest(PluginDataRequestType type, PluginRespondDataCallable func) : type(type), func(func) {} -}; - -class PluginCommunicationHandler -{ -public: - void RunFrame(); - void PushRequest(PluginDataRequestType type, PluginRespondDataCallable func); - -public: - std::queue<PluginDataRequest> requestQueue = {}; - std::mutex requestMutex; - - PluginEngineData m_sEngineData {}; -}; - -void InformPluginsDLLLoad(fs::path dllPath, void* address); -extern PluginCommunicationHandler* g_pPluginCommunicationhandler; diff --git a/NorthstarDLL/plugins/plugins.cpp b/NorthstarDLL/plugins/plugins.cpp deleted file mode 100644 index 72b64566..00000000 --- a/NorthstarDLL/plugins/plugins.cpp +++ /dev/null @@ -1,340 +0,0 @@ -#include "plugins.h" -#include "config/profile.h" - -#include "squirrel/squirrel.h" -#include "plugins.h" -#include "masterserver/masterserver.h" -#include "core/convar/convar.h" -#include "server/serverpresence.h" -#include <optional> -#include <regex> - -#include "util/version.h" -#include "pluginbackend.h" -#include "util/wininfo.h" -#include "logging/logging.h" -#include "dedicated/dedicated.h" - -PluginManager* g_pPluginManager; - -void freeLibrary(HMODULE hLib) -{ - if (!FreeLibrary(hLib)) - { - spdlog::error("There was an error while trying to free library"); - } -} - -EXPORT void PLUGIN_LOG(LogMsg* msg) -{ - spdlog::source_loc src {}; - src.filename = msg->source.file; - src.funcname = msg->source.func; - src.line = msg->source.line; - auto&& logger = g_pPluginManager->m_vLoadedPlugins[msg->pluginHandle].logger; - logger->log(src, (spdlog::level::level_enum)msg->level, msg->msg); -} - -EXPORT void* CreateObject(ObjectType type) -{ - switch (type) - { - case ObjectType::CONVAR: - return (void*)new ConVar; - case ObjectType::CONCOMMANDS: - return (void*)new ConCommand; - default: - return NULL; - } -} - -std::optional<Plugin> PluginManager::LoadPlugin(fs::path path, PluginInitFuncs* funcs, PluginNorthstarData* data) -{ - - Plugin plugin {}; - - std::string pathstring = path.string(); - std::wstring wpath = path.wstring(); - - LPCWSTR wpptr = wpath.c_str(); - HMODULE datafile = LoadLibraryExW(wpptr, 0, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE); // Load the DLL as a data file - if (datafile == NULL) - { - NS::log::PLUGINSYS->info("Failed to load library '{}': ", std::system_category().message(GetLastError())); - return std::nullopt; - } - HRSRC manifestResource = FindResourceW(datafile, MAKEINTRESOURCEW(IDR_RCDATA1), RT_RCDATA); - - if (manifestResource == NULL) - { - NS::log::PLUGINSYS->info("Could not find manifest for library '{}'", pathstring); - freeLibrary(datafile); - return std::nullopt; - } - HGLOBAL myResourceData = LoadResource(datafile, manifestResource); - if (myResourceData == NULL) - { - NS::log::PLUGINSYS->error("Failed to load manifest from library '{}'", pathstring); - freeLibrary(datafile); - return std::nullopt; - } - int manifestSize = SizeofResource(datafile, manifestResource); - std::string manifest = std::string((const char*)LockResource(myResourceData), 0, manifestSize); - freeLibrary(datafile); - - rapidjson_document manifestJSON; - manifestJSON.Parse(manifest.c_str()); - - if (manifestJSON.HasParseError()) - { - NS::log::PLUGINSYS->error("Manifest for '{}' was invalid", pathstring); - return std::nullopt; - } - if (!manifestJSON.HasMember("name")) - { - NS::log::PLUGINSYS->error("'{}' is missing a name in its manifest", pathstring); - return std::nullopt; - } - if (!manifestJSON.HasMember("displayname")) - { - NS::log::PLUGINSYS->error("'{}' is missing a displayname in its manifest", pathstring); - return std::nullopt; - } - if (!manifestJSON.HasMember("description")) - { - NS::log::PLUGINSYS->error("'{}' is missing a description in its manifest", pathstring); - return std::nullopt; - } - if (!manifestJSON.HasMember("api_version")) - { - NS::log::PLUGINSYS->error("'{}' is missing a api_version in its manifest", pathstring); - return std::nullopt; - } - if (!manifestJSON.HasMember("version")) - { - NS::log::PLUGINSYS->error("'{}' is missing a version in its manifest", pathstring); - return std::nullopt; - } - if (!manifestJSON.HasMember("run_on_server")) - { - NS::log::PLUGINSYS->error("'{}' is missing 'run_on_server' in its manifest", pathstring); - return std::nullopt; - } - if (!manifestJSON.HasMember("run_on_client")) - { - NS::log::PLUGINSYS->error("'{}' is missing 'run_on_client' in its manifest", pathstring); - return std::nullopt; - } - auto test = manifestJSON["api_version"].GetString(); - if (strcmp(manifestJSON["api_version"].GetString(), std::to_string(ABI_VERSION).c_str())) - { - NS::log::PLUGINSYS->error( - "'{}' has an incompatible API version number in its manifest. Current ABI version is '{}'", pathstring, ABI_VERSION); - return std::nullopt; - } - // Passed all checks, going to actually load it now - - HMODULE pluginLib = - LoadLibraryExW(wpptr, 0, LOAD_LIBRARY_SEARCH_USER_DIRS | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); // Load the DLL with lib folders - if (pluginLib == NULL) - { - NS::log::PLUGINSYS->info("Failed to load library '{}': ", std::system_category().message(GetLastError())); - return std::nullopt; - } - plugin.init = (PLUGIN_INIT_TYPE)GetProcAddress(pluginLib, "PLUGIN_INIT"); - if (plugin.init == NULL) - { - NS::log::PLUGINSYS->info("Library '{}' has no function 'PLUGIN_INIT'", pathstring); - return std::nullopt; - } - NS::log::PLUGINSYS->info("Succesfully loaded {}", pathstring); - - plugin.name = manifestJSON["name"].GetString(); - plugin.displayName = manifestJSON["displayname"].GetString(); - plugin.description = manifestJSON["description"].GetString(); - plugin.api_version = manifestJSON["api_version"].GetString(); - plugin.version = manifestJSON["version"].GetString(); - - plugin.run_on_client = manifestJSON["run_on_client"].GetBool(); - plugin.run_on_server = manifestJSON["run_on_server"].GetBool(); - - if (!plugin.run_on_server && IsDedicatedServer()) - return std::nullopt; - - if (manifestJSON.HasMember("dependencyName")) - { - plugin.dependencyName = manifestJSON["dependencyName"].GetString(); - } - else - { - plugin.dependencyName = plugin.name; - } - - if (std::find_if( - plugin.dependencyName.begin(), - plugin.dependencyName.end(), - [&](char c) -> bool { return !((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_'); }) != - plugin.dependencyName.end()) - { - NS::log::PLUGINSYS->warn("Dependency string \"{}\" in {} is not valid a squirrel constant!", plugin.dependencyName, plugin.name); - } - - plugin.init_sqvm_client = (PLUGIN_INIT_SQVM_TYPE)GetProcAddress(pluginLib, "PLUGIN_INIT_SQVM_CLIENT"); - plugin.init_sqvm_server = (PLUGIN_INIT_SQVM_TYPE)GetProcAddress(pluginLib, "PLUGIN_INIT_SQVM_SERVER"); - plugin.inform_sqvm_created = (PLUGIN_INFORM_SQVM_CREATED_TYPE)GetProcAddress(pluginLib, "PLUGIN_INFORM_SQVM_CREATED"); - plugin.inform_sqvm_destroyed = (PLUGIN_INFORM_SQVM_DESTROYED_TYPE)GetProcAddress(pluginLib, "PLUGIN_INFORM_SQVM_DESTROYED"); - - plugin.inform_dll_load = (PLUGIN_INFORM_DLL_LOAD_TYPE)GetProcAddress(pluginLib, "PLUGIN_INFORM_DLL_LOAD"); - - plugin.run_frame = (PLUGIN_RUNFRAME)GetProcAddress(pluginLib, "PLUGIN_RUNFRAME"); - - plugin.handle = m_vLoadedPlugins.size(); - plugin.logger = std::make_shared<ColoredLogger>(plugin.displayName.c_str(), NS::Colors::PLUGIN); - RegisterLogger(plugin.logger); - NS::log::PLUGINSYS->info("Loading plugin {} version {}", plugin.displayName, plugin.version); - m_vLoadedPlugins.push_back(plugin); - - plugin.init(funcs, data); - - return plugin; -} - -inline void FindPlugins(fs::path pluginPath, std::vector<fs::path>& paths) -{ - // ensure dirs exist - if (!fs::exists(pluginPath) || !fs::is_directory(pluginPath)) - { - return; - } - - for (const fs::directory_entry& entry : fs::directory_iterator(pluginPath)) - { - if (fs::is_regular_file(entry) && entry.path().extension() == ".dll") - paths.emplace_back(entry.path()); - } -} - -bool PluginManager::LoadPlugins() -{ - if (strstr(GetCommandLineA(), "-noplugins") != NULL) - { - NS::log::PLUGINSYS->warn("-noplugins detected; skipping loading plugins"); - return false; - } - - fs::create_directories(GetThunderstoreModFolderPath()); - - std::vector<fs::path> paths; - - pluginPath = GetNorthstarPrefix() + "\\plugins"; - - PluginNorthstarData data {}; - std::string ns_version {version}; - - PluginInitFuncs funcs {}; - funcs.logger = PLUGIN_LOG; - funcs.relayInviteFunc = nullptr; - funcs.createObject = CreateObject; - - data.version = ns_version.c_str(); - data.northstarModule = g_NorthstarModule; - - fs::path libPath = fs::absolute(pluginPath + "\\lib"); - if (fs::exists(libPath) && fs::is_directory(libPath)) - AddDllDirectory(libPath.wstring().c_str()); - - FindPlugins(pluginPath, paths); - - // Special case for Thunderstore mods dir - std::filesystem::directory_iterator thunderstoreModsDir = fs::directory_iterator(GetThunderstoreModFolderPath()); - // Set up regex for `AUTHOR-MOD-VERSION` pattern - std::regex pattern(R"(.*\\([a-zA-Z0-9_]+)-([a-zA-Z0-9_]+)-(\d+\.\d+\.\d+))"); - for (fs::directory_entry dir : thunderstoreModsDir) - { - fs::path pluginsDir = dir.path() / "plugins"; - // Use regex to match `AUTHOR-MOD-VERSION` pattern - if (!std::regex_match(dir.path().string(), pattern)) - { - spdlog::warn("The following directory did not match 'AUTHOR-MOD-VERSION': {}", dir.path().string()); - continue; // skip loading package that doesn't match - } - - fs::path libDir = fs::absolute(pluginsDir / "lib"); - if (fs::exists(libDir) && fs::is_directory(libDir)) - AddDllDirectory(libDir.wstring().c_str()); - - FindPlugins(pluginsDir, paths); - } - - if (paths.empty()) - { - NS::log::PLUGINSYS->warn("Could not find any plugins. Skipped loading plugins"); - return false; - } - - for (fs::path path : paths) - { - if (LoadPlugin(path, &funcs, &data)) - data.pluginHandle += 1; - } - return true; -} - -void PluginManager::InformSQVMLoad(ScriptContext context, SquirrelFunctions* s) -{ - for (auto plugin : m_vLoadedPlugins) - { - if (context == ScriptContext::CLIENT && plugin.init_sqvm_client != NULL) - { - plugin.init_sqvm_client(s); - } - else if (context == ScriptContext::SERVER && plugin.init_sqvm_server != NULL) - { - plugin.init_sqvm_server(s); - } - } -} - -void PluginManager::InformSQVMCreated(ScriptContext context, CSquirrelVM* sqvm) -{ - for (auto plugin : m_vLoadedPlugins) - { - if (plugin.inform_sqvm_created != NULL) - { - plugin.inform_sqvm_created(context, sqvm); - } - } -} - -void PluginManager::InformSQVMDestroyed(ScriptContext context) -{ - for (auto plugin : m_vLoadedPlugins) - { - if (plugin.inform_sqvm_destroyed != NULL) - { - plugin.inform_sqvm_destroyed(context); - } - } -} - -void PluginManager::InformDLLLoad(const char* dll, void* data, void* dllPtr) -{ - for (auto plugin : m_vLoadedPlugins) - { - if (plugin.inform_dll_load != NULL) - { - plugin.inform_dll_load(dll, (PluginEngineData*)data, dllPtr); - } - } -} - -void PluginManager::RunFrame() -{ - for (auto plugin : m_vLoadedPlugins) - { - if (plugin.run_frame != NULL) - { - plugin.run_frame(); - } - } -} diff --git a/NorthstarDLL/plugins/plugins.h b/NorthstarDLL/plugins/plugins.h deleted file mode 100644 index 4e841f27..00000000 --- a/NorthstarDLL/plugins/plugins.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -#include "plugin_abi.h" - -const int IDR_RCDATA1 = 101; - -class Plugin -{ -public: - std::string name; - std::string displayName; - std::string dependencyName; - std::string description; - - std::string api_version; - std::string version; - - // For now this is just implemented as the index into the plugins array - // Maybe a bit shit but it works - int handle; - - std::shared_ptr<ColoredLogger> logger; - - bool run_on_client = false; - bool run_on_server = false; - -public: - PLUGIN_INIT_TYPE init; - PLUGIN_INIT_SQVM_TYPE init_sqvm_client; - PLUGIN_INIT_SQVM_TYPE init_sqvm_server; - PLUGIN_INFORM_SQVM_CREATED_TYPE inform_sqvm_created; - PLUGIN_INFORM_SQVM_DESTROYED_TYPE inform_sqvm_destroyed; - - PLUGIN_INFORM_DLL_LOAD_TYPE inform_dll_load; - - PLUGIN_RUNFRAME run_frame; -}; - -class PluginManager -{ -public: - std::vector<Plugin> m_vLoadedPlugins; - -public: - bool LoadPlugins(); - std::optional<Plugin> LoadPlugin(fs::path path, PluginInitFuncs* funcs, PluginNorthstarData* data); - - void InformSQVMLoad(ScriptContext context, SquirrelFunctions* s); - void InformSQVMCreated(ScriptContext context, CSquirrelVM* sqvm); - void InformSQVMDestroyed(ScriptContext context); - - void InformDLLLoad(const char* dll, void* data, void* dllPtr); - - void RunFrame(); - -private: - std::string pluginPath; -}; - -extern PluginManager* g_pPluginManager; |