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.
This commit is contained in:
parent
6ad955ae0a
commit
edf013952c
|
@ -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"
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
#include "shared/misccommands.h"
|
||||
#include "engine/r2engine.h"
|
||||
|
||||
#include "plugins/pluginbackend.h"
|
||||
#include "plugins/plugin_abi.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)(
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "dedicated/dedicated.h"
|
||||
#include "plugins/pluginbackend.h"
|
||||
#include "plugins/pluginmanager.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <wchar.h>
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
@ -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:
|
||||
|
@ -65,7 +67,6 @@ bool InitialiseNorthstar()
|
|||
g_pServerPresence = new ServerPresenceManager();
|
||||
|
||||
g_pPluginManager = new PluginManager();
|
||||
g_pPluginCommunicationhandler = new PluginCommunicationHandler();
|
||||
g_pPluginManager->LoadPlugins();
|
||||
|
||||
InitialiseSquirrelManagers();
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "util/version.h"
|
||||
#include "mods/modmanager.h"
|
||||
#include "plugins/plugins.h"
|
||||
#include "plugins/pluginmanager.h"
|
||||
|
||||
#include <minidumpapiset.h>
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
|
@ -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
|
|
@ -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)();
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -0,0 +1,184 @@
|
|||
#include "pluginmanager.h"
|
||||
|
||||
#include <regex>
|
||||
#include "plugins.h"
|
||||
#include "config/profile.h"
|
||||
#include "core/convar/concommand.h"
|
||||
|
||||
PluginManager* g_pPluginManager;
|
||||
|
||||
const std::vector<Plugin>& PluginManager::GetLoadedPlugins() const
|
||||
{
|
||||
return this->plugins;
|
||||
}
|
||||
|
||||
const std::optional<Plugin> PluginManager::GetPlugin(HMODULE handle) const
|
||||
{
|
||||
for (const Plugin& plugin : GetLoadedPlugins())
|
||||
if (plugin.m_handle == handle)
|
||||
return plugin;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void PluginManager::LoadPlugin(fs::path path, bool reloaded)
|
||||
{
|
||||
Plugin plugin = Plugin(path.string());
|
||||
|
||||
if (!plugin.IsValid())
|
||||
{
|
||||
NS::log::PLUGINSYS->warn("Unloading invalid plugin '{}'", path.string());
|
||||
plugin.Unload();
|
||||
return;
|
||||
}
|
||||
|
||||
plugins.push_back(plugin);
|
||||
plugin.Init(reloaded);
|
||||
}
|
||||
|
||||
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(bool reloaded)
|
||||
{
|
||||
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";
|
||||
|
||||
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)
|
||||
{
|
||||
LoadPlugin(path, reloaded);
|
||||
}
|
||||
|
||||
InformAllPluginsInitialized();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PluginManager::ReloadPlugins()
|
||||
{
|
||||
for (const Plugin& plugin : this->GetLoadedPlugins())
|
||||
{
|
||||
plugin.Unload();
|
||||
}
|
||||
|
||||
this->plugins.clear();
|
||||
this->LoadPlugins(true);
|
||||
}
|
||||
|
||||
void PluginManager::RemovePlugin(HMODULE handle)
|
||||
{
|
||||
for (size_t i = 0; i < plugins.size(); i++)
|
||||
{
|
||||
Plugin* plugin = &plugins[i];
|
||||
if (plugin->m_handle == handle)
|
||||
{
|
||||
plugins.erase(plugins.begin() + i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::InformAllPluginsInitialized() const
|
||||
{
|
||||
for (const Plugin& plugin : GetLoadedPlugins())
|
||||
{
|
||||
plugin.Finalize();
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::InformSqvmCreated(CSquirrelVM* sqvm) const
|
||||
{
|
||||
for (const Plugin& plugin : GetLoadedPlugins())
|
||||
{
|
||||
plugin.OnSqvmCreated(sqvm);
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::InformSqvmDestroying(CSquirrelVM* sqvm) const
|
||||
{
|
||||
for (const Plugin& plugin : GetLoadedPlugins())
|
||||
{
|
||||
plugin.OnSqvmDestroying(sqvm);
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::InformDllLoad(HMODULE module, fs::path path) const
|
||||
{
|
||||
std::string fn = path.filename().string(); // without this the string gets freed immediately lmao
|
||||
const char* filename = fn.c_str();
|
||||
for (const Plugin& plugin : GetLoadedPlugins())
|
||||
{
|
||||
plugin.OnLibraryLoaded(module, filename);
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::RunFrame() const
|
||||
{
|
||||
for (const Plugin& plugin : GetLoadedPlugins())
|
||||
{
|
||||
plugin.RunFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void ConCommand_reload_plugins(const CCommand& args)
|
||||
{
|
||||
g_pPluginManager->ReloadPlugins();
|
||||
}
|
||||
|
||||
ON_DLL_LOAD_RELIESON("engine.dll", PluginManager, ConCommand, (CModule module))
|
||||
{
|
||||
RegisterConCommand("reload_plugins", ConCommand_reload_plugins, "reloads plugins", FCVAR_NONE);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef PLUGIN_MANAGER_H
|
||||
#define PLUGIN_MANAGER_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
class Plugin;
|
||||
|
||||
class PluginManager
|
||||
{
|
||||
public:
|
||||
const std::vector<Plugin>& GetLoadedPlugins() const;
|
||||
const std::optional<Plugin> GetPlugin(HMODULE handle) const;
|
||||
bool LoadPlugins(bool reloaded = false);
|
||||
void LoadPlugin(fs::path path, bool reloaded = false);
|
||||
void ReloadPlugins();
|
||||
void RemovePlugin(HMODULE handle);
|
||||
|
||||
// callback triggers
|
||||
void InformSqvmCreated(CSquirrelVM* sqvm) const;
|
||||
void InformSqvmDestroying(CSquirrelVM* sqvm) const;
|
||||
void InformDllLoad(HMODULE module, fs::path path) const;
|
||||
void RunFrame() const;
|
||||
|
||||
private:
|
||||
void InformAllPluginsInitialized() const;
|
||||
|
||||
std::vector<Plugin> plugins;
|
||||
std::string pluginPath;
|
||||
};
|
||||
|
||||
extern PluginManager* g_pPluginManager;
|
||||
|
||||
#endif
|
|
@ -1,340 +1,233 @@
|
|||
#include "plugins.h"
|
||||
#include "config/profile.h"
|
||||
|
||||
#include "pluginmanager.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 "core/sourceinterface.h"
|
||||
#include "logging/logging.h"
|
||||
#include "dedicated/dedicated.h"
|
||||
|
||||
PluginManager* g_pPluginManager;
|
||||
|
||||
void freeLibrary(HMODULE hLib)
|
||||
bool isValidSquirrelIdentifier(std::string s)
|
||||
{
|
||||
if (!FreeLibrary(hLib))
|
||||
if (!s.size())
|
||||
return false; // identifiers can't be empty
|
||||
if (s[0] <= 57)
|
||||
return false; // identifier can't start with a number
|
||||
for (char& c : s)
|
||||
{
|
||||
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 = (int)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");
|
||||
// only allow underscores, 0-9, A-Z and a-z
|
||||
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
|
||||
continue;
|
||||
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)
|
||||
Plugin::Plugin(std::string path) : m_location(path)
|
||||
{
|
||||
for (auto plugin : m_vLoadedPlugins)
|
||||
HMODULE pluginModule = GetModuleHandleA(path.c_str());
|
||||
|
||||
if (pluginModule)
|
||||
{
|
||||
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);
|
||||
}
|
||||
// plugins may refuse to get unloaded for any reason so we need to prevent them getting loaded twice when reloading plugins
|
||||
NS::log::PLUGINSYS->warn("Plugin has already been loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
m_handle = LoadLibraryExA(path.c_str(), 0, LOAD_LIBRARY_SEARCH_USER_DIRS | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
|
||||
|
||||
NS::log::PLUGINSYS->info("loaded plugin handle {}", static_cast<void*>(m_handle));
|
||||
|
||||
if (!m_handle)
|
||||
{
|
||||
NS::log::PLUGINSYS->error("Failed to load plugin '{}' (Error: {})", path, GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
m_initData = {.pluginHandle = m_handle};
|
||||
|
||||
CreateInterfaceFn CreatePluginInterface = (CreateInterfaceFn)GetProcAddress(m_handle, "CreateInterface");
|
||||
|
||||
if (!CreatePluginInterface)
|
||||
{
|
||||
NS::log::PLUGINSYS->error("Plugin at '{}' does not expose CreateInterface()", path);
|
||||
return;
|
||||
}
|
||||
|
||||
m_pluginId = (IPluginId*)CreatePluginInterface(PLUGIN_ID_VERSION, 0);
|
||||
|
||||
if (!m_pluginId)
|
||||
{
|
||||
NS::log::PLUGINSYS->error("Could not load IPluginId interface of plugin at '{}'", path);
|
||||
return;
|
||||
}
|
||||
|
||||
const char* name = m_pluginId->GetString(PluginString::NAME);
|
||||
const char* logName = m_pluginId->GetString(PluginString::LOG_NAME);
|
||||
const char* dependencyName = m_pluginId->GetString(PluginString::DEPENDENCY_NAME);
|
||||
int64_t context = m_pluginId->GetField(PluginField::CONTEXT);
|
||||
|
||||
m_runOnServer = context & PluginContext::DEDICATED;
|
||||
m_runOnClient = context & PluginContext::CLIENT;
|
||||
|
||||
m_name = std::string(name);
|
||||
m_logName = std::string(logName);
|
||||
m_dependencyName = std::string(dependencyName);
|
||||
|
||||
if (!name)
|
||||
{
|
||||
NS::log::PLUGINSYS->error("Could not load name of plugin at '{}'", path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!logName)
|
||||
{
|
||||
NS::log::PLUGINSYS->error("Could not load logName of plugin {}", name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dependencyName)
|
||||
{
|
||||
NS::log::PLUGINSYS->error("Could not load dependencyName of plugin {}", name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isValidSquirrelIdentifier(m_dependencyName))
|
||||
{
|
||||
NS::log::PLUGINSYS->error("Dependency name \"{}\" of plugin {} is not valid", dependencyName, name);
|
||||
return;
|
||||
}
|
||||
|
||||
m_callbacks = (IPluginCallbacks*)CreatePluginInterface("PluginCallbacks001", 0);
|
||||
|
||||
if (!m_callbacks)
|
||||
{
|
||||
NS::log::PLUGINSYS->error("Could not create callback interface of plugin {}", name);
|
||||
return;
|
||||
}
|
||||
|
||||
m_logger = std::make_shared<ColoredLogger>(m_logName, NS::Colors::PLUGIN);
|
||||
RegisterLogger(m_logger);
|
||||
|
||||
if (IsDedicatedServer() && !m_runOnServer)
|
||||
{
|
||||
NS::log::PLUGINSYS->info("Unloading {} because it's not supposed to run on dedicated servers", m_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsDedicatedServer() && !m_runOnClient)
|
||||
{
|
||||
NS::log::PLUGINSYS->info("Unloading {} because it's only supposed to run on dedicated servers", m_name);
|
||||
return;
|
||||
}
|
||||
|
||||
m_valid = true;
|
||||
}
|
||||
|
||||
void PluginManager::InformSQVMCreated(ScriptContext context, CSquirrelVM* sqvm)
|
||||
bool Plugin::Unload() const
|
||||
{
|
||||
for (auto plugin : m_vLoadedPlugins)
|
||||
if (!m_handle)
|
||||
return true;
|
||||
|
||||
if (IsValid())
|
||||
{
|
||||
if (plugin.inform_sqvm_created != NULL)
|
||||
{
|
||||
plugin.inform_sqvm_created(context, sqvm);
|
||||
}
|
||||
bool unloaded = m_callbacks->Unload();
|
||||
|
||||
if (!unloaded)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FreeLibrary(m_handle))
|
||||
{
|
||||
NS::log::PLUGINSYS->error("Failed to unload plugin at '{}'", m_location);
|
||||
return false;
|
||||
}
|
||||
|
||||
g_pPluginManager->RemovePlugin(m_handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PluginManager::InformSQVMDestroyed(ScriptContext context)
|
||||
void Plugin::Reload() const
|
||||
{
|
||||
for (auto plugin : m_vLoadedPlugins)
|
||||
{
|
||||
if (plugin.inform_sqvm_destroyed != NULL)
|
||||
{
|
||||
plugin.inform_sqvm_destroyed(context);
|
||||
}
|
||||
}
|
||||
bool unloaded = Unload();
|
||||
|
||||
if (!unloaded)
|
||||
return;
|
||||
|
||||
g_pPluginManager->LoadPlugin(fs::path(m_location), true);
|
||||
}
|
||||
|
||||
void PluginManager::InformDLLLoad(const char* dll, void* data, void* dllPtr)
|
||||
void Plugin::Log(spdlog::level::level_enum level, char* msg) const
|
||||
{
|
||||
for (auto plugin : m_vLoadedPlugins)
|
||||
{
|
||||
if (plugin.inform_dll_load != NULL)
|
||||
{
|
||||
plugin.inform_dll_load(dll, (PluginEngineData*)data, dllPtr);
|
||||
}
|
||||
}
|
||||
m_logger->log(level, msg);
|
||||
}
|
||||
|
||||
void PluginManager::RunFrame()
|
||||
bool Plugin::IsValid() const
|
||||
{
|
||||
for (auto plugin : m_vLoadedPlugins)
|
||||
{
|
||||
if (plugin.run_frame != NULL)
|
||||
{
|
||||
plugin.run_frame();
|
||||
}
|
||||
}
|
||||
return m_valid && m_pCreateInterface && m_pluginId && m_callbacks && m_handle;
|
||||
}
|
||||
|
||||
const std::string& Plugin::GetName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
const std::string& Plugin::GetLogName() const
|
||||
{
|
||||
return m_logName;
|
||||
}
|
||||
|
||||
const std::string& Plugin::GetDependencyName() const
|
||||
{
|
||||
return m_dependencyName;
|
||||
}
|
||||
|
||||
const std::string& Plugin::GetLocation() const
|
||||
{
|
||||
return m_location;
|
||||
}
|
||||
|
||||
bool Plugin::ShouldRunOnServer() const
|
||||
{
|
||||
return m_runOnServer;
|
||||
}
|
||||
|
||||
bool Plugin::ShouldRunOnClient() const
|
||||
{
|
||||
return m_runOnClient;
|
||||
}
|
||||
|
||||
void* Plugin::CreateInterface(const char* name, int* status) const
|
||||
{
|
||||
return m_pCreateInterface(name, status);
|
||||
}
|
||||
|
||||
void Plugin::Init(bool reloaded) const
|
||||
{
|
||||
m_callbacks->Init(g_NorthstarModule, &m_initData, reloaded);
|
||||
}
|
||||
|
||||
void Plugin::Finalize() const
|
||||
{
|
||||
m_callbacks->Finalize();
|
||||
}
|
||||
|
||||
void Plugin::OnSqvmCreated(CSquirrelVM* sqvm) const
|
||||
{
|
||||
m_callbacks->OnSqvmCreated(sqvm);
|
||||
}
|
||||
|
||||
void Plugin::OnSqvmDestroying(CSquirrelVM* sqvm) const
|
||||
{
|
||||
NS::log::PLUGINSYS->info("destroying sqvm {}", sqvm->vmContext);
|
||||
m_callbacks->OnSqvmDestroying(sqvm);
|
||||
}
|
||||
|
||||
void Plugin::OnLibraryLoaded(HMODULE module, const char* name) const
|
||||
{
|
||||
m_callbacks->OnLibraryLoaded(module, name);
|
||||
}
|
||||
|
||||
void Plugin::RunFrame() const
|
||||
{
|
||||
m_callbacks->RunFrame();
|
||||
}
|
||||
|
|
|
@ -1,59 +1,50 @@
|
|||
#pragma once
|
||||
#include "plugin_abi.h"
|
||||
|
||||
const int IDR_RCDATA1 = 101;
|
||||
#include "core/sourceinterface.h"
|
||||
#include "plugins/interfaces/interface.h"
|
||||
#include "plugins/interfaces/IPluginId.h"
|
||||
#include "plugins/interfaces/IPluginCallbacks.h"
|
||||
|
||||
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;
|
||||
};
|
||||
CreateInterfaceFn m_pCreateInterface;
|
||||
IPluginId* m_pluginId = 0;
|
||||
IPluginCallbacks* m_callbacks = 0;
|
||||
|
||||
extern PluginManager* g_pPluginManager;
|
||||
std::shared_ptr<ColoredLogger> m_logger;
|
||||
|
||||
bool m_valid = false;
|
||||
std::string m_name;
|
||||
std::string m_logName;
|
||||
std::string m_dependencyName;
|
||||
std::string m_location; // path of the dll
|
||||
bool m_runOnServer;
|
||||
bool m_runOnClient;
|
||||
|
||||
public:
|
||||
HMODULE m_handle;
|
||||
PluginNorthstarData m_initData;
|
||||
|
||||
Plugin(std::string path);
|
||||
bool Unload() const;
|
||||
void Reload() const;
|
||||
|
||||
// sys
|
||||
void Log(spdlog::level::level_enum level, char* msg) const;
|
||||
|
||||
// callbacks
|
||||
bool IsValid() const;
|
||||
const std::string& GetName() const;
|
||||
const std::string& GetLogName() const;
|
||||
const std::string& GetDependencyName() const;
|
||||
const std::string& GetLocation() const;
|
||||
bool ShouldRunOnServer() const;
|
||||
bool ShouldRunOnClient() const;
|
||||
void* CreateInterface(const char* pName, int* pStatus) const;
|
||||
void Init(bool reloaded) const;
|
||||
void Finalize() const;
|
||||
void OnSqvmCreated(CSquirrelVM* sqvm) const;
|
||||
void OnSqvmDestroying(CSquirrelVM* sqvm) const;
|
||||
void OnLibraryLoaded(HMODULE module, const char* name) const;
|
||||
void RunFrame() const;
|
||||
};
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
#include "dedicated/dedicated.h"
|
||||
#include "engine/r2engine.h"
|
||||
#include "core/tier0.h"
|
||||
#include "plugins/plugin_abi.h"
|
||||
#include "plugins/plugins.h"
|
||||
#include "plugins/pluginmanager.h"
|
||||
#include "ns_version.h"
|
||||
#include "core/vanilla.h"
|
||||
|
||||
|
@ -157,79 +157,6 @@ const char* SQTypeNameFromID(int type)
|
|||
return "";
|
||||
}
|
||||
|
||||
template <ScriptContext context> void SquirrelManager<context>::GenerateSquirrelFunctionsStruct(SquirrelFunctions* s)
|
||||
{
|
||||
s->RegisterSquirrelFunc = RegisterSquirrelFunc;
|
||||
s->__sq_defconst = __sq_defconst;
|
||||
|
||||
s->__sq_compilebuffer = __sq_compilebuffer;
|
||||
s->__sq_call = __sq_call;
|
||||
s->__sq_raiseerror = __sq_raiseerror;
|
||||
s->__sq_compilefile = __sq_compilefile;
|
||||
|
||||
s->__sq_newarray = __sq_newarray;
|
||||
s->__sq_arrayappend = __sq_arrayappend;
|
||||
|
||||
s->__sq_newtable = __sq_newtable;
|
||||
s->__sq_newslot = __sq_newslot;
|
||||
|
||||
s->__sq_pushroottable = __sq_pushroottable;
|
||||
s->__sq_pushstring = __sq_pushstring;
|
||||
s->__sq_pushinteger = __sq_pushinteger;
|
||||
s->__sq_pushfloat = __sq_pushfloat;
|
||||
s->__sq_pushbool = __sq_pushbool;
|
||||
s->__sq_pushasset = __sq_pushasset;
|
||||
s->__sq_pushvector = __sq_pushvector;
|
||||
s->__sq_pushobject = __sq_pushobject;
|
||||
|
||||
s->__sq_getstring = __sq_getstring;
|
||||
s->__sq_getinteger = __sq_getinteger;
|
||||
s->__sq_getfloat = __sq_getfloat;
|
||||
s->__sq_getbool = __sq_getbool;
|
||||
s->__sq_get = __sq_get;
|
||||
s->__sq_getasset = __sq_getasset;
|
||||
s->__sq_getuserdata = __sq_getuserdata;
|
||||
s->__sq_getvector = __sq_getvector;
|
||||
s->__sq_getthisentity = __sq_getthisentity;
|
||||
s->__sq_getobject = __sq_getobject;
|
||||
|
||||
s->__sq_stackinfos = __sq_stackinfos;
|
||||
|
||||
s->__sq_createuserdata = __sq_createuserdata;
|
||||
s->__sq_setuserdatatypeid = __sq_setuserdatatypeid;
|
||||
s->__sq_getfunction = __sq_getfunction;
|
||||
|
||||
s->__sq_schedule_call_external = AsyncCall_External;
|
||||
|
||||
s->__sq_getentityfrominstance = __sq_getentityfrominstance;
|
||||
s->__sq_GetEntityConstant_CBaseEntity = __sq_GetEntityConstant_CBaseEntity;
|
||||
|
||||
s->__sq_pushnewstructinstance = __sq_pushnewstructinstance;
|
||||
s->__sq_sealstructslot = __sq_sealstructslot;
|
||||
}
|
||||
|
||||
// Allows for generating squirrelmessages from plugins.
|
||||
void AsyncCall_External(ScriptContext context, const char* func_name, SquirrelMessage_External_Pop function, void* userdata)
|
||||
{
|
||||
SquirrelMessage message {};
|
||||
message.functionName = func_name;
|
||||
message.isExternal = true;
|
||||
message.externalFunc = function;
|
||||
message.userdata = userdata;
|
||||
switch (context)
|
||||
{
|
||||
case ScriptContext::CLIENT:
|
||||
g_pSquirrel<ScriptContext::CLIENT>->messageBuffer->push(message);
|
||||
break;
|
||||
case ScriptContext::SERVER:
|
||||
g_pSquirrel<ScriptContext::SERVER>->messageBuffer->push(message);
|
||||
break;
|
||||
case ScriptContext::UI:
|
||||
g_pSquirrel<ScriptContext::UI>->messageBuffer->push(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// needed to define implementations for squirrelmanager outside of squirrel.h without compiler errors
|
||||
template class SquirrelManager<ScriptContext::SERVER>;
|
||||
template class SquirrelManager<ScriptContext::CLIENT>;
|
||||
|
@ -264,11 +191,11 @@ template <ScriptContext context> void SquirrelManager<context>::VMCreated(CSquir
|
|||
defconst(m_pSQVM, pair.first.c_str(), bWasFound);
|
||||
}
|
||||
|
||||
auto loadedPlugins = &g_pPluginManager->m_vLoadedPlugins;
|
||||
std::vector<Plugin> loadedPlugins = g_pPluginManager->GetLoadedPlugins();
|
||||
for (const auto& pluginName : g_pModManager->m_PluginDependencyConstants)
|
||||
{
|
||||
auto f = [&](Plugin plugin) -> bool { return plugin.dependencyName == pluginName; };
|
||||
defconst(m_pSQVM, pluginName.c_str(), std::find_if(loadedPlugins->begin(), loadedPlugins->end(), f) != loadedPlugins->end());
|
||||
auto f = [&](Plugin plugin) -> bool { return plugin.GetDependencyName() == pluginName; };
|
||||
defconst(m_pSQVM, pluginName.c_str(), std::find_if(loadedPlugins.begin(), loadedPlugins.end(), f) != loadedPlugins.end());
|
||||
}
|
||||
|
||||
defconst(m_pSQVM, "MAX_FOLDER_SIZE", GetMaxSaveFolderSize() / 1024);
|
||||
|
@ -284,7 +211,7 @@ template <ScriptContext context> void SquirrelManager<context>::VMCreated(CSquir
|
|||
defconst(m_pSQVM, "VANILLA", g_pVanillaCompatibility->GetVanillaCompatibility());
|
||||
|
||||
g_pSquirrel<context>->messageBuffer = new SquirrelMessageBuffer();
|
||||
g_pPluginManager->InformSQVMCreated(context, newSqvm);
|
||||
g_pPluginManager->InformSqvmCreated(newSqvm);
|
||||
}
|
||||
|
||||
template <ScriptContext context> void SquirrelManager<context>::VMDestroyed()
|
||||
|
@ -312,7 +239,7 @@ template <ScriptContext context> void SquirrelManager<context>::VMDestroyed()
|
|||
}
|
||||
}
|
||||
|
||||
g_pPluginManager->InformSQVMDestroyed(context);
|
||||
g_pPluginManager->InformSqvmDestroying(m_pSQVM);
|
||||
|
||||
// Discard the previous vm and delete the message buffer.
|
||||
m_pSQVM = nullptr;
|
||||
|
@ -661,17 +588,10 @@ template <ScriptContext context> void SquirrelManager<context>::ProcessMessageBu
|
|||
|
||||
size_t argsAmount = message.args.size();
|
||||
|
||||
if (message.isExternal && message.externalFunc != NULL)
|
||||
for (auto& v : message.args)
|
||||
{
|
||||
argsAmount = message.externalFunc(m_pSQVM->sqvm, message.userdata);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& v : message.args)
|
||||
{
|
||||
// Execute lambda to push arg to stack
|
||||
v();
|
||||
}
|
||||
// Execute lambda to push arg to stack
|
||||
v();
|
||||
}
|
||||
|
||||
_call(m_pSQVM->sqvm, (SQInteger)argsAmount);
|
||||
|
@ -839,10 +759,6 @@ ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module))
|
|||
|
||||
g_pSquirrel<ScriptContext::CLIENT>->__sq_getfunction = module.Offset(0x6CB0).RCast<sq_getfunctionType>();
|
||||
g_pSquirrel<ScriptContext::UI>->__sq_getfunction = g_pSquirrel<ScriptContext::CLIENT>->__sq_getfunction;
|
||||
|
||||
SquirrelFunctions s = {};
|
||||
g_pSquirrel<ScriptContext::CLIENT>->GenerateSquirrelFunctionsStruct(&s);
|
||||
g_pPluginManager->InformSQVMLoad(ScriptContext::CLIENT, &s);
|
||||
}
|
||||
|
||||
ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module))
|
||||
|
@ -921,10 +837,6 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module))
|
|||
FCVAR_GAMEDLL | FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS | FCVAR_CHEAT);
|
||||
|
||||
StubUnsafeSQFuncs<ScriptContext::SERVER>();
|
||||
|
||||
SquirrelFunctions s = {};
|
||||
g_pSquirrel<ScriptContext::SERVER>->GenerateSquirrelFunctionsStruct(&s);
|
||||
g_pPluginManager->InformSQVMLoad(ScriptContext::SERVER, &s);
|
||||
}
|
||||
|
||||
void InitialiseSquirrelManagers()
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include "squirrelclasstypes.h"
|
||||
#include "squirrelautobind.h"
|
||||
#include "core/math/vector.h"
|
||||
#include "plugins/plugin_abi.h"
|
||||
#include "mods/modmanager.h"
|
||||
|
||||
/*
|
||||
|
@ -51,8 +50,6 @@ const char* GetContextName_Short(ScriptContext context);
|
|||
eSQReturnType SQReturnTypeFromString(const char* pReturnType);
|
||||
const char* SQTypeNameFromID(const int iTypeId);
|
||||
|
||||
void AsyncCall_External(ScriptContext context, const char* func_name, SquirrelMessage_External_Pop function, void* userdata);
|
||||
|
||||
ScriptContext ScriptContextFromString(std::string string);
|
||||
|
||||
namespace NS::log
|
||||
|
@ -417,7 +414,6 @@ public:
|
|||
SQRESULT setupfunc(const SQChar* funcname);
|
||||
void AddFuncOverride(std::string name, SQFunction func);
|
||||
void ProcessMessageBuffer();
|
||||
void GenerateSquirrelFunctionsStruct(SquirrelFunctions* s);
|
||||
};
|
||||
|
||||
template <ScriptContext context> SquirrelManager<context>* g_pSquirrel;
|
||||
|
@ -483,7 +479,7 @@ requires is_iterable<T>
|
|||
inline VoidFunction SQMessageBufferPushArg(T& arg) {
|
||||
FunctionVector localv = {};
|
||||
localv.push_back([]{g_pSquirrel<context>->newarray(g_pSquirrel<context>->m_pSQVM->sqvm, 0);});
|
||||
|
||||
|
||||
for (const auto& item : arg) {
|
||||
localv.push_back(SQMessageBufferPushArg<context>(item));
|
||||
localv.push_back([]{g_pSquirrel<context>->arrayappend(g_pSquirrel<context>->m_pSQVM->sqvm, -2);});
|
||||
|
@ -497,7 +493,7 @@ requires is_map<T>
|
|||
inline VoidFunction SQMessageBufferPushArg(T& map) {
|
||||
FunctionVector localv = {};
|
||||
localv.push_back([]{g_pSquirrel<context>->newtable(g_pSquirrel<context>->m_pSQVM->sqvm);});
|
||||
|
||||
|
||||
for (const auto& item : map) {
|
||||
localv.push_back(SQMessageBufferPushArg<context>(item.first));
|
||||
localv.push_back(SQMessageBufferPushArg<context>(item.second));
|
||||
|
|
|
@ -116,18 +116,11 @@ concept is_iterable = requires(std::ranges::range_value_t<T> x)
|
|||
|
||||
// clang-format on
|
||||
|
||||
typedef int (*SquirrelMessage_External_Pop)(HSquirrelVM* sqvm, void* userdata);
|
||||
typedef void (*sq_schedule_call_externalType)(
|
||||
ScriptContext context, const char* funcname, SquirrelMessage_External_Pop function, void* userdata);
|
||||
|
||||
class SquirrelMessage
|
||||
{
|
||||
public:
|
||||
std::string functionName;
|
||||
FunctionVector args;
|
||||
bool isExternal = false;
|
||||
void* userdata = NULL;
|
||||
SquirrelMessage_External_Pop externalFunc = NULL;
|
||||
};
|
||||
|
||||
class SquirrelMessageBuffer
|
||||
|
@ -243,6 +236,3 @@ typedef SQRESULT (*sq_pushnewstructinstanceType)(HSquirrelVM* sqvm, int fieldCou
|
|||
typedef SQRESULT (*sq_sealstructslotType)(HSquirrelVM* sqvm, int slotIndex);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
// These "external" versions of the types are for plugins
|
||||
typedef int64_t (*RegisterSquirrelFuncType_External)(ScriptContext context, SQFuncRegistration* funcReg, char unknown);
|
||||
|
|
Loading…
Reference in New Issue