Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adjust for restructured verified-mods.json #748

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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