From 64100065b55f79e76542ba689545c60e6fb0dcef Mon Sep 17 00:00:00 2001 From: Emma Miler Date: Thu, 22 Dec 2022 19:59:10 +0100 Subject: Add script concommands (#373) * Add script concommands * Fix indent * Formatting * Formatting stuff * Changes for review * Fix typo * Forgot to remove the return statement * Formatting --- NorthstarDLL/mods/modmanager.cpp | 159 +++++++++++++++++++++++++++++++-------- NorthstarDLL/mods/modmanager.h | 12 +++ 2 files changed, 138 insertions(+), 33 deletions(-) (limited to 'NorthstarDLL/mods') 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 auto ModConCommandCallback_Internal(std::string name, const CCommand& command) +{ + if (g_pSquirrel->m_pSQVM && g_pSquirrel->m_pSQVM) + { + std::vector args; + args.reserve(command.ArgC()); + for (int i = 1; i < command.ArgC(); i++) + args.push_back(command.Arg(i)); + g_pSquirrel->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(found->Function, command); + break; + case ScriptContext::SERVER: + ModConCommandCallback_Internal(found->Function, command); + break; + case ScriptContext::UI: + ModConCommandCallback_Internal(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 Scripts; // convars created by the mod std::vector ConVars; + // concommands created by the mod + std::vector ConCommands; // custom localisation files created by the mod std::vector LocalisationFiles; -- cgit v1.2.3