aboutsummaryrefslogtreecommitdiff
path: root/primedev/mods
diff options
context:
space:
mode:
Diffstat (limited to 'primedev/mods')
-rw-r--r--primedev/mods/autodownload/moddownloader.cpp42
-rw-r--r--primedev/mods/autodownload/moddownloader.h19
-rw-r--r--primedev/mods/modmanager.cpp51
-rw-r--r--primedev/mods/modmanager.h29
-rw-r--r--primedev/mods/modsavefiles.cpp2
5 files changed, 104 insertions, 39 deletions
diff --git a/primedev/mods/autodownload/moddownloader.cpp b/primedev/mods/autodownload/moddownloader.cpp
index 8e533dec..c20a3adb 100644
--- a/primedev/mods/autodownload/moddownloader.cpp
+++ b/primedev/mods/autodownload/moddownloader.cpp
@@ -103,23 +103,23 @@ void ModDownloader::FetchModsListFromAPI()
for (auto i = verifiedModsJson.MemberBegin(); i != verifiedModsJson.MemberEnd(); ++i)
{
// Format testing
- if (!i->value.HasMember("DependencyPrefix") || !i->value.HasMember("Versions"))
+ if (!i->value.HasMember("Repository") || !i->value.HasMember("Versions"))
{
spdlog::warn("Verified mods manifesto format is unrecognized, skipping loading.");
return;
}
std::string name = i->name.GetString();
- std::string dependency = i->value["DependencyPrefix"].GetString();
-
std::unordered_map<std::string, VerifiedModVersion> modVersions;
+
rapidjson::Value& versions = i->value["Versions"];
assert(versions.IsArray());
for (auto& attribute : versions.GetArray())
{
assert(attribute.IsObject());
// Format testing
- if (!attribute.HasMember("Version") || !attribute.HasMember("Checksum"))
+ if (!attribute.HasMember("Version") || !attribute.HasMember("Checksum") || !attribute.HasMember("DownloadLink") ||
+ !attribute.HasMember("Platform"))
{
spdlog::warn("Verified mods manifesto format is unrecognized, skipping loading.");
return;
@@ -127,10 +127,14 @@ void ModDownloader::FetchModsListFromAPI()
std::string version = attribute["Version"].GetString();
std::string checksum = attribute["Checksum"].GetString();
- modVersions.insert({version, {.checksum = checksum}});
+ std::string downloadLink = attribute["DownloadLink"].GetString();
+ std::string platformValue = attribute["Platform"].GetString();
+ VerifiedModPlatform platform =
+ platformValue.compare("thunderstore") == 0 ? VerifiedModPlatform::Thunderstore : VerifiedModPlatform::Unknown;
+ modVersions.insert({version, {.checksum = checksum, .downloadLink = downloadLink, .platform = platform}});
}
- VerifiedModDetails modConfig = {.dependencyPrefix = dependency, .versions = modVersions};
+ VerifiedModDetails modConfig = {.versions = modVersions};
verifiedMods.insert({name, modConfig});
spdlog::info("==> Loaded configuration for mod \"" + name + "\"");
}
@@ -164,13 +168,10 @@ int ModDownloader::ModFetchingProgressCallback(
return 0;
}
-std::optional<fs::path> ModDownloader::FetchModFromDistantStore(std::string_view modName, std::string_view modVersion)
+std::optional<fs::path> ModDownloader::FetchModFromDistantStore(std::string_view modName, VerifiedModVersion version)
{
- // Retrieve mod prefix from local mods list, or use mod name as mod prefix if bypass flag is set
- std::string modPrefix = strstr(GetCommandLineA(), VERIFICATION_FLAG) ? modName.data() : verifiedMods[modName.data()].dependencyPrefix;
- // Build archive distant URI
- std::string archiveName = std::format("{}-{}.zip", modPrefix, modVersion.data());
- std::string url = STORE_URL + archiveName;
+ std::string url = version.downloadLink;
+ std::string archiveName = fs::path(url).filename().generic_string();
spdlog::info(std::format("Fetching mod archive from {}", url));
// Download destination
@@ -390,7 +391,7 @@ int GetModArchiveSize(unzFile file, unz_global_info64 info)
return totalSize;
}
-void ModDownloader::ExtractMod(fs::path modPath)
+void ModDownloader::ExtractMod(fs::path modPath, VerifiedModPlatform platform)
{
unzFile file;
std::string name;
@@ -428,6 +429,14 @@ void ModDownloader::ExtractMod(fs::path modPath)
modState.total = GetModArchiveSize(file, gi);
modState.progress = 0;
+ // Right now, we only know how to extract Thunderstore mods
+ if (platform != VerifiedModPlatform::Thunderstore)
+ {
+ spdlog::error("Failed extracting mod from unknown platform (value: {}).", platform);
+ modState.state = UNKNOWN_PLATFORM;
+ return;
+ }
+
// Mod directory name (removing the ".zip" fom the archive name)
name = modPath.filename().string();
name = name.substr(0, name.length() - 4);
@@ -598,8 +607,9 @@ void ModDownloader::DownloadMod(std::string modName, std::string modVersion)
});
// Download mod archive
- std::string expectedHash = verifiedMods[modName].versions[modVersion].checksum;
- std::optional<fs::path> fetchingResult = FetchModFromDistantStore(std::string_view(modName), std::string_view(modVersion));
+ VerifiedModVersion fullVersion = verifiedMods[modName].versions[modVersion];
+ std::string expectedHash = fullVersion.checksum;
+ std::optional<fs::path> fetchingResult = FetchModFromDistantStore(std::string_view(modName), fullVersion);
if (!fetchingResult.has_value())
{
spdlog::error("Something went wrong while fetching archive, aborting.");
@@ -615,7 +625,7 @@ void ModDownloader::DownloadMod(std::string modName, std::string modVersion)
}
// Extract downloaded mod archive
- ExtractMod(archiveLocation);
+ ExtractMod(archiveLocation, fullVersion.platform);
});
requestThread.detach();
diff --git a/primedev/mods/autodownload/moddownloader.h b/primedev/mods/autodownload/moddownloader.h
index 98fc27ae..c7a88c19 100644
--- a/primedev/mods/autodownload/moddownloader.h
+++ b/primedev/mods/autodownload/moddownloader.h
@@ -5,17 +5,22 @@ class ModDownloader
private:
const char* VERIFICATION_FLAG = "-disablemodverification";
const char* CUSTOM_MODS_URL_FLAG = "-customverifiedurl=";
- const char* STORE_URL = "https://gcdn.thunderstore.io/live/repository/packages/";
const char* DEFAULT_MODS_LIST_URL = "https://raw.githubusercontent.com/R2Northstar/VerifiedMods/main/verified-mods.json";
char* modsListUrl;
+ enum class VerifiedModPlatform
+ {
+ Unknown,
+ Thunderstore
+ };
struct VerifiedModVersion
{
std::string checksum;
+ std::string downloadLink;
+ VerifiedModPlatform platform;
};
struct VerifiedModDetails
{
- std::string dependencyPrefix;
std::unordered_map<std::string, VerifiedModVersion> versions = {};
};
std::unordered_map<std::string, VerifiedModDetails> verifiedMods = {};
@@ -45,7 +50,7 @@ private:
* @param modVersion version of the mod to be downloaded
* @returns location of the downloaded archive
*/
- std::optional<fs::path> FetchModFromDistantStore(std::string_view modName, std::string_view modVersion);
+ std::optional<fs::path> FetchModFromDistantStore(std::string_view modName, VerifiedModVersion modVersion);
/**
* Tells if a mod archive has not been corrupted.
@@ -65,12 +70,13 @@ private:
* Extracts a mod archive to the game folder.
*
* This extracts a downloaded mod archive from its original location to the
- * current game profile, in the remote mods folder.
+ * current game profile; the install folder is defined by the platform parameter.
*
* @param modPath location of the downloaded archive
+ * @param platform origin of the downloaded archive
* @returns nothing
*/
- void ExtractMod(fs::path modPath);
+ void ExtractMod(fs::path modPath, VerifiedModPlatform platform);
public:
ModDownloader();
@@ -131,7 +137,8 @@ public:
MOD_FETCHING_FAILED,
MOD_CORRUPTED, // Downloaded archive checksum does not match verified hash
NO_DISK_SPACE_AVAILABLE,
- NOT_FOUND // Mod is not currently being auto-downloaded
+ NOT_FOUND, // Mod is not currently being auto-downloaded
+ UNKNOWN_PLATFORM
};
struct MOD_STATE
diff --git a/primedev/mods/modmanager.cpp b/primedev/mods/modmanager.cpp
index cc6195c7..056ea136 100644
--- a/primedev/mods/modmanager.cpp
+++ b/primedev/mods/modmanager.cpp
@@ -836,7 +836,7 @@ void ModManager::LoadMods()
modVpk.m_sVpkPath = (file.path().parent_path() / vpkName).string();
if (m_bHasLoadedMods && modVpk.m_bAutoLoad)
- (*g_pFilesystem)->m_vtable->MountVPK(*g_pFilesystem, vpkName.c_str());
+ g_pFilesystem->m_vtable->MountVPK(g_pFilesystem, vpkName.c_str());
}
}
}
@@ -883,7 +883,9 @@ void ModManager::LoadMods()
if (fs::is_regular_file(file) && file.path().extension() == ".rpak")
{
std::string pakName(file.path().filename().string());
- ModRpakEntry& modPak = mod.Rpaks.emplace_back();
+ ModRpakEntry& modPak = mod.Rpaks.emplace_back(mod);
+
+ modPak.m_pakName = pakName;
if (!bUseRpakJson)
{
@@ -891,19 +893,47 @@ void ModManager::LoadMods()
}
else
{
- modPak.m_bAutoLoad =
+ modPak.m_preload =
(dRpakJson.HasMember("Preload") && dRpakJson["Preload"].IsObject() && dRpakJson["Preload"].HasMember(pakName) &&
dRpakJson["Preload"][pakName].IsTrue());
+ // only one load method can be used for an rpak.
+ if (modPak.m_preload)
+ goto REGISTER_STARPAK;
+
// postload things
if (dRpakJson.HasMember("Postload") && dRpakJson["Postload"].IsObject() && dRpakJson["Postload"].HasMember(pakName))
{
- modPak.m_sLoadAfterPak = dRpakJson["Postload"][pakName].GetString();
+ modPak.m_dependentPakHash = STR_HASH(dRpakJson["Postload"][pakName].GetString());
+
+ // only one load method can be used for an rpak.
+ goto REGISTER_STARPAK;
}
- }
- modPak.m_sPakName = pakName;
+ // this is the only bit of rpak.json that isn't really deprecated. Even so, it will be moved over to the mod.json
+ // eventually
+ if (dRpakJson.HasMember(pakName))
+ {
+ if (!dRpakJson[pakName].IsString())
+ {
+ spdlog::error("Mod {} has invalid rpak.json. Rpak entries must be strings.", mod.Name);
+ continue;
+ }
+
+ std::string loadStr = dRpakJson[pakName].GetString();
+ try
+ {
+ modPak.m_loadRegex = std::regex(loadStr);
+ }
+ catch (...)
+ {
+ spdlog::error("Mod {} has invalid rpak.json. Malformed regex \"{}\" for {}", mod.Name, loadStr, pakName);
+ return;
+ }
+ }
+ }
+ REGISTER_STARPAK:
// read header of file and get the starpak paths
// this is done here as opposed to on starpak load because multiple rpaks can load a starpak
// and there is seemingly no good way to tell which rpak is causing the load of a starpak :/
@@ -943,12 +973,11 @@ void ModManager::LoadMods()
}
}
}
-
- // not using atm because we need to resolve path to rpak
- // if (m_hasLoadedMods && modPak.m_bAutoLoad)
- // g_pPakLoadManager->LoadPakAsync(pakName.c_str());
}
}
+
+ if (g_pPakLoadManager != nullptr)
+ g_pPakLoadManager->TrackModPaks(mod);
}
// read keyvalues paths
@@ -1076,6 +1105,8 @@ void ModManager::UnloadMods()
fs::remove_all(GetCompiledAssetsPath());
g_CustomAudioManager.ClearAudioOverrides();
+ if (g_pPakLoadManager != nullptr)
+ g_pPakLoadManager->UnloadAllModPaks();
if (!m_bHasEnabledModsCfg)
m_EnabledModsCfg.SetObject();
diff --git a/primedev/mods/modmanager.h b/primedev/mods/modmanager.h
index 3df7fe53..68a7c5d0 100644
--- a/primedev/mods/modmanager.h
+++ b/primedev/mods/modmanager.h
@@ -8,6 +8,7 @@
#include <vector>
#include <filesystem>
#include <unordered_set>
+#include <regex>
namespace fs = std::filesystem;
@@ -20,6 +21,8 @@ const std::string COMPILED_ASSETS_SUFFIX = "\\runtime\\compiled";
const std::set<std::string> MODS_BLACKLIST = {"Mod Settings"};
+class Mod;
+
struct ModConVar
{
public:
@@ -72,9 +75,22 @@ public:
struct ModRpakEntry
{
public:
- bool m_bAutoLoad;
- std::string m_sPakName;
- std::string m_sLoadAfterPak;
+ ModRpakEntry(Mod& parent)
+ : m_parent(parent)
+ , m_loadRegex("^thisMatchesNothing^") // discord couldnt give me a funny string
+ {
+ }
+
+ Mod& m_parent;
+ std::string m_pakName;
+ std::regex m_loadRegex;
+
+ // these exist purely for backwards compatibility, i don't really like them anymore
+
+ // Preload, loads before the first rpak is loaded
+ bool m_preload = false;
+ // Postload, this rpak depends on an rpak with this hash
+ size_t m_dependentPakHash;
};
class Mod
@@ -121,11 +137,12 @@ public:
std::string Pdiff; // only need one per mod
std::vector<ModRpakEntry> Rpaks;
- std::unordered_map<std::string, std::string>
- RpakAliases; // paks we alias to other rpaks, e.g. to load sp_crashsite paks on the map mp_crashsite
- std::vector<size_t> StarpakPaths; // starpaks that this mod contains
+ // paks we alias to other rpaks, e.g. to load sp_crashsite paks on the map mp_crashsite
+ std::unordered_map<std::string, std::string> RpakAliases;
+ // starpaks that this mod contains
// there seems to be no nice way to get the rpak that is causing the load of a starpak?
// hashed with STR_HASH
+ std::vector<size_t> StarpakPaths;
std::unordered_map<std::string, std::string> DependencyConstants;
std::vector<std::string> PluginDependencyConstants;
diff --git a/primedev/mods/modsavefiles.cpp b/primedev/mods/modsavefiles.cpp
index 68e33864..13239c99 100644
--- a/primedev/mods/modsavefiles.cpp
+++ b/primedev/mods/modsavefiles.cpp
@@ -537,7 +537,7 @@ ADD_SQFUNC("int", NSGetTotalSpaceRemaining, "", "", ScriptContext::CLIENT | Scri
// ok, I'm just gonna explain what the fuck is going on here because this
// is the pinnacle of my stupidity and I do not want to touch this ever
// again, yet someone will eventually have to maintain this.
-template <ScriptContext context> std::string EncodeJSON(HSquirrelVM* sqvm)
+template <ScriptContext context> std::string EncodeJSON(HSQUIRRELVM sqvm)
{
// new rapidjson
rapidjson_document doc;