diff options
author | BobTheBob <32057864+BobTheBob9@users.noreply.github.com> | 2021-07-20 02:14:17 +0100 |
---|---|---|
committer | BobTheBob <32057864+BobTheBob9@users.noreply.github.com> | 2021-07-20 02:14:17 +0100 |
commit | 958d03d2817e312c8eb70234f1c65e4bcbded716 (patch) | |
tree | e7fd88a8e6a5ececdd46abf9b95c7a2e82eb2b98 | |
parent | a71d52ffd1531a4a03cb4c87dc56ace4f5cb33c1 (diff) | |
download | NorthstarLauncher-958d03d2817e312c8eb70234f1c65e4bcbded716.tar.gz NorthstarLauncher-958d03d2817e312c8eb70234f1c65e4bcbded716.zip |
add basic mod support with support for mod convars
-rw-r--r-- | NorthstarDedicatedTest/convar.cpp | 5 | ||||
-rw-r--r-- | NorthstarDedicatedTest/convar.h | 5 | ||||
-rw-r--r-- | NorthstarDedicatedTest/modmanager.cpp | 178 | ||||
-rw-r--r-- | NorthstarDedicatedTest/modmanager.h | 17 | ||||
-rw-r--r-- | NorthstarDedicatedTest/pch.h | 2 | ||||
-rw-r--r-- | NorthstarDedicatedTest/sourceconsole.cpp | 5 |
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)); |