diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index a30f99439..ff8558ce7 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -316,7 +316,7 @@ void LaunchController::launchInstance() online_mode = "online"; // Prepend Server Status - QStringList servers = { "authserver.mojang.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" }; + QStringList servers = { "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" }; QString resolved_servers = ""; QHostInfo host_info; diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 3de3c5476..e65c69b25 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -255,7 +255,7 @@ class ResourceFolderModel : public QAbstractListModel { protected: // Represents the relationship between a column's index (represented by the list index), and it's sorting key. // As such, the order in with they appear is very important! - QList m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE }; + QList m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE, SortType::PROVIDER }; QStringList m_column_names = { "Enable", "Name", "Last Modified", "Provider" }; QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified"), tr("Provider") }; QList m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive }; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index b777dbafd..ccbf80367 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -51,8 +51,9 @@ ResourcePackFolderModel::ResourcePackFolderModel(const QDir& dir, BaseInstance* : ResourceFolderModel(dir, instance, is_indexed, create_dir, parent) { m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified", "Provider" }); - m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified"), tr("Provider") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE }; + m_column_names_translated = + QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified"), tr("Provider") }); + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE, SortType::PROVIDER }; m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true, true, true }; diff --git a/launcher/minecraft/mod/ShaderPackFolderModel.h b/launcher/minecraft/mod/ShaderPackFolderModel.h index c63def238..cd01f6226 100644 --- a/launcher/minecraft/mod/ShaderPackFolderModel.h +++ b/launcher/minecraft/mod/ShaderPackFolderModel.h @@ -20,4 +20,6 @@ class ShaderPackFolderModel : public ResourceFolderModel { { return new LocalShaderPackParseTask(m_next_resolution_ticket, static_cast(resource)); } + + RESOURCE_HELPERS(ShaderPack); }; diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index 2c7bec5d4..7b787c6a0 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -83,7 +83,7 @@ class ResourceAPI { struct VersionSearchArgs { ModPlatform::IndexedPack pack; - std::optional > mcVersions; + std::optional> mcVersions; std::optional loaders; VersionSearchArgs(VersionSearchArgs const&) = default; diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index c609e78fc..6e1f507fd 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -110,11 +110,14 @@ auto V1::createModFormat([[maybe_unused]] const QDir& index_dir, mod.hash = mod_version.hash; mod.provider = mod_pack.provider; - mod.version_number = mod_version.version_number; mod.file_id = mod_version.fileId; mod.project_id = mod_pack.addonId; mod.side = stringToSide(mod_pack.side); + mod.version_number = mod_version.version_number; + if (mod.version_number.isNull()) // on CurseForge, there is only a version name - not a versio + mod.version_number = mod_version.version; + return mod; } @@ -164,10 +167,9 @@ void V1::updateModIndex(const QDir& index_dir, Mod& mod) 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() }, - }; + update = toml::table{ { "file-id", mod.file_id.toInt() }, + { "project-id", mod.project_id.toInt() }, + { "x-prismlauncher-version-number", mod.version_number.toStdString() } }; break; case (ModPlatform::ResourceProvider::MODRINTH): if (mod.mod_id().toString().isEmpty() || mod.version().toString().isEmpty()) { @@ -177,6 +179,7 @@ void V1::updateModIndex(const QDir& index_dir, Mod& mod) update = toml::table{ { "mod-id", mod.mod_id().toString().toStdString() }, { "version", mod.version().toString().toStdString() }, + { "x-prismlauncher-version-number", mod.version_number.toStdString() }, }; break; } @@ -199,8 +202,7 @@ void V1::updateModIndex(const QDir& index_dir, Mod& mod) { "hash-format", mod.hash_format.toStdString() }, { "hash", mod.hash.toStdString() }, } }, - { "update", toml::table{ { ModPlatform::ProviderCapabilities::name(mod.provider), update }, - { "x-prismlauncher-version-number", mod.version_number.toStdString() } } } }; + { "update", toml::table{ { ModPlatform::ProviderCapabilities::name(mod.provider), update } } } }; std::stringstream ss; ss << tbl; in_stream << QString::fromStdString(ss.str()); @@ -295,23 +297,23 @@ auto V1::getIndexForMod(const QDir& index_dir, QString slug) -> Mod { // [update] info using Provider = ModPlatform::ResourceProvider; - auto update_table = table["update"].as_table(); - if (!update_table) { + auto update_table = table["update"]; + if (!update_table || !update_table.is_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)[ModPlatform::ProviderCapabilities::name(Provider::FLAME)].as_table())) { + if ((mod_provider_table = update_table[ModPlatform::ProviderCapabilities::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)[ModPlatform::ProviderCapabilities::name(Provider::MODRINTH)].as_table())) { + mod.version_number = stringEntry(*mod_provider_table, "x-prismlauncher-version-number"); + } else if ((mod_provider_table = update_table[ModPlatform::ProviderCapabilities::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"); + mod.version_number = stringEntry(*mod_provider_table, "x-prismlauncher-version-number"); } else { qCritical() << QString("No mod provider on mod metadata!"); return {}; diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 07bb4248d..19ef22dd8 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -52,9 +52,9 @@ class V1 { // [update] ModPlatform::ResourceProvider provider{}; - QString version_number{}; QVariant file_id{}; QVariant project_id{}; + QString version_number{}; public: // This is a totally heuristic, but should work for now. diff --git a/launcher/ui/dialogs/ResourceUpdateDialog.cpp b/launcher/ui/dialogs/ResourceUpdateDialog.cpp index 459ba7f23..6426c2bfd 100644 --- a/launcher/ui/dialogs/ResourceUpdateDialog.cpp +++ b/launcher/ui/dialogs/ResourceUpdateDialog.cpp @@ -38,7 +38,8 @@ ResourceUpdateDialog::ResourceUpdateDialog(QWidget* parent, BaseInstance* instance, const std::shared_ptr resource_model, QList& search_for, - bool includeDeps) + bool include_deps, + bool filter_loaders) : ReviewMessageBox(parent, tr("Confirm mods to update"), "") , m_parent(parent) , m_resource_model(resource_model) @@ -46,7 +47,8 @@ ResourceUpdateDialog::ResourceUpdateDialog(QWidget* parent, , m_second_try_metadata( new ConcurrentTask(nullptr, "Second Metadata Search", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt())) , m_instance(instance) - , m_include_deps(includeDeps) + , m_include_deps(include_deps) + , m_filter_loaders(filter_loaders) { ReviewMessageBox::setGeometry(0, 0, 800, 600); @@ -85,7 +87,7 @@ void ResourceUpdateDialog::checkCandidates() } auto versions = mcVersions(m_instance); - auto loaders = mcLoaders(m_instance); + auto loaders = m_filter_loaders ? mcLoaders(m_instance) : std::optional(); SequentialTask check_task(m_parent, tr("Checking for updates")); @@ -224,10 +226,10 @@ void ResourceUpdateDialog::checkCandidates() if (dep->pack->provider == ModPlatform::ResourceProvider::FLAME) changelog = api.getModFileChangelog(dep->version.addonId.toInt(), dep->version.fileId.toInt()); auto download_task = makeShared(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 - }; + 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 }; appendResource(updatable, getRequiredBy.value(dep->version.addonId.toString())); m_tasks.insert(updatable.name, updatable.download); diff --git a/launcher/ui/dialogs/ResourceUpdateDialog.h b/launcher/ui/dialogs/ResourceUpdateDialog.h index 31aab8f5d..de1d845d2 100644 --- a/launcher/ui/dialogs/ResourceUpdateDialog.h +++ b/launcher/ui/dialogs/ResourceUpdateDialog.h @@ -20,7 +20,8 @@ class ResourceUpdateDialog final : public ReviewMessageBox { BaseInstance* instance, std::shared_ptr resource_model, QList& search_for, - bool includeDeps); + bool include_deps, + bool filter_loaders); void checkCandidates(); @@ -63,4 +64,5 @@ class ResourceUpdateDialog final : public ReviewMessageBox { bool m_no_updates = false; bool m_aborted = false; bool m_include_deps = false; + bool m_filter_loaders = false; }; diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.ui b/launcher/ui/pages/instance/ExternalResourcesPage.ui index ff08e12d2..2b4a47b9d 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.ui +++ b/launcher/ui/pages/instance/ExternalResourcesPage.ui @@ -70,15 +70,15 @@ - - true - Actions Qt::ToolButtonTextOnly + + true + RightToolBarArea @@ -95,10 +95,10 @@ - &Add + &Add File - Add + Add a locally downloaded file. @@ -106,7 +106,7 @@ &Remove - Remove selected item + Remove all selected items. @@ -114,7 +114,7 @@ &Enable - Enable selected item + Enable all selected items. @@ -122,7 +122,7 @@ &Disable - Disable selected item + Disable all selected items. @@ -137,6 +137,9 @@ View &Folder + + Open the folder in the system file manager. + @@ -146,18 +149,7 @@ &Download - Download a new resource - - - - - false - - - Visit mod's page - - - Go to mods home page + Download resources from online mod platforms. @@ -168,7 +160,23 @@ Check for &Updates - Try to check or update all selected resources (all resources if none are selected) + Try to check or update all selected resources (all resources if none are selected). + + + + + Reset Update Metadata + + + QAction::NoRole + + + + + Verify Dependencies + + + QAction::NoRole diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index e513cb1fa..5b10a38df 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -68,88 +68,37 @@ #include "tasks/ConcurrentTask.h" #include "ui/dialogs/ProgressDialog.h" -ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr mods, QWidget* parent) - : ExternalResourcesPage(inst, mods, parent), m_model(mods) +ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr model, QWidget* parent) + : ExternalResourcesPage(inst, model, parent), m_model(model) { - // This is structured like that so that these changes - // do not affect the Resource pack and Shader pack tabs - { - ui->actionDownloadItem->setText(tr("Download mods")); - ui->actionDownloadItem->setToolTip(tr("Download mods from online mod platforms")); - ui->actionDownloadItem->setEnabled(true); - ui->actionAddItem->setText(tr("Add file")); - ui->actionAddItem->setToolTip(tr("Add a locally downloaded file")); + ui->actionDownloadItem->setText(tr("Download Mods")); + ui->actionDownloadItem->setToolTip(tr("Download mods from online mod platforms")); + ui->actionDownloadItem->setEnabled(true); + ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem); - ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem); + connect(ui->actionDownloadItem, &QAction::triggered, this, &ModFolderPage::downloadMods); - connect(ui->actionDownloadItem, &QAction::triggered, this, &ModFolderPage::installMods); + ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected mods (all mods if none are selected)")); + connect(ui->actionUpdateItem, &QAction::triggered, this, &ModFolderPage::updateMods); + ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionUpdateItem); - // update menu - auto updateMenu = ui->actionUpdateItem->menu(); - if (updateMenu) { - updateMenu->clear(); - } else { - updateMenu = new QMenu(this); - } + auto updateMenu = new QMenu(this); - auto update = updateMenu->addAction(tr("Check for Updates")); - update->setToolTip(tr("Try to check or update all selected mods (all mods if none are selected)")); - connect(update, &QAction::triggered, this, &ModFolderPage::updateMods); + auto update = updateMenu->addAction(tr("Check for Updates")); + connect(update, &QAction::triggered, this, &ModFolderPage::updateMods); - auto updateWithDeps = updateMenu->addAction(tr("Verify Dependencies")); - updateWithDeps->setToolTip( - tr("Try to update and check for missing dependencies all selected mods (all mods if none are selected)")); - connect(updateWithDeps, &QAction::triggered, this, [this] { updateMods(true); }); + updateMenu->addAction(ui->actionVerifyItemDependencies); + connect(ui->actionVerifyItemDependencies, &QAction::triggered, this, [this] { updateMods(true); }); - auto depsDisabled = APPLICATION->settings()->getSetting("ModDependenciesDisabled"); - updateWithDeps->setVisible(!depsDisabled->get().toBool()); - connect(depsDisabled.get(), &Setting::SettingChanged, this, - [updateWithDeps](const Setting& setting, QVariant value) { updateWithDeps->setVisible(!value.toBool()); }); + auto depsDisabled = APPLICATION->settings()->getSetting("ModDependenciesDisabled"); + ui->actionVerifyItemDependencies->setVisible(!depsDisabled->get().toBool()); + connect(depsDisabled.get(), &Setting::SettingChanged, this, + [this](const Setting& setting, const QVariant& value) { ui->actionVerifyItemDependencies->setVisible(!value.toBool()); }); - auto actionRemoveItemMetadata = updateMenu->addAction(tr("Reset update metadata")); - actionRemoveItemMetadata->setToolTip(tr("Remove mod's metadata")); - connect(actionRemoveItemMetadata, &QAction::triggered, this, &ModFolderPage::deleteModMetadata); - actionRemoveItemMetadata->setEnabled(false); + updateMenu->addAction(ui->actionResetItemMetadata); + connect(ui->actionResetItemMetadata, &QAction::triggered, this, &ModFolderPage::deleteModMetadata); - ui->actionUpdateItem->setMenu(updateMenu); - - ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected mods (all mods if none are selected)")); - connect(ui->actionUpdateItem, &QAction::triggered, this, &ModFolderPage::updateMods); - ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionUpdateItem); - - ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page")); - ui->actionsToolbar->addAction(ui->actionVisitItemPage); - connect(ui->actionVisitItemPage, &QAction::triggered, this, &ModFolderPage::visitModPages); - - auto check_allow_update = [this] { return ui->treeView->selectionModel()->hasSelection() || !m_model->empty(); }; - - connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, - [this, check_allow_update, actionRemoveItemMetadata] { - ui->actionUpdateItem->setEnabled(check_allow_update()); - - auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); - auto mods_list = m_model->selectedMods(selection); - auto selected = std::count_if(mods_list.cbegin(), mods_list.cend(), - [](Mod* v) { return v->metadata() != nullptr || v->homeurl().size() != 0; }); - if (selected <= 1) { - ui->actionVisitItemPage->setText(tr("Visit mod's page")); - ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page")); - - } else { - ui->actionVisitItemPage->setText(tr("Visit mods' pages")); - ui->actionVisitItemPage->setToolTip(tr("Go to the pages of the selected mods")); - } - ui->actionVisitItemPage->setEnabled(selected != 0); - actionRemoveItemMetadata->setEnabled(selected != 0); - }); - - auto updateButtons = [this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); }; - connect(mods.get(), &ModFolderModel::rowsInserted, this, updateButtons); - - connect(mods.get(), &ModFolderModel::rowsRemoved, this, updateButtons); - - connect(mods.get(), &ModFolderModel::updateFinished, this, updateButtons); - } + ui->actionUpdateItem->setMenu(updateMenu); } bool ModFolderPage::shouldDisplay() const @@ -182,7 +131,7 @@ void ModFolderPage::removeItems(const QItemSelection& selection) m_model->deleteResources(selection.indexes()); } -void ModFolderPage::installMods() +void ModFolderPage::downloadMods() { if (m_instance->typeName() != "Minecraft") return; // this is a null instance or a legacy instance @@ -195,7 +144,7 @@ void ModFolderPage::installMods() ResourceDownload::ModDownloadDialog mdownload(this, m_model, m_instance); if (mdownload.exec()) { - auto tasks = new ConcurrentTask(this, "Download Mods", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); + auto tasks = new ConcurrentTask(this, tr("Download Mods"), APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); connect(tasks, &Task::failed, [this, tasks](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); tasks->deleteLater(); @@ -257,7 +206,7 @@ void ModFolderPage::updateMods(bool includeDeps) if (use_all) mods_list = m_model->allResources(); - ResourceUpdateDialog update_dialog(this, m_instance, m_model, mods_list, includeDeps); + ResourceUpdateDialog update_dialog(this, m_instance, m_model, mods_list, includeDeps, true); update_dialog.checkCandidates(); if (update_dialog.aborted()) { @@ -307,6 +256,27 @@ void ModFolderPage::updateMods(bool includeDeps) } } +void ModFolderPage::deleteModMetadata() +{ + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + auto selectionCount = m_model->selectedMods(selection).length(); + if (selectionCount == 0) + return; + if (selectionCount > 1) { + auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), + tr("You are about to remove the metadata for %1 mods.\n" + "Are you sure?") + .arg(selectionCount), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } + + m_model->deleteMetadata(selection); +} + CoreModFolderPage::CoreModFolderPage(BaseInstance* inst, std::shared_ptr mods, QWidget* parent) : ModFolderPage(inst, mods, parent) {} @@ -340,34 +310,3 @@ bool NilModFolderPage::shouldDisplay() const { return m_model->dir().exists(); } - -void ModFolderPage::visitModPages() -{ - auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); - for (auto mod : m_model->selectedMods(selection)) { - auto url = mod->metaurl(); - if (!url.isEmpty()) - DesktopServices::openUrl(url); - } -} - -void ModFolderPage::deleteModMetadata() -{ - auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); - auto selectionCount = m_model->selectedMods(selection).length(); - if (selectionCount == 0) - return; - if (selectionCount > 1) { - auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), - tr("You are about to remove the metadata for %1 mods.\n" - "Are you sure?") - .arg(selectionCount), - QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) - ->exec(); - - if (response != QMessageBox::Yes) - return; - } - - m_model->deleteMetadata(selection); -} diff --git a/launcher/ui/pages/instance/ModFolderPage.h b/launcher/ui/pages/instance/ModFolderPage.h index 4672350c6..455db33cb 100644 --- a/launcher/ui/pages/instance/ModFolderPage.h +++ b/launcher/ui/pages/instance/ModFolderPage.h @@ -44,7 +44,7 @@ class ModFolderPage : public ExternalResourcesPage { Q_OBJECT public: - explicit ModFolderPage(BaseInstance* inst, std::shared_ptr mods, QWidget* parent = nullptr); + explicit ModFolderPage(BaseInstance* inst, std::shared_ptr model, QWidget* parent = nullptr); virtual ~ModFolderPage() = default; void setFilter(const QString& filter) { m_fileSelectionFilter = filter; } @@ -61,11 +61,10 @@ class ModFolderPage : public ExternalResourcesPage { private slots: void removeItems(const QItemSelection& selection) override; - void deleteModMetadata(); - void installMods(); + void downloadMods(); void updateMods(bool includeDeps = false); - void visitModPages(); + void deleteModMetadata(); protected: std::shared_ptr m_model; diff --git a/launcher/ui/pages/instance/ResourcePackPage.cpp b/launcher/ui/pages/instance/ResourcePackPage.cpp index 85be64256..e51ebafac 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.cpp +++ b/launcher/ui/pages/instance/ResourcePackPage.cpp @@ -42,17 +42,31 @@ #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ProgressDialog.h" #include "ui/dialogs/ResourceDownloadDialog.h" +#include "ui/dialogs/ResourceUpdateDialog.h" ResourcePackPage::ResourcePackPage(MinecraftInstance* instance, std::shared_ptr model, QWidget* parent) - : ExternalResourcesPage(instance, model, parent) + : ExternalResourcesPage(instance, model, parent), m_model(model) { - ui->actionDownloadItem->setText(tr("Download packs")); - ui->actionDownloadItem->setToolTip(tr("Download resource packs from online platforms")); + ui->actionDownloadItem->setText(tr("Download Packs")); + ui->actionDownloadItem->setToolTip(tr("Download resource packs from online mod platforms")); ui->actionDownloadItem->setEnabled(true); - connect(ui->actionDownloadItem, &QAction::triggered, this, &ResourcePackPage::downloadRPs); ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem); - ui->actionViewConfigs->setVisible(false); + connect(ui->actionDownloadItem, &QAction::triggered, this, &ResourcePackPage::downloadResourcePacks); + + ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected resource packs (all resource packs if none are selected)")); + connect(ui->actionUpdateItem, &QAction::triggered, this, &ResourcePackPage::updateResourcePacks); + ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionUpdateItem); + + auto updateMenu = new QMenu(this); + + auto update = updateMenu->addAction(ui->actionUpdateItem->text()); + connect(update, &QAction::triggered, this, &ResourcePackPage::updateResourcePacks); + + updateMenu->addAction(ui->actionResetItemMetadata); + connect(ui->actionResetItemMetadata, &QAction::triggered, this, &ResourcePackPage::deleteResourcePackMetadata); + + ui->actionUpdateItem->setMenu(updateMenu); } bool ResourcePackPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) @@ -65,12 +79,12 @@ bool ResourcePackPage::onSelectionChanged(const QModelIndex& current, [[maybe_un return true; } -void ResourcePackPage::downloadRPs() +void ResourcePackPage::downloadResourcePacks() { if (m_instance->typeName() != "Minecraft") return; // this is a null instance or a legacy instance - ResourceDownload::ResourcePackDownloadDialog mdownload(this, std::static_pointer_cast(m_model), m_instance); + ResourceDownload::ResourcePackDownloadDialog mdownload(this, m_model, m_instance); if (mdownload.exec()) { auto tasks = new ConcurrentTask(this, "Download Resource Pack", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); @@ -101,3 +115,103 @@ void ResourcePackPage::downloadRPs() m_model->update(); } } + +void ResourcePackPage::updateResourcePacks() +{ + if (m_instance->typeName() != "Minecraft") + return; // this is a null instance or a legacy instance + + auto profile = static_cast(m_instance)->getPackProfile(); + if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) { + QMessageBox::critical(this, tr("Error"), tr("Resource pack updates are unavailable when metadata is disabled!")); + return; + } + if (m_instance != nullptr && m_instance->isRunning()) { + auto response = + CustomMessageBox::selectable(this, tr("Confirm Update"), + tr("Updating resource packs while the game is running may cause pack duplication and game crashes.\n" + "The old files may not be deleted as they are in use.\n" + "Are you sure you want to do this?"), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + + auto mods_list = m_model->selectedResources(selection); + bool use_all = mods_list.empty(); + if (use_all) + mods_list = m_model->allResources(); + + ResourceUpdateDialog update_dialog(this, m_instance, m_model, mods_list, false, false); + update_dialog.checkCandidates(); + + if (update_dialog.aborted()) { + CustomMessageBox::selectable(this, tr("Aborted"), tr("The resource pack updater was aborted!"), QMessageBox::Warning)->show(); + return; + } + if (update_dialog.noUpdates()) { + QString message{ tr("'%1' is up-to-date! :)").arg(mods_list.front()->name()) }; + if (mods_list.size() > 1) { + if (use_all) { + message = tr("All resource packs are up-to-date! :)"); + } else { + message = tr("All selected resource packs are up-to-date! :)"); + } + } + CustomMessageBox::selectable(this, tr("Update checker"), message)->exec(); + return; + } + + if (update_dialog.exec()) { + auto tasks = new ConcurrentTask(this, "Download Resource Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); + connect(tasks, &Task::failed, [this, tasks](QString reason) { + CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::aborted, [this, tasks]() { + CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::succeeded, [this, tasks]() { + QStringList warnings = tasks->warnings(); + if (warnings.count()) { + CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); + } + tasks->deleteLater(); + }); + + for (auto task : update_dialog.getTasks()) { + tasks->addTask(task); + } + + ProgressDialog loadDialog(this); + loadDialog.setSkipButton(true, tr("Abort")); + loadDialog.execWithTask(tasks); + + m_model->update(); + } +} + +void ResourcePackPage::deleteResourcePackMetadata() +{ + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + auto selectionCount = m_model->selectedResourcePacks(selection).length(); + if (selectionCount == 0) + return; + if (selectionCount > 1) { + auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), + tr("You are about to remove the metadata for %1 resource packs.\n" + "Are you sure?") + .arg(selectionCount), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } + + m_model->deleteMetadata(selection); +} diff --git a/launcher/ui/pages/instance/ResourcePackPage.h b/launcher/ui/pages/instance/ResourcePackPage.h index cb84ca96d..c2d36cea1 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.h +++ b/launcher/ui/pages/instance/ResourcePackPage.h @@ -59,5 +59,12 @@ class ResourcePackPage : public ExternalResourcesPage { public slots: bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override; - void downloadRPs(); + + private slots: + void downloadResourcePacks(); + void updateResourcePacks(); + void deleteResourcePackMetadata(); + + protected: + std::shared_ptr m_model; }; diff --git a/launcher/ui/pages/instance/ShaderPackPage.cpp b/launcher/ui/pages/instance/ShaderPackPage.cpp index 40366a1be..768d7c710 100644 --- a/launcher/ui/pages/instance/ShaderPackPage.cpp +++ b/launcher/ui/pages/instance/ShaderPackPage.cpp @@ -45,27 +45,41 @@ #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ProgressDialog.h" #include "ui/dialogs/ResourceDownloadDialog.h" +#include "ui/dialogs/ResourceUpdateDialog.h" ShaderPackPage::ShaderPackPage(MinecraftInstance* instance, std::shared_ptr model, QWidget* parent) - : ExternalResourcesPage(instance, model, parent) + : ExternalResourcesPage(instance, model, parent), m_model(model) { - ui->actionDownloadItem->setText(tr("Download shaders")); - ui->actionDownloadItem->setToolTip(tr("Download shaders from online platforms")); + ui->actionDownloadItem->setText(tr("Download Packs")); + ui->actionDownloadItem->setToolTip(tr("Download shader packs from online mod platforms")); ui->actionDownloadItem->setEnabled(true); - connect(ui->actionDownloadItem, &QAction::triggered, this, &ShaderPackPage::downloadShaders); ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem); - ui->actionViewConfigs->setVisible(false); + connect(ui->actionDownloadItem, &QAction::triggered, this, &ShaderPackPage::downloadShaderPack); + + ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected shader packs (all shader packs if none are selected)")); + connect(ui->actionUpdateItem, &QAction::triggered, this, &ShaderPackPage::updateShaderPacks); + ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionUpdateItem); + + auto updateMenu = new QMenu(this); + + auto update = updateMenu->addAction(ui->actionUpdateItem->text()); + connect(update, &QAction::triggered, this, &ShaderPackPage::updateShaderPacks); + + updateMenu->addAction(ui->actionResetItemMetadata); + connect(ui->actionResetItemMetadata, &QAction::triggered, this, &ShaderPackPage::deleteShaderPackMetadata); + + ui->actionUpdateItem->setMenu(updateMenu); } -void ShaderPackPage::downloadShaders() +void ShaderPackPage::downloadShaderPack() { if (m_instance->typeName() != "Minecraft") return; // this is a null instance or a legacy instance - ResourceDownload::ShaderPackDownloadDialog mdownload(this, std::static_pointer_cast(m_model), m_instance); + ResourceDownload::ShaderPackDownloadDialog mdownload(this, m_model, m_instance); if (mdownload.exec()) { - auto tasks = new ConcurrentTask(this, "Download Shaders", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); + auto tasks = new ConcurrentTask(this, "Download Shader Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); connect(tasks, &Task::failed, [this, tasks](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); tasks->deleteLater(); @@ -93,3 +107,103 @@ void ShaderPackPage::downloadShaders() m_model->update(); } } + +void ShaderPackPage::updateShaderPacks() +{ + if (m_instance->typeName() != "Minecraft") + return; // this is a null instance or a legacy instance + + auto profile = static_cast(m_instance)->getPackProfile(); + if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) { + QMessageBox::critical(this, tr("Error"), tr("Shader pack updates are unavailable when metadata is disabled!")); + return; + } + if (m_instance != nullptr && m_instance->isRunning()) { + auto response = + CustomMessageBox::selectable(this, tr("Confirm Update"), + tr("Updating shader packs while the game is running may pack duplication and game crashes.\n" + "The old files may not be deleted as they are in use.\n" + "Are you sure you want to do this?"), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + + auto mods_list = m_model->selectedResources(selection); + bool use_all = mods_list.empty(); + if (use_all) + mods_list = m_model->allResources(); + + ResourceUpdateDialog update_dialog(this, m_instance, m_model, mods_list, false, false); + update_dialog.checkCandidates(); + + if (update_dialog.aborted()) { + CustomMessageBox::selectable(this, tr("Aborted"), tr("The shader pack updater was aborted!"), QMessageBox::Warning)->show(); + return; + } + if (update_dialog.noUpdates()) { + QString message{ tr("'%1' is up-to-date! :)").arg(mods_list.front()->name()) }; + if (mods_list.size() > 1) { + if (use_all) { + message = tr("All shader packs are up-to-date! :)"); + } else { + message = tr("All selected shader packs are up-to-date! :)"); + } + } + CustomMessageBox::selectable(this, tr("Update checker"), message)->exec(); + return; + } + + if (update_dialog.exec()) { + auto tasks = new ConcurrentTask(this, "Download Shader Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); + connect(tasks, &Task::failed, [this, tasks](QString reason) { + CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::aborted, [this, tasks]() { + CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::succeeded, [this, tasks]() { + QStringList warnings = tasks->warnings(); + if (warnings.count()) { + CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); + } + tasks->deleteLater(); + }); + + for (auto task : update_dialog.getTasks()) { + tasks->addTask(task); + } + + ProgressDialog loadDialog(this); + loadDialog.setSkipButton(true, tr("Abort")); + loadDialog.execWithTask(tasks); + + m_model->update(); + } +} + +void ShaderPackPage::deleteShaderPackMetadata() +{ + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + auto selectionCount = m_model->selectedShaderPacks(selection).length(); + if (selectionCount == 0) + return; + if (selectionCount > 1) { + auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), + tr("You are about to remove the metadata for %1 shader packs.\n" + "Are you sure?") + .arg(selectionCount), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } + + m_model->deleteMetadata(selection); +} diff --git a/launcher/ui/pages/instance/ShaderPackPage.h b/launcher/ui/pages/instance/ShaderPackPage.h index 7c43a3756..fadffe82d 100644 --- a/launcher/ui/pages/instance/ShaderPackPage.h +++ b/launcher/ui/pages/instance/ShaderPackPage.h @@ -53,5 +53,10 @@ class ShaderPackPage : public ExternalResourcesPage { bool shouldDisplay() const override { return true; } public slots: - void downloadShaders(); + void downloadShaderPack(); + void updateShaderPacks(); + void deleteShaderPackMetadata(); + + private: + std::shared_ptr m_model; }; diff --git a/launcher/ui/pages/instance/TexturePackPage.cpp b/launcher/ui/pages/instance/TexturePackPage.cpp index 7c8d7e061..75f8410bc 100644 --- a/launcher/ui/pages/instance/TexturePackPage.cpp +++ b/launcher/ui/pages/instance/TexturePackPage.cpp @@ -44,17 +44,31 @@ #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ProgressDialog.h" #include "ui/dialogs/ResourceDownloadDialog.h" +#include "ui/dialogs/ResourceUpdateDialog.h" TexturePackPage::TexturePackPage(MinecraftInstance* instance, std::shared_ptr model, QWidget* parent) - : ExternalResourcesPage(instance, model, parent) + : ExternalResourcesPage(instance, model, parent), m_model(model) { - ui->actionDownloadItem->setText(tr("Download packs")); - ui->actionDownloadItem->setToolTip(tr("Download texture packs from online platforms")); + ui->actionDownloadItem->setText(tr("Download Packs")); + ui->actionDownloadItem->setToolTip(tr("Download texture packs from online mod platforms")); ui->actionDownloadItem->setEnabled(true); - connect(ui->actionDownloadItem, &QAction::triggered, this, &TexturePackPage::downloadTPs); ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem); - ui->actionViewConfigs->setVisible(false); + connect(ui->actionDownloadItem, &QAction::triggered, this, &TexturePackPage::downloadTexturePacks); + + ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected texture packs (all texture packs if none are selected)")); + connect(ui->actionUpdateItem, &QAction::triggered, this, &TexturePackPage::updateTexturePacks); + ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionUpdateItem); + + auto updateMenu = new QMenu(this); + + auto update = updateMenu->addAction(ui->actionUpdateItem->text()); + connect(update, &QAction::triggered, this, &TexturePackPage::updateTexturePacks); + + updateMenu->addAction(ui->actionResetItemMetadata); + connect(ui->actionResetItemMetadata, &QAction::triggered, this, &TexturePackPage::deleteTexturePackMetadata); + + ui->actionUpdateItem->setMenu(updateMenu); } bool TexturePackPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) @@ -67,12 +81,12 @@ bool TexturePackPage::onSelectionChanged(const QModelIndex& current, [[maybe_unu return true; } -void TexturePackPage::downloadTPs() +void TexturePackPage::downloadTexturePacks() { if (m_instance->typeName() != "Minecraft") return; // this is a null instance or a legacy instance - ResourceDownload::TexturePackDownloadDialog mdownload(this, std::static_pointer_cast(m_model), m_instance); + ResourceDownload::TexturePackDownloadDialog mdownload(this, m_model, m_instance); if (mdownload.exec()) { auto tasks = new ConcurrentTask(this, "Download Texture Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); @@ -103,3 +117,103 @@ void TexturePackPage::downloadTPs() m_model->update(); } } + +void TexturePackPage::updateTexturePacks() +{ + if (m_instance->typeName() != "Minecraft") + return; // this is a null instance or a legacy instance + + auto profile = static_cast(m_instance)->getPackProfile(); + if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) { + QMessageBox::critical(this, tr("Error"), tr("Texture pack updates are unavailable when metadata is disabled!")); + return; + } + if (m_instance != nullptr && m_instance->isRunning()) { + auto response = + CustomMessageBox::selectable(this, tr("Confirm Update"), + tr("Updating texture packs while the game is running may cause pack duplication and game crashes.\n" + "The old files may not be deleted as they are in use.\n" + "Are you sure you want to do this?"), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + + auto mods_list = m_model->selectedResources(selection); + bool use_all = mods_list.empty(); + if (use_all) + mods_list = m_model->allResources(); + + ResourceUpdateDialog update_dialog(this, m_instance, m_model, mods_list, false, false); + update_dialog.checkCandidates(); + + if (update_dialog.aborted()) { + CustomMessageBox::selectable(this, tr("Aborted"), tr("The texture pack updater was aborted!"), QMessageBox::Warning)->show(); + return; + } + if (update_dialog.noUpdates()) { + QString message{ tr("'%1' is up-to-date! :)").arg(mods_list.front()->name()) }; + if (mods_list.size() > 1) { + if (use_all) { + message = tr("All texture packs are up-to-date! :)"); + } else { + message = tr("All selected texture packs are up-to-date! :)"); + } + } + CustomMessageBox::selectable(this, tr("Update checker"), message)->exec(); + return; + } + + if (update_dialog.exec()) { + auto tasks = new ConcurrentTask(this, "Download Texture Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); + connect(tasks, &Task::failed, [this, tasks](QString reason) { + CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::aborted, [this, tasks]() { + CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::succeeded, [this, tasks]() { + QStringList warnings = tasks->warnings(); + if (warnings.count()) { + CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); + } + tasks->deleteLater(); + }); + + for (auto task : update_dialog.getTasks()) { + tasks->addTask(task); + } + + ProgressDialog loadDialog(this); + loadDialog.setSkipButton(true, tr("Abort")); + loadDialog.execWithTask(tasks); + + m_model->update(); + } +} + +void TexturePackPage::deleteTexturePackMetadata() +{ + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + auto selectionCount = m_model->selectedTexturePacks(selection).length(); + if (selectionCount == 0) + return; + if (selectionCount > 1) { + auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), + tr("You are about to remove the metadata for %1 texture packs.\n" + "Are you sure?") + .arg(selectionCount), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } + + m_model->deleteMetadata(selection); +} diff --git a/launcher/ui/pages/instance/TexturePackPage.h b/launcher/ui/pages/instance/TexturePackPage.h index 9c4f24b70..e42613568 100644 --- a/launcher/ui/pages/instance/TexturePackPage.h +++ b/launcher/ui/pages/instance/TexturePackPage.h @@ -56,5 +56,10 @@ class TexturePackPage : public ExternalResourcesPage { public slots: bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override; - void downloadTPs(); + void downloadTexturePacks(); + void updateTexturePacks(); + void deleteTexturePackMetadata(); + + private: + std::shared_ptr m_model; };