Generalise resource metadata

Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
This commit is contained in:
TheKodeToad 2023-08-31 17:32:55 +01:00
parent b83fdbd1b7
commit 93876e27f8
No known key found for this signature in database
GPG Key ID: 5E39D70B4C93C38E
28 changed files with 209 additions and 370 deletions

View File

@ -350,9 +350,8 @@ set(MINECRAFT_SOURCES
minecraft/mod/TexturePackFolderModel.h minecraft/mod/TexturePackFolderModel.h
minecraft/mod/TexturePackFolderModel.cpp minecraft/mod/TexturePackFolderModel.cpp
minecraft/mod/ShaderPackFolderModel.h minecraft/mod/ShaderPackFolderModel.h
minecraft/mod/tasks/BasicFolderLoadTask.h minecraft/mod/tasks/ResourceFolderLoadTask.h
minecraft/mod/tasks/ModFolderLoadTask.h minecraft/mod/tasks/ResourceFolderLoadTask.cpp
minecraft/mod/tasks/ModFolderLoadTask.cpp
minecraft/mod/tasks/LocalModParseTask.h minecraft/mod/tasks/LocalModParseTask.h
minecraft/mod/tasks/LocalModParseTask.cpp minecraft/mod/tasks/LocalModParseTask.cpp
minecraft/mod/tasks/LocalModUpdateTask.h minecraft/mod/tasks/LocalModUpdateTask.h

View File

@ -33,11 +33,7 @@ class shared_qobject_ptr : public QSharedPointer<T> {
{} {}
void reset() { QSharedPointer<T>::reset(); } void reset() { QSharedPointer<T>::reset(); }
void reset(T*&& other) void reset(T* other) { QSharedPointer<T>::reset(other); }
{
shared_qobject_ptr<T> t(other);
this->swap(t);
}
void reset(const shared_qobject_ptr<T>& other) void reset(const shared_qobject_ptr<T>& other)
{ {
shared_qobject_ptr<T> t(other); shared_qobject_ptr<T> t(other);

View File

@ -1091,7 +1091,7 @@ std::shared_ptr<ModFolderModel> MinecraftInstance::loaderModList()
{ {
if (!m_loader_mod_list) { if (!m_loader_mod_list) {
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
m_loader_mod_list.reset(new ModFolderModel(modsRoot(), this, is_indexed)); m_loader_mod_list.reset(new ModFolderModel(modsRoot(), this, is_indexed, true));
} }
return m_loader_mod_list; return m_loader_mod_list;
} }
@ -1100,7 +1100,7 @@ std::shared_ptr<ModFolderModel> MinecraftInstance::coreModList()
{ {
if (!m_core_mod_list) { if (!m_core_mod_list) {
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
m_core_mod_list.reset(new ModFolderModel(coreModsDir(), this, is_indexed)); m_core_mod_list.reset(new ModFolderModel(coreModsDir(), this, is_indexed, true));
} }
return m_core_mod_list; return m_core_mod_list;
} }
@ -1117,7 +1117,8 @@ std::shared_ptr<ModFolderModel> MinecraftInstance::nilModList()
std::shared_ptr<ResourcePackFolderModel> MinecraftInstance::resourcePackList() std::shared_ptr<ResourcePackFolderModel> MinecraftInstance::resourcePackList()
{ {
if (!m_resource_pack_list) { if (!m_resource_pack_list) {
m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir(), this)); bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir(), this, is_indexed, true));
} }
return m_resource_pack_list; return m_resource_pack_list;
} }
@ -1125,7 +1126,8 @@ std::shared_ptr<ResourcePackFolderModel> MinecraftInstance::resourcePackList()
std::shared_ptr<TexturePackFolderModel> MinecraftInstance::texturePackList() std::shared_ptr<TexturePackFolderModel> MinecraftInstance::texturePackList()
{ {
if (!m_texture_pack_list) { if (!m_texture_pack_list) {
m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir(), this)); bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir(), this, is_indexed, true));
} }
return m_texture_pack_list; return m_texture_pack_list;
} }
@ -1133,7 +1135,8 @@ std::shared_ptr<TexturePackFolderModel> MinecraftInstance::texturePackList()
std::shared_ptr<ShaderPackFolderModel> MinecraftInstance::shaderPackList() std::shared_ptr<ShaderPackFolderModel> MinecraftInstance::shaderPackList()
{ {
if (!m_shader_pack_list) { if (!m_shader_pack_list) {
m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir(), this)); bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir(), this, is_indexed, true));
} }
return m_shader_pack_list; return m_shader_pack_list;
} }

View File

@ -25,30 +25,41 @@
// launcher/minecraft/mod/Mod.h // launcher/minecraft/mod/Mod.h
class Mod; class Mod;
/* Abstraction file for easily changing the way metadata is stored / handled namespace Metadata {
* Needs to be a class because of -Wunused-function and no C++17 [[maybe_unused]]
* */
class Metadata {
public:
using ModStruct = Packwiz::V1::Mod; using ModStruct = Packwiz::V1::Mod;
static auto create(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> ModStruct inline auto create(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> ModStruct
{ {
return Packwiz::V1::createModFormat(index_dir, mod_pack, mod_version); return Packwiz::V1::createModFormat(index_dir, mod_pack, mod_version);
} }
static auto create(QDir& index_dir, Mod& internal_mod, QString mod_slug) -> ModStruct inline auto create(QDir& index_dir, Mod& internal_mod, QString mod_slug) -> ModStruct
{ {
return Packwiz::V1::createModFormat(index_dir, internal_mod, mod_slug); return Packwiz::V1::createModFormat(index_dir, internal_mod, std::move(mod_slug));
} }
static void update(QDir& index_dir, ModStruct& mod) { Packwiz::V1::updateModIndex(index_dir, mod); } inline void update(QDir& index_dir, ModStruct& mod)
{
Packwiz::V1::updateModIndex(index_dir, mod);
}
static void remove(QDir& index_dir, QString mod_slug) { Packwiz::V1::deleteModIndex(index_dir, mod_slug); } inline void remove(QDir& index_dir, QString mod_slug)
{
Packwiz::V1::deleteModIndex(index_dir, mod_slug);
}
static void remove(QDir& index_dir, QVariant& mod_id) { Packwiz::V1::deleteModIndex(index_dir, mod_id); } inline void remove(QDir& index_dir, QVariant& mod_id)
{
Packwiz::V1::deleteModIndex(index_dir, mod_id);
}
static auto get(QDir& index_dir, QString mod_slug) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, mod_slug); } inline auto get(QDir& index_dir, QString mod_slug) -> ModStruct
{
return Packwiz::V1::getIndexForMod(index_dir, std::move(mod_slug));
}
static auto get(QDir& index_dir, QVariant& mod_id) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, mod_id); } inline auto get(QDir& index_dir, QVariant& mod_id) -> ModStruct
}; {
return Packwiz::V1::getIndexForMod(index_dir, mod_id);
}
}; // namespace Metadata

View File

@ -43,35 +43,16 @@
#include "MTPixmapCache.h" #include "MTPixmapCache.h"
#include "MetadataHandler.h" #include "MetadataHandler.h"
#include "Resource.h"
#include "Version.h" #include "Version.h"
#include "minecraft/mod/ModDetails.h" #include "minecraft/mod/ModDetails.h"
#include "minecraft/mod/tasks/LocalModParseTask.h" #include "minecraft/mod/tasks/LocalModParseTask.h"
static ModPlatform::ProviderCapabilities ProviderCaps;
Mod::Mod(const QFileInfo& file) : Resource(file), m_local_details() Mod::Mod(const QFileInfo& file) : Resource(file), m_local_details()
{ {
m_enabled = (file.suffix() != "disabled"); m_enabled = (file.suffix() != "disabled");
} }
Mod::Mod(const QDir& mods_dir, const Metadata::ModStruct& metadata) : Mod(mods_dir.absoluteFilePath(metadata.filename))
{
m_name = metadata.name;
m_local_details.metadata = std::make_shared<Metadata::ModStruct>(std::move(metadata));
}
void Mod::setStatus(ModStatus status)
{
m_local_details.status = status;
}
void Mod::setMetadata(std::shared_ptr<Metadata::ModStruct>&& metadata)
{
if (status() == ModStatus::NoMetadata)
setStatus(ModStatus::Installed);
m_local_details.metadata = metadata;
}
void Mod::setDetails(const ModDetails& details) void Mod::setDetails(const ModDetails& details)
{ {
m_local_details = details; m_local_details = details;
@ -103,8 +84,7 @@ std::pair<int, bool> Mod::compare(const Resource& other, SortType type) const
break; break;
} }
case SortType::PROVIDER: { case SortType::PROVIDER: {
auto compare_result = auto compare_result = QString::compare(provider(), cast_other->provider(), Qt::CaseInsensitive);
QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive);
if (compare_result != 0) if (compare_result != 0)
return { compare_result, type == SortType::PROVIDER }; return { compare_result, type == SortType::PROVIDER };
break; break;
@ -127,22 +107,6 @@ bool Mod::applyFilter(QRegularExpression filter) const
return Resource::applyFilter(filter); return Resource::applyFilter(filter);
} }
auto Mod::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_trash) -> bool
{
if (!preserve_metadata) {
qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name());
if (metadata()) {
Metadata::remove(index_dir, metadata()->slug);
} else {
auto n = name();
Metadata::remove(index_dir, n);
}
}
return Resource::destroy(attempt_trash);
}
auto Mod::details() const -> const ModDetails& auto Mod::details() const -> const ModDetails&
{ {
return m_local_details; return m_local_details;
@ -154,10 +118,7 @@ auto Mod::name() const -> QString
if (!d_name.isEmpty()) if (!d_name.isEmpty())
return d_name; return d_name;
if (metadata()) return Resource::name();
return metadata()->name;
return m_name;
} }
auto Mod::version() const -> QString auto Mod::version() const -> QString
@ -187,45 +148,17 @@ auto Mod::authors() const -> QStringList
return details().authors; return details().authors;
} }
auto Mod::status() const -> ModStatus
{
return details().status;
}
auto Mod::metadata() -> std::shared_ptr<Metadata::ModStruct>
{
return m_local_details.metadata;
}
auto Mod::metadata() const -> const std::shared_ptr<Metadata::ModStruct>
{
return m_local_details.metadata;
}
void Mod::finishResolvingWithDetails(ModDetails&& details) void Mod::finishResolvingWithDetails(ModDetails&& details)
{ {
m_is_resolving = false; m_is_resolving = false;
m_is_resolved = true; m_is_resolved = true;
std::shared_ptr<Metadata::ModStruct> metadata = details.metadata;
if (details.status == ModStatus::Unknown)
details.status = m_local_details.status;
m_local_details = std::move(details); m_local_details = std::move(details);
if (metadata)
setMetadata(std::move(metadata));
if (!iconPath().isEmpty()) { if (!iconPath().isEmpty()) {
m_pack_image_cache_key.was_read_attempt = false; m_pack_image_cache_key.was_read_attempt = false;
} }
} }
auto Mod::provider() const -> std::optional<QString>
{
if (metadata())
return ProviderCaps.readableName(metadata()->provider);
return {};
}
auto Mod::licenses() const -> const QList<ModLicense>& auto Mod::licenses() const -> const QList<ModLicense>&
{ {
return details().licenses; return details().licenses;

View File

@ -56,7 +56,6 @@ class Mod : public Resource {
Mod() = default; Mod() = default;
Mod(const QFileInfo& file); Mod(const QFileInfo& file);
Mod(const QDir& mods_dir, const Metadata::ModStruct& metadata);
Mod(QString file_path) : Mod(QFileInfo(file_path)) {} Mod(QString file_path) : Mod(QFileInfo(file_path)) {}
auto details() const -> const ModDetails&; auto details() const -> const ModDetails&;
@ -65,11 +64,10 @@ class Mod : public Resource {
auto homeurl() const -> QString; auto homeurl() const -> QString;
auto description() const -> QString; auto description() const -> QString;
auto authors() const -> QStringList; auto authors() const -> QStringList;
auto status() const -> ModStatus;
auto provider() const -> std::optional<QString>;
auto licenses() const -> const QList<ModLicense>&; auto licenses() const -> const QList<ModLicense>&;
auto issueTracker() const -> QString; auto issueTracker() const -> QString;
auto metaurl() const -> QString; auto metaurl() const -> QString;
void setDetails(const ModDetails& details);
/** Get the intneral path to the mod's icon file*/ /** Get the intneral path to the mod's icon file*/
QString iconPath() const { return m_local_details.icon_file; } QString iconPath() const { return m_local_details.icon_file; }
@ -78,22 +76,11 @@ class Mod : public Resource {
/** Thread-safe. */ /** Thread-safe. */
void setIcon(QImage new_image) const; void setIcon(QImage new_image) const;
auto metadata() -> std::shared_ptr<Metadata::ModStruct>;
auto metadata() const -> const std::shared_ptr<Metadata::ModStruct>;
void setStatus(ModStatus status);
void setMetadata(std::shared_ptr<Metadata::ModStruct>&& metadata);
void setMetadata(const Metadata::ModStruct& metadata) { setMetadata(std::make_shared<Metadata::ModStruct>(metadata)); }
void setDetails(const ModDetails& details);
bool valid() const override; bool valid() const override;
[[nodiscard]] auto compare(Resource const& other, SortType type) const -> std::pair<int, bool> override; [[nodiscard]] auto compare(Resource const& other, SortType type) const -> std::pair<int, bool> override;
[[nodiscard]] bool applyFilter(QRegularExpression filter) const override; [[nodiscard]] bool applyFilter(QRegularExpression filter) const override;
// Delete all the files of this mod
auto destroy(QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool;
void finishResolvingWithDetails(ModDetails&& details); void finishResolvingWithDetails(ModDetails&& details);
protected: protected:

View File

@ -43,13 +43,6 @@
#include "minecraft/mod/MetadataHandler.h" #include "minecraft/mod/MetadataHandler.h"
enum class ModStatus {
Installed, // Both JAR and Metadata are present
NotInstalled, // Only the Metadata is present
NoMetadata, // Only the JAR is present
Unknown, // Default status
};
struct ModLicense { struct ModLicense {
QString name = {}; QString name = {};
QString id = {}; QString id = {};
@ -149,12 +142,6 @@ struct ModDetails {
/* Path of mod logo */ /* Path of mod logo */
QString icon_file = {}; QString icon_file = {};
/* Installation status of the mod */
ModStatus status = ModStatus::Unknown;
/* Metadata information, if any */
std::shared_ptr<Metadata::ModStruct> metadata = nullptr;
ModDetails() = default; ModDetails() = default;
/** Metadata should be handled manually to properly set the mod status. */ /** Metadata should be handled manually to properly set the mod status. */
@ -169,40 +156,9 @@ struct ModDetails {
, issue_tracker(other.issue_tracker) , issue_tracker(other.issue_tracker)
, licenses(other.licenses) , licenses(other.licenses)
, icon_file(other.icon_file) , icon_file(other.icon_file)
, status(other.status)
{} {}
ModDetails& operator=(const ModDetails& other) ModDetails& operator=(const ModDetails& other) = default;
{
this->mod_id = other.mod_id;
this->name = other.name;
this->version = other.version;
this->mcversion = other.mcversion;
this->homeurl = other.homeurl;
this->description = other.description;
this->authors = other.authors;
this->issue_tracker = other.issue_tracker;
this->licenses = other.licenses;
this->icon_file = other.icon_file;
this->status = other.status;
return *this; ModDetails& operator=(ModDetails&& other) = default;
}
ModDetails& operator=(const ModDetails&& other)
{
this->mod_id = other.mod_id;
this->name = other.name;
this->version = other.version;
this->mcversion = other.mcversion;
this->homeurl = other.homeurl;
this->description = other.description;
this->authors = other.authors;
this->issue_tracker = other.issue_tracker;
this->licenses = other.licenses;
this->icon_file = other.icon_file;
this->status = other.status;
return *this;
}
}; };

View File

@ -52,15 +52,16 @@
#include "Application.h" #include "Application.h"
#include "Json.h" #include "Json.h"
#include "Resource.h"
#include "minecraft/mod/tasks/LocalModParseTask.h" #include "minecraft/mod/tasks/LocalModParseTask.h"
#include "minecraft/mod/tasks/LocalModUpdateTask.h" #include "minecraft/mod/tasks/LocalModUpdateTask.h"
#include "minecraft/mod/tasks/ModFolderLoadTask.h" #include "minecraft/mod/tasks/ResourceFolderLoadTask.h"
#include "modplatform/ModIndex.h" #include "modplatform/ModIndex.h"
#include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameAPI.h"
#include "modplatform/flame/FlameModIndex.h" #include "modplatform/flame/FlameModIndex.h"
ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir) ModFolderModel::ModFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent)
: ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed) : ResourceFolderModel(QDir(dir), instance, is_indexed, create_dir, parent)
{ {
m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider" }); m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider" });
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider") }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider") });
@ -96,15 +97,8 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const
} }
case DateColumn: case DateColumn:
return m_resources[row]->dateTimeChanged(); return m_resources[row]->dateTimeChanged();
case ProviderColumn: { case ProviderColumn:
auto provider = at(row)->provider(); return at(row)->provider();
if (!provider.has_value()) {
//: Unknown mod provider (i.e. not Modrinth, CurseForge, etc...)
return tr("Unknown");
}
return provider.value();
}
default: default:
return QVariant(); return QVariant();
} }
@ -185,14 +179,6 @@ int ModFolderModel::columnCount(const QModelIndex& parent) const
return parent.isValid() ? 0 : NUM_COLUMNS; return parent.isValid() ? 0 : NUM_COLUMNS;
} }
Task* ModFolderModel::createUpdateTask()
{
auto index_dir = indexDir();
auto task = new ModFolderLoadTask(dir(), index_dir, m_is_indexed, m_first_folder_load);
m_first_folder_load = false;
return task;
}
Task* ModFolderModel::createParseTask(Resource& resource) Task* ModFolderModel::createParseTask(Resource& resource)
{ {
return new LocalModParseTask(m_next_resolution_ticket, resource.type(), resource.fileinfo()); return new LocalModParseTask(m_next_resolution_ticket, resource.type(), resource.fileinfo());
@ -273,26 +259,6 @@ auto ModFolderModel::allMods() -> QList<Mod*>
return mods; return mods;
} }
void ModFolderModel::onUpdateSucceeded()
{
auto update_results = static_cast<ModFolderLoadTask*>(m_current_update_task.get())->result();
auto& new_mods = update_results->mods;
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
auto current_list = m_resources_index.keys();
QSet<QString> current_set(current_list.begin(), current_list.end());
auto new_list = new_mods.keys();
QSet<QString> new_set(new_list.begin(), new_list.end());
#else
QSet<QString> current_set(m_resources_index.keys().toSet());
QSet<QString> new_set(new_mods.keys().toSet());
#endif
applyUpdates(current_set, new_set, new_mods);
}
void ModFolderModel::onParseSucceeded(int ticket, QString mod_id) void ModFolderModel::onParseSucceeded(int ticket, QString mod_id)
{ {
auto iter = m_active_parse_tasks.constFind(ticket); auto iter = m_active_parse_tasks.constFind(ticket);

View File

@ -47,10 +47,9 @@
#include "ResourceFolderModel.h" #include "ResourceFolderModel.h"
#include "minecraft/mod/tasks/LocalModParseTask.h" #include "minecraft/mod/tasks/LocalModParseTask.h"
#include "minecraft/mod/tasks/ModFolderLoadTask.h" #include "minecraft/mod/tasks/ResourceFolderLoadTask.h"
#include "modplatform/ModIndex.h" #include "modplatform/ModIndex.h"
class LegacyInstance;
class BaseInstance; class BaseInstance;
class QFileSystemWatcher; class QFileSystemWatcher;
@ -63,7 +62,7 @@ class ModFolderModel : public ResourceFolderModel {
public: public:
enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, VersionColumn, DateColumn, ProviderColumn, NUM_COLUMNS }; enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, VersionColumn, DateColumn, ProviderColumn, NUM_COLUMNS };
enum ModStatusAction { Disable, Enable, Toggle }; enum ModStatusAction { Disable, Enable, Toggle };
ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed = false, bool create_dir = true); ModFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr);
virtual QString id() const override { return "mods"; } virtual QString id() const override { return "mods"; }
@ -72,7 +71,7 @@ class ModFolderModel : public ResourceFolderModel {
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
int columnCount(const QModelIndex& parent) const override; int columnCount(const QModelIndex& parent) const override;
[[nodiscard]] Task* createUpdateTask() override; [[nodiscard]] Resource* createResource(const QFileInfo& file) override { return new Mod(file); }
[[nodiscard]] Task* createParseTask(Resource&) override; [[nodiscard]] Task* createParseTask(Resource&) override;
bool installMod(QString file_path) { return ResourceFolderModel::installResource(file_path); } bool installMod(QString file_path) { return ResourceFolderModel::installResource(file_path); }
@ -87,18 +86,11 @@ class ModFolderModel : public ResourceFolderModel {
bool startWatching() override; bool startWatching() override;
bool stopWatching() override; bool stopWatching() override;
QDir indexDir() { return { QString("%1/.index").arg(dir().absolutePath()) }; }
auto selectedMods(QModelIndexList& indexes) -> QList<Mod*>; auto selectedMods(QModelIndexList& indexes) -> QList<Mod*>;
auto allMods() -> QList<Mod*>; auto allMods() -> QList<Mod*>;
RESOURCE_HELPERS(Mod) RESOURCE_HELPERS(Mod)
private slots: private slots:
void onUpdateSucceeded() override;
void onParseSucceeded(int ticket, QString resource_id) override; void onParseSucceeded(int ticket, QString resource_id) override;
protected:
bool m_is_indexed;
bool m_first_folder_load = true;
}; };

View File

@ -5,6 +5,8 @@
#include "FileSystem.h" #include "FileSystem.h"
static ModPlatform::ProviderCapabilities ProviderCaps;
Resource::Resource(QObject* parent) : QObject(parent) {} Resource::Resource(QObject* parent) : QObject(parent) {}
Resource::Resource(QFileInfo file_info) : QObject() Resource::Resource(QFileInfo file_info) : QObject()
@ -54,6 +56,14 @@ void Resource::parseFile()
m_changed_date_time = m_file_info.lastModified(); m_changed_date_time = m_file_info.lastModified();
} }
auto Resource::name() const -> QString
{
if (metadata())
return metadata()->name;
return m_name;
}
static void removeThePrefix(QString& string) static void removeThePrefix(QString& string)
{ {
QRegularExpression regex(QStringLiteral("^(?:the|teh) +"), QRegularExpression::CaseInsensitiveOption); QRegularExpression regex(QStringLiteral("^(?:the|teh) +"), QRegularExpression::CaseInsensitiveOption);
@ -61,6 +71,22 @@ static void removeThePrefix(QString& string)
string = string.trimmed(); string = string.trimmed();
} }
auto Resource::provider() const -> QString
{
if (metadata())
return ProviderCaps.readableName(metadata()->provider);
return tr("Unknown");
}
void Resource::setMetadata(std::shared_ptr<Metadata::ModStruct>&& metadata)
{
if (status() == ResourceStatus::NO_METADATA)
setStatus(ResourceStatus::INSTALLED);
m_metadata = metadata;
}
std::pair<int, bool> Resource::compare(const Resource& other, SortType type) const std::pair<int, bool> Resource::compare(const Resource& other, SortType type) const
{ {
switch (type) { switch (type) {
@ -152,6 +178,23 @@ bool Resource::destroy(bool attemptTrash)
return (attemptTrash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath()); 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());
if (metadata()) {
Metadata::remove(index_dir, metadata()->slug);
} else {
auto n = name();
Metadata::remove(index_dir, n);
}
}
return destroy(attempt_trash);
}
bool Resource::isSymLinkUnder(const QString& instPath) const bool Resource::isSymLinkUnder(const QString& instPath) const
{ {
if (isSymLink()) if (isSymLink())

View File

@ -5,6 +5,8 @@
#include <QObject> #include <QObject>
#include <QPointer> #include <QPointer>
#include "MetadataHandler.h"
#include "ModDetails.h"
#include "QObjectPtr.h" #include "QObjectPtr.h"
enum class ResourceType { enum class ResourceType {
@ -15,6 +17,13 @@ enum class ResourceType {
LITEMOD, //!< The resource is a litemod LITEMOD, //!< The resource is a litemod
}; };
enum class ResourceStatus {
INSTALLED, // Both JAR and Metadata are present
NOT_INSTALLED, // Only the Metadata is present
NO_METADATA, // Only the JAR is present
UNKNOWN, // Default status
};
enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER }; enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER };
enum class EnableAction { ENABLE, DISABLE, TOGGLE }; enum class EnableAction { ENABLE, DISABLE, TOGGLE };
@ -46,9 +55,18 @@ class Resource : public QObject {
[[nodiscard]] auto type() const -> ResourceType { return m_type; } [[nodiscard]] auto type() const -> ResourceType { return m_type; }
[[nodiscard]] bool enabled() const { return m_enabled; } [[nodiscard]] bool enabled() const { return m_enabled; }
[[nodiscard]] virtual auto name() const -> QString { return m_name; } [[nodiscard]] virtual auto name() const -> QString;
[[nodiscard]] virtual bool valid() const { return m_type != ResourceType::UNKNOWN; } [[nodiscard]] virtual bool valid() const { return m_type != ResourceType::UNKNOWN; }
[[nodiscard]] auto status() const -> ResourceStatus { return m_status; };
[[nodiscard]] auto metadata() -> std::shared_ptr<Metadata::ModStruct> { return m_metadata; }
[[nodiscard]] auto metadata() const -> std::shared_ptr<const Metadata::ModStruct> { return m_metadata; }
[[nodiscard]] auto provider() const -> QString;
void setStatus(ResourceStatus status) { m_status = status; }
void setMetadata(std::shared_ptr<Metadata::ModStruct>&& metadata);
void setMetadata(const Metadata::ModStruct& metadata) { setMetadata(std::make_shared<Metadata::ModStruct>(metadata)); }
/** Compares two Resources, for sorting purposes, considering a ascending order, returning: /** Compares two Resources, for sorting purposes, considering a ascending order, returning:
* > 0: 'this' comes after 'other' * > 0: 'this' comes after 'other'
* = 0: 'this' is equal to 'other' * = 0: 'this' is equal to 'other'
@ -81,7 +99,8 @@ class Resource : public QObject {
} }
// Delete all files of this resource. // Delete all files of this resource.
bool destroy(bool attemptTrash = true); auto destroy(bool attemptTrash = true) -> bool;
auto destroy(QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool;
[[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); } [[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); }
@ -110,6 +129,11 @@ class Resource : public QObject {
/* The type of file we're dealing with. */ /* The type of file we're dealing with. */
ResourceType m_type = ResourceType::UNKNOWN; ResourceType m_type = ResourceType::UNKNOWN;
/* Installation status of the resource. */
ResourceStatus m_status = ResourceStatus::UNKNOWN;
std::shared_ptr<Metadata::ModStruct> m_metadata = nullptr;
/* Whether the resource is enabled (e.g. shows up in the game) or not. */ /* Whether the resource is enabled (e.g. shows up in the game) or not. */
bool m_enabled = true; bool m_enabled = true;

View File

@ -15,14 +15,14 @@
#include "FileSystem.h" #include "FileSystem.h"
#include "QVariantUtils.h" #include "QVariantUtils.h"
#include "minecraft/mod/tasks/BasicFolderLoadTask.h" #include "minecraft/mod/tasks/ResourceFolderLoadTask.h"
#include "settings/Setting.h" #include "settings/Setting.h"
#include "tasks/Task.h" #include "tasks/Task.h"
#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/CustomMessageBox.h"
ResourceFolderModel::ResourceFolderModel(QDir dir, BaseInstance* instance, QObject* parent, bool create_dir) ResourceFolderModel::ResourceFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent)
: QAbstractListModel(parent), m_dir(dir), m_instance(instance), m_watcher(this) : QAbstractListModel(parent), m_dir(dir), m_instance(instance), m_watcher(this), m_is_indexed(is_indexed)
{ {
if (create_dir) { if (create_dir) {
FS::ensureFolderPathExists(m_dir.absolutePath()); FS::ensureFolderPathExists(m_dir.absolutePath());
@ -290,7 +290,7 @@ void ResourceFolderModel::resolveResource(Resource* res)
void ResourceFolderModel::onUpdateSucceeded() void ResourceFolderModel::onUpdateSucceeded()
{ {
auto update_results = static_cast<BasicFolderLoadTask*>(m_current_update_task.get())->result(); auto update_results = static_cast<ResourceFolderLoadTask*>(m_current_update_task.get())->result();
auto& new_resources = update_results->resources; auto& new_resources = update_results->resources;
@ -320,7 +320,11 @@ void ResourceFolderModel::onParseSucceeded(int ticket, QString resource_id)
Task* ResourceFolderModel::createUpdateTask() Task* ResourceFolderModel::createUpdateTask()
{ {
return new BasicFolderLoadTask(m_dir); auto index_dir = indexDir();
auto task = new ResourceFolderLoadTask(dir(), index_dir, m_is_indexed, m_first_folder_load,
[this](const QFileInfo& file) { return createResource(file); });
m_first_folder_load = false;
return task;
} }
bool ResourceFolderModel::hasPendingParseTasks() const bool ResourceFolderModel::hasPendingParseTasks() const

View File

@ -29,7 +29,7 @@ class QSortFilterProxyModel;
class ResourceFolderModel : public QAbstractListModel { class ResourceFolderModel : public QAbstractListModel {
Q_OBJECT Q_OBJECT
public: public:
ResourceFolderModel(QDir, BaseInstance* instance, QObject* parent = nullptr, bool create_dir = true); ResourceFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr);
~ResourceFolderModel() override; ~ResourceFolderModel() override;
virtual QString id() const { return "resource"; } virtual QString id() const { return "resource"; }
@ -52,6 +52,8 @@ class ResourceFolderModel : public QAbstractListModel {
virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); } virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); }
virtual bool stopWatching() { return stopWatching({ m_dir.absolutePath() }); } virtual bool stopWatching() { return stopWatching({ m_dir.absolutePath() }); }
QDir indexDir() { return { QString("%1/.index").arg(dir().absolutePath()) }; }
/** Given a path in the system, install that resource, moving it to its place in the /** Given a path in the system, install that resource, moving it to its place in the
* instance file hierarchy. * instance file hierarchy.
* *
@ -152,7 +154,9 @@ class ResourceFolderModel : public QAbstractListModel {
* This Task is normally executed when opening a page, so it shouldn't contain much heavy work. * This Task is normally executed when opening a page, so it shouldn't contain much heavy work.
* If such work is needed, try using it in the Task create by createParseTask() instead! * If such work is needed, try using it in the Task create by createParseTask() instead!
*/ */
[[nodiscard]] virtual Task* createUpdateTask(); [[nodiscard]] Task* createUpdateTask();
[[nodiscard]] virtual Resource* createResource(const QFileInfo& info) { return new Resource(info); }
/** This creates a new parse task to be executed by onUpdateSucceeded(). /** This creates a new parse task to be executed by onUpdateSucceeded().
* *
@ -210,6 +214,9 @@ class ResourceFolderModel : public QAbstractListModel {
QFileSystemWatcher m_watcher; QFileSystemWatcher m_watcher;
bool m_is_watching = false; bool m_is_watching = false;
bool m_is_indexed;
bool m_first_folder_load = true;
Task::Ptr m_current_update_task = nullptr; Task::Ptr m_current_update_task = nullptr;
bool m_scheduled_update = false; bool m_scheduled_update = false;

View File

@ -44,10 +44,11 @@
#include "Application.h" #include "Application.h"
#include "Version.h" #include "Version.h"
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
#include "minecraft/mod/tasks/LocalResourcePackParseTask.h" #include "minecraft/mod/tasks/LocalResourcePackParseTask.h"
#include "minecraft/mod/tasks/ResourceFolderLoadTask.h"
ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) ResourcePackFolderModel::ResourcePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent)
: ResourceFolderModel(dir, instance, is_indexed, create_dir, parent)
{ {
m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified" }); m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified" });
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified") }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified") });
@ -173,11 +174,6 @@ int ResourcePackFolderModel::columnCount(const QModelIndex& parent) const
return parent.isValid() ? 0 : NUM_COLUMNS; return parent.isValid() ? 0 : NUM_COLUMNS;
} }
Task* ResourcePackFolderModel::createUpdateTask()
{
return new BasicFolderLoadTask(m_dir, [](QFileInfo const& entry) { return makeShared<ResourcePack>(entry); });
}
Task* ResourcePackFolderModel::createParseTask(Resource& resource) Task* ResourcePackFolderModel::createParseTask(Resource& resource)
{ {
return new LocalResourcePackParseTask(m_next_resolution_ticket, static_cast<ResourcePack&>(resource)); return new LocalResourcePackParseTask(m_next_resolution_ticket, static_cast<ResourcePack&>(resource));

View File

@ -9,16 +9,16 @@ class ResourcePackFolderModel : public ResourceFolderModel {
public: public:
enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, PackFormatColumn, DateColumn, NUM_COLUMNS }; enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, PackFormatColumn, DateColumn, NUM_COLUMNS };
explicit ResourcePackFolderModel(const QString& dir, BaseInstance* instance); explicit ResourcePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr);
virtual QString id() const override { return "resourcepacks"; } QString id() const override { return "resourcepacks"; }
[[nodiscard]] QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; [[nodiscard]] QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
[[nodiscard]] int columnCount(const QModelIndex& parent) const override; [[nodiscard]] int columnCount(const QModelIndex& parent) const override;
[[nodiscard]] Task* createUpdateTask() override; [[nodiscard]] Resource* createResource(const QFileInfo& file) override { return new ResourcePack(file); }
[[nodiscard]] Task* createParseTask(Resource&) override; [[nodiscard]] Task* createParseTask(Resource&) override;
RESOURCE_HELPERS(ResourcePack) RESOURCE_HELPERS(ResourcePack)

View File

@ -6,7 +6,9 @@ class ShaderPackFolderModel : public ResourceFolderModel {
Q_OBJECT Q_OBJECT
public: public:
explicit ShaderPackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) {} explicit ShaderPackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr)
: ResourceFolderModel(dir, instance, is_indexed, create_dir, parent)
{}
virtual QString id() const override { return "shaderpacks"; } virtual QString id() const override { return "shaderpacks"; }
}; };

View File

@ -39,10 +39,11 @@
#include "TexturePackFolderModel.h" #include "TexturePackFolderModel.h"
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
#include "minecraft/mod/tasks/LocalTexturePackParseTask.h" #include "minecraft/mod/tasks/LocalTexturePackParseTask.h"
#include "minecraft/mod/tasks/ResourceFolderLoadTask.h"
TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) TexturePackFolderModel::TexturePackFolderModel(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", "Last Modified" }); m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified" });
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified") }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified") });
@ -52,11 +53,6 @@ TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance*
m_columnsHideable = { false, true, false, true }; m_columnsHideable = { false, true, false, true };
} }
Task* TexturePackFolderModel::createUpdateTask()
{
return new BasicFolderLoadTask(m_dir, [](QFileInfo const& entry) { return makeShared<TexturePack>(entry); });
}
Task* TexturePackFolderModel::createParseTask(Resource& resource) Task* TexturePackFolderModel::createParseTask(Resource& resource)
{ {
return new LocalTexturePackParseTask(m_next_resolution_ticket, static_cast<TexturePack&>(resource)); return new LocalTexturePackParseTask(m_next_resolution_ticket, static_cast<TexturePack&>(resource));

View File

@ -46,7 +46,7 @@ class TexturePackFolderModel : public ResourceFolderModel {
public: public:
enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, DateColumn, NUM_COLUMNS }; enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, DateColumn, NUM_COLUMNS };
explicit TexturePackFolderModel(const QString& dir, std::shared_ptr<const BaseInstance> instance); explicit TexturePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr);
virtual QString id() const override { return "texturepacks"; } virtual QString id() const override { return "texturepacks"; }
@ -55,8 +55,7 @@ class TexturePackFolderModel : public ResourceFolderModel {
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
[[nodiscard]] int columnCount(const QModelIndex& parent) const override; [[nodiscard]] int columnCount(const QModelIndex& parent) const override;
explicit TexturePackFolderModel(const QString& dir, BaseInstance* instance); [[nodiscard]] Resource* createResource(const QFileInfo& file) override { return new TexturePack(file); }
[[nodiscard]] Task* createUpdateTask() override;
[[nodiscard]] Task* createParseTask(Resource&) override; [[nodiscard]] Task* createParseTask(Resource&) override;
RESOURCE_HELPERS(TexturePack) RESOURCE_HELPERS(TexturePack)

View File

@ -1,74 +0,0 @@
#pragma once
#include <QDir>
#include <QMap>
#include <QObject>
#include <QThread>
#include <memory>
#include "minecraft/mod/Resource.h"
#include "tasks/Task.h"
/** Very simple task that just loads a folder's contents directly.
*/
class BasicFolderLoadTask : public Task {
Q_OBJECT
public:
struct Result {
QMap<QString, Resource::Ptr> resources;
};
using ResultPtr = std::shared_ptr<Result>;
[[nodiscard]] ResultPtr result() const { return m_result; }
public:
BasicFolderLoadTask(QDir dir) : Task(nullptr, false), m_dir(dir), m_result(new Result), m_thread_to_spawn_into(thread())
{
m_create_func = [](QFileInfo const& entry) -> Resource::Ptr { return makeShared<Resource>(entry); };
}
BasicFolderLoadTask(QDir dir, std::function<Resource::Ptr(QFileInfo const&)> create_function)
: Task(nullptr, false)
, m_dir(dir)
, m_result(new Result)
, m_create_func(std::move(create_function))
, m_thread_to_spawn_into(thread())
{}
[[nodiscard]] bool canAbort() const override { return true; }
bool abort() override
{
m_aborted.store(true);
return true;
}
void executeTask() override
{
if (thread() != m_thread_to_spawn_into)
connect(this, &Task::finished, this->thread(), &QThread::quit);
m_dir.refresh();
for (auto entry : m_dir.entryInfoList()) {
auto resource = m_create_func(entry);
resource->moveToThread(m_thread_to_spawn_into);
m_result->resources.insert(resource->internal_id(), resource);
}
if (m_aborted)
emit finished();
else
emitSucceeded();
}
private:
QDir m_dir;
ResultPtr m_result;
std::atomic<bool> m_aborted = false;
std::function<Resource::Ptr(QFileInfo const&)> m_create_func;
/** This is the thread in which we should put new mod objects */
QThread* m_thread_to_spawn_into;
};

View File

@ -47,11 +47,6 @@ class LocalModParseTask : public Task {
[[nodiscard]] int token() const { return m_token; } [[nodiscard]] int token() const { return m_token; }
private:
void processAsZip();
void processAsFolder();
void processAsLitemod();
private: private:
int m_token; int m_token;
ResourceType m_type; ResourceType m_type;

View File

@ -34,23 +34,20 @@
* limitations under the License. * limitations under the License.
*/ */
#include "ModFolderLoadTask.h" #include "ResourceFolderLoadTask.h"
#include "minecraft/mod/MetadataHandler.h" #include "minecraft/mod/MetadataHandler.h"
#include <QThread> #include <QThread>
ModFolderLoadTask::ModFolderLoadTask(QDir mods_dir, QDir index_dir, bool is_indexed, bool clean_orphan) ResourceFolderLoadTask::ResourceFolderLoadTask(const QDir& resource_dir,
: Task(nullptr, false) const QDir& index_dir,
, m_mods_dir(mods_dir) bool is_indexed,
, m_index_dir(index_dir) bool clean_orphan,
, m_is_indexed(is_indexed) std::function<Resource*(const QFileInfo&)> create_function)
, m_clean_orphan(clean_orphan)
, m_result(new Result())
, m_thread_to_spawn_into(thread())
{} {}
void ModFolderLoadTask::executeTask() void ResourceFolderLoadTask::executeTask()
{ {
if (thread() != m_thread_to_spawn_into) if (thread() != m_thread_to_spawn_into)
connect(this, &Task::finished, this->thread(), &QThread::quit); connect(this, &Task::finished, this->thread(), &QThread::quit);
@ -63,32 +60,32 @@ void ModFolderLoadTask::executeTask()
// Read JAR files that don't have metadata // Read JAR files that don't have metadata
m_mods_dir.refresh(); m_mods_dir.refresh();
for (auto entry : m_mods_dir.entryInfoList()) { for (auto entry : m_mods_dir.entryInfoList()) {
Mod* mod(new Mod(entry)); Resource* resource = m_create_func(entry);
if (mod->enabled()) { if (resource->enabled()) {
if (m_result->mods.contains(mod->internal_id())) { if (m_result->resources.contains(resource->internal_id())) {
m_result->mods[mod->internal_id()]->setStatus(ModStatus::Installed); m_result->resources[resource->internal_id()]->setStatus(ResourceStatus::INSTALLED);
// Delete the object we just created, since a valid one is already in the mods list. // Delete the object we just created, since a valid one is already in the mods list.
delete mod; delete resource;
} else { } else {
m_result->mods[mod->internal_id()].reset(std::move(mod)); m_result->resources[resource->internal_id()].reset(resource);
m_result->mods[mod->internal_id()]->setStatus(ModStatus::NoMetadata); m_result->resources[resource->internal_id()]->setStatus(ResourceStatus::NO_METADATA);
} }
} else { } else {
QString chopped_id = mod->internal_id().chopped(9); QString chopped_id = resource->internal_id().chopped(9);
if (m_result->mods.contains(chopped_id)) { if (m_result->resources.contains(chopped_id)) {
m_result->mods[mod->internal_id()].reset(std::move(mod)); m_result->resources[resource->internal_id()].reset(resource);
auto metadata = m_result->mods[chopped_id]->metadata(); auto metadata = m_result->resources[chopped_id]->metadata();
if (metadata) { if (metadata) {
mod->setMetadata(*metadata); resource->setMetadata(*metadata);
m_result->mods[mod->internal_id()]->setStatus(ModStatus::Installed); m_result->resources[resource->internal_id()]->setStatus(ResourceStatus::INSTALLED);
m_result->mods.remove(chopped_id); m_result->resources.remove(chopped_id);
} }
} else { } else {
m_result->mods[mod->internal_id()].reset(std::move(mod)); m_result->resources[resource->internal_id()].reset(resource);
m_result->mods[mod->internal_id()]->setStatus(ModStatus::NoMetadata); m_result->resources[resource->internal_id()]->setStatus(ResourceStatus::NO_METADATA);
} }
} }
} }
@ -96,17 +93,17 @@ void ModFolderLoadTask::executeTask()
// Remove orphan metadata to prevent issues // Remove orphan metadata to prevent issues
// See https://github.com/PolyMC/PolyMC/issues/996 // See https://github.com/PolyMC/PolyMC/issues/996
if (m_clean_orphan) { if (m_clean_orphan) {
QMutableMapIterator iter(m_result->mods); QMutableMapIterator iter(m_result->resources);
while (iter.hasNext()) { while (iter.hasNext()) {
auto mod = iter.next().value(); auto resource = iter.next().value();
if (mod->status() == ModStatus::NotInstalled) { if (resource->status() == ResourceStatus::NOT_INSTALLED) {
mod->destroy(m_index_dir, false, false); resource->destroy(m_index_dir, false, false);
iter.remove(); iter.remove();
} }
} }
} }
for (auto mod : m_result->mods) for (auto mod : m_result->resources)
mod->moveToThread(m_thread_to_spawn_into); mod->moveToThread(m_thread_to_spawn_into);
if (m_aborted) if (m_aborted)
@ -115,18 +112,18 @@ void ModFolderLoadTask::executeTask()
emitSucceeded(); emitSucceeded();
} }
void ModFolderLoadTask::getFromMetadata() void ResourceFolderLoadTask::getFromMetadata()
{ {
m_index_dir.refresh(); m_index_dir.refresh();
for (auto entry : m_index_dir.entryList(QDir::Files)) { for (auto entry : m_index_dir.entryList(QDir::Files)) {
auto metadata = Metadata::get(m_index_dir, entry); auto metadata = Metadata::get(m_index_dir, entry);
if (!metadata.isValid()) { if (!metadata.isValid())
return; return;
}
auto* mod = new Mod(m_mods_dir, metadata); auto* resource = m_create_func(QFileInfo(m_mods_dir.filePath(metadata.filename)));
mod->setStatus(ModStatus::NotInstalled); resource->setMetadata(metadata);
m_result->mods[mod->internal_id()].reset(std::move(mod)); resource->setStatus(ResourceStatus::NOT_INSTALLED);
m_result->resources[resource->internal_id()].reset(resource);
} }
} }

View File

@ -44,17 +44,21 @@
#include "minecraft/mod/Mod.h" #include "minecraft/mod/Mod.h"
#include "tasks/Task.h" #include "tasks/Task.h"
class ModFolderLoadTask : public Task { class ResourceFolderLoadTask : public Task {
Q_OBJECT Q_OBJECT
public: public:
struct Result { struct Result {
QMap<QString, Mod::Ptr> mods; QMap<QString, Resource::Ptr> resources;
}; };
using ResultPtr = std::shared_ptr<Result>; using ResultPtr = std::shared_ptr<Result>;
ResultPtr result() const { return m_result; } ResultPtr result() const { return m_result; }
public: public:
ModFolderLoadTask(QDir mods_dir, QDir index_dir, bool is_indexed, bool clean_orphan = false); ResourceFolderLoadTask(const QDir& resource_dir,
const QDir& index_dir,
bool is_indexed,
bool clean_orphan,
std::function<Resource*(const QFileInfo&)> create_function);
[[nodiscard]] bool canAbort() const override { return true; } [[nodiscard]] bool canAbort() const override { return true; }
bool abort() override bool abort() override
@ -76,6 +80,8 @@ class ModFolderLoadTask : public Task {
std::atomic<bool> m_aborted = false; std::atomic<bool> m_aborted = false;
std::function<Resource*(QFileInfo const&)> m_create_func;
/** This is the thread in which we should put new mod objects */ /** This is the thread in which we should put new mod objects */
QThread* m_thread_to_spawn_into; QThread* m_thread_to_spawn_into;
}; };

View File

@ -94,7 +94,7 @@ void EnsureMetadataTask::executeTask()
} }
// They already have the right metadata :o // They already have the right metadata :o
if (mod->status() != ModStatus::NoMetadata && mod->metadata() && mod->metadata()->provider == m_provider) { if (mod->status() != ResourceStatus::NO_METADATA && mod->metadata() && mod->metadata()->provider == m_provider) {
qDebug() << "Mod" << mod->name() << "already has metadata!"; qDebug() << "Mod" << mod->name() << "already has metadata!";
emitReady(mod); emitReady(mod);
continue; continue;

View File

@ -154,7 +154,7 @@ void FlameCheckUpdate::executeTask()
continue; continue;
} }
if (!latest_ver.hash.isEmpty() && (mod->metadata()->hash != latest_ver.hash || mod->status() == ModStatus::NotInstalled)) { if (!latest_ver.hash.isEmpty() && (mod->metadata()->hash != latest_ver.hash || mod->status() == ResourceStatus::NOT_INSTALLED)) {
// Fake pack with the necessary info to pass to the download task :) // Fake pack with the necessary info to pass to the download task :)
auto pack = std::make_shared<ModPlatform::IndexedPack>(); auto pack = std::make_shared<ModPlatform::IndexedPack>();
pack->name = mod->name(); pack->name = mod->name();
@ -167,7 +167,7 @@ void FlameCheckUpdate::executeTask()
pack->provider = ModPlatform::ResourceProvider::FLAME; pack->provider = ModPlatform::ResourceProvider::FLAME;
auto old_version = mod->version(); auto old_version = mod->version();
if (old_version.isEmpty() && mod->status() != ModStatus::NotInstalled) { if (old_version.isEmpty() && mod->status() != ResourceStatus::NOT_INSTALLED) {
auto current_ver = getFileInfo(latest_ver.addonId.toInt(), mod->metadata()->file_id.toInt()); auto current_ver = getFileInfo(latest_ver.addonId.toInt(), mod->metadata()->file_id.toInt());
old_version = current_ver.version; old_version = current_ver.version;
} }

View File

@ -144,7 +144,7 @@ void ModrinthCheckUpdate::executeTask()
auto mod = *mod_iter; auto mod = *mod_iter;
auto key = project_ver.hash; auto key = project_ver.hash;
if ((key != hash && project_ver.is_preferred) || (mod->status() == ModStatus::NotInstalled)) { if ((key != hash && project_ver.is_preferred) || (mod->status() == ResourceStatus::NOT_INSTALLED)) {
if (mod->version() == project_ver.version_number) if (mod->version() == project_ver.version_number)
continue; continue;

View File

@ -121,7 +121,7 @@ void ModrinthPackExportTask::collectHashes()
modIter != allMods.end()) { modIter != allMods.end()) {
const Mod* mod = *modIter; const Mod* mod = *modIter;
if (mod->metadata() != nullptr) { if (mod->metadata() != nullptr) {
QUrl& url = mod->metadata()->url; const QUrl& url = mod->metadata()->url;
// ensure the url is permitted on modrinth.com // ensure the url is permitted on modrinth.com
if (!url.isEmpty() && BuildConfig.MODRINTH_MRPACK_HOSTS.contains(url.host())) { if (!url.isEmpty() && BuildConfig.MODRINTH_MRPACK_HOSTS.contains(url.host())) {
qDebug() << "Resolving" << relative << "from index"; qDebug() << "Resolving" << relative << "from index";

View File

@ -35,6 +35,7 @@ auto getRealIndexName(QDir& index_dir, QString normalized_index_name, bool shoul
class V1 { class V1 {
public: public:
// can also represent other resources beside loader mods - but this is what packwiz calls it
struct Mod { struct Mod {
QString slug{}; QString slug{};
QString name{}; QString name{};

View File

@ -228,7 +228,7 @@ auto ModUpdateDialog::ensureMetadata() -> bool
}; };
for (auto candidate : m_candidates) { for (auto candidate : m_candidates) {
if (candidate->status() != ModStatus::NoMetadata) { if (candidate->status() != ResourceStatus::NO_METADATA) {
onMetadataEnsured(candidate); onMetadataEnsured(candidate);
continue; continue;
} }