aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmma Miler <emma.pi@protonmail.com>2022-12-22 19:59:10 +0100
committerGitHub <noreply@github.com>2022-12-22 19:59:10 +0100
commit64100065b55f79e76542ba689545c60e6fb0dcef (patch)
tree396bfeb9eacdd6d8070dee89de16c6c216ac6b10
parent7cdf27e6dfcf61329317e641e6625599cda3f5b0 (diff)
downloadNorthstarLauncher-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.cpp40
-rw-r--r--NorthstarDLL/core/convar/convar.h2
-rw-r--r--NorthstarDLL/mods/modmanager.cpp159
-rw-r--r--NorthstarDLL/mods/modmanager.h12
-rw-r--r--NorthstarDLL/squirrel/squirrel.cpp12
-rw-r--r--NorthstarDLL/squirrel/squirrel.h2
-rw-r--r--NorthstarDLL/squirrel/squirrelclasstypes.h1
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,