From fdbd8d9d2b2e04cd68fd800882309b40c05aba2c Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Tue, 1 Nov 2022 22:45:15 +0100 Subject: refactor: remove old updater Signed-off-by: Sefa Eyeoglu --- launcher/ui/pages/instance/LogPage.cpp | 2 +- launcher/ui/pages/instance/ScreenshotsPage.h | 1 + launcher/ui/pages/instance/ServersPage.cpp | 1 + launcher/ui/pages/instance/WorldListPage.cpp | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/pages/instance/LogPage.cpp b/launcher/ui/pages/instance/LogPage.cpp index 31c3e925..9985f426 100644 --- a/launcher/ui/pages/instance/LogPage.cpp +++ b/launcher/ui/pages/instance/LogPage.cpp @@ -39,7 +39,7 @@ #include "Application.h" -#include +#include #include #include diff --git a/launcher/ui/pages/instance/ScreenshotsPage.h b/launcher/ui/pages/instance/ScreenshotsPage.h index c22706af..cdd53cc9 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.h +++ b/launcher/ui/pages/instance/ScreenshotsPage.h @@ -42,6 +42,7 @@ class QFileSystemModel; class QIdentityProxyModel; +class QItemSelection; namespace Ui { class ScreenshotsPage; diff --git a/launcher/ui/pages/instance/ServersPage.cpp b/launcher/ui/pages/instance/ServersPage.cpp index d64bcb76..a4f9f330 100644 --- a/launcher/ui/pages/instance/ServersPage.cpp +++ b/launcher/ui/pages/instance/ServersPage.cpp @@ -48,6 +48,7 @@ #include #include +#include static const int COLUMN_COUNT = 2; // 3 , TBD: latency and other nice things. diff --git a/launcher/ui/pages/instance/WorldListPage.cpp b/launcher/ui/pages/instance/WorldListPage.cpp index 85cc01ff..7819d077 100644 --- a/launcher/ui/pages/instance/WorldListPage.cpp +++ b/launcher/ui/pages/instance/WorldListPage.cpp @@ -43,9 +43,9 @@ #include #include #include +#include #include #include -#include #include #include "tools/MCEditTool.h" -- cgit From 127b094c4158f7a2315bb35cea05f5644a0db1c5 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Wed, 14 Dec 2022 15:02:04 +0000 Subject: Improve handling of destructive actions Signed-off-by: TheKodeToad --- launcher/FileSystem.cpp | 5 ++- launcher/FileSystem.h | 5 ++- launcher/minecraft/World.cpp | 7 +++- launcher/minecraft/mod/Resource.cpp | 4 ++ launcher/ui/GuiUtil.cpp | 29 ++++++++++++- launcher/ui/GuiUtil.h | 2 +- launcher/ui/MainWindow.cpp | 39 ++++++++++-------- .../ui/pages/instance/ExternalResourcesPage.cpp | 48 ++++++++++++++++++++-- launcher/ui/pages/instance/ExternalResourcesPage.h | 3 +- launcher/ui/pages/instance/LogPage.cpp | 30 ++++++-------- launcher/ui/pages/instance/ModFolderPage.cpp | 10 ++--- launcher/ui/pages/instance/ModFolderPage.h | 5 ++- launcher/ui/pages/instance/OtherLogsPage.cpp | 31 ++++++++++---- launcher/ui/pages/instance/ScreenshotsPage.cpp | 48 ++++++++++++++++++---- launcher/ui/pages/instance/ServersPage.cpp | 15 ++++++- launcher/ui/pages/instance/WorldListPage.cpp | 18 ++++---- launcher/ui/pages/instance/WorldListPage.ui | 2 +- 17 files changed, 218 insertions(+), 83 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 3e8e10a5..b3af4f4e 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad * * 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 @@ -226,7 +227,7 @@ bool deletePath(QString path) return err.value() == 0; } -bool trash(QString path, QString *pathInTrash = nullptr) +bool trash(QString path, QString *pathInTrash) { #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) return false; diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index ac893725..15233b66 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad * * 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 @@ -129,7 +130,7 @@ bool deletePath(QString path); /** * Trash a folder / file */ -bool trash(QString path, QString *pathInTrash); +bool trash(QString path, QString *pathInTrash = nullptr); QString PathCombine(const QString& path1, const QString& path2); QString PathCombine(const QString& path1, const QString& path2, const QString& path3); diff --git a/launcher/minecraft/World.cpp b/launcher/minecraft/World.cpp index 90fcf337..d310f8b9 100644 --- a/launcher/minecraft/World.cpp +++ b/launcher/minecraft/World.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad * * 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 @@ -545,6 +546,10 @@ bool World::replace(World &with) bool World::destroy() { if(!is_valid) return false; + + if (FS::trash(m_containerFile.filePath())) + return true; + if (m_containerFile.isDir()) { QDir d(m_containerFile.filePath()); diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index 0fbcfd7c..7c572d92 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -143,5 +143,9 @@ bool Resource::enable(EnableAction action) bool Resource::destroy() { m_type = ResourceType::UNKNOWN; + + if (FS::trash(m_file_info.filePath())) + return true; + return FS::deletePath(m_file_info.filePath()); } diff --git a/launcher/ui/GuiUtil.cpp b/launcher/ui/GuiUtil.cpp index 5a62e4d0..241354cb 100644 --- a/launcher/ui/GuiUtil.cpp +++ b/launcher/ui/GuiUtil.cpp @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Lenny McLennington * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad * * 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 @@ -49,11 +50,35 @@ #include #include -QString GuiUtil::uploadPaste(const QString &text, QWidget *parentWidget) +QString GuiUtil::uploadPaste(const QString &name, const QString &text, QWidget *parentWidget) { ProgressDialog dialog(parentWidget); auto pasteTypeSetting = static_cast(APPLICATION->settings()->get("PastebinType").toInt()); auto pasteCustomAPIBaseSetting = APPLICATION->settings()->get("PastebinCustomAPIBase").toString(); + + { + QUrl baseUrl; + if (pasteCustomAPIBaseSetting.isEmpty()) + baseUrl = PasteUpload::PasteTypes[pasteTypeSetting].defaultBase; + else + baseUrl = pasteCustomAPIBaseSetting; + + if (baseUrl.isValid()) { + auto response = CustomMessageBox::selectable(parentWidget, "Confirm Upload", + QObject::tr("About to upload: %1\n" + "Uploading to: %2\n" + "You should double-check for personal information.\n\n" + "Are you sure?") + .arg(name) + .arg(baseUrl.host()), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return "canceled"; + } + } + std::unique_ptr paste(new PasteUpload(parentWidget, text, pasteCustomAPIBaseSetting, pasteTypeSetting)); dialog.execWithTask(paste.get()); diff --git a/launcher/ui/GuiUtil.h b/launcher/ui/GuiUtil.h index 5e109383..bf93b3c5 100644 --- a/launcher/ui/GuiUtil.h +++ b/launcher/ui/GuiUtil.h @@ -4,7 +4,7 @@ namespace GuiUtil { -QString uploadPaste(const QString &text, QWidget *parentWidget); +QString uploadPaste(const QString &name, const QString &text, QWidget *parentWidget); void setClipboardText(const QString &text); QStringList BrowseForFiles(QString context, QString caption, QString filter, QString defaultPath, QWidget *parentWidget); QString BrowseForFile(QString context, QString caption, QString filter, QString defaultPath, QWidget *parentWidget); diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 2f1976cc..4ddef6d4 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad * * 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 @@ -490,7 +491,7 @@ public: if (!BuildConfig.BUG_TRACKER_URL.isEmpty()) { helpMenu->addAction(actionReportBug); } - + if(!BuildConfig.MATRIX_URL.isEmpty()) { helpMenu->addAction(actionMATRIX); } @@ -2093,21 +2094,23 @@ void MainWindow::on_actionDeleteInstance_triggered() auto id = m_selectedInstance->id(); - auto response = - CustomMessageBox::selectable(this, tr("CAREFUL!"), - tr("About to delete: %1\nThis may be permanent and will completely delete the instance.\n\nAre you sure?") - .arg(m_selectedInstance->name()), - QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) - ->exec(); + auto response = CustomMessageBox::selectable(this, tr("CAREFUL!"), + tr("About to delete: %1\n" + "This may be permanent and will completely delete the instance.\n\n" + "Are you sure?") + .arg(m_selectedInstance->name()), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); - if (response == QMessageBox::Yes) { - if (APPLICATION->instances()->trashInstance(id)) { - ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething()); - return; - } + if (response != QMessageBox::Yes) + return; - APPLICATION->instances()->deleteInstance(id); + if (APPLICATION->instances()->trashInstance(id)) { + ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething()); + return; } + + APPLICATION->instances()->deleteInstance(id); } void MainWindow::on_actionExportInstance_triggered() @@ -2252,7 +2255,7 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() } QString iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.png"); - + QFile iconFile(iconPath); if (!iconFile.open(QFile::WriteOnly)) { @@ -2261,7 +2264,7 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() } bool success = icon->icon().pixmap(64, 64).save(&iconFile, "PNG"); iconFile.close(); - + if (!success) { iconFile.remove(); @@ -2302,7 +2305,7 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() } QString iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.ico"); - + // part of fix for weird bug involving the window icon being replaced // dunno why it happens, but this 2-line fix seems to be enough, so w/e auto appIcon = APPLICATION->getThemedIcon("logo"); @@ -2325,7 +2328,7 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut.")); return; } - + if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()), QApplication::applicationFilePath(), { "--launch", m_selectedInstance->id() }, m_selectedInstance->name(), iconPath)) { diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index c66d1368..41ccd1db 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -1,4 +1,5 @@ #include "ExternalResourcesPage.h" +#include "ui/dialogs/CustomMessageBox.h" #include "ui_ExternalResourcesPage.h" #include "DesktopServices.h" @@ -128,7 +129,7 @@ bool ExternalResourcesPage::eventFilter(QObject* obj, QEvent* ev) { if (ev->type() != QEvent::KeyPress) return QWidget::eventFilter(obj, ev); - + QKeyEvent* keyEvent = static_cast(ev); if (obj == ui->treeView) return listFilter(keyEvent); @@ -140,7 +141,6 @@ void ExternalResourcesPage::addItem() { if (!m_controlsEnabled) return; - auto list = GuiUtil::BrowseForFiles( helpPage(), tr("Select %1", "Select whatever type of files the page contains. Example: 'Loader Mods'").arg(displayName()), @@ -157,8 +157,49 @@ void ExternalResourcesPage::removeItem() { if (!m_controlsEnabled) return; - + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()); + + int count = 0; + bool folder = false; + for (auto i : selection.indexes()) { + if (i.column() == 0) { + count++; + + // if a folder is selected, show the confirmation dialog + if (m_model->at(i.row()).fileinfo().isDir()) + folder = true; + } + } + + bool enough = count > 1; + + if (enough || folder) { + QString text; + if (enough) + text = tr("About to remove: %1 items\n" + "This may be permanent and they will be gone from the folder.\n\n" + "Are you sure?") + .arg(count); + else + text = tr("About to remove: %1 (folder)\n" + "This may be permanent and it will be gone from the parent folder.\n\n" + "Are you sure?") + .arg(m_model->at(selection.indexes().at(0).row()).fileinfo().fileName()); + + auto response = CustomMessageBox::selectable(this, tr("CAREFUL!"), text, QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, + QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } + + removeItems(selection); +} + +void ExternalResourcesPage::removeItems(const QItemSelection& selection) +{ m_model->deleteResources(selection.indexes()); } @@ -209,4 +250,3 @@ bool ExternalResourcesPage::onSelectionChanged(const QModelIndex& current, const return true; } - diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.h b/launcher/ui/pages/instance/ExternalResourcesPage.h index 2d1a5b51..d17fbb7f 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.h +++ b/launcher/ui/pages/instance/ExternalResourcesPage.h @@ -50,7 +50,8 @@ class ExternalResourcesPage : public QMainWindow, public BasePage { void filterTextChanged(const QString& newContents); virtual void addItem(); - virtual void removeItem(); + void removeItem(); + virtual void removeItems(const QItemSelection &selection); virtual void enableItem(); virtual void disableItem(); diff --git a/launcher/ui/pages/instance/LogPage.cpp b/launcher/ui/pages/instance/LogPage.cpp index 31c3e925..2a6504a2 100644 --- a/launcher/ui/pages/instance/LogPage.cpp +++ b/launcher/ui/pages/instance/LogPage.cpp @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad * * 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 @@ -277,28 +278,21 @@ void LogPage::on_btnPaste_clicked() //FIXME: turn this into a proper task and move the upload logic out of GuiUtil! m_model->append( MessageLevel::Launcher, - QString("%2: Log upload triggered at: %1").arg( - QDateTime::currentDateTime().toString(Qt::RFC2822Date), - BuildConfig.LAUNCHER_DISPLAYNAME + QString("Log upload triggered at: %1").arg( + QDateTime::currentDateTime().toString(Qt::RFC2822Date) ) ); - auto url = GuiUtil::uploadPaste(m_model->toPlainText(), this); - if(!url.isEmpty()) + auto url = GuiUtil::uploadPaste(tr("Minecraft Log"), m_model->toPlainText(), this); + if(url == "canceled") { - m_model->append( - MessageLevel::Launcher, - QString("%2: Log uploaded to: %1").arg( - url, - BuildConfig.LAUNCHER_DISPLAYNAME - ) - ); + m_model->append(MessageLevel::Error, QString("Log upload canceled")); } - else + else if(!url.isEmpty()) { - m_model->append( - MessageLevel::Error, - QString("%1: Log upload failed!").arg(BuildConfig.LAUNCHER_DISPLAYNAME) - ); + m_model->append(MessageLevel::Launcher, QString("Log uploaded to: %1").arg(url)); + } + else { + m_model->append(MessageLevel::Error, QString("Log upload failed!")); } } diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 0a2e6155..627e71e5 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad * * 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 @@ -139,13 +140,8 @@ bool ModFolderPage::onSelectionChanged(const QModelIndex& current, const QModelI return true; } -void ModFolderPage::removeItem() +void ModFolderPage::removeItems(const QItemSelection &selection) { - - if (!m_controlsEnabled) - return; - - auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()); m_model->deleteMods(selection.indexes()); } diff --git a/launcher/ui/pages/instance/ModFolderPage.h b/launcher/ui/pages/instance/ModFolderPage.h index f20adf34..ff58b38a 100644 --- a/launcher/ui/pages/instance/ModFolderPage.h +++ b/launcher/ui/pages/instance/ModFolderPage.h @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad * * 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 @@ -59,7 +60,7 @@ class ModFolderPage : public ExternalResourcesPage { private slots: void runningStateChanged(bool running); - void removeItem() override; + void removeItems(const QItemSelection &selection) override; void installMods(); void updateMods(); diff --git a/launcher/ui/pages/instance/OtherLogsPage.cpp b/launcher/ui/pages/instance/OtherLogsPage.cpp index 0c1939c6..ad444e6b 100644 --- a/launcher/ui/pages/instance/OtherLogsPage.cpp +++ b/launcher/ui/pages/instance/OtherLogsPage.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield + * Copyright (C) 2022 TheKodeToad * * 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 @@ -204,7 +205,7 @@ void OtherLogsPage::on_btnReload_clicked() void OtherLogsPage::on_btnPaste_clicked() { - GuiUtil::uploadPaste(ui->text->toPlainText(), this); + GuiUtil::uploadPaste(m_currentFile, ui->text->toPlainText(), this); } void OtherLogsPage::on_btnCopy_clicked() @@ -219,13 +220,21 @@ void OtherLogsPage::on_btnDelete_clicked() setControlsEnabled(false); return; } - if (QMessageBox::question(this, tr("Delete"), - tr("Do you really want to delete %1?").arg(m_currentFile), - QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) - { + if (QMessageBox::question(this, tr("CAREFUL!"), + tr("About to delete: %1\n" + "This may be permanent and it will be gone from the logs folder.\n\n" + "Are you sure?") + .arg(m_currentFile), + QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) { return; } QFile file(FS::PathCombine(m_path, m_currentFile)); + + if (FS::trash(file.fileName())) + { + return; + } + if (!file.remove()) { QMessageBox::critical(this, tr("Error"), tr("Unable to delete %1: %2") @@ -243,15 +252,15 @@ void OtherLogsPage::on_btnClean_clicked() return; } QMessageBox *messageBox = new QMessageBox(this); - messageBox->setWindowTitle(tr("Clean up")); + messageBox->setWindowTitle(tr("CAREFUL!")); if(toDelete.size() > 5) { - messageBox->setText(tr("Do you really want to delete all log files?")); + messageBox->setText(tr("Are you sure you want to delete all log files?")); messageBox->setDetailedText(toDelete.join('\n')); } else { - messageBox->setText(tr("Do you really want to delete these files?\n%1").arg(toDelete.join('\n'))); + messageBox->setText(tr("Are you sure you want to delete all these files?\n%1").arg(toDelete.join('\n'))); } messageBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); messageBox->setDefaultButton(QMessageBox::Ok); @@ -267,6 +276,10 @@ void OtherLogsPage::on_btnClean_clicked() for(auto item: toDelete) { QFile file(FS::PathCombine(m_path, item)); + if (FS::trash(file.fileName())) + { + continue; + } if (!file.remove()) { failed.push_back(item); diff --git a/launcher/ui/pages/instance/ScreenshotsPage.cpp b/launcher/ui/pages/instance/ScreenshotsPage.cpp index 0092aef3..fff21670 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.cpp +++ b/launcher/ui/pages/instance/ScreenshotsPage.cpp @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad * * 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 @@ -379,6 +380,24 @@ void ScreenshotsPage::on_actionUpload_triggered() if (selection.isEmpty()) return; + + QString text; + if (selection.size() > 1) + text = tr("About to upload: %1 screenshots\n\n" + "Are you sure?") + .arg(selection.size()); + else + text = + tr("About to upload the selected screenshot.\n\n" + "Are you sure?"); + + auto response = CustomMessageBox::selectable(this, "Confirm Upload", text, QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, + QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + QList uploaded; auto job = NetJob::Ptr(new NetJob("Screenshot Upload", APPLICATION->network())); if(selection.size() < 2) @@ -491,17 +510,32 @@ void ScreenshotsPage::on_actionCopy_File_s_triggered() void ScreenshotsPage::on_actionDelete_triggered() { - auto mbox = CustomMessageBox::selectable( - this, tr("Are you sure?"), tr("This will delete all selected screenshots."), - QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No); - std::unique_ptr box(mbox); + auto selected = ui->listView->selectionModel()->selectedIndexes(); + + int count = ui->listView->selectionModel()->selectedRows().size(); + QString text; + if (count > 1) + text = tr("About to delete: %1 screenshots\n" + "This may be permanent and they will be gone from the folder.\n\n" + "Are you sure?") + .arg(count); + else + text = tr("About to delete the selected screenshot.\n" + "This may be permanent and it will be gone from the folder.\n\n" + "Are you sure?") + .arg(count); - if (box->exec() != QMessageBox::Yes) + auto response = + CustomMessageBox::selectable(this, tr("CAREFUL!"), text, QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No)->exec(); + + if (response != QMessageBox::Yes) return; - auto selected = ui->listView->selectionModel()->selectedIndexes(); for (auto item : selected) { + if (FS::trash(m_model->filePath(item))) + continue; + m_model->remove(item); } } diff --git a/launcher/ui/pages/instance/ServersPage.cpp b/launcher/ui/pages/instance/ServersPage.cpp index a625e20b..c636b236 100644 --- a/launcher/ui/pages/instance/ServersPage.cpp +++ b/launcher/ui/pages/instance/ServersPage.cpp @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad * * 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 @@ -35,6 +36,7 @@ */ #include "ServersPage.h" +#include "ui/dialogs/CustomMessageBox.h" #include "ui_ServersPage.h" #include @@ -799,6 +801,17 @@ void ServersPage::on_actionAdd_triggered() void ServersPage::on_actionRemove_triggered() { + auto response = CustomMessageBox::selectable(this, tr("CAREFUL!"), + tr("About to remove: %1\n" + "This is permanent and the server will be gone from your list forever (A LONG TIME).\n\n" + "Are you sure?") + .arg(m_model->at(currentServer)->m_name), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + m_model->removeRow(currentServer); } diff --git a/launcher/ui/pages/instance/WorldListPage.cpp b/launcher/ui/pages/instance/WorldListPage.cpp index 93458ce4..74cb5a05 100644 --- a/launcher/ui/pages/instance/WorldListPage.cpp +++ b/launcher/ui/pages/instance/WorldListPage.cpp @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad * * 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 @@ -35,6 +36,7 @@ */ #include "WorldListPage.h" +#include "ui/dialogs/CustomMessageBox.h" #include "ui_WorldListPage.h" #include "minecraft/WorldList.h" @@ -192,12 +194,14 @@ void WorldListPage::on_actionRemove_triggered() if(!proxiedIndex.isValid()) return; - auto result = QMessageBox::question(this, - tr("Are you sure?"), - tr("This will remove the selected world permenantly.\n" - "The world will be gone forever (A LONG TIME).\n" - "\n" - "Do you want to continue?")); + auto result = CustomMessageBox::selectable(this, tr("CAREFUL!"), + tr("About to delete: %1\n" + "The world may be gone forever (A LONG TIME).\n\n" + "Are you sure?") + .arg(m_worlds->allWorlds().at(proxiedIndex.row()).name()), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + if(result != QMessageBox::Yes) { return; diff --git a/launcher/ui/pages/instance/WorldListPage.ui b/launcher/ui/pages/instance/WorldListPage.ui index 7c68bfae..d74dd079 100644 --- a/launcher/ui/pages/instance/WorldListPage.ui +++ b/launcher/ui/pages/instance/WorldListPage.ui @@ -109,7 +109,7 @@ - Remove + Delete -- cgit From ee003cd9ee433a073393bdd47bd20d6d8cb38ca2 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Wed, 14 Dec 2022 15:36:42 +0000 Subject: Add confirmation on customised components Signed-off-by: TheKodeToad --- launcher/ui/pages/instance/VersionPage.cpp | 41 +++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 6 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index c8a65f10..413b2f85 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -318,13 +318,29 @@ void VersionPage::on_actionReload_triggered() void VersionPage::on_actionRemove_triggered() { - if (ui->packageView->currentIndex().isValid()) + if (!ui->packageView->currentIndex().isValid()) { - // FIXME: use actual model, not reloading. - if (!m_profile->remove(ui->packageView->currentIndex().row())) - { - QMessageBox::critical(this, tr("Error"), tr("Couldn't remove file")); - } + return; + } + int index = ui->packageView->currentIndex().row(); + auto component = m_profile->getComponent(index); + if (component->isCustom()) + { + auto response = CustomMessageBox::selectable(this, tr("CAREFUL!"), + tr("About to remove: %1\n" + "This is permanent and will completely remove the custom component.\n\n" + "Are you sure?") + .arg(component->getName()), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } + // FIXME: use actual model, not reloading. + if (!m_profile->remove(index)) + { + QMessageBox::critical(this, tr("Error"), tr("Couldn't remove file")); } updateButtons(); reloadPackProfile(); @@ -707,6 +723,19 @@ void VersionPage::on_actionRevert_triggered() { return; } + auto component = m_profile->getComponent(version); + + auto response = CustomMessageBox::selectable(this, tr("CAREFUL!"), + tr("About to revert: %1\n" + "This is permanent and will completely revert your customizations.\n\n" + "Are you sure?") + .arg(component->getName()), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + if(!m_profile->revertToBase(version)) { // TODO: some error box here -- cgit From cbe5af235ca2fc990efa0a2db9e4951f127f0131 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 17 Dec 2022 09:26:06 +0000 Subject: Make requested changes Signed-off-by: TheKodeToad --- launcher/ui/GuiUtil.cpp | 5 ++- launcher/ui/MainWindow.cpp | 2 +- .../ui/pages/instance/ExternalResourcesPage.cpp | 37 +++++++++++----------- launcher/ui/pages/instance/OtherLogsPage.cpp | 4 +-- launcher/ui/pages/instance/ScreenshotsPage.cpp | 2 +- launcher/ui/pages/instance/ServersPage.cpp | 2 +- launcher/ui/pages/instance/VersionPage.cpp | 4 +-- launcher/ui/pages/instance/WorldListPage.cpp | 2 +- 8 files changed, 29 insertions(+), 29 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/GuiUtil.cpp b/launcher/ui/GuiUtil.cpp index 241354cb..6a22ec2f 100644 --- a/launcher/ui/GuiUtil.cpp +++ b/launcher/ui/GuiUtil.cpp @@ -64,13 +64,12 @@ QString GuiUtil::uploadPaste(const QString &name, const QString &text, QWidget * baseUrl = pasteCustomAPIBaseSetting; if (baseUrl.isValid()) { - auto response = CustomMessageBox::selectable(parentWidget, "Confirm Upload", + auto response = CustomMessageBox::selectable(parentWidget, QObject::tr("Confirm Upload"), QObject::tr("About to upload: %1\n" "Uploading to: %2\n" "You should double-check for personal information.\n\n" "Are you sure?") - .arg(name) - .arg(baseUrl.host()), + .arg(name, baseUrl.host()), QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ->exec(); diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 4ddef6d4..7442b955 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -2094,7 +2094,7 @@ void MainWindow::on_actionDeleteInstance_triggered() auto id = m_selectedInstance->id(); - auto response = CustomMessageBox::selectable(this, tr("CAREFUL!"), + auto response = CustomMessageBox::selectable(this, tr("Confirm Deletion"), tr("About to delete: %1\n" "This may be permanent and will completely delete the instance.\n\n" "Are you sure?") diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index 41ccd1db..6f1abbff 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -162,7 +162,7 @@ void ExternalResourcesPage::removeItem() int count = 0; bool folder = false; - for (auto i : selection.indexes()) { + for (auto& i : selection.indexes()) { if (i.column() == 0) { count++; @@ -172,23 +172,24 @@ void ExternalResourcesPage::removeItem() } } - bool enough = count > 1; - - if (enough || folder) { - QString text; - if (enough) - text = tr("About to remove: %1 items\n" - "This may be permanent and they will be gone from the folder.\n\n" - "Are you sure?") - .arg(count); - else - text = tr("About to remove: %1 (folder)\n" - "This may be permanent and it will be gone from the parent folder.\n\n" - "Are you sure?") - .arg(m_model->at(selection.indexes().at(0).row()).fileinfo().fileName()); - - auto response = CustomMessageBox::selectable(this, tr("CAREFUL!"), text, QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, - QMessageBox::No) + QString text; + bool multiple = count > 1; + + if (multiple) { + text = tr("About to remove: %1 items\n" + "This may be permanent and they will be gone from the folder.\n\n" + "Are you sure?") + .arg(count); + } else if (folder) { + text = tr("About to remove: %1 (folder)\n" + "This may be permanent and it will be gone from the parent folder.\n\n" + "Are you sure?") + .arg(m_model->at(selection.indexes().at(0).row()).fileinfo().fileName()); + } + + if (!text.isEmpty()) { + auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), text, QMessageBox::Warning, + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ->exec(); if (response != QMessageBox::Yes) diff --git a/launcher/ui/pages/instance/OtherLogsPage.cpp b/launcher/ui/pages/instance/OtherLogsPage.cpp index ad444e6b..1be2a3f8 100644 --- a/launcher/ui/pages/instance/OtherLogsPage.cpp +++ b/launcher/ui/pages/instance/OtherLogsPage.cpp @@ -220,7 +220,7 @@ void OtherLogsPage::on_btnDelete_clicked() setControlsEnabled(false); return; } - if (QMessageBox::question(this, tr("CAREFUL!"), + if (QMessageBox::question(this, tr("Confirm Deletion"), tr("About to delete: %1\n" "This may be permanent and it will be gone from the logs folder.\n\n" "Are you sure?") @@ -252,7 +252,7 @@ void OtherLogsPage::on_btnClean_clicked() return; } QMessageBox *messageBox = new QMessageBox(this); - messageBox->setWindowTitle(tr("CAREFUL!")); + messageBox->setWindowTitle(tr("Confirm Cleanup")); if(toDelete.size() > 5) { messageBox->setText(tr("Are you sure you want to delete all log files?")); diff --git a/launcher/ui/pages/instance/ScreenshotsPage.cpp b/launcher/ui/pages/instance/ScreenshotsPage.cpp index fff21670..4b756766 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.cpp +++ b/launcher/ui/pages/instance/ScreenshotsPage.cpp @@ -526,7 +526,7 @@ void ScreenshotsPage::on_actionDelete_triggered() .arg(count); auto response = - CustomMessageBox::selectable(this, tr("CAREFUL!"), text, QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No)->exec(); + CustomMessageBox::selectable(this, tr("Confirm Deletion"), text, QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No)->exec(); if (response != QMessageBox::Yes) return; diff --git a/launcher/ui/pages/instance/ServersPage.cpp b/launcher/ui/pages/instance/ServersPage.cpp index c636b236..6925ffb4 100644 --- a/launcher/ui/pages/instance/ServersPage.cpp +++ b/launcher/ui/pages/instance/ServersPage.cpp @@ -801,7 +801,7 @@ void ServersPage::on_actionAdd_triggered() void ServersPage::on_actionRemove_triggered() { - auto response = CustomMessageBox::selectable(this, tr("CAREFUL!"), + auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), tr("About to remove: %1\n" "This is permanent and the server will be gone from your list forever (A LONG TIME).\n\n" "Are you sure?") diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index 413b2f85..08ab8641 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -326,7 +326,7 @@ void VersionPage::on_actionRemove_triggered() auto component = m_profile->getComponent(index); if (component->isCustom()) { - auto response = CustomMessageBox::selectable(this, tr("CAREFUL!"), + auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), tr("About to remove: %1\n" "This is permanent and will completely remove the custom component.\n\n" "Are you sure?") @@ -725,7 +725,7 @@ void VersionPage::on_actionRevert_triggered() } auto component = m_profile->getComponent(version); - auto response = CustomMessageBox::selectable(this, tr("CAREFUL!"), + auto response = CustomMessageBox::selectable(this, tr("Confirm Reversion"), tr("About to revert: %1\n" "This is permanent and will completely revert your customizations.\n\n" "Are you sure?") diff --git a/launcher/ui/pages/instance/WorldListPage.cpp b/launcher/ui/pages/instance/WorldListPage.cpp index 74cb5a05..c98f1e5a 100644 --- a/launcher/ui/pages/instance/WorldListPage.cpp +++ b/launcher/ui/pages/instance/WorldListPage.cpp @@ -194,7 +194,7 @@ void WorldListPage::on_actionRemove_triggered() if(!proxiedIndex.isValid()) return; - auto result = CustomMessageBox::selectable(this, tr("CAREFUL!"), + auto result = CustomMessageBox::selectable(this, tr("Confirm Deletion"), tr("About to delete: %1\n" "The world may be gone forever (A LONG TIME).\n\n" "Are you sure?") -- cgit From bf04becc9e05f147ca595868c9a51da14d1c0c34 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 26 Dec 2022 14:33:50 +0000 Subject: About to -> you are about to You're is used in some other places but im lazy Signed-off-by: TheKodeToad --- launcher/ui/GuiUtil.cpp | 3 +-- launcher/ui/MainWindow.cpp | 2 +- launcher/ui/pages/instance/ExternalResourcesPage.cpp | 4 ++-- launcher/ui/pages/instance/OtherLogsPage.cpp | 2 +- launcher/ui/pages/instance/ScreenshotsPage.cpp | 8 ++++---- launcher/ui/pages/instance/ServersPage.cpp | 2 +- launcher/ui/pages/instance/VersionPage.cpp | 4 ++-- launcher/ui/pages/instance/WorldListPage.cpp | 2 +- 8 files changed, 13 insertions(+), 14 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/GuiUtil.cpp b/launcher/ui/GuiUtil.cpp index 6a22ec2f..855ab400 100644 --- a/launcher/ui/GuiUtil.cpp +++ b/launcher/ui/GuiUtil.cpp @@ -65,8 +65,7 @@ QString GuiUtil::uploadPaste(const QString &name, const QString &text, QWidget * if (baseUrl.isValid()) { auto response = CustomMessageBox::selectable(parentWidget, QObject::tr("Confirm Upload"), - QObject::tr("About to upload: %1\n" - "Uploading to: %2\n" + QObject::tr("You are about to upload \"%1\" to %2.\n" "You should double-check for personal information.\n\n" "Are you sure?") .arg(name, baseUrl.host()), diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 7442b955..c8a1fddc 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -2095,7 +2095,7 @@ void MainWindow::on_actionDeleteInstance_triggered() auto id = m_selectedInstance->id(); auto response = CustomMessageBox::selectable(this, tr("Confirm Deletion"), - tr("About to delete: %1\n" + tr("You are about to delete \"%1\".\n" "This may be permanent and will completely delete the instance.\n\n" "Are you sure?") .arg(m_selectedInstance->name()), diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index 6f1abbff..1115ddc3 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -176,12 +176,12 @@ void ExternalResourcesPage::removeItem() bool multiple = count > 1; if (multiple) { - text = tr("About to remove: %1 items\n" + text = tr("You are about to remove %1 items.\n" "This may be permanent and they will be gone from the folder.\n\n" "Are you sure?") .arg(count); } else if (folder) { - text = tr("About to remove: %1 (folder)\n" + text = tr("You are about to remove the folder \"%1\".\n" "This may be permanent and it will be gone from the parent folder.\n\n" "Are you sure?") .arg(m_model->at(selection.indexes().at(0).row()).fileinfo().fileName()); diff --git a/launcher/ui/pages/instance/OtherLogsPage.cpp b/launcher/ui/pages/instance/OtherLogsPage.cpp index 1be2a3f8..bbdd7324 100644 --- a/launcher/ui/pages/instance/OtherLogsPage.cpp +++ b/launcher/ui/pages/instance/OtherLogsPage.cpp @@ -221,7 +221,7 @@ void OtherLogsPage::on_btnDelete_clicked() return; } if (QMessageBox::question(this, tr("Confirm Deletion"), - tr("About to delete: %1\n" + tr("You are about to delete \"%1\".\n" "This may be permanent and it will be gone from the logs folder.\n\n" "Are you sure?") .arg(m_currentFile), diff --git a/launcher/ui/pages/instance/ScreenshotsPage.cpp b/launcher/ui/pages/instance/ScreenshotsPage.cpp index 4b756766..ca368d3b 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.cpp +++ b/launcher/ui/pages/instance/ScreenshotsPage.cpp @@ -383,12 +383,12 @@ void ScreenshotsPage::on_actionUpload_triggered() QString text; if (selection.size() > 1) - text = tr("About to upload: %1 screenshots\n\n" + text = tr("You are about to upload %1 screenshots.\n\n" "Are you sure?") .arg(selection.size()); else text = - tr("About to upload the selected screenshot.\n\n" + tr("You are about to upload the selected screenshot.\n\n" "Are you sure?"); auto response = CustomMessageBox::selectable(this, "Confirm Upload", text, QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, @@ -515,12 +515,12 @@ void ScreenshotsPage::on_actionDelete_triggered() int count = ui->listView->selectionModel()->selectedRows().size(); QString text; if (count > 1) - text = tr("About to delete: %1 screenshots\n" + text = tr("You are about to delete %1 screenshots.\n" "This may be permanent and they will be gone from the folder.\n\n" "Are you sure?") .arg(count); else - text = tr("About to delete the selected screenshot.\n" + text = tr("You are about to delete the selected screenshot.\n" "This may be permanent and it will be gone from the folder.\n\n" "Are you sure?") .arg(count); diff --git a/launcher/ui/pages/instance/ServersPage.cpp b/launcher/ui/pages/instance/ServersPage.cpp index 6925ffb4..6f8591a1 100644 --- a/launcher/ui/pages/instance/ServersPage.cpp +++ b/launcher/ui/pages/instance/ServersPage.cpp @@ -802,7 +802,7 @@ void ServersPage::on_actionAdd_triggered() void ServersPage::on_actionRemove_triggered() { auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), - tr("About to remove: %1\n" + tr("You are about to remove \"%1\".\n" "This is permanent and the server will be gone from your list forever (A LONG TIME).\n\n" "Are you sure?") .arg(m_model->at(currentServer)->m_name), diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index 08ab8641..d200652a 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -327,7 +327,7 @@ void VersionPage::on_actionRemove_triggered() if (component->isCustom()) { auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), - tr("About to remove: %1\n" + tr("You are about to remove \"%1\".\n" "This is permanent and will completely remove the custom component.\n\n" "Are you sure?") .arg(component->getName()), @@ -726,7 +726,7 @@ void VersionPage::on_actionRevert_triggered() auto component = m_profile->getComponent(version); auto response = CustomMessageBox::selectable(this, tr("Confirm Reversion"), - tr("About to revert: %1\n" + tr("You are about to revert \"%1\".\n" "This is permanent and will completely revert your customizations.\n\n" "Are you sure?") .arg(component->getName()), diff --git a/launcher/ui/pages/instance/WorldListPage.cpp b/launcher/ui/pages/instance/WorldListPage.cpp index c98f1e5a..0020c461 100644 --- a/launcher/ui/pages/instance/WorldListPage.cpp +++ b/launcher/ui/pages/instance/WorldListPage.cpp @@ -195,7 +195,7 @@ void WorldListPage::on_actionRemove_triggered() return; auto result = CustomMessageBox::selectable(this, tr("Confirm Deletion"), - tr("About to delete: %1\n" + tr("You are about to delete \"%1\".\n" "The world may be gone forever (A LONG TIME).\n\n" "Are you sure?") .arg(m_worlds->allWorlds().at(proxiedIndex.row()).name()), -- cgit From 434f639b0c9af355703d6c64cfe5bbe9a28d0b9b Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 26 Dec 2022 14:58:02 +0000 Subject: Use optional instead of hardcoded cancelled string Signed-off-by: TheKodeToad --- launcher/ui/GuiUtil.cpp | 7 ++++--- launcher/ui/GuiUtil.h | 3 ++- launcher/ui/pages/instance/LogPage.cpp | 11 ++++++----- 3 files changed, 12 insertions(+), 9 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/GuiUtil.cpp b/launcher/ui/GuiUtil.cpp index 855ab400..29467c3c 100644 --- a/launcher/ui/GuiUtil.cpp +++ b/launcher/ui/GuiUtil.cpp @@ -50,7 +50,7 @@ #include #include -QString GuiUtil::uploadPaste(const QString &name, const QString &text, QWidget *parentWidget) +std::optional GuiUtil::uploadPaste(const QString &name, const QString &text, QWidget *parentWidget) { ProgressDialog dialog(parentWidget); auto pasteTypeSetting = static_cast(APPLICATION->settings()->get("PastebinType").toInt()); @@ -63,7 +63,8 @@ QString GuiUtil::uploadPaste(const QString &name, const QString &text, QWidget * else baseUrl = pasteCustomAPIBaseSetting; - if (baseUrl.isValid()) { + if (baseUrl.isValid()) + { auto response = CustomMessageBox::selectable(parentWidget, QObject::tr("Confirm Upload"), QObject::tr("You are about to upload \"%1\" to %2.\n" "You should double-check for personal information.\n\n" @@ -73,7 +74,7 @@ QString GuiUtil::uploadPaste(const QString &name, const QString &text, QWidget * ->exec(); if (response != QMessageBox::Yes) - return "canceled"; + return {}; } } diff --git a/launcher/ui/GuiUtil.h b/launcher/ui/GuiUtil.h index bf93b3c5..96ebd9a2 100644 --- a/launcher/ui/GuiUtil.h +++ b/launcher/ui/GuiUtil.h @@ -1,10 +1,11 @@ #pragma once #include +#include namespace GuiUtil { -QString uploadPaste(const QString &name, const QString &text, QWidget *parentWidget); +std::optional uploadPaste(const QString &name, const QString &text, QWidget *parentWidget); void setClipboardText(const QString &text); QStringList BrowseForFiles(QString context, QString caption, QString filter, QString defaultPath, QWidget *parentWidget); QString BrowseForFile(QString context, QString caption, QString filter, QString defaultPath, QWidget *parentWidget); diff --git a/launcher/ui/pages/instance/LogPage.cpp b/launcher/ui/pages/instance/LogPage.cpp index 2a6504a2..8f9e569e 100644 --- a/launcher/ui/pages/instance/LogPage.cpp +++ b/launcher/ui/pages/instance/LogPage.cpp @@ -283,17 +283,18 @@ void LogPage::on_btnPaste_clicked() ) ); auto url = GuiUtil::uploadPaste(tr("Minecraft Log"), m_model->toPlainText(), this); - if(url == "canceled") + if(!url.has_value()) { m_model->append(MessageLevel::Error, QString("Log upload canceled")); } - else if(!url.isEmpty()) + else if (url->isNull()) { - m_model->append(MessageLevel::Launcher, QString("Log uploaded to: %1").arg(url)); - } - else { m_model->append(MessageLevel::Error, QString("Log upload failed!")); } + else + { + m_model->append(MessageLevel::Launcher, QString("Log uploaded to: %1").arg(url.value())); + } } void LogPage::on_btnCopy_clicked() -- cgit From 7e2d78bab555ac17ff79711d41d59b14b226998f Mon Sep 17 00:00:00 2001 From: Aaron <10217842+byteduck@users.noreply.github.com> Date: Tue, 27 Dec 2022 15:00:15 -0700 Subject: Allow selecting a default account to use with an instance Signed-off-by: Aaron <10217842+byteduck@users.noreply.github.com> --- launcher/LaunchController.cpp | 14 +++- launcher/minecraft/MinecraftInstance.cpp | 4 ++ .../ui/pages/instance/InstanceSettingsPage.cpp | 83 +++++++++++++++++++++- launcher/ui/pages/instance/InstanceSettingsPage.h | 13 ++-- launcher/ui/pages/instance/InstanceSettingsPage.ui | 42 +++++++++++ 5 files changed, 150 insertions(+), 6 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index 11e3de15..5dd551ee 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -112,7 +112,19 @@ void LaunchController::decideAccount() } } - m_accountToUse = accounts->defaultAccount(); + // Select the account to use. If the instance has a specific account set, that will be used. Otherwise, the default account will be used + auto instanceAccountId = m_instance->settings()->get("InstanceAccountId").toString(); + auto instanceAccountIndex = accounts->findAccountByProfileId(instanceAccountId); + if (instanceAccountIndex == -1) + { + m_accountToUse = accounts->defaultAccount(); + } + else + { + m_accountToUse = accounts->at(instanceAccountIndex); + } + + if (!m_accountToUse) { // If no default account is set, ask the user which one to use. diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 1d37224a..d0a5ed31 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -192,6 +192,10 @@ void MinecraftInstance::loadSpecificSettings() m_settings->registerSetting("JoinServerOnLaunch", false); m_settings->registerSetting("JoinServerOnLaunchAddress", ""); + // Use account for instance, this does not have a global override + m_settings->registerSetting("UseAccountForInstance", false); + m_settings->registerSetting("InstanceAccountId", ""); + qDebug() << "Instance-type specific settings were loaded!"; setSpecificSettingsLoaded(true); diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index af2ba7c8..18f5f2ac 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -48,18 +48,23 @@ #include "JavaCommon.h" #include "Application.h" +#include "minecraft/auth/AccountList.h" #include "java/JavaInstallList.h" #include "java/JavaUtils.h" #include "FileSystem.h" - InstanceSettingsPage::InstanceSettingsPage(BaseInstance *inst, QWidget *parent) : QWidget(parent), ui(new Ui::InstanceSettingsPage), m_instance(inst) { m_settings = inst->settings(); ui->setupUi(this); + accountMenu = new QMenu(this); + // Use undocumented property... https://stackoverflow.com/questions/7121718/create-a-scrollbar-in-a-submenu-qt + accountMenu->setStyleSheet("QMenu { menu-scrollable: 1; }"); + ui->instanceAccountSelector->setMenu(accountMenu); + connect(ui->openGlobalJavaSettingsButton, &QCommandLinkButton::clicked, this, &InstanceSettingsPage::globalSettingsButtonClicked); connect(APPLICATION, &Application::globalSettingsAboutToOpen, this, &InstanceSettingsPage::applySettings); connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceSettingsPage::loadSettings); @@ -75,6 +80,7 @@ bool InstanceSettingsPage::shouldDisplay() const InstanceSettingsPage::~InstanceSettingsPage() { delete ui; + delete accountMenu; } void InstanceSettingsPage::globalSettingsButtonClicked(bool) @@ -275,6 +281,14 @@ void InstanceSettingsPage::applySettings() m_settings->reset("JoinServerOnLaunchAddress"); } + // Use an account for this instance + bool useAccountForInstance = ui->instanceAccountGroupBox->isChecked(); + m_settings->set("UseAccountForInstance", useAccountForInstance); + if (!useAccountForInstance) + { + m_settings->reset("InstanceAccountId"); + } + // FIXME: This should probably be called by a signal instead m_instance->updateRuntimeContext(); } @@ -372,6 +386,9 @@ void InstanceSettingsPage::loadSettings() ui->serverJoinGroupBox->setChecked(m_settings->get("JoinServerOnLaunch").toBool()); ui->serverJoinAddress->setText(m_settings->get("JoinServerOnLaunchAddress").toString()); + + ui->instanceAccountGroupBox->setChecked(m_settings->get("UseAccountForInstance").toBool()); + updateAccountsMenu(); } void InstanceSettingsPage::on_javaDetectBtn_clicked() @@ -437,6 +454,70 @@ void InstanceSettingsPage::on_javaTestBtn_clicked() checker->run(); } +void InstanceSettingsPage::updateAccountsMenu() +{ + accountMenu->clear(); + + auto accounts = APPLICATION->accounts(); + int accountIndex = accounts->findAccountByProfileId(m_settings->get("InstanceAccountId").toString()); + + if (accountIndex != -1) + { + auto account = accounts->at(accountIndex); + ui->instanceAccountSelector->setText(account->profileName()); + ui->instanceAccountSelector->setIcon(account->getFace()); + } else { + ui->instanceAccountSelector->setText(tr("No default account")); + ui->instanceAccountSelector->setIcon(APPLICATION->getThemedIcon("noaccount")); + } + + for (int i = 0; i < accounts->count(); i++) + { + MinecraftAccountPtr account = accounts->at(i); + QAction *action = new QAction(account->profileName(), this); + action->setData(i); + action->setCheckable(true); + if (accountIndex == i) + { + action->setChecked(true); + } + + auto face = account->getFace(); + if(!face.isNull()) { + action->setIcon(face); + } + else { + action->setIcon(APPLICATION->getThemedIcon("noaccount")); + } + + accountMenu->addAction(action); + connect(action, SIGNAL(triggered(bool)), SLOT(changeInstanceAccount())); + } +} + +void InstanceSettingsPage::changeInstanceAccount() +{ + QAction *sAction = (QAction *)sender(); + + // Profile's associated Mojang username + if (sAction->data().type() != QVariant::Type::Int) + return; + + QVariant data = sAction->data(); + bool valid = false; + int index = data.toInt(&valid); + if(!valid) { + index = -1; + } + auto accounts = APPLICATION->accounts(); + auto account = accounts->at(index); + + m_settings->set("InstanceAccountId", account->profileId()); + + ui->instanceAccountSelector->setText(account->profileName()); + ui->instanceAccountSelector->setIcon(account->getFace()); +} + void InstanceSettingsPage::on_maxMemSpinBox_valueChanged(int i) { updateThresholds(); diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.h b/launcher/ui/pages/instance/InstanceSettingsPage.h index 7450188d..b80db99a 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.h +++ b/launcher/ui/pages/instance/InstanceSettingsPage.h @@ -37,12 +37,13 @@ #include -#include "java/JavaChecker.h" -#include "BaseInstance.h" #include -#include "ui/pages/BasePage.h" -#include "JavaCommon.h" +#include #include "Application.h" +#include "BaseInstance.h" +#include "JavaCommon.h" +#include "java/JavaChecker.h" +#include "ui/pages/BasePage.h" class JavaChecker; namespace Ui @@ -92,9 +93,13 @@ private slots: void globalSettingsButtonClicked(bool checked); + void updateAccountsMenu(); + void changeInstanceAccount(); + private: Ui::InstanceSettingsPage *ui; BaseInstance *m_instance; SettingsObjectPtr m_settings; unique_qobject_ptr checker; + QMenu *accountMenu = nullptr; }; diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index b064367d..ef86ed7e 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -608,6 +608,48 @@ + + + + Set a default account to use with this instance + + + true + + + false + + + + + + + + + 0 + 0 + + + + Account: + + + + + + + QToolButton::InstantPopup + + + Qt::ToolButtonTextBesideIcon + + + + + + + + -- cgit From cba3d68063bea28acb1eae870aee7e0b2a57b2be Mon Sep 17 00:00:00 2001 From: Aaron <10217842+byteduck@users.noreply.github.com> Date: Tue, 27 Dec 2022 15:39:35 -0700 Subject: Fix conflicting layout name in InstanceSettingsPage Signed-off-by: Aaron <10217842+byteduck@users.noreply.github.com> --- launcher/ui/pages/instance/InstanceSettingsPage.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index ef86ed7e..a88fdb54 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -619,7 +619,7 @@ false - + -- cgit From 021e6c02d781706da82ca8ee5c77f716b5c210b9 Mon Sep 17 00:00:00 2001 From: Aaron Sonin <10217842+byteduck@users.noreply.github.com> Date: Mon, 2 Jan 2023 10:49:16 -0700 Subject: Replace unecessary type check with assertion in InstanceSettingsPage Co-authored-by: flow Signed-off-by: Aaron Sonin <10217842+byteduck@users.noreply.github.com> Signed-off-by: Aaron <10217842+byteduck@users.noreply.github.com> --- launcher/ui/pages/instance/InstanceSettingsPage.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 18f5f2ac..1c3989f6 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -500,8 +500,7 @@ void InstanceSettingsPage::changeInstanceAccount() QAction *sAction = (QAction *)sender(); // Profile's associated Mojang username - if (sAction->data().type() != QVariant::Type::Int) - return; + Q_ASSERT(sAction->data().type() == QVariant::Type::Int); QVariant data = sAction->data(); bool valid = false; -- cgit From e18652387835e61d6b006d88fe856c63e24a098f Mon Sep 17 00:00:00 2001 From: Aaron Sonin <10217842+byteduck@users.noreply.github.com> Date: Mon, 2 Jan 2023 10:50:24 -0700 Subject: Add null check for face in instance account settings selector Co-authored-by: flow Signed-off-by: Aaron Sonin <10217842+byteduck@users.noreply.github.com> Signed-off-by: Aaron <10217842+byteduck@users.noreply.github.com> --- launcher/ui/pages/instance/InstanceSettingsPage.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 1c3989f6..a870c01b 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -514,7 +514,11 @@ void InstanceSettingsPage::changeInstanceAccount() m_settings->set("InstanceAccountId", account->profileId()); ui->instanceAccountSelector->setText(account->profileName()); - ui->instanceAccountSelector->setIcon(account->getFace()); + if (auto face = account->getFace(); !face.isNull()) { + ui->instanceAccountSelector->setIcon(face); + } else { + ui->instanceAccountSelector->setIcon(APPLICATION->getThemedIcon("noaccount")); + } } void InstanceSettingsPage::on_maxMemSpinBox_valueChanged(int i) -- cgit From 9b8add196123f13b18b6a8c878da95921103b6f7 Mon Sep 17 00:00:00 2001 From: Aaron Sonin <10217842+byteduck@users.noreply.github.com> Date: Mon, 2 Jan 2023 10:50:59 -0700 Subject: Properly connect signal in instance settings for account selector Co-authored-by: flow Signed-off-by: Aaron Sonin <10217842+byteduck@users.noreply.github.com> Signed-off-by: Aaron <10217842+byteduck@users.noreply.github.com> --- launcher/ui/pages/instance/InstanceSettingsPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index a870c01b..6a823d5f 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -491,7 +491,7 @@ void InstanceSettingsPage::updateAccountsMenu() } accountMenu->addAction(action); - connect(action, SIGNAL(triggered(bool)), SLOT(changeInstanceAccount())); + connect(action, SIGNAL(triggered(bool)), this, SLOT(changeInstanceAccount())); } } -- cgit From eefb259ddff3de641457b6312bc125796e7661c9 Mon Sep 17 00:00:00 2001 From: Aaron Sonin <10217842+byteduck@users.noreply.github.com> Date: Mon, 2 Jan 2023 10:51:17 -0700 Subject: Remove unecessary delete in InstanceSettingsPage destructor Co-authored-by: flow Signed-off-by: Aaron Sonin <10217842+byteduck@users.noreply.github.com> Signed-off-by: Aaron <10217842+byteduck@users.noreply.github.com> --- launcher/ui/pages/instance/InstanceSettingsPage.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 6a823d5f..4b7c0f83 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -80,7 +80,6 @@ bool InstanceSettingsPage::shouldDisplay() const InstanceSettingsPage::~InstanceSettingsPage() { delete ui; - delete accountMenu; } void InstanceSettingsPage::globalSettingsButtonClicked(bool) -- cgit From ba81ad1ac3cff48b973ee167802a5d6398eac990 Mon Sep 17 00:00:00 2001 From: Aaron <10217842+byteduck@users.noreply.github.com> Date: Mon, 2 Jan 2023 11:16:09 -0700 Subject: Reword instance-specific account settings, apply clang-format Signed-off-by: Aaron <10217842+byteduck@users.noreply.github.com> --- launcher/LaunchController.cpp | 8 ++--- .../ui/pages/instance/InstanceSettingsPage.cpp | 39 +++++++++------------- launcher/ui/pages/instance/InstanceSettingsPage.ui | 2 +- 3 files changed, 19 insertions(+), 30 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index 5dd551ee..9741fd95 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -115,16 +115,12 @@ void LaunchController::decideAccount() // Select the account to use. If the instance has a specific account set, that will be used. Otherwise, the default account will be used auto instanceAccountId = m_instance->settings()->get("InstanceAccountId").toString(); auto instanceAccountIndex = accounts->findAccountByProfileId(instanceAccountId); - if (instanceAccountIndex == -1) - { + if (instanceAccountIndex == -1) { m_accountToUse = accounts->defaultAccount(); - } - else - { + } else { m_accountToUse = accounts->at(instanceAccountIndex); } - if (!m_accountToUse) { // If no default account is set, ask the user which one to use. diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 4b7c0f83..24b261ba 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -283,8 +283,7 @@ void InstanceSettingsPage::applySettings() // Use an account for this instance bool useAccountForInstance = ui->instanceAccountGroupBox->isChecked(); m_settings->set("UseAccountForInstance", useAccountForInstance); - if (!useAccountForInstance) - { + if (!useAccountForInstance) { m_settings->reset("InstanceAccountId"); } @@ -459,33 +458,33 @@ void InstanceSettingsPage::updateAccountsMenu() auto accounts = APPLICATION->accounts(); int accountIndex = accounts->findAccountByProfileId(m_settings->get("InstanceAccountId").toString()); + MinecraftAccountPtr defaultAccount = accounts->defaultAccount(); - if (accountIndex != -1) - { - auto account = accounts->at(accountIndex); - ui->instanceAccountSelector->setText(account->profileName()); - ui->instanceAccountSelector->setIcon(account->getFace()); + if (accountIndex != -1 && accounts->at(accountIndex)) { + defaultAccount = accounts->at(accountIndex); + } + + if (defaultAccount) { + ui->instanceAccountSelector->setText(defaultAccount->profileName()); + ui->instanceAccountSelector->setIcon(defaultAccount->getFace()); } else { ui->instanceAccountSelector->setText(tr("No default account")); ui->instanceAccountSelector->setIcon(APPLICATION->getThemedIcon("noaccount")); } - for (int i = 0; i < accounts->count(); i++) - { + for (int i = 0; i < accounts->count(); i++) { MinecraftAccountPtr account = accounts->at(i); - QAction *action = new QAction(account->profileName(), this); + QAction* action = new QAction(account->profileName(), this); action->setData(i); action->setCheckable(true); - if (accountIndex == i) - { + if (accountIndex == i) { action->setChecked(true); } auto face = account->getFace(); - if(!face.isNull()) { + if (!face.isNull()) { action->setIcon(face); - } - else { + } else { action->setIcon(APPLICATION->getThemedIcon("noaccount")); } @@ -496,20 +495,14 @@ void InstanceSettingsPage::updateAccountsMenu() void InstanceSettingsPage::changeInstanceAccount() { - QAction *sAction = (QAction *)sender(); + QAction* sAction = (QAction*)sender(); - // Profile's associated Mojang username Q_ASSERT(sAction->data().type() == QVariant::Type::Int); QVariant data = sAction->data(); - bool valid = false; - int index = data.toInt(&valid); - if(!valid) { - index = -1; - } + int index = data.toInt(); auto accounts = APPLICATION->accounts(); auto account = accounts->at(index); - m_settings->set("InstanceAccountId", account->profileId()); ui->instanceAccountSelector->setText(account->profileName()); diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index a88fdb54..1b986184 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -611,7 +611,7 @@ - Set a default account to use with this instance + Override default account true -- cgit From fff52cb24773c369bd02af2710611290a0a2380f Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 11 Jan 2023 13:50:57 +0100 Subject: feat: add button to import component JSONs Signed-off-by: Sefa Eyeoglu --- launcher/minecraft/PackProfile.cpp | 32 ++++++++++++++++++++++++++++++ launcher/minecraft/PackProfile.h | 4 ++++ launcher/ui/pages/instance/VersionPage.cpp | 11 ++++++++++ launcher/ui/pages/instance/VersionPage.h | 1 + launcher/ui/pages/instance/VersionPage.ui | 13 ++++++++++-- 5 files changed, 59 insertions(+), 2 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 43fa3f8d..2028b236 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -49,6 +49,7 @@ #include "minecraft/OneSixVersionFormat.h" #include "FileSystem.h" #include "minecraft/MinecraftInstance.h" +#include "minecraft/ProfileUtils.h" #include "Json.h" #include "PackProfile.h" @@ -737,6 +738,11 @@ void PackProfile::installCustomJar(QString selectedFile) installCustomJar_internal(selectedFile); } +void PackProfile::installComponents(QStringList selectedFiles) +{ + installComponents_internal(selectedFiles); +} + void PackProfile::installAgents(QStringList selectedFiles) { installAgents_internal(selectedFiles); @@ -939,6 +945,32 @@ bool PackProfile::installCustomJar_internal(QString filepath) return true; } +bool PackProfile::installComponents_internal(QStringList filepaths) +{ + const QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches"); + if (!FS::ensureFolderPathExists(patchDir)) + return false; + + for (const QString& source : filepaths) { + const QFileInfo sourceInfo(source); + + auto versionFile = ProfileUtils::parseJsonFile(sourceInfo, false); + const QString target = FS::PathCombine(patchDir, versionFile->uid + ".json"); + + if (!QFile::copy(source, target)) + { + return false; + } + + appendComponent(new Component(this, versionFile->uid, versionFile)); + } + + scheduleSave(); + invalidateLaunchProfile(); + + return true; +} + bool PackProfile::installAgents_internal(QStringList filepaths) { // FIXME code duplication diff --git a/launcher/minecraft/PackProfile.h b/launcher/minecraft/PackProfile.h index 2330cca1..35af9a56 100644 --- a/launcher/minecraft/PackProfile.h +++ b/launcher/minecraft/PackProfile.h @@ -86,6 +86,9 @@ public: /// install a jar/zip as a replacement for the main jar void installCustomJar(QString selectedFile); + /// install MMC/Prism component files + void installComponents(QStringList selectedFiles); + /// install Java agent files void installAgents(QStringList selectedFiles); @@ -171,6 +174,7 @@ private: bool load(); bool installJarMods_internal(QStringList filepaths); bool installCustomJar_internal(QString filepath); + bool installComponents_internal(QStringList filepaths); bool installAgents_internal(QStringList filepaths); bool removeComponent_internal(ComponentPtr patch); diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index d200652a..07a97813 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -282,6 +282,7 @@ void VersionPage::updateButtons(int row) ui->actionRevert->setEnabled(controlsEnabled && patch && patch->isRevertible()); ui->actionDownload_All->setEnabled(controlsEnabled); ui->actionAdd_Empty->setEnabled(controlsEnabled); + ui->actionImport_Components->setEnabled(controlsEnabled); ui->actionReload->setEnabled(controlsEnabled); ui->actionInstall_mods->setEnabled(controlsEnabled); ui->actionReplace_Minecraft_jar->setEnabled(controlsEnabled); @@ -375,6 +376,16 @@ void VersionPage::on_actionReplace_Minecraft_jar_triggered() updateButtons(); } +void VersionPage::on_actionImport_Components_triggered() +{ + QStringList list = GuiUtil::BrowseForFiles("component", tr("Select components"), tr("Components (*.json)"), + APPLICATION->settings()->get("CentralModsDir").toString(), this->parentWidget()); + + if (!list.isEmpty()) + m_profile->installComponents(list); + + updateButtons(); +} void VersionPage::on_actionAdd_Agents_triggered() { diff --git a/launcher/ui/pages/instance/VersionPage.h b/launcher/ui/pages/instance/VersionPage.h index 166f36bb..a56f016d 100644 --- a/launcher/ui/pages/instance/VersionPage.h +++ b/launcher/ui/pages/instance/VersionPage.h @@ -86,6 +86,7 @@ private slots: void on_actionMove_down_triggered(); void on_actionAdd_to_Minecraft_jar_triggered(); void on_actionReplace_Minecraft_jar_triggered(); + void on_actionImport_Components_triggered(); void on_actionAdd_Agents_triggered(); void on_actionRevert_triggered(); void on_actionEdit_triggered(); diff --git a/launcher/ui/pages/instance/VersionPage.ui b/launcher/ui/pages/instance/VersionPage.ui index 4cd50885..4777eafe 100644 --- a/launcher/ui/pages/instance/VersionPage.ui +++ b/launcher/ui/pages/instance/VersionPage.ui @@ -108,6 +108,7 @@ + @@ -226,10 +227,10 @@ - Add Agents + Add Agents - Add Java agents. + Add Java agents. @@ -272,6 +273,14 @@ Open the instance's local libraries folder. + + + Import Components + + + Import existing component JSON files. + + -- cgit From 24a4bd3a1c33702946b88a3d8017268fb8134210 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Fri, 6 Jan 2023 15:26:26 -0500 Subject: refactor: replace hoedown markdown parser with cmark Signed-off-by: Joshua Goins --- launcher/CMakeLists.txt | 4 +- launcher/HoeDown.h | 76 ---------------------- launcher/Markdown.h | 34 ++++++++++ launcher/ui/dialogs/AboutDialog.cpp | 6 +- launcher/ui/dialogs/ModUpdateDialog.cpp | 11 +--- launcher/ui/dialogs/UpdateDialog.cpp | 5 +- launcher/ui/pages/instance/ManagedPackPage.cpp | 6 +- launcher/ui/pages/modplatform/ModPage.cpp | 11 +--- launcher/ui/pages/modplatform/ftb/FtbPage.cpp | 5 +- .../ui/pages/modplatform/modrinth/ModrinthPage.cpp | 6 +- 10 files changed, 50 insertions(+), 114 deletions(-) delete mode 100644 launcher/HoeDown.h create mode 100644 launcher/Markdown.h (limited to 'launcher/ui/pages/instance') diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 6ca88ec6..7dc744aa 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -617,7 +617,7 @@ SET(LAUNCHER_SOURCES DesktopServices.cpp VersionProxyModel.h VersionProxyModel.cpp - HoeDown.h + Markdown.h # Super secret! KonamiCode.h @@ -1043,7 +1043,7 @@ target_link_libraries(Launcher_logic ) target_link_libraries(Launcher_logic QuaZip::QuaZip - hoedown + cmark LocalPeer Launcher_rainbow ) diff --git a/launcher/HoeDown.h b/launcher/HoeDown.h deleted file mode 100644 index cb62de6c..00000000 --- a/launcher/HoeDown.h +++ /dev/null @@ -1,76 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -#include -#include -#include -#include - -/** - * hoedown wrapper, because dealing with resource lifetime in C is stupid - */ -class HoeDown -{ -public: - class buffer - { - public: - buffer(size_t unit = 4096) - { - buf = hoedown_buffer_new(unit); - } - ~buffer() - { - hoedown_buffer_free(buf); - } - const char * cstr() - { - return hoedown_buffer_cstr(buf); - } - void put(QByteArray input) - { - hoedown_buffer_put(buf, reinterpret_cast(input.data()), input.size()); - } - const uint8_t * data() const - { - return buf->data; - } - size_t size() const - { - return buf->size; - } - hoedown_buffer * buf; - } ib, ob; - HoeDown() - { - renderer = hoedown_html_renderer_new((hoedown_html_flags) 0,0); - document = hoedown_document_new(renderer, (hoedown_extensions) 0, 8); - } - ~HoeDown() - { - hoedown_document_free(document); - hoedown_html_renderer_free(renderer); - } - QString process(QByteArray input) - { - ib.put(input); - hoedown_document_render(document, ob.buf, ib.data(), ib.size()); - return ob.cstr(); - } -private: - hoedown_document * document; - hoedown_renderer * renderer; -}; diff --git a/launcher/Markdown.h b/launcher/Markdown.h new file mode 100644 index 00000000..f115dd57 --- /dev/null +++ b/launcher/Markdown.h @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Joshua Goins + * + * 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 + +static QString markdownToHTML(const QString& markdown) +{ + const QByteArray markdownData = markdown.toUtf8(); + char* buffer = cmark_markdown_to_html(markdownData.constData(), markdownData.length(), CMARK_OPT_NOBREAKS | CMARK_OPT_UNSAFE); + + QString htmlStr(buffer); + + free(buffer); + + return htmlStr; +} \ No newline at end of file diff --git a/launcher/ui/dialogs/AboutDialog.cpp b/launcher/ui/dialogs/AboutDialog.cpp index a36e4a3d..76e3d8ed 100644 --- a/launcher/ui/dialogs/AboutDialog.cpp +++ b/launcher/ui/dialogs/AboutDialog.cpp @@ -39,12 +39,11 @@ #include #include "Application.h" #include "BuildConfig.h" +#include "Markdown.h" #include #include -#include "HoeDown.h" - namespace { QString getLink(QString link, QString name) { return QString("<%2>").arg(link).arg(name); @@ -114,10 +113,9 @@ QString getCreditsHtml() QString getLicenseHtml() { - HoeDown hoedown; QFile dataFile(":/documents/COPYING.md"); dataFile.open(QIODevice::ReadOnly); - QString output = hoedown.process(dataFile.readAll()); + QString output = markdownToHTML(dataFile.readAll()); return output; } diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index cedd4a96..2704243e 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -7,6 +7,7 @@ #include "FileSystem.h" #include "Json.h" +#include "Markdown.h" #include "tasks/ConcurrentTask.h" @@ -17,7 +18,6 @@ #include "modplatform/flame/FlameCheckUpdate.h" #include "modplatform/modrinth/ModrinthCheckUpdate.h" -#include #include #include @@ -369,14 +369,7 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info) QString text = info.changelog; switch (info.provider) { case ModPlatform::Provider::MODRINTH: { - HoeDown h; - // HoeDown bug?: \n aren't converted to
- text = h.process(info.changelog.toUtf8()); - - // Don't convert if there's an HTML tag right after (Qt rendering weirdness) - text.remove(QRegularExpression("(\n+)(?=<)")); - text.replace('\n', "
"); - + text = markdownToHTML(info.changelog.toUtf8()); break; } default: diff --git a/launcher/ui/dialogs/UpdateDialog.cpp b/launcher/ui/dialogs/UpdateDialog.cpp index 9e82531a..349d768f 100644 --- a/launcher/ui/dialogs/UpdateDialog.cpp +++ b/launcher/ui/dialogs/UpdateDialog.cpp @@ -41,7 +41,7 @@ #include #include "BuildConfig.h" -#include "HoeDown.h" +#include "Markdown.h" UpdateDialog::UpdateDialog(bool hasUpdate, QWidget *parent) : QDialog(parent), ui(new Ui::UpdateDialog) { @@ -89,8 +89,7 @@ void UpdateDialog::loadChangelog() QString reprocessMarkdown(QByteArray markdown) { - HoeDown hoedown; - QString output = hoedown.process(markdown); + QString output = markdownToHTML(markdown); // HACK: easier than customizing hoedown output.replace(QRegularExpression("GH-([0-9]+)"), "GH-\\1"); diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index 4de80468..8d56d894 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -9,14 +9,13 @@ #include #include -#include - #include "Application.h" #include "BuildConfig.h" #include "InstanceImportTask.h" #include "InstanceList.h" #include "InstanceTask.h" #include "Json.h" +#include "Markdown.h" #include "modplatform/modrinth/ModrinthPackManifest.h" @@ -263,8 +262,7 @@ void ModrinthManagedPackPage::suggestVersion() auto index = ui->versionsComboBox->currentIndex(); auto version = m_pack.versions.at(index); - HoeDown md_parser; - ui->changelogTextBrowser->setHtml(md_parser.process(version.changelog.toUtf8())); + ui->changelogTextBrowser->setHtml(markdownToHTML(version.changelog.toUtf8())); ManagedPackPage::suggestVersion(); } diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 75be25b2..0f30689e 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -43,13 +43,11 @@ #include #include -#include - #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "ui/dialogs/ModDownloadDialog.h" #include "ui/widgets/ProjectItem.h" - +#include "Markdown.h" ModPage::ModPage(ModDownloadDialog* dialog, BaseInstance* instance, ModAPI* api) : QWidget(dialog) @@ -427,11 +425,6 @@ void ModPage::updateUi() text += "
"; - HoeDown h; - - // hoedown bug: it doesn't handle markdown surrounded by block tags (like center, div) so strip them - current.extraData.body.remove(QRegularExpression("<[^>]*(?:center|div)\\W*>")); - - ui->packDescription->setHtml(text + (current.extraData.body.isEmpty() ? current.description : h.process(current.extraData.body.toUtf8()))); + ui->packDescription->setHtml(text + (current.extraData.body.isEmpty() ? current.description : markdownToHTML(current.extraData.body))); ui->packDescription->flush(); } diff --git a/launcher/ui/pages/modplatform/ftb/FtbPage.cpp b/launcher/ui/pages/modplatform/ftb/FtbPage.cpp index b08f3bc4..7d59a6ae 100644 --- a/launcher/ui/pages/modplatform/ftb/FtbPage.cpp +++ b/launcher/ui/pages/modplatform/ftb/FtbPage.cpp @@ -43,7 +43,7 @@ #include "ui/dialogs/NewInstanceDialog.h" #include "modplatform/modpacksch/FTBPackInstallTask.h" -#include "HoeDown.h" +#include "Markdown.h" FtbPage::FtbPage(NewInstanceDialog* dialog, QWidget *parent) : QWidget(parent), ui(new Ui::FtbPage), dialog(dialog) @@ -175,8 +175,7 @@ void FtbPage::onSelectionChanged(QModelIndex first, QModelIndex second) selected = filterModel->data(first, Qt::UserRole).value(); - HoeDown hoedown; - QString output = hoedown.process(selected.description.toUtf8()); + QString output = markdownToHTML(selected.description.toUtf8()); ui->packDescription->setHtml(output); // reverse foreach, so that the newest versions are first diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index 8ab2ad1d..0bb11d83 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -42,11 +42,10 @@ #include "BuildConfig.h" #include "InstanceImportTask.h" #include "Json.h" +#include "Markdown.h" #include "ui/widgets/ProjectItem.h" -#include - #include #include #include @@ -280,8 +279,7 @@ void ModrinthPage::updateUI() text += "
"; - HoeDown h; - text += h.process(current.extra.body.toUtf8()); + text += markdownToHTML(current.extra.body.toUtf8()); ui->packDescription->setHtml(text + current.description); ui->packDescription->flush(); -- cgit From 160dd09fc2788fea17c8e9e332c2877586640971 Mon Sep 17 00:00:00 2001 From: Aaron <10217842+byteduck@users.noreply.github.com> Date: Thu, 12 Jan 2023 20:03:31 -0800 Subject: Fix instance account selector face for offline accounts --- .../ui/pages/instance/InstanceSettingsPage.cpp | 26 ++++++++++------------ launcher/ui/pages/instance/InstanceSettingsPage.h | 1 + 2 files changed, 13 insertions(+), 14 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 24b261ba..4b4c73dc 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -466,7 +466,7 @@ void InstanceSettingsPage::updateAccountsMenu() if (defaultAccount) { ui->instanceAccountSelector->setText(defaultAccount->profileName()); - ui->instanceAccountSelector->setIcon(defaultAccount->getFace()); + ui->instanceAccountSelector->setIcon(getFaceForAccount(defaultAccount)); } else { ui->instanceAccountSelector->setText(tr("No default account")); ui->instanceAccountSelector->setIcon(APPLICATION->getThemedIcon("noaccount")); @@ -480,19 +480,21 @@ void InstanceSettingsPage::updateAccountsMenu() if (accountIndex == i) { action->setChecked(true); } - - auto face = account->getFace(); - if (!face.isNull()) { - action->setIcon(face); - } else { - action->setIcon(APPLICATION->getThemedIcon("noaccount")); - } - + action->setIcon(getFaceForAccount(account)); accountMenu->addAction(action); connect(action, SIGNAL(triggered(bool)), this, SLOT(changeInstanceAccount())); } } +QIcon InstanceSettingsPage::getFaceForAccount(MinecraftAccountPtr account) +{ + if (auto face = account->getFace(); !face.isNull()) { + return face; + } + + return APPLICATION->getThemedIcon("noaccount"); +} + void InstanceSettingsPage::changeInstanceAccount() { QAction* sAction = (QAction*)sender(); @@ -506,11 +508,7 @@ void InstanceSettingsPage::changeInstanceAccount() m_settings->set("InstanceAccountId", account->profileId()); ui->instanceAccountSelector->setText(account->profileName()); - if (auto face = account->getFace(); !face.isNull()) { - ui->instanceAccountSelector->setIcon(face); - } else { - ui->instanceAccountSelector->setIcon(APPLICATION->getThemedIcon("noaccount")); - } + ui->instanceAccountSelector->setIcon(getFaceForAccount(account)); } void InstanceSettingsPage::on_maxMemSpinBox_valueChanged(int i) diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.h b/launcher/ui/pages/instance/InstanceSettingsPage.h index b80db99a..cb6fbae0 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.h +++ b/launcher/ui/pages/instance/InstanceSettingsPage.h @@ -94,6 +94,7 @@ private slots: void globalSettingsButtonClicked(bool checked); void updateAccountsMenu(); + QIcon getFaceForAccount(MinecraftAccountPtr account); void changeInstanceAccount(); private: -- cgit From 6a1807995390b2a2cbe074ee1f47d3791e0e3f10 Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 25 Nov 2022 09:23:46 -0300 Subject: refactor: generalize mod models and APIs to resources Firstly, this abstract away behavior in the mod download models that can also be applied to other types of resources into a superclass, allowing other resource types to be implemented without so much code duplication. For that, this also generalizes the APIs used (currently, ModrinthAPI and FlameAPI) to be able to make requests to other types of resources. It also does a general cleanup of both of those. In particular, this makes use of std::optional instead of invalid values for errors and, well, optional values :p This is a squash of some commits that were becoming too interlaced together to be cleanly separated. Signed-off-by: flow --- launcher/CMakeLists.txt | 37 ++- launcher/ModDownloadTask.cpp | 72 ----- launcher/ModDownloadTask.h | 57 ---- launcher/ResourceDownloadTask.cpp | 80 +++++ launcher/ResourceDownloadTask.h | 57 ++++ launcher/minecraft/PackProfile.cpp | 28 +- launcher/minecraft/PackProfile.h | 4 +- launcher/modplatform/CheckUpdateTask.h | 14 +- launcher/modplatform/EnsureMetadataTask.cpp | 14 +- launcher/modplatform/EnsureMetadataTask.h | 6 +- launcher/modplatform/ModAPI.h | 118 ------- launcher/modplatform/ModIndex.cpp | 24 +- launcher/modplatform/ModIndex.h | 19 +- launcher/modplatform/ResourceAPI.h | 149 +++++++++ launcher/modplatform/flame/FlameAPI.cpp | 16 +- launcher/modplatform/flame/FlameAPI.h | 99 +++--- launcher/modplatform/flame/FlameCheckUpdate.cpp | 11 +- launcher/modplatform/flame/FlameCheckUpdate.h | 2 +- .../flame/FlameInstanceCreationTask.cpp | 4 +- .../modplatform/flame/FlameInstanceCreationTask.h | 2 +- launcher/modplatform/flame/FlameModIndex.cpp | 4 +- launcher/modplatform/helpers/HashUtils.cpp | 16 +- launcher/modplatform/helpers/HashUtils.h | 10 +- launcher/modplatform/helpers/NetworkModAPI.cpp | 97 ------ launcher/modplatform/helpers/NetworkModAPI.h | 17 - .../modplatform/helpers/NetworkResourceAPI.cpp | 124 +++++++ launcher/modplatform/helpers/NetworkResourceAPI.h | 18 ++ launcher/modplatform/modrinth/ModrinthAPI.cpp | 36 ++- launcher/modplatform/modrinth/ModrinthAPI.h | 106 +++--- .../modplatform/modrinth/ModrinthCheckUpdate.cpp | 25 +- .../modplatform/modrinth/ModrinthCheckUpdate.h | 2 +- .../modplatform/modrinth/ModrinthPackIndex.cpp | 4 +- launcher/modplatform/packwiz/Packwiz.cpp | 8 +- launcher/modplatform/packwiz/Packwiz.h | 2 +- launcher/net/NetAction.h | 4 - launcher/net/NetJob.cpp | 5 +- launcher/ui/dialogs/BlockedModsDialog.cpp | 2 +- launcher/ui/dialogs/ChooseProviderDialog.cpp | 6 +- launcher/ui/dialogs/ChooseProviderDialog.h | 6 +- launcher/ui/dialogs/ModDownloadDialog.cpp | 165 +--------- launcher/ui/dialogs/ModDownloadDialog.h | 43 +-- launcher/ui/dialogs/ModUpdateDialog.cpp | 44 +-- launcher/ui/dialogs/ModUpdateDialog.h | 8 +- launcher/ui/dialogs/ResourceDownloadDialog.cpp | 152 +++++++++ launcher/ui/dialogs/ResourceDownloadDialog.h | 55 ++++ launcher/ui/dialogs/ReviewMessageBox.cpp | 4 +- launcher/ui/dialogs/ReviewMessageBox.h | 8 +- launcher/ui/pages/instance/ModFolderPage.cpp | 6 +- launcher/ui/pages/instance/ResourcePackPage.h | 1 + launcher/ui/pages/modplatform/ModModel.cpp | 274 +++------------- launcher/ui/pages/modplatform/ModModel.h | 64 +--- launcher/ui/pages/modplatform/ModPage.cpp | 357 +++------------------ launcher/ui/pages/modplatform/ModPage.h | 78 +---- launcher/ui/pages/modplatform/ModPage.ui | 118 ------- launcher/ui/pages/modplatform/ResourceModel.cpp | 258 +++++++++++++++ launcher/ui/pages/modplatform/ResourceModel.h | 101 ++++++ launcher/ui/pages/modplatform/ResourcePage.cpp | 347 ++++++++++++++++++++ launcher/ui/pages/modplatform/ResourcePage.h | 95 ++++++ launcher/ui/pages/modplatform/ResourcePage.ui | 118 +++++++ .../ui/pages/modplatform/flame/FlameModModel.cpp | 31 -- .../ui/pages/modplatform/flame/FlameModModel.h | 26 -- .../ui/pages/modplatform/flame/FlameModPage.cpp | 97 ------ launcher/ui/pages/modplatform/flame/FlameModPage.h | 70 ---- .../modplatform/flame/FlameResourceModels.cpp | 31 ++ .../pages/modplatform/flame/FlameResourceModels.h | 26 ++ .../pages/modplatform/flame/FlameResourcePages.cpp | 97 ++++++ .../pages/modplatform/flame/FlameResourcePages.h | 71 ++++ .../modplatform/modrinth/ModrinthModModel.cpp | 48 --- .../pages/modplatform/modrinth/ModrinthModModel.h | 44 --- .../pages/modplatform/modrinth/ModrinthModPage.cpp | 84 ----- .../pages/modplatform/modrinth/ModrinthModPage.h | 66 ---- .../modrinth/ModrinthResourceModels.cpp | 53 +++ .../modplatform/modrinth/ModrinthResourceModels.h | 49 +++ .../modplatform/modrinth/ModrinthResourcePages.cpp | 89 +++++ .../modplatform/modrinth/ModrinthResourcePages.h | 72 +++++ launcher/ui/widgets/ProgressWidget.cpp | 6 +- launcher/ui/widgets/ProgressWidget.h | 6 +- tests/Packwiz_test.cpp | 4 +- 78 files changed, 2508 insertions(+), 2063 deletions(-) delete mode 100644 launcher/ModDownloadTask.cpp delete mode 100644 launcher/ModDownloadTask.h create mode 100644 launcher/ResourceDownloadTask.cpp create mode 100644 launcher/ResourceDownloadTask.h delete mode 100644 launcher/modplatform/ModAPI.h create mode 100644 launcher/modplatform/ResourceAPI.h delete mode 100644 launcher/modplatform/helpers/NetworkModAPI.cpp delete mode 100644 launcher/modplatform/helpers/NetworkModAPI.h create mode 100644 launcher/modplatform/helpers/NetworkResourceAPI.cpp create mode 100644 launcher/modplatform/helpers/NetworkResourceAPI.h create mode 100644 launcher/ui/dialogs/ResourceDownloadDialog.cpp create mode 100644 launcher/ui/dialogs/ResourceDownloadDialog.h delete mode 100644 launcher/ui/pages/modplatform/ModPage.ui create mode 100644 launcher/ui/pages/modplatform/ResourceModel.cpp create mode 100644 launcher/ui/pages/modplatform/ResourceModel.h create mode 100644 launcher/ui/pages/modplatform/ResourcePage.cpp create mode 100644 launcher/ui/pages/modplatform/ResourcePage.h create mode 100644 launcher/ui/pages/modplatform/ResourcePage.ui delete mode 100644 launcher/ui/pages/modplatform/flame/FlameModModel.cpp delete mode 100644 launcher/ui/pages/modplatform/flame/FlameModModel.h delete mode 100644 launcher/ui/pages/modplatform/flame/FlameModPage.cpp delete mode 100644 launcher/ui/pages/modplatform/flame/FlameModPage.h create mode 100644 launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp create mode 100644 launcher/ui/pages/modplatform/flame/FlameResourceModels.h create mode 100644 launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp create mode 100644 launcher/ui/pages/modplatform/flame/FlameResourcePages.h delete mode 100644 launcher/ui/pages/modplatform/modrinth/ModrinthModModel.cpp delete mode 100644 launcher/ui/pages/modplatform/modrinth/ModrinthModModel.h delete mode 100644 launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp delete mode 100644 launcher/ui/pages/modplatform/modrinth/ModrinthModPage.h create mode 100644 launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp create mode 100644 launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h create mode 100644 launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp create mode 100644 launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h (limited to 'launcher/ui/pages/instance') diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index eec6c787..a1a68f5b 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -38,9 +38,9 @@ set(CORE_SOURCES InstanceImportTask.h InstanceImportTask.cpp - # Mod downloading task - ModDownloadTask.h - ModDownloadTask.cpp + # Resource downloading task + ResourceDownloadTask.h + ResourceDownloadTask.cpp # Use tracking separate from memory management Usable.h @@ -473,7 +473,7 @@ set(API_SOURCES modplatform/ModIndex.h modplatform/ModIndex.cpp - modplatform/ModAPI.h + modplatform/ResourceAPI.h modplatform/EnsureMetadataTask.h modplatform/EnsureMetadataTask.cpp @@ -484,8 +484,8 @@ set(API_SOURCES modplatform/flame/FlameAPI.cpp modplatform/modrinth/ModrinthAPI.h modplatform/modrinth/ModrinthAPI.cpp - modplatform/helpers/NetworkModAPI.h - modplatform/helpers/NetworkModAPI.cpp + modplatform/helpers/NetworkResourceAPI.h + modplatform/helpers/NetworkResourceAPI.cpp modplatform/helpers/HashUtils.h modplatform/helpers/HashUtils.cpp modplatform/helpers/OverrideUtils.h @@ -771,6 +771,11 @@ SET(LAUNCHER_SOURCES ui/pages/modplatform/VanillaPage.cpp ui/pages/modplatform/VanillaPage.h + ui/pages/modplatform/ResourcePage.cpp + ui/pages/modplatform/ResourcePage.h + ui/pages/modplatform/ResourceModel.cpp + ui/pages/modplatform/ResourceModel.h + ui/pages/modplatform/ModPage.cpp ui/pages/modplatform/ModPage.h ui/pages/modplatform/ModModel.cpp @@ -803,10 +808,10 @@ SET(LAUNCHER_SOURCES ui/pages/modplatform/flame/FlameModel.h ui/pages/modplatform/flame/FlamePage.cpp ui/pages/modplatform/flame/FlamePage.h - ui/pages/modplatform/flame/FlameModModel.cpp - ui/pages/modplatform/flame/FlameModModel.h - ui/pages/modplatform/flame/FlameModPage.cpp - ui/pages/modplatform/flame/FlameModPage.h + ui/pages/modplatform/flame/FlameResourceModels.cpp + ui/pages/modplatform/flame/FlameResourceModels.h + ui/pages/modplatform/flame/FlameResourcePages.cpp + ui/pages/modplatform/flame/FlameResourcePages.h ui/pages/modplatform/modrinth/ModrinthPage.cpp ui/pages/modplatform/modrinth/ModrinthPage.h @@ -821,10 +826,10 @@ SET(LAUNCHER_SOURCES ui/pages/modplatform/ImportPage.cpp ui/pages/modplatform/ImportPage.h - ui/pages/modplatform/modrinth/ModrinthModModel.cpp - ui/pages/modplatform/modrinth/ModrinthModModel.h - ui/pages/modplatform/modrinth/ModrinthModPage.cpp - ui/pages/modplatform/modrinth/ModrinthModPage.h + ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp + ui/pages/modplatform/modrinth/ModrinthResourceModels.h + ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp + ui/pages/modplatform/modrinth/ModrinthResourcePages.h # GUI - dialogs ui/dialogs/AboutDialog.cpp @@ -869,6 +874,8 @@ SET(LAUNCHER_SOURCES ui/dialogs/VersionSelectDialog.h ui/dialogs/SkinUploadDialog.cpp ui/dialogs/SkinUploadDialog.h + ui/dialogs/ResourceDownloadDialog.cpp + ui/dialogs/ResourceDownloadDialog.h ui/dialogs/ModDownloadDialog.cpp ui/dialogs/ModDownloadDialog.h ui/dialogs/ScrollMessageBox.cpp @@ -965,7 +972,7 @@ qt_wrap_ui(LAUNCHER_UI ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui ui/pages/modplatform/atlauncher/AtlPage.ui ui/pages/modplatform/VanillaPage.ui - ui/pages/modplatform/ModPage.ui + ui/pages/modplatform/ResourcePage.ui ui/pages/modplatform/flame/FlamePage.ui ui/pages/modplatform/legacy_ftb/Page.ui ui/pages/modplatform/ImportPage.ui diff --git a/launcher/ModDownloadTask.cpp b/launcher/ModDownloadTask.cpp deleted file mode 100644 index 2b0343f4..00000000 --- a/launcher/ModDownloadTask.cpp +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* -* PolyMC - Minecraft Launcher -* Copyright (c) 2022 flowln -* Copyright (C) 2022 Sefa Eyeoglu -* -* 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 "ModDownloadTask.h" - -#include "Application.h" -#include "minecraft/mod/ModFolderModel.h" - -ModDownloadTask::ModDownloadTask(ModPlatform::IndexedPack mod, ModPlatform::IndexedVersion version, const std::shared_ptr mods, bool is_indexed) - : m_mod(mod), m_mod_version(version), mods(mods) -{ - if (is_indexed) { - m_update_task.reset(new LocalModUpdateTask(mods->indexDir(), m_mod, m_mod_version)); - connect(m_update_task.get(), &LocalModUpdateTask::hasOldMod, this, &ModDownloadTask::hasOldMod); - - addTask(m_update_task); - } - - m_filesNetJob.reset(new NetJob(tr("Mod download"), APPLICATION->network())); - m_filesNetJob->setStatus(tr("Downloading mod:\n%1").arg(m_mod_version.downloadUrl)); - - m_filesNetJob->addNetAction(Net::Download::makeFile(m_mod_version.downloadUrl, mods->dir().absoluteFilePath(getFilename()))); - connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ModDownloadTask::downloadSucceeded); - connect(m_filesNetJob.get(), &NetJob::progress, this, &ModDownloadTask::downloadProgressChanged); - connect(m_filesNetJob.get(), &NetJob::failed, this, &ModDownloadTask::downloadFailed); - - addTask(m_filesNetJob); -} - -void ModDownloadTask::downloadSucceeded() -{ - m_filesNetJob.reset(); - auto name = std::get<0>(to_delete); - auto filename = std::get<1>(to_delete); - if (!name.isEmpty() && filename != m_mod_version.fileName) { - mods->uninstallMod(filename, true); - } -} - -void ModDownloadTask::downloadFailed(QString reason) -{ - emitFailed(reason); - m_filesNetJob.reset(); -} - -void ModDownloadTask::downloadProgressChanged(qint64 current, qint64 total) -{ - emit progress(current, total); -} - -// This indirection is done so that we don't delete a mod before being sure it was -// downloaded successfully! -void ModDownloadTask::hasOldMod(QString name, QString filename) -{ - to_delete = {name, filename}; -} diff --git a/launcher/ModDownloadTask.h b/launcher/ModDownloadTask.h deleted file mode 100644 index 95020470..00000000 --- a/launcher/ModDownloadTask.h +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* -* PolyMC - Minecraft Launcher -* Copyright (c) 2022 flowln -* Copyright (C) 2022 Sefa Eyeoglu -* -* 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 "net/NetJob.h" -#include "tasks/SequentialTask.h" - -#include "modplatform/ModIndex.h" -#include "minecraft/mod/tasks/LocalModUpdateTask.h" - -class ModFolderModel; - -class ModDownloadTask : public SequentialTask { - Q_OBJECT -public: - explicit ModDownloadTask(ModPlatform::IndexedPack mod, ModPlatform::IndexedVersion version, const std::shared_ptr mods, bool is_indexed = true); - const QString& getFilename() const { return m_mod_version.fileName; } - -private: - ModPlatform::IndexedPack m_mod; - ModPlatform::IndexedVersion m_mod_version; - const std::shared_ptr mods; - - NetJob::Ptr m_filesNetJob; - LocalModUpdateTask::Ptr m_update_task; - - void downloadProgressChanged(qint64 current, qint64 total); - - void downloadFailed(QString reason); - - void downloadSucceeded(); - - std::tuple to_delete {"", ""}; - -private slots: - void hasOldMod(QString name, QString filename); -}; - - - diff --git a/launcher/ResourceDownloadTask.cpp b/launcher/ResourceDownloadTask.cpp new file mode 100644 index 00000000..687eaf51 --- /dev/null +++ b/launcher/ResourceDownloadTask.cpp @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* +* PolyMC - Minecraft Launcher +* Copyright (c) 2022 flowln +* Copyright (C) 2022 Sefa Eyeoglu +* +* 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 "ResourceDownloadTask.h" + +#include "Application.h" + +#include "minecraft/mod/ModFolderModel.h" +#include "minecraft/mod/ResourceFolderModel.h" + +ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack pack, + ModPlatform::IndexedVersion version, + const std::shared_ptr packs, + bool is_indexed) + : m_pack(std::move(pack)), m_pack_version(std::move(version)), m_pack_model(packs) +{ + if (auto model = dynamic_cast(m_pack_model.get()); model && is_indexed) { + m_update_task.reset(new LocalModUpdateTask(model->indexDir(), m_pack, m_pack_version)); + connect(m_update_task.get(), &LocalModUpdateTask::hasOldMod, this, &ResourceDownloadTask::hasOldResource); + + addTask(m_update_task); + } + + m_filesNetJob.reset(new NetJob(tr("Resource download"), APPLICATION->network())); + m_filesNetJob->setStatus(tr("Downloading resource:\n%1").arg(m_pack_version.downloadUrl)); + + m_filesNetJob->addNetAction(Net::Download::makeFile(m_pack_version.downloadUrl, m_pack_model->dir().absoluteFilePath(getFilename()))); + connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded); + connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged); + connect(m_filesNetJob.get(), &NetJob::failed, this, &ResourceDownloadTask::downloadFailed); + + addTask(m_filesNetJob); +} + +void ResourceDownloadTask::downloadSucceeded() +{ + m_filesNetJob.reset(); + auto name = std::get<0>(to_delete); + auto filename = std::get<1>(to_delete); + if (!name.isEmpty() && filename != m_pack_version.fileName) { + if (auto model = dynamic_cast(m_pack_model.get()); model) + model->uninstallMod(filename, true); + else + m_pack_model->uninstallResource(filename); + } +} + +void ResourceDownloadTask::downloadFailed(QString reason) +{ + emitFailed(reason); + m_filesNetJob.reset(); +} + +void ResourceDownloadTask::downloadProgressChanged(qint64 current, qint64 total) +{ + emit progress(current, total); +} + +// This indirection is done so that we don't delete a mod before being sure it was +// downloaded successfully! +void ResourceDownloadTask::hasOldResource(QString name, QString filename) +{ + to_delete = { name, filename }; +} diff --git a/launcher/ResourceDownloadTask.h b/launcher/ResourceDownloadTask.h new file mode 100644 index 00000000..350c2edd --- /dev/null +++ b/launcher/ResourceDownloadTask.h @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* +* PolyMC - Minecraft Launcher +* Copyright (c) 2022 flowln +* Copyright (C) 2022 Sefa Eyeoglu +* +* 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 "net/NetJob.h" +#include "tasks/SequentialTask.h" + +#include "modplatform/ModIndex.h" +#include "minecraft/mod/tasks/LocalModUpdateTask.h" + +class ResourceFolderModel; + +class ResourceDownloadTask : public SequentialTask { + Q_OBJECT +public: + explicit ResourceDownloadTask(ModPlatform::IndexedPack pack, ModPlatform::IndexedVersion version, const std::shared_ptr packs, bool is_indexed = true); + const QString& getFilename() const { return m_pack_version.fileName; } + +private: + ModPlatform::IndexedPack m_pack; + ModPlatform::IndexedVersion m_pack_version; + const std::shared_ptr m_pack_model; + + NetJob::Ptr m_filesNetJob; + LocalModUpdateTask::Ptr m_update_task; + + void downloadProgressChanged(qint64 current, qint64 total); + + void downloadFailed(QString reason); + + void downloadSucceeded(); + + std::tuple to_delete {"", ""}; + +private slots: + void hasOldResource(QString name, QString filename); +}; + + + diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 43fa3f8d..42021b3c 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -55,12 +55,13 @@ #include "PackProfile_p.h" #include "ComponentUpdateTask.h" -#include "modplatform/ModAPI.h" +#include "Application.h" +#include "modplatform/ResourceAPI.h" -static const QMap modloaderMapping{ - {"net.minecraftforge", ModAPI::Forge}, - {"net.fabricmc.fabric-loader", ModAPI::Fabric}, - {"org.quiltmc.quilt-loader", ModAPI::Quilt} +static const QMap modloaderMapping{ + {"net.minecraftforge", ResourceAPI::Forge}, + {"net.fabricmc.fabric-loader", ResourceAPI::Fabric}, + {"org.quiltmc.quilt-loader", ResourceAPI::Quilt} }; PackProfile::PackProfile(MinecraftInstance * instance) @@ -1066,19 +1067,22 @@ void PackProfile::disableInteraction(bool disable) } } -ModAPI::ModLoaderTypes PackProfile::getModLoaders() +std::optional PackProfile::getModLoaders() { - ModAPI::ModLoaderTypes result = ModAPI::Unspecified; + ResourceAPI::ModLoaderTypes result; + bool has_any_loader = false; - QMapIterator i(modloaderMapping); + QMapIterator i(modloaderMapping); - while (i.hasNext()) - { + while (i.hasNext()) { i.next(); - Component* c = getComponent(i.key()); - if (c != nullptr && c->isEnabled()) { + if (auto c = getComponent(i.key()); c != nullptr && c->isEnabled()) { result |= i.value(); + has_any_loader = true; } } + + if (!has_any_loader) + return {}; return result; } diff --git a/launcher/minecraft/PackProfile.h b/launcher/minecraft/PackProfile.h index 2330cca1..67b418f4 100644 --- a/launcher/minecraft/PackProfile.h +++ b/launcher/minecraft/PackProfile.h @@ -49,7 +49,7 @@ #include "BaseVersion.h" #include "MojangDownloadInfo.h" #include "net/Mode.h" -#include "modplatform/ModAPI.h" +#include "modplatform/ResourceAPI.h" class MinecraftInstance; struct PackProfileData; @@ -145,7 +145,7 @@ public: // todo(merged): is this the best approach void appendComponent(ComponentPtr component); - ModAPI::ModLoaderTypes getModLoaders(); + std::optional getModLoaders(); private: void scheduleSave(); diff --git a/launcher/modplatform/CheckUpdateTask.h b/launcher/modplatform/CheckUpdateTask.h index 91922034..932a62d9 100644 --- a/launcher/modplatform/CheckUpdateTask.h +++ b/launcher/modplatform/CheckUpdateTask.h @@ -1,18 +1,18 @@ #pragma once #include "minecraft/mod/Mod.h" -#include "modplatform/ModAPI.h" +#include "modplatform/ResourceAPI.h" #include "modplatform/ModIndex.h" #include "tasks/Task.h" -class ModDownloadTask; +class ResourceDownloadTask; class ModFolderModel; class CheckUpdateTask : public Task { Q_OBJECT public: - CheckUpdateTask(QList& mods, std::list& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr mods_folder) + CheckUpdateTask(QList& mods, std::list& mcVersions, std::optional loaders, std::shared_ptr mods_folder) : Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder) {}; struct UpdatableMod { @@ -21,11 +21,11 @@ class CheckUpdateTask : public Task { QString old_version; QString new_version; QString changelog; - ModPlatform::Provider provider; - ModDownloadTask* download; + ModPlatform::ResourceProvider provider; + ResourceDownloadTask* download; public: - UpdatableMod(QString name, QString old_h, QString old_v, QString new_v, QString changelog, ModPlatform::Provider p, ModDownloadTask* t) + UpdatableMod(QString name, QString old_h, QString old_v, QString new_v, QString changelog, ModPlatform::ResourceProvider p, ResourceDownloadTask* t) : name(name), old_hash(old_h), old_version(old_v), new_version(new_v), changelog(changelog), provider(p), download(t) {} }; @@ -44,7 +44,7 @@ class CheckUpdateTask : public Task { protected: QList& m_mods; std::list& m_game_versions; - ModAPI::ModLoaderTypes m_loaders; + std::optional m_loaders; std::shared_ptr m_mods_folder; std::vector m_updatable; diff --git a/launcher/modplatform/EnsureMetadataTask.cpp b/launcher/modplatform/EnsureMetadataTask.cpp index 234330a7..9bf81338 100644 --- a/launcher/modplatform/EnsureMetadataTask.cpp +++ b/launcher/modplatform/EnsureMetadataTask.cpp @@ -20,7 +20,7 @@ static ModPlatform::ProviderCapabilities ProviderCaps; static ModrinthAPI modrinth_api; static FlameAPI flame_api; -EnsureMetadataTask::EnsureMetadataTask(Mod* mod, QDir dir, ModPlatform::Provider prov) +EnsureMetadataTask::EnsureMetadataTask(Mod* mod, QDir dir, ModPlatform::ResourceProvider prov) : Task(nullptr), m_index_dir(dir), m_provider(prov), m_hashing_task(nullptr), m_current_task(nullptr) { auto hash_task = createNewHash(mod); @@ -31,7 +31,7 @@ EnsureMetadataTask::EnsureMetadataTask(Mod* mod, QDir dir, ModPlatform::Provider hash_task->start(); } -EnsureMetadataTask::EnsureMetadataTask(QList& mods, QDir dir, ModPlatform::Provider prov) +EnsureMetadataTask::EnsureMetadataTask(QList& mods, QDir dir, ModPlatform::ResourceProvider prov) : Task(nullptr), m_index_dir(dir), m_provider(prov), m_current_task(nullptr) { m_hashing_task = new ConcurrentTask(this, "MakeHashesTask", 10); @@ -110,10 +110,10 @@ void EnsureMetadataTask::executeTask() NetJob::Ptr version_task; switch (m_provider) { - case (ModPlatform::Provider::MODRINTH): + case (ModPlatform::ResourceProvider::MODRINTH): version_task = modrinthVersionsTask(); break; - case (ModPlatform::Provider::FLAME): + case (ModPlatform::ResourceProvider::FLAME): version_task = flameVersionsTask(); break; } @@ -130,10 +130,10 @@ void EnsureMetadataTask::executeTask() NetJob::Ptr project_task; switch (m_provider) { - case (ModPlatform::Provider::MODRINTH): + case (ModPlatform::ResourceProvider::MODRINTH): project_task = modrinthProjectsTask(); break; - case (ModPlatform::Provider::FLAME): + case (ModPlatform::ResourceProvider::FLAME): project_task = flameProjectsTask(); break; } @@ -212,7 +212,7 @@ void EnsureMetadataTask::emitFail(Mod* m, QString key, RemoveFromList remove) NetJob::Ptr EnsureMetadataTask::modrinthVersionsTask() { - auto hash_type = ProviderCaps.hashType(ModPlatform::Provider::MODRINTH).first(); + auto hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first(); auto* response = new QByteArray(); auto ver_task = modrinth_api.currentVersions(m_mods.keys(), hash_type, response); diff --git a/launcher/modplatform/EnsureMetadataTask.h b/launcher/modplatform/EnsureMetadataTask.h index a8b0851e..a79e5861 100644 --- a/launcher/modplatform/EnsureMetadataTask.h +++ b/launcher/modplatform/EnsureMetadataTask.h @@ -14,8 +14,8 @@ class EnsureMetadataTask : public Task { Q_OBJECT public: - EnsureMetadataTask(Mod*, QDir, ModPlatform::Provider = ModPlatform::Provider::MODRINTH); - EnsureMetadataTask(QList&, QDir, ModPlatform::Provider = ModPlatform::Provider::MODRINTH); + EnsureMetadataTask(Mod*, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH); + EnsureMetadataTask(QList&, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH); ~EnsureMetadataTask() = default; @@ -57,7 +57,7 @@ class EnsureMetadataTask : public Task { private: QHash m_mods; QDir m_index_dir; - ModPlatform::Provider m_provider; + ModPlatform::ResourceProvider m_provider; QHash m_temp_versions; ConcurrentTask* m_hashing_task; diff --git a/launcher/modplatform/ModAPI.h b/launcher/modplatform/ModAPI.h deleted file mode 100644 index 703de143..00000000 --- a/launcher/modplatform/ModAPI.h +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * PolyMC - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu - * - * 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 . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include "../Version.h" -#include "net/NetJob.h" - -namespace ModPlatform { -class ListModel; -struct IndexedPack; -} - -class ModAPI { - protected: - using CallerType = ModPlatform::ListModel; - - public: - virtual ~ModAPI() = default; - - enum ModLoaderType { - Unspecified = 0, - Forge = 1 << 0, - Cauldron = 1 << 1, - LiteLoader = 1 << 2, - Fabric = 1 << 3, - Quilt = 1 << 4 - }; - Q_DECLARE_FLAGS(ModLoaderTypes, ModLoaderType) - - struct SearchArgs { - int offset; - QString search; - QString sorting; - ModLoaderTypes loaders; - std::list versions; - }; - - virtual void searchMods(CallerType* caller, SearchArgs&& args) const = 0; - virtual void getModInfo(ModPlatform::IndexedPack& pack, std::function callback) = 0; - - virtual auto getProject(QString addonId, QByteArray* response) const -> NetJob* = 0; - virtual auto getProjects(QStringList addonIds, QByteArray* response) const -> NetJob* = 0; - - - struct VersionSearchArgs { - QString addonId; - std::list mcVersions; - ModLoaderTypes loaders; - }; - - virtual void getVersions(VersionSearchArgs&& args, std::function callback) const = 0; - - static auto getModLoaderString(ModLoaderType type) -> const QString { - switch (type) { - case Unspecified: - break; - case Forge: - return "forge"; - case Cauldron: - return "cauldron"; - case LiteLoader: - return "liteloader"; - case Fabric: - return "fabric"; - case Quilt: - return "quilt"; - } - return ""; - } - - protected: - inline auto getGameVersionsString(std::list mcVersions) const -> QString - { - QString s; - for(auto& ver : mcVersions){ - s += QString("\"%1\",").arg(ver.toString()); - } - s.remove(s.length() - 1, 1); //remove last comma - return s; - } -}; diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index 34fd9f30..6a507caf 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -24,47 +24,47 @@ namespace ModPlatform { -auto ProviderCapabilities::name(Provider p) -> const char* +auto ProviderCapabilities::name(ResourceProvider p) -> const char* { switch (p) { - case Provider::MODRINTH: + case ResourceProvider::MODRINTH: return "modrinth"; - case Provider::FLAME: + case ResourceProvider::FLAME: return "curseforge"; } return {}; } -auto ProviderCapabilities::readableName(Provider p) -> QString +auto ProviderCapabilities::readableName(ResourceProvider p) -> QString { switch (p) { - case Provider::MODRINTH: + case ResourceProvider::MODRINTH: return "Modrinth"; - case Provider::FLAME: + case ResourceProvider::FLAME: return "CurseForge"; } return {}; } -auto ProviderCapabilities::hashType(Provider p) -> QStringList +auto ProviderCapabilities::hashType(ResourceProvider p) -> QStringList { switch (p) { - case Provider::MODRINTH: + case ResourceProvider::MODRINTH: return { "sha512", "sha1" }; - case Provider::FLAME: + case ResourceProvider::FLAME: // Try newer formats first, fall back to old format return { "sha1", "md5", "murmur2" }; } return {}; } -auto ProviderCapabilities::hash(Provider p, QIODevice* device, QString type) -> QString +auto ProviderCapabilities::hash(ResourceProvider p, QIODevice* device, QString type) -> QString { QCryptographicHash::Algorithm algo = QCryptographicHash::Sha1; switch (p) { - case Provider::MODRINTH: { + case ResourceProvider::MODRINTH: { algo = (type == "sha1") ? QCryptographicHash::Sha1 : QCryptographicHash::Sha512; break; } - case Provider::FLAME: + case ResourceProvider::FLAME: algo = (type == "sha1") ? QCryptographicHash::Sha1 : QCryptographicHash::Md5; break; } diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 518fed7c..f65a6a4b 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -28,17 +28,16 @@ class QIODevice; namespace ModPlatform { -enum class Provider { - MODRINTH, - FLAME -}; +enum class ResourceProvider { MODRINTH, FLAME }; + +enum class ResourceType { MOD, RESOURCE_PACK }; class ProviderCapabilities { public: - auto name(Provider) -> const char*; - auto readableName(Provider) -> QString; - auto hashType(Provider) -> QStringList; - auto hash(Provider, QIODevice*, QString type = "") -> QString; + auto name(ResourceProvider) -> const char*; + auto readableName(ResourceProvider) -> QString; + auto hashType(ResourceProvider) -> QStringList; + auto hash(ResourceProvider, QIODevice*, QString type = "") -> QString; }; struct ModpackAuthor { @@ -81,7 +80,7 @@ struct ExtraPackData { struct IndexedPack { QVariant addonId; - Provider provider; + ResourceProvider provider; QString name; QString slug; QString description; @@ -101,4 +100,4 @@ struct IndexedPack { } // namespace ModPlatform Q_DECLARE_METATYPE(ModPlatform::IndexedPack) -Q_DECLARE_METATYPE(ModPlatform::Provider) +Q_DECLARE_METATYPE(ModPlatform::ResourceProvider) diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h new file mode 100644 index 00000000..d18a2caa --- /dev/null +++ b/launcher/modplatform/ResourceAPI.h @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * + * 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 . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include + +#include "../Version.h" + +#include "modplatform/ModIndex.h" +#include "net/NetJob.h" + +/* Simple class with a common interface for interacting with APIs */ +class ResourceAPI { + public: + virtual ~ResourceAPI() = default; + + enum ModLoaderType { Forge = 1 << 0, Cauldron = 1 << 1, LiteLoader = 1 << 2, Fabric = 1 << 3, Quilt = 1 << 4 }; + Q_DECLARE_FLAGS(ModLoaderTypes, ModLoaderType) + + struct SearchArgs { + ModPlatform::ResourceType type{}; + int offset = 0; + + std::optional search; + std::optional sorting; + std::optional loaders; + std::optional > versions; + }; + struct SearchCallbacks { + std::function on_succeed; + std::function on_fail; + std::function on_abort; + }; + + struct VersionSearchArgs { + QString addonId; + + std::optional > mcVersions; + std::optional loaders; + }; + struct VersionSearchCallbacks { + std::function on_succeed; + }; + + struct ProjectInfoArgs { + ModPlatform::IndexedPack& pack; + + void operator=(ProjectInfoArgs other) { pack = other.pack; } + }; + struct ProjectInfoCallbacks { + std::function on_succeed; + }; + + public slots: + [[nodiscard]] virtual NetJob::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const + { + qWarning() << "TODO"; + return nullptr; + } + [[nodiscard]] virtual NetJob::Ptr getProject(QString addonId, QByteArray* response) const + { + qWarning() << "TODO"; + return nullptr; + } + [[nodiscard]] virtual NetJob::Ptr getProjects(QStringList addonIds, QByteArray* response) const + { + qWarning() << "TODO"; + return nullptr; + } + + [[nodiscard]] virtual NetJob::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const + { + qWarning() << "TODO"; + return nullptr; + } + [[nodiscard]] virtual NetJob::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const + { + qWarning() << "TODO"; + return nullptr; + } + + static auto getModLoaderString(ModLoaderType type) -> const QString + { + switch (type) { + case Forge: + return "forge"; + case Cauldron: + return "cauldron"; + case LiteLoader: + return "liteloader"; + case Fabric: + return "fabric"; + case Quilt: + return "quilt"; + default: + break; + } + return ""; + } + + protected: + [[nodiscard]] inline QString debugName() const { return "External resource API"; } + + [[nodiscard]] inline auto getGameVersionsString(std::list mcVersions) const -> QString + { + QString s; + for (auto& ver : mcVersions) { + s += QString("\"%1\",").arg(ver.toString()); + } + s.remove(s.length() - 1, 1); // remove last comma + return s; + } +}; diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index 4d71da21..ae401399 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -106,13 +106,19 @@ auto FlameAPI::getModDescription(int modId) -> QString auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::IndexedVersion { + auto versions_url_optional = getVersionsURL(args); + if (!versions_url_optional.has_value()) + return {}; + + auto versions_url = versions_url_optional.value(); + QEventLoop loop; auto netJob = new NetJob(QString("Flame::GetLatestVersion(%1)").arg(args.addonId), APPLICATION->network()); auto response = new QByteArray(); ModPlatform::IndexedVersion ver; - netJob->addNetAction(Net::Download::makeByteArray(getVersionsURL(args), response)); + netJob->addNetAction(Net::Download::makeByteArray(versions_url, response)); QObject::connect(netJob, &NetJob::succeeded, [response, args, &ver] { QJsonParseError parse_error{}; @@ -161,7 +167,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe return ver; } -auto FlameAPI::getProjects(QStringList addonIds, QByteArray* response) const -> NetJob* +NetJob::Ptr FlameAPI::getProjects(QStringList addonIds, QByteArray* response) const { auto* netJob = new NetJob(QString("Flame::GetProjects"), APPLICATION->network()); @@ -178,13 +184,13 @@ auto FlameAPI::getProjects(QStringList addonIds, QByteArray* response) const -> netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods"), response, body_raw)); - QObject::connect(netJob, &NetJob::finished, [response, netJob] { delete response; netJob->deleteLater(); }); + QObject::connect(netJob, &NetJob::finished, [response] { delete response; }); QObject::connect(netJob, &NetJob::failed, [body_raw] { qDebug() << body_raw; }); return netJob; } -auto FlameAPI::getFiles(const QStringList& fileIds, QByteArray* response) const -> NetJob* +NetJob::Ptr FlameAPI::getFiles(const QStringList& fileIds, QByteArray* response) const { auto* netJob = new NetJob(QString("Flame::GetFiles"), APPLICATION->network()); @@ -201,7 +207,7 @@ auto FlameAPI::getFiles(const QStringList& fileIds, QByteArray* response) const netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods/files"), response, body_raw)); - QObject::connect(netJob, &NetJob::finished, [response, netJob] { delete response; netJob->deleteLater(); }); + QObject::connect(netJob, &NetJob::finished, [response] { delete response; }); QObject::connect(netJob, &NetJob::failed, [body_raw] { qDebug() << body_raw; }); return netJob; diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index 4c6ca64c..114a2716 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -1,21 +1,21 @@ #pragma once #include "modplatform/ModIndex.h" -#include "modplatform/helpers/NetworkModAPI.h" +#include "modplatform/helpers/NetworkResourceAPI.h" -class FlameAPI : public NetworkModAPI { +class FlameAPI : public NetworkResourceAPI { public: - auto matchFingerprints(const QList& fingerprints, QByteArray* response) -> NetJob::Ptr; auto getModFileChangelog(int modId, int fileId) -> QString; auto getModDescription(int modId) -> QString; auto getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::IndexedVersion; - auto getProjects(QStringList addonIds, QByteArray* response) const -> NetJob* override; - auto getFiles(const QStringList& fileIds, QByteArray* response) const -> NetJob*; + NetJob::Ptr getProjects(QStringList addonIds, QByteArray* response) const override; + NetJob::Ptr matchFingerprints(const QList& fingerprints, QByteArray* response); + NetJob::Ptr getFiles(const QStringList& fileIds, QByteArray* response) const; private: - inline auto getSortFieldInt(QString sortString) const -> int + static int getSortFieldInt(QString const& sortString) { return sortString == "Featured" ? 1 : sortString == "Popularity" ? 2 @@ -28,48 +28,16 @@ class FlameAPI : public NetworkModAPI { : 1; } - private: - inline auto getModSearchURL(SearchArgs& args) const -> QString override - { - auto gameVersionStr = args.versions.size() != 0 ? QString("gameVersion=%1").arg(args.versions.front().toString()) : QString(); - - return QString( - "https://api.curseforge.com/v1/mods/search?" - "gameId=432&" - "classId=6&" - - "index=%1&" - "pageSize=25&" - "searchFilter=%2&" - "sortField=%3&" - "sortOrder=desc&" - "modLoaderType=%4&" - "%5") - .arg(args.offset) - .arg(args.search) - .arg(getSortFieldInt(args.sorting)) - .arg(getMappedModLoader(args.loaders)) - .arg(gameVersionStr); - }; - - inline auto getModInfoURL(QString& id) const -> QString override + static int getClassId(ModPlatform::ResourceType type) { - return QString("https://api.curseforge.com/v1/mods/%1").arg(id); - }; - - inline auto getVersionsURL(VersionSearchArgs& args) const -> QString override - { - QString gameVersionQuery = args.mcVersions.size() == 1 ? QString("gameVersion=%1&").arg(args.mcVersions.front().toString()) : ""; - QString modLoaderQuery = QString("modLoaderType=%1&").arg(getMappedModLoader(args.loaders)); - - return QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&%2%3") - .arg(args.addonId) - .arg(gameVersionQuery) - .arg(modLoaderQuery); - }; + switch (type) { + default: + case ModPlatform::ResourceType::MOD: + return 6; + } + } - public: - static auto getMappedModLoader(const ModLoaderTypes loaders) -> int + static int getMappedModLoader(ModLoaderTypes loaders) { // https://docs.curseforge.com/?http#tocS_ModLoaderType if (loaders & Forge) @@ -81,4 +49,43 @@ class FlameAPI : public NetworkModAPI { return 4; // Quilt would probably be 5 return 0; } + + private: + [[nodiscard]] std::optional getSearchURL(SearchArgs const& args) const override + { + auto gameVersionStr = args.versions.has_value() ? QString("gameVersion=%1").arg(args.versions.value().front().toString()) : QString(); + + QStringList get_arguments; + get_arguments.append(QString("classId=%1").arg(getClassId(args.type))); + get_arguments.append(QString("index=%1").arg(args.offset)); + get_arguments.append("pageSize=25"); + if (args.search.has_value()) + get_arguments.append(QString("searchFilter=%1").arg(args.search.value())); + if (args.sorting.has_value()) + get_arguments.append(QString("sortField=%1").arg(getSortFieldInt(args.sorting.value()))); + get_arguments.append("sortOrder=desc"); + if (args.loaders.has_value()) + get_arguments.append(QString("modLoaderType=%1").arg(getMappedModLoader(args.loaders.value()))); + get_arguments.append(gameVersionStr); + + return "https://api.curseforge.com/v1/mods/search?gameId=432&" + get_arguments.join('&'); + }; + + [[nodiscard]] std::optional getInfoURL(QString const& id) const override + { + return QString("https://api.curseforge.com/v1/mods/%1").arg(id); + }; + + [[nodiscard]] std::optional getVersionsURL(VersionSearchArgs const& args) const override + { + QString url{QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&").arg(args.addonId)}; + + QStringList get_parameters; + if (args.mcVersions.has_value()) + get_parameters.append(QString("gameVersion=%1").arg(args.mcVersions.value().front().toString())); + if (args.loaders.has_value()) + get_parameters.append(QString("modLoaderType=%1").arg(getMappedModLoader(args.loaders.value()))); + + return url + get_parameters.join('&'); + }; }; diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp index 8dd3a846..285fa49f 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.cpp +++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp @@ -7,7 +7,10 @@ #include "FileSystem.h" #include "Json.h" -#include "ModDownloadTask.h" +#include "ResourceDownloadTask.h" + +#include "minecraft/mod/ModFolderModel.h" +#include "minecraft/mod/ResourceFolderModel.h" static FlameAPI api; @@ -160,7 +163,7 @@ void FlameCheckUpdate::executeTask() for (auto& author : mod->authors()) pack.authors.append({ author }); pack.description = mod->description(); - pack.provider = ModPlatform::Provider::FLAME; + pack.provider = ModPlatform::ResourceProvider::FLAME; auto old_version = mod->version(); if (old_version.isEmpty() && mod->status() != ModStatus::NotInstalled) { @@ -168,10 +171,10 @@ void FlameCheckUpdate::executeTask() old_version = current_ver.version; } - auto download_task = new ModDownloadTask(pack, latest_ver, m_mods_folder); + auto download_task = new ResourceDownloadTask(pack, latest_ver, m_mods_folder); m_updatable.emplace_back(pack.name, mod->metadata()->hash, old_version, latest_ver.version, api.getModFileChangelog(latest_ver.addonId.toInt(), latest_ver.fileId.toInt()), - ModPlatform::Provider::FLAME, download_task); + ModPlatform::ResourceProvider::FLAME, download_task); } } diff --git a/launcher/modplatform/flame/FlameCheckUpdate.h b/launcher/modplatform/flame/FlameCheckUpdate.h index 163c706c..4a98d684 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.h +++ b/launcher/modplatform/flame/FlameCheckUpdate.h @@ -8,7 +8,7 @@ class FlameCheckUpdate : public CheckUpdateTask { Q_OBJECT public: - FlameCheckUpdate(QList& mods, std::list& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr mods_folder) + FlameCheckUpdate(QList& mods, std::list& mcVersions, std::optional loaders, std::shared_ptr mods_folder) : CheckUpdateTask(mods, mcVersions, loaders, mods_folder) {} diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index dc69769a..fb6f78e8 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -183,7 +183,7 @@ bool FlameCreationTask::updateInstance() QEventLoop loop; - connect(job, &NetJob::succeeded, this, [this, raw_response, fileIds, old_inst_dir, &old_files, old_minecraft_dir] { + connect(job.get(), &NetJob::succeeded, this, [this, raw_response, fileIds, old_inst_dir, &old_files, old_minecraft_dir] { // Parse the API response QJsonParseError parse_error{}; auto doc = QJsonDocument::fromJson(*raw_response, &parse_error); @@ -225,7 +225,7 @@ bool FlameCreationTask::updateInstance() m_files_to_remove.append(old_minecraft_dir.absoluteFilePath(relative_path)); } }); - connect(job, &NetJob::finished, &loop, &QEventLoop::quit); + connect(job.get(), &NetJob::finished, &loop, &QEventLoop::quit); m_process_update_file_info_job = job; job->start(); diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.h b/launcher/modplatform/flame/FlameInstanceCreationTask.h index 498e1d6e..36b62e3e 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.h +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.h @@ -86,7 +86,7 @@ class FlameCreationTask final : public InstanceCreationTask { Flame::Manifest m_pack; // Handle to allow aborting - NetJob* m_process_update_file_info_job = nullptr; + NetJob::Ptr m_process_update_file_info_job = nullptr; NetJob::Ptr m_files_job = nullptr; QString m_managed_id, m_managed_version_id; diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 32aa4bdb..617b98ce 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -11,7 +11,7 @@ static ModPlatform::ProviderCapabilities ProviderCaps; void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) { pack.addonId = Json::requireInteger(obj, "id"); - pack.provider = ModPlatform::Provider::FLAME; + pack.provider = ModPlatform::ResourceProvider::FLAME; pack.name = Json::requireString(obj, "name"); pack.slug = Json::requireString(obj, "slug"); pack.websiteUrl = Json::ensureString(Json::ensureObject(obj, "links"), "websiteUrl", ""); @@ -127,7 +127,7 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> auto hash_list = Json::ensureArray(obj, "hashes"); for (auto h : hash_list) { auto hash_entry = Json::ensureObject(h); - auto hash_types = ProviderCaps.hashType(ModPlatform::Provider::FLAME); + auto hash_types = ProviderCaps.hashType(ModPlatform::ResourceProvider::FLAME); auto hash_algo = enumToString(Json::ensureInteger(hash_entry, "algo", 1, "algorithm")); if (hash_types.contains(hash_algo)) { file.hash = Json::requireString(hash_entry, "value"); diff --git a/launcher/modplatform/helpers/HashUtils.cpp b/launcher/modplatform/helpers/HashUtils.cpp index f1e4759e..af484be0 100644 --- a/launcher/modplatform/helpers/HashUtils.cpp +++ b/launcher/modplatform/helpers/HashUtils.cpp @@ -12,12 +12,12 @@ namespace Hashing { static ModPlatform::ProviderCapabilities ProviderCaps; -Hasher::Ptr createHasher(QString file_path, ModPlatform::Provider provider) +Hasher::Ptr createHasher(QString file_path, ModPlatform::ResourceProvider provider) { switch (provider) { - case ModPlatform::Provider::MODRINTH: + case ModPlatform::ResourceProvider::MODRINTH: return createModrinthHasher(file_path); - case ModPlatform::Provider::FLAME: + case ModPlatform::ResourceProvider::FLAME: return createFlameHasher(file_path); default: qCritical() << "[Hashing]" @@ -36,12 +36,12 @@ Hasher::Ptr createFlameHasher(QString file_path) return new FlameHasher(file_path); } -Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::Provider provider) +Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider) { return new BlockedModHasher(file_path, provider); } -Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::Provider provider, QString type) +Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider, QString type) { auto hasher = new BlockedModHasher(file_path, provider); hasher->useHashType(type); @@ -62,8 +62,8 @@ void ModrinthHasher::executeTask() return; } - auto hash_type = ProviderCaps.hashType(ModPlatform::Provider::MODRINTH).first(); - m_hash = ProviderCaps.hash(ModPlatform::Provider::MODRINTH, &file, hash_type); + auto hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first(); + m_hash = ProviderCaps.hash(ModPlatform::ResourceProvider::MODRINTH, &file, hash_type); file.close(); @@ -92,7 +92,7 @@ void FlameHasher::executeTask() } -BlockedModHasher::BlockedModHasher(QString file_path, ModPlatform::Provider provider) +BlockedModHasher::BlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider) : Hasher(file_path), provider(provider) { setObjectName(QString("BlockedModHasher: %1").arg(file_path)); hash_type = ProviderCaps.hashType(provider).first(); diff --git a/launcher/modplatform/helpers/HashUtils.h b/launcher/modplatform/helpers/HashUtils.h index fa3244f6..91146a52 100644 --- a/launcher/modplatform/helpers/HashUtils.h +++ b/launcher/modplatform/helpers/HashUtils.h @@ -42,21 +42,21 @@ class ModrinthHasher : public Hasher { class BlockedModHasher : public Hasher { public: - BlockedModHasher(QString file_path, ModPlatform::Provider provider); + BlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider); void executeTask() override; QStringList getHashTypes(); bool useHashType(QString type); private: - ModPlatform::Provider provider; + ModPlatform::ResourceProvider provider; QString hash_type; }; -Hasher::Ptr createHasher(QString file_path, ModPlatform::Provider provider); +Hasher::Ptr createHasher(QString file_path, ModPlatform::ResourceProvider provider); Hasher::Ptr createFlameHasher(QString file_path); Hasher::Ptr createModrinthHasher(QString file_path); -Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::Provider provider); -Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::Provider provider, QString type); +Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider); +Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider, QString type); } // namespace Hashing diff --git a/launcher/modplatform/helpers/NetworkModAPI.cpp b/launcher/modplatform/helpers/NetworkModAPI.cpp deleted file mode 100644 index 7633030e..00000000 --- a/launcher/modplatform/helpers/NetworkModAPI.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "NetworkModAPI.h" - -#include "ui/pages/modplatform/ModModel.h" - -#include "Application.h" -#include "net/NetJob.h" - -void NetworkModAPI::searchMods(CallerType* caller, SearchArgs&& args) const -{ - auto netJob = new NetJob(QString("%1::Search").arg(caller->debugName()), APPLICATION->network()); - auto searchUrl = getModSearchURL(args); - - auto response = new QByteArray(); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response)); - - QObject::connect(netJob, &NetJob::started, caller, [caller, netJob] { caller->setActiveJob(netJob); }); - QObject::connect(netJob, &NetJob::failed, caller, &CallerType::searchRequestFailed); - QObject::connect(netJob, &NetJob::aborted, caller, &CallerType::searchRequestAborted); - QObject::connect(netJob, &NetJob::succeeded, caller, [caller, response] { - QJsonParseError parse_error{}; - QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); - if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response from " << caller->debugName() << " at " << parse_error.offset - << " reason: " << parse_error.errorString(); - qWarning() << *response; - return; - } - - caller->searchRequestFinished(doc); - }); - - netJob->start(); -} - -void NetworkModAPI::getModInfo(ModPlatform::IndexedPack& pack, std::function callback) -{ - auto response = new QByteArray(); - auto job = getProject(pack.addonId.toString(), response); - - QObject::connect(job, &NetJob::succeeded, [callback, &pack, response] { - QJsonParseError parse_error{}; - QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); - if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset - << " reason: " << parse_error.errorString(); - qWarning() << *response; - return; - } - - callback(doc, pack); - }); - - job->start(); -} - -void NetworkModAPI::getVersions(VersionSearchArgs&& args, std::function callback) const -{ - auto netJob = new NetJob(QString("ModVersions(%2)").arg(args.addonId), APPLICATION->network()); - auto response = new QByteArray(); - - netJob->addNetAction(Net::Download::makeByteArray(getVersionsURL(args), response)); - - QObject::connect(netJob, &NetJob::succeeded, [response, callback, args] { - QJsonParseError parse_error{}; - QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); - if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response for getting versions at " << parse_error.offset - << " reason: " << parse_error.errorString(); - qWarning() << *response; - return; - } - - callback(doc, args.addonId); - }); - - QObject::connect(netJob, &NetJob::finished, [response, netJob] { - netJob->deleteLater(); - delete response; - }); - - netJob->start(); -} - -auto NetworkModAPI::getProject(QString addonId, QByteArray* response) const -> NetJob* -{ - auto netJob = new NetJob(QString("%1::GetProject").arg(addonId), APPLICATION->network()); - auto searchUrl = getModInfoURL(addonId); - - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response)); - - QObject::connect(netJob, &NetJob::finished, [response, netJob] { - netJob->deleteLater(); - delete response; - }); - - return netJob; -} diff --git a/launcher/modplatform/helpers/NetworkModAPI.h b/launcher/modplatform/helpers/NetworkModAPI.h deleted file mode 100644 index b8af22c7..00000000 --- a/launcher/modplatform/helpers/NetworkModAPI.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "modplatform/ModAPI.h" - -class NetworkModAPI : public ModAPI { - public: - void searchMods(CallerType* caller, SearchArgs&& args) const override; - void getModInfo(ModPlatform::IndexedPack& pack, std::function callback) override; - void getVersions(VersionSearchArgs&& args, std::function callback) const override; - - auto getProject(QString addonId, QByteArray* response) const -> NetJob* override; - - protected: - virtual auto getModSearchURL(SearchArgs& args) const -> QString = 0; - virtual auto getModInfoURL(QString& id) const -> QString = 0; - virtual auto getVersionsURL(VersionSearchArgs& args) const -> QString = 0; -}; diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.cpp b/launcher/modplatform/helpers/NetworkResourceAPI.cpp new file mode 100644 index 00000000..eb17008c --- /dev/null +++ b/launcher/modplatform/helpers/NetworkResourceAPI.cpp @@ -0,0 +1,124 @@ +#include "NetworkResourceAPI.h" + +#include "Application.h" +#include "net/NetJob.h" + +#include "modplatform/ModIndex.h" + +NetJob::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&& callbacks) const +{ + auto search_url_optional = getSearchURL(args); + if (!search_url_optional.has_value()) { + callbacks.on_fail("Failed to create search URL", -1); + return nullptr; + } + + auto search_url = search_url_optional.value(); + + auto response = new QByteArray(); + auto netJob = new NetJob(QString("%1::Search").arg(debugName()), APPLICATION->network()); + + netJob->addNetAction(Net::Download::makeByteArray(QUrl(search_url), response)); + + QObject::connect(netJob, &NetJob::succeeded, [=]{ + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from " << debugName() << " at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *response; + + callbacks.on_fail(parse_error.errorString(), -1); + + return; + } + + callbacks.on_succeed(doc); + }); + + QObject::connect(netJob, &NetJob::failed, [=](QString reason){ + int network_error_code = -1; + if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply) + network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + callbacks.on_fail(reason, network_error_code); + }); + QObject::connect(netJob, &NetJob::aborted, [=]{ + callbacks.on_abort(); + }); + + return netJob; +} + +NetJob::Ptr NetworkResourceAPI::getProjectInfo(ProjectInfoArgs&& args, ProjectInfoCallbacks&& callbacks) const +{ + auto response = new QByteArray(); + auto job = getProject(args.pack.addonId.toString(), response); + + QObject::connect(job.get(), &NetJob::succeeded, [response, callbacks, args] { + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *response; + return; + } + + callbacks.on_succeed(doc, args.pack); + }); + + return job; +} + +NetJob::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, VersionSearchCallbacks&& callbacks) const +{ + auto versions_url_optional = getVersionsURL(args); + if (!versions_url_optional.has_value()) + return nullptr; + + auto versions_url = versions_url_optional.value(); + + auto netJob = new NetJob(QString("%1::Versions").arg(args.addonId), APPLICATION->network()); + auto response = new QByteArray(); + + netJob->addNetAction(Net::Download::makeByteArray(versions_url, response)); + + QObject::connect(netJob, &NetJob::succeeded, [=] { + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response for getting versions at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *response; + return; + } + + callbacks.on_succeed(doc, args.addonId); + }); + + QObject::connect(netJob, &NetJob::finished, [response] { + delete response; + }); + + return netJob; +} + +NetJob::Ptr NetworkResourceAPI::getProject(QString addonId, QByteArray* response) const +{ + auto project_url_optional = getInfoURL(addonId); + if (!project_url_optional.has_value()) + return nullptr; + + auto project_url = project_url_optional.value(); + + auto netJob = new NetJob(QString("%1::GetProject").arg(addonId), APPLICATION->network()); + + netJob->addNetAction(Net::Download::makeByteArray(QUrl(project_url), response)); + + QObject::connect(netJob, &NetJob::finished, [response] { + delete response; + }); + + return netJob; +} diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.h b/launcher/modplatform/helpers/NetworkResourceAPI.h new file mode 100644 index 00000000..834f274a --- /dev/null +++ b/launcher/modplatform/helpers/NetworkResourceAPI.h @@ -0,0 +1,18 @@ +#pragma once + +#include "modplatform/ResourceAPI.h" + +class NetworkResourceAPI : public ResourceAPI { + public: + NetJob::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const override; + + NetJob::Ptr getProject(QString addonId, QByteArray* response) const override; + + NetJob::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const override; + NetJob::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const override; + + protected: + [[nodiscard]] virtual auto getSearchURL(SearchArgs const& args) const -> std::optional = 0; + [[nodiscard]] virtual auto getInfoURL(QString const& id) const -> std::optional = 0; + [[nodiscard]] virtual auto getVersionsURL(VersionSearchArgs const& args) const -> std::optional = 0; +}; diff --git a/launcher/modplatform/modrinth/ModrinthAPI.cpp b/launcher/modplatform/modrinth/ModrinthAPI.cpp index 747cf4c3..8e64be09 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.cpp +++ b/launcher/modplatform/modrinth/ModrinthAPI.cpp @@ -37,21 +37,24 @@ auto ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_format auto ModrinthAPI::latestVersion(QString hash, QString hash_format, - std::list mcVersions, - ModLoaderTypes loaders, + std::optional> mcVersions, + std::optional loaders, QByteArray* response) -> NetJob::Ptr { auto* netJob = new NetJob(QString("Modrinth::GetLatestVersion"), APPLICATION->network()); QJsonObject body_obj; - Json::writeStringList(body_obj, "loaders", getModLoaderStrings(loaders)); + if (loaders.has_value()) + Json::writeStringList(body_obj, "loaders", getModLoaderStrings(loaders.value())); - QStringList game_versions; - for (auto& ver : mcVersions) { - game_versions.append(ver.toString()); + if (mcVersions.has_value()) { + QStringList game_versions; + for (auto& ver : mcVersions.value()) { + game_versions.append(ver.toString()); + } + Json::writeStringList(body_obj, "game_versions", game_versions); } - Json::writeStringList(body_obj, "game_versions", game_versions); QJsonDocument body(body_obj); auto body_raw = body.toJson(); @@ -66,8 +69,8 @@ auto ModrinthAPI::latestVersion(QString hash, auto ModrinthAPI::latestVersions(const QStringList& hashes, QString hash_format, - std::list mcVersions, - ModLoaderTypes loaders, + std::optional> mcVersions, + std::optional loaders, QByteArray* response) -> NetJob::Ptr { auto* netJob = new NetJob(QString("Modrinth::GetLatestVersions"), APPLICATION->network()); @@ -77,13 +80,16 @@ auto ModrinthAPI::latestVersions(const QStringList& hashes, Json::writeStringList(body_obj, "hashes", hashes); Json::writeString(body_obj, "algorithm", hash_format); - Json::writeStringList(body_obj, "loaders", getModLoaderStrings(loaders)); + if (loaders.has_value()) + Json::writeStringList(body_obj, "loaders", getModLoaderStrings(loaders.value())); - QStringList game_versions; - for (auto& ver : mcVersions) { - game_versions.append(ver.toString()); + if (mcVersions.has_value()) { + QStringList game_versions; + for (auto& ver : mcVersions.value()) { + game_versions.append(ver.toString()); + } + Json::writeStringList(body_obj, "game_versions", game_versions); } - Json::writeStringList(body_obj, "game_versions", game_versions); QJsonDocument body(body_obj); auto body_raw = body.toJson(); @@ -95,7 +101,7 @@ auto ModrinthAPI::latestVersions(const QStringList& hashes, return netJob; } -auto ModrinthAPI::getProjects(QStringList addonIds, QByteArray* response) const -> NetJob* +NetJob::Ptr ModrinthAPI::getProjects(QStringList addonIds, QByteArray* response) const { auto netJob = new NetJob(QString("Modrinth::GetProjects"), APPLICATION->network()); auto searchUrl = getMultipleModInfoURL(addonIds); diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index e1a18681..bd84fb54 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -19,13 +19,12 @@ #pragma once #include "BuildConfig.h" -#include "modplatform/ModAPI.h" #include "modplatform/ModIndex.h" -#include "modplatform/helpers/NetworkModAPI.h" +#include "modplatform/helpers/NetworkResourceAPI.h" #include -class ModrinthAPI : public NetworkModAPI { +class ModrinthAPI : public NetworkResourceAPI { public: auto currentVersion(QString hash, QString hash_format, @@ -37,17 +36,17 @@ class ModrinthAPI : public NetworkModAPI { auto latestVersion(QString hash, QString hash_format, - std::list mcVersions, - ModLoaderTypes loaders, + std::optional> mcVersions, + std::optional loaders, QByteArray* response) -> NetJob::Ptr; auto latestVersions(const QStringList& hashes, QString hash_format, - std::list mcVersions, - ModLoaderTypes loaders, + std::optional> mcVersions, + std::optional loaders, QByteArray* response) -> NetJob::Ptr; - auto getProjects(QStringList addonIds, QByteArray* response) const -> NetJob* override; + NetJob::Ptr getProjects(QStringList addonIds, QByteArray* response) const override; public: inline auto getAuthorURL(const QString& name) const -> QString { return "https://modrinth.com/user/" + name; }; @@ -55,15 +54,13 @@ class ModrinthAPI : public NetworkModAPI { static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList { QStringList l; - for (auto loader : {Forge, Fabric, Quilt}) - { - if ((types & loader) || types == Unspecified) - { - l << ModAPI::getModLoaderString(loader); + for (auto loader : {Forge, Fabric, Quilt}) { + if (types & loader) { + l << getModLoaderString(loader); } } if ((types & Quilt) && (~types & Fabric)) // Add Fabric if Quilt is in use, if Fabric isn't already there - l << ModAPI::getModLoaderString(Fabric); + l << getModLoaderString(Fabric); return l; } @@ -78,28 +75,54 @@ class ModrinthAPI : public NetworkModAPI { } private: - inline auto getModSearchURL(SearchArgs& args) const -> QString override + [[nodiscard]] static QString resourceTypeParameter(ModPlatform::ResourceType type) { - if (!validateModLoaders(args.loaders)) { - qWarning() << "Modrinth only have Forge and Fabric-compatible mods!"; - return ""; + switch (type) { + case ModPlatform::ResourceType::MOD: + return "mod"; + default: + qWarning() << "Invalid resource type for Modrinth API!"; + break; } - return QString(BuildConfig.MODRINTH_PROD_URL + - "/search?" - "offset=%1&" - "limit=25&" - "query=%2&" - "index=%3&" - "facets=[[%4],%5[\"project_type:mod\"]]") - .arg(args.offset) - .arg(args.search) - .arg(args.sorting) - .arg(getModLoaderFilters(args.loaders)) - .arg(getGameVersionsArray(args.versions)); + return ""; + } + [[nodiscard]] QString createFacets(SearchArgs const& args) const + { + QStringList facets_list; + + if (args.loaders.has_value()) + facets_list.append(QString("[%1]").arg(getModLoaderFilters(args.loaders.value()))); + if (args.versions.has_value()) + facets_list.append(QString("[%1]").arg(getGameVersionsArray(args.versions.value()))); + facets_list.append(QString("[\"project_type:%1\"]").arg(resourceTypeParameter(args.type))); + + return QString("[%1]").arg(facets_list.join(',')); + } + + public: + [[nodiscard]] inline auto getSearchURL(SearchArgs const& args) const -> std::optional override + { + if (args.loaders.has_value()) { + if (!validateModLoaders(args.loaders.value())) { + qWarning() << "Modrinth only have Forge and Fabric-compatible mods!"; + return {}; + } + } + + QStringList get_arguments; + get_arguments.append(QString("offset=%1").arg(args.offset)); + get_arguments.append(QString("limit=25")); + if (args.search.has_value()) + get_arguments.append(QString("query=%1").arg(args.search.value())); + if (args.sorting.has_value()) + get_arguments.append(QString("index=%1").arg(args.sorting.value())); + get_arguments.append(QString("facets=%1").arg(createFacets(args))); + + return BuildConfig.MODRINTH_PROD_URL + "/search?" + get_arguments.join('&'); }; - inline auto getModInfoURL(QString& id) const -> QString override + inline auto getInfoURL(QString const& id) const -> std::optional override { return BuildConfig.MODRINTH_PROD_URL + "/project/" + id; }; @@ -109,15 +132,16 @@ class ModrinthAPI : public NetworkModAPI { return BuildConfig.MODRINTH_PROD_URL + QString("/projects?ids=[\"%1\"]").arg(ids.join("\",\"")); }; - inline auto getVersionsURL(VersionSearchArgs& args) const -> QString override + inline auto getVersionsURL(VersionSearchArgs const& args) const -> std::optional override { - return QString(BuildConfig.MODRINTH_PROD_URL + - "/project/%1/version?" - "game_versions=[%2]&" - "loaders=[\"%3\"]") - .arg(args.addonId, - getGameVersionsString(args.mcVersions), - getModLoaderStrings(args.loaders).join("\",\"")); + QStringList get_arguments; + if (args.mcVersions.has_value()) + get_arguments.append(QString("game_versions=[%1]").arg(getGameVersionsString(args.mcVersions.value()))); + if (args.loaders.has_value()) + get_arguments.append(QString("loaders=[\"%1\"]").arg(getModLoaderStrings(args.loaders.value()).join("\",\""))); + + return QString("%1/project/%2/version%3%4") + .arg(BuildConfig.MODRINTH_PROD_URL, args.addonId, get_arguments.isEmpty() ? "" : "?", get_arguments.join('&')); }; auto getGameVersionsArray(std::list mcVersions) const -> QString @@ -127,12 +151,12 @@ class ModrinthAPI : public NetworkModAPI { s += QString("\"versions:%1\",").arg(ver.toString()); } s.remove(s.length() - 1, 1); //remove last comma - return s.isEmpty() ? QString() : QString("[%1],").arg(s); + return s.isEmpty() ? QString() : s; } inline auto validateModLoaders(ModLoaderTypes loaders) const -> bool { - return (loaders == Unspecified) || (loaders & (Forge | Fabric | Quilt)); + return loaders & (Forge | Fabric | Quilt); } }; diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index e2d27547..7826b33d 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -4,12 +4,15 @@ #include "Json.h" -#include "ModDownloadTask.h" +#include "ResourceDownloadTask.h" #include "modplatform/helpers/HashUtils.h" #include "tasks/ConcurrentTask.h" +#include "minecraft/mod/ModFolderModel.h" +#include "minecraft/mod/ResourceFolderModel.h" + static ModrinthAPI api; static ModPlatform::ProviderCapabilities ProviderCaps; @@ -34,7 +37,7 @@ void ModrinthCheckUpdate::executeTask() // Create all hashes QStringList hashes; - auto best_hash_type = ProviderCaps.hashType(ModPlatform::Provider::MODRINTH).first(); + auto best_hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first(); ConcurrentTask hashing_task(this, "MakeModrinthHashesTask", 10); for (auto* mod : m_mods) { @@ -108,11 +111,13 @@ void ModrinthCheckUpdate::executeTask() // Sometimes a version may have multiple files, one with "forge" and one with "fabric", // so we may want to filter it QString loader_filter; - static auto flags = { ModAPI::ModLoaderType::Forge, ModAPI::ModLoaderType::Fabric, ModAPI::ModLoaderType::Quilt }; - for (auto flag : flags) { - if (m_loaders.testFlag(flag)) { - loader_filter = api.getModLoaderString(flag); - break; + if (m_loaders.has_value()) { + static auto flags = { ResourceAPI::ModLoaderType::Forge, ResourceAPI::ModLoaderType::Fabric, ResourceAPI::ModLoaderType::Quilt }; + for (auto flag : flags) { + if (m_loaders.value().testFlag(flag)) { + loader_filter = api.getModLoaderString(flag); + break; + } } } @@ -152,12 +157,12 @@ void ModrinthCheckUpdate::executeTask() for (auto& author : mod->authors()) pack.authors.append({ author }); pack.description = mod->description(); - pack.provider = ModPlatform::Provider::MODRINTH; + pack.provider = ModPlatform::ResourceProvider::MODRINTH; - auto download_task = new ModDownloadTask(pack, project_ver, m_mods_folder); + auto download_task = new ResourceDownloadTask(pack, project_ver, m_mods_folder); m_updatable.emplace_back(pack.name, hash, mod->version(), project_ver.version_number, project_ver.changelog, - ModPlatform::Provider::MODRINTH, download_task); + ModPlatform::ResourceProvider::MODRINTH, download_task); } } } catch (Json::JsonException& e) { diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.h b/launcher/modplatform/modrinth/ModrinthCheckUpdate.h index abf8ada1..177ce516 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.h +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.h @@ -8,7 +8,7 @@ class ModrinthCheckUpdate : public CheckUpdateTask { Q_OBJECT public: - ModrinthCheckUpdate(QList& mods, std::list& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr mods_folder) + ModrinthCheckUpdate(QList& mods, std::list& mcVersions, std::optional loaders, std::shared_ptr mods_folder) : CheckUpdateTask(mods, mcVersions, loaders, mods_folder) {} diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index aec45a73..a0161089 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -33,7 +33,7 @@ void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) if (pack.addonId.toString().isEmpty()) pack.addonId = Json::requireString(obj, "id"); - pack.provider = ModPlatform::Provider::MODRINTH; + pack.provider = ModPlatform::ResourceProvider::MODRINTH; pack.name = Json::requireString(obj, "title"); pack.slug = Json::ensureString(obj, "slug", ""); @@ -179,7 +179,7 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t file.hash = Json::requireString(hash_list, preferred_hash_type); file.hash_type = preferred_hash_type; } else { - auto hash_types = ProviderCaps.hashType(ModPlatform::Provider::MODRINTH); + auto hash_types = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH); for (auto& hash_type : hash_types) { if (hash_list.contains(hash_type)) { file.hash = Json::requireString(hash_list, hash_type); diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 0ed29311..510c7309 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -97,7 +97,7 @@ auto V1::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, Mo mod.name = mod_pack.name; mod.filename = mod_version.fileName; - if (mod_pack.provider == ModPlatform::Provider::FLAME) { + if (mod_pack.provider == ModPlatform::ResourceProvider::FLAME) { mod.mode = "metadata:curseforge"; } else { mod.mode = "url"; @@ -176,11 +176,11 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) in_stream << QString("\n[update]\n"); in_stream << QString("[update.%1]\n").arg(ProviderCaps.name(mod.provider)); switch (mod.provider) { - case (ModPlatform::Provider::FLAME): + 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()); break; - case (ModPlatform::Provider::MODRINTH): + case (ModPlatform::ResourceProvider::MODRINTH): addToStream("mod-id", mod.mod_id().toString()); addToStream("version", mod.version().toString()); break; @@ -273,7 +273,7 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod } { // [update] info - using Provider = ModPlatform::Provider; + using Provider = ModPlatform::ResourceProvider; auto update_table = table["update"]; if (!update_table || !update_table.is_table()) { diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 9754e5c4..4b096eec 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -49,7 +49,7 @@ class V1 { QString hash {}; // [update] - ModPlatform::Provider provider {}; + ModPlatform::ResourceProvider provider {}; QVariant file_id {}; QVariant project_id {}; diff --git a/launcher/net/NetAction.h b/launcher/net/NetAction.h index d9c4fadc..38fe058b 100644 --- a/launcher/net/NetAction.h +++ b/launcher/net/NetAction.h @@ -52,7 +52,6 @@ class NetAction : public Task { virtual ~NetAction() = default; QUrl url() { return m_url; } - auto index() -> int { return m_index_within_job; } void setNetwork(shared_qobject_ptr network) { m_network = network; } @@ -75,9 +74,6 @@ class NetAction : public Task { public: shared_qobject_ptr m_network; - /// index within the parent job, FIXME: nuke - int m_index_within_job = 0; - /// the network reply unique_qobject_ptr m_reply; diff --git a/launcher/net/NetJob.cpp b/launcher/net/NetJob.cpp index 9b5d4f1b..4bcd40b5 100644 --- a/launcher/net/NetJob.cpp +++ b/launcher/net/NetJob.cpp @@ -38,11 +38,10 @@ auto NetJob::addNetAction(NetAction::Ptr action) -> bool { - action->m_index_within_job = m_queue.size(); - m_queue.append(action); - action->setNetwork(m_network); + addTask(action); + return true; } diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp index 8b49bd1a..5977fd10 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.cpp +++ b/launcher/ui/dialogs/BlockedModsDialog.cpp @@ -230,7 +230,7 @@ void BlockedModsDialog::addHashTask(QString path) /// @param path the path to the local file being hashed void BlockedModsDialog::buildHashTask(QString path) { - auto hash_task = Hashing::createBlockedModHasher(path, ModPlatform::Provider::FLAME, "sha1"); + auto hash_task = Hashing::createBlockedModHasher(path, ModPlatform::ResourceProvider::FLAME, "sha1"); qDebug() << "[Blocked Mods Dialog] Creating Hash task for path: " << path; diff --git a/launcher/ui/dialogs/ChooseProviderDialog.cpp b/launcher/ui/dialogs/ChooseProviderDialog.cpp index 89935d9a..83748e1e 100644 --- a/launcher/ui/dialogs/ChooseProviderDialog.cpp +++ b/launcher/ui/dialogs/ChooseProviderDialog.cpp @@ -67,9 +67,9 @@ void ChooseProviderDialog::confirmAll() accept(); } -auto ChooseProviderDialog::getSelectedProvider() const -> ModPlatform::Provider +auto ChooseProviderDialog::getSelectedProvider() const -> ModPlatform::ResourceProvider { - return ModPlatform::Provider(m_providers.checkedId()); + return ModPlatform::ResourceProvider(m_providers.checkedId()); } void ChooseProviderDialog::addProviders() @@ -77,7 +77,7 @@ void ChooseProviderDialog::addProviders() int btn_index = 0; QRadioButton* btn; - for (auto& provider : { ModPlatform::Provider::MODRINTH, ModPlatform::Provider::FLAME }) { + for (auto& provider : { ModPlatform::ResourceProvider::MODRINTH, ModPlatform::ResourceProvider::FLAME }) { btn = new QRadioButton(ProviderCaps.readableName(provider), this); m_providers.addButton(btn, btn_index++); ui->providersLayout->addWidget(btn); diff --git a/launcher/ui/dialogs/ChooseProviderDialog.h b/launcher/ui/dialogs/ChooseProviderDialog.h index 4a3b9f29..be9735b5 100644 --- a/launcher/ui/dialogs/ChooseProviderDialog.h +++ b/launcher/ui/dialogs/ChooseProviderDialog.h @@ -8,7 +8,7 @@ class ChooseProviderDialog; } namespace ModPlatform { -enum class Provider; +enum class ResourceProvider; } class Mod; @@ -24,7 +24,7 @@ class ChooseProviderDialog : public QDialog { bool try_others = false; - ModPlatform::Provider chosen; + ModPlatform::ResourceProvider chosen; }; public: @@ -45,7 +45,7 @@ class ChooseProviderDialog : public QDialog { void addProviders(); void disableInput(); - auto getSelectedProvider() const -> ModPlatform::Provider; + auto getSelectedProvider() const -> ModPlatform::ResourceProvider; private: Ui::ChooseProviderDialog* ui; diff --git a/launcher/ui/dialogs/ModDownloadDialog.cpp b/launcher/ui/dialogs/ModDownloadDialog.cpp index 24d23ba9..8a77ef7f 100644 --- a/launcher/ui/dialogs/ModDownloadDialog.cpp +++ b/launcher/ui/dialogs/ModDownloadDialog.cpp @@ -19,76 +19,24 @@ #include "ModDownloadDialog.h" -#include -#include -#include - #include "Application.h" -#include "ReviewMessageBox.h" - -#include -#include -#include -#include -#include "ModDownloadTask.h" -#include "ui/pages/modplatform/flame/FlameModPage.h" -#include "ui/pages/modplatform/modrinth/ModrinthModPage.h" -#include "ui/widgets/PageContainer.h" +#include "ui/pages/modplatform/flame/FlameResourcePages.h" +#include "ui/pages/modplatform/modrinth/ModrinthResourcePages.h" -ModDownloadDialog::ModDownloadDialog(const std::shared_ptr& mods, QWidget* parent, BaseInstance* instance) - : QDialog(parent), mods(mods), m_verticalLayout(new QVBoxLayout(this)), m_instance(instance) +ModDownloadDialog::ModDownloadDialog(QWidget* parent, const std::shared_ptr& mods, BaseInstance* instance) + : ResourceDownloadDialog(parent, mods), m_instance(instance) { - setObjectName(QStringLiteral("ModDownloadDialog")); - m_verticalLayout->setObjectName(QStringLiteral("verticalLayout")); - - resize(std::max(0.5 * parent->width(), 400.0), std::max(0.75 * parent->height(), 400.0)); - - setWindowIcon(APPLICATION->getThemedIcon("new")); - // NOTE: m_buttons must be initialized before PageContainer, because it indirectly accesses m_buttons through setSuggestedPack! Do not - // move this below. - m_buttons = new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - - m_container = new PageContainer(this); - m_container->setSizePolicy(QSizePolicy::Policy::Preferred, QSizePolicy::Policy::Expanding); - m_container->layout()->setContentsMargins(0, 0, 0, 0); - m_verticalLayout->addWidget(m_container); - - m_container->addButtons(m_buttons); - - connect(m_container, &PageContainer::selectedPageChanged, this, &ModDownloadDialog::selectedPageChanged); - - // Bonk Qt over its stupid head and make sure it understands which button is the default one... - // See: https://stackoverflow.com/questions/24556831/qbuttonbox-set-default-button - auto OkButton = m_buttons->button(QDialogButtonBox::Ok); - OkButton->setEnabled(false); - OkButton->setDefault(true); - OkButton->setAutoDefault(true); - OkButton->setText(tr("Review and confirm")); - OkButton->setShortcut(tr("Ctrl+Return")); - OkButton->setToolTip(tr("Opens a new popup to review your selected mods and confirm your selection. Shortcut: Ctrl+Return")); - connect(OkButton, &QPushButton::clicked, this, &ModDownloadDialog::confirm); - - auto CancelButton = m_buttons->button(QDialogButtonBox::Cancel); - CancelButton->setDefault(false); - CancelButton->setAutoDefault(false); - connect(CancelButton, &QPushButton::clicked, this, &ModDownloadDialog::reject); - - auto HelpButton = m_buttons->button(QDialogButtonBox::Help); - HelpButton->setDefault(false); - HelpButton->setAutoDefault(false); - connect(HelpButton, &QPushButton::clicked, m_container, &PageContainer::help); - - QMetaObject::connectSlotsByName(this); - setWindowModality(Qt::WindowModal); - setWindowTitle(dialogTitle()); + initializeContainer(); + connectButtons(); restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("ModDownloadGeometry").toByteArray())); } -QString ModDownloadDialog::dialogTitle() +void ModDownloadDialog::accept() { - return tr("Download mods"); + APPLICATION->settings()->set("ModDownloadGeometry", saveGeometry().toBase64()); + QDialog::accept(); } void ModDownloadDialog::reject() @@ -97,106 +45,15 @@ void ModDownloadDialog::reject() QDialog::reject(); } -void ModDownloadDialog::confirm() -{ - auto keys = modTask.keys(); - keys.sort(Qt::CaseInsensitive); - - auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm mods to download")); - - for (auto& task : keys) { - confirm_dialog->appendMod({ task, modTask.find(task).value()->getFilename() }); - } - - if (confirm_dialog->exec()) { - auto deselected = confirm_dialog->deselectedMods(); - for (auto name : deselected) { - modTask.remove(name); - } - - this->accept(); - } -} - -void ModDownloadDialog::accept() -{ - APPLICATION->settings()->set("ModDownloadGeometry", saveGeometry().toBase64()); - QDialog::accept(); -} - QList ModDownloadDialog::getPages() { QList pages; - pages.append(ModrinthModPage::create(this, m_instance)); + pages.append(ModrinthModPage::create(this, *m_instance)); if (APPLICATION->capabilities() & Application::SupportsFlame) - pages.append(FlameModPage::create(this, m_instance)); + pages.append(FlameModPage::create(this, *m_instance)); m_selectedPage = dynamic_cast(pages[0]); return pages; } - -void ModDownloadDialog::addSelectedMod(QString name, ModDownloadTask* task) -{ - removeSelectedMod(name); - modTask.insert(name, task); - - m_buttons->button(QDialogButtonBox::Ok)->setEnabled(!modTask.isEmpty()); -} - -void ModDownloadDialog::removeSelectedMod(QString name) -{ - if (modTask.contains(name)) - delete modTask.find(name).value(); - modTask.remove(name); - - m_buttons->button(QDialogButtonBox::Ok)->setEnabled(!modTask.isEmpty()); -} - -bool ModDownloadDialog::isModSelected(QString name, QString filename) const -{ - // FIXME: Is there a way to check for versions without checking the filename - // as a heuristic, other than adding such info to ModDownloadTask itself? - auto iter = modTask.find(name); - return iter != modTask.end() && (iter.value()->getFilename() == filename); -} - -bool ModDownloadDialog::isModSelected(QString name) const -{ - auto iter = modTask.find(name); - return iter != modTask.end(); -} - -const QList ModDownloadDialog::getTasks() -{ - return modTask.values(); -} - -void ModDownloadDialog::selectedPageChanged(BasePage* previous, BasePage* selected) -{ - auto* prev_page = dynamic_cast(previous); - if (!prev_page) { - qCritical() << "Page '" << previous->displayName() << "' in ModDownloadDialog is not a ModPage!"; - return; - } - - m_selectedPage = dynamic_cast(selected); - if (!m_selectedPage) { - qCritical() << "Page '" << selected->displayName() << "' in ModDownloadDialog is not a ModPage!"; - return; - } - - // Same effect as having a global search bar - m_selectedPage->setSearchTerm(prev_page->getSearchTerm()); -} - -bool ModDownloadDialog::selectPage(QString pageId) -{ - return m_container->selectPage(pageId); -} - -ModPage* ModDownloadDialog::getSelectedPage() -{ - return m_selectedPage; -} diff --git a/launcher/ui/dialogs/ModDownloadDialog.h b/launcher/ui/dialogs/ModDownloadDialog.h index fcf6f4fc..19036042 100644 --- a/launcher/ui/dialogs/ModDownloadDialog.h +++ b/launcher/ui/dialogs/ModDownloadDialog.h @@ -19,60 +19,29 @@ #pragma once -#include -#include - -#include "ModDownloadTask.h" #include "minecraft/mod/ModFolderModel.h" -#include "ui/pages/BasePageProvider.h" -namespace Ui -{ -class ModDownloadDialog; -} +#include "ui/dialogs/ResourceDownloadDialog.h" -class PageContainer; class QDialogButtonBox; -class ModPage; -class ModrinthModPage; -class ModDownloadDialog final : public QDialog, public BasePageProvider +class ModDownloadDialog final : public ResourceDownloadDialog { Q_OBJECT public: - explicit ModDownloadDialog(const std::shared_ptr& mods, QWidget* parent, BaseInstance* instance); + explicit ModDownloadDialog(QWidget* parent, const std::shared_ptr& mods, BaseInstance* instance); ~ModDownloadDialog() override = default; - QString dialogTitle() override; - QList getPages() override; - - void addSelectedMod(QString name = QString(), ModDownloadTask* task = nullptr); - void removeSelectedMod(QString name = QString()); - bool isModSelected(QString name, QString filename) const; - bool isModSelected(QString name) const; + //: String that gets appended to the mod download dialog title ("Download " + resourcesString()) + [[nodiscard]] QString resourceString() const override { return tr("mods"); } - const QList getTasks(); - const std::shared_ptr& mods; - - bool selectPage(QString pageId); - ModPage* getSelectedPage(); + QList getPages() override; public slots: - void confirm(); void accept() override; void reject() override; - private slots: - void selectedPageChanged(BasePage* previous, BasePage* selected); - private: - Ui::ModDownloadDialog* ui = nullptr; - PageContainer* m_container = nullptr; - QDialogButtonBox* m_buttons = nullptr; - QVBoxLayout* m_verticalLayout = nullptr; - ModPage* m_selectedPage = nullptr; - - QHash modTask; BaseInstance* m_instance; }; diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 2704243e..4ef42d6c 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -21,6 +21,8 @@ #include #include +#include + static ModPlatform::ProviderCapabilities ProviderCaps; static std::list mcVersions(BaseInstance* inst) @@ -28,7 +30,7 @@ static std::list mcVersions(BaseInstance* inst) return { static_cast(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion() }; } -static ModAPI::ModLoaderTypes mcLoaders(BaseInstance* inst) +static std::optional mcLoaders(BaseInstance* inst) { return { static_cast(inst)->getPackProfile()->getModLoaders() }; } @@ -212,14 +214,14 @@ auto ModUpdateDialog::ensureMetadata() -> bool bool confirm_rest = false; bool try_others_rest = false; bool skip_rest = false; - ModPlatform::Provider provider_rest = ModPlatform::Provider::MODRINTH; + ModPlatform::ResourceProvider provider_rest = ModPlatform::ResourceProvider::MODRINTH; - auto addToTmp = [&](Mod* m, ModPlatform::Provider p) { + auto addToTmp = [&](Mod* m, ModPlatform::ResourceProvider p) { switch (p) { - case ModPlatform::Provider::MODRINTH: + case ModPlatform::ResourceProvider::MODRINTH: modrinth_tmp.push_back(m); break; - case ModPlatform::Provider::FLAME: + case ModPlatform::ResourceProvider::FLAME: flame_tmp.push_back(m); break; } @@ -264,10 +266,10 @@ auto ModUpdateDialog::ensureMetadata() -> bool } if (!modrinth_tmp.empty()) { - auto* modrinth_task = new EnsureMetadataTask(modrinth_tmp, index_dir, ModPlatform::Provider::MODRINTH); + auto* modrinth_task = new EnsureMetadataTask(modrinth_tmp, index_dir, ModPlatform::ResourceProvider::MODRINTH); connect(modrinth_task, &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); connect(modrinth_task, &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) { - onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::Provider::MODRINTH); + onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::MODRINTH); }); if (modrinth_task->getHashingTask()) @@ -277,10 +279,10 @@ auto ModUpdateDialog::ensureMetadata() -> bool } if (!flame_tmp.empty()) { - auto* flame_task = new EnsureMetadataTask(flame_tmp, index_dir, ModPlatform::Provider::FLAME); + auto* flame_task = new EnsureMetadataTask(flame_tmp, index_dir, ModPlatform::ResourceProvider::FLAME); connect(flame_task, &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); connect(flame_task, &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) { - onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::Provider::FLAME); + onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::FLAME); }); if (flame_task->getHashingTask()) @@ -306,28 +308,28 @@ void ModUpdateDialog::onMetadataEnsured(Mod* mod) return; switch (mod->metadata()->provider) { - case ModPlatform::Provider::MODRINTH: + case ModPlatform::ResourceProvider::MODRINTH: m_modrinth_to_update.push_back(mod); break; - case ModPlatform::Provider::FLAME: + case ModPlatform::ResourceProvider::FLAME: m_flame_to_update.push_back(mod); break; } } -ModPlatform::Provider next(ModPlatform::Provider p) +ModPlatform::ResourceProvider next(ModPlatform::ResourceProvider p) { switch (p) { - case ModPlatform::Provider::MODRINTH: - return ModPlatform::Provider::FLAME; - case ModPlatform::Provider::FLAME: - return ModPlatform::Provider::MODRINTH; + case ModPlatform::ResourceProvider::MODRINTH: + return ModPlatform::ResourceProvider::FLAME; + case ModPlatform::ResourceProvider::FLAME: + return ModPlatform::ResourceProvider::MODRINTH; } - return ModPlatform::Provider::FLAME; + return ModPlatform::ResourceProvider::FLAME; } -void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::Provider first_choice) +void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::ResourceProvider first_choice) { if (try_others) { auto index_dir = indexDir(); @@ -368,7 +370,7 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info) QString text = info.changelog; switch (info.provider) { - case ModPlatform::Provider::MODRINTH: { + case ModPlatform::ResourceProvider::MODRINTH: { text = markdownToHTML(info.changelog.toUtf8()); break; } @@ -386,9 +388,9 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info) ui->modTreeWidget->addTopLevelItem(item_top); } -auto ModUpdateDialog::getTasks() -> const QList +auto ModUpdateDialog::getTasks() -> const QList { - QList list; + QList list; auto* item = ui->modTreeWidget->topLevelItem(0); diff --git a/launcher/ui/dialogs/ModUpdateDialog.h b/launcher/ui/dialogs/ModUpdateDialog.h index bd486f0d..3e3dd90d 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.h +++ b/launcher/ui/dialogs/ModUpdateDialog.h @@ -1,7 +1,7 @@ #pragma once #include "BaseInstance.h" -#include "ModDownloadTask.h" +#include "ResourceDownloadTask.h" #include "ReviewMessageBox.h" #include "minecraft/mod/ModFolderModel.h" @@ -25,7 +25,7 @@ class ModUpdateDialog final : public ReviewMessageBox { void appendMod(const CheckUpdateTask::UpdatableMod& info); - const QList getTasks(); + const QList getTasks(); auto indexDir() const -> QDir { return m_mod_model->indexDir(); } auto noUpdates() const -> bool { return m_no_updates; }; @@ -36,7 +36,7 @@ class ModUpdateDialog final : public ReviewMessageBox { private slots: void onMetadataEnsured(Mod*); - void onMetadataFailed(Mod*, bool try_others = false, ModPlatform::Provider first_choice = ModPlatform::Provider::MODRINTH); + void onMetadataFailed(Mod*, bool try_others = false, ModPlatform::ResourceProvider first_choice = ModPlatform::ResourceProvider::MODRINTH); private: QWidget* m_parent; @@ -54,7 +54,7 @@ class ModUpdateDialog final : public ReviewMessageBox { QList> m_failed_metadata; QList> m_failed_check_update; - QHash m_tasks; + QHash m_tasks; BaseInstance* m_instance; bool m_no_updates = false; diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp new file mode 100644 index 00000000..7367548f --- /dev/null +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -0,0 +1,152 @@ +#include "ResourceDownloadDialog.h" + +#include + +#include "Application.h" +#include "ResourceDownloadTask.h" + +#include "ui/dialogs/ReviewMessageBox.h" +#include "ui/pages/modplatform/ResourcePage.h" +#include "ui/widgets/PageContainer.h" + +ResourceDownloadDialog::ResourceDownloadDialog(QWidget* parent, const std::shared_ptr base_model) + : QDialog(parent), m_base_model(base_model), m_buttons(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel), m_vertical_layout(this) +{ + setObjectName(QStringLiteral("ResourceDownloadDialog")); + + resize(std::max(0.5 * parent->width(), 400.0), std::max(0.75 * parent->height(), 400.0)); + + setWindowIcon(APPLICATION->getThemedIcon("new")); + + // Bonk Qt over its stupid head and make sure it understands which button is the default one... + // See: https://stackoverflow.com/questions/24556831/qbuttonbox-set-default-button + auto OkButton = m_buttons.button(QDialogButtonBox::Ok); + OkButton->setEnabled(false); + OkButton->setDefault(true); + OkButton->setAutoDefault(true); + OkButton->setText(tr("Review and confirm")); + OkButton->setShortcut(tr("Ctrl+Return")); + + auto CancelButton = m_buttons.button(QDialogButtonBox::Cancel); + CancelButton->setDefault(false); + CancelButton->setAutoDefault(false); + + auto HelpButton = m_buttons.button(QDialogButtonBox::Help); + HelpButton->setDefault(false); + HelpButton->setAutoDefault(false); + + setWindowModality(Qt::WindowModal); + setWindowTitle(dialogTitle()); +} + +// NOTE: We can't have this in the ctor because PageContainer calls a virtual function, and so +// won't work with subclasses if we put it in this ctor. +void ResourceDownloadDialog::initializeContainer() +{ + m_container = new PageContainer(this); + m_container->setSizePolicy(QSizePolicy::Policy::Preferred, QSizePolicy::Policy::Expanding); + m_container->layout()->setContentsMargins(0, 0, 0, 0); + m_vertical_layout.addWidget(m_container); + + m_container->addButtons(&m_buttons); + + connect(m_container, &PageContainer::selectedPageChanged, this, &ResourceDownloadDialog::selectedPageChanged); +} + +void ResourceDownloadDialog::connectButtons() +{ + auto OkButton = m_buttons.button(QDialogButtonBox::Ok); + OkButton->setToolTip(tr("Opens a new popup to review your selected %1 and confirm your selection. Shortcut: Ctrl+Return").arg(resourceString())); + connect(OkButton, &QPushButton::clicked, this, &ResourceDownloadDialog::confirm); + + auto CancelButton = m_buttons.button(QDialogButtonBox::Cancel); + connect(CancelButton, &QPushButton::clicked, this, &ResourceDownloadDialog::reject); + + auto HelpButton = m_buttons.button(QDialogButtonBox::Help); + connect(HelpButton, &QPushButton::clicked, m_container, &PageContainer::help); +} + +void ResourceDownloadDialog::confirm() +{ + auto keys = m_selected.keys(); + keys.sort(Qt::CaseInsensitive); + + auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourceString())); + + for (auto& task : keys) { + confirm_dialog->appendResource({ task, m_selected.find(task).value()->getFilename() }); + } + + if (confirm_dialog->exec()) { + auto deselected = confirm_dialog->deselectedResources(); + for (auto name : deselected) { + m_selected.remove(name); + } + + this->accept(); + } +} + +bool ResourceDownloadDialog::selectPage(QString pageId) +{ + return m_container->selectPage(pageId); +} + +ResourcePage* ResourceDownloadDialog::getSelectedPage() +{ + return m_selectedPage; +} + +void ResourceDownloadDialog::addResource(QString name, ResourceDownloadTask* task) +{ + removeResource(name); + m_selected.insert(name, task); + + m_buttons.button(QDialogButtonBox::Ok)->setEnabled(!m_selected.isEmpty()); +} + +void ResourceDownloadDialog::removeResource(QString name) +{ + if (m_selected.contains(name)) + m_selected.find(name).value()->deleteLater(); + m_selected.remove(name); + + m_buttons.button(QDialogButtonBox::Ok)->setEnabled(!m_selected.isEmpty()); +} + +bool ResourceDownloadDialog::isSelected(QString name, QString filename) const +{ + auto iter = m_selected.constFind(name); + if (iter == m_selected.constEnd()) + return false; + + // FIXME: Is there a way to check for versions without checking the filename + // as a heuristic, other than adding such info to ResourceDownloadTask itself? + if (!filename.isEmpty()) + return iter.value()->getFilename() == filename; + + return true; +} + +const QList ResourceDownloadDialog::getTasks() +{ + return m_selected.values(); +} + +void ResourceDownloadDialog::selectedPageChanged(BasePage* previous, BasePage* selected) +{ + auto* prev_page = dynamic_cast(previous); + if (!prev_page) { + qCritical() << "Page '" << previous->displayName() << "' in ResourceDownloadDialog is not a ResourcePage!"; + return; + } + + m_selectedPage = dynamic_cast(selected); + if (!m_selectedPage) { + qCritical() << "Page '" << selected->displayName() << "' in ResourceDownloadDialog is not a ResourcePage!"; + return; + } + + // Same effect as having a global search bar + m_selectedPage->setSearchTerm(prev_page->getSearchTerm()); +} diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.h b/launcher/ui/dialogs/ResourceDownloadDialog.h new file mode 100644 index 00000000..d6b3938b --- /dev/null +++ b/launcher/ui/dialogs/ResourceDownloadDialog.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include + +#include "ui/pages/BasePageProvider.h" + +class ResourceDownloadTask; +class ResourcePage; +class ResourceFolderModel; +class PageContainer; +class QVBoxLayout; +class QDialogButtonBox; + +class ResourceDownloadDialog : public QDialog, public BasePageProvider { + Q_OBJECT + + public: + ResourceDownloadDialog(QWidget* parent, const std::shared_ptr base_model); + + void initializeContainer(); + void connectButtons(); + + //: String that gets appended to the download dialog title ("Download " + resourcesString()) + [[nodiscard]] virtual QString resourceString() const { return tr("resources"); } + + QString dialogTitle() override { return tr("Download %1").arg(resourceString()); }; + + bool selectPage(QString pageId); + ResourcePage* getSelectedPage(); + + void addResource(QString name, ResourceDownloadTask* task); + void removeResource(QString name); + [[nodiscard]] bool isSelected(QString name, QString filename = "") const; + + const QList getTasks(); + [[nodiscard]] const std::shared_ptr getBaseModel() const { return m_base_model; } + + protected slots: + void selectedPageChanged(BasePage* previous, BasePage* selected); + + virtual void confirm(); + + protected: + const std::shared_ptr m_base_model; + + PageContainer* m_container = nullptr; + ResourcePage* m_selectedPage = nullptr; + + QDialogButtonBox m_buttons; + QVBoxLayout m_vertical_layout; + + QHash m_selected; +}; diff --git a/launcher/ui/dialogs/ReviewMessageBox.cpp b/launcher/ui/dialogs/ReviewMessageBox.cpp index 7c25c91c..f45a9c4a 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.cpp +++ b/launcher/ui/dialogs/ReviewMessageBox.cpp @@ -25,7 +25,7 @@ auto ReviewMessageBox::create(QWidget* parent, QString&& title, QString&& icon) return new ReviewMessageBox(parent, title, icon); } -void ReviewMessageBox::appendMod(ModInformation&& info) +void ReviewMessageBox::appendResource(ResourceInformation&& info) { auto itemTop = new QTreeWidgetItem(ui->modTreeWidget); itemTop->setCheckState(0, Qt::CheckState::Checked); @@ -39,7 +39,7 @@ void ReviewMessageBox::appendMod(ModInformation&& info) ui->modTreeWidget->addTopLevelItem(itemTop); } -auto ReviewMessageBox::deselectedMods() -> QStringList +auto ReviewMessageBox::deselectedResources() -> QStringList { QStringList list; diff --git a/launcher/ui/dialogs/ReviewMessageBox.h b/launcher/ui/dialogs/ReviewMessageBox.h index 9cfa679a..e2d0ce37 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.h +++ b/launcher/ui/dialogs/ReviewMessageBox.h @@ -12,15 +12,15 @@ class ReviewMessageBox : public QDialog { public: static auto create(QWidget* parent, QString&& title, QString&& icon = "") -> ReviewMessageBox*; - using ModInformation = struct { + using ResourceInformation = struct { QString name; QString filename; }; - void appendMod(ModInformation&& info); - auto deselectedMods() -> QStringList; + void appendResource(ResourceInformation&& info); + auto deselectedResources() -> QStringList; - ~ReviewMessageBox(); + ~ReviewMessageBox() override; protected: ReviewMessageBox(QWidget* parent, const QString& title, const QString& icon); diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 627e71e5..1bce3c0d 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -59,7 +59,7 @@ #include "minecraft/mod/Mod.h" #include "minecraft/mod/ModFolderModel.h" -#include "modplatform/ModAPI.h" +#include "modplatform/ResourceAPI.h" #include "Version.h" #include "tasks/ConcurrentTask.h" @@ -153,12 +153,12 @@ void ModFolderPage::installMods() return; // this is a null instance or a legacy instance auto profile = static_cast(m_instance)->getPackProfile(); - if (profile->getModLoaders() == ModAPI::Unspecified) { + if (!profile->getModLoaders().has_value()) { QMessageBox::critical(this, tr("Error"), tr("Please install a mod loader first!")); return; } - ModDownloadDialog mdownload(m_model, this, m_instance); + ModDownloadDialog mdownload(this, m_model, m_instance); if (mdownload.exec()) { ConcurrentTask* tasks = new ConcurrentTask(this); connect(tasks, &Task::failed, [this, tasks](QString reason) { diff --git a/launcher/ui/pages/instance/ResourcePackPage.h b/launcher/ui/pages/instance/ResourcePackPage.h index 9633e3b4..db8af0c5 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.h +++ b/launcher/ui/pages/instance/ResourcePackPage.h @@ -73,3 +73,4 @@ public: return true; } }; + diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index ed58eb32..31aae746 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -1,226 +1,81 @@ #include "ModModel.h" -#include "BuildConfig.h" #include "Json.h" #include "ModPage.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" -#include "ui/dialogs/ModDownloadDialog.h" - -#include "ui/widgets/ProjectItem.h" #include namespace ModPlatform { -// HACK: We need this to prevent callbacks from calling the ListModel after it has already been deleted. -// This leaks a tiny bit of memory per time the user has opened the mod dialog. How to make this better? -static QHash s_running; - -ListModel::ListModel(ModPage* parent) : QAbstractListModel(parent), m_parent(parent) { s_running.insert(this, true); } - -ListModel::~ListModel() -{ - s_running.find(this).value() = false; -} - -auto ListModel::debugName() const -> QString -{ - return m_parent->debugName(); -} +ListModel::ListModel(ModPage* parent, ResourceAPI* api) : ResourceModel(parent, api) {} /******** Make data requests ********/ -void ListModel::fetchMore(const QModelIndex& parent) +ResourceAPI::SearchArgs ListModel::createSearchArguments() { - if (parent.isValid()) - return; - if (nextSearchOffset == 0) { - qWarning() << "fetchMore with 0 offset is wrong..."; - return; - } - performPaginatedSearch(); + auto profile = static_cast(m_associated_page->m_base_instance).getPackProfile(); + return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, + getSorts()[currentSort], profile->getModLoaders(), getMineVersions() }; } - -auto ListModel::data(const QModelIndex& index, int role) const -> QVariant +ResourceAPI::SearchCallbacks ListModel::createSearchCallbacks() { - int pos = index.row(); - if (pos >= modpacks.size() || pos < 0 || !index.isValid()) { - return QString("INVALID INDEX %1").arg(pos); - } - - ModPlatform::IndexedPack pack = modpacks.at(pos); - switch (role) { - case Qt::ToolTipRole: { - if (pack.description.length() > 100) { - // some magic to prevent to long tooltips and replace html linebreaks - QString edit = pack.description.left(97); - edit = edit.left(edit.lastIndexOf("
")).left(edit.lastIndexOf(" ")).append("..."); - return edit; - } - return pack.description; - } - case Qt::DecorationRole: { - if (m_logoMap.contains(pack.logoName)) { - return m_logoMap.value(pack.logoName); - } - QIcon icon = APPLICATION->getThemedIcon("screenshot-placeholder"); - // un-const-ify this - ((ListModel*)this)->requestLogo(pack.logoName, pack.logoUrl); - return icon; - } - case Qt::SizeHintRole: - return QSize(0, 58); - case Qt::UserRole: { - QVariant v; - v.setValue(pack); - return v; - } - // Custom data - case UserDataTypes::TITLE: - return pack.name; - case UserDataTypes::DESCRIPTION: - return pack.description; - case UserDataTypes::SELECTED: - return m_parent->getDialog()->isModSelected(pack.name); - default: - break; - } - - return {}; + return { [this](auto& doc) { + if (!s_running_models.constFind(this).value()) + return; + searchRequestFinished(doc); + } }; } -bool ListModel::setData(const QModelIndex &index, const QVariant &value, int role) +ResourceAPI::VersionSearchArgs ListModel::createVersionsArguments(QModelIndex& entry) { - int pos = index.row(); - if (pos >= modpacks.size() || pos < 0 || !index.isValid()) - return false; + auto const& pack = m_packs[entry.row()]; + auto profile = static_cast(m_associated_page->m_base_instance).getPackProfile(); - modpacks[pos] = value.value(); - - return true; + return { pack.addonId.toString(), getMineVersions(), profile->getModLoaders() }; } - -void ListModel::requestModVersions(ModPlatform::IndexedPack const& current, QModelIndex index) +ResourceAPI::VersionSearchCallbacks ListModel::createVersionsCallbacks(QModelIndex& entry) { - auto profile = (dynamic_cast((dynamic_cast(parent()))->m_instance))->getPackProfile(); - - m_parent->apiProvider()->getVersions({ current.addonId.toString(), getMineVersions(), profile->getModLoaders() }, - [this, current, index](QJsonDocument& doc, QString addonId) { - if (!s_running.constFind(this).value()) - return; - versionRequestSucceeded(doc, addonId, index); - }); -} - -void ListModel::performPaginatedSearch() -{ - auto profile = (dynamic_cast((dynamic_cast(parent()))->m_instance))->getPackProfile(); + auto const& pack = m_packs[entry.row()]; - m_parent->apiProvider()->searchMods( - this, { nextSearchOffset, currentSearchTerm, getSorts()[currentSort], profile->getModLoaders(), getMineVersions() }); + return { [this, pack, entry](auto& doc, auto addonId) { + if (!s_running_models.constFind(this).value()) + return; + versionRequestSucceeded(doc, addonId, entry); + } }; } -void ListModel::requestModInfo(ModPlatform::IndexedPack& current, QModelIndex index) +ResourceAPI::ProjectInfoArgs ListModel::createInfoArguments(QModelIndex& entry) { - m_parent->apiProvider()->getModInfo(current, [this, index](QJsonDocument& doc, ModPlatform::IndexedPack& pack) { - if (!s_running.constFind(this).value()) - return; - infoRequestFinished(doc, pack, index); - }); + auto& pack = m_packs[entry.row()]; + return { pack }; } - -void ListModel::refresh() +ResourceAPI::ProjectInfoCallbacks ListModel::createInfoCallbacks(QModelIndex& entry) { - if (jobPtr) { - jobPtr->abort(); - searchState = ResetRequested; - return; - } else { - beginResetModel(); - modpacks.clear(); - endResetModel(); - searchState = None; - } - nextSearchOffset = 0; - performPaginatedSearch(); + return { [this, entry](auto& doc, auto& pack) { + if (!s_running_models.constFind(this).value()) + return; + infoRequestFinished(doc, pack, entry); + } }; } void ListModel::searchWithTerm(const QString& term, const int sort, const bool filter_changed) { - if (currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull() && currentSort == sort && !filter_changed) { + if (m_search_term == term && m_search_term.isNull() == term.isNull() && currentSort == sort && !filter_changed) { return; } - currentSearchTerm = term; + setSearchTerm(term); currentSort = sort; refresh(); } -void ListModel::getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback) -{ - if (m_logoMap.contains(logo)) { - callback(APPLICATION->metacache() - ->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo.section(".", 0, 0))) - ->getFullPath()); - } else { - requestLogo(logo, logoUrl); - } -} - -void ListModel::requestLogo(QString logo, QString url) -{ - if (m_loadingLogos.contains(logo) || m_failedLogos.contains(logo) || url.isEmpty()) { - return; - } - - MetaEntryPtr entry = - APPLICATION->metacache()->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo.section(".", 0, 0))); - auto job = new NetJob(QString("%1 Icon Download %2").arg(m_parent->debugName()).arg(logo), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); - - auto fullPath = entry->getFullPath(); - QObject::connect(job, &NetJob::succeeded, this, [this, logo, fullPath, job] { - job->deleteLater(); - emit logoLoaded(logo, QIcon(fullPath)); - if (waitingCallbacks.contains(logo)) { - waitingCallbacks.value(logo)(fullPath); - } - }); - - QObject::connect(job, &NetJob::failed, this, [this, logo, job] { - job->deleteLater(); - emit logoFailed(logo); - }); - - job->start(); - m_loadingLogos.append(logo); -} - /******** Request callbacks ********/ -void ListModel::logoLoaded(QString logo, QIcon out) -{ - m_loadingLogos.removeAll(logo); - m_logoMap.insert(logo, out); - for (int i = 0; i < modpacks.size(); i++) { - if (modpacks[i].logoName == logo) { - emit dataChanged(createIndex(i, 0), createIndex(i, 0), { Qt::DecorationRole }); - } - } -} - -void ListModel::logoFailed(QString logo) -{ - m_failedLogos.append(logo); - m_loadingLogos.removeAll(logo); -} - void ListModel::searchRequestFinished(QJsonDocument& doc) { - jobPtr.reset(); - QList newList; auto packs = documentToArray(doc); @@ -232,62 +87,27 @@ void ListModel::searchRequestFinished(QJsonDocument& doc) loadIndexedPack(pack, packObj); newList.append(pack); } catch (const JSONValidationError& e) { - qWarning() << "Error while loading mod from " << m_parent->debugName() << ": " << e.cause(); + qWarning() << "Error while loading mod from " << m_associated_page->debugName() << ": " << e.cause(); continue; } } if (packs.size() < 25) { - searchState = Finished; + m_search_state = SearchState::Finished; } else { - nextSearchOffset += 25; - searchState = CanPossiblyFetchMore; + m_next_search_offset += 25; + m_search_state = SearchState::CanFetchMore; } // When you have a Qt build with assertions turned on, proceeding here will abort the application if (newList.size() == 0) return; - beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size() + newList.size() - 1); - modpacks.append(newList); + beginInsertRows(QModelIndex(), m_packs.size(), m_packs.size() + newList.size() - 1); + m_packs.append(newList); endInsertRows(); } -void ListModel::searchRequestFailed(QString reason) -{ - auto failed_action = jobPtr->getFailedActions().at(0); - if (!failed_action->m_reply) { - // Network error - QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load mods.")); - } else if (failed_action->m_reply && failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 409) { - // 409 Gone, notify user to update - QMessageBox::critical(nullptr, tr("Error"), - //: %1 refers to the launcher itself - QString("%1 %2") - .arg(m_parent->displayName()) - .arg(tr("API version too old!\nPlease update %1!").arg(BuildConfig.LAUNCHER_DISPLAYNAME))); - } - - jobPtr.reset(); - searchState = Finished; -} - -void ListModel::searchRequestAborted() -{ - if (searchState != ResetRequested) - qCritical() << "Search task in ModModel aborted by an unknown reason!"; - - // Retry fetching - jobPtr.reset(); - - beginResetModel(); - modpacks.clear(); - endResetModel(); - - nextSearchOffset = 0; - performPaginatedSearch(); -} - void ListModel::infoRequestFinished(QJsonDocument& doc, ModPlatform::IndexedPack& pack, const QModelIndex& index) { qDebug() << "Loading mod info"; @@ -310,12 +130,12 @@ void ListModel::infoRequestFinished(QJsonDocument& doc, ModPlatform::IndexedPack } } - m_parent->updateUi(); + m_associated_page->updateUi(); } void ListModel::versionRequestSucceeded(QJsonDocument doc, QString addonId, const QModelIndex& index) { - auto& current = m_parent->getCurrent(); + auto current = m_associated_page->getCurrentPack(); if (addonId != current.addonId) { return; } @@ -336,15 +156,19 @@ void ListModel::versionRequestSucceeded(QJsonDocument doc, QString addonId, cons qWarning() << "Failed to cache mod versions!"; } - - m_parent->updateModVersions(); + m_associated_page->updateVersionList(); } } // namespace ModPlatform /******** Helpers ********/ -auto ModPlatform::ListModel::getMineVersions() const -> std::list +#define MOD_PAGE(x) static_cast(x) + +auto ModPlatform::ListModel::getMineVersions() const -> std::optional> { - return m_parent->getFilter()->versions; + auto versions = MOD_PAGE(m_associated_page)->getFilter()->versions; + if (!versions.empty()) + return versions; + return {}; } diff --git a/launcher/ui/pages/modplatform/ModModel.h b/launcher/ui/pages/modplatform/ModModel.h index 36840649..7c735d90 100644 --- a/launcher/ui/pages/modplatform/ModModel.h +++ b/launcher/ui/pages/modplatform/ModModel.h @@ -3,90 +3,52 @@ #include #include "modplatform/ModIndex.h" -#include "net/NetJob.h" +#include "modplatform/ResourceAPI.h" + +#include "ui/pages/modplatform/ResourceModel.h" class ModPage; class Version; namespace ModPlatform { -using LogoMap = QMap; -using LogoCallback = std::function; - -class ListModel : public QAbstractListModel { +class ListModel : public ResourceModel { Q_OBJECT public: - ListModel(ModPage* parent); - ~ListModel() override; - - inline auto rowCount(const QModelIndex& parent) const -> int override { return parent.isValid() ? 0 : modpacks.size(); }; - inline auto columnCount(const QModelIndex& parent) const -> int override { return parent.isValid() ? 0 : 1; }; - inline auto flags(const QModelIndex& index) const -> Qt::ItemFlags override { return QAbstractListModel::flags(index); }; - - auto debugName() const -> QString; - - /* Retrieve information from the model at a given index with the given role */ - auto data(const QModelIndex& index, int role) const -> QVariant override; - bool setData(const QModelIndex &index, const QVariant &value, int role) override; - - inline void setActiveJob(NetJob::Ptr ptr) { jobPtr = ptr; } - inline NetJob* activeJob() { return jobPtr.get(); } + ListModel(ModPage* parent, ResourceAPI* api); /* Ask the API for more information */ - void fetchMore(const QModelIndex& parent) override; - void refresh(); void searchWithTerm(const QString& term, const int sort, const bool filter_changed); - void requestModInfo(ModPlatform::IndexedPack& current, QModelIndex index); - void requestModVersions(const ModPlatform::IndexedPack& current, QModelIndex index); virtual void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) = 0; virtual void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) = 0; virtual void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) = 0; - void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback); - - inline auto canFetchMore(const QModelIndex& parent) const -> bool override { return parent.isValid() ? false : searchState == CanPossiblyFetchMore; }; - public slots: void searchRequestFinished(QJsonDocument& doc); - void searchRequestFailed(QString reason); - void searchRequestAborted(); void infoRequestFinished(QJsonDocument& doc, ModPlatform::IndexedPack& pack, const QModelIndex& index); void versionRequestSucceeded(QJsonDocument doc, QString addonId, const QModelIndex& index); - protected slots: + public slots: + ResourceAPI::SearchArgs createSearchArguments() override; + ResourceAPI::SearchCallbacks createSearchCallbacks() override; - void logoFailed(QString logo); - void logoLoaded(QString logo, QIcon out); + ResourceAPI::VersionSearchArgs createVersionsArguments(QModelIndex&) override; + ResourceAPI::VersionSearchCallbacks createVersionsCallbacks(QModelIndex&) override; - void performPaginatedSearch(); + ResourceAPI::ProjectInfoArgs createInfoArguments(QModelIndex&) override; + ResourceAPI::ProjectInfoCallbacks createInfoCallbacks(QModelIndex&) override; protected: virtual auto documentToArray(QJsonDocument& obj) const -> QJsonArray = 0; virtual auto getSorts() const -> const char** = 0; - void requestLogo(QString file, QString url); - - inline auto getMineVersions() const -> std::list; + inline auto getMineVersions() const -> std::optional>; protected: - ModPage* m_parent; - - QList modpacks; - - LogoMap m_logoMap; - QMap waitingCallbacks; - QStringList m_failedLogos; - QStringList m_loadingLogos; - - QString currentSearchTerm; int currentSort = 0; - int nextSearchOffset = 0; - enum SearchState { None, CanPossiblyFetchMore, ResetRequested, Finished } searchState = None; - - NetJob::Ptr jobPtr; }; } // namespace ModPlatform diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 0f30689e..853f2c54 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -35,59 +35,30 @@ */ #include "ModPage.h" -#include "Application.h" -#include "ui_ModPage.h" +#include "ui_ResourcePage.h" #include #include #include + #include +#include "Application.h" +#include "ResourceDownloadTask.h" + #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" -#include "ui/dialogs/ModDownloadDialog.h" -#include "ui/widgets/ProjectItem.h" -#include "Markdown.h" - -ModPage::ModPage(ModDownloadDialog* dialog, BaseInstance* instance, ModAPI* api) - : QWidget(dialog) - , m_instance(instance) - , ui(new Ui::ModPage) - , dialog(dialog) - , m_fetch_progress(this, false) - , api(api) -{ - ui->setupUi(this); - - connect(ui->searchButton, &QPushButton::clicked, this, &ModPage::triggerSearch); - connect(ui->modFilterButton, &QPushButton::clicked, this, &ModPage::filterMods); - connect(ui->packView, &QListView::doubleClicked, this, &ModPage::onModSelected); - - m_search_timer.setTimerType(Qt::TimerType::CoarseTimer); - m_search_timer.setSingleShot(true); - - connect(&m_search_timer, &QTimer::timeout, this, &ModPage::triggerSearch); - - ui->searchEdit->installEventFilter(this); - - ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300); - - m_fetch_progress.hideIfInactive(true); - m_fetch_progress.setFixedHeight(24); - m_fetch_progress.progressFormat(""); - - ui->gridLayout_3->addWidget(&m_fetch_progress, 0, 0, 1, ui->gridLayout_3->columnCount()); - ui->packView->setItemDelegate(new ProjectItemDelegate(this)); - ui->packView->installEventFilter(this); +#include "ui/dialogs/ModDownloadDialog.h" - connect(ui->packDescription, &QTextBrowser::anchorClicked, this, &ModPage::openUrl); -} +#include "ui/pages/modplatform/ModModel.h" -ModPage::~ModPage() +ModPage::ModPage(ModDownloadDialog* dialog, BaseInstance& instance) + : ResourcePage(dialog, instance) { - delete ui; + connect(m_ui->searchButton, &QPushButton::clicked, this, &ModPage::triggerSearch); + connect(m_ui->resourceFilterButton, &QPushButton::clicked, this, &ModPage::filterMods); + connect(m_ui->packView, &QListView::doubleClicked, this, &ModPage::onResourceSelected); } void ModPage::setFilterWidget(unique_qobject_ptr& widget) @@ -97,59 +68,19 @@ void ModPage::setFilterWidget(unique_qobject_ptr& widget) m_filter_widget.swap(widget); - ui->gridLayout_3->addWidget(m_filter_widget.get(), 0, 0, 1, ui->gridLayout_3->columnCount()); + m_ui->gridLayout_3->addWidget(m_filter_widget.get(), 0, 0, 1, m_ui->gridLayout_3->columnCount()); - m_filter_widget->setInstance(static_cast(m_instance)); + m_filter_widget->setInstance(&static_cast(m_base_instance)); m_filter = m_filter_widget->getFilter(); connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, [&]{ - ui->searchButton->setStyleSheet("text-decoration: underline"); + m_ui->searchButton->setStyleSheet("text-decoration: underline"); }); connect(m_filter_widget.get(), &ModFilterWidget::filterUnchanged, this, [&]{ - ui->searchButton->setStyleSheet("text-decoration: none"); + m_ui->searchButton->setStyleSheet("text-decoration: none"); }); } - -/******** Qt things ********/ - -void ModPage::openedImpl() -{ - updateSelectionButton(); - triggerSearch(); -} - -auto ModPage::eventFilter(QObject* watched, QEvent* event) -> bool -{ - if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) { - auto* keyEvent = dynamic_cast(event); - if (keyEvent->key() == Qt::Key_Return) { - triggerSearch(); - keyEvent->accept(); - return true; - } else { - if (m_search_timer.isActive()) - m_search_timer.stop(); - - m_search_timer.start(350); - } - } else if (watched == ui->packView && event->type() == QEvent::KeyPress) { - auto* keyEvent = dynamic_cast(event); - if (keyEvent->key() == Qt::Key_Return) { - onModSelected(); - - // To have the 'select mod' button outlined instead of the 'review and confirm' one - ui->modSelectionButton->setFocus(Qt::FocusReason::ShortcutFocusReason); - ui->packView->setFocus(Qt::FocusReason::NoFocusReason); - - keyEvent->accept(); - return true; - } - } - return QWidget::eventFilter(watched, event); -} - - /******** Callbacks to events in the UI (set up in the derived classes) ********/ void ModPage::filterMods() @@ -163,176 +94,37 @@ void ModPage::triggerSearch() m_filter = m_filter_widget->getFilter(); if (changed) { - ui->packView->clearSelection(); - ui->packDescription->clear(); - ui->versionSelectionBox->clear(); + m_ui->packView->clearSelection(); + m_ui->packDescription->clear(); + m_ui->versionSelectionBox->clear(); updateSelectionButton(); } - listModel->searchWithTerm(getSearchTerm(), ui->sortByBox->currentIndex(), changed); - m_fetch_progress.watch(listModel->activeJob()); -} - -QString ModPage::getSearchTerm() const -{ - return ui->searchEdit->text(); -} -void ModPage::setSearchTerm(QString term) -{ - ui->searchEdit->setText(term); + static_cast(m_model)->searchWithTerm(getSearchTerm(), m_ui->sortByBox->currentIndex(), changed); + m_fetch_progress.watch(&m_model->activeJob()); } -void ModPage::onSelectionChanged(QModelIndex curr, QModelIndex prev) +QMap ModPage::urlHandlers() const { - ui->versionSelectionBox->clear(); - - if (!curr.isValid()) { return; } - - current = listModel->data(curr, Qt::UserRole).value(); - - if (!current.versionsLoaded) { - qDebug() << QString("Loading %1 mod versions").arg(debugName()); - - ui->modSelectionButton->setText(tr("Loading versions...")); - ui->modSelectionButton->setEnabled(false); - - listModel->requestModVersions(current, curr); - } else { - for (int i = 0; i < current.versions.size(); i++) { - ui->versionSelectionBox->addItem(current.versions[i].version, QVariant(i)); - } - if (ui->versionSelectionBox->count() == 0) { ui->versionSelectionBox->addItem(tr("No valid version found."), QVariant(-1)); } - - updateSelectionButton(); - } - - if(!current.extraDataLoaded){ - qDebug() << QString("Loading %1 mod info").arg(debugName()); - - listModel->requestModInfo(current, curr); - } - - updateUi(); -} - -void ModPage::onVersionSelectionChanged(QString data) -{ - if (data.isNull() || data.isEmpty()) { - selectedVersion = -1; - return; - } - selectedVersion = ui->versionSelectionBox->currentData().toInt(); - updateSelectionButton(); -} - -void ModPage::onModSelected() -{ - if (selectedVersion < 0) - return; - - auto& version = current.versions[selectedVersion]; - if (dialog->isModSelected(current.name, version.fileName)) { - dialog->removeSelectedMod(current.name); - } else { - bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); - dialog->addSelectedMod(current.name, new ModDownloadTask(current, version, dialog->mods, is_indexed)); - } - - updateSelectionButton(); - - /* Force redraw on the mods list when the selection changes */ - ui->packView->adjustSize(); -} - -static const QRegularExpression modrinth(QRegularExpression::anchoredPattern("(?:www\\.)?modrinth\\.com\\/mod\\/([^\\/]+)\\/?")); -static const QRegularExpression curseForge(QRegularExpression::anchoredPattern("(?:www\\.)?curseforge\\.com\\/minecraft\\/mc-mods\\/([^\\/]+)\\/?")); -static const QRegularExpression curseForgeOld(QRegularExpression::anchoredPattern("minecraft\\.curseforge\\.com\\/projects\\/([^\\/]+)\\/?")); - -void ModPage::openUrl(const QUrl& url) -{ - // do not allow other url schemes for security reasons - if (!(url.scheme() == "http" || url.scheme() == "https")) { - qWarning() << "Unsupported scheme" << url.scheme(); - return; - } - - // detect mod URLs and search instead - - const QString address = url.host() + url.path(); - QRegularExpressionMatch match; - QString page; - - match = modrinth.match(address); - if (match.hasMatch()) - page = "modrinth"; - else if (APPLICATION->capabilities() & Application::SupportsFlame) { - match = curseForge.match(address); - if (!match.hasMatch()) - match = curseForgeOld.match(address); - - if (match.hasMatch()) - page = "curseforge"; - } - - if (!page.isNull()) { - const QString slug = match.captured(1); - - // ensure the user isn't opening the same mod - if (slug != current.slug) { - dialog->selectPage(page); - - ModPage* newPage = dialog->getSelectedPage(); - - QLineEdit* searchEdit = newPage->ui->searchEdit; - ModPlatform::ListModel* model = newPage->listModel; - QListView* view = newPage->ui->packView; - - auto jump = [url, slug, model, view] { - for (int row = 0; row < model->rowCount({}); row++) { - const QModelIndex index = model->index(row); - const auto pack = model->data(index, Qt::UserRole).value(); - - if (pack.slug == slug) { - view->setCurrentIndex(index); - return; - } - } - - // The final fallback. - QDesktopServices::openUrl(url); - }; - - searchEdit->setText(slug); - newPage->triggerSearch(); - - if (model->activeJob()) - connect(model->activeJob(), &Task::finished, jump); - else - jump(); - - return; - } - } - - // open in the user's web browser - QDesktopServices::openUrl(url); + QMap map; + map.insert(QRegularExpression::anchoredPattern("(?:www\\.)?modrinth\\.com\\/mod\\/([^\\/]+)\\/?"), "modrinth"); + map.insert(QRegularExpression::anchoredPattern("(?:www\\.)?curseforge\\.com\\/minecraft\\/mc-mods\\/([^\\/]+)\\/?"), "curseforge"); + map.insert(QRegularExpression::anchoredPattern("minecraft\\.curseforge\\.com\\/projects\\/([^\\/]+)\\/?"), "curseforge"); + return map; } /******** Make changes to the UI ********/ -void ModPage::retranslate() -{ - ui->retranslateUi(this); -} - -void ModPage::updateModVersions(int prev_count) +void ModPage::updateVersionList() { - auto packProfile = (dynamic_cast(m_instance))->getPackProfile(); + m_ui->versionSelectionBox->clear(); + auto packProfile = (dynamic_cast(m_base_instance)).getPackProfile(); QString mcVersion = packProfile->getComponentVersion("net.minecraft"); - for (int i = 0; i < current.versions.size(); i++) { - auto version = current.versions[i]; + auto current_pack = getCurrentPack(); + for (int i = 0; i < current_pack.versions.size(); i++) { + auto version = current_pack.versions[i]; bool valid = false; for(auto& mcVer : m_filter->versions){ //NOTE: Flame doesn't care about loader, so passing it changes nothing. @@ -344,87 +136,18 @@ void ModPage::updateModVersions(int prev_count) // Only add the version if it's valid or using the 'Any' filter, but never if the version is opted out if ((valid || m_filter->versions.empty()) && !optedOut(version)) - ui->versionSelectionBox->addItem(version.version, QVariant(i)); + m_ui->versionSelectionBox->addItem(version.version, QVariant(i)); } - if (ui->versionSelectionBox->count() == 0 && prev_count != 0) { - ui->versionSelectionBox->addItem(tr("No valid version found!"), QVariant(-1)); - ui->modSelectionButton->setText(tr("Cannot select invalid version :(")); + if (m_ui->versionSelectionBox->count() == 0) { + m_ui->versionSelectionBox->addItem(tr("No valid version found!"), QVariant(-1)); + m_ui->resourceSelectionButton->setText(tr("Cannot select invalid version :(")); } updateSelectionButton(); } - -void ModPage::updateSelectionButton() -{ - if (!isOpened || selectedVersion < 0) { - ui->modSelectionButton->setEnabled(false); - return; - } - - ui->modSelectionButton->setEnabled(true); - auto& version = current.versions[selectedVersion]; - if (!dialog->isModSelected(current.name, version.fileName)) { - ui->modSelectionButton->setText(tr("Select mod for download")); - } else { - ui->modSelectionButton->setText(tr("Deselect mod for download")); - } -} - -void ModPage::updateUi() +void ModPage::addResourceToDialog(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& version) { - QString text = ""; - QString name = current.name; - - if (current.websiteUrl.isEmpty()) - text = name; - else - text = "" + name + ""; - - if (!current.authors.empty()) { - auto authorToStr = [](ModPlatform::ModpackAuthor& author) -> QString { - if (author.url.isEmpty()) { return author.name; } - return QString("%2").arg(author.url, author.name); - }; - QStringList authorStrs; - for (auto& author : current.authors) { - authorStrs.push_back(authorToStr(author)); - } - text += "
" + tr(" by ") + authorStrs.join(", "); - } - - if (current.extraDataLoaded) { - if (!current.extraData.donate.isEmpty()) { - text += "

" + tr("Donate information: "); - auto donateToStr = [](ModPlatform::DonationData& donate) -> QString { - return QString("%2").arg(donate.url, donate.platform); - }; - QStringList donates; - for (auto& donate : current.extraData.donate) { - donates.append(donateToStr(donate)); - } - text += donates.join(", "); - } - - if (!current.extraData.issuesUrl.isEmpty() - || !current.extraData.sourceUrl.isEmpty() - || !current.extraData.wikiUrl.isEmpty() - || !current.extraData.discordUrl.isEmpty()) { - text += "

" + tr("External links:") + "
"; - } - - if (!current.extraData.issuesUrl.isEmpty()) - text += "- " + tr("Issues: %1").arg(current.extraData.issuesUrl) + "
"; - if (!current.extraData.wikiUrl.isEmpty()) - text += "- " + tr("Wiki: %1").arg(current.extraData.wikiUrl) + "
"; - if (!current.extraData.sourceUrl.isEmpty()) - text += "- " + tr("Source code: %1").arg(current.extraData.sourceUrl) + "
"; - if (!current.extraData.discordUrl.isEmpty()) - text += "- " + tr("Discord: %1").arg(current.extraData.discordUrl) + "
"; - } - - text += "
"; - - ui->packDescription->setHtml(text + (current.extraData.body.isEmpty() ? current.description : markdownToHTML(current.extraData.body))); - ui->packDescription->flush(); + bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); + m_parent_dialog->addResource(pack.name, new ResourceDownloadTask(pack, version, m_parent_dialog->getBaseModel(), is_indexed)); } diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h index c9ccbaf2..8c1fec84 100644 --- a/launcher/ui/pages/modplatform/ModPage.h +++ b/launcher/ui/pages/modplatform/ModPage.h @@ -2,104 +2,58 @@ #include -#include "Application.h" -#include "modplatform/ModAPI.h" #include "modplatform/ModIndex.h" -#include "ui/pages/BasePage.h" -#include "ui/pages/modplatform/ModModel.h" + +#include "ui/pages/modplatform/ResourcePage.h" #include "ui/widgets/ModFilterWidget.h" -#include "ui/widgets/ProgressWidget.h" class ModDownloadDialog; namespace Ui { -class ModPage; +class ResourcePage; } /* This page handles most logic related to browsing and selecting mods to download. */ -class ModPage : public QWidget, public BasePage { +class ModPage : public ResourcePage { Q_OBJECT public: template - static T* create(ModDownloadDialog* dialog, BaseInstance* instance) + static T* create(ModDownloadDialog* dialog, BaseInstance& instance) { auto page = new T(dialog, instance); - auto filter_widget = ModFilterWidget::create(static_cast(instance)->getPackProfile()->getComponentVersion("net.minecraft"), page); + auto filter_widget = ModFilterWidget::create(static_cast(instance).getPackProfile()->getComponentVersion("net.minecraft"), page); page->setFilterWidget(filter_widget); return page; } - ~ModPage() override; - - /* Affects what the user sees */ - auto displayName() const -> QString override = 0; - auto icon() const -> QIcon override = 0; - auto id() const -> QString override = 0; - auto helpPage() const -> QString override = 0; + ~ModPage() override = default; - /* Used internally */ - virtual auto metaEntryBase() const -> QString = 0; - virtual auto debugName() const -> QString = 0; + [[nodiscard]] inline QString resourceString() const override { return tr("mod"); } + [[nodiscard]] QMap urlHandlers() const override; - void retranslate() override; + void addResourceToDialog(ModPlatform::IndexedPack&, ModPlatform::IndexedVersion&) override; - void updateUi(); + virtual auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders = {}) const -> bool = 0; - auto shouldDisplay() const -> bool override = 0; - virtual auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderTypes loaders = ModAPI::Unspecified) const -> bool = 0; - virtual bool optedOut(ModPlatform::IndexedVersion& ver) const { return false; }; - - auto apiProvider() -> ModAPI* { return api.get(); }; + [[nodiscard]] bool supportsFiltering() const override { return true; }; auto getFilter() const -> const std::shared_ptr { return m_filter; } - auto getDialog() const -> const ModDownloadDialog* { return dialog; } - - /** Get the current term in the search bar. */ - auto getSearchTerm() const -> QString; - /** Programatically set the term in the search bar. */ - void setSearchTerm(QString); - void setFilterWidget(unique_qobject_ptr&); - auto getCurrent() -> ModPlatform::IndexedPack& { return current; } - void updateModVersions(int prev_count = -1); - - void openedImpl() override; - auto eventFilter(QObject* watched, QEvent* event) -> bool override; - - BaseInstance* m_instance; + public slots: + void updateVersionList() override; protected: - ModPage(ModDownloadDialog* dialog, BaseInstance* instance, ModAPI* api); - void updateSelectionButton(); + ModPage(ModDownloadDialog* dialog, BaseInstance& instance); protected slots: virtual void filterMods(); - void triggerSearch(); - void onSelectionChanged(QModelIndex first, QModelIndex second); - void onVersionSelectionChanged(QString data); - void onModSelected(); - virtual void openUrl(const QUrl& url); + void triggerSearch() override; protected: - Ui::ModPage* ui = nullptr; - ModDownloadDialog* dialog = nullptr; - unique_qobject_ptr m_filter_widget; std::shared_ptr m_filter; - - ProgressWidget m_fetch_progress; - - ModPlatform::ListModel* listModel = nullptr; - ModPlatform::IndexedPack current; - - std::unique_ptr api; - - int selectedVersion = -1; - - // Used to do instant searching with a delay to cache quick changes - QTimer m_search_timer; }; diff --git a/launcher/ui/pages/modplatform/ModPage.ui b/launcher/ui/pages/modplatform/ModPage.ui deleted file mode 100644 index 94365aa5..00000000 --- a/launcher/ui/pages/modplatform/ModPage.ui +++ /dev/null @@ -1,118 +0,0 @@ - - - ModPage - - - - 0 - 0 - 837 - 685 - - - - - - - - - false - - - false - - - - - - - Qt::ScrollBarAlwaysOff - - - true - - - - 48 - 48 - - - - - - - - - - Search - - - - - - - Search for mods... - - - - - - - - - - - - Version selected: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - Select mod for download - - - - - - - - - Filter options - - - - - - - Qt::Vertical - - - - - - - - ProjectDescriptionPage - QTextBrowser -
ui/widgets/ProjectDescriptionPage.h
-
-
- - searchEdit - searchButton - packView - packDescription - sortByBox - versionSelectionBox - - - -
diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp new file mode 100644 index 00000000..d672a2ac --- /dev/null +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -0,0 +1,258 @@ +#include "ResourceModel.h" + +#include +#include +#include +#include +#include + +#include "Application.h" +#include "BuildConfig.h" + +#include "net/Download.h" +#include "net/NetJob.h" + +#include "minecraft/MinecraftInstance.h" +#include "minecraft/PackProfile.h" + +#include "modplatform/ModIndex.h" + +#include "ui/pages/modplatform/ResourcePage.h" +#include "ui/widgets/ProjectItem.h" + +QHash ResourceModel::s_running_models; + +ResourceModel::ResourceModel(ResourcePage* parent, ResourceAPI* api) : QAbstractListModel(), m_api(api), m_associated_page(parent) +{ + s_running_models.insert(this, true); +} + +ResourceModel::~ResourceModel() +{ + s_running_models.find(this).value() = false; +} + +auto ResourceModel::data(const QModelIndex& index, int role) const -> QVariant +{ + int pos = index.row(); + if (pos >= m_packs.size() || pos < 0 || !index.isValid()) { + return QString("INVALID INDEX %1").arg(pos); + } + + auto pack = m_packs.at(pos); + switch (role) { + case Qt::ToolTipRole: { + if (pack.description.length() > 100) { + // some magic to prevent to long tooltips and replace html linebreaks + QString edit = pack.description.left(97); + edit = edit.left(edit.lastIndexOf("
")).left(edit.lastIndexOf(" ")).append("..."); + return edit; + } + return pack.description; + } + case Qt::DecorationRole: { + if (auto icon_or_none = const_cast(this)->getIcon(const_cast(index), pack.logoUrl); + icon_or_none.has_value()) + return icon_or_none.value(); + + return APPLICATION->getThemedIcon("screenshot-placeholder"); + } + case Qt::SizeHintRole: + return QSize(0, 58); + case Qt::UserRole: { + QVariant v; + v.setValue(pack); + return v; + } + // Custom data + case UserDataTypes::TITLE: + return pack.name; + case UserDataTypes::DESCRIPTION: + return pack.description; + case UserDataTypes::SELECTED: + return isPackSelected(pack); + default: + break; + } + + return {}; +} + +bool ResourceModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + int pos = index.row(); + if (pos >= m_packs.size() || pos < 0 || !index.isValid()) + return false; + + m_packs[pos] = value.value(); + + return true; +} + +QString ResourceModel::debugName() const +{ + return m_associated_page->debugName() + " (Model)"; +} + +void ResourceModel::fetchMore(const QModelIndex& parent) +{ + if (parent.isValid()) + return; + + Q_ASSERT(m_next_search_offset != 0); + + search(); +} + +void ResourceModel::search() +{ + if (!m_current_job.isRunning()) + m_current_job.clear(); + + auto args{ createSearchArguments() }; + + auto callbacks{ createSearchCallbacks() }; + Q_ASSERT(callbacks.on_succeed); + + // Use defaults if no callbacks are set + if (!callbacks.on_fail) + callbacks.on_fail = [this](QString reason, int network_error_code) { + if (!s_running_models.constFind(this).value()) + return; + searchRequestFailed(reason, network_error_code); + }; + if (!callbacks.on_abort) + callbacks.on_abort = [this] { + if (!s_running_models.constFind(this).value()) + return; + searchRequestAborted(); + }; + + if (auto job = m_api->searchProjects(std::move(args), std::move(callbacks)); job) + addActiveJob(job); +} + +void ResourceModel::loadEntry(QModelIndex& entry) +{ + auto const& pack = m_packs[entry.row()]; + + if (!m_current_job.isRunning()) + m_current_job.clear(); + + if (!pack.versionsLoaded) { + auto args{ createVersionsArguments(entry) }; + auto callbacks{ createVersionsCallbacks(entry) }; + + if (auto job = m_api->getProjectVersions(std::move(args), std::move(callbacks)); job) + addActiveJob(job); + } + + if (!pack.extraDataLoaded) { + auto args{ createInfoArguments(entry) }; + auto callbacks{ createInfoCallbacks(entry) }; + + if (auto job = m_api->getProjectInfo(std::move(args), std::move(callbacks)); job) + addActiveJob(job); + } +} + +void ResourceModel::refresh() +{ + if (m_current_job.isRunning()) { + m_current_job.abort(); + m_search_state = SearchState::ResetRequested; + return; + } + + clearData(); + m_search_state = SearchState::None; + + m_next_search_offset = 0; + search(); +} + +void ResourceModel::clearData() +{ + beginResetModel(); + m_packs.clear(); + endResetModel(); +} + +std::optional ResourceModel::getIcon(QModelIndex& index, const QUrl& url) +{ + QPixmap pixmap; + if (QPixmapCache::find(url.toString(), &pixmap)) + return { pixmap }; + + if (!m_current_icon_job) + m_current_icon_job = new NetJob("IconJob", APPLICATION->network()); + + if (m_currently_running_icon_actions.contains(url)) + return {}; + if (m_failed_icon_actions.contains(url)) + return {}; + + auto cache_entry = APPLICATION->metacache()->resolveEntry( + m_associated_page->metaEntryBase(), + QString("logos/%1").arg(QString(QCryptographicHash::hash(url.toEncoded(), QCryptographicHash::Algorithm::Sha1).toHex()))); + auto icon_fetch_action = Net::Download::makeCached(url, cache_entry); + + auto full_file_path = cache_entry->getFullPath(); + connect(icon_fetch_action.get(), &NetAction::succeeded, this, [=] { + auto icon = QIcon(full_file_path); + QPixmapCache::insert(url.toString(), icon.pixmap(icon.actualSize({ 64, 64 }))); + + m_currently_running_icon_actions.remove(url); + + emit dataChanged(index, index, { Qt::DecorationRole }); + }); + connect(icon_fetch_action.get(), &NetAction::failed, this, [=] { + m_currently_running_icon_actions.remove(url); + m_failed_icon_actions.insert(url); + }); + + m_currently_running_icon_actions.insert(url); + + m_current_icon_job->addNetAction(icon_fetch_action); + if (!m_current_icon_job->isRunning()) + QMetaObject::invokeMethod(m_current_icon_job.get(), &NetJob::start); + + return {}; +} + +bool ResourceModel::isPackSelected(const ModPlatform::IndexedPack& pack) const +{ + return m_associated_page->isPackSelected(pack); +} + +void ResourceModel::searchRequestFailed(QString reason, int network_error_code) +{ + switch (network_error_code) { + default: + // Network error + QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load mods.")); + break; + case 409: + // 409 Gone, notify user to update + QMessageBox::critical(nullptr, tr("Error"), + //: %1 refers to the launcher itself + QString("%1 %2") + .arg(m_associated_page->displayName()) + .arg(tr("API version too old!\nPlease update %1!").arg(BuildConfig.LAUNCHER_DISPLAYNAME))); + break; + } + + m_search_state = SearchState::Finished; +} + +void ResourceModel::searchRequestAborted() +{ + if (m_search_state != SearchState::ResetRequested) + qCritical() << "Search task in" << debugName() << "aborted by an unknown reason!"; + + // Retry fetching + clearData(); + + m_next_search_offset = 0; + search(); +} diff --git a/launcher/ui/pages/modplatform/ResourceModel.h b/launcher/ui/pages/modplatform/ResourceModel.h new file mode 100644 index 00000000..af0e9f55 --- /dev/null +++ b/launcher/ui/pages/modplatform/ResourceModel.h @@ -0,0 +1,101 @@ +#pragma once + +#include + +#include + +#include "QObjectPtr.h" +#include "modplatform/ResourceAPI.h" +#include "tasks/ConcurrentTask.h" + +class NetJob; +class ResourcePage; +class ResourceAPI; + +namespace ModPlatform { +struct IndexedPack; +} + + +class ResourceModel : public QAbstractListModel { + Q_OBJECT + + public: + ResourceModel(ResourcePage* parent, ResourceAPI* api); + ~ResourceModel() override; + + [[nodiscard]] auto data(const QModelIndex&, int role) const -> QVariant override; + bool setData(const QModelIndex& index, const QVariant& value, int role) override; + + [[nodiscard]] auto debugName() const -> QString; + + [[nodiscard]] inline int rowCount(const QModelIndex& parent) const override { return parent.isValid() ? 0 : m_packs.size(); } + [[nodiscard]] inline int columnCount(const QModelIndex& parent) const override { return parent.isValid() ? 0 : 1; }; + [[nodiscard]] inline auto flags(const QModelIndex& index) const -> Qt::ItemFlags override { return QAbstractListModel::flags(index); }; + + inline void addActiveJob(Task::Ptr ptr) { m_current_job.addTask(ptr); if (!m_current_job.isRunning()) m_current_job.start(); } + inline Task const& activeJob() { return m_current_job; } + + public slots: + void fetchMore(const QModelIndex& parent) override; + [[nodiscard]] inline bool canFetchMore(const QModelIndex& parent) const override + { + return parent.isValid() ? false : m_search_state == SearchState::CanFetchMore; + } + + void setSearchTerm(QString term) { m_search_term = term; } + + virtual ResourceAPI::SearchArgs createSearchArguments() = 0; + virtual ResourceAPI::SearchCallbacks createSearchCallbacks() = 0; + + virtual ResourceAPI::VersionSearchArgs createVersionsArguments(QModelIndex&) = 0; + virtual ResourceAPI::VersionSearchCallbacks createVersionsCallbacks(QModelIndex&) = 0; + + virtual ResourceAPI::ProjectInfoArgs createInfoArguments(QModelIndex&) = 0; + virtual ResourceAPI::ProjectInfoCallbacks createInfoCallbacks(QModelIndex&) = 0; + + /** Requests the API for more entries. */ + virtual void search(); + + /** Applies any processing / extra requests needed to fully load the specified entry's information. */ + virtual void loadEntry(QModelIndex&); + + /** Schedule a refresh, clearing the current state. */ + void refresh(); + + /** Gets the icon at the URL for the given index. If it's not fetched yet, fetch it and update when fisinhed. */ + std::optional getIcon(QModelIndex&, const QUrl&); + + protected: + /** Resets the model's data. */ + void clearData(); + + [[nodiscard]] bool isPackSelected(const ModPlatform::IndexedPack&) const; + + protected: + /* Basic search parameters */ + enum class SearchState { None, CanFetchMore, ResetRequested, Finished } m_search_state = SearchState::None; + int m_next_search_offset = 0; + QString m_search_term; + + std::unique_ptr m_api; + + ConcurrentTask m_current_job; + + shared_qobject_ptr m_current_icon_job; + QSet m_currently_running_icon_actions; + QSet m_failed_icon_actions; + + ResourcePage* m_associated_page = nullptr; + + QList m_packs; + + // HACK: We need this to prevent callbacks from calling the model after it has already been deleted. + // This leaks a tiny bit of memory per time the user has opened a resource dialog. How to make this better? + static QHash s_running_models; + + private: + /* Default search request callbacks */ + void searchRequestFailed(QString reason, int network_error_code); + void searchRequestAborted(); +}; diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp new file mode 100644 index 00000000..3b382d20 --- /dev/null +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -0,0 +1,347 @@ +#include "ResourcePage.h" +#include "ui_ResourcePage.h" + +#include +#include + +#include "Markdown.h" +#include "ResourceDownloadTask.h" + +#include "minecraft/MinecraftInstance.h" + +#include "ui/dialogs/ResourceDownloadDialog.h" +#include "ui/pages/modplatform/ResourceModel.h" +#include "ui/widgets/ProjectItem.h" + +ResourcePage::ResourcePage(ResourceDownloadDialog* parent, BaseInstance& base_instance) + : QWidget(parent), m_base_instance(base_instance), m_ui(new Ui::ResourcePage), m_parent_dialog(parent), m_fetch_progress(this, false) +{ + m_ui->setupUi(this); + + m_ui->searchEdit->installEventFilter(this); + + m_ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300); + + m_search_timer.setTimerType(Qt::TimerType::CoarseTimer); + m_search_timer.setSingleShot(true); + + connect(&m_search_timer, &QTimer::timeout, this, &ResourcePage::triggerSearch); + + m_fetch_progress.hideIfInactive(true); + m_fetch_progress.setFixedHeight(24); + m_fetch_progress.progressFormat(""); + + m_ui->gridLayout_3->addWidget(&m_fetch_progress, 0, 0, 1, m_ui->gridLayout_3->columnCount()); + + m_ui->packView->setItemDelegate(new ProjectItemDelegate(this)); + m_ui->packView->installEventFilter(this); + + connect(m_ui->packDescription, &QTextBrowser::anchorClicked, this, &ResourcePage::openUrl); +} + +ResourcePage::~ResourcePage() +{ + delete m_ui; +} + +void ResourcePage::retranslate() +{ + m_ui->retranslateUi(this); +} + +void ResourcePage::openedImpl() +{ + if (!supportsFiltering()) + m_ui->resourceFilterButton->setVisible(false); + + updateSelectionButton(); + triggerSearch(); +} + +auto ResourcePage::eventFilter(QObject* watched, QEvent* event) -> bool +{ + if (event->type() == QEvent::KeyPress) { + auto* keyEvent = static_cast(event); + if (watched == m_ui->searchEdit) { + if (keyEvent->key() == Qt::Key_Return) { + triggerSearch(); + keyEvent->accept(); + return true; + } else { + if (m_search_timer.isActive()) + m_search_timer.stop(); + + m_search_timer.start(350); + } + } else if (watched == m_ui->packView) { + if (keyEvent->key() == Qt::Key_Return) { + onResourceSelected(); + + // To have the 'select mod' button outlined instead of the 'review and confirm' one + m_ui->resourceSelectionButton->setFocus(Qt::FocusReason::ShortcutFocusReason); + m_ui->packView->setFocus(Qt::FocusReason::NoFocusReason); + + keyEvent->accept(); + return true; + } + } + } + + return QWidget::eventFilter(watched, event); +} + +QString ResourcePage::getSearchTerm() const +{ + return m_ui->searchEdit->text(); +} + +void ResourcePage::setSearchTerm(QString term) +{ + m_ui->searchEdit->setText(term); +} + +ModPlatform::IndexedPack ResourcePage::getCurrentPack() const +{ + return m_model->data(m_ui->packView->currentIndex(), Qt::UserRole).value(); +} + +bool ResourcePage::isPackSelected(const ModPlatform::IndexedPack& pack, int version) const +{ + if (version < 0 || !pack.versionsLoaded) + return m_parent_dialog->isSelected(pack.name); + + return m_parent_dialog->isSelected(pack.name, pack.versions[version].fileName); +} + +void ResourcePage::updateUi() +{ + auto current_pack = getCurrentPack(); + + QString text = ""; + QString name = current_pack.name; + + if (current_pack.websiteUrl.isEmpty()) + text = name; + else + text = "" + name + ""; + + if (!current_pack.authors.empty()) { + auto authorToStr = [](ModPlatform::ModpackAuthor& author) -> QString { + if (author.url.isEmpty()) { + return author.name; + } + return QString("%2").arg(author.url, author.name); + }; + QStringList authorStrs; + for (auto& author : current_pack.authors) { + authorStrs.push_back(authorToStr(author)); + } + text += "
" + tr(" by ") + authorStrs.join(", "); + } + + if (current_pack.extraDataLoaded) { + if (!current_pack.extraData.donate.isEmpty()) { + text += "

" + tr("Donate information: "); + auto donateToStr = [](ModPlatform::DonationData& donate) -> QString { + return QString("%2").arg(donate.url, donate.platform); + }; + QStringList donates; + for (auto& donate : current_pack.extraData.donate) { + donates.append(donateToStr(donate)); + } + text += donates.join(", "); + } + + if (!current_pack.extraData.issuesUrl.isEmpty() || !current_pack.extraData.sourceUrl.isEmpty() || + !current_pack.extraData.wikiUrl.isEmpty() || !current_pack.extraData.discordUrl.isEmpty()) { + text += "

" + tr("External links:") + "
"; + } + + if (!current_pack.extraData.issuesUrl.isEmpty()) + text += "- " + tr("Issues: %1").arg(current_pack.extraData.issuesUrl) + "
"; + if (!current_pack.extraData.wikiUrl.isEmpty()) + text += "- " + tr("Wiki: %1").arg(current_pack.extraData.wikiUrl) + "
"; + if (!current_pack.extraData.sourceUrl.isEmpty()) + text += "- " + tr("Source code: %1").arg(current_pack.extraData.sourceUrl) + "
"; + if (!current_pack.extraData.discordUrl.isEmpty()) + text += "- " + tr("Discord: %1").arg(current_pack.extraData.discordUrl) + "
"; + } + + text += "
"; + + m_ui->packDescription->setHtml( + text + (current_pack.extraData.body.isEmpty() ? current_pack.description : markdownToHTML(current_pack.extraData.body))); + m_ui->packDescription->flush(); +} + +void ResourcePage::updateSelectionButton() +{ + if (!isOpened || m_selected_version_index < 0) { + m_ui->resourceSelectionButton->setEnabled(false); + return; + } + + m_ui->resourceSelectionButton->setEnabled(true); + if (!isPackSelected(getCurrentPack(), m_selected_version_index)) { + m_ui->resourceSelectionButton->setText(tr("Select %1 for download").arg(resourceString())); + } else { + m_ui->resourceSelectionButton->setText(tr("Deselect %1 for download").arg(resourceString())); + } +} + +void ResourcePage::updateVersionList() +{ + auto current_pack = getCurrentPack(); + + m_ui->versionSelectionBox->blockSignals(true); + m_ui->versionSelectionBox->clear(); + m_ui->versionSelectionBox->blockSignals(false); + + for (int i = 0; i < current_pack.versions.size(); i++) { + auto& version = current_pack.versions[i]; + if (optedOut(version)) + continue; + + m_ui->versionSelectionBox->addItem(current_pack.versions[i].version, QVariant(i)); + } + + if (m_ui->versionSelectionBox->count() == 0) { + m_ui->versionSelectionBox->addItem(tr("No valid version found."), QVariant(-1)); + m_ui->resourceSelectionButton->setText(tr("Cannot select invalid version :(")); + } + + updateSelectionButton(); +} + +void ResourcePage::onSelectionChanged(QModelIndex curr, QModelIndex prev) +{ + if (!curr.isValid()) { + return; + } + + auto current_pack = getCurrentPack(); + + bool request_load = false; + if (!current_pack.versionsLoaded) { + m_ui->resourceSelectionButton->setText(tr("Loading versions...")); + m_ui->resourceSelectionButton->setEnabled(false); + + request_load = true; + } else { + updateVersionList(); + } + + if (!current_pack.extraDataLoaded) + request_load = true; + + if (request_load) + m_model->loadEntry(curr); + + updateUi(); +} + +void ResourcePage::onVersionSelectionChanged(QString data) +{ + if (data.isNull() || data.isEmpty()) { + m_selected_version_index = -1; + return; + } + + m_selected_version_index = m_ui->versionSelectionBox->currentData().toInt(); + updateSelectionButton(); +} + +void ResourcePage::addResourceToDialog(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& version) +{ + m_parent_dialog->addResource(pack.name, new ResourceDownloadTask(pack, version, m_parent_dialog->getBaseModel())); +} + +void ResourcePage::removeResourceFromDialog(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion&) +{ + m_parent_dialog->removeResource(pack.name); +} + +void ResourcePage::onResourceSelected() +{ + if (m_selected_version_index < 0) + return; + + auto current_pack = getCurrentPack(); + + auto& version = current_pack.versions[m_selected_version_index]; + if (m_parent_dialog->isSelected(current_pack.name, version.fileName)) + removeResourceFromDialog(current_pack, version); + else + addResourceToDialog(current_pack, version); + + updateSelectionButton(); + + /* Force redraw on the resource list when the selection changes */ + m_ui->packView->adjustSize(); +} + +void ResourcePage::openUrl(const QUrl& url) +{ + // do not allow other url schemes for security reasons + if (!(url.scheme() == "http" || url.scheme() == "https")) { + qWarning() << "Unsupported scheme" << url.scheme(); + return; + } + + // detect URLs and search instead + + const QString address = url.host() + url.path(); + QRegularExpressionMatch match; + QString page; + + for (auto&& [regex, candidate] : urlHandlers().asKeyValueRange()) { + if (match = QRegularExpression(regex).match(address); match.hasMatch()) { + page = candidate; + break; + } + } + + if (!page.isNull()) { + const QString slug = match.captured(1); + + // ensure the user isn't opening the same mod + if (slug != getCurrentPack().slug) { + m_parent_dialog->selectPage(page); + + auto newPage = m_parent_dialog->getSelectedPage(); + + QLineEdit* searchEdit = newPage->m_ui->searchEdit; + auto model = newPage->m_model; + QListView* view = newPage->m_ui->packView; + + auto jump = [url, slug, model, view] { + for (int row = 0; row < model->rowCount({}); row++) { + const QModelIndex index = model->index(row); + const auto pack = model->data(index, Qt::UserRole).value(); + + if (pack.slug == slug) { + view->setCurrentIndex(index); + return; + } + } + + // The final fallback. + QDesktopServices::openUrl(url); + }; + + searchEdit->setText(slug); + newPage->triggerSearch(); + + if (model->activeJob().isRunning()) + connect(&model->activeJob(), &Task::finished, jump); + else + jump(); + + return; + } + } + + // open in the user's web browser + QDesktopServices::openUrl(url); +} diff --git a/launcher/ui/pages/modplatform/ResourcePage.h b/launcher/ui/pages/modplatform/ResourcePage.h new file mode 100644 index 00000000..32aad3d9 --- /dev/null +++ b/launcher/ui/pages/modplatform/ResourcePage.h @@ -0,0 +1,95 @@ +#pragma once + +#include +#include + +#include "modplatform/ModIndex.h" +#include "modplatform/ResourceAPI.h" + +#include "ui/pages/BasePage.h" +#include "ui/widgets/ProgressWidget.h" + +namespace Ui { +class ResourcePage; +} + +class BaseInstance; +class ResourceModel; +class ResourceDownloadDialog; + +class ResourcePage : public QWidget, public BasePage { + Q_OBJECT + public: + ~ResourcePage() override; + + /* Affects what the user sees */ + [[nodiscard]] auto displayName() const -> QString override = 0; + [[nodiscard]] auto icon() const -> QIcon override = 0; + [[nodiscard]] auto id() const -> QString override = 0; + [[nodiscard]] auto helpPage() const -> QString override = 0; + [[nodiscard]] bool shouldDisplay() const override = 0; + + /* Used internally */ + [[nodiscard]] virtual auto metaEntryBase() const -> QString = 0; + [[nodiscard]] virtual auto debugName() const -> QString = 0; + + [[nodiscard]] virtual inline QString resourceString() const { return tr("resource"); } + + /* Features this resource's page supports */ + [[nodiscard]] virtual bool supportsFiltering() const = 0; + + void retranslate() override; + void openedImpl() override; + auto eventFilter(QObject* watched, QEvent* event) -> bool override; + + /** Get the current term in the search bar. */ + [[nodiscard]] auto getSearchTerm() const -> QString; + /** Programatically set the term in the search bar. */ + void setSearchTerm(QString); + + [[nodiscard]] bool isPackSelected(const ModPlatform::IndexedPack&, int version = -1) const; + [[nodiscard]] auto getCurrentPack() const -> ModPlatform::IndexedPack; + + [[nodiscard]] auto getDialog() const -> const ResourceDownloadDialog* { return m_parent_dialog; } + + protected: + ResourcePage(ResourceDownloadDialog* parent, BaseInstance&); + + public slots: + virtual void updateUi(); + virtual void updateSelectionButton(); + virtual void updateVersionList(); + + virtual void addResourceToDialog(ModPlatform::IndexedPack&, ModPlatform::IndexedVersion&); + virtual void removeResourceFromDialog(ModPlatform::IndexedPack&, ModPlatform::IndexedVersion&); + + protected slots: + virtual void triggerSearch() {} + + void onSelectionChanged(QModelIndex first, QModelIndex second); + void onVersionSelectionChanged(QString data); + void onResourceSelected(); + + /** Associates regex expressions to pages in the order they're given in the map. */ + [[nodiscard]] virtual QMap urlHandlers() const = 0; + virtual void openUrl(const QUrl&); + + /** Whether the version is opted out or not. Currently only makes sense in CF. */ + virtual bool optedOut(ModPlatform::IndexedVersion& ver) const { return false; }; + + public: + BaseInstance& m_base_instance; + + protected: + Ui::ResourcePage* m_ui; + + ResourceDownloadDialog* m_parent_dialog = nullptr; + ResourceModel* m_model = nullptr; + + int m_selected_version_index = -1; + + ProgressWidget m_fetch_progress; + + // Used to do instant searching with a delay to cache quick changes + QTimer m_search_timer; +}; diff --git a/launcher/ui/pages/modplatform/ResourcePage.ui b/launcher/ui/pages/modplatform/ResourcePage.ui new file mode 100644 index 00000000..8fe1d613 --- /dev/null +++ b/launcher/ui/pages/modplatform/ResourcePage.ui @@ -0,0 +1,118 @@ + + + ResourcePage + + + + 0 + 0 + 837 + 685 + + + + + + + + + false + + + false + + + + + + + Qt::ScrollBarAlwaysOff + + + true + + + + 48 + 48 + + + + + + + + + + Search + + + + + + + Search for resources... + + + + + + + + + + + + Version selected: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Select resource for download + + + + + + + + + Filter options + + + + + + + Qt::Vertical + + + + + + + + ProjectDescriptionPage + QTextBrowser +
ui/widgets/ProjectDescriptionPage.h
+
+
+ + searchEdit + searchButton + packView + packDescription + sortByBox + versionSelectionBox + + + +
diff --git a/launcher/ui/pages/modplatform/flame/FlameModModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModModel.cpp deleted file mode 100644 index bc2c686c..00000000 --- a/launcher/ui/pages/modplatform/flame/FlameModModel.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "FlameModModel.h" -#include "Json.h" -#include "modplatform/flame/FlameModIndex.h" - -namespace FlameMod { - -// NOLINTNEXTLINE(modernize-avoid-c-arrays) -const char* ListModel::sorts[6]{ "Featured", "Popularity", "LastUpdated", "Name", "Author", "TotalDownloads" }; - -void ListModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) -{ - FlameMod::loadIndexedPack(m, obj); -} - -// We already deal with the URLs when initializing the pack, due to the API response's structure -void ListModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) -{ - FlameMod::loadBody(m, obj); -} - -void ListModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) -{ - FlameMod::loadIndexedPackVersions(m, arr, APPLICATION->network(), m_parent->m_instance); -} - -auto ListModel::documentToArray(QJsonDocument& obj) const -> QJsonArray -{ - return Json::ensureArray(obj.object(), "data"); -} - -} // namespace FlameMod diff --git a/launcher/ui/pages/modplatform/flame/FlameModModel.h b/launcher/ui/pages/modplatform/flame/FlameModModel.h deleted file mode 100644 index 6a6aef2e..00000000 --- a/launcher/ui/pages/modplatform/flame/FlameModModel.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "FlameModPage.h" - -namespace FlameMod { - -class ListModel : public ModPlatform::ListModel { - Q_OBJECT - - public: - ListModel(FlameModPage* parent) : ModPlatform::ListModel(parent) {} - ~ListModel() override = default; - - private: - void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override; - void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override; - void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override; - - auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; - - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - static const char* sorts[6]; - inline auto getSorts() const -> const char** override { return sorts; }; -}; - -} // namespace FlameMod diff --git a/launcher/ui/pages/modplatform/flame/FlameModPage.cpp b/launcher/ui/pages/modplatform/flame/FlameModPage.cpp deleted file mode 100644 index bad78c97..00000000 --- a/launcher/ui/pages/modplatform/flame/FlameModPage.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (C) 2022 TheKodeToad - * - * 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 . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "FlameModPage.h" -#include "ui_ModPage.h" - -#include "FlameModModel.h" -#include "ui/dialogs/ModDownloadDialog.h" - -FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance* instance) - : ModPage(dialog, instance, new FlameAPI()) -{ - listModel = new FlameMod::ListModel(this); - ui->packView->setModel(listModel); - - // index is used to set the sorting with the flame api - ui->sortByBox->addItem(tr("Sort by Featured")); - ui->sortByBox->addItem(tr("Sort by Popularity")); - ui->sortByBox->addItem(tr("Sort by Last Updated")); - ui->sortByBox->addItem(tr("Sort by Name")); - ui->sortByBox->addItem(tr("Sort by Author")); - ui->sortByBox->addItem(tr("Sort by Downloads")); - - // sometimes Qt just ignores virtual slots and doesn't work as intended it seems, - // so it's best not to connect them in the parent's contructor... - connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch())); - connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FlameModPage::onSelectionChanged); - connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FlameModPage::onVersionSelectionChanged); - connect(ui->modSelectionButton, &QPushButton::clicked, this, &FlameModPage::onModSelected); - - ui->packDescription->setMetaEntry(metaEntryBase()); -} - -auto FlameModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderTypes loaders) const -> bool -{ - Q_UNUSED(loaders); - return ver.mcVersion.contains(mineVer) && !ver.downloadUrl.isEmpty(); -} - -bool FlameModPage::optedOut(ModPlatform::IndexedVersion& ver) const -{ - return ver.downloadUrl.isEmpty(); -} - -// I don't know why, but doing this on the parent class makes it so that -// other mod providers start loading before being selected, at least with -// my Qt, so we need to implement this in every derived class... -auto FlameModPage::shouldDisplay() const -> bool { return true; } - -void FlameModPage::openUrl(const QUrl& url) -{ - if (url.scheme().isEmpty()) { - QString query = url.query(QUrl::FullyDecoded); - - if (query.startsWith("remoteUrl=")) { - // attempt to resolve url from warning page - query.remove(0, 10); - ModPage::openUrl({QUrl::fromPercentEncoding(query.toUtf8())}); // double decoding is necessary - return; - } - } - - ModPage::openUrl(url); -} diff --git a/launcher/ui/pages/modplatform/flame/FlameModPage.h b/launcher/ui/pages/modplatform/flame/FlameModPage.h deleted file mode 100644 index 58479ab9..00000000 --- a/launcher/ui/pages/modplatform/flame/FlameModPage.h +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (C) 2022 TheKodeToad - * - * 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 . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "modplatform/ModAPI.h" -#include "ui/pages/modplatform/ModPage.h" - -#include "modplatform/flame/FlameAPI.h" - -class FlameModPage : public ModPage { - Q_OBJECT - - public: - static FlameModPage* create(ModDownloadDialog* dialog, BaseInstance* instance) - { - return ModPage::create(dialog, instance); - } - - FlameModPage(ModDownloadDialog* dialog, BaseInstance* instance); - ~FlameModPage() override = default; - - inline auto displayName() const -> QString override { return "CurseForge"; } - inline auto icon() const -> QIcon override { return APPLICATION->getThemedIcon("flame"); } - inline auto id() const -> QString override { return "curseforge"; } - inline auto helpPage() const -> QString override { return "Mod-platform"; } - - inline auto debugName() const -> QString override { return "Flame"; } - inline auto metaEntryBase() const -> QString override { return "FlameMods"; }; - - auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderTypes loaders = ModAPI::Unspecified) const -> bool override; - bool optedOut(ModPlatform::IndexedVersion& ver) const override; - - auto shouldDisplay() const -> bool override; - - void openUrl(const QUrl& url) override; -}; diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp new file mode 100644 index 00000000..b602dfac --- /dev/null +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp @@ -0,0 +1,31 @@ +#include "FlameResourceModels.h" +#include "Json.h" +#include "modplatform/flame/FlameModIndex.h" + +namespace FlameMod { + +// NOLINTNEXTLINE(modernize-avoid-c-arrays) +const char* ListModel::sorts[6]{ "Featured", "Popularity", "LastUpdated", "Name", "Author", "TotalDownloads" }; + +void ListModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) +{ + FlameMod::loadIndexedPack(m, obj); +} + +// We already deal with the URLs when initializing the pack, due to the API response's structure +void ListModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) +{ + FlameMod::loadBody(m, obj); +} + +void ListModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) +{ + FlameMod::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_associated_page->m_base_instance); +} + +auto ListModel::documentToArray(QJsonDocument& obj) const -> QJsonArray +{ + return Json::ensureArray(obj.object(), "data"); +} + +} // namespace FlameMod diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h new file mode 100644 index 00000000..b94377d3 --- /dev/null +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h @@ -0,0 +1,26 @@ +#pragma once + +#include "modplatform/flame/FlameAPI.h" + +namespace FlameMod { + +class ListModel : public ModPlatform::ListModel { + Q_OBJECT + + public: + ListModel(FlameModPage* parent) : ModPlatform::ListModel(parent, new FlameAPI) {} + ~ListModel() override = default; + + private: + void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override; + void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override; + void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override; + + auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; + + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + static const char* sorts[6]; + inline auto getSorts() const -> const char** override { return sorts; }; +}; + +} // namespace FlameMod diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp new file mode 100644 index 00000000..490578ad --- /dev/null +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad + * + * 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 . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FlameResourcePages.h" +#include "ui_ResourcePage.h" + +#include "FlameResourceModels.h" +#include "ui/dialogs/ModDownloadDialog.h" + +FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) + : ModPage(dialog, instance) +{ + m_model = new FlameMod::ListModel(this); + m_ui->packView->setModel(m_model); + + // index is used to set the sorting with the flame api + m_ui->sortByBox->addItem(tr("Sort by Featured")); + m_ui->sortByBox->addItem(tr("Sort by Popularity")); + m_ui->sortByBox->addItem(tr("Sort by Last Updated")); + m_ui->sortByBox->addItem(tr("Sort by Name")); + m_ui->sortByBox->addItem(tr("Sort by Author")); + m_ui->sortByBox->addItem(tr("Sort by Downloads")); + + // sometimes Qt just ignores virtual slots and doesn't work as intended it seems, + // so it's best not to connect them in the parent's contructor... + connect(m_ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch())); + connect(m_ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FlameModPage::onSelectionChanged); + connect(m_ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FlameModPage::onVersionSelectionChanged); + connect(m_ui->resourceSelectionButton, &QPushButton::clicked, this, &FlameModPage::onResourceSelected); + + m_ui->packDescription->setMetaEntry(metaEntryBase()); +} + +auto FlameModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders) const -> bool +{ + Q_UNUSED(loaders); + return ver.mcVersion.contains(mineVer) && !ver.downloadUrl.isEmpty(); +} + +bool FlameModPage::optedOut(ModPlatform::IndexedVersion& ver) const +{ + return ver.downloadUrl.isEmpty(); +} + +// I don't know why, but doing this on the parent class makes it so that +// other mod providers start loading before being selected, at least with +// my Qt, so we need to implement this in every derived class... +auto FlameModPage::shouldDisplay() const -> bool { return true; } + +void FlameModPage::openUrl(const QUrl& url) +{ + if (url.scheme().isEmpty()) { + QString query = url.query(QUrl::FullyDecoded); + + if (query.startsWith("remoteUrl=")) { + // attempt to resolve url from warning page + query.remove(0, 10); + ModPage::openUrl({QUrl::fromPercentEncoding(query.toUtf8())}); // double decoding is necessary + return; + } + } + + ModPage::openUrl(url); +} diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h new file mode 100644 index 00000000..597a0c25 --- /dev/null +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad + * + * 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 . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "Application.h" + +#include "modplatform/ResourceAPI.h" + +#include "ui/pages/modplatform/ModPage.h" + +class FlameModPage : public ModPage { + Q_OBJECT + + public: + static FlameModPage* create(ModDownloadDialog* dialog, BaseInstance& instance) + { + return ModPage::create(dialog, instance); + } + + FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance); + ~FlameModPage() override = default; + + inline auto displayName() const -> QString override { return "CurseForge"; } + inline auto icon() const -> QIcon override { return APPLICATION->getThemedIcon("flame"); } + inline auto id() const -> QString override { return "curseforge"; } + inline auto helpPage() const -> QString override { return "Mod-platform"; } + + inline auto debugName() const -> QString override { return "Flame"; } + inline auto metaEntryBase() const -> QString override { return "FlameMods"; }; + + auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders = {}) const -> bool override; + bool optedOut(ModPlatform::IndexedVersion& ver) const override; + + auto shouldDisplay() const -> bool override; + + void openUrl(const QUrl& url) override; +}; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModModel.cpp deleted file mode 100644 index af92e63e..00000000 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModModel.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * PolyMC - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu - * - * 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 "ModrinthModModel.h" - -#include "modplatform/modrinth/ModrinthPackIndex.h" - -namespace Modrinth { - -// NOLINTNEXTLINE(modernize-avoid-c-arrays) -const char* ListModel::sorts[5]{ "relevance", "downloads", "follows", "updated", "newest" }; - -void ListModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) -{ - Modrinth::loadIndexedPack(m, obj); -} - -void ListModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) -{ - Modrinth::loadExtraPackData(m, obj); -} - -void ListModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) -{ - Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), m_parent->m_instance); -} - -auto ListModel::documentToArray(QJsonDocument& obj) const -> QJsonArray -{ - return obj.object().value("hits").toArray(); -} - -} // namespace Modrinth diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModModel.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModModel.h deleted file mode 100644 index 386897fd..00000000 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModModel.h +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * PolyMC - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu - * - * 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 "ModrinthModPage.h" - -namespace Modrinth { - -class ListModel : public ModPlatform::ListModel { - Q_OBJECT - - public: - ListModel(ModrinthModPage* parent) : ModPlatform::ListModel(parent){}; - ~ListModel() override = default; - - private: - void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override; - void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override; - void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override; - - auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; - - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - static const char* sorts[5]; - inline auto getSorts() const -> const char** override { return sorts; }; -}; - -} // namespace Modrinth diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp deleted file mode 100644 index c531ea90..00000000 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * PolyMC - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu - * - * 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 . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ModrinthModPage.h" -#include "modplatform/modrinth/ModrinthAPI.h" -#include "ui_ModPage.h" - -#include "ModrinthModModel.h" -#include "ui/dialogs/ModDownloadDialog.h" - -ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance* instance) - : ModPage(dialog, instance, new ModrinthAPI()) -{ - listModel = new Modrinth::ListModel(this); - ui->packView->setModel(listModel); - - // index is used to set the sorting with the modrinth api - ui->sortByBox->addItem(tr("Sort by Relevance")); - ui->sortByBox->addItem(tr("Sort by Downloads")); - ui->sortByBox->addItem(tr("Sort by Follows")); - ui->sortByBox->addItem(tr("Sort by Last Updated")); - ui->sortByBox->addItem(tr("Sort by Newest")); - - // sometimes Qt just ignores virtual slots and doesn't work as intended it seems, - // so it's best not to connect them in the parent's constructor... - connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch())); - connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ModrinthModPage::onSelectionChanged); - connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &ModrinthModPage::onVersionSelectionChanged); - connect(ui->modSelectionButton, &QPushButton::clicked, this, &ModrinthModPage::onModSelected); - - ui->packDescription->setMetaEntry(metaEntryBase()); -} - -auto ModrinthModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderTypes loaders) const -> bool -{ - auto loaderStrings = ModrinthAPI::getModLoaderStrings(loaders); - - auto loaderCompatible = false; - for (auto remoteLoader : ver.loaders) - { - if (loaderStrings.contains(remoteLoader)) { - loaderCompatible = true; - break; - } - } - return ver.mcVersion.contains(mineVer) && loaderCompatible; -} - -// I don't know why, but doing this on the parent class makes it so that -// other mod providers start loading before being selected, at least with -// my Qt, so we need to implement this in every derived class... -auto ModrinthModPage::shouldDisplay() const -> bool { return true; } diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.h deleted file mode 100644 index 40d82e6f..00000000 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.h +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * PolyMC - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu - * - * 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 . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "modplatform/ModAPI.h" -#include "ui/pages/modplatform/ModPage.h" - -#include "modplatform/modrinth/ModrinthAPI.h" - -class ModrinthModPage : public ModPage { - Q_OBJECT - - public: - static ModrinthModPage* create(ModDownloadDialog* dialog, BaseInstance* instance) - { - return ModPage::create(dialog, instance); - } - - ModrinthModPage(ModDownloadDialog* dialog, BaseInstance* instance); - ~ModrinthModPage() override = default; - - inline auto displayName() const -> QString override { return "Modrinth"; } - inline auto icon() const -> QIcon override { return APPLICATION->getThemedIcon("modrinth"); } - inline auto id() const -> QString override { return "modrinth"; } - inline auto helpPage() const -> QString override { return "Mod-platform"; } - - inline auto debugName() const -> QString override { return "Modrinth"; } - inline auto metaEntryBase() const -> QString override { return "ModrinthPacks"; }; - - auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderTypes loaders = ModAPI::Unspecified) const -> bool override; - - auto shouldDisplay() const -> bool override; -}; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp new file mode 100644 index 00000000..51278546 --- /dev/null +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * + * 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 "ModrinthResourceModels.h" + +#include "ui/pages/modplatform/modrinth/ModrinthResourcePages.h" + +#include "modplatform/modrinth/ModrinthAPI.h" +#include "modplatform/modrinth/ModrinthPackIndex.h" + +namespace Modrinth { + +// NOLINTNEXTLINE(modernize-avoid-c-arrays) +const char* ListModel::sorts[5]{ "relevance", "downloads", "follows", "updated", "newest" }; + +void ListModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) +{ + Modrinth::loadIndexedPack(m, obj); +} + +void ListModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) +{ + Modrinth::loadExtraPackData(m, obj); +} + +void ListModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) +{ + Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_associated_page->m_base_instance); +} + +auto ListModel::documentToArray(QJsonDocument& obj) const -> QJsonArray +{ + return obj.object().value("hits").toArray(); +} + +} // namespace Modrinth + + diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h new file mode 100644 index 00000000..bf62d22f --- /dev/null +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * + * 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 "ui/pages/modplatform/ModModel.h" + +#include "ui/pages/modplatform/modrinth/ModrinthResourcePages.h" + +#include "modplatform/modrinth/ModrinthAPI.h" + +namespace Modrinth { + +class ListModel : public ModPlatform::ListModel { + Q_OBJECT + + public: + ListModel(ModrinthModPage* parent) : ModPlatform::ListModel(parent, new ModrinthAPI){}; + ~ListModel() override = default; + + private: + void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override; + void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override; + void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override; + + auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; + + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + static const char* sorts[5]; + inline auto getSorts() const -> const char** override { return sorts; }; +}; + +} // namespace Modrinth + diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp new file mode 100644 index 00000000..17f0bc93 --- /dev/null +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * + * 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 . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ModrinthResourcePages.h" +#include "ui_ResourcePage.h" + +#include "modplatform/modrinth/ModrinthAPI.h" + +#include "ModrinthResourceModels.h" +#include "ui/dialogs/ModDownloadDialog.h" + +ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance& instance) + : ModPage(dialog, instance) +{ + m_model = new Modrinth::ListModel(this); + m_ui->packView->setModel(m_model); + + // index is used to set the sorting with the modrinth api + m_ui->sortByBox->addItem(tr("Sort by Relevance")); + m_ui->sortByBox->addItem(tr("Sort by Downloads")); + m_ui->sortByBox->addItem(tr("Sort by Follows")); + m_ui->sortByBox->addItem(tr("Sort by Last Updated")); + m_ui->sortByBox->addItem(tr("Sort by Newest")); + + // sometimes Qt just ignores virtual slots and doesn't work as intended it seems, + // so it's best not to connect them in the parent's constructor... + connect(m_ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch())); + connect(m_ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ModrinthModPage::onSelectionChanged); + connect(m_ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &ModrinthModPage::onVersionSelectionChanged); + connect(m_ui->resourceSelectionButton, &QPushButton::clicked, this, &ModrinthModPage::onResourceSelected); + + m_ui->packDescription->setMetaEntry(metaEntryBase()); +} + +auto ModrinthModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders) const -> bool +{ + auto loaderCompatible = !loaders.has_value(); + + if (!loaderCompatible) { + auto loaderStrings = ModrinthAPI::getModLoaderStrings(loaders.value()); + for (auto remoteLoader : ver.loaders) + { + if (loaderStrings.contains(remoteLoader)) { + loaderCompatible = true; + break; + } + } + } + + return ver.mcVersion.contains(mineVer) && loaderCompatible; +} + +// I don't know why, but doing this on the parent class makes it so that +// other mod providers start loading before being selected, at least with +// my Qt, so we need to implement this in every derived class... +auto ModrinthModPage::shouldDisplay() const -> bool { return true; } + diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h new file mode 100644 index 00000000..6f816cfd --- /dev/null +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * + * 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 . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "Application.h" + +#include "modplatform/ResourceAPI.h" + +#include "ui/pages/modplatform/ModPage.h" + +static inline QString displayName() { return "Modrinth"; } +static inline QIcon icon() { return APPLICATION->getThemedIcon("modrinth"); } +static inline QString id() { return "modrinth"; } +static inline QString debugName() { return "Modrinth"; } +static inline QString metaEntryBase() { return "ModrinthPacks"; }; + +class ModrinthModPage : public ModPage { + Q_OBJECT + + public: + static ModrinthModPage* create(ModDownloadDialog* dialog, BaseInstance& instance) + { + return ModPage::create(dialog, instance); + } + + ModrinthModPage(ModDownloadDialog* dialog, BaseInstance& instance); + ~ModrinthModPage() override = default; + + [[nodiscard]] bool shouldDisplay() const override; + + [[nodiscard]] inline auto displayName() const -> QString override { return ::displayName(); } \ + [[nodiscard]] inline auto icon() const -> QIcon override { return ::icon(); } \ + [[nodiscard]] inline auto id() const -> QString override { return ::id(); } \ + [[nodiscard]] inline auto debugName() const -> QString override { return ::debugName(); } \ + [[nodiscard]] inline auto metaEntryBase() const -> QString override { return ::metaEntryBase(); } + inline auto helpPage() const -> QString override { return "Mod-platform"; } + + auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders = {}) const -> bool override; +}; diff --git a/launcher/ui/widgets/ProgressWidget.cpp b/launcher/ui/widgets/ProgressWidget.cpp index b60d9a7a..18b51fc3 100644 --- a/launcher/ui/widgets/ProgressWidget.cpp +++ b/launcher/ui/widgets/ProgressWidget.cpp @@ -39,7 +39,7 @@ void ProgressWidget::progressFormat(QString format) m_bar->setFormat(format); } -void ProgressWidget::watch(Task* task) +void ProgressWidget::watch(const Task* task) { if (!task) return; @@ -57,11 +57,11 @@ void ProgressWidget::watch(Task* task) show(); } -void ProgressWidget::start(Task* task) +void ProgressWidget::start(const Task* task) { watch(task); if (!m_task->isRunning()) - QMetaObject::invokeMethod(m_task, "start", Qt::QueuedConnection); + QMetaObject::invokeMethod(const_cast(m_task), "start", Qt::QueuedConnection); } bool ProgressWidget::exec(std::shared_ptr task) diff --git a/launcher/ui/widgets/ProgressWidget.h b/launcher/ui/widgets/ProgressWidget.h index 4d9097b8..b0458f33 100644 --- a/launcher/ui/widgets/ProgressWidget.h +++ b/launcher/ui/widgets/ProgressWidget.h @@ -27,10 +27,10 @@ class ProgressWidget : public QWidget { public slots: /** Watch the progress of a task. */ - void watch(Task* task); + void watch(const Task* task); /** Watch the progress of a task, and start it if needed */ - void start(Task* task); + void start(const Task* task); /** Blocking way of waiting for a task to finish. */ bool exec(std::shared_ptr task); @@ -50,7 +50,7 @@ class ProgressWidget : public QWidget { private: QLabel* m_label = nullptr; QProgressBar* m_bar = nullptr; - Task* m_task = nullptr; + const Task* m_task = nullptr; bool m_hide_if_inactive = false; }; diff --git a/tests/Packwiz_test.cpp b/tests/Packwiz_test.cpp index 098e8f89..29289469 100644 --- a/tests/Packwiz_test.cpp +++ b/tests/Packwiz_test.cpp @@ -48,7 +48,7 @@ class PackwizTest : public QObject { QCOMPARE(metadata.hash_format, "sha512"); QCOMPARE(metadata.hash, "c8fe6e15ddea32668822dddb26e1851e5f03834be4bcb2eff9c0da7fdc086a9b6cead78e31a44d3bc66335cba11144ee0337c6d5346f1ba63623064499b3188d"); - QCOMPARE(metadata.provider, ModPlatform::Provider::MODRINTH); + QCOMPARE(metadata.provider, ModPlatform::ResourceProvider::MODRINTH); QCOMPARE(metadata.version(), "ug2qKTPR"); QCOMPARE(metadata.mod_id(), "kYq5qkSL"); } @@ -76,7 +76,7 @@ class PackwizTest : public QObject { QCOMPARE(metadata.hash_format, "murmur2"); QCOMPARE(metadata.hash, "1781245820"); - QCOMPARE(metadata.provider, ModPlatform::Provider::FLAME); + QCOMPARE(metadata.provider, ModPlatform::ResourceProvider::FLAME); QCOMPARE(metadata.file_id, 3509043); QCOMPARE(metadata.project_id, 327154); } -- cgit From 433a802c6ed3070b1b2f4435937a456eb4192f78 Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 16 Dec 2022 19:03:52 -0300 Subject: refactor: put resource downloading classes in common namespace Puts them all inside the 'ResourceDownload' namespace, so that it's a bit clearer from the outside that those belong to the same 'module'. Signed-off-by: flow --- launcher/ui/dialogs/ModDownloadDialog.cpp | 4 +++ launcher/ui/dialogs/ModDownloadDialog.h | 7 +++-- launcher/ui/dialogs/ResourceDownloadDialog.cpp | 4 +++ launcher/ui/dialogs/ResourceDownloadDialog.h | 7 ++++- launcher/ui/pages/instance/ModFolderPage.cpp | 2 +- launcher/ui/pages/modplatform/ModModel.cpp | 30 +++++++++++----------- launcher/ui/pages/modplatform/ModModel.h | 12 +++++---- launcher/ui/pages/modplatform/ModPage.cpp | 6 ++++- launcher/ui/pages/modplatform/ModPage.h | 8 ++++-- launcher/ui/pages/modplatform/ResourceModel.cpp | 4 +++ launcher/ui/pages/modplatform/ResourceModel.h | 6 ++++- launcher/ui/pages/modplatform/ResourcePage.cpp | 4 +++ launcher/ui/pages/modplatform/ResourcePage.h | 7 ++++- .../modplatform/flame/FlameResourceModels.cpp | 18 ++++++++----- .../pages/modplatform/flame/FlameResourceModels.h | 14 ++++++---- .../pages/modplatform/flame/FlameResourcePages.cpp | 6 ++++- .../pages/modplatform/flame/FlameResourcePages.h | 30 +++++++++++++++------- .../modrinth/ModrinthResourceModels.cpp | 24 ++++++++--------- .../modplatform/modrinth/ModrinthResourceModels.h | 17 +++++------- .../modplatform/modrinth/ModrinthResourcePages.cpp | 8 ++++-- .../modplatform/modrinth/ModrinthResourcePages.h | 19 +++++++++----- 21 files changed, 156 insertions(+), 81 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/dialogs/ModDownloadDialog.cpp b/launcher/ui/dialogs/ModDownloadDialog.cpp index 8a77ef7f..89b87300 100644 --- a/launcher/ui/dialogs/ModDownloadDialog.cpp +++ b/launcher/ui/dialogs/ModDownloadDialog.cpp @@ -24,6 +24,8 @@ #include "ui/pages/modplatform/flame/FlameResourcePages.h" #include "ui/pages/modplatform/modrinth/ModrinthResourcePages.h" +namespace ResourceDownload { + ModDownloadDialog::ModDownloadDialog(QWidget* parent, const std::shared_ptr& mods, BaseInstance* instance) : ResourceDownloadDialog(parent, mods), m_instance(instance) { @@ -57,3 +59,5 @@ QList ModDownloadDialog::getPages() return pages; } + +} // namespace ResourceDownload diff --git a/launcher/ui/dialogs/ModDownloadDialog.h b/launcher/ui/dialogs/ModDownloadDialog.h index 19036042..b378b5a9 100644 --- a/launcher/ui/dialogs/ModDownloadDialog.h +++ b/launcher/ui/dialogs/ModDownloadDialog.h @@ -25,8 +25,9 @@ class QDialogButtonBox; -class ModDownloadDialog final : public ResourceDownloadDialog -{ +namespace ResourceDownload { + +class ModDownloadDialog final : public ResourceDownloadDialog { Q_OBJECT public: @@ -45,3 +46,5 @@ class ModDownloadDialog final : public ResourceDownloadDialog private: BaseInstance* m_instance; }; + +} // namespace ResourceDownload diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index 7367548f..b143750b 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -9,6 +9,8 @@ #include "ui/pages/modplatform/ResourcePage.h" #include "ui/widgets/PageContainer.h" +namespace ResourceDownload { + ResourceDownloadDialog::ResourceDownloadDialog(QWidget* parent, const std::shared_ptr base_model) : QDialog(parent), m_base_model(base_model), m_buttons(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel), m_vertical_layout(this) { @@ -150,3 +152,5 @@ void ResourceDownloadDialog::selectedPageChanged(BasePage* previous, BasePage* s // Same effect as having a global search bar m_selectedPage->setSearchTerm(prev_page->getSearchTerm()); } + +} // namespace ResourceDownload diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.h b/launcher/ui/dialogs/ResourceDownloadDialog.h index d6b3938b..3b234cd1 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.h +++ b/launcher/ui/dialogs/ResourceDownloadDialog.h @@ -7,12 +7,15 @@ #include "ui/pages/BasePageProvider.h" class ResourceDownloadTask; -class ResourcePage; class ResourceFolderModel; class PageContainer; class QVBoxLayout; class QDialogButtonBox; +namespace ResourceDownload { + +class ResourcePage; + class ResourceDownloadDialog : public QDialog, public BasePageProvider { Q_OBJECT @@ -53,3 +56,5 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider { QHash m_selected; }; + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 1bce3c0d..7c4b8952 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -158,7 +158,7 @@ void ModFolderPage::installMods() return; } - ModDownloadDialog mdownload(this, m_model, m_instance); + ResourceDownload::ModDownloadDialog mdownload(this, m_model, m_instance); if (mdownload.exec()) { ConcurrentTask* tasks = new ConcurrentTask(this); connect(tasks, &Task::failed, [this, tasks](QString reason) { diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index 31aae746..59399c59 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -7,19 +7,19 @@ #include -namespace ModPlatform { +namespace ResourceDownload { -ListModel::ListModel(ModPage* parent, ResourceAPI* api) : ResourceModel(parent, api) {} +ModModel::ModModel(ModPage* parent, ResourceAPI* api) : ResourceModel(parent, api) {} /******** Make data requests ********/ -ResourceAPI::SearchArgs ListModel::createSearchArguments() +ResourceAPI::SearchArgs ModModel::createSearchArguments() { auto profile = static_cast(m_associated_page->m_base_instance).getPackProfile(); return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, getSorts()[currentSort], profile->getModLoaders(), getMineVersions() }; } -ResourceAPI::SearchCallbacks ListModel::createSearchCallbacks() +ResourceAPI::SearchCallbacks ModModel::createSearchCallbacks() { return { [this](auto& doc) { if (!s_running_models.constFind(this).value()) @@ -28,14 +28,14 @@ ResourceAPI::SearchCallbacks ListModel::createSearchCallbacks() } }; } -ResourceAPI::VersionSearchArgs ListModel::createVersionsArguments(QModelIndex& entry) +ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& entry) { auto const& pack = m_packs[entry.row()]; auto profile = static_cast(m_associated_page->m_base_instance).getPackProfile(); return { pack.addonId.toString(), getMineVersions(), profile->getModLoaders() }; } -ResourceAPI::VersionSearchCallbacks ListModel::createVersionsCallbacks(QModelIndex& entry) +ResourceAPI::VersionSearchCallbacks ModModel::createVersionsCallbacks(QModelIndex& entry) { auto const& pack = m_packs[entry.row()]; @@ -46,12 +46,12 @@ ResourceAPI::VersionSearchCallbacks ListModel::createVersionsCallbacks(QModelInd } }; } -ResourceAPI::ProjectInfoArgs ListModel::createInfoArguments(QModelIndex& entry) +ResourceAPI::ProjectInfoArgs ModModel::createInfoArguments(QModelIndex& entry) { auto& pack = m_packs[entry.row()]; return { pack }; } -ResourceAPI::ProjectInfoCallbacks ListModel::createInfoCallbacks(QModelIndex& entry) +ResourceAPI::ProjectInfoCallbacks ModModel::createInfoCallbacks(QModelIndex& entry) { return { [this, entry](auto& doc, auto& pack) { if (!s_running_models.constFind(this).value()) @@ -60,7 +60,7 @@ ResourceAPI::ProjectInfoCallbacks ListModel::createInfoCallbacks(QModelIndex& en } }; } -void ListModel::searchWithTerm(const QString& term, const int sort, const bool filter_changed) +void ModModel::searchWithTerm(const QString& term, const int sort, const bool filter_changed) { if (m_search_term == term && m_search_term.isNull() == term.isNull() && currentSort == sort && !filter_changed) { return; @@ -74,7 +74,7 @@ void ListModel::searchWithTerm(const QString& term, const int sort, const bool f /******** Request callbacks ********/ -void ListModel::searchRequestFinished(QJsonDocument& doc) +void ModModel::searchRequestFinished(QJsonDocument& doc) { QList newList; auto packs = documentToArray(doc); @@ -108,7 +108,7 @@ void ListModel::searchRequestFinished(QJsonDocument& doc) endInsertRows(); } -void ListModel::infoRequestFinished(QJsonDocument& doc, ModPlatform::IndexedPack& pack, const QModelIndex& index) +void ModModel::infoRequestFinished(QJsonDocument& doc, ModPlatform::IndexedPack& pack, const QModelIndex& index) { qDebug() << "Loading mod info"; @@ -133,7 +133,7 @@ void ListModel::infoRequestFinished(QJsonDocument& doc, ModPlatform::IndexedPack m_associated_page->updateUi(); } -void ListModel::versionRequestSucceeded(QJsonDocument doc, QString addonId, const QModelIndex& index) +void ModModel::versionRequestSucceeded(QJsonDocument doc, QString addonId, const QModelIndex& index) { auto current = m_associated_page->getCurrentPack(); if (addonId != current.addonId) { @@ -159,16 +159,16 @@ void ListModel::versionRequestSucceeded(QJsonDocument doc, QString addonId, cons m_associated_page->updateVersionList(); } -} // namespace ModPlatform - /******** Helpers ********/ #define MOD_PAGE(x) static_cast(x) -auto ModPlatform::ListModel::getMineVersions() const -> std::optional> +auto ModModel::getMineVersions() const -> std::optional> { auto versions = MOD_PAGE(m_associated_page)->getFilter()->versions; if (!versions.empty()) return versions; return {}; } + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ModModel.h b/launcher/ui/pages/modplatform/ModModel.h index 7c735d90..e3d760a2 100644 --- a/launcher/ui/pages/modplatform/ModModel.h +++ b/launcher/ui/pages/modplatform/ModModel.h @@ -7,16 +7,17 @@ #include "ui/pages/modplatform/ResourceModel.h" -class ModPage; class Version; -namespace ModPlatform { +namespace ResourceDownload { + +class ModPage; -class ListModel : public ResourceModel { +class ModModel : public ResourceModel { Q_OBJECT public: - ListModel(ModPage* parent, ResourceAPI* api); + ModModel(ModPage* parent, ResourceAPI* api); /* Ask the API for more information */ void searchWithTerm(const QString& term, const int sort, const bool filter_changed); @@ -51,4 +52,5 @@ class ListModel : public ResourceModel { protected: int currentSort = 0; }; -} // namespace ModPlatform + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 853f2c54..8941d9b7 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -53,6 +53,8 @@ #include "ui/pages/modplatform/ModModel.h" +namespace ResourceDownload { + ModPage::ModPage(ModDownloadDialog* dialog, BaseInstance& instance) : ResourcePage(dialog, instance) { @@ -100,7 +102,7 @@ void ModPage::triggerSearch() updateSelectionButton(); } - static_cast(m_model)->searchWithTerm(getSearchTerm(), m_ui->sortByBox->currentIndex(), changed); + static_cast(m_model)->searchWithTerm(getSearchTerm(), m_ui->sortByBox->currentIndex(), changed); m_fetch_progress.watch(&m_model->activeJob()); } @@ -151,3 +153,5 @@ void ModPage::addResourceToDialog(ModPlatform::IndexedPack& pack, ModPlatform::I bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); m_parent_dialog->addResource(pack.name, new ResourceDownloadTask(pack, version, m_parent_dialog->getBaseModel(), is_indexed)); } + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h index 8c1fec84..137a6046 100644 --- a/launcher/ui/pages/modplatform/ModPage.h +++ b/launcher/ui/pages/modplatform/ModPage.h @@ -7,12 +7,14 @@ #include "ui/pages/modplatform/ResourcePage.h" #include "ui/widgets/ModFilterWidget.h" -class ModDownloadDialog; - namespace Ui { class ResourcePage; } +namespace ResourceDownload { + +class ModDownloadDialog; + /* This page handles most logic related to browsing and selecting mods to download. */ class ModPage : public ResourcePage { Q_OBJECT @@ -57,3 +59,5 @@ class ModPage : public ResourcePage { unique_qobject_ptr m_filter_widget; std::shared_ptr m_filter; }; + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index d672a2ac..e8af0e7a 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -20,6 +20,8 @@ #include "ui/pages/modplatform/ResourcePage.h" #include "ui/widgets/ProjectItem.h" +namespace ResourceDownload { + QHash ResourceModel::s_running_models; ResourceModel::ResourceModel(ResourcePage* parent, ResourceAPI* api) : QAbstractListModel(), m_api(api), m_associated_page(parent) @@ -256,3 +258,5 @@ void ResourceModel::searchRequestAborted() m_next_search_offset = 0; search(); } + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ResourceModel.h b/launcher/ui/pages/modplatform/ResourceModel.h index af0e9f55..6a94c399 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.h +++ b/launcher/ui/pages/modplatform/ResourceModel.h @@ -9,13 +9,15 @@ #include "tasks/ConcurrentTask.h" class NetJob; -class ResourcePage; class ResourceAPI; namespace ModPlatform { struct IndexedPack; } +namespace ResourceDownload { + +class ResourcePage; class ResourceModel : public QAbstractListModel { Q_OBJECT @@ -99,3 +101,5 @@ class ResourceModel : public QAbstractListModel { void searchRequestFailed(QString reason, int network_error_code); void searchRequestAborted(); }; + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index 3b382d20..161b5c22 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -13,6 +13,8 @@ #include "ui/pages/modplatform/ResourceModel.h" #include "ui/widgets/ProjectItem.h" +namespace ResourceDownload { + ResourcePage::ResourcePage(ResourceDownloadDialog* parent, BaseInstance& base_instance) : QWidget(parent), m_base_instance(base_instance), m_ui(new Ui::ResourcePage), m_parent_dialog(parent), m_fetch_progress(this, false) { @@ -345,3 +347,5 @@ void ResourcePage::openUrl(const QUrl& url) // open in the user's web browser QDesktopServices::openUrl(url); } + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ResourcePage.h b/launcher/ui/pages/modplatform/ResourcePage.h index 32aad3d9..f731cf56 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.h +++ b/launcher/ui/pages/modplatform/ResourcePage.h @@ -14,8 +14,11 @@ class ResourcePage; } class BaseInstance; -class ResourceModel; + +namespace ResourceDownload { + class ResourceDownloadDialog; +class ResourceModel; class ResourcePage : public QWidget, public BasePage { Q_OBJECT @@ -93,3 +96,5 @@ class ResourcePage : public QWidget, public BasePage { // Used to do instant searching with a delay to cache quick changes QTimer m_search_timer; }; + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp index b602dfac..cfe4080a 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp @@ -1,31 +1,35 @@ #include "FlameResourceModels.h" + #include "Json.h" + #include "modplatform/flame/FlameModIndex.h" -namespace FlameMod { +namespace ResourceDownload { // NOLINTNEXTLINE(modernize-avoid-c-arrays) -const char* ListModel::sorts[6]{ "Featured", "Popularity", "LastUpdated", "Name", "Author", "TotalDownloads" }; +const char* FlameModModel::sorts[6]{ "Featured", "Popularity", "LastUpdated", "Name", "Author", "TotalDownloads" }; + +FlameModModel::FlameModModel(FlameModPage* parent) : ModModel(parent, new FlameAPI) {} -void ListModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) +void FlameModModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) { FlameMod::loadIndexedPack(m, obj); } // We already deal with the URLs when initializing the pack, due to the API response's structure -void ListModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) +void FlameModModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) { FlameMod::loadBody(m, obj); } -void ListModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) +void FlameModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) { FlameMod::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_associated_page->m_base_instance); } -auto ListModel::documentToArray(QJsonDocument& obj) const -> QJsonArray +auto FlameModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray { return Json::ensureArray(obj.object(), "data"); } -} // namespace FlameMod +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h index b94377d3..501937e2 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h @@ -2,14 +2,18 @@ #include "modplatform/flame/FlameAPI.h" -namespace FlameMod { +#include "ui/pages/modplatform/ModModel.h" -class ListModel : public ModPlatform::ListModel { +#include "ui/pages/modplatform/flame/FlameResourcePages.h" + +namespace ResourceDownload { + +class FlameModModel : public ModModel { Q_OBJECT public: - ListModel(FlameModPage* parent) : ModPlatform::ListModel(parent, new FlameAPI) {} - ~ListModel() override = default; + FlameModModel(FlameModPage* parent); + ~FlameModModel() override = default; private: void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override; @@ -23,4 +27,4 @@ class ListModel : public ModPlatform::ListModel { inline auto getSorts() const -> const char** override { return sorts; }; }; -} // namespace FlameMod +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp index 490578ad..723819fb 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp @@ -40,10 +40,12 @@ #include "FlameResourceModels.h" #include "ui/dialogs/ModDownloadDialog.h" +namespace ResourceDownload { + FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) : ModPage(dialog, instance) { - m_model = new FlameMod::ListModel(this); + m_model = new FlameModModel(this); m_ui->packView->setModel(m_model); // index is used to set the sorting with the flame api @@ -95,3 +97,5 @@ void FlameModPage::openUrl(const QUrl& url) ModPage::openUrl(url); } + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h index 597a0c25..6c7d0247 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h @@ -42,6 +42,16 @@ #include "ui/pages/modplatform/ModPage.h" +namespace ResourceDownload { + +namespace Flame { +static inline QString displayName() { return "CurseForge"; } +static inline QIcon icon() { return APPLICATION->getThemedIcon("flame"); } +static inline QString id() { return "curseforge"; } +static inline QString debugName() { return "Flame"; } +static inline QString metaEntryBase() { return "FlameMods"; }; +} + class FlameModPage : public ModPage { Q_OBJECT @@ -54,18 +64,20 @@ class FlameModPage : public ModPage { FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance); ~FlameModPage() override = default; - inline auto displayName() const -> QString override { return "CurseForge"; } - inline auto icon() const -> QIcon override { return APPLICATION->getThemedIcon("flame"); } - inline auto id() const -> QString override { return "curseforge"; } - inline auto helpPage() const -> QString override { return "Mod-platform"; } + [[nodiscard]] bool shouldDisplay() const override; - inline auto debugName() const -> QString override { return "Flame"; } - inline auto metaEntryBase() const -> QString override { return "FlameMods"; }; + [[nodiscard]] inline auto displayName() const -> QString override { return Flame::displayName(); } + [[nodiscard]] inline auto icon() const -> QIcon override { return Flame::icon(); } + [[nodiscard]] inline auto id() const -> QString override { return Flame::id(); } + [[nodiscard]] inline auto debugName() const -> QString override { return Flame::debugName(); } + [[nodiscard]] inline auto metaEntryBase() const -> QString override { return Flame::metaEntryBase(); } - auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders = {}) const -> bool override; - bool optedOut(ModPlatform::IndexedVersion& ver) const override; + [[nodiscard]] inline auto helpPage() const -> QString override { return "Mod-platform"; } - auto shouldDisplay() const -> bool override; + bool validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders = {}) const override; + bool optedOut(ModPlatform::IndexedVersion& ver) const override; void openUrl(const QUrl& url) override; }; + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp index 51278546..ee96f0de 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp @@ -23,31 +23,31 @@ #include "modplatform/modrinth/ModrinthAPI.h" #include "modplatform/modrinth/ModrinthPackIndex.h" -namespace Modrinth { +namespace ResourceDownload { // NOLINTNEXTLINE(modernize-avoid-c-arrays) -const char* ListModel::sorts[5]{ "relevance", "downloads", "follows", "updated", "newest" }; +const char* ModrinthModModel::sorts[5]{ "relevance", "downloads", "follows", "updated", "newest" }; -void ListModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) +ModrinthModModel::ModrinthModModel(ModrinthModPage* parent) : ModModel(parent, new ModrinthAPI){}; + +void ModrinthModModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) { - Modrinth::loadIndexedPack(m, obj); + ::Modrinth::loadIndexedPack(m, obj); } -void ListModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) +void ModrinthModModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) { - Modrinth::loadExtraPackData(m, obj); + ::Modrinth::loadExtraPackData(m, obj); } -void ListModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) +void ModrinthModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) { - Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_associated_page->m_base_instance); + ::Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_associated_page->m_base_instance); } -auto ListModel::documentToArray(QJsonDocument& obj) const -> QJsonArray +auto ModrinthModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray { return obj.object().value("hits").toArray(); } -} // namespace Modrinth - - +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h index bf62d22f..b0088a73 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h @@ -20,24 +20,22 @@ #include "ui/pages/modplatform/ModModel.h" -#include "ui/pages/modplatform/modrinth/ModrinthResourcePages.h" +namespace ResourceDownload { -#include "modplatform/modrinth/ModrinthAPI.h" +class ModrinthModPage; -namespace Modrinth { - -class ListModel : public ModPlatform::ListModel { +class ModrinthModModel : public ModModel { Q_OBJECT public: - ListModel(ModrinthModPage* parent) : ModPlatform::ListModel(parent, new ModrinthAPI){}; - ~ListModel() override = default; + ModrinthModModel(ModrinthModPage* parent); + ~ModrinthModModel() override = default; private: void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override; void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override; void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override; - + auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; // NOLINTNEXTLINE(modernize-avoid-c-arrays) @@ -45,5 +43,4 @@ class ListModel : public ModPlatform::ListModel { inline auto getSorts() const -> const char** override { return sorts; }; }; -} // namespace Modrinth - +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp index 17f0bc93..5d2680b0 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -38,13 +38,16 @@ #include "modplatform/modrinth/ModrinthAPI.h" -#include "ModrinthResourceModels.h" #include "ui/dialogs/ModDownloadDialog.h" +#include "ui/pages/modplatform/modrinth/ModrinthResourceModels.h" + +namespace ResourceDownload { + ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance& instance) : ModPage(dialog, instance) { - m_model = new Modrinth::ListModel(this); + m_model = new ModrinthModModel(this); m_ui->packView->setModel(m_model); // index is used to set the sorting with the modrinth api @@ -87,3 +90,4 @@ auto ModrinthModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString // my Qt, so we need to implement this in every derived class... auto ModrinthModPage::shouldDisplay() const -> bool { return true; } +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h index 6f816cfd..07b32c0c 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h @@ -41,11 +41,15 @@ #include "ui/pages/modplatform/ModPage.h" +namespace ResourceDownload { + +namespace Modrinth { static inline QString displayName() { return "Modrinth"; } static inline QIcon icon() { return APPLICATION->getThemedIcon("modrinth"); } static inline QString id() { return "modrinth"; } static inline QString debugName() { return "Modrinth"; } static inline QString metaEntryBase() { return "ModrinthPacks"; }; +} class ModrinthModPage : public ModPage { Q_OBJECT @@ -61,12 +65,15 @@ class ModrinthModPage : public ModPage { [[nodiscard]] bool shouldDisplay() const override; - [[nodiscard]] inline auto displayName() const -> QString override { return ::displayName(); } \ - [[nodiscard]] inline auto icon() const -> QIcon override { return ::icon(); } \ - [[nodiscard]] inline auto id() const -> QString override { return ::id(); } \ - [[nodiscard]] inline auto debugName() const -> QString override { return ::debugName(); } \ - [[nodiscard]] inline auto metaEntryBase() const -> QString override { return ::metaEntryBase(); } - inline auto helpPage() const -> QString override { return "Mod-platform"; } + [[nodiscard]] inline auto displayName() const -> QString override { return Modrinth::displayName(); } + [[nodiscard]] inline auto icon() const -> QIcon override { return Modrinth::icon(); } + [[nodiscard]] inline auto id() const -> QString override { return Modrinth::id(); } + [[nodiscard]] inline auto debugName() const -> QString override { return Modrinth::debugName(); } + [[nodiscard]] inline auto metaEntryBase() const -> QString override { return Modrinth::metaEntryBase(); } + + [[nodiscard]] inline auto helpPage() const -> QString override { return "Mod-platform"; } auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders = {}) const -> bool override; }; + +} // namespace ResourceDownload -- cgit From 39b7ac90d40eb53d7b88ef99b0fa46fb3e1840b9 Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 16 Dec 2022 21:44:21 -0300 Subject: refactor(RD): unify download dialogs into a single file No need for multiple files since the subclasses are so small now Signed-off-by: flow --- launcher/CMakeLists.txt | 2 - launcher/ui/dialogs/ModDownloadDialog.cpp | 63 -------------------- launcher/ui/dialogs/ModDownloadDialog.h | 50 ---------------- launcher/ui/dialogs/ResourceDownloadDialog.cpp | 67 ++++++++++++++++++++++ launcher/ui/dialogs/ResourceDownloadDialog.h | 51 +++++++++++++++- launcher/ui/pages/instance/ModFolderPage.cpp | 2 +- launcher/ui/pages/modplatform/ModPage.cpp | 2 +- .../pages/modplatform/flame/FlameResourcePages.cpp | 2 +- .../pages/modplatform/modrinth/ModrinthModel.cpp | 1 - .../modplatform/modrinth/ModrinthResourcePages.cpp | 2 +- 10 files changed, 120 insertions(+), 122 deletions(-) delete mode 100644 launcher/ui/dialogs/ModDownloadDialog.cpp delete mode 100644 launcher/ui/dialogs/ModDownloadDialog.h (limited to 'launcher/ui/pages/instance') diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index a1a68f5b..77c69106 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -876,8 +876,6 @@ SET(LAUNCHER_SOURCES ui/dialogs/SkinUploadDialog.h ui/dialogs/ResourceDownloadDialog.cpp ui/dialogs/ResourceDownloadDialog.h - ui/dialogs/ModDownloadDialog.cpp - ui/dialogs/ModDownloadDialog.h ui/dialogs/ScrollMessageBox.cpp ui/dialogs/ScrollMessageBox.h ui/dialogs/BlockedModsDialog.cpp diff --git a/launcher/ui/dialogs/ModDownloadDialog.cpp b/launcher/ui/dialogs/ModDownloadDialog.cpp deleted file mode 100644 index 89b87300..00000000 --- a/launcher/ui/dialogs/ModDownloadDialog.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (C) 2022 TheKodeToad - * - * 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 "ModDownloadDialog.h" - -#include "Application.h" - -#include "ui/pages/modplatform/flame/FlameResourcePages.h" -#include "ui/pages/modplatform/modrinth/ModrinthResourcePages.h" - -namespace ResourceDownload { - -ModDownloadDialog::ModDownloadDialog(QWidget* parent, const std::shared_ptr& mods, BaseInstance* instance) - : ResourceDownloadDialog(parent, mods), m_instance(instance) -{ - initializeContainer(); - connectButtons(); - - restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("ModDownloadGeometry").toByteArray())); -} - -void ModDownloadDialog::accept() -{ - APPLICATION->settings()->set("ModDownloadGeometry", saveGeometry().toBase64()); - QDialog::accept(); -} - -void ModDownloadDialog::reject() -{ - APPLICATION->settings()->set("ModDownloadGeometry", saveGeometry().toBase64()); - QDialog::reject(); -} - -QList ModDownloadDialog::getPages() -{ - QList pages; - - pages.append(ModrinthModPage::create(this, *m_instance)); - if (APPLICATION->capabilities() & Application::SupportsFlame) - pages.append(FlameModPage::create(this, *m_instance)); - - m_selectedPage = dynamic_cast(pages[0]); - - return pages; -} - -} // namespace ResourceDownload diff --git a/launcher/ui/dialogs/ModDownloadDialog.h b/launcher/ui/dialogs/ModDownloadDialog.h deleted file mode 100644 index b378b5a9..00000000 --- a/launcher/ui/dialogs/ModDownloadDialog.h +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (C) 2022 TheKodeToad - * - * 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 "minecraft/mod/ModFolderModel.h" - -#include "ui/dialogs/ResourceDownloadDialog.h" - -class QDialogButtonBox; - -namespace ResourceDownload { - -class ModDownloadDialog final : public ResourceDownloadDialog { - Q_OBJECT - - public: - explicit ModDownloadDialog(QWidget* parent, const std::shared_ptr& mods, BaseInstance* instance); - ~ModDownloadDialog() override = default; - - //: String that gets appended to the mod download dialog title ("Download " + resourcesString()) - [[nodiscard]] QString resourceString() const override { return tr("mods"); } - - QList getPages() override; - - public slots: - void accept() override; - void reject() override; - - private: - BaseInstance* m_instance; -}; - -} // namespace ResourceDownload diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index b143750b..523a1636 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -1,3 +1,22 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad + * + * 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 "ResourceDownloadDialog.h" #include @@ -5,8 +24,15 @@ #include "Application.h" #include "ResourceDownloadTask.h" +#include "minecraft/mod/ModFolderModel.h" + #include "ui/dialogs/ReviewMessageBox.h" + #include "ui/pages/modplatform/ResourcePage.h" + +#include "ui/pages/modplatform/flame/FlameResourcePages.h" +#include "ui/pages/modplatform/modrinth/ModrinthResourcePages.h" + #include "ui/widgets/PageContainer.h" namespace ResourceDownload { @@ -41,6 +67,22 @@ ResourceDownloadDialog::ResourceDownloadDialog(QWidget* parent, const std::share setWindowTitle(dialogTitle()); } +void ResourceDownloadDialog::accept() +{ + if (!geometrySaveKey().isEmpty()) + APPLICATION->settings()->set(geometrySaveKey(), saveGeometry().toBase64()); + + QDialog::accept(); +} + +void ResourceDownloadDialog::reject() +{ + if (!geometrySaveKey().isEmpty()) + APPLICATION->settings()->set(geometrySaveKey(), saveGeometry().toBase64()); + + QDialog::reject(); +} + // NOTE: We can't have this in the ctor because PageContainer calls a virtual function, and so // won't work with subclasses if we put it in this ctor. void ResourceDownloadDialog::initializeContainer() @@ -153,4 +195,29 @@ void ResourceDownloadDialog::selectedPageChanged(BasePage* previous, BasePage* s m_selectedPage->setSearchTerm(prev_page->getSearchTerm()); } + + +ModDownloadDialog::ModDownloadDialog(QWidget* parent, const std::shared_ptr& mods, BaseInstance* instance) + : ResourceDownloadDialog(parent, mods), m_instance(instance) +{ + initializeContainer(); + connectButtons(); + + if (!geometrySaveKey().isEmpty()) + restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get(geometrySaveKey()).toByteArray())); +} + +QList ModDownloadDialog::getPages() +{ + QList pages; + + pages.append(ModrinthModPage::create(this, *m_instance)); + if (APPLICATION->capabilities() & Application::SupportsFlame) + pages.append(FlameModPage::create(this, *m_instance)); + + m_selectedPage = dynamic_cast(pages[0]); + + return pages; +} + } // namespace ResourceDownload diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.h b/launcher/ui/dialogs/ResourceDownloadDialog.h index 3b234cd1..29813493 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.h +++ b/launcher/ui/dialogs/ResourceDownloadDialog.h @@ -1,3 +1,22 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad + * + * 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 @@ -6,11 +25,13 @@ #include "ui/pages/BasePageProvider.h" -class ResourceDownloadTask; -class ResourceFolderModel; +class BaseInstance; +class ModFolderModel; class PageContainer; class QVBoxLayout; class QDialogButtonBox; +class ResourceDownloadTask; +class ResourceFolderModel; namespace ResourceDownload { @@ -40,11 +61,18 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider { const QList getTasks(); [[nodiscard]] const std::shared_ptr getBaseModel() const { return m_base_model; } + public slots: + void accept() override; + void reject() override; + protected slots: void selectedPageChanged(BasePage* previous, BasePage* selected); virtual void confirm(); + protected: + [[nodiscard]] virtual QString geometrySaveKey() const { return ""; } + protected: const std::shared_ptr m_base_model; @@ -57,4 +85,23 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider { QHash m_selected; }; + + +class ModDownloadDialog final : public ResourceDownloadDialog { + Q_OBJECT + + public: + explicit ModDownloadDialog(QWidget* parent, const std::shared_ptr& mods, BaseInstance* instance); + ~ModDownloadDialog() override = default; + + //: String that gets appended to the mod download dialog title ("Download " + resourcesString()) + [[nodiscard]] QString resourceString() const override { return tr("mods"); } + [[nodiscard]] QString geometrySaveKey() const override { return "ModDownloadGeometry"; } + + QList getPages() override; + + private: + BaseInstance* m_instance; +}; + } // namespace ResourceDownload diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 7c4b8952..d9069915 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -49,8 +49,8 @@ #include "ui/GuiUtil.h" #include "ui/dialogs/CustomMessageBox.h" -#include "ui/dialogs/ModDownloadDialog.h" #include "ui/dialogs/ModUpdateDialog.h" +#include "ui/dialogs/ResourceDownloadDialog.h" #include "DesktopServices.h" diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 8941d9b7..8d441546 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -49,7 +49,7 @@ #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" -#include "ui/dialogs/ModDownloadDialog.h" +#include "ui/dialogs/ResourceDownloadDialog.h" #include "ui/pages/modplatform/ModModel.h" diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp index 723819fb..2a8ab526 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp @@ -38,7 +38,7 @@ #include "ui_ResourcePage.h" #include "FlameResourceModels.h" -#include "ui/dialogs/ModDownloadDialog.h" +#include "ui/dialogs/ResourceDownloadDialog.h" namespace ResourceDownload { diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index e6704eef..80850b4c 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -40,7 +40,6 @@ #include "Json.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" -#include "ui/dialogs/ModDownloadDialog.h" #include "ui/widgets/ProjectItem.h" #include diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp index 5d2680b0..1352e2f6 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -38,7 +38,7 @@ #include "modplatform/modrinth/ModrinthAPI.h" -#include "ui/dialogs/ModDownloadDialog.h" +#include "ui/dialogs/ResourceDownloadDialog.h" #include "ui/pages/modplatform/modrinth/ModrinthResourceModels.h" -- cgit From ba677a8cb76dd6cde4a08ff4b6f142f7be1bdb29 Mon Sep 17 00:00:00 2001 From: flow Date: Tue, 3 Jan 2023 13:58:27 -0300 Subject: refactor: change some ResourceAPI from NetJob to Task This makes it easier to create resource apis that aren't network-based. Signed-off-by: flow --- launcher/QObjectPtr.h | 4 +++ launcher/modplatform/EnsureMetadataTask.cpp | 28 +++++++++---------- launcher/modplatform/EnsureMetadataTask.h | 10 +++---- launcher/modplatform/ResourceAPI.h | 13 ++++----- launcher/modplatform/flame/FlameAPI.cpp | 6 ++--- launcher/modplatform/flame/FlameAPI.h | 6 ++--- .../flame/FlameInstanceCreationTask.cpp | 4 +-- .../modplatform/flame/FlameInstanceCreationTask.h | 2 +- .../modplatform/helpers/NetworkResourceAPI.cpp | 8 +++--- launcher/modplatform/helpers/NetworkResourceAPI.h | 8 +++--- launcher/modplatform/modrinth/ModrinthAPI.cpp | 31 ++++++++++++---------- launcher/modplatform/modrinth/ModrinthAPI.h | 10 +++---- .../modplatform/modrinth/ModrinthCheckUpdate.cpp | 2 +- .../modplatform/modrinth/ModrinthCheckUpdate.h | 2 +- launcher/ui/pages/instance/ManagedPackPage.h | 2 ++ launcher/ui/pages/modplatform/ResourceModel.cpp | 2 +- launcher/ui/pages/modplatform/ResourceModel.h | 4 +-- 17 files changed, 75 insertions(+), 67 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/QObjectPtr.h b/launcher/QObjectPtr.h index b1ef1c8d..ec466096 100644 --- a/launcher/QObjectPtr.h +++ b/launcher/QObjectPtr.h @@ -28,6 +28,10 @@ class shared_qobject_ptr : public QSharedPointer { constexpr shared_qobject_ptr(const shared_qobject_ptr& other) : QSharedPointer(other) {} + template + constexpr shared_qobject_ptr(const QSharedPointer& other) : QSharedPointer(other) + {} + void reset() { QSharedPointer::reset(); } void reset(const shared_qobject_ptr& other) { diff --git a/launcher/modplatform/EnsureMetadataTask.cpp b/launcher/modplatform/EnsureMetadataTask.cpp index 9bf81338..fb451938 100644 --- a/launcher/modplatform/EnsureMetadataTask.cpp +++ b/launcher/modplatform/EnsureMetadataTask.cpp @@ -13,8 +13,6 @@ #include "modplatform/modrinth/ModrinthAPI.h" #include "modplatform/modrinth/ModrinthPackIndex.h" -#include "net/NetJob.h" - static ModPlatform::ProviderCapabilities ProviderCaps; static ModrinthAPI modrinth_api; @@ -107,7 +105,7 @@ void EnsureMetadataTask::executeTask() } } - NetJob::Ptr version_task; + Task::Ptr version_task; switch (m_provider) { case (ModPlatform::ResourceProvider::MODRINTH): @@ -127,7 +125,7 @@ void EnsureMetadataTask::executeTask() }; connect(version_task.get(), &Task::finished, this, [this, invalidade_leftover] { - NetJob::Ptr project_task; + Task::Ptr project_task; switch (m_provider) { case (ModPlatform::ResourceProvider::MODRINTH): @@ -149,7 +147,7 @@ void EnsureMetadataTask::executeTask() m_current_task = nullptr; }); - m_current_task = project_task.get(); + m_current_task = project_task; project_task->start(); }); @@ -164,7 +162,7 @@ void EnsureMetadataTask::executeTask() setStatus(tr("Requesting metadata information from %1 for '%2'...") .arg(ProviderCaps.readableName(m_provider), m_mods.begin().value()->name())); - m_current_task = version_task.get(); + m_current_task = version_task; version_task->start(); } @@ -210,7 +208,7 @@ void EnsureMetadataTask::emitFail(Mod* m, QString key, RemoveFromList remove) // Modrinth -NetJob::Ptr EnsureMetadataTask::modrinthVersionsTask() +Task::Ptr EnsureMetadataTask::modrinthVersionsTask() { auto hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first(); @@ -221,7 +219,7 @@ NetJob::Ptr EnsureMetadataTask::modrinthVersionsTask() if (!ver_task) return {}; - connect(ver_task.get(), &NetJob::succeeded, this, [this, response] { + connect(ver_task.get(), &Task::succeeded, this, [this, response] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { @@ -260,14 +258,14 @@ NetJob::Ptr EnsureMetadataTask::modrinthVersionsTask() return ver_task; } -NetJob::Ptr EnsureMetadataTask::modrinthProjectsTask() +Task::Ptr EnsureMetadataTask::modrinthProjectsTask() { QHash addonIds; for (auto const& data : m_temp_versions) addonIds.insert(data.addonId.toString(), data.hash); auto response = new QByteArray(); - NetJob::Ptr proj_task; + Task::Ptr proj_task; if (addonIds.isEmpty()) { qWarning() << "No addonId found!"; @@ -281,7 +279,7 @@ NetJob::Ptr EnsureMetadataTask::modrinthProjectsTask() if (!proj_task) return {}; - connect(proj_task.get(), &NetJob::succeeded, this, [this, response, addonIds] { + connect(proj_task.get(), &Task::succeeded, this, [this, response, addonIds] { QJsonParseError parse_error{}; auto doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { @@ -335,7 +333,7 @@ NetJob::Ptr EnsureMetadataTask::modrinthProjectsTask() } // Flame -NetJob::Ptr EnsureMetadataTask::flameVersionsTask() +Task::Ptr EnsureMetadataTask::flameVersionsTask() { auto* response = new QByteArray(); @@ -400,7 +398,7 @@ NetJob::Ptr EnsureMetadataTask::flameVersionsTask() return ver_task; } -NetJob::Ptr EnsureMetadataTask::flameProjectsTask() +Task::Ptr EnsureMetadataTask::flameProjectsTask() { QHash addonIds; for (auto const& hash : m_mods.keys()) { @@ -414,7 +412,7 @@ NetJob::Ptr EnsureMetadataTask::flameProjectsTask() } auto response = new QByteArray(); - NetJob::Ptr proj_task; + Task::Ptr proj_task; if (addonIds.isEmpty()) { qWarning() << "No addonId found!"; @@ -428,7 +426,7 @@ NetJob::Ptr EnsureMetadataTask::flameProjectsTask() if (!proj_task) return {}; - connect(proj_task.get(), &NetJob::succeeded, this, [this, response, addonIds] { + connect(proj_task.get(), &Task::succeeded, this, [this, response, addonIds] { QJsonParseError parse_error{}; auto doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { diff --git a/launcher/modplatform/EnsureMetadataTask.h b/launcher/modplatform/EnsureMetadataTask.h index a79e5861..635f4a2b 100644 --- a/launcher/modplatform/EnsureMetadataTask.h +++ b/launcher/modplatform/EnsureMetadataTask.h @@ -28,11 +28,11 @@ class EnsureMetadataTask : public Task { private: // FIXME: Move to their own namespace - auto modrinthVersionsTask() -> NetJob::Ptr; - auto modrinthProjectsTask() -> NetJob::Ptr; + auto modrinthVersionsTask() -> Task::Ptr; + auto modrinthProjectsTask() -> Task::Ptr; - auto flameVersionsTask() -> NetJob::Ptr; - auto flameProjectsTask() -> NetJob::Ptr; + auto flameVersionsTask() -> Task::Ptr; + auto flameProjectsTask() -> Task::Ptr; // Helpers enum class RemoveFromList { @@ -61,5 +61,5 @@ class EnsureMetadataTask : public Task { QHash m_temp_versions; ConcurrentTask* m_hashing_task; - NetJob* m_current_task; + Task::Ptr m_current_task; }; diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index 8f794955..dfb3652c 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -35,6 +35,7 @@ #pragma once +#include #include #include @@ -44,7 +45,7 @@ #include "../Version.h" #include "modplatform/ModIndex.h" -#include "net/NetJob.h" +#include "tasks/Task.h" /* Simple class with a common interface for interacting with APIs */ class ResourceAPI { @@ -113,28 +114,28 @@ class ResourceAPI { [[nodiscard]] virtual auto getSortingMethods() const -> QList = 0; public slots: - [[nodiscard]] virtual NetJob::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const + [[nodiscard]] virtual Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const { qWarning() << "TODO"; return nullptr; } - [[nodiscard]] virtual NetJob::Ptr getProject(QString addonId, QByteArray* response) const + [[nodiscard]] virtual Task::Ptr getProject(QString addonId, QByteArray* response) const { qWarning() << "TODO"; return nullptr; } - [[nodiscard]] virtual NetJob::Ptr getProjects(QStringList addonIds, QByteArray* response) const + [[nodiscard]] virtual Task::Ptr getProjects(QStringList addonIds, QByteArray* response) const { qWarning() << "TODO"; return nullptr; } - [[nodiscard]] virtual NetJob::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const + [[nodiscard]] virtual Task::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const { qWarning() << "TODO"; return nullptr; } - [[nodiscard]] virtual NetJob::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const + [[nodiscard]] virtual Task::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const { qWarning() << "TODO"; return nullptr; diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index 32729a14..c8981585 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -7,7 +7,7 @@ #include "net/Upload.h" -auto FlameAPI::matchFingerprints(const QList& fingerprints, QByteArray* response) -> NetJob::Ptr +Task::Ptr FlameAPI::matchFingerprints(const QList& fingerprints, QByteArray* response) { auto* netJob = new NetJob(QString("Flame::MatchFingerprints"), APPLICATION->network()); @@ -167,7 +167,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe return ver; } -NetJob::Ptr FlameAPI::getProjects(QStringList addonIds, QByteArray* response) const +Task::Ptr FlameAPI::getProjects(QStringList addonIds, QByteArray* response) const { auto* netJob = new NetJob(QString("Flame::GetProjects"), APPLICATION->network()); @@ -190,7 +190,7 @@ NetJob::Ptr FlameAPI::getProjects(QStringList addonIds, QByteArray* response) co return netJob; } -NetJob::Ptr FlameAPI::getFiles(const QStringList& fileIds, QByteArray* response) const +Task::Ptr FlameAPI::getFiles(const QStringList& fileIds, QByteArray* response) const { auto* netJob = new NetJob(QString("Flame::GetFiles"), APPLICATION->network()); diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index 2b288564..8e7ed727 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -10,9 +10,9 @@ class FlameAPI : public NetworkResourceAPI { auto getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::IndexedVersion; - NetJob::Ptr getProjects(QStringList addonIds, QByteArray* response) const override; - NetJob::Ptr matchFingerprints(const QList& fingerprints, QByteArray* response); - NetJob::Ptr getFiles(const QStringList& fileIds, QByteArray* response) const; + Task::Ptr getProjects(QStringList addonIds, QByteArray* response) const override; + Task::Ptr matchFingerprints(const QList& fingerprints, QByteArray* response); + Task::Ptr getFiles(const QStringList& fileIds, QByteArray* response) const; [[nodiscard]] auto getSortingMethods() const -> QList override; diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index fb6f78e8..890bff48 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -183,7 +183,7 @@ bool FlameCreationTask::updateInstance() QEventLoop loop; - connect(job.get(), &NetJob::succeeded, this, [this, raw_response, fileIds, old_inst_dir, &old_files, old_minecraft_dir] { + connect(job.get(), &Task::succeeded, this, [this, raw_response, fileIds, old_inst_dir, &old_files, old_minecraft_dir] { // Parse the API response QJsonParseError parse_error{}; auto doc = QJsonDocument::fromJson(*raw_response, &parse_error); @@ -225,7 +225,7 @@ bool FlameCreationTask::updateInstance() m_files_to_remove.append(old_minecraft_dir.absoluteFilePath(relative_path)); } }); - connect(job.get(), &NetJob::finished, &loop, &QEventLoop::quit); + connect(job.get(), &Task::finished, &loop, &QEventLoop::quit); m_process_update_file_info_job = job; job->start(); diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.h b/launcher/modplatform/flame/FlameInstanceCreationTask.h index 36b62e3e..0ae4735b 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.h +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.h @@ -86,7 +86,7 @@ class FlameCreationTask final : public InstanceCreationTask { Flame::Manifest m_pack; // Handle to allow aborting - NetJob::Ptr m_process_update_file_info_job = nullptr; + Task::Ptr m_process_update_file_info_job = nullptr; NetJob::Ptr m_files_job = nullptr; QString m_managed_id, m_managed_version_id; diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.cpp b/launcher/modplatform/helpers/NetworkResourceAPI.cpp index 77b085c0..88bbc045 100644 --- a/launcher/modplatform/helpers/NetworkResourceAPI.cpp +++ b/launcher/modplatform/helpers/NetworkResourceAPI.cpp @@ -5,7 +5,7 @@ #include "modplatform/ModIndex.h" -NetJob::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&& callbacks) const +Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&& callbacks) const { auto search_url_optional = getSearchURL(args); if (!search_url_optional.has_value()) { @@ -50,7 +50,7 @@ NetJob::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallback return netJob; } -NetJob::Ptr NetworkResourceAPI::getProjectInfo(ProjectInfoArgs&& args, ProjectInfoCallbacks&& callbacks) const +Task::Ptr NetworkResourceAPI::getProjectInfo(ProjectInfoArgs&& args, ProjectInfoCallbacks&& callbacks) const { auto response = new QByteArray(); auto job = getProject(args.pack.addonId.toString(), response); @@ -71,7 +71,7 @@ NetJob::Ptr NetworkResourceAPI::getProjectInfo(ProjectInfoArgs&& args, ProjectIn return job; } -NetJob::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, VersionSearchCallbacks&& callbacks) const +Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, VersionSearchCallbacks&& callbacks) const { auto versions_url_optional = getVersionsURL(args); if (!versions_url_optional.has_value()) @@ -104,7 +104,7 @@ NetJob::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Ver return netJob; } -NetJob::Ptr NetworkResourceAPI::getProject(QString addonId, QByteArray* response) const +Task::Ptr NetworkResourceAPI::getProject(QString addonId, QByteArray* response) const { auto project_url_optional = getInfoURL(addonId); if (!project_url_optional.has_value()) diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.h b/launcher/modplatform/helpers/NetworkResourceAPI.h index 834f274a..ab5586fd 100644 --- a/launcher/modplatform/helpers/NetworkResourceAPI.h +++ b/launcher/modplatform/helpers/NetworkResourceAPI.h @@ -4,12 +4,12 @@ class NetworkResourceAPI : public ResourceAPI { public: - NetJob::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const override; + Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const override; - NetJob::Ptr getProject(QString addonId, QByteArray* response) const override; + Task::Ptr getProject(QString addonId, QByteArray* response) const override; - NetJob::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const override; - NetJob::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const override; + Task::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const override; + Task::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const override; protected: [[nodiscard]] virtual auto getSearchURL(SearchArgs const& args) const -> std::optional = 0; diff --git a/launcher/modplatform/modrinth/ModrinthAPI.cpp b/launcher/modplatform/modrinth/ModrinthAPI.cpp index 8d7e3acf..028480a9 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.cpp +++ b/launcher/modplatform/modrinth/ModrinthAPI.cpp @@ -4,7 +4,7 @@ #include "Json.h" #include "net/Upload.h" -auto ModrinthAPI::currentVersion(QString hash, QString hash_format, QByteArray* response) -> NetJob::Ptr +Task::Ptr ModrinthAPI::currentVersion(QString hash, QString hash_format, QByteArray* response) { auto* netJob = new NetJob(QString("Modrinth::GetCurrentVersion"), APPLICATION->network()); @@ -16,7 +16,7 @@ auto ModrinthAPI::currentVersion(QString hash, QString hash_format, QByteArray* return netJob; } -auto ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_format, QByteArray* response) -> NetJob::Ptr +Task::Ptr ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_format, QByteArray* response) { auto* netJob = new NetJob(QString("Modrinth::GetCurrentVersions"), APPLICATION->network()); @@ -35,11 +35,11 @@ auto ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_format return netJob; } -auto ModrinthAPI::latestVersion(QString hash, - QString hash_format, - std::optional> mcVersions, - std::optional loaders, - QByteArray* response) -> NetJob::Ptr +Task::Ptr ModrinthAPI::latestVersion(QString hash, + QString hash_format, + std::optional> mcVersions, + std::optional loaders, + QByteArray* response) { auto* netJob = new NetJob(QString("Modrinth::GetLatestVersion"), APPLICATION->network()); @@ -67,11 +67,11 @@ auto ModrinthAPI::latestVersion(QString hash, return netJob; } -auto ModrinthAPI::latestVersions(const QStringList& hashes, - QString hash_format, - std::optional> mcVersions, - std::optional loaders, - QByteArray* response) -> NetJob::Ptr +Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes, + QString hash_format, + std::optional> mcVersions, + std::optional loaders, + QByteArray* response) { auto* netJob = new NetJob(QString("Modrinth::GetLatestVersions"), APPLICATION->network()); @@ -101,14 +101,17 @@ auto ModrinthAPI::latestVersions(const QStringList& hashes, return netJob; } -NetJob::Ptr ModrinthAPI::getProjects(QStringList addonIds, QByteArray* response) const +Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, QByteArray* response) const { auto netJob = new NetJob(QString("Modrinth::GetProjects"), APPLICATION->network()); auto searchUrl = getMultipleModInfoURL(addonIds); netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response)); - QObject::connect(netJob, &NetJob::finished, [response, netJob] { delete response; netJob->deleteLater(); }); + QObject::connect(netJob, &NetJob::finished, [response, netJob] { + delete response; + netJob->deleteLater(); + }); return netJob; } diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index 949fc46e..cba3afc8 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -28,25 +28,25 @@ class ModrinthAPI : public NetworkResourceAPI { public: auto currentVersion(QString hash, QString hash_format, - QByteArray* response) -> NetJob::Ptr; + QByteArray* response) -> Task::Ptr; auto currentVersions(const QStringList& hashes, QString hash_format, - QByteArray* response) -> NetJob::Ptr; + QByteArray* response) -> Task::Ptr; auto latestVersion(QString hash, QString hash_format, std::optional> mcVersions, std::optional loaders, - QByteArray* response) -> NetJob::Ptr; + QByteArray* response) -> Task::Ptr; auto latestVersions(const QStringList& hashes, QString hash_format, std::optional> mcVersions, std::optional loaders, - QByteArray* response) -> NetJob::Ptr; + QByteArray* response) -> Task::Ptr; - NetJob::Ptr getProjects(QStringList addonIds, QByteArray* response) const override; + Task::Ptr getProjects(QStringList addonIds, QByteArray* response) const override; public: [[nodiscard]] auto getSortingMethods() const -> QList override; diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index 7826b33d..daca68d7 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -175,7 +175,7 @@ void ModrinthCheckUpdate::executeTask() setStatus(tr("Waiting for the API response from Modrinth...")); setProgress(1, 3); - m_net_job = job.get(); + m_net_job = qSharedPointerObjectCast(job); job->start(); lock.exec(); diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.h b/launcher/modplatform/modrinth/ModrinthCheckUpdate.h index 177ce516..88e1a675 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.h +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.h @@ -19,5 +19,5 @@ class ModrinthCheckUpdate : public CheckUpdateTask { void executeTask() override; private: - NetJob* m_net_job = nullptr; + NetJob::Ptr m_net_job = nullptr; }; diff --git a/launcher/ui/pages/instance/ManagedPackPage.h b/launcher/ui/pages/instance/ManagedPackPage.h index d29a5e88..55782ba7 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.h +++ b/launcher/ui/pages/instance/ManagedPackPage.h @@ -12,6 +12,8 @@ #include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlamePackIndex.h" +#include "net/NetJob.h" + #include "ui/pages/BasePage.h" #include diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 202aa29a..be5ead90 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -225,7 +225,7 @@ void ResourceModel::clearData() endResetModel(); } -void ResourceModel::runSearchJob(NetJob::Ptr ptr) +void ResourceModel::runSearchJob(Task::Ptr ptr) { m_current_search_job = ptr; m_current_search_job->start(); diff --git a/launcher/ui/pages/modplatform/ResourceModel.h b/launcher/ui/pages/modplatform/ResourceModel.h index 02014fd6..7e813373 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.h +++ b/launcher/ui/pages/modplatform/ResourceModel.h @@ -80,7 +80,7 @@ class ResourceModel : public QAbstractListModel { /** Resets the model's data. */ void clearData(); - void runSearchJob(NetJob::Ptr); + void runSearchJob(Task::Ptr); void runInfoJob(Task::Ptr); [[nodiscard]] auto getCurrentSortingMethodByIndex() const -> std::optional; @@ -111,7 +111,7 @@ class ResourceModel : public QAbstractListModel { std::unique_ptr m_api; // Job for searching for new entries - shared_qobject_ptr m_current_search_job; + shared_qobject_ptr m_current_search_job; // Job for fetching versions and extra info on existing entries ConcurrentTask m_current_info_job; -- cgit From cd893e18d24d61c62f048d0c82c85b981f6e9a65 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sat, 14 Jan 2023 17:21:52 +0100 Subject: chore: update license headers Signed-off-by: Sefa Eyeoglu --- launcher/minecraft/PackProfile.cpp | 7 +++++-- launcher/minecraft/PackProfile.h | 7 +++++-- launcher/ui/pages/instance/VersionPage.cpp | 7 +++++-- launcher/ui/pages/instance/VersionPage.h | 6 +++++- 4 files changed, 20 insertions(+), 7 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 2028b236..270f3d22 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -1,7 +1,10 @@ -// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2022-2023 Sefa Eyeoglu +// +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 + /* * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022-2023 Sefa Eyeoglu * Copyright (C) 2022 TheKodeToad * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/minecraft/PackProfile.h b/launcher/minecraft/PackProfile.h index 35af9a56..8b885aa8 100644 --- a/launcher/minecraft/PackProfile.h +++ b/launcher/minecraft/PackProfile.h @@ -1,7 +1,10 @@ -// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2022-2023 Sefa Eyeoglu +// +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 + /* * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022-2023 Sefa Eyeoglu * Copyright (C) 2022 TheKodeToad * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index 07a97813..bce50a09 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -1,8 +1,11 @@ -// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2022-2023 Sefa Eyeoglu +// +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 + /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield - * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022-2023 Sefa Eyeoglu * Copyright (C) 2022 TheKodeToad * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/ui/pages/instance/VersionPage.h b/launcher/ui/pages/instance/VersionPage.h index a56f016d..ca98dfd1 100644 --- a/launcher/ui/pages/instance/VersionPage.h +++ b/launcher/ui/pages/instance/VersionPage.h @@ -1,7 +1,11 @@ -// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2022-2023 Sefa Eyeoglu +// +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 + /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield + * Copyright (C) 2022-2023 Sefa Eyeoglu * Copyright (C) 2022 TheKodeToad * * This program is free software: you can redistribute it and/or modify -- cgit From ea5020e188d7cb6d4c8dcf7f953161759ed17899 Mon Sep 17 00:00:00 2001 From: flow Date: Mon, 23 Jan 2023 11:03:55 -0300 Subject: fix(license): add/fix my copyright/license headers *sobbing in messy legal stuff i know nothing about* Signed-off-by: flow --- launcher/ResourceDownloadTask.cpp | 4 +-- launcher/ResourceDownloadTask.h | 4 +-- launcher/modplatform/ResourceAPI.h | 4 ++- launcher/modplatform/flame/FlameAPI.cpp | 4 +++ launcher/modplatform/flame/FlameAPI.h | 4 +++ .../modplatform/helpers/NetworkResourceAPI.cpp | 4 +++ launcher/modplatform/helpers/NetworkResourceAPI.h | 4 +++ launcher/modplatform/modrinth/ModrinthAPI.cpp | 4 +++ launcher/modplatform/modrinth/ModrinthAPI.h | 18 ++-------- launcher/ui/pages/instance/ManagedPackPage.cpp | 2 +- launcher/ui/pages/instance/ManagedPackPage.h | 2 +- launcher/ui/pages/modplatform/ModModel.cpp | 4 +++ launcher/ui/pages/modplatform/ModModel.h | 4 +++ launcher/ui/pages/modplatform/ModPage.cpp | 4 ++- launcher/ui/pages/modplatform/ModPage.h | 4 +++ launcher/ui/pages/modplatform/ResourceModel.cpp | 4 +++ launcher/ui/pages/modplatform/ResourceModel.h | 4 +++ launcher/ui/pages/modplatform/ResourcePage.cpp | 38 ++++++++++++++++++++++ launcher/ui/pages/modplatform/ResourcePage.h | 4 +++ .../modplatform/flame/FlameResourceModels.cpp | 4 +++ .../pages/modplatform/flame/FlameResourceModels.h | 4 +++ .../pages/modplatform/flame/FlameResourcePages.cpp | 4 ++- .../pages/modplatform/flame/FlameResourcePages.h | 4 ++- .../modrinth/ModrinthResourceModels.cpp | 2 ++ .../modplatform/modrinth/ModrinthResourceModels.h | 2 ++ .../modplatform/modrinth/ModrinthResourcePages.cpp | 2 ++ .../modplatform/modrinth/ModrinthResourcePages.h | 4 ++- 27 files changed, 119 insertions(+), 27 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ResourceDownloadTask.cpp b/launcher/ResourceDownloadTask.cpp index 8c9dae6f..98bcf259 100644 --- a/launcher/ResourceDownloadTask.cpp +++ b/launcher/ResourceDownloadTask.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* -* PolyMC - Minecraft Launcher -* Copyright (c) 2022 flowln +* Prism Launcher - Minecraft Launcher +* Copyright (c) 2022-2023 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/ResourceDownloadTask.h b/launcher/ResourceDownloadTask.h index 5ce39d69..73ad2d07 100644 --- a/launcher/ResourceDownloadTask.h +++ b/launcher/ResourceDownloadTask.h @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* -* PolyMC - Minecraft Launcher -* Copyright (c) 2022 flowln +* Prism Launcher - Minecraft Launcher +* Copyright (c) 2022-2023 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index dfb3652c..34f33779 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -1,4 +1,6 @@ -// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* * PolyMC - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index c8981585..57f70047 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #include "FlameAPI.h" #include "FlameModIndex.h" diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index 8e7ed727..06d749e6 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #pragma once #include "modplatform/ModIndex.h" diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.cpp b/launcher/modplatform/helpers/NetworkResourceAPI.cpp index 88bbc045..ac994c31 100644 --- a/launcher/modplatform/helpers/NetworkResourceAPI.cpp +++ b/launcher/modplatform/helpers/NetworkResourceAPI.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #include "NetworkResourceAPI.h" #include "Application.h" diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.h b/launcher/modplatform/helpers/NetworkResourceAPI.h index ab5586fd..94813bec 100644 --- a/launcher/modplatform/helpers/NetworkResourceAPI.h +++ b/launcher/modplatform/helpers/NetworkResourceAPI.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #pragma once #include "modplatform/ResourceAPI.h" diff --git a/launcher/modplatform/modrinth/ModrinthAPI.cpp b/launcher/modplatform/modrinth/ModrinthAPI.cpp index 028480a9..0c601d22 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.cpp +++ b/launcher/modplatform/modrinth/ModrinthAPI.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #include "ModrinthAPI.h" #include "Application.h" diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index cba3afc8..dda27303 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -1,20 +1,6 @@ +// SPDX-FileCopyrightText: 2022-2023 flowln +// // SPDX-License-Identifier: GPL-3.0-only -/* - * PolyMC - Minecraft Launcher - * Copyright (c) 2022 flowln - * - * 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 diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index 8d56d894..dc983d9a 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 flow +// SPDX-FileCopyrightText: 2022 flowln // // SPDX-License-Identifier: GPL-3.0-only diff --git a/launcher/ui/pages/instance/ManagedPackPage.h b/launcher/ui/pages/instance/ManagedPackPage.h index 55782ba7..1ac6fc03 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.h +++ b/launcher/ui/pages/instance/ManagedPackPage.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 flow +// SPDX-FileCopyrightText: 2022 flowln // // SPDX-License-Identifier: GPL-3.0-only diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index 433c7b10..3ffe6cb0 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #include "ModModel.h" #include "minecraft/MinecraftInstance.h" diff --git a/launcher/ui/pages/modplatform/ModModel.h b/launcher/ui/pages/modplatform/ModModel.h index 1fac9040..5d4a7785 100644 --- a/launcher/ui/pages/modplatform/ModModel.h +++ b/launcher/ui/pages/modplatform/ModModel.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #pragma once #include diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index d57e748b..04be43ad 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -1,4 +1,6 @@ -// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h index a3aab1de..c3b58cd6 100644 --- a/launcher/ui/pages/modplatform/ModPage.h +++ b/launcher/ui/pages/modplatform/ModPage.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #pragma once #include diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index eb723159..8af70104 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #include "ResourceModel.h" #include diff --git a/launcher/ui/pages/modplatform/ResourceModel.h b/launcher/ui/pages/modplatform/ResourceModel.h index 7e813373..610b631c 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.h +++ b/launcher/ui/pages/modplatform/ResourceModel.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #pragma once #include diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index bfa7e33d..bbd465bc 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -1,3 +1,41 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad + * + * 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 . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #include "ResourcePage.h" #include "ui_ResourcePage.h" diff --git a/launcher/ui/pages/modplatform/ResourcePage.h b/launcher/ui/pages/modplatform/ResourcePage.h index 71fc6593..1896d53e 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.h +++ b/launcher/ui/pages/modplatform/ResourcePage.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #pragma once #include diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp index a1cd1f26..de1f2122 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #include "FlameResourceModels.h" #include "Json.h" diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h index 47fbbe1a..625a2a7d 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #pragma once #include "ui/pages/modplatform/ModModel.h" diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp index e34be7fd..485431a7 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp @@ -1,4 +1,6 @@ -// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h index 12b51aa9..b21a53ad 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h @@ -1,4 +1,6 @@ -// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp index 06b72fd0..73d55133 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp @@ -1,3 +1,5 @@ +// SPDX-FileCopyrightText: 2023 flowln +// // SPDX-License-Identifier: GPL-3.0-only /* * PolyMC - Minecraft Launcher diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h index 2511f5e5..56cab146 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h @@ -1,3 +1,5 @@ +// SPDX-FileCopyrightText: 2023 flowln +// // SPDX-License-Identifier: GPL-3.0-only /* * PolyMC - Minecraft Launcher diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp index 45902d16..b82f800e 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -1,3 +1,5 @@ +// SPDX-FileCopyrightText: 2023 flowln +// // SPDX-License-Identifier: GPL-3.0-only /* * PolyMC - Minecraft Launcher diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h index a263bd44..be38eff1 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h @@ -1,4 +1,6 @@ -// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* * PolyMC - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu -- cgit From 199a7df807994ded1469cc893e6c68c21307444f Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 25 Jan 2023 10:43:23 +0100 Subject: refactor: add error handling to component import Signed-off-by: Sefa Eyeoglu --- launcher/minecraft/PackProfile.cpp | 56 +++++++++++++++--------------- launcher/minecraft/PackProfile.h | 3 +- launcher/ui/pages/instance/VersionPage.cpp | 8 +++-- 3 files changed, 35 insertions(+), 32 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 270f3d22..54fbf7f3 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -733,21 +733,47 @@ void PackProfile::invalidateLaunchProfile() void PackProfile::installJarMods(QStringList selectedFiles) { + // FIXME: get rid of _internal installJarMods_internal(selectedFiles); } void PackProfile::installCustomJar(QString selectedFile) { + // FIXME: get rid of _internal installCustomJar_internal(selectedFile); } -void PackProfile::installComponents(QStringList selectedFiles) +bool PackProfile::installComponents(QStringList selectedFiles) { - installComponents_internal(selectedFiles); + const QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches"); + if (!FS::ensureFolderPathExists(patchDir)) + return false; + + bool result = true; + for (const QString& source : selectedFiles) { + const QFileInfo sourceInfo(source); + + auto versionFile = ProfileUtils::parseJsonFile(sourceInfo, false); + const QString target = FS::PathCombine(patchDir, versionFile->uid + ".json"); + + if (!QFile::copy(source, target)) { + qWarning() << "Component" << source << "could not be copied to target" << target; + result = false; + continue; + } + + appendComponent(new Component(this, versionFile->uid, versionFile)); + } + + scheduleSave(); + invalidateLaunchProfile(); + + return result; } void PackProfile::installAgents(QStringList selectedFiles) { + // FIXME: get rid of _internal installAgents_internal(selectedFiles); } @@ -948,32 +974,6 @@ bool PackProfile::installCustomJar_internal(QString filepath) return true; } -bool PackProfile::installComponents_internal(QStringList filepaths) -{ - const QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches"); - if (!FS::ensureFolderPathExists(patchDir)) - return false; - - for (const QString& source : filepaths) { - const QFileInfo sourceInfo(source); - - auto versionFile = ProfileUtils::parseJsonFile(sourceInfo, false); - const QString target = FS::PathCombine(patchDir, versionFile->uid + ".json"); - - if (!QFile::copy(source, target)) - { - return false; - } - - appendComponent(new Component(this, versionFile->uid, versionFile)); - } - - scheduleSave(); - invalidateLaunchProfile(); - - return true; -} - bool PackProfile::installAgents_internal(QStringList filepaths) { // FIXME code duplication diff --git a/launcher/minecraft/PackProfile.h b/launcher/minecraft/PackProfile.h index 8b885aa8..e5b398db 100644 --- a/launcher/minecraft/PackProfile.h +++ b/launcher/minecraft/PackProfile.h @@ -90,7 +90,7 @@ public: void installCustomJar(QString selectedFile); /// install MMC/Prism component files - void installComponents(QStringList selectedFiles); + bool installComponents(QStringList selectedFiles); /// install Java agent files void installAgents(QStringList selectedFiles); @@ -177,7 +177,6 @@ private: bool load(); bool installJarMods_internal(QStringList filepaths); bool installCustomJar_internal(QString filepath); - bool installComponents_internal(QStringList filepaths); bool installAgents_internal(QStringList filepaths); bool removeComponent_internal(ComponentPtr patch); diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index bce50a09..f92a7660 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -384,8 +384,12 @@ void VersionPage::on_actionImport_Components_triggered() QStringList list = GuiUtil::BrowseForFiles("component", tr("Select components"), tr("Components (*.json)"), APPLICATION->settings()->get("CentralModsDir").toString(), this->parentWidget()); - if (!list.isEmpty()) - m_profile->installComponents(list); + if (!list.isEmpty()) { + if (!m_profile->installComponents(list)) { + QMessageBox::warning(this, tr("Failed to import components"), + tr("Some components could not be imported. Check logs for details")); + } + } updateButtons(); } -- cgit From 29f7ea752fd34bdea64a7c7f2c505982ac39ce0d Mon Sep 17 00:00:00 2001 From: flow Date: Tue, 24 Jan 2023 16:52:09 -0300 Subject: refactor: make shared_qobject_ptr ctor explicit This turns issues like creating two shared ptrs from a single raw ptr from popping up at runtime, instead making them a compile error. Signed-off-by: flow --- launcher/Application.cpp | 2 +- launcher/InstanceImportTask.cpp | 4 +- launcher/LaunchController.cpp | 6 +- launcher/QObjectPtr.h | 16 +++- launcher/java/JavaInstallList.cpp | 4 +- launcher/launch/steps/CheckJava.cpp | 2 +- launcher/meta/BaseEntity.cpp | 2 +- launcher/minecraft/AssetsUtils.cpp | 2 +- launcher/minecraft/ComponentUpdateTask.cpp | 2 +- launcher/minecraft/MinecraftInstance.cpp | 39 ++++---- launcher/minecraft/MinecraftUpdate.cpp | 8 +- launcher/minecraft/PackProfile.cpp | 20 ++-- launcher/minecraft/PackProfile.h | 4 +- launcher/minecraft/auth/MinecraftAccount.cpp | 4 +- launcher/minecraft/auth/flows/MSA.cpp | 36 +++---- launcher/minecraft/auth/flows/Mojang.cpp | 16 ++-- launcher/minecraft/auth/flows/Offline.cpp | 4 +- launcher/minecraft/mod/ResourcePackFolderModel.cpp | 2 +- launcher/minecraft/mod/TexturePackFolderModel.cpp | 2 +- launcher/minecraft/mod/tasks/BasicFolderLoadTask.h | 8 +- launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp | 8 +- launcher/minecraft/update/AssetUpdateTask.cpp | 2 +- launcher/minecraft/update/FMLLibrariesTask.cpp | 10 +- launcher/minecraft/update/LibrariesTask.cpp | 2 +- launcher/modplatform/CheckUpdateTask.h | 4 +- launcher/modplatform/EnsureMetadataTask.cpp | 8 +- launcher/modplatform/EnsureMetadataTask.h | 2 +- .../modplatform/atlauncher/ATLPackInstallTask.cpp | 19 ++-- launcher/modplatform/flame/FileResolvingTask.cpp | 4 +- launcher/modplatform/flame/FlameAPI.cpp | 16 ++-- launcher/modplatform/flame/FlameCheckUpdate.cpp | 2 +- .../flame/FlameInstanceCreationTask.cpp | 4 +- launcher/modplatform/helpers/HashUtils.cpp | 8 +- .../modplatform/helpers/NetworkResourceAPI.cpp | 18 ++-- launcher/modplatform/legacy_ftb/PackFetchTask.cpp | 2 +- .../modplatform/legacy_ftb/PackInstallTask.cpp | 2 +- .../modplatform/modpacksch/FTBPackInstallTask.cpp | 22 ++--- launcher/modplatform/modrinth/ModrinthAPI.cpp | 21 ++--- .../modplatform/modrinth/ModrinthCheckUpdate.cpp | 2 +- .../modrinth/ModrinthInstanceCreationTask.cpp | 2 +- .../technic/SingleZipPackInstallTask.cpp | 4 +- .../modplatform/technic/SolderPackInstallTask.cpp | 6 +- launcher/net/Download.cpp | 11 +-- launcher/net/Download.h | 3 - launcher/net/Upload.cpp | 2 +- launcher/net/Upload.h | 2 + launcher/news/NewsChecker.cpp | 6 +- launcher/tasks/ConcurrentTask.h | 2 + launcher/translations/TranslationsModel.cpp | 4 +- launcher/ui/dialogs/ModUpdateDialog.cpp | 30 +++--- launcher/ui/dialogs/ModUpdateDialog.h | 10 +- launcher/ui/dialogs/ResourceDownloadDialog.cpp | 2 +- launcher/ui/pages/instance/VersionPage.cpp | 2 +- launcher/ui/pages/instance/VersionPage.h | 2 +- launcher/ui/pages/modplatform/ResourceModel.cpp | 2 +- .../pages/modplatform/atlauncher/AtlListModel.cpp | 6 +- launcher/ui/pages/modplatform/flame/FlameModel.cpp | 6 +- launcher/ui/pages/modplatform/ftb/FtbListModel.cpp | 18 ++-- .../pages/modplatform/modrinth/ModrinthModel.cpp | 6 +- .../ui/pages/modplatform/technic/TechnicModel.cpp | 6 +- .../ui/pages/modplatform/technic/TechnicPage.cpp | 8 +- tests/DummyResourceAPI.h | 5 +- tests/Task_test.cpp | 104 +++++++++++---------- 63 files changed, 301 insertions(+), 287 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/Application.cpp b/launcher/Application.cpp index d4a1284f..387f735c 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -679,7 +679,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // initialize network access and proxy setup { - m_network = new QNetworkAccessManager(); + m_network.reset(new QNetworkAccessManager()); QString proxyTypeStr = settings()->get("ProxyType").toString(); QString addr = settings()->get("ProxyAddr").toString(); int port = settings()->get("ProxyPort").value(); diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 6b3fd296..70bf5784 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -88,7 +88,7 @@ void InstanceImportTask::executeTask() entry->setStale(true); m_archivePath = entry->getFullPath(); - m_filesNetJob = new NetJob(tr("Modpack download"), APPLICATION->network()); + m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network())); m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry)); connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded); @@ -301,7 +301,7 @@ void InstanceImportTask::processFlame() void InstanceImportTask::processTechnic() { - shared_qobject_ptr packProcessor = new Technic::TechnicPackProcessor(); + shared_qobject_ptr packProcessor{ new Technic::TechnicPackProcessor }; connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &InstanceImportTask::emitSucceeded); connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &InstanceImportTask::emitFailed); packProcessor->run(m_globalSettings, name(), m_instIcon, m_stagingPath); diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index 9741fd95..070ee283 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -382,15 +382,15 @@ void LaunchController::launchInstance() } resolved_servers = resolved_servers + "]\n\n"; } - m_launcher->prependStep(new TextPrint(m_launcher.get(), resolved_servers, MessageLevel::Launcher)); + m_launcher->prependStep(makeShared(m_launcher.get(), resolved_servers, MessageLevel::Launcher)); } else { online_mode = m_demo ? "demo" : "offline"; } - m_launcher->prependStep(new TextPrint(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher)); + m_launcher->prependStep(makeShared(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher)); // Prepend Version - m_launcher->prependStep(new TextPrint(m_launcher.get(), BuildConfig.LAUNCHER_DISPLAYNAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher)); + m_launcher->prependStep(makeShared(m_launcher.get(), BuildConfig.LAUNCHER_DISPLAYNAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher)); m_launcher->start(); } diff --git a/launcher/QObjectPtr.h b/launcher/QObjectPtr.h index ec466096..a1c64b43 100644 --- a/launcher/QObjectPtr.h +++ b/launcher/QObjectPtr.h @@ -20,8 +20,8 @@ using unique_qobject_ptr = QScopedPointer; template class shared_qobject_ptr : public QSharedPointer { public: - constexpr shared_qobject_ptr() : QSharedPointer() {} - constexpr shared_qobject_ptr(T* ptr) : QSharedPointer(ptr, &QObject::deleteLater) {} + constexpr explicit shared_qobject_ptr() : QSharedPointer() {} + constexpr explicit shared_qobject_ptr(T* ptr) : QSharedPointer(ptr, &QObject::deleteLater) {} constexpr shared_qobject_ptr(std::nullptr_t null_ptr) : QSharedPointer(null_ptr, &QObject::deleteLater) {} template @@ -33,9 +33,21 @@ class shared_qobject_ptr : public QSharedPointer { {} void reset() { QSharedPointer::reset(); } + void reset(T*&& other) + { + shared_qobject_ptr t(other); + this->swap(t); + } void reset(const shared_qobject_ptr& other) { shared_qobject_ptr t(other); this->swap(t); } }; + +template +shared_qobject_ptr makeShared(Args... args) +{ + auto obj = new T(args...); + return shared_qobject_ptr(obj); +} diff --git a/launcher/java/JavaInstallList.cpp b/launcher/java/JavaInstallList.cpp index e2f0aa00..b29af857 100644 --- a/launcher/java/JavaInstallList.cpp +++ b/launcher/java/JavaInstallList.cpp @@ -67,7 +67,7 @@ void JavaInstallList::load() if(m_status != Status::InProgress) { m_status = Status::InProgress; - m_loadTask = new JavaListLoadTask(this); + m_loadTask.reset(new JavaListLoadTask(this)); m_loadTask->start(); } } @@ -167,7 +167,7 @@ void JavaListLoadTask::executeTask() JavaUtils ju; QList candidate_paths = ju.FindJavaPaths(); - m_job = new JavaCheckerJob("Java detection"); + m_job.reset(new JavaCheckerJob("Java detection")); connect(m_job.get(), &Task::finished, this, &JavaListLoadTask::javaCheckerFinished); connect(m_job.get(), &Task::progress, this, &Task::setProgress); diff --git a/launcher/launch/steps/CheckJava.cpp b/launcher/launch/steps/CheckJava.cpp index 7aeb61bf..f0187586 100644 --- a/launcher/launch/steps/CheckJava.cpp +++ b/launcher/launch/steps/CheckJava.cpp @@ -93,7 +93,7 @@ void CheckJava::executeTask() || storedArchitecture.size() == 0 || storedRealArchitecture.size() == 0 || storedVendor.size() == 0) { - m_JavaChecker = new JavaChecker(); + m_JavaChecker.reset(new JavaChecker); emit logLine(QString("Checking Java version..."), MessageLevel::Launcher); connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished); m_JavaChecker->m_path = realJavaPath; diff --git a/launcher/meta/BaseEntity.cpp b/launcher/meta/BaseEntity.cpp index de4e1012..97815eba 100644 --- a/launcher/meta/BaseEntity.cpp +++ b/launcher/meta/BaseEntity.cpp @@ -126,7 +126,7 @@ void Meta::BaseEntity::load(Net::Mode loadType) { return; } - m_updateTask = new NetJob(QObject::tr("Download of meta file %1").arg(localFilename()), APPLICATION->network()); + m_updateTask.reset(new NetJob(QObject::tr("Download of meta file %1").arg(localFilename()), APPLICATION->network())); auto url = this->url(); auto entry = APPLICATION->metacache()->resolveEntry("meta", localFilename()); entry->setStale(true); diff --git a/launcher/minecraft/AssetsUtils.cpp b/launcher/minecraft/AssetsUtils.cpp index 15062c2b..16fdfdb1 100644 --- a/launcher/minecraft/AssetsUtils.cpp +++ b/launcher/minecraft/AssetsUtils.cpp @@ -340,7 +340,7 @@ QString AssetObject::getRelPath() NetJob::Ptr AssetsIndex::getDownloadJob() { - auto job = new NetJob(QObject::tr("Assets for %1").arg(id), APPLICATION->network()); + auto job = makeShared(QObject::tr("Assets for %1").arg(id), APPLICATION->network()); for (auto &object : objects.values()) { auto dl = object.getDownloadAction(); diff --git a/launcher/minecraft/ComponentUpdateTask.cpp b/launcher/minecraft/ComponentUpdateTask.cpp index 6db21622..d55bc17f 100644 --- a/launcher/minecraft/ComponentUpdateTask.cpp +++ b/launcher/minecraft/ComponentUpdateTask.cpp @@ -572,7 +572,7 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly) // add stuff... for(auto &add: toAdd) { - ComponentPtr component = new Component(d->m_list, add.uid); + auto component = makeShared(d->m_list, add.uid); if(!add.equalsVersion.isEmpty()) { // exact version diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index d0a5ed31..8a814cbf 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -962,12 +962,12 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt // print a header { - process->appendStep(new TextPrint(pptr, "Minecraft folder is:\n" + gameRoot() + "\n\n", MessageLevel::Launcher)); + process->appendStep(makeShared(pptr, "Minecraft folder is:\n" + gameRoot() + "\n\n", MessageLevel::Launcher)); } // check java { - process->appendStep(new CheckJava(pptr)); + process->appendStep(makeShared(pptr)); } // check launch method @@ -975,13 +975,13 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt QString method = launchMethod(); if(!validMethods.contains(method)) { - process->appendStep(new TextPrint(pptr, "Selected launch method \"" + method + "\" is not valid.\n", MessageLevel::Fatal)); + process->appendStep(makeShared(pptr, "Selected launch method \"" + method + "\" is not valid.\n", MessageLevel::Fatal)); return process; } // create the .minecraft folder and server-resource-packs (workaround for Minecraft bug MCL-3732) { - process->appendStep(new CreateGameFolders(pptr)); + process->appendStep(makeShared(pptr)); } if (!serverToJoin && settings()->get("JoinServerOnLaunch").toBool()) @@ -993,7 +993,7 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt if(serverToJoin && serverToJoin->port == 25565) { // Resolve server address to join on launch - auto *step = new LookupServerAddress(pptr); + auto step = makeShared(pptr); step->setLookupAddress(serverToJoin->address); step->setOutputAddressPtr(serverToJoin); process->appendStep(step); @@ -1002,7 +1002,7 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt // run pre-launch command if that's needed if(getPreLaunchCommand().size()) { - auto step = new PreLaunchCommand(pptr); + auto step = makeShared(pptr); step->setWorkingDirectory(gameRoot()); process->appendStep(step); } @@ -1011,43 +1011,43 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt if(session->status != AuthSession::PlayableOffline) { if(!session->demo) { - process->appendStep(new ClaimAccount(pptr, session)); + process->appendStep(makeShared(pptr, session)); } - process->appendStep(new Update(pptr, Net::Mode::Online)); + process->appendStep(makeShared(pptr, Net::Mode::Online)); } else { - process->appendStep(new Update(pptr, Net::Mode::Offline)); + process->appendStep(makeShared(pptr, Net::Mode::Offline)); } // if there are any jar mods { - process->appendStep(new ModMinecraftJar(pptr)); + process->appendStep(makeShared(pptr)); } // Scan mods folders for mods { - process->appendStep(new ScanModFolders(pptr)); + process->appendStep(makeShared(pptr)); } // print some instance info here... { - process->appendStep(new PrintInstanceInfo(pptr, session, serverToJoin)); + process->appendStep(makeShared(pptr, session, serverToJoin)); } // extract native jars if needed { - process->appendStep(new ExtractNatives(pptr)); + process->appendStep(makeShared(pptr)); } // reconstruct assets if needed { - process->appendStep(new ReconstructAssets(pptr)); + process->appendStep(makeShared(pptr)); } // verify that minimum Java requirements are met { - process->appendStep(new VerifyJavaInstall(pptr)); + process->appendStep(makeShared(pptr)); } { @@ -1055,7 +1055,7 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt auto method = launchMethod(); if(method == "LauncherPart") { - auto step = new LauncherPartLaunch(pptr); + auto step = makeShared(pptr); step->setWorkingDirectory(gameRoot()); step->setAuthSession(session); step->setServerToJoin(serverToJoin); @@ -1063,7 +1063,7 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt } else if (method == "DirectJava") { - auto step = new DirectJavaLaunch(pptr); + auto step = makeShared(pptr); step->setWorkingDirectory(gameRoot()); step->setAuthSession(session); step->setServerToJoin(serverToJoin); @@ -1074,7 +1074,7 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt // run post-exit command if that's needed if(getPostExitCommand().size()) { - auto step = new PostLaunchCommand(pptr); + auto step = makeShared(pptr); step->setWorkingDirectory(gameRoot()); process->appendStep(step); } @@ -1084,8 +1084,7 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt } if(m_settings->get("QuitAfterGameStop").toBool()) { - auto step = new QuitAfterGameStop(pptr); - process->appendStep(step); + process->appendStep(makeShared(pptr)); } m_launchProcess = process; emit launchTaskChanged(m_launchProcess); diff --git a/launcher/minecraft/MinecraftUpdate.cpp b/launcher/minecraft/MinecraftUpdate.cpp index 3a3aa864..07ad4882 100644 --- a/launcher/minecraft/MinecraftUpdate.cpp +++ b/launcher/minecraft/MinecraftUpdate.cpp @@ -43,7 +43,7 @@ void MinecraftUpdate::executeTask() m_tasks.clear(); // create folders { - m_tasks.append(new FoldersTask(m_inst)); + m_tasks.append(makeShared(m_inst)); } // add metadata update task if necessary @@ -59,17 +59,17 @@ void MinecraftUpdate::executeTask() // libraries download { - m_tasks.append(new LibrariesTask(m_inst)); + m_tasks.append(makeShared(m_inst)); } // FML libraries download and copy into the instance { - m_tasks.append(new FMLLibrariesTask(m_inst)); + m_tasks.append(makeShared(m_inst)); } // assets update { - m_tasks.append(new AssetUpdateTask(m_inst)); + m_tasks.append(makeShared(m_inst)); } if(!m_preFailure.isEmpty()) diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 42021b3c..da7c1d84 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -130,7 +130,7 @@ static ComponentPtr componentFromJsonV1(PackProfile * parent, const QString & co // critical auto uid = Json::requireString(obj.value("uid")); auto filePath = componentJsonPattern.arg(uid); - auto component = new Component(parent, uid); + auto component = makeShared(parent, uid); component->m_version = Json::ensureString(obj.value("version")); component->m_dependencyOnly = Json::ensureBoolean(obj.value("dependencyOnly"), false); component->m_important = Json::ensureBoolean(obj.value("important"), false); @@ -518,23 +518,23 @@ bool PackProfile::revertToBase(int index) return true; } -Component * PackProfile::getComponent(const QString &id) +ComponentPtr PackProfile::getComponent(const QString &id) { auto iter = d->componentIndex.find(id); if (iter == d->componentIndex.end()) { return nullptr; } - return (*iter).get(); + return (*iter); } -Component * PackProfile::getComponent(int index) +ComponentPtr PackProfile::getComponent(int index) { if(index < 0 || index >= d->components.size()) { return nullptr; } - return d->components[index].get(); + return d->components[index]; } QVariant PackProfile::data(const QModelIndex &index, int role) const @@ -765,7 +765,7 @@ bool PackProfile::installEmpty(const QString& uid, const QString& name) file.write(OneSixVersionFormat::versionFileToJson(f).toJson()); file.close(); - appendComponent(new Component(this, f->uid, f)); + appendComponent(makeShared(this, f->uid, f)); scheduleSave(); invalidateLaunchProfile(); return true; @@ -872,7 +872,7 @@ bool PackProfile::installJarMods_internal(QStringList filepaths) file.write(OneSixVersionFormat::versionFileToJson(f).toJson()); file.close(); - appendComponent(new Component(this, f->uid, f)); + appendComponent(makeShared(this, f->uid, f)); } scheduleSave(); invalidateLaunchProfile(); @@ -933,7 +933,7 @@ bool PackProfile::installCustomJar_internal(QString filepath) file.write(OneSixVersionFormat::versionFileToJson(f).toJson()); file.close(); - appendComponent(new Component(this, f->uid, f)); + appendComponent(makeShared(this, f->uid, f)); scheduleSave(); invalidateLaunchProfile(); @@ -989,7 +989,7 @@ bool PackProfile::installAgents_internal(QStringList filepaths) patchFile.write(OneSixVersionFormat::versionFileToJson(versionFile).toJson()); patchFile.close(); - appendComponent(new Component(this, versionFile->uid, versionFile)); + appendComponent(makeShared(this, versionFile->uid, versionFile)); } scheduleSave(); @@ -1038,7 +1038,7 @@ bool PackProfile::setComponentVersion(const QString& uid, const QString& version else { // add new - auto component = new Component(this, uid); + auto component = makeShared(this, uid); component->m_version = version; component->m_important = important; appendComponent(component); diff --git a/launcher/minecraft/PackProfile.h b/launcher/minecraft/PackProfile.h index 67b418f4..731cd0ba 100644 --- a/launcher/minecraft/PackProfile.h +++ b/launcher/minecraft/PackProfile.h @@ -136,10 +136,10 @@ signals: public: /// get the profile component by id - Component * getComponent(const QString &id); + ComponentPtr getComponent(const QString &id); /// get the profile component by index - Component * getComponent(int index); + ComponentPtr getComponent(int index); /// Add the component to the internal list of patches // todo(merged): is this the best approach diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index 73d570f1..48cf5d42 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -75,7 +75,7 @@ MinecraftAccountPtr MinecraftAccount::loadFromJsonV3(const QJsonObject& json) { MinecraftAccountPtr MinecraftAccount::createFromUsername(const QString &username) { - MinecraftAccountPtr account = new MinecraftAccount(); + auto account = makeShared(); account->data.type = AccountType::Mojang; account->data.yggdrasilToken.extra["userName"] = username; account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")); @@ -91,7 +91,7 @@ MinecraftAccountPtr MinecraftAccount::createBlankMSA() MinecraftAccountPtr MinecraftAccount::createOffline(const QString &username) { - MinecraftAccountPtr account = new MinecraftAccount(); + auto account = makeShared(); account->data.type = AccountType::Offline; account->data.yggdrasilToken.token = "offline"; account->data.yggdrasilToken.validity = Katabasis::Validity::Certain; diff --git a/launcher/minecraft/auth/flows/MSA.cpp b/launcher/minecraft/auth/flows/MSA.cpp index 416b8f2c..f1987e0c 100644 --- a/launcher/minecraft/auth/flows/MSA.cpp +++ b/launcher/minecraft/auth/flows/MSA.cpp @@ -10,28 +10,28 @@ #include "minecraft/auth/steps/GetSkinStep.h" MSASilent::MSASilent(AccountData* data, QObject* parent) : AuthFlow(data, parent) { - m_steps.append(new MSAStep(m_data, MSAStep::Action::Refresh)); - m_steps.append(new XboxUserStep(m_data)); - m_steps.append(new XboxAuthorizationStep(m_data, &m_data->xboxApiToken, "http://xboxlive.com", "Xbox")); - m_steps.append(new XboxAuthorizationStep(m_data, &m_data->mojangservicesToken, "rp://api.minecraftservices.com/", "Mojang")); - m_steps.append(new LauncherLoginStep(m_data)); - m_steps.append(new XboxProfileStep(m_data)); - m_steps.append(new EntitlementsStep(m_data)); - m_steps.append(new MinecraftProfileStep(m_data)); - m_steps.append(new GetSkinStep(m_data)); + m_steps.append(makeShared(m_data, MSAStep::Action::Refresh)); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data, &m_data->xboxApiToken, "http://xboxlive.com", "Xbox")); + m_steps.append(makeShared(m_data, &m_data->mojangservicesToken, "rp://api.minecraftservices.com/", "Mojang")); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data)); } MSAInteractive::MSAInteractive( AccountData* data, QObject* parent ) : AuthFlow(data, parent) { - m_steps.append(new MSAStep(m_data, MSAStep::Action::Login)); - m_steps.append(new XboxUserStep(m_data)); - m_steps.append(new XboxAuthorizationStep(m_data, &m_data->xboxApiToken, "http://xboxlive.com", "Xbox")); - m_steps.append(new XboxAuthorizationStep(m_data, &m_data->mojangservicesToken, "rp://api.minecraftservices.com/", "Mojang")); - m_steps.append(new LauncherLoginStep(m_data)); - m_steps.append(new XboxProfileStep(m_data)); - m_steps.append(new EntitlementsStep(m_data)); - m_steps.append(new MinecraftProfileStep(m_data)); - m_steps.append(new GetSkinStep(m_data)); + m_steps.append(makeShared(m_data, MSAStep::Action::Login)); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data, &m_data->xboxApiToken, "http://xboxlive.com", "Xbox")); + m_steps.append(makeShared(m_data, &m_data->mojangservicesToken, "rp://api.minecraftservices.com/", "Mojang")); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data)); } diff --git a/launcher/minecraft/auth/flows/Mojang.cpp b/launcher/minecraft/auth/flows/Mojang.cpp index b86b0936..5900ea98 100644 --- a/launcher/minecraft/auth/flows/Mojang.cpp +++ b/launcher/minecraft/auth/flows/Mojang.cpp @@ -9,10 +9,10 @@ MojangRefresh::MojangRefresh( AccountData *data, QObject *parent ) : AuthFlow(data, parent) { - m_steps.append(new YggdrasilStep(m_data, QString())); - m_steps.append(new MinecraftProfileStepMojang(m_data)); - m_steps.append(new MigrationEligibilityStep(m_data)); - m_steps.append(new GetSkinStep(m_data)); + m_steps.append(makeShared(m_data, QString())); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data)); } MojangLogin::MojangLogin( @@ -20,8 +20,8 @@ MojangLogin::MojangLogin( QString password, QObject *parent ): AuthFlow(data, parent), m_password(password) { - m_steps.append(new YggdrasilStep(m_data, m_password)); - m_steps.append(new MinecraftProfileStepMojang(m_data)); - m_steps.append(new MigrationEligibilityStep(m_data)); - m_steps.append(new GetSkinStep(m_data)); + m_steps.append(makeShared(m_data, m_password)); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data)); } diff --git a/launcher/minecraft/auth/flows/Offline.cpp b/launcher/minecraft/auth/flows/Offline.cpp index fc614a8c..d5c63271 100644 --- a/launcher/minecraft/auth/flows/Offline.cpp +++ b/launcher/minecraft/auth/flows/Offline.cpp @@ -6,12 +6,12 @@ OfflineRefresh::OfflineRefresh( AccountData *data, QObject *parent ) : AuthFlow(data, parent) { - m_steps.append(new OfflineStep(m_data)); + m_steps.append(makeShared(m_data)); } OfflineLogin::OfflineLogin( AccountData *data, QObject *parent ) : AuthFlow(data, parent) { - m_steps.append(new OfflineStep(m_data)); + m_steps.append(makeShared(m_data)); } diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index ebac707d..da4bd091 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -142,7 +142,7 @@ int ResourcePackFolderModel::columnCount(const QModelIndex& parent) const Task* ResourcePackFolderModel::createUpdateTask() { - return new BasicFolderLoadTask(m_dir, [](QFileInfo const& entry) { return new ResourcePack(entry); }); + return new BasicFolderLoadTask(m_dir, [](QFileInfo const& entry) { return makeShared(entry); }); } Task* ResourcePackFolderModel::createParseTask(Resource& resource) diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index 561f6202..5a32cfaf 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -43,7 +43,7 @@ TexturePackFolderModel::TexturePackFolderModel(const QString &dir) : ResourceFol Task* TexturePackFolderModel::createUpdateTask() { - return new BasicFolderLoadTask(m_dir, [](QFileInfo const& entry) { return new TexturePack(entry); }); + return new BasicFolderLoadTask(m_dir, [](QFileInfo const& entry) { return makeShared(entry); }); } Task* TexturePackFolderModel::createParseTask(Resource& resource) diff --git a/launcher/minecraft/mod/tasks/BasicFolderLoadTask.h b/launcher/minecraft/mod/tasks/BasicFolderLoadTask.h index 2fce2942..3ee7e2e0 100644 --- a/launcher/minecraft/mod/tasks/BasicFolderLoadTask.h +++ b/launcher/minecraft/mod/tasks/BasicFolderLoadTask.h @@ -26,11 +26,11 @@ class BasicFolderLoadTask : public Task { public: BasicFolderLoadTask(QDir dir) : Task(nullptr, false), m_dir(dir), m_result(new Result), m_thread_to_spawn_into(thread()) { - m_create_func = [](QFileInfo const& entry) -> Resource* { - return new Resource(entry); + m_create_func = [](QFileInfo const& entry) -> Resource::Ptr { + return makeShared(entry); }; } - BasicFolderLoadTask(QDir dir, std::function create_function) + BasicFolderLoadTask(QDir dir, std::function create_function) : Task(nullptr, false), m_dir(dir), m_result(new Result), m_create_func(std::move(create_function)), m_thread_to_spawn_into(thread()) {} @@ -65,7 +65,7 @@ private: std::atomic m_aborted = false; - std::function m_create_func; + std::function m_create_func; /** This is the thread in which we should put new mod objects */ QThread* m_thread_to_spawn_into; diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp index 78ef4386..3677a1dc 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp +++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp @@ -72,14 +72,14 @@ void ModFolderLoadTask::executeTask() delete mod; } else { - m_result->mods[mod->internal_id()] = mod; + m_result->mods[mod->internal_id()].reset(std::move(mod)); m_result->mods[mod->internal_id()]->setStatus(ModStatus::NoMetadata); } } else { QString chopped_id = mod->internal_id().chopped(9); if (m_result->mods.contains(chopped_id)) { - m_result->mods[mod->internal_id()] = mod; + m_result->mods[mod->internal_id()].reset(std::move(mod)); auto metadata = m_result->mods[chopped_id]->metadata(); if (metadata) { @@ -90,7 +90,7 @@ void ModFolderLoadTask::executeTask() } } else { - m_result->mods[mod->internal_id()] = mod; + m_result->mods[mod->internal_id()].reset(std::move(mod)); m_result->mods[mod->internal_id()]->setStatus(ModStatus::NoMetadata); } } @@ -130,6 +130,6 @@ void ModFolderLoadTask::getFromMetadata() auto* mod = new Mod(m_mods_dir, metadata); mod->setStatus(ModStatus::NotInstalled); - m_result->mods[mod->internal_id()] = mod; + m_result->mods[mod->internal_id()].reset(std::move(mod)); } } diff --git a/launcher/minecraft/update/AssetUpdateTask.cpp b/launcher/minecraft/update/AssetUpdateTask.cpp index dd246665..8ccb0e1d 100644 --- a/launcher/minecraft/update/AssetUpdateTask.cpp +++ b/launcher/minecraft/update/AssetUpdateTask.cpp @@ -24,7 +24,7 @@ void AssetUpdateTask::executeTask() auto assets = profile->getMinecraftAssets(); QUrl indexUrl = assets->url; QString localPath = assets->id + ".json"; - auto job = new NetJob( + auto job = makeShared( tr("Asset index for %1").arg(m_inst->name()), APPLICATION->network() ); diff --git a/launcher/minecraft/update/FMLLibrariesTask.cpp b/launcher/minecraft/update/FMLLibrariesTask.cpp index 7a0bd2f3..96fd3ba3 100644 --- a/launcher/minecraft/update/FMLLibrariesTask.cpp +++ b/launcher/minecraft/update/FMLLibrariesTask.cpp @@ -61,7 +61,7 @@ void FMLLibrariesTask::executeTask() // download missing libs to our place setStatus(tr("Downloading FML libraries...")); - auto dljob = new NetJob("FML libraries", APPLICATION->network()); + NetJob::Ptr dljob{ new NetJob("FML libraries", APPLICATION->network()) }; auto metacache = APPLICATION->metacache(); Net::Download::Options options = Net::Download::Option::MakeEternal; for (auto &lib : fmlLibsToProcess) @@ -71,10 +71,10 @@ void FMLLibrariesTask::executeTask() dljob->addNetAction(Net::Download::makeCached(QUrl(urlString), entry, options)); } - connect(dljob, &NetJob::succeeded, this, &FMLLibrariesTask::fmllibsFinished); - connect(dljob, &NetJob::failed, this, &FMLLibrariesTask::fmllibsFailed); - connect(dljob, &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); - connect(dljob, &NetJob::progress, this, &FMLLibrariesTask::progress); + connect(dljob.get(), &NetJob::succeeded, this, &FMLLibrariesTask::fmllibsFinished); + connect(dljob.get(), &NetJob::failed, this, &FMLLibrariesTask::fmllibsFailed); + connect(dljob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); + connect(dljob.get(), &NetJob::progress, this, &FMLLibrariesTask::progress); downloadJob.reset(dljob); downloadJob->start(); } diff --git a/launcher/minecraft/update/LibrariesTask.cpp b/launcher/minecraft/update/LibrariesTask.cpp index 33a575c2..b9410111 100644 --- a/launcher/minecraft/update/LibrariesTask.cpp +++ b/launcher/minecraft/update/LibrariesTask.cpp @@ -20,7 +20,7 @@ void LibrariesTask::executeTask() auto components = inst->getPackProfile(); auto profile = components->getProfile(); - auto job = new NetJob(tr("Libraries for instance %1").arg(inst->name()), APPLICATION->network()); + NetJob::Ptr job{ new NetJob(tr("Libraries for instance %1").arg(inst->name()), APPLICATION->network()) }; downloadJob.reset(job); auto metacache = APPLICATION->metacache(); diff --git a/launcher/modplatform/CheckUpdateTask.h b/launcher/modplatform/CheckUpdateTask.h index 932a62d9..f7582b8f 100644 --- a/launcher/modplatform/CheckUpdateTask.h +++ b/launcher/modplatform/CheckUpdateTask.h @@ -22,10 +22,10 @@ class CheckUpdateTask : public Task { QString new_version; QString changelog; ModPlatform::ResourceProvider provider; - ResourceDownloadTask* download; + shared_qobject_ptr download; public: - UpdatableMod(QString name, QString old_h, QString old_v, QString new_v, QString changelog, ModPlatform::ResourceProvider p, ResourceDownloadTask* t) + UpdatableMod(QString name, QString old_h, QString old_v, QString new_v, QString changelog, ModPlatform::ResourceProvider p, shared_qobject_ptr t) : name(name), old_hash(old_h), old_version(old_v), new_version(new_v), changelog(changelog), provider(p), download(t) {} }; diff --git a/launcher/modplatform/EnsureMetadataTask.cpp b/launcher/modplatform/EnsureMetadataTask.cpp index d9523052..34d969f0 100644 --- a/launcher/modplatform/EnsureMetadataTask.cpp +++ b/launcher/modplatform/EnsureMetadataTask.cpp @@ -32,7 +32,7 @@ EnsureMetadataTask::EnsureMetadataTask(Mod* mod, QDir dir, ModPlatform::Resource EnsureMetadataTask::EnsureMetadataTask(QList& mods, QDir dir, ModPlatform::ResourceProvider prov) : Task(nullptr), m_index_dir(dir), m_provider(prov), m_current_task(nullptr) { - m_hashing_task = new ConcurrentTask(this, "MakeHashesTask", 10); + m_hashing_task.reset(new ConcurrentTask(this, "MakeHashesTask", 10)); for (auto* mod : mods) { auto hash_task = createNewHash(mod); if (!hash_task) @@ -217,7 +217,7 @@ Task::Ptr EnsureMetadataTask::modrinthVersionsTask() // Prevents unfortunate timings when aborting the task if (!ver_task) - return {}; + return Task::Ptr{nullptr}; connect(ver_task.get(), &Task::succeeded, this, [this, response] { QJsonParseError parse_error{}; @@ -277,7 +277,7 @@ Task::Ptr EnsureMetadataTask::modrinthProjectsTask() // Prevents unfortunate timings when aborting the task if (!proj_task) - return {}; + return Task::Ptr{nullptr}; connect(proj_task.get(), &Task::succeeded, this, [this, response, addonIds] { QJsonParseError parse_error{}; @@ -434,7 +434,7 @@ Task::Ptr EnsureMetadataTask::flameProjectsTask() // Prevents unfortunate timings when aborting the task if (!proj_task) - return {}; + return Task::Ptr{nullptr}; connect(proj_task.get(), &Task::succeeded, this, [this, response, addonIds] { QJsonParseError parse_error{}; diff --git a/launcher/modplatform/EnsureMetadataTask.h b/launcher/modplatform/EnsureMetadataTask.h index 635f4a2b..03cae4e4 100644 --- a/launcher/modplatform/EnsureMetadataTask.h +++ b/launcher/modplatform/EnsureMetadataTask.h @@ -60,6 +60,6 @@ class EnsureMetadataTask : public Task { ModPlatform::ResourceProvider m_provider; QHash m_temp_versions; - ConcurrentTask* m_hashing_task; + ConcurrentTask::Ptr m_hashing_task; Task::Ptr m_current_task; }; diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 291ad916..4bd8b7f2 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -81,16 +81,17 @@ bool PackInstallTask::abort() void PackInstallTask::executeTask() { qDebug() << "PackInstallTask::executeTask: " << QThread::currentThreadId(); - auto *netJob = new NetJob("ATLauncher::VersionFetch", APPLICATION->network()); + NetJob::Ptr netJob{ new NetJob("ATLauncher::VersionFetch", APPLICATION->network()) }; auto searchUrl = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.json") .arg(m_pack_safe_name).arg(m_version_name); netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response)); + + QObject::connect(netJob.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded); + QObject::connect(netJob.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed); + QObject::connect(netJob.get(), &NetJob::aborted, this, &PackInstallTask::onDownloadAborted); + jobPtr = netJob; jobPtr->start(); - - QObject::connect(netJob, &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded); - QObject::connect(netJob, &NetJob::failed, this, &PackInstallTask::onDownloadFailed); - QObject::connect(netJob, &NetJob::aborted, this, &PackInstallTask::onDownloadAborted); } void PackInstallTask::onDownloadSucceeded() @@ -552,7 +553,7 @@ bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared file.write(OneSixVersionFormat::versionFileToJson(f).toJson()); file.close(); - profile->appendComponent(new Component(profile.get(), target_id, f)); + profile->appendComponent(ComponentPtr{ new Component(profile.get(), target_id, f) }); return true; } @@ -641,7 +642,7 @@ bool PackInstallTask::createPackComponent(QString instanceRoot, std::shared_ptr< file.write(OneSixVersionFormat::versionFileToJson(f).toJson()); file.close(); - profile->appendComponent(new Component(profile.get(), target_id, f)); + profile->appendComponent(ComponentPtr{ new Component(profile.get(), target_id, f) }); return true; } @@ -649,7 +650,7 @@ void PackInstallTask::installConfigs() { qDebug() << "PackInstallTask::installConfigs: " << QThread::currentThreadId(); setStatus(tr("Downloading configs...")); - jobPtr = new NetJob(tr("Config download"), APPLICATION->network()); + jobPtr.reset(new NetJob(tr("Config download"), APPLICATION->network())); auto path = QString("Configs/%1/%2.zip").arg(m_pack_safe_name).arg(m_version_name); auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.zip") @@ -747,7 +748,7 @@ void PackInstallTask::downloadMods() setStatus(tr("Downloading mods...")); jarmods.clear(); - jobPtr = new NetJob(tr("Mod download"), APPLICATION->network()); + jobPtr.reset(new NetJob(tr("Mod download"), APPLICATION->network())); for(const auto& mod : m_version.mods) { // skip non-client mods if(!mod.client) continue; diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp index 7f1beb1a..d3a737bb 100644 --- a/launcher/modplatform/flame/FileResolvingTask.cpp +++ b/launcher/modplatform/flame/FileResolvingTask.cpp @@ -23,7 +23,7 @@ void Flame::FileResolvingTask::executeTask() { setStatus(tr("Resolving mod IDs...")); setProgress(0, 3); - m_dljob = new NetJob("Mod id resolver", m_network); + m_dljob.reset(new NetJob("Mod id resolver", m_network)); result.reset(new QByteArray()); //build json data to send QJsonObject object; @@ -43,7 +43,7 @@ void Flame::FileResolvingTask::netJobFinished() { setProgress(1, 3); // job to check modrinth for blocked projects - m_checkJob = new NetJob("Modrinth check", m_network); + m_checkJob.reset(new NetJob("Modrinth check", m_network)); blockedProjects = QMap(); QJsonDocument doc; diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index 4b926ec3..5ef9a409 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -13,7 +13,7 @@ Task::Ptr FlameAPI::matchFingerprints(const QList& fingerprints, QByteArray* response) { - auto* netJob = new NetJob(QString("Flame::MatchFingerprints"), APPLICATION->network()); + auto netJob = makeShared(QString("Flame::MatchFingerprints"), APPLICATION->network()); QJsonObject body_obj; QJsonArray fingerprints_arr; @@ -28,7 +28,7 @@ Task::Ptr FlameAPI::matchFingerprints(const QList& fingerprints, QByteArra netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/fingerprints"), response, body_raw)); - QObject::connect(netJob, &NetJob::finished, [response] { delete response; }); + QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; }); return netJob; } @@ -173,7 +173,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe Task::Ptr FlameAPI::getProjects(QStringList addonIds, QByteArray* response) const { - auto* netJob = new NetJob(QString("Flame::GetProjects"), APPLICATION->network()); + auto netJob = makeShared(QString("Flame::GetProjects"), APPLICATION->network()); QJsonObject body_obj; QJsonArray addons_arr; @@ -188,15 +188,15 @@ Task::Ptr FlameAPI::getProjects(QStringList addonIds, QByteArray* response) cons netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods"), response, body_raw)); - QObject::connect(netJob, &NetJob::finished, [response] { delete response; }); - QObject::connect(netJob, &NetJob::failed, [body_raw] { qDebug() << body_raw; }); + QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; }); + QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; }); return netJob; } Task::Ptr FlameAPI::getFiles(const QStringList& fileIds, QByteArray* response) const { - auto* netJob = new NetJob(QString("Flame::GetFiles"), APPLICATION->network()); + auto netJob = makeShared(QString("Flame::GetFiles"), APPLICATION->network()); QJsonObject body_obj; QJsonArray files_arr; @@ -211,8 +211,8 @@ Task::Ptr FlameAPI::getFiles(const QStringList& fileIds, QByteArray* response) c netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods/files"), response, body_raw)); - QObject::connect(netJob, &NetJob::finished, [response] { delete response; }); - QObject::connect(netJob, &NetJob::failed, [body_raw] { qDebug() << body_raw; }); + QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; }); + QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; }); return netJob; } diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp index 7aee4f4c..06a89502 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.cpp +++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp @@ -172,7 +172,7 @@ void FlameCheckUpdate::executeTask() old_version = current_ver.version; } - auto download_task = new ResourceDownloadTask(pack, latest_ver, m_mods_folder); + auto download_task = makeShared(pack, latest_ver, m_mods_folder); m_updatable.emplace_back(pack.name, mod->metadata()->hash, old_version, latest_ver.version, api.getModFileChangelog(latest_ver.addonId.toInt(), latest_ver.fileId.toInt()), ModPlatform::ResourceProvider::FLAME, download_task); diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index 890bff48..964b559c 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -373,7 +373,7 @@ bool FlameCreationTask::createInstance() instance.setManagedPack("flame", m_managed_id, m_pack.name, m_managed_version_id, m_pack.version); instance.setName(name()); - m_mod_id_resolver = new Flame::FileResolvingTask(APPLICATION->network(), m_pack); + m_mod_id_resolver.reset(new Flame::FileResolvingTask(APPLICATION->network(), m_pack)); connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::succeeded, this, [this, &loop] { idResolverSucceeded(loop); }); connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason) { m_mod_id_resolver.reset(); @@ -452,7 +452,7 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop) void FlameCreationTask::setupDownloadJob(QEventLoop& loop) { - m_files_job = new NetJob(tr("Mod download"), APPLICATION->network()); + m_files_job.reset(new NetJob(tr("Mod download"), APPLICATION->network())); for (const auto& result : m_mod_id_resolver->getResults().files) { QString filename = result.fileName; if (!result.required) { diff --git a/launcher/modplatform/helpers/HashUtils.cpp b/launcher/modplatform/helpers/HashUtils.cpp index af484be0..2177ddad 100644 --- a/launcher/modplatform/helpers/HashUtils.cpp +++ b/launcher/modplatform/helpers/HashUtils.cpp @@ -28,22 +28,22 @@ Hasher::Ptr createHasher(QString file_path, ModPlatform::ResourceProvider provid Hasher::Ptr createModrinthHasher(QString file_path) { - return new ModrinthHasher(file_path); + return makeShared(file_path); } Hasher::Ptr createFlameHasher(QString file_path) { - return new FlameHasher(file_path); + return makeShared(file_path); } Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider) { - return new BlockedModHasher(file_path, provider); + return makeShared(file_path, provider); } Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider, QString type) { - auto hasher = new BlockedModHasher(file_path, provider); + auto hasher = makeShared(file_path, provider); hasher->useHashType(type); return hasher; } diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.cpp b/launcher/modplatform/helpers/NetworkResourceAPI.cpp index ac994c31..010ac15e 100644 --- a/launcher/modplatform/helpers/NetworkResourceAPI.cpp +++ b/launcher/modplatform/helpers/NetworkResourceAPI.cpp @@ -20,11 +20,11 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks& auto search_url = search_url_optional.value(); auto response = new QByteArray(); - auto netJob = new NetJob(QString("%1::Search").arg(debugName()), APPLICATION->network()); + auto netJob = makeShared(QString("%1::Search").arg(debugName()), APPLICATION->network()); netJob->addNetAction(Net::Download::makeByteArray(QUrl(search_url), response)); - QObject::connect(netJob, &NetJob::succeeded, [=]{ + QObject::connect(netJob.get(), &NetJob::succeeded, [=]{ QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { @@ -40,14 +40,14 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks& callbacks.on_succeed(doc); }); - QObject::connect(netJob, &NetJob::failed, [=](QString reason){ + QObject::connect(netJob.get(), &NetJob::failed, [=](QString reason){ int network_error_code = -1; if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply) network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); callbacks.on_fail(reason, network_error_code); }); - QObject::connect(netJob, &NetJob::aborted, [=]{ + QObject::connect(netJob.get(), &NetJob::aborted, [=]{ callbacks.on_abort(); }); @@ -83,12 +83,12 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi auto versions_url = versions_url_optional.value(); - auto netJob = new NetJob(QString("%1::Versions").arg(args.pack.name), APPLICATION->network()); + auto netJob = makeShared(QString("%1::Versions").arg(args.pack.name), APPLICATION->network()); auto response = new QByteArray(); netJob->addNetAction(Net::Download::makeByteArray(versions_url, response)); - QObject::connect(netJob, &NetJob::succeeded, [=] { + QObject::connect(netJob.get(), &NetJob::succeeded, [=] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { @@ -101,7 +101,7 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi callbacks.on_succeed(doc, args.pack); }); - QObject::connect(netJob, &NetJob::finished, [response] { + QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; }); @@ -116,11 +116,11 @@ Task::Ptr NetworkResourceAPI::getProject(QString addonId, QByteArray* response) auto project_url = project_url_optional.value(); - auto netJob = new NetJob(QString("%1::GetProject").arg(addonId), APPLICATION->network()); + auto netJob = makeShared(QString("%1::GetProject").arg(addonId), APPLICATION->network()); netJob->addNetAction(Net::Download::makeByteArray(QUrl(project_url), response)); - QObject::connect(netJob, &NetJob::finished, [response] { + QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; }); diff --git a/launcher/modplatform/legacy_ftb/PackFetchTask.cpp b/launcher/modplatform/legacy_ftb/PackFetchTask.cpp index 36aa60c7..e8768c5c 100644 --- a/launcher/modplatform/legacy_ftb/PackFetchTask.cpp +++ b/launcher/modplatform/legacy_ftb/PackFetchTask.cpp @@ -47,7 +47,7 @@ void PackFetchTask::fetch() publicPacks.clear(); thirdPartyPacks.clear(); - jobPtr = new NetJob("LegacyFTB::ModpackFetch", m_network); + jobPtr.reset(new NetJob("LegacyFTB::ModpackFetch", m_network)); QUrl publicPacksUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/modpacks.xml"); qDebug() << "Downloading public version info from" << publicPacksUrl.toString(); diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp index 06b3788b..8d45fc5c 100644 --- a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp +++ b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp @@ -69,7 +69,7 @@ void PackInstallTask::downloadPack() archivePath = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file); - netJobContainer = new NetJob("Download FTB Pack", m_network); + 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); diff --git a/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp b/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp index 2979663d..68d4751c 100644 --- a/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp +++ b/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp @@ -87,15 +87,15 @@ void PackInstallTask::executeTask() auto version = *version_it; - auto* netJob = new NetJob("ModpacksCH::VersionFetch", APPLICATION->network()); + auto netJob = makeShared("ModpacksCH::VersionFetch", APPLICATION->network()); auto searchUrl = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/%1/%2").arg(m_pack.id).arg(version.id); netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &m_response)); - QObject::connect(netJob, &NetJob::succeeded, this, &PackInstallTask::onManifestDownloadSucceeded); - QObject::connect(netJob, &NetJob::failed, this, &PackInstallTask::onManifestDownloadFailed); - QObject::connect(netJob, &NetJob::aborted, this, &PackInstallTask::abort); - QObject::connect(netJob, &NetJob::progress, this, &PackInstallTask::setProgress); + QObject::connect(netJob.get(), &NetJob::succeeded, this, &PackInstallTask::onManifestDownloadSucceeded); + QObject::connect(netJob.get(), &NetJob::failed, this, &PackInstallTask::onManifestDownloadFailed); + QObject::connect(netJob.get(), &NetJob::aborted, this, &PackInstallTask::abort); + QObject::connect(netJob.get(), &NetJob::progress, this, &PackInstallTask::setProgress); m_net_job = netJob; @@ -162,7 +162,7 @@ void PackInstallTask::resolveMods() index++; } - m_mod_id_resolver_task = new Flame::FileResolvingTask(APPLICATION->network(), manifest); + m_mod_id_resolver_task.reset(new Flame::FileResolvingTask(APPLICATION->network(), manifest)); connect(m_mod_id_resolver_task.get(), &Flame::FileResolvingTask::succeeded, this, &PackInstallTask::onResolveModsSucceeded); connect(m_mod_id_resolver_task.get(), &Flame::FileResolvingTask::failed, this, &PackInstallTask::onResolveModsFailed); @@ -294,7 +294,7 @@ void PackInstallTask::downloadPack() setStatus(tr("Downloading mods...")); setAbortable(false); - auto* jobPtr = new NetJob(tr("Mod download"), APPLICATION->network()); + auto jobPtr = makeShared(tr("Mod download"), APPLICATION->network()); for (auto const& file : m_version.files) { if (file.serverOnly || file.url.isEmpty()) continue; @@ -313,10 +313,10 @@ void PackInstallTask::downloadPack() jobPtr->addNetAction(dl); } - connect(jobPtr, &NetJob::succeeded, this, &PackInstallTask::onModDownloadSucceeded); - connect(jobPtr, &NetJob::failed, this, &PackInstallTask::onModDownloadFailed); - connect(jobPtr, &NetJob::aborted, this, &PackInstallTask::abort); - connect(jobPtr, &NetJob::progress, this, &PackInstallTask::setProgress); + connect(jobPtr.get(), &NetJob::succeeded, this, &PackInstallTask::onModDownloadSucceeded); + connect(jobPtr.get(), &NetJob::failed, this, &PackInstallTask::onModDownloadFailed); + connect(jobPtr.get(), &NetJob::aborted, this, &PackInstallTask::abort); + connect(jobPtr.get(), &NetJob::progress, this, &PackInstallTask::setProgress); m_net_job = jobPtr; diff --git a/launcher/modplatform/modrinth/ModrinthAPI.cpp b/launcher/modplatform/modrinth/ModrinthAPI.cpp index 5a16113d..29e3d129 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.cpp +++ b/launcher/modplatform/modrinth/ModrinthAPI.cpp @@ -11,19 +11,19 @@ Task::Ptr ModrinthAPI::currentVersion(QString hash, QString hash_format, QByteArray* response) { - auto* netJob = new NetJob(QString("Modrinth::GetCurrentVersion"), APPLICATION->network()); + auto netJob = makeShared(QString("Modrinth::GetCurrentVersion"), APPLICATION->network()); netJob->addNetAction(Net::Download::makeByteArray( QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1?algorithm=%2").arg(hash, hash_format), response)); - QObject::connect(netJob, &NetJob::finished, [response] { delete response; }); + QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; }); return netJob; } Task::Ptr ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_format, QByteArray* response) { - auto* netJob = new NetJob(QString("Modrinth::GetCurrentVersions"), APPLICATION->network()); + auto netJob = makeShared(QString("Modrinth::GetCurrentVersions"), APPLICATION->network()); QJsonObject body_obj; @@ -35,7 +35,7 @@ Task::Ptr ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_f netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files"), response, body_raw)); - QObject::connect(netJob, &NetJob::finished, [response] { delete response; }); + QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; }); return netJob; } @@ -46,7 +46,7 @@ Task::Ptr ModrinthAPI::latestVersion(QString hash, std::optional loaders, QByteArray* response) { - auto* netJob = new NetJob(QString("Modrinth::GetLatestVersion"), APPLICATION->network()); + auto netJob = makeShared(QString("Modrinth::GetLatestVersion"), APPLICATION->network()); QJsonObject body_obj; @@ -67,7 +67,7 @@ Task::Ptr ModrinthAPI::latestVersion(QString hash, netJob->addNetAction(Net::Upload::makeByteArray( QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1/update?algorithm=%2").arg(hash, hash_format), response, body_raw)); - QObject::connect(netJob, &NetJob::finished, [response] { delete response; }); + QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; }); return netJob; } @@ -78,7 +78,7 @@ Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes, std::optional loaders, QByteArray* response) { - auto* netJob = new NetJob(QString("Modrinth::GetLatestVersions"), APPLICATION->network()); + auto netJob = makeShared(QString("Modrinth::GetLatestVersions"), APPLICATION->network()); QJsonObject body_obj; @@ -101,21 +101,20 @@ Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes, netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files/update"), response, body_raw)); - QObject::connect(netJob, &NetJob::finished, [response] { delete response; }); + QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; }); return netJob; } Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, QByteArray* response) const { - auto netJob = new NetJob(QString("Modrinth::GetProjects"), APPLICATION->network()); + auto netJob = makeShared(QString("Modrinth::GetProjects"), APPLICATION->network()); auto searchUrl = getMultipleModInfoURL(addonIds); netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response)); - QObject::connect(netJob, &NetJob::finished, [response, netJob] { + QObject::connect(netJob.get(), &NetJob::finished, [response, netJob] { delete response; - netJob->deleteLater(); }); return netJob; diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index daca68d7..d1be7209 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -159,7 +159,7 @@ void ModrinthCheckUpdate::executeTask() pack.description = mod->description(); pack.provider = ModPlatform::ResourceProvider::MODRINTH; - auto download_task = new ResourceDownloadTask(pack, project_ver, m_mods_folder); + auto download_task = makeShared(pack, project_ver, m_mods_folder); m_updatable.emplace_back(pack.name, hash, mod->version(), project_ver.version_number, project_ver.changelog, ModPlatform::ResourceProvider::MODRINTH, download_task); diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index c5a27c9d..94c0bf77 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -223,7 +223,7 @@ bool ModrinthCreationTask::createInstance() instance.setName(name()); instance.saveNow(); - m_files_job = new NetJob(tr("Mod download"), APPLICATION->network()); + m_files_job.reset(new NetJob(tr("Mod download"), APPLICATION->network())); for (auto file : m_files) { auto path = FS::PathCombine(m_stagingPath, ".minecraft", file.path); diff --git a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp index 6438d9ef..8fd43d21 100644 --- a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp +++ b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp @@ -44,7 +44,7 @@ void Technic::SingleZipPackInstallTask::executeTask() const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path(); auto entry = APPLICATION->metacache()->resolveEntry("general", path); entry->setStale(true); - m_filesNetJob = new NetJob(tr("Modpack download"), APPLICATION->network()); + m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network())); m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry)); m_archivePath = entry->getFullPath(); auto job = m_filesNetJob.get(); @@ -130,7 +130,7 @@ void Technic::SingleZipPackInstallTask::extractFinished() } } - shared_qobject_ptr packProcessor = new Technic::TechnicPackProcessor(); + auto packProcessor = makeShared(); connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &Technic::SingleZipPackInstallTask::emitSucceeded); connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &Technic::SingleZipPackInstallTask::emitFailed); packProcessor->run(m_globalSettings, name(), m_instIcon, m_stagingPath, m_minecraftVersion); diff --git a/launcher/modplatform/technic/SolderPackInstallTask.cpp b/launcher/modplatform/technic/SolderPackInstallTask.cpp index 19731b38..77c503f0 100644 --- a/launcher/modplatform/technic/SolderPackInstallTask.cpp +++ b/launcher/modplatform/technic/SolderPackInstallTask.cpp @@ -70,7 +70,7 @@ void Technic::SolderPackInstallTask::executeTask() { setStatus(tr("Resolving modpack files")); - m_filesNetJob = new NetJob(tr("Resolving modpack files"), m_network); + m_filesNetJob.reset(new NetJob(tr("Resolving modpack files"), m_network)); auto sourceUrl = QString("%1/modpack/%2/%3").arg(m_solderUrl.toString(), m_pack, m_version); m_filesNetJob->addNetAction(Net::Download::makeByteArray(sourceUrl, &m_response)); @@ -107,7 +107,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded() if (!build.minecraft.isEmpty()) m_minecraftVersion = build.minecraft; - m_filesNetJob = new NetJob(tr("Downloading modpack"), m_network); + m_filesNetJob.reset(new NetJob(tr("Downloading modpack"), m_network)); int i = 0; for (const auto &mod : build.mods) { @@ -219,7 +219,7 @@ void Technic::SolderPackInstallTask::extractFinished() } } - shared_qobject_ptr packProcessor = new Technic::TechnicPackProcessor(); + auto packProcessor = makeShared(); connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &Technic::SolderPackInstallTask::emitSucceeded); connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &Technic::SolderPackInstallTask::emitFailed); packProcessor->run(m_globalSettings, name(), m_instIcon, m_stagingPath, m_minecraftVersion, true); diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp index fd3dbedc..5982c8c9 100644 --- a/launcher/net/Download.cpp +++ b/launcher/net/Download.cpp @@ -49,14 +49,9 @@ namespace Net { -Download::Download() : NetAction() -{ - m_state = State::Inactive; -} - auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Download::Ptr { - auto* dl = new Download(); + auto dl = makeShared(); dl->m_url = url; dl->m_options = options; auto md5Node = new ChecksumValidator(QCryptographicHash::Md5); @@ -67,7 +62,7 @@ auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Down auto Download::makeByteArray(QUrl url, QByteArray* output, Options options) -> Download::Ptr { - auto* dl = new Download(); + auto dl = makeShared(); dl->m_url = url; dl->m_options = options; dl->m_sink.reset(new ByteArraySink(output)); @@ -76,7 +71,7 @@ auto Download::makeByteArray(QUrl url, QByteArray* output, Options options) -> D auto Download::makeFile(QUrl url, QString path, Options options) -> Download::Ptr { - auto* dl = new Download(); + auto dl = makeShared(); dl->m_url = url; dl->m_options = options; dl->m_sink.reset(new FileSink(path)); diff --git a/launcher/net/Download.h b/launcher/net/Download.h index 3faa5db5..7e1df322 100644 --- a/launcher/net/Download.h +++ b/launcher/net/Download.h @@ -52,9 +52,6 @@ class Download : public NetAction { enum class Option { NoOptions = 0, AcceptLocalFiles = 1, MakeEternal = 2 }; Q_DECLARE_FLAGS(Options, Option) - protected: - explicit Download(); - public: ~Download() override = default; diff --git a/launcher/net/Upload.cpp b/launcher/net/Upload.cpp index f3b19022..79b6af8d 100644 --- a/launcher/net/Upload.cpp +++ b/launcher/net/Upload.cpp @@ -233,7 +233,7 @@ namespace Net { } Upload::Ptr Upload::makeByteArray(QUrl url, QByteArray *output, QByteArray m_post_data) { - auto* up = new Upload(); + auto up = makeShared(); up->m_url = std::move(url); up->m_sink.reset(new ByteArraySink(output)); up->m_post_data = std::move(m_post_data); diff --git a/launcher/net/Upload.h b/launcher/net/Upload.h index 7c194bbc..5a0b2e74 100644 --- a/launcher/net/Upload.h +++ b/launcher/net/Upload.h @@ -45,6 +45,8 @@ namespace Net { Q_OBJECT public: + using Ptr = shared_qobject_ptr; + static Upload::Ptr makeByteArray(QUrl url, QByteArray *output, QByteArray m_post_data); auto abort() -> bool override; auto canAbort() const -> bool override { return true; }; diff --git a/launcher/news/NewsChecker.cpp b/launcher/news/NewsChecker.cpp index 3b969732..1f1520d0 100644 --- a/launcher/news/NewsChecker.cpp +++ b/launcher/news/NewsChecker.cpp @@ -57,10 +57,10 @@ void NewsChecker::reloadNews() qDebug() << "Reloading news."; - NetJob* job = new NetJob("News RSS Feed", m_network); + NetJob::Ptr job{ new NetJob("News RSS Feed", m_network) }; job->addNetAction(Net::Download::makeByteArray(m_feedUrl, &newsData)); - QObject::connect(job, &NetJob::succeeded, this, &NewsChecker::rssDownloadFinished); - QObject::connect(job, &NetJob::failed, this, &NewsChecker::rssDownloadFailed); + QObject::connect(job.get(), &NetJob::succeeded, this, &NewsChecker::rssDownloadFinished); + QObject::connect(job.get(), &NetJob::failed, this, &NewsChecker::rssDownloadFailed); m_newsNetJob.reset(job); job->start(); } diff --git a/launcher/tasks/ConcurrentTask.h b/launcher/tasks/ConcurrentTask.h index b46919fb..d074d2e2 100644 --- a/launcher/tasks/ConcurrentTask.h +++ b/launcher/tasks/ConcurrentTask.h @@ -8,6 +8,8 @@ class ConcurrentTask : public Task { Q_OBJECT public: + using Ptr = shared_qobject_ptr; + explicit ConcurrentTask(QObject* parent = nullptr, QString task_name = "", int max_concurrent = 6); ~ConcurrentTask() override; diff --git a/launcher/translations/TranslationsModel.cpp b/launcher/translations/TranslationsModel.cpp index 38f48296..46db4804 100644 --- a/launcher/translations/TranslationsModel.cpp +++ b/launcher/translations/TranslationsModel.cpp @@ -670,7 +670,7 @@ void TranslationsModel::downloadIndex() return; } qDebug() << "Downloading Translations Index..."; - d->m_index_job = new NetJob("Translations Index", APPLICATION->network()); + d->m_index_job.reset(new NetJob("Translations Index", APPLICATION->network())); MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("translations", "index_v2.json"); entry->setStale(true); d->m_index_task = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATIONS_BASE_URL + "index_v2.json"), entry); @@ -722,7 +722,7 @@ void TranslationsModel::downloadTranslation(QString key) dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash)); dl->setProgress(dl->getProgress(), lang->file_size); - d->m_dl_job = new NetJob("Translation for " + key, APPLICATION->network()); + d->m_dl_job.reset(new NetJob("Translation for " + key, APPLICATION->network())); d->m_dl_job->addNetAction(dl); connect(d->m_dl_job.get(), &NetJob::succeeded, this, &TranslationsModel::dlGood); diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 4ef42d6c..8618b924 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -88,15 +88,15 @@ void ModUpdateDialog::checkCandidates() SequentialTask check_task(m_parent, tr("Checking for updates")); if (!m_modrinth_to_update.empty()) { - m_modrinth_check_task = new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_mod_model); - connect(m_modrinth_check_task, &CheckUpdateTask::checkFailed, this, + m_modrinth_check_task.reset(new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_mod_model)); + connect(m_modrinth_check_task.get(), &CheckUpdateTask::checkFailed, this, [this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.append({mod, reason, recover_url}); }); check_task.addTask(m_modrinth_check_task); } if (!m_flame_to_update.empty()) { - m_flame_check_task = new FlameCheckUpdate(m_flame_to_update, versions, loaders, m_mod_model); - connect(m_flame_check_task, &CheckUpdateTask::checkFailed, this, + m_flame_check_task.reset(new FlameCheckUpdate(m_flame_to_update, versions, loaders, m_mod_model)); + connect(m_flame_check_task.get(), &CheckUpdateTask::checkFailed, this, [this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.append({mod, reason, recover_url}); }); check_task.addTask(m_flame_check_task); } @@ -266,9 +266,9 @@ auto ModUpdateDialog::ensureMetadata() -> bool } if (!modrinth_tmp.empty()) { - auto* modrinth_task = new EnsureMetadataTask(modrinth_tmp, index_dir, ModPlatform::ResourceProvider::MODRINTH); - connect(modrinth_task, &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); - connect(modrinth_task, &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) { + auto modrinth_task = makeShared(modrinth_tmp, index_dir, ModPlatform::ResourceProvider::MODRINTH); + connect(modrinth_task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); + connect(modrinth_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) { onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::MODRINTH); }); @@ -279,9 +279,9 @@ auto ModUpdateDialog::ensureMetadata() -> bool } if (!flame_tmp.empty()) { - auto* flame_task = new EnsureMetadataTask(flame_tmp, index_dir, ModPlatform::ResourceProvider::FLAME); - connect(flame_task, &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); - connect(flame_task, &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) { + auto flame_task = makeShared(flame_tmp, index_dir, ModPlatform::ResourceProvider::FLAME); + connect(flame_task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); + connect(flame_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) { onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::FLAME); }); @@ -334,9 +334,9 @@ void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::R if (try_others) { auto index_dir = indexDir(); - auto* task = new EnsureMetadataTask(mod, index_dir, next(first_choice)); - connect(task, &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); - connect(task, &EnsureMetadataTask::metadataFailed, [this](Mod* candidate) { onMetadataFailed(candidate, false); }); + auto task = makeShared(mod, index_dir, next(first_choice)); + connect(task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); + connect(task.get(), &EnsureMetadataTask::metadataFailed, [this](Mod* candidate) { onMetadataFailed(candidate, false); }); m_second_try_metadata->addTask(task); } else { @@ -388,9 +388,9 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info) ui->modTreeWidget->addTopLevelItem(item_top); } -auto ModUpdateDialog::getTasks() -> const QList +auto ModUpdateDialog::getTasks() -> const QList { - QList list; + QList list; auto* item = ui->modTreeWidget->topLevelItem(0); diff --git a/launcher/ui/dialogs/ModUpdateDialog.h b/launcher/ui/dialogs/ModUpdateDialog.h index 3e3dd90d..1a92f613 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.h +++ b/launcher/ui/dialogs/ModUpdateDialog.h @@ -25,7 +25,7 @@ class ModUpdateDialog final : public ReviewMessageBox { void appendMod(const CheckUpdateTask::UpdatableMod& info); - const QList getTasks(); + const QList getTasks(); auto indexDir() const -> QDir { return m_mod_model->indexDir(); } auto noUpdates() const -> bool { return m_no_updates; }; @@ -41,8 +41,8 @@ class ModUpdateDialog final : public ReviewMessageBox { private: QWidget* m_parent; - ModrinthCheckUpdate* m_modrinth_check_task = nullptr; - FlameCheckUpdate* m_flame_check_task = nullptr; + shared_qobject_ptr m_modrinth_check_task; + shared_qobject_ptr m_flame_check_task; const std::shared_ptr m_mod_model; @@ -50,11 +50,11 @@ class ModUpdateDialog final : public ReviewMessageBox { QList m_modrinth_to_update; QList m_flame_to_update; - ConcurrentTask* m_second_try_metadata; + ConcurrentTask::Ptr m_second_try_metadata; QList> m_failed_metadata; QList> m_failed_check_update; - QHash m_tasks; + QHash m_tasks; BaseInstance* m_instance; bool m_no_updates = false; diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index b9367c16..fa829bfb 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -147,7 +147,7 @@ void ResourceDownloadDialog::addResource(ModPlatform::IndexedPack& pack, ModPlat removeResource(pack, ver); ver.is_currently_selected = true; - m_selected.insert(pack.name, new ResourceDownloadTask(pack, ver, getBaseModel(), is_indexed)); + m_selected.insert(pack.name, makeShared(pack, ver, getBaseModel(), is_indexed)); m_buttons.button(QDialogButtonBox::Ok)->setEnabled(!m_selected.isEmpty()); } diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index d200652a..94315395 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -660,7 +660,7 @@ void VersionPage::onGameUpdateError(QString error) CustomMessageBox::selectable(this, tr("Error updating instance"), error, QMessageBox::Warning)->show(); } -Component * VersionPage::current() +ComponentPtr VersionPage::current() { auto row = currentRow(); if(row < 0) diff --git a/launcher/ui/pages/instance/VersionPage.h b/launcher/ui/pages/instance/VersionPage.h index 166f36bb..183bad9a 100644 --- a/launcher/ui/pages/instance/VersionPage.h +++ b/launcher/ui/pages/instance/VersionPage.h @@ -99,7 +99,7 @@ private slots: void updateVersionControls(); private: - Component * current(); + ComponentPtr current(); int currentRow(); void updateButtons(int row = -1); void preselect(int row = 0); diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 8af70104..db7d26f8 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -265,7 +265,7 @@ std::optional ResourceModel::getIcon(QModelIndex& index, const QUrl& url) return { pixmap }; if (!m_current_icon_job) - m_current_icon_job = new NetJob("IconJob", APPLICATION->network()); + m_current_icon_job.reset(new NetJob("IconJob", APPLICATION->network())); if (m_currently_running_icon_actions.contains(url)) return {}; diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp index 2ce04068..9ad26f47 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp @@ -86,14 +86,14 @@ void ListModel::request() modpacks.clear(); endResetModel(); - auto *netJob = new NetJob("Atl::Request", APPLICATION->network()); + auto netJob = makeShared("Atl::Request", APPLICATION->network()); auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/json/packsnew.json"); netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response)); jobPtr = netJob; jobPtr->start(); - QObject::connect(netJob, &NetJob::succeeded, this, &ListModel::requestFinished); - QObject::connect(netJob, &NetJob::failed, this, &ListModel::requestFailed); + QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::requestFinished); + QObject::connect(netJob.get(), &NetJob::failed, this, &ListModel::requestFailed); } void ListModel::requestFinished() diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModel.cpp index 127c3de5..5961ea02 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModel.cpp @@ -155,7 +155,7 @@ void ListModel::fetchMore(const QModelIndex& parent) void ListModel::performPaginatedSearch() { - NetJob* netJob = new NetJob("Flame::Search", APPLICATION->network()); + auto netJob = makeShared("Flame::Search", APPLICATION->network()); auto searchUrl = QString( "https://api.curseforge.com/v1/mods/search?" "gameId=432&" @@ -172,8 +172,8 @@ void ListModel::performPaginatedSearch() netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response)); jobPtr = netJob; jobPtr->start(); - QObject::connect(netJob, &NetJob::succeeded, this, &ListModel::searchRequestFinished); - QObject::connect(netJob, &NetJob::failed, this, &ListModel::searchRequestFailed); + QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::searchRequestFinished); + QObject::connect(netJob.get(), &NetJob::failed, this, &ListModel::searchRequestFailed); } void ListModel::searchWithTerm(const QString& term, int sort) diff --git a/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp b/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp index ce2b2b18..e8065415 100644 --- a/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp +++ b/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp @@ -109,14 +109,14 @@ void ListModel::request() modpacks.clear(); endResetModel(); - auto *netJob = new NetJob("Ftb::Request", APPLICATION->network()); + auto netJob = makeShared("Ftb::Request", APPLICATION->network()); auto url = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/all"); netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response)); jobPtr = netJob; jobPtr->start(); - QObject::connect(netJob, &NetJob::succeeded, this, &ListModel::requestFinished); - QObject::connect(netJob, &NetJob::failed, this, &ListModel::requestFailed); + QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::requestFinished); + QObject::connect(netJob.get(), &NetJob::failed, this, &ListModel::requestFailed); } void ListModel::abortRequest() @@ -158,14 +158,14 @@ void ListModel::requestFailed(QString reason) void ListModel::requestPack() { - auto *netJob = new NetJob("Ftb::Search", APPLICATION->network()); + auto netJob = makeShared("Ftb::Search", APPLICATION->network()); auto searchUrl = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/%1").arg(currentPack); netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response)); jobPtr = netJob; jobPtr->start(); - QObject::connect(netJob, &NetJob::succeeded, this, &ListModel::packRequestFinished); - QObject::connect(netJob, &NetJob::failed, this, &ListModel::packRequestFailed); + QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::packRequestFinished); + QObject::connect(netJob.get(), &NetJob::failed, this, &ListModel::packRequestFailed); } void ListModel::packRequestFinished() @@ -281,16 +281,16 @@ void ListModel::requestLogo(QString logo, QString url) bool stale = entry->isStale(); - NetJob *job = new NetJob(QString("ModpacksCH Icon Download %1").arg(logo), APPLICATION->network()); + auto job = makeShared(QString("ModpacksCH Icon Download %1").arg(logo), APPLICATION->network()); job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); auto fullPath = entry->getFullPath(); - QObject::connect(job, &NetJob::finished, this, [this, logo, fullPath, stale] + QObject::connect(job.get(), &NetJob::finished, this, [this, logo, fullPath, stale] { logoLoaded(logo, stale); }); - QObject::connect(job, &NetJob::failed, this, [this, logo] + QObject::connect(job.get(), &NetJob::failed, this, [this, logo] { logoFailed(logo); }); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 80850b4c..346a00b0 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -127,7 +127,7 @@ bool ModpackListModel::setData(const QModelIndex &index, const QVariant &value, void ModpackListModel::performPaginatedSearch() { // TODO: Move to standalone API - NetJob* netJob = new NetJob("Modrinth::SearchModpack", APPLICATION->network()); + auto netJob = makeShared("Modrinth::SearchModpack", APPLICATION->network()); auto searchAllUrl = QString(BuildConfig.MODRINTH_PROD_URL + "/search?" "offset=%1&" @@ -142,7 +142,7 @@ void ModpackListModel::performPaginatedSearch() netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchAllUrl), &m_all_response)); - QObject::connect(netJob, &NetJob::succeeded, this, [this] { + QObject::connect(netJob.get(), &NetJob::succeeded, this, [this] { QJsonParseError parse_error_all{}; QJsonDocument doc_all = QJsonDocument::fromJson(m_all_response, &parse_error_all); @@ -155,7 +155,7 @@ void ModpackListModel::performPaginatedSearch() searchRequestFinished(doc_all); }); - QObject::connect(netJob, &NetJob::failed, this, &ModpackListModel::searchRequestFailed); + QObject::connect(netJob.get(), &NetJob::failed, this, &ModpackListModel::searchRequestFailed); jobPtr = netJob; jobPtr->start(); diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp index b2af1ac0..50f0c72d 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp @@ -112,7 +112,7 @@ void Technic::ListModel::searchWithTerm(const QString& term) void Technic::ListModel::performSearch() { - NetJob *netJob = new NetJob("Technic::Search", APPLICATION->network()); + auto netJob = makeShared("Technic::Search", APPLICATION->network()); QString searchUrl = ""; if (currentSearchTerm.isEmpty()) { searchUrl = QString("%1trending?build=%2") @@ -137,8 +137,8 @@ void Technic::ListModel::performSearch() netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response)); jobPtr = netJob; jobPtr->start(); - QObject::connect(netJob, &NetJob::succeeded, this, &ListModel::searchRequestFinished); - QObject::connect(netJob, &NetJob::failed, this, &ListModel::searchRequestFailed); + QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::searchRequestFinished); + QObject::connect(netJob.get(), &NetJob::failed, this, &ListModel::searchRequestFailed); } void Technic::ListModel::searchRequestFinished() diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp index b15af244..859da97e 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp @@ -141,10 +141,10 @@ void TechnicPage::suggestCurrent() return; } - NetJob *netJob = new NetJob(QString("Technic::PackMeta(%1)").arg(current.name), APPLICATION->network()); + auto netJob = makeShared(QString("Technic::PackMeta(%1)").arg(current.name), APPLICATION->network()); QString slug = current.slug; netJob->addNetAction(Net::Download::makeByteArray(QString("%1modpack/%2?build=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, slug, BuildConfig.TECHNIC_API_BUILD), &response)); - QObject::connect(netJob, &NetJob::succeeded, this, [this, slug] + QObject::connect(netJob.get(), &NetJob::succeeded, this, [this, slug] { jobPtr.reset(); @@ -247,11 +247,11 @@ void TechnicPage::metadataLoaded() // version so we can display something quicker ui->versionSelectionBox->addItem(current.currentVersion); - auto* netJob = new NetJob(QString("Technic::SolderMeta(%1)").arg(current.name), APPLICATION->network()); + auto netJob = makeShared(QString("Technic::SolderMeta(%1)").arg(current.name), APPLICATION->network()); auto url = QString("%1/modpack/%2").arg(current.url, current.slug); netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response)); - QObject::connect(netJob, &NetJob::succeeded, this, &TechnicPage::onSolderLoaded); + QObject::connect(netJob.get(), &NetJob::succeeded, this, &TechnicPage::onSolderLoaded); jobPtr = netJob; jobPtr->start(); diff --git a/tests/DummyResourceAPI.h b/tests/DummyResourceAPI.h index e91be96c..0cc90958 100644 --- a/tests/DummyResourceAPI.h +++ b/tests/DummyResourceAPI.h @@ -36,12 +36,11 @@ class DummyResourceAPI : public ResourceAPI { [[nodiscard]] Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&& callbacks) const override { - auto task = new SearchTask; - QObject::connect(task, &Task::succeeded, [=] { + auto task = makeShared(); + QObject::connect(task.get(), &Task::succeeded, [=] { auto json = searchRequestResult(); callbacks.on_succeed(json); }); - QObject::connect(task, &Task::finished, task, &Task::deleteLater); return task; } }; diff --git a/tests/Task_test.cpp b/tests/Task_test.cpp index 6649b724..558cd2c0 100644 --- a/tests/Task_test.cpp +++ b/tests/Task_test.cpp @@ -49,10 +49,10 @@ class BigConcurrentTask : public QThread { // NOTE: Arbitrary value that manages to trigger a problem when there is one. static const unsigned s_num_tasks = 1 << 14; - auto sub_tasks = new BasicTask*[s_num_tasks]; + auto sub_tasks = new BasicTask::Ptr[s_num_tasks]; for (unsigned i = 0; i < s_num_tasks; i++) { - sub_tasks[i] = new BasicTask(false); + sub_tasks[i] = makeShared(false); big_task.addTask(sub_tasks[i]); } @@ -119,21 +119,21 @@ class TaskTest : public QObject { } void test_basicConcurrentRun(){ - BasicTask t1; - BasicTask t2; - BasicTask t3; + auto t1 = makeShared(); + auto t2 = makeShared(); + auto t3 = makeShared(); ConcurrentTask t; - t.addTask(&t1); - t.addTask(&t2); - t.addTask(&t3); + t.addTask(t1); + t.addTask(t2); + t.addTask(t3); QObject::connect(&t, &Task::finished, [&]{ QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); - QVERIFY(t1.wasSuccessful()); - QVERIFY(t2.wasSuccessful()); - QVERIFY(t3.wasSuccessful()); + QVERIFY(t1->wasSuccessful()); + QVERIFY(t2->wasSuccessful()); + QVERIFY(t3->wasSuccessful()); }); t.start(); @@ -144,31 +144,39 @@ class TaskTest : public QObject { // Tests if starting new tasks after the 6 initial ones is working void test_moreConcurrentRun(){ - BasicTask t1, t2, t3, t4, t5, t6, t7, t8, t9; + auto t1 = makeShared(); + auto t2 = makeShared(); + auto t3 = makeShared(); + auto t4 = makeShared(); + auto t5 = makeShared(); + auto t6 = makeShared(); + auto t7 = makeShared(); + auto t8 = makeShared(); + auto t9 = makeShared(); ConcurrentTask t; - t.addTask(&t1); - t.addTask(&t2); - t.addTask(&t3); - t.addTask(&t4); - t.addTask(&t5); - t.addTask(&t6); - t.addTask(&t7); - t.addTask(&t8); - t.addTask(&t9); + t.addTask(t1); + t.addTask(t2); + t.addTask(t3); + t.addTask(t4); + t.addTask(t5); + t.addTask(t6); + t.addTask(t7); + t.addTask(t8); + t.addTask(t9); QObject::connect(&t, &Task::finished, [&]{ QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); - QVERIFY(t1.wasSuccessful()); - QVERIFY(t2.wasSuccessful()); - QVERIFY(t3.wasSuccessful()); - QVERIFY(t4.wasSuccessful()); - QVERIFY(t5.wasSuccessful()); - QVERIFY(t6.wasSuccessful()); - QVERIFY(t7.wasSuccessful()); - QVERIFY(t8.wasSuccessful()); - QVERIFY(t9.wasSuccessful()); + QVERIFY(t1->wasSuccessful()); + QVERIFY(t2->wasSuccessful()); + QVERIFY(t3->wasSuccessful()); + QVERIFY(t4->wasSuccessful()); + QVERIFY(t5->wasSuccessful()); + QVERIFY(t6->wasSuccessful()); + QVERIFY(t7->wasSuccessful()); + QVERIFY(t8->wasSuccessful()); + QVERIFY(t9->wasSuccessful()); }); t.start(); @@ -178,21 +186,21 @@ class TaskTest : public QObject { } void test_basicSequentialRun(){ - BasicTask t1; - BasicTask t2; - BasicTask t3; + auto t1 = makeShared(); + auto t2 = makeShared(); + auto t3 = makeShared(); SequentialTask t; - t.addTask(&t1); - t.addTask(&t2); - t.addTask(&t3); + t.addTask(t1); + t.addTask(t2); + t.addTask(t3); QObject::connect(&t, &Task::finished, [&]{ QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); - QVERIFY(t1.wasSuccessful()); - QVERIFY(t2.wasSuccessful()); - QVERIFY(t3.wasSuccessful()); + QVERIFY(t1->wasSuccessful()); + QVERIFY(t2->wasSuccessful()); + QVERIFY(t3->wasSuccessful()); }); t.start(); @@ -202,21 +210,21 @@ class TaskTest : public QObject { } void test_basicMultipleOptionsRun(){ - BasicTask t1; - BasicTask t2; - BasicTask t3; + auto t1 = makeShared(); + auto t2 = makeShared(); + auto t3 = makeShared(); MultipleOptionsTask t; - t.addTask(&t1); - t.addTask(&t2); - t.addTask(&t3); + t.addTask(t1); + t.addTask(t2); + t.addTask(t3); QObject::connect(&t, &Task::finished, [&]{ QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); - QVERIFY(t1.wasSuccessful()); - QVERIFY(!t2.wasSuccessful()); - QVERIFY(!t3.wasSuccessful()); + QVERIFY(t1->wasSuccessful()); + QVERIFY(!t2->wasSuccessful()); + QVERIFY(!t3->wasSuccessful()); }); t.start(); -- cgit From c3ea303a3742c886aae9e05d2e5f5fbb497260a1 Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 16 Dec 2022 20:26:10 -0300 Subject: feat(RD): add resource pack downloader Signed-off-by: flow --- launcher/Application.cpp | 1 + launcher/CMakeLists.txt | 4 + launcher/modplatform/flame/FlameAPI.h | 2 + launcher/modplatform/modrinth/ModrinthAPI.h | 2 + launcher/ui/dialogs/ResourceDownloadDialog.cpp | 27 ++++++ launcher/ui/dialogs/ResourceDownloadDialog.h | 20 ++++ launcher/ui/pages/instance/ResourcePackPage.cpp | 102 +++++++++++++++++++++ launcher/ui/pages/instance/ResourcePackPage.h | 18 +--- .../ui/pages/modplatform/ResourcePackModel.cpp | 42 +++++++++ launcher/ui/pages/modplatform/ResourcePackModel.h | 39 ++++++++ launcher/ui/pages/modplatform/ResourcePackPage.cpp | 42 +++++++++ launcher/ui/pages/modplatform/ResourcePackPage.h | 48 ++++++++++ .../modplatform/flame/FlameResourceModels.cpp | 24 +++++ .../pages/modplatform/flame/FlameResourceModels.h | 19 ++++ .../pages/modplatform/flame/FlameResourcePages.cpp | 57 ++++++++++-- .../pages/modplatform/flame/FlameResourcePages.h | 28 ++++++ .../modrinth/ModrinthResourceModels.cpp | 22 +++++ .../modplatform/modrinth/ModrinthResourceModels.h | 21 ++++- .../modplatform/modrinth/ModrinthResourcePages.cpp | 19 ++++ .../modplatform/modrinth/ModrinthResourcePages.h | 24 +++++ 20 files changed, 538 insertions(+), 23 deletions(-) create mode 100644 launcher/ui/pages/instance/ResourcePackPage.cpp create mode 100644 launcher/ui/pages/modplatform/ResourcePackModel.cpp create mode 100644 launcher/ui/pages/modplatform/ResourcePackModel.h create mode 100644 launcher/ui/pages/modplatform/ResourcePackPage.cpp create mode 100644 launcher/ui/pages/modplatform/ResourcePackPage.h (limited to 'launcher/ui/pages/instance') diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 0d3b086f..c1ce4659 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -610,6 +610,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("UpdateDialogGeometry", ""); m_settings->registerSetting("ModDownloadGeometry", ""); + m_settings->registerSetting("RPDownloadGeometry", ""); // HACK: This code feels so stupid is there a less stupid way of doing this? { diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 1bfe9cbc..7ae148e0 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -721,6 +721,7 @@ SET(LAUNCHER_SOURCES ui/pages/instance/ManagedPackPage.h ui/pages/instance/TexturePackPage.h ui/pages/instance/ResourcePackPage.h + ui/pages/instance/ResourcePackPage.cpp ui/pages/instance/ShaderPackPage.h ui/pages/instance/ModFolderPage.cpp ui/pages/instance/ModFolderPage.h @@ -773,6 +774,9 @@ SET(LAUNCHER_SOURCES ui/pages/modplatform/ModModel.cpp ui/pages/modplatform/ModModel.h + ui/pages/modplatform/ResourcePackPage.cpp + ui/pages/modplatform/ResourcePackModel.cpp + ui/pages/modplatform/atlauncher/AtlFilterModel.cpp ui/pages/modplatform/atlauncher/AtlFilterModel.h ui/pages/modplatform/atlauncher/AtlListModel.cpp diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index 06d749e6..5811d717 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -27,6 +27,8 @@ class FlameAPI : public NetworkResourceAPI { default: case ModPlatform::ResourceType::MOD: return 6; + case ModPlatform::ResourceType::RESOURCE_PACK: + return 12; } } diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index dda27303..0b2d149e 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -68,6 +68,8 @@ class ModrinthAPI : public NetworkResourceAPI { switch (type) { case ModPlatform::ResourceType::MOD: return "mod"; + case ModPlatform::ResourceType::RESOURCE_PACK: + return "resourcepack"; default: qWarning() << "Invalid resource type for Modrinth API!"; break; diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index fa829bfb..edcd642e 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -25,6 +25,7 @@ #include "ResourceDownloadTask.h" #include "minecraft/mod/ModFolderModel.h" +#include "minecraft/mod/ResourcePackFolderModel.h" #include "ui/dialogs/ReviewMessageBox.h" @@ -229,4 +230,30 @@ QList ModDownloadDialog::getPages() return pages; } + +ResourcePackDownloadDialog::ResourcePackDownloadDialog(QWidget* parent, + const std::shared_ptr& resource_packs, + BaseInstance* instance) + : ResourceDownloadDialog(parent, resource_packs), m_instance(instance) +{ + setWindowTitle(dialogTitle()); + + initializeContainer(); + connectButtons(); + + if (!geometrySaveKey().isEmpty()) + restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get(geometrySaveKey()).toByteArray())); +} + +QList ResourcePackDownloadDialog::getPages() +{ + QList pages; + + pages.append(ModrinthResourcePackPage::create(this, *m_instance)); + if (APPLICATION->capabilities() & Application::SupportsFlame) + pages.append(FlameResourcePackPage::create(this, *m_instance)); + + return pages; +} + } // namespace ResourceDownload diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.h b/launcher/ui/dialogs/ResourceDownloadDialog.h index 19843532..f534cccc 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.h +++ b/launcher/ui/dialogs/ResourceDownloadDialog.h @@ -35,6 +35,7 @@ class QVBoxLayout; class QDialogButtonBox; class ResourceDownloadTask; class ResourceFolderModel; +class ResourcePackFolderModel; namespace ResourceDownload { @@ -108,4 +109,23 @@ class ModDownloadDialog final : public ResourceDownloadDialog { BaseInstance* m_instance; }; +class ResourcePackDownloadDialog final : public ResourceDownloadDialog { + Q_OBJECT + + public: + explicit ResourcePackDownloadDialog(QWidget* parent, + const std::shared_ptr& resource_packs, + BaseInstance* instance); + ~ResourcePackDownloadDialog() override = default; + + //: String that gets appended to the resource pack download dialog title ("Download " + resourcesString()) + [[nodiscard]] QString resourcesString() const override { return tr("resource packs"); } + [[nodiscard]] QString geometrySaveKey() const override { return "RPDownloadGeometry"; } + + QList getPages() override; + + private: + BaseInstance* m_instance; +}; + } // namespace ResourceDownload diff --git a/launcher/ui/pages/instance/ResourcePackPage.cpp b/launcher/ui/pages/instance/ResourcePackPage.cpp new file mode 100644 index 00000000..e705f29e --- /dev/null +++ b/launcher/ui/pages/instance/ResourcePackPage.cpp @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 Jamie Mansfield + * + * 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 . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ResourcePackPage.h" + +#include "ResourceDownloadTask.h" + +#include "ui/dialogs/CustomMessageBox.h" +#include "ui/dialogs/ProgressDialog.h" +#include "ui/dialogs/ResourceDownloadDialog.h" + +ResourcePackPage::ResourcePackPage(MinecraftInstance* instance, std::shared_ptr model, QWidget* parent) + : ExternalResourcesPage(instance, model, parent) +{ + ui->actionDownloadItem->setText(tr("Download RPs")); + ui->actionDownloadItem->setToolTip(tr("Download RPs from online platforms")); + ui->actionDownloadItem->setEnabled(true); + connect(ui->actionDownloadItem, &QAction::triggered, this, &ResourcePackPage::downloadRPs); + ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem); + + ui->actionViewConfigs->setVisible(false); +} + +bool ResourcePackPage::onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) +{ + auto sourceCurrent = m_filterModel->mapToSource(current); + int row = sourceCurrent.row(); + auto& rp = static_cast(m_model->at(row)); + ui->frame->updateWithResourcePack(rp); + + return true; +} + +void ResourcePackPage::downloadRPs() +{ + if (!m_controlsEnabled) + return; + 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); + if (mdownload.exec()) { + auto tasks = new ConcurrentTask(this); + 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 : mdownload.getTasks()) { + tasks->addTask(task); + } + + ProgressDialog loadDialog(this); + loadDialog.setSkipButton(true, tr("Abort")); + loadDialog.execWithTask(tasks); + + m_model->update(); + } +} diff --git a/launcher/ui/pages/instance/ResourcePackPage.h b/launcher/ui/pages/instance/ResourcePackPage.h index db8af0c5..fc05158c 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.h +++ b/launcher/ui/pages/instance/ResourcePackPage.h @@ -44,12 +44,7 @@ class ResourcePackPage : public ExternalResourcesPage { Q_OBJECT public: - explicit ResourcePackPage(MinecraftInstance *instance, std::shared_ptr model, QWidget *parent = 0) - : ExternalResourcesPage(instance, model, parent) - { - ui->actionViewConfigs->setVisible(false); - } - virtual ~ResourcePackPage() {} + explicit ResourcePackPage(MinecraftInstance *instance, std::shared_ptr model, QWidget *parent = 0); QString displayName() const override { return tr("Resource packs"); } QIcon icon() const override { return APPLICATION->getThemedIcon("resourcepacks"); } @@ -63,14 +58,7 @@ public: } public slots: - bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override - { - auto sourceCurrent = m_filterModel->mapToSource(current); - int row = sourceCurrent.row(); - auto& rp = static_cast(m_model->at(row)); - ui->frame->updateWithResourcePack(rp); - - return true; - } + bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override; + void downloadRPs(); }; diff --git a/launcher/ui/pages/modplatform/ResourcePackModel.cpp b/launcher/ui/pages/modplatform/ResourcePackModel.cpp new file mode 100644 index 00000000..fd1afa0d --- /dev/null +++ b/launcher/ui/pages/modplatform/ResourcePackModel.cpp @@ -0,0 +1,42 @@ +#include "ResourcePackModel.h" + +#include + +namespace ResourceDownload { + +ResourcePackResourceModel::ResourcePackResourceModel(BaseInstance const& base_inst, ResourceAPI* api) + : ResourceModel(api), m_base_instance(base_inst){}; + +/******** Make data requests ********/ + +ResourceAPI::SearchArgs ResourcePackResourceModel::createSearchArguments() +{ + auto sort = getCurrentSortingMethodByIndex(); + return { ModPlatform::ResourceType::RESOURCE_PACK, m_next_search_offset, m_search_term, sort }; +} + +ResourceAPI::VersionSearchArgs ResourcePackResourceModel::createVersionsArguments(QModelIndex& entry) +{ + auto& pack = m_packs[entry.row()]; + return { pack }; +} + +ResourceAPI::ProjectInfoArgs ResourcePackResourceModel::createInfoArguments(QModelIndex& entry) +{ + auto& pack = m_packs[entry.row()]; + return { pack }; +} + +void ResourcePackResourceModel::searchWithTerm(const QString& term, unsigned int sort) +{ + if (m_search_term == term && m_search_term.isNull() == term.isNull() && m_current_sort_index == sort) { + return; + } + + setSearchTerm(term); + m_current_sort_index = sort; + + refresh(); +} + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ResourcePackModel.h b/launcher/ui/pages/modplatform/ResourcePackModel.h new file mode 100644 index 00000000..63aa533c --- /dev/null +++ b/launcher/ui/pages/modplatform/ResourcePackModel.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include "BaseInstance.h" + +#include "modplatform/ModIndex.h" + +#include "ui/pages/modplatform/ResourceModel.h" + +class Version; + +namespace ResourceDownload { + +class ResourcePackResourceModel : public ResourceModel { + Q_OBJECT + + public: + ResourcePackResourceModel(BaseInstance const&, ResourceAPI*); + + /* Ask the API for more information */ + void searchWithTerm(const QString& term, unsigned int sort); + + void loadIndexedPack(ModPlatform::IndexedPack&, QJsonObject&) override = 0; + void loadExtraPackInfo(ModPlatform::IndexedPack&, QJsonObject&) override = 0; + void loadIndexedPackVersions(ModPlatform::IndexedPack&, QJsonArray&) override = 0; + + public slots: + ResourceAPI::SearchArgs createSearchArguments() override; + ResourceAPI::VersionSearchArgs createVersionsArguments(QModelIndex&) override; + ResourceAPI::ProjectInfoArgs createInfoArguments(QModelIndex&) override; + + protected: + const BaseInstance& m_base_instance; + + auto documentToArray(QJsonDocument& obj) const -> QJsonArray override = 0; +}; + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ResourcePackPage.cpp b/launcher/ui/pages/modplatform/ResourcePackPage.cpp new file mode 100644 index 00000000..8d663aa8 --- /dev/null +++ b/launcher/ui/pages/modplatform/ResourcePackPage.cpp @@ -0,0 +1,42 @@ +#include "ResourcePackPage.h" +#include "ui_ResourcePage.h" + +#include "ResourcePackModel.h" + +#include "ui/dialogs/ResourceDownloadDialog.h" + +#include + +namespace ResourceDownload { + +ResourcePackResourcePage::ResourcePackResourcePage(ResourcePackDownloadDialog* dialog, BaseInstance& instance) + : ResourcePage(dialog, instance) +{ + connect(m_ui->searchButton, &QPushButton::clicked, this, &ResourcePackResourcePage::triggerSearch); + connect(m_ui->packView, &QListView::doubleClicked, this, &ResourcePackResourcePage::onResourceSelected); +} + +/******** Callbacks to events in the UI (set up in the derived classes) ********/ + +void ResourcePackResourcePage::triggerSearch() +{ + m_ui->packView->clearSelection(); + m_ui->packDescription->clear(); + m_ui->versionSelectionBox->clear(); + + updateSelectionButton(); + + static_cast(m_model)->searchWithTerm(getSearchTerm(), m_ui->sortByBox->currentData().toUInt()); + m_fetch_progress.watch(m_model->activeSearchJob().get()); +} + +QMap ResourcePackResourcePage::urlHandlers() const +{ + QMap map; + map.insert(QRegularExpression::anchoredPattern("(?:www\\.)?modrinth\\.com\\/resourcepack\\/([^\\/]+)\\/?"), "modrinth"); + map.insert(QRegularExpression::anchoredPattern("(?:www\\.)?curseforge\\.com\\/minecraft\\/texture-packs\\/([^\\/]+)\\/?"), "curseforge"); + map.insert(QRegularExpression::anchoredPattern("minecraft\\.curseforge\\.com\\/projects\\/([^\\/]+)\\/?"), "curseforge"); + return map; +} + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ResourcePackPage.h b/launcher/ui/pages/modplatform/ResourcePackPage.h new file mode 100644 index 00000000..2ecff390 --- /dev/null +++ b/launcher/ui/pages/modplatform/ResourcePackPage.h @@ -0,0 +1,48 @@ +#pragma once + +#include "ui/pages/modplatform/ResourcePage.h" +#include "ui/pages/modplatform/ResourcePackModel.h" + +namespace Ui { +class ResourcePage; +} + +namespace ResourceDownload { + +class ResourcePackDownloadDialog; + +class ResourcePackResourcePage : public ResourcePage { + Q_OBJECT + + public: + template + static T* create(ResourcePackDownloadDialog* dialog, BaseInstance& instance) + { + auto page = new T(dialog, instance); + auto model = static_cast(page->getModel()); + + connect(model, &ResourceModel::versionListUpdated, page, &ResourcePage::updateVersionList); + connect(model, &ResourceModel::projectInfoUpdated, page, &ResourcePage::updateUi); + + return page; + } + + ~ResourcePackResourcePage() override = default; + + //: The plural version of 'resource pack' + [[nodiscard]] inline QString resourcesString() const override { return tr("resource packs"); } + //: The singular version of 'resource packs' + [[nodiscard]] inline QString resourceString() const override { return tr("resource pack"); } + + [[nodiscard]] bool supportsFiltering() const override { return false; }; + + [[nodiscard]] QMap urlHandlers() const override; + + protected: + ResourcePackResourcePage(ResourcePackDownloadDialog* dialog, BaseInstance& instance); + + protected slots: + void triggerSearch() override; +}; + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp index de1f2122..95d915fc 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp @@ -34,4 +34,28 @@ auto FlameModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray return Json::ensureArray(obj.object(), "data"); } +FlameResourcePackModel::FlameResourcePackModel(const BaseInstance& base) : ResourcePackResourceModel(base, new FlameAPI) {} + +void FlameResourcePackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) +{ + FlameMod::loadIndexedPack(m, obj); +} + +// We already deal with the URLs when initializing the pack, due to the API response's structure +void FlameResourcePackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) +{ + FlameMod::loadBody(m, obj); +} + +void FlameResourcePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) +{ + FlameMod::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); +} + +auto FlameResourcePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray +{ + return Json::ensureArray(obj.object(), "data"); +} + + } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h index 625a2a7d..be214716 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h @@ -5,6 +5,7 @@ #pragma once #include "ui/pages/modplatform/ModModel.h" +#include "ui/pages/modplatform/ResourcePackModel.h" #include "ui/pages/modplatform/flame/FlameResourcePages.h" namespace ResourceDownload { @@ -27,4 +28,22 @@ class FlameModModel : public ModModel { auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; }; +class FlameResourcePackModel : public ResourcePackResourceModel { + Q_OBJECT + + public: + FlameResourcePackModel(const BaseInstance&); + ~FlameResourcePackModel() override = default; + + private: + [[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; } + [[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); } + + void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override; + void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override; + void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override; + + auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; +}; + } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp index 485431a7..15b04ab4 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp @@ -44,6 +44,11 @@ namespace ResourceDownload { +static bool isOptedOut(ModPlatform::IndexedVersion const& ver) +{ + return ver.downloadUrl.isEmpty(); +} + FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) : ModPage(dialog, instance) { @@ -70,14 +75,9 @@ auto FlameModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString min bool FlameModPage::optedOut(ModPlatform::IndexedVersion& ver) const { - return ver.downloadUrl.isEmpty(); + return isOptedOut(ver); } -// I don't know why, but doing this on the parent class makes it so that -// other mod providers start loading before being selected, at least with -// my Qt, so we need to implement this in every derived class... -auto FlameModPage::shouldDisplay() const -> bool { return true; } - void FlameModPage::openUrl(const QUrl& url) { if (url.scheme().isEmpty()) { @@ -94,4 +94,49 @@ void FlameModPage::openUrl(const QUrl& url) ModPage::openUrl(url); } +FlameResourcePackPage::FlameResourcePackPage(ResourcePackDownloadDialog* dialog, BaseInstance& instance) + : ResourcePackResourcePage(dialog, instance) +{ + m_model = new FlameResourcePackModel(instance); + m_ui->packView->setModel(m_model); + + addSortings(); + + // sometimes Qt just ignores virtual slots and doesn't work as intended it seems, + // so it's best not to connect them in the parent's contructor... + connect(m_ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch())); + connect(m_ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FlameResourcePackPage::onSelectionChanged); + connect(m_ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FlameResourcePackPage::onVersionSelectionChanged); + connect(m_ui->resourceSelectionButton, &QPushButton::clicked, this, &FlameResourcePackPage::onResourceSelected); + + m_ui->packDescription->setMetaEntry(metaEntryBase()); +} + +bool FlameResourcePackPage::optedOut(ModPlatform::IndexedVersion& ver) const +{ + return isOptedOut(ver); +} + +void FlameResourcePackPage::openUrl(const QUrl& url) +{ + if (url.scheme().isEmpty()) { + QString query = url.query(QUrl::FullyDecoded); + + if (query.startsWith("remoteUrl=")) { + // attempt to resolve url from warning page + query.remove(0, 10); + ResourcePackResourcePage::openUrl({QUrl::fromPercentEncoding(query.toUtf8())}); // double decoding is necessary + return; + } + } + + ResourcePackResourcePage::openUrl(url); +} + +// I don't know why, but doing this on the parent class makes it so that +// other mod providers start loading before being selected, at least with +// my Qt, so we need to implement this in every derived class... +auto FlameModPage::shouldDisplay() const -> bool { return true; } +auto FlameResourcePackPage::shouldDisplay() const -> bool { return true; } + } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h index b21a53ad..03cf7795 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h @@ -43,6 +43,7 @@ #include "modplatform/ResourceAPI.h" #include "ui/pages/modplatform/ModPage.h" +#include "ui/pages/modplatform/ResourcePackPage.h" namespace ResourceDownload { @@ -82,4 +83,31 @@ class FlameModPage : public ModPage { void openUrl(const QUrl& url) override; }; +class FlameResourcePackPage : public ResourcePackResourcePage { + Q_OBJECT + + public: + static FlameResourcePackPage* create(ResourcePackDownloadDialog* dialog, BaseInstance& instance) + { + return ResourcePackResourcePage::create(dialog, instance); + } + + FlameResourcePackPage(ResourcePackDownloadDialog* dialog, BaseInstance& instance); + ~FlameResourcePackPage() override = default; + + [[nodiscard]] bool shouldDisplay() const override; + + [[nodiscard]] inline auto displayName() const -> QString override { return Flame::displayName(); } + [[nodiscard]] inline auto icon() const -> QIcon override { return Flame::icon(); } + [[nodiscard]] inline auto id() const -> QString override { return Flame::id(); } + [[nodiscard]] inline auto debugName() const -> QString override { return Flame::debugName(); } + [[nodiscard]] inline auto metaEntryBase() const -> QString override { return Flame::metaEntryBase(); } + + [[nodiscard]] inline auto helpPage() const -> QString override { return ""; } + + bool optedOut(ModPlatform::IndexedVersion& ver) const override; + + void openUrl(const QUrl& url) override; +}; + } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp index 73d55133..b36339d7 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp @@ -47,4 +47,26 @@ auto ModrinthModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray return obj.object().value("hits").toArray(); } +ModrinthResourcePackModel::ModrinthResourcePackModel(const BaseInstance& base) : ResourcePackResourceModel(base, new ModrinthAPI){} + +void ModrinthResourcePackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) +{ + ::Modrinth::loadIndexedPack(m, obj); +} + +void ModrinthResourcePackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) +{ + ::Modrinth::loadExtraPackData(m, obj); +} + +void ModrinthResourcePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) +{ + ::Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); +} + +auto ModrinthResourcePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray +{ + return obj.object().value("hits").toArray(); +} + } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h index 56cab146..0a91ef23 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h @@ -21,12 +21,11 @@ #pragma once #include "ui/pages/modplatform/ModModel.h" +#include "ui/pages/modplatform/ResourcePackModel.h" #include "ui/pages/modplatform/modrinth/ModrinthResourcePages.h" namespace ResourceDownload { -class ModrinthModPage; - class ModrinthModModel : public ModModel { Q_OBJECT @@ -45,4 +44,22 @@ class ModrinthModModel : public ModModel { auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; }; +class ModrinthResourcePackModel : public ResourcePackResourceModel { + Q_OBJECT + + public: + ModrinthResourcePackModel(const BaseInstance&); + ~ModrinthResourcePackModel() override = default; + + private: + [[nodiscard]] QString debugName() const override { return Modrinth::debugName() + " (Model)"; } + [[nodiscard]] QString metaEntryBase() const override { return Modrinth::metaEntryBase(); } + + void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override; + void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override; + void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override; + + auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; +}; + } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp index b82f800e..d32b9091 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -82,9 +82,28 @@ auto ModrinthModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString return ver.mcVersion.contains(mineVer) && loaderCompatible; } +ModrinthResourcePackPage::ModrinthResourcePackPage(ResourcePackDownloadDialog* dialog, BaseInstance& instance) + : ResourcePackResourcePage(dialog, instance) +{ + m_model = new ModrinthResourcePackModel(instance); + m_ui->packView->setModel(m_model); + + addSortings(); + + // sometimes Qt just ignores virtual slots and doesn't work as intended it seems, + // so it's best not to connect them in the parent's constructor... + connect(m_ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch())); + connect(m_ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ModrinthResourcePackPage::onSelectionChanged); + connect(m_ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &ModrinthResourcePackPage::onVersionSelectionChanged); + connect(m_ui->resourceSelectionButton, &QPushButton::clicked, this, &ModrinthResourcePackPage::onResourceSelected); + + m_ui->packDescription->setMetaEntry(metaEntryBase()); +} + // I don't know why, but doing this on the parent class makes it so that // other mod providers start loading before being selected, at least with // my Qt, so we need to implement this in every derived class... auto ModrinthModPage::shouldDisplay() const -> bool { return true; } +auto ModrinthResourcePackPage::shouldDisplay() const -> bool { return true; } } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h index be38eff1..be3740a1 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h @@ -42,6 +42,7 @@ #include "modplatform/ResourceAPI.h" #include "ui/pages/modplatform/ModPage.h" +#include "ui/pages/modplatform/ResourcePackPage.h" namespace ResourceDownload { @@ -78,4 +79,27 @@ class ModrinthModPage : public ModPage { auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders = {}) const -> bool override; }; +class ModrinthResourcePackPage : public ResourcePackResourcePage { + Q_OBJECT + + public: + static ModrinthResourcePackPage* create(ResourcePackDownloadDialog* dialog, BaseInstance& instance) + { + return ResourcePackResourcePage::create(dialog, instance); + } + + ModrinthResourcePackPage(ResourcePackDownloadDialog* dialog, BaseInstance& instance); + ~ModrinthResourcePackPage() override = default; + + [[nodiscard]] bool shouldDisplay() const override; + + [[nodiscard]] inline auto displayName() const -> QString override { return Modrinth::displayName(); } + [[nodiscard]] inline auto icon() const -> QIcon override { return Modrinth::icon(); } + [[nodiscard]] inline auto id() const -> QString override { return Modrinth::id(); } + [[nodiscard]] inline auto debugName() const -> QString override { return Modrinth::debugName(); } + [[nodiscard]] inline auto metaEntryBase() const -> QString override { return Modrinth::metaEntryBase(); } + + [[nodiscard]] inline auto helpPage() const -> QString override { return ""; } +}; + } // namespace ResourceDownload -- cgit From b724607e31d102c50cb42225b4a31f2932b2eb61 Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 30 Dec 2022 14:06:07 -0300 Subject: feat(RD): add shader pack downloader Signed-off-by: flow --- launcher/Application.cpp | 1 + launcher/CMakeLists.txt | 4 + launcher/modplatform/ModIndex.h | 2 +- launcher/modplatform/modrinth/ModrinthAPI.h | 2 + launcher/ui/MainWindow.cpp | 1 + launcher/ui/dialogs/ResourceDownloadDialog.cpp | 25 ++++++ launcher/ui/dialogs/ResourceDownloadDialog.h | 20 +++++ launcher/ui/pages/instance/ShaderPackPage.cpp | 96 ++++++++++++++++++++++ launcher/ui/pages/instance/ShaderPackPage.h | 19 ++--- launcher/ui/pages/modplatform/ShaderPackModel.cpp | 42 ++++++++++ launcher/ui/pages/modplatform/ShaderPackModel.h | 39 +++++++++ launcher/ui/pages/modplatform/ShaderPackPage.cpp | 50 +++++++++++ launcher/ui/pages/modplatform/ShaderPackPage.h | 50 +++++++++++ .../pages/modplatform/flame/FlameResourcePages.h | 1 + .../modrinth/ModrinthResourceModels.cpp | 22 +++++ .../modplatform/modrinth/ModrinthResourceModels.h | 18 ++++ .../modplatform/modrinth/ModrinthResourcePages.cpp | 19 +++++ .../modplatform/modrinth/ModrinthResourcePages.h | 24 ++++++ 18 files changed, 421 insertions(+), 14 deletions(-) create mode 100644 launcher/ui/pages/instance/ShaderPackPage.cpp create mode 100644 launcher/ui/pages/modplatform/ShaderPackModel.cpp create mode 100644 launcher/ui/pages/modplatform/ShaderPackModel.h create mode 100644 launcher/ui/pages/modplatform/ShaderPackPage.cpp create mode 100644 launcher/ui/pages/modplatform/ShaderPackPage.h (limited to 'launcher/ui/pages/instance') diff --git a/launcher/Application.cpp b/launcher/Application.cpp index c1ce4659..0f7cce66 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -611,6 +611,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("ModDownloadGeometry", ""); m_settings->registerSetting("RPDownloadGeometry", ""); + m_settings->registerSetting("ShaderDownloadGeometry", ""); // HACK: This code feels so stupid is there a less stupid way of doing this? { diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 7ae148e0..795aeb9f 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -723,6 +723,7 @@ SET(LAUNCHER_SOURCES ui/pages/instance/ResourcePackPage.h ui/pages/instance/ResourcePackPage.cpp ui/pages/instance/ShaderPackPage.h + ui/pages/instance/ShaderPackPage.cpp ui/pages/instance/ModFolderPage.cpp ui/pages/instance/ModFolderPage.h ui/pages/instance/NotesPage.cpp @@ -777,6 +778,9 @@ SET(LAUNCHER_SOURCES ui/pages/modplatform/ResourcePackPage.cpp ui/pages/modplatform/ResourcePackModel.cpp + ui/pages/modplatform/ShaderPackPage.cpp + ui/pages/modplatform/ShaderPackModel.cpp + ui/pages/modplatform/atlauncher/AtlFilterModel.cpp ui/pages/modplatform/atlauncher/AtlFilterModel.h ui/pages/modplatform/atlauncher/AtlListModel.cpp diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index b1f8050d..40f1efc4 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -30,7 +30,7 @@ namespace ModPlatform { enum class ResourceProvider { MODRINTH, FLAME }; -enum class ResourceType { MOD, RESOURCE_PACK }; +enum class ResourceType { MOD, RESOURCE_PACK, SHADER_PACK }; class ProviderCapabilities { public: diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index 0b2d149e..b91ac5c1 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -70,6 +70,8 @@ class ModrinthAPI : public NetworkResourceAPI { return "mod"; case ModPlatform::ResourceType::RESOURCE_PACK: return "resourcepack"; + case ModPlatform::ResourceType::SHADER_PACK: + return "shader"; default: qWarning() << "Invalid resource type for Modrinth API!"; break; diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 6d21f5ed..8490b292 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -113,6 +113,7 @@ #include "minecraft/mod/tasks/LocalResourceParse.h" #include "minecraft/mod/ModFolderModel.h" +#include "minecraft/mod/ShaderPackFolderModel.h" #include "minecraft/WorldList.h" #include "KonamiCode.h" diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index edcd642e..98a2eb88 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -26,6 +26,7 @@ #include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ResourcePackFolderModel.h" +#include "minecraft/mod/ShaderPackFolderModel.h" #include "ui/dialogs/ReviewMessageBox.h" @@ -256,4 +257,28 @@ QList ResourcePackDownloadDialog::getPages() return pages; } + +ShaderPackDownloadDialog::ShaderPackDownloadDialog(QWidget* parent, + const std::shared_ptr& shaders, + BaseInstance* instance) + : ResourceDownloadDialog(parent, shaders), m_instance(instance) +{ + setWindowTitle(dialogTitle()); + + initializeContainer(); + connectButtons(); + + if (!geometrySaveKey().isEmpty()) + restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get(geometrySaveKey()).toByteArray())); +} + +QList ShaderPackDownloadDialog::getPages() +{ + QList pages; + + pages.append(ModrinthShaderPackPage::create(this, *m_instance)); + + return pages; +} + } // namespace ResourceDownload diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.h b/launcher/ui/dialogs/ResourceDownloadDialog.h index f534cccc..203bac66 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.h +++ b/launcher/ui/dialogs/ResourceDownloadDialog.h @@ -36,6 +36,7 @@ class QDialogButtonBox; class ResourceDownloadTask; class ResourceFolderModel; class ResourcePackFolderModel; +class ShaderPackFolderModel; namespace ResourceDownload { @@ -128,4 +129,23 @@ class ResourcePackDownloadDialog final : public ResourceDownloadDialog { BaseInstance* m_instance; }; +class ShaderPackDownloadDialog final : public ResourceDownloadDialog { + Q_OBJECT + + public: + explicit ShaderPackDownloadDialog(QWidget* parent, + const std::shared_ptr& shader_packs, + BaseInstance* instance); + ~ShaderPackDownloadDialog() override = default; + + //: String that gets appended to the shader pack download dialog title ("Download " + resourcesString()) + [[nodiscard]] QString resourcesString() const override { return tr("shader packs"); } + [[nodiscard]] QString geometrySaveKey() const override { return "ShaderDownloadGeometry"; } + + QList getPages() override; + + private: + BaseInstance* m_instance; +}; + } // namespace ResourceDownload diff --git a/launcher/ui/pages/instance/ShaderPackPage.cpp b/launcher/ui/pages/instance/ShaderPackPage.cpp new file mode 100644 index 00000000..2dde4dc7 --- /dev/null +++ b/launcher/ui/pages/instance/ShaderPackPage.cpp @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 Jamie Mansfield + * + * 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 . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ShaderPackPage.h" +#include "ui_ExternalResourcesPage.h" + +#include "ResourceDownloadTask.h" + +#include "minecraft/mod/ShaderPackFolderModel.h" + +#include "ui/dialogs/CustomMessageBox.h" +#include "ui/dialogs/ProgressDialog.h" +#include "ui/dialogs/ResourceDownloadDialog.h" + + +ShaderPackPage::ShaderPackPage(MinecraftInstance* instance, std::shared_ptr model, QWidget* parent) + : ExternalResourcesPage(instance, model, parent) +{ + ui->actionDownloadItem->setText(tr("Download shaders")); + ui->actionDownloadItem->setToolTip(tr("Download shaders from online platforms")); + ui->actionDownloadItem->setEnabled(true); + connect(ui->actionDownloadItem, &QAction::triggered, this, &ShaderPackPage::downloadShaders); + ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem); + + ui->actionViewConfigs->setVisible(false); +} + +void ShaderPackPage::downloadShaders() +{ + if (!m_controlsEnabled) + return; + 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); + if (mdownload.exec()) { + auto tasks = new ConcurrentTask(this); + 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 : mdownload.getTasks()) { + tasks->addTask(task); + } + + ProgressDialog loadDialog(this); + loadDialog.setSkipButton(true, tr("Abort")); + loadDialog.execWithTask(tasks); + + m_model->update(); + } +} diff --git a/launcher/ui/pages/instance/ShaderPackPage.h b/launcher/ui/pages/instance/ShaderPackPage.h index 7f7ff8c1..09895949 100644 --- a/launcher/ui/pages/instance/ShaderPackPage.h +++ b/launcher/ui/pages/instance/ShaderPackPage.h @@ -36,28 +36,21 @@ #pragma once #include "ExternalResourcesPage.h" -#include "ui_ExternalResourcesPage.h" - -#include "minecraft/mod/ShaderPackFolderModel.h" class ShaderPackPage : public ExternalResourcesPage { Q_OBJECT public: - explicit ShaderPackPage(MinecraftInstance *instance, std::shared_ptr model, QWidget *parent = 0) - : ExternalResourcesPage(instance, model, parent) - { - ui->actionViewConfigs->setVisible(false); - } - virtual ~ShaderPackPage() {} + explicit ShaderPackPage(MinecraftInstance *instance, std::shared_ptr model, QWidget *parent = nullptr); + ~ShaderPackPage() override = default; QString displayName() const override { return tr("Shader packs"); } QIcon icon() const override { return APPLICATION->getThemedIcon("shaderpacks"); } QString id() const override { return "shaderpacks"; } QString helpPage() const override { return "Resource-packs"; } - virtual bool shouldDisplay() const override - { - return true; - } + bool shouldDisplay() const override { return true; } + + public slots: + void downloadShaders(); }; diff --git a/launcher/ui/pages/modplatform/ShaderPackModel.cpp b/launcher/ui/pages/modplatform/ShaderPackModel.cpp new file mode 100644 index 00000000..36993288 --- /dev/null +++ b/launcher/ui/pages/modplatform/ShaderPackModel.cpp @@ -0,0 +1,42 @@ +#include "ShaderPackModel.h" + +#include + +namespace ResourceDownload { + +ShaderPackResourceModel::ShaderPackResourceModel(BaseInstance const& base_inst, ResourceAPI* api) + : ResourceModel(api), m_base_instance(base_inst){}; + +/******** Make data requests ********/ + +ResourceAPI::SearchArgs ShaderPackResourceModel::createSearchArguments() +{ + auto sort = getCurrentSortingMethodByIndex(); + return { ModPlatform::ResourceType::SHADER_PACK, m_next_search_offset, m_search_term, sort }; +} + +ResourceAPI::VersionSearchArgs ShaderPackResourceModel::createVersionsArguments(QModelIndex& entry) +{ + auto& pack = m_packs[entry.row()]; + return { pack }; +} + +ResourceAPI::ProjectInfoArgs ShaderPackResourceModel::createInfoArguments(QModelIndex& entry) +{ + auto& pack = m_packs[entry.row()]; + return { pack }; +} + +void ShaderPackResourceModel::searchWithTerm(const QString& term, unsigned int sort) +{ + if (m_search_term == term && m_search_term.isNull() == term.isNull() && m_current_sort_index == sort) { + return; + } + + setSearchTerm(term); + m_current_sort_index = sort; + + refresh(); +} + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ShaderPackModel.h b/launcher/ui/pages/modplatform/ShaderPackModel.h new file mode 100644 index 00000000..c84df15a --- /dev/null +++ b/launcher/ui/pages/modplatform/ShaderPackModel.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include "BaseInstance.h" + +#include "modplatform/ModIndex.h" + +#include "ui/pages/modplatform/ResourceModel.h" + +class Version; + +namespace ResourceDownload { + +class ShaderPackResourceModel : public ResourceModel { + Q_OBJECT + + public: + ShaderPackResourceModel(BaseInstance const&, ResourceAPI*); + + /* Ask the API for more information */ + void searchWithTerm(const QString& term, unsigned int sort); + + void loadIndexedPack(ModPlatform::IndexedPack&, QJsonObject&) override = 0; + void loadExtraPackInfo(ModPlatform::IndexedPack&, QJsonObject&) override = 0; + void loadIndexedPackVersions(ModPlatform::IndexedPack&, QJsonArray&) override = 0; + + public slots: + ResourceAPI::SearchArgs createSearchArguments() override; + ResourceAPI::VersionSearchArgs createVersionsArguments(QModelIndex&) override; + ResourceAPI::ProjectInfoArgs createInfoArguments(QModelIndex&) override; + + protected: + const BaseInstance& m_base_instance; + + auto documentToArray(QJsonDocument& obj) const -> QJsonArray override = 0; +}; + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ShaderPackPage.cpp b/launcher/ui/pages/modplatform/ShaderPackPage.cpp new file mode 100644 index 00000000..364d0a54 --- /dev/null +++ b/launcher/ui/pages/modplatform/ShaderPackPage.cpp @@ -0,0 +1,50 @@ +#include "ShaderPackPage.h" +#include "ui_ResourcePage.h" + +#include "ShaderPackModel.h" + +#include "ui/dialogs/ResourceDownloadDialog.h" + +#include + +namespace ResourceDownload { + +ShaderPackResourcePage::ShaderPackResourcePage(ShaderPackDownloadDialog* dialog, BaseInstance& instance) + : ResourcePage(dialog, instance) +{ + connect(m_ui->searchButton, &QPushButton::clicked, this, &ShaderPackResourcePage::triggerSearch); + connect(m_ui->packView, &QListView::doubleClicked, this, &ShaderPackResourcePage::onResourceSelected); +} + +/******** Callbacks to events in the UI (set up in the derived classes) ********/ + +void ShaderPackResourcePage::triggerSearch() +{ + m_ui->packView->clearSelection(); + m_ui->packDescription->clear(); + m_ui->versionSelectionBox->clear(); + + updateSelectionButton(); + + static_cast(m_model)->searchWithTerm(getSearchTerm(), m_ui->sortByBox->currentData().toUInt()); + m_fetch_progress.watch(m_model->activeSearchJob().get()); +} + +QMap ShaderPackResourcePage::urlHandlers() const +{ + QMap map; + map.insert(QRegularExpression::anchoredPattern("(?:www\\.)?modrinth\\.com\\/shaders\\/([^\\/]+)\\/?"), "modrinth"); + map.insert(QRegularExpression::anchoredPattern("(?:www\\.)?curseforge\\.com\\/minecraft\\/customization\\/([^\\/]+)\\/?"), "curseforge"); + map.insert(QRegularExpression::anchoredPattern("minecraft\\.curseforge\\.com\\/projects\\/([^\\/]+)\\/?"), "curseforge"); + return map; +} + +void ShaderPackResourcePage::addResourceToDialog(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& version) +{ + if (version.loaders.contains(QStringLiteral("canvas"))) + version.custom_target_folder = QStringLiteral("resourcepacks"); + + m_parent_dialog->addResource(pack, version); +} + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ShaderPackPage.h b/launcher/ui/pages/modplatform/ShaderPackPage.h new file mode 100644 index 00000000..92a9c3ee --- /dev/null +++ b/launcher/ui/pages/modplatform/ShaderPackPage.h @@ -0,0 +1,50 @@ +#pragma once + +#include "ui/pages/modplatform/ResourcePage.h" +#include "ui/pages/modplatform/ShaderPackModel.h" + +namespace Ui { +class ResourcePage; +} + +namespace ResourceDownload { + +class ShaderPackDownloadDialog; + +class ShaderPackResourcePage : public ResourcePage { + Q_OBJECT + + public: + template + static T* create(ShaderPackDownloadDialog* dialog, BaseInstance& instance) + { + auto page = new T(dialog, instance); + auto model = static_cast(page->getModel()); + + connect(model, &ResourceModel::versionListUpdated, page, &ResourcePage::updateVersionList); + connect(model, &ResourceModel::projectInfoUpdated, page, &ResourcePage::updateUi); + + return page; + } + + ~ShaderPackResourcePage() override = default; + + //: The plural version of 'shader pack' + [[nodiscard]] inline QString resourcesString() const override { return tr("shader packs"); } + //: The singular version of 'shader packs' + [[nodiscard]] inline QString resourceString() const override { return tr("shader pack"); } + + [[nodiscard]] bool supportsFiltering() const override { return false; }; + + void addResourceToDialog(ModPlatform::IndexedPack&, ModPlatform::IndexedVersion&) override; + + [[nodiscard]] QMap urlHandlers() const override; + + protected: + ShaderPackResourcePage(ShaderPackDownloadDialog* dialog, BaseInstance& instance); + + protected slots: + void triggerSearch() override; +}; + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h index 03cf7795..4507a32a 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h @@ -44,6 +44,7 @@ #include "ui/pages/modplatform/ModPage.h" #include "ui/pages/modplatform/ResourcePackPage.h" +#include "ui/pages/modplatform/ShaderPackPage.h" namespace ResourceDownload { diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp index b36339d7..bd433121 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp @@ -69,4 +69,26 @@ auto ModrinthResourcePackModel::documentToArray(QJsonDocument& obj) const -> QJs return obj.object().value("hits").toArray(); } +ModrinthShaderPackModel::ModrinthShaderPackModel(const BaseInstance& base) : ShaderPackResourceModel(base, new ModrinthAPI){} + +void ModrinthShaderPackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) +{ + ::Modrinth::loadIndexedPack(m, obj); +} + +void ModrinthShaderPackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) +{ + ::Modrinth::loadExtraPackData(m, obj); +} + +void ModrinthShaderPackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) +{ + ::Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); +} + +auto ModrinthShaderPackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray +{ + return obj.object().value("hits").toArray(); +} + } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h index 0a91ef23..80a48089 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h @@ -62,4 +62,22 @@ class ModrinthResourcePackModel : public ResourcePackResourceModel { auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; }; +class ModrinthShaderPackModel : public ShaderPackResourceModel { + Q_OBJECT + + public: + ModrinthShaderPackModel(const BaseInstance&); + ~ModrinthShaderPackModel() override = default; + + private: + [[nodiscard]] QString debugName() const override { return Modrinth::debugName() + " (Model)"; } + [[nodiscard]] QString metaEntryBase() const override { return Modrinth::metaEntryBase(); } + + void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override; + void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override; + void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override; + + auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; +}; + } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp index d32b9091..2826b5d3 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -100,10 +100,29 @@ ModrinthResourcePackPage::ModrinthResourcePackPage(ResourcePackDownloadDialog* d m_ui->packDescription->setMetaEntry(metaEntryBase()); } +ModrinthShaderPackPage::ModrinthShaderPackPage(ShaderPackDownloadDialog* dialog, BaseInstance& instance) + : ShaderPackResourcePage(dialog, instance) +{ + m_model = new ModrinthShaderPackModel(instance); + m_ui->packView->setModel(m_model); + + addSortings(); + + // sometimes Qt just ignores virtual slots and doesn't work as intended it seems, + // so it's best not to connect them in the parent's constructor... + connect(m_ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch())); + connect(m_ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ModrinthShaderPackPage::onSelectionChanged); + connect(m_ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &ModrinthShaderPackPage::onVersionSelectionChanged); + connect(m_ui->resourceSelectionButton, &QPushButton::clicked, this, &ModrinthShaderPackPage::onResourceSelected); + + m_ui->packDescription->setMetaEntry(metaEntryBase()); +} + // I don't know why, but doing this on the parent class makes it so that // other mod providers start loading before being selected, at least with // my Qt, so we need to implement this in every derived class... auto ModrinthModPage::shouldDisplay() const -> bool { return true; } auto ModrinthResourcePackPage::shouldDisplay() const -> bool { return true; } +auto ModrinthShaderPackPage::shouldDisplay() const -> bool { return true; } } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h index be3740a1..8733a1b2 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h @@ -43,6 +43,7 @@ #include "ui/pages/modplatform/ModPage.h" #include "ui/pages/modplatform/ResourcePackPage.h" +#include "ui/pages/modplatform/ShaderPackPage.h" namespace ResourceDownload { @@ -102,4 +103,27 @@ class ModrinthResourcePackPage : public ResourcePackResourcePage { [[nodiscard]] inline auto helpPage() const -> QString override { return ""; } }; +class ModrinthShaderPackPage : public ShaderPackResourcePage { + Q_OBJECT + + public: + static ModrinthShaderPackPage* create(ShaderPackDownloadDialog* dialog, BaseInstance& instance) + { + return ShaderPackResourcePage::create(dialog, instance); + } + + ModrinthShaderPackPage(ShaderPackDownloadDialog* dialog, BaseInstance& instance); + ~ModrinthShaderPackPage() override = default; + + [[nodiscard]] bool shouldDisplay() const override; + + [[nodiscard]] inline auto displayName() const -> QString override { return Modrinth::displayName(); } + [[nodiscard]] inline auto icon() const -> QIcon override { return Modrinth::icon(); } + [[nodiscard]] inline auto id() const -> QString override { return Modrinth::id(); } + [[nodiscard]] inline auto debugName() const -> QString override { return Modrinth::debugName(); } + [[nodiscard]] inline auto metaEntryBase() const -> QString override { return Modrinth::metaEntryBase(); } + + [[nodiscard]] inline auto helpPage() const -> QString override { return ""; } +}; + } // namespace ResourceDownload -- cgit From ada5e88eb933a41691121316c78cd2e564965fa0 Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 29 Jan 2023 18:07:49 -0300 Subject: feat(RD): add texture pack downloader This extends the resource pack downloader, with the custom behavior of filtering the versions that shows up, to those <= 1.6. As always, Flame is funky and requires a bit more workarounds than average. This will also get a nice improvement when the Version parsing and comparison PR gets merged! :D Signed-off-by: flow --- launcher/Application.cpp | 1 + launcher/CMakeLists.txt | 5 + launcher/meta/Version.cpp | 5 + launcher/meta/Version.h | 3 + launcher/ui/dialogs/ResourceDownloadDialog.cpp | 27 ++++++ launcher/ui/dialogs/ResourceDownloadDialog.h | 20 ++++ launcher/ui/pages/instance/TexturePackPage.cpp | 104 +++++++++++++++++++++ launcher/ui/pages/instance/TexturePackPage.h | 19 +--- launcher/ui/pages/modplatform/ResourceModel.h | 2 +- launcher/ui/pages/modplatform/ResourcePackPage.cpp | 2 +- launcher/ui/pages/modplatform/ResourcePackPage.h | 2 +- launcher/ui/pages/modplatform/TexturePackModel.cpp | 80 ++++++++++++++++ launcher/ui/pages/modplatform/TexturePackModel.h | 23 +++++ launcher/ui/pages/modplatform/TexturePackPage.h | 46 +++++++++ .../modplatform/flame/FlameResourceModels.cpp | 58 ++++++++++++ .../pages/modplatform/flame/FlameResourceModels.h | 21 +++++ .../pages/modplatform/flame/FlameResourcePages.cpp | 40 ++++++++ .../pages/modplatform/flame/FlameResourcePages.h | 29 +++++- .../modrinth/ModrinthResourceModels.cpp | 22 +++++ .../modplatform/modrinth/ModrinthResourceModels.h | 18 ++++ .../modplatform/modrinth/ModrinthResourcePages.cpp | 19 ++++ .../modplatform/modrinth/ModrinthResourcePages.h | 24 +++++ 22 files changed, 550 insertions(+), 20 deletions(-) create mode 100644 launcher/ui/pages/instance/TexturePackPage.cpp create mode 100644 launcher/ui/pages/modplatform/TexturePackModel.cpp create mode 100644 launcher/ui/pages/modplatform/TexturePackModel.h create mode 100644 launcher/ui/pages/modplatform/TexturePackPage.h (limited to 'launcher/ui/pages/instance') diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 0f7cce66..2956d798 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -611,6 +611,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("ModDownloadGeometry", ""); m_settings->registerSetting("RPDownloadGeometry", ""); + m_settings->registerSetting("TPDownloadGeometry", ""); m_settings->registerSetting("ShaderDownloadGeometry", ""); // HACK: This code feels so stupid is there a less stupid way of doing this? diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 795aeb9f..202e633c 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -720,6 +720,7 @@ SET(LAUNCHER_SOURCES ui/pages/instance/ManagedPackPage.cpp ui/pages/instance/ManagedPackPage.h ui/pages/instance/TexturePackPage.h + ui/pages/instance/TexturePackPage.cpp ui/pages/instance/ResourcePackPage.h ui/pages/instance/ResourcePackPage.cpp ui/pages/instance/ShaderPackPage.h @@ -778,6 +779,10 @@ SET(LAUNCHER_SOURCES ui/pages/modplatform/ResourcePackPage.cpp ui/pages/modplatform/ResourcePackModel.cpp + # Needed for MOC to find them without a corresponding .cpp + ui/pages/modplatform/TexturePackPage.h + ui/pages/modplatform/TexturePackModel.cpp + ui/pages/modplatform/ShaderPackPage.cpp ui/pages/modplatform/ShaderPackModel.cpp diff --git a/launcher/meta/Version.cpp b/launcher/meta/Version.cpp index 68cfa55c..e617abf8 100644 --- a/launcher/meta/Version.cpp +++ b/launcher/meta/Version.cpp @@ -99,6 +99,11 @@ QString Meta::Version::localFilename() const return m_uid + '/' + m_version + ".json"; } +::Version Meta::Version::toComparableVersion() const +{ + return { const_cast(this)->descriptor() }; +} + void Meta::Version::setType(const QString &type) { m_type = type; diff --git a/launcher/meta/Version.h b/launcher/meta/Version.h index 7228fa36..78156193 100644 --- a/launcher/meta/Version.h +++ b/launcher/meta/Version.h @@ -16,6 +16,7 @@ #pragma once #include "BaseVersion.h" +#include "../Version.h" #include #include @@ -85,6 +86,8 @@ public: QString localFilename() const override; + [[nodiscard]] ::Version toComparableVersion() const; + public: // for usage by format parsers only void setType(const QString &type); void setTime(const qint64 time); diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index 98a2eb88..edb7d063 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -26,6 +26,7 @@ #include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ResourcePackFolderModel.h" +#include "minecraft/mod/TexturePackFolderModel.h" #include "minecraft/mod/ShaderPackFolderModel.h" #include "ui/dialogs/ReviewMessageBox.h" @@ -258,6 +259,32 @@ QList ResourcePackDownloadDialog::getPages() } +TexturePackDownloadDialog::TexturePackDownloadDialog(QWidget* parent, + const std::shared_ptr& resource_packs, + BaseInstance* instance) + : ResourceDownloadDialog(parent, resource_packs), m_instance(instance) +{ + setWindowTitle(dialogTitle()); + + initializeContainer(); + connectButtons(); + + if (!geometrySaveKey().isEmpty()) + restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get(geometrySaveKey()).toByteArray())); +} + +QList TexturePackDownloadDialog::getPages() +{ + QList pages; + + pages.append(ModrinthTexturePackPage::create(this, *m_instance)); + if (APPLICATION->capabilities() & Application::SupportsFlame) + pages.append(FlameTexturePackPage::create(this, *m_instance)); + + return pages; +} + + ShaderPackDownloadDialog::ShaderPackDownloadDialog(QWidget* parent, const std::shared_ptr& shaders, BaseInstance* instance) diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.h b/launcher/ui/dialogs/ResourceDownloadDialog.h index 203bac66..5678dc8b 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.h +++ b/launcher/ui/dialogs/ResourceDownloadDialog.h @@ -36,6 +36,7 @@ class QDialogButtonBox; class ResourceDownloadTask; class ResourceFolderModel; class ResourcePackFolderModel; +class TexturePackFolderModel; class ShaderPackFolderModel; namespace ResourceDownload { @@ -129,6 +130,25 @@ class ResourcePackDownloadDialog final : public ResourceDownloadDialog { BaseInstance* m_instance; }; +class TexturePackDownloadDialog final : public ResourceDownloadDialog { + Q_OBJECT + + public: + explicit TexturePackDownloadDialog(QWidget* parent, + const std::shared_ptr& resource_packs, + BaseInstance* instance); + ~TexturePackDownloadDialog() override = default; + + //: String that gets appended to the texture pack download dialog title ("Download " + resourcesString()) + [[nodiscard]] QString resourcesString() const override { return tr("texture packs"); } + [[nodiscard]] QString geometrySaveKey() const override { return "TPDownloadGeometry"; } + + QList getPages() override; + + private: + BaseInstance* m_instance; +}; + class ShaderPackDownloadDialog final : public ResourceDownloadDialog { Q_OBJECT diff --git a/launcher/ui/pages/instance/TexturePackPage.cpp b/launcher/ui/pages/instance/TexturePackPage.cpp new file mode 100644 index 00000000..5b68c102 --- /dev/null +++ b/launcher/ui/pages/instance/TexturePackPage.cpp @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 Jamie Mansfield + * + * 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 . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TexturePackPage.h" + +#include "ResourceDownloadTask.h" + +#include "minecraft/mod/TexturePack.h" + +#include "ui/dialogs/CustomMessageBox.h" +#include "ui/dialogs/ProgressDialog.h" +#include "ui/dialogs/ResourceDownloadDialog.h" + +TexturePackPage::TexturePackPage(MinecraftInstance* instance, std::shared_ptr model, QWidget* parent) + : ExternalResourcesPage(instance, model, parent) +{ + ui->actionDownloadItem->setText(tr("Download TPs")); + ui->actionDownloadItem->setToolTip(tr("Download TPs from online platforms")); + ui->actionDownloadItem->setEnabled(true); + connect(ui->actionDownloadItem, &QAction::triggered, this, &TexturePackPage::downloadTPs); + ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem); + + ui->actionViewConfigs->setVisible(false); +} + +bool TexturePackPage::onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) +{ + auto sourceCurrent = m_filterModel->mapToSource(current); + int row = sourceCurrent.row(); + auto& rp = static_cast(m_model->at(row)); + ui->frame->updateWithTexturePack(rp); + + return true; +} + +void TexturePackPage::downloadTPs() +{ + if (!m_controlsEnabled) + return; + 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); + if (mdownload.exec()) { + auto tasks = new ConcurrentTask(this); + 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 : mdownload.getTasks()) { + tasks->addTask(task); + } + + ProgressDialog loadDialog(this); + loadDialog.setSkipButton(true, tr("Abort")); + loadDialog.execWithTask(tasks); + + m_model->update(); + } +} diff --git a/launcher/ui/pages/instance/TexturePackPage.h b/launcher/ui/pages/instance/TexturePackPage.h index 69b836ca..5712f7d6 100644 --- a/launcher/ui/pages/instance/TexturePackPage.h +++ b/launcher/ui/pages/instance/TexturePackPage.h @@ -39,18 +39,12 @@ #include "ui_ExternalResourcesPage.h" #include "minecraft/mod/TexturePackFolderModel.h" -#include "minecraft/mod/TexturePack.h" class TexturePackPage : public ExternalResourcesPage { Q_OBJECT public: - explicit TexturePackPage(MinecraftInstance *instance, std::shared_ptr model, QWidget *parent = 0) - : ExternalResourcesPage(instance, model, parent) - { - ui->actionViewConfigs->setVisible(false); - } - virtual ~TexturePackPage() {} + explicit TexturePackPage(MinecraftInstance *instance, std::shared_ptr model, QWidget* parent = nullptr); QString displayName() const override { return tr("Texture packs"); } QIcon icon() const override { return APPLICATION->getThemedIcon("resourcepacks"); } @@ -63,13 +57,6 @@ public: } public slots: - bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override - { - auto sourceCurrent = m_filterModel->mapToSource(current); - int row = sourceCurrent.row(); - auto& rp = static_cast(m_model->at(row)); - ui->frame->updateWithTexturePack(rp); - - return true; - } + bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override; + void downloadTPs(); }; diff --git a/launcher/ui/pages/modplatform/ResourceModel.h b/launcher/ui/pages/modplatform/ResourceModel.h index 610b631c..46a02d6e 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.h +++ b/launcher/ui/pages/modplatform/ResourceModel.h @@ -98,7 +98,7 @@ class ResourceModel : public QAbstractListModel { /** Functions to load data into a pack. * - * Those are needed for the same reason as ddocumentToArray, and NEED to be re-implemented in the same way. + * Those are needed for the same reason as documentToArray, and NEED to be re-implemented in the same way. */ virtual void loadIndexedPack(ModPlatform::IndexedPack&, QJsonObject&); diff --git a/launcher/ui/pages/modplatform/ResourcePackPage.cpp b/launcher/ui/pages/modplatform/ResourcePackPage.cpp index 8d663aa8..c2de9e3b 100644 --- a/launcher/ui/pages/modplatform/ResourcePackPage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePackPage.cpp @@ -9,7 +9,7 @@ namespace ResourceDownload { -ResourcePackResourcePage::ResourcePackResourcePage(ResourcePackDownloadDialog* dialog, BaseInstance& instance) +ResourcePackResourcePage::ResourcePackResourcePage(ResourceDownloadDialog* dialog, BaseInstance& instance) : ResourcePage(dialog, instance) { connect(m_ui->searchButton, &QPushButton::clicked, this, &ResourcePackResourcePage::triggerSearch); diff --git a/launcher/ui/pages/modplatform/ResourcePackPage.h b/launcher/ui/pages/modplatform/ResourcePackPage.h index 2ecff390..97780047 100644 --- a/launcher/ui/pages/modplatform/ResourcePackPage.h +++ b/launcher/ui/pages/modplatform/ResourcePackPage.h @@ -39,7 +39,7 @@ class ResourcePackResourcePage : public ResourcePage { [[nodiscard]] QMap urlHandlers() const override; protected: - ResourcePackResourcePage(ResourcePackDownloadDialog* dialog, BaseInstance& instance); + ResourcePackResourcePage(ResourceDownloadDialog* dialog, BaseInstance& instance); protected slots: void triggerSearch() override; diff --git a/launcher/ui/pages/modplatform/TexturePackModel.cpp b/launcher/ui/pages/modplatform/TexturePackModel.cpp new file mode 100644 index 00000000..076a200d --- /dev/null +++ b/launcher/ui/pages/modplatform/TexturePackModel.cpp @@ -0,0 +1,80 @@ +#include "TexturePackModel.h" + +#include "Application.h" + +#include "meta/Index.h" +#include "meta/Version.h" + +static std::list s_availableVersions = {}; + +namespace ResourceDownload { +TexturePackResourceModel::TexturePackResourceModel(BaseInstance const& inst, ResourceAPI* api) + : ResourcePackResourceModel(inst, api), m_version_list(APPLICATION->metadataIndex()->get("net.minecraft")) +{ + if (!m_version_list->isLoaded()) { + qDebug() << "Loading version list..."; + auto task = m_version_list->getLoadTask(); + if (!task->isRunning()) + task->start(); + } +} + +void waitOnVersionListLoad(Meta::VersionList::Ptr version_list) +{ + QEventLoop load_version_list_loop; + + QTimer time_limit_for_list_load; + time_limit_for_list_load.setTimerType(Qt::TimerType::CoarseTimer); + time_limit_for_list_load.setSingleShot(true); + time_limit_for_list_load.callOnTimeout(&load_version_list_loop, &QEventLoop::quit); + time_limit_for_list_load.start(4000); + + auto task = version_list->getLoadTask(); + QObject::connect(task.get(), &Task::finished, &load_version_list_loop, &QEventLoop::quit); + + load_version_list_loop.exec(); + if (time_limit_for_list_load.isActive()) + time_limit_for_list_load.stop(); +} + +ResourceAPI::SearchArgs TexturePackResourceModel::createSearchArguments() +{ + if (s_availableVersions.empty()) + waitOnVersionListLoad(m_version_list); + + auto args = ResourcePackResourceModel::createSearchArguments(); + + if (!m_version_list->isLoaded()) { + qCritical() << "The version list could not be loaded. Falling back to showing all entries."; + return args; + } + + if (s_availableVersions.empty()) { + for (auto&& version : m_version_list->versions()) { + // FIXME: This duplicates the logic in meta for the 'texturepacks' trait. However, we don't have access to that + // information from the index file alone. Also, downloading every version's file isn't a very good idea. + if (auto ver = version->toComparableVersion(); ver <= maximumTexturePackVersion()) + s_availableVersions.push_back(ver); + } + } + + Q_ASSERT(!s_availableVersions.empty()); + + args.versions = s_availableVersions; + + return args; +} + +ResourceAPI::VersionSearchArgs TexturePackResourceModel::createVersionsArguments(QModelIndex& entry) +{ + auto args = ResourcePackResourceModel::createVersionsArguments(entry); + if (!m_version_list->isLoaded()) { + qCritical() << "The version list could not be loaded. Falling back to showing all entries."; + return args; + } + + args.mcVersions = s_availableVersions; + return args; +} + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/TexturePackModel.h b/launcher/ui/pages/modplatform/TexturePackModel.h new file mode 100644 index 00000000..0ae5bdd4 --- /dev/null +++ b/launcher/ui/pages/modplatform/TexturePackModel.h @@ -0,0 +1,23 @@ +#pragma once + +#include "meta/VersionList.h" +#include "ui/pages/modplatform/ResourcePackModel.h" + +namespace ResourceDownload { + +class TexturePackResourceModel : public ResourcePackResourceModel { + Q_OBJECT + + public: + TexturePackResourceModel(BaseInstance const& inst, ResourceAPI* api); + + [[nodiscard]] inline ::Version maximumTexturePackVersion() const { return { "1.6" }; } + + ResourceAPI::SearchArgs createSearchArguments() override; + ResourceAPI::VersionSearchArgs createVersionsArguments(QModelIndex&) override; + + protected: + Meta::VersionList::Ptr m_version_list; +}; + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/TexturePackPage.h b/launcher/ui/pages/modplatform/TexturePackPage.h new file mode 100644 index 00000000..3466d3ea --- /dev/null +++ b/launcher/ui/pages/modplatform/TexturePackPage.h @@ -0,0 +1,46 @@ +#pragma once + +#include "ui_ResourcePage.h" +#include "ui/dialogs/ResourceDownloadDialog.h" +#include "ui/pages/modplatform/ResourcePackPage.h" +#include "ui/pages/modplatform/TexturePackModel.h" + +namespace Ui { +class ResourcePage; +} + +namespace ResourceDownload { + +class TexturePackDownloadDialog; + +class TexturePackResourcePage : public ResourcePackResourcePage { + Q_OBJECT + + public: + template + static T* create(TexturePackDownloadDialog* dialog, BaseInstance& instance) + { + auto page = new T(dialog, instance); + auto model = static_cast(page->getModel()); + + connect(model, &ResourceModel::versionListUpdated, page, &ResourcePage::updateVersionList); + connect(model, &ResourceModel::projectInfoUpdated, page, &ResourcePage::updateUi); + + return page; + } + + //: The plural version of 'texture pack' + [[nodiscard]] inline QString resourcesString() const override { return tr("texture packs"); } + //: The singular version of 'texture packs' + [[nodiscard]] inline QString resourceString() const override { return tr("texture pack"); } + + protected: + TexturePackResourcePage(TexturePackDownloadDialog* dialog, BaseInstance& instance) + : ResourcePackResourcePage(dialog, instance) + { + connect(m_ui->searchButton, &QPushButton::clicked, this, &TexturePackResourcePage::triggerSearch); + connect(m_ui->packView, &QListView::doubleClicked, this, &TexturePackResourcePage::onResourceSelected); + } +}; + +} // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp index 95d915fc..e3d0bc14 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp @@ -57,5 +57,63 @@ auto FlameResourcePackModel::documentToArray(QJsonDocument& obj) const -> QJsonA return Json::ensureArray(obj.object(), "data"); } +FlameTexturePackModel::FlameTexturePackModel(const BaseInstance& base) : TexturePackResourceModel(base, new FlameAPI) {} + +void FlameTexturePackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) +{ + FlameMod::loadIndexedPack(m, obj); +} + +// We already deal with the URLs when initializing the pack, due to the API response's structure +void FlameTexturePackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) +{ + FlameMod::loadBody(m, obj); +} + +void FlameTexturePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) +{ + FlameMod::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); + + QVector filtered_versions(m.versions.size()); + + // FIXME: Client-side version filtering. This won't take into account any user-selected filtering. + for (auto const& version : m.versions) { + auto const& mc_versions = version.mcVersion; + + if (std::any_of(mc_versions.constBegin(), mc_versions.constEnd(), + [this](auto const& mc_version){ return Version(mc_version) <= maximumTexturePackVersion(); })) + filtered_versions.push_back(version); + } + + m.versions = filtered_versions; +} + +ResourceAPI::SearchArgs FlameTexturePackModel::createSearchArguments() +{ + auto args = TexturePackResourceModel::createSearchArguments(); + + auto profile = static_cast(m_base_instance).getPackProfile(); + QString instance_minecraft_version = profile->getComponentVersion("net.minecraft"); + + // Bypass the texture pack logic, because we can't do multiple versions in the API query + args.versions = { instance_minecraft_version }; + + return args; +} + +ResourceAPI::VersionSearchArgs FlameTexturePackModel::createVersionsArguments(QModelIndex& entry) +{ + auto args = TexturePackResourceModel::createVersionsArguments(entry); + + // Bypass the texture pack logic, because we can't do multiple versions in the API query + args.mcVersions = {}; + + return args; +} + +auto FlameTexturePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray +{ + return Json::ensureArray(obj.object(), "data"); +} } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h index be214716..0252ac40 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h @@ -46,4 +46,25 @@ class FlameResourcePackModel : public ResourcePackResourceModel { auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; }; +class FlameTexturePackModel : public TexturePackResourceModel { + Q_OBJECT + + public: + FlameTexturePackModel(const BaseInstance&); + ~FlameTexturePackModel() override = default; + + private: + [[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; } + [[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); } + + void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override; + void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override; + void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override; + + ResourceAPI::SearchArgs createSearchArguments() override; + ResourceAPI::VersionSearchArgs createVersionsArguments(QModelIndex&) override; + + auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; +}; + } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp index 15b04ab4..f93e27e6 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp @@ -133,10 +133,50 @@ void FlameResourcePackPage::openUrl(const QUrl& url) ResourcePackResourcePage::openUrl(url); } +FlameTexturePackPage::FlameTexturePackPage(TexturePackDownloadDialog* dialog, BaseInstance& instance) + : TexturePackResourcePage(dialog, instance) +{ + m_model = new FlameTexturePackModel(instance); + m_ui->packView->setModel(m_model); + + addSortings(); + + // sometimes Qt just ignores virtual slots and doesn't work as intended it seems, + // so it's best not to connect them in the parent's contructor... + connect(m_ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch())); + connect(m_ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FlameTexturePackPage::onSelectionChanged); + connect(m_ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FlameTexturePackPage::onVersionSelectionChanged); + connect(m_ui->resourceSelectionButton, &QPushButton::clicked, this, &FlameTexturePackPage::onResourceSelected); + + m_ui->packDescription->setMetaEntry(metaEntryBase()); +} + +bool FlameTexturePackPage::optedOut(ModPlatform::IndexedVersion& ver) const +{ + return isOptedOut(ver); +} + +void FlameTexturePackPage::openUrl(const QUrl& url) +{ + if (url.scheme().isEmpty()) { + QString query = url.query(QUrl::FullyDecoded); + + if (query.startsWith("remoteUrl=")) { + // attempt to resolve url from warning page + query.remove(0, 10); + ResourcePackResourcePage::openUrl({QUrl::fromPercentEncoding(query.toUtf8())}); // double decoding is necessary + return; + } + } + + TexturePackResourcePage::openUrl(url); +} + // I don't know why, but doing this on the parent class makes it so that // other mod providers start loading before being selected, at least with // my Qt, so we need to implement this in every derived class... auto FlameModPage::shouldDisplay() const -> bool { return true; } auto FlameResourcePackPage::shouldDisplay() const -> bool { return true; } +auto FlameTexturePackPage::shouldDisplay() const -> bool { return true; } } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h index 4507a32a..103a6bb9 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h @@ -44,7 +44,7 @@ #include "ui/pages/modplatform/ModPage.h" #include "ui/pages/modplatform/ResourcePackPage.h" -#include "ui/pages/modplatform/ShaderPackPage.h" +#include "ui/pages/modplatform/TexturePackPage.h" namespace ResourceDownload { @@ -111,4 +111,31 @@ class FlameResourcePackPage : public ResourcePackResourcePage { void openUrl(const QUrl& url) override; }; +class FlameTexturePackPage : public TexturePackResourcePage { + Q_OBJECT + + public: + static FlameTexturePackPage* create(TexturePackDownloadDialog* dialog, BaseInstance& instance) + { + return TexturePackResourcePage::create(dialog, instance); + } + + FlameTexturePackPage(TexturePackDownloadDialog* dialog, BaseInstance& instance); + ~FlameTexturePackPage() override = default; + + [[nodiscard]] bool shouldDisplay() const override; + + [[nodiscard]] inline auto displayName() const -> QString override { return Flame::displayName(); } + [[nodiscard]] inline auto icon() const -> QIcon override { return Flame::icon(); } + [[nodiscard]] inline auto id() const -> QString override { return Flame::id(); } + [[nodiscard]] inline auto debugName() const -> QString override { return Flame::debugName(); } + [[nodiscard]] inline auto metaEntryBase() const -> QString override { return Flame::metaEntryBase(); } + + [[nodiscard]] inline auto helpPage() const -> QString override { return ""; } + + bool optedOut(ModPlatform::IndexedVersion& ver) const override; + + void openUrl(const QUrl& url) override; +}; + } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp index bd433121..f5d1cc28 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp @@ -69,6 +69,28 @@ auto ModrinthResourcePackModel::documentToArray(QJsonDocument& obj) const -> QJs return obj.object().value("hits").toArray(); } +ModrinthTexturePackModel::ModrinthTexturePackModel(const BaseInstance& base) : TexturePackResourceModel(base, new ModrinthAPI){} + +void ModrinthTexturePackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) +{ + ::Modrinth::loadIndexedPack(m, obj); +} + +void ModrinthTexturePackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) +{ + ::Modrinth::loadExtraPackData(m, obj); +} + +void ModrinthTexturePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) +{ + ::Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); +} + +auto ModrinthTexturePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray +{ + return obj.object().value("hits").toArray(); +} + ModrinthShaderPackModel::ModrinthShaderPackModel(const BaseInstance& base) : ShaderPackResourceModel(base, new ModrinthAPI){} void ModrinthShaderPackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h index 80a48089..b351b19b 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h @@ -62,6 +62,24 @@ class ModrinthResourcePackModel : public ResourcePackResourceModel { auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; }; +class ModrinthTexturePackModel : public TexturePackResourceModel { + Q_OBJECT + + public: + ModrinthTexturePackModel(const BaseInstance&); + ~ModrinthTexturePackModel() override = default; + + private: + [[nodiscard]] QString debugName() const override { return Modrinth::debugName() + " (Model)"; } + [[nodiscard]] QString metaEntryBase() const override { return Modrinth::metaEntryBase(); } + + void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override; + void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override; + void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override; + + auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; +}; + class ModrinthShaderPackModel : public ShaderPackResourceModel { Q_OBJECT diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp index 2826b5d3..dd143700 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -100,6 +100,24 @@ ModrinthResourcePackPage::ModrinthResourcePackPage(ResourcePackDownloadDialog* d m_ui->packDescription->setMetaEntry(metaEntryBase()); } +ModrinthTexturePackPage::ModrinthTexturePackPage(TexturePackDownloadDialog* dialog, BaseInstance& instance) + : TexturePackResourcePage(dialog, instance) +{ + m_model = new ModrinthTexturePackModel(instance); + m_ui->packView->setModel(m_model); + + addSortings(); + + // sometimes Qt just ignores virtual slots and doesn't work as intended it seems, + // so it's best not to connect them in the parent's constructor... + connect(m_ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch())); + connect(m_ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ModrinthTexturePackPage::onSelectionChanged); + connect(m_ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &ModrinthTexturePackPage::onVersionSelectionChanged); + connect(m_ui->resourceSelectionButton, &QPushButton::clicked, this, &ModrinthTexturePackPage::onResourceSelected); + + m_ui->packDescription->setMetaEntry(metaEntryBase()); +} + ModrinthShaderPackPage::ModrinthShaderPackPage(ShaderPackDownloadDialog* dialog, BaseInstance& instance) : ShaderPackResourcePage(dialog, instance) { @@ -123,6 +141,7 @@ ModrinthShaderPackPage::ModrinthShaderPackPage(ShaderPackDownloadDialog* dialog, // my Qt, so we need to implement this in every derived class... auto ModrinthModPage::shouldDisplay() const -> bool { return true; } auto ModrinthResourcePackPage::shouldDisplay() const -> bool { return true; } +auto ModrinthTexturePackPage::shouldDisplay() const -> bool { return true; } auto ModrinthShaderPackPage::shouldDisplay() const -> bool { return true; } } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h index 8733a1b2..f4eb5ce0 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h @@ -43,6 +43,7 @@ #include "ui/pages/modplatform/ModPage.h" #include "ui/pages/modplatform/ResourcePackPage.h" +#include "ui/pages/modplatform/TexturePackPage.h" #include "ui/pages/modplatform/ShaderPackPage.h" namespace ResourceDownload { @@ -103,6 +104,29 @@ class ModrinthResourcePackPage : public ResourcePackResourcePage { [[nodiscard]] inline auto helpPage() const -> QString override { return ""; } }; +class ModrinthTexturePackPage : public TexturePackResourcePage { + Q_OBJECT + + public: + static ModrinthTexturePackPage* create(TexturePackDownloadDialog* dialog, BaseInstance& instance) + { + return TexturePackResourcePage::create(dialog, instance); + } + + ModrinthTexturePackPage(TexturePackDownloadDialog* dialog, BaseInstance& instance); + ~ModrinthTexturePackPage() override = default; + + [[nodiscard]] bool shouldDisplay() const override; + + [[nodiscard]] inline auto displayName() const -> QString override { return Modrinth::displayName(); } + [[nodiscard]] inline auto icon() const -> QIcon override { return Modrinth::icon(); } + [[nodiscard]] inline auto id() const -> QString override { return Modrinth::id(); } + [[nodiscard]] inline auto debugName() const -> QString override { return Modrinth::debugName(); } + [[nodiscard]] inline auto metaEntryBase() const -> QString override { return Modrinth::metaEntryBase(); } + + [[nodiscard]] inline auto helpPage() const -> QString override { return ""; } +}; + class ModrinthShaderPackPage : public ShaderPackResourcePage { Q_OBJECT -- cgit From 46c6cc2d2b59ee3c81abe690218d8f60e6e02c76 Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 29 Jan 2023 20:40:55 -0300 Subject: chore: add my copyright headers .-. Signed-off-by: flow --- launcher/ui/pages/instance/ResourcePackPage.cpp | 6 ++++-- launcher/ui/pages/instance/ResourcePackPage.h | 6 ++++-- launcher/ui/pages/instance/ShaderPackPage.cpp | 6 ++++-- launcher/ui/pages/instance/ShaderPackPage.h | 6 ++++-- launcher/ui/pages/instance/TexturePackPage.cpp | 6 ++++-- launcher/ui/pages/instance/TexturePackPage.h | 6 ++++-- launcher/ui/pages/modplatform/ResourcePackModel.cpp | 4 ++++ launcher/ui/pages/modplatform/ResourcePackModel.h | 4 ++++ launcher/ui/pages/modplatform/ResourcePackPage.cpp | 4 ++++ launcher/ui/pages/modplatform/ResourcePackPage.h | 4 ++++ launcher/ui/pages/modplatform/ShaderPackModel.cpp | 4 ++++ launcher/ui/pages/modplatform/ShaderPackModel.h | 4 ++++ launcher/ui/pages/modplatform/ShaderPackPage.cpp | 4 ++++ launcher/ui/pages/modplatform/ShaderPackPage.h | 4 ++++ launcher/ui/pages/modplatform/TexturePackModel.cpp | 4 ++++ launcher/ui/pages/modplatform/TexturePackModel.h | 4 ++++ launcher/ui/pages/modplatform/TexturePackPage.h | 4 ++++ 17 files changed, 68 insertions(+), 12 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/pages/instance/ResourcePackPage.cpp b/launcher/ui/pages/instance/ResourcePackPage.cpp index e705f29e..f46a7939 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.cpp +++ b/launcher/ui/pages/instance/ResourcePackPage.cpp @@ -1,6 +1,8 @@ -// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/ui/pages/instance/ResourcePackPage.h b/launcher/ui/pages/instance/ResourcePackPage.h index fc05158c..b04aa2e9 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.h +++ b/launcher/ui/pages/instance/ResourcePackPage.h @@ -1,6 +1,8 @@ -// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/ui/pages/instance/ShaderPackPage.cpp b/launcher/ui/pages/instance/ShaderPackPage.cpp index 2dde4dc7..2d0c10aa 100644 --- a/launcher/ui/pages/instance/ShaderPackPage.cpp +++ b/launcher/ui/pages/instance/ShaderPackPage.cpp @@ -1,6 +1,8 @@ -// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/ui/pages/instance/ShaderPackPage.h b/launcher/ui/pages/instance/ShaderPackPage.h index 09895949..a779fd8c 100644 --- a/launcher/ui/pages/instance/ShaderPackPage.h +++ b/launcher/ui/pages/instance/ShaderPackPage.h @@ -1,6 +1,8 @@ -// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/ui/pages/instance/TexturePackPage.cpp b/launcher/ui/pages/instance/TexturePackPage.cpp index 5b68c102..76fc04e7 100644 --- a/launcher/ui/pages/instance/TexturePackPage.cpp +++ b/launcher/ui/pages/instance/TexturePackPage.cpp @@ -1,6 +1,8 @@ -// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/ui/pages/instance/TexturePackPage.h b/launcher/ui/pages/instance/TexturePackPage.h index 5712f7d6..47a8fa60 100644 --- a/launcher/ui/pages/instance/TexturePackPage.h +++ b/launcher/ui/pages/instance/TexturePackPage.h @@ -1,6 +1,8 @@ -// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/ui/pages/modplatform/ResourcePackModel.cpp b/launcher/ui/pages/modplatform/ResourcePackModel.cpp index fd1afa0d..3df9a787 100644 --- a/launcher/ui/pages/modplatform/ResourcePackModel.cpp +++ b/launcher/ui/pages/modplatform/ResourcePackModel.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #include "ResourcePackModel.h" #include diff --git a/launcher/ui/pages/modplatform/ResourcePackModel.h b/launcher/ui/pages/modplatform/ResourcePackModel.h index 63aa533c..e2b4a195 100644 --- a/launcher/ui/pages/modplatform/ResourcePackModel.h +++ b/launcher/ui/pages/modplatform/ResourcePackModel.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #pragma once #include diff --git a/launcher/ui/pages/modplatform/ResourcePackPage.cpp b/launcher/ui/pages/modplatform/ResourcePackPage.cpp index c2de9e3b..52fb4802 100644 --- a/launcher/ui/pages/modplatform/ResourcePackPage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePackPage.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #include "ResourcePackPage.h" #include "ui_ResourcePage.h" diff --git a/launcher/ui/pages/modplatform/ResourcePackPage.h b/launcher/ui/pages/modplatform/ResourcePackPage.h index 97780047..c01c89c4 100644 --- a/launcher/ui/pages/modplatform/ResourcePackPage.h +++ b/launcher/ui/pages/modplatform/ResourcePackPage.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #pragma once #include "ui/pages/modplatform/ResourcePage.h" diff --git a/launcher/ui/pages/modplatform/ShaderPackModel.cpp b/launcher/ui/pages/modplatform/ShaderPackModel.cpp index 36993288..2101b394 100644 --- a/launcher/ui/pages/modplatform/ShaderPackModel.cpp +++ b/launcher/ui/pages/modplatform/ShaderPackModel.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #include "ShaderPackModel.h" #include diff --git a/launcher/ui/pages/modplatform/ShaderPackModel.h b/launcher/ui/pages/modplatform/ShaderPackModel.h index c84df15a..f3c695e9 100644 --- a/launcher/ui/pages/modplatform/ShaderPackModel.h +++ b/launcher/ui/pages/modplatform/ShaderPackModel.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #pragma once #include diff --git a/launcher/ui/pages/modplatform/ShaderPackPage.cpp b/launcher/ui/pages/modplatform/ShaderPackPage.cpp index 364d0a54..251c07e7 100644 --- a/launcher/ui/pages/modplatform/ShaderPackPage.cpp +++ b/launcher/ui/pages/modplatform/ShaderPackPage.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #include "ShaderPackPage.h" #include "ui_ResourcePage.h" diff --git a/launcher/ui/pages/modplatform/ShaderPackPage.h b/launcher/ui/pages/modplatform/ShaderPackPage.h index 92a9c3ee..972419a8 100644 --- a/launcher/ui/pages/modplatform/ShaderPackPage.h +++ b/launcher/ui/pages/modplatform/ShaderPackPage.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #pragma once #include "ui/pages/modplatform/ResourcePage.h" diff --git a/launcher/ui/pages/modplatform/TexturePackModel.cpp b/launcher/ui/pages/modplatform/TexturePackModel.cpp index 076a200d..fa636951 100644 --- a/launcher/ui/pages/modplatform/TexturePackModel.cpp +++ b/launcher/ui/pages/modplatform/TexturePackModel.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #include "TexturePackModel.h" #include "Application.h" diff --git a/launcher/ui/pages/modplatform/TexturePackModel.h b/launcher/ui/pages/modplatform/TexturePackModel.h index 0ae5bdd4..bb2db5cf 100644 --- a/launcher/ui/pages/modplatform/TexturePackModel.h +++ b/launcher/ui/pages/modplatform/TexturePackModel.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #pragma once #include "meta/VersionList.h" diff --git a/launcher/ui/pages/modplatform/TexturePackPage.h b/launcher/ui/pages/modplatform/TexturePackPage.h index 3466d3ea..0bdce2f9 100644 --- a/launcher/ui/pages/modplatform/TexturePackPage.h +++ b/launcher/ui/pages/modplatform/TexturePackPage.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 flowln +// +// SPDX-License-Identifier: GPL-3.0-only + #pragma once #include "ui_ResourcePage.h" -- cgit From 7896dd19c12c0276551ba188adc6184dcf0a3184 Mon Sep 17 00:00:00 2001 From: kumquat-ir <66188216+kumquat-ir@users.noreply.github.com> Date: Sat, 11 Feb 2023 17:36:06 -0500 Subject: nilmods instance page mostly copied from the coremod page impl Signed-off-by: kumquat-ir <66188216+kumquat-ir@users.noreply.github.com> --- launcher/InstancePageProvider.h | 1 + launcher/minecraft/MinecraftInstance.cpp | 17 +++++++++++++++++ launcher/minecraft/MinecraftInstance.h | 3 +++ launcher/minecraft/launch/ScanModFolders.cpp | 14 +++++++++++++- launcher/minecraft/launch/ScanModFolders.h | 2 ++ launcher/ui/pages/instance/ModFolderPage.cpp | 9 +++++++++ launcher/ui/pages/instance/ModFolderPage.h | 13 +++++++++++++ 7 files changed, 58 insertions(+), 1 deletion(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/InstancePageProvider.h b/launcher/InstancePageProvider.h index 286298cc..b4b6e739 100644 --- a/launcher/InstancePageProvider.h +++ b/launcher/InstancePageProvider.h @@ -39,6 +39,7 @@ public: modsPage->setFilter("%1 (*.zip *.jar *.litemod *.nilmod)"); values.append(modsPage); values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList())); + values.append(new NilModFolderPage(onesix.get(), onesix->nilModList())); values.append(new ResourcePackPage(onesix.get(), onesix->resourcePackList())); values.append(new TexturePackPage(onesix.get(), onesix->texturePackList())); values.append(new ShaderPackPage(onesix.get(), onesix->shaderPackList())); diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 4fe234c4..0737a648 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -290,6 +290,11 @@ QString MinecraftInstance::coreModsDir() const return FS::PathCombine(gameRoot(), "coremods"); } +QString MinecraftInstance::nilModsDir() const +{ + return FS::PathCombine(gameRoot(), "nilmods"); +} + QString MinecraftInstance::resourcePacksDir() const { return FS::PathCombine(gameRoot(), "resourcepacks"); @@ -1125,6 +1130,18 @@ std::shared_ptr MinecraftInstance::coreModList() const return m_core_mod_list; } +std::shared_ptr MinecraftInstance::nilModList() const +{ + if (!m_nil_mod_list) + { + bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); + m_nil_mod_list.reset(new ModFolderModel(nilModsDir(), is_indexed)); + m_nil_mod_list->disableInteraction(isRunning()); + connect(this, &BaseInstance::runningStatusChanged, m_nil_mod_list.get(), &ModFolderModel::disableInteraction); + } + return m_nil_mod_list; +} + std::shared_ptr MinecraftInstance::resourcePackList() const { if (!m_resource_pack_list) diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index 1bbd7b83..a75fa481 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -84,6 +84,7 @@ public: QString shaderPacksDir() const; QString modsRoot() const override; QString coreModsDir() const; + QString nilModsDir() const; QString modsCacheLocation() const; QString libDir() const; QString worldDir() const; @@ -116,6 +117,7 @@ public: ////// Mod Lists ////// std::shared_ptr loaderModList() const; std::shared_ptr coreModList() const; + std::shared_ptr nilModList() const; std::shared_ptr resourcePackList() const; std::shared_ptr texturePackList() const; std::shared_ptr shaderPackList() const; @@ -170,6 +172,7 @@ protected: // data std::shared_ptr m_components; mutable std::shared_ptr m_loader_mod_list; mutable std::shared_ptr m_core_mod_list; + mutable std::shared_ptr m_nil_mod_list; mutable std::shared_ptr m_resource_pack_list; mutable std::shared_ptr m_shader_pack_list; mutable std::shared_ptr m_texture_pack_list; diff --git a/launcher/minecraft/launch/ScanModFolders.cpp b/launcher/minecraft/launch/ScanModFolders.cpp index bdffeadd..71e7638c 100644 --- a/launcher/minecraft/launch/ScanModFolders.cpp +++ b/launcher/minecraft/launch/ScanModFolders.cpp @@ -55,6 +55,12 @@ void ScanModFolders::executeTask() if(!cores->update()) { m_coreModsDone = true; } + + auto nils = m_inst->nilModList(); + connect(nils.get(), &ModFolderModel::updateFinished, this, &ScanModFolders::nilModsDone); + if(!nils->update()) { + m_nilModsDone = true; + } checkDone(); } @@ -70,9 +76,15 @@ void ScanModFolders::coreModsDone() checkDone(); } +void ScanModFolders::nilModsDone() +{ + m_nilModsDone = true; + checkDone(); +} + void ScanModFolders::checkDone() { - if(m_modsDone && m_coreModsDone) { + if(m_modsDone && m_coreModsDone && m_nilModsDone) { emitSucceeded(); } } diff --git a/launcher/minecraft/launch/ScanModFolders.h b/launcher/minecraft/launch/ScanModFolders.h index d5989170..111a5850 100644 --- a/launcher/minecraft/launch/ScanModFolders.h +++ b/launcher/minecraft/launch/ScanModFolders.h @@ -33,10 +33,12 @@ public: private slots: void coreModsDone(); void modsDone(); + void nilModsDone(); private: void checkDone(); private: // DATA bool m_modsDone = false; + bool m_nilModsDone = false; bool m_coreModsDone = false; }; diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index d9069915..8c87a8a0 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -273,3 +273,12 @@ bool CoreModFolderPage::shouldDisplay() const } return false; } + +NilModFolderPage::NilModFolderPage(BaseInstance* inst, std::shared_ptr mods, QWidget* parent) + : ModFolderPage(inst, mods, parent) +{} + +bool NilModFolderPage::shouldDisplay() const +{ + return !m_model->dir().isEmpty(); +} diff --git a/launcher/ui/pages/instance/ModFolderPage.h b/launcher/ui/pages/instance/ModFolderPage.h index ff58b38a..cad240bd 100644 --- a/launcher/ui/pages/instance/ModFolderPage.h +++ b/launcher/ui/pages/instance/ModFolderPage.h @@ -81,3 +81,16 @@ class CoreModFolderPage : public ModFolderPage { virtual bool shouldDisplay() const override; }; + +class NilModFolderPage : public ModFolderPage { + public: + explicit NilModFolderPage(BaseInstance* inst, std::shared_ptr mods, QWidget* parent = 0); + virtual ~NilModFolderPage() = default; + + virtual QString displayName() const override { return tr("Nilmods"); } + virtual QIcon icon() const override { return APPLICATION->getThemedIcon("coremods"); } + virtual QString id() const override { return "nilmods"; } + virtual QString helpPage() const override { return "Nil-mods"; } + + virtual bool shouldDisplay() const override; +}; -- cgit From 9c2a3231c5ee1b15b09bae9b064827ad3dcb86e0 Mon Sep 17 00:00:00 2001 From: kumquat-ir <66188216+kumquat-ir@users.noreply.github.com> Date: Mon, 13 Feb 2023 01:45:23 -0500 Subject: do not create nilmods folder "it cant be that easy" - me, clueless Signed-off-by: kumquat-ir <66188216+kumquat-ir@users.noreply.github.com> --- launcher/minecraft/MinecraftInstance.cpp | 2 +- launcher/minecraft/mod/ModFolderModel.cpp | 2 +- launcher/minecraft/mod/ModFolderModel.h | 2 +- launcher/minecraft/mod/ResourceFolderModel.cpp | 6 ++++-- launcher/minecraft/mod/ResourceFolderModel.h | 3 ++- launcher/ui/pages/instance/ModFolderPage.cpp | 2 +- launcher/ui/pages/instance/ModFolderPage.h | 2 +- 7 files changed, 11 insertions(+), 8 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 0737a648..af4da5d0 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -1135,7 +1135,7 @@ std::shared_ptr MinecraftInstance::nilModList() const if (!m_nil_mod_list) { bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); - m_nil_mod_list.reset(new ModFolderModel(nilModsDir(), is_indexed)); + m_nil_mod_list.reset(new ModFolderModel(nilModsDir(), is_indexed, false)); m_nil_mod_list->disableInteraction(isRunning()); connect(this, &BaseInstance::runningStatusChanged, m_nil_mod_list.get(), &ModFolderModel::disableInteraction); } diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index f258ad69..2d777656 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -50,7 +50,7 @@ #include "minecraft/mod/tasks/ModFolderLoadTask.h" #include "modplatform/ModIndex.h" -ModFolderModel::ModFolderModel(const QString &dir, bool is_indexed) : ResourceFolderModel(QDir(dir)), m_is_indexed(is_indexed) +ModFolderModel::ModFolderModel(const QString &dir, bool is_indexed, bool create_dir) : ResourceFolderModel(QDir(dir), create_dir), m_is_indexed(is_indexed) { m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER }; } diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 6898f6eb..84e70db9 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -75,7 +75,7 @@ public: Enable, Toggle }; - ModFolderModel(const QString &dir, bool is_indexed = false); + ModFolderModel(const QString &dir, bool is_indexed = false, bool create_dir = true); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index fdfb434b..b8353133 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -12,9 +12,11 @@ #include "tasks/Task.h" -ResourceFolderModel::ResourceFolderModel(QDir dir, QObject* parent) : QAbstractListModel(parent), m_dir(dir), m_watcher(this) +ResourceFolderModel::ResourceFolderModel(QDir dir, bool create_dir, QObject* parent) : QAbstractListModel(parent), m_dir(dir), m_watcher(this) { - FS::ensureFolderPathExists(m_dir.absolutePath()); + if (create_dir) { + FS::ensureFolderPathExists(m_dir.absolutePath()); + } m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware); diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index f1bc2dd7..e4227a33 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -24,7 +24,8 @@ class QSortFilterProxyModel; class ResourceFolderModel : public QAbstractListModel { Q_OBJECT public: - ResourceFolderModel(QDir, QObject* parent = nullptr); + ResourceFolderModel(QDir, bool, QObject* parent = nullptr); + ResourceFolderModel(QDir dir, QObject* parent = nullptr) : ResourceFolderModel(dir, true, parent) {}; ~ResourceFolderModel() override; /** Starts watching the paths for changes. diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 8c87a8a0..4548af59 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -280,5 +280,5 @@ NilModFolderPage::NilModFolderPage(BaseInstance* inst, std::shared_ptrdir().isEmpty(); + return m_model->dir().exists(); } diff --git a/launcher/ui/pages/instance/ModFolderPage.h b/launcher/ui/pages/instance/ModFolderPage.h index cad240bd..2fc7b574 100644 --- a/launcher/ui/pages/instance/ModFolderPage.h +++ b/launcher/ui/pages/instance/ModFolderPage.h @@ -90,7 +90,7 @@ class NilModFolderPage : public ModFolderPage { virtual QString displayName() const override { return tr("Nilmods"); } virtual QIcon icon() const override { return APPLICATION->getThemedIcon("coremods"); } virtual QString id() const override { return "nilmods"; } - virtual QString helpPage() const override { return "Nil-mods"; } + virtual QString helpPage() const override { return "Nilmods"; } virtual bool shouldDisplay() const override; }; -- cgit From 89255e34bd0651ab226518090667bef92066d80c Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Thu, 2 Mar 2023 09:42:28 +0100 Subject: fix: change wording of TP/RP download buttons Signed-off-by: Sefa Eyeoglu --- launcher/ui/pages/instance/ResourcePackPage.cpp | 4 ++-- launcher/ui/pages/instance/TexturePackPage.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/pages/instance/ResourcePackPage.cpp b/launcher/ui/pages/instance/ResourcePackPage.cpp index f46a7939..24bfb38d 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.cpp +++ b/launcher/ui/pages/instance/ResourcePackPage.cpp @@ -46,8 +46,8 @@ ResourcePackPage::ResourcePackPage(MinecraftInstance* instance, std::shared_ptr model, QWidget* parent) : ExternalResourcesPage(instance, model, parent) { - ui->actionDownloadItem->setText(tr("Download RPs")); - ui->actionDownloadItem->setToolTip(tr("Download RPs from online platforms")); + ui->actionDownloadItem->setText(tr("Download packs")); + ui->actionDownloadItem->setToolTip(tr("Download resource packs from online platforms")); ui->actionDownloadItem->setEnabled(true); connect(ui->actionDownloadItem, &QAction::triggered, this, &ResourcePackPage::downloadRPs); ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem); diff --git a/launcher/ui/pages/instance/TexturePackPage.cpp b/launcher/ui/pages/instance/TexturePackPage.cpp index 76fc04e7..427aba11 100644 --- a/launcher/ui/pages/instance/TexturePackPage.cpp +++ b/launcher/ui/pages/instance/TexturePackPage.cpp @@ -48,8 +48,8 @@ TexturePackPage::TexturePackPage(MinecraftInstance* instance, std::shared_ptr model, QWidget* parent) : ExternalResourcesPage(instance, model, parent) { - ui->actionDownloadItem->setText(tr("Download TPs")); - ui->actionDownloadItem->setToolTip(tr("Download TPs from online platforms")); + ui->actionDownloadItem->setText(tr("Download packs")); + ui->actionDownloadItem->setToolTip(tr("Download texture packs from online platforms")); ui->actionDownloadItem->setEnabled(true); connect(ui->actionDownloadItem, &QAction::triggered, this, &TexturePackPage::downloadTPs); ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem); -- cgit From a1053a4c5ac8651be3b57814918e28179eb2a1f9 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 12 Feb 2023 02:44:39 -0700 Subject: feat: warnings when instance resources are linked Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/FileSystem.cpp | 4 +++ launcher/FileSystem.h | 2 ++ launcher/minecraft/World.cpp | 24 +++++++++++++++++ launcher/minecraft/World.h | 15 +++++++++++ launcher/minecraft/WorldList.cpp | 30 ++++++++++++++++++++-- launcher/minecraft/WorldList.h | 5 +++- launcher/minecraft/mod/ModFolderModel.cpp | 20 +++++++++++++++ launcher/minecraft/mod/Resource.cpp | 20 +++++++++++++++ launcher/minecraft/mod/Resource.h | 13 ++++++++++ launcher/minecraft/mod/ResourceFolderModel.cpp | 26 +++++++++++++++++++ launcher/minecraft/mod/ResourceFolderModel.h | 2 ++ launcher/minecraft/mod/ResourcePackFolderModel.cpp | 20 +++++++++++++++ launcher/ui/pages/instance/WorldListPage.cpp | 1 + 13 files changed, 179 insertions(+), 3 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index a9461bb0..af2ba299 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -1641,5 +1641,9 @@ bool canLink(const QString& src, const QString& dst) return canLinkOnFS(src) && canLinkOnFS(dst); } +uintmax_t hardLinkCount(const QString& path) +{ + return fs::hard_link_count(StringUtils::toStdString(path)); +} } diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index cafbd2a8..361993eb 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -528,4 +528,6 @@ bool canLinkOnFS(FilesystemType type); */ bool canLink(const QString& src, const QString& dst); +uintmax_t hardLinkCount(const QString& path); + } diff --git a/launcher/minecraft/World.cpp b/launcher/minecraft/World.cpp index d310f8b9..54fb9434 100644 --- a/launcher/minecraft/World.cpp +++ b/launcher/minecraft/World.cpp @@ -56,6 +56,8 @@ #include +#include "FileSystem.h" + using std::optional; using std::nullopt; @@ -567,3 +569,25 @@ bool World::operator==(const World &other) const { return is_valid == other.is_valid && folderName() == other.folderName(); } + +bool World::isSymLinkUnder(const QString& instPath) const +{ + if (isSymLink()) + return true; + + auto instDir = QDir(instPath); + + auto relAbsPath = instDir.relativeFilePath(m_containerFile.absoluteFilePath()); + auto relCanonPath = instDir.relativeFilePath(m_containerFile.canonicalFilePath()); + + return relAbsPath != relCanonPath; +} + +bool World::isMoreThanOneHardLink() const +{ + if (m_containerFile.isDir()) + { + return FS::hardLinkCount(QDir(m_containerFile.absoluteFilePath()).filePath("level.dat")) > 1; + } + return FS::hardLinkCount(m_containerFile.absoluteFilePath()) > 1; +} diff --git a/launcher/minecraft/World.h b/launcher/minecraft/World.h index 8327253a..10328cce 100644 --- a/launcher/minecraft/World.h +++ b/launcher/minecraft/World.h @@ -95,6 +95,21 @@ public: // WEAK compare operator - used for replacing worlds bool operator==(const World &other) const; + [[nodiscard]] auto isSymLink() const -> bool{ return m_containerFile.isSymLink(); } + + /** + * @brief Take a instance path, checks if the file pointed to by the resource is a symlink or under a symlink in that instance + * + * @param instPath path to an instance directory + * @return true + * @return false + */ + [[nodiscard]] bool isSymLinkUnder(const QString& instPath) const; + + [[nodiscard]] bool isMoreThanOneHardLink() const; + + QString canonicalFilePath() const { return m_containerFile.canonicalFilePath(); } + private: void readFromZip(const QFileInfo &file); void readFromFS(const QFileInfo &file); diff --git a/launcher/minecraft/WorldList.cpp b/launcher/minecraft/WorldList.cpp index ae29a972..1262fa1d 100644 --- a/launcher/minecraft/WorldList.cpp +++ b/launcher/minecraft/WorldList.cpp @@ -128,6 +128,10 @@ bool WorldList::isValid() return m_dir.exists() && m_dir.isReadable(); } +QString WorldList::instDirPath() const { + return QFileInfo(m_dir.filePath("../..")).absoluteFilePath(); +} + bool WorldList::deleteWorld(int index) { if (index >= worlds.size() || index < 0) @@ -173,7 +177,7 @@ bool WorldList::resetIcon(int row) int WorldList::columnCount(const QModelIndex &parent) const { - return parent.isValid()? 0 : 4; + return parent.isValid()? 0 : 5; } QVariant WorldList::data(const QModelIndex &index, int role) const @@ -207,6 +211,14 @@ QVariant WorldList::data(const QModelIndex &index, int role) const case SizeColumn: return locale.formattedDataSize(world.bytes()); + case InfoColumn: + if (world.isSymLinkUnder(instDirPath())) { + return tr("This world is symbolicly linked from elsewhere."); + } + if (world.isMoreThanOneHardLink()) { + return tr("\nThis world is hard linked elsewhere."); + } + return ""; default: return QVariant(); } @@ -222,7 +234,16 @@ QVariant WorldList::data(const QModelIndex &index, int role) const } case Qt::ToolTipRole: - { + { + if (column == InfoColumn) { + if (world.isSymLinkUnder(instDirPath())) { + return tr("Warning: This world is symbolicly linked from elsewhere. Editing it will also change the origonal") + + tr("\nCanonical Path: %1").arg(world.canonicalFilePath()); + } + if (world.isMoreThanOneHardLink()) { + return tr("Warning: This world is hard linked elsewhere. Editing it will also change the origonal"); + } + } return world.folderName(); } case ObjectRole: @@ -274,6 +295,9 @@ QVariant WorldList::headerData(int section, Qt::Orientation orientation, int rol case SizeColumn: //: World size on disk return tr("Size"); + case InfoColumn: + //: special warnings? + return tr("Info"); default: return QVariant(); } @@ -289,6 +313,8 @@ QVariant WorldList::headerData(int section, Qt::Orientation orientation, int rol return tr("Date and time the world was last played."); case SizeColumn: return tr("Size of the world on disk."); + case InfoColumn: + return tr("Information and warnings about the world."); default: return QVariant(); } diff --git a/launcher/minecraft/WorldList.h b/launcher/minecraft/WorldList.h index 08294755..bd32dd4e 100644 --- a/launcher/minecraft/WorldList.h +++ b/launcher/minecraft/WorldList.h @@ -33,7 +33,8 @@ public: NameColumn, GameModeColumn, LastPlayedColumn, - SizeColumn + SizeColumn, + InfoColumn }; enum Roles @@ -112,6 +113,8 @@ public: return m_dir; } + QString instDirPath() const; + const QList &allWorlds() const { return worlds; diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 3f31b93c..e2053f92 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -39,13 +39,17 @@ #include #include #include +#include #include #include +#include #include #include #include #include +#include "Application.h" + #include "minecraft/mod/tasks/LocalModParseTask.h" #include "minecraft/mod/tasks/ModFolderLoadTask.h" #include "modplatform/ModIndex.h" @@ -97,8 +101,24 @@ QVariant ModFolderModel::data(const QModelIndex &index, int role) const } case Qt::ToolTipRole: + if (column == NAME_COLUMN) { + if (at(row)->isSymLinkUnder(instDirPath())) { + return m_resources[row]->internal_id() + + tr("\nWarning: This resource is symbolicly linked from elsewhere. Editing it will also change the origonal") + + tr("\nCanonical Path: %1").arg(at(row)->fileinfo().canonicalFilePath()); + } + if (at(row)->isMoreThanOneHardLink()) { + return m_resources[row]->internal_id() + + tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the origonal"); + } + } return m_resources[row]->internal_id(); + case Qt::DecorationRole: { + if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink())) + return APPLICATION->getThemedIcon("status-yellow"); + return {}; + } case Qt::CheckStateRole: switch (column) { diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index 0d35d755..a0b8a4bb 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -1,6 +1,8 @@ #include "Resource.h" + #include +#include #include "FileSystem.h" @@ -152,3 +154,21 @@ bool Resource::destroy() return FS::deletePath(m_file_info.filePath()); } + +bool Resource::isSymLinkUnder(const QString& instPath) const +{ + if (isSymLink()) + return true; + + auto instDir = QDir(instPath); + + auto relAbsPath = instDir.relativeFilePath(m_file_info.absoluteFilePath()); + auto relCanonPath = instDir.relativeFilePath(m_file_info.canonicalFilePath()); + + return relAbsPath != relCanonPath; +} + +bool Resource::isMoreThanOneHardLink() const +{ + return FS::hardLinkCount(m_file_info.absoluteFilePath()) > 1; +} diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index 0c37f3a3..a5e9ae91 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -94,6 +94,19 @@ class Resource : public QObject { // Delete all files of this resource. bool destroy(); + [[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); } + + /** + * @brief Take a instance path, checks if the file pointed to by the resource is a symlink or under a symlink in that instance + * + * @param instPath path to an instance directory + * @return true + * @return false + */ + [[nodiscard]] bool isSymLinkUnder(const QString& instPath) const; + + [[nodiscard]] bool isMoreThanOneHardLink() const; + protected: /* The file corresponding to this resource. */ QFileInfo m_file_info; diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index f2a77c12..d1748c1c 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -2,10 +2,14 @@ #include #include +#include +#include #include +#include #include #include +#include "Application.h" #include "FileSystem.h" #include "minecraft/mod/tasks/BasicFolderLoadTask.h" @@ -417,7 +421,25 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const return {}; } case Qt::ToolTipRole: + if (column == NAME_COLUMN) { + if (at(row).isSymLinkUnder(instDirPath())) { + return m_resources[row]->internal_id() + + tr("\nWarning: This resource is symbolicly linked from elsewhere. Editing it will also change the origonal") + + tr("\nCanonical Path: %1").arg(at(row).fileinfo().canonicalFilePath());; + } + if (at(row).isMoreThanOneHardLink()) { + return m_resources[row]->internal_id() + + tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the origonal"); + } + } + return m_resources[row]->internal_id(); + case Qt::DecorationRole: { + if (column == NAME_COLUMN && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink())) + return APPLICATION->getThemedIcon("status-yellow"); + + return {}; + } case Qt::CheckStateRole: switch (column) { case ACTIVE_COLUMN: @@ -531,3 +553,7 @@ void ResourceFolderModel::enableInteraction(bool enabled) return (compare_result.first < 0); return (compare_result.first > 0); } + +QString ResourceFolderModel::instDirPath() const { + return QFileInfo(m_dir.filePath("../..")).absoluteFilePath(); +} diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 3bd78870..f840b2de 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -125,6 +125,8 @@ class ResourceFolderModel : public QAbstractListModel { [[nodiscard]] bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override; }; + QString instDirPath() const; + public slots: void enableInteraction(bool enabled); void disableInteraction(bool disabled) { enableInteraction(!disabled); } diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index da4bd091..56584a34 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -36,6 +36,10 @@ #include "ResourcePackFolderModel.h" +#include +#include + +#include "Application.h" #include "Version.h" #include "minecraft/mod/tasks/BasicFolderLoadTask.h" @@ -78,12 +82,28 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const default: return {}; } + case Qt::DecorationRole: { + if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink())) + return APPLICATION->getThemedIcon("status-yellow"); + return {}; + } case Qt::ToolTipRole: { if (column == PackFormatColumn) { //: The string being explained by this is in the format: ID (Lower version - Upper version) return tr("The resource pack format ID, as well as the Minecraft versions it was designed for."); } + if (column == NAME_COLUMN) { + if (at(row)->isSymLinkUnder(instDirPath())) { + return m_resources[row]->internal_id() + + tr("\nWarning: This resource is symbolicly linked from elsewhere. Editing it will also change the origonal") + + tr("\nCanonical Path: %1").arg(at(row)->fileinfo().canonicalFilePath());; + } + if (at(row)->isMoreThanOneHardLink()) { + return m_resources[row]->internal_id() + + tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the origonal"); + } + } return m_resources[row]->internal_id(); } case Qt::CheckStateRole: diff --git a/launcher/ui/pages/instance/WorldListPage.cpp b/launcher/ui/pages/instance/WorldListPage.cpp index d4a395d9..b6ad159e 100644 --- a/launcher/ui/pages/instance/WorldListPage.cpp +++ b/launcher/ui/pages/instance/WorldListPage.cpp @@ -107,6 +107,7 @@ WorldListPage::WorldListPage(BaseInstance *inst, std::shared_ptr worl auto head = ui->worldTreeView->header(); head->setSectionResizeMode(0, QHeaderView::Stretch); head->setSectionResizeMode(1, QHeaderView::ResizeToContents); + head->setSectionResizeMode(4, QHeaderView::ResizeToContents); connect(ui->worldTreeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &WorldListPage::worldChanged); worldChanged(QModelIndex(), QModelIndex()); -- cgit From 12f0d51c0cd03d660425566264b502736b104310 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Mon, 17 Apr 2023 17:51:34 -0700 Subject: Fix: signal/slot macro -> func pointer & network fixes - convert qt connect calls to use function pointers instead of the signal/slot macros wherever practical (UI classes were mostly left alone, target was tasks and processes) - give signals an explicit receivers to use the static method over the instance method wherever practical - ensure networks tasks are using the `errorOccured` signal added in Qt5.15 over the deprecated `error` signal - ensure all networks tasks have an sslErrors signal connected - add seemingly missing `MinecraftAccount::authSucceeded` connection for `MSAInteractive` login flow Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/JavaCommon.cpp | 6 ++--- launcher/LoggedProcess.cpp | 8 +++---- launcher/icons/IconList.cpp | 5 ++--- launcher/java/JavaChecker.cpp | 14 ++++++------ launcher/java/JavaCheckerJob.cpp | 2 +- launcher/launch/steps/Update.cpp | 6 ++--- launcher/minecraft/WorldList.cpp | 3 +-- launcher/minecraft/auth/AuthRequest.cpp | 22 +++++++++---------- launcher/minecraft/auth/MinecraftAccount.cpp | 16 +++++++------- launcher/minecraft/services/CapeChange.cpp | 33 +++++++++++++++++++++------- launcher/minecraft/services/CapeChange.h | 1 + launcher/minecraft/services/SkinDelete.cpp | 22 ++++++++++++++----- launcher/minecraft/services/SkinDelete.h | 1 + launcher/minecraft/services/SkinUpload.cpp | 22 ++++++++++++++----- launcher/minecraft/services/SkinUpload.h | 1 + launcher/net/Download.cpp | 6 ++--- launcher/net/Download.h | 2 +- launcher/net/HttpMetaCache.cpp | 2 +- launcher/net/NetAction.h | 11 ++++++++++ launcher/net/Upload.cpp | 10 ++++++--- launcher/net/Upload.h | 2 +- launcher/screenshots/ImgurAlbumCreation.cpp | 10 ++++++--- launcher/screenshots/ImgurUpload.cpp | 10 ++++++--- launcher/settings/SettingsObject.cpp | 9 ++++---- launcher/tools/JProfiler.cpp | 4 ++-- launcher/tools/JVisualVM.cpp | 4 ++-- launcher/ui/pages/instance/VersionPage.cpp | 2 +- 27 files changed, 148 insertions(+), 86 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/JavaCommon.cpp b/launcher/JavaCommon.cpp index 52cc868a..e29e2270 100644 --- a/launcher/JavaCommon.cpp +++ b/launcher/JavaCommon.cpp @@ -122,8 +122,7 @@ void JavaCommon::TestCheck::run() return; } checker.reset(new JavaChecker()); - connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, - SLOT(checkFinished(JavaCheckResult))); + connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinished); checker->m_path = m_path; checker->performCheck(); } @@ -137,8 +136,7 @@ void JavaCommon::TestCheck::checkFinished(JavaCheckResult result) return; } checker.reset(new JavaChecker()); - connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, - SLOT(checkFinishedWithArgs(JavaCheckResult))); + connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinishedWithArgs); checker->m_path = m_path; checker->m_args = m_args; checker->m_minMem = m_minMem; diff --git a/launcher/LoggedProcess.cpp b/launcher/LoggedProcess.cpp index c8d5c34e..763a9b5c 100644 --- a/launcher/LoggedProcess.cpp +++ b/launcher/LoggedProcess.cpp @@ -44,11 +44,11 @@ LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent) // QProcess has a strange interface... let's map a lot of those into a few. connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut); connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr); - connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(on_exit(int,QProcess::ExitStatus))); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - connect(this, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError))); + connect(this, QOverload::of(&QProcess::finished), this, &LoggedProcess::on_exit); +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) // &QProcess::errorOccurred added in 5.6 + connect(this, &QProcess::errorOccurred, this, &LoggedProcess::on_error); #else - connect(this, SIGNAL(error(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError))); + connect(this, QOverload::of(&QProcess::error), this, &LoggedProcess::on_error); #endif connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange); } diff --git a/launcher/icons/IconList.cpp b/launcher/icons/IconList.cpp index 1dfc6432..13174f6e 100644 --- a/launcher/icons/IconList.cpp +++ b/launcher/icons/IconList.cpp @@ -66,9 +66,8 @@ IconList::IconList(const QStringList &builtinPaths, QString path, QObject *paren m_watcher.reset(new QFileSystemWatcher()); is_watching = false; - connect(m_watcher.get(), SIGNAL(directoryChanged(QString)), - SLOT(directoryChanged(QString))); - connect(m_watcher.get(), SIGNAL(fileChanged(QString)), SLOT(fileChanged(QString))); + connect(m_watcher.get(), &QFileSystemWatcher::directoryChanged, this, &IconList::directoryChanged); + connect(m_watcher.get(), &QFileSystemWatcher::fileChanged, this, &IconList::fileChanged); directoryChanged(path); diff --git a/launcher/java/JavaChecker.cpp b/launcher/java/JavaChecker.cpp index 041583d1..922580ce 100644 --- a/launcher/java/JavaChecker.cpp +++ b/launcher/java/JavaChecker.cpp @@ -87,15 +87,15 @@ void JavaChecker::performCheck() process->setProcessEnvironment(CleanEnviroment()); qDebug() << "Running java checker: " + m_path + args.join(" ");; - connect(process.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus))); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - connect(process.get(), SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError))); + connect(process.get(), QOverload::of(&QProcess::finished), this, &JavaChecker::finished); +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) // &QProcess::errorOccurred added in 5.6 + connect(process.get(), &QProcess::errorOccurred, this, &JavaChecker::error); #else - connect(process.get(), SIGNAL(error(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError))); + connect(process.get(), &QProcess::error, this, &JavaChecker::error); #endif - connect(process.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(stdoutReady())); - connect(process.get(), SIGNAL(readyReadStandardError()), this, SLOT(stderrReady())); - connect(&killTimer, SIGNAL(timeout()), SLOT(timeout())); + connect(process.get(), &QProcess::readyReadStandardOutput, this, &JavaChecker::stdoutReady); + connect(process.get(), &QProcess::readyReadStandardError, this, &JavaChecker::stderrReady); + connect(&killTimer, &QTimer::timeout, this, &JavaChecker::timeout); killTimer.setSingleShot(true); killTimer.start(15000); process->start(); diff --git a/launcher/java/JavaCheckerJob.cpp b/launcher/java/JavaCheckerJob.cpp index 67d70066..48274974 100644 --- a/launcher/java/JavaCheckerJob.cpp +++ b/launcher/java/JavaCheckerJob.cpp @@ -38,7 +38,7 @@ void JavaCheckerJob::executeTask() for (auto iter : javacheckers) { javaresults.append(JavaCheckResult()); - connect(iter.get(), SIGNAL(checkFinished(JavaCheckResult)), SLOT(partFinished(JavaCheckResult))); + connect(iter.get(), &JavaChecker::checkFinished, this, &JavaCheckerJob::partFinished); iter->performCheck(); } } diff --git a/launcher/launch/steps/Update.cpp b/launcher/launch/steps/Update.cpp index 28bd153d..b67316b0 100644 --- a/launcher/launch/steps/Update.cpp +++ b/launcher/launch/steps/Update.cpp @@ -26,9 +26,9 @@ void Update::executeTask() m_updateTask.reset(m_parent->instance()->createUpdateTask(m_mode)); if(m_updateTask) { - connect(m_updateTask.get(), SIGNAL(finished()), this, SLOT(updateFinished())); - connect(m_updateTask.get(), &Task::progress, this, &Task::setProgress); - connect(m_updateTask.get(), &Task::status, this, &Task::setStatus); + connect(m_updateTask.get(), &Task::finished, this, &Update::updateFinished); + connect(m_updateTask.get(), &Task::progress, this, &Update::setProgress); + connect(m_updateTask.get(), &Task::status, this, &Update::setStatus); emit progressReportingRequest(); return; } diff --git a/launcher/minecraft/WorldList.cpp b/launcher/minecraft/WorldList.cpp index ae29a972..de21c474 100644 --- a/launcher/minecraft/WorldList.cpp +++ b/launcher/minecraft/WorldList.cpp @@ -53,8 +53,7 @@ WorldList::WorldList(const QString &dir) m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware); m_watcher = new QFileSystemWatcher(this); is_watching = false; - connect(m_watcher, SIGNAL(directoryChanged(QString)), this, - SLOT(directoryChanged(QString))); + connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &WorldList::directoryChanged); } void WorldList::startWatching() diff --git a/launcher/minecraft/auth/AuthRequest.cpp b/launcher/minecraft/auth/AuthRequest.cpp index bb82e1e2..a21634b7 100644 --- a/launcher/minecraft/auth/AuthRequest.cpp +++ b/launcher/minecraft/auth/AuthRequest.cpp @@ -55,12 +55,12 @@ void AuthRequest::get(const QNetworkRequest &req, int timeout/* = 60*1000*/) { reply_ = APPLICATION->network()->get(request_); status_ = Requesting; timedReplies_.add(new Katabasis::Reply(reply_, timeout)); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - connect(reply_, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError))); -#else - connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError))); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 + connect(reply_, &QNetworkReply::errorOccurred, this, &AuthRequest::onRequestError); +#else // &QNetworkReply::error SIGNAL depricated + connect(reply_, QOverload::of(&QNetworkReply::error), this, &AuthRequest::onRequestError); #endif - connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished())); + connect(reply_, &QNetworkReply::finished, this, &AuthRequest::onRequestFinished); connect(reply_, &QNetworkReply::sslErrors, this, &AuthRequest::onSslErrors); } @@ -70,14 +70,14 @@ void AuthRequest::post(const QNetworkRequest &req, const QByteArray &data, int t status_ = Requesting; reply_ = APPLICATION->network()->post(request_, data_); timedReplies_.add(new Katabasis::Reply(reply_, timeout)); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - connect(reply_, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError))); -#else - connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError))); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 + connect(reply_, &QNetworkReply::errorOccurred, this, &AuthRequest::onRequestError); +#else // &QNetworkReply::error SIGNAL depricated + connect(reply_, QOverload::of(&QNetworkReply::error), this, &AuthRequest::onRequestError); #endif - connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished())); + connect(reply_, &QNetworkReply::finished, this, &AuthRequest::onRequestFinished); connect(reply_, &QNetworkReply::sslErrors, this, &AuthRequest::onSslErrors); - connect(reply_, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(onUploadProgress(qint64,qint64))); + connect(reply_, &QNetworkReply::uploadProgress, this, &AuthRequest::onUploadProgress); } void AuthRequest::onRequestFinished() { diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index 48cf5d42..3b050ac0 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -133,8 +133,8 @@ shared_qobject_ptr MinecraftAccount::login(QString password) { Q_ASSERT(m_currentTask.get() == nullptr); m_currentTask.reset(new MojangLogin(&data, password)); - connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); - connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); + connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); + connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; @@ -144,8 +144,8 @@ shared_qobject_ptr MinecraftAccount::loginMSA() { Q_ASSERT(m_currentTask.get() == nullptr); m_currentTask.reset(new MSAInteractive(&data)); - connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); - connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); + connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); + connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; @@ -155,8 +155,8 @@ shared_qobject_ptr MinecraftAccount::loginOffline() { Q_ASSERT(m_currentTask.get() == nullptr); m_currentTask.reset(new OfflineLogin(&data)); - connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); - connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); + connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); + connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; @@ -177,8 +177,8 @@ shared_qobject_ptr MinecraftAccount::refresh() { m_currentTask.reset(new MojangRefresh(&data)); } - connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); - connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); + connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); + connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; diff --git a/launcher/minecraft/services/CapeChange.cpp b/launcher/minecraft/services/CapeChange.cpp index c73a11b6..1d5ea36d 100644 --- a/launcher/minecraft/services/CapeChange.cpp +++ b/launcher/minecraft/services/CapeChange.cpp @@ -54,9 +54,14 @@ void CapeChange::setCape(QString& cape) { setStatus(tr("Equipping cape")); m_reply = shared_qobject_ptr(rep); - connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress); - connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError))); - connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished())); + connect(rep, &QNetworkReply::uploadProgress, this, &CapeChange::setProgress); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 + connect(rep, &QNetworkReply::errorOccurred, this, &CapeChange::downloadError); +#else + connect(rep, QOverload::of(&QNetworkReply::error), this, &CapeChange::downloadError); +#endif + connect(rep, &QNetworkReply::sslErrors, this, &CapeChange::sslErrors); + connect(rep, &QNetworkReply::finished, this, &CapeChange::downloadFinished); } void CapeChange::clearCape() { @@ -68,13 +73,14 @@ void CapeChange::clearCape() { setStatus(tr("Removing cape")); m_reply = shared_qobject_ptr(rep); - connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - connect(rep, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError))); + connect(rep, &QNetworkReply::uploadProgress, this, &CapeChange::setProgress); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 + connect(rep, &QNetworkReply::errorOccurred, this, &CapeChange::downloadError); #else - connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError))); + connect(rep, QOverload::of(&QNetworkReply::error), this, &CapeChange::downloadError); #endif - connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished())); + connect(rep, &QNetworkReply::sslErrors, this, &CapeChange::sslErrors); + connect(rep, &QNetworkReply::finished, this, &CapeChange::downloadFinished); } @@ -95,6 +101,17 @@ void CapeChange::downloadError(QNetworkReply::NetworkError error) emitFailed(m_reply->errorString()); } +void CapeChange::sslErrors(const QList& errors) +{ + int i = 1; + for (auto error : errors) { + qCritical() << "Cape change SSL Error #" << i << " : " << error.errorString(); + auto cert = error.certificate(); + qCritical() << "Certificate in question:\n" << cert.toText(); + i++; + } +} + void CapeChange::downloadFinished() { // if the download failed diff --git a/launcher/minecraft/services/CapeChange.h b/launcher/minecraft/services/CapeChange.h index 185d69b6..38069f90 100644 --- a/launcher/minecraft/services/CapeChange.h +++ b/launcher/minecraft/services/CapeChange.h @@ -27,6 +27,7 @@ protected: public slots: void downloadError(QNetworkReply::NetworkError); + void sslErrors(const QList& errors); void downloadFinished(); }; diff --git a/launcher/minecraft/services/SkinDelete.cpp b/launcher/minecraft/services/SkinDelete.cpp index 921bd094..fbaaeacb 100644 --- a/launcher/minecraft/services/SkinDelete.cpp +++ b/launcher/minecraft/services/SkinDelete.cpp @@ -53,13 +53,14 @@ void SkinDelete::executeTask() m_reply = shared_qobject_ptr(rep); setStatus(tr("Deleting skin")); - connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - connect(rep, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError))); + connect(rep, &QNetworkReply::uploadProgress, this, &SkinDelete::setProgress); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 + connect(rep, &QNetworkReply::errorOccurred, this, &SkinDelete::downloadError); #else - connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError))); + connect(rep, QOverload::of(&QNetworkReply::error), this, &SkinDelete::downloadError); #endif - connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished())); + connect(rep, &QNetworkReply::sslErrors, this, &SkinDelete::sslErrors); + connect(rep, &QNetworkReply::finished, this, &SkinDelete::downloadFinished); } void SkinDelete::downloadError(QNetworkReply::NetworkError error) @@ -69,6 +70,17 @@ void SkinDelete::downloadError(QNetworkReply::NetworkError error) emitFailed(m_reply->errorString()); } +void SkinDelete::sslErrors(const QList& errors) +{ + int i = 1; + for (auto error : errors) { + qCritical() << "Skin Delete SSL Error #" << i << " : " << error.errorString(); + auto cert = error.certificate(); + qCritical() << "Certificate in question:\n" << cert.toText(); + i++; + } +} + void SkinDelete::downloadFinished() { // if the download failed diff --git a/launcher/minecraft/services/SkinDelete.h b/launcher/minecraft/services/SkinDelete.h index 83a84685..b9a1c9d3 100644 --- a/launcher/minecraft/services/SkinDelete.h +++ b/launcher/minecraft/services/SkinDelete.h @@ -22,5 +22,6 @@ protected: public slots: void downloadError(QNetworkReply::NetworkError); + void sslErrors(const QList& errors); void downloadFinished(); }; diff --git a/launcher/minecraft/services/SkinUpload.cpp b/launcher/minecraft/services/SkinUpload.cpp index c7987875..711f8739 100644 --- a/launcher/minecraft/services/SkinUpload.cpp +++ b/launcher/minecraft/services/SkinUpload.cpp @@ -78,13 +78,14 @@ void SkinUpload::executeTask() m_reply = shared_qobject_ptr(rep); setStatus(tr("Uploading skin")); - connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - connect(rep, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError))); + connect(rep, &QNetworkReply::uploadProgress, this, &SkinUpload::setProgress); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 + connect(rep, &QNetworkReply::errorOccurred, this, &SkinUpload::downloadError); #else - connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError))); + connect(rep, QOverload::of(&QNetworkReply::error), this, &SkinUpload::downloadError); #endif - connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished())); + connect(rep, &QNetworkReply::sslErrors, this, &SkinUpload::sslErrors); + connect(rep, &QNetworkReply::finished, this, &SkinUpload::downloadFinished); } void SkinUpload::downloadError(QNetworkReply::NetworkError error) @@ -94,6 +95,17 @@ void SkinUpload::downloadError(QNetworkReply::NetworkError error) emitFailed(m_reply->errorString()); } +void SkinUpload::sslErrors(const QList& errors) +{ + int i = 1; + for (auto error : errors) { + qCritical() << "Skin Upload SSL Error #" << i << " : " << error.errorString(); + auto cert = error.certificate(); + qCritical() << "Certificate in question:\n" << cert.toText(); + i++; + } +} + void SkinUpload::downloadFinished() { // if the download failed diff --git a/launcher/minecraft/services/SkinUpload.h b/launcher/minecraft/services/SkinUpload.h index 2c1f0a2e..ac8c5b36 100644 --- a/launcher/minecraft/services/SkinUpload.h +++ b/launcher/minecraft/services/SkinUpload.h @@ -32,6 +32,7 @@ protected: public slots: void downloadError(QNetworkReply::NetworkError); + void sslErrors(const QList& errors); void downloadFinished(); }; diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp index e8a1d0b0..30c1953f 100644 --- a/launcher/net/Download.cpp +++ b/launcher/net/Download.cpp @@ -129,10 +129,10 @@ void Download::executeTask() m_reply.reset(rep); connect(rep, &QNetworkReply::downloadProgress, this, &Download::downloadProgress); connect(rep, &QNetworkReply::finished, this, &Download::downloadFinished); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - connect(rep, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError))); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 + connect(rep, &QNetworkReply::errorOccurred, this, &Download::downloadError); #else - connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError))); + connect(rep, QOverload::of(&QNetworkReply::error), this, &Download::downloadError); #endif connect(rep, &QNetworkReply::sslErrors, this, &Download::sslErrors); connect(rep, &QNetworkReply::readyRead, this, &Download::downloadReadyRead); diff --git a/launcher/net/Download.h b/launcher/net/Download.h index 7e1df322..01ec46db 100644 --- a/launcher/net/Download.h +++ b/launcher/net/Download.h @@ -70,7 +70,7 @@ class Download : public NetAction { protected slots: void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; void downloadError(QNetworkReply::NetworkError error) override; - void sslErrors(const QList& errors); + void sslErrors(const QList& errors) override; void downloadFinished() override; void downloadReadyRead() override; diff --git a/launcher/net/HttpMetaCache.cpp b/launcher/net/HttpMetaCache.cpp index 0d7ca769..0ec82251 100644 --- a/launcher/net/HttpMetaCache.cpp +++ b/launcher/net/HttpMetaCache.cpp @@ -55,7 +55,7 @@ HttpMetaCache::HttpMetaCache(QString path) : QObject(), m_index_file(path) saveBatchingTimer.setSingleShot(true); saveBatchingTimer.setTimerType(Qt::VeryCoarseTimer); - connect(&saveBatchingTimer, SIGNAL(timeout()), SLOT(SaveNow())); + connect(&saveBatchingTimer, &QTimer::timeout, this, &HttpMetaCache::SaveNow); } HttpMetaCache::~HttpMetaCache() diff --git a/launcher/net/NetAction.h b/launcher/net/NetAction.h index 38fe058b..1ff8f601 100644 --- a/launcher/net/NetAction.h +++ b/launcher/net/NetAction.h @@ -61,6 +61,17 @@ class NetAction : public Task { virtual void downloadFinished() = 0; virtual void downloadReadyRead() = 0; + virtual void sslErrors(const QList& errors) { + int i = 1; + for (auto error : errors) { + qCritical() << "Network SSL Error #" << i << " : " << error.errorString(); + auto cert = error.certificate(); + qCritical() << "Certificate in question:\n" << cert.toText(); + i++; + } + + }; + public slots: void startAction(shared_qobject_ptr network) { diff --git a/launcher/net/Upload.cpp b/launcher/net/Upload.cpp index ccf43c2d..f3cdb786 100644 --- a/launcher/net/Upload.cpp +++ b/launcher/net/Upload.cpp @@ -232,9 +232,13 @@ namespace Net { QNetworkReply* rep = m_network->post(request, m_post_data); m_reply.reset(rep); - connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SLOT(downloadProgress(qint64, qint64))); - connect(rep, SIGNAL(finished()), SLOT(downloadFinished())); - connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError))); + connect(rep, &QNetworkReply::downloadProgress, this, &Upload::downloadProgress); + connect(rep, &QNetworkReply::finished, this, &Upload::downloadFinished); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 + connect(rep, &QNetworkReply::errorOccurred, this, &Upload::downloadError); +#else + connect(rep, QOverload::of(&QNetworkReply::error), this, &Upload::downloadError); +#endif connect(rep, &QNetworkReply::sslErrors, this, &Upload::sslErrors); connect(rep, &QNetworkReply::readyRead, this, &Upload::downloadReadyRead); } diff --git a/launcher/net/Upload.h b/launcher/net/Upload.h index 5a0b2e74..f58b746a 100644 --- a/launcher/net/Upload.h +++ b/launcher/net/Upload.h @@ -54,7 +54,7 @@ namespace Net { protected slots: void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; void downloadError(QNetworkReply::NetworkError error) override; - void sslErrors(const QList & errors); + void sslErrors(const QList & errors) override; void downloadFinished() override; void downloadReadyRead() override; diff --git a/launcher/screenshots/ImgurAlbumCreation.cpp b/launcher/screenshots/ImgurAlbumCreation.cpp index a72c32d3..ab425f1a 100644 --- a/launcher/screenshots/ImgurAlbumCreation.cpp +++ b/launcher/screenshots/ImgurAlbumCreation.cpp @@ -74,17 +74,20 @@ void ImgurAlbumCreation::executeTask() m_reply.reset(rep); connect(rep, &QNetworkReply::uploadProgress, this, &ImgurAlbumCreation::downloadProgress); connect(rep, &QNetworkReply::finished, this, &ImgurAlbumCreation::downloadFinished); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - connect(rep, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError))); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 + connect(rep, &QNetworkReply::errorOccurred, this, &ImgurAlbumCreation::downloadError); #else - connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError))); + connect(rep, QOverload::of(&QNetworkReply::error), this, &ImgurAlbumCreation::downloadError); #endif + connect(rep, &QNetworkReply::sslErrors, this, &ImgurAlbumCreation::sslErrors); } + void ImgurAlbumCreation::downloadError(QNetworkReply::NetworkError error) { qDebug() << m_reply->errorString(); m_state = State::Failed; } + void ImgurAlbumCreation::downloadFinished() { if (m_state != State::Failed) @@ -120,6 +123,7 @@ void ImgurAlbumCreation::downloadFinished() return; } } + void ImgurAlbumCreation::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { setProgress(bytesReceived, bytesTotal); diff --git a/launcher/screenshots/ImgurUpload.cpp b/launcher/screenshots/ImgurUpload.cpp index f8ac9bc2..a50f9afa 100644 --- a/launcher/screenshots/ImgurUpload.cpp +++ b/launcher/screenshots/ImgurUpload.cpp @@ -89,12 +89,14 @@ void ImgurUpload::executeTask() m_reply.reset(rep); connect(rep, &QNetworkReply::uploadProgress, this, &ImgurUpload::downloadProgress); connect(rep, &QNetworkReply::finished, this, &ImgurUpload::downloadFinished); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - connect(rep, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError))); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 + connect(rep, &QNetworkReply::errorOccurred, this, &ImgurUpload::downloadError); #else - connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError))); + connect(rep, QOverload::of(&QNetworkReply::error), this, &ImgurUpload::downloadError); #endif + connect(rep, &QNetworkReply::sslErrors, this, &ImgurUpload::sslErrors); } + void ImgurUpload::downloadError(QNetworkReply::NetworkError error) { qCritical() << "ImgurUpload failed with error" << m_reply->errorString() << "Server reply:\n" << m_reply->readAll(); @@ -108,6 +110,7 @@ void ImgurUpload::downloadError(QNetworkReply::NetworkError error) m_reply.reset(); emitFailed(); } + void ImgurUpload::downloadFinished() { if(finished) @@ -144,6 +147,7 @@ void ImgurUpload::downloadFinished() emit succeeded(); return; } + void ImgurUpload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { setProgress(bytesReceived, bytesTotal); diff --git a/launcher/settings/SettingsObject.cpp b/launcher/settings/SettingsObject.cpp index 8a0bc045..634acd34 100644 --- a/launcher/settings/SettingsObject.cpp +++ b/launcher/settings/SettingsObject.cpp @@ -132,11 +132,10 @@ bool SettingsObject::reload() void SettingsObject::connectSignals(const Setting &setting) { - connect(&setting, SIGNAL(SettingChanged(const Setting &, QVariant)), - SLOT(changeSetting(const Setting &, QVariant))); - connect(&setting, SIGNAL(SettingChanged(const Setting &, QVariant)), + connect(&setting, &Setting::SettingChanged, this, &SettingsObject::changeSetting); + connect(&setting, SIGNAL(SettingChanged(const Setting &, QVariant)), this, SIGNAL(SettingChanged(const Setting &, QVariant))); - connect(&setting, SIGNAL(settingReset(Setting)), SLOT(resetSetting(const Setting &))); - connect(&setting, SIGNAL(settingReset(Setting)), SIGNAL(settingReset(const Setting &))); + connect(&setting, &Setting::settingReset, this, &SettingsObject::resetSetting); + connect(&setting, SIGNAL(settingReset(Setting)), this, SIGNAL(settingReset(const Setting &))); } diff --git a/launcher/tools/JProfiler.cpp b/launcher/tools/JProfiler.cpp index 1dc0d109..15c0cab6 100644 --- a/launcher/tools/JProfiler.cpp +++ b/launcher/tools/JProfiler.cpp @@ -68,8 +68,8 @@ void JProfiler::beginProfilingImpl(shared_qobject_ptr process) profiler->setArguments(profilerArgs); profiler->setProgram(profilerProgram); - connect(profiler, SIGNAL(started()), SLOT(profilerStarted())); - connect(profiler, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(profilerFinished(int,QProcess::ExitStatus))); + connect(profiler, &QProcess::started, this, &JProfiler::profilerStarted); + connect(profiler, QOverload::of(&QProcess::finished), this, &JProfiler::profilerFinished); m_profilerProcess = profiler; profiler->start(); diff --git a/launcher/tools/JVisualVM.cpp b/launcher/tools/JVisualVM.cpp index b1acc3c0..28ffb9cd 100644 --- a/launcher/tools/JVisualVM.cpp +++ b/launcher/tools/JVisualVM.cpp @@ -57,8 +57,8 @@ void JVisualVM::beginProfilingImpl(shared_qobject_ptr process) profiler->setArguments(profilerArgs); profiler->setProgram(programPath); - connect(profiler, SIGNAL(started()), SLOT(profilerStarted())); - connect(profiler, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(profilerFinished(int,QProcess::ExitStatus))); + connect(profiler, &QProcess::started, this, &JVisualVM::profilerStarted); + connect(profiler, QOverload::of(&QProcess::finished), this, &JVisualVM::profilerFinished); profiler->start(); m_profilerProcess = profiler; diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index 7fff3b93..fffb96f2 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -501,7 +501,7 @@ void VersionPage::on_actionDownload_All_triggered() return; } ProgressDialog tDialog(this); - connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString))); + connect(updateTask.get(), &Task::failed, this, &VersionPage::onGameUpdateError); // FIXME: unused return value tDialog.execWithTask(updateTask.get()); updateButtons(); -- cgit From 6b8fe283f0bda66806175de10ba5874a4afdae45 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 21 May 2023 01:49:13 -0700 Subject: fix: memory leak, set parent so it's in tree to get cleaned up. Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/ui/pages/instance/VersionPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index fffb96f2..74b7ec7c 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -165,7 +165,7 @@ VersionPage::VersionPage(MinecraftInstance *inst, QWidget *parent) auto proxy = new IconProxy(ui->packageView); proxy->setSourceModel(m_profile.get()); - m_filterModel = new QSortFilterProxyModel(); + m_filterModel = new QSortFilterProxyModel(this); m_filterModel->setDynamicSortFilter(true); m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive); m_filterModel->setSortCaseSensitivity(Qt::CaseInsensitive); -- cgit From e61d8e4dc870aaeb2949557a87cf2749df573667 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 25 May 2023 16:16:58 -0700 Subject: fix: katabasis and QStyle leaks Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/ui/pages/instance/ManagedPackPage.cpp | 7 +++++-- libraries/katabasis/src/Reply.cpp | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index dc983d9a..593590f7 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -62,8 +62,11 @@ ManagedPackPage::ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_wi // NOTE: GTK2 themes crash with the proxy style. // This seems like an upstream bug, so there's not much else that can be done. - if (!QStyleFactory::keys().contains("gtk2")) - ui->versionsComboBox->setStyle(new NoBigComboBoxStyle(ui->versionsComboBox->style())); + if (!QStyleFactory::keys().contains("gtk2")){ + auto comboStyle = new NoBigComboBoxStyle(ui->versionsComboBox->style()); + comboStyle->setParent(APPLICATION); // make sure this gets cleaned up (setting to simply `this` causes it to be freed too soon) + ui->versionsComboBox->setStyle(comboStyle); + } ui->reloadButton->setVisible(false); connect(ui->reloadButton, &QPushButton::clicked, this, [this](bool){ diff --git a/libraries/katabasis/src/Reply.cpp b/libraries/katabasis/src/Reply.cpp index 3e27a7e6..c2607900 100644 --- a/libraries/katabasis/src/Reply.cpp +++ b/libraries/katabasis/src/Reply.cpp @@ -40,6 +40,8 @@ void ReplyList::remove(QNetworkReply *reply) { if (o2Reply) { o2Reply->stop(); (void)replies_.removeOne(o2Reply); + // we took ownership, we must free + delete o2Reply; } } -- cgit From 37420405c7b5dddb003533e1487ba45a2da5b808 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 27 May 2023 23:22:40 -0700 Subject: fix(memory leak): refactor NoBigComboStyle -> singleton Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/ui/pages/instance/ManagedPackPage.cpp | 33 ++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index 593590f7..ac34a5f4 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -41,8 +41,38 @@ class NoBigComboBoxStyle : public QProxyStyle { return QProxyStyle::styleHint(hint, option, widget, returnData); } // clang-format on + + static NoBigComboBoxStyle* GetInstance(QStyle* style); + + private: + static QMap s_singleton_instances_; + static std::mutex s_singleton_instances_mutex_; }; +QMap NoBigComboBoxStyle::s_singleton_instances_ = {}; +std::mutex NoBigComboBoxStyle::s_singleton_instances_mutex_; + +/** + * QProxyStyle and QStyle objects object to being freed even if all the widgets using them are gone + * so make singlestons tied to the lifetime of the application to clean them up and ensure they arn't + * being remade over and over agian leaking memory. + * */ +NoBigComboBoxStyle* NoBigComboBoxStyle::GetInstance(QStyle* style) +{ + std::lock_guard lock(s_singleton_instances_mutex_); + auto inst_iter = s_singleton_instances_.constFind(style); + NoBigComboBoxStyle* inst = nullptr; + if(inst_iter == s_singleton_instances_.constEnd() || *inst_iter == nullptr) { + inst = new NoBigComboBoxStyle(style); + inst->setParent(APPLICATION); + s_singleton_instances_.insert(style, inst); + qDebug() << "QProxyStyle NoBigComboBox created for" << style->objectName() << style; + } else { + inst = *inst_iter; + } + return inst; +} + ManagedPackPage* ManagedPackPage::createPage(BaseInstance* inst, QString type, QWidget* parent) { if (type == "modrinth") @@ -63,8 +93,7 @@ ManagedPackPage::ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_wi // NOTE: GTK2 themes crash with the proxy style. // This seems like an upstream bug, so there's not much else that can be done. if (!QStyleFactory::keys().contains("gtk2")){ - auto comboStyle = new NoBigComboBoxStyle(ui->versionsComboBox->style()); - comboStyle->setParent(APPLICATION); // make sure this gets cleaned up (setting to simply `this` causes it to be freed too soon) + auto comboStyle = NoBigComboBoxStyle::GetInstance(ui->versionsComboBox->style()); ui->versionsComboBox->setStyle(comboStyle); } -- cgit From 0357921284f68c7948104fe95d23209757afde09 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 28 May 2023 04:37:09 -0700 Subject: cleanup: move qstyle getInstance decl inline Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/ui/pages/instance/ManagedPackPage.cpp | 54 +++++++++++++------------- 1 file changed, 28 insertions(+), 26 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index ac34a5f4..a708377c 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -30,8 +30,6 @@ class NoBigComboBoxStyle : public QProxyStyle { Q_OBJECT public: - NoBigComboBoxStyle(QStyle* style) : QProxyStyle(style) {} - // clang-format off int styleHint(QStyle::StyleHint hint, const QStyleOption* option = nullptr, const QWidget* widget = nullptr, QStyleHintReturn* returnData = nullptr) const override { @@ -42,36 +40,40 @@ class NoBigComboBoxStyle : public QProxyStyle { } // clang-format on - static NoBigComboBoxStyle* GetInstance(QStyle* style); + /** + * Something about QProxyStyle and QStyle objects means they can't be free'd just + * because all the widgets using them are gone. + * They seems to be tied to the QApplicaiton lifecycle. + * So make singletons tied to the lifetime of the application to clean them up and ensure they aren't + * being remade over and over again, thus leaking memory. + */ + public: + static NoBigComboBoxStyle* getInstance(QStyle* style) + { + std::lock_guard lock(s_singleton_instances_mutex_); + auto inst_iter = s_singleton_instances_.constFind(style); + NoBigComboBoxStyle* inst = nullptr; + if (inst_iter == s_singleton_instances_.constEnd() || *inst_iter == nullptr) { + inst = new NoBigComboBoxStyle(style); + inst->setParent(APPLICATION); + s_singleton_instances_.insert(style, inst); + qDebug() << "QProxyStyle NoBigComboBox created for" << style->objectName() << style; + } else { + inst = *inst_iter; + } + return inst; + } private: - static QMap s_singleton_instances_; + NoBigComboBoxStyle(QStyle* style) : QProxyStyle(style) {} + + static QHash s_singleton_instances_; static std::mutex s_singleton_instances_mutex_; }; -QMap NoBigComboBoxStyle::s_singleton_instances_ = {}; +QHash NoBigComboBoxStyle::s_singleton_instances_ = {}; std::mutex NoBigComboBoxStyle::s_singleton_instances_mutex_; -/** - * QProxyStyle and QStyle objects object to being freed even if all the widgets using them are gone - * so make singlestons tied to the lifetime of the application to clean them up and ensure they arn't - * being remade over and over agian leaking memory. - * */ -NoBigComboBoxStyle* NoBigComboBoxStyle::GetInstance(QStyle* style) -{ - std::lock_guard lock(s_singleton_instances_mutex_); - auto inst_iter = s_singleton_instances_.constFind(style); - NoBigComboBoxStyle* inst = nullptr; - if(inst_iter == s_singleton_instances_.constEnd() || *inst_iter == nullptr) { - inst = new NoBigComboBoxStyle(style); - inst->setParent(APPLICATION); - s_singleton_instances_.insert(style, inst); - qDebug() << "QProxyStyle NoBigComboBox created for" << style->objectName() << style; - } else { - inst = *inst_iter; - } - return inst; -} ManagedPackPage* ManagedPackPage::createPage(BaseInstance* inst, QString type, QWidget* parent) { @@ -93,7 +95,7 @@ ManagedPackPage::ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_wi // NOTE: GTK2 themes crash with the proxy style. // This seems like an upstream bug, so there's not much else that can be done. if (!QStyleFactory::keys().contains("gtk2")){ - auto comboStyle = NoBigComboBoxStyle::GetInstance(ui->versionsComboBox->style()); + auto comboStyle = NoBigComboBoxStyle::getInstance(ui->versionsComboBox->style()); ui->versionsComboBox->setStyle(comboStyle); } -- cgit From 7af116fb006e2eb62429740bd0abbe14f50ff244 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 28 May 2023 05:06:28 -0700 Subject: refactor: function scope statics Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/ui/pages/instance/ManagedPackPage.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'launcher/ui/pages/instance') diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index a708377c..d0701a7a 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -50,6 +50,9 @@ class NoBigComboBoxStyle : public QProxyStyle { public: static NoBigComboBoxStyle* getInstance(QStyle* style) { + static QHash s_singleton_instances_ = {}; + static std::mutex s_singleton_instances_mutex_; + std::lock_guard lock(s_singleton_instances_mutex_); auto inst_iter = s_singleton_instances_.constFind(style); NoBigComboBoxStyle* inst = nullptr; @@ -67,14 +70,8 @@ class NoBigComboBoxStyle : public QProxyStyle { private: NoBigComboBoxStyle(QStyle* style) : QProxyStyle(style) {} - static QHash s_singleton_instances_; - static std::mutex s_singleton_instances_mutex_; }; -QHash NoBigComboBoxStyle::s_singleton_instances_ = {}; -std::mutex NoBigComboBoxStyle::s_singleton_instances_mutex_; - - ManagedPackPage* ManagedPackPage::createPage(BaseInstance* inst, QString type, QWidget* parent) { if (type == "modrinth") -- cgit