mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2025-06-13 05:37:42 +02:00
Refactor updating mechanisms to work with all resources
Summary: - It compiles - I need to go to bed Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
This commit is contained in:
@ -1031,8 +1031,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/dialogs/BlockedModsDialog.h
|
||||
ui/dialogs/ChooseProviderDialog.h
|
||||
ui/dialogs/ChooseProviderDialog.cpp
|
||||
ui/dialogs/ModUpdateDialog.cpp
|
||||
ui/dialogs/ModUpdateDialog.h
|
||||
ui/dialogs/ResourceUpdateDialog.cpp
|
||||
ui/dialogs/ResourceUpdateDialog.h
|
||||
ui/dialogs/InstallLoaderDialog.cpp
|
||||
ui/dialogs/InstallLoaderDialog.h
|
||||
|
||||
|
@ -89,35 +89,35 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return at(row)->version();
|
||||
return at(row).version();
|
||||
}
|
||||
case DateColumn:
|
||||
return m_resources[row]->dateTimeChanged();
|
||||
case ProviderColumn:
|
||||
return at(row)->provider();
|
||||
return at(row).provider();
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
case Qt::ToolTipRole:
|
||||
if (column == NAME_COLUMN) {
|
||||
if (at(row)->isSymLinkUnder(instDirPath())) {
|
||||
if (at(row).isSymLinkUnder(instDirPath())) {
|
||||
return m_resources[row]->internal_id() +
|
||||
tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
|
||||
"\nCanonical Path: %1")
|
||||
.arg(at(row)->fileinfo().canonicalFilePath());
|
||||
.arg(at(row).fileinfo().canonicalFilePath());
|
||||
}
|
||||
if (at(row)->isMoreThanOneHardLink()) {
|
||||
if (at(row).isMoreThanOneHardLink()) {
|
||||
return m_resources[row]->internal_id() +
|
||||
tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
|
||||
}
|
||||
}
|
||||
return m_resources[row]->internal_id();
|
||||
case Qt::DecorationRole: {
|
||||
if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
|
||||
if (column == NAME_COLUMN && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink()))
|
||||
return APPLICATION->getThemedIcon("status-yellow");
|
||||
if (column == ImageColumn) {
|
||||
return at(row)->icon({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding);
|
||||
return at(row).icon({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@ -129,7 +129,7 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const
|
||||
case Qt::CheckStateRole:
|
||||
switch (column) {
|
||||
case ActiveColumn:
|
||||
return at(row)->enabled() ? Qt::Checked : Qt::Unchecked;
|
||||
return at(row).enabled() ? Qt::Checked : Qt::Unchecked;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
@ -190,29 +190,6 @@ bool ModFolderModel::isValid()
|
||||
return m_dir.exists() && m_dir.isReadable();
|
||||
}
|
||||
|
||||
auto ModFolderModel::selectedMods(QModelIndexList& indexes) -> QList<Mod*>
|
||||
{
|
||||
QList<Mod*> selected_resources;
|
||||
for (auto i : indexes) {
|
||||
if (i.column() != 0)
|
||||
continue;
|
||||
|
||||
selected_resources.push_back(at(i.row()));
|
||||
}
|
||||
return selected_resources;
|
||||
}
|
||||
|
||||
auto ModFolderModel::allMods() -> QList<Mod*>
|
||||
{
|
||||
QList<Mod*> mods;
|
||||
|
||||
for (auto& res : qAsConst(m_resources)) {
|
||||
mods.append(static_cast<Mod*>(res.get()));
|
||||
}
|
||||
|
||||
return mods;
|
||||
}
|
||||
|
||||
void ModFolderModel::onParseSucceeded(int ticket, QString mod_id)
|
||||
{
|
||||
auto iter = m_active_parse_tasks.constFind(ticket);
|
||||
|
@ -76,9 +76,6 @@ class ModFolderModel : public ResourceFolderModel {
|
||||
|
||||
bool isValid();
|
||||
|
||||
auto selectedMods(QModelIndexList& indexes) -> QList<Mod*>;
|
||||
auto allMods() -> QList<Mod*>;
|
||||
|
||||
RESOURCE_HELPERS(Mod)
|
||||
|
||||
private slots:
|
||||
|
@ -184,7 +184,7 @@ auto Resource::destroy(const QDir& index_dir, bool preserve_metadata, bool attem
|
||||
return (attempt_trash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath());
|
||||
}
|
||||
|
||||
auto Resource::destroyMetadata(const QDir& index_dir) -> bool
|
||||
auto Resource::destroyMetadata(const QDir& index_dir) -> void
|
||||
{
|
||||
if (metadata()) {
|
||||
Metadata::remove(index_dir, metadata()->slug);
|
||||
|
@ -101,7 +101,7 @@ class Resource : public QObject {
|
||||
// Delete all files of this resource.
|
||||
auto destroy(const QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool;
|
||||
// Delete the metadata only.
|
||||
auto destroyMetadata(const QDir& index_dir) -> bool;
|
||||
auto destroyMetadata(const QDir& index_dir) -> void;
|
||||
|
||||
[[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); }
|
||||
|
||||
|
@ -242,10 +242,10 @@ bool ResourceFolderModel::deleteResources(const QModelIndexList& indexes)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceFolderModel::deleteMetadata(const QModelIndexList& indexes)
|
||||
void ResourceFolderModel::deleteMetadata(const QModelIndexList& indexes)
|
||||
{
|
||||
if (indexes.isEmpty())
|
||||
return true;
|
||||
return;
|
||||
|
||||
for (auto i : indexes) {
|
||||
if (i.column() != 0)
|
||||
@ -256,8 +256,6 @@ bool ResourceFolderModel::deleteMetadata(const QModelIndexList& indexes)
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceFolderModel::setResourceEnabled(const QModelIndexList& indexes, EnableAction action)
|
||||
|
@ -19,6 +19,58 @@
|
||||
|
||||
class QSortFilterProxyModel;
|
||||
|
||||
/* A macro to define useful functions to handle Resource* -> T* more easily on derived classes */
|
||||
#define RESOURCE_HELPERS(T) \
|
||||
[[nodiscard]] T& operator[](int index) \
|
||||
{ \
|
||||
return *static_cast<T*>(m_resources[index].get()); \
|
||||
} \
|
||||
[[nodiscard]] T& at(int index) \
|
||||
{ \
|
||||
return *static_cast<T*>(m_resources[index].get()); \
|
||||
} \
|
||||
[[nodiscard]] const T& at(int index) const \
|
||||
{ \
|
||||
return *static_cast<const T*>(m_resources.at(index).get()); \
|
||||
} \
|
||||
[[nodiscard]] T& first() \
|
||||
{ \
|
||||
return *static_cast<T*>(m_resources.first().get()); \
|
||||
} \
|
||||
[[nodiscard]] T& last() \
|
||||
{ \
|
||||
return *static_cast<T*>(m_resources.last().get()); \
|
||||
} \
|
||||
[[nodiscard]] T* find(QString id) \
|
||||
{ \
|
||||
auto iter = std::find_if(m_resources.constBegin(), m_resources.constEnd(), \
|
||||
[&](Resource::Ptr const& r) { return r->internal_id() == id; }); \
|
||||
if (iter == m_resources.constEnd()) \
|
||||
return nullptr; \
|
||||
return static_cast<T*>((*iter).get()); \
|
||||
} \
|
||||
QList<T*> selected##T##s(const QModelIndexList& indexes) \
|
||||
{ \
|
||||
QList<T*> result; \
|
||||
for (const QModelIndex& index : indexes) { \
|
||||
if (index.column() != 0) \
|
||||
continue; \
|
||||
\
|
||||
result.append(&at(index.row())); \
|
||||
} \
|
||||
return result; \
|
||||
} \
|
||||
QList<T*> all##T##s() \
|
||||
{ \
|
||||
QList<T*> result; \
|
||||
result.reserve(m_resources.size()); \
|
||||
\
|
||||
for (const Resource::Ptr& resource : m_resources) \
|
||||
result.append(static_cast<T*>(resource.get())); \
|
||||
\
|
||||
return result; \
|
||||
}
|
||||
|
||||
/** A basic model for external resources.
|
||||
*
|
||||
* This model manages a list of resources. As such, external users of such resources do not own them,
|
||||
@ -69,7 +121,7 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||
*/
|
||||
virtual bool uninstallResource(QString file_name, bool preserve_metadata = false);
|
||||
virtual bool deleteResources(const QModelIndexList&);
|
||||
virtual bool deleteMetadata(const QModelIndexList&);
|
||||
virtual void deleteMetadata(const QModelIndexList&);
|
||||
|
||||
/** Applies the given 'action' to the resources in 'indexes'.
|
||||
*
|
||||
@ -85,9 +137,7 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||
|
||||
[[nodiscard]] qsizetype size() const { return m_resources.size(); }
|
||||
[[nodiscard]] bool empty() const { return size() == 0; }
|
||||
[[nodiscard]] Resource& at(int index) { return *m_resources.at(index); }
|
||||
[[nodiscard]] Resource const& at(int index) const { return *m_resources.at(index); }
|
||||
[[nodiscard]] QList<Resource::Ptr> const& all() const { return m_resources; }
|
||||
RESOURCE_HELPERS(Resource)
|
||||
|
||||
[[nodiscard]] QDir const& dir() const { return m_dir; }
|
||||
|
||||
@ -232,37 +282,6 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||
std::atomic<int> m_next_resolution_ticket = 0;
|
||||
};
|
||||
|
||||
/* A macro to define useful functions to handle Resource* -> T* more easily on derived classes */
|
||||
#define RESOURCE_HELPERS(T) \
|
||||
[[nodiscard]] T* operator[](int index) \
|
||||
{ \
|
||||
return static_cast<T*>(m_resources[index].get()); \
|
||||
} \
|
||||
[[nodiscard]] T* at(int index) \
|
||||
{ \
|
||||
return static_cast<T*>(m_resources[index].get()); \
|
||||
} \
|
||||
[[nodiscard]] const T* at(int index) const \
|
||||
{ \
|
||||
return static_cast<const T*>(m_resources.at(index).get()); \
|
||||
} \
|
||||
[[nodiscard]] T* first() \
|
||||
{ \
|
||||
return static_cast<T*>(m_resources.first().get()); \
|
||||
} \
|
||||
[[nodiscard]] T* last() \
|
||||
{ \
|
||||
return static_cast<T*>(m_resources.last().get()); \
|
||||
} \
|
||||
[[nodiscard]] T* find(QString id) \
|
||||
{ \
|
||||
auto iter = std::find_if(m_resources.constBegin(), m_resources.constEnd(), \
|
||||
[&](Resource::Ptr const& r) { return r->internal_id() == id; }); \
|
||||
if (iter == m_resources.constEnd()) \
|
||||
return nullptr; \
|
||||
return static_cast<T*>((*iter).get()); \
|
||||
}
|
||||
|
||||
/* Template definition to avoid some code duplication */
|
||||
template <typename T>
|
||||
void ResourceFolderModel::applyUpdates(QSet<QString>& current_set, QSet<QString>& new_set, QMap<QString, T>& new_resources)
|
||||
|
@ -72,12 +72,12 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
||||
case NameColumn:
|
||||
return m_resources[row]->name();
|
||||
case PackFormatColumn: {
|
||||
auto resource = at(row);
|
||||
auto pack_format = resource->packFormat();
|
||||
auto& resource = at(row);
|
||||
auto pack_format = resource.packFormat();
|
||||
if (pack_format == 0)
|
||||
return tr("Unrecognized");
|
||||
|
||||
auto version_bounds = resource->compatibleVersions();
|
||||
auto version_bounds = resource.compatibleVersions();
|
||||
if (version_bounds.first.toString().isEmpty())
|
||||
return QString::number(pack_format);
|
||||
|
||||
@ -92,10 +92,10 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
||||
return {};
|
||||
}
|
||||
case Qt::DecorationRole: {
|
||||
if (column == NameColumn && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
|
||||
if (column == NameColumn && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink()))
|
||||
return APPLICATION->getThemedIcon("status-yellow");
|
||||
if (column == ImageColumn) {
|
||||
return at(row)->image({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding);
|
||||
return at(row).image({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@ -105,14 +105,14 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
||||
return tr("The resource pack format ID, as well as the Minecraft versions it was designed for.");
|
||||
}
|
||||
if (column == NameColumn) {
|
||||
if (at(row)->isSymLinkUnder(instDirPath())) {
|
||||
if (at(row).isSymLinkUnder(instDirPath())) {
|
||||
return m_resources[row]->internal_id() +
|
||||
tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
|
||||
"\nCanonical Path: %1")
|
||||
.arg(at(row)->fileinfo().canonicalFilePath());
|
||||
.arg(at(row).fileinfo().canonicalFilePath());
|
||||
;
|
||||
}
|
||||
if (at(row)->isMoreThanOneHardLink()) {
|
||||
if (at(row).isMoreThanOneHardLink()) {
|
||||
return m_resources[row]->internal_id() +
|
||||
tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
|
||||
}
|
||||
@ -127,7 +127,7 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
||||
case Qt::CheckStateRole:
|
||||
switch (column) {
|
||||
case ActiveColumn:
|
||||
return at(row)->enabled() ? Qt::Checked : Qt::Unchecked;
|
||||
return at(row).enabled() ? Qt::Checked : Qt::Unchecked;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
|
@ -52,11 +52,6 @@ TexturePackFolderModel::TexturePackFolderModel(const QDir& dir, BaseInstance* in
|
||||
m_columnsHideable = { false, true, false, true, true };
|
||||
}
|
||||
|
||||
Task* TexturePackFolderModel::createUpdateTask()
|
||||
{
|
||||
return new BasicFolderLoadTask(m_dir, [](QFileInfo const& entry) { return makeShared<TexturePack>(entry); });
|
||||
}
|
||||
|
||||
Task* TexturePackFolderModel::createParseTask(Resource& resource)
|
||||
{
|
||||
return new LocalTexturePackParseTask(m_next_resolution_ticket, static_cast<TexturePack&>(resource));
|
||||
@ -84,14 +79,14 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const
|
||||
}
|
||||
case Qt::ToolTipRole:
|
||||
if (column == NameColumn) {
|
||||
if (at(row)->isSymLinkUnder(instDirPath())) {
|
||||
if (at(row).isSymLinkUnder(instDirPath())) {
|
||||
return m_resources[row]->internal_id() +
|
||||
tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
|
||||
"\nCanonical Path: %1")
|
||||
.arg(at(row)->fileinfo().canonicalFilePath());
|
||||
.arg(at(row).fileinfo().canonicalFilePath());
|
||||
;
|
||||
}
|
||||
if (at(row)->isMoreThanOneHardLink()) {
|
||||
if (at(row).isMoreThanOneHardLink()) {
|
||||
return m_resources[row]->internal_id() +
|
||||
tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
|
||||
}
|
||||
@ -99,10 +94,10 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const
|
||||
|
||||
return m_resources[row]->internal_id();
|
||||
case Qt::DecorationRole: {
|
||||
if (column == NameColumn && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
|
||||
if (column == NameColumn && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink()))
|
||||
return APPLICATION->getThemedIcon("status-yellow");
|
||||
if (column == ImageColumn) {
|
||||
return at(row)->image({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding);
|
||||
return at(row).image({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
@ -13,13 +13,13 @@ class CheckUpdateTask : public Task {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CheckUpdateTask(QList<Mod*>& mods,
|
||||
CheckUpdateTask(QList<Resource*>& resources,
|
||||
std::list<Version>& mcVersions,
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||
std::shared_ptr<ModFolderModel> mods_folder)
|
||||
: Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder){};
|
||||
std::shared_ptr<ResourceFolderModel> resource_model)
|
||||
: Task(nullptr), m_resources(resources), m_game_versions(mcVersions), m_loaders(loaders), m_resource_model(resource_model){};
|
||||
|
||||
struct UpdatableMod {
|
||||
struct Update {
|
||||
QString name;
|
||||
QString old_hash;
|
||||
QString old_version;
|
||||
@ -30,7 +30,7 @@ class CheckUpdateTask : public Task {
|
||||
shared_qobject_ptr<ResourceDownloadTask> download;
|
||||
|
||||
public:
|
||||
UpdatableMod(QString name,
|
||||
Update(QString name,
|
||||
QString old_h,
|
||||
QString old_v,
|
||||
QString new_v,
|
||||
@ -49,7 +49,7 @@ class CheckUpdateTask : public Task {
|
||||
{}
|
||||
};
|
||||
|
||||
auto getUpdatable() -> std::vector<UpdatableMod>&& { return std::move(m_updatable); }
|
||||
auto getUpdates() -> std::vector<Update>&& { return std::move(m_updates); }
|
||||
auto getDependencies() -> QList<std::shared_ptr<GetModDependenciesTask::PackDependency>>&& { return std::move(m_deps); }
|
||||
|
||||
public slots:
|
||||
@ -59,14 +59,14 @@ class CheckUpdateTask : public Task {
|
||||
void executeTask() override = 0;
|
||||
|
||||
signals:
|
||||
void checkFailed(Mod* failed, QString reason, QUrl recover_url = {});
|
||||
void checkFailed(Resource* failed, QString reason, QUrl recover_url = {});
|
||||
|
||||
protected:
|
||||
QList<Mod*>& m_mods;
|
||||
QList<Resource*>& m_resources;
|
||||
std::list<Version>& m_game_versions;
|
||||
std::optional<ModPlatform::ModLoaderTypes> m_loaders;
|
||||
std::shared_ptr<ModFolderModel> m_mods_folder;
|
||||
std::shared_ptr<ResourceFolderModel> m_resource_model;
|
||||
|
||||
std::vector<UpdatableMod> m_updatable;
|
||||
std::vector<Update> m_updates;
|
||||
QList<std::shared_ptr<GetModDependenciesTask::PackDependency>> m_deps;
|
||||
};
|
||||
|
@ -120,19 +120,19 @@ ModPlatform::IndexedVersion FlameCheckUpdate::getFileInfo(int addonId, int fileI
|
||||
* */
|
||||
void FlameCheckUpdate::executeTask()
|
||||
{
|
||||
setStatus(tr("Preparing mods for CurseForge..."));
|
||||
setStatus(tr("Preparing resources for CurseForge..."));
|
||||
|
||||
int i = 0;
|
||||
for (auto* mod : m_mods) {
|
||||
if (!mod->enabled()) {
|
||||
emit checkFailed(mod, tr("Disabled mods won't be updated, to prevent mod duplication issues!"));
|
||||
for (auto* resource : m_resources) {
|
||||
if (!resource->enabled()) {
|
||||
emit checkFailed(resource, tr("Disabled resources won't be updated, to prevent resource duplication issues!"));
|
||||
continue;
|
||||
}
|
||||
|
||||
setStatus(tr("Getting API response from CurseForge for '%1'...").arg(mod->name()));
|
||||
setProgress(i++, m_mods.size());
|
||||
setStatus(tr("Getting API response from CurseForge for '%1'...").arg(resource->name()));
|
||||
setProgress(i++, m_resources.size());
|
||||
|
||||
auto latest_ver = api.getLatestVersion({ { mod->metadata()->project_id.toString() }, m_game_versions, m_loaders });
|
||||
auto latest_ver = api.getLatestVersion({ { resource->metadata()->project_id.toString() }, m_game_versions, m_loaders });
|
||||
|
||||
// Check if we were aborted while getting the latest version
|
||||
if (m_was_aborted) {
|
||||
@ -140,41 +140,48 @@ void FlameCheckUpdate::executeTask()
|
||||
return;
|
||||
}
|
||||
|
||||
setStatus(tr("Parsing the API response from CurseForge for '%1'...").arg(mod->name()));
|
||||
setStatus(tr("Parsing the API response from CurseForge for '%1'...").arg(resource->name()));
|
||||
|
||||
if (!latest_ver.addonId.isValid()) {
|
||||
emit checkFailed(mod, tr("No valid version found for this mod. It's probably unavailable for the current game "
|
||||
"version / mod loader."));
|
||||
QString reason;
|
||||
if (dynamic_cast<Mod*>(resource) != nullptr)
|
||||
reason =
|
||||
tr("No valid version found for this resource. It's probably unavailable for the current game "
|
||||
"version / mod loader.");
|
||||
else
|
||||
reason = tr("No valid version found for this resource. It's probably unavailable for the current game version.");
|
||||
|
||||
emit checkFailed(resource, reason);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (latest_ver.downloadUrl.isEmpty() && latest_ver.fileId != mod->metadata()->file_id) {
|
||||
if (latest_ver.downloadUrl.isEmpty() && latest_ver.fileId != resource->metadata()->file_id) {
|
||||
auto pack = getProjectInfo(latest_ver);
|
||||
auto recover_url = QString("%1/download/%2").arg(pack.websiteUrl, latest_ver.fileId.toString());
|
||||
emit checkFailed(mod, tr("Mod has a new update available, but is not downloadable using CurseForge."), recover_url);
|
||||
emit checkFailed(resource, tr("Resource has a new update available, but is not downloadable using CurseForge."), recover_url);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Fake pack with the necessary info to pass to the download task :)
|
||||
auto pack = std::make_shared<ModPlatform::IndexedPack>();
|
||||
pack->name = mod->name();
|
||||
pack->slug = mod->metadata()->slug;
|
||||
pack->addonId = mod->metadata()->project_id;
|
||||
pack->websiteUrl = mod->homeurl();
|
||||
for (auto& author : mod->authors())
|
||||
pack->authors.append({ author });
|
||||
pack->description = mod->description();
|
||||
pack->name = resource->name();
|
||||
pack->slug = resource->metadata()->slug;
|
||||
pack->addonId = resource->metadata()->project_id;
|
||||
pack->provider = ModPlatform::ResourceProvider::FLAME;
|
||||
if (!latest_ver.hash.isEmpty() && (mod->metadata()->hash != latest_ver.hash || mod->status() == ResourceStatus::NOT_INSTALLED)) {
|
||||
auto old_version = mod->version();
|
||||
if (old_version.isEmpty() && mod->status() != ResourceStatus::NOT_INSTALLED) {
|
||||
auto current_ver = getFileInfo(latest_ver.addonId.toInt(), mod->metadata()->file_id.toInt());
|
||||
old_version = current_ver.version;
|
||||
if (!latest_ver.hash.isEmpty() &&
|
||||
(resource->metadata()->hash != latest_ver.hash || resource->status() == ResourceStatus::NOT_INSTALLED)) {
|
||||
auto download_task = makeShared<ResourceDownloadTask>(pack, latest_ver, m_resource_model);
|
||||
|
||||
QString old_version = resource->metadata()->version_number;
|
||||
if (old_version.isEmpty()) {
|
||||
if (resource->status() == ResourceStatus::NOT_INSTALLED)
|
||||
old_version = tr("Not installed");
|
||||
else
|
||||
old_version = tr("Unknown");
|
||||
}
|
||||
|
||||
auto download_task = makeShared<ResourceDownloadTask>(pack, latest_ver, m_mods_folder);
|
||||
m_updatable.emplace_back(pack->name, mod->metadata()->hash, old_version, latest_ver.version, latest_ver.version_type,
|
||||
m_updates.emplace_back(pack->name, resource->metadata()->hash, old_version, latest_ver.version, latest_ver.version_type,
|
||||
api.getModFileChangelog(latest_ver.addonId.toInt(), latest_ver.fileId.toInt()),
|
||||
ModPlatform::ResourceProvider::FLAME, download_task);
|
||||
}
|
||||
|
@ -8,11 +8,11 @@ class FlameCheckUpdate : public CheckUpdateTask {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FlameCheckUpdate(QList<Mod*>& mods,
|
||||
FlameCheckUpdate(QList<Resource*>& resources,
|
||||
std::list<Version>& mcVersions,
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||
std::shared_ptr<ModFolderModel> mods_folder)
|
||||
: CheckUpdateTask(mods, mcVersions, loaders, mods_folder)
|
||||
std::shared_ptr<ResourceFolderModel> resource_model)
|
||||
: CheckUpdateTask(resources, mcVersions, loaders, resource_model)
|
||||
{}
|
||||
|
||||
public slots:
|
||||
|
@ -29,38 +29,38 @@ bool ModrinthCheckUpdate::abort()
|
||||
* */
|
||||
void ModrinthCheckUpdate::executeTask()
|
||||
{
|
||||
setStatus(tr("Preparing mods for Modrinth..."));
|
||||
setStatus(tr("Preparing resources for Modrinth..."));
|
||||
setProgress(0, 3);
|
||||
|
||||
QHash<QString, Mod*> mappings;
|
||||
QHash<QString, Resource*> mappings;
|
||||
|
||||
// Create all hashes
|
||||
QStringList hashes;
|
||||
auto best_hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first();
|
||||
|
||||
ConcurrentTask hashing_task(this, "MakeModrinthHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt());
|
||||
for (auto* mod : m_mods) {
|
||||
if (!mod->enabled()) {
|
||||
emit checkFailed(mod, tr("Disabled mods won't be updated, to prevent mod duplication issues!"));
|
||||
for (auto* resource : m_resources) {
|
||||
if (!resource->enabled()) {
|
||||
emit checkFailed(resource, tr("Disabled resources won't be updated, to prevent resource duplication issues!"));
|
||||
continue;
|
||||
}
|
||||
|
||||
auto hash = mod->metadata()->hash;
|
||||
auto hash = resource->metadata()->hash;
|
||||
|
||||
// Sadly the API can only handle one hash type per call, se we
|
||||
// need to generate a new hash if the current one is innadequate
|
||||
// (though it will rarely happen, if at all)
|
||||
if (mod->metadata()->hash_format != best_hash_type) {
|
||||
auto hash_task = Hashing::createModrinthHasher(mod->fileinfo().absoluteFilePath());
|
||||
connect(hash_task.get(), &Hashing::Hasher::resultsReady, [&hashes, &mappings, mod](QString hash) {
|
||||
if (resource->metadata()->hash_format != best_hash_type) {
|
||||
auto hash_task = Hashing::createModrinthHasher(resource->fileinfo().absoluteFilePath());
|
||||
connect(hash_task.get(), &Hashing::Hasher::resultsReady, [&hashes, &mappings, resource](QString hash) {
|
||||
hashes.append(hash);
|
||||
mappings.insert(hash, mod);
|
||||
mappings.insert(hash, resource);
|
||||
});
|
||||
connect(hash_task.get(), &Task::failed, [this] { failed("Failed to generate hash"); });
|
||||
hashing_task.addTask(hash_task);
|
||||
} else {
|
||||
hashes.append(hash);
|
||||
mappings.insert(hash, mod);
|
||||
mappings.insert(hash, resource);
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,19 +88,26 @@ void ModrinthCheckUpdate::executeTask()
|
||||
setProgress(2, 3);
|
||||
|
||||
try {
|
||||
for (auto hash : mappings.keys()) {
|
||||
for (auto iter = mappings.begin(); iter != mappings.end(); iter++) {
|
||||
const QString& hash = iter.key();
|
||||
Resource* resource = iter.value();
|
||||
auto project_obj = doc[hash].toObject();
|
||||
|
||||
// If the returned project is empty, but we have Modrinth metadata,
|
||||
// it means this specific version is not available
|
||||
if (project_obj.isEmpty()) {
|
||||
qDebug() << "Mod " << mappings.find(hash).value()->name() << " got an empty response.";
|
||||
qDebug() << "Resource " << resource->name() << " got an empty response.";
|
||||
qDebug() << "Hash: " << hash;
|
||||
|
||||
emit checkFailed(
|
||||
mappings.find(hash).value(),
|
||||
tr("No valid version found for this mod. It's probably unavailable for the current game version / mod loader."));
|
||||
QString reason;
|
||||
if (dynamic_cast<Mod*>(resource) != nullptr)
|
||||
reason =
|
||||
tr("No valid version found for this resource. It's probably unavailable for the current game "
|
||||
"version / mod loader.");
|
||||
else
|
||||
reason = tr("No valid version found for this resource. It's probably unavailable for the current game version.");
|
||||
|
||||
emit checkFailed(resource, reason);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -109,7 +116,8 @@ void ModrinthCheckUpdate::executeTask()
|
||||
QString loader_filter;
|
||||
if (m_loaders.has_value()) {
|
||||
static auto flags = { ModPlatform::ModLoaderType::NeoForge, ModPlatform::ModLoaderType::Forge,
|
||||
ModPlatform::ModLoaderType::Fabric, ModPlatform::ModLoaderType::Quilt };
|
||||
ModPlatform::ModLoaderType::Fabric, ModPlatform::ModLoaderType::Quilt,
|
||||
ModPlatform::ModLoaderType::LiteLoader };
|
||||
for (auto flag : flags) {
|
||||
if (m_loaders.value().testFlag(flag)) {
|
||||
loader_filter = ModPlatform::getModLoaderString(flag);
|
||||
@ -126,46 +134,44 @@ void ModrinthCheckUpdate::executeTask()
|
||||
|
||||
auto project_ver = Modrinth::loadIndexedPackVersion(project_obj, best_hash_type, loader_filter);
|
||||
if (project_ver.downloadUrl.isEmpty()) {
|
||||
qCritical() << "Modrinth mod without download url!";
|
||||
qCritical() << "Modrinth resource without download url!";
|
||||
qCritical() << project_ver.fileName;
|
||||
|
||||
emit checkFailed(mappings.find(hash).value(), tr("Mod has an empty download URL"));
|
||||
emit checkFailed(mappings.find(hash).value(), tr("Resource has an empty download URL"));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
auto mod_iter = mappings.find(hash);
|
||||
if (mod_iter == mappings.end()) {
|
||||
qCritical() << "Failed to remap mod from Modrinth!";
|
||||
auto resource_iter = mappings.find(hash);
|
||||
if (resource_iter == mappings.end()) {
|
||||
qCritical() << "Failed to remap resource from Modrinth!";
|
||||
continue;
|
||||
}
|
||||
auto mod = *mod_iter;
|
||||
|
||||
auto key = project_ver.hash;
|
||||
|
||||
// Fake pack with the necessary info to pass to the download task :)
|
||||
auto pack = std::make_shared<ModPlatform::IndexedPack>();
|
||||
pack->name = mod->name();
|
||||
pack->slug = mod->metadata()->slug;
|
||||
pack->addonId = mod->metadata()->project_id;
|
||||
pack->websiteUrl = mod->homeurl();
|
||||
for (auto& author : mod->authors())
|
||||
pack->authors.append({ author });
|
||||
pack->description = mod->description();
|
||||
pack->name = resource->name();
|
||||
pack->slug = resource->metadata()->slug;
|
||||
pack->addonId = resource->metadata()->project_id;
|
||||
pack->provider = ModPlatform::ResourceProvider::MODRINTH;
|
||||
if ((key != hash && project_ver.is_preferred) || (mod->status() == ResourceStatus::NOT_INSTALLED)) {
|
||||
if (mod->version() == project_ver.version_number)
|
||||
continue;
|
||||
if ((project_ver.hash != hash && project_ver.is_preferred) || (resource->status() == ResourceStatus::NOT_INSTALLED)) {
|
||||
auto download_task = makeShared<ResourceDownloadTask>(pack, project_ver, m_resource_model);
|
||||
|
||||
auto download_task = makeShared<ResourceDownloadTask>(pack, project_ver, m_mods_folder);
|
||||
QString old_version = resource->metadata()->version_number;
|
||||
if (old_version.isEmpty()) {
|
||||
if (resource->status() == ResourceStatus::NOT_INSTALLED)
|
||||
old_version = tr("Not installed");
|
||||
else
|
||||
old_version = tr("Unknown");
|
||||
}
|
||||
|
||||
m_updatable.emplace_back(pack->name, hash, mod->version(), project_ver.version_number, project_ver.version_type,
|
||||
m_updates.emplace_back(pack->name, hash, old_version, project_ver.version_number, project_ver.version_type,
|
||||
project_ver.changelog, ModPlatform::ResourceProvider::MODRINTH, download_task);
|
||||
}
|
||||
m_deps.append(std::make_shared<GetModDependenciesTask::PackDependency>(pack, project_ver));
|
||||
}
|
||||
} catch (Json::JsonException& e) {
|
||||
emitFailed(e.cause() + " : " + e.what());
|
||||
emitFailed(e.cause() + ": " + e.what());
|
||||
return;
|
||||
}
|
||||
emitSucceeded();
|
||||
|
@ -8,11 +8,11 @@ class ModrinthCheckUpdate : public CheckUpdateTask {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModrinthCheckUpdate(QList<Mod*>& mods,
|
||||
ModrinthCheckUpdate(QList<Resource*>& resources,
|
||||
std::list<Version>& mcVersions,
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||
std::shared_ptr<ModFolderModel> mods_folder)
|
||||
: CheckUpdateTask(mods, mcVersions, loaders, mods_folder)
|
||||
std::shared_ptr<ResourceFolderModel> resource_model)
|
||||
: CheckUpdateTask(resources, mcVersions, loaders, resource_model)
|
||||
{}
|
||||
|
||||
public slots:
|
||||
|
@ -297,18 +297,20 @@ auto V1::getIndexForMod(const QDir& index_dir, QString slug) -> Mod
|
||||
{ // [update] info
|
||||
using Provider = ModPlatform::ResourceProvider;
|
||||
|
||||
auto update_table = table["update"];
|
||||
if (!update_table || !update_table.is_table()) {
|
||||
auto update_table = table["update"].as_table();
|
||||
if (!update_table) {
|
||||
qCritical() << QString("No [update] section found on mod metadata!");
|
||||
return {};
|
||||
}
|
||||
|
||||
mod.version_number = stringEntry(*update_table, "x-prismlauncher-version-number");
|
||||
|
||||
toml::table* mod_provider_table = nullptr;
|
||||
if ((mod_provider_table = update_table[ProviderCaps.name(Provider::FLAME)].as_table())) {
|
||||
if ((mod_provider_table = (*update_table)[ProviderCaps.name(Provider::FLAME)].as_table())) {
|
||||
mod.provider = Provider::FLAME;
|
||||
mod.file_id = intEntry(*mod_provider_table, "file-id");
|
||||
mod.project_id = intEntry(*mod_provider_table, "project-id");
|
||||
} else if ((mod_provider_table = update_table[ProviderCaps.name(Provider::MODRINTH)].as_table())) {
|
||||
} else if ((mod_provider_table = (*update_table)[ProviderCaps.name(Provider::MODRINTH)].as_table())) {
|
||||
mod.provider = Provider::MODRINTH;
|
||||
mod.mod_id() = stringEntry(*mod_provider_table, "mod-id");
|
||||
mod.version() = stringEntry(*mod_provider_table, "version");
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "ModUpdateDialog.h"
|
||||
#include "ResourceUpdateDialog.h"
|
||||
#include "ChooseProviderDialog.h"
|
||||
#include "CustomMessageBox.h"
|
||||
#include "ProgressDialog.h"
|
||||
@ -36,14 +36,14 @@ static std::optional<ModPlatform::ModLoaderTypes> mcLoaders(BaseInstance* inst)
|
||||
return { static_cast<MinecraftInstance*>(inst)->getPackProfile()->getSupportedModLoaders() };
|
||||
}
|
||||
|
||||
ModUpdateDialog::ModUpdateDialog(QWidget* parent,
|
||||
ResourceUpdateDialog::ResourceUpdateDialog(QWidget* parent,
|
||||
BaseInstance* instance,
|
||||
const std::shared_ptr<ModFolderModel> mods,
|
||||
QList<Mod*>& search_for,
|
||||
const std::shared_ptr<ResourceFolderModel> resource_model,
|
||||
QList<Resource*>& search_for,
|
||||
bool includeDeps)
|
||||
: ReviewMessageBox(parent, tr("Confirm mods to update"), "")
|
||||
, m_parent(parent)
|
||||
, m_mod_model(mods)
|
||||
, m_resource_model(resource_model)
|
||||
, m_candidates(search_for)
|
||||
, m_second_try_metadata(
|
||||
new ConcurrentTask(nullptr, "Second Metadata Search", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()))
|
||||
@ -56,7 +56,7 @@ ModUpdateDialog::ModUpdateDialog(QWidget* parent,
|
||||
ui->onlyCheckedLabel->setText(tr("Only mods with a check will be updated!"));
|
||||
}
|
||||
|
||||
void ModUpdateDialog::checkCandidates()
|
||||
void ResourceUpdateDialog::checkCandidates()
|
||||
{
|
||||
// Ensure mods have valid metadata
|
||||
auto went_well = ensureMetadata();
|
||||
@ -92,17 +92,19 @@ void ModUpdateDialog::checkCandidates()
|
||||
SequentialTask check_task(m_parent, tr("Checking for updates"));
|
||||
|
||||
if (!m_modrinth_to_update.empty()) {
|
||||
m_modrinth_check_task.reset(new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_mod_model));
|
||||
connect(m_modrinth_check_task.get(), &CheckUpdateTask::checkFailed, this, [this](Mod* mod, QString reason, QUrl recover_url) {
|
||||
m_failed_check_update.append({ mod, reason, recover_url });
|
||||
m_modrinth_check_task.reset(new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_resource_model));
|
||||
connect(m_modrinth_check_task.get(), &CheckUpdateTask::checkFailed, this,
|
||||
[this](Resource* resource, QString reason, QUrl recover_url) {
|
||||
m_failed_check_update.append({ resource, reason, recover_url });
|
||||
});
|
||||
check_task.addTask(m_modrinth_check_task);
|
||||
}
|
||||
|
||||
if (!m_flame_to_update.empty()) {
|
||||
m_flame_check_task.reset(new FlameCheckUpdate(m_flame_to_update, versions, loaders, m_mod_model));
|
||||
connect(m_flame_check_task.get(), &CheckUpdateTask::checkFailed, this, [this](Mod* mod, QString reason, QUrl recover_url) {
|
||||
m_failed_check_update.append({ mod, reason, recover_url });
|
||||
m_flame_check_task.reset(new FlameCheckUpdate(m_flame_to_update, versions, loaders, m_resource_model));
|
||||
connect(m_flame_check_task.get(), &CheckUpdateTask::checkFailed, this,
|
||||
[this](Resource* resource, QString reason, QUrl recover_url) {
|
||||
m_failed_check_update.append({ resource, reason, recover_url });
|
||||
});
|
||||
check_task.addTask(m_flame_check_task);
|
||||
}
|
||||
@ -134,11 +136,11 @@ void ModUpdateDialog::checkCandidates()
|
||||
|
||||
// Add found updates for Modrinth
|
||||
if (m_modrinth_check_task) {
|
||||
auto modrinth_updates = m_modrinth_check_task->getUpdatable();
|
||||
auto modrinth_updates = m_modrinth_check_task->getUpdates();
|
||||
for (auto& updatable : modrinth_updates) {
|
||||
qDebug() << QString("Mod %1 has an update available!").arg(updatable.name);
|
||||
|
||||
appendMod(updatable);
|
||||
appendResource(updatable);
|
||||
m_tasks.insert(updatable.name, updatable.download);
|
||||
}
|
||||
selectedVers.append(m_modrinth_check_task->getDependencies());
|
||||
@ -146,11 +148,11 @@ void ModUpdateDialog::checkCandidates()
|
||||
|
||||
// Add found updated for Flame
|
||||
if (m_flame_check_task) {
|
||||
auto flame_updates = m_flame_check_task->getUpdatable();
|
||||
auto flame_updates = m_flame_check_task->getUpdates();
|
||||
for (auto& updatable : flame_updates) {
|
||||
qDebug() << QString("Mod %1 has an update available!").arg(updatable.name);
|
||||
|
||||
appendMod(updatable);
|
||||
appendResource(updatable);
|
||||
m_tasks.insert(updatable.name, updatable.download);
|
||||
}
|
||||
selectedVers.append(m_flame_check_task->getDependencies());
|
||||
@ -189,10 +191,13 @@ void ModUpdateDialog::checkCandidates()
|
||||
}
|
||||
|
||||
if (m_include_deps && !APPLICATION->settings()->get("ModDependenciesDisabled").toBool()) { // dependencies
|
||||
auto depTask = makeShared<GetModDependenciesTask>(this, m_instance, m_mod_model.get(), selectedVers);
|
||||
auto* mod_model = dynamic_cast<ModFolderModel*>(m_resource_model.get());
|
||||
|
||||
if (mod_model != nullptr) {
|
||||
auto depTask = makeShared<GetModDependenciesTask>(this, m_instance, mod_model, selectedVers);
|
||||
|
||||
connect(depTask.get(), &Task::failed, this,
|
||||
[&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
||||
[&](const QString& reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
||||
|
||||
connect(depTask.get(), &Task::succeeded, this, [&]() {
|
||||
QStringList warnings = depTask->warnings();
|
||||
@ -216,22 +221,23 @@ void ModUpdateDialog::checkCandidates()
|
||||
|
||||
auto getRequiredBy = depTask->getRequiredBy();
|
||||
|
||||
for (auto dep : depTask->getDependecies()) {
|
||||
for (const auto& dep : depTask->getDependecies()) {
|
||||
auto changelog = dep->version.changelog;
|
||||
if (dep->pack->provider == ModPlatform::ResourceProvider::FLAME)
|
||||
changelog = api.getModFileChangelog(dep->version.addonId.toInt(), dep->version.fileId.toInt());
|
||||
auto download_task = makeShared<ResourceDownloadTask>(dep->pack, dep->version, m_mod_model);
|
||||
CheckUpdateTask::UpdatableMod updatable = {
|
||||
dep->pack->name, dep->version.hash, "", dep->version.version, dep->version.version_type,
|
||||
auto download_task = makeShared<ResourceDownloadTask>(dep->pack, dep->version, m_resource_model);
|
||||
CheckUpdateTask::Update updatable = {
|
||||
dep->pack->name, dep->version.hash, tr("Not installed"), dep->version.version, dep->version.version_type,
|
||||
changelog, dep->pack->provider, download_task
|
||||
};
|
||||
|
||||
appendMod(updatable, getRequiredBy.value(dep->version.addonId.toString()));
|
||||
appendResource(updatable, getRequiredBy.value(dep->version.addonId.toString()));
|
||||
m_tasks.insert(updatable.name, updatable.download);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there's no mod to be updated
|
||||
// If there's no resource to be updated
|
||||
if (ui->modTreeWidget->topLevelItemCount() == 0) {
|
||||
m_no_updates = true;
|
||||
} else {
|
||||
@ -253,7 +259,7 @@ void ModUpdateDialog::checkCandidates()
|
||||
}
|
||||
|
||||
// Part 1: Ensure we have a valid metadata
|
||||
auto ModUpdateDialog::ensureMetadata() -> bool
|
||||
auto ResourceUpdateDialog::ensureMetadata() -> bool
|
||||
{
|
||||
auto index_dir = indexDir();
|
||||
|
||||
@ -261,21 +267,21 @@ auto ModUpdateDialog::ensureMetadata() -> bool
|
||||
|
||||
// A better use of data structures here could remove the need for this QHash
|
||||
QHash<QString, bool> should_try_others;
|
||||
QList<Mod*> modrinth_tmp;
|
||||
QList<Mod*> flame_tmp;
|
||||
QList<Resource*> modrinth_tmp;
|
||||
QList<Resource*> flame_tmp;
|
||||
|
||||
bool confirm_rest = false;
|
||||
bool try_others_rest = false;
|
||||
bool skip_rest = false;
|
||||
ModPlatform::ResourceProvider provider_rest = ModPlatform::ResourceProvider::MODRINTH;
|
||||
|
||||
auto addToTmp = [&](Mod* m, ModPlatform::ResourceProvider p) {
|
||||
auto addToTmp = [&](Resource* resource, ModPlatform::ResourceProvider p) {
|
||||
switch (p) {
|
||||
case ModPlatform::ResourceProvider::MODRINTH:
|
||||
modrinth_tmp.push_back(m);
|
||||
modrinth_tmp.push_back(resource);
|
||||
break;
|
||||
case ModPlatform::ResourceProvider::FLAME:
|
||||
flame_tmp.push_back(m);
|
||||
flame_tmp.push_back(resource);
|
||||
break;
|
||||
}
|
||||
};
|
||||
@ -300,7 +306,7 @@ auto ModUpdateDialog::ensureMetadata() -> bool
|
||||
}
|
||||
|
||||
ChooseProviderDialog chooser(this);
|
||||
chooser.setDescription(tr("The mod '%1' does not have a metadata yet. We need to generate it in order to track relevant "
|
||||
chooser.setDescription(tr("The resource '%1' does not have a metadata yet. We need to generate it in order to track relevant "
|
||||
"information on how to update this mod. "
|
||||
"To do this, please select a mod provider which we can use to check for updates for this mod.")
|
||||
.arg(candidate->name()));
|
||||
@ -324,8 +330,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool
|
||||
|
||||
if (!modrinth_tmp.empty()) {
|
||||
auto modrinth_task = makeShared<EnsureMetadataTask>(modrinth_tmp, index_dir, ModPlatform::ResourceProvider::MODRINTH);
|
||||
connect(modrinth_task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); });
|
||||
connect(modrinth_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) {
|
||||
connect(modrinth_task.get(), &EnsureMetadataTask::metadataReady, [this](Resource* candidate) { onMetadataEnsured(candidate); });
|
||||
connect(modrinth_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Resource* candidate) {
|
||||
onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::MODRINTH);
|
||||
});
|
||||
connect(modrinth_task.get(), &EnsureMetadataTask::failed,
|
||||
@ -339,8 +345,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool
|
||||
|
||||
if (!flame_tmp.empty()) {
|
||||
auto flame_task = makeShared<EnsureMetadataTask>(flame_tmp, index_dir, ModPlatform::ResourceProvider::FLAME);
|
||||
connect(flame_task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); });
|
||||
connect(flame_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) {
|
||||
connect(flame_task.get(), &EnsureMetadataTask::metadataReady, [this](Resource* candidate) { onMetadataEnsured(candidate); });
|
||||
connect(flame_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Resource* candidate) {
|
||||
onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::FLAME);
|
||||
});
|
||||
connect(flame_task.get(), &EnsureMetadataTask::failed,
|
||||
@ -362,18 +368,18 @@ auto ModUpdateDialog::ensureMetadata() -> bool
|
||||
return (ret_metadata != QDialog::DialogCode::Rejected);
|
||||
}
|
||||
|
||||
void ModUpdateDialog::onMetadataEnsured(Mod* mod)
|
||||
void ResourceUpdateDialog::onMetadataEnsured(Resource* resource)
|
||||
{
|
||||
// When the mod is a folder, for instance
|
||||
if (!mod->metadata())
|
||||
if (!resource->metadata())
|
||||
return;
|
||||
|
||||
switch (mod->metadata()->provider) {
|
||||
switch (resource->metadata()->provider) {
|
||||
case ModPlatform::ResourceProvider::MODRINTH:
|
||||
m_modrinth_to_update.push_back(mod);
|
||||
m_modrinth_to_update.push_back(resource);
|
||||
break;
|
||||
case ModPlatform::ResourceProvider::FLAME:
|
||||
m_flame_to_update.push_back(mod);
|
||||
m_flame_to_update.push_back(resource);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -390,26 +396,26 @@ ModPlatform::ResourceProvider next(ModPlatform::ResourceProvider p)
|
||||
return ModPlatform::ResourceProvider::FLAME;
|
||||
}
|
||||
|
||||
void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::ResourceProvider first_choice)
|
||||
void ResourceUpdateDialog::onMetadataFailed(Resource* resource, bool try_others, ModPlatform::ResourceProvider first_choice)
|
||||
{
|
||||
if (try_others) {
|
||||
auto index_dir = indexDir();
|
||||
|
||||
auto task = makeShared<EnsureMetadataTask>(mod, index_dir, next(first_choice));
|
||||
connect(task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); });
|
||||
connect(task.get(), &EnsureMetadataTask::metadataFailed, [this](Mod* candidate) { onMetadataFailed(candidate, false); });
|
||||
auto task = makeShared<EnsureMetadataTask>(resource, index_dir, next(first_choice));
|
||||
connect(task.get(), &EnsureMetadataTask::metadataReady, [this](Resource* candidate) { onMetadataEnsured(candidate); });
|
||||
connect(task.get(), &EnsureMetadataTask::metadataFailed, [this](Resource* candidate) { onMetadataFailed(candidate, false); });
|
||||
connect(task.get(), &EnsureMetadataTask::failed,
|
||||
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
||||
[this](const QString& reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
||||
|
||||
m_second_try_metadata->addTask(task);
|
||||
} else {
|
||||
QString reason{ tr("Couldn't find a valid version on the selected mod provider(s)") };
|
||||
|
||||
m_failed_metadata.append({ mod, reason });
|
||||
m_failed_metadata.append({ resource, reason });
|
||||
}
|
||||
}
|
||||
|
||||
void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStringList requiredBy)
|
||||
void ResourceUpdateDialog::appendResource(CheckUpdateTask::Update const& info, QStringList requiredBy)
|
||||
{
|
||||
auto item_top = new QTreeWidgetItem(ui->modTreeWidget);
|
||||
item_top->setCheckState(0, Qt::CheckState::Checked);
|
||||
@ -420,7 +426,7 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStri
|
||||
provider_item->setText(0, tr("Provider: %1").arg(ProviderCaps.readableName(info.provider)));
|
||||
|
||||
auto old_version_item = new QTreeWidgetItem(item_top);
|
||||
old_version_item->setText(0, tr("Old version: %1").arg(info.old_version.isEmpty() ? tr("Not installed") : info.old_version));
|
||||
old_version_item->setText(0, tr("Old version: %1").arg(info.old_version));
|
||||
|
||||
auto new_version_item = new QTreeWidgetItem(item_top);
|
||||
new_version_item->setText(0, tr("New version: %1").arg(info.new_version));
|
||||
@ -474,7 +480,7 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStri
|
||||
ui->modTreeWidget->addTopLevelItem(item_top);
|
||||
}
|
||||
|
||||
auto ModUpdateDialog::getTasks() -> const QList<ResourceDownloadTask::Ptr>
|
||||
auto ResourceUpdateDialog::getTasks() -> const QList<ResourceDownloadTask::Ptr>
|
||||
{
|
||||
QList<ResourceDownloadTask::Ptr> list;
|
||||
|
@ -13,22 +13,21 @@ class ModrinthCheckUpdate;
|
||||
class FlameCheckUpdate;
|
||||
class ConcurrentTask;
|
||||
|
||||
class ModUpdateDialog final : public ReviewMessageBox {
|
||||
class ResourceUpdateDialog final : public ReviewMessageBox {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ModUpdateDialog(QWidget* parent, BaseInstance* instance, std::shared_ptr<ModFolderModel> mod_model, QList<Mod*>& search_for);
|
||||
explicit ModUpdateDialog(QWidget* parent,
|
||||
explicit ResourceUpdateDialog(QWidget* parent,
|
||||
BaseInstance* instance,
|
||||
std::shared_ptr<ModFolderModel> mod_model,
|
||||
QList<Mod*>& search_for,
|
||||
std::shared_ptr<ResourceFolderModel> resource_model,
|
||||
QList<Resource*>& search_for,
|
||||
bool includeDeps);
|
||||
|
||||
void checkCandidates();
|
||||
|
||||
void appendMod(const CheckUpdateTask::UpdatableMod& info, QStringList requiredBy = {});
|
||||
void appendResource(const CheckUpdateTask::Update& info, QStringList requiredBy = {});
|
||||
|
||||
const QList<ResourceDownloadTask::Ptr> getTasks();
|
||||
auto indexDir() const -> QDir { return m_mod_model->indexDir(); }
|
||||
auto indexDir() const -> QDir { return m_resource_model->indexDir(); }
|
||||
|
||||
auto noUpdates() const -> bool { return m_no_updates; };
|
||||
auto aborted() const -> bool { return m_aborted; };
|
||||
@ -37,8 +36,8 @@ class ModUpdateDialog final : public ReviewMessageBox {
|
||||
auto ensureMetadata() -> bool;
|
||||
|
||||
private slots:
|
||||
void onMetadataEnsured(Mod*);
|
||||
void onMetadataFailed(Mod*,
|
||||
void onMetadataEnsured(Resource* resource);
|
||||
void onMetadataFailed(Resource* resource,
|
||||
bool try_others = false,
|
||||
ModPlatform::ResourceProvider first_choice = ModPlatform::ResourceProvider::MODRINTH);
|
||||
|
||||
@ -48,15 +47,15 @@ class ModUpdateDialog final : public ReviewMessageBox {
|
||||
shared_qobject_ptr<ModrinthCheckUpdate> m_modrinth_check_task;
|
||||
shared_qobject_ptr<FlameCheckUpdate> m_flame_check_task;
|
||||
|
||||
const std::shared_ptr<ModFolderModel> m_mod_model;
|
||||
const std::shared_ptr<ResourceFolderModel> m_resource_model;
|
||||
|
||||
QList<Mod*>& m_candidates;
|
||||
QList<Mod*> m_modrinth_to_update;
|
||||
QList<Mod*> m_flame_to_update;
|
||||
QList<Resource*>& m_candidates;
|
||||
QList<Resource*> m_modrinth_to_update;
|
||||
QList<Resource*> m_flame_to_update;
|
||||
|
||||
ConcurrentTask::Ptr m_second_try_metadata;
|
||||
QList<std::tuple<Mod*, QString>> m_failed_metadata;
|
||||
QList<std::tuple<Mod*, QString, QUrl>> m_failed_check_update;
|
||||
QList<std::tuple<Resource*, QString>> m_failed_metadata;
|
||||
QList<std::tuple<Resource*, QString, QUrl>> m_failed_check_update;
|
||||
|
||||
QHash<QString, ResourceDownloadTask::Ptr> m_tasks;
|
||||
BaseInstance* m_instance;
|
@ -51,8 +51,8 @@
|
||||
|
||||
#include "ui/GuiUtil.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui/dialogs/ModUpdateDialog.h"
|
||||
#include "ui/dialogs/ResourceDownloadDialog.h"
|
||||
#include "ui/dialogs/ResourceUpdateDialog.h"
|
||||
|
||||
#include "DesktopServices.h"
|
||||
|
||||
@ -161,9 +161,8 @@ bool ModFolderPage::onSelectionChanged(const QModelIndex& current, [[maybe_unuse
|
||||
{
|
||||
auto sourceCurrent = m_filterModel->mapToSource(current);
|
||||
int row = sourceCurrent.row();
|
||||
Mod const* m = m_model->at(row);
|
||||
if (m)
|
||||
ui->frame->updateWithMod(*m);
|
||||
const Mod& mod = m_model->at(row);
|
||||
ui->frame->updateWithMod(mod);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -253,12 +252,12 @@ void ModFolderPage::updateMods(bool includeDeps)
|
||||
}
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
|
||||
auto mods_list = m_model->selectedMods(selection);
|
||||
auto mods_list = m_model->selectedResources(selection);
|
||||
bool use_all = mods_list.empty();
|
||||
if (use_all)
|
||||
mods_list = m_model->allMods();
|
||||
mods_list = m_model->allResources();
|
||||
|
||||
ModUpdateDialog update_dialog(this, m_instance, m_model, mods_list, includeDeps);
|
||||
ResourceUpdateDialog update_dialog(this, m_instance, m_model, mods_list, includeDeps);
|
||||
update_dialog.checkCandidates();
|
||||
|
||||
if (update_dialog.aborted()) {
|
||||
|
Reference in New Issue
Block a user