From ee487669960ba8ee7c1997621b9fd7e778d3f65a Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 1 Sep 2023 00:27:05 +0100 Subject: [PATCH] More generalistaion for ResourceFolderModels Signed-off-by: TheKodeToad --- launcher/ResourceDownloadTask.cpp | 8 +- launcher/minecraft/mod/MetadataHandler.h | 14 +-- launcher/minecraft/mod/Mod.cpp | 1 - launcher/minecraft/mod/ModFolderModel.cpp | 95 ------------------- launcher/minecraft/mod/ModFolderModel.h | 10 -- launcher/minecraft/mod/Resource.cpp | 9 +- launcher/minecraft/mod/Resource.h | 3 +- .../minecraft/mod/ResourceFolderModel.cpp | 58 ++++++++++- launcher/minecraft/mod/ResourceFolderModel.h | 8 +- launcher/modplatform/packwiz/Packwiz.cpp | 16 ++-- launcher/modplatform/packwiz/Packwiz.h | 16 ++-- launcher/ui/MainWindow.cpp | 2 +- launcher/ui/pages/instance/ModFolderPage.cpp | 2 +- 13 files changed, 90 insertions(+), 152 deletions(-) diff --git a/launcher/ResourceDownloadTask.cpp b/launcher/ResourceDownloadTask.cpp index a02151ca1..dffbaf478 100644 --- a/launcher/ResourceDownloadTask.cpp +++ b/launcher/ResourceDownloadTask.cpp @@ -67,12 +67,8 @@ void ResourceDownloadTask::downloadSucceeded() m_filesNetJob.reset(); auto name = std::get<0>(to_delete); auto filename = std::get<1>(to_delete); - if (!name.isEmpty() && filename != m_pack_version.fileName) { - if (auto model = dynamic_cast(m_pack_model.get()); model) - model->uninstallMod(filename, true); - else - m_pack_model->uninstallResource(filename); - } + if (!name.isEmpty() && filename != m_pack_version.fileName) + m_pack_model->uninstallResource(filename, true); } void ResourceDownloadTask::downloadFailed(QString reason) diff --git a/launcher/minecraft/mod/MetadataHandler.h b/launcher/minecraft/mod/MetadataHandler.h index 747375d3f..3794db484 100644 --- a/launcher/minecraft/mod/MetadataHandler.h +++ b/launcher/minecraft/mod/MetadataHandler.h @@ -28,37 +28,37 @@ class Mod; namespace Metadata { using ModStruct = Packwiz::V1::Mod; -inline auto create(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> ModStruct +inline auto create(const QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> ModStruct { return Packwiz::V1::createModFormat(index_dir, mod_pack, mod_version); } -inline auto create(QDir& index_dir, Mod& internal_mod, QString mod_slug) -> ModStruct +inline auto create(const QDir& index_dir, Mod& internal_mod, QString mod_slug) -> ModStruct { return Packwiz::V1::createModFormat(index_dir, internal_mod, std::move(mod_slug)); } -inline void update(QDir& index_dir, ModStruct& mod) +inline void update(const QDir& index_dir, ModStruct& mod) { Packwiz::V1::updateModIndex(index_dir, mod); } -inline void remove(QDir& index_dir, QString mod_slug) +inline void remove(const QDir& index_dir, QString mod_slug) { Packwiz::V1::deleteModIndex(index_dir, mod_slug); } -inline void remove(QDir& index_dir, QVariant& mod_id) +inline void remove(const QDir& index_dir, QVariant& mod_id) { Packwiz::V1::deleteModIndex(index_dir, mod_id); } -inline auto get(QDir& index_dir, QString mod_slug) -> ModStruct +inline auto get(const QDir& index_dir, QString mod_slug) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, std::move(mod_slug)); } -inline auto get(QDir& index_dir, QVariant& mod_id) -> ModStruct +inline auto get(const QDir& index_dir, QVariant& mod_id) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, mod_id); } diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 53f7d72de..27688b5f2 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -36,7 +36,6 @@ #include "Mod.h" -#include #include #include #include diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 2f0e9b3e4..733e1f655 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -51,13 +51,9 @@ #include "Application.h" -#include "Json.h" #include "Resource.h" #include "minecraft/mod/tasks/LocalModParseTask.h" #include "minecraft/mod/tasks/LocalModUpdateTask.h" -#include "minecraft/mod/tasks/ResourceFolderLoadTask.h" -#include "modplatform/ModIndex.h" -#include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameModIndex.h" ModFolderModel::ModFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent) @@ -184,58 +180,11 @@ Task* ModFolderModel::createParseTask(Resource& resource) return new LocalModParseTask(m_next_resolution_ticket, resource.type(), resource.fileinfo()); } -bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadata) -{ - for (auto mod : allMods()) { - if (mod->fileinfo().fileName() == filename) { - auto index_dir = indexDir(); - mod->destroy(index_dir, preserve_metadata, false); - - update(); - - return true; - } - } - - return false; -} - -bool ModFolderModel::deleteMods(const QModelIndexList& indexes) -{ - if (indexes.isEmpty()) - return true; - - for (auto i : indexes) { - if (i.column() != 0) { - continue; - } - auto m = at(i.row()); - auto index_dir = indexDir(); - m->destroy(index_dir); - } - - update(); - - return true; -} - bool ModFolderModel::isValid() { return m_dir.exists() && m_dir.isReadable(); } -bool ModFolderModel::startWatching() -{ - // Remove orphaned metadata next time - m_first_folder_load = true; - return ResourceFolderModel::startWatching({ m_dir.absolutePath(), indexDir().absolutePath() }); -} - -bool ModFolderModel::stopWatching() -{ - return ResourceFolderModel::stopWatching({ m_dir.absolutePath(), indexDir().absolutePath() }); -} - auto ModFolderModel::selectedMods(QModelIndexList& indexes) -> QList { QList selected_resources; @@ -280,47 +229,3 @@ void ModFolderModel::onParseSucceeded(int ticket, QString mod_id) emit dataChanged(index(row), index(row, columnCount(QModelIndex()) - 1)); } - -static const FlameAPI flameAPI; -bool ModFolderModel::installMod(QString file_path, ModPlatform::IndexedVersion& vers) -{ - if (vers.addonId.isValid()) { - ModPlatform::IndexedPack pack{ - vers.addonId, - ModPlatform::ResourceProvider::FLAME, - }; - - QEventLoop loop; - - auto response = std::make_shared(); - auto job = flameAPI.getProject(vers.addonId.toString(), response); - - QObject::connect(job.get(), &Task::failed, [&loop] { loop.quit(); }); - QObject::connect(job.get(), &Task::aborted, &loop, &QEventLoop::quit); - QObject::connect(job.get(), &Task::succeeded, [response, this, &vers, &loop, &pack] { - QJsonParseError parse_error{}; - QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); - if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset - << " reason: " << parse_error.errorString(); - qDebug() << *response; - return; - } - try { - auto obj = Json::requireObject(Json::requireObject(doc), "data"); - FlameMod::loadIndexedPack(pack, obj); - } catch (const JSONValidationError& e) { - qDebug() << doc; - qWarning() << "Error while reading mod info: " << e.cause(); - } - LocalModUpdateTask update_metadata(indexDir(), pack, vers); - QObject::connect(&update_metadata, &Task::finished, &loop, &QEventLoop::quit); - update_metadata.start(); - }); - - job->start(); - - loop.exec(); - } - return ResourceFolderModel::installResource(file_path); -} diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 9326158d4..c1db33c0c 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -74,18 +74,8 @@ class ModFolderModel : public ResourceFolderModel { [[nodiscard]] Resource* createResource(const QFileInfo& file) override { return new Mod(file); } [[nodiscard]] Task* createParseTask(Resource&) override; - bool installMod(QString file_path) { return ResourceFolderModel::installResource(file_path); } - bool installMod(QString file_path, ModPlatform::IndexedVersion& vers); - bool uninstallMod(const QString& filename, bool preserve_metadata = false); - - /// Deletes all the selected mods - bool deleteMods(const QModelIndexList& indexes); - bool isValid(); - bool startWatching() override; - bool stopWatching() override; - auto selectedMods(QModelIndexList& indexes) -> QList; auto allMods() -> QList; diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index 0dd7f8992..bfe2a8802 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -172,15 +172,10 @@ bool Resource::enable(EnableAction action) return true; } -bool Resource::destroy(bool attemptTrash) +auto Resource::destroy(const QDir& index_dir, bool preserve_metadata, bool attempt_trash) -> bool { m_type = ResourceType::UNKNOWN; - return (attemptTrash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath()); -} - -auto Resource::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_trash) -> bool -{ if (!preserve_metadata) { qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name()); @@ -192,7 +187,7 @@ auto Resource::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_tra } } - return destroy(attempt_trash); + return (attempt_trash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath()); } bool Resource::isSymLinkUnder(const QString& instPath) const diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index 5012c23a4..77063cbf0 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -99,8 +99,7 @@ class Resource : public QObject { } // Delete all files of this resource. - auto destroy(bool attemptTrash = true) -> bool; - auto destroy(QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool; + auto destroy(const QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool; [[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); } diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index a39e9adee..9ac387904 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "Application.h" #include "FileSystem.h" @@ -17,6 +18,10 @@ #include "QVariantUtils.h" #include "minecraft/mod/tasks/ResourceFolderLoadTask.h" +#include "Json.h" +#include "minecraft/mod/tasks/LocalModUpdateTask.h" +#include "modplatform/flame/FlameAPI.h" +#include "modplatform/flame/FlameModIndex.h" #include "settings/Setting.h" #include "tasks/Task.h" #include "ui/dialogs/CustomMessageBox.h" @@ -43,6 +48,9 @@ ResourceFolderModel::~ResourceFolderModel() bool ResourceFolderModel::startWatching(const QStringList paths) { + // Remove orphaned metadata next time + m_first_folder_load = true; + if (m_is_watching) return false; @@ -153,11 +161,55 @@ bool ResourceFolderModel::installResource(QString original_path) return false; } -bool ResourceFolderModel::uninstallResource(QString file_name) +bool ResourceFolderModel::installResource(QString path, ModPlatform::IndexedVersion& vers) +{ + if (vers.addonId.isValid()) { + ModPlatform::IndexedPack pack{ + vers.addonId, + ModPlatform::ResourceProvider::FLAME, + }; + + QEventLoop loop; + + auto response = std::make_shared(); + auto job = FlameAPI().getProject(vers.addonId.toString(), response); + + QObject::connect(job.get(), &Task::failed, [&loop] { loop.quit(); }); + QObject::connect(job.get(), &Task::aborted, &loop, &QEventLoop::quit); + QObject::connect(job.get(), &Task::succeeded, [response, this, &vers, &loop, &pack] { + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qDebug() << *response; + return; + } + try { + auto obj = Json::requireObject(Json::requireObject(doc), "data"); + FlameMod::loadIndexedPack(pack, obj); + } catch (const JSONValidationError& e) { + qDebug() << doc; + qWarning() << "Error while reading mod info: " << e.cause(); + } + LocalModUpdateTask update_metadata(indexDir(), pack, vers); + QObject::connect(&update_metadata, &Task::finished, &loop, &QEventLoop::quit); + update_metadata.start(); + }); + + job->start(); + + loop.exec(); + } + + return installResource(std::move(path)); +} + +bool ResourceFolderModel::uninstallResource(QString file_name, bool preserve_metadata) { for (auto& resource : m_resources) { if (resource->fileinfo().fileName() == file_name) { - auto res = resource->destroy(false); + auto res = resource->destroy(indexDir(), preserve_metadata, false); update(); @@ -179,7 +231,7 @@ bool ResourceFolderModel::deleteResources(const QModelIndexList& indexes) auto& resource = m_resources.at(i.row()); - resource->destroy(); + resource->destroy(indexDir()); } update(); diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 3a4287b30..e8f8d2b9e 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -49,8 +49,8 @@ class ResourceFolderModel : public QAbstractListModel { bool stopWatching(const QStringList paths); /* Helper methods for subclasses, using a predetermined list of paths. */ - virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); } - virtual bool stopWatching() { return stopWatching({ m_dir.absolutePath() }); } + virtual bool startWatching() { return startWatching({ indexDir().absolutePath(), m_dir.absolutePath() }); } + virtual bool stopWatching() { return stopWatching({ indexDir().absolutePath(), m_dir.absolutePath() }); } QDir indexDir() { return { QString("%1/.index").arg(dir().absolutePath()) }; } @@ -61,11 +61,13 @@ class ResourceFolderModel : public QAbstractListModel { */ virtual bool installResource(QString path); + virtual bool installResource(QString path, ModPlatform::IndexedVersion& vers); + /** Uninstall (i.e. remove all data about it) a resource, given its file name. * * Returns whether the removal was successful. */ - virtual bool uninstallResource(QString file_name); + virtual bool uninstallResource(QString file_name, bool preserve_metadata = false); virtual bool deleteResources(const QModelIndexList&); /** Applies the given 'action' to the resources in 'indexes'. diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 71f66bf3e..bfc1dea44 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -32,7 +32,7 @@ namespace Packwiz { -auto getRealIndexName(QDir& index_dir, QString normalized_fname, bool should_find_match) -> QString +auto getRealIndexName(const QDir& index_dir, QString normalized_fname, bool should_find_match) -> QString { QFile index_file(index_dir.absoluteFilePath(normalized_fname)); @@ -89,7 +89,7 @@ auto intEntry(toml::table table, QString entry_name) -> int return node.value_or(0); } -auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) +auto V1::createModFormat([[maybe_unused]] const QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod { Mod mod; @@ -115,7 +115,7 @@ auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedP return mod; } -auto V1::createModFormat(QDir& index_dir, [[maybe_unused]] ::Mod& internal_mod, QString slug) -> Mod +auto V1::createModFormat(const QDir& index_dir, [[maybe_unused]] ::Mod& internal_mod, QString slug) -> Mod { // Try getting metadata if it exists Mod mod{ getIndexForMod(index_dir, slug) }; @@ -127,7 +127,7 @@ auto V1::createModFormat(QDir& index_dir, [[maybe_unused]] ::Mod& internal_mod, return {}; } -void V1::updateModIndex(QDir& index_dir, Mod& mod) +void V1::updateModIndex(const QDir& index_dir, Mod& mod) { if (!mod.isValid()) { qCritical() << QString("Tried to update metadata of an invalid mod!"); @@ -192,7 +192,7 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) index_file.close(); } -void V1::deleteModIndex(QDir& index_dir, QString& mod_slug) +void V1::deleteModIndex(const QDir& index_dir, QString& mod_slug) { auto normalized_fname = indexFileName(mod_slug); auto real_fname = getRealIndexName(index_dir, normalized_fname); @@ -211,7 +211,7 @@ void V1::deleteModIndex(QDir& index_dir, QString& mod_slug) } } -void V1::deleteModIndex(QDir& index_dir, QVariant& mod_id) +void V1::deleteModIndex(const QDir& index_dir, QVariant& mod_id) { for (auto& file_name : index_dir.entryList(QDir::Filter::Files)) { auto mod = getIndexForMod(index_dir, file_name); @@ -223,7 +223,7 @@ void V1::deleteModIndex(QDir& index_dir, QVariant& mod_id) } } -auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod +auto V1::getIndexForMod(const QDir& index_dir, QString slug) -> Mod { Mod mod; @@ -301,7 +301,7 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod return mod; } -auto V1::getIndexForMod(QDir& index_dir, QVariant& mod_id) -> Mod +auto V1::getIndexForMod(const QDir& index_dir, QVariant& mod_id) -> Mod { for (auto& file_name : index_dir.entryList(QDir::Filter::Files)) { auto mod = getIndexForMod(index_dir, file_name); diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index c49165557..ffc1d40c8 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -31,7 +31,7 @@ class Mod; namespace Packwiz { -auto getRealIndexName(QDir& index_dir, QString normalized_index_name, bool should_match = false) -> QString; +auto getRealIndexName(const QDir& index_dir, QString normalized_index_name, bool should_match = false) -> QString; class V1 { public: @@ -67,33 +67,33 @@ class V1 { /* Generates the object representing the information in a mod.pw.toml file via * its common representation in the launcher, when downloading mods. * */ - static auto createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod; + static auto createModFormat(const QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod; /* Generates the object representing the information in a mod.pw.toml file via * its common representation in the launcher, plus a necessary slug. * */ - static auto createModFormat(QDir& index_dir, ::Mod& internal_mod, QString slug) -> Mod; + static auto createModFormat(const QDir& index_dir, ::Mod& internal_mod, QString slug) -> Mod; /* Updates the mod index for the provided mod. * This creates a new index if one does not exist already * TODO: Ask the user if they want to override, and delete the old mod's files, or keep the old one. * */ - static void updateModIndex(QDir& index_dir, Mod& mod); + static void updateModIndex(const QDir& index_dir, Mod& mod); /* Deletes the metadata for the mod with the given slug. If the metadata doesn't exist, it does nothing. */ - static void deleteModIndex(QDir& index_dir, QString& mod_slug); + static void deleteModIndex(const QDir& index_dir, QString& mod_slug); /* Deletes the metadata for the mod with the given id. If the metadata doesn't exist, it does nothing. */ - static void deleteModIndex(QDir& index_dir, QVariant& mod_id); + static void deleteModIndex(const QDir& index_dir, QVariant& mod_id); /* Gets the metadata for a mod with a particular file name. * If the mod doesn't have a metadata, it simply returns an empty Mod object. * */ - static auto getIndexForMod(QDir& index_dir, QString slug) -> Mod; + static auto getIndexForMod(const QDir& index_dir, QString slug) -> Mod; /* Gets the metadata for a mod with a particular id. * If the mod doesn't have a metadata, it simply returns an empty Mod object. * */ - static auto getIndexForMod(QDir& index_dir, QVariant& mod_id) -> Mod; + static auto getIndexForMod(const QDir& index_dir, QVariant& mod_id) -> Mod; }; } // namespace Packwiz diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 5e55a5abb..a7e3def38 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1048,7 +1048,7 @@ void MainWindow::processURLs(QList urls) qWarning() << "Importing of Data Packs not supported at this time. Ignoring" << localFileName; break; case PackedResourceType::Mod: - minecraftInst->loaderModList()->installMod(localFileName, version); + minecraftInst->loaderModList()->installResource(localFileName, version); break; case PackedResourceType::ShaderPack: minecraftInst->shaderPackList()->installResource(localFileName); diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 0f5e29cb6..6333dec1d 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -150,7 +150,7 @@ void ModFolderPage::removeItems(const QItemSelection& selection) if (response != QMessageBox::Yes) return; } - m_model->deleteMods(selection.indexes()); + m_model->deleteResources(selection.indexes()); } void ModFolderPage::installMods()