More generalistaion for ResourceFolderModels

Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
This commit is contained in:
TheKodeToad
2023-09-01 00:27:05 +01:00
parent 93876e27f8
commit ee48766996
13 changed files with 90 additions and 152 deletions

View File

@ -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<ModFolderModel*>(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)

View File

@ -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);
}

View File

@ -36,7 +36,6 @@
#include "Mod.h"
#include <QDebug>
#include <QDir>
#include <QRegularExpression>
#include <QString>

View File

@ -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<Mod*>
{
QList<Mod*> 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<QByteArray>();
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);
}

View File

@ -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<Mod*>;
auto allMods() -> QList<Mod*>;

View File

@ -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

View File

@ -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(); }

View File

@ -10,6 +10,7 @@
#include <QStyle>
#include <QThreadPool>
#include <QUrl>
#include <utility>
#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<QByteArray>();
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();

View File

@ -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'.

View File

@ -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);

View File

@ -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

View File

@ -1048,7 +1048,7 @@ void MainWindow::processURLs(QList<QUrl> 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);

View File

@ -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()