From 2af6902b4204b93b97c08162f7980e075d63e532 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 17 Jun 2024 01:33:11 +0300 Subject: [PATCH 1/7] validate metadata on launch Signed-off-by: Trial97 --- launcher/Application.cpp | 4 +- launcher/java/JavaInstallList.h | 2 +- launcher/meta/BaseEntity.cpp | 137 +++++++++++------- launcher/meta/BaseEntity.h | 47 ++++-- launcher/meta/Index.cpp | 29 +++- launcher/meta/Index.h | 7 +- launcher/meta/JsonFormat.cpp | 4 + launcher/meta/JsonFormat.h | 2 - launcher/meta/Version.cpp | 6 +- launcher/meta/Version.h | 2 +- launcher/meta/VersionList.cpp | 47 ++++-- launcher/meta/VersionList.h | 3 +- launcher/minecraft/AssetsUtils.cpp | 3 +- launcher/minecraft/Component.cpp | 24 ++- launcher/minecraft/Component.h | 1 - launcher/minecraft/ComponentUpdateTask.cpp | 41 +----- launcher/minecraft/ComponentUpdateTask.h | 1 + launcher/minecraft/Library.cpp | 3 +- launcher/minecraft/update/AssetUpdateTask.cpp | 3 +- .../atlauncher/ATLPackInstallTask.cpp | 54 ++----- .../modplatform/flame/FlamePackExportTask.cpp | 2 +- .../modrinth/ModrinthPackExportTask.cpp | 6 +- .../technic/SolderPackInstallTask.cpp | 3 +- launcher/net/ChecksumValidator.h | 3 + launcher/translations/TranslationsModel.cpp | 3 +- launcher/ui/pages/instance/VersionPage.h | 1 + .../ui/pages/modplatform/TexturePackModel.cpp | 9 +- .../ui/pages/modplatform/TexturePackModel.h | 1 + launcher/ui/widgets/VersionSelectWidget.cpp | 18 +-- launcher/ui/widgets/VersionSelectWidget.h | 2 +- 30 files changed, 253 insertions(+), 215 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 6d30d1101..f8111b938 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -47,7 +47,6 @@ #include "net/PasteUpload.h" #include "pathmatcher/MultiMatcher.h" #include "pathmatcher/SimplePrefixMatcher.h" -#include "settings/INIFile.h" #include "tools/GenericProfiler.h" #include "ui/InstanceWindow.h" #include "ui/MainWindow.h" @@ -106,7 +105,7 @@ #include "icons/IconList.h" #include "net/HttpMetaCache.h" -#include "java/JavaUtils.h" +#include "java/JavaInstallList.h" #include "updater/ExternalUpdater.h" @@ -151,6 +150,7 @@ #endif #if defined Q_OS_WIN32 +#include #include "WindowsConsole.h" #endif diff --git a/launcher/java/JavaInstallList.h b/launcher/java/JavaInstallList.h index 1eebadf23..f629af174 100644 --- a/launcher/java/JavaInstallList.h +++ b/launcher/java/JavaInstallList.h @@ -35,7 +35,7 @@ class JavaInstallList : public BaseVersionList { public: explicit JavaInstallList(QObject* parent = 0); - Task::Ptr getLoadTask() override; + [[nodiscard]] Task::Ptr getLoadTask() override; bool isLoaded() override; const BaseVersion::Ptr at(int i) const override; int count() const override; diff --git a/launcher/meta/BaseEntity.cpp b/launcher/meta/BaseEntity.cpp index e74406752..581eaa7f4 100644 --- a/launcher/meta/BaseEntity.cpp +++ b/launcher/meta/BaseEntity.cpp @@ -17,17 +17,23 @@ #include "FileSystem.h" #include "Json.h" +#include "modplatform/helpers/HashUtils.h" #include "net/ApiDownload.h" +#include "net/ChecksumValidator.h" #include "net/HttpMetaCache.h" +#include "net/Mode.h" #include "net/NetJob.h" #include "Application.h" #include "BuildConfig.h" +#include "tasks/Task.h" + +namespace Meta { class ParsingValidator : public Net::Validator { public: /* con/des */ - ParsingValidator(Meta::BaseEntity* entity) : m_entity(entity) {}; - virtual ~ParsingValidator() {}; + ParsingValidator(BaseEntity* entity) : m_entity(entity){}; + virtual ~ParsingValidator() = default; public: /* methods */ bool init(QNetworkRequest&) override { return true; } @@ -53,92 +59,113 @@ class ParsingValidator : public Net::Validator { private: /* data */ QByteArray m_data; - Meta::BaseEntity* m_entity; + BaseEntity* m_entity; }; -Meta::BaseEntity::~BaseEntity() {} - -QUrl Meta::BaseEntity::url() const +QUrl BaseEntity::url() const { auto s = APPLICATION->settings(); QString metaOverride = s->get("MetaURLOverride").toString(); if (metaOverride.isEmpty()) { return QUrl(BuildConfig.META_URL).resolved(localFilename()); - } else { - return QUrl(metaOverride).resolved(localFilename()); } + return QUrl(metaOverride).resolved(localFilename()); } -bool Meta::BaseEntity::loadLocalFile() +Task::Ptr BaseEntity::loadTask(Net::Mode mode) { - const QString fname = QDir("meta").absoluteFilePath(localFilename()); - if (!QFile::exists(fname)) { - return false; - } - // TODO: check if the file has the expected checksum - try { - auto doc = Json::requireDocument(fname, fname); - auto obj = Json::requireObject(doc, fname); - parse(obj); - return true; - } catch (const Exception& e) { - qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause()); - // just make sure it's gone and we never consider it again. - return !FS::deletePath(fname); + if (m_task && m_task->isRunning()) { + return m_task; } + m_task.reset(new BaseEntityLoadTask(this, mode)); + return m_task; } -void Meta::BaseEntity::load(Net::Mode loadType) +bool BaseEntity::isLoaded() const { + return m_load_status != LoadStatus::NotLoaded; +} + +void BaseEntity::setSha256(QString sha256) +{ + m_sha256 = sha256; +} + +BaseEntity::LoadStatus BaseEntity::status() const +{ + return m_load_status; +} + +BaseEntityLoadTask::BaseEntityLoadTask(BaseEntity* parent, Net::Mode mode) : m_entity(parent), m_mode(mode) {} + +void BaseEntityLoadTask::executeTask() +{ + const QString fname = QDir("meta").absoluteFilePath(m_entity->localFilename()); // load local file if nothing is loaded yet - if (!isLoaded()) { - if (loadLocalFile()) { - m_loadStatus = LoadStatus::Local; + if (m_entity->m_load_status != BaseEntity::LoadStatus::NotLoaded && QFile::exists(fname)) { + setStatus(tr("Loading local file")); + try { + auto fileData = FS::read(fname); + m_entity->m_file_sha256 = Hashing::hash(fileData, Hashing::Algorithm::Sha256); + if (m_mode == Net::Mode::Online && !m_entity->m_sha256.isEmpty() && m_entity->m_sha256 != m_entity->m_file_sha256) { + FS::deletePath(fname); + } else { + auto doc = Json::requireDocument(fileData, fname); + auto obj = Json::requireObject(doc, fname); + m_entity->parse(obj); + m_entity->m_load_status = BaseEntity::LoadStatus::Local; + } + } catch (const Exception& e) { + qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause()); + // just make sure it's gone and we never consider it again. + FS::deletePath(fname); } } // if we need remote update, run the update task - if (loadType == Net::Mode::Offline || !shouldStartRemoteUpdate()) { + if (m_mode == Net::Mode::Offline || (!m_entity->m_sha256.isEmpty() && m_entity->m_sha256 == m_entity->m_file_sha256)) { + emitSucceeded(); return; } - m_updateTask.reset(new NetJob(QObject::tr("Download of meta file %1").arg(localFilename()), APPLICATION->network())); - auto url = this->url(); - auto entry = APPLICATION->metacache()->resolveEntry("meta", localFilename()); + m_task.reset(new NetJob(QObject::tr("Download of meta file %1").arg(m_entity->localFilename()), APPLICATION->network())); + auto url = m_entity->url(); + auto entry = APPLICATION->metacache()->resolveEntry("meta", m_entity->localFilename()); entry->setStale(true); auto dl = Net::ApiDownload::makeCached(url, entry); /* * The validator parses the file and loads it into the object. * If that fails, the file is not written to storage. */ - dl->addValidator(new ParsingValidator(this)); - m_updateTask->addNetAction(dl); - m_updateStatus = UpdateStatus::InProgress; - QObject::connect(m_updateTask.get(), &NetJob::succeeded, [&]() { - m_loadStatus = LoadStatus::Remote; - m_updateStatus = UpdateStatus::Succeeded; - m_updateTask.reset(); + if (!m_entity->m_sha256.isEmpty()) + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Sha256, m_entity->m_sha256)); + dl->addValidator(new ParsingValidator(m_entity)); + m_task->addNetAction(dl); + connect(m_task.get(), &Task::failed, this, &BaseEntityLoadTask::emitFailed); + connect(m_task.get(), &Task::succeeded, this, &BaseEntityLoadTask::emitSucceeded); + connect(m_task.get(), &Task::succeeded, this, [this]() { + m_entity->m_load_status = BaseEntity::LoadStatus::Remote; + m_entity->m_file_sha256 = m_entity->m_sha256; }); - QObject::connect(m_updateTask.get(), &NetJob::failed, [&]() { - m_updateStatus = UpdateStatus::Failed; - m_updateTask.reset(); - }); - m_updateTask->start(); + + connect(m_task.get(), &Task::progress, this, &Task::setProgress); + connect(m_task.get(), &Task::stepProgress, this, &BaseEntityLoadTask::propagateStepProgress); + connect(m_task.get(), &Task::status, this, &Task::setStatus); + connect(m_task.get(), &Task::details, this, &Task::setDetails); + + m_task->start(); } -bool Meta::BaseEntity::isLoaded() const +bool BaseEntityLoadTask::canAbort() const { - return m_loadStatus > LoadStatus::NotLoaded; + return m_task ? m_task->canAbort() : false; } -bool Meta::BaseEntity::shouldStartRemoteUpdate() const +bool BaseEntityLoadTask::abort() { - // TODO: version-locks and offline mode? - return m_updateStatus != UpdateStatus::InProgress; -} - -Task::Ptr Meta::BaseEntity::getCurrentTask() -{ - if (m_updateStatus == UpdateStatus::InProgress) { - return m_updateTask; + if (m_task) { + Task::abort(); + return m_task->abort(); } - return nullptr; + return Task::abort(); } + +} // namespace Meta diff --git a/launcher/meta/BaseEntity.h b/launcher/meta/BaseEntity.h index 1336a5217..17aa0cb87 100644 --- a/launcher/meta/BaseEntity.h +++ b/launcher/meta/BaseEntity.h @@ -17,38 +17,57 @@ #include #include -#include "QObjectPtr.h" #include "net/Mode.h" #include "net/NetJob.h" +#include "tasks/Task.h" namespace Meta { +class BaseEntityLoadTask; class BaseEntity { + friend BaseEntityLoadTask; + public: /* types */ using Ptr = std::shared_ptr; enum class LoadStatus { NotLoaded, Local, Remote }; - enum class UpdateStatus { NotDone, InProgress, Failed, Succeeded }; public: - virtual ~BaseEntity(); - - virtual void parse(const QJsonObject& obj) = 0; + virtual ~BaseEntity() = default; virtual QString localFilename() const = 0; virtual QUrl url() const; - bool isLoaded() const; - bool shouldStartRemoteUpdate() const; + LoadStatus status() const; - void load(Net::Mode loadType); - Task::Ptr getCurrentTask(); + /* for parsers */ + void setSha256(QString sha256); - protected: /* methods */ - bool loadLocalFile(); + virtual void parse(const QJsonObject& obj) = 0; + [[nodiscard]] Task::Ptr loadTask(Net::Mode loadType = Net::Mode::Online); + + protected: + QString m_sha256; // the expected sha256 + QString m_file_sha256; // the file sha256 private: - LoadStatus m_loadStatus = LoadStatus::NotLoaded; - UpdateStatus m_updateStatus = UpdateStatus::NotDone; - NetJob::Ptr m_updateTask; + LoadStatus m_load_status = LoadStatus::NotLoaded; + Task::Ptr m_task; +}; + +class BaseEntityLoadTask : public Task { + Q_OBJECT + + public: + explicit BaseEntityLoadTask(BaseEntity* parent, Net::Mode mode); + ~BaseEntityLoadTask() override = default; + + virtual void executeTask() override; + virtual bool canAbort() const override; + virtual bool abort() override; + + private: + BaseEntity* m_entity; + Net::Mode m_mode; + NetJob::Ptr m_task; }; } // namespace Meta diff --git a/launcher/meta/Index.cpp b/launcher/meta/Index.cpp index 657019f8a..ddcebc453 100644 --- a/launcher/meta/Index.cpp +++ b/launcher/meta/Index.cpp @@ -16,7 +16,10 @@ #include "Index.h" #include "JsonFormat.h" +#include "QObjectPtr.h" #include "VersionList.h" +#include "meta/BaseEntity.h" +#include "tasks/SequentialTask.h" namespace Meta { Index::Index(QObject* parent) : QAbstractListModel(parent) {} @@ -51,14 +54,17 @@ QVariant Index::data(const QModelIndex& index, int role) const } return QVariant(); } + int Index::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_lists.size(); } + int Index::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : 1; } + QVariant Index::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section == 0) { @@ -79,6 +85,7 @@ VersionList::Ptr Index::get(const QString& uid) if (!out) { out = std::make_shared(uid); m_uids[uid] = out; + m_lists.append(out); } return out; } @@ -96,7 +103,7 @@ void Index::parse(const QJsonObject& obj) void Index::merge(const std::shared_ptr& other) { - const QVector lists = std::dynamic_pointer_cast(other)->m_lists; + const QVector lists = other->m_lists; // initial load, no need to merge if (m_lists.isEmpty()) { beginResetModel(); @@ -123,7 +130,23 @@ void Index::merge(const std::shared_ptr& other) void Index::connectVersionList(const int row, const VersionList::Ptr& list) { - connect(list.get(), &VersionList::nameChanged, this, - [this, row]() { emit dataChanged(index(row), index(row), QVector() << Qt::DisplayRole); }); + connect(list.get(), &VersionList::nameChanged, this, [this, row] { emit dataChanged(index(row), index(row), { Qt::DisplayRole }); }); +} + +Task::Ptr Index::loadVersion(const QString& uid, const QString& version, Net::Mode mode, bool force) +{ + if (mode == Net::Mode::Offline) { + return get(uid, version)->loadTask(mode); + } + + auto versionList = get(uid); + auto loadTask = makeShared( + this, tr("Load meta for %1:%2", "This is for the task name that loads the meta index.").arg(uid, version)); + if (status() != BaseEntity::LoadStatus::Remote || force) { + loadTask->addTask(this->loadTask(mode)); + } + loadTask->addTask(versionList->loadTask(mode)); + loadTask->addTask(versionList->getVersion(version)->loadTask(mode)); + return loadTask; } } // namespace Meta diff --git a/launcher/meta/Index.h b/launcher/meta/Index.h index 2c650ce2f..e54cdd88b 100644 --- a/launcher/meta/Index.h +++ b/launcher/meta/Index.h @@ -16,10 +16,10 @@ #pragma once #include -#include #include "BaseEntity.h" #include "meta/VersionList.h" +#include "net/Mode.h" class Task; @@ -30,6 +30,7 @@ class Index : public QAbstractListModel, public BaseEntity { public: explicit Index(QObject* parent = nullptr); explicit Index(const QVector& lists, QObject* parent = nullptr); + virtual ~Index() = default; enum { UidRole = Qt::UserRole, NameRole, ListPtrRole }; @@ -47,8 +48,12 @@ class Index : public QAbstractListModel, public BaseEntity { QVector lists() const { return m_lists; } + Task::Ptr loadVersion(const QString& uid, const QString& version = {}, Net::Mode mode = Net::Mode::Online, bool force = false); + public: // for usage by parsers only void merge(const std::shared_ptr& other); + + protected: void parse(const QJsonObject& obj) override; private: diff --git a/launcher/meta/JsonFormat.cpp b/launcher/meta/JsonFormat.cpp index 6c993f720..86af7277e 100644 --- a/launcher/meta/JsonFormat.cpp +++ b/launcher/meta/JsonFormat.cpp @@ -41,6 +41,7 @@ static std::shared_ptr parseIndexInternal(const QJsonObject& obj) std::transform(objects.begin(), objects.end(), std::back_inserter(lists), [](const QJsonObject& obj) { VersionList::Ptr list = std::make_shared(requireString(obj, "uid")); list->setName(ensureString(obj, "name", QString())); + list->setSha256(ensureString(obj, "sha256", QString())); return list; }); return std::make_shared(lists); @@ -58,6 +59,9 @@ static Version::Ptr parseCommonVersion(const QString& uid, const QJsonObject& ob parseRequires(obj, &reqs, "requires"); parseRequires(obj, &conflicts, "conflicts"); version->setRequires(reqs, conflicts); + if (auto sha256 = ensureString(obj, "sha256", QString()); !sha256.isEmpty()) { + version->setSha256(sha256); + } return version; } diff --git a/launcher/meta/JsonFormat.h b/launcher/meta/JsonFormat.h index d474bcc39..7fbf808a7 100644 --- a/launcher/meta/JsonFormat.h +++ b/launcher/meta/JsonFormat.h @@ -16,11 +16,9 @@ #pragma once #include -#include #include #include "Exception.h" -#include "meta/BaseEntity.h" namespace Meta { class Index; diff --git a/launcher/meta/Version.cpp b/launcher/meta/Version.cpp index 655a20b93..74e71e91c 100644 --- a/launcher/meta/Version.cpp +++ b/launcher/meta/Version.cpp @@ -18,12 +18,9 @@ #include #include "JsonFormat.h" -#include "minecraft/PackProfile.h" Meta::Version::Version(const QString& uid, const QString& version) : BaseVersion(), m_uid(uid), m_version(version) {} -Meta::Version::~Version() {} - QString Meta::Version::descriptor() { return m_version; @@ -71,6 +68,9 @@ void Meta::Version::mergeFromList(const Meta::Version::Ptr& other) if (m_volatile != other->m_volatile) { setVolatile(other->m_volatile); } + if (!other->m_sha256.isEmpty()) { + m_sha256 = other->m_sha256; + } } void Meta::Version::merge(const Version::Ptr& other) diff --git a/launcher/meta/Version.h b/launcher/meta/Version.h index 24da12d6d..149af86f6 100644 --- a/launcher/meta/Version.h +++ b/launcher/meta/Version.h @@ -38,7 +38,7 @@ class Version : public QObject, public BaseVersion, public BaseEntity { using Ptr = std::shared_ptr; explicit Version(const QString& uid, const QString& version); - virtual ~Version(); + virtual ~Version() = default; QString descriptor() override; QString name() override; diff --git a/launcher/meta/VersionList.cpp b/launcher/meta/VersionList.cpp index 7b7ae1fa3..f7269c57f 100644 --- a/launcher/meta/VersionList.cpp +++ b/launcher/meta/VersionList.cpp @@ -17,8 +17,13 @@ #include +#include "Application.h" +#include "Index.h" #include "JsonFormat.h" #include "Version.h" +#include "meta/BaseEntity.h" +#include "net/Mode.h" +#include "tasks/SequentialTask.h" namespace Meta { VersionList::VersionList(const QString& uid, QObject* parent) : BaseVersionList(parent), m_uid(uid) @@ -28,8 +33,11 @@ VersionList::VersionList(const QString& uid, QObject* parent) : BaseVersionList( Task::Ptr VersionList::getLoadTask() { - load(Net::Mode::Online); - return getCurrentTask(); + auto loadTask = + makeShared(this, tr("Load meta for %1", "This is for the task name that loads the meta index.").arg(m_uid)); + loadTask->addTask(APPLICATION->metadataIndex()->loadTask(Net::Mode::Online)); + loadTask->addTask(this->loadTask(Net::Mode::Online)); + return loadTask; } bool VersionList::isLoaded() @@ -191,6 +199,9 @@ void VersionList::mergeFromIndex(const VersionList::Ptr& other) if (m_name != other->m_name) { setName(other->m_name); } + if (!other->m_sha256.isEmpty()) { + m_sha256 = other->m_sha256; + } } void VersionList::merge(const VersionList::Ptr& other) @@ -198,23 +209,27 @@ void VersionList::merge(const VersionList::Ptr& other) if (m_name != other->m_name) { setName(other->m_name); } + if (!other->m_sha256.isEmpty()) { + m_sha256 = other->m_sha256; + } // TODO: do not reset the whole model. maybe? beginResetModel(); - m_versions.clear(); if (other->m_versions.isEmpty()) { qWarning() << "Empty list loaded ..."; } - for (const Version::Ptr& version : other->m_versions) { + for (auto version : other->m_versions) { // we already have the version. merge the contents if (m_lookup.contains(version->version())) { - m_lookup.value(version->version())->mergeFromList(version); + auto existing = m_lookup.value(version->version()); + existing->mergeFromList(version); + version = existing; } else { - m_lookup.insert(version->uid(), version); + m_lookup.insert(version->version(), version); + // connect it. + setupAddedVersion(m_versions.size(), version); + m_versions.append(version); } - // connect it. - setupAddedVersion(m_versions.size(), version); - m_versions.append(version); m_recommended = getBetterVersion(m_recommended, version); } endResetModel(); @@ -222,14 +237,16 @@ void VersionList::merge(const VersionList::Ptr& other) void VersionList::setupAddedVersion(const int row, const Version::Ptr& version) { - // FIXME: do not disconnect from everythin, disconnect only the lambdas here - version->disconnect(); + disconnect(version.get(), &Version::requiresChanged, this, nullptr); + disconnect(version.get(), &Version::timeChanged, this, nullptr); + disconnect(version.get(), &Version::typeChanged, this, nullptr); + connect(version.get(), &Version::requiresChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector() << RequiresRole); }); - connect(version.get(), &Version::timeChanged, this, - [this, row]() { emit dataChanged(index(row), index(row), QVector() << TimeRole << SortRole); }); - connect(version.get(), &Version::typeChanged, this, - [this, row]() { emit dataChanged(index(row), index(row), QVector() << TypeRole); }); + connect(version.get(), &Version::timeChanged, this, [this, row]() { + emit dataChanged(index(row), index(row), { TimeRole, SortRole }); + }); + connect(version.get(), &Version::typeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), { TypeRole }); }); } BaseVersion::Ptr VersionList::getRecommended() const diff --git a/launcher/meta/VersionList.h b/launcher/meta/VersionList.h index 2c5624701..22f9a3a92 100644 --- a/launcher/meta/VersionList.h +++ b/launcher/meta/VersionList.h @@ -30,13 +30,14 @@ class VersionList : public BaseVersionList, public BaseEntity { Q_PROPERTY(QString name READ name NOTIFY nameChanged) public: explicit VersionList(const QString& uid, QObject* parent = nullptr); + virtual ~VersionList() = default; using Ptr = std::shared_ptr; enum Roles { UidRole = Qt::UserRole + 100, TimeRole, RequiresRole, VersionPtrRole }; - Task::Ptr getLoadTask() override; bool isLoaded() override; + [[nodiscard]] Task::Ptr getLoadTask() override; const BaseVersion::Ptr at(int i) const override; int count() const override; void sortVersions() override; diff --git a/launcher/minecraft/AssetsUtils.cpp b/launcher/minecraft/AssetsUtils.cpp index 6bbe0bb2c..4406d9b34 100644 --- a/launcher/minecraft/AssetsUtils.cpp +++ b/launcher/minecraft/AssetsUtils.cpp @@ -283,8 +283,7 @@ Net::NetRequest::Ptr AssetObject::getDownloadAction() if ((!objectFile.isFile()) || (objectFile.size() != size)) { auto objectDL = Net::ApiDownload::makeFile(getUrl(), objectFile.filePath()); if (hash.size()) { - auto rawHash = QByteArray::fromHex(hash.toLatin1()); - objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash)); + objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, hash)); } objectDL->setProgress(objectDL->getProgress(), size); return objectDL; diff --git a/launcher/minecraft/Component.cpp b/launcher/minecraft/Component.cpp index ad2e4023c..1ffcccada 100644 --- a/launcher/minecraft/Component.cpp +++ b/launcher/minecraft/Component.cpp @@ -56,18 +56,6 @@ Component::Component(PackProfile* parent, const QString& uid) m_uid = uid; } -Component::Component(PackProfile* parent, std::shared_ptr version) -{ - assert(parent); - m_parent = parent; - - m_metaVersion = version; - m_uid = version->uid(); - m_version = m_cachedVersion = version->version(); - m_cachedName = version->name(); - m_loaded = version->isLoaded(); -} - Component::Component(PackProfile* parent, const QString& uid, std::shared_ptr file) { assert(parent); @@ -103,7 +91,11 @@ std::shared_ptr Component::getVersionFile() const { if (m_metaVersion) { if (!m_metaVersion->isLoaded()) { - m_metaVersion->load(Net::Mode::Online); + QEventLoop ev; + auto task = APPLICATION->metadataIndex()->loadVersion(m_metaVersion->uid(), m_metaVersion->version(), Net::Mode::Online); + connect(task.get(), &Task::finished, &ev, &QEventLoop::quit); + task->start(); + ev.exec(); } return m_metaVersion->data(); } else { @@ -228,7 +220,11 @@ bool Component::isVersionChangeable() auto list = getVersionList(); if (list) { if (!list->isLoaded()) { - list->load(Net::Mode::Online); + QEventLoop ev; + auto task = list->getLoadTask(); + connect(task.get(), &Task::finished, &ev, &QEventLoop::quit); + task->start(); + ev.exec(); } return list->count() != 0; } diff --git a/launcher/minecraft/Component.h b/launcher/minecraft/Component.h index fdb61c45e..8aa6b4743 100644 --- a/launcher/minecraft/Component.h +++ b/launcher/minecraft/Component.h @@ -22,7 +22,6 @@ class Component : public QObject, public ProblemProvider { Component(PackProfile* parent, const QString& uid); // DEPRECATED: remove these constructors? - Component(PackProfile* parent, std::shared_ptr version); Component(PackProfile* parent, const QString& uid, std::shared_ptr file); virtual ~Component() {} diff --git a/launcher/minecraft/ComponentUpdateTask.cpp b/launcher/minecraft/ComponentUpdateTask.cpp index bb838043a..1368ca5e3 100644 --- a/launcher/minecraft/ComponentUpdateTask.cpp +++ b/launcher/minecraft/ComponentUpdateTask.cpp @@ -93,9 +93,9 @@ static LoadResult loadComponent(ComponentPtr component, Task::Ptr& loadTask, Net component->m_loaded = true; result = LoadResult::LoadedLocal; } else { - metaVersion->load(netmode); - loadTask = metaVersion->getCurrentTask(); - if (loadTask) + loadTask = APPLICATION->metadataIndex()->loadVersion(component->m_uid, component->m_version, netmode); + loadTask->start(); + if (netmode == Net::Mode::Online) result = LoadResult::RequiresRemote; else if (metaVersion->isLoaded()) result = LoadResult::LoadedLocal; @@ -133,21 +133,6 @@ static LoadResult loadPackProfile(ComponentPtr component, Task::Ptr& loadTask, N } */ -static LoadResult loadIndex(Task::Ptr& loadTask, Net::Mode netmode) -{ - // FIXME: DECIDE. do we want to run the update task anyway? - if (APPLICATION->metadataIndex()->isLoaded()) { - qDebug() << "Index is already loaded"; - return LoadResult::LoadedLocal; - } - APPLICATION->metadataIndex()->load(netmode); - loadTask = APPLICATION->metadataIndex()->getCurrentTask(); - if (loadTask) { - return LoadResult::RequiresRemote; - } - // FIXME: this is assuming the load succeeded... did it really? - return LoadResult::LoadedLocal; -} } // namespace void ComponentUpdateTask::loadComponents() @@ -156,23 +141,8 @@ void ComponentUpdateTask::loadComponents() size_t taskIndex = 0; size_t componentIndex = 0; d->remoteLoadSuccessful = true; - // load the main index (it is needed to determine if components can revert) - { - // FIXME: tear out as a method? or lambda? - Task::Ptr indexLoadTask; - auto singleResult = loadIndex(indexLoadTask, d->netmode); - result = composeLoadResult(result, singleResult); - if (indexLoadTask) { - qDebug() << "Remote loading is being run for metadata index"; - RemoteLoadStatus status; - status.type = RemoteLoadStatus::Type::Index; - d->remoteLoadStatusList.append(status); - connect(indexLoadTask.get(), &Task::succeeded, [=]() { remoteLoadSucceeded(taskIndex); }); - connect(indexLoadTask.get(), &Task::failed, [=](const QString& error) { remoteLoadFailed(taskIndex, error); }); - connect(indexLoadTask.get(), &Task::aborted, [=]() { remoteLoadFailed(taskIndex, tr("Aborted")); }); - taskIndex++; - } - } + + m_load_tasks.clear(); // load all the components OR their lists... for (auto component : d->m_list->d->components) { Task::Ptr loadTask; @@ -205,6 +175,7 @@ void ComponentUpdateTask::loadComponents() } result = composeLoadResult(result, singleResult); if (loadTask) { + m_load_tasks.append(loadTask); qDebug() << "Remote loading is being run for" << component->getName(); connect(loadTask.get(), &Task::succeeded, [=]() { remoteLoadSucceeded(taskIndex); }); connect(loadTask.get(), &Task::failed, [=](const QString& error) { remoteLoadFailed(taskIndex, error); }); diff --git a/launcher/minecraft/ComponentUpdateTask.h b/launcher/minecraft/ComponentUpdateTask.h index 2f396a049..f225364cf 100644 --- a/launcher/minecraft/ComponentUpdateTask.h +++ b/launcher/minecraft/ComponentUpdateTask.h @@ -29,4 +29,5 @@ class ComponentUpdateTask : public Task { private: std::unique_ptr d; + QList m_load_tasks; }; diff --git a/launcher/minecraft/Library.cpp b/launcher/minecraft/Library.cpp index 4e30f72d1..c6b65bcb4 100644 --- a/launcher/minecraft/Library.cpp +++ b/launcher/minecraft/Library.cpp @@ -116,9 +116,8 @@ QList Library::getDownloads(const RuntimeContext& runtimeC options |= Net::Download::Option::MakeEternal; if (sha1.size()) { - auto rawSha1 = QByteArray::fromHex(sha1.toLatin1()); auto dl = Net::ApiDownload::makeCached(url, entry, options); - dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, sha1)); qDebug() << "Checksummed Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url; out.append(dl); } else { diff --git a/launcher/minecraft/update/AssetUpdateTask.cpp b/launcher/minecraft/update/AssetUpdateTask.cpp index 8af014996..8add02d84 100644 --- a/launcher/minecraft/update/AssetUpdateTask.cpp +++ b/launcher/minecraft/update/AssetUpdateTask.cpp @@ -32,8 +32,7 @@ void AssetUpdateTask::executeTask() auto hexSha1 = assets->sha1.toLatin1(); qDebug() << "Asset index SHA1:" << hexSha1; auto dl = Net::ApiDownload::makeCached(indexUrl, entry); - auto rawSha1 = QByteArray::fromHex(assets->sha1.toLatin1()); - dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, assets->sha1)); job->addNetAction(dl); downloadJob.reset(job); diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index a7721673e..9ceb1e3d2 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -36,6 +36,7 @@ #include "ATLPackInstallTask.h" +#include #include #include @@ -344,7 +345,11 @@ QString PackInstallTask::getVersionForLoader(QString uid) } if (!vlist->isLoaded()) { - vlist->load(Net::Mode::Online); + QEventLoop ev; + auto task = vlist->getLoadTask(); + connect(task.get(), &Task::finished, &ev, &QEventLoop::quit); + task->start(); + ev.exec(); } if (m_version.loader.recommended || m_version.loader.latest) { @@ -638,8 +643,7 @@ void PackInstallTask::installConfigs() auto dl = Net::ApiDownload::makeCached(url, entry); if (!m_version.configs.sha1.isEmpty()) { - auto rawSha1 = QByteArray::fromHex(m_version.configs.sha1.toLatin1()); - dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, m_version.configs.sha1)); } jobPtr->addNetAction(dl); archivePath = entry->getFullPath(); @@ -758,8 +762,7 @@ void PackInstallTask::downloadMods() auto dl = Net::ApiDownload::makeCached(url, entry); if (!mod.md5.isEmpty()) { - auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); - dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, mod.md5)); } jobPtr->addNetAction(dl); } else if (mod.type == ModType::Decomp) { @@ -769,8 +772,7 @@ void PackInstallTask::downloadMods() auto dl = Net::ApiDownload::makeCached(url, entry); if (!mod.md5.isEmpty()) { - auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); - dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, mod.md5)); } jobPtr->addNetAction(dl); } else { @@ -783,8 +785,7 @@ void PackInstallTask::downloadMods() auto dl = Net::ApiDownload::makeCached(url, entry); if (!mod.md5.isEmpty()) { - auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); - dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, mod.md5)); } jobPtr->addNetAction(dl); @@ -1075,36 +1076,13 @@ void PackInstallTask::install() static Meta::Version::Ptr getComponentVersion(const QString& uid, const QString& version) { - auto vlist = APPLICATION->metadataIndex()->get(uid); - if (!vlist) - return {}; + QEventLoop ev; + auto task = APPLICATION->metadataIndex()->loadVersion(uid, version); + QObject::connect(task.get(), &Task::finished, &ev, &QEventLoop::quit); + task->start(); + ev.exec(); - if (!vlist->isLoaded()) { - QEventLoop loadVersionLoop; - auto task = vlist->getLoadTask(); - QObject::connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit); - if (!task->isRunning()) - task->start(); - - loadVersionLoop.exec(); - } - - auto ver = vlist->getVersion(version); - if (!ver) - return {}; - - if (!ver->isLoaded()) { - QEventLoop loadVersionLoop; - ver->load(Net::Mode::Online); - auto task = ver->getCurrentTask(); - QObject::connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit); - if (!task->isRunning()) - task->start(); - - loadVersionLoop.exec(); - } - - return ver; + return APPLICATION->metadataIndex()->get(uid, version); } } // namespace ATLauncher diff --git a/launcher/modplatform/flame/FlamePackExportTask.cpp b/launcher/modplatform/flame/FlamePackExportTask.cpp index 11bc3553b..d661f1f05 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.cpp +++ b/launcher/modplatform/flame/FlamePackExportTask.cpp @@ -246,7 +246,7 @@ void FlamePackExportTask::makeApiRequest() pendingHashes.clear(); getProjectsInfo(); }); - connect(task.get(), &NetJob::failed, this, &FlamePackExportTask::getProjectsInfo); + connect(task.get(), &Task::failed, this, &FlamePackExportTask::getProjectsInfo); task->start(); } diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp index ef0a3df16..b7c2757e5 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp @@ -18,6 +18,7 @@ #include "ModrinthPackExportTask.h" +#include #include #include #include @@ -28,6 +29,7 @@ #include "minecraft/mod/MetadataHandler.h" #include "minecraft/mod/ModFolderModel.h" #include "modplatform/helpers/HashUtils.h" +#include "tasks/Task.h" const QStringList ModrinthPackExportTask::PREFIXES({ "mods/", "coremods/", "resourcepacks/", "texturepacks/", "shaderpacks/" }); const QStringList ModrinthPackExportTask::FILE_EXTENSIONS({ "jar", "litemod", "zip" }); @@ -154,8 +156,8 @@ void ModrinthPackExportTask::makeApiRequest() setStatus(tr("Finding versions for hashes...")); auto response = std::make_shared(); task = api.currentVersions(pendingHashes.values(), "sha512", response); - connect(task.get(), &NetJob::succeeded, [this, response]() { parseApiResponse(response); }); - connect(task.get(), &NetJob::failed, this, &ModrinthPackExportTask::emitFailed); + connect(task.get(), &Task::succeeded, [this, response]() { parseApiResponse(response); }); + connect(task.get(), &Task::failed, this, &ModrinthPackExportTask::emitFailed); task->start(); } } diff --git a/launcher/modplatform/technic/SolderPackInstallTask.cpp b/launcher/modplatform/technic/SolderPackInstallTask.cpp index ed8b0a8a4..ffda05ee9 100644 --- a/launcher/modplatform/technic/SolderPackInstallTask.cpp +++ b/launcher/modplatform/technic/SolderPackInstallTask.cpp @@ -114,8 +114,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded() auto dl = Net::ApiDownload::makeFile(mod.url, path); if (!mod.md5.isEmpty()) { - auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); - dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, mod.md5)); } m_filesNetJob->addNetAction(dl); diff --git a/launcher/net/ChecksumValidator.h b/launcher/net/ChecksumValidator.h index 34ee5f856..7e01153c6 100644 --- a/launcher/net/ChecksumValidator.h +++ b/launcher/net/ChecksumValidator.h @@ -43,6 +43,9 @@ namespace Net { class ChecksumValidator : public Validator { public: + ChecksumValidator(QCryptographicHash::Algorithm algorithm, QString expectedHex) + : Net::ChecksumValidator(algorithm, QByteArray::fromHex(expectedHex.toLatin1())) + {} ChecksumValidator(QCryptographicHash::Algorithm algorithm, QByteArray expected = QByteArray()) : m_checksum(algorithm), m_expected(expected) {}; virtual ~ChecksumValidator() = default; diff --git a/launcher/translations/TranslationsModel.cpp b/launcher/translations/TranslationsModel.cpp index 56ade8e32..7fc1e46ac 100644 --- a/launcher/translations/TranslationsModel.cpp +++ b/launcher/translations/TranslationsModel.cpp @@ -591,8 +591,7 @@ void TranslationsModel::downloadTranslation(QString key) entry->setStale(true); auto dl = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATIONS_BASE_URL + lang->file_name), entry); - auto rawHash = QByteArray::fromHex(lang->file_sha1.toLatin1()); - dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash)); + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, lang->file_sha1)); dl->setProgress(dl->getProgress(), lang->file_size); d->m_dl_job.reset(new NetJob("Translation for " + key, APPLICATION->network())); diff --git a/launcher/ui/pages/instance/VersionPage.h b/launcher/ui/pages/instance/VersionPage.h index 951643743..602d09206 100644 --- a/launcher/ui/pages/instance/VersionPage.h +++ b/launcher/ui/pages/instance/VersionPage.h @@ -41,6 +41,7 @@ #pragma once #include +#include #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" diff --git a/launcher/ui/pages/modplatform/TexturePackModel.cpp b/launcher/ui/pages/modplatform/TexturePackModel.cpp index fa6369514..cb4cafd41 100644 --- a/launcher/ui/pages/modplatform/TexturePackModel.cpp +++ b/launcher/ui/pages/modplatform/TexturePackModel.cpp @@ -17,9 +17,9 @@ TexturePackResourceModel::TexturePackResourceModel(BaseInstance const& inst, Res { if (!m_version_list->isLoaded()) { qDebug() << "Loading version list..."; - auto task = m_version_list->getLoadTask(); - if (!task->isRunning()) - task->start(); + m_task = m_version_list->getLoadTask(); + if (!m_task->isRunning()) + m_task->start(); } } @@ -35,7 +35,8 @@ void waitOnVersionListLoad(Meta::VersionList::Ptr version_list) auto task = version_list->getLoadTask(); QObject::connect(task.get(), &Task::finished, &load_version_list_loop, &QEventLoop::quit); - + if (!task->isRunning()) + task->start(); load_version_list_loop.exec(); if (time_limit_for_list_load.isActive()) time_limit_for_list_load.stop(); diff --git a/launcher/ui/pages/modplatform/TexturePackModel.h b/launcher/ui/pages/modplatform/TexturePackModel.h index bb2db5cfc..607a03be3 100644 --- a/launcher/ui/pages/modplatform/TexturePackModel.h +++ b/launcher/ui/pages/modplatform/TexturePackModel.h @@ -22,6 +22,7 @@ class TexturePackResourceModel : public ResourcePackResourceModel { protected: Meta::VersionList::Ptr m_version_list; + Task::Ptr m_task; }; } // namespace ResourceDownload diff --git a/launcher/ui/widgets/VersionSelectWidget.cpp b/launcher/ui/widgets/VersionSelectWidget.cpp index a24630b31..1a62e689b 100644 --- a/launcher/ui/widgets/VersionSelectWidget.cpp +++ b/launcher/ui/widgets/VersionSelectWidget.cpp @@ -129,16 +129,12 @@ void VersionSelectWidget::closeEvent(QCloseEvent* event) void VersionSelectWidget::loadList() { - auto newTask = m_vlist->getLoadTask(); - if (!newTask) { - return; - } - loadTask = newTask.get(); - connect(loadTask, &Task::succeeded, this, &VersionSelectWidget::onTaskSucceeded); - connect(loadTask, &Task::failed, this, &VersionSelectWidget::onTaskFailed); - connect(loadTask, &Task::progress, this, &VersionSelectWidget::changeProgress); - if (!loadTask->isRunning()) { - loadTask->start(); + m_load_task = m_vlist->getLoadTask(); + connect(m_load_task.get(), &Task::succeeded, this, &VersionSelectWidget::onTaskSucceeded); + connect(m_load_task.get(), &Task::failed, this, &VersionSelectWidget::onTaskFailed); + connect(m_load_task.get(), &Task::progress, this, &VersionSelectWidget::changeProgress); + if (!m_load_task->isRunning()) { + m_load_task->start(); } sneakyProgressBar->setHidden(false); } @@ -150,7 +146,7 @@ void VersionSelectWidget::onTaskSucceeded() } sneakyProgressBar->setHidden(true); preselect(); - loadTask = nullptr; + m_load_task.reset(); } void VersionSelectWidget::onTaskFailed(const QString& reason) diff --git a/launcher/ui/widgets/VersionSelectWidget.h b/launcher/ui/widgets/VersionSelectWidget.h index d5ef1cc9f..d8d8506ea 100644 --- a/launcher/ui/widgets/VersionSelectWidget.h +++ b/launcher/ui/widgets/VersionSelectWidget.h @@ -98,7 +98,7 @@ class VersionSelectWidget : public QWidget { BaseVersionList* m_vlist = nullptr; VersionProxyModel* m_proxyModel = nullptr; int resizeOnColumn = 0; - Task* loadTask; + Task::Ptr m_load_task; bool preselectedAlready = false; QVBoxLayout* verticalLayout = nullptr; From 06e1cab41f4afa473a10d39d2986b3c509454054 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 22 Jun 2024 12:33:32 +0300 Subject: [PATCH 2/7] force online in case the offline load failed Signed-off-by: Trial97 --- launcher/meta/BaseEntity.cpp | 5 ++++- launcher/minecraft/ComponentUpdateTask.cpp | 24 +++++++++++++++++----- launcher/minecraft/ComponentUpdateTask.h | 1 - launcher/minecraft/ComponentUpdateTask_p.h | 2 ++ 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/launcher/meta/BaseEntity.cpp b/launcher/meta/BaseEntity.cpp index 581eaa7f4..5079ef5b9 100644 --- a/launcher/meta/BaseEntity.cpp +++ b/launcher/meta/BaseEntity.cpp @@ -122,7 +122,10 @@ void BaseEntityLoadTask::executeTask() } } // if we need remote update, run the update task - if (m_mode == Net::Mode::Offline || (!m_entity->m_sha256.isEmpty() && m_entity->m_sha256 == m_entity->m_file_sha256)) { + auto hashMatches = !m_entity->m_sha256.isEmpty() && m_entity->m_sha256 == m_entity->m_file_sha256; + auto wasLoadedOffline = m_entity->m_load_status != BaseEntity::LoadStatus::NotLoaded && m_mode == Net::Mode::Offline; + if (wasLoadedOffline || hashMatches) { + m_entity->m_load_status = BaseEntity::LoadStatus::Local; emitSucceeded(); return; } diff --git a/launcher/minecraft/ComponentUpdateTask.cpp b/launcher/minecraft/ComponentUpdateTask.cpp index 1368ca5e3..4d205af6c 100644 --- a/launcher/minecraft/ComponentUpdateTask.cpp +++ b/launcher/minecraft/ComponentUpdateTask.cpp @@ -13,6 +13,7 @@ #include "net/Mode.h" #include "Application.h" +#include "tasks/Task.h" /* * This is responsible for loading the components of a component list AND resolving dependency issues between them @@ -142,7 +143,6 @@ void ComponentUpdateTask::loadComponents() size_t componentIndex = 0; d->remoteLoadSuccessful = true; - m_load_tasks.clear(); // load all the components OR their lists... for (auto component : d->m_list->d->components) { Task::Ptr loadTask; @@ -175,14 +175,14 @@ void ComponentUpdateTask::loadComponents() } result = composeLoadResult(result, singleResult); if (loadTask) { - m_load_tasks.append(loadTask); qDebug() << "Remote loading is being run for" << component->getName(); - connect(loadTask.get(), &Task::succeeded, [=]() { remoteLoadSucceeded(taskIndex); }); - connect(loadTask.get(), &Task::failed, [=](const QString& error) { remoteLoadFailed(taskIndex, error); }); - connect(loadTask.get(), &Task::aborted, [=]() { remoteLoadFailed(taskIndex, tr("Aborted")); }); + connect(loadTask.get(), &Task::succeeded, this, [this, taskIndex]() { remoteLoadSucceeded(taskIndex); }); + connect(loadTask.get(), &Task::failed, this, [this, taskIndex](const QString& error) { remoteLoadFailed(taskIndex, error); }); + connect(loadTask.get(), &Task::aborted, this, [this, taskIndex]() { remoteLoadFailed(taskIndex, tr("Aborted")); }); RemoteLoadStatus status; status.type = loadType; status.PackProfileIndex = componentIndex; + status.task = loadTask; d->remoteLoadStatusList.append(status); taskIndex++; } @@ -489,7 +489,14 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly) void ComponentUpdateTask::remoteLoadSucceeded(size_t taskIndex) { + if (static_cast(d->remoteLoadStatusList.size()) < taskIndex) { + qWarning() << "Got task index outside of results" << taskIndex; + return; + } auto& taskSlot = d->remoteLoadStatusList[taskIndex]; + disconnect(taskSlot.task.get(), &Task::succeeded, this, nullptr); + disconnect(taskSlot.task.get(), &Task::failed, this, nullptr); + disconnect(taskSlot.task.get(), &Task::aborted, this, nullptr); if (taskSlot.finished) { qWarning() << "Got multiple results from remote load task" << taskIndex; return; @@ -509,7 +516,14 @@ void ComponentUpdateTask::remoteLoadSucceeded(size_t taskIndex) void ComponentUpdateTask::remoteLoadFailed(size_t taskIndex, const QString& msg) { + if (static_cast(d->remoteLoadStatusList.size()) < taskIndex) { + qWarning() << "Got task index outside of results" << taskIndex; + return; + } auto& taskSlot = d->remoteLoadStatusList[taskIndex]; + disconnect(taskSlot.task.get(), &Task::succeeded, this, nullptr); + disconnect(taskSlot.task.get(), &Task::failed, this, nullptr); + disconnect(taskSlot.task.get(), &Task::aborted, this, nullptr); if (taskSlot.finished) { qWarning() << "Got multiple results from remote load task" << taskIndex; return; diff --git a/launcher/minecraft/ComponentUpdateTask.h b/launcher/minecraft/ComponentUpdateTask.h index f225364cf..2f396a049 100644 --- a/launcher/minecraft/ComponentUpdateTask.h +++ b/launcher/minecraft/ComponentUpdateTask.h @@ -29,5 +29,4 @@ class ComponentUpdateTask : public Task { private: std::unique_ptr d; - QList m_load_tasks; }; diff --git a/launcher/minecraft/ComponentUpdateTask_p.h b/launcher/minecraft/ComponentUpdateTask_p.h index 00e8f2fbe..b82553700 100644 --- a/launcher/minecraft/ComponentUpdateTask_p.h +++ b/launcher/minecraft/ComponentUpdateTask_p.h @@ -4,6 +4,7 @@ #include #include #include "net/Mode.h" +#include "tasks/Task.h" class PackProfile; @@ -13,6 +14,7 @@ struct RemoteLoadStatus { bool finished = false; bool succeeded = false; QString error; + Task::Ptr task; }; struct ComponentUpdateTaskData { From 0a95b57c0a724bf1739c86fe8067502c55a2caea Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 22 Jun 2024 12:48:55 +0300 Subject: [PATCH 3/7] moved QEventLoops inside functions Signed-off-by: Trial97 --- launcher/meta/Index.cpp | 10 ++++++++++ launcher/meta/Index.h | 3 +++ launcher/meta/VersionList.cpp | 10 ++++++++++ launcher/meta/VersionList.h | 3 +++ launcher/minecraft/Component.cpp | 19 +++++++------------ .../atlauncher/ATLPackInstallTask.cpp | 17 ++--------------- 6 files changed, 35 insertions(+), 27 deletions(-) diff --git a/launcher/meta/Index.cpp b/launcher/meta/Index.cpp index ddcebc453..bd0745b6b 100644 --- a/launcher/meta/Index.cpp +++ b/launcher/meta/Index.cpp @@ -149,4 +149,14 @@ Task::Ptr Index::loadVersion(const QString& uid, const QString& version, Net::Mo loadTask->addTask(versionList->getVersion(version)->loadTask(mode)); return loadTask; } + +Version::Ptr Index::getLoadedVersion(const QString& uid, const QString& version) +{ + QEventLoop ev; + auto task = loadVersion(uid, version); + QObject::connect(task.get(), &Task::finished, &ev, &QEventLoop::quit); + task->start(); + ev.exec(); + return get(uid, version); +} } // namespace Meta diff --git a/launcher/meta/Index.h b/launcher/meta/Index.h index e54cdd88b..026a00c07 100644 --- a/launcher/meta/Index.h +++ b/launcher/meta/Index.h @@ -50,6 +50,9 @@ class Index : public QAbstractListModel, public BaseEntity { Task::Ptr loadVersion(const QString& uid, const QString& version = {}, Net::Mode mode = Net::Mode::Online, bool force = false); + // this blocks until the version is loaded + Version::Ptr getLoadedVersion(const QString& uid, const QString& version); + public: // for usage by parsers only void merge(const std::shared_ptr& other); diff --git a/launcher/meta/VersionList.cpp b/launcher/meta/VersionList.cpp index f7269c57f..45acc5d00 100644 --- a/launcher/meta/VersionList.cpp +++ b/launcher/meta/VersionList.cpp @@ -254,4 +254,14 @@ BaseVersion::Ptr VersionList::getRecommended() const return m_recommended; } +void VersionList::waitToLoad() +{ + if (isLoaded()) + return; + QEventLoop ev; + auto task = getLoadTask(); + QObject::connect(task.get(), &Task::finished, &ev, &QEventLoop::quit); + task->start(); + ev.exec(); +} } // namespace Meta diff --git a/launcher/meta/VersionList.h b/launcher/meta/VersionList.h index 22f9a3a92..90e0c8e5e 100644 --- a/launcher/meta/VersionList.h +++ b/launcher/meta/VersionList.h @@ -59,6 +59,9 @@ class VersionList : public BaseVersionList, public BaseEntity { QVector versions() const { return m_versions; } + // this blocks until the version list is loaded + void waitToLoad(); + public: // for usage only by parsers void setName(const QString& name); void setVersions(const QVector& versions); diff --git a/launcher/minecraft/Component.cpp b/launcher/minecraft/Component.cpp index 1ffcccada..9594d9a73 100644 --- a/launcher/minecraft/Component.cpp +++ b/launcher/minecraft/Component.cpp @@ -91,11 +91,8 @@ std::shared_ptr Component::getVersionFile() const { if (m_metaVersion) { if (!m_metaVersion->isLoaded()) { - QEventLoop ev; - auto task = APPLICATION->metadataIndex()->loadVersion(m_metaVersion->uid(), m_metaVersion->version(), Net::Mode::Online); - connect(task.get(), &Task::finished, &ev, &QEventLoop::quit); - task->start(); - ev.exec(); + // this method is const but the loading of meta changes the information + APPLICATION->metadataIndex()->getLoadedVersion(m_metaVersion->uid(), m_metaVersion->version()); } return m_metaVersion->data(); } else { @@ -197,10 +194,12 @@ bool Component::isCustomizable() } return false; } + bool Component::isRemovable() { return !m_important; } + bool Component::isRevertible() { if (isCustom()) { @@ -210,22 +209,18 @@ bool Component::isRevertible() } return false; } + bool Component::isMoveable() { // HACK, FIXME: this was too dumb and wouldn't follow dependency constraints anyway. For now hardcoded to 'true'. return true; } + bool Component::isVersionChangeable() { auto list = getVersionList(); if (list) { - if (!list->isLoaded()) { - QEventLoop ev; - auto task = list->getLoadTask(); - connect(task.get(), &Task::finished, &ev, &QEventLoop::quit); - task->start(); - ev.exec(); - } + list->waitToLoad(); return list->count() != 0; } return false; diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 9ceb1e3d2..abe7d0177 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -36,7 +36,6 @@ #include "ATLPackInstallTask.h" -#include #include #include @@ -344,13 +343,7 @@ QString PackInstallTask::getVersionForLoader(QString uid) return Q_NULLPTR; } - if (!vlist->isLoaded()) { - QEventLoop ev; - auto task = vlist->getLoadTask(); - connect(task.get(), &Task::finished, &ev, &QEventLoop::quit); - task->start(); - ev.exec(); - } + vlist->waitToLoad(); if (m_version.loader.recommended || m_version.loader.latest) { for (int i = 0; i < vlist->versions().size(); i++) { @@ -1076,13 +1069,7 @@ void PackInstallTask::install() static Meta::Version::Ptr getComponentVersion(const QString& uid, const QString& version) { - QEventLoop ev; - auto task = APPLICATION->metadataIndex()->loadVersion(uid, version); - QObject::connect(task.get(), &Task::finished, &ev, &QEventLoop::quit); - task->start(); - ev.exec(); - - return APPLICATION->metadataIndex()->get(uid, version); + return APPLICATION->metadataIndex()->getLoadedVersion(uid, version); } } // namespace ATLauncher From 715c99636da442e6c8abafc7146a7563c919d98d Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 23 Jun 2024 01:47:49 +0300 Subject: [PATCH 4/7] fix offline mode for meta Signed-off-by: Trial97 --- launcher/meta/BaseEntity.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/meta/BaseEntity.cpp b/launcher/meta/BaseEntity.cpp index 5079ef5b9..6c064c99a 100644 --- a/launcher/meta/BaseEntity.cpp +++ b/launcher/meta/BaseEntity.cpp @@ -102,7 +102,7 @@ void BaseEntityLoadTask::executeTask() { const QString fname = QDir("meta").absoluteFilePath(m_entity->localFilename()); // load local file if nothing is loaded yet - if (m_entity->m_load_status != BaseEntity::LoadStatus::NotLoaded && QFile::exists(fname)) { + if (m_entity->m_load_status == BaseEntity::LoadStatus::NotLoaded && QFile::exists(fname)) { setStatus(tr("Loading local file")); try { auto fileData = FS::read(fname); @@ -125,7 +125,6 @@ void BaseEntityLoadTask::executeTask() auto hashMatches = !m_entity->m_sha256.isEmpty() && m_entity->m_sha256 == m_entity->m_file_sha256; auto wasLoadedOffline = m_entity->m_load_status != BaseEntity::LoadStatus::NotLoaded && m_mode == Net::Mode::Offline; if (wasLoadedOffline || hashMatches) { - m_entity->m_load_status = BaseEntity::LoadStatus::Local; emitSucceeded(); return; } @@ -142,6 +141,7 @@ void BaseEntityLoadTask::executeTask() dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Sha256, m_entity->m_sha256)); dl->addValidator(new ParsingValidator(m_entity)); m_task->addNetAction(dl); + m_task->setAskRetry(false); connect(m_task.get(), &Task::failed, this, &BaseEntityLoadTask::emitFailed); connect(m_task.get(), &Task::succeeded, this, &BaseEntityLoadTask::emitSucceeded); connect(m_task.get(), &Task::succeeded, this, [this]() { From cf220356b15af613fc06d981d61eb697f7f87ea0 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 26 Jun 2024 08:56:17 +0300 Subject: [PATCH 5/7] fixed panic with recursive eventloop Signed-off-by: Trial97 --- launcher/minecraft/Component.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/launcher/minecraft/Component.cpp b/launcher/minecraft/Component.cpp index 9594d9a73..32a1deb68 100644 --- a/launcher/minecraft/Component.cpp +++ b/launcher/minecraft/Component.cpp @@ -90,10 +90,6 @@ void Component::applyTo(LaunchProfile* profile) std::shared_ptr Component::getVersionFile() const { if (m_metaVersion) { - if (!m_metaVersion->isLoaded()) { - // this method is const but the loading of meta changes the information - APPLICATION->metadataIndex()->getLoadedVersion(m_metaVersion->uid(), m_metaVersion->version()); - } return m_metaVersion->data(); } else { return m_file; @@ -120,29 +116,35 @@ int Component::getOrder() } return 0; } + void Component::setOrder(int order) { m_orderOverride = true; m_order = order; } + QString Component::getID() { return m_uid; } + QString Component::getName() { if (!m_cachedName.isEmpty()) return m_cachedName; return m_uid; } + QString Component::getVersion() { return m_cachedVersion; } + QString Component::getFilename() { return m_parent->patchFilePathForUid(m_uid); } + QDateTime Component::getReleaseDateTime() { if (m_metaVersion) { @@ -187,12 +189,7 @@ bool Component::isCustom() bool Component::isCustomizable() { - if (m_metaVersion) { - if (getVersionFile()) { - return true; - } - } - return false; + return m_metaVersion && getVersionFile(); } bool Component::isRemovable() From 9d903175ab77fad96281b0f29ed29cf7b6c10c16 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 28 Jun 2024 20:51:52 +0300 Subject: [PATCH 6/7] format Signed-off-by: Trial97 --- launcher/meta/BaseEntity.cpp | 2 +- launcher/meta/VersionList.cpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/launcher/meta/BaseEntity.cpp b/launcher/meta/BaseEntity.cpp index 6c064c99a..a8350eaeb 100644 --- a/launcher/meta/BaseEntity.cpp +++ b/launcher/meta/BaseEntity.cpp @@ -32,7 +32,7 @@ namespace Meta { class ParsingValidator : public Net::Validator { public: /* con/des */ - ParsingValidator(BaseEntity* entity) : m_entity(entity){}; + ParsingValidator(BaseEntity* entity) : m_entity(entity) {}; virtual ~ParsingValidator() = default; public: /* methods */ diff --git a/launcher/meta/VersionList.cpp b/launcher/meta/VersionList.cpp index 45acc5d00..f21e4594a 100644 --- a/launcher/meta/VersionList.cpp +++ b/launcher/meta/VersionList.cpp @@ -243,9 +243,8 @@ void VersionList::setupAddedVersion(const int row, const Version::Ptr& version) connect(version.get(), &Version::requiresChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector() << RequiresRole); }); - connect(version.get(), &Version::timeChanged, this, [this, row]() { - emit dataChanged(index(row), index(row), { TimeRole, SortRole }); - }); + connect(version.get(), &Version::timeChanged, this, + [this, row]() { emit dataChanged(index(row), index(row), { TimeRole, SortRole }); }); connect(version.get(), &Version::typeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), { TypeRole }); }); } From 6bd8b72f68f4dd52d746e8ffc7e503ad985fd2b0 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 18 Aug 2024 22:38:45 +0300 Subject: [PATCH 7/7] fix meta not validating for specific versions Signed-off-by: Trial97 --- launcher/meta/BaseEntity.cpp | 38 +++++++++++++++++++++++++---------- launcher/meta/Version.h | 2 +- launcher/meta/VersionList.cpp | 2 ++ 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/launcher/meta/BaseEntity.cpp b/launcher/meta/BaseEntity.cpp index 24bced3a8..b0e754ada 100644 --- a/launcher/meta/BaseEntity.cpp +++ b/launcher/meta/BaseEntity.cpp @@ -15,6 +15,7 @@ #include "BaseEntity.h" +#include "Exception.h" #include "FileSystem.h" #include "Json.h" #include "modplatform/helpers/HashUtils.h" @@ -91,7 +92,8 @@ Task::Ptr BaseEntity::loadTask(Net::Mode mode) bool BaseEntity::isLoaded() const { - return m_load_status != LoadStatus::NotLoaded; + // consider it loaded only if the main hash is either empty and was remote loadded or the hashes match and was loaded + return m_sha256.isEmpty() ? m_load_status == LoadStatus::Remote : m_load_status != LoadStatus::NotLoaded && m_sha256 == m_file_sha256; } void BaseEntity::setSha256(QString sha256) @@ -109,30 +111,44 @@ BaseEntityLoadTask::BaseEntityLoadTask(BaseEntity* parent, Net::Mode mode) : m_e void BaseEntityLoadTask::executeTask() { const QString fname = QDir("meta").absoluteFilePath(m_entity->localFilename()); - // load local file if nothing is loaded yet - if (m_entity->m_load_status == BaseEntity::LoadStatus::NotLoaded && QFile::exists(fname)) { - setStatus(tr("Loading local file")); + auto hashMatches = false; + // the file exists on disk try to load it + if (QFile::exists(fname)) { try { - auto fileData = FS::read(fname); - m_entity->m_file_sha256 = Hashing::hash(fileData, Hashing::Algorithm::Sha256); - if (m_mode == Net::Mode::Online && !m_entity->m_sha256.isEmpty() && m_entity->m_sha256 != m_entity->m_file_sha256) { - FS::deletePath(fname); - } else { + QByteArray fileData; + // read local file if nothing is loaded yet + if (m_entity->m_load_status == BaseEntity::LoadStatus::NotLoaded || m_entity->m_file_sha256.isEmpty()) { + setStatus(tr("Loading local file")); + fileData = FS::read(fname); + m_entity->m_file_sha256 = Hashing::hash(fileData, Hashing::Algorithm::Sha256); + } + + // on online the hash needs to match + hashMatches = m_entity->m_sha256 == m_entity->m_file_sha256; + if (m_mode == Net::Mode::Online && !m_entity->m_sha256.isEmpty() && !hashMatches) { + throw Exception("mismatched checksum"); + } + + // load local file + if (m_entity->m_load_status == BaseEntity::LoadStatus::NotLoaded) { auto doc = Json::requireDocument(fileData, fname); auto obj = Json::requireObject(doc, fname); m_entity->parse(obj); m_entity->m_load_status = BaseEntity::LoadStatus::Local; } + } catch (const Exception& e) { qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause()); // just make sure it's gone and we never consider it again. FS::deletePath(fname); + m_entity->m_load_status = BaseEntity::LoadStatus::NotLoaded; } } // if we need remote update, run the update task - auto hashMatches = !m_entity->m_sha256.isEmpty() && m_entity->m_sha256 == m_entity->m_file_sha256; auto wasLoadedOffline = m_entity->m_load_status != BaseEntity::LoadStatus::NotLoaded && m_mode == Net::Mode::Offline; - if (wasLoadedOffline || hashMatches) { + // if has is not present allways fetch from remote(e.g. the main index file), else only fetch if hash doesn't match + auto wasLoadedRemote = m_entity->m_sha256.isEmpty() ? m_entity->m_load_status == BaseEntity::LoadStatus::Remote : hashMatches; + if (wasLoadedOffline || wasLoadedRemote) { emitSucceeded(); return; } diff --git a/launcher/meta/Version.h b/launcher/meta/Version.h index 149af86f6..46dc740da 100644 --- a/launcher/meta/Version.h +++ b/launcher/meta/Version.h @@ -52,7 +52,7 @@ class Version : public QObject, public BaseVersion, public BaseEntity { const Meta::RequireSet& requiredSet() const { return m_requires; } VersionFilePtr data() const { return m_data; } bool isRecommended() const { return m_recommended; } - bool isLoaded() const { return m_data != nullptr; } + bool isLoaded() const { return m_data != nullptr && BaseEntity::isLoaded(); } void merge(const Version::Ptr& other); void mergeFromList(const Version::Ptr& other); diff --git a/launcher/meta/VersionList.cpp b/launcher/meta/VersionList.cpp index f21e4594a..698c73ef4 100644 --- a/launcher/meta/VersionList.cpp +++ b/launcher/meta/VersionList.cpp @@ -139,6 +139,8 @@ Version::Ptr VersionList::getVersion(const QString& version) if (!out) { out = std::make_shared(m_uid, version); m_lookup[version] = out; + setupAddedVersion(m_versions.size(), out); + m_versions.append(out); } return out; }