From 49fee1abd89cb82d9b66a0eae3a3711a4e15d76b Mon Sep 17 00:00:00 2001 From: Remy Raes Date: Sun, 7 Jul 2024 02:07:31 +0200 Subject: [PATCH 1/6] docs: add TODO notes --- primedev/mods/autodownload/moddownloader.cpp | 1 + primedev/mods/autodownload/moddownloader.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/primedev/mods/autodownload/moddownloader.cpp b/primedev/mods/autodownload/moddownloader.cpp index 3a9462630..b713a7945 100644 --- a/primedev/mods/autodownload/moddownloader.cpp +++ b/primedev/mods/autodownload/moddownloader.cpp @@ -140,6 +140,7 @@ int ModDownloader::ModFetchingProgressCallback( return 0; } +// TODO use link from mod version std::optional ModDownloader::FetchModFromDistantStore(std::string_view modName, std::string_view modVersion) { // Retrieve mod prefix from local mods list, or use mod name as mod prefix if bypass flag is set diff --git a/primedev/mods/autodownload/moddownloader.h b/primedev/mods/autodownload/moddownloader.h index 10df39ceb..51468af31 100644 --- a/primedev/mods/autodownload/moddownloader.h +++ b/primedev/mods/autodownload/moddownloader.h @@ -3,10 +3,13 @@ class ModDownloader private: const char* VERIFICATION_FLAG = "-disablemodverification"; const char* CUSTOM_MODS_URL_FLAG = "-customverifiedurl="; + // TODO remove 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; + // TODO create platform enum + // TODO complete below strucs struct VerifiedModVersion { std::string checksum; From 66d1e30e2d94b5a2cc88134c5e83ac29f97d184c Mon Sep 17 00:00:00 2001 From: Remy Raes Date: Mon, 8 Jul 2024 01:32:12 +0200 Subject: [PATCH 2/6] feat: complete VerifiedModVersion struct and use it in implementation --- primedev/mods/autodownload/moddownloader.cpp | 25 +++++++++++++------- primedev/mods/autodownload/moddownloader.h | 12 ++++++---- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/primedev/mods/autodownload/moddownloader.cpp b/primedev/mods/autodownload/moddownloader.cpp index b713a7945..4e62cdfc0 100644 --- a/primedev/mods/autodownload/moddownloader.cpp +++ b/primedev/mods/autodownload/moddownloader.cpp @@ -93,9 +93,8 @@ void ModDownloader::FetchModsListFromAPI() for (auto i = verifiedModsJson.MemberBegin(); i != verifiedModsJson.MemberEnd(); ++i) { std::string name = i->name.GetString(); - std::string dependency = i->value["DependencyPrefix"].GetString(); - std::unordered_map modVersions; + rapidjson::Value& versions = i->value["Versions"]; assert(versions.IsArray()); for (auto& attribute : versions.GetArray()) @@ -103,10 +102,14 @@ void ModDownloader::FetchModsListFromAPI() assert(attribute.IsObject()); std::string version = attribute["Version"].GetString(); std::string checksum = attribute["Checksum"].GetString(); - modVersions.insert({version, {.checksum = checksum}}); + std::string downloadLink = attribute["DownloadLink"].GetString(); + VerifiedModPlatform platform = strcmp(attribute["DownloadLink"].GetString(), "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 + "\""); } @@ -140,14 +143,17 @@ int ModDownloader::ModFetchingProgressCallback( return 0; } -// TODO use link from mod version -std::optional ModDownloader::FetchModFromDistantStore(std::string_view modName, std::string_view modVersion) +std::optional 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 = 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 @@ -575,8 +581,9 @@ void ModDownloader::DownloadMod(std::string modName, std::string modVersion) }); // Download mod archive - std::string expectedHash = verifiedMods[modName].versions[modVersion].checksum; - std::optional fetchingResult = FetchModFromDistantStore(std::string_view(modName), std::string_view(modVersion)); + VerifiedModVersion fullVersion = verifiedMods[modName].versions[modVersion]; + std::string expectedHash = fullVersion.checksum; + std::optional fetchingResult = FetchModFromDistantStore(std::string_view(modName), fullVersion); if (!fetchingResult.has_value()) { spdlog::error("Something went wrong while fetching archive, aborting."); diff --git a/primedev/mods/autodownload/moddownloader.h b/primedev/mods/autodownload/moddownloader.h index 51468af31..a8e1cc7df 100644 --- a/primedev/mods/autodownload/moddownloader.h +++ b/primedev/mods/autodownload/moddownloader.h @@ -8,15 +8,19 @@ class ModDownloader const char* DEFAULT_MODS_LIST_URL = "https://raw.githubusercontent.com/R2Northstar/VerifiedMods/main/verified-mods.json"; char* modsListUrl; - // TODO create platform enum - // TODO complete below strucs + enum class VerifiedModPlatform + { + Thunderstore, + Unknown + }; struct VerifiedModVersion { std::string checksum; + std::string downloadLink; + VerifiedModPlatform platform; }; struct VerifiedModDetails { - std::string dependencyPrefix; std::unordered_map versions = {}; }; std::unordered_map verifiedMods = {}; @@ -46,7 +50,7 @@ class ModDownloader * @param modVersion version of the mod to be downloaded * @returns location of the downloaded archive */ - std::optional FetchModFromDistantStore(std::string_view modName, std::string_view modVersion); + std::optional FetchModFromDistantStore(std::string_view modName, VerifiedModVersion modVersion); /** * Tells if a mod archive has not been corrupted. From 04d0b5bd111ca0d631c06bdc9b827dd8ad595551 Mon Sep 17 00:00:00 2001 From: Remy Raes Date: Mon, 8 Jul 2024 01:43:55 +0200 Subject: [PATCH 3/6] refactor: remove unused STORE_URL member Each mod version now having its own download link, having a global Thunderstore URL is therefore useless. --- primedev/mods/autodownload/moddownloader.cpp | 7 ------- primedev/mods/autodownload/moddownloader.h | 2 -- 2 files changed, 9 deletions(-) diff --git a/primedev/mods/autodownload/moddownloader.cpp b/primedev/mods/autodownload/moddownloader.cpp index 4e62cdfc0..a23dac126 100644 --- a/primedev/mods/autodownload/moddownloader.cpp +++ b/primedev/mods/autodownload/moddownloader.cpp @@ -145,13 +145,6 @@ int ModDownloader::ModFetchingProgressCallback( std::optional 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)); diff --git a/primedev/mods/autodownload/moddownloader.h b/primedev/mods/autodownload/moddownloader.h index a8e1cc7df..a5d421c64 100644 --- a/primedev/mods/autodownload/moddownloader.h +++ b/primedev/mods/autodownload/moddownloader.h @@ -3,8 +3,6 @@ class ModDownloader private: const char* VERIFICATION_FLAG = "-disablemodverification"; const char* CUSTOM_MODS_URL_FLAG = "-customverifiedurl="; - // TODO remove - 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; From e5e56ceb56ae1d1b19e7336a5d7cd191cca1ddb8 Mon Sep 17 00:00:00 2001 From: Remy Raes Date: Mon, 8 Jul 2024 18:38:40 +0200 Subject: [PATCH 4/6] feat: throw an error when extracting non-Thunderstore mods At the time, we only know how to install mods from Thunderstore, so we throw an error when extracting mods from elsewhere. --- primedev/mods/autodownload/moddownloader.cpp | 15 ++++++++++++--- primedev/mods/autodownload/moddownloader.h | 12 +++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/primedev/mods/autodownload/moddownloader.cpp b/primedev/mods/autodownload/moddownloader.cpp index a23dac126..5050d5aca 100644 --- a/primedev/mods/autodownload/moddownloader.cpp +++ b/primedev/mods/autodownload/moddownloader.cpp @@ -103,7 +103,8 @@ void ModDownloader::FetchModsListFromAPI() std::string version = attribute["Version"].GetString(); std::string checksum = attribute["Checksum"].GetString(); std::string downloadLink = attribute["DownloadLink"].GetString(); - VerifiedModPlatform platform = strcmp(attribute["DownloadLink"].GetString(), "thunderstore") == 0 + 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}}); @@ -366,7 +367,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; @@ -404,6 +405,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); @@ -592,7 +601,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 a5d421c64..f89d728c9 100644 --- a/primedev/mods/autodownload/moddownloader.h +++ b/primedev/mods/autodownload/moddownloader.h @@ -8,8 +8,8 @@ class ModDownloader enum class VerifiedModPlatform { - Thunderstore, - Unknown + Unknown, + Thunderstore }; struct VerifiedModVersion { @@ -68,12 +68,13 @@ class ModDownloader * 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(); @@ -132,7 +133,8 @@ class ModDownloader 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 From 2fe4d86ec388b9a8001cb74c1fb6c7a67f0e2da6 Mon Sep 17 00:00:00 2001 From: Remy Raes Date: Mon, 8 Jul 2024 19:07:31 +0200 Subject: [PATCH 5/6] style: apply clang formatting --- primedev/mods/autodownload/moddownloader.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/primedev/mods/autodownload/moddownloader.cpp b/primedev/mods/autodownload/moddownloader.cpp index 5050d5aca..c6fb726b8 100644 --- a/primedev/mods/autodownload/moddownloader.cpp +++ b/primedev/mods/autodownload/moddownloader.cpp @@ -104,9 +104,8 @@ void ModDownloader::FetchModsListFromAPI() std::string checksum = attribute["Checksum"].GetString(); std::string downloadLink = attribute["DownloadLink"].GetString(); std::string platformValue = attribute["Platform"].GetString(); - VerifiedModPlatform platform = platformValue.compare("thunderstore") == 0 - ? VerifiedModPlatform::Thunderstore - : VerifiedModPlatform::Unknown; + VerifiedModPlatform platform = + platformValue.compare("thunderstore") == 0 ? VerifiedModPlatform::Thunderstore : VerifiedModPlatform::Unknown; modVersions.insert({version, {.checksum = checksum, .downloadLink = downloadLink, .platform = platform}}); } From ca9587b3a717e651479f83f512fc934ade9a3f08 Mon Sep 17 00:00:00 2001 From: Remy Raes Date: Tue, 30 Jul 2024 19:45:08 +0200 Subject: [PATCH 6/6] fix: update manifesto format checking --- primedev/mods/autodownload/moddownloader.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/primedev/mods/autodownload/moddownloader.cpp b/primedev/mods/autodownload/moddownloader.cpp index 6947d2714..c20a3adbc 100644 --- a/primedev/mods/autodownload/moddownloader.cpp +++ b/primedev/mods/autodownload/moddownloader.cpp @@ -103,7 +103,7 @@ 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; @@ -118,7 +118,8 @@ void ModDownloader::FetchModsListFromAPI() { 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;