summaryrefslogtreecommitdiff
path: root/launcher/InstanceImportTask.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'launcher/InstanceImportTask.cpp')
-rw-r--r--launcher/InstanceImportTask.cpp1144
1 files changed, 549 insertions, 595 deletions
diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp
index 72d38d1a51..df569edc6c 100644
--- a/launcher/InstanceImportTask.cpp
+++ b/launcher/InstanceImportTask.cpp
@@ -65,656 +65,610 @@
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl)
{
- m_sourceUrl = sourceUrl;
+ m_sourceUrl = sourceUrl;
}
void InstanceImportTask::executeTask()
{
- if (m_sourceUrl.isLocalFile())
- {
- m_archivePath = m_sourceUrl.toLocalFile();
- processZipPack();
- }
- else
- {
- setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
- m_downloadRequired = true;
-
- const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
- auto entry = APPLICATION->metacache()->resolveEntry("general", path);
- entry->setStale(true);
- m_filesNetJob = new NetJob(tr("Modpack download"), APPLICATION->network());
- m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
- m_archivePath = entry->getFullPath();
- auto job = m_filesNetJob.get();
- connect(job, &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
- connect(job, &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
- connect(job, &NetJob::failed, this, &InstanceImportTask::downloadFailed);
- m_filesNetJob->start();
- }
+ if (m_sourceUrl.isLocalFile()) {
+ m_archivePath = m_sourceUrl.toLocalFile();
+ processZipPack();
+ } else {
+ setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
+ m_downloadRequired = true;
+
+ const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
+ auto entry = APPLICATION->metacache()->resolveEntry("general", path);
+ entry->setStale(true);
+ m_filesNetJob =
+ new NetJob(tr("Modpack download"), APPLICATION->network());
+ m_filesNetJob->addNetAction(
+ Net::Download::makeCached(m_sourceUrl, entry));
+ m_archivePath = entry->getFullPath();
+ auto job = m_filesNetJob.get();
+ connect(job, &NetJob::succeeded, this,
+ &InstanceImportTask::downloadSucceeded);
+ connect(job, &NetJob::progress, this,
+ &InstanceImportTask::downloadProgressChanged);
+ connect(job, &NetJob::failed, this,
+ &InstanceImportTask::downloadFailed);
+ m_filesNetJob->start();
+ }
}
void InstanceImportTask::downloadSucceeded()
{
- processZipPack();
- m_filesNetJob.reset();
+ processZipPack();
+ m_filesNetJob.reset();
}
void InstanceImportTask::downloadFailed(QString reason)
{
- emitFailed(reason);
- m_filesNetJob.reset();
+ emitFailed(reason);
+ m_filesNetJob.reset();
}
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
{
- setProgress(current / 2, total);
+ setProgress(current / 2, total);
}
void InstanceImportTask::processZipPack()
{
- setStatus(tr("Extracting modpack"));
- QDir extractDir(m_stagingPath);
- qDebug() << "Attempting to create instance from" << m_archivePath;
-
- // find relevant files in the zip
- QStringList blacklist = {"instance.cfg", "manifest.json", "modrinth.index.json"};
- QString mmcFound = MMCZip::findFolderOfFileInZip(m_archivePath, "instance.cfg");
- bool technicFound = MMCZip::entryExists(m_archivePath, "bin/modpack.jar") ||
- MMCZip::entryExists(m_archivePath, "bin/version.json");
- QString flameFound = MMCZip::findFolderOfFileInZip(m_archivePath, "manifest.json");
- QString modrinthFound = MMCZip::findFolderOfFileInZip(m_archivePath, "modrinth.index.json");
- QString root;
- if(!mmcFound.isNull())
- {
- // process as MeshMC instance/pack
- qDebug() << "MeshMC:" << mmcFound;
- root = mmcFound;
- m_modpackType = ModpackType::MeshMC;
- }
- else if(!modrinthFound.isNull())
- {
- // process as Modrinth pack
- qDebug() << "Modrinth:" << modrinthFound;
- root = modrinthFound;
- m_modpackType = ModpackType::Modrinth;
- }
- else if (technicFound)
- {
- // process as Technic pack
- qDebug() << "Technic:" << technicFound;
- extractDir.mkpath(".minecraft");
- extractDir.cd(".minecraft");
- m_modpackType = ModpackType::Technic;
- }
- else if(!flameFound.isNull())
- {
- // process as Flame pack
- qDebug() << "Flame:" << flameFound;
- root = flameFound;
- m_modpackType = ModpackType::Flame;
- }
- if(m_modpackType == ModpackType::Unknown)
- {
- emitFailed(tr("Archive does not contain a recognized modpack type."));
- return;
- }
-
- // make sure we extract just the pack
- QString archivePath = m_archivePath;
- m_extractFuture = QtConcurrent::run(
- QThreadPool::globalInstance(), MMCZip::extractSubDir,
- archivePath, root, extractDir.absolutePath());
- connect(&m_extractFutureWatcher,
- &QFutureWatcher<QStringList>::finished,
- this, &InstanceImportTask::extractFinished);
- connect(&m_extractFutureWatcher,
- &QFutureWatcher<QStringList>::canceled,
- this, &InstanceImportTask::extractAborted);
- m_extractFutureWatcher.setFuture(m_extractFuture);
+ setStatus(tr("Extracting modpack"));
+ QDir extractDir(m_stagingPath);
+ qDebug() << "Attempting to create instance from" << m_archivePath;
+
+ // find relevant files in the zip
+ QStringList blacklist = {"instance.cfg", "manifest.json",
+ "modrinth.index.json"};
+ QString mmcFound =
+ MMCZip::findFolderOfFileInZip(m_archivePath, "instance.cfg");
+ bool technicFound = MMCZip::entryExists(m_archivePath, "bin/modpack.jar") ||
+ MMCZip::entryExists(m_archivePath, "bin/version.json");
+ QString flameFound =
+ MMCZip::findFolderOfFileInZip(m_archivePath, "manifest.json");
+ QString modrinthFound =
+ MMCZip::findFolderOfFileInZip(m_archivePath, "modrinth.index.json");
+ QString root;
+ if (!mmcFound.isNull()) {
+ // process as MeshMC instance/pack
+ qDebug() << "MeshMC:" << mmcFound;
+ root = mmcFound;
+ m_modpackType = ModpackType::MeshMC;
+ } else if (!modrinthFound.isNull()) {
+ // process as Modrinth pack
+ qDebug() << "Modrinth:" << modrinthFound;
+ root = modrinthFound;
+ m_modpackType = ModpackType::Modrinth;
+ } else if (technicFound) {
+ // process as Technic pack
+ qDebug() << "Technic:" << technicFound;
+ extractDir.mkpath(".minecraft");
+ extractDir.cd(".minecraft");
+ m_modpackType = ModpackType::Technic;
+ } else if (!flameFound.isNull()) {
+ // process as Flame pack
+ qDebug() << "Flame:" << flameFound;
+ root = flameFound;
+ m_modpackType = ModpackType::Flame;
+ }
+ if (m_modpackType == ModpackType::Unknown) {
+ emitFailed(tr("Archive does not contain a recognized modpack type."));
+ return;
+ }
+
+ // make sure we extract just the pack
+ QString archivePath = m_archivePath;
+ m_extractFuture =
+ QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir,
+ archivePath, root, extractDir.absolutePath());
+ connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished,
+ this, &InstanceImportTask::extractFinished);
+ connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled,
+ this, &InstanceImportTask::extractAborted);
+ m_extractFutureWatcher.setFuture(m_extractFuture);
}
void InstanceImportTask::extractFinished()
{
- if (!m_extractFuture.result())
- {
- emitFailed(tr("Failed to extract modpack"));
- return;
- }
- QDir extractDir(m_stagingPath);
-
- qDebug() << "Fixing permissions for extracted pack files...";
- QDirIterator it(extractDir, QDirIterator::Subdirectories);
- while (it.hasNext())
- {
- auto filepath = it.next();
- QFileInfo file(filepath);
- auto permissions = QFile::permissions(filepath);
- auto origPermissions = permissions;
- if(file.isDir())
- {
- // Folder +rwx for current user
- permissions |= QFileDevice::Permission::ReadUser |
- QFileDevice::Permission::WriteUser |
- QFileDevice::Permission::ExeUser;
- }
- else
- {
- // File +rw for current user
- permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
- }
- if(origPermissions != permissions)
- {
- if(!QFile::setPermissions(filepath, permissions))
- {
- logWarning(tr("Could not fix permissions for %1").arg(filepath));
- }
- else
- {
- qDebug() << "Fixed" << filepath;
- }
- }
- }
-
- switch(m_modpackType)
- {
- case ModpackType::Flame:
- processFlame();
- return;
- case ModpackType::Modrinth:
- processModrinth();
- return;
- case ModpackType::MeshMC:
- processMeshMC();
- return;
- case ModpackType::Technic:
- processTechnic();
- return;
- case ModpackType::Unknown:
- emitFailed(tr("Archive does not contain a recognized modpack type."));
- return;
- }
+ if (!m_extractFuture.result()) {
+ emitFailed(tr("Failed to extract modpack"));
+ return;
+ }
+ QDir extractDir(m_stagingPath);
+
+ qDebug() << "Fixing permissions for extracted pack files...";
+ QDirIterator it(extractDir, QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ auto filepath = it.next();
+ QFileInfo file(filepath);
+ auto permissions = QFile::permissions(filepath);
+ auto origPermissions = permissions;
+ if (file.isDir()) {
+ // Folder +rwx for current user
+ permissions |= QFileDevice::Permission::ReadUser |
+ QFileDevice::Permission::WriteUser |
+ QFileDevice::Permission::ExeUser;
+ } else {
+ // File +rw for current user
+ permissions |= QFileDevice::Permission::ReadUser |
+ QFileDevice::Permission::WriteUser;
+ }
+ if (origPermissions != permissions) {
+ if (!QFile::setPermissions(filepath, permissions)) {
+ logWarning(
+ tr("Could not fix permissions for %1").arg(filepath));
+ } else {
+ qDebug() << "Fixed" << filepath;
+ }
+ }
+ }
+
+ switch (m_modpackType) {
+ case ModpackType::Flame:
+ processFlame();
+ return;
+ case ModpackType::Modrinth:
+ processModrinth();
+ return;
+ case ModpackType::MeshMC:
+ processMeshMC();
+ return;
+ case ModpackType::Technic:
+ processTechnic();
+ return;
+ case ModpackType::Unknown:
+ emitFailed(
+ tr("Archive does not contain a recognized modpack type."));
+ return;
+ }
}
void InstanceImportTask::extractAborted()
{
- emitFailed(tr("Instance import has been aborted."));
- return;
+ emitFailed(tr("Instance import has been aborted."));
+ return;
}
void InstanceImportTask::processFlame()
{
- Flame::Manifest pack;
- try
- {
- QString configPath = FS::PathCombine(m_stagingPath, "manifest.json");
- Flame::loadManifest(pack, configPath);
- if (!QFile::remove(configPath))
- {
- qWarning() << "Could not remove manifest.json from staging";
- }
- }
- catch (const JSONValidationError &e)
- {
- emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
- return;
- }
- if(!pack.overrides.isEmpty())
- {
- QString overridePath = FS::PathCombine(m_stagingPath, pack.overrides);
- if (QFile::exists(overridePath))
- {
- QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
- if (!QFile::rename(overridePath, mcPath))
- {
- emitFailed(tr("Could not rename the overrides folder:\n") + pack.overrides);
- return;
- }
- }
- else
- {
- logWarning(tr("The specified overrides folder (%1) is missing. Maybe the modpack was already used before?").arg(pack.overrides));
- }
- }
-
- configureFlameInstance(pack);
-
- m_modIdResolver = new Flame::FileResolvingTask(APPLICATION->network(), pack);
- connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded,
- this, &InstanceImportTask::onFlameFileResolutionSucceeded);
- connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason)
- {
- m_modIdResolver.reset();
- emitFailed(tr("Unable to resolve mod IDs:\n") + reason);
- });
- connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, [&](qint64 current, qint64 total)
- {
- setProgress(current, total);
- });
- connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, [&](QString status)
- {
- setStatus(status);
- });
- m_modIdResolver->start();
+ Flame::Manifest pack;
+ try {
+ QString configPath = FS::PathCombine(m_stagingPath, "manifest.json");
+ Flame::loadManifest(pack, configPath);
+ if (!QFile::remove(configPath)) {
+ qWarning() << "Could not remove manifest.json from staging";
+ }
+ } catch (const JSONValidationError& e) {
+ emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
+ return;
+ }
+ if (!pack.overrides.isEmpty()) {
+ QString overridePath = FS::PathCombine(m_stagingPath, pack.overrides);
+ if (QFile::exists(overridePath)) {
+ QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
+ if (!QFile::rename(overridePath, mcPath)) {
+ emitFailed(tr("Could not rename the overrides folder:\n") +
+ pack.overrides);
+ return;
+ }
+ } else {
+ logWarning(tr("The specified overrides folder (%1) is missing. "
+ "Maybe the modpack was already used before?")
+ .arg(pack.overrides));
+ }
+ }
+
+ configureFlameInstance(pack);
+
+ m_modIdResolver =
+ new Flame::FileResolvingTask(APPLICATION->network(), pack);
+ connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, this,
+ &InstanceImportTask::onFlameFileResolutionSucceeded);
+ connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed,
+ [&](QString reason) {
+ m_modIdResolver.reset();
+ emitFailed(tr("Unable to resolve mod IDs:\n") + reason);
+ });
+ connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress,
+ [&](qint64 current, qint64 total) { setProgress(current, total); });
+ connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status,
+ [&](QString status) { setStatus(status); });
+ m_modIdResolver->start();
}
-static QString selectFlameIcon(const QString &instIcon, const Flame::Manifest &pack)
+static QString selectFlameIcon(const QString& instIcon,
+ const Flame::Manifest& pack)
{
- if (instIcon != "default")
- return instIcon;
- if (pack.name.contains("Direwolf20"))
- return "steve";
- if (pack.name.contains("FTB") || pack.name.contains("Feed The Beast"))
- return "ftb_logo";
- // default to something other than the MeshMC default to distinguish these
- return "flame";
+ if (instIcon != "default")
+ return instIcon;
+ if (pack.name.contains("Direwolf20"))
+ return "steve";
+ if (pack.name.contains("FTB") || pack.name.contains("Feed The Beast"))
+ return "ftb_logo";
+ // default to something other than the MeshMC default to distinguish these
+ return "flame";
}
-void InstanceImportTask::configureFlameInstance(Flame::Manifest &pack)
+void InstanceImportTask::configureFlameInstance(Flame::Manifest& pack)
{
- const static QMap<QString,QString> forgemap = {
- {"1.2.5", "3.4.9.171"},
- {"1.4.2", "6.0.1.355"},
- {"1.4.7", "6.6.2.534"},
- {"1.5.2", "7.8.1.737"}
- };
-
- struct FlameLoaderMapping {
- const char *prefix;
- QString version;
- const char *componentId;
- };
- FlameLoaderMapping loaderMappings[] = {
- {"forge-", {}, "net.minecraftforge"},
- {"fabric-", {}, "net.fabricmc.fabric-loader"},
- {"neoforge-", {}, "net.neoforged"},
- {"quilt-", {}, "org.quiltmc.quilt-loader"},
- };
- for(auto &loader: pack.minecraft.modLoaders)
- {
- auto id = loader.id;
- bool matched = false;
- for (auto &mapping : loaderMappings)
- {
- if (id.startsWith(mapping.prefix))
- {
- id.remove(mapping.prefix);
- mapping.version = id;
- matched = true;
- break;
- }
- }
- if (!matched)
- {
- logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
- }
- }
-
- QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
- auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
- instanceSettings->registerSetting("InstanceType", "Legacy");
- instanceSettings->set("InstanceType", "OneSix");
- MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
- auto mcVersion = pack.minecraft.version;
- // Hack to correct some 'special sauce'...
- if(mcVersion.endsWith('.'))
- {
- mcVersion.remove(QRegularExpression("[.]+$"));
- logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack."));
- }
- auto components = instance.getPackProfile();
- components->buildingFromScratch();
- components->setComponentVersion("net.minecraft", mcVersion, true);
-
- // Handle Forge "recommended" version mapping
- auto &forgeMapping = loaderMappings[0];
- if(forgeMapping.version == "recommended")
- {
- if(forgemap.contains(mcVersion))
- {
- forgeMapping.version = forgemap[mcVersion];
- }
- else
- {
- logWarning(tr("Could not map recommended forge version for Minecraft %1").arg(mcVersion));
- }
- }
-
- for (const auto &mapping : loaderMappings)
- {
- if (!mapping.version.isEmpty())
- {
- components->setComponentVersion(mapping.componentId, mapping.version);
- }
- }
-
- instance.setIconKey(selectFlameIcon(m_instIcon, pack));
-
- QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods");
- QFileInfo jarmodsInfo(jarmodsPath);
- if(jarmodsInfo.isDir())
- {
- // install all the jar mods
- qDebug() << "Found jarmods:";
- QDir jarmodsDir(jarmodsPath);
- QStringList jarMods;
- for (auto info: jarmodsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
- {
- qDebug() << info.fileName();
- jarMods.push_back(info.absoluteFilePath());
- }
- auto profile = instance.getPackProfile();
- profile->installJarMods(jarMods);
- // nuke the original files
- FS::deletePath(jarmodsPath);
- }
- instance.setName(m_instName);
+ const static QMap<QString, QString> forgemap = {{"1.2.5", "3.4.9.171"},
+ {"1.4.2", "6.0.1.355"},
+ {"1.4.7", "6.6.2.534"},
+ {"1.5.2", "7.8.1.737"}};
+
+ struct FlameLoaderMapping {
+ const char* prefix;
+ QString version;
+ const char* componentId;
+ };
+ FlameLoaderMapping loaderMappings[] = {
+ {"forge-", {}, "net.minecraftforge"},
+ {"fabric-", {}, "net.fabricmc.fabric-loader"},
+ {"neoforge-", {}, "net.neoforged"},
+ {"quilt-", {}, "org.quiltmc.quilt-loader"},
+ };
+ for (auto& loader : pack.minecraft.modLoaders) {
+ auto id = loader.id;
+ bool matched = false;
+ for (auto& mapping : loaderMappings) {
+ if (id.startsWith(mapping.prefix)) {
+ id.remove(mapping.prefix);
+ mapping.version = id;
+ matched = true;
+ break;
+ }
+ }
+ if (!matched) {
+ logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
+ }
+ }
+
+ QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
+ auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
+ instanceSettings->registerSetting("InstanceType", "Legacy");
+ instanceSettings->set("InstanceType", "OneSix");
+ MinecraftInstance instance(m_globalSettings, instanceSettings,
+ m_stagingPath);
+ auto mcVersion = pack.minecraft.version;
+ // Hack to correct some 'special sauce'...
+ if (mcVersion.endsWith('.')) {
+ mcVersion.remove(QRegularExpression("[.]+$"));
+ logWarning(tr("Mysterious trailing dots removed from Minecraft version "
+ "while importing pack."));
+ }
+ auto components = instance.getPackProfile();
+ components->buildingFromScratch();
+ components->setComponentVersion("net.minecraft", mcVersion, true);
+
+ // Handle Forge "recommended" version mapping
+ auto& forgeMapping = loaderMappings[0];
+ if (forgeMapping.version == "recommended") {
+ if (forgemap.contains(mcVersion)) {
+ forgeMapping.version = forgemap[mcVersion];
+ } else {
+ logWarning(
+ tr("Could not map recommended forge version for Minecraft %1")
+ .arg(mcVersion));
+ }
+ }
+
+ for (const auto& mapping : loaderMappings) {
+ if (!mapping.version.isEmpty()) {
+ components->setComponentVersion(mapping.componentId,
+ mapping.version);
+ }
+ }
+
+ instance.setIconKey(selectFlameIcon(m_instIcon, pack));
+
+ QString jarmodsPath =
+ FS::PathCombine(m_stagingPath, "minecraft", "jarmods");
+ QFileInfo jarmodsInfo(jarmodsPath);
+ if (jarmodsInfo.isDir()) {
+ // install all the jar mods
+ qDebug() << "Found jarmods:";
+ QDir jarmodsDir(jarmodsPath);
+ QStringList jarMods;
+ for (auto info :
+ jarmodsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) {
+ qDebug() << info.fileName();
+ jarMods.push_back(info.absoluteFilePath());
+ }
+ auto profile = instance.getPackProfile();
+ profile->installJarMods(jarMods);
+ // nuke the original files
+ FS::deletePath(jarmodsPath);
+ }
+ instance.setName(m_instName);
}
void InstanceImportTask::onFlameFileResolutionSucceeded()
{
- auto results = m_modIdResolver->getResults();
- m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
-
- // Collect restricted mods that need browser download
- QList<BlockedMod> blockedMods;
-
- for(auto result: results.files)
- {
- QString filename = result.fileName;
- if(!result.required)
- {
- filename += ".disabled";
- }
-
- auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
- auto path = FS::PathCombine(m_stagingPath , relpath);
-
- switch(result.type)
- {
- case Flame::File::Type::Folder:
- {
- logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
- [[fallthrough]];
- }
- case Flame::File::Type::SingleFile:
- [[fallthrough]];
- case Flame::File::Type::Mod:
- {
- bool isBlocked = !result.resolved || !result.url.isValid() || result.url.isEmpty();
- if (isBlocked && !result.fileName.isEmpty())
- {
- blockedMods.append({result.projectId, result.fileId, result.fileName, path, false});
- break;
- }
- if (isBlocked)
- {
- logWarning(tr("Skipping mod %1 (project %2) - no download URL and no filename available")
- .arg(result.fileId).arg(result.projectId));
- break;
- }
- qDebug() << "Will download" << result.url << "to" << path;
- auto dl = Net::Download::makeFile(result.url, path);
- m_filesNetJob->addNetAction(dl);
- break;
- }
- case Flame::File::Type::Modpack:
- logWarning(tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(relpath));
- break;
- case Flame::File::Type::Cmod2:
- [[fallthrough]];
- case Flame::File::Type::Ctoc:
- [[fallthrough]];
- case Flame::File::Type::Unknown:
- logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
- break;
- }
- }
-
- // Handle restricted mods via dialog
- if (!blockedMods.isEmpty())
- {
- BlockedModsDialog dlg(nullptr,
- tr("Restricted Mods"),
- tr("The following mods have restricted downloads and are not available through the API.\n"
- "Click the Download button next to each mod to open its download page in your browser.\n"
- "Once all files appear in your Downloads folder, click Continue."),
- blockedMods);
-
- if (dlg.exec() == QDialog::Accepted)
- {
- QString downloadDir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
- for (const auto &mod : blockedMods)
- {
- if (mod.found)
- {
- QString srcPath = FS::PathCombine(downloadDir, mod.fileName);
- QFileInfo targetInfo(mod.targetPath);
- QDir().mkpath(targetInfo.absolutePath());
-
- if (QFile::copy(srcPath, mod.targetPath))
- {
- qDebug() << "Copied restricted mod:" << mod.fileName;
- }
- else
- {
- logWarning(tr("Failed to copy %1 from downloads folder").arg(mod.fileName));
- }
- }
- }
- }
- else
- {
- logWarning(tr("User cancelled restricted mod downloads - %1 mod(s) will be missing").arg(blockedMods.size()));
- }
- }
-
- m_modIdResolver.reset();
- connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
- {
- emitSucceeded();
- m_filesNetJob.reset();
- }
- );
- connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason)
- {
- emitFailed(reason);
- m_filesNetJob.reset();
- });
- connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
- {
- setProgress(current, total);
- });
- setStatus(tr("Downloading mods..."));
- m_filesNetJob->start();
+ auto results = m_modIdResolver->getResults();
+ m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
+
+ // Collect restricted mods that need browser download
+ QList<BlockedMod> blockedMods;
+
+ for (auto result : results.files) {
+ QString filename = result.fileName;
+ if (!result.required) {
+ filename += ".disabled";
+ }
+
+ auto relpath =
+ FS::PathCombine("minecraft", result.targetFolder, filename);
+ auto path = FS::PathCombine(m_stagingPath, relpath);
+
+ switch (result.type) {
+ case Flame::File::Type::Folder: {
+ logWarning(
+ tr("This 'Folder' may need extracting: %1").arg(relpath));
+ [[fallthrough]];
+ }
+ case Flame::File::Type::SingleFile:
+ [[fallthrough]];
+ case Flame::File::Type::Mod: {
+ bool isBlocked = !result.resolved || !result.url.isValid() ||
+ result.url.isEmpty();
+ if (isBlocked && !result.fileName.isEmpty()) {
+ blockedMods.append({result.projectId, result.fileId,
+ result.fileName, path, false});
+ break;
+ }
+ if (isBlocked) {
+ logWarning(tr("Skipping mod %1 (project %2) - no download "
+ "URL and no filename available")
+ .arg(result.fileId)
+ .arg(result.projectId));
+ break;
+ }
+ qDebug() << "Will download" << result.url << "to" << path;
+ auto dl = Net::Download::makeFile(result.url, path);
+ m_filesNetJob->addNetAction(dl);
+ break;
+ }
+ case Flame::File::Type::Modpack:
+ logWarning(tr("Nesting modpacks in modpacks is not "
+ "implemented, nothing was downloaded: %1")
+ .arg(relpath));
+ break;
+ case Flame::File::Type::Cmod2:
+ [[fallthrough]];
+ case Flame::File::Type::Ctoc:
+ [[fallthrough]];
+ case Flame::File::Type::Unknown:
+ logWarning(tr("Unrecognized/unhandled PackageType for: %1")
+ .arg(relpath));
+ break;
+ }
+ }
+
+ // Handle restricted mods via dialog
+ if (!blockedMods.isEmpty()) {
+ BlockedModsDialog dlg(nullptr, tr("Restricted Mods"),
+ tr("The following mods have restricted downloads "
+ "and are not available through the API.\n"
+ "Click the Download button next to each mod "
+ "to open its download page in your browser.\n"
+ "Once all files appear in your Downloads "
+ "folder, click Continue."),
+ blockedMods);
+
+ if (dlg.exec() == QDialog::Accepted) {
+ QString downloadDir = QStandardPaths::writableLocation(
+ QStandardPaths::DownloadLocation);
+ for (const auto& mod : blockedMods) {
+ if (mod.found) {
+ QString srcPath =
+ FS::PathCombine(downloadDir, mod.fileName);
+ QFileInfo targetInfo(mod.targetPath);
+ QDir().mkpath(targetInfo.absolutePath());
+
+ if (QFile::copy(srcPath, mod.targetPath)) {
+ qDebug() << "Copied restricted mod:" << mod.fileName;
+ } else {
+ logWarning(tr("Failed to copy %1 from downloads folder")
+ .arg(mod.fileName));
+ }
+ }
+ }
+ } else {
+ logWarning(tr("User cancelled restricted mod downloads - %1 mod(s) "
+ "will be missing")
+ .arg(blockedMods.size()));
+ }
+ }
+
+ m_modIdResolver.reset();
+ connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() {
+ emitSucceeded();
+ m_filesNetJob.reset();
+ });
+ connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason) {
+ emitFailed(reason);
+ m_filesNetJob.reset();
+ });
+ connect(m_filesNetJob.get(), &NetJob::progress,
+ [&](qint64 current, qint64 total) { setProgress(current, total); });
+ setStatus(tr("Downloading mods..."));
+ m_filesNetJob->start();
}
-static void applyModrinthOverrides(const QString &stagingPath, const QString &mcPath)
+static void applyModrinthOverrides(const QString& stagingPath,
+ const QString& mcPath)
{
- QString overridePath = FS::PathCombine(stagingPath, "overrides");
- QString clientOverridePath = FS::PathCombine(stagingPath, "client-overrides");
-
- if (QFile::exists(overridePath))
- {
- if (!FS::copy(overridePath, mcPath)())
- {
- qWarning() << "Could not apply overrides from the modpack.";
- }
- FS::deletePath(overridePath);
- }
-
- if (QFile::exists(clientOverridePath))
- {
- if (!FS::copy(clientOverridePath, mcPath)())
- {
- qWarning() << "Could not apply client-overrides from the modpack.";
- }
- FS::deletePath(clientOverridePath);
- }
+ QString overridePath = FS::PathCombine(stagingPath, "overrides");
+ QString clientOverridePath =
+ FS::PathCombine(stagingPath, "client-overrides");
+
+ if (QFile::exists(overridePath)) {
+ if (!FS::copy(overridePath, mcPath)()) {
+ qWarning() << "Could not apply overrides from the modpack.";
+ }
+ FS::deletePath(overridePath);
+ }
+
+ if (QFile::exists(clientOverridePath)) {
+ if (!FS::copy(clientOverridePath, mcPath)()) {
+ qWarning() << "Could not apply client-overrides from the modpack.";
+ }
+ FS::deletePath(clientOverridePath);
+ }
}
void InstanceImportTask::processModrinth()
{
- Modrinth::Manifest pack;
- try
- {
- QString configPath = FS::PathCombine(m_stagingPath, "modrinth.index.json");
- Modrinth::loadManifest(pack, configPath);
- if (!QFile::remove(configPath))
- {
- qWarning() << "Could not remove modrinth.index.json from staging";
- }
- }
- catch (const JSONValidationError &e)
- {
- emitFailed(tr("Could not understand Modrinth modpack manifest:\n") + e.cause());
- return;
- }
-
- // Move overrides folder contents to minecraft directory
- QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
-
- QDir mcDir(mcPath);
- if (!mcDir.exists())
- {
- mcDir.mkpath(".");
- }
-
- applyModrinthOverrides(m_stagingPath, mcPath);
-
- // Create instance config
- QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
- auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
- instanceSettings->registerSetting("InstanceType", "Legacy");
- instanceSettings->set("InstanceType", "OneSix");
- MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
-
- auto components = instance.getPackProfile();
- components->buildingFromScratch();
-
- struct ModLoaderMapping {
- const QString &version;
- const char *componentId;
- };
- const ModLoaderMapping loaders[] = {
- {pack.forgeVersion, "net.minecraftforge"},
- {pack.fabricVersion, "net.fabricmc.fabric-loader"},
- {pack.quiltVersion, "org.quiltmc.quilt-loader"},
- {pack.neoForgeVersion, "net.neoforged"},
- };
-
- if (!pack.minecraftVersion.isEmpty())
- {
- components->setComponentVersion("net.minecraft", pack.minecraftVersion, true);
- }
- for (const auto &loader : loaders)
- {
- if (!loader.version.isEmpty())
- {
- components->setComponentVersion(loader.componentId, loader.version);
- }
- }
-
- if (m_instIcon != "default")
- {
- instance.setIconKey(m_instIcon);
- }
- else
- {
- instance.setIconKey("modrinth");
- }
-
- instance.setName(m_instName);
-
- // Download all mod files
- m_filesNetJob = new NetJob(tr("Modrinth mod download"), APPLICATION->network());
- auto minecraftDir = FS::PathCombine(m_stagingPath, "minecraft");
- auto canonicalBase = QDir(minecraftDir).canonicalPath();
- for (auto &file : pack.files)
- {
- if (file.path.contains("..") || QDir::isAbsolutePath(file.path))
- {
- qWarning() << "Skipping potentially malicious file path:" << file.path;
- continue;
- }
- auto path = FS::PathCombine(minecraftDir, file.path);
- auto canonicalDir = QFileInfo(path).absolutePath();
- if (!canonicalDir.startsWith(canonicalBase))
- {
- qWarning() << "Skipping file path that escapes staging directory:" << file.path;
- continue;
- }
- if (!file.downloadUrl.isValid() || file.downloadUrl.isEmpty())
- {
- logWarning(tr("Skipping file with no download URL: %1").arg(file.path));
- continue;
- }
- qDebug() << "Will download" << file.downloadUrl << "to" << path;
- auto dl = Net::Download::makeFile(file.downloadUrl, path);
- m_filesNetJob->addNetAction(dl);
- }
-
- connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
- {
- emitSucceeded();
- m_filesNetJob.reset();
- });
- connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason)
- {
- emitFailed(reason);
- m_filesNetJob.reset();
- });
- connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
- {
- setProgress(current, total);
- });
-
- setStatus(tr("Downloading mods..."));
- m_filesNetJob->start();
+ Modrinth::Manifest pack;
+ try {
+ QString configPath =
+ FS::PathCombine(m_stagingPath, "modrinth.index.json");
+ Modrinth::loadManifest(pack, configPath);
+ if (!QFile::remove(configPath)) {
+ qWarning() << "Could not remove modrinth.index.json from staging";
+ }
+ } catch (const JSONValidationError& e) {
+ emitFailed(tr("Could not understand Modrinth modpack manifest:\n") +
+ e.cause());
+ return;
+ }
+
+ // Move overrides folder contents to minecraft directory
+ QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
+
+ QDir mcDir(mcPath);
+ if (!mcDir.exists()) {
+ mcDir.mkpath(".");
+ }
+
+ applyModrinthOverrides(m_stagingPath, mcPath);
+
+ // Create instance config
+ QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
+ auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
+ instanceSettings->registerSetting("InstanceType", "Legacy");
+ instanceSettings->set("InstanceType", "OneSix");
+ MinecraftInstance instance(m_globalSettings, instanceSettings,
+ m_stagingPath);
+
+ auto components = instance.getPackProfile();
+ components->buildingFromScratch();
+
+ struct ModLoaderMapping {
+ const QString& version;
+ const char* componentId;
+ };
+ const ModLoaderMapping loaders[] = {
+ {pack.forgeVersion, "net.minecraftforge"},
+ {pack.fabricVersion, "net.fabricmc.fabric-loader"},
+ {pack.quiltVersion, "org.quiltmc.quilt-loader"},
+ {pack.neoForgeVersion, "net.neoforged"},
+ };
+
+ if (!pack.minecraftVersion.isEmpty()) {
+ components->setComponentVersion("net.minecraft", pack.minecraftVersion,
+ true);
+ }
+ for (const auto& loader : loaders) {
+ if (!loader.version.isEmpty()) {
+ components->setComponentVersion(loader.componentId, loader.version);
+ }
+ }
+
+ if (m_instIcon != "default") {
+ instance.setIconKey(m_instIcon);
+ } else {
+ instance.setIconKey("modrinth");
+ }
+
+ instance.setName(m_instName);
+
+ // Download all mod files
+ m_filesNetJob =
+ new NetJob(tr("Modrinth mod download"), APPLICATION->network());
+ auto minecraftDir = FS::PathCombine(m_stagingPath, "minecraft");
+ auto canonicalBase = QDir(minecraftDir).canonicalPath();
+ for (auto& file : pack.files) {
+ if (file.path.contains("..") || QDir::isAbsolutePath(file.path)) {
+ qWarning() << "Skipping potentially malicious file path:"
+ << file.path;
+ continue;
+ }
+ auto path = FS::PathCombine(minecraftDir, file.path);
+ auto canonicalDir = QFileInfo(path).absolutePath();
+ if (!canonicalDir.startsWith(canonicalBase)) {
+ qWarning() << "Skipping file path that escapes staging directory:"
+ << file.path;
+ continue;
+ }
+ if (!file.downloadUrl.isValid() || file.downloadUrl.isEmpty()) {
+ logWarning(
+ tr("Skipping file with no download URL: %1").arg(file.path));
+ continue;
+ }
+ qDebug() << "Will download" << file.downloadUrl << "to" << path;
+ auto dl = Net::Download::makeFile(file.downloadUrl, path);
+ m_filesNetJob->addNetAction(dl);
+ }
+
+ connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() {
+ emitSucceeded();
+ m_filesNetJob.reset();
+ });
+ connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason) {
+ emitFailed(reason);
+ m_filesNetJob.reset();
+ });
+ connect(m_filesNetJob.get(), &NetJob::progress,
+ [&](qint64 current, qint64 total) { setProgress(current, total); });
+
+ setStatus(tr("Downloading mods..."));
+ m_filesNetJob->start();
}
void InstanceImportTask::processTechnic()
{
- shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor = new Technic::TechnicPackProcessor();
- connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &InstanceImportTask::emitSucceeded);
- connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &InstanceImportTask::emitFailed);
- packProcessor->run(m_globalSettings, m_instName, m_instIcon, m_stagingPath);
+ shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor =
+ new Technic::TechnicPackProcessor();
+ connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded,
+ this, &InstanceImportTask::emitSucceeded);
+ connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this,
+ &InstanceImportTask::emitFailed);
+ packProcessor->run(m_globalSettings, m_instName, m_instIcon, m_stagingPath);
}
void InstanceImportTask::processMeshMC()
{
- QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
- auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
- instanceSettings->registerSetting("InstanceType", "Legacy");
-
- NullInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
-
- // reset time played on import... because packs.
- instance.resetTimePlayed();
-
- // Set a new name for the imported instance
- instance.setName(m_instName);
-
- // Use user-specified icon if available, otherwise import from the pack
- if (m_instIcon != "default")
- {
- instance.setIconKey(m_instIcon);
- }
- else
- {
- m_instIcon = instance.iconKey();
-
- auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon);
- if (!importIconPath.isNull() && QFile::exists(importIconPath))
- {
- // import icon
- auto iconList = APPLICATION->icons();
- if (iconList->iconFileExists(m_instIcon))
- {
- iconList->deleteIcon(m_instIcon);
- }
- iconList->installIcons({importIconPath});
- }
- }
- emitSucceeded();
+ QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
+ auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
+ instanceSettings->registerSetting("InstanceType", "Legacy");
+
+ NullInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
+
+ // reset time played on import... because packs.
+ instance.resetTimePlayed();
+
+ // Set a new name for the imported instance
+ instance.setName(m_instName);
+
+ // Use user-specified icon if available, otherwise import from the pack
+ if (m_instIcon != "default") {
+ instance.setIconKey(m_instIcon);
+ } else {
+ m_instIcon = instance.iconKey();
+
+ auto importIconPath =
+ IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon);
+ if (!importIconPath.isNull() && QFile::exists(importIconPath)) {
+ // import icon
+ auto iconList = APPLICATION->icons();
+ if (iconList->iconFileExists(m_instIcon)) {
+ iconList->deleteIcon(m_instIcon);
+ }
+ iconList->installIcons({importIconPath});
+ }
+ }
+ emitSucceeded();
}