From 8277fd41ae658480e23dd5bd8df0cfc32a77a0e5 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 5 May 2025 00:35:35 +0300 Subject: [PATCH] feat: make dependencies auto disable/enable Signed-off-by: Trial97 --- launcher/minecraft/mod/ModFolderModel.cpp | 97 ++++++++++++++++--- launcher/minecraft/mod/ModFolderModel.h | 4 + .../minecraft/mod/ResourceFolderModel.cpp | 1 + launcher/ui/pages/instance/ModFolderPage.cpp | 20 +++- 4 files changed, 104 insertions(+), 18 deletions(-) diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index cd8a0d609..60ba0e6e2 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -52,6 +52,9 @@ #include "Application.h" +#include "minecraft/Component.h" +#include "minecraft/mod/Resource.h" +#include "minecraft/mod/ResourceFolderModel.h" #include "minecraft/mod/tasks/LocalModParseTask.h" #include "modplatform/ModIndex.h" @@ -260,6 +263,12 @@ void ModFolderModel::onParseSucceeded(int ticket, QString mod_id) emit dataChanged(index(row), index(row, columnCount(QModelIndex()) - 1)); } +Mod* findById(QList mods, QString modId) +{ + auto found = std::find_if(mods.begin(), mods.end(), [modId](Mod* m) { return m->mod_id() == modId; }); + return found != mods.end() ? *found : nullptr; +} + void ModFolderModel::onParseFinished() { if (hasPendingParseTasks()) { @@ -267,37 +276,37 @@ void ModFolderModel::onParseFinished() } auto mods = allMods(); - auto findById = [mods](QString modId) -> Mod* { - auto found = std::find_if(mods.begin(), mods.end(), [modId](Mod* m) { return m->mod_id() == modId; }); - return found != mods.end() ? *found : nullptr; - }; auto findByProjectID = [mods](QVariant modId, ModPlatform::ResourceProvider provider) -> Mod* { auto found = std::find_if(mods.begin(), mods.end(), [modId, provider](Mod* m) { - return m->metadata()->provider == provider && m->metadata()->project_id == modId; + return m->metadata() && m->metadata()->provider == provider && m->metadata()->project_id == modId; }); return found != mods.end() ? *found : nullptr; }; for (auto mod : mods) { - auto id = mod->internal_id(); + auto id = mod->mod_id(); for (auto dep : mod->dependencies()) { - auto d = findById(dep); + auto d = findById(mods, dep); if (d) { m_requires[id] << d; - m_requiredBy[d->internal_id()] << mod; + m_requiredBy[d->mod_id()] << mod; } } - for (auto dep : mod->metadata()->dependencies) { - auto d = findByProjectID(dep.addonId, mod->metadata()->provider); - if (d) { - m_requires[id] << d; - m_requiredBy[d->internal_id()] << mod; + if (mod->metadata()) { + for (auto dep : mod->metadata()->dependencies) { + if (dep.type == ModPlatform::DependencyType::REQUIRED) { + auto d = findByProjectID(dep.addonId, mod->metadata()->provider); + if (d) { + m_requires[id] << d; + m_requiredBy[d->mod_id()] << mod; + } + } } } } auto removeDuplicates = [](QList& list) { std::set seen; auto it = std::remove_if(list.begin(), list.end(), [&seen](Mod* m) { - auto id = m->internal_id(); + auto id = m->mod_id(); if (seen.count(id) > 0) { return true; } @@ -313,10 +322,66 @@ void ModFolderModel::onParseFinished() removeDuplicates(m_requires[key]); } for (auto mod : mods) { - auto id = mod->internal_id(); + auto id = mod->mod_id(); mod->setRequiredByCount(m_requiredBy[id].count()); mod->setRequiresCount(m_requires[id].count()); - int row = m_resources_index[id]; + int row = m_resources_index[mod->internal_id()]; emit dataChanged(index(row), index(row, columnCount(QModelIndex()) - 1)); } } + +QModelIndexList ModFolderModel::getAffectedMods(const QModelIndexList& indexes, EnableAction action) +{ + if (indexes.isEmpty()) + return {}; + + QModelIndexList affectedList = {}; + auto indexedMods = selectedMods(indexes); + if (action == EnableAction::TOGGLE) { + if (indexedMods.length() != 1) { + return {}; // not sure how to handle a bunch of rows that are toggled(not even sure it is posible) + } + action = indexedMods.first()->enabled() ? EnableAction::DISABLE : EnableAction::ENABLE; + } + + std::set seen; + bool shouldBeEnabled = action == EnableAction::ENABLE; + for (auto mod : indexedMods) { + auto id = mod->mod_id(); + QList mods; + switch (action) { + case EnableAction::DISABLE: { + mods = m_requiredBy[id]; + break; + } + case EnableAction::ENABLE: { + mods = m_requires[id]; + break; + } + case EnableAction::TOGGLE: + break; + } + for (auto affected : mods) { + auto affectedId = affected->mod_id(); + + if (findById(indexedMods, affectedId) == nullptr && seen.count(affectedId) == 0) { + seen.insert(affectedId); + if (shouldBeEnabled != affected->enabled()) { + auto row = m_resources_index[affected->internal_id()]; + affectedList << index(row, 0); + } + } + } + } + // collect the affected mods until all of them are included in the list + if (!affectedList.isEmpty()) { + affectedList += getAffectedMods(indexes + affectedList, action); + } + return affectedList; +} + +bool ModFolderModel::setResourceEnabled(const QModelIndexList& indexes, EnableAction action) +{ + auto affected = getAffectedMods(indexes, action); + return ResourceFolderModel::setResourceEnabled(indexes + affected, action); +} diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 40f9eb733..ec1c60daa 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -47,6 +47,7 @@ #include "Mod.h" #include "ResourceFolderModel.h" +#include "minecraft/Component.h" #include "minecraft/mod/Resource.h" class BaseInstance; @@ -89,6 +90,9 @@ class ModFolderModel : public ResourceFolderModel { bool isValid(); + bool setResourceEnabled(const QModelIndexList& indexes, EnableAction action) override; + QModelIndexList getAffectedMods(const QModelIndexList& indexes, EnableAction action); + RESOURCE_HELPERS(Mod) private slots: diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index f93002f06..af3ec68e1 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -882,6 +882,7 @@ QList ResourceFolderModel::allResources() result.append((resource.get())); return result; } + QList ResourceFolderModel::selectedResources(const QModelIndexList& indexes) { QList result; diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 7b79766ee..5951a5e33 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -37,6 +37,7 @@ */ #include "ModFolderPage.h" +#include "minecraft/mod/Resource.h" #include "ui/dialogs/ExportToModListDialog.h" #include "ui_ExternalResourcesPage.h" @@ -90,7 +91,7 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr auto depsDisabled = APPLICATION->settings()->getSetting("ModDependenciesDisabled"); ui->actionVerifyItemDependencies->setVisible(!depsDisabled->get().toBool()); connect(depsDisabled.get(), &Setting::SettingChanged, this, - [this](const Setting& setting, const QVariant& value) { ui->actionVerifyItemDependencies->setVisible(!value.toBool()); }); + [this](const Setting&, const QVariant& value) { ui->actionVerifyItemDependencies->setVisible(!value.toBool()); }); updateMenu->addAction(ui->actionResetItemMetadata); connect(ui->actionResetItemMetadata, &QAction::triggered, this, &ModFolderPage::deleteModMetadata); @@ -133,7 +134,22 @@ void ModFolderPage::removeItems(const QItemSelection& selection) if (response != QMessageBox::Yes) return; } - m_model->deleteResources(selection.indexes()); + + auto indexes = selection.indexes(); + auto affected = m_model->getAffectedMods(indexes, EnableAction::DISABLE); + if (!affected.isEmpty()) { + auto response = CustomMessageBox::selectable(this, tr("Confirm Disable"), + tr("The mods you are tring to disable are required by %1 mods.\n" + "Do you want to disable them?") + .arg(affected.length()), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) { + m_model->setResourceEnabled(affected, EnableAction::DISABLE); + } + } + m_model->deleteResources(indexes); } void ModFolderPage::downloadMods()