From 844b2457769d61131f97b5e82bb134568dfd42ed Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 3 Jun 2022 19:08:01 -0300 Subject: feat: add EnsureMetadataTask This task is responsible for checking if the mod has metadata for a specific provider, and create it if it doesn't. In the context of the mod updater, this is not the best architecture, since we do a single task for each mod. However, this way of structuring it allows us to use it later on in more diverse scenarios. This way we decouple this task from the mod updater, trading off some performance (though that will be mitigated when we have a way of running arbitrary tasks concurrently). Signed-off-by: flow --- launcher/modplatform/EnsureMetadataTask.h | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 launcher/modplatform/EnsureMetadataTask.h (limited to 'launcher/modplatform/EnsureMetadataTask.h') diff --git a/launcher/modplatform/EnsureMetadataTask.h b/launcher/modplatform/EnsureMetadataTask.h new file mode 100644 index 00000000..624e253a --- /dev/null +++ b/launcher/modplatform/EnsureMetadataTask.h @@ -0,0 +1,41 @@ +#pragma once + +#include "ModIndex.h" +#include "tasks/SequentialTask.h" + +class Mod; +class QDir; +class MultipleOptionsTask; + +class EnsureMetadataTask : public Task { + Q_OBJECT + + public: + EnsureMetadataTask(Mod&, QDir&, bool try_all, ModPlatform::Provider = ModPlatform::Provider::MODRINTH); + + public slots: + bool abort() override; + protected slots: + void executeTask() override; + + private: + // FIXME: Move to their own namespace + void modrinthEnsureMetadata(SequentialTask&, QByteArray&); + void flameEnsureMetadata(SequentialTask&, QByteArray&); + + // Helpers + void emitReady(); + void emitFail(); + + signals: + void metadataReady(); + void metadataFailed(); + + private: + Mod& m_mod; + QDir& m_index_dir; + ModPlatform::Provider m_provider; + bool m_try_all; + + MultipleOptionsTask* m_task_handler = nullptr; +}; -- cgit From 4e6978ff6f61777a2e2e989cba58a9f0c48d2782 Mon Sep 17 00:00:00 2001 From: flow Date: Thu, 16 Jun 2022 11:45:29 -0300 Subject: feat: improve metadata gen. networking and performance This makes the metadata generation code a lot messier and harder to use, but there's not really much else that can be done about it while preserving all it's capabilities :( At least we now have speed Signed-off-by: flow --- launcher/modplatform/CheckUpdateTask.h | 2 +- launcher/modplatform/EnsureMetadataTask.cpp | 550 ++++++++++++++++----- launcher/modplatform/EnsureMetadataTask.h | 35 +- launcher/modplatform/flame/FlameCheckUpdate.cpp | 209 +++----- .../modplatform/modrinth/ModrinthCheckUpdate.cpp | 5 +- launcher/ui/dialogs/ModUpdateDialog.cpp | 113 ++++- launcher/ui/dialogs/ModUpdateDialog.h | 8 +- 7 files changed, 614 insertions(+), 308 deletions(-) (limited to 'launcher/modplatform/EnsureMetadataTask.h') diff --git a/launcher/modplatform/CheckUpdateTask.h b/launcher/modplatform/CheckUpdateTask.h index 8c701e46..d96fc340 100644 --- a/launcher/modplatform/CheckUpdateTask.h +++ b/launcher/modplatform/CheckUpdateTask.h @@ -13,7 +13,7 @@ class CheckUpdateTask : public Task { public: CheckUpdateTask(std::list& mods, std::list& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr mods_folder) - : m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder) {}; + : Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder) {}; struct UpdatableMod { QString name; diff --git a/launcher/modplatform/EnsureMetadataTask.cpp b/launcher/modplatform/EnsureMetadataTask.cpp index dc92d8ab..cf4e55b9 100644 --- a/launcher/modplatform/EnsureMetadataTask.cpp +++ b/launcher/modplatform/EnsureMetadataTask.cpp @@ -19,226 +19,502 @@ static ModPlatform::ProviderCapabilities ProviderCaps; static ModrinthAPI modrinth_api; static FlameAPI flame_api; -EnsureMetadataTask::EnsureMetadataTask(Mod& mod, QDir& dir, bool try_all, ModPlatform::Provider prov) - : m_mod(mod), m_index_dir(dir), m_provider(prov), m_try_all(try_all) -{} - -bool EnsureMetadataTask::abort() +EnsureMetadataTask::EnsureMetadataTask(Mod& mod, QDir dir, ModPlatform::Provider prov) : Task(nullptr), m_index_dir(dir), m_provider(prov) { - return m_task_handler->abort(); + auto hash = getHash(mod); + if (hash.isEmpty()) + emitFail(mod); + else + m_mods.insert(hash, mod); } -void EnsureMetadataTask::executeTask() +EnsureMetadataTask::EnsureMetadataTask(std::list& mods, QDir dir, ModPlatform::Provider prov) + : Task(nullptr), m_index_dir(dir), m_provider(prov) { - // They already have the right metadata :o - if (m_mod.status() != ModStatus::NoMetadata && m_mod.metadata() && m_mod.metadata()->provider == m_provider) { - emitReady(); - return; - } + for (auto& mod : mods) { + if (!mod.valid()) { + emitFail(mod); + continue; + } - // Folders don't have metadata - if (m_mod.type() == Mod::MOD_FOLDER) { - emitReady(); - return; - } + auto hash = getHash(mod); + if (hash.isEmpty()) { + emitFail(mod); + continue; + } - setStatus(tr("Generating %1's metadata...").arg(m_mod.name())); - qDebug() << QString("Generating %1's metadata...").arg(m_mod.name()); + m_mods.insert(hash, mod); + } +} +QString EnsureMetadataTask::getHash(Mod& mod) +{ + /* Here we create a mapping hash -> mod, because we need that relationship to parse the API routes */ QByteArray jar_data; - try { - jar_data = FS::read(m_mod.fileinfo().absoluteFilePath()); + jar_data = FS::read(mod.fileinfo().absoluteFilePath()); } catch (FS::FileSystemException& e) { - qCritical() << QString("Failed to open / read JAR file of %1").arg(m_mod.name()); + qCritical() << QString("Failed to open / read JAR file of %1").arg(mod.name()); qCritical() << QString("Reason: ") << e.cause(); - emitFail(); - return; + return {}; } - auto tsk = new MultipleOptionsTask(nullptr, "GetMetadataTask"); + switch (m_provider) { + case ModPlatform::Provider::MODRINTH: { + auto hash_type = ProviderCaps.hashType(ModPlatform::Provider::MODRINTH).first(); + + return QString(ProviderCaps.hash(ModPlatform::Provider::MODRINTH, jar_data, hash_type).toHex()); + } + case ModPlatform::Provider::FLAME: { + QByteArray jar_data_treated; + for (char c : jar_data) { + // CF-specific + if (!(c == 9 || c == 10 || c == 13 || c == 32)) + jar_data_treated.push_back(c); + } + + return QString::number(MurmurHash2(jar_data_treated, jar_data_treated.length())); + } + } + + return {}; +} + +bool EnsureMetadataTask::abort() +{ + // Prevent sending signals to a dead object + disconnect(this, 0, 0, 0); + + if (m_current_task) + return m_current_task->abort(); + return true; +} + +void EnsureMetadataTask::executeTask() +{ + setStatus(tr("Checking if mods have metadata...")); + + for (auto mod : m_mods) { + if (!mod.valid()) + continue; + + // They already have the right metadata :o + if (mod.status() != ModStatus::NoMetadata && mod.metadata() && mod.metadata()->provider == m_provider) { + qDebug() << "Mod" << mod.name() << "already has metadata!"; + emitReady(mod); + return; + } + + // Folders don't have metadata + if (mod.type() == Mod::MOD_FOLDER) { + emitReady(mod); + return; + } + } + + NetJob::Ptr version_task; switch (m_provider) { case (ModPlatform::Provider::MODRINTH): - modrinthEnsureMetadata(*tsk, jar_data); - if (m_try_all) - flameEnsureMetadata(*tsk, jar_data); - + version_task = modrinthVersionsTask(); break; case (ModPlatform::Provider::FLAME): - flameEnsureMetadata(*tsk, jar_data); - if (m_try_all) - modrinthEnsureMetadata(*tsk, jar_data); - + version_task = flameVersionsTask(); break; } - connect(tsk, &MultipleOptionsTask::finished, this, [tsk] { tsk->deleteLater(); }); - connect(tsk, &MultipleOptionsTask::failed, [this] { - qCritical() << QString("Download of %1's metadata failed").arg(m_mod.name()); + auto invalidade_leftover = [this] { + QMutableHashIterator mods_iter(m_mods); + while (mods_iter.hasNext()) { + auto mod = mods_iter.next(); + emitFail(mod.value()); + } + + emitSucceeded(); + }; + + connect(version_task.get(), &Task::finished, this, [this, invalidade_leftover] { + NetJob::Ptr project_task; + + switch (m_provider) { + case (ModPlatform::Provider::MODRINTH): + project_task = modrinthProjectsTask(); + break; + case (ModPlatform::Provider::FLAME): + project_task = flameProjectsTask(); + break; + } + + if (!project_task) { + invalidade_leftover(); + return; + } + + connect(project_task.get(), &Task::finished, this, [=] { + invalidade_leftover(); + project_task->deleteLater(); + m_current_task = nullptr; + }); - emitFail(); + m_current_task = project_task.get(); + project_task->start(); }); - connect(tsk, &MultipleOptionsTask::succeeded, this, &EnsureMetadataTask::emitReady); - m_task_handler = tsk; + connect(version_task.get(), &Task::finished, [=] { + version_task->deleteLater(); + m_current_task = nullptr; + }); + + if (m_mods.size() > 1) + setStatus(tr("Requesting metadata information from %1...").arg(ProviderCaps.readableName(m_provider))); + else if (!m_mods.empty()) + setStatus(tr("Requesting metadata information from %1 for '%2'...") + .arg(ProviderCaps.readableName(m_provider), m_mods.begin().value().name())); - tsk->start(); + m_current_task = version_task.get(); + version_task->start(); } -void EnsureMetadataTask::emitReady() +void EnsureMetadataTask::emitReady(Mod& m) { - emit metadataReady(); - emitSucceeded(); + qDebug() << QString("Generated metadata for %1").arg(m.name()); + emit metadataReady(m); + + m_mods.remove(getHash(m)); } -void EnsureMetadataTask::emitFail() +void EnsureMetadataTask::emitFail(Mod& m) { - qDebug() << QString("Failed to generate metadata for %1").arg(m_mod.name()); - emit metadataFailed(); - //emitFailed(tr("Failed to generate metadata for %1").arg(m_mod.name())); - emitSucceeded(); + qDebug() << QString("Failed to generate metadata for %1").arg(m.name()); + emit metadataFailed(m); + + m_mods.remove(getHash(m)); } -void EnsureMetadataTask::modrinthEnsureMetadata(SequentialTask& tsk, QByteArray& jar_data) +// Modrinth + +NetJob::Ptr EnsureMetadataTask::modrinthVersionsTask() { - // Modrinth currently garantees that some hash types will always be present. - // But let's be sure and cover all cases anyways :) - for (auto hash_type : ProviderCaps.hashType(ModPlatform::Provider::MODRINTH)) { - auto* response = new QByteArray(); - auto hash = QString(ProviderCaps.hash(ModPlatform::Provider::MODRINTH, jar_data, hash_type).toHex()); - auto ver_task = modrinth_api.currentVersion(hash, hash_type, response); - - // Prevents unfortunate timings when aborting the task - if (!ver_task) - return; + auto hash_type = ProviderCaps.hashType(ModPlatform::Provider::MODRINTH).first(); - connect(ver_task.get(), &NetJob::succeeded, this, [this, ver_task, response] { - QJsonParseError parse_error{}; - QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); - if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response from " << m_mod.name() << " at " << parse_error.offset - << " reason: " << parse_error.errorString(); - qWarning() << *response; + auto* response = new QByteArray(); + auto ver_task = modrinth_api.currentVersions(m_mods.keys(), hash_type, response); - ver_task->failed(parse_error.errorString()); - return; + // Prevents unfortunate timings when aborting the task + if (!ver_task) + return {}; + + connect(ver_task.get(), &NetJob::succeeded, this, [this, response] { + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from Modrinth::CurrentVersions at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *response; + + failed(parse_error.errorString()); + return; + } + + try { + auto entries = Json::requireObject(doc); + for (auto& hash : m_mods.keys()) { + auto mod = m_mods.find(hash).value(); + try { + auto entry = Json::requireObject(entries, hash); + + setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(mod.name())); + qDebug() << "Getting version for" << mod.name() << "from Modrinth"; + + m_temp_versions.insert(hash, Modrinth::loadIndexedPackVersion(entry)); + } catch (Json::JsonException& e) { + qDebug() << e.cause(); + qDebug() << entries; + + emitFail(mod); + } } + } catch (Json::JsonException& e) { + qDebug() << e.cause(); + qDebug() << doc; + } + }); - auto doc_obj = Json::requireObject(doc); - auto ver = Modrinth::loadIndexedPackVersion(doc_obj, {}, m_mod.fileinfo().fileName()); + return ver_task; +} + +NetJob::Ptr EnsureMetadataTask::modrinthProjectsTask() +{ + QHash addonIds; + for (auto const& data : m_temp_versions) + addonIds.insert(data.addonId.toString(), data.hash); + + auto response = new QByteArray(); + NetJob::Ptr proj_task; + + if (addonIds.isEmpty()) { + qWarning() << "No addonId found!"; + } else if (addonIds.size() == 1) { + proj_task = modrinth_api.getProject(*addonIds.keyBegin(), response); + } else { + proj_task = modrinth_api.getProjects(addonIds.keys(), response); + } + + // Prevents unfortunate timings when aborting the task + if (!proj_task) + return {}; + + connect(proj_task.get(), &NetJob::succeeded, this, [this, response, addonIds] { + QJsonParseError parse_error{}; + auto doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from Modrinth projects task at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *response; + return; + } - // Minimal IndexedPack to create the metadata - ModPlatform::IndexedPack pack; - pack.name = m_mod.name(); - pack.provider = ModPlatform::Provider::MODRINTH; - pack.addonId = ver.addonId; + try { + QJsonArray entries; + if (addonIds.size() == 1) + entries = { doc.object() }; + else + entries = Json::requireArray(doc); - // Prevent file name mismatch - ver.fileName = m_mod.fileinfo().fileName(); + for (auto entry : entries) { + auto entry_obj = Json::requireObject(entry); + auto entry_id = Json::requireString(entry_obj, "id"); - QDir tmp_index_dir(m_index_dir); + auto hash = addonIds.find(entry_id).value(); - { - LocalModUpdateTask update_metadata(m_index_dir, pack, ver); - QEventLoop loop; - QTimer timeout; + auto mod = m_mods.find(hash).value(); - QObject::connect(&update_metadata, &Task::finished, &loop, &QEventLoop::quit); - QObject::connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit); + try { + setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(mod.name())); - update_metadata.start(); - timeout.start(100); + ModPlatform::IndexedPack pack; + Modrinth::loadIndexedPack(pack, entry_obj); - loop.exec(); - } + modrinthCallback(pack, m_temp_versions.find(hash).value(), mod); + } catch (Json::JsonException& e) { + qDebug() << e.cause(); + qDebug() << entries; - auto mod_name = m_mod.name(); - auto meta = new Metadata::ModStruct(Metadata::get(tmp_index_dir, mod_name)); - m_mod.setMetadata(meta); - }); + emitFail(mod); + } + } + } catch (Json::JsonException& e) { + qDebug() << e.cause(); + qDebug() << doc; + } + }); - tsk.addTask(ver_task); - } + return proj_task; } -void EnsureMetadataTask::flameEnsureMetadata(SequentialTask& tsk, QByteArray& jar_data) +// Flame +NetJob::Ptr EnsureMetadataTask::flameVersionsTask() { - QByteArray jar_data_treated; - for (char c : jar_data) { - // CF-specific - if (!(c == 9 || c == 10 || c == 13 || c == 32)) - jar_data_treated.push_back(c); - } - auto* response = new QByteArray(); std::list fingerprints; - auto murmur = MurmurHash2(jar_data_treated, jar_data_treated.length()); - fingerprints.push_back(murmur); + for (auto& murmur : m_mods.keys()) { + fingerprints.push_back(murmur.toUInt()); + } auto ver_task = flame_api.matchFingerprints(fingerprints, response); - connect(ver_task.get(), &Task::succeeded, this, [this, ver_task, response] { - QDir tmp_index_dir(m_index_dir); - + connect(ver_task.get(), &Task::succeeded, this, [this, response] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response from " << m_mod.name() << " at " << parse_error.offset + qWarning() << "Error while parsing JSON response from Modrinth::CurrentVersions at " << parse_error.offset << " reason: " << parse_error.errorString(); qWarning() << *response; - ver_task->failed(parse_error.errorString()); + failed(parse_error.errorString()); return; } try { auto doc_obj = Json::requireObject(doc); - auto data_obj = Json::ensureObject(doc_obj, "data"); - auto match_obj = Json::ensureObject(Json::ensureArray(data_obj, "exactMatches")[0], {}); - if (match_obj.isEmpty()) { - qCritical() << "Fingerprint match is empty!"; + auto data_obj = Json::requireObject(doc_obj, "data"); + auto data_arr = Json::requireArray(data_obj, "exactMatches"); + + if (data_arr.isEmpty()) { + qWarning() << "No matches found for fingerprint search!"; - ver_task->failed(parse_error.errorString()); return; } - auto file_obj = Json::ensureObject(match_obj, "file"); + for (auto match : data_arr) { + auto match_obj = Json::ensureObject(match, {}); + auto file_obj = Json::ensureObject(match_obj, "file", {}); - ModPlatform::IndexedPack pack; - pack.name = m_mod.name(); - pack.provider = ModPlatform::Provider::FLAME; - pack.addonId = Json::requireInteger(file_obj, "modId"); + if (match_obj.isEmpty() || file_obj.isEmpty()) { + qWarning() << "Fingerprint match is empty!"; - ModPlatform::IndexedVersion ver = FlameMod::loadIndexedPackVersion(file_obj); + return; + } - // Prevent file name mismatch - ver.fileName = m_mod.fileinfo().fileName(); + auto fingerprint = QString::number(Json::ensureVariant(file_obj, "fileFingerprint").toUInt()); + auto mod = m_mods.find(fingerprint); + if (mod == m_mods.end()) { + qWarning() << "Invalid fingerprint from the API response."; + continue; + } - { - LocalModUpdateTask update_metadata(m_index_dir, pack, ver); - QEventLoop loop; - QTimer timeout; + setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(mod->name())); - QObject::connect(&update_metadata, &Task::finished, &loop, &QEventLoop::quit); - QObject::connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit); + m_temp_versions.insert(fingerprint, FlameMod::loadIndexedPackVersion(file_obj)); + } - update_metadata.start(); - timeout.start(100); + } catch (Json::JsonException& e) { + qDebug() << e.cause(); + qDebug() << doc; + } + }); - loop.exec(); - } + return ver_task; +} + +NetJob::Ptr EnsureMetadataTask::flameProjectsTask() +{ + QHash addonIds; + for (auto const& hash : m_mods.keys()) { + if (m_temp_versions.contains(hash)) { + auto const& data = m_temp_versions.find(hash).value(); + addonIds.insert(data.addonId.toString(), hash); + } + } + + auto response = new QByteArray(); + NetJob::Ptr proj_task; + + if (addonIds.isEmpty()) { + qWarning() << "No addonId found!"; + } else if (addonIds.size() == 1) { + proj_task = flame_api.getProject(*addonIds.keyBegin(), response); + } else { + proj_task = flame_api.getProjects(addonIds.keys(), response); + } + + // Prevents unfortunate timings when aborting the task + if (!proj_task) + return {}; + + connect(proj_task.get(), &NetJob::succeeded, this, [this, response, addonIds] { + QJsonParseError parse_error{}; + auto doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from Modrinth projects task at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *response; + return; + } + + try { + QJsonArray entries; + if (addonIds.size() == 1) + entries = { Json::requireObject(Json::requireObject(doc), "data") }; + else + entries = Json::requireArray(Json::requireObject(doc), "data"); + + for (auto entry : entries) { + auto entry_obj = Json::requireObject(entry); + + auto id = QString::number(Json::requireInteger(entry_obj, "id")); + auto hash = addonIds.find(id).value(); + auto mod = m_mods.find(hash).value(); + + try { + setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(mod.name())); - auto mod_name = m_mod.name(); - auto meta = new Metadata::ModStruct(Metadata::get(tmp_index_dir, mod_name)); - m_mod.setMetadata(meta); + ModPlatform::IndexedPack pack; + FlameMod::loadIndexedPack(pack, entry_obj); + flameCallback(pack, m_temp_versions.find(hash).value(), mod); + } catch (Json::JsonException& e) { + qDebug() << e.cause(); + qDebug() << entries; + + emitFail(mod); + } + } } catch (Json::JsonException& e) { - emitFailed(e.cause() + " : " + e.what()); + qDebug() << e.cause(); + qDebug() << doc; } }); - tsk.addTask(ver_task); + return proj_task; +} + +void EnsureMetadataTask::modrinthCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod& mod) +{ + // Prevent file name mismatch + ver.fileName = mod.fileinfo().fileName(); + + QDir tmp_index_dir(m_index_dir); + + { + LocalModUpdateTask update_metadata(m_index_dir, pack, ver); + QEventLoop loop; + + QObject::connect(&update_metadata, &Task::finished, &loop, &QEventLoop::quit); + + update_metadata.start(); + + if (!update_metadata.isFinished()) + loop.exec(); + } + + auto metadata = Metadata::get(tmp_index_dir, pack.slug); + if (!metadata.isValid()) { + qCritical() << "Failed to generate metadata at last step!"; + emitFail(mod); + return; + } + + mod.setMetadata(metadata); + + emitReady(mod); +} + +void EnsureMetadataTask::flameCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod& mod) +{ + try { + // Prevent file name mismatch + ver.fileName = mod.fileinfo().fileName(); + + QDir tmp_index_dir(m_index_dir); + + { + LocalModUpdateTask update_metadata(m_index_dir, pack, ver); + QEventLoop loop; + + QObject::connect(&update_metadata, &Task::finished, &loop, &QEventLoop::quit); + + update_metadata.start(); + + if (!update_metadata.isFinished()) + loop.exec(); + } + + auto metadata = Metadata::get(tmp_index_dir, pack.slug); + if (!metadata.isValid()) { + qCritical() << "Failed to generate metadata at last step!"; + emitFail(mod); + return; + } + + mod.setMetadata(metadata); + + emitReady(mod); + } catch (Json::JsonException& e) { + qDebug() << e.cause(); + + emitFail(mod); + } } diff --git a/launcher/modplatform/EnsureMetadataTask.h b/launcher/modplatform/EnsureMetadataTask.h index 624e253a..880503b9 100644 --- a/launcher/modplatform/EnsureMetadataTask.h +++ b/launcher/modplatform/EnsureMetadataTask.h @@ -2,6 +2,7 @@ #include "ModIndex.h" #include "tasks/SequentialTask.h" +#include "net/NetJob.h" class Mod; class QDir; @@ -11,7 +12,10 @@ class EnsureMetadataTask : public Task { Q_OBJECT public: - EnsureMetadataTask(Mod&, QDir&, bool try_all, ModPlatform::Provider = ModPlatform::Provider::MODRINTH); + EnsureMetadataTask(Mod&, QDir, ModPlatform::Provider = ModPlatform::Provider::MODRINTH); + EnsureMetadataTask(std::list&, QDir, ModPlatform::Provider = ModPlatform::Provider::MODRINTH); + + ~EnsureMetadataTask() = default; public slots: bool abort() override; @@ -20,22 +24,31 @@ class EnsureMetadataTask : public Task { private: // FIXME: Move to their own namespace - void modrinthEnsureMetadata(SequentialTask&, QByteArray&); - void flameEnsureMetadata(SequentialTask&, QByteArray&); + auto modrinthVersionsTask() -> NetJob::Ptr; + auto modrinthProjectsTask() -> NetJob::Ptr; + + auto flameVersionsTask() -> NetJob::Ptr; + auto flameProjectsTask() -> NetJob::Ptr; // Helpers - void emitReady(); - void emitFail(); + void emitReady(Mod&); + void emitFail(Mod&); + + auto getHash(Mod&) -> QString; + + private slots: + void modrinthCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod&); + void flameCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod&); signals: - void metadataReady(); - void metadataFailed(); + void metadataReady(Mod&); + void metadataFailed(Mod&); private: - Mod& m_mod; - QDir& m_index_dir; + QHash m_mods; + QDir m_index_dir; ModPlatform::Provider m_provider; - bool m_try_all; - MultipleOptionsTask* m_task_handler = nullptr; + QHash m_temp_versions; + NetJob* m_current_task; }; diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp index 12d99029..3658bf8d 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.cpp +++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp @@ -10,7 +10,6 @@ #include "ModDownloadTask.h" static FlameAPI api; -static ModPlatform::ProviderCapabilities ProviderCaps; bool FlameCheckUpdate::abort() { @@ -64,156 +63,112 @@ ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info) return pack; } -/* Check for update: - * - Get latest version available - * - Compare hash of the latest version with the current hash - * - If equal, no updates, else, there's updates, so add to the list - * */ -void FlameCheckUpdate::executeTask() +ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId) { - setStatus(tr("Preparing mods for CurseForge...")); - setProgress(0, 5); - - QHash mappings; + ModPlatform::IndexedVersion ver; - // Create all hashes - std::list murmur_hashes; + QEventLoop loop; - auto best_hash_type = ProviderCaps.hashType(ModPlatform::Provider::FLAME).first(); - for (auto mod : m_mods) { - QByteArray jar_data; + auto get_file_info_job = new NetJob("Flame::GetFileInfoJob", APPLICATION->network()); - try { - jar_data = FS::read(mod.fileinfo().absoluteFilePath()); - } catch (FS::FileSystemException& e) { - qCritical() << QString("Failed to open / read JAR file of %1").arg(mod.name()); - qCritical() << QString("Reason: ") << e.cause(); + auto response = new QByteArray(); + auto url = QString("https://api.curseforge.com/v1/mods/%1/files/%2").arg(QString::number(addonId), QString::number(fileId)); + auto dl = Net::Download::makeByteArray(url, response); + get_file_info_job->addNetAction(dl); - failed(e.what()); + QObject::connect(get_file_info_job, &NetJob::succeeded, [response, &ver]() { + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from FlameCheckUpdate at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *response; return; } - QByteArray jar_data_treated; - for (char c : jar_data) { - // CF-specific - if (!(c == 9 || c == 10 || c == 13 || c == 32)) - jar_data_treated.push_back(c); + try { + auto doc_obj = Json::requireObject(doc); + auto data_obj = Json::requireObject(doc_obj, "data"); + ver = FlameMod::loadIndexedPackVersion(data_obj); + } catch (Json::JsonException& e) { + qWarning() << e.cause(); + qDebug() << doc; } + }); - auto murmur_hash = MurmurHash2(jar_data_treated, jar_data_treated.length()); - murmur_hashes.emplace_back(murmur_hash); + QObject::connect(get_file_info_job, &NetJob::finished, [&loop, get_file_info_job] { + get_file_info_job->deleteLater(); + loop.quit(); + }); - mappings.insert(mod.metadata()->mod_id().toInt(), mod); - } + get_file_info_job->start(); + loop.exec(); - auto* response = new QByteArray(); - auto job = api.matchFingerprints(murmur_hashes, response); + return ver; +} - QEventLoop lock; +/* Check for update: + * - Get latest version available + * - Compare hash of the latest version with the current hash + * - If equal, no updates, else, there's updates, so add to the list + * */ +void FlameCheckUpdate::executeTask() +{ + setStatus(tr("Preparing mods for CurseForge...")); - connect(job.get(), &Task::succeeded, this, [this, response, &mappings] { - QJsonParseError parse_error{}; - QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); - if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response from FlameCheckUpdate at " << parse_error.offset - << " reason: " << parse_error.errorString(); - qWarning() << *response; + int i = 0; + for (auto mod : m_mods) { + setStatus(tr("Getting API response from CurseForge for '%1'").arg(mod.name())); + setProgress(i++, m_mods.size()); + + auto latest_ver = api.getLatestVersion({ mod.metadata()->project_id.toString(), m_game_versions, m_loaders }); - failed(parse_error.errorString()); + // Check if we were aborted while getting the latest version + if (m_was_aborted) { + aborted(); return; } - setStatus(tr("Parsing the first API response from CurseForge...")); - setProgress(2, 5); - - try { - auto doc_obj = Json::requireObject(doc); - auto data_obj = Json::ensureObject(doc_obj, "data"); - auto match_arr = Json::ensureArray(data_obj, "exactMatches"); - for (auto match : match_arr) { - auto match_obj = Json::ensureObject(match); - - ModPlatform::IndexedVersion current_ver; - try { - auto file_obj = Json::requireObject(match_obj, "file"); - current_ver = FlameMod::loadIndexedPackVersion(file_obj); - } catch (Json::JsonException& e) { - qCritical() << "Error while parsing Flame indexed version"; - qCritical() << e.what(); - failed(tr("An error occured while parsing a CurseForge indexed version!")); - return; - } - - auto mod_iter = mappings.find(current_ver.addonId.toInt()); - if (mod_iter == mappings.end()) { - qCritical() << "Failed to remap mod from Flame!"; - qDebug() << match_obj; - continue; - } - - auto mod = mod_iter.value(); - - setStatus(tr("Waiting for the API response from CurseForge for '%1'...").arg(mod.name())); - setProgress(3, 5); - - auto latest_ver = api.getLatestVersion({ current_ver.addonId.toString(), m_game_versions, m_loaders }); - - // Check if we were aborted while getting the latest version - if (m_was_aborted) { - aborted(); - return; - } - - setStatus(tr("Parsing the API response from CurseForge for '%1'...").arg(mod.name())); - setProgress(4, 5); - - if (!latest_ver.addonId.isValid()) { - emit checkFailed( - mod, - tr("No valid version found for this mod. It's probably unavailable for the current game version / mod loader.")); - continue; - } - - if (latest_ver.downloadUrl.isEmpty() && latest_ver.fileId != current_ver.fileId) { - auto pack = getProjectInfo(latest_ver); - auto recover_url = QString("%1/download/%2").arg(pack.websiteUrl, latest_ver.fileId.toString()); - emit checkFailed(mod, tr("Mod has a new update available, but is opted-out on CurseForge"), recover_url); - - continue; - } - - if (!latest_ver.hash.isEmpty() && (current_ver.hash != latest_ver.hash || mod.status() == ModStatus::NotInstalled)) { - // Fake pack with the necessary info to pass to the download task :) - ModPlatform::IndexedPack pack; - pack.name = mod.name(); - pack.addonId = mod.metadata()->project_id; - pack.websiteUrl = mod.homeurl(); - for (auto& author : mod.authors()) - pack.authors.append({ author }); - pack.description = mod.description(); - pack.provider = ModPlatform::Provider::FLAME; - - auto download_task = new ModDownloadTask(pack, latest_ver, m_mods_folder); - m_updatable.emplace_back(mod.name(), current_ver.hash, current_ver.version, latest_ver.version, - api.getModFileChangelog(latest_ver.addonId.toInt(), latest_ver.fileId.toInt()), - ModPlatform::Provider::FLAME, download_task); - } - } + setStatus(tr("Parsing the API response from CurseForge for '%1'...").arg(mod.name())); - } catch (Json::JsonException& e) { - failed(e.cause() + " : " + e.what()); + if (!latest_ver.addonId.isValid()) { + emit checkFailed(mod, tr("No valid version found for this mod. It's probably unavailable for the current game " + "version / mod loader.")); + continue; } - }); - connect(job.get(), &Task::finished, &lock, &QEventLoop::quit); + if (latest_ver.downloadUrl.isEmpty() && latest_ver.fileId != mod.metadata()->file_id) { + auto pack = getProjectInfo(latest_ver); + auto recover_url = QString("%1/download/%2").arg(pack.websiteUrl, latest_ver.fileId.toString()); + emit checkFailed(mod, tr("Mod has a new update available, but is opted-out on CurseForge"), recover_url); - setStatus(tr("Waiting for the first API response from CurseForge...")); - setProgress(1, 5); + continue; + } - m_net_job = job.get(); - job->start(); + if (!latest_ver.hash.isEmpty() && (mod.metadata()->hash != latest_ver.hash || mod.status() == ModStatus::NotInstalled)) { + // Fake pack with the necessary info to pass to the download task :) + ModPlatform::IndexedPack pack; + pack.name = mod.name(); + pack.slug = mod.metadata()->slug; + pack.addonId = mod.metadata()->project_id; + pack.websiteUrl = mod.homeurl(); + for (auto& author : mod.authors()) + pack.authors.append({ author }); + pack.description = mod.description(); + pack.provider = ModPlatform::Provider::FLAME; + + auto old_version = mod.version(); + if (old_version.isEmpty() && mod.status() != ModStatus::NotInstalled) { + auto current_ver = getFileInfo(latest_ver.addonId.toInt(), mod.metadata()->file_id.toInt()); + old_version = current_ver.version; + } - lock.exec(); + auto download_task = new ModDownloadTask(pack, latest_ver, m_mods_folder); + m_updatable.emplace_back(mod.name(), mod.metadata()->hash, old_version, latest_ver.version, + api.getModFileChangelog(latest_ver.addonId.toInt(), latest_ver.fileId.toInt()), + ModPlatform::Provider::FLAME, download_task); + } + } emitSucceeded(); } diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index 981c4216..78275cf0 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -88,7 +88,9 @@ void ModrinthCheckUpdate::executeTask() qDebug() << "Mod " << mappings.find(hash).value().name() << " got an empty response."; qDebug() << "Hash: " << hash; - emit checkFailed(mappings.find(hash).value(), tr("Couldn't find the latest version of this mod with the correct mod loader and game version.")); + emit checkFailed( + mappings.find(hash).value(), + tr("No valid version found for this mod. It's probably unavailable for the current game version / mod loader.")); continue; } @@ -134,6 +136,7 @@ void ModrinthCheckUpdate::executeTask() // Fake pack with the necessary info to pass to the download task :) ModPlatform::IndexedPack pack; pack.name = mod.name(); + pack.slug = mod.metadata()->slug; pack.addonId = mod.metadata()->project_id; pack.websiteUrl = mod.homeurl(); for (auto& author : mod.authors()) diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index a4d83483..7584621a 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -34,12 +34,13 @@ static ModAPI::ModLoaderTypes mcLoaders(BaseInstance* inst) ModUpdateDialog::ModUpdateDialog(QWidget* parent, BaseInstance* instance, - const std::shared_ptr& mods, + const std::shared_ptr mods, std::list& search_for) : ReviewMessageBox(parent, tr("Confirm mods to update"), "") , m_parent(parent) , m_mod_model(mods) , m_candidates(search_for) + , m_second_try_metadata(new SequentialTask()) , m_instance(instance) { ReviewMessageBox::setGeometry(0, 0, 800, 600); @@ -47,15 +48,6 @@ ModUpdateDialog::ModUpdateDialog(QWidget* parent, ui->explainLabel->setText(tr("You're about to update the following mods:")); ui->onlyCheckedLabel->setText(tr("Only mods with a check will be updated!")); - connect(&m_check_task, &Task::failed, this, - [&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); - - connect(&m_check_task, &Task::succeeded, this, [&]() { - QStringList warnings = m_check_task.warnings(); - if (warnings.count()) { - CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->exec(); - } - }); } void ModUpdateDialog::checkCandidates() @@ -89,26 +81,39 @@ void ModUpdateDialog::checkCandidates() auto versions = mcVersions(m_instance); auto loaders = mcLoaders(m_instance); + SequentialTask check_task (m_parent, tr("Checking for updates")); + if (!m_modrinth_to_update.empty()) { m_modrinth_check_task = new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_mod_model); connect(m_modrinth_check_task, &CheckUpdateTask::checkFailed, this, [this](Mod mod, QString reason, QUrl recover_url) { m_failed_check_update.emplace_back(mod, reason, recover_url); }); - m_check_task.addTask(m_modrinth_check_task); + check_task.addTask(m_modrinth_check_task); } if (!m_flame_to_update.empty()) { m_flame_check_task = new FlameCheckUpdate(m_flame_to_update, versions, loaders, m_mod_model); connect(m_flame_check_task, &CheckUpdateTask::checkFailed, this, [this](Mod mod, QString reason, QUrl recover_url) { m_failed_check_update.emplace_back(mod, reason, recover_url); }); - m_check_task.addTask(m_flame_check_task); + check_task.addTask(m_flame_check_task); } + connect(&check_task, &Task::failed, this, + [&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); + + connect(&check_task, &Task::succeeded, this, [&]() { + QStringList warnings = check_task.warnings(); + if (warnings.count()) { + CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->exec(); + } + }); + // Check for updates + // FIXME: SOMEHOW THIS IS NOT MODAL??????? ProgressDialog progress_dialog(m_parent); progress_dialog.setSkipButton(true, tr("Abort")); progress_dialog.setVisible(true); progress_dialog.setWindowTitle(tr("Checking for updates...")); - auto ret = progress_dialog.execWithTask(&m_check_task); + auto ret = progress_dialog.execWithTask(&check_task); // If the dialog was skipped / some download error happened if (ret == QDialog::DialogCode::Rejected) { @@ -183,13 +188,29 @@ auto ModUpdateDialog::ensureMetadata() -> bool { auto index_dir = indexDir(); - auto* seq = new SequentialTask(m_parent, tr("Looking for metadata")); + SequentialTask seq(m_parent, tr("Looking for metadata")); + + // A better use of data structures here could remove the need for this QHash + QHash should_try_others; + std::list modrinth_tmp; + std::list flame_tmp; bool confirm_rest = false; bool try_others_rest = false; bool skip_rest = false; ModPlatform::Provider provider_rest = ModPlatform::Provider::MODRINTH; + auto addToTmp = [&](Mod& m, ModPlatform::Provider p) { + switch (p) { + case ModPlatform::Provider::MODRINTH: + modrinth_tmp.push_back(m); + break; + case ModPlatform::Provider::FLAME: + flame_tmp.push_back(m); + break; + } + }; + for (auto& candidate : m_candidates) { if (candidate.status() != ModStatus::NoMetadata) { onMetadataEnsured(candidate); @@ -200,10 +221,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool continue; if (confirm_rest) { - auto* task = new EnsureMetadataTask(candidate, index_dir, try_others_rest, provider_rest); - connect(task, &EnsureMetadataTask::metadataReady, [this, &candidate] { onMetadataEnsured(candidate); }); - connect(task, &EnsureMetadataTask::metadataFailed, [this, &candidate] { onMetadataFailed(candidate); }); - seq->addTask(task); + addToTmp(candidate, provider_rest); + should_try_others.insert(candidate.internal_id(), try_others_rest); continue; } @@ -224,18 +243,36 @@ auto ModUpdateDialog::ensureMetadata() -> bool try_others_rest = response.try_others; } - if (confirmed) { - auto* task = new EnsureMetadataTask(candidate, index_dir, response.try_others, response.chosen); - connect(task, &EnsureMetadataTask::metadataReady, [this, &candidate] { onMetadataEnsured(candidate); }); - connect(task, &EnsureMetadataTask::metadataFailed, [this, &candidate] { onMetadataFailed(candidate); }); - seq->addTask(task); - } + should_try_others.insert(candidate.internal_id(), response.try_others); + + if (confirmed) + addToTmp(candidate, response.chosen); } + if (!modrinth_tmp.empty()) { + auto* modrinth_task = new EnsureMetadataTask(modrinth_tmp, index_dir, ModPlatform::Provider::MODRINTH); + connect(modrinth_task, &EnsureMetadataTask::metadataReady, [this](Mod& candidate) { onMetadataEnsured(candidate); }); + connect(modrinth_task, &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod& candidate) { + onMetadataFailed(candidate, should_try_others.find(candidate.internal_id()).value(), ModPlatform::Provider::MODRINTH); + }); + seq.addTask(modrinth_task); + } + + if (!flame_tmp.empty()) { + auto* flame_task = new EnsureMetadataTask(flame_tmp, index_dir, ModPlatform::Provider::FLAME); + connect(flame_task, &EnsureMetadataTask::metadataReady, [this](Mod& candidate) { onMetadataEnsured(candidate); }); + connect(flame_task, &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod& candidate) { + onMetadataFailed(candidate, should_try_others.find(candidate.internal_id()).value(), ModPlatform::Provider::FLAME); + }); + seq.addTask(flame_task); + } + + seq.addTask(m_second_try_metadata); + ProgressDialog checking_dialog(m_parent); checking_dialog.setSkipButton(true, tr("Abort")); checking_dialog.setWindowTitle(tr("Generating metadata...")); - auto ret_metadata = checking_dialog.execWithTask(seq); + auto ret_metadata = checking_dialog.execWithTask(&seq); return (ret_metadata != QDialog::DialogCode::Rejected); } @@ -256,9 +293,31 @@ void ModUpdateDialog::onMetadataEnsured(Mod& mod) } } -void ModUpdateDialog::onMetadataFailed(Mod& mod) +ModPlatform::Provider next(ModPlatform::Provider p) { - m_failed_metadata.push_back(mod); + switch (p) { + case ModPlatform::Provider::MODRINTH: + return ModPlatform::Provider::FLAME; + case ModPlatform::Provider::FLAME: + return ModPlatform::Provider::MODRINTH; + } + + return ModPlatform::Provider::FLAME; +} + +void ModUpdateDialog::onMetadataFailed(Mod& mod, bool try_others, ModPlatform::Provider first_choice) +{ + if (try_others) { + auto index_dir = indexDir(); + + auto* task = new EnsureMetadataTask(mod, index_dir, next(first_choice)); + connect(task, &EnsureMetadataTask::metadataReady, [this](Mod& candidate) { onMetadataEnsured(candidate); }); + connect(task, &EnsureMetadataTask::metadataFailed, [this](Mod& candidate) { onMetadataFailed(candidate, false); }); + + m_second_try_metadata->addTask(task); + } else { + m_failed_metadata.push_back(mod); + } } void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info) diff --git a/launcher/ui/dialogs/ModUpdateDialog.h b/launcher/ui/dialogs/ModUpdateDialog.h index 30cd5cbd..f40fc594 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.h +++ b/launcher/ui/dialogs/ModUpdateDialog.h @@ -17,7 +17,7 @@ class ModUpdateDialog final : public ReviewMessageBox { public: explicit ModUpdateDialog(QWidget* parent, BaseInstance* instance, - const std::shared_ptr& mod_model, + const std::shared_ptr mod_model, std::list& search_for); void checkCandidates(); @@ -35,21 +35,21 @@ class ModUpdateDialog final : public ReviewMessageBox { private slots: void onMetadataEnsured(Mod&); - void onMetadataFailed(Mod&); + void onMetadataFailed(Mod&, bool try_others = false, ModPlatform::Provider first_choice = ModPlatform::Provider::MODRINTH); private: QWidget* m_parent; - SequentialTask m_check_task; ModrinthCheckUpdate* m_modrinth_check_task = nullptr; FlameCheckUpdate* m_flame_check_task = nullptr; - const std::shared_ptr& m_mod_model; + const std::shared_ptr m_mod_model; std::list& m_candidates; std::list m_modrinth_to_update; std::list m_flame_to_update; + SequentialTask* m_second_try_metadata; std::list m_failed_metadata; std::list> m_failed_check_update; -- cgit From c4316e81e64ad4ac63b0b50106b324a73abdc150 Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 26 Jun 2022 14:17:15 -0300 Subject: change: make Mod a QObject used as a pointer Prevents problems when copying it around! Signed-off-by: flow --- launcher/MMCZip.cpp | 30 ++++---- launcher/MMCZip.h | 2 +- launcher/minecraft/MinecraftInstance.cpp | 24 +++---- launcher/minecraft/MinecraftInstance.h | 2 +- launcher/minecraft/mod/Mod.h | 6 +- launcher/minecraft/mod/ModFolderModel.cpp | 76 ++++++++++---------- launcher/minecraft/mod/ModFolderModel.h | 16 ++--- launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp | 32 ++++----- launcher/minecraft/mod/tasks/ModFolderLoadTask.h | 2 +- launcher/modplatform/CheckUpdateTask.h | 6 +- launcher/modplatform/EnsureMetadataTask.cpp | 80 ++++++++++++---------- launcher/modplatform/EnsureMetadataTask.h | 20 +++--- launcher/modplatform/flame/FlameCheckUpdate.cpp | 34 ++++----- launcher/modplatform/flame/FlameCheckUpdate.h | 2 +- .../modplatform/modrinth/ModrinthCheckUpdate.cpp | 34 ++++----- .../modplatform/modrinth/ModrinthCheckUpdate.h | 2 +- launcher/ui/dialogs/ModUpdateDialog.cpp | 59 ++++++++-------- launcher/ui/dialogs/ModUpdateDialog.h | 16 ++--- launcher/ui/pages/instance/ModFolderPage.cpp | 2 +- 19 files changed, 229 insertions(+), 216 deletions(-) (limited to 'launcher/modplatform/EnsureMetadataTask.h') diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index f20d6dff..1627ee07 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -127,7 +127,7 @@ bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList } // ours -bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList& mods) +bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList& mods) { QuaZip zipOut(targetJarPath); if (!zipOut.open(QuaZip::mdCreate)) @@ -141,42 +141,40 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QSet addedFiles; // Modify the jar - QListIterator i(mods); - i.toBack(); - while (i.hasPrevious()) + for (auto i = mods.constEnd(); i != mods.constBegin(); --i) { - const Mod &mod = i.previous(); + const Mod* mod = *i; // do not merge disabled mods. - if (!mod.enabled()) + if (!mod->enabled()) continue; - if (mod.type() == Mod::MOD_ZIPFILE) + if (mod->type() == Mod::MOD_ZIPFILE) { - if (!mergeZipFiles(&zipOut, mod.fileinfo(), addedFiles)) + if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles)) { zipOut.close(); QFile::remove(targetJarPath); - qCritical() << "Failed to add" << mod.fileinfo().fileName() << "to the jar."; + qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; return false; } } - else if (mod.type() == Mod::MOD_SINGLEFILE) + else if (mod->type() == Mod::MOD_SINGLEFILE) { // FIXME: buggy - does not work with addedFiles - auto filename = mod.fileinfo(); + auto filename = mod->fileinfo(); if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) { zipOut.close(); QFile::remove(targetJarPath); - qCritical() << "Failed to add" << mod.fileinfo().fileName() << "to the jar."; + qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; return false; } addedFiles.insert(filename.fileName()); } - else if (mod.type() == Mod::MOD_FOLDER) + else if (mod->type() == Mod::MOD_FOLDER) { // untested, but seems to be unused / not possible to reach // FIXME: buggy - does not work with addedFiles - auto filename = mod.fileinfo(); + auto filename = mod->fileinfo(); QString what_to_zip = filename.absoluteFilePath(); QDir dir(what_to_zip); dir.cdUp(); @@ -193,7 +191,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const { zipOut.close(); QFile::remove(targetJarPath); - qCritical() << "Failed to add" << mod.fileinfo().fileName() << "to the jar."; + qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; return false; } qDebug() << "Adding folder " << filename.fileName() << " from " @@ -204,7 +202,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const // Make sure we do not continue launching when something is missing or undefined... zipOut.close(); QFile::remove(targetJarPath); - qCritical() << "Failed to add unknown mod type" << mod.fileinfo().fileName() << "to the jar."; + qCritical() << "Failed to add unknown mod type" << mod->fileinfo().fileName() << "to the jar."; return false; } } diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h index bf90cd0b..7f43d158 100644 --- a/launcher/MMCZip.h +++ b/launcher/MMCZip.h @@ -75,7 +75,7 @@ namespace MMCZip /** * take a source jar, add mods to it, resulting in target jar */ - bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList& mods); + bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList& mods); /** * Find a single file in archive by file name (not path) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index abc022b6..360e754d 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -700,24 +700,24 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr { out << QString("%1:").arg(label); auto modList = model.allMods(); - std::sort(modList.begin(), modList.end(), [](Mod &a, Mod &b) { - auto aName = a.fileinfo().completeBaseName(); - auto bName = b.fileinfo().completeBaseName(); + std::sort(modList.begin(), modList.end(), [](Mod::Ptr a, Mod::Ptr b) { + auto aName = a->fileinfo().completeBaseName(); + auto bName = b->fileinfo().completeBaseName(); return aName.localeAwareCompare(bName) < 0; }); - for(auto & mod: modList) + for(auto mod: modList) { - if(mod.type() == Mod::MOD_FOLDER) + if(mod->type() == Mod::MOD_FOLDER) { - out << u8" [📁] " + mod.fileinfo().completeBaseName() + " (folder)"; + out << u8" [📁] " + mod->fileinfo().completeBaseName() + " (folder)"; continue; } - if(mod.enabled()) { - out << u8" [✔️] " + mod.fileinfo().completeBaseName(); + if(mod->enabled()) { + out << u8" [✔️]" + mod->fileinfo().completeBaseName(); } else { - out << u8" [❌] " + mod.fileinfo().completeBaseName() + " (disabled)"; + out << u8" [❌] " + mod->fileinfo().completeBaseName() + " (disabled)"; } } @@ -1136,16 +1136,16 @@ std::shared_ptr MinecraftInstance::gameOptionsModel() const return m_game_options; } -QList< Mod > MinecraftInstance::getJarMods() const +QList MinecraftInstance::getJarMods() const { auto profile = m_components->getProfile(); - QList mods; + QList mods; for (auto jarmod : profile->getJarMods()) { QStringList jar, temp1, temp2, temp3; jarmod->getApplicableFiles(currentSystem, jar, temp1, temp2, temp3, jarmodsPath().absolutePath()); // QString filePath = jarmodsPath().absoluteFilePath(jarmod->filename(currentSystem)); - mods.push_back(Mod(QFileInfo(jar[0]))); + mods.push_back(new Mod(QFileInfo(jar[0]))); } return mods; } diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index 05450d41..8e1c67f2 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -81,7 +81,7 @@ public: shared_qobject_ptr createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override; QStringList extraArguments() const override; QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override; - QList getJarMods() const; + QList getJarMods() const; QString createLaunchScript(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin); /// get arguments passed to java QStringList javaArguments() const; diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index cbbbd362..3d3becd7 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -39,10 +39,12 @@ #include #include +#include "QObjectPtr.h" #include "ModDetails.h" -class Mod +class Mod : public QObject { + Q_OBJECT public: enum ModType { @@ -53,6 +55,8 @@ public: MOD_LITEMOD, //!< The mod is a litemod }; + using Ptr = shared_qobject_ptr; + Mod() = default; Mod(const QFileInfo &file); explicit Mod(const QDir& mods_dir, const Metadata::ModStruct& metadata); diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index d8170067..e0391c01 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -134,7 +134,7 @@ void ModFolderModel::finishUpdate() QSet newSet(newList.begin(), newList.end()); #else QSet currentSet = modsIndex.keys().toSet(); - auto & newMods = m_update->mods; + auto& newMods = m_update->mods; QSet newSet = newMods.keys().toSet(); #endif @@ -142,19 +142,20 @@ void ModFolderModel::finishUpdate() { QSet kept = currentSet; kept.intersect(newSet); - for(auto & keptMod: kept) { - auto & newMod = newMods[keptMod]; + for(auto& keptMod : kept) { + auto* newMod = newMods[keptMod]; auto row = modsIndex[keptMod]; - auto & currentMod = mods[row]; - if(newMod.dateTimeChanged() == currentMod.dateTimeChanged()) { + auto currentMod = mods[row]; + if(newMod->dateTimeChanged() == currentMod->dateTimeChanged()) { // no significant change, ignore... continue; } - auto & oldMod = mods[row]; - if(oldMod.isResolving()) { - activeTickets.remove(oldMod.resolutionTicket()); + auto oldMod = mods[row]; + if(oldMod->isResolving()) { + activeTickets.remove(oldMod->resolutionTicket()); } - oldMod = newMod; + + mods[row] = newMod; resolveMod(mods[row]); emit dataChanged(index(row, 0), index(row, columnCount(QModelIndex()) - 1)); } @@ -173,9 +174,10 @@ void ModFolderModel::finishUpdate() int removedIndex = *iter; beginRemoveRows(QModelIndex(), removedIndex, removedIndex); auto removedIter = mods.begin() + removedIndex; - if(removedIter->isResolving()) { - activeTickets.remove(removedIter->resolutionTicket()); + if((*removedIter)->isResolving()) { + activeTickets.remove((*removedIter)->resolutionTicket()); } + mods.erase(removedIter); endRemoveRows(); } @@ -201,8 +203,8 @@ void ModFolderModel::finishUpdate() { modsIndex.clear(); int idx = 0; - for(auto & mod: mods) { - modsIndex[mod.internal_id()] = idx; + for(auto mod: mods) { + modsIndex[mod->internal_id()] = idx; idx++; } } @@ -217,17 +219,17 @@ void ModFolderModel::finishUpdate() } } -void ModFolderModel::resolveMod(Mod& m) +void ModFolderModel::resolveMod(Mod::Ptr m) { - if(!m.shouldResolve()) { + if(!m->shouldResolve()) { return; } - auto task = new LocalModParseTask(nextResolutionTicket, m.type(), m.fileinfo()); + auto task = new LocalModParseTask(nextResolutionTicket, m->type(), m->fileinfo()); auto result = task->result(); - result->id = m.internal_id(); + result->id = m->internal_id(); activeTickets.insert(nextResolutionTicket, result); - m.setResolving(true, nextResolutionTicket); + m->setResolving(true, nextResolutionTicket); nextResolutionTicket++; QThreadPool *threadPool = QThreadPool::globalInstance(); connect(task, &LocalModParseTask::finished, this, &ModFolderModel::finishModParse); @@ -243,8 +245,8 @@ void ModFolderModel::finishModParse(int token) auto result = *iter; activeTickets.remove(token); int row = modsIndex[result->id]; - auto & mod = mods[row]; - mod.finishResolvingWithDetails(result->details); + auto mod = mods[row]; + mod->finishResolvingWithDetails(result->details); emit dataChanged(index(row), index(row, columnCount(QModelIndex()) - 1)); } @@ -269,9 +271,9 @@ bool ModFolderModel::isValid() return m_dir.exists() && m_dir.isReadable(); } -auto ModFolderModel::selectedMods(QModelIndexList& indexes) -> std::list +auto ModFolderModel::selectedMods(QModelIndexList& indexes) -> std::list { - std::list selected_mods; + std::list selected_mods; for (auto i : indexes) { if(i.column() != 0) continue; @@ -370,9 +372,9 @@ bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadat { for(auto mod : allMods()){ - if(mod.fileinfo().fileName() == filename){ + if(mod->fileinfo().fileName() == filename){ auto index_dir = indexDir(); - mod.destroy(index_dir, preserve_metadata); + mod->destroy(index_dir, preserve_metadata); return true; } } @@ -413,9 +415,9 @@ bool ModFolderModel::deleteMods(const QModelIndexList& indexes) if(i.column() != 0) { continue; } - Mod &m = mods[i.row()]; + auto m = mods[i.row()]; auto index_dir = indexDir(); - m.destroy(index_dir); + m->destroy(index_dir); } return true; } @@ -442,9 +444,9 @@ QVariant ModFolderModel::data(const QModelIndex &index, int role) const switch (column) { case NameColumn: - return mods[row].name(); + return mods[row]->name(); case VersionColumn: { - switch(mods[row].type()) { + switch(mods[row]->type()) { case Mod::MOD_FOLDER: return tr("Folder"); case Mod::MOD_SINGLEFILE: @@ -452,23 +454,23 @@ QVariant ModFolderModel::data(const QModelIndex &index, int role) const default: break; } - return mods[row].version(); + return mods[row]->version(); } case DateColumn: - return mods[row].dateTimeChanged(); + return mods[row]->dateTimeChanged(); default: return QVariant(); } case Qt::ToolTipRole: - return mods[row].internal_id(); + return mods[row]->internal_id(); case Qt::CheckStateRole: switch (column) { case ActiveColumn: - return mods[row].enabled() ? Qt::Checked : Qt::Unchecked; + return mods[row]->enabled() ? Qt::Checked : Qt::Unchecked; default: return QVariant(); } @@ -508,20 +510,20 @@ bool ModFolderModel::setModStatus(int row, ModFolderModel::ModStatusAction actio break; case Toggle: default: - desiredStatus = !mod.enabled(); + desiredStatus = !mod->enabled(); break; } - if(desiredStatus == mod.enabled()) { + if(desiredStatus == mod->enabled()) { return true; } // preserve the row, but change its ID - auto oldId = mod.internal_id(); - if(!mod.enable(!mod.enabled())) { + auto oldId = mod->internal_id(); + if(!mod->enable(!mod->enabled())) { return false; } - auto newId = mod.internal_id(); + auto newId = mod->internal_id(); if(modsIndex.contains(newId)) { // NOTE: this could handle a corner case, where we are overwriting a file, because the same 'mod' exists both enabled and disabled // But is it necessary? diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 10289f8d..04681879 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -101,13 +101,13 @@ public: { return size() == 0; } - Mod &operator[](size_t index) + Mod& operator[](size_t index) { - return mods[index]; + return *mods[index]; } - const Mod &at(size_t index) const + const Mod& at(size_t index) const { - return mods.at(index); + return *mods.at(index); } /// Reloads the mod list and returns true if the list changed. @@ -141,12 +141,12 @@ public: return { QString("%1/.index").arg(dir().absolutePath()) }; } - const QList & allMods() + const QList& allMods() { return mods; } - auto selectedMods(QModelIndexList& indexes) -> std::list; + auto selectedMods(QModelIndexList& indexes) -> std::list; public slots: void disableInteraction(bool disabled); @@ -161,7 +161,7 @@ signals: void updateFinished(); private: - void resolveMod(Mod& m); + void resolveMod(Mod::Ptr m); bool setModStatus(int index, ModStatusAction action); protected: @@ -175,5 +175,5 @@ protected: QMap modsIndex; QMap activeTickets; int nextResolutionTicket = 0; - QList mods; + QList mods; }; diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp index 4ffb626a..63a6ca90 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp +++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp @@ -53,33 +53,33 @@ void ModFolderLoadTask::run() // Read JAR files that don't have metadata m_mods_dir.refresh(); for (auto entry : m_mods_dir.entryInfoList()) { - Mod mod(entry); + auto* mod = new Mod(entry); - if (mod.enabled()) { - if (m_result->mods.contains(mod.internal_id())) { - m_result->mods[mod.internal_id()].setStatus(ModStatus::Installed); + if (mod->enabled()) { + if (m_result->mods.contains(mod->internal_id())) { + m_result->mods[mod->internal_id()]->setStatus(ModStatus::Installed); } else { - m_result->mods[mod.internal_id()] = mod; - m_result->mods[mod.internal_id()].setStatus(ModStatus::NoMetadata); + m_result->mods[mod->internal_id()] = mod; + m_result->mods[mod->internal_id()]->setStatus(ModStatus::NoMetadata); } } else { - QString chopped_id = mod.internal_id().chopped(9); + QString chopped_id = mod->internal_id().chopped(9); if (m_result->mods.contains(chopped_id)) { - m_result->mods[mod.internal_id()] = mod; + m_result->mods[mod->internal_id()] = mod; - auto metadata = m_result->mods[chopped_id].metadata(); + auto metadata = m_result->mods[chopped_id]->metadata(); if (metadata) { - mod.setMetadata(*metadata); + mod->setMetadata(*metadata); - m_result->mods[mod.internal_id()].setStatus(ModStatus::Installed); + m_result->mods[mod->internal_id()]->setStatus(ModStatus::Installed); m_result->mods.remove(chopped_id); } } else { - m_result->mods[mod.internal_id()] = mod; - m_result->mods[mod.internal_id()].setStatus(ModStatus::NoMetadata); + m_result->mods[mod->internal_id()] = mod; + m_result->mods[mod->internal_id()]->setStatus(ModStatus::NoMetadata); } } } @@ -97,8 +97,8 @@ void ModFolderLoadTask::getFromMetadata() return; } - Mod mod(m_mods_dir, metadata); - mod.setStatus(ModStatus::NotInstalled); - m_result->mods[mod.internal_id()] = mod; + auto* mod = new Mod(m_mods_dir, metadata); + mod->setStatus(ModStatus::NotInstalled); + m_result->mods[mod->internal_id()] = mod; } } diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.h b/launcher/minecraft/mod/tasks/ModFolderLoadTask.h index 088f873e..7568fdf5 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.h +++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.h @@ -48,7 +48,7 @@ class ModFolderLoadTask : public QObject, public QRunnable Q_OBJECT public: struct Result { - QMap mods; + QMap mods; }; using ResultPtr = std::shared_ptr; ResultPtr result() const { diff --git a/launcher/modplatform/CheckUpdateTask.h b/launcher/modplatform/CheckUpdateTask.h index d96fc340..94ee19b3 100644 --- a/launcher/modplatform/CheckUpdateTask.h +++ b/launcher/modplatform/CheckUpdateTask.h @@ -12,7 +12,7 @@ class CheckUpdateTask : public Task { Q_OBJECT public: - CheckUpdateTask(std::list& mods, std::list& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr mods_folder) + CheckUpdateTask(std::list& mods, std::list& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr mods_folder) : Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder) {}; struct UpdatableMod { @@ -39,10 +39,10 @@ class CheckUpdateTask : public Task { void executeTask() override = 0; signals: - void checkFailed(Mod failed, QString reason, QUrl recover_url = {}); + void checkFailed(Mod* failed, QString reason, QUrl recover_url = {}); protected: - std::list& m_mods; + std::list& m_mods; std::list& m_game_versions; ModAPI::ModLoaderTypes m_loaders; std::shared_ptr m_mods_folder; diff --git a/launcher/modplatform/EnsureMetadataTask.cpp b/launcher/modplatform/EnsureMetadataTask.cpp index 19e44ce0..7c153511 100644 --- a/launcher/modplatform/EnsureMetadataTask.cpp +++ b/launcher/modplatform/EnsureMetadataTask.cpp @@ -19,7 +19,7 @@ static ModPlatform::ProviderCapabilities ProviderCaps; static ModrinthAPI modrinth_api; static FlameAPI flame_api; -EnsureMetadataTask::EnsureMetadataTask(Mod& mod, QDir dir, ModPlatform::Provider prov) : Task(nullptr), m_index_dir(dir), m_provider(prov) +EnsureMetadataTask::EnsureMetadataTask(Mod* mod, QDir dir, ModPlatform::Provider prov) : Task(nullptr), m_index_dir(dir), m_provider(prov) { auto hash = getHash(mod); if (hash.isEmpty()) @@ -28,11 +28,11 @@ EnsureMetadataTask::EnsureMetadataTask(Mod& mod, QDir dir, ModPlatform::Provider m_mods.insert(hash, mod); } -EnsureMetadataTask::EnsureMetadataTask(std::list& mods, QDir dir, ModPlatform::Provider prov) +EnsureMetadataTask::EnsureMetadataTask(std::list& mods, QDir dir, ModPlatform::Provider prov) : Task(nullptr), m_index_dir(dir), m_provider(prov) { - for (auto& mod : mods) { - if (!mod.valid()) { + for (auto* mod : mods) { + if (!mod->valid()) { emitFail(mod); continue; } @@ -47,14 +47,14 @@ EnsureMetadataTask::EnsureMetadataTask(std::list& mods, QDir dir, ModPlatfo } } -QString EnsureMetadataTask::getHash(Mod& mod) +QString EnsureMetadataTask::getHash(Mod* mod) { /* Here we create a mapping hash -> mod, because we need that relationship to parse the API routes */ QByteArray jar_data; try { - jar_data = FS::read(mod.fileinfo().absoluteFilePath()); + jar_data = FS::read(mod->fileinfo().absoluteFilePath()); } catch (FS::FileSystemException& e) { - qCritical() << QString("Failed to open / read JAR file of %1").arg(mod.name()); + qCritical() << QString("Failed to open / read JAR file of %1").arg(mod->name()); qCritical() << QString("Reason: ") << e.cause(); return {}; @@ -95,19 +95,19 @@ void EnsureMetadataTask::executeTask() { setStatus(tr("Checking if mods have metadata...")); - for (auto mod : m_mods) { - if (!mod.valid()) + for (auto* mod : m_mods) { + if (!mod->valid()) continue; // They already have the right metadata :o - if (mod.status() != ModStatus::NoMetadata && mod.metadata() && mod.metadata()->provider == m_provider) { - qDebug() << "Mod" << mod.name() << "already has metadata!"; + if (mod->status() != ModStatus::NoMetadata && mod->metadata() && mod->metadata()->provider == m_provider) { + qDebug() << "Mod" << mod->name() << "already has metadata!"; emitReady(mod); return; } // Folders don't have metadata - if (mod.type() == Mod::MOD_FOLDER) { + if (mod->type() == Mod::MOD_FOLDER) { emitReady(mod); return; } @@ -125,7 +125,7 @@ void EnsureMetadataTask::executeTask() } auto invalidade_leftover = [this] { - QMutableHashIterator mods_iter(m_mods); + QMutableHashIterator mods_iter(m_mods); while (mods_iter.hasNext()) { auto mod = mods_iter.next(); emitFail(mod.value()); @@ -170,23 +170,23 @@ void EnsureMetadataTask::executeTask() setStatus(tr("Requesting metadata information from %1...").arg(ProviderCaps.readableName(m_provider))); else if (!m_mods.empty()) setStatus(tr("Requesting metadata information from %1 for '%2'...") - .arg(ProviderCaps.readableName(m_provider), m_mods.begin().value().name())); + .arg(ProviderCaps.readableName(m_provider), m_mods.begin().value()->name())); m_current_task = version_task.get(); version_task->start(); } -void EnsureMetadataTask::emitReady(Mod& m) +void EnsureMetadataTask::emitReady(Mod* m) { - qDebug() << QString("Generated metadata for %1").arg(m.name()); + qDebug() << QString("Generated metadata for %1").arg(m->name()); emit metadataReady(m); m_mods.remove(getHash(m)); } -void EnsureMetadataTask::emitFail(Mod& m) +void EnsureMetadataTask::emitFail(Mod* m) { - qDebug() << QString("Failed to generate metadata for %1").arg(m.name()); + qDebug() << QString("Failed to generate metadata for %1").arg(m->name()); emit metadataFailed(m); m_mods.remove(getHash(m)); @@ -224,8 +224,8 @@ NetJob::Ptr EnsureMetadataTask::modrinthVersionsTask() try { auto entry = Json::requireObject(entries, hash); - setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(mod.name())); - qDebug() << "Getting version for" << mod.name() << "from Modrinth"; + setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(mod->name())); + qDebug() << "Getting version for" << mod->name() << "from Modrinth"; m_temp_versions.insert(hash, Modrinth::loadIndexedPackVersion(entry)); } catch (Json::JsonException& e) { @@ -284,17 +284,22 @@ NetJob::Ptr EnsureMetadataTask::modrinthProjectsTask() for (auto entry : entries) { auto entry_obj = Json::requireObject(entry); - auto entry_id = Json::requireString(entry_obj, "id"); - auto hash = addonIds.find(entry_id).value(); + ModPlatform::IndexedPack pack; + Modrinth::loadIndexedPack(pack, entry_obj); - auto mod = m_mods.find(hash).value(); + auto hash = addonIds.find(pack.addonId.toString()).value(); - try { - setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(mod.name())); + auto mod_iter = m_mods.find(hash); + if (mod_iter == m_mods.end()) { + qWarning() << "Invalid project id from the API response."; + continue; + } - ModPlatform::IndexedPack pack; - Modrinth::loadIndexedPack(pack, entry_obj); + auto* mod = mod_iter.value(); + + try { + setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(mod->name())); modrinthCallback(pack, m_temp_versions.find(hash).value(), mod); } catch (Json::JsonException& e) { @@ -365,7 +370,7 @@ NetJob::Ptr EnsureMetadataTask::flameVersionsTask() continue; } - setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(mod->name())); + setStatus(tr("Parsing API response from CurseForge for '%1'...").arg((*mod)->name())); m_temp_versions.insert(fingerprint, FlameMod::loadIndexedPackVersion(file_obj)); } @@ -385,7 +390,10 @@ NetJob::Ptr EnsureMetadataTask::flameProjectsTask() for (auto const& hash : m_mods.keys()) { if (m_temp_versions.contains(hash)) { auto const& data = m_temp_versions.find(hash).value(); - addonIds.insert(data.addonId.toString(), hash); + + auto id_str = data.addonId.toString(); + if (!id_str.isEmpty()) + addonIds.insert(data.addonId.toString(), hash); } } @@ -429,7 +437,7 @@ NetJob::Ptr EnsureMetadataTask::flameProjectsTask() auto mod = m_mods.find(hash).value(); try { - setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(mod.name())); + setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(mod->name())); ModPlatform::IndexedPack pack; FlameMod::loadIndexedPack(pack, entry_obj); @@ -451,10 +459,10 @@ NetJob::Ptr EnsureMetadataTask::flameProjectsTask() return proj_task; } -void EnsureMetadataTask::modrinthCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod& mod) +void EnsureMetadataTask::modrinthCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod* mod) { // Prevent file name mismatch - ver.fileName = mod.fileinfo().fileName(); + ver.fileName = mod->fileinfo().fileName(); if (ver.fileName.endsWith(".disabled")) ver.fileName.chop(9); @@ -479,16 +487,16 @@ void EnsureMetadataTask::modrinthCallback(ModPlatform::IndexedPack& pack, ModPla return; } - mod.setMetadata(metadata); + mod->setMetadata(metadata); emitReady(mod); } -void EnsureMetadataTask::flameCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod& mod) +void EnsureMetadataTask::flameCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod* mod) { try { // Prevent file name mismatch - ver.fileName = mod.fileinfo().fileName(); + ver.fileName = mod->fileinfo().fileName(); if (ver.fileName.endsWith(".disabled")) ver.fileName.chop(9); @@ -513,7 +521,7 @@ void EnsureMetadataTask::flameCallback(ModPlatform::IndexedPack& pack, ModPlatfo return; } - mod.setMetadata(metadata); + mod->setMetadata(metadata); emitReady(mod); } catch (Json::JsonException& e) { diff --git a/launcher/modplatform/EnsureMetadataTask.h b/launcher/modplatform/EnsureMetadataTask.h index 880503b9..d40a972e 100644 --- a/launcher/modplatform/EnsureMetadataTask.h +++ b/launcher/modplatform/EnsureMetadataTask.h @@ -12,8 +12,8 @@ class EnsureMetadataTask : public Task { Q_OBJECT public: - EnsureMetadataTask(Mod&, QDir, ModPlatform::Provider = ModPlatform::Provider::MODRINTH); - EnsureMetadataTask(std::list&, QDir, ModPlatform::Provider = ModPlatform::Provider::MODRINTH); + EnsureMetadataTask(Mod*, QDir, ModPlatform::Provider = ModPlatform::Provider::MODRINTH); + EnsureMetadataTask(std::list&, QDir, ModPlatform::Provider = ModPlatform::Provider::MODRINTH); ~EnsureMetadataTask() = default; @@ -31,21 +31,21 @@ class EnsureMetadataTask : public Task { auto flameProjectsTask() -> NetJob::Ptr; // Helpers - void emitReady(Mod&); - void emitFail(Mod&); + void emitReady(Mod*); + void emitFail(Mod*); - auto getHash(Mod&) -> QString; + auto getHash(Mod*) -> QString; private slots: - void modrinthCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod&); - void flameCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod&); + void modrinthCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod*); + void flameCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod*); signals: - void metadataReady(Mod&); - void metadataFailed(Mod&); + void metadataReady(Mod*); + void metadataFailed(Mod*); private: - QHash m_mods; + QHash m_mods; QDir m_index_dir; ModPlatform::Provider m_provider; diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp index be12dee3..68a4589b 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.cpp +++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp @@ -117,16 +117,16 @@ void FlameCheckUpdate::executeTask() setStatus(tr("Preparing mods for CurseForge...")); int i = 0; - for (auto mod : m_mods) { - if (!mod.enabled()) { + for (auto* mod : m_mods) { + if (!mod->enabled()) { emit checkFailed(mod, tr("Disabled mods won't be updated, to prevent mod duplication issues!")); continue; } - setStatus(tr("Getting API response from CurseForge for '%1'").arg(mod.name())); + setStatus(tr("Getting API response from CurseForge for '%1'").arg(mod->name())); setProgress(i++, m_mods.size()); - auto latest_ver = api.getLatestVersion({ mod.metadata()->project_id.toString(), m_game_versions, m_loaders }); + auto latest_ver = api.getLatestVersion({ mod->metadata()->project_id.toString(), m_game_versions, m_loaders }); // Check if we were aborted while getting the latest version if (m_was_aborted) { @@ -134,7 +134,7 @@ void FlameCheckUpdate::executeTask() return; } - setStatus(tr("Parsing the API response from CurseForge for '%1'...").arg(mod.name())); + setStatus(tr("Parsing the API response from CurseForge for '%1'...").arg(mod->name())); if (!latest_ver.addonId.isValid()) { emit checkFailed(mod, tr("No valid version found for this mod. It's probably unavailable for the current game " @@ -142,7 +142,7 @@ void FlameCheckUpdate::executeTask() continue; } - if (latest_ver.downloadUrl.isEmpty() && latest_ver.fileId != mod.metadata()->file_id) { + if (latest_ver.downloadUrl.isEmpty() && latest_ver.fileId != mod->metadata()->file_id) { auto pack = getProjectInfo(latest_ver); auto recover_url = QString("%1/download/%2").arg(pack.websiteUrl, latest_ver.fileId.toString()); emit checkFailed(mod, tr("Mod has a new update available, but is opted-out on CurseForge"), recover_url); @@ -150,26 +150,26 @@ void FlameCheckUpdate::executeTask() continue; } - if (!latest_ver.hash.isEmpty() && (mod.metadata()->hash != latest_ver.hash || mod.status() == ModStatus::NotInstalled)) { + if (!latest_ver.hash.isEmpty() && (mod->metadata()->hash != latest_ver.hash || mod->status() == ModStatus::NotInstalled)) { // Fake pack with the necessary info to pass to the download task :) ModPlatform::IndexedPack pack; - pack.name = mod.name(); - pack.slug = mod.metadata()->slug; - pack.addonId = mod.metadata()->project_id; - pack.websiteUrl = mod.homeurl(); - for (auto& author : mod.authors()) + pack.name = mod->name(); + pack.slug = mod->metadata()->slug; + pack.addonId = mod->metadata()->project_id; + pack.websiteUrl = mod->homeurl(); + for (auto& author : mod->authors()) pack.authors.append({ author }); - pack.description = mod.description(); + pack.description = mod->description(); pack.provider = ModPlatform::Provider::FLAME; - auto old_version = mod.version(); - if (old_version.isEmpty() && mod.status() != ModStatus::NotInstalled) { - auto current_ver = getFileInfo(latest_ver.addonId.toInt(), mod.metadata()->file_id.toInt()); + auto old_version = mod->version(); + if (old_version.isEmpty() && mod->status() != ModStatus::NotInstalled) { + auto current_ver = getFileInfo(latest_ver.addonId.toInt(), mod->metadata()->file_id.toInt()); old_version = current_ver.version; } auto download_task = new ModDownloadTask(pack, latest_ver, m_mods_folder); - m_updatable.emplace_back(mod.name(), mod.metadata()->hash, old_version, latest_ver.version, + m_updatable.emplace_back(pack.name, mod->metadata()->hash, old_version, latest_ver.version, api.getModFileChangelog(latest_ver.addonId.toInt(), latest_ver.fileId.toInt()), ModPlatform::Provider::FLAME, download_task); } diff --git a/launcher/modplatform/flame/FlameCheckUpdate.h b/launcher/modplatform/flame/FlameCheckUpdate.h index f068f08f..0891891d 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.h +++ b/launcher/modplatform/flame/FlameCheckUpdate.h @@ -8,7 +8,7 @@ class FlameCheckUpdate : public CheckUpdateTask { Q_OBJECT public: - FlameCheckUpdate(std::list& mods, std::list& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr mods_folder) + FlameCheckUpdate(std::list& mods, std::list& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr mods_folder) : CheckUpdateTask(mods, mcVersions, loaders, mods_folder) {} diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index 5d936fec..79d8edf7 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -27,29 +27,29 @@ void ModrinthCheckUpdate::executeTask() setStatus(tr("Preparing mods for Modrinth...")); setProgress(0, 3); - QHash mappings; + QHash mappings; // Create all hashes QStringList hashes; auto best_hash_type = ProviderCaps.hashType(ModPlatform::Provider::MODRINTH).first(); - for (auto mod : m_mods) { - if (!mod.enabled()) { + for (auto* mod : m_mods) { + if (!mod->enabled()) { emit checkFailed(mod, tr("Disabled mods won't be updated, to prevent mod duplication issues!")); continue; } - auto hash = mod.metadata()->hash; + auto hash = mod->metadata()->hash; // Sadly the API can only handle one hash type per call, se we // need to generate a new hash if the current one is innadequate // (though it will rarely happen, if at all) - if (mod.metadata()->hash_format != best_hash_type) { + if (mod->metadata()->hash_format != best_hash_type) { QByteArray jar_data; try { - jar_data = FS::read(mod.fileinfo().absoluteFilePath()); + jar_data = FS::read(mod->fileinfo().absoluteFilePath()); } catch (FS::FileSystemException& e) { - qCritical() << QString("Failed to open / read JAR file of %1").arg(mod.name()); + qCritical() << QString("Failed to open / read JAR file of %1").arg(mod->name()); qCritical() << QString("Reason: ") << e.cause(); failed(e.what()); @@ -90,7 +90,7 @@ void ModrinthCheckUpdate::executeTask() // If the returned project is empty, but we have Modrinth metadata, // it means this specific version is not available if (project_obj.isEmpty()) { - qDebug() << "Mod " << mappings.find(hash).value().name() << " got an empty response."; + qDebug() << "Mod " << mappings.find(hash).value()->name() << " got an empty response."; qDebug() << "Hash: " << hash; emit checkFailed( @@ -134,24 +134,24 @@ void ModrinthCheckUpdate::executeTask() auto mod = *mod_iter; auto key = project_ver.hash; - if ((key != hash && project_ver.is_preferred) || (mod.status() == ModStatus::NotInstalled)) { - if (mod.version() == project_ver.version_number) + if ((key != hash && project_ver.is_preferred) || (mod->status() == ModStatus::NotInstalled)) { + if (mod->version() == project_ver.version_number) continue; // Fake pack with the necessary info to pass to the download task :) ModPlatform::IndexedPack pack; - pack.name = mod.name(); - pack.slug = mod.metadata()->slug; - pack.addonId = mod.metadata()->project_id; - pack.websiteUrl = mod.homeurl(); - for (auto& author : mod.authors()) + pack.name = mod->name(); + pack.slug = mod->metadata()->slug; + pack.addonId = mod->metadata()->project_id; + pack.websiteUrl = mod->homeurl(); + for (auto& author : mod->authors()) pack.authors.append({ author }); - pack.description = mod.description(); + pack.description = mod->description(); pack.provider = ModPlatform::Provider::MODRINTH; auto download_task = new ModDownloadTask(pack, project_ver, m_mods_folder); - m_updatable.emplace_back(mod.name(), hash, mod.version(), project_ver.version_number, project_ver.changelog, + m_updatable.emplace_back(pack.name, hash, mod->version(), project_ver.version_number, project_ver.changelog, ModPlatform::Provider::MODRINTH, download_task); } } diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.h b/launcher/modplatform/modrinth/ModrinthCheckUpdate.h index 7e685a6d..d61667f5 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.h +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.h @@ -8,7 +8,7 @@ class ModrinthCheckUpdate : public CheckUpdateTask { Q_OBJECT public: - ModrinthCheckUpdate(std::list& mods, std::list& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr mods_folder) + ModrinthCheckUpdate(std::list& mods, std::list& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr mods_folder) : CheckUpdateTask(mods, mcVersions, loaders, mods_folder) {} diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 51e37bcf..3dc2a85f 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -34,7 +34,7 @@ static ModAPI::ModLoaderTypes mcLoaders(BaseInstance* inst) ModUpdateDialog::ModUpdateDialog(QWidget* parent, BaseInstance* instance, const std::shared_ptr mods, - std::list& search_for) + std::list& search_for) : ReviewMessageBox(parent, tr("Confirm mods to update"), "") , m_parent(parent) , m_mod_model(mods) @@ -63,7 +63,7 @@ void ModUpdateDialog::checkCandidates() for (const auto& failed : m_failed_metadata) { const auto& mod = std::get<0>(failed); const auto& reason = std::get<1>(failed); - text += tr("Mod name: %1
File name: %2
Reason: %3

").arg(mod.name(), mod.fileinfo().fileName(), reason); + text += tr("Mod name: %1
File name: %2
Reason: %3

").arg(mod->name(), mod->fileinfo().fileName(), reason); } ScrollMessageBox message_dialog(m_parent, tr("Metadata generation failed"), @@ -86,14 +86,14 @@ void ModUpdateDialog::checkCandidates() if (!m_modrinth_to_update.empty()) { m_modrinth_check_task = new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_mod_model); connect(m_modrinth_check_task, &CheckUpdateTask::checkFailed, this, - [this](Mod mod, QString reason, QUrl recover_url) { m_failed_check_update.emplace_back(mod, reason, recover_url); }); + [this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.emplace_back(mod, reason, recover_url); }); check_task.addTask(m_modrinth_check_task); } if (!m_flame_to_update.empty()) { m_flame_check_task = new FlameCheckUpdate(m_flame_to_update, versions, loaders, m_mod_model); connect(m_flame_check_task, &CheckUpdateTask::checkFailed, this, - [this](Mod mod, QString reason, QUrl recover_url) { m_failed_check_update.emplace_back(mod, reason, recover_url); }); + [this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.emplace_back(mod, reason, recover_url); }); check_task.addTask(m_flame_check_task); } @@ -152,9 +152,9 @@ void ModUpdateDialog::checkCandidates() const auto& reason = std::get<1>(failed); const auto& recover_url = std::get<2>(failed); - qDebug() << mod.name() << " failed to check for updates!"; + qDebug() << mod->name() << " failed to check for updates!"; - text += tr("Mod name: %1").arg(mod.name()) + "
"; + text += tr("Mod name: %1").arg(mod->name()) + "
"; if (!reason.isEmpty()) text += tr("Reason: %1").arg(reason) + "
"; if (!recover_url.isEmpty()) @@ -205,15 +205,15 @@ auto ModUpdateDialog::ensureMetadata() -> bool // A better use of data structures here could remove the need for this QHash QHash should_try_others; - std::list modrinth_tmp; - std::list flame_tmp; + std::list modrinth_tmp; + std::list flame_tmp; bool confirm_rest = false; bool try_others_rest = false; bool skip_rest = false; ModPlatform::Provider provider_rest = ModPlatform::Provider::MODRINTH; - auto addToTmp = [&](Mod& m, ModPlatform::Provider p) { + auto addToTmp = [&](Mod* m, ModPlatform::Provider p) { switch (p) { case ModPlatform::Provider::MODRINTH: modrinth_tmp.push_back(m); @@ -224,9 +224,10 @@ auto ModUpdateDialog::ensureMetadata() -> bool } }; - for (auto& candidate : m_candidates) { - if (candidate.status() != ModStatus::NoMetadata) { - onMetadataEnsured(candidate); + for (auto candidate : m_candidates) { + auto* candidate_ptr = candidate.get(); + if (candidate->status() != ModStatus::NoMetadata) { + onMetadataEnsured(candidate_ptr); continue; } @@ -234,8 +235,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool continue; if (confirm_rest) { - addToTmp(candidate, provider_rest); - should_try_others.insert(candidate.internal_id(), try_others_rest); + addToTmp(candidate_ptr, provider_rest); + should_try_others.insert(candidate->internal_id(), try_others_rest); continue; } @@ -243,7 +244,7 @@ auto ModUpdateDialog::ensureMetadata() -> bool chooser.setDescription(tr("This mod (%1) does not have a metadata yet. We need to create one in order to keep relevant " "information on how to update this " "mod. To do this, please select a mod provider from which we can search for updates for %1.") - .arg(candidate.name())); + .arg(candidate->name())); auto confirmed = chooser.exec() == QDialog::DialogCode::Accepted; auto response = chooser.getResponse(); @@ -256,26 +257,26 @@ auto ModUpdateDialog::ensureMetadata() -> bool try_others_rest = response.try_others; } - should_try_others.insert(candidate.internal_id(), response.try_others); + should_try_others.insert(candidate->internal_id(), response.try_others); if (confirmed) - addToTmp(candidate, response.chosen); + addToTmp(candidate_ptr, response.chosen); } if (!modrinth_tmp.empty()) { auto* modrinth_task = new EnsureMetadataTask(modrinth_tmp, index_dir, ModPlatform::Provider::MODRINTH); - connect(modrinth_task, &EnsureMetadataTask::metadataReady, [this](Mod& candidate) { onMetadataEnsured(candidate); }); - connect(modrinth_task, &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod& candidate) { - onMetadataFailed(candidate, should_try_others.find(candidate.internal_id()).value(), ModPlatform::Provider::MODRINTH); + connect(modrinth_task, &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); + connect(modrinth_task, &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) { + onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::Provider::MODRINTH); }); seq.addTask(modrinth_task); } if (!flame_tmp.empty()) { auto* flame_task = new EnsureMetadataTask(flame_tmp, index_dir, ModPlatform::Provider::FLAME); - connect(flame_task, &EnsureMetadataTask::metadataReady, [this](Mod& candidate) { onMetadataEnsured(candidate); }); - connect(flame_task, &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod& candidate) { - onMetadataFailed(candidate, should_try_others.find(candidate.internal_id()).value(), ModPlatform::Provider::FLAME); + connect(flame_task, &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); + connect(flame_task, &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) { + onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::Provider::FLAME); }); seq.addTask(flame_task); } @@ -290,13 +291,13 @@ auto ModUpdateDialog::ensureMetadata() -> bool return (ret_metadata != QDialog::DialogCode::Rejected); } -void ModUpdateDialog::onMetadataEnsured(Mod& mod) +void ModUpdateDialog::onMetadataEnsured(Mod* mod) { // When the mod is a folder, for instance - if (!mod.metadata()) + if (!mod->metadata()) return; - switch (mod.metadata()->provider) { + switch (mod->metadata()->provider) { case ModPlatform::Provider::MODRINTH: m_modrinth_to_update.push_back(mod); break; @@ -318,14 +319,14 @@ ModPlatform::Provider next(ModPlatform::Provider p) return ModPlatform::Provider::FLAME; } -void ModUpdateDialog::onMetadataFailed(Mod& mod, bool try_others, ModPlatform::Provider first_choice) +void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::Provider first_choice) { if (try_others) { auto index_dir = indexDir(); auto* task = new EnsureMetadataTask(mod, index_dir, next(first_choice)); - connect(task, &EnsureMetadataTask::metadataReady, [this](Mod& candidate) { onMetadataEnsured(candidate); }); - connect(task, &EnsureMetadataTask::metadataFailed, [this](Mod& candidate) { onMetadataFailed(candidate, false); }); + connect(task, &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); + connect(task, &EnsureMetadataTask::metadataFailed, [this](Mod* candidate) { onMetadataFailed(candidate, false); }); m_second_try_metadata->addTask(task); } else { diff --git a/launcher/ui/dialogs/ModUpdateDialog.h b/launcher/ui/dialogs/ModUpdateDialog.h index 336fbba2..b598447d 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.h +++ b/launcher/ui/dialogs/ModUpdateDialog.h @@ -18,7 +18,7 @@ class ModUpdateDialog final : public ReviewMessageBox { explicit ModUpdateDialog(QWidget* parent, BaseInstance* instance, const std::shared_ptr mod_model, - std::list& search_for); + std::list& search_for); void checkCandidates(); @@ -34,8 +34,8 @@ class ModUpdateDialog final : public ReviewMessageBox { auto ensureMetadata() -> bool; private slots: - void onMetadataEnsured(Mod&); - void onMetadataFailed(Mod&, bool try_others = false, ModPlatform::Provider first_choice = ModPlatform::Provider::MODRINTH); + void onMetadataEnsured(Mod*); + void onMetadataFailed(Mod*, bool try_others = false, ModPlatform::Provider first_choice = ModPlatform::Provider::MODRINTH); private: QWidget* m_parent; @@ -45,13 +45,13 @@ class ModUpdateDialog final : public ReviewMessageBox { const std::shared_ptr m_mod_model; - std::list& m_candidates; - std::list m_modrinth_to_update; - std::list m_flame_to_update; + std::list& m_candidates; + std::list m_modrinth_to_update; + std::list m_flame_to_update; SequentialTask* m_second_try_metadata; - std::list> m_failed_metadata; - std::list> m_failed_check_update; + std::list> m_failed_metadata; + std::list> m_failed_check_update; QHash m_tasks; BaseInstance* m_instance; diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index a40cef77..c21cdda4 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -192,7 +192,7 @@ void ModFolderPage::updateMods() if (update_dialog.noUpdates()) { CustomMessageBox::selectable(this, tr("Update checker"), (mods_list.size() == 1) - ? tr("'%1' is up-to-date! :)").arg(mods_list.front().name()) + ? tr("'%1' is up-to-date! :)").arg(mods_list.front()->name()) : tr("All %1mods are up-to-date! :)").arg(use_all ? "" : (tr("selected") + " "))) ->exec(); return; -- cgit From de9e304236ac0c11dd2b6bfb8b6f55943349c0e9 Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 10 Jul 2022 15:15:25 -0300 Subject: fix: std::list -> QList Qt6 removed Qlist::toStdList() :sob: Signed-off-by: flow --- launcher/minecraft/mod/ModFolderModel.cpp | 4 ++-- launcher/minecraft/mod/ModFolderModel.h | 2 +- launcher/modplatform/CheckUpdateTask.h | 4 ++-- launcher/modplatform/EnsureMetadataTask.cpp | 4 ++-- launcher/modplatform/EnsureMetadataTask.h | 2 +- launcher/modplatform/flame/FlameAPI.cpp | 2 +- launcher/modplatform/flame/FlameAPI.h | 2 +- launcher/modplatform/flame/FlameCheckUpdate.h | 2 +- launcher/modplatform/modrinth/ModrinthCheckUpdate.h | 2 +- launcher/ui/dialogs/ModUpdateDialog.cpp | 16 ++++++++-------- launcher/ui/dialogs/ModUpdateDialog.h | 14 +++++++------- launcher/ui/pages/instance/ModFolderPage.cpp | 2 +- 12 files changed, 28 insertions(+), 28 deletions(-) (limited to 'launcher/modplatform/EnsureMetadataTask.h') diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index e0391c01..c4449b2a 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -271,9 +271,9 @@ bool ModFolderModel::isValid() return m_dir.exists() && m_dir.isReadable(); } -auto ModFolderModel::selectedMods(QModelIndexList& indexes) -> std::list +auto ModFolderModel::selectedMods(QModelIndexList& indexes) -> QList { - std::list selected_mods; + QList selected_mods; for (auto i : indexes) { if(i.column() != 0) continue; diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 04681879..a7d3ece0 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -146,7 +146,7 @@ public: return mods; } - auto selectedMods(QModelIndexList& indexes) -> std::list; + auto selectedMods(QModelIndexList& indexes) -> QList; public slots: void disableInteraction(bool disabled); diff --git a/launcher/modplatform/CheckUpdateTask.h b/launcher/modplatform/CheckUpdateTask.h index 94ee19b3..91922034 100644 --- a/launcher/modplatform/CheckUpdateTask.h +++ b/launcher/modplatform/CheckUpdateTask.h @@ -12,7 +12,7 @@ class CheckUpdateTask : public Task { Q_OBJECT public: - CheckUpdateTask(std::list& mods, std::list& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr mods_folder) + CheckUpdateTask(QList& mods, std::list& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr mods_folder) : Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder) {}; struct UpdatableMod { @@ -42,7 +42,7 @@ class CheckUpdateTask : public Task { void checkFailed(Mod* failed, QString reason, QUrl recover_url = {}); protected: - std::list& m_mods; + QList& m_mods; std::list& m_game_versions; ModAPI::ModLoaderTypes m_loaders; std::shared_ptr m_mods_folder; diff --git a/launcher/modplatform/EnsureMetadataTask.cpp b/launcher/modplatform/EnsureMetadataTask.cpp index 7c153511..f0c1fa99 100644 --- a/launcher/modplatform/EnsureMetadataTask.cpp +++ b/launcher/modplatform/EnsureMetadataTask.cpp @@ -28,7 +28,7 @@ EnsureMetadataTask::EnsureMetadataTask(Mod* mod, QDir dir, ModPlatform::Provider m_mods.insert(hash, mod); } -EnsureMetadataTask::EnsureMetadataTask(std::list& mods, QDir dir, ModPlatform::Provider prov) +EnsureMetadataTask::EnsureMetadataTask(QList& mods, QDir dir, ModPlatform::Provider prov) : Task(nullptr), m_index_dir(dir), m_provider(prov) { for (auto* mod : mods) { @@ -323,7 +323,7 @@ NetJob::Ptr EnsureMetadataTask::flameVersionsTask() { auto* response = new QByteArray(); - std::list fingerprints; + QList fingerprints; for (auto& murmur : m_mods.keys()) { fingerprints.push_back(murmur.toUInt()); } diff --git a/launcher/modplatform/EnsureMetadataTask.h b/launcher/modplatform/EnsureMetadataTask.h index d40a972e..79db6976 100644 --- a/launcher/modplatform/EnsureMetadataTask.h +++ b/launcher/modplatform/EnsureMetadataTask.h @@ -13,7 +13,7 @@ class EnsureMetadataTask : public Task { public: EnsureMetadataTask(Mod*, QDir, ModPlatform::Provider = ModPlatform::Provider::MODRINTH); - EnsureMetadataTask(std::list&, QDir, ModPlatform::Provider = ModPlatform::Provider::MODRINTH); + EnsureMetadataTask(QList&, QDir, ModPlatform::Provider = ModPlatform::Provider::MODRINTH); ~EnsureMetadataTask() = default; diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index e40d84f7..0ff04f72 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -7,7 +7,7 @@ #include "net/Upload.h" -auto FlameAPI::matchFingerprints(const std::list& fingerprints, QByteArray* response) -> NetJob::Ptr +auto FlameAPI::matchFingerprints(const QList& fingerprints, QByteArray* response) -> NetJob::Ptr { auto* netJob = new NetJob(QString("Flame::MatchFingerprints"), APPLICATION->network()); diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index ec6706e5..336df387 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -5,7 +5,7 @@ class FlameAPI : public NetworkModAPI { public: - auto matchFingerprints(const std::list& fingerprints, QByteArray* response) -> NetJob::Ptr; + auto matchFingerprints(const QList& fingerprints, QByteArray* response) -> NetJob::Ptr; auto getModFileChangelog(int modId, int fileId) -> QString; auto getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::IndexedVersion; diff --git a/launcher/modplatform/flame/FlameCheckUpdate.h b/launcher/modplatform/flame/FlameCheckUpdate.h index 0891891d..163c706c 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.h +++ b/launcher/modplatform/flame/FlameCheckUpdate.h @@ -8,7 +8,7 @@ class FlameCheckUpdate : public CheckUpdateTask { Q_OBJECT public: - FlameCheckUpdate(std::list& mods, std::list& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr mods_folder) + FlameCheckUpdate(QList& mods, std::list& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr mods_folder) : CheckUpdateTask(mods, mcVersions, loaders, mods_folder) {} diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.h b/launcher/modplatform/modrinth/ModrinthCheckUpdate.h index d61667f5..abf8ada1 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.h +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.h @@ -8,7 +8,7 @@ class ModrinthCheckUpdate : public CheckUpdateTask { Q_OBJECT public: - ModrinthCheckUpdate(std::list& mods, std::list& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr mods_folder) + ModrinthCheckUpdate(QList& mods, std::list& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr mods_folder) : CheckUpdateTask(mods, mcVersions, loaders, mods_folder) {} diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index c54d178b..b6e76ff1 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -36,7 +36,7 @@ static ModAPI::ModLoaderTypes mcLoaders(BaseInstance* inst) ModUpdateDialog::ModUpdateDialog(QWidget* parent, BaseInstance* instance, const std::shared_ptr mods, - std::list& search_for) + QList& search_for) : ReviewMessageBox(parent, tr("Confirm mods to update"), "") , m_parent(parent) , m_mod_model(mods) @@ -88,14 +88,14 @@ void ModUpdateDialog::checkCandidates() if (!m_modrinth_to_update.empty()) { m_modrinth_check_task = new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_mod_model); connect(m_modrinth_check_task, &CheckUpdateTask::checkFailed, this, - [this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.emplace_back(mod, reason, recover_url); }); + [this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.append({mod, reason, recover_url}); }); check_task.addTask(m_modrinth_check_task); } if (!m_flame_to_update.empty()) { m_flame_check_task = new FlameCheckUpdate(m_flame_to_update, versions, loaders, m_mod_model); connect(m_flame_check_task, &CheckUpdateTask::checkFailed, this, - [this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.emplace_back(mod, reason, recover_url); }); + [this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.append({mod, reason, recover_url}); }); check_task.addTask(m_flame_check_task); } @@ -205,8 +205,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool // A better use of data structures here could remove the need for this QHash QHash should_try_others; - std::list modrinth_tmp; - std::list flame_tmp; + QList modrinth_tmp; + QList flame_tmp; bool confirm_rest = false; bool try_others_rest = false; @@ -332,7 +332,7 @@ void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::P } else { QString reason{ tr("Didn't find a valid version on the selected mod provider(s)") }; - m_failed_metadata.emplace_back(mod, reason); + m_failed_metadata.append({mod, reason}); } } @@ -390,9 +390,9 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info) ui->modTreeWidget->addTopLevelItem(item_top); } -auto ModUpdateDialog::getTasks() -> const std::list +auto ModUpdateDialog::getTasks() -> const QList { - std::list list; + QList list; auto* item = ui->modTreeWidget->topLevelItem(0); diff --git a/launcher/ui/dialogs/ModUpdateDialog.h b/launcher/ui/dialogs/ModUpdateDialog.h index 638f64bc..76aaab36 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.h +++ b/launcher/ui/dialogs/ModUpdateDialog.h @@ -19,13 +19,13 @@ class ModUpdateDialog final : public ReviewMessageBox { explicit ModUpdateDialog(QWidget* parent, BaseInstance* instance, const std::shared_ptr mod_model, - std::list& search_for); + QList& search_for); void checkCandidates(); void appendMod(const CheckUpdateTask::UpdatableMod& info); - const std::list getTasks(); + const QList getTasks(); auto indexDir() const -> QDir { return m_mod_model->indexDir(); } auto noUpdates() const -> bool { return m_no_updates; }; @@ -46,13 +46,13 @@ class ModUpdateDialog final : public ReviewMessageBox { const std::shared_ptr m_mod_model; - std::list& m_candidates; - std::list m_modrinth_to_update; - std::list m_flame_to_update; + QList& m_candidates; + QList m_modrinth_to_update; + QList m_flame_to_update; ConcurrentTask* m_second_try_metadata; - std::list> m_failed_metadata; - std::list> m_failed_check_update; + QList> m_failed_metadata; + QList> m_failed_check_update; QHash m_tasks; BaseInstance* m_instance; diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index c21cdda4..b190e51a 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -180,7 +180,7 @@ void ModFolderPage::updateMods() auto mods_list = m_model->selectedMods(selection); bool use_all = mods_list.empty(); if (use_all) - mods_list = m_model->allMods().toStdList(); + mods_list = m_model->allMods(); ModUpdateDialog update_dialog(this, m_instance, m_model, mods_list); update_dialog.checkCandidates(); -- cgit