From 13dcc7d9307b160131ed80832efda99001aa6c6d Mon Sep 17 00:00:00 2001 From: Emma Miler <27428383+emma-miler@users.noreply.github.com> Date: Fri, 4 Mar 2022 00:16:01 +0100 Subject: Add launcher code for plugin support (#93) * rpc * RPC * Undoing broken shit * temp * Revert "temp" This reverts commit 4875b5aca2b0d6b03183d220120c03df28759a95. * Include a whole bunch of new stuff * fix id * Added manifest and added some verification * Fixed my oopsies * Safety commit * Moved over to new plugin abi * Small docs changes and made plugin loading use ABI_VERSION * Moving discord rpc plugin stuff to a separate repo * Add some comments to `plugins.cpp` * Moved internal structs from `plugin_abi.h` to `plugins.cpp` * Add CLA `-noplugins` * Code formatting changes so clang-format doesn't scream at me * i hate clang-format * why does this fail to build now wtf * Update plugins.cpp * Merged and updated to new convar system * Implemented changes requested in review * Removed a few more of those nasty comments * Removed extra build configs Co-authored-by: BobTheBob <32057864+BobTheBob9@users.noreply.github.com> --- .../NorthstarDedicatedTest.vcxproj | 4 + .../NorthstarDedicatedTest.vcxproj.filters | 12 + NorthstarDedicatedTest/dllmain.cpp | 112 ++++++ NorthstarDedicatedTest/main.h | 3 +- NorthstarDedicatedTest/masterserver.h | 3 +- NorthstarDedicatedTest/plugin_abi.h | 72 ++++ NorthstarDedicatedTest/plugins.cpp | 410 +++++++++++++++++++++ NorthstarDedicatedTest/plugins.h | 19 + 8 files changed, 633 insertions(+), 2 deletions(-) create mode 100644 NorthstarDedicatedTest/plugin_abi.h create mode 100644 NorthstarDedicatedTest/plugins.cpp create mode 100644 NorthstarDedicatedTest/plugins.h (limited to 'NorthstarDedicatedTest') diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj index 6da5f2b7..f12a9eee 100644 --- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj +++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj @@ -115,6 +115,8 @@ + + @@ -555,6 +557,7 @@ + @@ -595,6 +598,7 @@ + diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters index f0c8c380..37277e2e 100644 --- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters +++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters @@ -1473,6 +1473,15 @@ Header Files\Client + + Header Files + + + Header Files + + + Header Files + @@ -1622,6 +1631,9 @@ Source Files\Client + + Source Files + diff --git a/NorthstarDedicatedTest/dllmain.cpp b/NorthstarDedicatedTest/dllmain.cpp index dcff9f4a..d6bc5091 100644 --- a/NorthstarDedicatedTest/dllmain.cpp +++ b/NorthstarDedicatedTest/dllmain.cpp @@ -39,6 +39,15 @@ #include "localchatwriter.h" #include #include "pch.h" +#include "plugin_abi.h" +#include "plugins.h" + +#include "rapidjson/document.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/writer.h" +#include "rapidjson/error/en.h" + +typedef void (*initPluginFuncPtr)(void* getPluginObject); bool initialised = false; @@ -68,6 +77,108 @@ void WaitForDebugger(HMODULE baseAddress) } } +void freeLibrary(HMODULE hLib) +{ + if (!FreeLibrary(hLib)) + { + spdlog::error("There was an error while trying to free library"); + } +} + +bool LoadPlugins() +{ + + std::vector paths; + + std::string pluginPath = GetNorthstarPrefix() + "/plugins"; + + // ensure dirs exist + fs::recursive_directory_iterator iterator(pluginPath); + if (std::filesystem::begin(iterator) == std::filesystem::end(iterator)) + { + spdlog::warn("Could not find any plugins. Skipped loading plugins"); + return false; + } + for (auto const& entry : iterator) + { + if (fs::is_regular_file(entry) && entry.path().extension() == ".dll") + paths.emplace_back(entry.path().filename()); + } + // system("pause"); + initGameState(); + // spdlog::info("Loading the following DLLs in plugins folder:"); + for (fs::path path : paths) + { + std::string pathstring = (pluginPath / path).string(); + std::wstring wpath = (pluginPath / 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) + { + spdlog::info("Failed to load library {}: ", std::system_category().message(GetLastError())); + continue; + } + HRSRC manifestResource = FindResourceW(datafile, MAKEINTRESOURCE(101), MAKEINTRESOURCE(RT_RCDATA)); + + if (manifestResource == NULL) + { + spdlog::info("Could not find manifest for library {}", pathstring); + freeLibrary(datafile); + continue; + } + spdlog::info("Loading resource from library"); + HGLOBAL myResourceData = LoadResource(datafile, manifestResource); + if (myResourceData == NULL) + { + spdlog::error("Failed to load resource from library"); + freeLibrary(datafile); + continue; + } + 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()) + { + spdlog::error("Manifest for {} was invalid", pathstring); + continue; + } + if (!manifestJSON.HasMember("api_version")) + { + spdlog::error("{} does not have a version number in its manifest", pathstring); + continue; + // spdlog::info(manifestJSON["version"].GetString()); + } + if (strcmp(manifestJSON["api_version"].GetString(), std::to_string(ABI_VERSION).c_str())) + { + spdlog::error("{} has an incompatible API version number in its manifest", pathstring); + continue; + } + // Passed all checks, going to actually load it now + + HMODULE pluginLib = LoadLibraryW(wpptr); // Load the DLL as a data file + if (pluginLib == NULL) + { + spdlog::info("Failed to load library {}: ", std::system_category().message(GetLastError())); + continue; + } + initPluginFuncPtr initPlugin = (initPluginFuncPtr)GetProcAddress(pluginLib, "initializePlugin"); + if (initPlugin == NULL) + { + spdlog::info("Library {} has no function initializePlugin", pathstring); + continue; + } + spdlog::info("Succesfully loaded {}", pathstring); + initPlugin(&getPluginObject); + } + return true; +} + bool InitialiseNorthstar() { if (initialised) @@ -123,6 +234,7 @@ bool InitialiseNorthstar() AddDllLoadCallback("client.dll", InitialiseScriptMainMenuPromos); AddDllLoadCallback("client.dll", InitialiseMiscClientFixes); AddDllLoadCallback("client.dll", InitialiseClientPrintHooks); + AddDllLoadCallback("client.dll", InitialisePluginCommands); AddDllLoadCallback("client.dll", InitialiseClientChatHooks); AddDllLoadCallback("client.dll", InitialiseLocalChatWriter); } diff --git a/NorthstarDedicatedTest/main.h b/NorthstarDedicatedTest/main.h index 90e88912..64f9cfd2 100644 --- a/NorthstarDedicatedTest/main.h +++ b/NorthstarDedicatedTest/main.h @@ -1,3 +1,4 @@ #pragma once -extern "C" __declspec(dllexport) bool InitialiseNorthstar(); \ No newline at end of file +extern "C" __declspec(dllexport) bool InitialiseNorthstar(); +extern "C" __declspec(dllexport) bool LoadPlugins(); \ No newline at end of file diff --git a/NorthstarDedicatedTest/masterserver.h b/NorthstarDedicatedTest/masterserver.h index 2eae2081..c8fd0cb5 100644 --- a/NorthstarDedicatedTest/masterserver.h +++ b/NorthstarDedicatedTest/masterserver.h @@ -123,4 +123,5 @@ void UpdateServerInfoFromUnicodeToUTF8(); void InitialiseSharedMasterServer(HMODULE baseAddress); extern MasterServerManager* g_MasterServerManager; -extern ConVar* Cvar_ns_masterserver_hostname; \ No newline at end of file +extern ConVar* Cvar_ns_masterserver_hostname; +extern ConVar* Cvar_ns_server_password; \ No newline at end of file diff --git a/NorthstarDedicatedTest/plugin_abi.h b/NorthstarDedicatedTest/plugin_abi.h new file mode 100644 index 00000000..edd44ea1 --- /dev/null +++ b/NorthstarDedicatedTest/plugin_abi.h @@ -0,0 +1,72 @@ +#pragma once +#include + +#define ABI_VERSION 1 +//clang-format off +// I hate clang-format +/// +/// 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 +/// +enum PluginObject +{ + UNSUPPORTED = 0, + GAMESTATE = 1, + SERVERINFO = 2, + PLAYERINFO = 3, + DUMMY = 0xFFFF +}; + +enum GameStateInfoType +{ + ourScore = 0, + secondHighestScore = 1, + highestScore = 2, + connected = 3, + loading = 4, + map = 5, + mapDisplayName = 6, + playlist = 7, + playlistDisplayName = 8, + players = 9 +}; +struct GameState +{ + 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); +}; + +enum ServerInfoType +{ + id = 0, + name = 1, + description = 2, + password = 3, + maxPlayers = 4, + roundBased = 5, + scoreLimit = 6, + endTime = 7 +}; +struct ServerInfo +{ + 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); +}; + +enum PlayerInfoType +{ + uid = 0 +}; +struct PlayerInfo +{ + 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); +}; + +//clang-format on \ No newline at end of file diff --git a/NorthstarDedicatedTest/plugins.cpp b/NorthstarDedicatedTest/plugins.cpp new file mode 100644 index 00000000..383555cf --- /dev/null +++ b/NorthstarDedicatedTest/plugins.cpp @@ -0,0 +1,410 @@ +#include "pch.h" +#include "squirrel.h" +#include "plugins.h" +#include +#include "masterserver.h" +#include "convar.h" +#include + +/// +/// 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 +/// +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; + +/// +/// 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 +/// +static SRWLOCK gameStateLock; +static SRWLOCK serverInfoLock; +static SRWLOCK playerInfoLock; + +void* getPluginObject(PluginObject var) +{ + switch (var) + { + case PluginObject::GAMESTATE: + return &gameStateExport; + case PluginObject::SERVERINFO: + return &serverInfoExport; + case PluginObject::PLAYERINFO: + return &playerInfoExport; + default: + return (void*)-1; + } +} + +void initGameState() +{ + // 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; +} + +// string gamemode, string gamemodeName, string map, string mapName, bool connected, bool loading +SQRESULT SQ_UpdateGameStateUI(void* sqvm) +{ + AcquireSRWLockExclusive(&gameStateLock); + gameState.map = ClientSq_getstring(sqvm, 1); + gameState.mapDisplayName = ClientSq_getstring(sqvm, 2); + gameState.playlist = ClientSq_getstring(sqvm, 3); + gameState.playlistDisplayName = ClientSq_getstring(sqvm, 4); + gameState.connected = ClientSq_getbool(sqvm, 5); + gameState.loading = ClientSq_getbool(sqvm, 6); + ReleaseSRWLockExclusive(&gameStateLock); + return SQRESULT_NOTNULL; +} + +// int playerCount, int outScore, int secondHighestScore, int highestScore, bool roundBased, int scoreLimit +SQRESULT SQ_UpdateGameStateClient(void* sqvm) +{ + AcquireSRWLockExclusive(&gameStateLock); + AcquireSRWLockExclusive(&serverInfoLock); + gameState.players = ClientSq_getinteger(sqvm, 1); + gameState.ourScore = ClientSq_getinteger(sqvm, 2); + gameState.secondHighestScore = ClientSq_getinteger(sqvm, 3); + gameState.highestScore = ClientSq_getinteger(sqvm, 4); + serverInfo.roundBased = ClientSq_getbool(sqvm, 5); + serverInfo.scoreLimit = ClientSq_getbool(sqvm, 6); + ReleaseSRWLockExclusive(&gameStateLock); + ReleaseSRWLockExclusive(&serverInfoLock); + return SQRESULT_NOTNULL; +} + +// string id, string name, string password, int players, int maxPlayers, string map, string mapDisplayName, string playlist, string +// playlistDisplayName +SQRESULT SQ_UpdateServerInfo(void* sqvm) +{ + AcquireSRWLockExclusive(&gameStateLock); + AcquireSRWLockExclusive(&serverInfoLock); + serverInfo.id = ClientSq_getstring(sqvm, 1); + serverInfo.name = ClientSq_getstring(sqvm, 2); + serverInfo.password = ClientSq_getstring(sqvm, 3); + gameState.players = ClientSq_getinteger(sqvm, 4); + serverInfo.maxPlayers = ClientSq_getinteger(sqvm, 5); + gameState.map = ClientSq_getstring(sqvm, 6); + gameState.mapDisplayName = ClientSq_getstring(sqvm, 7); + gameState.playlist = ClientSq_getstring(sqvm, 8); + gameState.playlistDisplayName = ClientSq_getstring(sqvm, 9); + ReleaseSRWLockExclusive(&gameStateLock); + ReleaseSRWLockExclusive(&serverInfoLock); + return SQRESULT_NOTNULL; +} + +// int maxPlayers +SQRESULT SQ_UpdateServerInfoBetweenRounds(void* sqvm) +{ + AcquireSRWLockExclusive(&serverInfoLock); + serverInfo.id = ClientSq_getstring(sqvm, 1); + serverInfo.name = ClientSq_getstring(sqvm, 2); + serverInfo.password = ClientSq_getstring(sqvm, 3); + serverInfo.maxPlayers = ClientSq_getinteger(sqvm, 4); + ReleaseSRWLockExclusive(&serverInfoLock); + return SQRESULT_NOTNULL; +} + +// float timeInFuture +SQRESULT SQ_UpdateTimeInfo(void* sqvm) +{ + AcquireSRWLockExclusive(&serverInfoLock); + int endTimeFromNow = ceil(ClientSq_getfloat(sqvm, 1)); + const auto p1 = std::chrono::system_clock::now().time_since_epoch(); + serverInfo.endTime = std::chrono::duration_cast(p1).count() + endTimeFromNow; + ReleaseSRWLockExclusive(&serverInfoLock); + return SQRESULT_NOTNULL; +} + +// bool loading +SQRESULT SQ_SetConnected(void* sqvm) +{ + AcquireSRWLockExclusive(&gameStateLock); + gameState.loading = ClientSq_getbool(sqvm, 1); + ReleaseSRWLockExclusive(&gameStateLock); + return SQRESULT_NOTNULL; +} + +SQRESULT SQ_UpdateListenServer(void* sqvm) +{ + AcquireSRWLockExclusive(&serverInfoLock); + serverInfo.id = g_MasterServerManager->m_ownServerId; + serverInfo.password = Cvar_ns_server_password->GetString(); + ReleaseSRWLockExclusive(&serverInfoLock); + return SQRESULT_NOTNULL; +} + +int getServerInfoChar(char* out_buf, size_t out_buf_len, ServerInfoType var) +{ + AcquireSRWLockShared(&serverInfoLock); + int n = 0; + switch (var) + { + 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; + } + + ReleaseSRWLockShared(&serverInfoLock); + + return n; +} +int getServerInfoInt(int* out_ptr, ServerInfoType var) +{ + AcquireSRWLockShared(&serverInfoLock); + int n = 0; + switch (var) + { + 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; + } + + ReleaseSRWLockShared(&serverInfoLock); + + return n; +} +int getServerInfoBool(bool* out_ptr, ServerInfoType var) +{ + AcquireSRWLockShared(&serverInfoLock); + int n = 0; + switch (var) + { + case ServerInfoType::roundBased: + *out_ptr = serverInfo.roundBased; + break; + default: + n = -1; + } + + ReleaseSRWLockShared(&serverInfoLock); + + return n; +} + +int getGameStateChar(char* out_buf, size_t out_buf_len, GameStateInfoType var) +{ + AcquireSRWLockShared(&gameStateLock); + int n = 0; + switch (var) + { + 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; + } + + ReleaseSRWLockShared(&gameStateLock); + + return n; +} +int getGameStateInt(int* out_ptr, GameStateInfoType var) +{ + AcquireSRWLockShared(&gameStateLock); + int n = 0; + switch (var) + { + 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; + } + + ReleaseSRWLockShared(&gameStateLock); + + return n; +} +int getGameStateBool(bool* out_ptr, GameStateInfoType var) +{ + AcquireSRWLockShared(&gameStateLock); + int n = 0; + switch (var) + { + case GameStateInfoType::connected: + *out_ptr = gameState.connected; + break; + case GameStateInfoType::loading: + *out_ptr = gameState.loading; + break; + default: + n = -1; + } + + ReleaseSRWLockShared(&gameStateLock); + + return n; +} + +int getPlayerInfoChar(char* out_buf, size_t out_buf_len, PlayerInfoType var) +{ + AcquireSRWLockShared(&playerInfoLock); + int n = 0; + switch (var) + { + default: + n = -1; + } + + ReleaseSRWLockShared(&playerInfoLock); + + return n; +} +int getPlayerInfoInt(int* out_ptr, PlayerInfoType var) +{ + AcquireSRWLockShared(&playerInfoLock); + int n = 0; + switch (var) + { + case PlayerInfoType::uid: + *out_ptr = playerInfo.uid; + break; + default: + n = -1; + } + + ReleaseSRWLockShared(&playerInfoLock); + + return n; +} +int getPlayerInfoBool(bool* out_ptr, PlayerInfoType var) +{ + AcquireSRWLockShared(&playerInfoLock); + int n = 0; + switch (var) + { + default: + n = -1; + } + + ReleaseSRWLockShared(&playerInfoLock); + + return n; +} + +void InitialisePluginCommands(HMODULE baseAddress) +{ + // 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 + + g_UISquirrelManager->AddFuncRegistration( + "void", "NSUpdateGameStateUI", "string gamemode, string gamemodeName, string map, string mapName, bool connected, bool loading", "", + SQ_UpdateGameStateUI); + g_ClientSquirrelManager->AddFuncRegistration( + "void", "NSUpdateGameStateClient", + "int playerCount, int outScore, int secondHighestScore, int highestScore, bool roundBased, int scoreLimit", "", + SQ_UpdateGameStateClient); + g_UISquirrelManager->AddFuncRegistration( + "void", "NSUpdateServerInfo", + "string id, string name, string password, int players, int maxPlayers, string map, string mapDisplayName, string playlist, string " + "playlistDisplayName", + "", SQ_UpdateServerInfo); + g_ClientSquirrelManager->AddFuncRegistration( + "void", "NSUpdateServerInfoReload", "int maxPlayers", "", SQ_UpdateServerInfoBetweenRounds); + g_ClientSquirrelManager->AddFuncRegistration("void", "NSUpdateTimeInfo", "float timeInFuture", "", SQ_UpdateTimeInfo); + g_UISquirrelManager->AddFuncRegistration("void", "NSSetLoading", "bool loading", "", SQ_SetConnected); + g_UISquirrelManager->AddFuncRegistration("void", "NSUpdateListenServer", "", "", SQ_UpdateListenServer); +} diff --git a/NorthstarDedicatedTest/plugins.h b/NorthstarDedicatedTest/plugins.h new file mode 100644 index 00000000..27312da9 --- /dev/null +++ b/NorthstarDedicatedTest/plugins.h @@ -0,0 +1,19 @@ +#pragma once +#include "plugin_abi.h" + +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); + +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); + +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); + +void initGameState(); +void* getPluginObject(PluginObject var); + +void InitialisePluginCommands(HMODULE baseAddress); \ No newline at end of file -- cgit v1.2.3