diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp index 0080cc516..d65e07df0 100644 --- a/launcher/BaseInstance.cpp +++ b/launcher/BaseInstance.cpp @@ -46,13 +46,10 @@ #include "Application.h" #include "Json.h" #include "launch/LaunchTask.h" -#include "settings/INISettingsObject.h" -#include "settings/OverrideSetting.h" #include "settings/Setting.h" #include "BuildConfig.h" #include "Commandline.h" -#include "FileSystem.h" int getConsoleMaxLines(SettingsObject* settings) { @@ -71,20 +68,18 @@ bool shouldStopOnConsoleOverflow(SettingsObject* settings) return settings->get("ConsoleOverflowStop").toBool(); } -BaseInstance::BaseInstance(SettingsObject* globalSettings, std::unique_ptr settings, const QString& rootDir) : QObject() +BaseInstance::BaseInstance(SettingsObject* globalSettings, std::unique_ptr settings, QString rootDir) + : m_rootDir(std::move(rootDir)), m_settings(std::move(settings)), m_global_settings(globalSettings) { - m_settings = std::move(settings); - m_global_settings = globalSettings; - m_rootDir = rootDir; - m_settings->registerSetting("name", "Unnamed Instance"); m_settings->registerSetting("iconKey", "default"); m_settings->registerSetting("notes", ""); m_settings->registerSetting("lastLaunchTime", 0); m_settings->registerSetting("totalTimePlayed", 0); - if (m_settings->get("totalTimePlayed").toLongLong() < 0) + if (m_settings->get("totalTimePlayed").toLongLong() < 0) { m_settings->reset("totalTimePlayed"); + } m_settings->registerSetting("lastTimePlayed", 0); m_settings->registerSetting("linkedInstances", "[]"); @@ -97,8 +92,9 @@ BaseInstance::BaseInstance(SettingsObject* globalSettings, std::unique_ptrgetSetting("InstanceType")) + if (!m_settings->getSetting("InstanceType")) { m_settings->registerSetting("InstanceType", ""); + } // Custom Commands auto commandSetting = m_settings->registerSetting({ "OverrideCommands", "OverrideLaunchCmd" }, false); @@ -128,8 +124,6 @@ BaseInstance::BaseInstance(SettingsObject* globalSettings, std::unique_ptrregisterSetting("Profiler", ""); } -BaseInstance::~BaseInstance() {} - QString BaseInstance::getPreLaunchCommand() { return settings()->get("PreLaunchCommand").toString(); @@ -189,22 +183,6 @@ void BaseInstance::setManagedPack(const QString& type, m_settings->set("ManagedPackVersionName", version); } -void BaseInstance::copyManagedPack(BaseInstance& other) -{ - m_settings->set("ManagedPack", other.isManagedPack()); - m_settings->set("ManagedPackType", other.getManagedPackType()); - m_settings->set("ManagedPackID", other.getManagedPackID()); - m_settings->set("ManagedPackName", other.getManagedPackName()); - m_settings->set("ManagedPackVersionID", other.getManagedPackVersionID()); - m_settings->set("ManagedPackVersionName", other.getManagedPackVersionName()); - - if (APPLICATION->settings()->get("AutomaticJavaSwitch").toBool() && m_settings->get("AutomaticJava").toBool() && - m_settings->get("OverrideJavaLocation").toBool()) { - m_settings->set("OverrideJavaLocation", false); - m_settings->set("JavaPath", ""); - } -} - QStringList BaseInstance::getLinkedInstances() const { auto setting = m_settings->get("linkedInstances").toString(); @@ -226,7 +204,7 @@ void BaseInstance::addLinkedInstanceId(const QString& id) bool BaseInstance::removeLinkedInstanceId(const QString& id) { auto linkedInstances = getLinkedInstances(); - int numRemoved = linkedInstances.removeAll(id); + auto numRemoved = linkedInstances.removeAll(id); setLinkedInstances(linkedInstances); return numRemoved > 0; } @@ -237,7 +215,7 @@ bool BaseInstance::isLinkedToInstanceId(const QString& id) const return linkedInstances.contains(id); } -void BaseInstance::iconUpdated(QString key) +void BaseInstance::iconUpdated(const QString& key) { if (iconKey() == key) { emit propertiesChanged(this); @@ -276,8 +254,9 @@ bool BaseInstance::isRunning() const void BaseInstance::setRunning(bool running) { - if (running == m_isRunning) + if (running == m_isRunning) { return; + } m_isRunning = running; @@ -368,7 +347,7 @@ void BaseInstance::setLastLaunch(qint64 val) emit propertiesChanged(this); } -void BaseInstance::setNotes(QString val) +void BaseInstance::setNotes(const QString& val) { // FIXME: if no change, do not set. setting involves saving a file. m_settings->set("notes", val); @@ -379,7 +358,7 @@ QString BaseInstance::notes() const return m_settings->get("notes").toString(); } -void BaseInstance::setIconKey(QString val) +void BaseInstance::setIconKey(const QString& val) { // FIXME: if no change, do not set. setting involves saving a file. m_settings->set("iconKey", val); @@ -391,7 +370,7 @@ QString BaseInstance::iconKey() const return m_settings->get("iconKey").toString(); } -void BaseInstance::setName(QString val) +void BaseInstance::setName(const QString& val) { // FIXME: if no change, do not set. setting involves saving a file. m_settings->set("name", val); @@ -430,19 +409,23 @@ QList BaseInstance::shortcuts() const auto data = m_settings->get("shortcuts").toString().toUtf8(); QJsonParseError parseError; auto document = QJsonDocument::fromJson(data, &parseError); - if (parseError.error != QJsonParseError::NoError || !document.isArray()) + if (parseError.error != QJsonParseError::NoError || !document.isArray()) { return {}; + } QList results; for (const auto& elem : document.array()) { - if (!elem.isObject()) + if (!elem.isObject()) { continue; + } auto dict = elem.toObject(); - if (!dict.contains("name") || !dict.contains("filePath") || !dict.contains("target")) + if (!dict.contains("name") || !dict.contains("filePath") || !dict.contains("target")) { continue; + } int value = dict["target"].toInt(-1); - if (!dict["name"].isString() || !dict["filePath"].isString() || value < 0 || value >= 3) + if (!dict.value("name").isString() || !dict.value("filePath").isString() || value < 0 || value >= 3) { continue; + } QString shortcutName = dict["name"].toString(); QString filePath = dict["filePath"].toString(); @@ -481,7 +464,7 @@ void BaseInstance::updateRuntimeContext() // NOOP } -bool BaseInstance::isLegacy() +bool BaseInstance::isLegacy() const { return traits().contains("legacyLaunch") || traits().contains("alphaLaunch"); } diff --git a/launcher/BaseInstance.h b/launcher/BaseInstance.h index 9280d2e1c..c789a4525 100644 --- a/launcher/BaseInstance.h +++ b/launcher/BaseInstance.h @@ -45,16 +45,12 @@ #include #include #include +#include #include "QObjectPtr.h" #include "settings/SettingsObject.h" -#include "BaseVersionList.h" -#include "MessageLevel.h" #include "minecraft/auth/MinecraftAccount.h" -#include "settings/INIFile.h" - -#include "net/Mode.h" #include "RuntimeContext.h" #include "minecraft/launch/MinecraftTarget.h" @@ -65,7 +61,7 @@ class LaunchTask; class BaseInstance; /// Shortcut saving target representations -enum class ShortcutTarget { Desktop, Applications, Other }; +enum class ShortcutTarget : std::uint8_t { Desktop, Applications, Other }; /// Shortcut data representation struct ShortcutData { @@ -90,17 +86,17 @@ class BaseInstance : public QObject { Q_OBJECT protected: /// no-touchy! - BaseInstance(SettingsObject* globalSettings, std::unique_ptr settings, const QString& rootDir); + BaseInstance(SettingsObject* globalSettings, std::unique_ptr settings, QString rootDir); public: /* types */ - enum class Status { + enum class Status : std::uint8_t { Present, Gone // either nuked or invalidated }; public: /// virtual destructor to make sure the destruction is COMPLETE - virtual ~BaseInstance(); + ~BaseInstance() override = default; virtual void saveNow() = 0; @@ -136,7 +132,7 @@ class BaseInstance : public QObject { virtual QString modsRoot() const = 0; QString name() const; - void setName(QString val); + void setName(const QString& val); /// Sync name and rename instance dir accordingly; returns true if successful bool syncInstanceDirName(const QString& newRoot) const; @@ -150,10 +146,10 @@ class BaseInstance : public QObject { QString windowTitle() const; QString iconKey() const; - void setIconKey(QString val); + void setIconKey(const QString& val); QString notes() const; - void setNotes(QString val); + void setNotes(const QString& val); QString getPreLaunchCommand(); QString getPostExitCommand(); @@ -166,7 +162,6 @@ class BaseInstance : public QObject { QString getManagedPackVersionID() const; QString getManagedPackVersionName() const; void setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version); - void copyManagedPack(BaseInstance& other); virtual QStringList extraArguments(); @@ -278,7 +273,7 @@ class BaseInstance : public QObject { bool removeLinkedInstanceId(const QString& id); bool isLinkedToInstanceId(const QString& id) const; - bool isLegacy(); + bool isLegacy() const; protected: void changeStatus(Status newStatus); @@ -303,7 +298,7 @@ class BaseInstance : public QObject { void statusChanged(Status from, Status to); protected slots: - void iconUpdated(QString key); + void iconUpdated(const QString& key); protected: /* data */ QString m_rootDir; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 7d4430fd2..2c0032258 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -41,8 +41,6 @@ set(CORE_SOURCES PSaveFile.h # Basic instance manipulation tasks (derived from InstanceTask) - InstanceCreationTask.h - InstanceCreationTask.cpp InstanceCopyPrefs.h InstanceCopyPrefs.cpp InstanceCopyTask.h diff --git a/launcher/InstanceCreationTask.cpp b/launcher/InstanceCreationTask.cpp deleted file mode 100644 index e58926660..000000000 --- a/launcher/InstanceCreationTask.cpp +++ /dev/null @@ -1,142 +0,0 @@ -#include "InstanceCreationTask.h" - -#include -#include - -#include "Application.h" -#include "InstanceTask.h" -#include "minecraft/MinecraftLoadAndCheck.h" -#include "tasks/SequentialTask.h" - -bool InstanceCreationTask::abort() -{ - if (!canAbort()) { - return false; - } - - m_abort = true; - if (m_gameFilesTask) { - return m_gameFilesTask->abort(); - } - - return InstanceTask::abort(); -} - -void InstanceCreationTask::executeTask() -{ - setAbortable(true); - - if (updateInstance()) { - emitSucceeded(); - return; - } - - // When the user aborted in the update stage. - if (m_abort) { - emitAborted(); - return; - } - - m_instance = createInstance(); - if (!m_instance) { - if (m_abort) { - return; - } - - qWarning() << "Instance creation failed!"; - if (!m_error_message.isEmpty()) { - qWarning() << "Reason:" << m_error_message; - emitFailed(tr("Error while creating new instance:\n%1").arg(m_error_message)); - } else { - emitFailed(tr("Error while creating new instance.")); - } - - return; - } - - // If this is set, it means we're updating an instance. So, we now need to remove the - // files scheduled to, and we'd better not let the user abort in the middle of it, since it'd - // put the instance in an invalid state. - if (shouldOverride()) { - bool deleteFailed = false; - - setAbortable(false); - setStatus(tr("Removing old conflicting files...")); - qDebug() << "Removing old files"; - - for (const QString& path : m_filesToRemove) { - if (!QFile::exists(path)) { - continue; - } - - qDebug() << "Removing" << path; - - if (!QFile::remove(path)) { - qCritical() << "Could not remove" << path; - deleteFailed = true; - } - } - - if (deleteFailed) { - emitFailed(tr("Failed to remove old conflicting files.")); - return; - } - } - - if (!m_abort) { - if (!APPLICATION->settings()->get("DownloadGameFilesDuringInstanceCreation").toBool()) { - emitSucceeded(); - return; - } - setAbortable(true); - setAbortButtonText(tr("Skip")); - qDebug() << "Downloading game files"; - - auto updateTasks = m_instance->createUpdateTask(); - if (updateTasks.isEmpty()) { - emitSucceeded(); - return; - } - auto task = makeShared(); - task->addTask(makeShared(m_instance.get(), Net::Mode::Online)); - for (const auto& t : updateTasks) { - task->addTask(t); - } - connect(task.get(), &Task::finished, this, [this, task] { - if (task->wasSuccessful() || m_abort) { - emitSucceeded(); - } else { - emitFailed(tr("Could not download game files: %1").arg(task->failReason())); - } - }); - propagateFromOther(task.get()); - setDetails(tr("Downloading game files")); - - m_gameFilesTask = task; - m_gameFilesTask->start(); - } -} - -void InstanceCreationTask::scheduleToDelete(QWidget* parent, const QDir& dir, const QString& path, bool checkDisabled) -{ - if (path.isEmpty()) { - return; - } - if (path.startsWith("saves/")) { - if (m_shouldDeleteSaves == ShouldDeleteSaves::NotAsked) { - m_shouldDeleteSaves = askIfShouldDeleteSaves(parent); - } - if (m_shouldDeleteSaves == ShouldDeleteSaves::No) { - return; - } - } - qDebug() << "Scheduling" << path << "for removal"; - m_filesToRemove.append(dir.absoluteFilePath(path)); - if (checkDisabled) { - if (path.endsWith(".disabled")) { // remove it if it was enabled/disabled by user - m_filesToRemove.append(dir.absoluteFilePath(path.chopped(9))); - } else { - m_filesToRemove.append(dir.absoluteFilePath(path + ".disabled")); - } - } -} diff --git a/launcher/InstanceCreationTask.h b/launcher/InstanceCreationTask.h deleted file mode 100644 index 39acaf8b2..000000000 --- a/launcher/InstanceCreationTask.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "BaseVersion.h" -#include "InstanceTask.h" -#include "minecraft/MinecraftInstance.h" - -class InstanceCreationTask : public InstanceTask { - Q_OBJECT - public: - InstanceCreationTask() = default; - virtual ~InstanceCreationTask() = default; - - bool abort() override; - - protected: - void executeTask() final override; - - /** - * Tries to update an already existing instance. - * - * This can be implemented by subclasses to provide a way of updating an already existing - * instance, according to that implementation's concept of 'identity' (i.e. instances that - * are updates / downgrades of one another). - * - * If this returns true, createInstance() will not run, so you should do all update steps in here. - * Otherwise, createInstance() is run as normal. - */ - virtual bool updateInstance() { return false; }; - - /** - * Creates a new instance. - * - * Returns the instance if it was created or nullptr otherwise. - */ - virtual std::unique_ptr createInstance() { return nullptr; } - - QString getError() const { return m_error_message; } - - protected: - void setError(const QString& message) { m_error_message = message; }; - void scheduleToDelete(QWidget* parent, const QDir& dir, const QString& path, bool checkDisabled = false); - - protected: - bool m_abort = false; - - QStringList m_filesToRemove; - ShouldDeleteSaves m_shouldDeleteSaves; - - private: - QString m_error_message; - std::unique_ptr m_instance; - Task::Ptr m_gameFilesTask; -}; diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 9b04f99b6..79fd9bc0a 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -58,19 +58,22 @@ #include #include #include +#include -InstanceImportTask::InstanceImportTask(const QUrl& sourceUrl, QWidget* parent, QMap&& extra_info) - : m_sourceUrl(sourceUrl), m_extra_info(extra_info), m_parent(parent) +InstanceImportTask::InstanceImportTask(QUrl sourceUrl, QWidget* parent, QMap extraInfo) + : m_sourceUrl(std::move(sourceUrl)), m_extra_info(std::move(extraInfo)), m_parent(parent) {} bool InstanceImportTask::abort() { - if (!canAbort()) + if (!canAbort()) { return false; + } bool wasAborted = false; - if (m_task) + if (m_task) { wasAborted = m_task->abort(); + } return wasAborted; } @@ -107,16 +110,20 @@ void InstanceImportTask::downloadFromUrl() m_task.reset(filesNetJob); filesNetJob->start(); } +namespace { -QString cleanPath(QString path) +QString cleanPath(const QString& path) { - if (path == ".") + if (path == ".") { return QString(); + } QString result = path; - if (result.startsWith("./")) + if (result.startsWith("./")) { result = result.mid(2); + } return result; } +} // namespace void InstanceImportTask::processZipPack() { @@ -187,7 +194,7 @@ void InstanceImportTask::processZipPack() connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) { progressStep->state = TaskStepState::Failed; stepProgress(*progressStep); - emitFailed(reason); + emitFailed(std::move(reason)); }); connect(zipTask.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress); @@ -196,7 +203,7 @@ void InstanceImportTask::processZipPack() stepProgress(*progressStep); }); connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) { - progressStep->status = status; + progressStep->status = std::move(status); stepProgress(*progressStep); }); connect(zipTask.get(), &Task::warningLogged, this, [this](const QString& line) { m_Warnings.append(line); }); @@ -251,16 +258,20 @@ void InstanceImportTask::extractFinished() } } -bool installIcon(QString root, QString instIconKey) +namespace { + +bool installIcon(const QString& root, const QString& instIconKey) { auto importIconPath = IconUtils::findBestIconIn(root, instIconKey); - if (importIconPath.isNull() || !QFile::exists(importIconPath)) + if (importIconPath.isNull() || !QFile::exists(importIconPath)) { importIconPath = IconUtils::findBestIconIn(root, "icon.png"); - if (importIconPath.isNull() || !QFile::exists(importIconPath)) + } + if (importIconPath.isNull() || !QFile::exists(importIconPath)) { importIconPath = IconUtils::findBestIconIn(FS::PathCombine(root, "overrides"), "icon.png"); + } if (!importIconPath.isNull() && QFile::exists(importIconPath)) { // import icon - auto iconList = APPLICATION->icons(); + auto* iconList = APPLICATION->icons(); if (iconList->iconFileExists(instIconKey)) { iconList->deleteIcon(instIconKey); } @@ -269,32 +280,35 @@ bool installIcon(QString root, QString instIconKey) } return false; } +} // namespace void InstanceImportTask::processFlame() { - shared_qobject_ptr inst_creation_task = nullptr; + shared_qobject_ptr instCreationTask = nullptr; if (!m_extra_info.isEmpty()) { - auto pack_id_it = m_extra_info.constFind("pack_id"); - Q_ASSERT(pack_id_it != m_extra_info.constEnd()); - auto pack_id = pack_id_it.value(); + auto packIdIt = m_extra_info.constFind("pack_id"); + Q_ASSERT(packIdIt != m_extra_info.constEnd()); + const auto& packId = packIdIt.value(); - auto pack_version_id_it = m_extra_info.constFind("pack_version_id"); - Q_ASSERT(pack_version_id_it != m_extra_info.constEnd()); - auto pack_version_id = pack_version_id_it.value(); + auto packVersionIdIt = m_extra_info.constFind("pack_version_id"); + Q_ASSERT(packVersionIdIt != m_extra_info.constEnd()); + const auto& packVersionId = packVersionIdIt.value(); - QString original_instance_id; - auto original_instance_id_it = m_extra_info.constFind("original_instance_id"); - if (original_instance_id_it != m_extra_info.constEnd()) - original_instance_id = original_instance_id_it.value(); + QString originalInstanceId; + auto originalInstanceIdIt = m_extra_info.constFind("original_instance_id"); + if (originalInstanceIdIt != m_extra_info.constEnd()) { + originalInstanceId = originalInstanceIdIt.value(); + } - inst_creation_task = - makeShared(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); + instCreationTask = + makeShared(m_stagingPath, m_globalSettings, m_parent, packId, packVersionId, originalInstanceId); } else { // FIXME: Find a way to get IDs in directly imported ZIPs - inst_creation_task = makeShared(m_stagingPath, m_globalSettings, m_parent, QString(), QString()); + instCreationTask = makeShared(m_stagingPath, m_globalSettings, m_parent, QString(), QString()); } - inst_creation_task->setName(*this); + instCreationTask->setName(modifiedName()); + instCreationTask->setOriginalName(originalName(), version()); // if the icon was specified by user, use that. otherwise pull icon from the pack if (m_instIcon == "default") { auto iconKey = QString("Flame_%1_Icon").arg(name()); @@ -303,30 +317,30 @@ void InstanceImportTask::processFlame() m_instIcon = iconKey; } } - inst_creation_task->setIcon(m_instIcon); - inst_creation_task->setGroup(m_instGroup); - inst_creation_task->setConfirmUpdate(shouldConfirmUpdate()); + instCreationTask->setIcon(m_instIcon); + instCreationTask->setGroup(m_instGroup); + instCreationTask->setConfirmUpdate(shouldConfirmUpdate()); - auto weak = inst_creation_task.toWeakRef(); - connect(inst_creation_task.get(), &Task::succeeded, this, [this, weak] { + auto weak = instCreationTask.toWeakRef(); + connect(instCreationTask.get(), &Task::succeeded, this, [this, weak] { if (auto sp = weak.lock()) { setOverride(sp->shouldOverride(), sp->originalInstanceID()); } emitSucceeded(); }); - connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed); - connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress); - connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress); - connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus); - connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails); + connect(instCreationTask.get(), &Task::failed, this, &InstanceImportTask::emitFailed); + connect(instCreationTask.get(), &Task::progress, this, &InstanceImportTask::setProgress); + connect(instCreationTask.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress); + connect(instCreationTask.get(), &Task::status, this, &InstanceImportTask::setStatus); + connect(instCreationTask.get(), &Task::details, this, &InstanceImportTask::setDetails); - connect(inst_creation_task.get(), &Task::aborted, this, &InstanceImportTask::emitAborted); - connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable); - connect(inst_creation_task.get(), &Task::abortButtonTextChanged, this, &Task::setAbortButtonText); + connect(instCreationTask.get(), &Task::aborted, this, &InstanceImportTask::emitAborted); + connect(instCreationTask.get(), &Task::abortStatusChanged, this, &Task::setAbortable); + connect(instCreationTask.get(), &Task::abortButtonTextChanged, this, &Task::setAbortButtonText); - connect(inst_creation_task.get(), &Task::warningLogged, this, [this](const QString& line) { m_Warnings.append(line); }); + connect(instCreationTask.get(), &Task::warningLogged, this, [this](const QString& line) { m_Warnings.append(line); }); - m_task.reset(inst_creation_task); + m_task.reset(instCreationTask); setAbortable(true); m_task->start(); } @@ -365,36 +379,39 @@ void InstanceImportTask::processMultiMC() void InstanceImportTask::processModrinth() { - shared_qobject_ptr inst_creation_task = nullptr; + shared_qobject_ptr instCreationTask = nullptr; if (!m_extra_info.isEmpty()) { - auto pack_id_it = m_extra_info.constFind("pack_id"); - Q_ASSERT(pack_id_it != m_extra_info.constEnd()); - auto pack_id = pack_id_it.value(); + auto packIdIt = m_extra_info.constFind("pack_id"); + Q_ASSERT(packIdIt != m_extra_info.constEnd()); + const auto& packId = packIdIt.value(); - QString pack_version_id; - auto pack_version_id_it = m_extra_info.constFind("pack_version_id"); - if (pack_version_id_it != m_extra_info.constEnd()) - pack_version_id = pack_version_id_it.value(); + QString packVersionId; + auto packVersionIdIt = m_extra_info.constFind("pack_version_id"); + if (packVersionIdIt != m_extra_info.constEnd()) { + packVersionId = packVersionIdIt.value(); + } - QString original_instance_id; - auto original_instance_id_it = m_extra_info.constFind("original_instance_id"); - if (original_instance_id_it != m_extra_info.constEnd()) - original_instance_id = original_instance_id_it.value(); + QString originalInstanceId; + auto originalInstanceIdIt = m_extra_info.constFind("original_instance_id"); + if (originalInstanceIdIt != m_extra_info.constEnd()) { + originalInstanceId = originalInstanceIdIt.value(); + } - inst_creation_task = - makeShared(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); + instCreationTask = + makeShared(m_stagingPath, m_globalSettings, m_parent, packId, packVersionId, originalInstanceId); } else { - QString pack_id; + QString packId; if (!m_sourceUrl.isEmpty()) { static const QRegularExpression s_regex(R"(data\/([^\/]*)\/versions)"); - pack_id = s_regex.match(m_sourceUrl.toString()).captured(1); + packId = s_regex.match(m_sourceUrl.toString()).captured(1); } // FIXME: Find a way to get the ID in directly imported ZIPs - inst_creation_task = makeShared(m_stagingPath, m_globalSettings, m_parent, pack_id); + instCreationTask = makeShared(m_stagingPath, m_globalSettings, m_parent, packId); } - inst_creation_task->setName(*this); + instCreationTask->setName(modifiedName()); + instCreationTask->setOriginalName(originalName(), version()); // if the icon was specified by user, use that. otherwise pull icon from the pack if (m_instIcon == "default") { auto iconKey = QString("Modrinth_%1_Icon").arg(name()); @@ -403,30 +420,30 @@ void InstanceImportTask::processModrinth() m_instIcon = iconKey; } } - inst_creation_task->setIcon(m_instIcon); - inst_creation_task->setGroup(m_instGroup); - inst_creation_task->setConfirmUpdate(shouldConfirmUpdate()); + instCreationTask->setIcon(m_instIcon); + instCreationTask->setGroup(m_instGroup); + instCreationTask->setConfirmUpdate(shouldConfirmUpdate()); - auto weak = inst_creation_task.toWeakRef(); - connect(inst_creation_task.get(), &Task::succeeded, this, [this, weak] { + auto weak = instCreationTask.toWeakRef(); + connect(instCreationTask.get(), &Task::succeeded, this, [this, weak] { if (auto sp = weak.lock()) { setOverride(sp->shouldOverride(), sp->originalInstanceID()); } emitSucceeded(); }); - connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed); - connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress); - connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress); - connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus); - connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails); + connect(instCreationTask.get(), &Task::failed, this, &InstanceImportTask::emitFailed); + connect(instCreationTask.get(), &Task::progress, this, &InstanceImportTask::setProgress); + connect(instCreationTask.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress); + connect(instCreationTask.get(), &Task::status, this, &InstanceImportTask::setStatus); + connect(instCreationTask.get(), &Task::details, this, &InstanceImportTask::setDetails); - connect(inst_creation_task.get(), &Task::aborted, this, &InstanceImportTask::emitAborted); - connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable); - connect(inst_creation_task.get(), &Task::abortButtonTextChanged, this, &Task::setAbortButtonText); + connect(instCreationTask.get(), &Task::aborted, this, &InstanceImportTask::emitAborted); + connect(instCreationTask.get(), &Task::abortStatusChanged, this, &Task::setAbortable); + connect(instCreationTask.get(), &Task::abortButtonTextChanged, this, &Task::setAbortButtonText); - connect(inst_creation_task.get(), &Task::warningLogged, this, [this](const QString& line) { m_Warnings.append(line); }); + connect(instCreationTask.get(), &Task::warningLogged, this, [this](const QString& line) { m_Warnings.append(line); }); - m_task.reset(inst_creation_task); + m_task.reset(instCreationTask); setAbortable(true); m_task->start(); } diff --git a/launcher/InstanceImportTask.h b/launcher/InstanceImportTask.h index c92e229a0..0b871861e 100644 --- a/launcher/InstanceImportTask.h +++ b/launcher/InstanceImportTask.h @@ -43,19 +43,20 @@ class InstanceImportTask : public InstanceTask { Q_OBJECT public: - explicit InstanceImportTask(const QUrl& sourceUrl, QWidget* parent = nullptr, QMap&& extra_info = {}); - virtual ~InstanceImportTask() = default; + explicit InstanceImportTask(QUrl sourceUrl, QWidget* parent = nullptr, QMap extraInfo = {}); + ~InstanceImportTask() override = default; bool abort() override; protected: //! Entry point for tasks. - virtual void executeTask() override; + void executeTask() override; private: void processMultiMC(); void processTechnic(); void processFlame(); void processModrinth(); + void downloadFromUrl(); private slots: void processZipPack(); @@ -65,7 +66,7 @@ class InstanceImportTask : public InstanceTask { QUrl m_sourceUrl; QString m_archivePath; Task::Ptr m_task; - enum class ModpackType { + enum class ModpackType : std::uint8_t { Unknown, MultiMC, Technic, @@ -79,5 +80,4 @@ class InstanceImportTask : public InstanceTask { // FIXME: nuke QWidget* m_parent; - void downloadFromUrl(); }; diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index 1339499c7..3cdfe7942 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include "BaseInstance.h" #include "ExponentialSeries.h" @@ -62,7 +63,7 @@ #include #endif -const static int GROUP_FILE_FORMAT_VERSION = 1; +const static int g_GROUP_FILE_FORMAT_VERSION = 1; InstanceList::InstanceList(SettingsObject* settings, const QString& instDir, QObject* parent) : QAbstractListModel(parent), m_globalSettings(settings) @@ -82,8 +83,6 @@ InstanceList::InstanceList(SettingsObject* settings, const QString& instDir, QOb m_watcher->addPath(m_instDir); } -InstanceList::~InstanceList() {} - Qt::DropActions InstanceList::supportedDragActions() const { return Qt::MoveAction; @@ -100,10 +99,7 @@ bool InstanceList::canDropMimeData(const QMimeData* data, [[maybe_unused]] int column, [[maybe_unused]] const QModelIndex& parent) const { - if (data && data->hasFormat("application/x-instanceid")) { - return true; - } - return false; + return (data != nullptr) && data->hasFormat("application/x-instanceid"); } bool InstanceList::dropMimeData(const QMimeData* data, @@ -112,10 +108,7 @@ bool InstanceList::dropMimeData(const QMimeData* data, [[maybe_unused]] int column, [[maybe_unused]] const QModelIndex& parent) { - if (data && data->hasFormat("application/x-instanceid")) { - return true; - } - return false; + return (data != nullptr) && data->hasFormat("application/x-instanceid"); } QStringList InstanceList::mimeTypes() const @@ -127,7 +120,7 @@ QStringList InstanceList::mimeTypes() const QMimeData* InstanceList::mimeData(const QModelIndexList& indexes) const { - auto mimeData = QAbstractListModel::mimeData(indexes); + auto* mimeData = QAbstractListModel::mimeData(indexes); if (indexes.size() == 1) { auto instanceId = data(indexes[0], InstanceIDRole).toString(); mimeData->setData("application/x-instanceid", instanceId.toUtf8()); @@ -138,9 +131,10 @@ QMimeData* InstanceList::mimeData(const QModelIndexList& indexes) const QStringList InstanceList::getLinkedInstancesById(const QString& id) const { QStringList linkedInstances; - for (auto& inst : m_instances) { - if (inst->isLinkedToInstanceId(id)) + for (const auto& inst : m_instances) { + if (inst->isLinkedToInstanceId(id)) { linkedInstances.append(inst->id()); + } } return linkedInstances; } @@ -154,8 +148,9 @@ int InstanceList::rowCount(const QModelIndex& parent) const QModelIndex InstanceList::index(int row, int column, const QModelIndex& parent) const { Q_UNUSED(parent); - if (row < 0 || row >= count()) - return QModelIndex(); + if (row < 0 || row >= count()) { + return {}; + } return createIndex(row, column, m_instances.at(row).get()); } @@ -164,7 +159,7 @@ QVariant InstanceList::data(const QModelIndex& index, int role) const if (!index.isValid()) { return QVariant(); } - BaseInstance* pdata = static_cast(index.internalPointer()); + auto* pdata = static_cast(index.internalPointer()); switch (role) { case InstancePointerRole: { QVariant v = QVariant::fromValue((void*)pdata); @@ -204,7 +199,7 @@ bool InstanceList::setData(const QModelIndex& index, const QVariant& value, int if (role != Qt::EditRole) { return false; } - BaseInstance* pdata = static_cast(index.internalPointer()); + auto* pdata = static_cast(index.internalPointer()); auto newName = value.toString(); if (pdata->name() == newName) { return true; @@ -224,23 +219,24 @@ Qt::ItemFlags InstanceList::flags(const QModelIndex& index) const GroupId InstanceList::getInstanceGroup(const InstanceId& id) const { - auto inst = getInstanceById(id); + auto* inst = getInstanceById(id); if (!inst) { - return GroupId(); + return {}; } auto iter = m_instanceGroupIndex.find(inst->id()); if (iter != m_instanceGroupIndex.end()) { return *iter; } - return GroupId(); + return {}; } void InstanceList::setInstanceGroup(const InstanceId& id, GroupId name) { - if (name.isEmpty() && !name.isNull()) + if (name.isEmpty() && !name.isNull()) { name = QString(); + } - auto inst = getInstanceById(id); + auto* inst = getInstanceById(id); if (!inst) { qDebug() << "Attempt to set a null instance's group"; return; @@ -287,19 +283,22 @@ void InstanceList::deleteGroup(const GroupId& name) qDebug() << "Remove" << instID << "from group" << name; removed = true; auto idx = getInstIndex(instance.get()); - if (idx >= 0) + if (idx >= 0) { emit dataChanged(index(idx), index(idx), { GroupRole }); + } } } - if (removed) + if (removed) { saveGroupList(); + } } void InstanceList::renameGroup(const QString& src, const QString& dst) { m_groupNameCache.remove(src); - if (m_collapsedGroups.remove(src)) + if (m_collapsedGroups.remove(src)) { m_collapsedGroups.insert(dst); + } bool modified = false; qDebug() << "Rename group" << src << "to" << dst; @@ -312,12 +311,14 @@ void InstanceList::renameGroup(const QString& src, const QString& dst) qDebug() << "Set" << instID << "group to" << dst; modified = true; auto idx = getInstIndex(instance.get()); - if (idx >= 0) + if (idx >= 0) { emit dataChanged(index(idx), index(idx), { GroupRole }); + } } } - if (modified) + if (modified) { saveGroupList(); + } } bool InstanceList::isGroupCollapsed(const QString& group) @@ -327,7 +328,7 @@ bool InstanceList::isGroupCollapsed(const QString& group) bool InstanceList::trashInstance(const InstanceId& id) { - auto inst = getInstanceById(id); + auto* inst = getInstanceById(id); if (!inst) { qWarning() << "Cannot trash instance" << id << ". No such instance is present (deleted externally?)."; return false; @@ -338,7 +339,7 @@ bool InstanceList::trashInstance(const InstanceId& id) qDebug() << "Will trash instance" << id; QString trashedLoc; - if (m_instanceGroupIndex.remove(id)) { + if (m_instanceGroupIndex.remove(id) != 0) { decreaseGroupCount(cachedGroupId); saveGroupList(); } @@ -422,7 +423,7 @@ bool InstanceList::undoTrashInstance() void InstanceList::deleteInstance(const InstanceId& id) { - auto inst = getInstanceById(id); + auto* inst = getInstanceById(id); if (!inst) { qWarning() << "Cannot delete instance" << id << ". No such instance is present (deleted externally?)."; return; @@ -430,7 +431,7 @@ void InstanceList::deleteInstance(const InstanceId& id) QString cachedGroupId = m_instanceGroupIndex[id]; - if (m_instanceGroupIndex.remove(id)) { + if (m_instanceGroupIndex.remove(id) != 0) { decreaseGroupCount(cachedGroupId); saveGroupList(); } @@ -452,11 +453,12 @@ void InstanceList::deleteInstance(const InstanceId& id) } } -static QMap getIdMapping(const std::vector>& list) +namespace { +QMap getIdMapping(const std::vector>& list) { QMap out; int i = 0; - for (auto& item : list) { + for (const auto& item : list) { auto id = item->id(); if (out.contains(id)) { qWarning() << "Duplicate ID" << id << "in instance list"; @@ -466,6 +468,7 @@ static QMap getIdMapping(const std::vector InstanceList::discoverInstances() { @@ -475,8 +478,9 @@ QList InstanceList::discoverInstances() while (iter.hasNext()) { QString subDir = iter.next(); QFileInfo dirInfo(subDir); - if (!QFileInfo(FS::PathCombine(subDir, "instance.cfg")).exists()) + if (!QFileInfo(FS::PathCombine(subDir, "instance.cfg")).exists()) { continue; + } // if it is a symlink, ignore it if it goes to the instance folder if (dirInfo.isSymLink()) { QFileInfo targetInfo(dirInfo.symLinkTarget()); @@ -490,7 +494,7 @@ QList InstanceList::discoverInstances() out.append(id); qInfo() << "Found instance ID" << id; } - instanceSet = QSet(out.begin(), out.end()); + m_instanceSet = QSet(out.begin(), out.end()); m_instancesProbed = true; return out; } @@ -518,38 +522,38 @@ InstanceList::InstListError InstanceList::loadList() // get the list of removed instances and sort it by their original index, from last to first auto deadList = existingIds.values(); auto orderSortPredicate = [](const InstanceLocator& a, const InstanceLocator& b) -> bool { return a.second > b.second; }; - std::sort(deadList.begin(), deadList.end(), orderSortPredicate); + std::ranges::sort(deadList, orderSortPredicate); // remove the contiguous ranges of rows - int front_bookmark = -1; - int back_bookmark = -1; + int frontBookmark = -1; + int backBookmark = -1; int currentItem = -1; - auto removeNow = [this, &front_bookmark, &back_bookmark, ¤tItem]() { - beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark); - m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1); + auto removeNow = [this, &frontBookmark, &backBookmark, ¤tItem]() { + beginRemoveRows(QModelIndex(), frontBookmark, backBookmark); + m_instances.erase(m_instances.begin() + frontBookmark, m_instances.begin() + backBookmark + 1); endRemoveRows(); - front_bookmark = -1; - back_bookmark = currentItem; + frontBookmark = -1; + backBookmark = currentItem; }; for (auto& removedItem : deadList) { - auto instPtr = removedItem.first; + auto* instPtr = removedItem.first; instPtr->invalidate(); currentItem = removedItem.second; - if (back_bookmark == -1) { + if (backBookmark == -1) { // no bookmark yet - back_bookmark = currentItem; - } else if (currentItem == front_bookmark - 1) { + backBookmark = currentItem; + } else if (currentItem == frontBookmark - 1) { // part of contiguous sequence, continue } else { // seam between previous and current item removeNow(); } - front_bookmark = currentItem; + frontBookmark = currentItem; } - if (back_bookmark != -1) { + if (backBookmark != -1) { removeNow(); } } - if (newList.size()) { + if (!newList.empty()) { add(newList); } m_dirty = false; @@ -559,9 +563,9 @@ InstanceList::InstListError InstanceList::loadList() void InstanceList::updateTotalPlayTime() { - totalPlayTime = 0; + m_totalPlayTime = 0; for (const auto& itr : m_instances) { - totalPlayTime += itr->totalTimePlayed(); + m_totalPlayTime += static_cast(itr->totalTimePlayed()); } } @@ -607,11 +611,12 @@ void InstanceList::providerUpdated() } } -BaseInstance* InstanceList::getInstanceById(QString instId) const +BaseInstance* InstanceList::getInstanceById(const QString& instId) const { - if (instId.isEmpty()) + if (instId.isEmpty()) { return nullptr; - for (auto& inst : m_instances) { + } + for (const auto& inst : m_instances) { if (inst->id() == instId) { return inst.get(); } @@ -619,14 +624,16 @@ BaseInstance* InstanceList::getInstanceById(QString instId) const return nullptr; } -BaseInstance* InstanceList::getInstanceByManagedName(const QString& managed_name) const +BaseInstance* InstanceList::getInstanceByManagedName(const QString& managedName) const { - if (managed_name.isEmpty()) + if (managedName.isEmpty()) { return {}; + } - for (auto& instance : m_instances) { - if (instance->getManagedPackName() == managed_name) + for (const auto& instance : m_instances) { + if (instance->getManagedPackName() == managedName) { return instance.get(); + } } return {}; @@ -669,11 +676,11 @@ std::unique_ptr InstanceList::loadInstance(const InstanceId& id) instanceSettings->registerSetting("InstanceType", ""); - QString inst_type = instanceSettings->get("InstanceType").toString(); + const QString instType = instanceSettings->get("InstanceType").toString(); // NOTE: Some launcher versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a // OneSix instance - if (inst_type == "OneSix" || inst_type.isEmpty()) { + if (instType == "OneSix" || instType.isEmpty()) { inst.reset(new MinecraftInstance(m_globalSettings, std::move(instanceSettings), instanceRoot)); } else { inst.reset(new NullInstance(m_globalSettings, std::move(instanceSettings), instanceRoot)); @@ -681,24 +688,27 @@ std::unique_ptr InstanceList::loadInstance(const InstanceId& id) qDebug() << "Loaded instance" << inst->name() << "from" << inst->instanceRoot(); auto shortcut = inst->shortcuts(); - if (!shortcut.isEmpty()) + if (!shortcut.isEmpty()) { qDebug() << "Loaded" << shortcut.size() << "shortcut(s) for instance" << inst->name(); + } return inst; } void InstanceList::increaseGroupCount(const QString& group) { - if (group.isEmpty()) + if (group.isEmpty()) { return; + } ++m_groupNameCache[group]; } void InstanceList::decreaseGroupCount(const QString& group) { - if (group.isEmpty()) + if (group.isEmpty()) { return; + } if (--m_groupNameCache[group] < 1) { m_groupNameCache.remove(group); @@ -718,15 +728,16 @@ void InstanceList::saveGroupList() QMap> reverseGroupMap; for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++) { const QString& id = iter.key(); - QString group = iter.value(); - if (group.isEmpty()) + const QString& group = iter.value(); + if (group.isEmpty()) { continue; - if (!instanceSet.contains(id)) { + } + if (!m_instanceSet.contains(id)) { qDebug() << "Skipping saving missing instance" << id << "to groups list."; continue; } - if (!reverseGroupMap.count(group)) { + if (!reverseGroupMap.contains(group)) { QSet set; set.insert(id); reverseGroupMap[group] = set; @@ -740,11 +751,11 @@ void InstanceList::saveGroupList() QJsonObject groupsArr; for (auto iter = reverseGroupMap.begin(); iter != reverseGroupMap.end(); iter++) { auto list = iter.value(); - auto name = iter.key(); + const auto& name = iter.key(); QJsonObject groupObj; QJsonArray instanceArr; groupObj.insert("hidden", QJsonValue(m_collapsedGroups.contains(name))); - for (auto item : list) { + for (const auto& item : list) { instanceArr.append(QJsonValue(item)); } groupObj.insert("instances", instanceArr); @@ -773,8 +784,9 @@ void InstanceList::loadGroupList() QString groupFileName = m_instDir + "/instgroups.json"; // if there's no group file, fail - if (!QFileInfo(groupFileName).exists()) + if (!QFileInfo(groupFileName).exists()) { return; + } QByteArray jsonData; try { @@ -804,8 +816,9 @@ void InstanceList::loadGroupList() QJsonObject rootObj = jsonDoc.object(); // Make sure the format version matches, otherwise fail. - if (rootObj.value("formatVersion").toVariant().toInt() != GROUP_FILE_FORMAT_VERSION) + if (rootObj.value("formatVersion").toVariant().toInt() != g_GROUP_FILE_FORMAT_VERSION) { return; + } // Get the groups. if it's not an object, fail if (!rootObj.value("groups").isObject()) { @@ -841,8 +854,9 @@ void InstanceList::loadGroupList() } auto hidden = groupObj.value("hidden").toBool(false); - if (hidden) + if (hidden) { m_collapsedGroups.insert(groupName); + } // Iterate through the list of instances in the group. QJsonArray instancesArray = groupObj.value("instances").toArray(); @@ -872,7 +886,7 @@ void InstanceList::instanceDirContentsChanged(const QString& path) emit instancesChanged(); } -void InstanceList::on_InstFolderChanged([[maybe_unused]] const Setting& setting, QVariant value) +void InstanceList::on_InstFolderChanged([[maybe_unused]] const Setting& setting, const QVariant& value) { QString newInstDir = QDir(value.toString()).canonicalPath(); if (newInstDir != m_instDir) { @@ -899,13 +913,16 @@ void InstanceList::on_GroupStateChanged(const QString& group, bool collapsed) saveGroupList(); } +namespace { + class InstanceStaging : public Task { Q_OBJECT const unsigned minBackoff = 1; const unsigned maxBackoff = 16; public: - InstanceStaging(InstanceList* parent, InstanceTask* child, SettingsObject* settings) : m_parent(parent), backoff(minBackoff, maxBackoff) + InstanceStaging(InstanceList* parent, InstanceTask* child, SettingsObject* settings) + : m_parent(parent), m_backoff(minBackoff, maxBackoff) { m_stagingPath = parent->getStagedInstancePath(); @@ -954,8 +971,8 @@ class InstanceStaging : public Task { private slots: void childSucceeded() { - unsigned sleepTime = backoff(); - if (m_parent->commitStagedInstance(m_stagingPath, *m_child, m_child->group(), *m_child)) { + const unsigned sleepTime = m_backoff(); + if (m_parent->commitStagedInstance(m_stagingPath, *m_child, m_child->group())) { m_backoffTimer.stop(); emitSucceeded(); return; @@ -972,14 +989,14 @@ class InstanceStaging : public Task { void childFailed(const QString& reason) { m_backoffTimer.stop(); - m_parent->destroyStagingPath(m_stagingPath); + FS::deletePath(m_stagingPath); emitFailed(reason); } void childAborted() { m_backoffTimer.stop(); - m_parent->destroyStagingPath(m_stagingPath); + FS::deletePath(m_stagingPath); emitAborted(); } @@ -990,11 +1007,12 @@ class InstanceStaging : public Task { * Basically, it starts messing things up while the launcher is extracting/creating instances * and causes that horrible failure that is NTFS to lock files in place because they are open. */ - ExponentialSeries backoff; + ExponentialSeries m_backoff; QString m_stagingPath; std::unique_ptr m_child; QTimer m_backoffTimer; }; +} // namespace Task* InstanceList::wrapInstanceTask(InstanceTask* task) { @@ -1009,37 +1027,37 @@ QString InstanceList::getStagedInstancePath() int tries = 0; do { - if (++tries > 256) + if (++tries > 256) { return {}; + } const QString key = QUuid::createUuid().toString(QUuid::Id128).left(6); result = FS::PathCombine(tempRoot, key); } while (QFileInfo::exists(result)); - if (!QDir::current().mkpath(result)) + if (!QDir::current().mkpath(result)) { return {}; + } #ifdef Q_OS_WIN32 SetFileAttributesA(tempRoot.toStdString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED); #endif return result; } -bool InstanceList::commitStagedInstance(const QString& path, - const InstanceName& instanceName, - QString groupName, - const InstanceTask& commiting) +bool InstanceList::commitStagedInstance(const QString& path, const InstanceTask& instanceTask, QString groupName) { - if (groupName.isEmpty() && !groupName.isNull()) + if (groupName.isEmpty() && !groupName.isNull()) { groupName = QString(); + } QString instID; - auto should_override = commiting.shouldOverride(); + auto shouldOverride = instanceTask.shouldOverride(); - if (should_override) { - instID = commiting.originalInstanceID(); + if (shouldOverride) { + instID = instanceTask.originalInstanceID(); } else { - instID = FS::DirNameFromString(instanceName.modifiedName(), m_instDir); + instID = FS::DirNameFromString(instanceTask.modifiedName(), m_instDir); } Q_ASSERT(!instID.isEmpty()); @@ -1048,7 +1066,7 @@ bool InstanceList::commitStagedInstance(const QString& path, WatchLock lock(m_watcher, m_instDir); QString destination = FS::PathCombine(m_instDir, instID); - if (should_override) { + if (shouldOverride) { if (!FS::overrideFolder(destination, path)) { qWarning() << "Failed to override" << path << "to" << destination; return false; @@ -1063,7 +1081,7 @@ bool InstanceList::commitStagedInstance(const QString& path, increaseGroupCount(groupName); } - instanceSet.insert(instID); + m_instanceSet.insert(instID); emit instancesChanged(); emit instanceSelectRequest(instID); @@ -1073,15 +1091,10 @@ bool InstanceList::commitStagedInstance(const QString& path, return true; } -bool InstanceList::destroyStagingPath(const QString& keyPath) -{ - return FS::deletePath(keyPath); -} - int InstanceList::getTotalPlayTime() { updateTotalPlayTime(); - return totalPlayTime; + return m_totalPlayTime; } #include "InstanceList.moc" diff --git a/launcher/InstanceList.h b/launcher/InstanceList.h index f0a92d273..e3ecabca3 100644 --- a/launcher/InstanceList.h +++ b/launcher/InstanceList.h @@ -41,20 +41,20 @@ #include #include #include +#include #include "BaseInstance.h" class QFileSystemWatcher; class InstanceTask; -struct InstanceName; using InstanceId = QString; using GroupId = QString; using InstanceLocator = std::pair; -enum class InstCreateError { NoCreateError = 0, NoSuchVersion, UnknownCreateError, InstExists, CantCreateDir }; +enum class InstCreateError : std::uint8_t { NoCreateError = 0, NoSuchVersion, UnknownCreateError, InstExists, CantCreateDir }; -enum class GroupsState { NotLoaded, Steady, Dirty }; +enum class GroupsState : std::uint8_t { NotLoaded, Steady, Dirty }; struct TrashShortcutItem { ShortcutData data; @@ -73,8 +73,8 @@ class InstanceList : public QAbstractListModel { Q_OBJECT public: - explicit InstanceList(SettingsObject* settings, const QString& instDir, QObject* parent = 0); - virtual ~InstanceList(); + explicit InstanceList(SettingsObject* settings, const QString& instDir, QObject* parent = nullptr); + ~InstanceList() override = default; public: QModelIndex index(int row, int column = 0, const QModelIndex& parent = QModelIndex()) const override; @@ -94,7 +94,7 @@ class InstanceList : public QAbstractListModel { * NoError Indicates that no error occurred. * UnknownError indicates that an unspecified error occurred. */ - enum InstListError { NoError = 0, UnknownError }; + enum InstListError : std::uint8_t { NoError = 0, UnknownError }; BaseInstance* at(int i) const { return m_instances.at(i).get(); } @@ -104,9 +104,9 @@ class InstanceList : public QAbstractListModel { void saveNow(); /* O(n) */ - BaseInstance* getInstanceById(QString id) const; + BaseInstance* getInstanceById(const QString& id) const; /* O(n) */ - BaseInstance* getInstanceByManagedName(const QString& managed_name) const; + BaseInstance* getInstanceByManagedName(const QString& managedName) const; QModelIndex getInstanceIndexById(const QString& id) const; QStringList getGroups(); bool isGroupCollapsed(const QString& groupName); @@ -136,13 +136,7 @@ class InstanceList : public QAbstractListModel { * should_override is used when another similar instance already exists, and we want to override it * - for instance, when updating it. */ - bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, QString groupName, const InstanceTask&); - - /** - * Destroy a previously created staging area given by @keyPath - used when creation fails. - * Used by instance manipulation tasks. - */ - bool destroyStagingPath(const QString& keyPath); + bool commitStagedInstance(const QString& keyPath, const InstanceTask& instanceTask, QString groupName); int getTotalPlayTime(); @@ -166,7 +160,7 @@ class InstanceList : public QAbstractListModel { void groupsChanged(QSet groups); public slots: - void on_InstFolderChanged(const Setting& setting, QVariant value); + void on_InstFolderChanged(const Setting& setting, const QVariant& value); void on_GroupStateChanged(const QString& group, bool collapsed); private slots: @@ -190,7 +184,7 @@ class InstanceList : public QAbstractListModel { private: int m_watchLevel = 0; - int totalPlayTime = 0; + int m_totalPlayTime = 0; bool m_dirty = false; std::vector> m_instances; // id -> refs @@ -202,7 +196,7 @@ class InstanceList : public QAbstractListModel { // FIXME: this is so inefficient that looking at it is almost painful. QSet m_collapsedGroups; QMap m_instanceGroupIndex; - QSet instanceSet; + QSet m_instanceSet; bool m_groupsLoaded = false; bool m_instancesProbed = false; diff --git a/launcher/InstanceTask.cpp b/launcher/InstanceTask.cpp index 01998a7aa..a66aeb9c3 100644 --- a/launcher/InstanceTask.cpp +++ b/launcher/InstanceTask.cpp @@ -2,94 +2,182 @@ #include #include "Application.h" +#include "minecraft/MinecraftInstance.h" +#include "minecraft/MinecraftLoadAndCheck.h" #include "settings/SettingsObject.h" +#include "tasks/SequentialTask.h" #include "ui/dialogs/CustomMessageBox.h" #include -InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name) +InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& oldName, const QString& newName) { - auto dialog = + auto* dialog = CustomMessageBox::selectable(parent, QObject::tr("Change instance name"), QObject::tr("The instance's name seems to include the old version. Would you like to update it?\n\n" "Old name: %1\n" "New name: %2") - .arg(old_name, new_name), + .arg(oldName, newName), QMessageBox::Question, QMessageBox::No | QMessageBox::Yes); auto result = dialog->exec(); - if (result == QMessageBox::Yes) + if (result == QMessageBox::Yes) { return InstanceNameChange::ShouldChange; + } return InstanceNameChange::ShouldKeep; } -ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name) +ShouldUpdate askIfShouldUpdate(QWidget* parent, const QString& originalVersionName) { - if (APPLICATION->settings()->get("SkipModpackUpdatePrompt").toBool()) + if (APPLICATION->settings()->get("SkipModpackUpdatePrompt").toBool()) { return ShouldUpdate::SkipUpdating; + } - auto info = CustomMessageBox::selectable( + auto* info = CustomMessageBox::selectable( parent, QObject::tr("Similar modpack was found!"), QObject::tr( "One or more of your instances are from this same modpack%1. Do you want to create a " "separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before " "updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).") - .arg(original_version_name), + .arg(originalVersionName), QMessageBox::Information, QMessageBox::Cancel); QAbstractButton* update = info->addButton(QObject::tr("Update existing instance"), QMessageBox::AcceptRole); QAbstractButton* skip = info->addButton(QObject::tr("Create new instance"), QMessageBox::ResetRole); info->exec(); - if (info->clickedButton() == update) + if (info->clickedButton() == update) { return ShouldUpdate::Update; - if (info->clickedButton() == skip) + } + if (info->clickedButton() == skip) { return ShouldUpdate::SkipUpdating; + } return ShouldUpdate::Cancel; } -QString InstanceName::name() const +QString InstanceTask::name() const { - if (!m_modified_name.isEmpty()) + if (!m_modifiedName.isEmpty()) { return modifiedName(); - if (!m_original_version.isEmpty()) - return QString("%1 %2").arg(m_original_name, m_original_version); + } + if (!m_originalVersion.isEmpty()) { + return QString("%1 %2").arg(m_originalName, m_originalVersion); + } - return m_original_name; + return m_originalName; } -QString InstanceName::originalName() const +QString InstanceTask::originalName() const { - return m_original_name; + return m_originalName; } -QString InstanceName::modifiedName() const +QString InstanceTask::modifiedName() const { - if (!m_modified_name.isEmpty()) - return m_modified_name; - return m_original_name; + if (!m_modifiedName.isEmpty()) { + return m_modifiedName; + } + return m_originalName; } -QString InstanceName::version() const +QString InstanceTask::version() const { - return m_original_version; + return m_originalVersion; } -void InstanceName::setName(InstanceName& other) +void InstanceTask::setOriginalName(const QString& name, const QString& version) { - m_original_name = other.m_original_name; - m_original_version = other.m_original_version; - m_modified_name = other.m_modified_name; + m_originalName = name; + m_originalVersion = version; +} +void InstanceTask::setOverride(bool override, const QString& instanceIdToOverride) +{ + m_overrideExisting = override; + if (!instanceIdToOverride.isEmpty()) { + m_originalInstanceId = instanceIdToOverride; + } } - -InstanceTask::InstanceTask() : Task(), InstanceName() {} ShouldDeleteSaves askIfShouldDeleteSaves(QWidget* parent) { - auto dialog = CustomMessageBox::selectable(parent, QObject::tr("Delete Existing Save Files"), - QObject::tr("An earlier version of this mod pack installed save files.\n" - "Would you like to remove those existing saves as part of this update?"), - QMessageBox::Question, QMessageBox::No | QMessageBox::Yes); + auto* dialog = CustomMessageBox::selectable(parent, QObject::tr("Delete Existing Save Files"), + QObject::tr("An earlier version of this mod pack installed save files.\n" + "Would you like to remove those existing saves as part of this update?"), + QMessageBox::Question, QMessageBox::No | QMessageBox::Yes); auto result = dialog->exec(); return result == QMessageBox::Yes ? ShouldDeleteSaves::Yes : ShouldDeleteSaves::No; } + +void InstanceTask::scheduleToDelete(QWidget* parent, const QDir& dir, const QString& path, bool checkDisabled) +{ + if (path.isEmpty()) { + return; + } + if (path.startsWith("saves/")) { + if (m_shouldDeleteSaves == ShouldDeleteSaves::NotAsked) { + m_shouldDeleteSaves = askIfShouldDeleteSaves(parent); + } + if (m_shouldDeleteSaves == ShouldDeleteSaves::No) { + return; + } + } + qDebug() << "Scheduling" << path << "for removal"; + m_filesToRemove.append(dir.absoluteFilePath(path)); + if (checkDisabled) { + if (path.endsWith(".disabled")) { // remove it if it was enabled/disabled by user + m_filesToRemove.append(dir.absoluteFilePath(path.chopped(9))); + } else { + m_filesToRemove.append(dir.absoluteFilePath(path + ".disabled")); + } + } +} + +void InstanceTask::downloadFiles(MinecraftInstance* inst) +{ + if (!APPLICATION->settings()->get("DownloadGameFilesDuringInstanceCreation").toBool()) { + emitSucceeded(); + return; + } + setAbortable(true); + setAbortButtonText(tr("Skip")); + qDebug() << "Downloading game files"; + + auto updateTasks = inst->createUpdateTask(); + if (updateTasks.isEmpty()) { + emitSucceeded(); + return; + } + auto task = makeShared(); + task->addTask(makeShared(inst, Net::Mode::Online)); + for (const auto& t : updateTasks) { + task->addTask(t); + } + connect(task.get(), &Task::finished, this, [this, task] { + if (isRunning()) { + return; + } + if (task->wasSuccessful()) { + emitSucceeded(); + } else { + emitFailed(tr("Could not download game files: %1").arg(task->failReason())); + } + }); + propagateFromOther(task.get()); + setDetails(tr("Downloading game files")); + + m_gameFilesTask = task; + m_gameFilesTask->start(); +} + +bool InstanceTask::abort() +{ + if (!canAbort()) { + return false; + } + + if (m_gameFilesTask) { + return m_gameFilesTask->abort(); + } + + return Task::abort(); +} diff --git a/launcher/InstanceTask.h b/launcher/InstanceTask.h index 125930a27..76a3e84c2 100644 --- a/launcher/InstanceTask.h +++ b/launcher/InstanceTask.h @@ -1,40 +1,24 @@ #pragma once +#include +#include #include "settings/SettingsObject.h" #include "tasks/Task.h" +class MinecraftInstance; + /* Helpers */ -enum class InstanceNameChange { ShouldChange, ShouldKeep }; -[[nodiscard]] InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name); -enum class ShouldUpdate { Update, SkipUpdating, Cancel }; -[[nodiscard]] ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name); -enum class ShouldDeleteSaves { NotAsked, Yes, No }; +enum class InstanceNameChange : std::uint8_t { ShouldChange, ShouldKeep }; +[[nodiscard]] InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& oldName, const QString& newName); +enum class ShouldUpdate : std::uint8_t { Update, SkipUpdating, Cancel }; +[[nodiscard]] ShouldUpdate askIfShouldUpdate(QWidget* parent, const QString& originalVersionName); +enum class ShouldDeleteSaves : std::uint8_t { NotAsked, Yes, No }; [[nodiscard]] ShouldDeleteSaves askIfShouldDeleteSaves(QWidget* parent); -struct InstanceName { - public: - InstanceName() = default; - InstanceName(QString name, QString version) : m_original_name(std::move(name)), m_original_version(std::move(version)) {} - - QString modifiedName() const; - QString originalName() const; - QString name() const; - QString version() const; - - void setName(QString name) { m_modified_name = name; } - void setName(InstanceName& other); - - protected: - QString m_original_name; - QString m_original_version; - - QString m_modified_name; -}; - -class InstanceTask : public Task, public InstanceName { +class InstanceTask : public Task { Q_OBJECT public: - InstanceTask(); + InstanceTask() = default; ~InstanceTask() override = default; void setParentSettings(SettingsObject* settings) { m_globalSettings = settings; } @@ -46,29 +30,48 @@ class InstanceTask : public Task, public InstanceName { void setGroup(const QString& group) { m_instGroup = group; } QString group() const { return m_instGroup; } - bool shouldConfirmUpdate() const { return m_confirm_update; } - void setConfirmUpdate(bool confirm) { m_confirm_update = confirm; } + bool shouldConfirmUpdate() const { return m_confirmUpdate; } + void setConfirmUpdate(bool confirm) { m_confirmUpdate = confirm; } - bool shouldOverride() const { return m_override_existing; } + bool shouldOverride() const { return m_overrideExisting; } - QString originalInstanceID() const { return m_original_instance_id; }; + QString originalInstanceID() const { return m_originalInstanceId; }; + + QString modifiedName() const; + QString originalName() const; + QString name() const; + QString version() const; + + void setName(const QString& name) { m_modifiedName = name; } + void setOriginalName(const QString& name, const QString& version); protected: - void setOverride(bool override, QString instance_id_to_override = {}) - { - m_override_existing = override; - if (!instance_id_to_override.isEmpty()) - m_original_instance_id = instance_id_to_override; - } + void setOverride(bool override, const QString& instanceIdToOverride = {}); + + void scheduleToDelete(QWidget* parent, const QDir& dir, const QString& path, bool checkDisabled = false); + void downloadFiles(MinecraftInstance* inst); + + public slots: + bool abort() override; protected: /* data */ - SettingsObject* m_globalSettings; + SettingsObject* m_globalSettings{}; QString m_instIcon; QString m_instGroup; QString m_stagingPath; - bool m_override_existing = false; - bool m_confirm_update = true; + bool m_overrideExisting = false; + bool m_confirmUpdate = true; - QString m_original_instance_id; + QString m_originalInstanceId; + + QString m_originalName; + QString m_originalVersion; + + QString m_modifiedName; + + QStringList m_filesToRemove; + ShouldDeleteSaves m_shouldDeleteSaves{}; + + Task::Ptr m_gameFilesTask; }; diff --git a/launcher/minecraft/VanillaInstanceCreationTask.cpp b/launcher/minecraft/VanillaInstanceCreationTask.cpp index e646e2c52..fb059f59a 100644 --- a/launcher/minecraft/VanillaInstanceCreationTask.cpp +++ b/launcher/minecraft/VanillaInstanceCreationTask.cpp @@ -8,26 +8,30 @@ #include "settings/INISettingsObject.h" VanillaCreationTask::VanillaCreationTask(BaseVersion::Ptr version, QString loader, BaseVersion::Ptr loaderVersion) - : m_version(std::move(version)), m_using_loader(true), m_loader(std::move(loader)), m_loader_version(std::move(loaderVersion)) + : m_version(std::move(version)), m_usingLoader(true), m_loader(std::move(loader)), m_loaderVersion(std::move(loaderVersion)) {} -std::unique_ptr VanillaCreationTask::createInstance() +void VanillaCreationTask::executeTask() { setStatus(tr("Creating instance from version %1").arg(m_version->name())); - auto inst = std::make_unique( + m_instance = std::make_unique( m_globalSettings, std::make_unique(FS::PathCombine(m_stagingPath, "instance.cfg")), m_stagingPath); - SettingsObject::Lock lock(inst->settings()); + { + SettingsObject::Lock const lock(m_instance->settings()); - auto* components = inst->getPackProfile(); - components->buildingFromScratch(); - components->setComponentVersion("net.minecraft", m_version->descriptor(), true); - if (m_using_loader) { - components->setComponentVersion(m_loader, m_loader_version->descriptor()); + auto* components = m_instance->getPackProfile(); + components->buildingFromScratch(); + components->setComponentVersion("net.minecraft", m_version->descriptor(), true); + if (m_usingLoader) { + components->setComponentVersion(m_loader, m_loaderVersion->descriptor()); + } + + m_instance->setName(name()); + m_instance->setIconKey(m_instIcon); + + components->saveNow(); } - inst->setName(name()); - inst->setIconKey(m_instIcon); - components->saveNow(); - return inst; + downloadFiles(m_instance.get()); } diff --git a/launcher/minecraft/VanillaInstanceCreationTask.h b/launcher/minecraft/VanillaInstanceCreationTask.h index c1a69ab62..4e8b26da7 100644 --- a/launcher/minecraft/VanillaInstanceCreationTask.h +++ b/launcher/minecraft/VanillaInstanceCreationTask.h @@ -1,22 +1,25 @@ #pragma once -#include "InstanceCreationTask.h" +#include +#include "BaseVersion.h" +#include "InstanceTask.h" +#include "minecraft/MinecraftInstance.h" -#include - -class VanillaCreationTask final : public InstanceCreationTask { +class VanillaCreationTask final : public InstanceTask { Q_OBJECT public: explicit VanillaCreationTask(BaseVersion::Ptr version) : m_version(std::move(version)) {} VanillaCreationTask(BaseVersion::Ptr version, QString loader, BaseVersion::Ptr loaderVersion); - std::unique_ptr createInstance() override; + void executeTask() override; private: + std::unique_ptr m_instance; + // Version to update to / create of the instance. BaseVersion::Ptr m_version; - bool m_using_loader = false; + bool m_usingLoader = false; QString m_loader; - BaseVersion::Ptr m_loader_version; + BaseVersion::Ptr m_loaderVersion; }; diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index f3f66997b..855706315 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -1091,7 +1091,7 @@ void PackInstallTask::install() jarmods.clear(); } - emitSucceeded(); + downloadFiles(&instance); } } // namespace ATLauncher diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index 534132a6e..cfa2b1167 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -35,7 +35,6 @@ #include "FlameInstanceCreationTask.h" -#include "InstanceTask.h" #include "QObjectPtr.h" #include "minecraft/mod/tasks/LocalResourceUpdateTask.h" #include "modplatform/flame/FileResolvingTask.h" @@ -55,13 +54,13 @@ #include "settings/INISettingsObject.h" -#include "SysInfo.h" #include "tasks/ConcurrentTask.h" #include "ui/dialogs/BlockedModsDialog.h" #include "ui/dialogs/CustomMessageBox.h" #include #include +#include #include "HardwareInfo.h" #include "meta/Index.h" @@ -70,199 +69,223 @@ #include "net/ApiDownload.h" #include "ui/pages/modplatform/OptionalModDialog.h" -static const FlameAPI api; - bool FlameCreationTask::abort() { - if (!canAbort()) + if (!canAbort()) { return false; + } - if (m_processUpdateFileInfoJob) + if (m_processUpdateFileInfoJob) { m_processUpdateFileInfoJob->abort(); - if (m_filesJob) + } + if (m_filesJob) { m_filesJob->abort(); - if (m_modIdResolver) + } + if (m_modIdResolver) { m_modIdResolver->abort(); + } - return InstanceCreationTask::abort(); + return InstanceTask::abort(); } -bool FlameCreationTask::updateInstance() +void FlameCreationTask::executeTask() { - auto instance_list = APPLICATION->instances(); + auto* instanceList = APPLICATION->instances(); // FIXME: How to handle situations when there's more than one install already for a given modpack? - BaseInstance* inst; - if (auto original_id = originalInstanceID(); !original_id.isEmpty()) { - inst = instance_list->getInstanceById(original_id); + BaseInstance* inst = nullptr; + if (auto originalId = originalInstanceID(); !originalId.isEmpty()) { + inst = instanceList->getInstanceById(originalId); Q_ASSERT(inst); } else { - inst = instance_list->getInstanceByManagedName(originalName()); + inst = instanceList->getInstanceByManagedName(originalName()); if (!inst) { - inst = instance_list->getInstanceById(originalName()); + inst = instanceList->getInstanceById(originalName()); - if (!inst) - return false; + if (!inst) { + createInstance(); + return; + } } } - QString index_path(FS::PathCombine(m_stagingPath, "manifest.json")); + QString indexPath(FS::PathCombine(m_stagingPath, "manifest.json")); try { - Flame::loadManifest(m_pack, index_path); - } catch (const JSONValidationError& e) { - setError(tr("Could not understand pack manifest:\n") + e.cause()); - return false; + Flame::loadManifest(m_pack, indexPath); + } catch (const JSONValidationError&) { + // emitFailed(tr("Could not understand pack manifest:\n") + e.cause()); + createInstance(); // to keep the backwards comatibility here just create the instance + return; } - auto version_id = inst->getManagedPackVersionName(); - auto version_str = !version_id.isEmpty() ? tr(" (version %1)").arg(version_id) : ""; + auto versionId = inst->getManagedPackVersionName(); + auto versionStr = !versionId.isEmpty() ? tr(" (version %1)").arg(versionId) : ""; if (shouldConfirmUpdate()) { - auto should_update = askIfShouldUpdate(m_parent, version_str); - if (should_update == ShouldUpdate::SkipUpdating) - return false; - if (should_update == ShouldUpdate::Cancel) { - m_abort = true; - return false; + auto shouldUpdate = askIfShouldUpdate(m_parent, versionStr); + if (shouldUpdate == ShouldUpdate::SkipUpdating) { + createInstance(); + return; + } + if (shouldUpdate == ShouldUpdate::Cancel) { + emitAborted(); + return; } } - QDir old_inst_dir(inst->instanceRoot()); + QDir const oldInstDir(inst->instanceRoot()); - QString old_index_folder(FS::PathCombine(old_inst_dir.absolutePath(), "flame")); - QString old_index_path(FS::PathCombine(old_index_folder, "manifest.json")); + QString const oldIndexFolder(FS::PathCombine(oldInstDir.absolutePath(), "flame")); + QString const oldIndexPath(FS::PathCombine(oldIndexFolder, "manifest.json")); - QFileInfo old_index_file(old_index_path); - if (old_index_file.exists()) { - Flame::Manifest old_pack; - Flame::loadManifest(old_pack, old_index_path); + QFileInfo const oldIndexFile(oldIndexPath); + auto createInst = [this, inst] { + setOverride(true, inst->id()); + qDebug() << "Will override instance!"; - auto& old_files = old_pack.files; + m_oldInstance = inst; + + // We let it go through the createInstance() stage, just with a couple modifications for updating + createInstance(); + }; + + auto warnUser = [this, createInst](const QString& title, + const QString& text) { // We don't have an old index file, so we may duplicate stuff! + auto* dialog = CustomMessageBox::selectable(m_parent, title, text, QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Cancel); + + if (dialog->exec() == QDialog::DialogCode::Rejected) { + emitAborted(); + return; + } + + createInst(); + }; + + if (oldIndexFile.exists()) { + Flame::Manifest oldPack; + Flame::loadManifest(oldPack, oldIndexPath); + + auto oldFiles = oldPack.files; auto& files = m_pack.files; // Remove repeated files, we don't need to download them! - auto files_iterator = files.begin(); - while (files_iterator != files.end()) { - auto const& file = files_iterator; + auto filesIterator = files.begin(); + while (filesIterator != files.end()) { + const auto& file = filesIterator; - auto old_file = old_files.find(file.key()); - if (old_file != old_files.end()) { + auto oldFile = oldFiles.find(file.key()); + if (oldFile != oldFiles.end()) { // We found a match, but is it a different version? - if (old_file->fileId == file->fileId) { + if (oldFile->fileId == file->fileId) { qDebug() << "Removed file at" << file->targetFolder << "with id" << file->fileId << "from list of downloads"; - old_files.remove(file.key()); - files_iterator = files.erase(files_iterator); + oldFiles.remove(file.key()); + filesIterator = files.erase(filesIterator); - if (files_iterator != files.begin()) - files_iterator--; + if (filesIterator != files.begin()) { + filesIterator--; + } } } - files_iterator++; + filesIterator++; } - QDir old_minecraft_dir(inst->gameRoot()); + QDir const oldMinecraftDir(inst->gameRoot()); // We will remove all the previous overrides, to prevent duplicate files! // TODO: Currently 'overrides' will always override the stuff on update. How do we preserve unchanged overrides? // FIXME: We may want to do something about disabled mods. - auto old_overrides = Override::readOverrides("overrides", old_index_folder); - for (const auto& entry : old_overrides) { - scheduleToDelete(m_parent, old_minecraft_dir, entry); + auto oldOverrides = Override::readOverrides("overrides", oldIndexFolder); + for (const auto& entry : oldOverrides) { + scheduleToDelete(m_parent, oldMinecraftDir, entry); } // Remove remaining old files (we need to do an API request to know which ids are which files...) QStringList fileIds; - for (auto& file : old_files) { + for (auto& file : oldFiles) { fileIds.append(QString::number(file.fileId)); } - auto [job, raw_response] = api.getFiles(fileIds); + auto [job, rawResponse] = FlameAPI().getFiles(fileIds); - QEventLoop loop; + connect(job.get(), &Task::succeeded, this, + [this, rawResponse, fileIds, oldInstDir, oldFiles, oldMinecraftDir, createInst]() mutable { + // Parse the API response + QJsonParseError parseError{}; + auto doc = QJsonDocument::fromJson(*rawResponse, &parseError); + if (parseError.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from Flame files task at" << parseError.offset + << "reason:" << parseError.errorString(); + qWarning() << *rawResponse; + return; + } - connect(job.get(), &Task::succeeded, this, [this, raw_response, fileIds, old_inst_dir, &old_files, old_minecraft_dir] { - // Parse the API response - QJsonParseError parse_error{}; - auto doc = QJsonDocument::fromJson(*raw_response, &parse_error); - if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response from Flame files task at" << parse_error.offset - << "reason:" << parse_error.errorString(); - qWarning() << *raw_response; - return; - } + try { + QJsonArray entries; + if (fileIds.size() == 1) { + entries = { Json::requireObject(Json::requireObject(doc), "data") }; + } else { + entries = Json::requireArray(Json::requireObject(doc), "data"); + } - try { - QJsonArray entries; - if (fileIds.size() == 1) - entries = { Json::requireObject(Json::requireObject(doc), "data") }; - else - entries = Json::requireArray(Json::requireObject(doc), "data"); + for (auto entry : entries) { + auto entryObj = Json::requireObject(entry); - for (auto entry : entries) { - auto entry_obj = Json::requireObject(entry); + Flame::File file; + // We don't care about blocked mods, we just need local data to delete the file + file.version = FlameMod::loadIndexedPackVersion(entryObj); + auto id = Json::requireInteger(entryObj, "id"); + oldFiles.insert(id, file); + } + } catch (Json::JsonException& e) { + qCritical() << e.cause() << e.what(); + } - Flame::File file; - // We don't care about blocked mods, we just need local data to delete the file - file.version = FlameMod::loadIndexedPackVersion(entry_obj); - auto id = Json::requireInteger(entry_obj, "id"); - old_files.insert(id, file); - } - } catch (Json::JsonException& e) { - qCritical() << e.cause() << e.what(); - } + // Delete the files + for (const auto& file : oldFiles) { + if (file.version.fileName.isEmpty() || file.targetFolder.isEmpty()) { + continue; + } - // Delete the files - for (auto& file : old_files) { - if (file.version.fileName.isEmpty() || file.targetFolder.isEmpty()) - continue; + QString const relativePath(FS::PathCombine(file.targetFolder, file.version.fileName)); + scheduleToDelete(m_parent, oldMinecraftDir, relativePath, true); + } - QString relative_path(FS::PathCombine(file.targetFolder, file.version.fileName)); - scheduleToDelete(m_parent, old_minecraft_dir, relative_path, true); - } + createInst(); + }); + connect(job.get(), &Task::aborted, this, [warnUser] { + warnUser(tr("Failed to fetch the old files."), + tr("We couldn't fetch the old files because the task was aborted. This may cause " + "some of the files to be duplicated. Do you want to continue?")); + }); + connect(job.get(), &Task::failed, this, [warnUser](const QString& reason) { + warnUser(tr("Failed to fetch the old files."), tr("We couldn't fetch the old files because: %1. This may cause some of the " + "files to be duplicated. Do you want to continue?") + .arg(reason)); }); - connect(job.get(), &Task::failed, this, [](QString reason) { qCritical() << "Failed to get files:" << reason; }); - connect(job.get(), &Task::finished, &loop, &QEventLoop::quit); m_processUpdateFileInfoJob = job; job->start(); - - loop.exec(); - - m_processUpdateFileInfoJob = nullptr; - } else { - // We don't have an old index file, so we may duplicate stuff! - auto dialog = CustomMessageBox::selectable(m_parent, tr("No index file."), - tr("We couldn't find a suitable index file for the older version. This may cause some " - "of the files to be duplicated. Do you want to continue?"), - QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Cancel); - - if (dialog->exec() == QDialog::DialogCode::Rejected) { - m_abort = true; - return false; - } + return; } - - setOverride(true, inst->id()); - qDebug() << "Will override instance!"; - - m_instance = inst; - - // We let it go through the createInstance() stage, just with a couple modifications for updating - return false; + warnUser(tr("No index file."), tr("We couldn't find a suitable index file for the older version. This may cause some of the files to " + "be duplicated. Do you want to continue?")); } -QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType, QString loaderVersion, QString mcVersion) +QString FlameCreationTask::getVersionForLoader(const QString& uid, + const QString& loaderType, + const QString& loaderVersion, + const QString& mcVersion) { if (loaderVersion == "recommended") { auto vlist = APPLICATION->metadataIndex()->get(uid); if (!vlist) { - setError(tr("Failed to get local metadata index for %1").arg(uid)); + emitFailed(tr("Failed to get local metadata index for %1").arg(uid)); return {}; } @@ -270,16 +293,18 @@ QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType, QEventLoop loadVersionLoop; auto task = vlist->getLoadTask(); connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit); - if (!task->isRunning()) + if (!task->isRunning()) { task->start(); + } loadVersionLoop.exec(); } - for (auto version : vlist->versions()) { + for (const auto& version : vlist->versions()) { // first recommended build we find, we use. - if (!version->isRecommended()) + if (!version->isRecommended()) { continue; + } auto reqs = version->requiredSet(); // filter by minecraft version, if the loader depends on a certain version. @@ -289,13 +314,14 @@ QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType, auto iter = std::find_if(reqs.begin(), reqs.end(), [mcVersion](const Meta::Require& req) { return req.uid == "net.minecraft" && req.equalsVersion == mcVersion; }); - if (iter == reqs.end()) + if (iter == reqs.end()) { continue; + } } return version->descriptor(); } - setError(tr("Failed to find version for %1 loader").arg(loaderType)); + emitFailed(tr("Failed to find version for %1 loader").arg(loaderType)); return {}; } @@ -307,37 +333,46 @@ QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType, return loaderVersion; } -std::unique_ptr FlameCreationTask::createInstance() +void FlameCreationTask::setManagedPack(BaseInstance* instance) { - QEventLoop loop; + // Don't add managed info to packs without an ID (most likely imported from ZIP) + if (!m_managedId.isEmpty()) { + instance->setManagedPack("flame", m_managedId, m_pack.name, m_managedVersionId, m_pack.version); + } else { + instance->setManagedPack("flame", "", name(), "", ""); + } +} - QString parent_folder(FS::PathCombine(m_stagingPath, "flame")); +void FlameCreationTask::createInstance() +{ + QString const parentFolder(FS::PathCombine(m_stagingPath, "flame")); try { - QString index_path(FS::PathCombine(m_stagingPath, "manifest.json")); - if (!m_pack.is_loaded) - Flame::loadManifest(m_pack, index_path); + QString const indexPath(FS::PathCombine(m_stagingPath, "manifest.json")); + if (!m_pack.isLoaded) { + Flame::loadManifest(m_pack, indexPath); + } // Keep index file in case we need it some other time (like when changing versions) - QString new_index_place(FS::PathCombine(parent_folder, "manifest.json")); - FS::ensureFilePathExists(new_index_place); - FS::move(index_path, new_index_place); + QString const newIndexPlace(FS::PathCombine(parentFolder, "manifest.json")); + FS::ensureFilePathExists(newIndexPlace); + FS::move(indexPath, newIndexPlace); } catch (const JSONValidationError& e) { - setError(tr("Could not understand pack manifest:\n") + e.cause()); - return nullptr; + emitFailed(tr("Could not understand pack manifest:\n") + e.cause()); + return; } if (!m_pack.overrides.isEmpty()) { QString overridePath = FS::PathCombine(m_stagingPath, m_pack.overrides); if (QFile::exists(overridePath)) { // Create a list of overrides in "overrides.txt" inside flame/ - Override::createOverrides("overrides", parent_folder, overridePath); + Override::createOverrides("overrides", parentFolder, overridePath); QString mcPath = FS::PathCombine(m_stagingPath, "minecraft"); if (!FS::move(overridePath, mcPath)) { - setError(tr("Could not rename the overrides folder:\n") + m_pack.overrides); - return nullptr; + emitFailed(tr("Could not rename the overrides folder:\n") + m_pack.overrides); + return; } } else { logWarning( @@ -353,8 +388,9 @@ std::unique_ptr FlameCreationTask::createInstance() auto id = loader.id; if (id.startsWith("neoforge-")) { id.remove("neoforge-"); - if (id.startsWith("1.20.1-")) + if (id.startsWith("1.20.1-")) { id.remove("1.20.1-"); // this is a mess for curseforge + } loaderType = "neoforge"; loaderUid = "net.neoforged"; } else if (id.startsWith("forge-")) { @@ -378,7 +414,7 @@ std::unique_ptr FlameCreationTask::createInstance() QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); auto instanceSettings = std::make_unique(configPath); - auto instance = std::make_unique(m_globalSettings, std::move(instanceSettings), m_stagingPath); + m_newInstance = std::make_unique(m_globalSettings, std::move(instanceSettings), m_stagingPath); auto mcVersion = m_pack.minecraft.version; // Hack to correct some 'special sauce'... @@ -388,32 +424,33 @@ std::unique_ptr FlameCreationTask::createInstance() logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack.")); } - auto components = instance->getPackProfile(); + auto* components = m_newInstance->getPackProfile(); components->buildingFromScratch(); components->setComponentVersion("net.minecraft", mcVersion, true); if (!loaderType.isEmpty()) { auto version = getVersionForLoader(loaderUid, loaderType, loaderVersion, mcVersion); - if (version.isEmpty()) - return nullptr; + if (version.isEmpty()) { // because there are more info in getVersionForLoader the emitFailed is trigered inside it + return; + } components->setComponentVersion(loaderUid, version); } if (m_instIcon != "default") { - instance->setIconKey(m_instIcon); + m_newInstance->setIconKey(m_instIcon); } else { if (m_pack.name.contains("Direwolf20")) { - instance->setIconKey("steve"); + m_newInstance->setIconKey("steve"); } else if (m_pack.name.contains("FTB") || m_pack.name.contains("Feed The Beast")) { - instance->setIconKey("ftb_logo"); + m_newInstance->setIconKey("ftb_logo"); } else { - instance->setIconKey("flame"); + m_newInstance->setIconKey("flame"); } } int recommendedRAM = m_pack.minecraft.recommendedRAM; // only set memory if this is a fresh instance - if (!m_instance && recommendedRAM > 0) { + if (!m_oldInstance && recommendedRAM > 0) { const uint64_t sysMiB = HardwareInfo::totalRamMiB(); const uint64_t max = sysMiB * 0.9; @@ -424,8 +461,8 @@ std::unique_ptr FlameCreationTask::createInstance() recommendedRAM = max; } - instance->settings()->set("OverrideMemory", true); - instance->settings()->set("MaxMemAlloc", recommendedRAM); + m_newInstance->settings()->set("OverrideMemory", true); + m_newInstance->settings()->set("MaxMemAlloc", recommendedRAM); } QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods"); @@ -439,53 +476,31 @@ std::unique_ptr FlameCreationTask::createInstance() qDebug() << info.fileName(); jarMods.push_back(info.absoluteFilePath()); } - auto profile = instance->getPackProfile(); + auto* profile = m_newInstance->getPackProfile(); profile->installJarMods(jarMods); // nuke the original files FS::deletePath(jarmodsPath); } - // Don't add managed info to packs without an ID (most likely imported from ZIP) - if (!m_managedId.isEmpty()) - instance->setManagedPack("flame", m_managedId, m_pack.name, m_managedVersionId, m_pack.version); - else - instance->setManagedPack("flame", "", name(), "", ""); + setManagedPack(m_newInstance.get()); - instance->setName(name()); + m_newInstance->setName(name()); m_modIdResolver.reset(new Flame::FileResolvingTask(m_pack)); - connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, this, [this, &loop] { idResolverSucceeded(loop); }); - connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [this, &loop](QString reason) { + connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, this, &FlameCreationTask::idResolverSucceeded); + connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [this](const QString& reason) { m_modIdResolver.reset(); - setError(tr("Unable to resolve mod IDs:\n") + reason); - loop.quit(); + emitFailed(tr("Unable to resolve mod IDs:\n") + reason); }); - connect(m_modIdResolver.get(), &Flame::FileResolvingTask::aborted, &loop, &QEventLoop::quit); + connect(m_modIdResolver.get(), &Flame::FileResolvingTask::aborted, this, &FlameCreationTask::emitAborted); connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, this, &FlameCreationTask::setProgress); connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, this, &FlameCreationTask::setStatus); connect(m_modIdResolver.get(), &Flame::FileResolvingTask::stepProgress, this, &FlameCreationTask::propagateStepProgress); connect(m_modIdResolver.get(), &Flame::FileResolvingTask::details, this, &FlameCreationTask::setDetails); m_modIdResolver->start(); - - loop.exec(); - - bool did_succeed = getError().isEmpty(); - - // Update information of the already installed instance, if any. - if (m_instance && did_succeed) { - setAbortable(false); - auto inst = m_instance.value(); - - inst->copyManagedPack(*instance); - } - - if (did_succeed) { - return instance; - } - return nullptr; } -void FlameCreationTask::idResolverSucceeded(QEventLoop& loop) +void FlameCreationTask::idResolverSucceeded() { auto results = m_modIdResolver->getResults().files; @@ -500,7 +515,6 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop) OptionalModDialog optionalModDialog(m_parent, optionalFiles); if (optionalModDialog.exec() == QDialog::Rejected) { emitAborted(); - loop.quit(); return; } @@ -508,7 +522,7 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop) } // first check for blocked mods - QList blocked_mods; + QList blockedMods; auto anyBlocked = false; for (const auto& result : results.values()) { if (result.resourceType != ModPlatform::ResourceType::Mod) { @@ -517,19 +531,19 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop) // skip optional mods that were not selected if (result.version.downloadUrl.isEmpty()) { - BlockedMod blocked_mod; - blocked_mod.name = result.version.fileName; - blocked_mod.websiteUrl = QString("%1/download/%2").arg(result.pack.websiteUrl, QString::number(result.fileId)); - blocked_mod.hash = result.version.hash; - blocked_mod.matched = false; - blocked_mod.localPath = ""; - blocked_mod.targetFolder = result.targetFolder; + BlockedMod blockedMod; + blockedMod.name = result.version.fileName; + blockedMod.websiteUrl = QString("%1/download/%2").arg(result.pack.websiteUrl, QString::number(result.fileId)); + blockedMod.hash = result.version.hash; + blockedMod.matched = false; + blockedMod.localPath = ""; + blockedMod.targetFolder = result.targetFolder; auto fileName = result.version.fileName; fileName = FS::RemoveInvalidPathChars(fileName); auto relpath = FS::PathCombine(result.targetFolder, fileName); - blocked_mod.disabled = !result.required && !m_selectedOptionalMods.contains(relpath); + blockedMod.disabled = !result.required && !m_selectedOptionalMods.contains(relpath); - blocked_mods.append(blocked_mod); + blockedMods.append(blockedMod); anyBlocked = true; } @@ -537,28 +551,28 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop) if (anyBlocked) { qWarning() << "Blocked mods found, displaying mod list"; - BlockedModsDialog message_dialog(m_parent, tr("Blocked mods found"), - tr("The following files are not available for download in third party launchers.
" - "You will need to manually download them and add them to the instance."), - blocked_mods); + BlockedModsDialog messageDialog(m_parent, tr("Blocked mods found"), + tr("The following files are not available for download in third party launchers.
" + "You will need to manually download them and add them to the instance."), + blockedMods); - message_dialog.setModal(true); + messageDialog.setModal(true); - if (message_dialog.exec()) { - qDebug() << "Post dialog blocked mods list:" << blocked_mods; - copyBlockedMods(blocked_mods); - setupDownloadJob(loop); + if (messageDialog.exec() != 0) { + qDebug() << "Post dialog blocked mods list:" << blockedMods; + copyBlockedMods(blockedMods); + setupDownloadJob(); } else { m_modIdResolver.reset(); - setError("Canceled"); - loop.quit(); + emitAborted(); + return; } } else { - setupDownloadJob(loop); + setupDownloadJob(); } } -void FlameCreationTask::setupDownloadJob(QEventLoop& loop) +void FlameCreationTask::setupDownloadJob() { m_filesJob.reset(new NetJob(tr("Mod Download Flame"), APPLICATION->network())); auto results = m_modIdResolver->getResults().files; @@ -582,13 +596,13 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) } } - connect(m_filesJob.get(), &NetJob::finished, this, [this, &loop]() { + connect(m_filesJob.get(), &NetJob::finished, this, [this]() { m_filesJob.reset(); - validateOtherResources(loop); + validateOtherResources(); }); connect(m_filesJob.get(), &NetJob::failed, [this](QString reason) { m_filesJob.reset(); - setError(reason); + emitFailed(std::move(reason)); }); connect(m_filesJob.get(), &NetJob::progress, this, [this](qint64 current, qint64 total) { setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); @@ -601,23 +615,24 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) } /// @brief copy the matched blocked mods to the instance staging area -/// @param blocked_mods list of the blocked mods and their matched paths -void FlameCreationTask::copyBlockedMods(QList const& blocked_mods) +/// @param blockedMods list of the blocked mods and their matched paths +void FlameCreationTask::copyBlockedMods(const QList& blockedMods) { setStatus(tr("Copying Blocked Mods...")); setAbortable(false); int i = 0; - int total = blocked_mods.length(); - setProgress(i, total); - for (auto const& mod : blocked_mods) { + auto const total = blockedMods.length(); + setProgress(i, static_cast(total)); + for (const auto& mod : blockedMods) { if (!mod.matched) { qDebug() << mod.name << "was not matched to a local file, skipping copy"; continue; } auto destPath = FS::PathCombine(m_stagingPath, "minecraft", mod.targetFolder, mod.name); - if (mod.disabled) + if (mod.disabled) { destPath += ".disabled"; + } setStatus(tr("Copying Blocked Mods (%1 out of %2 are done)").arg(QString::number(i), QString::number(total))); @@ -640,17 +655,17 @@ void FlameCreationTask::copyBlockedMods(QList const& blocked_mods) setAbortable(true); } -void FlameCreationTask::validateOtherResources(QEventLoop& loop) +void FlameCreationTask::validateOtherResources() { qDebug() << "Validating whether other resources are in the right place"; QStringList zipMods; - for (auto [fileName, targetFolder] : m_otherResources) { + for (const auto& [fileName, targetFolder] : m_otherResources) { qDebug() << "Checking" << fileName << "..."; auto localPath = FS::PathCombine(m_stagingPath, "minecraft", targetFolder, fileName); /// @brief check the target and move the the file /// @return path where file can now be found - auto validatePath = [&localPath, this](QString fileName, QString targetFolder, QString realTarget) { + auto validatePath = [&localPath, this](const QString& fileName, const QString& targetFolder, const QString& realTarget) { if (targetFolder != realTarget) { qDebug() << "Target folder of" << fileName << "is incorrect, it belongs in" << realTarget; auto destPath = FS::PathCombine(m_stagingPath, "minecraft", realTarget, fileName); @@ -664,7 +679,7 @@ void FlameCreationTask::validateOtherResources(QEventLoop& loop) return localPath; }; - auto installWorld = [this](QString worldPath) { + auto installWorld = [this](const QString& worldPath) { qDebug() << "Installing World from" << worldPath; QFileInfo worldFileInfo(worldPath); World w(worldFileInfo); @@ -714,13 +729,49 @@ void FlameCreationTask::validateOtherResources(QEventLoop& loop) auto task = makeShared("CreateModMetadata", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()); auto results = m_modIdResolver->getResults().files; auto folder = FS::PathCombine(m_stagingPath, "minecraft", "mods", ".index"); - for (auto file : results) { + for (const auto& file : results) { if (file.targetFolder != "mods" || (file.version.fileName.endsWith(".zip") && !zipMods.contains(file.version.fileName))) { continue; } task->addTask(makeShared(folder, file.pack, file.version)); } - connect(task.get(), &Task::finished, &loop, &QEventLoop::quit); + connect(task.get(), &Task::finished, this, &FlameCreationTask::finishInstall); m_processUpdateFileInfoJob = task; task->start(); } + +void FlameCreationTask::finishInstall() +{ + // Update information of the already installed instance, if any. + if (m_oldInstance) { + setAbortable(false); + setManagedPack(*m_oldInstance); + } + + if (shouldOverride()) { + bool deleteFailed = false; + + setAbortable(false); + setStatus(tr("Removing old conflicting files...")); + qDebug() << "Removing old files"; + + for (const QString& path : m_filesToRemove) { + if (!QFile::exists(path)) { + continue; + } + + qDebug() << "Removing" << path; + + if (!QFile::remove(path)) { + qCritical() << "Could not remove" << path; + deleteFailed = true; + } + } + + if (deleteFailed) { + emitFailed(tr("Failed to remove old conflicting files.")); + return; + } + } + downloadFiles(m_newInstance.get()); +} diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.h b/launcher/modplatform/flame/FlameInstanceCreationTask.h index 221ceaf22..872a71969 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.h +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.h @@ -35,47 +35,51 @@ #pragma once -#include "InstanceCreationTask.h" - +#include #include +#include "BaseInstance.h" +#include "InstanceTask.h" #include "minecraft/MinecraftInstance.h" - #include "modplatform/flame/FileResolvingTask.h" #include "net/NetJob.h" #include "ui/dialogs/BlockedModsDialog.h" -class FlameCreationTask final : public InstanceCreationTask { +class FlameCreationTask final : public InstanceTask { Q_OBJECT public: - FlameCreationTask(const QString& staging_path, - SettingsObject* global_settings, + FlameCreationTask(const QString& stagingPath, + SettingsObject* globalSettings, QWidget* parent, QString id, - QString version_id, - QString original_instance_id = {}) - : InstanceCreationTask(), m_parent(parent), m_managedId(std::move(id)), m_managedVersionId(std::move(version_id)) + QString versionId, + const QString& originalInstanceId = {}) + : m_parent(parent), m_managedId(std::move(id)), m_managedVersionId(std::move(versionId)) { - setStagingPath(staging_path); - setParentSettings(global_settings); + setStagingPath(stagingPath); + setParentSettings(globalSettings); - m_original_instance_id = std::move(original_instance_id); + m_originalInstanceId = originalInstanceId; } bool abort() override; - bool updateInstance() override; - std::unique_ptr createInstance() override; + void createInstance(); + void executeTask() override; private slots: - void idResolverSucceeded(QEventLoop&); - void setupDownloadJob(QEventLoop&); - void copyBlockedMods(QList const& blocked_mods); - void validateOtherResources(QEventLoop& loop); - QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion); + void idResolverSucceeded(); + void setupDownloadJob(); + void copyBlockedMods(const QList& blockedMods); + void validateOtherResources(); + QString getVersionForLoader(const QString& uid, const QString& loaderType, const QString& version, const QString& mcVersion); + void finishInstall(); + + private: + void setManagedPack(BaseInstance* instance); private: QWidget* m_parent = nullptr; @@ -91,7 +95,8 @@ class FlameCreationTask final : public InstanceCreationTask { QList> m_otherResources; - std::optional m_instance; + std::optional m_oldInstance{}; + std::unique_ptr m_newInstance{}; QStringList m_selectedOptionalMods; }; diff --git a/launcher/modplatform/flame/PackManifest.cpp b/launcher/modplatform/flame/PackManifest.cpp index dc176d770..c27a90511 100644 --- a/launcher/modplatform/flame/PackManifest.cpp +++ b/launcher/modplatform/flame/PackManifest.cpp @@ -1,20 +1,20 @@ #include "PackManifest.h" #include "Json.h" - -static void loadFileV1(Flame::File& f, QJsonObject& file) +namespace { +void loadFileV1(Flame::File& f, QJsonObject& file) { f.projectId = Json::requireInteger(file, "projectID"); f.fileId = Json::requireInteger(file, "fileID"); f.required = file["required"].toBool(true); } -static void loadModloaderV1(Flame::Modloader& m, QJsonObject& modLoader) +void loadModloaderV1(Flame::Modloader& m, QJsonObject& modLoader) { m.id = Json::requireString(modLoader, "id"); m.primary = modLoader["primary"].toBool(); } -static void loadMinecraftV1(Flame::Minecraft& m, QJsonObject& minecraft) +void loadMinecraftV1(Flame::Minecraft& m, QJsonObject& minecraft) { m.version = Json::requireString(minecraft, "version"); // extra libraries... apparently only used for a custom Minecraft launcher in the 1.2.5 FTB retro pack @@ -30,7 +30,7 @@ static void loadMinecraftV1(Flame::Minecraft& m, QJsonObject& minecraft) m.recommendedRAM = minecraft["recommendedRam"].toInt(); } -static void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest) +void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest) { auto mc = Json::requireObject(manifest, "minecraft"); @@ -52,8 +52,9 @@ static void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest) pack.overrides = manifest["overrides"].toString("overrides"); - pack.is_loaded = true; + pack.isLoaded = true; } +} // namespace void Flame::loadManifest(Flame::Manifest& m, const QString& filepath) { diff --git a/launcher/modplatform/flame/PackManifest.h b/launcher/modplatform/flame/PackManifest.h index 049a99871..cba21ff41 100644 --- a/launcher/modplatform/flame/PackManifest.h +++ b/launcher/modplatform/flame/PackManifest.h @@ -81,7 +81,7 @@ struct Manifest { QMap files; QString overrides; - bool is_loaded = false; + bool isLoaded = false; }; void loadManifest(Flame::Manifest& m, const QString& filepath); diff --git a/launcher/modplatform/ftb/FTBPackInstallTask.cpp b/launcher/modplatform/ftb/FTBPackInstallTask.cpp index 6081807cf..006b98238 100644 --- a/launcher/modplatform/ftb/FTBPackInstallTask.cpp +++ b/launcher/modplatform/ftb/FTBPackInstallTask.cpp @@ -38,6 +38,8 @@ #include "FTBPackInstallTask.h" +#include + #include "FileSystem.h" #include "Json.h" #include "minecraft/MinecraftInstance.h" @@ -59,15 +61,18 @@ PackInstallTask::PackInstallTask(Modpack pack, QString version, QWidget* parent) bool PackInstallTask::abort() { - if (!canAbort()) + if (!canAbort()) { return false; + } bool aborted = true; - if (m_net_job) + if (m_net_job) { aborted &= m_net_job->abort(); - if (m_modIdResolverTask) + } + if (m_modIdResolverTask) { aborted &= m_modIdResolverTask->abort(); + } return aborted ? InstanceTask::abort() : false; } @@ -78,15 +83,15 @@ void PackInstallTask::executeTask() setAbortable(false); // Find pack version - auto version_it = std::find_if(m_pack.versions.constBegin(), m_pack.versions.constEnd(), - [this](const FTB::VersionInfo& a) { return a.name == m_versionName; }); + auto versionIt = std::find_if(m_pack.versions.constBegin(), m_pack.versions.constEnd(), + [this](const FTB::VersionInfo& a) { return a.name == m_versionName; }); - if (version_it == m_pack.versions.constEnd()) { + if (versionIt == m_pack.versions.constEnd()) { emitFailed(tr("Failed to find pack version %1").arg(m_versionName)); return; } - auto version = *version_it; + const auto& version = *versionIt; auto netJob = makeShared("FTB::VersionFetch", APPLICATION->network()); @@ -112,10 +117,10 @@ void PackInstallTask::onManifestDownloadSucceeded(QByteArray* responsePtr) QByteArray response = std::move(*responsePtr); m_net_job.reset(); - QJsonParseError parse_error{}; - QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error); - if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response from FTB at " << parse_error.offset << " reason: " << parse_error.errorString(); + QJsonParseError parseError{}; + QJsonDocument const doc = QJsonDocument::fromJson(response, &parseError); + if (parseError.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from FTB at " << parseError.offset << " reason: " << parseError.errorString(); qWarning() << response; return; } @@ -179,24 +184,25 @@ void PackInstallTask::onResolveModsSucceeded() Flame::Manifest results = m_modIdResolverTask->getResults(); for (int index = 0; index < m_fileIds.size(); index++) { - const auto file_id = m_fileIds.at(index); - if (file_id < 0) + const auto fileId = m_fileIds.at(index); + if (fileId < 0) { continue; + } - Flame::File resultsFile = results.files[file_id]; + Flame::File const resultsFile = results.files.value(fileId); VersionFile& localFile = m_version.files[index]; // First check for blocked mods if (resultsFile.version.downloadUrl.isEmpty()) { - BlockedMod blocked_mod; - blocked_mod.name = resultsFile.version.fileName; - blocked_mod.websiteUrl = QString("%1/download/%2").arg(resultsFile.pack.websiteUrl, QString::number(resultsFile.fileId)); - blocked_mod.hash = resultsFile.version.hash; - blocked_mod.matched = false; - blocked_mod.localPath = ""; - blocked_mod.targetFolder = resultsFile.targetFolder; + BlockedMod blockedMod; + blockedMod.name = resultsFile.version.fileName; + blockedMod.websiteUrl = QString("%1/download/%2").arg(resultsFile.pack.websiteUrl, QString::number(resultsFile.fileId)); + blockedMod.hash = resultsFile.version.hash; + blockedMod.matched = false; + blockedMod.localPath = ""; + blockedMod.targetFolder = resultsFile.targetFolder; - m_blockedMods.append(blocked_mod); + m_blockedMods.append(blockedMod); anyBlocked = true; } else { @@ -209,14 +215,14 @@ void PackInstallTask::onResolveModsSucceeded() if (anyBlocked) { qDebug() << "Blocked files found, displaying file list"; - BlockedModsDialog message_dialog(m_parent, tr("Blocked files found"), - tr("The following files are not available for download in third party launchers.
" - "You will need to manually download them and add them to the instance."), - m_blockedMods); + BlockedModsDialog messageDialog(m_parent, tr("Blocked files found"), + tr("The following files are not available for download in third party launchers.
" + "You will need to manually download them and add them to the instance."), + m_blockedMods); - message_dialog.setModal(true); + messageDialog.setModal(true); - if (message_dialog.exec() == QDialog::Accepted) { + if (messageDialog.exec() == QDialog::Accepted) { qDebug() << "Post dialog blocked mods list: " << m_blockedMods; createInstance(); } else { @@ -238,20 +244,21 @@ void PackInstallTask::createInstance() auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg"); auto instanceSettings = std::make_unique(instanceConfigPath); - MinecraftInstance instance(m_globalSettings, std::move(instanceSettings), m_stagingPath); - auto components = instance.getPackProfile(); + m_instance = std::make_unique(m_globalSettings, std::move(instanceSettings), m_stagingPath); + auto* components = m_instance->getPackProfile(); components->buildingFromScratch(); - for (auto target : m_version.targets) { + for (const auto& target : m_version.targets) { if (target.type == "game" && target.name == "minecraft") { components->setComponentVersion("net.minecraft", target.version, true); break; } } - for (auto target : m_version.targets) { - if (target.type != "modloader") + for (const auto& target : m_version.targets) { + if (target.type != "modloader") { continue; + } if (target.name == "forge") { components->setComponentVersion("net.minecraftforge", target.version); @@ -278,11 +285,11 @@ void PackInstallTask::createInstance() components->saveNow(); - instance.setName(name()); - instance.setIconKey(m_instIcon); - instance.setManagedPack("ftb", QString::number(m_pack.id), m_pack.name, QString::number(m_version.id), m_version.name); + m_instance->setName(name()); + m_instance->setIconKey(m_instIcon); + m_instance->setManagedPack("ftb", QString::number(m_pack.id), m_pack.name, QString::number(m_version.id), m_version.name); - instance.saveNow(); + m_instance->saveNow(); onCreateInstanceSucceeded(); } @@ -299,13 +306,14 @@ void PackInstallTask::downloadPack() auto jobPtr = makeShared(tr("Mod download"), APPLICATION->network()); for (const auto& file : m_version.files) { - if (file.serverOnly || file.url.isEmpty()) + if (file.serverOnly || file.url.isEmpty()) { continue; + } auto path = FS::PathCombine(m_stagingPath, ".minecraft", file.path, file.name); qDebug() << "Will try to download" << file.url << "to" << path; - QFileInfo file_info(file.name); + QFileInfo const fileInfo(file.name); auto dl = Net::Download::makeFile(file.url, path); if (!file.sha1.isEmpty()) { @@ -333,27 +341,27 @@ void PackInstallTask::onModDownloadSucceeded() if (!m_blockedMods.isEmpty()) { copyBlockedMods(); } - emitSucceeded(); + downloadFiles(m_instance.get()); } void PackInstallTask::onManifestDownloadFailed(QString reason) { m_net_job.reset(); - emitFailed(reason); + emitFailed(std::move(reason)); } void PackInstallTask::onResolveModsFailed(QString reason) { m_net_job.reset(); - emitFailed(reason); + emitFailed(std::move(reason)); } void PackInstallTask::onCreateInstanceFailed(QString reason) { - emitFailed(reason); + emitFailed(std::move(reason)); } void PackInstallTask::onModDownloadFailed(QString reason) { m_net_job.reset(); - emitFailed(reason); + emitFailed(std::move(reason)); } /// @brief copy the matched blocked mods to the instance staging area @@ -362,7 +370,7 @@ void PackInstallTask::copyBlockedMods() setStatus(tr("Copying Blocked Mods...")); setAbortable(false); int i = 0; - int total = m_blockedMods.length(); + auto total = m_blockedMods.length(); setProgress(i, total); for (const auto& mod : m_blockedMods) { if (!mod.matched) { @@ -370,14 +378,14 @@ void PackInstallTask::copyBlockedMods() continue; } - auto dest_path = FS::PathCombine(m_stagingPath, ".minecraft", mod.targetFolder, mod.name); + auto destPath = FS::PathCombine(m_stagingPath, ".minecraft", mod.targetFolder, mod.name); setStatus(tr("Copying Blocked Mods (%1 out of %2 are done)").arg(QString::number(i), QString::number(total))); - qDebug() << "Will try to copy" << mod.localPath << "to" << dest_path; + qDebug() << "Will try to copy" << mod.localPath << "to" << destPath; - if (!FS::copy(mod.localPath, dest_path)()) { - qDebug() << "Copy of" << mod.localPath << "to" << dest_path << "Failed"; + if (!FS::copy(mod.localPath, destPath)()) { + qDebug() << "Copy of" << mod.localPath << "to" << destPath << "Failed"; } i++; diff --git a/launcher/modplatform/ftb/FTBPackInstallTask.h b/launcher/modplatform/ftb/FTBPackInstallTask.h index 49d2bb991..d265f31c6 100644 --- a/launcher/modplatform/ftb/FTBPackInstallTask.h +++ b/launcher/modplatform/ftb/FTBPackInstallTask.h @@ -46,6 +46,7 @@ #include "ui/dialogs/BlockedModsDialog.h" #include +#include namespace FTB { @@ -91,6 +92,8 @@ class PackInstallTask final : public InstanceTask { QMap m_filesToCopy; QList m_blockedMods; + std::unique_ptr m_instance; + // FIXME: nuke QWidget* m_parent; }; diff --git a/launcher/modplatform/import_ftb/PackInstallTask.cpp b/launcher/modplatform/import_ftb/PackInstallTask.cpp index 659c5d2ed..75fef6701 100644 --- a/launcher/modplatform/import_ftb/PackInstallTask.cpp +++ b/launcher/modplatform/import_ftb/PackInstallTask.cpp @@ -20,11 +20,9 @@ #include -#include "BaseInstance.h" #include "FileSystem.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" -#include "modplatform/ResourceAPI.h" #include "modplatform/import_ftb/PackHelpers.h" #include "settings/INISettingsObject.h" @@ -51,19 +49,20 @@ void PackInstallTask::copySettings() progress(2, 2); QString instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg"); - MinecraftInstance instance(m_globalSettings, std::make_unique(instanceConfigPath), m_stagingPath); + m_instance = + std::make_unique(m_globalSettings, std::make_unique(instanceConfigPath), m_stagingPath); { - SettingsObject::Lock lock(instance.settings()); - instance.settings()->set("InstanceType", "OneSix"); - instance.settings()->set("totalTimePlayed", m_pack.totalPlayTime / 1000); + SettingsObject::Lock const lock(m_instance->settings()); + m_instance->settings()->set("InstanceType", "OneSix"); + m_instance->settings()->set("totalTimePlayed", m_pack.totalPlayTime / 1000); if (m_pack.jvmArgs.isValid() && !m_pack.jvmArgs.toString().isEmpty()) { - instance.settings()->set("OverrideJavaArgs", true); - instance.settings()->set("JvmArgs", m_pack.jvmArgs.toString()); + m_instance->settings()->set("OverrideJavaArgs", true); + m_instance->settings()->set("JvmArgs", m_pack.jvmArgs.toString()); } - auto* components = instance.getPackProfile(); + auto* components = m_instance->getPackProfile(); components->buildingFromScratch(); components->setComponentVersion("net.minecraft", m_pack.mcVersion, true); @@ -92,13 +91,13 @@ void PackInstallTask::copySettings() } components->saveNow(); - instance.setName(name()); + m_instance->setName(name()); if (m_instIcon == "default") { m_instIcon = "ftb_logo"; } - instance.setIconKey(m_instIcon); + m_instance->setIconKey(m_instIcon); } - emitSucceeded(); + downloadFiles(m_instance.get()); } } // namespace FTBImportAPP diff --git a/launcher/modplatform/import_ftb/PackInstallTask.h b/launcher/modplatform/import_ftb/PackInstallTask.h index 842e4b35e..f9a771a7d 100644 --- a/launcher/modplatform/import_ftb/PackInstallTask.h +++ b/launcher/modplatform/import_ftb/PackInstallTask.h @@ -21,8 +21,11 @@ #include #include +#include +#include #include "InstanceTask.h" #include "PackHelpers.h" +#include "minecraft/MinecraftInstance.h" namespace FTBImportAPP { @@ -30,11 +33,11 @@ class PackInstallTask : public InstanceTask { Q_OBJECT public: - explicit PackInstallTask(const Modpack& pack) : m_pack(pack) {} - virtual ~PackInstallTask() = default; + explicit PackInstallTask(Modpack pack) : m_pack(std::move(pack)) {} + ~PackInstallTask() override = default; protected: - virtual void executeTask() override; + void executeTask() override; private slots: void copySettings(); @@ -43,6 +46,8 @@ class PackInstallTask : public InstanceTask { QFuture m_copyFuture; QFutureWatcher m_copyFutureWatcher; + std::unique_ptr m_instance; + const Modpack m_pack; }; diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp index 8220676fc..f88071dd2 100644 --- a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp +++ b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp @@ -36,6 +36,7 @@ #include "PackInstallTask.h" #include +#include #include "BaseInstance.h" #include "FileSystem.h" @@ -52,12 +53,9 @@ namespace LegacyFTB { -PackInstallTask::PackInstallTask(QNetworkAccessManager* network, const Modpack& pack, QString version) -{ - m_pack = pack; - m_version = version; - m_network = network; -} +PackInstallTask::PackInstallTask(QNetworkAccessManager* network, Modpack pack, QString version) + : m_network(network), m_pack(std::move(pack)), m_version(std::move(version)) +{} void PackInstallTask::executeTask() { @@ -73,22 +71,22 @@ void PackInstallTask::downloadPack() auto path = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file); auto entry = APPLICATION->metacache()->resolveEntry("FTBPacks", path); entry->setStale(true); - archivePath = entry->getFullPath(); - netJobContainer.reset(new NetJob("Download FTB Pack", m_network)); + m_archivePath = entry->getFullPath(); + m_netJobContainer.reset(new NetJob("Download FTB Pack", m_network)); QString url; if (m_pack.type == PackType::Private) { url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "privatepacks/%1").arg(path); } else { url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "modpacks/%1").arg(path); } - netJobContainer->addNetAction(Net::ApiDownload::makeCached(url, entry)); + m_netJobContainer->addNetAction(Net::ApiDownload::makeCached(url, entry)); - connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip); - connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed); - connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress); - connect(netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::emitAborted); + connect(m_netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip); + connect(m_netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed); + connect(m_netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress); + connect(m_netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::emitAborted); - netJobContainer->start(); + m_netJobContainer->start(); setAbortable(true); progress(1, 4); @@ -102,7 +100,7 @@ void PackInstallTask::unzip() QDir extractDir(m_stagingPath); - m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), QOverload::of(MMCZip::extractDir), archivePath, + m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), QOverload::of(MMCZip::extractDir), m_archivePath, extractDir.absolutePath() + "/unzip"); connect(&m_extractFutureWatcher, &QFutureWatcher::finished, this, &PackInstallTask::onUnzipFinished); connect(&m_extractFutureWatcher, &QFutureWatcher::canceled, this, &PackInstallTask::onUnzipCanceled); @@ -133,11 +131,12 @@ void PackInstallTask::install() } QString instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg"); - MinecraftInstance instance(m_globalSettings, std::make_unique(instanceConfigPath), m_stagingPath); + m_instance = + std::make_unique(m_globalSettings, std::make_unique(instanceConfigPath), m_stagingPath); { - SettingsObject::Lock lock(instance.settings()); + SettingsObject::Lock const lock(m_instance->settings()); - auto components = instance.getPackProfile(); + auto* components = m_instance->getPackProfile(); components->buildingFromScratch(); components->setComponentVersion("net.minecraft", m_pack.mcVersion, true); @@ -177,7 +176,7 @@ void PackInstallTask::install() qDebug() << "Found jarmods, installing..."; QStringList jarmods; - for (auto info : jarmodDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) { + for (const auto& info : jarmodDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) { qDebug() << "Jarmod:" << info.fileName(); jarmods.push_back(info.absoluteFilePath()); } @@ -199,14 +198,14 @@ void PackInstallTask::install() progress(4, 4); - instance.setName(name()); + m_instance->setName(name()); if (m_instIcon == "default") { m_instIcon = "ftb_logo"; } - instance.setIconKey(m_instIcon); + m_instance->setIconKey(m_instIcon); } - emitSucceeded(); + downloadFiles(m_instance.get()); } bool PackInstallTask::abort() @@ -215,7 +214,7 @@ bool PackInstallTask::abort() return false; } - netJobContainer->abort(); + m_netJobContainer->abort(); return InstanceTask::abort(); } diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.h b/launcher/modplatform/legacy_ftb/PackInstallTask.h index 98777214f..c13e052e3 100644 --- a/launcher/modplatform/legacy_ftb/PackInstallTask.h +++ b/launcher/modplatform/legacy_ftb/PackInstallTask.h @@ -1,11 +1,10 @@ #pragma once #include "InstanceTask.h" #include "PackHelpers.h" -#include "meta/Index.h" -#include "meta/Version.h" -#include "meta/VersionList.h" +#include "minecraft/MinecraftInstance.h" #include "net/NetJob.h" +#include #include namespace LegacyFTB { @@ -14,15 +13,15 @@ class PackInstallTask : public InstanceTask { Q_OBJECT public: - explicit PackInstallTask(QNetworkAccessManager* network, const Modpack& pack, QString version); - virtual ~PackInstallTask() {} + explicit PackInstallTask(QNetworkAccessManager* network, Modpack pack, QString version); + ~PackInstallTask() override = default; bool canAbort() const override { return true; } bool abort() override; protected: //! Entry point for tasks. - virtual void executeTask() override; + void executeTask() override; private: void downloadPack(); @@ -36,11 +35,13 @@ class PackInstallTask : public InstanceTask { private: /* data */ QNetworkAccessManager* m_network; - bool abortable = false; + bool m_abortable = false; QFuture> m_extractFuture; QFutureWatcher> m_extractFutureWatcher; - NetJob::Ptr netJobContainer; - QString archivePath; + NetJob::Ptr m_netJobContainer; + QString m_archivePath; + + std::unique_ptr m_instance; Modpack m_pack; QString m_version; diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index 0cb2c547d..f7a4888ef 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -16,7 +16,6 @@ #include "net/ChecksumValidator.h" #include "net/ApiDownload.h" -#include "net/ApiHeaderProxy.h" #include "net/NetJob.h" #include "modplatform/ModIndex.h" @@ -39,10 +38,10 @@ bool ModrinthCreationTask::abort() if (m_task) { m_task->abort(); } - return InstanceCreationTask::abort(); + return InstanceTask::abort(); } -bool ModrinthCreationTask::updateInstance() +void ModrinthCreationTask::executeTask() { auto* instanceList = APPLICATION->instances(); @@ -58,28 +57,30 @@ bool ModrinthCreationTask::updateInstance() inst = instanceList->getInstanceById(originalName()); if (!inst) { - return false; + createInstance(); + return; } } } QString indexPath = FS::PathCombine(m_stagingPath, "modrinth.index.json"); if (!parseManifest(indexPath, m_files, true, false)) { - return false; + return; } auto versionName = inst->getManagedPackVersionName(); - m_root_path = QFileInfo(inst->gameRoot()).fileName(); + m_rootPath = QFileInfo(inst->gameRoot()).fileName(); auto versionStr = !versionName.isEmpty() ? tr(" (version %1)").arg(versionName) : ""; if (shouldConfirmUpdate()) { auto shouldUpdate = askIfShouldUpdate(m_parent, versionStr); if (shouldUpdate == ShouldUpdate::SkipUpdating) { - return false; + createInstance(); + return; } if (shouldUpdate == ShouldUpdate::Cancel) { - m_abort = true; - return false; + emitAborted(); + return; } } @@ -147,30 +148,28 @@ bool ModrinthCreationTask::updateInstance() QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Cancel); if (dialog->exec() == QDialog::DialogCode::Rejected) { - m_abort = true; - return false; + emitAborted(); + return; } } setOverride(true, inst->id()); qDebug() << "Will override instance!"; - m_instance = inst; + m_oldInstance = inst; // We let it go through the createInstance() stage, just with a couple modifications for updating - return false; + createInstance(); } // https://docs.modrinth.com/docs/modpacks/format_definition/ -std::unique_ptr ModrinthCreationTask::createInstance() +void ModrinthCreationTask::createInstance() { - QEventLoop loop; - QString parentFolder(FS::PathCombine(m_stagingPath, "mrpack")); QString indexPath = FS::PathCombine(m_stagingPath, "modrinth.index.json"); if (m_files.empty() && !parseManifest(indexPath, m_files, true, true)) { - return nullptr; + return; } // Keep index file in case we need it some other time (like when changing versions) @@ -178,7 +177,7 @@ std::unique_ptr ModrinthCreationTask::createInstance() FS::ensureFilePathExists(newIndexPlace); FS::move(indexPath, newIndexPlace); - auto mcPath = FS::PathCombine(m_stagingPath, m_root_path); + auto mcPath = FS::PathCombine(m_stagingPath, m_rootPath); auto overridePath = FS::PathCombine(m_stagingPath, "overrides"); if (QFile::exists(overridePath)) { @@ -187,8 +186,8 @@ std::unique_ptr ModrinthCreationTask::createInstance() // Apply the overrides if (!FS::move(overridePath, mcPath)) { - setError(tr("Could not rename the overrides folder:\n") + "overrides"); - return nullptr; + emitFailed(tr("Could not rename the overrides folder:\n") + "overrides"); + return; } } @@ -200,85 +199,80 @@ std::unique_ptr ModrinthCreationTask::createInstance() // Apply the overrides if (!FS::overrideFolder(mcPath, clientOverridePath)) { - setError(tr("Could not rename the client overrides folder:\n") + "client overrides"); - return nullptr; + emitFailed(tr("Could not rename the client overrides folder:\n") + "client overrides"); + return; } } QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); auto instanceSettings = std::make_unique(configPath); - auto instance = std::make_unique(m_globalSettings, std::move(instanceSettings), m_stagingPath); + m_newInstance = std::make_unique(m_globalSettings, std::move(instanceSettings), m_stagingPath); - auto* components = instance->getPackProfile(); + auto* components = m_newInstance->getPackProfile(); components->buildingFromScratch(); - components->setComponentVersion("net.minecraft", m_minecraft_version, true); + components->setComponentVersion("net.minecraft", m_minecraftVersion, true); QString loader; - if (!m_fabric_version.isEmpty()) { - components->setComponentVersion("net.fabricmc.fabric-loader", m_fabric_version); + if (!m_fabricVersion.isEmpty()) { + components->setComponentVersion("net.fabricmc.fabric-loader", m_fabricVersion); loader = ModPlatform::getModLoaderAsString(ModPlatform::ModLoaderType::Fabric); } - if (!m_quilt_version.isEmpty()) { - components->setComponentVersion("org.quiltmc.quilt-loader", m_quilt_version); + if (!m_quiltVersion.isEmpty()) { + components->setComponentVersion("org.quiltmc.quilt-loader", m_quiltVersion); loader = ModPlatform::getModLoaderAsString(ModPlatform::ModLoaderType::Quilt); } - if (!m_forge_version.isEmpty()) { - components->setComponentVersion("net.minecraftforge", m_forge_version); + if (!m_forgeVersion.isEmpty()) { + components->setComponentVersion("net.minecraftforge", m_forgeVersion); loader = ModPlatform::getModLoaderAsString(ModPlatform::ModLoaderType::Forge); } - if (!m_neoForge_version.isEmpty()) { - components->setComponentVersion("net.neoforged", m_neoForge_version); + if (!m_neoForgeVersion.isEmpty()) { + components->setComponentVersion("net.neoforged", m_neoForgeVersion); loader = ModPlatform::getModLoaderAsString(ModPlatform::ModLoaderType::NeoForge); } if (m_instIcon != "default") { - instance->setIconKey(m_instIcon); - } else if (!m_managed_id.isEmpty()) { - instance->setIconKey("modrinth"); + m_newInstance->setIconKey(m_instIcon); + } else if (!m_managedId.isEmpty()) { + m_newInstance->setIconKey("modrinth"); } - // Don't add managed info to packs without an ID (most likely imported from ZIP) - if (!m_managed_id.isEmpty()) { - instance->setManagedPack("modrinth", m_managed_id, m_managed_name, m_managed_version_id, version()); - } else { - instance->setManagedPack("modrinth", "", name(), "", ""); - } + setManagedPack(m_newInstance.get()); - instance->setName(name()); - instance->saveNow(); + m_newInstance->setName(name()); + m_newInstance->saveNow(); auto downloadMods = makeShared(tr("Mod Download Modrinth"), APPLICATION->network()); - auto rootModpackPath = FS::PathCombine(m_stagingPath, m_root_path); + auto rootModpackPath = FS::PathCombine(m_stagingPath, m_rootPath); auto rootModpackUrl = QUrl::fromLocalFile(rootModpackPath); // TODO make this work with other sorts of resource - QHash resources; for (auto& file : m_files) { auto fileName = file.path; fileName = FS::RemoveInvalidPathChars(fileName); auto filePath = FS::PathCombine(rootModpackPath, fileName); if (!rootModpackUrl.isParentOf(QUrl::fromLocalFile(filePath))) { // This means we somehow got out of the root folder, so abort here to prevent exploits - setError(tr("One of the files has a path that leads to an arbitrary location (%1). This is a security risk and isn't allowed.") - .arg(fileName)); - return nullptr; + emitFailed( + tr("One of the files has a path that leads to an arbitrary location (%1). This is a security risk and isn't allowed.") + .arg(fileName)); + return; } if (fileName.startsWith("mods/")) { auto* mod = new Mod(filePath); ModDetails d; d.mod_id = filePath; mod->setDetails(d); - resources[file.hash.toHex()] = mod; + m_resources.insert(file.hash.toHex(), mod); } if (file.downloads.empty()) { - setError(tr("The file '%1' is missing a download link. This is invalid in the pack format.").arg(fileName)); - return nullptr; + emitFailed(tr("The file '%1' is missing a download link. This is invalid in the pack format.").arg(fileName)); + return; } qDebug() << "Will try to download" << file.downloads.front() << "to" << filePath; Net::ModrinthDownloadMeta meta{ - .reason = m_instance.has_value() ? "update" : "modpack", - .gameVersion = m_minecraft_version, + .reason = m_oldInstance.has_value() ? "update" : "modpack", + .gameVersion = m_minecraftVersion, .loader = loader, }; @@ -302,14 +296,9 @@ std::unique_ptr ModrinthCreationTask::createInstance() } } - bool endedWell = false; - - connect(downloadMods.get(), &NetJob::succeeded, this, [&endedWell]() { endedWell = true; }); - connect(downloadMods.get(), &NetJob::failed, [this, &endedWell](const QString& reason) { - endedWell = false; - setError(reason); - }); - connect(downloadMods.get(), &NetJob::finished, &loop, &QEventLoop::quit); + connect(downloadMods.get(), &NetJob::succeeded, this, &ModrinthCreationTask::ensureMetaLoop); + connect(downloadMods.get(), &NetJob::failed, this, &ModrinthCreationTask::emitFailed); + connect(downloadMods.get(), &NetJob::aborted, this, &ModrinthCreationTask::emitAborted); connect(downloadMods.get(), &NetJob::progress, [this](qint64 current, qint64 total) { setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); setProgress(current, total); @@ -319,57 +308,6 @@ std::unique_ptr ModrinthCreationTask::createInstance() setStatus(tr("Downloading mods...")); downloadMods->start(); m_task = downloadMods; - - loop.exec(); - - if (!endedWell) { - for (auto* resource : resources) { - delete resource; - } - return nullptr; - } - - QEventLoop ensureMetaLoop; - QDir folder = FS::PathCombine(instance->modsRoot(), ".index"); - auto ensureMetadataTask = makeShared(resources, folder, ModPlatform::ResourceProvider::MODRINTH); - connect(ensureMetadataTask.get(), &Task::succeeded, this, [&endedWell]() { endedWell = true; }); - connect(ensureMetadataTask.get(), &Task::finished, &ensureMetaLoop, &QEventLoop::quit); - connect(ensureMetadataTask.get(), &Task::progress, [this](qint64 current, qint64 total) { - setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); - setProgress(current, total); - }); - connect(ensureMetadataTask.get(), &Task::stepProgress, this, &ModrinthCreationTask::propagateStepProgress); - - ensureMetadataTask->start(); - m_task = ensureMetadataTask; - - ensureMetaLoop.exec(); - for (auto* resource : resources) { - delete resource; - } - resources.clear(); - - // Update information of the already installed instance, if any. - if (m_instance && endedWell) { - setAbortable(false); - auto* inst = m_instance.value(); - - // Only change the name if it didn't use a custom name, so that the previous custom name - // is preserved, but if we're using the original one, we update the version string. - // NOTE: This needs to come before the copyManagedPack call! - if (inst->name().contains(inst->getManagedPackVersionName()) && inst->name() != instance->name()) { - if (askForChangingInstanceName(m_parent, inst->name(), instance->name()) == InstanceNameChange::ShouldChange) { - inst->setName(instance->name()); - } - } - - inst->copyManagedPack(*instance); - } - - if (endedWell) { - return instance; - } - return nullptr; } bool ModrinthCreationTask::parseManifest(const QString& indexPath, std::vector& files, bool setInternalData, bool showOptionalDialog) @@ -385,10 +323,10 @@ bool ModrinthCreationTask::parseManifest(const QString& indexPath, std::vector(obj, "files", "modrinth.index.json"); @@ -470,15 +408,15 @@ bool ModrinthCreationTask::parseManifest(const QString& indexPath, std::vector(m_resources, folder, ModPlatform::ResourceProvider::MODRINTH); + connect(ensureMetadataTask.get(), &Task::succeeded, this, &ModrinthCreationTask::finishInstall); + connect(ensureMetadataTask.get(), &Task::failed, this, &ModrinthCreationTask::emitFailed); + connect(ensureMetadataTask.get(), &Task::aborted, this, &ModrinthCreationTask::emitAborted); + connect(ensureMetadataTask.get(), &Task::progress, [this](qint64 current, qint64 total) { + setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); + setProgress(current, total); + }); + connect(ensureMetadataTask.get(), &Task::stepProgress, this, &ModrinthCreationTask::propagateStepProgress); + + ensureMetadataTask->start(); + m_task = ensureMetadataTask; +} + +ModrinthCreationTask::~ModrinthCreationTask() +{ + for (auto* resource : m_resources) { + delete resource; + } + m_resources.clear(); +} + +void ModrinthCreationTask::setManagedPack(BaseInstance* instance) +{ + // Don't add managed info to packs without an ID (most likely imported from ZIP) + if (!m_managedId.isEmpty()) { + instance->setManagedPack("modrinth", m_managedId, m_managedName, m_managedVersionId, version()); + } else { + instance->setManagedPack("modrinth", "", name(), "", ""); + } +} + +void ModrinthCreationTask::finishInstall() +{ + // Update information of the already installed instance, if any. + if (m_oldInstance) { + setAbortable(false); + auto* inst = *m_oldInstance; + + // Only change the name if it didn't use a custom name, so that the previous custom name + // is preserved, but if we're using the original one, we update the version string. + // NOTE: This needs to come before the setManagedPack call! + if (inst->name().contains(inst->getManagedPackVersionName()) && inst->name() != name()) { + if (askForChangingInstanceName(m_parent, inst->name(), name()) == InstanceNameChange::ShouldChange) { + inst->setName(name()); + } + } + + setManagedPack(*m_oldInstance); + } + + if (shouldOverride()) { + bool deleteFailed = false; + + setAbortable(false); + setStatus(tr("Removing old conflicting files...")); + qDebug() << "Removing old files"; + + for (const QString& path : m_filesToRemove) { + if (!QFile::exists(path)) { + continue; + } + + qDebug() << "Removing" << path; + + if (!QFile::remove(path)) { + qCritical() << "Could not remove" << path; + deleteFailed = true; + } + } + + if (deleteFailed) { + emitFailed(tr("Failed to remove old conflicting files.")); + return; + } + } + downloadFiles(m_newInstance.get()); +} diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h index c8835e142..9abf6de5e 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h @@ -10,9 +10,11 @@ #include #include "BaseInstance.h" -#include "InstanceCreationTask.h" +#include "InstanceTask.h" -class ModrinthCreationTask final : public InstanceCreationTask { +class Resource; + +class ModrinthCreationTask final : public InstanceTask { Q_OBJECT struct File { QString path; @@ -30,32 +32,42 @@ class ModrinthCreationTask final : public InstanceCreationTask { QString id, QString versionId = {}, QString originalInstanceId = {}) - : m_parent(parent), m_managed_id(std::move(id)), m_managed_version_id(std::move(versionId)) + : m_parent(parent), m_managedId(std::move(id)), m_managedVersionId(std::move(versionId)) { setStagingPath(stagingPath); setParentSettings(globalSettings); - m_original_instance_id = std::move(originalInstanceId); + m_originalInstanceId = std::move(originalInstanceId); } + ~ModrinthCreationTask() override; bool abort() override; - bool updateInstance() override; - std::unique_ptr createInstance() override; + void createInstance(); + void executeTask() override; + + private slots: + void finishInstall(); private: bool parseManifest(const QString&, std::vector&, bool setInternalData = true, bool showOptionalDialog = true); + void ensureMetaLoop(); + void setManagedPack(BaseInstance* instance); + private: QWidget* m_parent = nullptr; - QString m_minecraft_version, m_fabric_version, m_quilt_version, m_forge_version, m_neoForge_version; - QString m_managed_id, m_managed_version_id, m_managed_name; + QString m_minecraftVersion, m_fabricVersion, m_quiltVersion, m_forgeVersion, m_neoForgeVersion; + QString m_managedId, m_managedVersionId, m_managedName; std::vector m_files; Task::Ptr m_task; - std::optional m_instance; + std::optional m_oldInstance; + std::unique_ptr m_newInstance{}; - QString m_root_path = "minecraft"; + QString m_rootPath = "minecraft"; + + QHash m_resources; }; diff --git a/launcher/ui/dialogs/NewInstanceDialog.cpp b/launcher/ui/dialogs/NewInstanceDialog.cpp index 8cf094527..147d93b6f 100644 --- a/launcher/ui/dialogs/NewInstanceDialog.cpp +++ b/launcher/ui/dialogs/NewInstanceDialog.cpp @@ -46,7 +46,6 @@ #include #include "IconPickerDialog.h" -#include "ProgressDialog.h" #include "VersionSelectDialog.h" #include @@ -69,20 +68,19 @@ NewInstanceDialog::NewInstanceDialog(const QString& initialGroup, const QString& url, - const QMap& extra_info, + const QMap& extraInfo, QWidget* parent) - : QDialog(parent), ui(new Ui::NewInstanceDialog) + : QDialog(parent), ui(new Ui::NewInstanceDialog), m_instIconKey("default") { ui->setupUi(this); setWindowIcon(QIcon::fromTheme("new")); - InstIconKey = "default"; - ui->iconButton->setIcon(APPLICATION->icons()->getIcon(InstIconKey)); + ui->iconButton->setIcon(APPLICATION->icons()->getIcon(m_instIconKey)); QStringList groups = APPLICATION->instances()->getGroups(); groups.prepend(""); - int index = groups.indexOf(initialGroup); + auto index = groups.indexOf(initialGroup); if (index == -1) { index = 1; groups.insert(index, initialGroup); @@ -102,35 +100,35 @@ NewInstanceDialog::NewInstanceDialog(const QString& initialGroup, ui->verticalLayout->insertWidget(2, m_container); m_container->addButtons(m_buttons); - connect(m_container, &PageContainer::selectedPageChanged, this, [this](BasePage* previous, BasePage* selected) { - m_buttons->button(QDialogButtonBox::Ok)->setEnabled(creationTask && !instName().isEmpty()); + connect(m_container, &PageContainer::selectedPageChanged, this, [this](BasePage* /*previous*/, BasePage* /*selected*/) { + m_buttons->button(QDialogButtonBox::Ok)->setEnabled(m_creationTask && !instName().isEmpty()); }); // Bonk Qt over its stupid head and make sure it understands which button is the default one... // See: https://stackoverflow.com/questions/24556831/qbuttonbox-set-default-button - auto OkButton = m_buttons->button(QDialogButtonBox::Ok); - OkButton->setDefault(true); - OkButton->setAutoDefault(true); - OkButton->setText(tr("OK")); - connect(OkButton, &QPushButton::clicked, this, &NewInstanceDialog::accept); + auto* okButton = m_buttons->button(QDialogButtonBox::Ok); + okButton->setDefault(true); + okButton->setAutoDefault(true); + okButton->setText(tr("OK")); + connect(okButton, &QPushButton::clicked, this, &NewInstanceDialog::accept); - auto CancelButton = m_buttons->button(QDialogButtonBox::Cancel); - CancelButton->setDefault(false); - CancelButton->setAutoDefault(false); - CancelButton->setText(tr("Cancel")); - connect(CancelButton, &QPushButton::clicked, this, &NewInstanceDialog::reject); + auto* cancelButton = m_buttons->button(QDialogButtonBox::Cancel); + cancelButton->setDefault(false); + cancelButton->setAutoDefault(false); + cancelButton->setText(tr("Cancel")); + connect(cancelButton, &QPushButton::clicked, this, &NewInstanceDialog::reject); - auto HelpButton = m_buttons->button(QDialogButtonBox::Help); - HelpButton->setDefault(false); - HelpButton->setAutoDefault(false); - HelpButton->setText(tr("Help")); - connect(HelpButton, &QPushButton::clicked, m_container, &PageContainer::help); + auto* helpButton = m_buttons->button(QDialogButtonBox::Help); + helpButton->setDefault(false); + helpButton->setAutoDefault(false); + helpButton->setText(tr("Help")); + connect(helpButton, &QPushButton::clicked, m_container, &PageContainer::help); if (!url.isEmpty()) { QUrl actualUrl(url); m_container->selectPage("import"); - importPage->setUrl(url); - importPage->setExtraInfo(extra_info); + m_importPage->setUrl(url); + m_importPage->setExtraInfo(extraInfo); } updateDialogState(); @@ -138,7 +136,7 @@ NewInstanceDialog::NewInstanceDialog(const QString& initialGroup, if (APPLICATION->settings()->get("NewInstanceGeometry").isValid()) { restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("NewInstanceGeometry").toString().toUtf8())); } else { - auto screen = parent->screen(); + auto* screen = parent->screen(); auto geometry = screen->availableSize(); resize(width(), qMin(geometry.height() - 50, 710)); } @@ -171,13 +169,14 @@ QList NewInstanceDialog::getPages() { QList pages; - importPage = new ImportPage(this); + m_importPage = new ImportPage(this); pages.append(new CustomPage(this)); - pages.append(importPage); + pages.append(m_importPage); pages.append(new AtlPage(this)); - if (APPLICATION->capabilities() & Application::SupportsFlame) + if (APPLICATION->capabilities() & Application::SupportsFlame) { pages.append(new FlamePage(this)); + } pages.append(new FtbPage(this)); pages.append(new LegacyFTB::Page(this)); pages.append(new FTBImportAPP::ImportFTBPage(this)); @@ -199,41 +198,41 @@ NewInstanceDialog::~NewInstanceDialog() void NewInstanceDialog::setSuggestedPack(const QString& name, InstanceTask* task) { - creationTask.reset(task); + m_creationTask.reset(task); ui->instNameTextBox->setPlaceholderText(name); - importVersion.clear(); + m_importVersion.clear(); if (!task) { - ui->iconButton->setIcon(APPLICATION->icons()->getIcon(InstIconKey)); - importIcon = false; + ui->iconButton->setIcon(APPLICATION->icons()->getIcon(m_instIconKey)); + m_importIcon = false; } - auto allowOK = task && !instName().isEmpty(); + auto allowOK = (task != nullptr) && !instName().isEmpty(); m_buttons->button(QDialogButtonBox::Ok)->setEnabled(allowOK); } void NewInstanceDialog::setSuggestedPack(const QString& name, QString version, InstanceTask* task) { - creationTask.reset(task); + m_creationTask.reset(task); ui->instNameTextBox->setPlaceholderText(name); - importVersion = std::move(version); + m_importVersion = std::move(version); if (!task) { - ui->iconButton->setIcon(APPLICATION->icons()->getIcon(InstIconKey)); - importIcon = false; + ui->iconButton->setIcon(APPLICATION->icons()->getIcon(m_instIconKey)); + m_importIcon = false; } - auto allowOK = task && !instName().isEmpty(); + auto allowOK = (task != nullptr) && !instName().isEmpty(); m_buttons->button(QDialogButtonBox::Ok)->setEnabled(allowOK); } void NewInstanceDialog::setSuggestedIconFromFile(const QString& path, const QString& name) { - importIcon = true; - importIconPath = path; - importIconName = name; + m_importIcon = true; + m_importIconPath = path; + m_importIconName = name; // Hmm, for some reason they can be to small ui->iconButton->setIcon(QIcon(path)); @@ -241,22 +240,22 @@ void NewInstanceDialog::setSuggestedIconFromFile(const QString& path, const QStr void NewInstanceDialog::setSuggestedIcon(const QString& key) { - if (key == "default") + if (key == "default") { return; + } auto icon = APPLICATION->icons()->getIcon(key); - importIcon = false; + m_importIcon = false; ui->iconButton->setIcon(icon); } InstanceTask* NewInstanceDialog::extractTask() { - InstanceTask* extracted = creationTask.release(); + InstanceTask* extracted = m_creationTask.release(); - InstanceName inst_name(ui->instNameTextBox->placeholderText().trimmed(), importVersion); - inst_name.setName(ui->instNameTextBox->text().trimmed()); - extracted->setName(inst_name); + extracted->setName(ui->instNameTextBox->text().trimmed()); + extracted->setOriginalName(ui->instNameTextBox->placeholderText().trimmed(), m_importVersion); extracted->setGroup(instGroup()); extracted->setIcon(iconKey()); @@ -265,21 +264,21 @@ InstanceTask* NewInstanceDialog::extractTask() void NewInstanceDialog::updateDialogState() { - auto allowOK = creationTask && !instName().isEmpty(); - auto OkButton = m_buttons->button(QDialogButtonBox::Ok); - if (OkButton->isEnabled() != allowOK) { - OkButton->setEnabled(allowOK); + auto allowOK = m_creationTask && !instName().isEmpty(); + auto* okButton = m_buttons->button(QDialogButtonBox::Ok); + if (okButton->isEnabled() != allowOK) { + okButton->setEnabled(allowOK); } } QString NewInstanceDialog::instName() const { auto result = ui->instNameTextBox->text().trimmed(); - if (result.size()) { + if (result.size() != 0) { return result; } result = ui->instNameTextBox->placeholderText().trimmed(); - if (result.size()) { + if (result.size() != 0) { return result; } return QString(); @@ -291,19 +290,19 @@ QString NewInstanceDialog::instGroup() const } QString NewInstanceDialog::iconKey() const { - return InstIconKey; + return m_instIconKey; } void NewInstanceDialog::on_iconButton_clicked() { importIconNow(); // so the user can switch back IconPickerDialog dlg(this); - dlg.execWithSelection(InstIconKey); + dlg.execWithSelection(m_instIconKey); if (dlg.result() == QDialog::Accepted) { - InstIconKey = dlg.selectedIconKey; - ui->iconButton->setIcon(APPLICATION->icons()->getIcon(InstIconKey)); - importIcon = false; + m_instIconKey = dlg.selectedIconKey; + ui->iconButton->setIcon(APPLICATION->icons()->getIcon(m_instIconKey)); + m_importIcon = false; } } @@ -314,22 +313,22 @@ void NewInstanceDialog::on_instNameTextBox_textChanged([[maybe_unused]] const QS void NewInstanceDialog::importIconNow() { - if (importIcon) { - APPLICATION->icons()->installIcon(importIconPath, importIconName); - InstIconKey = importIconName.mid(0, importIconName.lastIndexOf('.')); - importIcon = false; + if (m_importIcon) { + APPLICATION->icons()->installIcon(m_importIconPath, m_importIconName); + m_instIconKey = m_importIconName.mid(0, m_importIconName.lastIndexOf('.')); + m_importIcon = false; } APPLICATION->settings()->set("NewInstanceGeometry", QString::fromUtf8(saveGeometry().toBase64())); } void NewInstanceDialog::selectedPageChanged(BasePage* previous, BasePage* selected) { - auto prevPage = dynamic_cast(previous); + auto* prevPage = dynamic_cast(previous); if (prevPage) { m_searchTerm = prevPage->getSerachTerm(); } - auto nextPage = dynamic_cast(selected); + auto* nextPage = dynamic_cast(selected); if (nextPage) { nextPage->setSearchTerm(m_searchTerm); } diff --git a/launcher/ui/dialogs/NewInstanceDialog.h b/launcher/ui/dialogs/NewInstanceDialog.h index e97c9f543..7f1cf6b4f 100644 --- a/launcher/ui/dialogs/NewInstanceDialog.h +++ b/launcher/ui/dialogs/NewInstanceDialog.h @@ -55,9 +55,9 @@ class NewInstanceDialog : public QDialog, public BasePageProvider { public: explicit NewInstanceDialog(const QString& initialGroup, const QString& url = QString(), - const QMap& extra_info = {}, - QWidget* parent = 0); - ~NewInstanceDialog(); + const QMap& extraInfo = {}, + QWidget* parent = nullptr); + ~NewInstanceDialog() override; void updateDialogState(); @@ -84,22 +84,23 @@ class NewInstanceDialog : public QDialog, public BasePageProvider { void on_instNameTextBox_textChanged(const QString& arg1); void selectedPageChanged(BasePage* previous, BasePage* selected); + private: + void importIconNow(); + private: Ui::NewInstanceDialog* ui = nullptr; PageContainer* m_container = nullptr; QDialogButtonBox* m_buttons = nullptr; - QString InstIconKey; - ImportPage* importPage = nullptr; - std::unique_ptr creationTask; + QString m_instIconKey; + ImportPage* m_importPage = nullptr; + std::unique_ptr m_creationTask; - bool importIcon = false; - QString importIconPath; - QString importIconName; + bool m_importIcon = false; + QString m_importIconPath; + QString m_importIconName; - QString importVersion; + QString m_importVersion; QString m_searchTerm; - - void importIconNow(); }; diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index d2683fa92..d371e4791 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -17,11 +17,9 @@ #include #include "Application.h" -#include "BuildConfig.h" #include "InstanceImportTask.h" #include "InstanceList.h" #include "InstanceTask.h" -#include "Json.h" #include "Markdown.h" #include "StringUtils.h" @@ -29,11 +27,10 @@ #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ProgressDialog.h" -#include "net/ApiDownload.h" - /** This is just to override the combo box popup behavior so that the combo box doesn't take the whole screen. * ... thanks Qt. */ +namespace { class NoBigComboBoxStyle : public QProxyStyle { Q_OBJECT @@ -41,8 +38,9 @@ class NoBigComboBoxStyle : public QProxyStyle { // clang-format off int styleHint(QStyle::StyleHint hint, const QStyleOption* option = nullptr, const QWidget* widget = nullptr, QStyleHintReturn* returnData = nullptr) const override { - if (hint == QStyle::SH_ComboBox_Popup) - return false; + if (hint == QStyle::SH_ComboBox_Popup) { + return 0; +} return QProxyStyle::styleHint(hint, option, widget, returnData); } @@ -58,39 +56,42 @@ class NoBigComboBoxStyle : public QProxyStyle { public: static NoBigComboBoxStyle* getInstance(QStyle* style) { - static QHash s_singleton_instances_ = {}; - static std::mutex s_singleton_instances_mutex_; + static QHash s_singleton_instances = {}; + static std::mutex s_singleton_instances_mutex; - std::lock_guard lock(s_singleton_instances_mutex_); - auto inst_iter = s_singleton_instances_.constFind(style); + std::scoped_lock const lock(s_singleton_instances_mutex); + auto instIter = s_singleton_instances.constFind(style); NoBigComboBoxStyle* inst = nullptr; - if (inst_iter == s_singleton_instances_.constEnd() || *inst_iter == nullptr) { + if (instIter == s_singleton_instances.constEnd() || *instIter == nullptr) { inst = new NoBigComboBoxStyle(style); inst->setParent(APPLICATION); - s_singleton_instances_.insert(style, inst); + s_singleton_instances.insert(style, inst); qDebug() << "QProxyStyle NoBigComboBox created for" << style->objectName() << style; } else { - inst = *inst_iter; + inst = *instIter; } return inst; } private: - NoBigComboBoxStyle(QStyle* style) : QProxyStyle(style) {} + explicit NoBigComboBoxStyle(QStyle* style) : QProxyStyle(style) {} }; +} // namespace -ManagedPackPage* ManagedPackPage::createPage(BaseInstance* inst, QString type, QWidget* parent) +ManagedPackPage* ManagedPackPage::createPage(BaseInstance* inst, const QString& type, QWidget* parent) { - if (type == "modrinth") + if (type == "modrinth") { return new ModrinthManagedPackPage(inst, nullptr, parent); - if (type == "flame" && (APPLICATION->capabilities() & Application::SupportsFlame)) + } + if (type == "flame" && ((APPLICATION->capabilities() & Application::SupportsFlame) != 0U)) { return new FlameManagedPackPage(inst, nullptr, parent); + } return new GenericManagedPackPage(inst, nullptr, parent); } -ManagedPackPage::ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent) - : QWidget(parent), m_instance_window(instance_window), ui(new Ui::ManagedPackPage), m_inst(inst) +ManagedPackPage::ManagedPackPage(BaseInstance* inst, InstanceWindow* instanceWindow, QWidget* parent) + : QWidget(parent), m_instanceWindow(instanceWindow), ui(new Ui::ManagedPackPage), m_inst(inst) { Q_ASSERT(inst); @@ -99,7 +100,7 @@ ManagedPackPage::ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_wi // NOTE: GTK2 themes crash with the proxy style. // This seems like an upstream bug, so there's not much else that can be done. if (!QStyleFactory::keys().contains("gtk2")) { - auto comboStyle = NoBigComboBoxStyle::getInstance(ui->versionsComboBox->style()); + auto* comboStyle = NoBigComboBoxStyle::getInstance(ui->versionsComboBox->style()); ui->versionsComboBox->setStyle(comboStyle); } @@ -115,20 +116,22 @@ ManagedPackPage::ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_wi openedImpl(); }); - connect(ui->changelogTextBrowser, &QTextBrowser::anchorClicked, this, [](const QUrl url) { + connect(ui->changelogTextBrowser, &QTextBrowser::anchorClicked, this, [](const QUrl& url) { if (url.scheme().isEmpty()) { auto querry = QUrlQuery(url.query()).queryItemValue("remoteUrl", QUrl::FullyDecoded); // curseforge workaround for linkout?remoteUrl= auto decoded = QUrl::fromPercentEncoding(querry.toUtf8()); auto newUrl = QUrl(decoded); - if (newUrl.isValid() && (newUrl.scheme() == "http" || newUrl.scheme() == "https")) + if (newUrl.isValid() && (newUrl.scheme() == "http" || newUrl.scheme() == "https")) { QDesktopServices ::openUrl(newUrl); + } return; } QDesktopServices::openUrl(url); }); - connect(ui->urlLine, &QLineEdit::textChanged, this, [this](QString text) { m_inst->settings()->set("ManagedPackURL", text.trimmed()); }); + connect(ui->urlLine, &QLineEdit::textChanged, this, + [this](const QString& text) { m_inst->settings()->set("ManagedPackURL", text.trimmed()); }); } ManagedPackPage::~ManagedPackPage() @@ -169,10 +172,12 @@ void ManagedPackPage::openedImpl() QString ManagedPackPage::displayName() const { auto type = m_inst->getManagedPackType(); - if (type.isEmpty()) + if (type.isEmpty()) { return {}; - if (type == "flame") + } + if (type == "flame") { type = "CurseForge"; + } return type.replace(0, 1, type[0].toUpper()); } @@ -200,26 +205,26 @@ bool ManagedPackPage::runUpdateTask(InstanceTask* task) { Q_ASSERT(task); - unique_qobject_ptr wrapped_task(APPLICATION->instances()->wrapInstanceTask(task)); + unique_qobject_ptr const wrappedTask(APPLICATION->instances()->wrapInstanceTask(task)); - connect(wrapped_task.get(), &Task::failed, + connect(wrappedTask.get(), &Task::failed, [this](const QString& reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); - connect(wrapped_task.get(), &Task::succeeded, [this, task]() { + connect(wrappedTask.get(), &Task::succeeded, [this, task]() { QStringList warnings = task->warnings(); if (warnings.count()) { CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); } }); - connect(wrapped_task.get(), &Task::aborted, [this] { + connect(wrappedTask.get(), &Task::aborted, [this] { CustomMessageBox::selectable(this, tr("Task aborted"), tr("The task has been aborted by the user."), QMessageBox::Information) ->show(); }); ProgressDialog loadDialog(this); loadDialog.setSkipButton(true, tr("Abort")); - loadDialog.execWithTask(wrapped_task.get()); + loadDialog.execWithTask(wrappedTask.get()); - return wrapped_task->wasSuccessful(); + return wrappedTask->wasSuccessful(); } void ManagedPackPage::suggestVersion() @@ -246,8 +251,8 @@ void ManagedPackPage::setFailState() ui->reloadButton->setVisible(true); } -ModrinthManagedPackPage::ModrinthManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent) - : ManagedPackPage(inst, instance_window, parent) +ModrinthManagedPackPage::ModrinthManagedPackPage(BaseInstance* inst, InstanceWindow* instanceWindow, QWidget* parent) + : ManagedPackPage(inst, instanceWindow, parent) { Q_ASSERT(inst->isManagedPack()); connect(ui->versionsComboBox, &QComboBox::currentIndexChanged, this, &ModrinthManagedPackPage::suggestVersion); @@ -265,8 +270,8 @@ void ModrinthManagedPackPage::parseManagedPack() return; } - if (m_fetch_job && m_fetch_job->isRunning()) { - m_fetch_job->abort(); + if (m_fetchJob && m_fetchJob->isRunning()) { + m_fetchJob->abort(); } ResourceAPI::Callback> callbacks{}; @@ -300,16 +305,16 @@ void ModrinthManagedPackPage::parseManagedPack() }; callbacks.on_fail = [this](const QString& /*reason*/, int) { setFailState(); }; callbacks.on_abort = [this]() { setFailState(); }; - m_fetch_job = m_api.getProjectVersions({ .pack = std::make_shared(m_pack), - .mcVersions = {}, - .loaders = {}, - .resourceType = ModPlatform::ResourceType::Modpack, - .includeChangelog = true }, - std::move(callbacks)); + m_fetchJob = m_api.getProjectVersions({ .pack = std::make_shared(m_pack), + .mcVersions = {}, + .loaders = {}, + .resourceType = ModPlatform::ResourceType::Modpack, + .includeChangelog = true }, + std::move(callbacks)); ui->changelogTextBrowser->setText(tr("Fetching changelogs...")); - m_fetch_job->start(); + m_fetchJob->start(); } QString ModrinthManagedPackPage::url() const @@ -334,12 +339,13 @@ void ModrinthManagedPackPage::suggestVersion() /// @brief Called when the update task has completed. /// Internally handles the closing of the instance window if the update was successful and shows a message box. /// @param did_succeed Whether the update task was successful. -void ManagedPackPage::onUpdateTaskCompleted(bool did_succeed) const +void ManagedPackPage::onUpdateTaskCompleted(bool didSucceed) const { // Close the window if the update was successful - if (did_succeed) { - if (m_instance_window != nullptr) - m_instance_window->close(); + if (didSucceed) { + if (m_instanceWindow != nullptr) { + m_instanceWindow->close(); + } CustomMessageBox::selectable(nullptr, tr("Update Successful"), tr("The instance updated to pack version %1 successfully.").arg(m_inst->getManagedPackVersionName()), @@ -375,15 +381,16 @@ void ModrinthManagedPackPage::update() void ModrinthManagedPackPage::updateFromFile() { auto output = QFileDialog::getOpenFileUrl(this, tr("Choose update file"), QDir::homePath(), tr("Modrinth pack") + " (*.mrpack *.zip)"); - if (output.isEmpty()) + if (output.isEmpty()) { return; + } updatePack(output); } // FLAME -FlameManagedPackPage::FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent) - : ManagedPackPage(inst, instance_window, parent) +FlameManagedPackPage::FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instanceWindow, QWidget* parent) + : ManagedPackPage(inst, instanceWindow, parent) { Q_ASSERT(inst->isManagedPack()); connect(ui->versionsComboBox, &QComboBox::currentIndexChanged, this, &FlameManagedPackPage::suggestVersion); @@ -418,8 +425,8 @@ void FlameManagedPackPage::parseManagedPack() return; } - if (m_fetch_job && m_fetch_job->isRunning()) { - m_fetch_job->abort(); + if (m_fetchJob && m_fetchJob->isRunning()) { + m_fetchJob->abort(); } QString id = m_inst->getManagedPackID(); @@ -453,14 +460,14 @@ void FlameManagedPackPage::parseManagedPack() }; callbacks.on_fail = [this](const QString& /*reason*/, int) { setFailState(); }; callbacks.on_abort = [this]() { setFailState(); }; - m_fetch_job = m_api.getProjectVersions({ .pack = std::make_shared(m_pack), - .mcVersions = {}, - .loaders = {}, - .resourceType = ModPlatform::ResourceType::Modpack, - .includeChangelog = true }, - std::move(callbacks)); + m_fetchJob = m_api.getProjectVersions({ .pack = std::make_shared(m_pack), + .mcVersions = {}, + .loaders = {}, + .resourceType = ModPlatform::ResourceType::Modpack, + .includeChangelog = true }, + std::move(callbacks)); - m_fetch_job->start(); + m_fetchJob->start(); } QString FlameManagedPackPage::url() const @@ -504,36 +511,36 @@ void FlameManagedPackPage::update() void FlameManagedPackPage::updateFromFile() { auto output = QFileDialog::getOpenFileUrl(this, tr("Choose update file"), QDir::homePath(), tr("CurseForge pack") + " (*.zip)"); - if (output.isEmpty()) + if (output.isEmpty()) { return; + } updatePack(output); } -void ManagedPackPage::updatePack(const QUrl& url, QString versionID, QString versionName) +void ManagedPackPage::updatePack(const QUrl& url, const QString& versionID, const QString& versionName) { - QMap extra_info; + QMap extraInfo; // NOTE: Don't use 'm_pack.id' here, since we didn't completely parse all the metadata for the pack, including this field. - extra_info.insert("pack_id", m_inst->getManagedPackID()); - extra_info.insert("pack_version_id", versionID); - extra_info.insert("original_instance_id", m_inst->id()); + extraInfo.insert("pack_id", m_inst->getManagedPackID()); + extraInfo.insert("pack_version_id", versionID); + extraInfo.insert("original_instance_id", m_inst->id()); - auto extracted = new InstanceImportTask(url, this, std::move(extra_info)); + auto* extracted = new InstanceImportTask(url, this, std::move(extraInfo)); if (versionName.isEmpty()) { extracted->setName(m_inst->name()); } else { - InstanceName inst_name(m_inst->getManagedPackName(), versionName); - inst_name.setName(m_inst->name().replace(m_inst->getManagedPackVersionName(), versionName)); - extracted->setName(inst_name); + extracted->setOriginalName(m_inst->getManagedPackName(), versionName); + extracted->setName(m_inst->name().replace(m_inst->getManagedPackVersionName(), versionName)); } extracted->setGroup(APPLICATION->instances()->getInstanceGroup(m_inst->id())); extracted->setIcon(m_inst->iconKey()); extracted->setConfirmUpdate(false); // Run our task then handle the result - auto did_succeed = runUpdateTask(extracted); - onUpdateTaskCompleted(did_succeed); + auto didSucceed = runUpdateTask(extracted); + onUpdateTaskCompleted(didSucceed); } #include "ManagedPackPage.moc" diff --git a/launcher/ui/pages/instance/ManagedPackPage.h b/launcher/ui/pages/instance/ManagedPackPage.h index 4b7332896..17d05c402 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.h +++ b/launcher/ui/pages/instance/ManagedPackPage.h @@ -11,8 +11,6 @@ #include "modplatform/flame/FlameAPI.h" -#include "net/NetJob.h" - #include "ui/pages/BasePage.h" #include @@ -28,12 +26,12 @@ class ManagedPackPage : public QWidget, public BasePage { Q_OBJECT public: - inline static ManagedPackPage* createPage(BaseInstance* inst, QWidget* parent = nullptr) + static ManagedPackPage* createPage(BaseInstance* inst, QWidget* parent = nullptr) { return ManagedPackPage::createPage(inst, inst->getManagedPackType(), parent); } - static ManagedPackPage* createPage(BaseInstance* inst, QString type, QWidget* parent = nullptr); + static ManagedPackPage* createPage(BaseInstance* inst, const QString& type, QWidget* parent = nullptr); ~ManagedPackPage() override; QString displayName() const override; @@ -56,7 +54,7 @@ class ManagedPackPage : public QWidget, public BasePage { */ virtual QString url() const { return {}; }; - void setInstanceWindow(InstanceWindow* window) { m_instance_window = window; } + void setInstanceWindow(InstanceWindow* window) { m_instanceWindow = window; } public slots: /** Gets the current version selection and update the UI, including the update button and the changelog. @@ -77,7 +75,7 @@ class ManagedPackPage : public QWidget, public BasePage { void setFailState(); protected: - ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr); + ManagedPackPage(BaseInstance* inst, InstanceWindow* instanceWindow, QWidget* parent = nullptr); /** Run the InstanceTask, with a progress dialog and all. * Similar to MainWindow::instanceFromInstanceTask @@ -86,17 +84,17 @@ class ManagedPackPage : public QWidget, public BasePage { */ bool runUpdateTask(InstanceTask*); - void updatePack(const QUrl& url, QString versionID = {}, QString versionName = {}); + void updatePack(const QUrl& url, const QString& versionID = {}, const QString& versionName = {}); + + void onUpdateTaskCompleted(bool didSucceed) const; protected: - InstanceWindow* m_instance_window = nullptr; + InstanceWindow* m_instanceWindow = nullptr; Ui::ManagedPackPage* ui; BaseInstance* m_inst; bool m_loaded = false; - - void onUpdateTaskCompleted(bool did_succeed) const; }; /** Simple page for when we aren't a managed pack. */ @@ -104,8 +102,8 @@ class GenericManagedPackPage final : public ManagedPackPage { Q_OBJECT public: - GenericManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr) - : ManagedPackPage(inst, instance_window, parent) + GenericManagedPackPage(BaseInstance* inst, InstanceWindow* instanceWindow, QWidget* parent = nullptr) + : ManagedPackPage(inst, instanceWindow, parent) {} ~GenericManagedPackPage() override = default; @@ -117,7 +115,7 @@ class ModrinthManagedPackPage final : public ManagedPackPage { Q_OBJECT public: - ModrinthManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr); + ModrinthManagedPackPage(BaseInstance* inst, InstanceWindow* instanceWindow, QWidget* parent = nullptr); ~ModrinthManagedPackPage() override = default; void parseManagedPack() override; @@ -131,7 +129,7 @@ class ModrinthManagedPackPage final : public ManagedPackPage { void updateFromFile() override; private: - Task::Ptr m_fetch_job = nullptr; + Task::Ptr m_fetchJob = nullptr; ModPlatform::IndexedPack m_pack; ModrinthAPI m_api; @@ -141,7 +139,7 @@ class FlameManagedPackPage final : public ManagedPackPage { Q_OBJECT public: - FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr); + FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instanceWindow, QWidget* parent = nullptr); ~FlameManagedPackPage() override = default; void parseManagedPack() override; @@ -155,7 +153,7 @@ class FlameManagedPackPage final : public ManagedPackPage { void updateFromFile() override; private: - Task::Ptr m_fetch_job = nullptr; + Task::Ptr m_fetchJob = nullptr; ModPlatform::IndexedPack m_pack; FlameAPI m_api;