chore(clang-tidy): fix clang tidy warnings

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
Trial97 2026-05-07 13:43:57 +03:00
parent ca721f9d67
commit a63048d7e2
19 changed files with 494 additions and 405 deletions

View file

@ -60,11 +60,11 @@ Net::ModrinthDownloadMeta createModrinthMeta(BaseInstance* instance, QString rea
ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion version, ModPlatform::IndexedVersion version,
ResourceFolderModel* packs, ResourceFolderModel* packs,
bool is_indexed, bool isIndexed,
QString downloadReason) QString downloadReason)
: m_pack(std::move(pack)), m_pack_version(std::move(version)), m_pack_model(packs) : m_pack(std::move(pack)), m_pack_version(std::move(version)), m_pack_model(packs)
{ {
if (is_indexed) { if (isIndexed) {
m_update_task.reset(new LocalResourceUpdateTask(m_pack_model->indexDir(), *m_pack, m_pack_version)); m_update_task.reset(new LocalResourceUpdateTask(m_pack_model->indexDir(), *m_pack, m_pack_version));
connect(m_update_task.get(), &LocalResourceUpdateTask::hasOldResource, this, &ResourceDownloadTask::hasOldResource); connect(m_update_task.get(), &LocalResourceUpdateTask::hasOldResource, this, &ResourceDownloadTask::hasOldResource);
@ -113,8 +113,9 @@ void ResourceDownloadTask::downloadSucceeded()
auto oldName = std::get<0>(to_delete); auto oldName = std::get<0>(to_delete);
auto oldFilename = std::get<1>(to_delete); auto oldFilename = std::get<1>(to_delete);
if (oldName.isEmpty() || oldFilename == m_pack_version.fileName) if (oldName.isEmpty() || oldFilename == m_pack_version.fileName) {
return; return;
}
m_pack_model->uninstallResource(oldFilename, true); m_pack_model->uninstallResource(oldFilename, true);
@ -126,16 +127,17 @@ void ResourceDownloadTask::downloadSucceeded()
if (oldConfig.exists() && !newConfig.exists()) { if (oldConfig.exists() && !newConfig.exists()) {
bool success = FS::move(oldConfig.filePath(), newConfig.filePath()); bool success = FS::move(oldConfig.filePath(), newConfig.filePath());
if (!success) if (!success) {
emit logWarning(tr("Failed to rename shader config from '%1' to '%2'").arg(oldConfig.fileName(), newConfig.fileName())); emit logWarning(tr("Failed to rename shader config from '%1' to '%2'").arg(oldConfig.fileName(), newConfig.fileName()));
} }
} }
}
} }
void ResourceDownloadTask::downloadFailed(QString reason) void ResourceDownloadTask::downloadFailed(QString reason)
{ {
m_filesNetJob.reset(); m_filesNetJob.reset();
emitFailed(reason); emitFailed(std::move(reason));
} }
void ResourceDownloadTask::downloadProgressChanged(qint64 current, qint64 total) void ResourceDownloadTask::downloadProgressChanged(qint64 current, qint64 total)
@ -145,7 +147,7 @@ void ResourceDownloadTask::downloadProgressChanged(qint64 current, qint64 total)
// This indirection is done so that we don't delete a mod before being sure it was // This indirection is done so that we don't delete a mod before being sure it was
// downloaded successfully! // downloaded successfully!
void ResourceDownloadTask::hasOldResource(QString name, QString filename) void ResourceDownloadTask::hasOldResource(const QString& name, const QString& filename)
{ {
to_delete = { name, filename }; to_delete = { name, filename };
} }

View file

@ -20,7 +20,6 @@
#pragma once #pragma once
#include "net/NetJob.h" #include "net/NetJob.h"
#include "net/ApiHeaderProxy.h"
#include "tasks/SequentialTask.h" #include "tasks/SequentialTask.h"
#include "minecraft/mod/tasks/LocalResourceUpdateTask.h" #include "minecraft/mod/tasks/LocalResourceUpdateTask.h"
@ -34,7 +33,7 @@ class ResourceDownloadTask : public SequentialTask {
explicit ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, explicit ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion version, ModPlatform::IndexedVersion version,
ResourceFolderModel* packs, ResourceFolderModel* packs,
bool is_indexed = true, bool isIndexed = true,
QString downloadReason = "standalone"); QString downloadReason = "standalone");
const QString& getFilename() const { return m_pack_version.fileName; } const QString& getFilename() const { return m_pack_version.fileName; }
const QVariant& getVersionID() const { return m_pack_version.fileId; } const QVariant& getVersionID() const { return m_pack_version.fileId; }
@ -58,5 +57,5 @@ class ResourceDownloadTask : public SequentialTask {
std::tuple<QString, QString> to_delete{ "", "" }; std::tuple<QString, QString> to_delete{ "", "" };
private slots: private slots:
void hasOldResource(QString name, QString filename); void hasOldResource(const QString& name, const QString& filename);
}; };

View file

@ -32,111 +32,116 @@
bool ModrinthCreationTask::abort() bool ModrinthCreationTask::abort()
{ {
if (!canAbort()) if (!canAbort()) {
return false; return false;
}
if (m_task) if (m_task) {
m_task->abort(); m_task->abort();
}
return InstanceCreationTask::abort(); return InstanceCreationTask::abort();
} }
bool ModrinthCreationTask::updateInstance() bool ModrinthCreationTask::updateInstance()
{ {
auto instance_list = APPLICATION->instances(); auto* instanceList = APPLICATION->instances();
// FIXME: How to handle situations when there's more than one install already for a given modpack? // FIXME: How to handle situations when there's more than one install already for a given modpack?
BaseInstance* inst; BaseInstance* inst = nullptr;
if (auto original_id = originalInstanceID(); !original_id.isEmpty()) { if (auto originalId = originalInstanceID(); !originalId.isEmpty()) {
inst = instance_list->getInstanceById(original_id); inst = instanceList->getInstanceById(originalId);
Q_ASSERT(inst); Q_ASSERT(inst);
} else { } else {
inst = instance_list->getInstanceByManagedName(originalName()); inst = instanceList->getInstanceByManagedName(originalName());
if (!inst) { if (!inst) {
inst = instance_list->getInstanceById(originalName()); inst = instanceList->getInstanceById(originalName());
if (!inst) if (!inst) {
return false; return false;
} }
} }
}
QString index_path = FS::PathCombine(m_stagingPath, "modrinth.index.json"); QString indexPath = FS::PathCombine(m_stagingPath, "modrinth.index.json");
if (!parseManifest(index_path, m_files, true, false)) if (!parseManifest(indexPath, m_files, true, false)) {
return false; return false;
}
auto version_name = inst->getManagedPackVersionName(); auto versionName = inst->getManagedPackVersionName();
m_root_path = QFileInfo(inst->gameRoot()).fileName(); m_root_path = QFileInfo(inst->gameRoot()).fileName();
auto version_str = !version_name.isEmpty() ? tr(" (version %1)").arg(version_name) : ""; auto versionStr = !versionName.isEmpty() ? tr(" (version %1)").arg(versionName) : "";
if (shouldConfirmUpdate()) { if (shouldConfirmUpdate()) {
auto should_update = askIfShouldUpdate(m_parent, version_str); auto shouldUpdate = askIfShouldUpdate(m_parent, versionStr);
if (should_update == ShouldUpdate::SkipUpdating) if (shouldUpdate == ShouldUpdate::SkipUpdating) {
return false; return false;
if (should_update == ShouldUpdate::Cancel) { }
if (shouldUpdate == ShouldUpdate::Cancel) {
m_abort = true; m_abort = true;
return false; return false;
} }
} }
// Remove repeated files, we don't need to download them! // Remove repeated files, we don't need to download them!
QDir old_inst_dir(inst->instanceRoot()); QDir oldInstDir(inst->instanceRoot());
QString old_index_folder(FS::PathCombine(old_inst_dir.absolutePath(), "mrpack")); QString oldIndexFolder(FS::PathCombine(oldInstDir.absolutePath(), "mrpack"));
QString old_index_path(FS::PathCombine(old_index_folder, "modrinth.index.json")); QString oldIndexPath(FS::PathCombine(oldIndexFolder, "modrinth.index.json"));
QFileInfo old_index_file(old_index_path); QFileInfo oldIndexFile(oldIndexPath);
if (old_index_file.exists()) { if (oldIndexFile.exists()) {
std::vector<File> old_files; std::vector<File> oldFiles;
parseManifest(old_index_path, old_files, false, false); parseManifest(oldIndexPath, oldFiles, false, false);
// Let's remove all duplicated, identical resources! // Let's remove all duplicated, identical resources!
auto files_iterator = m_files.begin(); auto filesIterator = m_files.begin();
begin: begin:
while (files_iterator != m_files.end()) { while (filesIterator != m_files.end()) {
auto const& file = *files_iterator; const auto& file = *filesIterator;
auto old_files_iterator = old_files.begin(); auto oldFilesIterator = oldFiles.begin();
while (old_files_iterator != old_files.end()) { while (oldFilesIterator != oldFiles.end()) {
auto const& old_file = *old_files_iterator; const auto& oldFile = *oldFilesIterator;
if (old_file.hash == file.hash) { if (oldFile.hash == file.hash) {
qDebug() << "Removed file at" << file.path << "from list of downloads"; qDebug() << "Removed file at" << file.path << "from list of downloads";
files_iterator = m_files.erase(files_iterator); filesIterator = m_files.erase(filesIterator);
old_files_iterator = old_files.erase(old_files_iterator); oldFilesIterator = oldFiles.erase(oldFilesIterator);
goto begin; // Sorry :c goto begin; // Sorry :c
} }
old_files_iterator++; oldFilesIterator++;
} }
files_iterator++; filesIterator++;
} }
QDir old_minecraft_dir(inst->gameRoot()); QDir oldMinecraftDir(inst->gameRoot());
// Some files were removed from the old version, and some will be downloaded in an updated version, // Some files were removed from the old version, and some will be downloaded in an updated version,
// so we're fine removing them! // so we're fine removing them!
if (!old_files.empty()) { if (!oldFiles.empty()) {
for (auto const& file : old_files) { for (const auto& file : oldFiles) {
scheduleToDelete(m_parent, old_minecraft_dir, file.path, true); scheduleToDelete(m_parent, oldMinecraftDir, file.path, true);
} }
} }
// We will remove all the previous overrides, to prevent duplicate files! // We will remove all the previous overrides, to prevent duplicate files!
// TODO: Currently 'overrides' will always override the stuff on update. How do we preserve unchanged overrides? // TODO: Currently 'overrides' will always override the stuff on update. How do we preserve unchanged overrides?
// FIXME: We may want to do something about disabled mods. // FIXME: We may want to do something about disabled mods.
auto old_overrides = Override::readOverrides("overrides", old_index_folder); auto oldOverrides = Override::readOverrides("overrides", oldIndexFolder);
for (const auto& entry : old_overrides) { for (const auto& entry : oldOverrides) {
scheduleToDelete(m_parent, old_minecraft_dir, entry); scheduleToDelete(m_parent, oldMinecraftDir, entry);
} }
auto old_client_overrides = Override::readOverrides("client-overrides", old_index_folder); auto oldClientOverrides = Override::readOverrides("client-overrides", oldIndexFolder);
for (const auto& entry : old_client_overrides) { for (const auto& entry : oldClientOverrides) {
scheduleToDelete(m_parent, old_minecraft_dir, entry); scheduleToDelete(m_parent, oldMinecraftDir, entry);
} }
} else { } else {
// We don't have an old index file, so we may duplicate stuff! // We don't have an old index file, so we may duplicate stuff!
auto dialog = CustomMessageBox::selectable(m_parent, tr("No index file."), auto* dialog = CustomMessageBox::selectable(m_parent, tr("No index file."),
tr("We couldn't find a suitable index file for the older version. This may cause some " tr("We couldn't find a suitable index file for the older version. This may cause some "
"of the files to be duplicated. Do you want to continue?"), "of the files to be duplicated. Do you want to continue?"),
QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Cancel); QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Cancel);
@ -161,39 +166,40 @@ std::unique_ptr<MinecraftInstance> ModrinthCreationTask::createInstance()
{ {
QEventLoop loop; QEventLoop loop;
QString parent_folder(FS::PathCombine(m_stagingPath, "mrpack")); QString parentFolder(FS::PathCombine(m_stagingPath, "mrpack"));
QString index_path = FS::PathCombine(m_stagingPath, "modrinth.index.json"); QString indexPath = FS::PathCombine(m_stagingPath, "modrinth.index.json");
if (m_files.empty() && !parseManifest(index_path, m_files, true, true)) if (m_files.empty() && !parseManifest(indexPath, m_files, true, true)) {
return nullptr; return nullptr;
}
// Keep index file in case we need it some other time (like when changing versions) // Keep index file in case we need it some other time (like when changing versions)
QString new_index_place(FS::PathCombine(parent_folder, "modrinth.index.json")); QString newIndexPlace(FS::PathCombine(parentFolder, "modrinth.index.json"));
FS::ensureFilePathExists(new_index_place); FS::ensureFilePathExists(newIndexPlace);
FS::move(index_path, new_index_place); FS::move(indexPath, newIndexPlace);
auto mcPath = FS::PathCombine(m_stagingPath, m_root_path); auto mcPath = FS::PathCombine(m_stagingPath, m_root_path);
auto override_path = FS::PathCombine(m_stagingPath, "overrides"); auto overridePath = FS::PathCombine(m_stagingPath, "overrides");
if (QFile::exists(override_path)) { if (QFile::exists(overridePath)) {
// Create a list of overrides in "overrides.txt" inside mrpack/ // Create a list of overrides in "overrides.txt" inside mrpack/
Override::createOverrides("overrides", parent_folder, override_path); Override::createOverrides("overrides", parentFolder, overridePath);
// Apply the overrides // Apply the overrides
if (!FS::move(override_path, mcPath)) { if (!FS::move(overridePath, mcPath)) {
setError(tr("Could not rename the overrides folder:\n") + "overrides"); setError(tr("Could not rename the overrides folder:\n") + "overrides");
return nullptr; return nullptr;
} }
} }
// Do client overrides // Do client overrides
auto client_override_path = FS::PathCombine(m_stagingPath, "client-overrides"); auto clientOverridePath = FS::PathCombine(m_stagingPath, "client-overrides");
if (QFile::exists(client_override_path)) { if (QFile::exists(clientOverridePath)) {
// Create a list of overrides in "client-overrides.txt" inside mrpack/ // Create a list of overrides in "client-overrides.txt" inside mrpack/
Override::createOverrides("client-overrides", parent_folder, client_override_path); Override::createOverrides("client-overrides", parentFolder, clientOverridePath);
// Apply the overrides // Apply the overrides
if (!FS::overrideFolder(mcPath, client_override_path)) { if (!FS::overrideFolder(mcPath, clientOverridePath)) {
setError(tr("Could not rename the client overrides folder:\n") + "client overrides"); setError(tr("Could not rename the client overrides folder:\n") + "client overrides");
return nullptr; return nullptr;
} }
@ -203,18 +209,22 @@ std::unique_ptr<MinecraftInstance> ModrinthCreationTask::createInstance()
auto instanceSettings = std::make_unique<INISettingsObject>(configPath); auto instanceSettings = std::make_unique<INISettingsObject>(configPath);
auto instance = std::make_unique<MinecraftInstance>(m_globalSettings, std::move(instanceSettings), m_stagingPath); auto instance = std::make_unique<MinecraftInstance>(m_globalSettings, std::move(instanceSettings), m_stagingPath);
auto components = instance->getPackProfile(); auto* components = instance->getPackProfile();
components->buildingFromScratch(); components->buildingFromScratch();
components->setComponentVersion("net.minecraft", m_minecraft_version, true); components->setComponentVersion("net.minecraft", m_minecraft_version, true);
if (!m_fabric_version.isEmpty()) if (!m_fabric_version.isEmpty()) {
components->setComponentVersion("net.fabricmc.fabric-loader", m_fabric_version); components->setComponentVersion("net.fabricmc.fabric-loader", m_fabric_version);
if (!m_quilt_version.isEmpty()) }
if (!m_quilt_version.isEmpty()) {
components->setComponentVersion("org.quiltmc.quilt-loader", m_quilt_version); components->setComponentVersion("org.quiltmc.quilt-loader", m_quilt_version);
if (!m_forge_version.isEmpty()) }
if (!m_forge_version.isEmpty()) {
components->setComponentVersion("net.minecraftforge", m_forge_version); components->setComponentVersion("net.minecraftforge", m_forge_version);
if (!m_neoForge_version.isEmpty()) }
if (!m_neoForge_version.isEmpty()) {
components->setComponentVersion("net.neoforged", m_neoForge_version); components->setComponentVersion("net.neoforged", m_neoForge_version);
}
if (m_instIcon != "default") { if (m_instIcon != "default") {
instance->setIconKey(m_instIcon); instance->setIconKey(m_instIcon);
@ -223,34 +233,35 @@ std::unique_ptr<MinecraftInstance> ModrinthCreationTask::createInstance()
} }
// Don't add managed info to packs without an ID (most likely imported from ZIP) // Don't add managed info to packs without an ID (most likely imported from ZIP)
if (!m_managed_id.isEmpty()) if (!m_managed_id.isEmpty()) {
instance->setManagedPack("modrinth", m_managed_id, m_managed_name, m_managed_version_id, version()); instance->setManagedPack("modrinth", m_managed_id, m_managed_name, m_managed_version_id, version());
else } else {
instance->setManagedPack("modrinth", "", name(), "", ""); instance->setManagedPack("modrinth", "", name(), "", "");
}
instance->setName(name()); instance->setName(name());
instance->saveNow(); instance->saveNow();
auto downloadMods = makeShared<NetJob>(tr("Mod Download Modrinth"), APPLICATION->network()); auto downloadMods = makeShared<NetJob>(tr("Mod Download Modrinth"), APPLICATION->network());
auto root_modpack_path = FS::PathCombine(m_stagingPath, m_root_path); auto rootModpackPath = FS::PathCombine(m_stagingPath, m_root_path);
auto root_modpack_url = QUrl::fromLocalFile(root_modpack_path); auto rootModpackUrl = QUrl::fromLocalFile(rootModpackPath);
// TODO make this work with other sorts of resource // TODO make this work with other sorts of resource
QHash<QString, Resource*> resources; QHash<QString, Resource*> resources;
for (auto& file : m_files) { for (auto& file : m_files) {
auto fileName = file.path; auto fileName = file.path;
fileName = FS::RemoveInvalidPathChars(fileName); fileName = FS::RemoveInvalidPathChars(fileName);
auto file_path = FS::PathCombine(root_modpack_path, fileName); auto filePath = FS::PathCombine(rootModpackPath, fileName);
if (!root_modpack_url.isParentOf(QUrl::fromLocalFile(file_path))) { if (!rootModpackUrl.isParentOf(QUrl::fromLocalFile(filePath))) {
// This means we somehow got out of the root folder, so abort here to prevent exploits // This means we somehow got out of the root folder, so abort here to prevent exploits
setError(tr("One of the files has a path that leads to an arbitrary location (%1). This is a security risk and isn't allowed.") setError(tr("One of the files has a path that leads to an arbitrary location (%1). This is a security risk and isn't allowed.")
.arg(fileName)); .arg(fileName));
return nullptr; return nullptr;
} }
if (fileName.startsWith("mods/")) { if (fileName.startsWith("mods/")) {
auto mod = new Mod(file_path); auto* mod = new Mod(filePath);
ModDetails d; ModDetails d;
d.mod_id = file_path; d.mod_id = filePath;
mod->setDetails(d); mod->setDetails(d);
resources[file.hash.toHex()] = mod; resources[file.hash.toHex()] = mod;
} }
@ -258,7 +269,7 @@ std::unique_ptr<MinecraftInstance> ModrinthCreationTask::createInstance()
setError(tr("The file '%1' is missing a download link. This is invalid in the pack format.").arg(fileName)); setError(tr("The file '%1' is missing a download link. This is invalid in the pack format.").arg(fileName));
return nullptr; return nullptr;
} }
qDebug() << "Will try to download" << file.downloads.front() << "to" << file_path; qDebug() << "Will try to download" << file.downloads.front() << "to" << filePath;
QString loader; QString loader;
if (m_instance.has_value()) { if (m_instance.has_value()) {
@ -281,29 +292,30 @@ std::unique_ptr<MinecraftInstance> ModrinthCreationTask::createInstance()
}; };
QUrl downloadUrl = file.downloads.dequeue(); QUrl downloadUrl = file.downloads.dequeue();
auto dl = Net::ApiDownload::makeFile(downloadUrl, file_path, Net::Download::Option::NoOptions, meta); auto dl = Net::ApiDownload::makeFile(downloadUrl, filePath, Net::Download::Option::NoOptions, meta);
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash)); dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
downloadMods->addNetAction(dl); downloadMods->addNetAction(dl);
if (!file.downloads.empty()) { if (!file.downloads.empty()) {
// FIXME: This really needs to be put into a ConcurrentTask of // FIXME: This really needs to be put into a ConcurrentTask of
// MultipleOptionsTask's , once those exist :) // MultipleOptionsTask's , once those exist :)
auto param = dl.toWeakRef(); auto param = dl.toWeakRef();
connect(dl.get(), &Task::failed, [&file, file_path, param, downloadMods, meta] { connect(dl.get(), &Task::failed, [&file, filePath, param, downloadMods, meta] {
QUrl fallbackUrl = file.downloads.dequeue(); QUrl fallbackUrl = file.downloads.dequeue();
auto ndl = Net::ApiDownload::makeFile(fallbackUrl, file_path, Net::Download::Option::NoOptions, meta); auto ndl = Net::ApiDownload::makeFile(fallbackUrl, filePath, Net::Download::Option::NoOptions, meta);
ndl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash)); ndl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
downloadMods->addNetAction(ndl); downloadMods->addNetAction(ndl);
if (auto shared = param.lock()) if (auto shared = param.lock()) {
shared->succeeded(); shared->succeeded();
}
}); });
} }
} }
bool ended_well = false; bool endedWell = false;
connect(downloadMods.get(), &NetJob::succeeded, this, [&ended_well]() { ended_well = true; }); connect(downloadMods.get(), &NetJob::succeeded, this, [&endedWell]() { endedWell = true; });
connect(downloadMods.get(), &NetJob::failed, [this, &ended_well](const QString& reason) { connect(downloadMods.get(), &NetJob::failed, [this, &endedWell](const QString& reason) {
ended_well = false; endedWell = false;
setError(reason); setError(reason);
}); });
connect(downloadMods.get(), &NetJob::finished, &loop, &QEventLoop::quit); connect(downloadMods.get(), &NetJob::finished, &loop, &QEventLoop::quit);
@ -319,8 +331,8 @@ std::unique_ptr<MinecraftInstance> ModrinthCreationTask::createInstance()
loop.exec(); loop.exec();
if (!ended_well) { if (!endedWell) {
for (auto resource : resources) { for (auto* resource : resources) {
delete resource; delete resource;
} }
return nullptr; return nullptr;
@ -329,7 +341,7 @@ std::unique_ptr<MinecraftInstance> ModrinthCreationTask::createInstance()
QEventLoop ensureMetaLoop; QEventLoop ensureMetaLoop;
QDir folder = FS::PathCombine(instance->modsRoot(), ".index"); QDir folder = FS::PathCombine(instance->modsRoot(), ".index");
auto ensureMetadataTask = makeShared<EnsureMetadataTask>(resources, folder, ModPlatform::ResourceProvider::MODRINTH); auto ensureMetadataTask = makeShared<EnsureMetadataTask>(resources, folder, ModPlatform::ResourceProvider::MODRINTH);
connect(ensureMetadataTask.get(), &Task::succeeded, this, [&ended_well]() { ended_well = true; }); connect(ensureMetadataTask.get(), &Task::succeeded, this, [&endedWell]() { endedWell = true; });
connect(ensureMetadataTask.get(), &Task::finished, &ensureMetaLoop, &QEventLoop::quit); connect(ensureMetadataTask.get(), &Task::finished, &ensureMetaLoop, &QEventLoop::quit);
connect(ensureMetadataTask.get(), &Task::progress, [this](qint64 current, qint64 total) { connect(ensureMetadataTask.get(), &Task::progress, [this](qint64 current, qint64 total) {
setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
@ -341,40 +353,38 @@ std::unique_ptr<MinecraftInstance> ModrinthCreationTask::createInstance()
m_task = ensureMetadataTask; m_task = ensureMetadataTask;
ensureMetaLoop.exec(); ensureMetaLoop.exec();
for (auto resource : resources) { for (auto* resource : resources) {
delete resource; delete resource;
} }
resources.clear(); resources.clear();
// Update information of the already installed instance, if any. // Update information of the already installed instance, if any.
if (m_instance && ended_well) { if (m_instance && endedWell) {
setAbortable(false); setAbortable(false);
auto inst = m_instance.value(); auto* inst = m_instance.value();
// Only change the name if it didn't use a custom name, so that the previous custom name // Only change the name if it didn't use a custom name, so that the previous custom name
// is preserved, but if we're using the original one, we update the version string. // is preserved, but if we're using the original one, we update the version string.
// NOTE: This needs to come before the copyManagedPack call! // NOTE: This needs to come before the copyManagedPack call!
if (inst->name().contains(inst->getManagedPackVersionName()) && inst->name() != instance->name()) { if (inst->name().contains(inst->getManagedPackVersionName()) && inst->name() != instance->name()) {
if (askForChangingInstanceName(m_parent, inst->name(), instance->name()) == InstanceNameChange::ShouldChange) if (askForChangingInstanceName(m_parent, inst->name(), instance->name()) == InstanceNameChange::ShouldChange) {
inst->setName(instance->name()); inst->setName(instance->name());
} }
}
inst->copyManagedPack(*instance); inst->copyManagedPack(*instance);
} }
if (ended_well) { if (endedWell) {
return instance; return instance;
} }
return nullptr; return nullptr;
} }
bool ModrinthCreationTask::parseManifest(const QString& index_path, bool ModrinthCreationTask::parseManifest(const QString& indexPath, std::vector<File>& files, bool setInternalData, bool showOptionalDialog)
std::vector<File>& files,
bool set_internal_data,
bool show_optional_dialog)
{ {
try { try {
auto doc = Json::requireDocument(index_path); auto doc = Json::requireDocument(indexPath);
auto obj = Json::requireObject(doc, "modrinth.index.json"); auto obj = Json::requireObject(doc, "modrinth.index.json");
int formatVersion = Json::requireInteger(obj, "formatVersion", "modrinth.index.json"); int formatVersion = Json::requireInteger(obj, "formatVersion", "modrinth.index.json");
if (formatVersion == 1) { if (formatVersion == 1) {
@ -383,9 +393,10 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path,
throw JSONValidationError("Unknown game: " + game); throw JSONValidationError("Unknown game: " + game);
} }
if (set_internal_data) { if (setInternalData) {
if (m_managed_version_id.isEmpty()) if (m_managed_version_id.isEmpty()) {
m_managed_version_id = obj["versionId"].toString(); m_managed_version_id = obj["versionId"].toString();
}
m_managed_name = obj["name"].toString(); m_managed_name = obj["name"].toString();
} }
@ -401,7 +412,8 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path,
QString support = env["client"].toString("unsupported"); QString support = env["client"].toString("unsupported");
if (support == "unsupported") { if (support == "unsupported") {
continue; continue;
} else if (support == "optional") { }
if (support == "optional") {
file.required = false; file.required = false;
} }
} }
@ -413,20 +425,21 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path,
// Do not use requireUrl, which uses StrictMode, instead use QUrl's default TolerantMode // Do not use requireUrl, which uses StrictMode, instead use QUrl's default TolerantMode
// (as Modrinth seems to incorrectly handle spaces) // (as Modrinth seems to incorrectly handle spaces)
auto download_arr = modInfo["downloads"].toArray(); auto downloadArr = modInfo["downloads"].toArray();
for (auto download : download_arr) { for (auto download : downloadArr) {
qWarning() << download.toString(); qWarning() << download.toString();
bool is_last = download.toString() == download_arr.last().toString(); bool isLast = download.toString() == downloadArr.last().toString();
auto download_url = QUrl(download.toString()); auto downloadUrl = QUrl(download.toString());
if (!download_url.isValid()) { if (!downloadUrl.isValid()) {
qDebug() qDebug()
<< QString("Download URL (%1) for %2 is not a correctly formatted URL").arg(download_url.toString(), file.path); << QString("Download URL (%1) for %2 is not a correctly formatted URL").arg(downloadUrl.toString(), file.path);
if (is_last && file.downloads.isEmpty()) if (isLast && file.downloads.isEmpty()) {
throw JSONValidationError(tr("Download URL for %1 is not a correctly formatted URL").arg(file.path)); throw JSONValidationError(tr("Download URL for %1 is not a correctly formatted URL").arg(file.path));
}
} else { } else {
file.downloads.push_back(download_url); file.downloads.push_back(downloadUrl);
} }
} }
@ -434,10 +447,11 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path,
} }
if (!optionalFiles.empty()) { if (!optionalFiles.empty()) {
if (show_optional_dialog) { if (showOptionalDialog) {
QStringList oFiles; QStringList oFiles;
for (auto file : optionalFiles) for (const auto& file : optionalFiles) {
oFiles.push_back(file.path); oFiles.push_back(file.path);
}
OptionalModDialog optionalModDialog(m_parent, oFiles); OptionalModDialog optionalModDialog(m_parent, oFiles);
if (optionalModDialog.exec() == QDialog::Rejected) { if (optionalModDialog.exec() == QDialog::Rejected) {
emitAborted(); emitAborted();
@ -460,7 +474,7 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path,
} }
} }
} }
if (set_internal_data) { if (setInternalData) {
auto dependencies = Json::requireObject(obj, "dependencies", "modrinth.index.json"); auto dependencies = Json::requireObject(obj, "dependencies", "modrinth.index.json");
for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) { for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) {
QString name = it.key(); QString name = it.key();

View file

@ -24,18 +24,18 @@ class ModrinthCreationTask final : public InstanceCreationTask {
}; };
public: public:
ModrinthCreationTask(QString staging_path, ModrinthCreationTask(const QString& stagingPath,
SettingsObject* global_settings, SettingsObject* globalSettings,
QWidget* parent, QWidget* parent,
QString id, QString id,
QString version_id = {}, QString versionId = {},
QString original_instance_id = {}) QString originalInstanceId = {})
: InstanceCreationTask(), m_parent(parent), m_managed_id(std::move(id)), m_managed_version_id(std::move(version_id)) : m_parent(parent), m_managed_id(std::move(id)), m_managed_version_id(std::move(versionId))
{ {
setStagingPath(staging_path); setStagingPath(stagingPath);
setParentSettings(global_settings); setParentSettings(globalSettings);
m_original_instance_id = std::move(original_instance_id); m_original_instance_id = std::move(originalInstanceId);
} }
bool abort() override; bool abort() override;
@ -44,7 +44,7 @@ class ModrinthCreationTask final : public InstanceCreationTask {
std::unique_ptr<MinecraftInstance> createInstance() override; std::unique_ptr<MinecraftInstance> createInstance() override;
private: private:
bool parseManifest(const QString&, std::vector<File>&, bool set_internal_data = true, bool show_optional_dialog = true); bool parseManifest(const QString&, std::vector<File>&, bool setInternalData = true, bool showOptionalDialog = true);
private: private:
QWidget* m_parent = nullptr; QWidget* m_parent = nullptr;

View file

@ -18,27 +18,29 @@
*/ */
#include "net/ApiDownload.h" #include "net/ApiDownload.h"
#include <utility>
#include "net/ApiHeaderProxy.h" #include "net/ApiHeaderProxy.h"
namespace Net { namespace Net {
Download::Ptr ApiDownload::makeCached(QUrl url, MetaEntryPtr entry, Download::Options options) Download::Ptr ApiDownload::makeCached(QUrl url, MetaEntryPtr entry, Download::Options options)
{ {
auto dl = Download::makeCached(url, entry, options); auto dl = Download::makeCached(std::move(url), std::move(entry), options);
dl->addHeaderProxy(std::make_unique<ApiHeaderProxy>()); dl->addHeaderProxy(std::make_unique<ApiHeaderProxy>());
return dl; return dl;
} }
std::pair<Download::Ptr, QByteArray*> ApiDownload::makeByteArray(QUrl url, Download::Options options) std::pair<Download::Ptr, QByteArray*> ApiDownload::makeByteArray(QUrl url, Download::Options options)
{ {
auto [dl, response] = Download::makeByteArray(url, options); auto [dl, response] = Download::makeByteArray(std::move(url), options);
dl->addHeaderProxy(std::make_unique<ApiHeaderProxy>()); dl->addHeaderProxy(std::make_unique<ApiHeaderProxy>());
return { dl, response }; return { dl, response };
} }
Download::Ptr ApiDownload::makeFile(QUrl url, QString path, Download::Options options, ModrinthDownloadMeta meta) Download::Ptr ApiDownload::makeFile(QUrl url, QString path, Download::Options options, ModrinthDownloadMeta meta)
{ {
auto dl = Download::makeFile(url, path, options); auto dl = Download::makeFile(std::move(url), std::move(path), options);
dl->addHeaderProxy(std::make_unique<ApiHeaderProxy>(std::move(meta))); dl->addHeaderProxy(std::make_unique<ApiHeaderProxy>(std::move(meta)));
return dl; return dl;
} }

View file

@ -27,7 +27,9 @@ namespace Net {
namespace ApiDownload { namespace ApiDownload {
Download::Ptr makeCached(QUrl url, MetaEntryPtr entry, Download::Options options = Download::Option::NoOptions); Download::Ptr makeCached(QUrl url, MetaEntryPtr entry, Download::Options options = Download::Option::NoOptions);
std::pair<Download::Ptr, QByteArray*> makeByteArray(QUrl url, Download::Options options = Download::Option::NoOptions); std::pair<Download::Ptr, QByteArray*> makeByteArray(QUrl url, Download::Options options = Download::Option::NoOptions);
Download::Ptr makeFile(QUrl url, QString path, Download::Options options = Download::Option::NoOptions, Download::Ptr makeFile(QUrl url,
QString path,
Download::Options options = Download::Option::NoOptions,
ModrinthDownloadMeta meta = ModrinthDownloadMeta()); ModrinthDownloadMeta meta = ModrinthDownloadMeta());
}; // namespace ApiDownload }; // namespace ApiDownload

View file

@ -38,28 +38,31 @@ struct ModrinthDownloadMeta {
QByteArray toJson() const QByteArray toJson() const
{ {
QJsonObject obj; QJsonObject obj;
if (!reason.isEmpty()) if (!reason.isEmpty()) {
obj["reason"] = reason; obj["reason"] = reason;
if (!gameVersion.isEmpty()) }
if (!gameVersion.isEmpty()) {
obj["game_version"] = gameVersion; obj["game_version"] = gameVersion;
if (!loader.isEmpty()) }
if (!loader.isEmpty()) {
obj["loader"] = loader; obj["loader"] = loader;
}
return QJsonDocument(obj).toJson(QJsonDocument::Compact); return QJsonDocument(obj).toJson(QJsonDocument::Compact);
} }
}; };
class ApiHeaderProxy : public HeaderProxy { class ApiHeaderProxy : public HeaderProxy {
public: public:
ApiHeaderProxy() : HeaderProxy() {} ApiHeaderProxy() = default;
explicit ApiHeaderProxy(ModrinthDownloadMeta meta) : m_meta(std::move(meta)) {} explicit ApiHeaderProxy(ModrinthDownloadMeta meta) : m_meta(std::move(meta)) {}
virtual ~ApiHeaderProxy() = default; ~ApiHeaderProxy() override = default;
public: public:
virtual QList<HeaderPair> headers(const QNetworkRequest& request) const override QList<HeaderPair> headers(const QNetworkRequest& request) const override
{ {
QList<HeaderPair> hdrs; QList<HeaderPair> hdrs;
if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) { if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) {
hdrs.append({ "x-api-key", APPLICATION->getFlameAPIKey().toUtf8() }); hdrs.append({ .headerName = "x-api-key", .headerValue = APPLICATION->getFlameAPIKey().toUtf8() });
} else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() || } else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() ||
request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) { request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) {
QString token = APPLICATION->getModrinthAPIToken(); QString token = APPLICATION->getModrinthAPIToken();

View file

@ -237,7 +237,7 @@ ResourcePage* ResourceDownloadDialog::selectedPage()
void ResourceDownloadDialog::addResource(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion& ver, QString downloadReason) void ResourceDownloadDialog::addResource(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion& ver, QString downloadReason)
{ {
removeResource(pack->name); removeResource(pack->name);
selectedPage()->addResourceToPage(pack, ver, getBaseModel(), downloadReason); selectedPage()->addResourceToPage(pack, ver, getBaseModel(), std::move(downloadReason));
setButtonStatus(); setButtonStatus();
} }

View file

@ -121,7 +121,10 @@ class ResourcePackDownloadDialog final : public ResourceDownloadDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit ResourcePackDownloadDialog(QWidget* parent, ResourcePackFolderModel* resourcePacks, BaseInstance* instance, bool suppressInitialSearch = false); explicit ResourcePackDownloadDialog(QWidget* parent,
ResourcePackFolderModel* resourcePacks,
BaseInstance* instance,
bool suppressInitialSearch = false);
~ResourcePackDownloadDialog() override = default; ~ResourcePackDownloadDialog() override = default;
//: String that gets appended to the resource pack download dialog title ("Download " + resourcesString()) //: String that gets appended to the resource pack download dialog title ("Download " + resourcesString())
@ -138,7 +141,10 @@ class TexturePackDownloadDialog final : public ResourceDownloadDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit TexturePackDownloadDialog(QWidget* parent, TexturePackFolderModel* resourcePacks, BaseInstance* instance, bool suppressInitialSearch = false); explicit TexturePackDownloadDialog(QWidget* parent,
TexturePackFolderModel* resourcePacks,
BaseInstance* instance,
bool suppressInitialSearch = false);
~TexturePackDownloadDialog() override = default; ~TexturePackDownloadDialog() override = default;
//: String that gets appended to the texture pack download dialog title ("Download " + resourcesString()) //: String that gets appended to the texture pack download dialog title ("Download " + resourcesString())
@ -155,7 +161,10 @@ class ShaderPackDownloadDialog final : public ResourceDownloadDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit ShaderPackDownloadDialog(QWidget* parent, ShaderPackFolderModel* shaders, BaseInstance* instance, bool suppressInitialSearch = false); explicit ShaderPackDownloadDialog(QWidget* parent,
ShaderPackFolderModel* shaders,
BaseInstance* instance,
bool suppressInitialSearch = false);
~ShaderPackDownloadDialog() override = default; ~ShaderPackDownloadDialog() override = default;
//: String that gets appended to the shader pack download dialog title ("Download " + resourcesString()) //: String that gets appended to the shader pack download dialog title ("Download " + resourcesString())
@ -172,7 +181,10 @@ class DataPackDownloadDialog final : public ResourceDownloadDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit DataPackDownloadDialog(QWidget* parent, DataPackFolderModel* dataPacks, BaseInstance* instance, bool suppressInitialSearch = false); explicit DataPackDownloadDialog(QWidget* parent,
DataPackFolderModel* dataPacks,
BaseInstance* instance,
bool suppressInitialSearch = false);
~DataPackDownloadDialog() override = default; ~DataPackDownloadDialog() override = default;
//: String that gets appended to the data pack download dialog title ("Download " + resourcesString()) //: String that gets appended to the data pack download dialog title ("Download " + resourcesString())

View file

@ -29,10 +29,12 @@
#include <optional> #include <optional>
static std::vector<Version> mcVersions(BaseInstance* inst) namespace {
std::vector<Version> mcVersions(BaseInstance* inst)
{ {
return { static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion() }; return { static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion() };
} }
} // namespace
ResourceUpdateDialog::ResourceUpdateDialog(QWidget* parent, ResourceUpdateDialog::ResourceUpdateDialog(QWidget* parent,
BaseInstance* instance, BaseInstance* instance,
@ -58,8 +60,8 @@ ResourceUpdateDialog::ResourceUpdateDialog(QWidget* parent,
void ResourceUpdateDialog::checkCandidates() void ResourceUpdateDialog::checkCandidates()
{ {
// Ensure mods have valid metadata // Ensure mods have valid metadata
auto went_well = ensureMetadata(); auto wentWell = ensureMetadata();
if (!went_well) { if (!wentWell) {
m_aborted = true; m_aborted = true;
return; return;
} }
@ -73,12 +75,12 @@ void ResourceUpdateDialog::checkCandidates()
text += tr("Mod name: %1<br>File name: %2<br>Reason: %3<br><br>").arg(mod->name(), mod->fileinfo().fileName(), reason); text += tr("Mod name: %1<br>File name: %2<br>Reason: %3<br><br>").arg(mod->name(), mod->fileinfo().fileName(), reason);
} }
ScrollMessageBox message_dialog(m_parent, tr("Metadata generation failed"), ScrollMessageBox messageDialog(m_parent, tr("Metadata generation failed"),
tr("Could not generate metadata for the following resources:<br>" tr("Could not generate metadata for the following resources:<br>"
"Do you wish to proceed without those resources?"), "Do you wish to proceed without those resources?"),
text); text);
message_dialog.setModal(true); messageDialog.setModal(true);
if (message_dialog.exec() == QDialog::Rejected) { if (messageDialog.exec() == QDialog::Rejected) {
m_aborted = true; m_aborted = true;
QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection);
return; return;
@ -87,40 +89,41 @@ void ResourceUpdateDialog::checkCandidates()
auto versions = mcVersions(m_instance); auto versions = mcVersions(m_instance);
SequentialTask check_task(tr("Checking for updates")); SequentialTask checkTask(tr("Checking for updates"));
if (!m_modrinthToUpdate.empty()) { if (!m_modrinthToUpdate.empty()) {
m_modrinthCheckTask.reset(new ModrinthCheckUpdate(m_modrinthToUpdate, versions, m_loadersList, m_resourceModel)); m_modrinthCheckTask.reset(new ModrinthCheckUpdate(m_modrinthToUpdate, versions, m_loadersList, m_resourceModel));
connect(m_modrinthCheckTask.get(), &CheckUpdateTask::checkFailed, this, connect(m_modrinthCheckTask.get(), &CheckUpdateTask::checkFailed, this,
[this](Resource* resource, QString reason, QUrl recover_url) { [this](Resource* resource, const QString& reason, const QUrl& recoverUrl) {
m_failedCheckUpdate.append({ resource, reason, recover_url }); m_failedCheckUpdate.append({ resource, reason, recoverUrl });
}); });
check_task.addTask(m_modrinthCheckTask); checkTask.addTask(m_modrinthCheckTask);
} }
if (!m_flameToUpdate.empty()) { if (!m_flameToUpdate.empty()) {
m_flameCheckTask.reset(new FlameCheckUpdate(m_flameToUpdate, versions, m_loadersList, m_resourceModel)); m_flameCheckTask.reset(new FlameCheckUpdate(m_flameToUpdate, versions, m_loadersList, m_resourceModel));
connect(m_flameCheckTask.get(), &CheckUpdateTask::checkFailed, this, [this](Resource* resource, QString reason, QUrl recover_url) { connect(m_flameCheckTask.get(), &CheckUpdateTask::checkFailed, this,
m_failedCheckUpdate.append({ resource, reason, recover_url }); [this](Resource* resource, const QString& reason, const QUrl& recoverUrl) {
m_failedCheckUpdate.append({ resource, reason, recoverUrl });
}); });
check_task.addTask(m_flameCheckTask); checkTask.addTask(m_flameCheckTask);
} }
connect(&check_task, &Task::failed, this, connect(&checkTask, &Task::failed, this,
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); [this](const QString& reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
connect(&check_task, &Task::succeeded, this, [this, &check_task]() { connect(&checkTask, &Task::succeeded, this, [this, &checkTask]() {
QStringList warnings = check_task.warnings(); QStringList warnings = checkTask.warnings();
if (warnings.count()) { if (warnings.count()) {
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->exec(); CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->exec();
} }
}); });
// Check for updates // Check for updates
ProgressDialog progress_dialog(m_parent); ProgressDialog progressDialog(m_parent);
progress_dialog.setSkipButton(true, tr("Abort")); progressDialog.setSkipButton(true, tr("Abort"));
progress_dialog.setWindowTitle(tr("Checking for updates...")); progressDialog.setWindowTitle(tr("Checking for updates..."));
auto ret = progress_dialog.execWithTask(&check_task); auto ret = progressDialog.execWithTask(&checkTask);
// If the dialog was skipped / some download error happened // If the dialog was skipped / some download error happened
if (ret == QDialog::DialogCode::Rejected) { if (ret == QDialog::DialogCode::Rejected) {
@ -133,8 +136,8 @@ void ResourceUpdateDialog::checkCandidates()
// Add found updates for Modrinth // Add found updates for Modrinth
if (m_modrinthCheckTask) { if (m_modrinthCheckTask) {
auto modrinth_updates = m_modrinthCheckTask->getUpdates(); auto modrinthUpdates = m_modrinthCheckTask->getUpdates();
for (auto& updatable : modrinth_updates) { for (auto& updatable : modrinthUpdates) {
qDebug() << QString("Mod %1 has an update available!").arg(updatable.name); qDebug() << QString("Mod %1 has an update available!").arg(updatable.name);
appendResource(updatable); appendResource(updatable);
@ -145,8 +148,8 @@ void ResourceUpdateDialog::checkCandidates()
// Add found updated for Flame // Add found updated for Flame
if (m_flameCheckTask) { if (m_flameCheckTask) {
auto flame_updates = m_flameCheckTask->getUpdates(); auto flameUpdates = m_flameCheckTask->getUpdates();
for (auto& updatable : flame_updates) { for (auto& updatable : flameUpdates) {
qDebug() << QString("Mod %1 has an update available!").arg(updatable.name); qDebug() << QString("Mod %1 has an update available!").arg(updatable.name);
appendResource(updatable); appendResource(updatable);
@ -161,33 +164,35 @@ void ResourceUpdateDialog::checkCandidates()
for (const auto& failed : m_failedCheckUpdate) { for (const auto& failed : m_failedCheckUpdate) {
const auto& mod = std::get<0>(failed); const auto& mod = std::get<0>(failed);
const auto& reason = std::get<1>(failed); const auto& reason = std::get<1>(failed);
const auto& recover_url = std::get<2>(failed); const auto& recoverUrl = 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()) + "<br>"; text += tr("Mod name: %1").arg(mod->name()) + "<br>";
if (!reason.isEmpty()) if (!reason.isEmpty()) {
text += tr("Reason: %1").arg(reason) + "<br>"; text += tr("Reason: %1").arg(reason) + "<br>";
if (!recover_url.isEmpty()) }
if (!recoverUrl.isEmpty()) {
//: %1 is the link to download it manually //: %1 is the link to download it manually
text += tr("Possible solution: Getting the latest version manually:<br>%1<br>") text += tr("Possible solution: Getting the latest version manually:<br>%1<br>")
.arg(QString("<a href='%1'>%1</a>").arg(recover_url.toString())); .arg(QString("<a href='%1'>%1</a>").arg(recoverUrl.toString()));
}
text += "<br>"; text += "<br>";
} }
ScrollMessageBox message_dialog(m_parent, tr("Failed to check for updates"), ScrollMessageBox messageDialog(m_parent, tr("Failed to check for updates"),
tr("Could not check or get the following resources for updates:<br>" tr("Could not check or get the following resources for updates:<br>"
"Do you wish to proceed without those resources?"), "Do you wish to proceed without those resources?"),
text, "Disable unavailable mods"); text, "Disable unavailable mods");
message_dialog.setModal(true); messageDialog.setModal(true);
if (message_dialog.exec() == QDialog::Rejected) { if (messageDialog.exec() == QDialog::Rejected) {
m_aborted = true; m_aborted = true;
QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection);
return; return;
} }
// Disable unavailable mods // Disable unavailable mods
if (message_dialog.isOptionChecked()) { if (messageDialog.isOptionChecked()) {
for (const auto& failed : m_failedCheckUpdate) { for (const auto& failed : m_failedCheckUpdate) {
const auto& mod = std::get<0>(failed); const auto& mod = std::get<0>(failed);
mod->enable(EnableAction::DISABLE); mod->enable(EnableAction::DISABLE);
@ -196,10 +201,10 @@ void ResourceUpdateDialog::checkCandidates()
} }
if (m_includeDeps && !APPLICATION->settings()->get("ModDependenciesDisabled").toBool()) { // dependencies if (m_includeDeps && !APPLICATION->settings()->get("ModDependenciesDisabled").toBool()) { // dependencies
auto* mod_model = dynamic_cast<ModFolderModel*>(m_resourceModel); auto* modModel = dynamic_cast<ModFolderModel*>(m_resourceModel);
if (mod_model != nullptr) { if (modModel != nullptr) {
auto depTask = makeShared<GetModDependenciesTask>(m_instance, mod_model, selectedVers); auto depTask = makeShared<GetModDependenciesTask>(m_instance, modModel, selectedVers);
connect(depTask.get(), &Task::failed, this, [this](const QString& reason) { connect(depTask.get(), &Task::failed, this, [this](const QString& reason) {
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec();
@ -215,10 +220,10 @@ void ResourceUpdateDialog::checkCandidates()
} }
}); });
ProgressDialog progress_dialog_deps(m_parent); ProgressDialog progressDialogDeps(m_parent);
progress_dialog_deps.setSkipButton(true, tr("Abort")); progressDialogDeps.setSkipButton(true, tr("Abort"));
progress_dialog_deps.setWindowTitle(tr("Checking for dependencies...")); progressDialogDeps.setWindowTitle(tr("Checking for dependencies..."));
auto dret = progress_dialog_deps.execWithTask(depTask.get()); auto dret = progressDialogDeps.execWithTask(depTask.get());
// If the dialog was skipped / some download error happened // If the dialog was skipped / some download error happened
if (dret == QDialog::DialogCode::Rejected) { if (dret == QDialog::DialogCode::Rejected) {
@ -226,19 +231,20 @@ void ResourceUpdateDialog::checkCandidates()
QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection);
return; return;
} }
static FlameAPI api; static FlameAPI s_api;
auto dependencyExtraInfo = depTask->getExtraInfo(); auto dependencyExtraInfo = depTask->getExtraInfo();
for (const auto& dep : depTask->getDependecies()) { for (const auto& dep : depTask->getDependecies()) {
auto changelog = dep->version.changelog; auto changelog = dep->version.changelog;
if (dep->pack->provider == ModPlatform::ResourceProvider::FLAME) if (dep->pack->provider == ModPlatform::ResourceProvider::FLAME) {
changelog = api.getModFileChangelog(dep->version.addonId.toInt(), dep->version.fileId.toInt()); changelog = s_api.getModFileChangelog(dep->version.addonId.toInt(), dep->version.fileId.toInt());
auto download_task = makeShared<ResourceDownloadTask>(dep->pack, dep->version, m_resourceModel, true, "dependency"); }
auto downloadTask = makeShared<ResourceDownloadTask>(dep->pack, dep->version, m_resourceModel, true, "dependency");
auto extraInfo = dependencyExtraInfo.value(dep->version.addonId.toString()); auto extraInfo = dependencyExtraInfo.value(dep->version.addonId.toString());
CheckUpdateTask::Update updatable = { CheckUpdateTask::Update updatable = {
dep->pack->name, dep->version.hash, tr("Not installed"), dep->version.version, dep->version.version_type, dep->pack->name, dep->version.hash, tr("Not installed"), dep->version.version, dep->version.version_type,
changelog, dep->pack->provider, download_task, !extraInfo.maybe_installed changelog, dep->pack->provider, downloadTask, !extraInfo.maybe_installed
}; };
appendResource(updatable, extraInfo.required_by); appendResource(updatable, extraInfo.required_by);
@ -264,56 +270,58 @@ void ResourceUpdateDialog::checkCandidates()
} }
} }
if (m_aborted || m_noUpdates) if (m_aborted || m_noUpdates) {
QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection);
}
} }
// Part 1: Ensure we have a valid metadata // Part 1: Ensure we have a valid metadata
auto ResourceUpdateDialog::ensureMetadata() -> bool auto ResourceUpdateDialog::ensureMetadata() -> bool
{ {
auto index_dir = indexDir(); auto indexDir2 = indexDir();
SequentialTask seq(tr("Looking for metadata")); SequentialTask seq(tr("Looking for metadata"));
// A better use of data structures here could remove the need for this QHash // A better use of data structures here could remove the need for this QHash
QHash<QString, bool> should_try_others; QHash<QString, bool> shouldTryOthers;
QList<Resource*> modrinth_tmp; QList<Resource*> modrinthTmp;
QList<Resource*> flame_tmp; QList<Resource*> flameTmp;
bool confirm_rest = false; bool confirmRest = false;
bool try_others_rest = false; bool tryOthersRest = false;
bool skip_rest = false; bool skipRest = false;
ModPlatform::ResourceProvider provider_rest = ModPlatform::ResourceProvider::MODRINTH; ModPlatform::ResourceProvider providerRest = ModPlatform::ResourceProvider::MODRINTH;
// adds resource to list based on provider // adds resource to list based on provider
auto addToTmp = [&modrinth_tmp, &flame_tmp](Resource* resource, ModPlatform::ResourceProvider p) { auto addToTmp = [&modrinthTmp, &flameTmp](Resource* resource, ModPlatform::ResourceProvider p) {
switch (p) { switch (p) {
case ModPlatform::ResourceProvider::MODRINTH: case ModPlatform::ResourceProvider::MODRINTH:
modrinth_tmp.push_back(resource); modrinthTmp.push_back(resource);
break; break;
case ModPlatform::ResourceProvider::FLAME: case ModPlatform::ResourceProvider::FLAME:
flame_tmp.push_back(resource); flameTmp.push_back(resource);
break; break;
} }
}; };
// ask the user on what provider to seach for the mod first // ask the user on what provider to seach for the mod first
for (auto candidate : m_candidates) { for (auto* candidate : m_candidates) {
if (candidate->status() != ResourceStatus::NO_METADATA) { if (candidate->status() != ResourceStatus::NO_METADATA) {
onMetadataEnsured(candidate); onMetadataEnsured(candidate);
continue; continue;
} }
if (skip_rest) if (skipRest) {
continue; continue;
}
if (candidate->type() == ResourceType::FOLDER) { if (candidate->type() == ResourceType::FOLDER) {
continue; continue;
} }
if (confirm_rest) { if (confirmRest) {
addToTmp(candidate, provider_rest); addToTmp(candidate, providerRest);
should_try_others.insert(candidate->internal_id(), try_others_rest); shouldTryOthers.insert(candidate->internal_id(), tryOthersRest);
continue; continue;
} }
@ -326,68 +334,73 @@ auto ResourceUpdateDialog::ensureMetadata() -> bool
auto response = chooser.getResponse(); auto response = chooser.getResponse();
if (response.skip_all) if (response.skip_all) {
skip_rest = true; skipRest = true;
}
if (response.confirm_all) { if (response.confirm_all) {
confirm_rest = true; confirmRest = true;
provider_rest = response.chosen; providerRest = response.chosen;
try_others_rest = response.try_others; tryOthersRest = response.try_others;
} }
should_try_others.insert(candidate->internal_id(), response.try_others); shouldTryOthers.insert(candidate->internal_id(), response.try_others);
if (confirmed) if (confirmed) {
addToTmp(candidate, response.chosen); addToTmp(candidate, response.chosen);
} }
}
// prepare task for the modrinth mods // prepare task for the modrinth mods
if (!modrinth_tmp.empty()) { if (!modrinthTmp.empty()) {
auto modrinth_task = makeShared<EnsureMetadataTask>(modrinth_tmp, index_dir, ModPlatform::ResourceProvider::MODRINTH); auto modrinthTask = makeShared<EnsureMetadataTask>(modrinthTmp, indexDir2, ModPlatform::ResourceProvider::MODRINTH);
connect(modrinth_task.get(), &EnsureMetadataTask::metadataReady, [this](Resource* candidate) { onMetadataEnsured(candidate); }); connect(modrinthTask.get(), &EnsureMetadataTask::metadataReady, [this](Resource* candidate) { onMetadataEnsured(candidate); });
connect(modrinth_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Resource* candidate) { connect(modrinthTask.get(), &EnsureMetadataTask::metadataFailed, [this, &shouldTryOthers](Resource* candidate) {
onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::MODRINTH); onMetadataFailed(candidate, shouldTryOthers.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::MODRINTH);
}); });
connect(modrinth_task.get(), &EnsureMetadataTask::failed, connect(modrinthTask.get(), &EnsureMetadataTask::failed,
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); [this](const QString& reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
if (modrinth_task->getHashingTask()) if (modrinthTask->getHashingTask()) {
seq.addTask(modrinth_task->getHashingTask()); seq.addTask(modrinthTask->getHashingTask());
}
seq.addTask(modrinth_task); seq.addTask(modrinthTask);
} }
// prepare task for the flame mods // prepare task for the flame mods
if (!flame_tmp.empty()) { if (!flameTmp.empty()) {
auto flame_task = makeShared<EnsureMetadataTask>(flame_tmp, index_dir, ModPlatform::ResourceProvider::FLAME); auto flameTask = makeShared<EnsureMetadataTask>(flameTmp, indexDir2, ModPlatform::ResourceProvider::FLAME);
connect(flame_task.get(), &EnsureMetadataTask::metadataReady, [this](Resource* candidate) { onMetadataEnsured(candidate); }); connect(flameTask.get(), &EnsureMetadataTask::metadataReady, [this](Resource* candidate) { onMetadataEnsured(candidate); });
connect(flame_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Resource* candidate) { connect(flameTask.get(), &EnsureMetadataTask::metadataFailed, [this, &shouldTryOthers](Resource* candidate) {
onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::FLAME); onMetadataFailed(candidate, shouldTryOthers.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::FLAME);
}); });
connect(flame_task.get(), &EnsureMetadataTask::failed, connect(flameTask.get(), &EnsureMetadataTask::failed,
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); [this](const QString& reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
if (flame_task->getHashingTask()) if (flameTask->getHashingTask()) {
seq.addTask(flame_task->getHashingTask()); seq.addTask(flameTask->getHashingTask());
}
seq.addTask(flame_task); seq.addTask(flameTask);
} }
seq.addTask(m_secondTryMetadata); seq.addTask(m_secondTryMetadata);
// execute all the tasks // execute all the tasks
ProgressDialog checking_dialog(m_parent); ProgressDialog checkingDialog(m_parent);
checking_dialog.setSkipButton(true, tr("Abort")); checkingDialog.setSkipButton(true, tr("Abort"));
checking_dialog.setWindowTitle(tr("Generating metadata...")); checkingDialog.setWindowTitle(tr("Generating metadata..."));
auto ret_metadata = checking_dialog.execWithTask(&seq); auto retMetadata = checkingDialog.execWithTask(&seq);
return (ret_metadata != QDialog::DialogCode::Rejected); return (retMetadata != QDialog::DialogCode::Rejected);
} }
void ResourceUpdateDialog::onMetadataEnsured(Resource* resource) void ResourceUpdateDialog::onMetadataEnsured(Resource* resource)
{ {
// When the mod is a folder, for instance // When the mod is a folder, for instance
if (!resource->metadata()) if (!resource->metadata()) {
return; return;
}
switch (resource->metadata()->provider) { switch (resource->metadata()->provider) {
case ModPlatform::ResourceProvider::MODRINTH: case ModPlatform::ResourceProvider::MODRINTH:
@ -411,12 +424,12 @@ ModPlatform::ResourceProvider next(ModPlatform::ResourceProvider p)
return ModPlatform::ResourceProvider::FLAME; return ModPlatform::ResourceProvider::FLAME;
} }
void ResourceUpdateDialog::onMetadataFailed(Resource* resource, bool try_others, ModPlatform::ResourceProvider first_choice) void ResourceUpdateDialog::onMetadataFailed(Resource* resource, bool tryOthers, ModPlatform::ResourceProvider firstChoice)
{ {
if (try_others) { if (tryOthers) {
auto index_dir = indexDir(); auto indexDir2 = indexDir();
auto task = makeShared<EnsureMetadataTask>(resource, index_dir, next(first_choice)); auto task = makeShared<EnsureMetadataTask>(resource, indexDir2, next(firstChoice));
connect(task.get(), &EnsureMetadataTask::metadataReady, [this](Resource* candidate) { onMetadataEnsured(candidate); }); connect(task.get(), &EnsureMetadataTask::metadataReady, [this](Resource* candidate) { onMetadataEnsured(candidate); });
connect(task.get(), &EnsureMetadataTask::metadataFailed, [this](Resource* candidate) { onMetadataFailed(candidate, false); }); connect(task.get(), &EnsureMetadataTask::metadataFailed, [this](Resource* candidate) { onMetadataFailed(candidate, false); });
connect(task.get(), &EnsureMetadataTask::failed, connect(task.get(), &EnsureMetadataTask::failed,
@ -436,57 +449,57 @@ void ResourceUpdateDialog::onMetadataFailed(Resource* resource, bool try_others,
} }
} }
void ResourceUpdateDialog::appendResource(CheckUpdateTask::Update const& info, QStringList requiredBy) void ResourceUpdateDialog::appendResource(const CheckUpdateTask::Update& info, QStringList requiredBy)
{ {
auto item_top = new QTreeWidgetItem(ui->modTreeWidget); auto* itemTop = new QTreeWidgetItem(ui->modTreeWidget);
item_top->setCheckState(0, info.enabled ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); itemTop->setCheckState(0, info.enabled ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
if (!info.enabled) { if (!info.enabled) {
item_top->setToolTip(0, tr("Mod was disabled as it may be already installed.")); itemTop->setToolTip(0, tr("Mod was disabled as it may be already installed."));
} }
item_top->setText(0, info.name); itemTop->setText(0, info.name);
item_top->setExpanded(true); itemTop->setExpanded(true);
auto provider_item = new QTreeWidgetItem(item_top); auto* providerItem = new QTreeWidgetItem(itemTop);
QString provider_name = ModPlatform::ProviderCapabilities::readableName(info.provider); QString providerName = ModPlatform::ProviderCapabilities::readableName(info.provider);
provider_item->setText(0, tr("Provider: %1").arg(provider_name)); providerItem->setText(0, tr("Provider: %1").arg(providerName));
provider_item->setData(0, Qt::UserRole, provider_name); providerItem->setData(0, Qt::UserRole, providerName);
auto old_version_item = new QTreeWidgetItem(item_top); auto* oldVersionItem = new QTreeWidgetItem(itemTop);
old_version_item->setText(0, tr("Old version: %1").arg(info.old_version)); oldVersionItem->setText(0, tr("Old version: %1").arg(info.old_version));
old_version_item->setData(0, Qt::UserRole, info.old_version); oldVersionItem->setData(0, Qt::UserRole, info.old_version);
auto new_version_item = new QTreeWidgetItem(item_top); auto* newVersionItem = new QTreeWidgetItem(itemTop);
new_version_item->setText(0, tr("New version: %1").arg(info.new_version)); newVersionItem->setText(0, tr("New version: %1").arg(info.new_version));
new_version_item->setData(0, Qt::UserRole, info.new_version); newVersionItem->setData(0, Qt::UserRole, info.new_version);
if (info.new_version_type.has_value()) { if (info.new_version_type.has_value()) {
auto new_version_type_item = new QTreeWidgetItem(item_top); auto* newVersionTypeItem = new QTreeWidgetItem(itemTop);
new_version_type_item->setText(0, tr("New Version Type: %1").arg(info.new_version_type.value().toString())); newVersionTypeItem->setText(0, tr("New Version Type: %1").arg(info.new_version_type.value().toString()));
new_version_type_item->setData(0, Qt::UserRole, info.new_version_type.value().toString()); newVersionTypeItem->setData(0, Qt::UserRole, info.new_version_type.value().toString());
} }
if (!requiredBy.isEmpty()) { if (!requiredBy.isEmpty()) {
auto requiredByItem = new QTreeWidgetItem(item_top); auto* requiredByItem = new QTreeWidgetItem(itemTop);
if (requiredBy.length() == 1) { if (requiredBy.length() == 1) {
requiredByItem->setText(0, tr("Required by: %1").arg(requiredBy.back())); requiredByItem->setText(0, tr("Required by: %1").arg(requiredBy.back()));
requiredByItem->setData(0, Qt::UserRole, requiredBy.back()); requiredByItem->setData(0, Qt::UserRole, requiredBy.back());
} else { } else {
requiredByItem->setText(0, tr("Required by:")); requiredByItem->setText(0, tr("Required by:"));
for (auto req : requiredBy) { for (const auto& req : requiredBy) {
auto reqItem = new QTreeWidgetItem(requiredByItem); auto* reqItem = new QTreeWidgetItem(requiredByItem);
reqItem->setText(0, req); reqItem->setText(0, req);
} }
} }
ui->toggleDepsButton->show(); ui->toggleDepsButton->show();
m_deps << item_top; m_deps << itemTop;
} }
auto changelog_item = new QTreeWidgetItem(item_top); auto* changelogItem = new QTreeWidgetItem(itemTop);
changelog_item->setText(0, tr("Changelog of the latest version")); changelogItem->setText(0, tr("Changelog of the latest version"));
auto changelog = new QTreeWidgetItem(changelog_item); auto* changelog = new QTreeWidgetItem(changelogItem);
auto changelog_area = new QTextBrowser(); auto* changelogArea = new QTextBrowser();
QString text = info.changelog; QString text = info.changelog;
changelog->setData(0, Qt::UserRole, text); changelog->setData(0, Qt::UserRole, text);
@ -494,14 +507,14 @@ void ResourceUpdateDialog::appendResource(CheckUpdateTask::Update const& info, Q
text = markdownToHTML(info.changelog.toUtf8()); text = markdownToHTML(info.changelog.toUtf8());
} }
changelog_area->setHtml(StringUtils::htmlListPatch(text)); changelogArea->setHtml(StringUtils::htmlListPatch(text));
changelog_area->setOpenExternalLinks(true); changelogArea->setOpenExternalLinks(true);
changelog_area->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth); changelogArea->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth);
changelog_area->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); changelogArea->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded);
ui->modTreeWidget->setItemWidget(changelog, 0, changelog_area); ui->modTreeWidget->setItemWidget(changelog, 0, changelogArea);
ui->modTreeWidget->addTopLevelItem(item_top); ui->modTreeWidget->addTopLevelItem(itemTop);
} }
auto ResourceUpdateDialog::getTasks() -> const QList<ResourceDownloadTask::Ptr> auto ResourceUpdateDialog::getTasks() -> const QList<ResourceDownloadTask::Ptr>

View file

@ -39,7 +39,7 @@ class ResourceUpdateDialog final : public ReviewMessageBox {
private slots: private slots:
void onMetadataEnsured(Resource* resource); void onMetadataEnsured(Resource* resource);
void onMetadataFailed(Resource* resource, void onMetadataFailed(Resource* resource,
bool try_others = false, bool tryOthers = false,
ModPlatform::ResourceProvider firstChoice = ModPlatform::ResourceProvider::MODRINTH); ModPlatform::ResourceProvider firstChoice = ModPlatform::ResourceProvider::MODRINTH);
private: private:

View file

@ -50,7 +50,6 @@
#include "ResourceDownloadTask.h" #include "ResourceDownloadTask.h"
#include "minecraft/MinecraftInstance.h" #include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h"
#include "ui/dialogs/ResourceDownloadDialog.h" #include "ui/dialogs/ResourceDownloadDialog.h"
@ -63,15 +62,15 @@ ModPage::ModPage(ModDownloadDialog* dialog, BaseInstance& instance) : ResourcePa
void ModPage::setFilterWidget(std::unique_ptr<ModFilterWidget>& widget) void ModPage::setFilterWidget(std::unique_ptr<ModFilterWidget>& widget)
{ {
if (m_filter_widget) if (m_filter_widget) {
disconnect(m_filter_widget.get(), nullptr, nullptr, nullptr); disconnect(m_filter_widget.get(), nullptr, nullptr, nullptr);
auto old = m_ui->splitter->replaceWidget(0, widget.get());
// because we replaced the widget we also need to delete it
if (old) {
delete old;
} }
auto* old = m_ui->splitter->replaceWidget(0, widget.get());
// because we replaced the widget we also need to delete it
delete old;
m_filter_widget.swap(widget); m_filter_widget.swap(widget);
m_filter = m_filter_widget->getFilter(); m_filter = m_filter_widget->getFilter();
@ -112,10 +111,13 @@ QMap<QString, QString> ModPage::urlHandlers() const
/******** Make changes to the UI ********/ /******** Make changes to the UI ********/
void ModPage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion& version, ResourceFolderModel* base_model, QString downloadReason) void ModPage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion& version,
ResourceFolderModel* baseModel,
QString downloadReason)
{ {
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); bool isIndexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
m_model->addPack(pack, version, base_model, is_indexed, downloadReason); m_model->addPack(pack, version, baseModel, isIndexed, downloadReason);
} }
} // namespace ResourceDownload } // namespace ResourceDownload

View file

@ -29,10 +29,10 @@ class ModPage : public ResourcePage {
static T* create(ModDownloadDialog* dialog, BaseInstance& instance) static T* create(ModDownloadDialog* dialog, BaseInstance& instance)
{ {
auto page = new T(dialog, instance); auto page = new T(dialog, instance);
auto model = static_cast<ModModel*>(page->getModel()); auto* model = static_cast<ModModel*>(page->getModel());
auto filter_widget = page->createFilterWidget(); auto filterWidget = page->createFilterWidget();
page->setFilterWidget(filter_widget); page->setFilterWidget(filterWidget);
model->setFilter(page->getFilter()); model->setFilter(page->getFilter());
connect(model, &ResourceModel::versionListUpdated, page, &ResourcePage::versionListUpdated); connect(model, &ResourceModel::versionListUpdated, page, &ResourcePage::versionListUpdated);
@ -43,18 +43,21 @@ class ModPage : public ResourcePage {
} }
//: The plural version of 'mod' //: The plural version of 'mod'
inline QString resourcesString() const override { return tr("mods"); } QString resourcesString() const override { return tr("mods"); }
//: The singular version of 'mods' //: The singular version of 'mods'
inline QString resourceString() const override { return tr("mod"); } QString resourceString() const override { return tr("mod"); }
QMap<QString, QString> urlHandlers() const override; QMap<QString, QString> urlHandlers() const override;
void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, ResourceFolderModel*, QString downloadReason = "standalone") override; void addResourceToPage(ModPlatform::IndexedPack::Ptr /*unused*/,
ModPlatform::IndexedVersion& /*unused*/,
ResourceFolderModel* /*unused*/,
QString downloadReason = "standalone") override;
virtual std::unique_ptr<ModFilterWidget> createFilterWidget() = 0; virtual std::unique_ptr<ModFilterWidget> createFilterWidget() = 0;
bool supportsFiltering() const override { return true; }; bool supportsFiltering() const override { return true; };
auto getFilter() const -> const std::shared_ptr<ModFilterWidget::Filter> { return m_filter; } auto getFilter() const -> std::shared_ptr<ModFilterWidget::Filter> { return m_filter; }
void setFilterWidget(std::unique_ptr<ModFilterWidget>&); void setFilterWidget(std::unique_ptr<ModFilterWidget>&);
protected: protected:

View file

@ -12,10 +12,11 @@
#include <QUrl> #include <QUrl>
#include <algorithm> #include <algorithm>
#include <memory> #include <memory>
#include <utility>
#include "Application.h" #include "Application.h"
#include "settings/SettingsObject.h"
#include "BuildConfig.h" #include "BuildConfig.h"
#include "settings/SettingsObject.h"
#include "modplatform/ResourceAPI.h" #include "modplatform/ResourceAPI.h"
#include "net/ApiDownload.h" #include "net/ApiDownload.h"
@ -29,7 +30,7 @@ namespace ResourceDownload {
QHash<ResourceModel*, bool> ResourceModel::s_running_models; QHash<ResourceModel*, bool> ResourceModel::s_running_models;
ResourceModel::ResourceModel(ResourceAPI* api) : QAbstractListModel(), m_api(api) ResourceModel::ResourceModel(ResourceAPI* api) : m_api(api)
{ {
s_running_models.insert(this, true); s_running_models.insert(this, true);
if (APPLICATION_DYN) { if (APPLICATION_DYN) {
@ -62,14 +63,14 @@ auto ResourceModel::data(const QModelIndex& index, int role) const -> QVariant
} }
case Qt::DecorationRole: { case Qt::DecorationRole: {
if (APPLICATION_DYN) { if (APPLICATION_DYN) {
if (auto icon_or_none = const_cast<ResourceModel*>(this)->getIcon(const_cast<QModelIndex&>(index), pack->logoUrl); if (auto iconOrNone = const_cast<ResourceModel*>(this)->getIcon(const_cast<QModelIndex&>(index), pack->logoUrl);
icon_or_none.has_value()) iconOrNone.has_value()) {
return icon_or_none.value(); return iconOrNone.value();
}
return QIcon::fromTheme("screenshot-placeholder"); return QIcon::fromTheme("screenshot-placeholder");
} else {
return {};
} }
return {};
} }
case Qt::SizeHintRole: case Qt::SizeHintRole:
return QSize(0, 58); return QSize(0, 58);
@ -112,8 +113,9 @@ QHash<int, QByteArray> ResourceModel::roleNames() const
bool ResourceModel::setData(const QModelIndex& index, const QVariant& value, [[maybe_unused]] int role) bool ResourceModel::setData(const QModelIndex& index, const QVariant& value, [[maybe_unused]] int role)
{ {
int pos = index.row(); int pos = index.row();
if (pos >= m_packs.size() || pos < 0 || !index.isValid()) if (pos >= m_packs.size() || pos < 0 || !index.isValid()) {
return false; return false;
}
m_packs[pos] = value.value<ModPlatform::IndexedPack::Ptr>(); m_packs[pos] = value.value<ModPlatform::IndexedPack::Ptr>();
emit dataChanged(index, index); emit dataChanged(index, index);
@ -128,45 +130,51 @@ QString ResourceModel::debugName() const
void ResourceModel::fetchMore(const QModelIndex& parent) void ResourceModel::fetchMore(const QModelIndex& parent)
{ {
if (parent.isValid() || m_search_state == SearchState::Finished) if (parent.isValid() || m_search_state == SearchState::Finished) {
return; return;
}
search(); search();
} }
void ResourceModel::search() void ResourceModel::search()
{ {
if (hasActiveSearchJob()) if (hasActiveSearchJob()) {
return; return;
}
if (m_search_state != SearchState::ResetRequested && m_search_term.startsWith("#")) { if (m_search_state != SearchState::ResetRequested && m_search_term.startsWith("#")) {
auto projectId = m_search_term.mid(1); auto projectId = m_search_term.mid(1);
if (!projectId.isEmpty()) { if (!projectId.isEmpty()) {
ResourceAPI::Callback<ModPlatform::IndexedPack::Ptr> callbacks; ResourceAPI::Callback<ModPlatform::IndexedPack::Ptr> callbacks;
callbacks.on_fail = [this](QString reason, int network_error_code) { callbacks.on_fail = [this](QString reason, int networkErrorCode) {
if (!s_running_models.constFind(this).value()) if (!s_running_models.constFind(this).value()) {
return; return;
if (network_error_code == 404) { }
if (networkErrorCode == 404) {
m_search_state = SearchState::ResetRequested; m_search_state = SearchState::ResetRequested;
} }
searchRequestFailed(reason, network_error_code); searchRequestFailed(std::move(reason), networkErrorCode);
}; };
callbacks.on_abort = [this] { callbacks.on_abort = [this] {
if (!s_running_models.constFind(this).value()) if (!s_running_models.constFind(this).value()) {
return; return;
}
searchRequestAborted(); searchRequestAborted();
}; };
callbacks.on_succeed = [this](auto& pack) { callbacks.on_succeed = [this](auto& pack) {
if (!s_running_models.constFind(this).value()) if (!s_running_models.constFind(this).value()) {
return; return;
}
searchRequestForOneSucceeded(pack); searchRequestForOneSucceeded(pack);
}; };
auto project = std::make_shared<ModPlatform::IndexedPack>(); auto project = std::make_shared<ModPlatform::IndexedPack>();
project->addonId = projectId; project->addonId = projectId;
if (auto job = m_api->getProjectInfo({ project }, std::move(callbacks), false); job) if (auto job = m_api->getProjectInfo({ project }, std::move(callbacks), false); job) {
runSearchJob(job); runSearchJob(job);
}
return; return;
} }
} }
@ -175,31 +183,36 @@ void ResourceModel::search()
ResourceAPI::Callback<QList<ModPlatform::IndexedPack::Ptr>> callbacks{}; ResourceAPI::Callback<QList<ModPlatform::IndexedPack::Ptr>> callbacks{};
callbacks.on_succeed = [this](auto& doc) { callbacks.on_succeed = [this](auto& doc) {
if (!s_running_models.constFind(this).value()) if (!s_running_models.constFind(this).value()) {
return; return;
}
searchRequestSucceeded(doc); searchRequestSucceeded(doc);
}; };
callbacks.on_fail = [this](QString reason, int network_error_code) { callbacks.on_fail = [this](QString reason, int networkErrorCode) {
if (!s_running_models.constFind(this).value()) if (!s_running_models.constFind(this).value()) {
return; return;
searchRequestFailed(reason, network_error_code); }
searchRequestFailed(std::move(reason), networkErrorCode);
}; };
callbacks.on_abort = [this] { callbacks.on_abort = [this] {
if (!s_running_models.constFind(this).value()) if (!s_running_models.constFind(this).value()) {
return; return;
}
searchRequestAborted(); searchRequestAborted();
}; };
if (auto job = m_api->searchProjects(std::move(args), std::move(callbacks)); job) if (auto job = m_api->searchProjects(std::move(args), std::move(callbacks)); job) {
runSearchJob(job); runSearchJob(job);
}
} }
void ResourceModel::loadEntry(const QModelIndex& entry) void ResourceModel::loadEntry(const QModelIndex& entry)
{ {
auto const& pack = m_packs[entry.row()]; const auto& pack = m_packs[entry.row()];
if (!hasActiveInfoJob()) if (!hasActiveInfoJob()) {
m_current_info_job.clear(); m_current_info_job.clear();
}
if (!pack->versionsLoaded) { if (!pack->versionsLoaded) {
auto args{ createVersionsArguments(entry) }; auto args{ createVersionsArguments(entry) };
@ -207,62 +220,70 @@ void ResourceModel::loadEntry(const QModelIndex& entry)
auto addonId = pack->addonId; auto addonId = pack->addonId;
// Use default if no callbacks are set // Use default if no callbacks are set
if (!callbacks.on_succeed) if (!callbacks.on_succeed) {
callbacks.on_succeed = [this, entry, addonId](auto& doc) { callbacks.on_succeed = [this, entry, addonId](auto& doc) {
if (!s_running_models.constFind(this).value()) if (!s_running_models.constFind(this).value()) {
return; return;
}
versionRequestSucceeded(doc, addonId, entry); versionRequestSucceeded(doc, addonId, entry);
}; };
if (!callbacks.on_fail) }
callbacks.on_fail = [](QString reason, int) { if (!callbacks.on_fail) {
callbacks.on_fail = [](const QString& reason, int) {
QMessageBox::critical(nullptr, tr("Error"), QMessageBox::critical(nullptr, tr("Error"),
tr("A network error occurred. Could not load project versions: %1").arg(reason)); tr("A network error occurred. Could not load project versions: %1").arg(reason));
}; };
}
if (auto job = m_api->getProjectVersions(std::move(args), std::move(callbacks)); job) if (auto job = m_api->getProjectVersions(std::move(args), std::move(callbacks)); job) {
runInfoJob(job); runInfoJob(job);
} }
}
if (!pack->extraDataLoaded) { if (!pack->extraDataLoaded) {
auto args{ createInfoArguments(entry) }; auto args{ createInfoArguments(entry) };
ResourceAPI::Callback<ModPlatform::IndexedPack::Ptr> callbacks{}; ResourceAPI::Callback<ModPlatform::IndexedPack::Ptr> callbacks{};
callbacks.on_succeed = [this, entry](auto& newpack) { callbacks.on_succeed = [this, entry](auto& newpack) {
if (!s_running_models.constFind(this).value()) if (!s_running_models.constFind(this).value()) {
return; return;
}
infoRequestSucceeded(newpack, entry); infoRequestSucceeded(newpack, entry);
}; };
callbacks.on_fail = [this](QString reason, int) { callbacks.on_fail = [this](const QString& reason, int) {
if (!s_running_models.constFind(this).value()) if (!s_running_models.constFind(this).value()) {
return; return;
}
QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load project info: %1").arg(reason)); QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load project info: %1").arg(reason));
}; };
callbacks.on_abort = [this] { callbacks.on_abort = [this] {
if (!s_running_models.constFind(this).value()) if (!s_running_models.constFind(this).value()) {
return; return;
}
qCritical() << tr("The request was aborted for an unknown reason"); qCritical() << tr("The request was aborted for an unknown reason");
}; };
if (auto job = m_api->getProjectInfo(std::move(args), std::move(callbacks)); job) if (auto job = m_api->getProjectInfo(std::move(args), std::move(callbacks)); job) {
runInfoJob(job); runInfoJob(job);
} }
}
} }
void ResourceModel::refresh() void ResourceModel::refresh()
{ {
bool reset_requested = false; bool resetRequested = false;
if (hasActiveInfoJob()) { if (hasActiveInfoJob()) {
m_current_info_job.abort(); m_current_info_job.abort();
reset_requested = true; resetRequested = true;
} }
if (hasActiveSearchJob()) { if (hasActiveSearchJob()) {
m_current_search_job->abort(); m_current_search_job->abort();
reset_requested = true; resetRequested = true;
} }
if (reset_requested) { if (resetRequested) {
m_search_state = SearchState::ResetRequested; m_search_state = SearchState::ResetRequested;
return; return;
} }
@ -288,13 +309,15 @@ void ResourceModel::runSearchJob(Task::Ptr ptr)
} }
void ResourceModel::runInfoJob(Task::Ptr ptr) void ResourceModel::runInfoJob(Task::Ptr ptr)
{ {
if (!m_current_info_job.isRunning()) if (!m_current_info_job.isRunning()) {
m_current_info_job.clear(); m_current_info_job.clear();
}
m_current_info_job.addTask(ptr); m_current_info_job.addTask(std::move(ptr));
if (!m_current_info_job.isRunning()) if (!m_current_info_job.isRunning()) {
m_current_info_job.run(); m_current_info_job.run();
}
} }
std::optional<ResourceAPI::SortingMethod> ResourceModel::getCurrentSortingMethodByIndex() const std::optional<ResourceAPI::SortingMethod> ResourceModel::getCurrentSortingMethodByIndex() const
@ -302,12 +325,13 @@ std::optional<ResourceAPI::SortingMethod> ResourceModel::getCurrentSortingMethod
std::optional<ResourceAPI::SortingMethod> sort{}; std::optional<ResourceAPI::SortingMethod> sort{};
{ // Find sorting method by ID { // Find sorting method by ID
auto sorting_methods = getSortingMethods(); auto sortingMethods = getSortingMethods();
auto method = std::find_if(sorting_methods.constBegin(), sorting_methods.constEnd(), auto method = std::find_if(sortingMethods.constBegin(), sortingMethods.constEnd(),
[this](auto const& e) { return m_current_sort_index == e.index; }); [this](const auto& e) { return m_current_sort_index == e.index; });
if (method != sorting_methods.constEnd()) if (method != sortingMethods.constEnd()) {
sort = *method; sort = *method;
} }
}
return sort; return sort;
} }
@ -315,43 +339,47 @@ std::optional<ResourceAPI::SortingMethod> ResourceModel::getCurrentSortingMethod
std::optional<QIcon> ResourceModel::getIcon(QModelIndex& index, const QUrl& url) std::optional<QIcon> ResourceModel::getIcon(QModelIndex& index, const QUrl& url)
{ {
QPixmap pixmap; QPixmap pixmap;
if (QPixmapCache::find(url.toString(), &pixmap)) if (QPixmapCache::find(url.toString(), &pixmap)) {
return { pixmap }; return { pixmap };
}
if (!m_current_icon_job) { if (!m_current_icon_job) {
m_current_icon_job.reset(new NetJob("IconJob", APPLICATION->network())); m_current_icon_job.reset(new NetJob("IconJob", APPLICATION->network()));
m_current_icon_job->setAskRetry(false); m_current_icon_job->setAskRetry(false);
} }
if (m_currently_running_icon_actions.contains(url)) if (m_currently_running_icon_actions.contains(url)) {
return {}; return {};
if (m_failed_icon_actions.contains(url)) }
if (m_failed_icon_actions.contains(url)) {
return {}; return {};
}
auto cache_entry = APPLICATION->metacache()->resolveEntry( auto cacheEntry = APPLICATION->metacache()->resolveEntry(
metaEntryBase(), metaEntryBase(),
QString("logos/%1").arg(QString(QCryptographicHash::hash(url.toEncoded(), QCryptographicHash::Algorithm::Sha1).toHex()))); QString("logos/%1").arg(QString(QCryptographicHash::hash(url.toEncoded(), QCryptographicHash::Algorithm::Sha1).toHex())));
auto icon_fetch_action = Net::ApiDownload::makeCached(url, cache_entry); auto iconFetchAction = Net::ApiDownload::makeCached(url, cacheEntry);
auto full_file_path = cache_entry->getFullPath(); auto fullFilePath = cacheEntry->getFullPath();
connect(icon_fetch_action.get(), &Task::succeeded, this, [this, url, full_file_path, index] { connect(iconFetchAction.get(), &Task::succeeded, this, [this, url, fullFilePath, index] {
auto icon = QIcon(full_file_path); auto icon = QIcon(fullFilePath);
QPixmapCache::insert(url.toString(), icon.pixmap(icon.actualSize({ 64, 64 }))); QPixmapCache::insert(url.toString(), icon.pixmap(icon.actualSize({ 64, 64 })));
m_currently_running_icon_actions.remove(url); m_currently_running_icon_actions.remove(url);
emit dataChanged(index, index, { Qt::DecorationRole }); emit dataChanged(index, index, { Qt::DecorationRole });
}); });
connect(icon_fetch_action.get(), &Task::failed, this, [this, url] { connect(iconFetchAction.get(), &Task::failed, this, [this, url] {
m_currently_running_icon_actions.remove(url); m_currently_running_icon_actions.remove(url);
m_failed_icon_actions.insert(url); m_failed_icon_actions.insert(url);
}); });
m_currently_running_icon_actions.insert(url); m_currently_running_icon_actions.insert(url);
m_current_icon_job->addNetAction(icon_fetch_action); m_current_icon_job->addNetAction(iconFetchAction);
if (!m_current_icon_job->isRunning()) if (!m_current_icon_job->isRunning()) {
QMetaObject::invokeMethod(m_current_icon_job.get(), &NetJob::start); QMetaObject::invokeMethod(m_current_icon_job.get(), &NetJob::start);
}
return {}; return {};
} }
@ -363,8 +391,8 @@ void ResourceModel::searchRequestSucceeded(QList<ModPlatform::IndexedPack::Ptr>&
QList<ModPlatform::IndexedPack::Ptr> filteredNewList; QList<ModPlatform::IndexedPack::Ptr> filteredNewList;
for (auto pack : newList) { for (auto pack : newList) {
ModPlatform::IndexedPack::Ptr p; ModPlatform::IndexedPack::Ptr p;
if (auto sel = std::find_if(m_selected.begin(), m_selected.end(), if (auto sel = std::ranges::find_if(m_selected,
[&pack](const DownloadTaskPtr i) { [&pack](const DownloadTaskPtr& i) {
const auto ipack = i->getPack(); const auto ipack = i->getPack();
return ipack->provider == pack->provider && ipack->addonId == pack->addonId; return ipack->provider == pack->provider && ipack->addonId == pack->addonId;
}); });
@ -386,8 +414,9 @@ void ResourceModel::searchRequestSucceeded(QList<ModPlatform::IndexedPack::Ptr>&
} }
// When you have a Qt build with assertions turned on, proceeding here will abort the application // When you have a Qt build with assertions turned on, proceeding here will abort the application
if (filteredNewList.size() == 0) if (filteredNewList.size() == 0) {
return; return;
}
beginInsertRows(QModelIndex(), m_packs.size(), m_packs.size() + filteredNewList.size() - 1); beginInsertRows(QModelIndex(), m_packs.size(), m_packs.size() + filteredNewList.size() - 1);
m_packs.append(filteredNewList); m_packs.append(filteredNewList);
@ -403,9 +432,9 @@ void ResourceModel::searchRequestForOneSucceeded(ModPlatform::IndexedPack::Ptr p
endInsertRows(); endInsertRows();
} }
void ResourceModel::searchRequestFailed([[maybe_unused]] QString reason, int network_error_code) void ResourceModel::searchRequestFailed([[maybe_unused]] QString reason, int networkErrorCode)
{ {
switch (network_error_code) { switch (networkErrorCode) {
default: default:
// Network error // Network error
QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load mods.")); QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load mods."));
@ -432,8 +461,9 @@ void ResourceModel::searchRequestFailed([[maybe_unused]] QString reason, int net
void ResourceModel::searchRequestAborted() void ResourceModel::searchRequestAborted()
{ {
if (m_search_state != SearchState::ResetRequested) if (m_search_state != SearchState::ResetRequested) {
qCritical() << "Search task in" << debugName() << "aborted by an unknown reason!"; qCritical() << "Search task in" << debugName() << "aborted by an unknown reason!";
}
// Retry fetching // Retry fetching
clearData(); clearData();
@ -444,19 +474,20 @@ void ResourceModel::searchRequestAborted()
void ResourceModel::versionRequestSucceeded(QVector<ModPlatform::IndexedVersion>& doc, QVariant pack, const QModelIndex& index) void ResourceModel::versionRequestSucceeded(QVector<ModPlatform::IndexedVersion>& doc, QVariant pack, const QModelIndex& index)
{ {
auto current_pack = data(index, Qt::UserRole).value<ModPlatform::IndexedPack::Ptr>(); auto currentPack = data(index, Qt::UserRole).value<ModPlatform::IndexedPack::Ptr>();
// Check if the index is still valid for this resource or not // Check if the index is still valid for this resource or not
if (pack != current_pack->addonId) if (pack != currentPack->addonId) {
return; return;
}
current_pack->versions = doc; currentPack->versions = doc;
current_pack->versionsLoaded = true; currentPack->versionsLoaded = true;
// Cache info :^) // Cache info :^)
QVariant new_pack; QVariant newPack;
new_pack.setValue(current_pack); newPack.setValue(currentPack);
if (!setData(index, new_pack, Qt::UserRole)) { if (!setData(index, newPack, Qt::UserRole)) {
qWarning() << "Failed to cache resource versions!"; qWarning() << "Failed to cache resource versions!";
return; return;
} }
@ -466,16 +497,17 @@ void ResourceModel::versionRequestSucceeded(QVector<ModPlatform::IndexedVersion>
void ResourceModel::infoRequestSucceeded(ModPlatform::IndexedPack::Ptr pack, const QModelIndex& index) void ResourceModel::infoRequestSucceeded(ModPlatform::IndexedPack::Ptr pack, const QModelIndex& index)
{ {
auto current_pack = data(index, Qt::UserRole).value<ModPlatform::IndexedPack::Ptr>(); auto currentPack = data(index, Qt::UserRole).value<ModPlatform::IndexedPack::Ptr>();
// Check if the index is still valid for this resource or not // Check if the index is still valid for this resource or not
if (pack->addonId != current_pack->addonId) if (pack->addonId != currentPack->addonId) {
return; return;
}
// Cache info :^) // Cache info :^)
QVariant new_pack; QVariant newPack;
new_pack.setValue(pack); newPack.setValue(pack);
if (!setData(index, new_pack, Qt::UserRole)) { if (!setData(index, newPack, Qt::UserRole)) {
qWarning() << "Failed to cache resource info!"; qWarning() << "Failed to cache resource info!";
return; return;
} }
@ -486,16 +518,16 @@ void ResourceModel::infoRequestSucceeded(ModPlatform::IndexedPack::Ptr pack, con
void ResourceModel::addPack(ModPlatform::IndexedPack::Ptr pack, void ResourceModel::addPack(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion& version, ModPlatform::IndexedVersion& version,
ResourceFolderModel* packs, ResourceFolderModel* packs,
bool is_indexed, bool isIndexed,
QString downloadReason) QString downloadReason)
{ {
version.is_currently_selected = true; version.is_currently_selected = true;
m_selected.append(makeShared<ResourceDownloadTask>(pack, version, packs, is_indexed, downloadReason)); m_selected.append(makeShared<ResourceDownloadTask>(std::move(pack), version, packs, isIndexed, std::move(downloadReason)));
} }
void ResourceModel::removePack(const QString& rem) void ResourceModel::removePack(const QString& rem)
{ {
auto pred = [&rem](const DownloadTaskPtr i) { return rem == i->getName(); }; auto pred = [&rem](const DownloadTaskPtr& i) { return rem == i->getName(); };
#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
m_selected.removeIf(pred); m_selected.removeIf(pred);
#else #else
@ -507,15 +539,16 @@ void ResourceModel::removePack(const QString& rem)
++it; ++it;
} }
#endif #endif
auto pack = std::find_if(m_packs.begin(), m_packs.end(), [&rem](const ModPlatform::IndexedPack::Ptr i) { return rem == i->name; }); auto pack = std::ranges::find_if(m_packs, [&rem](const ModPlatform::IndexedPack::Ptr& i) { return rem == i->name; });
if (pack == m_packs.end()) { // ignore it if is not in the current search if (pack == m_packs.end()) { // ignore it if is not in the current search
return; return;
} }
if (!pack->get()->versionsLoaded) { if (!pack->get()->versionsLoaded) {
return; return;
} }
for (auto& ver : pack->get()->versions) for (auto& ver : pack->get()->versions) {
ver.is_currently_selected = false; ver.is_currently_selected = false;
}
} }
bool ResourceModel::checkVersionFilters(const ModPlatform::IndexedVersion& v) bool ResourceModel::checkVersionFilters(const ModPlatform::IndexedVersion& v)

View file

@ -36,16 +36,16 @@ class ResourceModel : public QAbstractListModel {
ResourceModel(ResourceAPI* api); ResourceModel(ResourceAPI* api);
~ResourceModel() override; ~ResourceModel() override;
auto data(const QModelIndex&, int role) const -> QVariant override; auto data(const QModelIndex& /*index*/, int role) const -> QVariant override;
auto roleNames() const -> QHash<int, QByteArray> override; auto roleNames() const -> QHash<int, QByteArray> override;
bool setData(const QModelIndex& index, const QVariant& value, int role) override; bool setData(const QModelIndex& index, const QVariant& value, int role) override;
virtual auto debugName() const -> QString; virtual auto debugName() const -> QString;
virtual auto metaEntryBase() const -> QString = 0; virtual auto metaEntryBase() const -> QString = 0;
inline int rowCount(const QModelIndex& parent) const override { return parent.isValid() ? 0 : static_cast<int>(m_packs.size()); } int rowCount(const QModelIndex& parent) const override { return parent.isValid() ? 0 : static_cast<int>(m_packs.size()); }
inline int columnCount(const QModelIndex& parent) const override { return parent.isValid() ? 0 : 1; } int columnCount(const QModelIndex& parent) const override { return parent.isValid() ? 0 : 1; }
inline auto flags(const QModelIndex& index) const -> Qt::ItemFlags override { return QAbstractListModel::flags(index); } auto flags(const QModelIndex& index) const -> Qt::ItemFlags override { return QAbstractListModel::flags(index); }
bool hasActiveSearchJob() const { return m_current_search_job && m_current_search_job->isRunning(); } bool hasActiveSearchJob() const { return m_current_search_job && m_current_search_job->isRunning(); }
bool hasActiveInfoJob() const { return m_current_info_job.isRunning(); } bool hasActiveInfoJob() const { return m_current_info_job.isRunning(); }
@ -66,7 +66,7 @@ class ResourceModel : public QAbstractListModel {
public slots: public slots:
void fetchMore(const QModelIndex& parent) override; void fetchMore(const QModelIndex& parent) override;
inline bool canFetchMore(const QModelIndex& parent) const override bool canFetchMore(const QModelIndex& parent) const override
{ {
return parent.isValid() ? false : m_search_state == SearchState::CanFetchMore; return parent.isValid() ? false : m_search_state == SearchState::CanFetchMore;
} }
@ -94,7 +94,7 @@ class ResourceModel : public QAbstractListModel {
void addPack(ModPlatform::IndexedPack::Ptr pack, void addPack(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion& version, ModPlatform::IndexedVersion& version,
ResourceFolderModel* packs, ResourceFolderModel* packs,
bool is_indexed = false, bool isIndexed = false,
QString downloadReason = "standalone"); QString downloadReason = "standalone");
void removePack(const QString& rem); void removePack(const QString& rem);
QList<DownloadTaskPtr> selectedPacks() { return m_selected; } QList<DownloadTaskPtr> selectedPacks() { return m_selected; }

View file

@ -401,7 +401,7 @@ void ResourcePage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack,
QString downloadReason) QString downloadReason)
{ {
bool isIndexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); bool isIndexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
m_model->addPack(std::move(pack), ver, baseModel, isIndexed); m_model->addPack(std::move(pack), ver, baseModel, isIndexed, std::move(downloadReason));
} }
void ResourcePage::modelReset() void ResourcePage::modelReset()

View file

@ -9,7 +9,6 @@
#include "ResourceDownloadTask.h" #include "ResourceDownloadTask.h"
#include "modplatform/ModIndex.h" #include "modplatform/ModIndex.h"
#include "modplatform/ResourceAPI.h"
#include "ui/pages/BasePage.h" #include "ui/pages/BasePage.h"
#include "ui/pages/modplatform/ResourceModel.h" #include "ui/pages/modplatform/ResourceModel.h"
@ -78,7 +77,10 @@ class ResourcePage : public QWidget, public BasePage {
void addResourceToDialog(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&); void addResourceToDialog(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&);
void removeResourceFromDialog(const QString& packName); void removeResourceFromDialog(const QString& packName);
virtual void removeResourceFromPage(const QString& name); virtual void removeResourceFromPage(const QString& name);
virtual void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, ResourceFolderModel*, QString downloadReason = "standalone"); virtual void addResourceToPage(ModPlatform::IndexedPack::Ptr,
ModPlatform::IndexedVersion&,
ResourceFolderModel*,
QString downloadReason = "standalone");
virtual void modelReset(); virtual void modelReset();

View file

@ -36,19 +36,18 @@ QMap<QString, QString> ShaderPackResourcePage::urlHandlers() const
{ {
QMap<QString, QString> map; QMap<QString, QString> map;
map.insert(QRegularExpression::anchoredPattern("(?:www\\.)?modrinth\\.com\\/shaders\\/([^\\/]+)\\/?"), "modrinth"); map.insert(QRegularExpression::anchoredPattern("(?:www\\.)?modrinth\\.com\\/shaders\\/([^\\/]+)\\/?"), "modrinth");
map.insert(QRegularExpression::anchoredPattern("(?:www\\.)?curseforge\\.com\\/minecraft\\/customization\\/([^\\/]+)\\/?"), map.insert(QRegularExpression::anchoredPattern(R"((?:www\.)?curseforge\.com\/minecraft\/customization\/([^\/]+)\/?)"), "curseforge");
"curseforge"); map.insert(QRegularExpression::anchoredPattern(R"(minecraft\.curseforge\.com\/projects\/([^\/]+)\/?)"), "curseforge");
map.insert(QRegularExpression::anchoredPattern("minecraft\\.curseforge\\.com\\/projects\\/([^\\/]+)\\/?"), "curseforge");
return map; return map;
} }
void ShaderPackResourcePage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack, void ShaderPackResourcePage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion& version, ModPlatform::IndexedVersion& version,
ResourceFolderModel* base_model, ResourceFolderModel* baseModel,
QString downloadReason) QString downloadReason)
{ {
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); bool isIndexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
m_model->addPack(pack, version, base_model, is_indexed, downloadReason); m_model->addPack(pack, version, baseModel, isIndexed, downloadReason);
} }
} // namespace ResourceDownload } // namespace ResourceDownload

View file

@ -23,7 +23,7 @@ class ShaderPackResourcePage : public ResourcePage {
static T* create(ShaderPackDownloadDialog* dialog, BaseInstance& instance) static T* create(ShaderPackDownloadDialog* dialog, BaseInstance& instance)
{ {
auto page = new T(dialog, instance); auto page = new T(dialog, instance);
auto model = static_cast<ShaderPackResourceModel*>(page->getModel()); auto* model = static_cast<ShaderPackResourceModel*>(page->getModel());
connect(model, &ResourceModel::versionListUpdated, page, &ResourcePage::versionListUpdated); connect(model, &ResourceModel::versionListUpdated, page, &ResourcePage::versionListUpdated);
connect(model, &ResourceModel::projectInfoUpdated, page, &ResourcePage::updateUi); connect(model, &ResourceModel::projectInfoUpdated, page, &ResourcePage::updateUi);
@ -33,17 +33,20 @@ class ShaderPackResourcePage : public ResourcePage {
} }
//: The plural version of 'shader pack' //: The plural version of 'shader pack'
inline QString resourcesString() const override { return tr("shader packs"); } QString resourcesString() const override { return tr("shader packs"); }
//: The singular version of 'shader packs' //: The singular version of 'shader packs'
inline QString resourceString() const override { return tr("shader pack"); } QString resourceString() const override { return tr("shader pack"); }
bool supportsFiltering() const override { return false; }; bool supportsFiltering() const override { return false; };
void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, ResourceFolderModel*, QString downloadReason = "standalone") override; void addResourceToPage(ModPlatform::IndexedPack::Ptr /*unused*/,
ModPlatform::IndexedVersion& /*unused*/,
ResourceFolderModel* /*unused*/,
QString downloadReason = "standalone") override;
QMap<QString, QString> urlHandlers() const override; QMap<QString, QString> urlHandlers() const override;
inline auto helpPage() const -> QString override { return "shaderpack-platform"; } auto helpPage() const -> QString override { return "shaderpack-platform"; }
protected: protected:
ShaderPackResourcePage(ShaderPackDownloadDialog* dialog, BaseInstance& instance); ShaderPackResourcePage(ShaderPackDownloadDialog* dialog, BaseInstance& instance);