diff options
author | pg9182 <96569817+pg9182@users.noreply.github.com> | 2023-05-05 19:45:04 -0400 |
---|---|---|
committer | pg9182 <96569817+pg9182@users.noreply.github.com> | 2023-05-05 19:45:04 -0400 |
commit | 97df63bc21e53556f3cc31112f713d1ae63e691a (patch) | |
tree | 619eb57b5574e6f0f9541a570bbe537ac9ac0eb8 /NorthstarDLL/plugins | |
parent | 39f73a68730e9336f2469fdf1024dab771a85fb3 (diff) | |
download | NorthstarLauncher-97df63bc21e53556f3cc31112f713d1ae63e691a.tar.gz NorthstarLauncher-97df63bc21e53556f3cc31112f713d1ae63e691a.zip |
Revert "Plugin system v2 (#343)"temp/test-dedi-crash-plugin
This reverts commit 450d0b1ed437cf37b4309af952af8904f3f07768.
Diffstat (limited to 'NorthstarDLL/plugins')
-rw-r--r-- | NorthstarDLL/plugins/plugin_abi.h | 192 | ||||
-rw-r--r-- | NorthstarDLL/plugins/pluginbackend.cpp | 94 | ||||
-rw-r--r-- | NorthstarDLL/plugins/pluginbackend.h | 45 | ||||
-rw-r--r-- | NorthstarDLL/plugins/plugins.cpp | 581 | ||||
-rw-r--r-- | NorthstarDLL/plugins/plugins.h | 62 |
5 files changed, 414 insertions, 560 deletions
diff --git a/NorthstarDLL/plugins/plugin_abi.h b/NorthstarDLL/plugins/plugin_abi.h index c9fa9d25..4b176a32 100644 --- a/NorthstarDLL/plugins/plugin_abi.h +++ b/NorthstarDLL/plugins/plugin_abi.h @@ -1,164 +1,68 @@ #pragma once #include <string> -#include "squirrel/squirrelclasstypes.h" -#define ABI_VERSION 2 - -enum GameState +#define ABI_VERSION 1 +/// <summary> +/// This enum is used for referencing the different types of objects we can pass to a plugin +/// Anything exposed to a plugin must not a be C++ type, as they could break when compiling with a different compiler. +/// Any ABI incompatible change must increment the version number. +/// Nothing must be removed from this enum, only appended. When it absolutely necessary to deprecate an object, it should return UNSUPPORTED +/// when retrieved +/// </summary> +enum PluginObject { - LOADING = 0, - MAINMENU = 1, - LOBBY = 2, - INGAME = 3 + UNSUPPORTED = 0, + GAMESTATE = 1, + SERVERINFO = 2, + PLAYERINFO = 3, + DUMMY = 0xFFFF }; -enum PluginLoadDLL +enum GameStateInfoType { - ENGINE = 0, - CLIENT, - SERVER + ourScore = 0, + secondHighestScore = 1, + highestScore = 2, + connected = 3, + loading = 4, + map = 5, + mapDisplayName = 6, + playlist = 7, + playlistDisplayName = 8, + players = 9 }; - -enum ObjectType +struct GameState { - CONCOMMANDS = 0, - CONVAR = 1, + int (*getGameStateChar)(char* out_buf, size_t out_buf_len, GameStateInfoType var); + int (*getGameStateInt)(int* out_ptr, GameStateInfoType var); + int (*getGameStateBool)(bool* out_ptr, GameStateInfoType var); }; -struct SquirrelFunctions +enum ServerInfoType { - RegisterSquirrelFuncType RegisterSquirrelFunc; - sq_defconstType __sq_defconst; - - sq_compilebufferType __sq_compilebuffer; - sq_callType __sq_call; - sq_raiseerrorType __sq_raiseerror; - - 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_getthisentityType __sq_getthisentity; - sq_getobjectType __sq_getobject; - - sq_stackinfosType __sq_stackinfos; - - 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_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; -}; - -struct MessageSource -{ - const char* file; - const char* func; - int line; + id = 0, + name = 1, + description = 2, + password = 3, + maxPlayers = 4, + roundBased = 5, + scoreLimit = 6, + endTime = 7 }; - -// This is a modified version of spdlog::details::log_msg -// This is so that we can make it cross DLL boundaries -struct LogMsg +struct ServerInfo { - int level; - uint64_t timestamp; - const char* msg; - MessageSource source; - int pluginHandle; + int (*getServerInfoChar)(char* out_buf, size_t out_buf_len, ServerInfoType var); + int (*getServerInfoInt)(int* out_ptr, ServerInfoType var); + int (*getServerInfoBool)(bool* out_ptr, ServerInfoType var); }; -typedef void (*loggerfunc_t)(LogMsg* msg); -typedef void (*PLUGIN_RELAY_INVITE_TYPE)(const char* invite); -typedef void* (*CreateObjectFunc)(ObjectType type); - -struct PluginNorthstarData +enum PlayerInfoType { - const char* version; - HMODULE northstarModule; - int pluginHandle; + uid = 0 }; - -struct PluginInitFuncs +struct PlayerInfo { - loggerfunc_t logger; - PLUGIN_RELAY_INVITE_TYPE relayInviteFunc; - CreateObjectFunc createObject; + int (*getPlayerInfoChar)(char* out_buf, size_t out_buf_len, PlayerInfoType var); + int (*getPlayerInfoInt)(int* out_ptr, PlayerInfoType var); + int (*getPlayerInfoBool)(bool* out_ptr, PlayerInfoType var); }; - -struct PluginEngineData -{ - void* ConCommandConstructor; - void* conVarMalloc; - void* conVarRegister; - void* ConVar_Vtable; - void* IConVar_Vtable; -}; - -struct PluginGameStatePresence -{ - const char* id; - const char* name; - const char* description; - const char* password; - - bool isServer; - bool isLocal; - GameState state; - - const char* map; - const char* mapDisplayname; - const char* playlist; - const char* playlistDisplayname; - - int currentPlayers; - int maxPlayers; - - int ownScore; - int otherHighestScore; // NOTE: The highest score OR the second highest score if we have the highest - int maxScore; - - int timestampEnd; -}; - -/// <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); - -// Async Communication types - -// Northstar -> Plugin -typedef void (*PLUGIN_PUSH_PRESENCE_TYPE)(PluginGameStatePresence* data); -typedef void (*PLUGIN_INFORM_DLL_LOAD_TYPE)(PluginLoadDLL dll, void* data); diff --git a/NorthstarDLL/plugins/pluginbackend.cpp b/NorthstarDLL/plugins/pluginbackend.cpp deleted file mode 100644 index b442d702..00000000 --- a/NorthstarDLL/plugins/pluginbackend.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "pch.h" -#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" - -#define EXPORT extern "C" __declspec(dllexport) - -AUTOHOOK_INIT() - -PluginCommunicationHandler* g_pPluginCommunicationhandler; - -static PluginDataRequest storedRequest {PluginDataRequestType::END, (PluginRespondDataCallable) nullptr}; - -void init_plugincommunicationhandler() -{ - g_pPluginCommunicationhandler = new PluginCommunicationHandler; - g_pPluginCommunicationhandler->requestQueue = {}; -} - -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 PluginCommunicationHandler::GeneratePresenceObjects() -{ - PluginGameStatePresence presence {}; - - presence.id = g_pGameStatePresence->id.c_str(); - presence.name = g_pGameStatePresence->name.c_str(); - presence.description = g_pGameStatePresence->description.c_str(); - presence.password = g_pGameStatePresence->password.c_str(); - - presence.isServer = g_pGameStatePresence->isServer; - presence.isLocal = g_pGameStatePresence->isLocal; - - if (g_pGameStatePresence->isLoading) - presence.state = GameState::LOADING; - else if (g_pGameStatePresence->uiMap == "") - presence.state = GameState::MAINMENU; - else if (g_pGameStatePresence->map == "mp_lobby" && g_pGameStatePresence->isLocal && g_pGameStatePresence->isLobby) - presence.state = GameState::LOBBY; - else - presence.state = GameState::INGAME; - - presence.map = g_pGameStatePresence->map.c_str(); - presence.mapDisplayname = g_pGameStatePresence->mapDisplayname.c_str(); - presence.playlist = g_pGameStatePresence->playlist.c_str(); - presence.playlistDisplayname = g_pGameStatePresence->playlistDisplayname.c_str(); - - presence.currentPlayers = g_pGameStatePresence->currentPlayers; - presence.maxPlayers = g_pGameStatePresence->maxPlayers; - presence.ownScore = g_pGameStatePresence->ownScore; - presence.otherHighestScore = g_pGameStatePresence->otherHighestScore; - presence.maxScore = g_pGameStatePresence->maxScore; - presence.timestampEnd = g_pGameStatePresence->timestampEnd; - g_pPluginManager->PushPresence(&presence); -} - -ON_DLL_LOAD_RELIESON("engine.dll", PluginBackendEngine, ConCommand, (CModule module)) -{ - g_pPluginManager->InformDLLLoad(PluginLoadDLL::ENGINE, &g_pPluginCommunicationhandler->m_sEngineData); -} - -ON_DLL_LOAD_RELIESON("client.dll", PluginBackendClient, ConCommand, (CModule module)) -{ - g_pPluginManager->InformDLLLoad(PluginLoadDLL::CLIENT, nullptr); -} - -ON_DLL_LOAD_RELIESON("server.dll", PluginBackendServer, ConCommand, (CModule module)) -{ - g_pPluginManager->InformDLLLoad(PluginLoadDLL::SERVER, nullptr); -} diff --git a/NorthstarDLL/plugins/pluginbackend.h b/NorthstarDLL/plugins/pluginbackend.h deleted file mode 100644 index ef42c508..00000000 --- a/NorthstarDLL/plugins/pluginbackend.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -#include "pch.h" -#include "plugin_abi.h" -#include "shared/gamepresence.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); - - void GeneratePresenceObjects(); - - public: - std::queue<PluginDataRequest> requestQueue; - std::mutex requestMutex; - - PluginEngineData m_sEngineData {}; -}; - -void init_plugincommunicationhandler(); - -extern PluginCommunicationHandler* g_pPluginCommunicationhandler; diff --git a/NorthstarDLL/plugins/plugins.cpp b/NorthstarDLL/plugins/plugins.cpp index 6e3ceb5a..47d27da1 100644 --- a/NorthstarDLL/plugins/plugins.cpp +++ b/NorthstarDLL/plugins/plugins.cpp @@ -1,292 +1,421 @@ -#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 "util/version.h" -#include "pluginbackend.h" -#include "util/wininfo.h" -#include "logging/logging.h" -#include "dedicated/dedicated.h" -PluginManager* g_pPluginManager; +#include <chrono> +#include <windows.h> -void freeLibrary(HMODULE hLib) +/// <summary> +/// The data is split into two different representations: one for internal, and one for plugins, for thread safety reasons +/// The struct exposed to plugins contains getter functions for the various data types. +/// We can safely use C++ types like std::string here since these are only ever handled by Northstar internally +/// </summary> +struct InternalGameState +{ + int ourScore; + int secondHighestScore; + int highestScore; + + bool connected; + bool loading; + std::string map; + std::string mapDisplayName; + std::string playlist; + std::string playlistDisplayName; + int players; +}; +struct InternalServerInfo +{ + std::string id; + std::string name; + std::string description; + std::string password; + int maxPlayers; + bool roundBased; + int scoreLimit; + int endTime; +}; +// TODO: need to extend this to include current player data like loadouts +struct InternalPlayerInfo +{ + int uid; +}; + +InternalGameState gameState; +InternalServerInfo serverInfo; +InternalPlayerInfo playerInfo; + +GameState gameStateExport; +ServerInfo serverInfoExport; +PlayerInfo playerInfoExport; + +/// <summary> +/// We use SRW Locks because plugins will often be running their own thread +/// To ensure thread safety, and to make it difficult to fuck up, we force them to use *our* functions to get data +/// </summary> +static SRWLOCK gameStateLock; +static SRWLOCK serverInfoLock; +static SRWLOCK playerInfoLock; + +void* getPluginObject(PluginObject var) { - if (!FreeLibrary(hLib)) + switch (var) { - spdlog::error("There was an error while trying to free library"); + case PluginObject::GAMESTATE: + return &gameStateExport; + case PluginObject::SERVERINFO: + return &serverInfoExport; + case PluginObject::PLAYERINFO: + return &playerInfoExport; + default: + return (void*)-1; } } -EXPORT void PLUGIN_LOG(LogMsg* msg) +void initGameState() { - 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); + // Initalize the Slim Reader / Writer locks + InitializeSRWLock(&gameStateLock); + InitializeSRWLock(&serverInfoLock); + InitializeSRWLock(&playerInfoLock); + + gameStateExport.getGameStateChar = &getGameStateChar; + gameStateExport.getGameStateInt = &getGameStateInt; + gameStateExport.getGameStateBool = &getGameStateBool; + + serverInfoExport.getServerInfoChar = &getServerInfoChar; + serverInfoExport.getServerInfoInt = &getServerInfoInt; + serverInfoExport.getServerInfoBool = &getServerInfoBool; + + playerInfoExport.getPlayerInfoChar = &getPlayerInfoChar; + playerInfoExport.getPlayerInfoInt = &getPlayerInfoInt; + playerInfoExport.getPlayerInfoBool = &getPlayerInfoBool; + + serverInfo.id = ""; + serverInfo.name = ""; + serverInfo.description = ""; + serverInfo.password = ""; + serverInfo.maxPlayers = 0; + gameState.connected = false; + gameState.loading = false; + gameState.map = ""; + gameState.mapDisplayName = ""; + gameState.playlist = ""; + gameState.playlistDisplayName = ""; + gameState.players = 0; + + playerInfo.uid = 123; } -EXPORT void* CreateObject(ObjectType type) +// string gamemode, string gamemodeName, string map, string mapName, bool connected, bool loading +SQRESULT SQ_UpdateGameStateUI(HSquirrelVM* sqvm) { - switch (type) - { - case ObjectType::CONVAR: - return (void*)new ConVar; - case ObjectType::CONCOMMANDS: - return (void*)new ConCommand; - default: - return NULL; - } + AcquireSRWLockExclusive(&gameStateLock); + gameState.map = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 1); + gameState.mapDisplayName = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 2); + gameState.playlist = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 3); + gameState.playlistDisplayName = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 4); + gameState.connected = g_pSquirrel<ScriptContext::UI>->getbool(sqvm, 5); + gameState.loading = g_pSquirrel<ScriptContext::UI>->getbool(sqvm, 6); + ReleaseSRWLockExclusive(&gameStateLock); + return SQRESULT_NOTNULL; } -std::optional<Plugin> PluginManager::LoadPlugin(fs::path path, PluginInitFuncs* funcs, PluginNorthstarData* data) +// int playerCount, int outScore, int secondHighestScore, int highestScore, bool roundBased, int scoreLimit +SQRESULT SQ_UpdateGameStateClient(HSquirrelVM* sqvm) { + AcquireSRWLockExclusive(&gameStateLock); + AcquireSRWLockExclusive(&serverInfoLock); + gameState.players = g_pSquirrel<ScriptContext::CLIENT>->getinteger(sqvm, 1); + serverInfo.maxPlayers = g_pSquirrel<ScriptContext::CLIENT>->getinteger(sqvm, 2); + gameState.ourScore = g_pSquirrel<ScriptContext::CLIENT>->getinteger(sqvm, 3); + gameState.secondHighestScore = g_pSquirrel<ScriptContext::CLIENT>->getinteger(sqvm, 4); + gameState.highestScore = g_pSquirrel<ScriptContext::CLIENT>->getinteger(sqvm, 5); + serverInfo.roundBased = g_pSquirrel<ScriptContext::CLIENT>->getbool(sqvm, 6); + serverInfo.scoreLimit = g_pSquirrel<ScriptContext::CLIENT>->getbool(sqvm, 7); + ReleaseSRWLockExclusive(&gameStateLock); + ReleaseSRWLockExclusive(&serverInfoLock); + return SQRESULT_NOTNULL; +} - Plugin plugin {}; +// string id, string name, string password, int players, int maxPlayers, string map, string mapDisplayName, string playlist, string +// playlistDisplayName +SQRESULT SQ_UpdateServerInfo(HSquirrelVM* sqvm) +{ + AcquireSRWLockExclusive(&gameStateLock); + AcquireSRWLockExclusive(&serverInfoLock); + serverInfo.id = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 1); + serverInfo.name = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 2); + serverInfo.password = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 3); + gameState.players = g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 4); + serverInfo.maxPlayers = g_pSquirrel<ScriptContext::UI>->getinteger(sqvm, 5); + gameState.map = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 6); + gameState.mapDisplayName = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 7); + gameState.playlist = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 8); + gameState.playlistDisplayName = g_pSquirrel<ScriptContext::UI>->getstring(sqvm, 9); + ReleaseSRWLockExclusive(&gameStateLock); + ReleaseSRWLockExclusive(&serverInfoLock); + return SQRESULT_NOTNULL; +} - std::string pathstring = (pluginPath / path).string(); - std::wstring wpath = (pluginPath / path).wstring(); +// int maxPlayers +SQRESULT SQ_UpdateServerInfoBetweenRounds(HSquirrelVM* sqvm) +{ + AcquireSRWLockExclusive(&serverInfoLock); + serverInfo.id = g_pSquirrel<ScriptContext::CLIENT>->getstring(sqvm, 1); + serverInfo.name = g_pSquirrel<ScriptContext::CLIENT>->getstring(sqvm, 2); + serverInfo.password = g_pSquirrel<ScriptContext::CLIENT>->getstring(sqvm, 3); + serverInfo.maxPlayers = g_pSquirrel<ScriptContext::CLIENT>->getinteger(sqvm, 4); + ReleaseSRWLockExclusive(&serverInfoLock); + return SQRESULT_NOTNULL; +} - 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), MAKEINTRESOURCEW(RT_RCDATA)); +// float timeInFuture +SQRESULT SQ_UpdateTimeInfo(HSquirrelVM* sqvm) +{ + AcquireSRWLockExclusive(&serverInfoLock); + serverInfo.endTime = ceil(g_pSquirrel<ScriptContext::CLIENT>->getfloat(sqvm, 1)); + ReleaseSRWLockExclusive(&serverInfoLock); + return SQRESULT_NOTNULL; +} - 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); +// bool loading +SQRESULT SQ_SetConnected(HSquirrelVM* sqvm) +{ + AcquireSRWLockExclusive(&gameStateLock); + gameState.loading = g_pSquirrel<ScriptContext::UI>->getbool(sqvm, 1); + ReleaseSRWLockExclusive(&gameStateLock); + return SQRESULT_NOTNULL; +} - rapidjson_document manifestJSON; - manifestJSON.Parse(manifest.c_str()); +SQRESULT SQ_UpdateListenServer(HSquirrelVM* sqvm) +{ + AcquireSRWLockExclusive(&serverInfoLock); + serverInfo.id = g_pMasterServerManager->m_sOwnServerId; + serverInfo.password = ""; // g_pServerPresence->Cvar_ns_server_password->GetString(); todo this fr + ReleaseSRWLockExclusive(&serverInfoLock); + return SQRESULT_NOTNULL; +} - 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())) +int getServerInfoChar(char* out_buf, size_t out_buf_len, ServerInfoType var) +{ + AcquireSRWLockShared(&serverInfoLock); + int n = 0; + switch (var) { - NS::log::PLUGINSYS->error( - "'{}' has an incompatible API version number '{}' in its manifest. Current ABI version is '{}'", pathstring, ABI_VERSION); - return std::nullopt; + case ServerInfoType::id: + strncpy(out_buf, serverInfo.id.c_str(), out_buf_len); + break; + case ServerInfoType::name: + strncpy(out_buf, serverInfo.name.c_str(), out_buf_len); + break; + case ServerInfoType::description: + strncpy(out_buf, serverInfo.id.c_str(), out_buf_len); + break; + case ServerInfoType::password: + strncpy(out_buf, serverInfo.password.c_str(), out_buf_len); + break; + default: + n = -1; } - // Passed all checks, going to actually load it now - HMODULE pluginLib = LoadLibraryW(wpptr); // Load the DLL as a data file - 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) + ReleaseSRWLockShared(&serverInfoLock); + + return n; +} +int getServerInfoInt(int* out_ptr, ServerInfoType var) +{ + AcquireSRWLockShared(&serverInfoLock); + int n = 0; + switch (var) { - NS::log::PLUGINSYS->info("Library '{}' has no function 'PLUGIN_INIT'", pathstring); - return std::nullopt; + case ServerInfoType::maxPlayers: + *out_ptr = serverInfo.maxPlayers; + break; + case ServerInfoType::scoreLimit: + *out_ptr = serverInfo.scoreLimit; + break; + case ServerInfoType::endTime: + *out_ptr = serverInfo.endTime; + break; + default: + n = -1; } - 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(); + ReleaseSRWLockShared(&serverInfoLock); - 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 + return n; +} +int getServerInfoBool(bool* out_ptr, ServerInfoType var) +{ + AcquireSRWLockShared(&serverInfoLock); + int n = 0; + switch (var) { - plugin.dependencyName = plugin.name; + case ServerInfoType::roundBased: + *out_ptr = serverInfo.roundBased; + break; + default: + n = -1; } - 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.push_presence = (PLUGIN_PUSH_PRESENCE_TYPE)GetProcAddress(pluginLib, "PLUGIN_RECEIVE_PRESENCE"); - - plugin.inform_dll_load = (PLUGIN_INFORM_DLL_LOAD_TYPE)GetProcAddress(pluginLib, "PLUGIN_INFORM_DLL_LOAD"); + ReleaseSRWLockShared(&serverInfoLock); - 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; + return n; } -bool PluginManager::LoadPlugins() +int getGameStateChar(char* out_buf, size_t out_buf_len, GameStateInfoType var) { - 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; - - init_plugincommunicationhandler(); - - data.version = ns_version.c_str(); - data.northstarModule = g_NorthstarModule; - - if (!fs::exists(pluginPath)) - { - NS::log::PLUGINSYS->warn("Could not find a plugins directory. Skipped loading plugins"); - return false; - } - // ensure dirs exist - fs::recursive_directory_iterator iterator(pluginPath); - if (std::filesystem::begin(iterator) == std::filesystem::end(iterator)) - { - NS::log::PLUGINSYS->warn("Could not find any plugins. Skipped loading plugins"); - return false; - } - for (auto const& entry : iterator) + AcquireSRWLockShared(&gameStateLock); + int n = 0; + switch (var) { - if (fs::is_regular_file(entry) && entry.path().extension() == ".dll") - paths.emplace_back(entry.path().filename()); + case GameStateInfoType::map: + strncpy(out_buf, gameState.map.c_str(), out_buf_len); + break; + case GameStateInfoType::mapDisplayName: + strncpy(out_buf, gameState.mapDisplayName.c_str(), out_buf_len); + break; + case GameStateInfoType::playlist: + strncpy(out_buf, gameState.playlist.c_str(), out_buf_len); + break; + case GameStateInfoType::playlistDisplayName: + strncpy(out_buf, gameState.playlistDisplayName.c_str(), out_buf_len); + break; + default: + n = -1; } - for (fs::path path : paths) + + ReleaseSRWLockShared(&gameStateLock); + + return n; +} +int getGameStateInt(int* out_ptr, GameStateInfoType var) +{ + AcquireSRWLockShared(&gameStateLock); + int n = 0; + switch (var) { - if (LoadPlugin(path, &funcs, &data)) - data.pluginHandle += 1; + case GameStateInfoType::ourScore: + *out_ptr = gameState.ourScore; + break; + case GameStateInfoType::secondHighestScore: + *out_ptr = gameState.secondHighestScore; + break; + case GameStateInfoType::highestScore: + *out_ptr = gameState.highestScore; + break; + case GameStateInfoType::players: + *out_ptr = gameState.players; + break; + default: + n = -1; } - return true; -} -void PluginManager::InformSQVMLoad(ScriptContext context, SquirrelFunctions* s) + ReleaseSRWLockShared(&gameStateLock); + + return n; +} +int getGameStateBool(bool* out_ptr, GameStateInfoType var) { - for (auto plugin : m_vLoadedPlugins) + AcquireSRWLockShared(&gameStateLock); + int n = 0; + switch (var) { - 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); - } + case GameStateInfoType::connected: + *out_ptr = gameState.connected; + break; + case GameStateInfoType::loading: + *out_ptr = gameState.loading; + break; + default: + n = -1; } + + ReleaseSRWLockShared(&gameStateLock); + + return n; } -void PluginManager::InformSQVMCreated(ScriptContext context, CSquirrelVM* sqvm) +int getPlayerInfoChar(char* out_buf, size_t out_buf_len, PlayerInfoType var) { - for (auto plugin : m_vLoadedPlugins) + AcquireSRWLockShared(&playerInfoLock); + int n = 0; + switch (var) { - if (plugin.inform_sqvm_created != NULL) - { - plugin.inform_sqvm_created(context, sqvm); - } + default: + n = -1; } -} -void PluginManager::InformSQVMDestroyed(ScriptContext context) + ReleaseSRWLockShared(&playerInfoLock); + + return n; +} +int getPlayerInfoInt(int* out_ptr, PlayerInfoType var) { - for (auto plugin : m_vLoadedPlugins) + AcquireSRWLockShared(&playerInfoLock); + int n = 0; + switch (var) { - if (plugin.inform_sqvm_destroyed != NULL) - { - plugin.inform_sqvm_destroyed(context); - } + case PlayerInfoType::uid: + *out_ptr = playerInfo.uid; + break; + default: + n = -1; } -} -void PluginManager::PushPresence(PluginGameStatePresence* data) + ReleaseSRWLockShared(&playerInfoLock); + + return n; +} +int getPlayerInfoBool(bool* out_ptr, PlayerInfoType var) { - for (auto plugin : m_vLoadedPlugins) + AcquireSRWLockShared(&playerInfoLock); + int n = 0; + switch (var) { - if (plugin.push_presence != NULL) - { - plugin.push_presence(data); - } + default: + n = -1; } + + ReleaseSRWLockShared(&playerInfoLock); + + return n; } -void PluginManager::InformDLLLoad(PluginLoadDLL dll, void* data) +ON_DLL_LOAD_CLIENT_RELIESON("client.dll", PluginCommands, ClientSquirrel, (CModule module)) { - for (auto plugin : m_vLoadedPlugins) + // i swear there's a way to make this not have be run in 2 contexts but i can't figure it out + // some funcs i need are just not available in UI or CLIENT + + if (g_pSquirrel<ScriptContext::UI> && g_pSquirrel<ScriptContext::CLIENT>) { - if (plugin.inform_dll_load != NULL) - { - plugin.inform_dll_load(dll, data); - } + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( + "void", + "NSUpdateGameStateUI", + "string gamemode, string gamemodeName, string map, string mapName, bool connected, bool loading", + "", + SQ_UpdateGameStateUI); + g_pSquirrel<ScriptContext::CLIENT>->AddFuncRegistration( + "void", + "NSUpdateGameStateClient", + "int playerCount, int maxPlayers, int outScore, int secondHighestScore, int highestScore, bool roundBased, int scoreLimit", + "", + SQ_UpdateGameStateClient); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( + "void", + "NSUpdateServerInfo", + "string id, string name, string password, int players, int maxPlayers, string map, string mapDisplayName, string playlist, " + "string " + "playlistDisplayName", + "", + SQ_UpdateServerInfo); + g_pSquirrel<ScriptContext::CLIENT>->AddFuncRegistration( + "void", "NSUpdateServerInfoReload", "int maxPlayers", "", SQ_UpdateServerInfoBetweenRounds); + g_pSquirrel<ScriptContext::CLIENT>->AddFuncRegistration("void", "NSUpdateTimeInfo", "float timeInFuture", "", SQ_UpdateTimeInfo); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("void", "NSSetLoading", "bool loading", "", SQ_SetConnected); + g_pSquirrel<ScriptContext::UI>->AddFuncRegistration("void", "NSUpdateListenServer", "", "", SQ_UpdateListenServer); } } diff --git a/NorthstarDLL/plugins/plugins.h b/NorthstarDLL/plugins/plugins.h index ffa277d0..953801a2 100644 --- a/NorthstarDLL/plugins/plugins.h +++ b/NorthstarDLL/plugins/plugins.h @@ -1,57 +1,17 @@ #pragma once #include "plugin_abi.h" -const int IDR_RCDATA1 = 101; +int getServerInfoChar(char* out_buf, size_t out_buf_len, ServerInfoType var); +int getServerInfoInt(int* out_ptr, ServerInfoType var); +int getServerInfoBool(bool* out_ptr, ServerInfoType var); -class Plugin -{ - public: - std::string name; - std::string displayName; - std::string dependencyName; - std::string description; +int getGameStateChar(char* out_buf, size_t out_buf_len, GameStateInfoType var); +int getGameStateInt(int* out_ptr, GameStateInfoType var); +int getGameStateBool(bool* out_ptr, GameStateInfoType var); - std::string api_version; - std::string version; +int getPlayerInfoChar(char* out_buf, size_t out_buf_len, PlayerInfoType var); +int getPlayerInfoInt(int* out_ptr, PlayerInfoType var); +int getPlayerInfoBool(bool* out_ptr, PlayerInfoType var); - // 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_PUSH_PRESENCE_TYPE push_presence; - PLUGIN_INFORM_DLL_LOAD_TYPE inform_dll_load; -}; - -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 PushPresence(PluginGameStatePresence* data); - - void InformDLLLoad(PluginLoadDLL dll, void* data); - - private: - std::string pluginPath; -}; - -extern PluginManager* g_pPluginManager; +void initGameState(); +void* getPluginObject(PluginObject var); |