From 72d2ca234e80fe65bb6a7d5fe106b01d9dc6f096 Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 8 Jul 2022 18:44:43 -0300 Subject: [PATCH] refactor: move flame modpack import to separate file Signed-off-by: flow --- launcher/CMakeLists.txt | 2 + launcher/InstanceCreationTask.h | 2 + launcher/InstanceImportTask.cpp | 298 +----------------- .../flame/FlameInstanceCreationTask.cpp | 284 +++++++++++++++++ .../flame/FlameInstanceCreationTask.h | 32 ++ 5 files changed, 337 insertions(+), 281 deletions(-) create mode 100644 launcher/modplatform/flame/FlameInstanceCreationTask.cpp create mode 100644 launcher/modplatform/flame/FlameInstanceCreationTask.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 68439ee8..7bd92fac 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -486,6 +486,8 @@ set(FLAME_SOURCES modplatform/flame/FileResolvingTask.cpp modplatform/flame/FlameCheckUpdate.cpp modplatform/flame/FlameCheckUpdate.h + modplatform/flame/FlameInstanceCreationTask.h + modplatform/flame/FlameInstanceCreationTask.cpp ) set(MODRINTH_SOURCES diff --git a/launcher/InstanceCreationTask.h b/launcher/InstanceCreationTask.h index af854713..68c5de59 100644 --- a/launcher/InstanceCreationTask.h +++ b/launcher/InstanceCreationTask.h @@ -31,6 +31,8 @@ class InstanceCreationTask : public InstanceTask { */ virtual bool createInstance() { return false; }; + QString getError() const { return m_error_message; } + protected: void setError(QString message) { m_error_message = message; }; diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 4bdf9cd2..72c2496f 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -50,10 +50,9 @@ #include "Json.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" -#include "modplatform/flame/FileResolvingTask.h" -#include "modplatform/flame/PackManifest.h" #include "modplatform/technic/TechnicPackProcessor.h" #include "modplatform/modrinth/ModrinthInstanceCreationTask.h" +#include "modplatform/flame/FlameInstanceCreationTask.h" #include "Application.h" #include "icons/IconList.h" @@ -262,287 +261,24 @@ void InstanceImportTask::extractAborted() void InstanceImportTask::processFlame() { - const static QMap 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"} - }; - Flame::Manifest pack; - try - { - QString configPath = FS::PathCombine(m_stagingPath, "manifest.json"); - Flame::loadManifest(pack, configPath); - QFile::remove(configPath); - } - 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)); - } - } + auto* inst_creation_task = new FlameCreationTask(m_stagingPath, m_globalSettings, m_parent); - QString forgeVersion; - QString fabricVersion; - // TODO: is Quilt relevant here? - for(auto &loader: pack.minecraft.modLoaders) - { - auto id = loader.id; - if(id.startsWith("forge-")) - { - id.remove("forge-"); - forgeVersion = id; - continue; - } - if(id.startsWith("fabric-")) - { - id.remove("fabric-"); - fabricVersion = id; - continue; - } - logWarning(tr("Unknown mod loader in manifest: %1").arg(id)); - } - - QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); - auto instanceSettings = std::make_shared(configPath); - 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); - if(!forgeVersion.isEmpty()) - { - // FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata. - if(forgeVersion == "recommended") - { - if(forgemap.contains(mcVersion)) - { - forgeVersion = forgemap[mcVersion]; - } - else - { - logWarning(tr("Could not map recommended Forge version for Minecraft %1").arg(mcVersion)); - } - } - components->setComponentVersion("net.minecraftforge", forgeVersion); - } - if(!fabricVersion.isEmpty()) - { - components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion); - } - if (m_instIcon != "default") - { - instance.setIconKey(m_instIcon); - } - else - { - if(pack.name.contains("Direwolf20")) - { - instance.setIconKey("steve"); - } - else if(pack.name.contains("FTB") || pack.name.contains("Feed The Beast")) - { - instance.setIconKey("ftb_logo"); - } - else - { - // default to something other than the MultiMC default to distinguish these - instance.setIconKey("flame"); - } - } - 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); - m_modIdResolver = new Flame::FileResolvingTask(APPLICATION->network(), pack); - connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]() - { - auto results = m_modIdResolver->getResults(); - //first check for blocked mods - QString text; - QList urls; - auto anyBlocked = false; - for(const auto& result: results.files.values()) { - if (!result.resolved || result.url.isEmpty()) { - text += QString("%1: %2
").arg(result.fileName, result.websiteUrl); - urls.append(QUrl(result.websiteUrl)); - anyBlocked = true; - } - } - if(anyBlocked) { - qWarning() << "Blocked mods found, displaying mod list"; - - auto message_dialog = new BlockedModsDialog(m_parent, - tr("Blocked mods found"), - tr("The following mods were blocked on third party launchers.
" - "You will need to manually download them and add them to the modpack"), - text, - urls); - message_dialog->setModal(true); - - if (message_dialog->exec()) { - m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network()); - for (const auto &result: m_modIdResolver->getResults().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)); - // fall-through intentional, we treat these as plain old mods and dump them wherever. - } - case Flame::File::Type::SingleFile: - case Flame::File::Type::Mod: { - if (!result.url.isEmpty()) { - 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: - case Flame::File::Type::Ctoc: - case Flame::File::Type::Unknown: - logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath)); - break; - } - } - m_modIdResolver.reset(); - connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() { - m_filesNetJob.reset(); - emitSucceeded(); - } - ); - connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason) { - m_filesNetJob.reset(); - emitFailed(reason); - }); - connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total) { - setProgress(current, total); - }); - setStatus(tr("Downloading mods...")); - m_filesNetJob->start(); - } else { - m_modIdResolver.reset(); - emitFailed("Canceled"); - } - } else { - //TODO extract to function ? - m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network()); - for (const auto &result: m_modIdResolver->getResults().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)); - // fall-through intentional, we treat these as plain old mods and dump them wherever. - } - case Flame::File::Type::SingleFile: - case Flame::File::Type::Mod: { - if (!result.url.isEmpty()) { - 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: - case Flame::File::Type::Ctoc: - case Flame::File::Type::Unknown: - logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath)); - break; - } - } - m_modIdResolver.reset(); - connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() { - m_filesNetJob.reset(); - emitSucceeded(); - } - ); - connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason) { - m_filesNetJob.reset(); - emitFailed(reason); - }); - connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total) { - setProgress(current, total); - }); - setStatus(tr("Downloading mods...")); - m_filesNetJob->start(); - } - } - ); - connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason) - { - m_modIdResolver.reset(); - emitFailed(tr("Unable to resolve mod IDs:\n") + reason); + inst_creation_task->setName(m_instName); + inst_creation_task->setIcon(m_instIcon); + inst_creation_task->setGroup(m_instGroup); + + connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] { + setOverride(inst_creation_task->shouldOverride()); + emitSucceeded(); }); - 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(); + connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed); + connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress); + connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus); + connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater); + + connect(this, &Task::aborted, inst_creation_task, &InstanceCreationTask::abort); + + inst_creation_task->start(); } void InstanceImportTask::processTechnic() diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp new file mode 100644 index 00000000..431c8b3d --- /dev/null +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -0,0 +1,284 @@ +#include "FlameInstanceCreationTask.h" + +#include "modplatform/flame/PackManifest.h" + +#include "Application.h" +#include "FileSystem.h" +#include "Json.h" + +#include "minecraft/MinecraftInstance.h" +#include "minecraft/PackProfile.h" + +#include "settings/INISettingsObject.h" + +#include "ui/dialogs/BlockedModsDialog.h" + +bool FlameCreationTask::abort() +{ + if (m_files_job) + m_files_job->abort(); + if (m_mod_id_resolver) + m_mod_id_resolver->abort(); + + return true; +} + +const static QMap 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" } }; + +bool FlameCreationTask::createInstance() +{ + QEventLoop loop; + + Flame::Manifest pack; + try { + QString configPath = FS::PathCombine(m_stagingPath, "manifest.json"); + Flame::loadManifest(pack, configPath); + QFile::remove(configPath); + } catch (const JSONValidationError& e) { + setError(tr("Could not understand pack manifest:\n") + e.cause()); + return false; + } + + 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)) { + setError(tr("Could not rename the overrides folder:\n") + pack.overrides); + return false; + } + } else { + logWarning( + tr("The specified overrides folder (%1) is missing. Maybe the modpack was already used before?").arg(pack.overrides)); + } + } + + QString forgeVersion; + QString fabricVersion; + // TODO: is Quilt relevant here? + for (auto& loader : pack.minecraft.modLoaders) { + auto id = loader.id; + if (id.startsWith("forge-")) { + id.remove("forge-"); + forgeVersion = id; + continue; + } + if (id.startsWith("fabric-")) { + id.remove("fabric-"); + fabricVersion = id; + continue; + } + logWarning(tr("Unknown mod loader in manifest: %1").arg(id)); + } + + QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); + auto instanceSettings = std::make_shared(configPath); + 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); + if (!forgeVersion.isEmpty()) { + // FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata. + if (forgeVersion == "recommended") { + if (forgemap.contains(mcVersion)) { + forgeVersion = forgemap[mcVersion]; + } else { + logWarning(tr("Could not map recommended Forge version for Minecraft %1").arg(mcVersion)); + } + } + components->setComponentVersion("net.minecraftforge", forgeVersion); + } + if (!fabricVersion.isEmpty()) + components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion); + + if (m_instIcon != "default") { + instance.setIconKey(m_instIcon); + } else { + if (pack.name.contains("Direwolf20")) { + instance.setIconKey("steve"); + } else if (pack.name.contains("FTB") || pack.name.contains("Feed The Beast")) { + instance.setIconKey("ftb_logo"); + } else { + instance.setIconKey("flame"); + } + } + + 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); + + m_mod_id_resolver = new Flame::FileResolvingTask(APPLICATION->network(), pack); + connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::succeeded, this, [this, &loop]{ + idResolverSucceeded(loop); + }); + connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason) { + m_mod_id_resolver.reset(); + setError(tr("Unable to resolve mod IDs:\n") + reason); + }); + connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::progress, this, &FlameCreationTask::setProgress); + connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::status, this, &FlameCreationTask::setStatus); + + m_mod_id_resolver->start(); + + loop.exec(); + + return getError().isEmpty(); +} + +void FlameCreationTask::idResolverSucceeded(QEventLoop& loop) +{ + auto results = m_mod_id_resolver->getResults(); + // first check for blocked mods + QString text; + QList urls; + auto anyBlocked = false; + for (const auto& result : results.files.values()) { + if (!result.resolved || result.url.isEmpty()) { + text += QString("%1: %2
").arg(result.fileName, result.websiteUrl); + urls.append(QUrl(result.websiteUrl)); + anyBlocked = true; + } + } + if (anyBlocked) { + qWarning() << "Blocked mods found, displaying mod list"; + + auto message_dialog = new BlockedModsDialog(m_parent, tr("Blocked mods found"), + tr("The following mods were blocked on third party launchers.
" + "You will need to manually download them and add them to the modpack"), + text, + urls); + message_dialog->setModal(true); + + if (message_dialog->exec()) { + m_files_job = new NetJob(tr("Mod download"), APPLICATION->network()); + for (const auto& result : m_mod_id_resolver->getResults().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)); + // fall-through intentional, we treat these as plain old mods and dump them wherever. + } + case Flame::File::Type::SingleFile: + case Flame::File::Type::Mod: { + if (!result.url.isEmpty()) { + qDebug() << "Will download" << result.url << "to" << path; + auto dl = Net::Download::makeFile(result.url, path); + m_files_job->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: + case Flame::File::Type::Ctoc: + case Flame::File::Type::Unknown: + logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath)); + break; + } + } + + m_mod_id_resolver.reset(); + connect(m_files_job.get(), &NetJob::succeeded, this, [&]() { + m_files_job.reset(); + emitSucceeded(); + }); + connect(m_files_job.get(), &NetJob::failed, [&](QString reason) { + m_files_job.reset(); + setError(reason); + }); + connect(m_files_job.get(), &NetJob::progress, [&](qint64 current, qint64 total) { setProgress(current, total); }); + connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit); + + setStatus(tr("Downloading mods...")); + m_files_job->start(); + } else { + m_mod_id_resolver.reset(); + setError("Canceled"); + } + } else { + // TODO extract to function ? + m_files_job = new NetJob(tr("Mod download"), APPLICATION->network()); + for (const auto& result : m_mod_id_resolver->getResults().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)); + // fall-through intentional, we treat these as plain old mods and dump them wherever. + } + case Flame::File::Type::SingleFile: + case Flame::File::Type::Mod: { + if (!result.url.isEmpty()) { + qDebug() << "Will download" << result.url << "to" << path; + auto dl = Net::Download::makeFile(result.url, path); + m_files_job->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: + case Flame::File::Type::Ctoc: + case Flame::File::Type::Unknown: + logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath)); + break; + } + } + + m_mod_id_resolver.reset(); + connect(m_files_job.get(), &NetJob::succeeded, this, [&]() { + m_files_job.reset(); + emitSucceeded(); + }); + connect(m_files_job.get(), &NetJob::failed, [&](QString reason) { + m_files_job.reset(); + setError(reason); + }); + connect(m_files_job.get(), &NetJob::progress, [&](qint64 current, qint64 total) { setProgress(current, total); }); + connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit); + + setStatus(tr("Downloading mods...")); + m_files_job->start(); + } +} diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.h b/launcher/modplatform/flame/FlameInstanceCreationTask.h new file mode 100644 index 00000000..efb099d9 --- /dev/null +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.h @@ -0,0 +1,32 @@ +#pragma once + +#include "InstanceCreationTask.h" + +#include "modplatform/flame/FileResolvingTask.h" + +#include "net/NetJob.h" + +class FlameCreationTask final : public InstanceCreationTask { + Q_OBJECT + + public: + FlameCreationTask(QString staging_path, SettingsObjectPtr global_settings, QWidget* parent) + : InstanceCreationTask(), m_parent(parent) + { + setStagingPath(staging_path); + setParentSettings(global_settings); + } + + bool abort() override; + + bool createInstance() override; + + private slots: + void idResolverSucceeded(QEventLoop&); + + private: + QWidget* m_parent = nullptr; + + shared_qobject_ptr m_mod_id_resolver; + NetJob::Ptr m_files_job; +};