Skip to content

Commit

Permalink
Adjust for restructured verified-mods.json (#748)
Browse files Browse the repository at this point in the history
Updates the launcher code to deal with adjusted verified mods JSON structure from the default manifest source
The idea here is to allow installing mods from other sources than Thunderstore.
  • Loading branch information
Alystrasz committed Sep 7, 2024
1 parent 160f503 commit 8c546ed
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 22 deletions.
42 changes: 26 additions & 16 deletions primedev/mods/autodownload/moddownloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,34 +103,38 @@ 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;
}

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 + "\"");
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.");
Expand All @@ -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();
Expand Down
19 changes: 13 additions & 6 deletions primedev/mods/autodownload/moddownloader.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};
Expand Down Expand Up @@ -45,7 +50,7 @@ class ModDownloader
* @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.
Expand All @@ -65,12 +70,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();
Expand Down Expand Up @@ -131,7 +137,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
Expand Down

0 comments on commit 8c546ed

Please sign in to comment.