diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 0384e9226..ed151bc5b 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -106,6 +106,20 @@ int Mod::compare(const Resource& other, SortType type) const return compare_result; break; } + case SortType::REQUIRED_BY: { + if (requiredByCount() > cast_other->requiredByCount()) + return 1; + if (requiredByCount() < cast_other->requiredByCount()) + return -1; + break; + } + case SortType::REQUIRES: { + if (requiresCount() > cast_other->requiresCount()) + return 1; + if (requiresCount() < cast_other->requiresCount()) + return -1; + break; + } } return 0; } @@ -289,3 +303,19 @@ QStringList Mod::dependencies() const { return details().dependencies; } +int Mod::requiredByCount() const +{ + return m_requiredByCount; +} +int Mod::requiresCount() const +{ + return m_requiresCount; +} +void Mod::setRequiredByCount(int value) +{ + m_requiredByCount = value; +} +void Mod::setRequiresCount(int value) +{ + m_requiresCount = value; +} diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index 63257429e..16f4b32f7 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -74,6 +74,12 @@ class Mod : public Resource { auto releaseType() const -> QString; QStringList dependencies() const; + int requiredByCount() const; + int requiresCount() const; + + void setRequiredByCount(int value); + void setRequiresCount(int value); + /** Get the intneral path to the mod's icon file*/ QString iconPath() const { return m_local_details.icon_file; } /** Gets the icon of the mod, converted to a QPixmap for drawing, and scaled to size. */ @@ -105,4 +111,7 @@ class Mod : public Resource { bool wasEverUsed = false; bool wasReadAttempt = false; } mutable m_packImageCacheKey; + + int m_requiredByCount = 0; + int m_requiresCount = 0; }; diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index b613e0af1..cd8a0d609 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -48,25 +48,31 @@ #include #include #include +#include #include "Application.h" #include "minecraft/mod/tasks/LocalModParseTask.h" +#include "modplatform/ModIndex.h" ModFolderModel::ModFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent) : ResourceFolderModel(QDir(dir), instance, is_indexed, create_dir, parent) { m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Size", "Side", "Loaders", - "Minecraft Versions", "Release Type" }); - m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider"), - tr("Size"), tr("Side"), tr("Loaders"), tr("Minecraft Versions"), tr("Release Type") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, - SortType::DATE, SortType::PROVIDER, SortType::SIZE, SortType::SIDE, - SortType::LOADERS, SortType::MC_VERSIONS, SortType::RELEASE_TYPE }; + "Minecraft Versions", "Release Type", "Requires", "Required by" }); + m_column_names_translated = + QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider"), tr("Size"), tr("Side"), + tr("Loaders"), tr("Minecraft Versions"), tr("Release Type"), tr("Requires "), tr("Required by") }); + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, + SortType::PROVIDER, SortType::SIZE, SortType::SIDE, SortType::LOADERS, SortType::MC_VERSIONS, + SortType::RELEASE_TYPE, SortType::REQUIRES, SortType::REQUIRED_BY }; m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, - QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive }; - m_columnsHideable = { false, true, false, true, true, true, true, true, true, true, true }; + QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, + QHeaderView::Interactive }; + m_columnsHideable = { false, true, false, true, true, true, true, true, true, true, true, true, true }; + + connect(this, &ModFolderModel::parseFinished, this, &ModFolderModel::onParseFinished); } QVariant ModFolderModel::data(const QModelIndex& index, int role) const @@ -110,8 +116,15 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const case ReleaseTypeColumn: { return at(row).releaseType(); } - case SizeColumn: + case SizeColumn: { return at(row).sizeStr(); + } + case RequiredByColumn: { + return at(row).requiredByCount(); + } + case RequiresColumn: { + return at(row).requiresCount(); + } default: return QVariant(); } @@ -168,6 +181,8 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio case McVersionsColumn: case ReleaseTypeColumn: case SizeColumn: + case RequiredByColumn: + case RequiresColumn: return columnNames().at(section); default: return QVariant(); @@ -195,6 +210,10 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio return tr("The release type."); case SizeColumn: return tr("The size of the mod."); + case RequiredByColumn: + return tr("Number of mods for what this is needed."); + case RequiresColumn: + return tr("Number of mods that this requires."); default: return QVariant(); } @@ -240,3 +259,64 @@ void ModFolderModel::onParseSucceeded(int ticket, QString mod_id) emit dataChanged(index(row), index(row, columnCount(QModelIndex()) - 1)); } + +void ModFolderModel::onParseFinished() +{ + if (hasPendingParseTasks()) { + return; + } + 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 found != mods.end() ? *found : nullptr; + }; + for (auto mod : mods) { + auto id = mod->internal_id(); + for (auto dep : mod->dependencies()) { + auto d = findById(dep); + if (d) { + m_requires[id] << d; + m_requiredBy[d->internal_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; + } + } + } + auto removeDuplicates = [](QList& list) { + std::set seen; + auto it = std::remove_if(list.begin(), list.end(), [&seen](Mod* m) { + auto id = m->internal_id(); + if (seen.count(id) > 0) { + return true; + } + seen.insert(id); + return false; + }); + list.erase(it, list.end()); + }; + for (auto key : m_requiredBy.keys()) { + removeDuplicates(m_requiredBy[key]); + } + for (auto key : m_requires.keys()) { + removeDuplicates(m_requires[key]); + } + for (auto mod : mods) { + auto id = mod->internal_id(); + mod->setRequiredByCount(m_requiredBy[id].count()); + mod->setRequiresCount(m_requires[id].count()); + int row = m_resources_index[id]; + emit dataChanged(index(row), index(row, columnCount(QModelIndex()) - 1)); + } +} diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 42868dc91..40f9eb733 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -46,6 +47,7 @@ #include "Mod.h" #include "ResourceFolderModel.h" +#include "minecraft/mod/Resource.h" class BaseInstance; class QFileSystemWatcher; @@ -69,6 +71,8 @@ class ModFolderModel : public ResourceFolderModel { LoadersColumn, McVersionsColumn, ReleaseTypeColumn, + RequiresColumn, + RequiredByColumn, NUM_COLUMNS }; ModFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr); @@ -89,4 +93,9 @@ class ModFolderModel : public ResourceFolderModel { private slots: void onParseSucceeded(int ticket, QString resource_id) override; + void onParseFinished(); + + private: + QHash> m_requiredBy; + QHash> m_requires; }; diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index 242b8a30d..3eda4c013 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -58,7 +58,21 @@ enum class ResourceStatus { UNKNOWN, // Default status }; -enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER, SIZE, SIDE, MC_VERSIONS, LOADERS, RELEASE_TYPE }; +enum class SortType { + NAME, + DATE, + VERSION, + ENABLED, + PACK_FORMAT, + PROVIDER, + SIZE, + SIDE, + MC_VERSIONS, + LOADERS, + RELEASE_TYPE, + REQUIRES, + REQUIRED_BY, +}; enum class EnableAction { ENABLE, DISABLE, TOGGLE };