mirror of
https://github.com/R2Northstar/NorthstarLauncher
synced 2026-08-23 20:26:04 -04:00
feat: Add ModWorkshop support to MAD (#873)
* feat: add ModWorkshop support for MAD
This commit is contained in:
parent
f5bd7c9b95
commit
c0ed69f795
3 changed files with 77 additions and 14 deletions
|
|
@ -129,8 +129,7 @@ void ModDownloader::FetchModsListFromAPI()
|
||||||
std::string checksum = attribute["Checksum"].GetString();
|
std::string checksum = attribute["Checksum"].GetString();
|
||||||
std::string downloadLink = attribute["DownloadLink"].GetString();
|
std::string downloadLink = attribute["DownloadLink"].GetString();
|
||||||
std::string platformValue = attribute["Platform"].GetString();
|
std::string platformValue = attribute["Platform"].GetString();
|
||||||
VerifiedModPlatform platform =
|
VerifiedModPlatform platform = resolvePlatform(platformValue);
|
||||||
platformValue.compare("thunderstore") == 0 ? VerifiedModPlatform::Thunderstore : VerifiedModPlatform::Unknown;
|
|
||||||
modVersions.insert({version, {.checksum = checksum, .downloadLink = downloadLink, .platform = platform}});
|
modVersions.insert({version, {.checksum = checksum, .downloadLink = downloadLink, .platform = platform}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,13 +175,28 @@ int ModDownloader::ModFetchingProgressCallback(
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ModDownloader::GetModArchiveName(std::string url)
|
||||||
|
{
|
||||||
|
std::string name = fs::path(url).filename().generic_string();
|
||||||
|
std::string::size_type charIndex = name.find("?");
|
||||||
|
|
||||||
|
// Thunderstore format
|
||||||
|
if (std::string::npos == charIndex)
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModWorkshop format (removing the "?filename=" part)
|
||||||
|
return name.substr(charIndex + 10);
|
||||||
|
}
|
||||||
|
|
||||||
std::tuple<fs::path, bool> ModDownloader::FetchModFromDistantStore(std::string_view modName, VerifiedModVersion version)
|
std::tuple<fs::path, bool> ModDownloader::FetchModFromDistantStore(std::string_view modName, VerifiedModVersion version)
|
||||||
{
|
{
|
||||||
std::string url = version.downloadLink;
|
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
|
// Download destination
|
||||||
|
spdlog::info(std::format("Fetching mod archive from {}", url));
|
||||||
|
std::string archiveName = ModDownloader::GetModArchiveName(url);
|
||||||
std::filesystem::path downloadPath = std::filesystem::temp_directory_path() / archiveName;
|
std::filesystem::path downloadPath = std::filesystem::temp_directory_path() / archiveName;
|
||||||
spdlog::info(std::format("Downloading archive to {}", downloadPath.generic_string()));
|
spdlog::info(std::format("Downloading archive to {}", downloadPath.generic_string()));
|
||||||
|
|
||||||
|
|
@ -436,10 +450,10 @@ void ModDownloader::ExtractMod(fs::path modPath, fs::path destinationPath, Verif
|
||||||
modState.total = GetModArchiveSize(file, gi);
|
modState.total = GetModArchiveSize(file, gi);
|
||||||
modState.progress = 0;
|
modState.progress = 0;
|
||||||
|
|
||||||
// Right now, we only know how to extract Thunderstore mods
|
// We don't know how to extract mods from unknown platforms
|
||||||
if (platform != VerifiedModPlatform::Thunderstore)
|
if (platform == VerifiedModPlatform::Unknown)
|
||||||
{
|
{
|
||||||
spdlog::error("Failed extracting mod from unknown platform (value: {}).", platform);
|
spdlog::error("Failed extracting mod from unknown platform.");
|
||||||
modState.state = UNKNOWN_PLATFORM;
|
modState.state = UNKNOWN_PLATFORM;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -658,10 +672,19 @@ void ModDownloader::DownloadMod(std::string modName, std::string modVersion)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mod directory name (removing the ".zip" fom the archive name)
|
// Mod directory name
|
||||||
name = archiveLocation.filename().string();
|
/// Don't use archive name as destination with ModWorkshop
|
||||||
name = name.substr(0, name.length() - 4);
|
if (fullVersion.platform == VerifiedModPlatform::ModWorkshop)
|
||||||
modDirectory = GetRemoteModFolderPath() / name;
|
{
|
||||||
|
modDirectory = GetRemoteModFolderPath();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
/// Removes the ".zip" fom the archive name and use it as parent directory
|
||||||
|
{
|
||||||
|
name = archiveLocation.filename().string();
|
||||||
|
name = name.substr(0, name.length() - 4);
|
||||||
|
modDirectory = GetRemoteModFolderPath() / name;
|
||||||
|
}
|
||||||
|
|
||||||
// Extract downloaded mod archive
|
// Extract downloaded mod archive
|
||||||
ExtractMod(archiveLocation, modDirectory, fullVersion.platform);
|
ExtractMod(archiveLocation, modDirectory, fullVersion.platform);
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,21 @@ private:
|
||||||
enum class VerifiedModPlatform
|
enum class VerifiedModPlatform
|
||||||
{
|
{
|
||||||
Unknown,
|
Unknown,
|
||||||
|
ModWorkshop,
|
||||||
Thunderstore
|
Thunderstore
|
||||||
};
|
};
|
||||||
|
VerifiedModPlatform resolvePlatform(std::string input)
|
||||||
|
{
|
||||||
|
if (input.compare("thunderstore") == 0)
|
||||||
|
{
|
||||||
|
return VerifiedModPlatform::Thunderstore;
|
||||||
|
}
|
||||||
|
if (input.compare("modworkshop") == 0)
|
||||||
|
{
|
||||||
|
return VerifiedModPlatform::ModWorkshop;
|
||||||
|
}
|
||||||
|
return VerifiedModPlatform::Unknown;
|
||||||
|
}
|
||||||
struct VerifiedModVersion
|
struct VerifiedModVersion
|
||||||
{
|
{
|
||||||
std::string checksum;
|
std::string checksum;
|
||||||
|
|
@ -78,6 +91,22 @@ private:
|
||||||
*/
|
*/
|
||||||
void ExtractMod(fs::path modPath, fs::path destinationPath, VerifiedModPlatform platform);
|
void ExtractMod(fs::path modPath, fs::path destinationPath, VerifiedModPlatform platform);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves archive name from input URL.
|
||||||
|
*
|
||||||
|
* Thunderstore and ModWorkshop do not format their URLs the same way:
|
||||||
|
* - Thunderstore: https://gcdn.thunderstore.io/live/repository/packages/Nyami11-mp_brick-1.0.2.zip
|
||||||
|
* - ModWorkshop:
|
||||||
|
* https://storage.modworkshop.net/mods/files/46563_156759_CSio1Cd3QVXYRZLd1h6iNu0IPuuaY9ePKGceQD31.zip?filename=em4v.zip
|
||||||
|
*
|
||||||
|
* This takes those differences into account and returns an archive name for
|
||||||
|
* both types of URLs.
|
||||||
|
*
|
||||||
|
* @param url URL from which to extract archive file name
|
||||||
|
* @returns name of the archive to be downloaded
|
||||||
|
*/
|
||||||
|
std::string GetModArchiveName(std::string url);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ModDownloader();
|
ModDownloader();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -448,19 +448,30 @@ void ModManager::SearchFilesystemForMods()
|
||||||
std::filesystem::directory_iterator remoteModsDir = fs::directory_iterator(GetRemoteModFolderPath());
|
std::filesystem::directory_iterator remoteModsDir = fs::directory_iterator(GetRemoteModFolderPath());
|
||||||
std::filesystem::directory_iterator thunderstoreModsDir = fs::directory_iterator(GetThunderstoreModFolderPath());
|
std::filesystem::directory_iterator thunderstoreModsDir = fs::directory_iterator(GetThunderstoreModFolderPath());
|
||||||
|
|
||||||
for (fs::directory_entry dir : classicModsDir)
|
for (fs::directory_iterator dirIterator : {classicModsDir, remoteModsDir})
|
||||||
if (fs::exists(dir.path() / "mod.json"))
|
for (fs::directory_entry dir : dirIterator)
|
||||||
modDirs.push_back(dir.path());
|
if (fs::exists(dir.path() / "mod.json"))
|
||||||
|
modDirs.push_back(dir.path());
|
||||||
|
|
||||||
// Special case for Thunderstore and remote mods directories
|
// Special case for Thunderstore and remote mods directories
|
||||||
// Set up regex for `AUTHOR-MOD-VERSION` pattern
|
// Set up regex for `AUTHOR-MOD-VERSION` pattern
|
||||||
std::regex pattern(R"(.*\\([a-zA-Z0-9_]+)-([a-zA-Z0-9_]+)-(\d+\.\d+\.\d+))");
|
std::regex pattern(R"(.*\\([a-zA-Z0-9_]+)-([a-zA-Z0-9_]+)-(\d+\.\d+\.\d+))");
|
||||||
|
|
||||||
|
// Reset directory iterator
|
||||||
|
remoteModsDir = fs::directory_iterator(GetRemoteModFolderPath());
|
||||||
|
|
||||||
for (fs::directory_iterator dirIterator : {thunderstoreModsDir, remoteModsDir})
|
for (fs::directory_iterator dirIterator : {thunderstoreModsDir, remoteModsDir})
|
||||||
{
|
{
|
||||||
for (fs::directory_entry dir : dirIterator)
|
for (fs::directory_entry dir : dirIterator)
|
||||||
{
|
{
|
||||||
fs::path modsDir = dir.path() / "mods"; // Check for mods folder in the Thunderstore mod
|
fs::path modsDir = dir.path() / "mods"; // Check for mods folder in the Thunderstore mod
|
||||||
|
|
||||||
|
// Do not register ModWorkshop mods twice
|
||||||
|
if (std::find(modDirs.begin(), modDirs.end(), dir.path()) != modDirs.end())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Use regex to match `AUTHOR-MOD-VERSION` pattern
|
// Use regex to match `AUTHOR-MOD-VERSION` pattern
|
||||||
if (!std::regex_match(dir.path().string(), pattern))
|
if (!std::regex_match(dir.path().string(), pattern))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue