aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-07-20 02:14:17 +0100
committerBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-07-20 02:14:17 +0100
commit958d03d2817e312c8eb70234f1c65e4bcbded716 (patch)
treee7fd88a8e6a5ececdd46abf9b95c7a2e82eb2b98
parenta71d52ffd1531a4a03cb4c87dc56ace4f5cb33c1 (diff)
downloadNorthstarLauncher-958d03d2817e312c8eb70234f1c65e4bcbded716.tar.gz
NorthstarLauncher-958d03d2817e312c8eb70234f1c65e4bcbded716.zip
add basic mod support with support for mod convars
-rw-r--r--NorthstarDedicatedTest/convar.cpp5
-rw-r--r--NorthstarDedicatedTest/convar.h5
-rw-r--r--NorthstarDedicatedTest/modmanager.cpp178
-rw-r--r--NorthstarDedicatedTest/modmanager.h17
-rw-r--r--NorthstarDedicatedTest/pch.h2
-rw-r--r--NorthstarDedicatedTest/sourceconsole.cpp5
6 files changed, 207 insertions, 5 deletions
diff --git a/NorthstarDedicatedTest/convar.cpp b/NorthstarDedicatedTest/convar.cpp
index 18828a27..ca5d8ef3 100644
--- a/NorthstarDedicatedTest/convar.cpp
+++ b/NorthstarDedicatedTest/convar.cpp
@@ -1,5 +1,8 @@
#include "pch.h"
#include "convar.h"
+#include <set>
+
+std::set<std::string> g_CustomConvars; // this is used in modloading code to determine whether we've registered a mod convar already
typedef void(*ConVarConstructorType)(ConVar* newVar, const char* name, const char* defaultValue, int flags, const char* helpString);
ConVarConstructorType conVarConstructor;
@@ -12,6 +15,8 @@ ConVar* RegisterConVar(const char* name, const char* defaultValue, int flags, co
ConVar* newVar = new ConVar;
conVarConstructor(newVar, name, defaultValue, flags, helpString);
+ g_CustomConvars.insert(name);
+
return newVar;
}
diff --git a/NorthstarDedicatedTest/convar.h b/NorthstarDedicatedTest/convar.h
index e189e594..014cb823 100644
--- a/NorthstarDedicatedTest/convar.h
+++ b/NorthstarDedicatedTest/convar.h
@@ -1,4 +1,5 @@
#pragma once
+#include <set>
// taken directly from iconvar.h
// The default, no flags at all
@@ -94,4 +95,6 @@ public:
ConVar* RegisterConVar(const char* name, const char* defaultValue, int flags, const char* helpString);
-void InitialiseConVars(HMODULE baseAddress); \ No newline at end of file
+void InitialiseConVars(HMODULE baseAddress);
+
+extern std::set<std::string> g_CustomConvars; \ No newline at end of file
diff --git a/NorthstarDedicatedTest/modmanager.cpp b/NorthstarDedicatedTest/modmanager.cpp
index d6ccfa6a..72998347 100644
--- a/NorthstarDedicatedTest/modmanager.cpp
+++ b/NorthstarDedicatedTest/modmanager.cpp
@@ -1,9 +1,136 @@
#include "pch.h"
-#include "ModManager.h"
-#include "rapidjson/rapidjson.h"
+#include "modmanager.h"
+#include "convar.h"
+
+#include "rapidjson/error/en.h"
+#include "rapidjson/document.h"
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <sstream>
+#include <vector>
ModManager* g_ModManager;
+Mod::Mod(fs::path modDir, char* jsonBuf)
+{
+ wasReadSuccessfully = false;
+
+ rapidjson::Document modJson;
+ modJson.Parse<rapidjson::ParseFlag::kParseCommentsFlag | rapidjson::ParseFlag::kParseTrailingCommasFlag>(jsonBuf);
+
+ // fail if parse error
+ if (modJson.HasParseError())
+ {
+ spdlog::error("Failed reading mod file {}: encountered parse error \"{}\" at offset {}", (modDir / "mod.json").string(), GetParseError_En(modJson.GetParseError()), modJson.GetErrorOffset());
+ return;
+ }
+
+ // fail if it's not a json obj (could be an array, string, etc)
+ if (!modJson.IsObject())
+ {
+ spdlog::error("Failed reading mod file {}: file is not a JSON object", (modDir / "mod.json").string());
+ return;
+ }
+
+ // basic mod info
+ // name is required
+ if (!modJson.HasMember("Name"))
+ {
+ spdlog::error("Failed reading mod file {}: missing required member \"Name\"", (modDir / "mod.json").string());
+ return;
+ }
+
+ Name = modJson["Name"].GetString();
+
+ if (modJson.HasMember("Description"))
+ Description = modJson["Description"].GetString();
+ else
+ Description = "";
+
+ if (modJson.HasMember("Version"))
+ Version = modJson["Version"].GetString();
+ else
+ {
+ Version = "0.0.0";
+ spdlog::warn("Mod file {} is missing a version, consider adding a version", (modDir / "mod.json").string());
+ }
+
+ if (modJson.HasMember("DownloadLink"))
+ DownloadLink = modJson["DownloadLink"].GetString();
+ else
+ DownloadLink = "";
+
+ if (modJson.HasMember("RequiredOnClient"))
+ RequiredOnClient = modJson["RequiredOnClient"].GetBool();
+ else
+ RequiredOnClient = false;
+
+ // mod convars
+ if (modJson.HasMember("ConVars") && modJson["ConVars"].IsArray())
+ {
+ for (auto& convarObj : modJson["ConVars"].GetArray())
+ {
+ if (!convarObj.IsObject() || !convarObj.HasMember("Name") || !convarObj.HasMember("DefaultValue"))
+ continue;
+
+ ModConVar* convar = new ModConVar;
+ convar->Name = convarObj["Name"].GetString();
+ convar->DefaultValue = convarObj["DefaultValue"].GetString();
+
+ if (convarObj.HasMember("HelpString"))
+ convar->HelpString = convarObj["HelpString"].GetString();
+ else
+ convar->HelpString = "";
+
+ // todo: could possibly parse FCVAR names here instead
+ if (convarObj.HasMember("Flags"))
+ convar->Flags = convarObj["Flags"].GetInt();
+ else
+ convar->Flags = FCVAR_NONE;
+
+ ConVars.push_back(convar);
+ }
+ }
+
+ // mod scripts
+ if (modJson.HasMember("Scripts") && modJson["Scripts"].IsArray())
+ {
+ for (auto& scriptObj : modJson["Scripts"].GetArray())
+ {
+ if (!scriptObj.IsObject() || !scriptObj.HasMember("Path") || !scriptObj.HasMember("RunOn"))
+ continue;
+
+ ModScript* script = new ModScript;
+
+ script->Path = scriptObj["Path"].GetString();
+ script->RsonRunOn = scriptObj["RunOn"].GetString();
+
+ // callbacks
+ for (auto iterator = scriptObj.MemberBegin(); iterator != scriptObj.MemberEnd(); iterator++)
+ {
+ if (!iterator->name.IsString() || !iterator->value.IsObject())
+ continue;
+
+ ModScriptCallback* callback = new ModScriptCallback;
+ callback->HookedCodeCallback = iterator->name.GetString();
+
+ if (iterator->value.HasMember("Before") && iterator->value["Before"].IsString())
+ callback->BeforeCallback = iterator->value["Before"].GetString();
+
+ if (iterator->value.HasMember("After") && iterator->value["After"].IsString())
+ callback->AfterCallback = iterator->value["After"].GetString();
+
+ script->Callbacks.push_back(callback);
+ }
+
+ Scripts.push_back(script);
+ }
+ }
+
+ wasReadSuccessfully = true;
+}
+
ModManager::ModManager()
{
LoadMods();
@@ -11,10 +138,55 @@ ModManager::ModManager()
void ModManager::LoadMods()
{
+ std::vector<fs::path> modDirs;
+
+ // get mod directories
+ for (fs::directory_entry dir : fs::directory_iterator(MOD_FOLDER_PATH))
+ if (fs::exists(dir.path() / "mod.json"))
+ modDirs.push_back(dir.path());
+
+ for (fs::path modDir : modDirs)
+ {
+ // read mod json file
+ std::ifstream jsonStream(modDir / "mod.json");
+ std::stringstream jsonStringStream;
+
+ // fail if no mod json
+ if (jsonStream.fail())
+ {
+ spdlog::warn("Mod {} has a directory but no mod.json", modDir.string());
+ continue;
+ }
+
+ while (jsonStream.peek() != EOF)
+ jsonStringStream << (char)jsonStream.get();
+
+ jsonStream.close();
+
+ Mod* mod = new Mod(modDir, (char*)jsonStringStream.str().c_str());
+
+ if (mod->wasReadSuccessfully)
+ {
+ spdlog::info("Loaded mod {} successfully", mod->Name);
+ loadedMods.push_back(mod);
+ }
+ else
+ {
+ spdlog::warn("Skipping loading mod file {}", (modDir / "mod.json").string());
+ delete mod;
+ }
+ }
+
+ for (Mod* mod : loadedMods)
+ {
+ for (ModConVar* convar : mod->ConVars)
+ if (g_CustomConvars.find(convar->Name) == g_CustomConvars.end()) // make sure convar isn't registered yet
+ RegisterConVar(convar->Name.c_str(), convar->DefaultValue.c_str(), convar->Flags, convar->HelpString.c_str());
+ }
}
void InitialiseModManager(HMODULE baseAddress)
{
- g_ModManager = new ModManager;
+ g_ModManager = new ModManager();
} \ No newline at end of file
diff --git a/NorthstarDedicatedTest/modmanager.h b/NorthstarDedicatedTest/modmanager.h
index 32492283..f2501bed 100644
--- a/NorthstarDedicatedTest/modmanager.h
+++ b/NorthstarDedicatedTest/modmanager.h
@@ -1,9 +1,15 @@
#pragma once
#include <string>
#include <vector>
+#include <filesystem>
+
+namespace fs = std::filesystem;
+
+const fs::path MOD_FOLDER_PATH = "R2Northstar/mods";
class ModConVar
{
+public:
std::string Name;
std::string DefaultValue;
std::string HelpString;
@@ -12,6 +18,7 @@ class ModConVar
class ModScriptCallback
{
+public:
std::string HookedCodeCallback;
// called before the codecallback is executed
@@ -22,8 +29,9 @@ class ModScriptCallback
class ModScript
{
+public:
std::string Path;
- std::string ScriptsRsonSide;
+ std::string RsonRunOn;
std::vector<ModScriptCallback*> Callbacks;
};
@@ -54,6 +62,13 @@ public:
std::vector<std::string> Vpks;
//std::vector<ModKeyValues*> KeyValues;
+
+ // other stuff
+
+ bool wasReadSuccessfully = false;
+
+public:
+ Mod(fs::path modPath, char* jsonBuf);
};
class ModManager
diff --git a/NorthstarDedicatedTest/pch.h b/NorthstarDedicatedTest/pch.h
index 697cea6f..3044923f 100644
--- a/NorthstarDedicatedTest/pch.h
+++ b/NorthstarDedicatedTest/pch.h
@@ -2,6 +2,8 @@
#define PCH_H
#define _CRT_SECURE_NO_WARNINGS
+#define RAPIDJSON_NOMEMBERITERATORCLASS // need this for rapidjson
+#define NOMINMAX // this too
// add headers that you want to pre-compile here
#include <Windows.h>
diff --git a/NorthstarDedicatedTest/sourceconsole.cpp b/NorthstarDedicatedTest/sourceconsole.cpp
index a65c62d5..88d038df 100644
--- a/NorthstarDedicatedTest/sourceconsole.cpp
+++ b/NorthstarDedicatedTest/sourceconsole.cpp
@@ -28,6 +28,8 @@ void OnCommandSubmittedHook(CConsoleDialog* consoleDialog, const char* pCommand)
consoleDialog->m_pConsolePanel->Print("\n");
// todo: call the help command in the future
+
+ onCommandSubmittedOriginal(consoleDialog, pCommand);
}
// called from sourceinterface.cpp in client createinterface hooks, on GameClientExports001
@@ -51,6 +53,9 @@ void InitialiseSourceConsole(HMODULE baseAddress)
RegisterConCommand("toggleconsole", ConCommand_toggleconsole, "toggles the console", FCVAR_NONE);
}
+
+// logging stuff
+
SourceConsoleSink::SourceConsoleSink()
{
logColours.emplace(spdlog::level::trace, SourceColor(0, 255, 255, 255));