feat: add mod update dialog
This subclasses the Review mods dialog to make a "Update review" one. Also, all the necessary components built until now are put together in a coherent unity that checks and generates metadata on-the-fly and checks for mod updates, while giving and receiving feedback to the user. Signed-off-by: flow <flowlnlnln@gmail.com>
This commit is contained in:
parent
b8b71c7dd2
commit
6e2869834f
@ -850,6 +850,8 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/dialogs/ScrollMessageBox.h
|
ui/dialogs/ScrollMessageBox.h
|
||||||
ui/dialogs/ChooseProviderDialog.h
|
ui/dialogs/ChooseProviderDialog.h
|
||||||
ui/dialogs/ChooseProviderDialog.cpp
|
ui/dialogs/ChooseProviderDialog.cpp
|
||||||
|
ui/dialogs/ModUpdateDialog.cpp
|
||||||
|
ui/dialogs/ModUpdateDialog.h
|
||||||
|
|
||||||
# GUI - widgets
|
# GUI - widgets
|
||||||
ui/widgets/Common.cpp
|
ui/widgets/Common.cpp
|
||||||
|
@ -74,16 +74,13 @@ void FlameCheckUpdate::executeTask()
|
|||||||
setStatus(tr("Preparing mods for CurseForge..."));
|
setStatus(tr("Preparing mods for CurseForge..."));
|
||||||
setProgress(0, 5);
|
setProgress(0, 5);
|
||||||
|
|
||||||
QHash<QString, Mod> mappings;
|
QHash<int, Mod> mappings;
|
||||||
|
|
||||||
// Create all hashes
|
// Create all hashes
|
||||||
QStringList hashes;
|
|
||||||
std::list<uint> murmur_hashes;
|
std::list<uint> murmur_hashes;
|
||||||
|
|
||||||
auto best_hash_type = ProviderCaps.hashType(ModPlatform::Provider::FLAME).first();
|
auto best_hash_type = ProviderCaps.hashType(ModPlatform::Provider::FLAME).first();
|
||||||
for (auto mod : m_mods) {
|
for (auto mod : m_mods) {
|
||||||
auto hash = mod.metadata()->hash;
|
|
||||||
|
|
||||||
QByteArray jar_data;
|
QByteArray jar_data;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -106,14 +103,7 @@ void FlameCheckUpdate::executeTask()
|
|||||||
auto murmur_hash = MurmurHash2(jar_data_treated, jar_data_treated.length());
|
auto murmur_hash = MurmurHash2(jar_data_treated, jar_data_treated.length());
|
||||||
murmur_hashes.emplace_back(murmur_hash);
|
murmur_hashes.emplace_back(murmur_hash);
|
||||||
|
|
||||||
// Sadly the API can only handle one hash type per call, se we
|
mappings.insert(mod.metadata()->mod_id().toInt(), mod);
|
||||||
// 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)
|
|
||||||
hash = QString(ProviderCaps.hash(ModPlatform::Provider::FLAME, jar_data, best_hash_type).toHex());
|
|
||||||
|
|
||||||
hashes.append(hash);
|
|
||||||
mappings.insert(hash, mod);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* response = new QByteArray();
|
auto* response = new QByteArray();
|
||||||
@ -154,9 +144,10 @@ void FlameCheckUpdate::executeTask()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mod_iter = mappings.find(current_ver.hash);
|
auto mod_iter = mappings.find(current_ver.addonId.toInt());
|
||||||
if (mod_iter == mappings.end()) {
|
if (mod_iter == mappings.end()) {
|
||||||
qCritical() << "Failed to remap mod from Flame!";
|
qCritical() << "Failed to remap mod from Flame!";
|
||||||
|
qDebug() << match_obj;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,7 +182,7 @@ void FlameCheckUpdate::executeTask()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!latest_ver.hash.isEmpty() && current_ver.hash != latest_ver.hash) {
|
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 :)
|
// Fake pack with the necessary info to pass to the download task :)
|
||||||
ModPlatform::IndexedPack pack;
|
ModPlatform::IndexedPack pack;
|
||||||
pack.name = mod.name();
|
pack.name = mod.name();
|
||||||
|
@ -81,11 +81,14 @@ void ModrinthCheckUpdate::executeTask()
|
|||||||
try {
|
try {
|
||||||
for (auto hash : mappings.keys()) {
|
for (auto hash : mappings.keys()) {
|
||||||
auto project_obj = doc[hash].toObject();
|
auto project_obj = doc[hash].toObject();
|
||||||
|
|
||||||
|
// If the returned project is empty, but we have Modrinth metadata,
|
||||||
|
// it means this specific version is not available
|
||||||
if (project_obj.isEmpty()) {
|
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;
|
qDebug() << "Hash: " << hash;
|
||||||
|
|
||||||
emit checkFailed(mappings.find(hash).value(), tr("Couldn't find mod in Modrinth"));
|
emit checkFailed(mappings.find(hash).value(), tr("Couldn't find the latest version of this mod with the correct mod loader and game version."));
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
317
launcher/ui/dialogs/ModUpdateDialog.cpp
Normal file
317
launcher/ui/dialogs/ModUpdateDialog.cpp
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
#include "ModUpdateDialog.h"
|
||||||
|
#include "ChooseProviderDialog.h"
|
||||||
|
#include "CustomMessageBox.h"
|
||||||
|
#include "ProgressDialog.h"
|
||||||
|
#include "ScrollMessageBox.h"
|
||||||
|
#include "ui_ReviewMessageBox.h"
|
||||||
|
|
||||||
|
#include "FileSystem.h"
|
||||||
|
#include "Json.h"
|
||||||
|
|
||||||
|
#include "minecraft/MinecraftInstance.h"
|
||||||
|
#include "minecraft/PackProfile.h"
|
||||||
|
|
||||||
|
#include "modplatform/EnsureMetadataTask.h"
|
||||||
|
#include "modplatform/flame/FlameCheckUpdate.h"
|
||||||
|
#include "modplatform/modrinth/ModrinthCheckUpdate.h"
|
||||||
|
|
||||||
|
#include <HoeDown.h>
|
||||||
|
|
||||||
|
#include <QTextEdit>
|
||||||
|
#include <QTreeWidgetItem>
|
||||||
|
|
||||||
|
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||||
|
|
||||||
|
static std::list<Version> mcVersions(BaseInstance* inst)
|
||||||
|
{
|
||||||
|
return { static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion() };
|
||||||
|
}
|
||||||
|
|
||||||
|
static ModAPI::ModLoaderTypes mcLoaders(BaseInstance* inst)
|
||||||
|
{
|
||||||
|
return { static_cast<MinecraftInstance*>(inst)->getPackProfile()->getModLoaders() };
|
||||||
|
}
|
||||||
|
|
||||||
|
ModUpdateDialog::ModUpdateDialog(QWidget* parent,
|
||||||
|
BaseInstance* instance,
|
||||||
|
const std::shared_ptr<ModFolderModel>& mods,
|
||||||
|
std::list<Mod>& search_for)
|
||||||
|
: ReviewMessageBox(parent, tr("Confirm mods to update"), "")
|
||||||
|
, m_parent(parent)
|
||||||
|
, m_mod_model(mods)
|
||||||
|
, m_candidates(search_for)
|
||||||
|
, m_instance(instance)
|
||||||
|
{
|
||||||
|
ReviewMessageBox::setGeometry(0, 0, 800, 600);
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
// Ensure mods have valid metadata
|
||||||
|
auto went_well = ensureMetadata();
|
||||||
|
if (!went_well) {
|
||||||
|
m_aborted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report failed metadata generation
|
||||||
|
if (!m_failed_metadata.empty()) {
|
||||||
|
QString text;
|
||||||
|
for (const auto& mod : m_failed_metadata) {
|
||||||
|
text += tr("Mod name: %1<br>File name: %2<br><br>").arg(mod.name(), mod.fileinfo().fileName());
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollMessageBox message_dialog(m_parent, tr("Metadata generation failed"),
|
||||||
|
tr("Could not generate metadata for the following mods:<br>"
|
||||||
|
"Do you wish to proceed without those mods?"),
|
||||||
|
text);
|
||||||
|
message_dialog.setModal(true);
|
||||||
|
if (message_dialog.exec() == QDialog::Rejected) {
|
||||||
|
m_aborted = true;
|
||||||
|
QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto versions = mcVersions(m_instance);
|
||||||
|
auto loaders = mcLoaders(m_instance);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 for updates
|
||||||
|
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);
|
||||||
|
|
||||||
|
// If the dialog was skipped / some download error happened
|
||||||
|
if (ret == QDialog::DialogCode::Rejected) {
|
||||||
|
m_aborted = true;
|
||||||
|
QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add found updates for Modrinth
|
||||||
|
if (m_modrinth_check_task) {
|
||||||
|
auto modrinth_updates = m_modrinth_check_task->getUpdatable();
|
||||||
|
for (auto& updatable : modrinth_updates) {
|
||||||
|
qDebug() << QString("Mod %1 has an update available!").arg(updatable.name);
|
||||||
|
|
||||||
|
appendMod(updatable);
|
||||||
|
m_tasks.insert(updatable.name, updatable.download);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add found updated for Flame
|
||||||
|
if (m_flame_check_task) {
|
||||||
|
auto flame_updates = m_flame_check_task->getUpdatable();
|
||||||
|
for (auto& updatable : flame_updates) {
|
||||||
|
qDebug() << QString("Mod %1 has an update available!").arg(updatable.name);
|
||||||
|
|
||||||
|
appendMod(updatable);
|
||||||
|
m_tasks.insert(updatable.name, updatable.download);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report failed update checking
|
||||||
|
if (!m_failed_check_update.empty()) {
|
||||||
|
QString text;
|
||||||
|
for (const auto& failed : m_failed_check_update) {
|
||||||
|
const auto& mod = std::get<0>(failed);
|
||||||
|
const auto& reason = std::get<1>(failed);
|
||||||
|
const auto& recover_url = std::get<2>(failed);
|
||||||
|
|
||||||
|
qDebug() << mod.name() << " failed to check for updates!";
|
||||||
|
|
||||||
|
text += tr("Mod name: %1").arg(mod.name()) + "<br>";
|
||||||
|
if (!reason.isEmpty())
|
||||||
|
text += tr("Reason: %1").arg(reason) + "<br>";
|
||||||
|
if (!recover_url.isEmpty())
|
||||||
|
text += tr("Possible solution: ") + tr("Getting the latest version manually:") + "<br>" +
|
||||||
|
QString("<a href='%1'>").arg(recover_url.toString()) + recover_url.toString() + "</a><br>";
|
||||||
|
text += "<br>";
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollMessageBox message_dialog(m_parent, tr("Failed to check for updates"),
|
||||||
|
tr("Could not check or get the following mods for updates:<br>"
|
||||||
|
"Do you wish to proceed without those mods?"),
|
||||||
|
text);
|
||||||
|
message_dialog.setModal(true);
|
||||||
|
if (message_dialog.exec() == QDialog::Rejected) {
|
||||||
|
m_aborted = true;
|
||||||
|
QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's no mod to be updated
|
||||||
|
if (ui->modTreeWidget->topLevelItemCount() == 0)
|
||||||
|
m_no_updates = true;
|
||||||
|
|
||||||
|
if (m_aborted || m_no_updates)
|
||||||
|
QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Part 1: Ensure we have a valid metadata
|
||||||
|
auto ModUpdateDialog::ensureMetadata() -> bool
|
||||||
|
{
|
||||||
|
auto index_dir = indexDir();
|
||||||
|
|
||||||
|
auto* seq = new SequentialTask(m_parent, tr("Looking for metadata"));
|
||||||
|
|
||||||
|
bool confirm_rest = false;
|
||||||
|
bool try_others_rest = false;
|
||||||
|
bool skip_rest = false;
|
||||||
|
ModPlatform::Provider provider_rest = ModPlatform::Provider::MODRINTH;
|
||||||
|
|
||||||
|
for (auto& candidate : m_candidates) {
|
||||||
|
if (candidate.status() != ModStatus::NoMetadata) {
|
||||||
|
onMetadataEnsured(candidate);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skip_rest)
|
||||||
|
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);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChooseProviderDialog chooser(this);
|
||||||
|
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()));
|
||||||
|
auto confirmed = chooser.exec() == QDialog::DialogCode::Accepted;
|
||||||
|
|
||||||
|
auto response = chooser.getResponse();
|
||||||
|
|
||||||
|
if (response.skip_all)
|
||||||
|
skip_rest = true;
|
||||||
|
if (response.confirm_all) {
|
||||||
|
confirm_rest = true;
|
||||||
|
provider_rest = response.chosen;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressDialog checking_dialog(m_parent);
|
||||||
|
checking_dialog.setSkipButton(true, tr("Abort"));
|
||||||
|
checking_dialog.setWindowTitle(tr("Generating metadata..."));
|
||||||
|
auto ret_metadata = checking_dialog.execWithTask(seq);
|
||||||
|
|
||||||
|
return (ret_metadata != QDialog::DialogCode::Rejected);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModUpdateDialog::onMetadataEnsured(Mod& mod)
|
||||||
|
{
|
||||||
|
// When the mod is a folder, for instance
|
||||||
|
if (!mod.metadata())
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (mod.metadata()->provider) {
|
||||||
|
case ModPlatform::Provider::MODRINTH:
|
||||||
|
m_modrinth_to_update.push_back(mod);
|
||||||
|
break;
|
||||||
|
case ModPlatform::Provider::FLAME:
|
||||||
|
m_flame_to_update.push_back(mod);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModUpdateDialog::onMetadataFailed(Mod& mod)
|
||||||
|
{
|
||||||
|
m_failed_metadata.push_back(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info)
|
||||||
|
{
|
||||||
|
auto item_top = new QTreeWidgetItem(ui->modTreeWidget);
|
||||||
|
item_top->setCheckState(0, Qt::CheckState::Checked);
|
||||||
|
item_top->setText(0, info.name);
|
||||||
|
item_top->setExpanded(true);
|
||||||
|
|
||||||
|
auto provider_item = new QTreeWidgetItem(item_top);
|
||||||
|
provider_item->setText(0, tr("Provider: %1").arg(ProviderCaps.readableName(info.provider)));
|
||||||
|
|
||||||
|
auto old_version_item = new QTreeWidgetItem(item_top);
|
||||||
|
old_version_item->setText(0, tr("Old version: %1").arg(info.old_version.isEmpty() ? tr("Not installed") : info.old_version));
|
||||||
|
|
||||||
|
auto new_version_item = new QTreeWidgetItem(item_top);
|
||||||
|
new_version_item->setText(0, tr("New version: %1").arg(info.new_version));
|
||||||
|
|
||||||
|
auto changelog_item = new QTreeWidgetItem(item_top);
|
||||||
|
changelog_item->setText(0, tr("Changelog of the latest version"));
|
||||||
|
|
||||||
|
auto changelog = new QTreeWidgetItem(changelog_item);
|
||||||
|
|
||||||
|
auto changelog_area = new QTextEdit();
|
||||||
|
HoeDown h;
|
||||||
|
changelog_area->setText(h.process(info.changelog.toUtf8()));
|
||||||
|
changelog_area->setReadOnly(true);
|
||||||
|
if (info.changelog.size() < 250) // heuristic
|
||||||
|
changelog_area->setSizeAdjustPolicy(QTextEdit::SizeAdjustPolicy::AdjustToContents);
|
||||||
|
|
||||||
|
ui->modTreeWidget->setItemWidget(changelog, 0, changelog_area);
|
||||||
|
changelog_item->insertChildren(0, { changelog });
|
||||||
|
|
||||||
|
item_top->insertChildren(0, { old_version_item });
|
||||||
|
item_top->insertChildren(1, { new_version_item });
|
||||||
|
item_top->insertChildren(2, { changelog_item });
|
||||||
|
|
||||||
|
ui->modTreeWidget->addTopLevelItem(item_top);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ModUpdateDialog::getTasks() -> const std::list<ModDownloadTask*>
|
||||||
|
{
|
||||||
|
std::list<ModDownloadTask*> list;
|
||||||
|
|
||||||
|
auto* item = ui->modTreeWidget->topLevelItem(0);
|
||||||
|
|
||||||
|
for (int i = 0; item != nullptr; ++i) {
|
||||||
|
if (item->checkState(0) == Qt::CheckState::Checked) {
|
||||||
|
list.push_back(m_tasks.find(item->text(0)).value());
|
||||||
|
}
|
||||||
|
|
||||||
|
item = ui->modTreeWidget->topLevelItem(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
61
launcher/ui/dialogs/ModUpdateDialog.h
Normal file
61
launcher/ui/dialogs/ModUpdateDialog.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "BaseInstance.h"
|
||||||
|
#include "ModDownloadTask.h"
|
||||||
|
#include "ReviewMessageBox.h"
|
||||||
|
|
||||||
|
#include "minecraft/mod/ModFolderModel.h"
|
||||||
|
|
||||||
|
#include "modplatform/CheckUpdateTask.h"
|
||||||
|
|
||||||
|
class Mod;
|
||||||
|
class ModrinthCheckUpdate;
|
||||||
|
class FlameCheckUpdate;
|
||||||
|
|
||||||
|
class ModUpdateDialog final : public ReviewMessageBox {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ModUpdateDialog(QWidget* parent,
|
||||||
|
BaseInstance* instance,
|
||||||
|
const std::shared_ptr<ModFolderModel>& mod_model,
|
||||||
|
std::list<Mod>& search_for);
|
||||||
|
|
||||||
|
void checkCandidates();
|
||||||
|
|
||||||
|
void appendMod(const CheckUpdateTask::UpdatableMod& info);
|
||||||
|
|
||||||
|
const std::list<ModDownloadTask*> getTasks();
|
||||||
|
auto indexDir() const -> QDir { return m_mod_model->indexDir(); }
|
||||||
|
|
||||||
|
auto noUpdates() const -> bool { return m_no_updates; };
|
||||||
|
auto aborted() const -> bool { return m_aborted; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
auto ensureMetadata() -> bool;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onMetadataEnsured(Mod&);
|
||||||
|
void onMetadataFailed(Mod&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QWidget* m_parent;
|
||||||
|
|
||||||
|
SequentialTask m_check_task;
|
||||||
|
ModrinthCheckUpdate* m_modrinth_check_task = nullptr;
|
||||||
|
FlameCheckUpdate* m_flame_check_task = nullptr;
|
||||||
|
|
||||||
|
const std::shared_ptr<ModFolderModel>& m_mod_model;
|
||||||
|
|
||||||
|
std::list<Mod>& m_candidates;
|
||||||
|
std::list<Mod> m_modrinth_to_update;
|
||||||
|
std::list<Mod> m_flame_to_update;
|
||||||
|
|
||||||
|
std::list<Mod> m_failed_metadata;
|
||||||
|
std::list<std::tuple<Mod, QString, QUrl>> m_failed_check_update;
|
||||||
|
|
||||||
|
QHash<QString, ModDownloadTask*> m_tasks;
|
||||||
|
BaseInstance* m_instance;
|
||||||
|
|
||||||
|
bool m_no_updates = false;
|
||||||
|
bool m_aborted = false;
|
||||||
|
};
|
@ -6,7 +6,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>400</width>
|
<width>500</width>
|
||||||
<height>455</height>
|
<height>455</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
Loading…
Reference in New Issue
Block a user