add basic mod support with support for mod convars
This commit is contained in:
parent
a71d52ffd1
commit
958d03d281
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
void InitialiseConVars(HMODULE baseAddress);
|
||||
|
||||
extern std::set<std::string> g_CustomConvars;
|
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in New Issue