From 5eec0f5901a0b11c9c01fd7b086968951b792fb6 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 18 Jul 2023 23:40:03 +0300 Subject: [PATCH 01/21] feat:managed packs can update from file Signed-off-by: Trial97 --- .../ui/pages/instance/ManagedPackPage.cpp | 45 +++++++++++++++++++ launcher/ui/pages/instance/ManagedPackPage.h | 3 ++ launcher/ui/pages/instance/ManagedPackPage.ui | 13 ++++++ 3 files changed, 61 insertions(+) diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index 0fc0c9867..1dbbae0f4 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -5,6 +5,7 @@ #include "ManagedPackPage.h" #include "ui_ManagedPackPage.h" +#include #include #include #include @@ -205,6 +206,7 @@ ModrinthManagedPackPage::ModrinthManagedPackPage(BaseInstance* inst, InstanceWin Q_ASSERT(inst->isManagedPack()); connect(ui->versionsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(suggestVersion())); connect(ui->updateButton, &QPushButton::clicked, this, &ModrinthManagedPackPage::update); + connect(ui->updateFromFileButton, &QPushButton::clicked, this, &ModrinthManagedPackPage::updateFromFile); } // MODRINTH @@ -332,6 +334,27 @@ void ModrinthManagedPackPage::update() m_instance_window->close(); } +void ModrinthManagedPackPage::updateFromFile() +{ + auto output = QFileDialog::getOpenFileUrl(this, tr("Choose update file"), QDir::homePath(), "Modrinth pack (*.mrpack *.zip)"); + QMap extra_info; + extra_info.insert("pack_id", m_inst->getManagedPackID()); + extra_info.insert("pack_version_id", QString()); + extra_info.insert("original_instance_id", m_inst->id()); + + auto extracted = new InstanceImportTask(output, this, std::move(extra_info)); + + extracted->setName(m_inst->name()); + extracted->setGroup(APPLICATION->instances()->getInstanceGroup(m_inst->id())); + extracted->setIcon(m_inst->iconKey()); + extracted->setConfirmUpdate(false); + + auto did_succeed = runUpdateTask(extracted); + + if (m_instance_window && did_succeed) + m_instance_window->close(); +} + // FLAME FlameManagedPackPage::FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent) @@ -340,6 +363,7 @@ FlameManagedPackPage::FlameManagedPackPage(BaseInstance* inst, InstanceWindow* i Q_ASSERT(inst->isManagedPack()); connect(ui->versionsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(suggestVersion())); connect(ui->updateButton, &QPushButton::clicked, this, &FlameManagedPackPage::update); + connect(ui->updateFromFileButton, &QPushButton::clicked, this, &FlameManagedPackPage::updateFromFile); } void FlameManagedPackPage::parseManagedPack() @@ -474,4 +498,25 @@ void FlameManagedPackPage::update() m_instance_window->close(); } +void FlameManagedPackPage::updateFromFile() +{ + auto output = QFileDialog::getOpenFileUrl(this, tr("Choose update file"), QDir::homePath(), "CurseForge pack (*.zip)"); + + QMap extra_info; + extra_info.insert("pack_id", m_inst->getManagedPackID()); + extra_info.insert("pack_version_id", QString()); + extra_info.insert("original_instance_id", m_inst->id()); + + auto extracted = new InstanceImportTask(output, this, std::move(extra_info)); + + extracted->setName(m_inst->name()); + extracted->setGroup(APPLICATION->instances()->getInstanceGroup(m_inst->id())); + extracted->setIcon(m_inst->iconKey()); + extracted->setConfirmUpdate(false); + + auto did_succeed = runUpdateTask(extracted); + + if (m_instance_window && did_succeed) + m_instance_window->close(); +} #include "ManagedPackPage.moc" diff --git a/launcher/ui/pages/instance/ManagedPackPage.h b/launcher/ui/pages/instance/ManagedPackPage.h index 1ac6fc038..d77cb97b8 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.h +++ b/launcher/ui/pages/instance/ManagedPackPage.h @@ -65,6 +65,7 @@ class ManagedPackPage : public QWidget, public BasePage { virtual void suggestVersion(); virtual void update(){}; + virtual void updateFromFile(){}; protected slots: /** Does the necessary UI changes for when something failed. @@ -123,6 +124,7 @@ class ModrinthManagedPackPage final : public ManagedPackPage { void suggestVersion() override; void update() override; + void updateFromFile() override; private: NetJob::Ptr m_fetch_job = nullptr; @@ -145,6 +147,7 @@ class FlameManagedPackPage final : public ManagedPackPage { void suggestVersion() override; void update() override; + void updateFromFile() override; private: NetJob::Ptr m_fetch_job = nullptr; diff --git a/launcher/ui/pages/instance/ManagedPackPage.ui b/launcher/ui/pages/instance/ManagedPackPage.ui index bbe44a940..96317be9f 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.ui +++ b/launcher/ui/pages/instance/ManagedPackPage.ui @@ -153,6 +153,19 @@ + + + + + 0 + 0 + + + + Update from file + + + From d25e89a4c1304092f99b3516e91a5b989a110212 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 20 Aug 2023 00:48:46 +0300 Subject: [PATCH 02/21] refactor packwiz file write Signed-off-by: Trial97 --- .../minecraft/mod/tasks/ModFolderLoadTask.cpp | 2 +- launcher/modplatform/packwiz/Packwiz.cpp | 43 +++++++++++-------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp index 9f79ba098..2094df4fc 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp +++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp @@ -122,7 +122,7 @@ void ModFolderLoadTask::getFromMetadata() auto metadata = Metadata::get(m_index_dir, entry); if (!metadata.isValid()) { - return; + continue; } auto* mod = new Mod(m_mods_dir, metadata); diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 71f66bf3e..381476697 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include "FileSystem.h" #include "StringUtils.h" @@ -161,31 +163,36 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) // Put TOML data into the file QTextStream in_stream(&index_file); - auto addToStream = [&in_stream](QString&& key, QString value) { in_stream << QString("%1 = \"%2\"\n").arg(key, value); }; - { - addToStream("name", mod.name); - addToStream("filename", mod.filename); - addToStream("side", mod.side); - - in_stream << QString("\n[download]\n"); - addToStream("mode", mod.mode); - addToStream("url", mod.url.toString()); - addToStream("hash-format", mod.hash_format); - addToStream("hash", mod.hash); - - in_stream << QString("\n[update]\n"); - in_stream << QString("[update.%1]\n").arg(ProviderCaps.name(mod.provider)); + toml::table update; switch (mod.provider) { case (ModPlatform::ResourceProvider::FLAME): - in_stream << QString("file-id = %1\n").arg(mod.file_id.toString()); - in_stream << QString("project-id = %1\n").arg(mod.project_id.toString()); + update = toml::table{ + { "file-id", mod.file_id.toInt() }, + { "project-id", mod.project_id.toInt() }, + }; break; case (ModPlatform::ResourceProvider::MODRINTH): - addToStream("mod-id", mod.mod_id().toString()); - addToStream("version", mod.version().toString()); + update = toml::table{ + { "mod-id", mod.mod_id().toString().toStdString() }, + { "version", mod.version().toString().toStdString() }, + }; break; } + auto tbl = toml::table{ { "name", mod.name.toStdString() }, + { "filename", mod.filename.toStdString() }, + { "side", mod.side.toStdString() }, + { "download", + toml::table{ + { "mode", mod.mode.toStdString() }, + { "url", mod.url.toString().toStdString() }, + { "hash-format", mod.hash_format.toStdString() }, + { "hash", mod.hash.toStdString() }, + } }, + { "update", toml::table{ { ProviderCaps.name(mod.provider), update } } } }; + std::stringstream ss; + ss << tbl; + in_stream << QString::fromStdString(ss.str()); } index_file.flush(); From ea384d59fbff194f26f4fc678ce2388f01b2f484 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 24 Aug 2023 12:41:37 +0300 Subject: [PATCH 03/21] use qt separtor for file path Signed-off-by: Trial97 --- launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index 9ff6b374d..47a0de5fe 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -322,7 +322,7 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, bool had_optional = false; for (const auto& modInfo : jsonFiles) { Modrinth::File file; - file.path = Json::requireString(modInfo, "path"); + file.path = Json::requireString(modInfo, "path").replace("\\", "/"); auto env = Json::ensureObject(modInfo, "env"); // 'env' field is optional From 2990c5d0c9ef9e36daa0ed7315521852164fe59e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 24 Aug 2023 12:44:11 +0300 Subject: [PATCH 04/21] Added optional mods dialog Signed-off-by: Trial97 --- launcher/CMakeLists.txt | 4 + .../flame/FlameInstanceCreationTask.cpp | 29 +++- launcher/modplatform/flame/PackManifest.h | 2 +- .../modrinth/ModrinthInstanceCreationTask.cpp | 40 +++-- .../modrinth/ModrinthPackManifest.h | 1 + .../pages/modplatform/OptionalModDialog.cpp | 143 ++++++++++++++++++ .../ui/pages/modplatform/OptionalModDialog.h | 76 ++++++++++ .../ui/pages/modplatform/OptionalModDialog.ui | 105 +++++++++++++ .../atlauncher/AtlOptionalModDialog.h | 2 +- 9 files changed, 382 insertions(+), 20 deletions(-) create mode 100644 launcher/ui/pages/modplatform/OptionalModDialog.cpp create mode 100644 launcher/ui/pages/modplatform/OptionalModDialog.h create mode 100644 launcher/ui/pages/modplatform/OptionalModDialog.ui diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 18e0acab1..c73d89ec3 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -916,6 +916,9 @@ SET(LAUNCHER_SOURCES ui/pages/modplatform/ImportPage.cpp ui/pages/modplatform/ImportPage.h + ui/pages/modplatform/OptionalModDialog.cpp + ui/pages/modplatform/OptionalModDialog.h + ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp ui/pages/modplatform/modrinth/ModrinthResourceModels.h ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -1080,6 +1083,7 @@ qt_wrap_ui(LAUNCHER_UI ui/pages/modplatform/legacy_ftb/Page.ui ui/pages/modplatform/import_ftb/ImportFTBPage.ui ui/pages/modplatform/ImportPage.ui + ui/pages/modplatform/OptionalModDialog.ui ui/pages/modplatform/modrinth/ModrinthPage.ui ui/pages/modplatform/technic/TechnicPage.ui ui/widgets/InstanceCardWidget.ui diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index 45b4e2125..ead5b464e 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -62,6 +62,7 @@ #include "minecraft/World.h" #include "minecraft/mod/tasks/LocalResourceParse.h" #include "net/ApiDownload.h" +#include "ui/pages/modplatform/OptionalModDialog.h" static const FlameAPI api; @@ -509,13 +510,33 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop) void FlameCreationTask::setupDownloadJob(QEventLoop& loop) { m_files_job.reset(new NetJob(tr("Mod Download Flame"), APPLICATION->network())); - for (const auto& result : m_mod_id_resolver->getResults().files) { - QString filename = result.fileName; + auto results = m_mod_id_resolver->getResults().files; + + QStringList optionalFiles; + for (auto& result : results) { if (!result.required) { - filename += ".disabled"; + optionalFiles << FS::PathCombine(result.targetFolder, result.fileName); + } + } + + QStringList selectedOptionalMods; + if (!optionalFiles.empty()) { + OptionalModDialog optionalModDialog(m_parent, optionalFiles); + if (optionalModDialog.exec() == QDialog::Rejected) { + emitAborted(); + loop.quit(); + return; } - auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename); + selectedOptionalMods = optionalModDialog.getResult(); + } + for (const auto& result : results) { + auto relpath = FS::PathCombine(result.targetFolder, result.fileName); + if (!result.required && !selectedOptionalMods.contains(relpath)) { + relpath += ".disabled"; + } + + relpath = FS::PathCombine("minecraft", relpath); auto path = FS::PathCombine(m_stagingPath, relpath); switch (result.type) { diff --git a/launcher/modplatform/flame/PackManifest.h b/launcher/modplatform/flame/PackManifest.h index 854cdbc41..4417c2430 100644 --- a/launcher/modplatform/flame/PackManifest.h +++ b/launcher/modplatform/flame/PackManifest.h @@ -48,7 +48,7 @@ struct File { int projectId = 0; int fileId = 0; - // NOTE: the opposite to 'optional'. This is at the time of writing unused. + // NOTE: the opposite to 'optional' bool required = true; QString hash; // NOTE: only set on blocked files ! Empty otherwise. diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index 47a0de5fe..1be58c2b8 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -9,6 +9,7 @@ #include "modplatform/helpers/OverrideUtils.h" +#include "modplatform/modrinth/ModrinthPackManifest.h" #include "net/ChecksumValidator.h" #include "net/ApiDownload.h" @@ -16,8 +17,10 @@ #include "settings/INISettingsObject.h" #include "ui/dialogs/CustomMessageBox.h" +#include "ui/pages/modplatform/OptionalModDialog.h" #include +#include bool ModrinthCreationTask::abort() { @@ -319,7 +322,7 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, } auto jsonFiles = Json::requireIsArrayOf(obj, "files", "modrinth.index.json"); - bool had_optional = false; + std::vector optionalFiles; for (const auto& modInfo : jsonFiles) { Modrinth::File file; file.path = Json::requireString(modInfo, "path").replace("\\", "/"); @@ -331,18 +334,7 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, if (support == "unsupported") { continue; } else if (support == "optional") { - // TODO: Make a review dialog for choosing which ones the user wants! - if (!had_optional && show_optional_dialog) { - had_optional = true; - auto info = CustomMessageBox::selectable( - m_parent, tr("Optional mod detected!"), - tr("One or more mods from this modpack are optional. They will be downloaded, but disabled by default!"), - QMessageBox::Information); - info->exec(); - } - - if (file.path.endsWith(".jar")) - file.path += ".disabled"; + file.required = false; } } @@ -385,9 +377,29 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, } } - files.push_back(file); + (file.required ? files : optionalFiles).push_back(file); } + if (!optionalFiles.empty()) { + QStringList oFiles; + for (auto file : optionalFiles) + oFiles.push_back(file.path); + OptionalModDialog optionalModDialog(m_parent, oFiles); + if (optionalModDialog.exec() == QDialog::Rejected) { + emitAborted(); + return false; + } + + auto selectedMods = optionalModDialog.getResult(); + for (auto file : optionalFiles) { + if (selectedMods.contains(file.path)) { + file.required = true; + } else if (file.path.endsWith(".jar")) { + file.path += ".disabled"; + } + files.push_back(file); + } + } if (set_internal_data) { auto dependencies = Json::requireObject(obj, "dependencies", "modrinth.index.json"); for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) { diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h index effa1a84a..93ae69697 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.h +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -55,6 +55,7 @@ struct File { QCryptographicHash::Algorithm hashAlgorithm; QByteArray hash; QQueue downloads; + bool required = true; }; struct DonationData { diff --git a/launcher/ui/pages/modplatform/OptionalModDialog.cpp b/launcher/ui/pages/modplatform/OptionalModDialog.cpp new file mode 100644 index 000000000..620de4f74 --- /dev/null +++ b/launcher/ui/pages/modplatform/OptionalModDialog.cpp @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "OptionalModDialog.h" +#include "ui_OptionalModDialog.h" + +OptionalModListModel::OptionalModListModel(QWidget* parent, QStringList mods) : QAbstractListModel(parent), m_mods(mods) {} + +QStringList OptionalModListModel::getResult() +{ + QStringList result; + for (const auto& mod : m_mods) { + if (m_selected.value(mod, false)) { + result << mod; + } + } + return result; +} + +int OptionalModListModel::rowCount(const QModelIndex& parent) const +{ + return parent.isValid() ? 0 : m_mods.size(); +} + +int OptionalModListModel::columnCount(const QModelIndex& parent) const +{ + // Enabled, Name + return parent.isValid() ? 0 : 2; +} + +QVariant OptionalModListModel::data(const QModelIndex& index, int role) const +{ + auto row = index.row(); + auto mod = m_mods.at(row); + + if (role == Qt::DisplayRole && index.column() == NameColumn) { + return mod; + } else if (role == Qt::CheckStateRole && index.column() == EnabledColumn) { + return m_selected.value(mod, false) ? Qt::Checked : Qt::Unchecked; + } + + return {}; +} + +bool OptionalModListModel::setData(const QModelIndex& index, [[maybe_unused]] const QVariant& value, int role) +{ + if (role == Qt::CheckStateRole) { + auto row = index.row(); + auto mod = m_mods.at(row); + + toggleMod(mod, row); + return true; + } + + return false; +} + +QVariant OptionalModListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { + switch (section) { + case EnabledColumn: + return QString(); + case NameColumn: + return QString("Name"); + } + } + + return {}; +} + +Qt::ItemFlags OptionalModListModel::flags(const QModelIndex& index) const +{ + auto flags = QAbstractListModel::flags(index); + if (index.isValid() && index.column() == EnabledColumn) { + flags |= Qt::ItemIsUserCheckable; + } + return flags; +} + +void OptionalModListModel::toggleAll(bool enabled) +{ + for (const auto& mod : m_mods) { + m_selected[mod] = enabled; + } + + emit dataChanged(OptionalModListModel::index(0, EnabledColumn), OptionalModListModel::index(m_mods.size() - 1, EnabledColumn)); +} + +void OptionalModListModel::toggleMod(QString mod, int index) +{ + auto enable = !m_selected.value(mod, false); + + setMod(mod, index, enable); +} + +void OptionalModListModel::setMod(QString mod, int index, bool enable, bool shouldEmit) +{ + if (m_selected.value(mod, false) == enable) + return; + + m_selected[mod] = enable; + + if (shouldEmit) { + emit dataChanged(OptionalModListModel::index(index, EnabledColumn), OptionalModListModel::index(index, EnabledColumn)); + } +} + +OptionalModDialog::OptionalModDialog(QWidget* parent, QStringList mods) : QDialog(parent), ui(new Ui::OptionalModDialog) +{ + ui->setupUi(this); + + listModel = new OptionalModListModel(this, mods); + ui->treeView->setModel(listModel); + + ui->treeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + ui->treeView->header()->setSectionResizeMode(OptionalModListModel::NameColumn, QHeaderView::Stretch); + + connect(ui->selectAllButton, &QPushButton::clicked, listModel, &OptionalModListModel::selectAll); + connect(ui->clearAllButton, &QPushButton::clicked, listModel, &OptionalModListModel::clearAll); + connect(ui->installButton, &QPushButton::clicked, this, &QDialog::accept); + connect(ui->cancelButton, &QPushButton::clicked, this, &QDialog::reject); +} + +OptionalModDialog::~OptionalModDialog() +{ + delete ui; +} diff --git a/launcher/ui/pages/modplatform/OptionalModDialog.h b/launcher/ui/pages/modplatform/OptionalModDialog.h new file mode 100644 index 000000000..4e22d0e46 --- /dev/null +++ b/launcher/ui/pages/modplatform/OptionalModDialog.h @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +namespace Ui { +class OptionalModDialog; +} + +class OptionalModListModel : public QAbstractListModel { + Q_OBJECT + public: + enum Columns { + EnabledColumn = 0, + NameColumn, + }; + + OptionalModListModel(QWidget* parent, QStringList mods); + + QStringList getResult(); + + int rowCount(const QModelIndex& parent) const override; + int columnCount(const QModelIndex& parent) const override; + + QVariant data(const QModelIndex& index, int role) const override; + bool setData(const QModelIndex& index, const QVariant& value, int role) override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + + Qt::ItemFlags flags(const QModelIndex& index) const override; + + public slots: + void selectAll() { toggleAll(true); } + void clearAll() { toggleAll(false); }; + void toggleAll(bool enabled); + + private: + void toggleMod(QString mod, int index); + void setMod(QString mod, int index, bool enable, bool shouldEmit = true); + + private: + QStringList m_mods; + QHash m_selected; +}; + +class OptionalModDialog : public QDialog { + Q_OBJECT + + public: + OptionalModDialog(QWidget* parent, QStringList mods); + ~OptionalModDialog() override; + + QStringList getResult() { return listModel->getResult(); } + + private: + Ui::OptionalModDialog* ui; + + OptionalModListModel* listModel; +}; diff --git a/launcher/ui/pages/modplatform/OptionalModDialog.ui b/launcher/ui/pages/modplatform/OptionalModDialog.ui new file mode 100644 index 000000000..76d1da89d --- /dev/null +++ b/launcher/ui/pages/modplatform/OptionalModDialog.ui @@ -0,0 +1,105 @@ + + + OptionalModDialog + + + + 0 + 0 + 550 + 310 + + + + Select Mods To Install + + + + + + + + + 11 + 75 + true + + + + Select optional mods to install. + + + Qt::AlignCenter + + + + + + + + true + + + + Note: All files will be downloaded but the unselected mods will be disabled. + + + Qt::AlignCenter + + + + + + + + + + + + + + true + + + Cancel + + + + + + + Clear All + + + + + + + Select All + + + + + + + Install + + + true + + + + + + + + + + ModListView + QTreeView +
ui/widgets/ModListView.h
+
+
+ + +
diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h index 55903003b..767d277d9 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h +++ b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h @@ -38,7 +38,7 @@ #include #include -#include "modplatform/atlauncher/ATLPackIndex.h" +#include "modplatform/atlauncher/ATLPackManifest.h" #include "net/NetJob.h" namespace Ui { From bb4b89470d2c6f051d4c75de4a29c8dc1d836122 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 24 Aug 2023 12:46:23 +0300 Subject: [PATCH 05/21] fixed icon importing Signed-off-by: Trial97 --- launcher/modplatform/atlauncher/ATLPackIndex.cpp | 2 +- launcher/modplatform/flame/FlamePackIndex.cpp | 4 +++- launcher/modplatform/modrinth/ModrinthPackManifest.cpp | 3 ++- launcher/ui/dialogs/NewInstanceDialog.cpp | 3 +-- launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp | 2 +- launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp | 2 +- launcher/ui/pages/modplatform/flame/FlamePage.cpp | 3 +-- launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp | 2 +- launcher/ui/pages/modplatform/legacy_ftb/Page.cpp | 2 -- launcher/ui/pages/modplatform/technic/TechnicModel.cpp | 5 +++-- 10 files changed, 14 insertions(+), 14 deletions(-) diff --git a/launcher/modplatform/atlauncher/ATLPackIndex.cpp b/launcher/modplatform/atlauncher/ATLPackIndex.cpp index 3be169739..84b0cc9e2 100644 --- a/launcher/modplatform/atlauncher/ATLPackIndex.cpp +++ b/launcher/modplatform/atlauncher/ATLPackIndex.cpp @@ -43,5 +43,5 @@ void ATLauncher::loadIndexedPack(ATLauncher::IndexedPack& m, QJsonObject& obj) m.system = Json::ensureBoolean(obj, QString("system"), false); m.description = Json::ensureString(obj, "description", ""); - m.safeName = Json::requireString(obj, "name").replace(QRegularExpression("[^A-Za-z0-9]"), ""); + m.safeName = "atl_" + Json::requireString(obj, "name").replace(QRegularExpression("[^A-Za-z0-9]"), "").toLower() + ".png"; } diff --git a/launcher/modplatform/flame/FlamePackIndex.cpp b/launcher/modplatform/flame/FlamePackIndex.cpp index 21835a543..1b8534cee 100644 --- a/launcher/modplatform/flame/FlamePackIndex.cpp +++ b/launcher/modplatform/flame/FlamePackIndex.cpp @@ -1,4 +1,6 @@ #include "FlamePackIndex.h" +#include +#include #include "Json.h" @@ -9,8 +11,8 @@ void Flame::loadIndexedPack(Flame::IndexedPack& pack, QJsonObject& obj) pack.description = Json::ensureString(obj, "summary", ""); auto logo = Json::requireObject(obj, "logo"); - pack.logoName = Json::requireString(logo, "title"); pack.logoUrl = Json::requireString(logo, "thumbnailUrl"); + pack.logoName = Json::requireString(obj, "slug") + "." + QFileInfo(QUrl(pack.logoUrl).fileName()).suffix(); auto authors = Json::requireArray(obj, "authors"); for (auto authorIter : authors) { diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index 0d07c6361..4565e585f 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -35,6 +35,7 @@ */ #include "ModrinthPackManifest.h" +#include #include "Json.h" #include "modplatform/modrinth/ModrinthAPI.h" @@ -56,8 +57,8 @@ void loadIndexedPack(Modpack& pack, QJsonObject& obj) pack.description = Json::ensureString(obj, "description"); auto temp_author_name = Json::ensureString(obj, "author"); pack.author = std::make_tuple(temp_author_name, api.getAuthorURL(temp_author_name)); - pack.iconName = QString("modrinth_%1").arg(Json::ensureString(obj, "slug")); pack.iconUrl = Json::ensureString(obj, "icon_url"); + pack.iconName = QString("modrinth_%1.%2").arg(Json::ensureString(obj, "slug"), QFileInfo(pack.iconUrl.fileName()).suffix()); } void loadIndexedInfo(Modpack& pack, QJsonObject& obj) diff --git a/launcher/ui/dialogs/NewInstanceDialog.cpp b/launcher/ui/dialogs/NewInstanceDialog.cpp index 9613c6b00..4c8708bc7 100644 --- a/launcher/ui/dialogs/NewInstanceDialog.cpp +++ b/launcher/ui/dialogs/NewInstanceDialog.cpp @@ -237,8 +237,7 @@ void NewInstanceDialog::setSuggestedIcon(const QString& key) InstanceTask* NewInstanceDialog::extractTask() { - InstanceTask* extracted = creationTask.get(); - creationTask.release(); + InstanceTask* extracted = creationTask.release(); InstanceName inst_name(ui->instNameTextBox->placeholderText().trimmed(), importVersion); inst_name.setName(ui->instNameTextBox->text().trimmed()); diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp index 39f4f346a..5fc9ce06f 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp @@ -56,7 +56,7 @@ QVariant ListModel::data(const QModelIndex& index, int role) const } auto icon = APPLICATION->getThemedIcon("atlauncher-placeholder"); - auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1.png").arg(pack.safeName.toLower()); + auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1").arg(pack.safeName); ((ListModel*)this)->requestLogo(pack.safeName, url); return icon; diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp index 5e3b9ecf1..61512df5e 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp @@ -113,7 +113,7 @@ void AtlPage::suggestCurrent() dialog->setSuggestedPack(selected.name, selectedVersion, new ATLauncher::PackInstallTask(uiSupport, selected.name, selectedVersion)); auto editedLogoName = selected.safeName; - auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1.png").arg(selected.safeName.toLower()); + auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1").arg(selected.safeName); listModel->getLogo(selected.safeName, url, [this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); }); } diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index 183e16f90..6891882cd 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -209,8 +209,7 @@ void FlamePage::suggestCurrent() extra_info.insert("pack_version_id", QString::number(version.fileId)); dialog->setSuggestedPack(current.name, new InstanceImportTask(version.downloadUrl, this, std::move(extra_info))); - QString editedLogoName; - editedLogoName = "curseforge_" + current.logoName; + QString editedLogoName = "curseforge_" + current.logoName; listModel->getLogo(current.logoName, current.logoUrl, [this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); }); } diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp index 5c9ff63b2..b3b18e2ca 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp @@ -76,7 +76,7 @@ void ImportFTBPage::suggestCurrent() } dialog->setSuggestedPack(selected.name, new PackInstallTask(selected)); - QString editedLogoName = QString("ftb_%1").arg(selected.id); + QString editedLogoName = QString("ftb_%1.jpg").arg(selected.id); dialog->setSuggestedIconFromFile(FS::PathCombine(selected.path, "folder.jpg"), editedLogoName); } diff --git a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp index 0103bbaa2..243c90dcd 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp @@ -180,8 +180,6 @@ void Page::suggestCurrent() editedLogoName = "ftb_" + selected.logo; } - editedLogoName = editedLogoName.left(editedLogoName.lastIndexOf(".png")); - if (selected.type == PackType::Public) { publicListModel->getLogo(selected.logo, [this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); }); diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp index e8c5ac922..6fbfd7624 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp @@ -34,6 +34,7 @@ */ #include "TechnicModel.h" +#include #include "Application.h" #include "BuildConfig.h" #include "Json.h" @@ -159,7 +160,7 @@ void Technic::ListModel::searchRequestFinished() pack.logoName = "null"; } else { pack.logoUrl = rawURL; - pack.logoName = rawURL.section(QLatin1Char('/'), -1); + pack.logoName = QUrl(rawURL).fileName(); } pack.broken = false; newList.append(pack); @@ -181,7 +182,7 @@ void Technic::ListModel::searchRequestFinished() auto iconUrl = Json::requireString(iconObj, "url"); pack.logoUrl = iconUrl; - pack.logoName = iconUrl.section(QLatin1Char('/'), -1); + pack.logoName = QUrl(iconUrl).fileName(); } else { pack.logoUrl = "null"; pack.logoName = "null"; From f897b14e3e8ae658a6e63ffd9f61b7ad8a4259bf Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 24 Aug 2023 13:05:27 +0300 Subject: [PATCH 06/21] changed technic icon name retrival Signed-off-by: Trial97 --- .../ui/pages/modplatform/import_ftb/ImportFTBPage.cpp | 2 +- launcher/ui/pages/modplatform/legacy_ftb/Page.cpp | 8 +++----- launcher/ui/pages/modplatform/technic/TechnicModel.cpp | 7 ++++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp index b3b18e2ca..e54a487c6 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp @@ -76,7 +76,7 @@ void ImportFTBPage::suggestCurrent() } dialog->setSuggestedPack(selected.name, new PackInstallTask(selected)); - QString editedLogoName = QString("ftb_%1.jpg").arg(selected.id); + QString editedLogoName = QString("ftb_%1_%2,jpg").arg(selected.name, selected.id); dialog->setSuggestedIconFromFile(FS::PathCombine(selected.path, "folder.jpg"), editedLogoName); } diff --git a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp index 243c90dcd..9ba70da05 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp @@ -173,11 +173,9 @@ void Page::suggestCurrent() } dialog->setSuggestedPack(selected.name, selectedVersion, new PackInstallTask(APPLICATION->network(), selected, selectedVersion)); - QString editedLogoName; - if (selected.logo.toLower().startsWith("ftb")) { - editedLogoName = selected.logo; - } else { - editedLogoName = "ftb_" + selected.logo; + QString editedLogoName = selected.logo; + if (!selected.logo.toLower().startsWith("ftb")) { + editedLogoName = "ftb_" + editedLogoName; } if (selected.type == PackType::Public) { diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp index 6fbfd7624..9675534e1 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp @@ -34,14 +34,15 @@ */ #include "TechnicModel.h" -#include #include "Application.h" #include "BuildConfig.h" #include "Json.h" #include "net/ApiDownload.h" +#include #include +#include Technic::ListModel::ListModel(QObject* parent) : QAbstractListModel(parent) {} @@ -160,7 +161,7 @@ void Technic::ListModel::searchRequestFinished() pack.logoName = "null"; } else { pack.logoUrl = rawURL; - pack.logoName = QUrl(rawURL).fileName(); + pack.logoName = pack.slug + "." + QFileInfo(QUrl(rawURL).fileName()).suffix(); } pack.broken = false; newList.append(pack); @@ -182,7 +183,7 @@ void Technic::ListModel::searchRequestFinished() auto iconUrl = Json::requireString(iconObj, "url"); pack.logoUrl = iconUrl; - pack.logoName = QUrl(iconUrl).fileName(); + pack.logoName = pack.slug + "." + QFileInfo(QUrl(iconUrl).fileName()).suffix(); } else { pack.logoUrl = "null"; pack.logoName = "null"; From a2d44744fe71af431420f93b178a074a48b9232d Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 26 Aug 2023 23:36:46 +0300 Subject: [PATCH 07/21] do not update the metadata if mod is invalid Signed-off-by: Trial97 --- launcher/modplatform/packwiz/Packwiz.cpp | 41 +++++++++++++++--------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 381476697..1757da3e0 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -156,29 +156,38 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) FS::ensureFilePathExists(index_file.fileName()); } + toml::table update; + switch (mod.provider) { + case (ModPlatform::ResourceProvider::FLAME): + if (mod.file_id.toInt() == 0 || mod.project_id.toInt() == 0) { + qCritical() << QString("Did not write file %1 because missing information!").arg(normalized_fname); + return; + } + update = toml::table{ + { "file-id", mod.file_id.toInt() }, + { "project-id", mod.project_id.toInt() }, + }; + break; + case (ModPlatform::ResourceProvider::MODRINTH): + if (mod.mod_id().toString().isEmpty() || mod.version().toString().isEmpty()) { + qCritical() << QString("Did not write file %1 because missing information!").arg(normalized_fname); + return; + } + update = toml::table{ + { "mod-id", mod.mod_id().toString().toStdString() }, + { "version", mod.version().toString().toStdString() }, + }; + break; + } + if (!index_file.open(QIODevice::ReadWrite)) { - qCritical() << QString("Could not open file %1!").arg(indexFileName(mod.name)); + qCritical() << QString("Could not open file %1!").arg(normalized_fname); return; } // Put TOML data into the file QTextStream in_stream(&index_file); { - toml::table update; - switch (mod.provider) { - case (ModPlatform::ResourceProvider::FLAME): - update = toml::table{ - { "file-id", mod.file_id.toInt() }, - { "project-id", mod.project_id.toInt() }, - }; - break; - case (ModPlatform::ResourceProvider::MODRINTH): - update = toml::table{ - { "mod-id", mod.mod_id().toString().toStdString() }, - { "version", mod.version().toString().toStdString() }, - }; - break; - } auto tbl = toml::table{ { "name", mod.name.toStdString() }, { "filename", mod.filename.toStdString() }, { "side", mod.side.toStdString() }, From 479335dfe08466ce77f769e550c7f68a10b522a5 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 27 Aug 2023 00:49:21 +0100 Subject: [PATCH 08/21] Rewrite optional mod dialog Signed-off-by: TheKodeToad --- .../pages/modplatform/OptionalModDialog.cpp | 146 ++++------------- .../ui/pages/modplatform/OptionalModDialog.h | 41 +---- .../ui/pages/modplatform/OptionalModDialog.ui | 148 +++++++++--------- 3 files changed, 113 insertions(+), 222 deletions(-) diff --git a/launcher/ui/pages/modplatform/OptionalModDialog.cpp b/launcher/ui/pages/modplatform/OptionalModDialog.cpp index 620de4f74..fc1c8b3cb 100644 --- a/launcher/ui/pages/modplatform/OptionalModDialog.cpp +++ b/launcher/ui/pages/modplatform/OptionalModDialog.cpp @@ -19,125 +19,45 @@ #include "OptionalModDialog.h" #include "ui_OptionalModDialog.h" -OptionalModListModel::OptionalModListModel(QWidget* parent, QStringList mods) : QAbstractListModel(parent), m_mods(mods) {} - -QStringList OptionalModListModel::getResult() -{ - QStringList result; - for (const auto& mod : m_mods) { - if (m_selected.value(mod, false)) { - result << mod; - } - } - return result; -} - -int OptionalModListModel::rowCount(const QModelIndex& parent) const -{ - return parent.isValid() ? 0 : m_mods.size(); -} - -int OptionalModListModel::columnCount(const QModelIndex& parent) const -{ - // Enabled, Name - return parent.isValid() ? 0 : 2; -} - -QVariant OptionalModListModel::data(const QModelIndex& index, int role) const -{ - auto row = index.row(); - auto mod = m_mods.at(row); - - if (role == Qt::DisplayRole && index.column() == NameColumn) { - return mod; - } else if (role == Qt::CheckStateRole && index.column() == EnabledColumn) { - return m_selected.value(mod, false) ? Qt::Checked : Qt::Unchecked; - } - - return {}; -} - -bool OptionalModListModel::setData(const QModelIndex& index, [[maybe_unused]] const QVariant& value, int role) -{ - if (role == Qt::CheckStateRole) { - auto row = index.row(); - auto mod = m_mods.at(row); - - toggleMod(mod, row); - return true; - } - - return false; -} - -QVariant OptionalModListModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { - switch (section) { - case EnabledColumn: - return QString(); - case NameColumn: - return QString("Name"); - } - } - - return {}; -} - -Qt::ItemFlags OptionalModListModel::flags(const QModelIndex& index) const -{ - auto flags = QAbstractListModel::flags(index); - if (index.isValid() && index.column() == EnabledColumn) { - flags |= Qt::ItemIsUserCheckable; - } - return flags; -} - -void OptionalModListModel::toggleAll(bool enabled) -{ - for (const auto& mod : m_mods) { - m_selected[mod] = enabled; - } - - emit dataChanged(OptionalModListModel::index(0, EnabledColumn), OptionalModListModel::index(m_mods.size() - 1, EnabledColumn)); -} - -void OptionalModListModel::toggleMod(QString mod, int index) -{ - auto enable = !m_selected.value(mod, false); - - setMod(mod, index, enable); -} - -void OptionalModListModel::setMod(QString mod, int index, bool enable, bool shouldEmit) -{ - if (m_selected.value(mod, false) == enable) - return; - - m_selected[mod] = enable; - - if (shouldEmit) { - emit dataChanged(OptionalModListModel::index(index, EnabledColumn), OptionalModListModel::index(index, EnabledColumn)); - } -} - -OptionalModDialog::OptionalModDialog(QWidget* parent, QStringList mods) : QDialog(parent), ui(new Ui::OptionalModDialog) +OptionalModDialog::OptionalModDialog(QWidget* parent, const QStringList& mods) : QDialog(parent), ui(new Ui::OptionalModDialog) { ui->setupUi(this); + for (const QString& mod : mods) { + auto item = new QListWidgetItem(mod, ui->list); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(Qt::Unchecked); + item->setData(Qt::UserRole, mod); + } - listModel = new OptionalModListModel(this, mods); - ui->treeView->setModel(listModel); - - ui->treeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - ui->treeView->header()->setSectionResizeMode(OptionalModListModel::NameColumn, QHeaderView::Stretch); - - connect(ui->selectAllButton, &QPushButton::clicked, listModel, &OptionalModListModel::selectAll); - connect(ui->clearAllButton, &QPushButton::clicked, listModel, &OptionalModListModel::clearAll); - connect(ui->installButton, &QPushButton::clicked, this, &QDialog::accept); - connect(ui->cancelButton, &QPushButton::clicked, this, &QDialog::reject); + connect(ui->selectAllButton, &QPushButton::clicked, ui->list, [this] { + for (int i = 0; i < ui->list->count(); i++) + ui->list->item(i)->setCheckState(Qt::Checked); + }); + connect(ui->clearAllButton, &QPushButton::clicked, ui->list, [this] { + for (int i = 0; i < ui->list->count(); i++) + ui->list->item(i)->setCheckState(Qt::Unchecked); + }); + connect(ui->list, &QListWidget::itemActivated, [](QListWidgetItem* item) { + if (item->checkState() == Qt::Checked) + item->setCheckState(Qt::Unchecked); + else + item->setCheckState(Qt::Checked); + }); } OptionalModDialog::~OptionalModDialog() { delete ui; } + +QStringList OptionalModDialog::getResult() +{ + QStringList result; + result.reserve(ui->list->count()); + for (int i = 0; i < ui->list->count(); i++) { + auto item = ui->list->item(i); + if (item->checkState() == Qt::Checked) + result.append(item->data(Qt::UserRole).toString()); + } + return result; +} diff --git a/launcher/ui/pages/modplatform/OptionalModDialog.h b/launcher/ui/pages/modplatform/OptionalModDialog.h index 4e22d0e46..1897c1fca 100644 --- a/launcher/ui/pages/modplatform/OptionalModDialog.h +++ b/launcher/ui/pages/modplatform/OptionalModDialog.h @@ -25,52 +25,15 @@ namespace Ui { class OptionalModDialog; } -class OptionalModListModel : public QAbstractListModel { - Q_OBJECT - public: - enum Columns { - EnabledColumn = 0, - NameColumn, - }; - - OptionalModListModel(QWidget* parent, QStringList mods); - - QStringList getResult(); - - int rowCount(const QModelIndex& parent) const override; - int columnCount(const QModelIndex& parent) const override; - - QVariant data(const QModelIndex& index, int role) const override; - bool setData(const QModelIndex& index, const QVariant& value, int role) override; - QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - - Qt::ItemFlags flags(const QModelIndex& index) const override; - - public slots: - void selectAll() { toggleAll(true); } - void clearAll() { toggleAll(false); }; - void toggleAll(bool enabled); - - private: - void toggleMod(QString mod, int index); - void setMod(QString mod, int index, bool enable, bool shouldEmit = true); - - private: - QStringList m_mods; - QHash m_selected; -}; - class OptionalModDialog : public QDialog { Q_OBJECT public: - OptionalModDialog(QWidget* parent, QStringList mods); + OptionalModDialog(QWidget* parent, const QStringList& mods); ~OptionalModDialog() override; - QStringList getResult() { return listModel->getResult(); } + QStringList getResult(); private: Ui::OptionalModDialog* ui; - - OptionalModListModel* listModel; }; diff --git a/launcher/ui/pages/modplatform/OptionalModDialog.ui b/launcher/ui/pages/modplatform/OptionalModDialog.ui index 76d1da89d..0b809d2cb 100644 --- a/launcher/ui/pages/modplatform/OptionalModDialog.ui +++ b/launcher/ui/pages/modplatform/OptionalModDialog.ui @@ -11,81 +11,63 @@ - Select Mods To Install + Select Optional Mods - - - - 11 - 75 - true - + + + Qt::IgnoreAction - - Select optional mods to install. - - - Qt::AlignCenter - - - - - - - - true - - - - Note: All files will be downloaded but the unselected mods will be disabled. - - - Qt::AlignCenter - - - - - - - - - - - - - + true - - Cancel - - - - Clear All - - + + + + + Select All + + + + + + + Deselect All + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Unchecked mods will be disabled. + + + + - - - Select All - - - - - - - Install - - - true + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok @@ -93,13 +75,39 @@ - - - ModListView - QTreeView -
ui/widgets/ModListView.h
-
-
- + + + buttonBox + accepted() + OptionalModDialog + accept() + + + 274 + 284 + + + 274 + 154 + + + + + buttonBox + rejected() + OptionalModDialog + reject() + + + 274 + 284 + + + 274 + 154 + + + + From 5d70f4dbca4405704e03e8428a7829b6a7cc32eb Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 28 Aug 2023 08:50:10 +0300 Subject: [PATCH 09/21] removed if Signed-off-by: Trial97 --- launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index 1be58c2b8..e732ad39c 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -394,7 +394,7 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, for (auto file : optionalFiles) { if (selectedMods.contains(file.path)) { file.required = true; - } else if (file.path.endsWith(".jar")) { + } else { file.path += ".disabled"; } files.push_back(file); From 30ff417074a6a5f2200df9bac92a537425417103 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 1 Sep 2023 16:25:49 +0300 Subject: [PATCH 10/21] fix: make cached instead of file for ftb pack import Signed-off-by: Trial97 --- launcher/modplatform/legacy_ftb/PackInstallTask.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp index 761f622bb..091296751 100644 --- a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp +++ b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp @@ -70,16 +70,18 @@ void PackInstallTask::downloadPack() setProgress(1, 4); setAbortable(false); - archivePath = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file); - + auto path = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file); + auto entry = APPLICATION->metacache()->resolveEntry("FTBPacks", path); + entry->setStale(true); + archivePath = entry->getFullPath(); netJobContainer.reset(new NetJob("Download FTB Pack", m_network)); QString url; if (m_pack.type == PackType::Private) { - url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "privatepacks/%1").arg(archivePath); + url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "privatepacks/%1").arg(path); } else { - url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "modpacks/%1").arg(archivePath); + url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "modpacks/%1").arg(path); } - netJobContainer->addNetAction(Net::ApiDownload::makeFile(url, archivePath)); + netJobContainer->addNetAction(Net::ApiDownload::makeCached(url, entry)); connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip); connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed); From 17f696bffcf83ff9d40964137c9743b590161e77 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 1 Sep 2023 22:25:15 +0300 Subject: [PATCH 11/21] small tweaks to atl icons Signed-off-by: Trial97 --- launcher/modplatform/atlauncher/ATLPackIndex.cpp | 2 +- launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/modplatform/atlauncher/ATLPackIndex.cpp b/launcher/modplatform/atlauncher/ATLPackIndex.cpp index 84b0cc9e2..678db63cc 100644 --- a/launcher/modplatform/atlauncher/ATLPackIndex.cpp +++ b/launcher/modplatform/atlauncher/ATLPackIndex.cpp @@ -43,5 +43,5 @@ void ATLauncher::loadIndexedPack(ATLauncher::IndexedPack& m, QJsonObject& obj) m.system = Json::ensureBoolean(obj, QString("system"), false); m.description = Json::ensureString(obj, "description", ""); - m.safeName = "atl_" + Json::requireString(obj, "name").replace(QRegularExpression("[^A-Za-z0-9]"), "").toLower() + ".png"; + m.safeName = Json::requireString(obj, "name").replace(QRegularExpression("[^A-Za-z0-9]"), "").toLower() + ".png"; } diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp index 6298cd0b4..e492830c6 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp @@ -114,7 +114,7 @@ void AtlPage::suggestCurrent() auto uiSupport = new AtlUserInteractionSupportImpl(this); dialog->setSuggestedPack(selected.name, selectedVersion, new ATLauncher::PackInstallTask(uiSupport, selected.name, selectedVersion)); - auto editedLogoName = selected.safeName; + auto editedLogoName = "atl_" + selected.safeName; auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1").arg(selected.safeName); listModel->getLogo(selected.safeName, url, [this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); }); From 47d1f23568a6ea72d16b95ea6481996c64e0a9fe Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 10 Sep 2023 16:22:57 +0300 Subject: [PATCH 12/21] added side for modrinth mods Signed-off-by: Trial97 --- launcher/minecraft/mod/MetadataHandler.h | 1 + launcher/modplatform/ModIndex.h | 2 +- .../modrinth/ModrinthPackExportTask.cpp | 25 ++++++++++++---- .../modrinth/ModrinthPackExportTask.h | 1 + .../modrinth/ModrinthPackIndex.cpp | 16 ++++++++++ launcher/modplatform/packwiz/Packwiz.cpp | 29 +++++++++++++++++-- launcher/modplatform/packwiz/Packwiz.h | 7 +++-- tests/Packwiz_test.cpp | 4 +-- 8 files changed, 73 insertions(+), 12 deletions(-) diff --git a/launcher/minecraft/mod/MetadataHandler.h b/launcher/minecraft/mod/MetadataHandler.h index 88e9ff2b6..3496da2a0 100644 --- a/launcher/minecraft/mod/MetadataHandler.h +++ b/launcher/minecraft/mod/MetadataHandler.h @@ -31,6 +31,7 @@ class Mod; class Metadata { public: using ModStruct = Packwiz::V1::Mod; + using ModSide = Packwiz::V1::Side; static auto create(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> ModStruct { diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index cad217034..7036241db 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -104,6 +104,7 @@ struct IndexedPack { QString logoName; QString logoUrl; QString websiteUrl; + QString side; bool versionsLoaded = false; QVector versions; @@ -128,7 +129,6 @@ struct IndexedPack { return std::any_of(versions.constBegin(), versions.constEnd(), [](auto const& v) { return v.is_currently_selected; }); } }; -QString getMetaURL(ResourceProvider provider, QVariant projectID); struct OverrideDep { QString quilt; diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp index ad8fefac1..72f0bd6c4 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp @@ -25,6 +25,7 @@ #include "Json.h" #include "MMCZip.h" #include "minecraft/PackProfile.h" +#include "minecraft/mod/MetadataHandler.h" #include "minecraft/mod/ModFolderModel.h" const QStringList ModrinthPackExportTask::PREFIXES({ "mods/", "coremods/", "resourcepacks/", "texturepacks/", "shaderpacks/" }); @@ -127,7 +128,8 @@ void ModrinthPackExportTask::collectHashes() QCryptographicHash sha1(QCryptographicHash::Algorithm::Sha1); sha1.addData(data); - ResolvedFile resolvedFile{ sha1.result().toHex(), sha512.result().toHex(), url.toEncoded(), openFile.size() }; + ResolvedFile resolvedFile{ sha1.result().toHex(), sha512.result().toHex(), url.toEncoded(), openFile.size(), + mod->metadata()->side }; resolvedFiles[relative] = resolvedFile; // nice! we've managed to resolve based on local metadata! @@ -272,18 +274,31 @@ QByteArray ModrinthPackExportTask::generateIndex() // detect disabled mod const QFileInfo pathInfo(path); + + QJsonObject env; if (pathInfo.suffix() == "disabled") { // rename it path = pathInfo.dir().filePath(pathInfo.completeBaseName()); - // ...and make it optional - QJsonObject env; env["client"] = "optional"; env["server"] = "optional"; - fileOut["env"] = env; + } else { + env["client"] = "required"; + env["server"] = "required"; } + switch (iterator->side) { + case Metadata::ModSide::ClientSide: + env["server"] = "unsupported"; + break; + case Metadata::ModSide::ServerSide: + env["client"] = "unsupported"; + break; + case Metadata::ModSide::UniversalSide: + break; + } + fileOut["env"] = env; fileOut["path"] = path; - fileOut["downloads"] = QJsonArray{ iterator.value().url }; + fileOut["downloads"] = QJsonArray{ iterator->url }; QJsonObject hashes; hashes["sha1"] = value.sha1; diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.h b/launcher/modplatform/modrinth/ModrinthPackExportTask.h index 1f9e0eb77..388b733e5 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.h +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.h @@ -43,6 +43,7 @@ class ModrinthPackExportTask : public Task { struct ResolvedFile { QString sha1, sha512, url; qint64 size; + Metadata::ModSide side; }; static const QStringList PREFIXES; diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 85e66a91e..9cc59ac39 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -27,6 +27,11 @@ static ModrinthAPI api; static ModPlatform::ProviderCapabilities ProviderCaps; +bool shouldDownloadOnSide(QString side) +{ + return side == "required" || side == "optional"; +} + // https://docs.modrinth.com/api-spec/#tag/projects/operation/getProject void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) { @@ -53,6 +58,17 @@ void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) modAuthor.url = api.getAuthorURL(modAuthor.name); pack.authors.append(modAuthor); + auto client = shouldDownloadOnSide(Json::ensureString(obj, "client_side")); + auto server = shouldDownloadOnSide(Json::ensureString(obj, "server_side")); + + if (server && client) { + pack.side = "both"; + } else if (server) { + pack.side = "server"; + } else if (client) { + pack.side = "client"; + } + // Modrinth can have more data than what's provided by the basic search :) pack.extraDataLoaded = false; } diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 1757da3e0..e35567f24 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -113,6 +113,7 @@ auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedP mod.provider = mod_pack.provider; mod.file_id = mod_version.fileId; mod.project_id = mod_pack.addonId; + mod.side = stringToSide(mod_pack.side); return mod; } @@ -190,7 +191,7 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) { auto tbl = toml::table{ { "name", mod.name.toStdString() }, { "filename", mod.filename.toStdString() }, - { "side", mod.side.toStdString() }, + { "side", sideToString(mod.side).toStdString() }, { "download", toml::table{ { "mode", mod.mode.toStdString() }, @@ -274,7 +275,7 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod { // Basic info mod.name = stringEntry(table, "name"); mod.filename = stringEntry(table, "filename"); - mod.side = stringEntry(table, "side"); + mod.side = stringToSide(stringEntry(table, "side")); } { // [download] info @@ -329,4 +330,28 @@ auto V1::getIndexForMod(QDir& index_dir, QVariant& mod_id) -> Mod return {}; } +auto V1::sideToString(Side side) -> QString +{ + switch (side) { + case Side::ClientSide: + return "client"; + case Side::ServerSide: + return "server"; + case Side::UniversalSide: + return "both"; + } + return {}; +} + +auto V1::stringToSide(QString side) -> Side +{ + if (side == "client") + return Side::ClientSide; + if (side == "server") + return Side::ServerSide; + if (side == "both") + return Side::UniversalSide; + return Side::UniversalSide; +} + } // namespace Packwiz diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 7edc18cde..dce198b0e 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -35,12 +35,12 @@ auto getRealIndexName(QDir& index_dir, QString normalized_index_name, bool shoul class V1 { public: + enum class Side { ClientSide = 1 << 0, ServerSide = 1 << 1, UniversalSide = ClientSide | ServerSide }; struct Mod { QString slug{}; QString name{}; QString filename{}; - // FIXME: make side an enum - QString side{ "both" }; + Side side{ Side::UniversalSide }; // [download] QString mode{}; @@ -93,6 +93,9 @@ class V1 { * If the mod doesn't have a metadata, it simply returns an empty Mod object. * */ static auto getIndexForMod(QDir& index_dir, QVariant& mod_id) -> Mod; + + static auto sideToString(Side side) -> QString; + static auto stringToSide(QString side) -> Side; }; } // namespace Packwiz diff --git a/tests/Packwiz_test.cpp b/tests/Packwiz_test.cpp index d1b274d12..e4abda9f9 100644 --- a/tests/Packwiz_test.cpp +++ b/tests/Packwiz_test.cpp @@ -42,7 +42,7 @@ class PackwizTest : public QObject { QCOMPARE(metadata.name, "Borderless Mining"); QCOMPARE(metadata.filename, "borderless-mining-1.1.1+1.18.jar"); - QCOMPARE(metadata.side, "client"); + QCOMPARE(metadata.side, Packwiz::V1::Side::ClientSide); QCOMPARE(metadata.url, QUrl("https://cdn.modrinth.com/data/kYq5qkSL/versions/1.1.1+1.18/borderless-mining-1.1.1+1.18.jar")); QCOMPARE(metadata.hash_format, "sha512"); @@ -72,7 +72,7 @@ class PackwizTest : public QObject { QCOMPARE(metadata.name, "Screenshot to Clipboard (Fabric)"); QCOMPARE(metadata.filename, "screenshot-to-clipboard-1.0.7-fabric.jar"); - QCOMPARE(metadata.side, "both"); + QCOMPARE(metadata.side, Packwiz::V1::Side::UniversalSide); QCOMPARE(metadata.url, QUrl("https://edge.forgecdn.net/files/3509/43/screenshot-to-clipboard-1.0.7-fabric.jar")); QCOMPARE(metadata.hash_format, "murmur2"); From 4ee6a6711d462d4f85c84afe6bc0e0e6640b1244 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 25 Sep 2023 23:04:21 +0300 Subject: [PATCH 13/21] Added version check for neoforge filter Signed-off-by: Trial97 --- launcher/minecraft/PackProfile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 9e42c5dd6..31974cd00 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -1018,8 +1018,8 @@ std::optional PackProfile::getSupportedModLoaders() // TODO: remove this or add version condition once Quilt drops official Fabric support if (loaders & ModPlatform::Quilt) loaders |= ModPlatform::Fabric; - // TODO: remove this or add version condition once NeoForge drops official Forge support - if (loaders & ModPlatform::NeoForge) + Version instance_ver{ getComponentVersion("net.minecraft") }; + if (instance_ver <= Version("1.20.1") && loaders & ModPlatform::NeoForge) loaders |= ModPlatform::Forge; return loaders; } From 8069de29b2857a357809c3d99a8b07c1bcb49b74 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 2 Oct 2023 18:03:00 +0300 Subject: [PATCH 14/21] fix folder attributes on windows copy Signed-off-by: Trial97 --- launcher/FileSystem.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index defb2cb9e..1f60a7db2 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -238,6 +238,28 @@ bool ensureFolderPathExists(QString foldernamepath) return success; } +bool copyFileAttributes(QString src, QString dst) +{ +#ifdef Q_OS_WIN32 + auto attrs = GetFileAttributesW(src.toStdWString().c_str()); + if (attrs == INVALID_FILE_ATTRIBUTES) + return false; + return SetFileAttributesW(dst.toStdWString().c_str(), attrs); +#endif + return true; +} + +// needs folders to exists +void copyFolderAttributes(QString src, QString dst, QString relative) +{ + auto path = PathCombine(src, relative); + QDir dsrc(src); + while ((path = QFileInfo(path).path()).length() >= src.length()) { + auto dst_path = PathCombine(dst, dsrc.relativeFilePath(path)); + copyFileAttributes(path, dst_path); + } +} + /** * @brief Copies a directory and it's contents from src to dest * @param offset subdirectory form src to copy to dest @@ -273,6 +295,9 @@ bool copy::operator()(const QString& offset, bool dryRun) auto dst_path = PathCombine(dst, relative_dst_path); if (!dryRun) { ensureFilePathExists(dst_path); +#ifdef Q_OS_WIN32 + copyFolderAttributes(src, dst, relative_dst_path); +#endif fs::copy(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), opt, err); } if (err) { From c78d4e7bb624cd79fcd330708040d92b9e607c88 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 4 Oct 2023 19:16:21 +0300 Subject: [PATCH 15/21] added version test for snapshot Signed-off-by: Trial97 --- tests/Version_test.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Version_test.cpp b/tests/Version_test.cpp index d25bf7bb5..4c67cc544 100644 --- a/tests/Version_test.cpp +++ b/tests/Version_test.cpp @@ -55,6 +55,8 @@ class VersionTest : public QObject { << "2.2.0" << true << false; QTest::newRow("lessThan, two-digit") << "1.41" << "1.42" << true << false; + QTest::newRow("lessThan, snapshot") << "1.20.0-rc2" + << "1.20.1" << true << false; QTest::newRow("greaterThan, explicit 1") << "1.2.1" << "1.2.0" << false << false; @@ -72,6 +74,8 @@ class VersionTest : public QObject { << "1.2" << false << false; QTest::newRow("greaterThan, two-digit") << "1.42" << "1.41" << false << false; + QTest::newRow("greaterThan, snapshot") << "1.20.2-rc2" + << "1.20.1" << false << false; } private slots: From e913f61305de434cda5755f1db6bd3bb2ce4ea5e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 4 Oct 2023 19:29:37 +0300 Subject: [PATCH 16/21] added a more strict condition for neoforge forge support Signed-off-by: Trial97 --- launcher/minecraft/PackProfile.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 31974cd00..aea78b552 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -1018,8 +1018,7 @@ std::optional PackProfile::getSupportedModLoaders() // TODO: remove this or add version condition once Quilt drops official Fabric support if (loaders & ModPlatform::Quilt) loaders |= ModPlatform::Fabric; - Version instance_ver{ getComponentVersion("net.minecraft") }; - if (instance_ver <= Version("1.20.1") && loaders & ModPlatform::NeoForge) + if (getComponentVersion("net.minecraft") == "1.20.1" && loaders & ModPlatform::NeoForge) loaders |= ModPlatform::Forge; return loaders; } From b67c2c71d10945775b9121c5f22e2b1ac5531fe8 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 5 Oct 2023 22:44:49 +0300 Subject: [PATCH 17/21] removed windows legacy builds Signed-off-by: Trial97 --- .github/workflows/build.yml | 12 ------------ .github/workflows/trigger_release.yml | 10 +++------- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 059795a11..4665a7d56 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,18 +54,6 @@ jobs: msystem: clang64 vcvars_arch: 'amd64_x86' - - os: windows-2022 - name: "Windows-MSVC-Legacy" - msystem: '' - architecture: 'win32' - vcvars_arch: 'amd64_x86' - qt_ver: 5 - qt_host: windows - qt_arch: 'win32_msvc2019' - qt_version: '5.15.2' - qt_modules: '' - qt_tools: 'tools_openssl_x86' - - os: windows-2022 name: "Windows-MSVC" msystem: '' diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index bda75e354..b67346d80 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -3,10 +3,9 @@ name: Build Application and Make Release on: push: tags: - - '*' + - "*" jobs: - build_release: name: Build Release uses: ./.github/workflows/build.yml @@ -28,8 +27,8 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - submodules: 'true' - path: 'PrismLauncher-source' + submodules: "true" + path: "PrismLauncher-source" - name: Download artifacts uses: actions/download-artifact@v3 - name: Grab and store version @@ -95,9 +94,6 @@ jobs: PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip PrismLauncher-Windows-MinGW-w64-Setup-${{ env.VERSION }}.exe - PrismLauncher-Windows-MSVC-Legacy-${{ env.VERSION }}.zip - PrismLauncher-Windows-MSVC-Legacy-Portable-${{ env.VERSION }}.zip - PrismLauncher-Windows-MSVC-Legacy-Setup-${{ env.VERSION }}.exe PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.zip PrismLauncher-Windows-MSVC-arm64-Portable-${{ env.VERSION }}.zip PrismLauncher-Windows-MSVC-arm64-Setup-${{ env.VERSION }}.exe From bca5e8f3952d44a1ff517123ff51ab439b295f84 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Fri, 6 Oct 2023 16:16:03 +0300 Subject: [PATCH 18/21] Update launcher/minecraft/PackProfile.cpp Co-authored-by: TheKodeToad Signed-off-by: Alexandru Ionut Tripon --- launcher/minecraft/PackProfile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index aea78b552..9e706ae0a 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -1018,7 +1018,7 @@ std::optional PackProfile::getSupportedModLoaders() // TODO: remove this or add version condition once Quilt drops official Fabric support if (loaders & ModPlatform::Quilt) loaders |= ModPlatform::Fabric; - if (getComponentVersion("net.minecraft") == "1.20.1" && loaders & ModPlatform::NeoForge) + if (getComponentVersion("net.minecraft") == "1.20.1" && (loaders & ModPlatform::NeoForge)) loaders |= ModPlatform::Forge; return loaders; } From 32eaaa25d984519a3fddc9844f921c3b22f4d5de Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 13 Oct 2023 00:04:36 +0300 Subject: [PATCH 19/21] removed the better release for modrinth modpacks Signed-off-by: Trial97 --- launcher/modplatform/modrinth/ModrinthPackManifest.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index a154317fe..c1531a94d 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -111,9 +111,8 @@ void loadIndexedVersions(Modpack& pack, QJsonDocument& doc) unsortedVersions.append(file); } auto orderSortPredicate = [](const ModpackVersion& a, const ModpackVersion& b) -> bool { - bool a_better_release = a.version_type <= b.version_type; // dates are in RFC 3339 format - return a.date > b.date && a_better_release; + return a.date > b.date; }; std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate); From 023b3e3c3945592845fcae6eec8fc9d90871d2c2 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 14 Oct 2023 09:40:56 +0300 Subject: [PATCH 20/21] Fixed arch build Signed-off-by: Trial97 --- launcher/translations/TranslationsModel.cpp | 36 +++++++++++---------- launcher/translations/TranslationsModel.h | 4 ++- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/launcher/translations/TranslationsModel.cpp b/launcher/translations/TranslationsModel.cpp index 933fe2d35..56ade8e32 100644 --- a/launcher/translations/TranslationsModel.cpp +++ b/launcher/translations/TranslationsModel.cpp @@ -206,7 +206,7 @@ void TranslationsModel::indexReceived() reloadLocalFiles(); auto language = d->m_system_locale; - if (!findLanguage(language)) { + if (!findLanguageAsOptional(language).has_value()) { language = d->m_system_language; } selectLanguage(language); @@ -417,14 +417,17 @@ int TranslationsModel::columnCount([[maybe_unused]] const QModelIndex& parent) c return 2; } -Language* TranslationsModel::findLanguage(const QString& key) +QVector::Iterator TranslationsModel::findLanguage(const QString& key) { - auto found = std::find_if(d->m_languages.begin(), d->m_languages.end(), [&](Language& lang) { return lang.key == key; }); - if (found == d->m_languages.end()) { - return nullptr; - } else { - return found; - } + return std::find_if(d->m_languages.begin(), d->m_languages.end(), [&](Language& lang) { return lang.key == key; }); +} + +std::optional TranslationsModel::findLanguageAsOptional(const QString& key) +{ + auto found = findLanguage(key); + if (found != d->m_languages.end()) + return *found; + return {}; } void TranslationsModel::setUseSystemLocale(bool useSystemLocale) @@ -436,13 +439,13 @@ void TranslationsModel::setUseSystemLocale(bool useSystemLocale) bool TranslationsModel::selectLanguage(QString key) { QString& langCode = key; - auto langPtr = findLanguage(key); + auto langPtr = findLanguageAsOptional(key); if (langCode.isEmpty()) { d->no_language_set = true; } - if (!langPtr) { + if (!langPtr.has_value()) { qWarning() << "Selected invalid language" << key << ", defaulting to" << defaultLangCode; langCode = defaultLangCode; } else { @@ -527,9 +530,8 @@ bool TranslationsModel::selectLanguage(QString key) QModelIndex TranslationsModel::selectedIndex() { auto found = findLanguage(d->m_selectedLanguage); - if (found) { - // QVector iterator freely converts to pointer to contained type - return index(found - d->m_languages.begin(), 0, QModelIndex()); + if (found != d->m_languages.end()) { + return index(std::distance(d->m_languages.begin(), found), 0, QModelIndex()); } return QModelIndex(); } @@ -562,8 +564,8 @@ void TranslationsModel::updateLanguage(QString key) qWarning() << "Cannot update builtin language" << key; return; } - auto found = findLanguage(key); - if (!found) { + auto found = findLanguageAsOptional(key); + if (!found.has_value()) { qWarning() << "Cannot update invalid language" << key; return; } @@ -578,8 +580,8 @@ void TranslationsModel::downloadTranslation(QString key) d->m_nextDownload = key; return; } - auto lang = findLanguage(key); - if (!lang) { + auto lang = findLanguageAsOptional(key); + if (!lang.has_value()) { qWarning() << "Will not download an unknown translation" << key; return; } diff --git a/launcher/translations/TranslationsModel.h b/launcher/translations/TranslationsModel.h index cff23ce74..96a0e9f8b 100644 --- a/launcher/translations/TranslationsModel.h +++ b/launcher/translations/TranslationsModel.h @@ -17,6 +17,7 @@ #include #include +#include struct Language; @@ -40,7 +41,8 @@ class TranslationsModel : public QAbstractListModel { void setUseSystemLocale(bool useSystemLocale); private: - Language* findLanguage(const QString& key); + QVector::Iterator findLanguage(const QString& key); + std::optional findLanguageAsOptional(const QString& key); void reloadLocalFiles(); void downloadTranslation(QString key); void downloadNext(); From 4939a33456916ceb03fbe04bb850089895cbf62d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 15 Oct 2023 00:18:39 +0000 Subject: [PATCH 21/21] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/2de1be5b51c3d6fa833f1c1f222dc867dd054b31' (2023-10-07) → 'github:nixos/nixpkgs/01441e14af5e29c9d27ace398e6dd0b293e25a54' (2023-10-11) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/66c352d33e0907239e4a69416334f64af2c685cc' (2023-10-05) → 'github:cachix/pre-commit-hooks.nix/42e1b6095ef80a51f79595d9951eb38e91c4e6ca' (2023-10-09) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index b381db5ae..ad18ff615 100644 --- a/flake.lock +++ b/flake.lock @@ -106,11 +106,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1696661029, - "narHash": "sha256-GIB5VTkvsDIqfMpdtuetOzpm64P8wm8nBSv5Eo8XM3Y=", + "lastModified": 1697009197, + "narHash": "sha256-viVRhBTFT8fPJTb1N3brQIpFZnttmwo3JVKNuWRVc3s=", "owner": "nixos", "repo": "nixpkgs", - "rev": "2de1be5b51c3d6fa833f1c1f222dc867dd054b31", + "rev": "01441e14af5e29c9d27ace398e6dd0b293e25a54", "type": "github" }, "original": { @@ -153,11 +153,11 @@ ] }, "locked": { - "lastModified": 1696516544, - "narHash": "sha256-8rKE8Je6twTNFRTGF63P9mE3lZIq917RAicdc4XJO80=", + "lastModified": 1696846637, + "narHash": "sha256-0hv4kbXxci2+pxhuXlVgftj/Jq79VSmtAyvfabCCtYk=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "66c352d33e0907239e4a69416334f64af2c685cc", + "rev": "42e1b6095ef80a51f79595d9951eb38e91c4e6ca", "type": "github" }, "original": {