mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
224 lines
6.7 KiB
C++
224 lines
6.7 KiB
C++
//=============================================================================//
|
|
//
|
|
// Purpose: Manage loading mods
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//=============================================================================//
|
|
|
|
#include "core/stdafx.h"
|
|
#include "modsystem.h"
|
|
#include "localize/localize.h"
|
|
#include "tier1/cvar.h"
|
|
#include "vpc/rson.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: initialize the mod system
|
|
// Input :
|
|
//-----------------------------------------------------------------------------
|
|
void CModSystem::Init()
|
|
{
|
|
LoadModStatusList();
|
|
|
|
CreateDirectories("platform\\mods");
|
|
|
|
for (auto& it : fs::directory_iterator("platform\\mods"))
|
|
{
|
|
if (!fs::is_directory(it))
|
|
continue;
|
|
|
|
fs::path basePath = it.path();
|
|
DevMsg(eDLL_T::ENGINE, "Found mod at '%s'.\n", basePath.string().c_str());
|
|
fs::path settingsPath = basePath / "mod.vdf";
|
|
|
|
if (fs::exists(settingsPath))
|
|
{
|
|
CModSystem::ModInstance_t modInst = CModSystem::ModInstance_t(basePath);
|
|
if (modInst.m_iState != eModState::UNLOADED)
|
|
m_vModList.push_back(modInst);
|
|
}
|
|
}
|
|
|
|
WriteModStatusList();
|
|
}
|
|
|
|
void CModSystem::LoadModStatusList()
|
|
{
|
|
if (FileSystem()->FileExists("platform/mods.vdf"))
|
|
{
|
|
KeyValues* pModList = FileSystem()->LoadKeyValues(IFileSystem::TYPE_COMMON, "platform/mods.vdf", "GAME");
|
|
|
|
for (KeyValues* pSubKey = pModList->GetFirstSubKey(); pSubKey != nullptr; pSubKey = pSubKey->GetNextKey())
|
|
{
|
|
size_t idHash = std::hash<std::string>{}(std::string(pSubKey->GetName()));
|
|
m_vEnabledList.emplace(idHash, pSubKey->GetBool());
|
|
}
|
|
}
|
|
}
|
|
|
|
void CModSystem::WriteModStatusList()
|
|
{
|
|
KeyValues kv = KeyValues("ModList");
|
|
KeyValues* pModListKV = kv.FindKey("ModList", true);
|
|
|
|
for (auto& it : m_vModList)
|
|
{
|
|
bool enabled = false;
|
|
if (it.m_iState == eModState::ENABLED)
|
|
enabled = true;
|
|
|
|
pModListKV->SetBool(it.m_szModID.c_str(), enabled);
|
|
}
|
|
|
|
CUtlBuffer uBuf = CUtlBuffer(0i64, 0, CUtlBuffer::TEXT_BUFFER);
|
|
|
|
kv.RecursiveSaveToFile(uBuf, 0);
|
|
|
|
FileSystem()->WriteFile("platform/mods.vdf", NULL, uBuf); // NULL instead of "GAME" because otherwise for some reason the file clears itself when the process exits
|
|
}
|
|
|
|
|
|
CModSystem::ModInstance_t::ModInstance_t(const fs::path& basePath) : m_szName(std::string()), m_szModID(std::string()), m_BasePath(basePath), m_szDescription(std::string()), m_szVersion(std::string()), m_SettingsKV(nullptr), m_iState(eModState::LOADING)
|
|
{
|
|
std::string settingsPath = (m_BasePath / "mod.vdf").string();
|
|
|
|
KeyValues* pSettingsKV = FileSystem()->LoadKeyValues(IFileSystem::TYPE_COMMON, settingsPath.c_str(), "GAME");
|
|
m_SettingsKV = pSettingsKV;
|
|
|
|
if (!m_SettingsKV)
|
|
{
|
|
SetState(eModState::UNLOADED);
|
|
Error(eDLL_T::ENGINE, NO_ERROR, "Failed to parse mod.vdf for mod at path '%s'\n", m_BasePath.string().c_str());
|
|
return;
|
|
}
|
|
|
|
/////////////////////////////
|
|
// "name" "An R5Reloaded Mod"
|
|
// [rexx]: could be optional and have id as fallback
|
|
KeyValues* pName = pSettingsKV->FindKey("name");
|
|
|
|
if (!pName)
|
|
{
|
|
SetState(eModState::UNLOADED);
|
|
Error(eDLL_T::ENGINE, NO_ERROR, "Mod settings file '%s' was missing required 'name' field. Skipping mod...\n", settingsPath.c_str());
|
|
return;
|
|
}
|
|
|
|
m_szName = pName->GetString();
|
|
|
|
/////////////////////////////
|
|
// "version" "1.0.0"
|
|
KeyValues* pVersion = pSettingsKV->FindKey("version");
|
|
|
|
if (!pVersion)
|
|
{
|
|
SetState(eModState::UNLOADED);
|
|
Error(eDLL_T::ENGINE, NO_ERROR, "Mod settings file '%s' was missing required 'version' field. Skipping mod...\n", settingsPath.c_str());
|
|
return;
|
|
}
|
|
|
|
m_szVersion = pVersion->GetString();
|
|
|
|
/////////////////////////////
|
|
// "id" "r5reloaded.TestMod"
|
|
KeyValues* pId = pSettingsKV->FindKey("id");
|
|
|
|
if (!pId)
|
|
{
|
|
SetState(eModState::UNLOADED);
|
|
Error(eDLL_T::ENGINE, NO_ERROR, "Mod settings file '%s' was missing required 'id' field. Skipping mod...\n", settingsPath.c_str());
|
|
return;
|
|
}
|
|
|
|
m_szModID = pId->GetString();
|
|
|
|
/////////////////////////////
|
|
// optional mod description field
|
|
m_szDescription = pSettingsKV->GetString("description");
|
|
|
|
size_t idHash = std::hash<std::string>{}(m_szModID);
|
|
|
|
auto& enabledList = g_pModSystem->GetEnabledList();
|
|
if (enabledList.count(idHash) == 0)
|
|
{
|
|
DevMsg(eDLL_T::ENGINE, "Mod does not exist in 'mods.vdf'. Enabling...\n");
|
|
SetState(eModState::ENABLED);
|
|
}
|
|
else
|
|
{
|
|
bool bEnable = enabledList[idHash];
|
|
SetState(bEnable ? eModState::ENABLED : eModState::DISABLED);
|
|
|
|
DevMsg(eDLL_T::ENGINE, "Mod exists in 'mods.vdf' and is %s.\n", bEnable ? "enabled" : "disabled");
|
|
}
|
|
|
|
if (m_iState != eModState::ENABLED)
|
|
return;
|
|
|
|
// parse any additional info from mod.vdf
|
|
|
|
// add mod folder to search paths so files can be easily loaded from here
|
|
// [rexx]: maybe this isn't ideal as the only way of finding the mod's files, as there may be name clashes in files where the engine
|
|
// won't really care about the input file name. it may be better to, where possible, request files by file path relative to root (i.e. including platform/mods/{mod}/)
|
|
FileSystem()->AddSearchPath(m_BasePath.string().c_str(), "GAME", SearchPathAdd_t::PATH_ADD_TO_TAIL);
|
|
|
|
KeyValues* pLocalizationFiles = pSettingsKV->FindKey("LocalizationFiles");
|
|
|
|
if (pLocalizationFiles)
|
|
{
|
|
for (KeyValues* pSubKey = pLocalizationFiles->GetFirstSubKey(); pSubKey != nullptr; pSubKey = pSubKey->GetNextKey())
|
|
{
|
|
this->m_vszLocalizationFiles.push_back(pSubKey->GetName());
|
|
}
|
|
}
|
|
|
|
KeyValues* pConVars = pSettingsKV->FindKey("ConVars");
|
|
|
|
if (pConVars)
|
|
{
|
|
for (KeyValues* pSubKey = pConVars->GetFirstSubKey(); pSubKey != nullptr; pSubKey = pSubKey->GetNextKey())
|
|
{
|
|
const char* pszName = pSubKey->GetName();
|
|
const char* pszHelpString = pSubKey->GetString("helpString");
|
|
const char* pszFlagsString = pSubKey->GetString("flags", "NONE");
|
|
|
|
KeyValues* pValues = pSubKey->FindKey("Values");
|
|
|
|
const char* pszDefaultValue = "0";
|
|
bool bMin = false;
|
|
bool bMax = false;
|
|
float fMin = 0.f;
|
|
float fMax = 0.f;
|
|
|
|
if (pValues)
|
|
{
|
|
pszDefaultValue = pValues->GetString("default", "0");
|
|
|
|
// minimum cvar value
|
|
if (pValues->FindKey("min"))
|
|
{
|
|
bMin = true; // has min value
|
|
fMin = pValues->GetFloat("min", 0.f);
|
|
}
|
|
|
|
// maximum cvar value
|
|
if (pValues->FindKey("max"))
|
|
{
|
|
bMax = true; // has max value
|
|
fMax = pValues->GetFloat("max", 1.f);
|
|
}
|
|
}
|
|
|
|
int flags = FCVAR_NONE;
|
|
if (ConVar::ParseFlagString(pszFlagsString, flags, pszName))
|
|
ConVar::StaticCreate(pszName, pszDefaultValue, flags, pszHelpString, bMin, fMin, bMax, fMax, nullptr, nullptr);
|
|
}
|
|
}
|
|
|
|
std::string scriptsRsonPath = (m_BasePath / "scripts/vscripts/scripts.rson").string();
|
|
|
|
if (FileSystem()->FileExists(scriptsRsonPath.c_str(), "GAME"))
|
|
m_bHasScriptCompileList = true;
|
|
};
|
|
|
|
CModSystem* g_pModSystem = new CModSystem(); |