diff options
author | Emma Miler <27428383+emma-miler@users.noreply.github.com> | 2022-03-04 00:16:01 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-03 23:16:01 +0000 |
commit | 13dcc7d9307b160131ed80832efda99001aa6c6d (patch) | |
tree | ff7e021c2927f7d3e0ee0bb84642c6ee1c9b24ab /NorthstarDedicatedTest/plugins.cpp | |
parent | 62e137b32ad7c601b2db37e69b9f435a7dbc730d (diff) | |
download | NorthstarLauncher-13dcc7d9307b160131ed80832efda99001aa6c6d.tar.gz NorthstarLauncher-13dcc7d9307b160131ed80832efda99001aa6c6d.zip |
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>
Diffstat (limited to 'NorthstarDedicatedTest/plugins.cpp')
-rw-r--r-- | NorthstarDedicatedTest/plugins.cpp | 410 |
1 files changed, 410 insertions, 0 deletions
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 <chrono> +#include "masterserver.h" +#include "convar.h" +#include <windows.h> + +/// <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) +{ + 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<std::chrono::seconds>(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); +} |