diff options
author | Emma Miler <emma.pi@protonmail.com> | 2022-12-22 19:59:10 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-22 19:59:10 +0100 |
commit | 64100065b55f79e76542ba689545c60e6fb0dcef (patch) | |
tree | 396bfeb9eacdd6d8070dee89de16c6c216ac6b10 | |
parent | 7cdf27e6dfcf61329317e641e6625599cda3f5b0 (diff) | |
download | NorthstarLauncher-64100065b55f79e76542ba689545c60e6fb0dcef.tar.gz NorthstarLauncher-64100065b55f79e76542ba689545c60e6fb0dcef.zip |
Add script concommands (#373)
* Add script concommands
* Fix indent
* Formatting
* Formatting stuff
* Changes for review
* Fix typo
* Forgot to remove the return statement
* Formatting
-rw-r--r-- | NorthstarDLL/core/convar/convar.cpp | 40 | ||||
-rw-r--r-- | NorthstarDLL/core/convar/convar.h | 2 | ||||
-rw-r--r-- | NorthstarDLL/mods/modmanager.cpp | 159 | ||||
-rw-r--r-- | NorthstarDLL/mods/modmanager.h | 12 | ||||
-rw-r--r-- | NorthstarDLL/squirrel/squirrel.cpp | 12 | ||||
-rw-r--r-- | NorthstarDLL/squirrel/squirrel.h | 2 | ||||
-rw-r--r-- | NorthstarDLL/squirrel/squirrelclasstypes.h | 1 |
7 files changed, 195 insertions, 33 deletions
diff --git a/NorthstarDLL/core/convar/convar.cpp b/NorthstarDLL/core/convar/convar.cpp index 5ff0e4c1..11411c0a 100644 --- a/NorthstarDLL/core/convar/convar.cpp +++ b/NorthstarDLL/core/convar/convar.cpp @@ -488,3 +488,43 @@ bool ConVar::ClampValue(float& flValue) return false; } + +int ParseConVarFlagsString(std::string modName, std::string sFlags) +{ + sFlags += '|'; // add additional | so we register the last flag + std::string sCurrentFlag; + + for (int i = 0; i < sFlags.length(); i++) + { + if (isspace(sFlags[i])) + continue; + + // if we encounter a |, add current string as a flag + if (sFlags[i] == '|') + { + bool bHasFlags = false; + int iCurrentFlags; + + for (auto& flagPair : g_PrintCommandFlags) + { + if (!sCurrentFlag.compare(flagPair.second)) + { + iCurrentFlags = flagPair.first; + bHasFlags = true; + break; + } + } + + if (bHasFlags) + return iCurrentFlags; + else + spdlog::warn("Mod ConCommand {} has unknown flag {}", modName, sCurrentFlag); + + sCurrentFlag = ""; + } + else + { + sCurrentFlag += sFlags[i]; + } + } +} diff --git a/NorthstarDLL/core/convar/convar.h b/NorthstarDLL/core/convar/convar.h index edc6ac97..4b00e25f 100644 --- a/NorthstarDLL/core/convar/convar.h +++ b/NorthstarDLL/core/convar/convar.h @@ -190,3 +190,5 @@ class ConVar void* m_pMalloc {}; // 0x0070 char m_pPad80[10] {}; // 0x0080 }; // Size: 0x0080 + +int ParseConVarFlagsString(std::string modName, std::string flags); diff --git a/NorthstarDLL/mods/modmanager.cpp b/NorthstarDLL/mods/modmanager.cpp index 2196b118..6310b7ec 100644 --- a/NorthstarDLL/mods/modmanager.cpp +++ b/NorthstarDLL/mods/modmanager.cpp @@ -118,46 +118,59 @@ Mod::Mod(fs::path modDir, char* jsonBuf) { // parse cvar flags from string // example string: ARCHIVE_PLAYERPROFILE | GAMEDLL + convar->Flags |= ParseConVarFlagsString(convar->Name, convarObj["Flags"].GetString()); + } + } - std::string sFlags = convarObj["Flags"].GetString(); - sFlags += '|'; // add additional | so we register the last flag - std::string sCurrentFlag; + ConVars.push_back(convar); + } + } - for (int i = 0; i < sFlags.length(); i++) - { - if (isspace(sFlags[i])) - continue; + if (modJson.HasMember("ConCommands") && modJson["ConCommands"].IsArray()) + { + for (auto& concommandObj : modJson["ConCommands"].GetArray()) + { + if (!concommandObj.IsObject() || !concommandObj.HasMember("Name") || !concommandObj.HasMember("Function") || + !concommandObj.HasMember("Context")) + { + continue; + } - // if we encounter a |, add current string as a flag - if (sFlags[i] == '|') - { - bool bHasFlags = false; - int iCurrentFlags; + // have to allocate this manually, otherwise concommand registration will break + // unfortunately this causes us to leak memory on reload, unsure of a way around this rn + ModConCommand* concommand = new ModConCommand; + concommand->Name = concommandObj["Name"].GetString(); + concommand->Function = concommandObj["Function"].GetString(); + concommand->Context = ScriptContextFromString(concommandObj["Context"].GetString()); + if (concommand->Context == ScriptContext::INVALID) + { + spdlog::warn("Mod ConCommand {} has invalid context {}", concommand->Name, concommandObj["Context"].GetString()); + continue; + } - for (auto& flagPair : g_PrintCommandFlags) - { - if (!sCurrentFlag.compare(flagPair.second)) - { - iCurrentFlags = flagPair.first; - bHasFlags = true; - break; - } - } + if (concommandObj.HasMember("HelpString")) + concommand->HelpString = concommandObj["HelpString"].GetString(); + else + concommand->HelpString = ""; - if (bHasFlags) - convar->Flags |= iCurrentFlags; - else - spdlog::warn("Mod ConVar {} has unknown flag {}", convar->Name, sCurrentFlag); + concommand->Flags = FCVAR_NONE; - sCurrentFlag = ""; - } - else - sCurrentFlag += sFlags[i]; - } + if (concommandObj.HasMember("Flags")) + { + // read raw integer flags + if (concommandObj["Flags"].IsInt()) + { + concommand->Flags = concommandObj["Flags"].GetInt(); + } + else if (concommandObj["Flags"].IsString()) + { + // parse cvar flags from string + // example string: ARCHIVE_PLAYERPROFILE | GAMEDLL + concommand->Flags |= ParseConVarFlagsString(concommand->Name, concommandObj["Flags"].GetString()); } } - ConVars.push_back(convar); + ConCommands.push_back(concommand); } } @@ -267,6 +280,70 @@ ModManager::ModManager() LoadMods(); } +struct Test +{ + std::string funcName; + ScriptContext context; +}; + +template <ScriptContext context> auto ModConCommandCallback_Internal(std::string name, const CCommand& command) +{ + if (g_pSquirrel<context>->m_pSQVM && g_pSquirrel<context>->m_pSQVM) + { + std::vector<std::string> args; + args.reserve(command.ArgC()); + for (int i = 1; i < command.ArgC(); i++) + args.push_back(command.Arg(i)); + g_pSquirrel<context>->AsyncCall(name, args); + } + else + { + spdlog::warn("ConCommand `{}` was called while the associated Squirrel VM `{}` was unloaded", name, GetContextName(context)); + } +} + +auto ModConCommandCallback(const CCommand& command) +{ + ModConCommand* found = nullptr; + auto commandString = std::string(command.GetCommandString()); + + // Finding the first space to remove the command's name + auto firstSpace = commandString.find(' '); + if (firstSpace) + { + commandString = commandString.substr(0, firstSpace); + } + + // Find the mod this command belongs to + for (auto& mod : g_pModManager->m_LoadedMods) + { + auto res = std::find_if( + mod.ConCommands.begin(), + mod.ConCommands.end(), + [&commandString](const ModConCommand* other) { return other->Name == commandString; }); + if (res != mod.ConCommands.end()) + { + found = *res; + break; + } + } + if (!found) + return; + + switch (found->Context) + { + case ScriptContext::CLIENT: + ModConCommandCallback_Internal<ScriptContext::CLIENT>(found->Function, command); + break; + case ScriptContext::SERVER: + ModConCommandCallback_Internal<ScriptContext::SERVER>(found->Function, command); + break; + case ScriptContext::UI: + ModConCommandCallback_Internal<ScriptContext::UI>(found->Function, command); + break; + }; +} + void ModManager::LoadMods() { if (m_bHasLoadedMods) @@ -365,9 +442,25 @@ void ModManager::LoadMods() // preexisting convars note: we don't delete convars if they already exist because they're used for script stuff, unfortunately this // causes us to leak memory on reload, but not much, potentially find a way to not do this at some point for (ModConVar* convar : mod.ConVars) - if (!R2::g_pCVar->FindVar(convar->Name.c_str())) // make sure convar isn't registered yet, unsure if necessary but idk what - // behaviour is for defining same convar multiple times + { + // make sure convar isn't registered yet, unsure if necessary but idk what + // behaviour is for defining same convar multiple times + if (!R2::g_pCVar->FindVar(convar->Name.c_str())) + { new ConVar(convar->Name.c_str(), convar->DefaultValue.c_str(), convar->Flags, convar->HelpString.c_str()); + } + } + + for (ModConCommand* command : mod.ConCommands) + { + // make sure command isnt't registered multiple times. + if (!R2::g_pCVar->FindCommand(command->Name.c_str())) + { + ConCommand* newCommand = new ConCommand(); + std::string funcName = command->Function; + RegisterConCommand(command->Name.c_str(), ModConCommandCallback, command->HelpString.c_str(), command->Flags); + } + } // read vpk paths if (fs::exists(mod.m_ModDirectory / "vpk")) diff --git a/NorthstarDLL/mods/modmanager.h b/NorthstarDLL/mods/modmanager.h index a77d85bd..ded6ff06 100644 --- a/NorthstarDLL/mods/modmanager.h +++ b/NorthstarDLL/mods/modmanager.h @@ -22,6 +22,16 @@ struct ModConVar int Flags; }; +struct ModConCommand +{ + public: + std::string Name; + std::string Function; + std::string HelpString; + ScriptContext Context; + int Flags; +}; + struct ModScriptCallback { public: @@ -87,6 +97,8 @@ class Mod std::vector<ModScript> Scripts; // convars created by the mod std::vector<ModConVar*> ConVars; + // concommands created by the mod + std::vector<ModConCommand*> ConCommands; // custom localisation files created by the mod std::vector<std::string> LocalisationFiles; diff --git a/NorthstarDLL/squirrel/squirrel.cpp b/NorthstarDLL/squirrel/squirrel.cpp index 5e968daa..8761fb13 100644 --- a/NorthstarDLL/squirrel/squirrel.cpp +++ b/NorthstarDLL/squirrel/squirrel.cpp @@ -88,6 +88,18 @@ eSQReturnType SQReturnTypeFromString(const char* pReturnType) return eSQReturnType::Default; // previous default value } +ScriptContext ScriptContextFromString(std::string string) +{ + if (string == "UI") + return ScriptContext::UI; + if (string == "CLIENT") + return ScriptContext::CLIENT; + if (string == "SERVER") + return ScriptContext::SERVER; + else + return ScriptContext::INVALID; +} + const char* SQTypeNameFromID(int type) { switch (type) diff --git a/NorthstarDLL/squirrel/squirrel.h b/NorthstarDLL/squirrel/squirrel.h index 3e3d08c9..51fa93b3 100644 --- a/NorthstarDLL/squirrel/squirrel.h +++ b/NorthstarDLL/squirrel/squirrel.h @@ -37,6 +37,8 @@ const char* GetContextName_Short(ScriptContext context); eSQReturnType SQReturnTypeFromString(const char* pReturnType); const char* SQTypeNameFromID(const int iTypeId); +ScriptContext ScriptContextFromString(std::string string); + std::shared_ptr<ColoredLogger> getSquirrelLoggerByContext(ScriptContext context); namespace NS::log diff --git a/NorthstarDLL/squirrel/squirrelclasstypes.h b/NorthstarDLL/squirrel/squirrelclasstypes.h index e26bc8d0..0672724c 100644 --- a/NorthstarDLL/squirrel/squirrelclasstypes.h +++ b/NorthstarDLL/squirrel/squirrelclasstypes.h @@ -73,6 +73,7 @@ struct SQFuncRegistration enum class ScriptContext : int { + INVALID = -1, SERVER, CLIENT, UI, |