aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yml8
-rw-r--r--CMakeLists.txt10
-rw-r--r--README.md6
-rw-r--r--launcher/Application.cpp44
-rw-r--r--launcher/Application.h5
-rw-r--r--launcher/CMakeLists.txt60
-rw-r--r--launcher/InstanceCreationTask.h2
-rw-r--r--launcher/InstanceImportTask.cpp27
-rw-r--r--launcher/InstanceList.cpp2
-rw-r--r--launcher/JavaCommon.cpp6
-rw-r--r--launcher/LoggedProcess.cpp8
-rw-r--r--launcher/MMCTime.cpp64
-rw-r--r--launcher/MMCTime.h9
-rw-r--r--launcher/ResourceDownloadTask.cpp1
-rw-r--r--launcher/StringUtils.cpp67
-rw-r--r--launcher/StringUtils.h12
-rw-r--r--launcher/icons/IconList.cpp5
-rw-r--r--launcher/java/JavaChecker.cpp14
-rw-r--r--launcher/java/JavaCheckerJob.cpp2
-rw-r--r--launcher/launch/steps/Update.cpp8
-rw-r--r--launcher/minecraft/MinecraftInstance.cpp30
-rw-r--r--launcher/minecraft/MinecraftInstance.h16
-rw-r--r--launcher/minecraft/MinecraftLoadAndCheck.cpp1
-rw-r--r--launcher/minecraft/MinecraftUpdate.cpp4
-rw-r--r--launcher/minecraft/WorldList.cpp5
-rw-r--r--launcher/minecraft/WorldList.h4
-rw-r--r--launcher/minecraft/auth/AuthRequest.cpp22
-rw-r--r--launcher/minecraft/auth/MinecraftAccount.cpp16
-rw-r--r--launcher/minecraft/auth/steps/XboxUserStep.cpp4
-rw-r--r--launcher/minecraft/mod/ModFolderModel.cpp2
-rw-r--r--launcher/minecraft/mod/ModFolderModel.h2
-rw-r--r--launcher/minecraft/mod/ResourceFolderModel.cpp2
-rw-r--r--launcher/minecraft/mod/ResourceFolderModel.h4
-rw-r--r--launcher/minecraft/mod/ResourcePackFolderModel.cpp2
-rw-r--r--launcher/minecraft/mod/ResourcePackFolderModel.h2
-rw-r--r--launcher/minecraft/mod/ShaderPackFolderModel.h2
-rw-r--r--launcher/minecraft/mod/TexturePackFolderModel.cpp2
-rw-r--r--launcher/minecraft/mod/TexturePackFolderModel.h1
-rw-r--r--launcher/minecraft/services/CapeChange.cpp33
-rw-r--r--launcher/minecraft/services/CapeChange.h1
-rw-r--r--launcher/minecraft/services/SkinDelete.cpp22
-rw-r--r--launcher/minecraft/services/SkinDelete.h1
-rw-r--r--launcher/minecraft/services/SkinUpload.cpp22
-rw-r--r--launcher/minecraft/services/SkinUpload.h1
-rw-r--r--launcher/minecraft/update/AssetUpdateTask.cpp2
-rw-r--r--launcher/minecraft/update/FMLLibrariesTask.cpp1
-rw-r--r--launcher/minecraft/update/LibrariesTask.cpp2
-rw-r--r--launcher/modplatform/ModIndex.h3
-rw-r--r--launcher/modplatform/atlauncher/ATLPackInstallTask.cpp3
-rw-r--r--launcher/modplatform/flame/FileResolvingTask.cpp111
-rw-r--r--launcher/modplatform/flame/FileResolvingTask.h36
-rw-r--r--launcher/modplatform/flame/FlameAPI.cpp41
-rw-r--r--launcher/modplatform/flame/FlameInstanceCreationTask.cpp12
-rw-r--r--launcher/modplatform/helpers/NetworkResourceAPI.cpp12
-rw-r--r--launcher/modplatform/legacy_ftb/PackInstallTask.cpp1
-rw-r--r--launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp9
-rw-r--r--launcher/modplatform/technic/SingleZipPackInstallTask.cpp1
-rw-r--r--launcher/modplatform/technic/SolderPackInstallTask.cpp1
-rw-r--r--launcher/net/Download.cpp97
-rw-r--r--launcher/net/Download.h12
-rw-r--r--launcher/net/FileSink.cpp12
-rw-r--r--launcher/net/FileSink.h2
-rw-r--r--launcher/net/HttpMetaCache.cpp20
-rw-r--r--launcher/net/HttpMetaCache.h2
-rw-r--r--launcher/net/Logging.cpp26
-rw-r--r--launcher/net/Logging.h28
-rw-r--r--launcher/net/MetaCacheSink.cpp12
-rw-r--r--launcher/net/MetaCacheSink.h2
-rw-r--r--launcher/net/NetAction.h18
-rw-r--r--launcher/net/NetJob.cpp3
-rw-r--r--launcher/net/NetJob.h3
-rw-r--r--launcher/net/PasteUpload.cpp24
-rw-r--r--launcher/net/PasteUpload.h2
-rw-r--r--launcher/net/Upload.cpp49
-rw-r--r--launcher/net/Upload.h5
-rw-r--r--launcher/qtlogging.ini16
-rw-r--r--launcher/screenshots/ImgurAlbumCreation.cpp10
-rw-r--r--launcher/screenshots/ImgurUpload.cpp10
-rw-r--r--launcher/settings/SettingsObject.cpp9
-rw-r--r--launcher/tasks/ConcurrentTask.cpp194
-rw-r--r--launcher/tasks/ConcurrentTask.h70
-rw-r--r--launcher/tasks/MultipleOptionsTask.cpp34
-rw-r--r--launcher/tasks/MultipleOptionsTask.h34
-rw-r--r--launcher/tasks/SequentialTask.cpp35
-rw-r--r--launcher/tasks/SequentialTask.h35
-rw-r--r--launcher/tasks/Task.cpp52
-rw-r--r--launcher/tasks/Task.h66
-rw-r--r--launcher/tools/JProfiler.cpp4
-rw-r--r--launcher/tools/JVisualVM.cpp4
-rw-r--r--launcher/ui/dialogs/BlockedModsDialog.cpp39
-rw-r--r--launcher/ui/dialogs/NewInstanceDialog.cpp2
-rw-r--r--launcher/ui/dialogs/ProgressDialog.cpp136
-rw-r--r--launcher/ui/dialogs/ProgressDialog.h54
-rw-r--r--launcher/ui/dialogs/ProgressDialog.ui150
-rw-r--r--launcher/ui/dialogs/ResourceDownloadDialog.cpp2
-rw-r--r--launcher/ui/pages/global/MinecraftPage.cpp1
-rw-r--r--launcher/ui/pages/global/MinecraftPage.ui133
-rw-r--r--launcher/ui/pages/instance/ManagedPackPage.cpp39
-rw-r--r--launcher/ui/pages/instance/VersionPage.cpp4
-rw-r--r--launcher/ui/pages/modplatform/ModModel.cpp4
-rw-r--r--launcher/ui/pages/modplatform/ModPage.h2
-rw-r--r--launcher/ui/pages/modplatform/ResourceModel.cpp31
-rw-r--r--launcher/ui/pages/modplatform/ResourceModel.h2
-rw-r--r--launcher/ui/pages/modplatform/ResourcePackModel.cpp4
-rw-r--r--launcher/ui/pages/modplatform/ResourcePackPage.h2
-rw-r--r--launcher/ui/pages/modplatform/ResourcePage.cpp2
-rw-r--r--launcher/ui/pages/modplatform/ShaderPackModel.cpp4
-rw-r--r--launcher/ui/pages/modplatform/ShaderPackPage.h2
-rw-r--r--launcher/ui/widgets/PageContainer.cpp4
-rw-r--r--launcher/ui/widgets/ProgressWidget.cpp1
-rw-r--r--launcher/ui/widgets/SubTaskProgressBar.cpp58
-rw-r--r--launcher/ui/widgets/SubTaskProgressBar.h48
-rw-r--r--launcher/ui/widgets/SubTaskProgressBar.ui94
-rw-r--r--libraries/katabasis/src/Reply.cpp2
-rw-r--r--program_info/org.prismlauncher.PrismLauncher.desktop.in2
-rw-r--r--program_info/org.prismlauncher.PrismLauncher.metainfo.xml.in2
-rw-r--r--program_info/win_install.nsi.in1
-rw-r--r--tests/ResourceFolderModel_test.cpp17
-rw-r--r--tests/ResourceModel_test.cpp6
-rw-r--r--tests/Task_test.cpp9
120 files changed, 1959 insertions, 584 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 02cc8b1f..691e257b 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -68,7 +68,7 @@ jobs:
qt_ver: 6
qt_host: windows
qt_arch: ''
- qt_version: '6.5.0'
+ qt_version: '6.5.1'
qt_modules: 'qt5compat qtimageformats'
qt_tools: ''
@@ -80,7 +80,7 @@ jobs:
qt_ver: 6
qt_host: windows
qt_arch: 'win64_msvc2019_arm64'
- qt_version: '6.5.0'
+ qt_version: '6.5.1'
qt_modules: 'qt5compat qtimageformats'
qt_tools: ''
@@ -90,7 +90,7 @@ jobs:
qt_ver: 6
qt_host: mac
qt_arch: ''
- qt_version: '6.5.0'
+ qt_version: '6.5.1'
qt_modules: 'qt5compat qtimageformats'
qt_tools: ''
@@ -587,7 +587,7 @@ jobs:
submodules: 'true'
- name: Install nix
if: inputs.build_type == 'Debug'
- uses: cachix/install-nix-action@v20
+ uses: cachix/install-nix-action@v21
with:
install_url: https://nixos.org/nix/install
extra_nix_config: |
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 05c69c89..6bd12630 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -164,13 +164,13 @@ set(Launcher_BUG_TRACKER_URL "https://github.com/PrismLauncher/PrismLauncher/iss
set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/prismlauncher/launcher/" CACHE STRING "URL for the translations platform.")
# Matrix Space
-set(Launcher_MATRIX_URL "https://matrix.to/#/#prismlauncher:matrix.org" CACHE STRING "URL to the Matrix Space")
+set(Launcher_MATRIX_URL "https://prismlauncher.org/matrix" CACHE STRING "URL to the Matrix Space")
# Discord URL
-set(Launcher_DISCORD_URL "https://discord.gg/prismlauncher" CACHE STRING "URL for the Discord guild.")
+set(Launcher_DISCORD_URL "https://prismlauncher.org/discord" CACHE STRING "URL for the Discord guild.")
# Subreddit URL
-set(Launcher_SUBREDDIT_URL "https://www.reddit.com/r/PrismLauncher/" CACHE STRING "URL for the subreddit.")
+set(Launcher_SUBREDDIT_URL "https://prismlauncher.org/reddit" CACHE STRING "URL for the subreddit.")
# Builds
set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
@@ -345,6 +345,8 @@ elseif(UNIX)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION "${KDE_INSTALL_ICONDIR}/hicolor/scalable/apps")
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
+ install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "${KDE_INSTALL_DATADIR}/${Launcher_Name}")
+
if(Launcher_ManPage)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")
endif()
@@ -372,6 +374,8 @@ else()
message(FATAL_ERROR "Platform not supported")
endif()
+
+
################################ Included Libs ################################
include(ExternalProject)
diff --git a/README.md b/README.md
index aaa1fd4c..993f02f5 100644
--- a/README.md
+++ b/README.md
@@ -38,15 +38,15 @@ Feel free to create a GitHub issue if you find a bug or want to suggest a new fe
- **Our Discord server:**
-[![Prism Launcher Discord server](https://discordapp.com/api/guilds/1031648380885147709/widget.png?style=banner3)](https://discord.gg/prismlauncher)
+[![Prism Launcher Discord server](https://discordapp.com/api/guilds/1031648380885147709/widget.png?style=banner3)](https://prismlauncher.org/discord)
- **Our Matrix space:**
-[![PrismLauncher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge&label=Matrix%20Space&logo=matrix&color=purple)](https://matrix.to/#/#prismlauncher:matrix.org)
+[![PrismLauncher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge&label=Matrix%20Space&logo=matrix&color=purple)](https://prismlauncher.org/matrix)
- **Our Subreddit:**
-[![r/PrismLauncher](https://img.shields.io/reddit/subreddit-subscribers/prismlauncher?style=for-the-badge&logo=reddit)](https://www.reddit.com/r/PrismLauncher/)
+[![r/PrismLauncher](https://img.shields.io/reddit/subreddit-subscribers/prismlauncher?style=for-the-badge&logo=reddit)](https://prismlauncher.org/reddit)
## Translations
diff --git a/launcher/Application.cpp b/launcher/Application.cpp
index a7c97aa7..1659eb44 100644
--- a/launcher/Application.cpp
+++ b/launcher/Application.cpp
@@ -8,6 +8,7 @@
* Copyright (C) 2022 Lenny McLennington <lenny@sneed.church>
* Copyright (C) 2022 Tayou <tayou@gmx.net>
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
+ * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
* 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
@@ -46,6 +47,7 @@
#include "net/PasteUpload.h"
#include "pathmatcher/MultiMatcher.h"
#include "pathmatcher/SimplePrefixMatcher.h"
+#include "settings/INIFile.h"
#include "ui/MainWindow.h"
#include "ui/InstanceWindow.h"
@@ -286,6 +288,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) {
dataPath = m_rootPath;
adjustedBy = "Portable data path";
+ m_portable = true;
}
#endif
}
@@ -410,6 +413,47 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
" " "|" " "
"%{if-category}[%{category}]: %{endif}"
"%{message}");
+
+ bool foundLoggingRules = false;
+
+ auto logRulesFile = QStringLiteral("qtlogging.ini");
+ auto logRulesPath = FS::PathCombine(dataPath, logRulesFile);
+
+ qDebug() << "Testing" << logRulesPath << "...";
+ foundLoggingRules = QFile::exists(logRulesPath);
+
+ // search the dataPath()
+ // seach app data standard path
+ if(!foundLoggingRules && !isPortable() && dirParam.isEmpty()) {
+ logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile));
+ if(!logRulesPath.isEmpty()) {
+ qDebug() << "Found" << logRulesPath << "...";
+ foundLoggingRules = true;
+ }
+ }
+ // seach root path
+ if(!foundLoggingRules) {
+ logRulesPath = FS::PathCombine(m_rootPath, logRulesFile);
+ qDebug() << "Testing" << logRulesPath << "...";
+ foundLoggingRules = QFile::exists(logRulesPath);
+ }
+
+ if(foundLoggingRules) {
+ // load and set logging rules
+ qDebug() << "Loading logging rules from:" << logRulesPath;
+ QSettings loggingRules(logRulesPath, QSettings::IniFormat);
+ loggingRules.beginGroup("Rules");
+ QStringList rule_names = loggingRules.childKeys();
+ QStringList rules;
+ qDebug() << "Setting log rules:";
+ for (auto rule_name : rule_names) {
+ auto rule = QString("%1=%2").arg(rule_name).arg(loggingRules.value(rule_name).toString());
+ rules.append(rule);
+ qDebug() << " " << rule;
+ }
+ auto rules_str = rules.join("\n");
+ QLoggingCategory::setFilterRules(rules_str);
+ }
qDebug() << "<> Log initialized.";
}
diff --git a/launcher/Application.h b/launcher/Application.h
index 0d24a4e9..ced0af17 100644
--- a/launcher/Application.h
+++ b/launcher/Application.h
@@ -187,6 +187,10 @@ public:
return m_rootPath;
}
+ bool isPortable() {
+ return m_portable;
+ }
+
const Capabilities capabilities() {
return m_capabilities;
}
@@ -275,6 +279,7 @@ private:
QString m_rootPath;
Status m_status = Application::StartingUp;
Capabilities m_capabilities;
+ bool m_portable = false;
#ifdef Q_OS_MACOS
Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive;
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index 6eec9b3a..273b5449 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -124,6 +124,8 @@ set(NET_SOURCES
net/HttpMetaCache.h
net/MetaCacheSink.cpp
net/MetaCacheSink.h
+ net/Logging.h
+ net/Logging.cpp
net/NetAction.h
net/NetJob.cpp
net/NetJob.h
@@ -576,6 +578,55 @@ ecm_qt_declare_logging_category(CORE_SOURCES
EXPORT "${Launcher_Name}"
)
+ecm_qt_export_logging_category(
+ IDENTIFIER taskLogC
+ CATEGORY_NAME "launcher.task"
+ DEFAULT_SEVERITY Debug
+ DESCRIPTION "Task actions"
+ EXPORT "${Launcher_Name}"
+)
+
+ecm_qt_export_logging_category(
+ IDENTIFIER taskNetLogC
+ CATEGORY_NAME "launcher.task.net"
+ DEFAULT_SEVERITY Debug
+ DESCRIPTION "task network action"
+ EXPORT "${Launcher_Name}"
+)
+
+ecm_qt_export_logging_category(
+ IDENTIFIER taskDownloadLogC
+ CATEGORY_NAME "launcher.task.net.download"
+ DEFAULT_SEVERITY Debug
+ DESCRIPTION "task network download actions"
+ EXPORT "${Launcher_Name}"
+)
+ecm_qt_export_logging_category(
+ IDENTIFIER taskUploadLogC
+ CATEGORY_NAME "launcher.task.net.upload"
+ DEFAULT_SEVERITY Debug
+ DESCRIPTION "task network upload actions"
+ EXPORT "${Launcher_Name}"
+)
+
+ecm_qt_export_logging_category(
+ IDENTIFIER taskMetaCacheLogC
+ CATEGORY_NAME "launcher.task.net.metacache"
+ DEFAULT_SEVERITY Debug
+ DESCRIPTION "task network meta-cache actions"
+ EXPORT "${Launcher_Name}"
+)
+
+ecm_qt_export_logging_category(
+ IDENTIFIER taskHttpMetaCacheLogC
+ CATEGORY_NAME "launcher.task.net.metacache.http"
+ DEFAULT_SEVERITY Debug
+ DESCRIPTION "task network http meta-cache actions"
+ EXPORT "${Launcher_Name}"
+)
+
+
+
if(KDE_INSTALL_LOGGINGCATEGORIESDIR) # only install if there is a standard path for this
ecm_qt_install_logging_categories(
EXPORT "${Launcher_Name}"
@@ -922,6 +973,8 @@ SET(LAUNCHER_SOURCES
ui/widgets/VariableSizedImageObject.cpp
ui/widgets/ProjectItem.h
ui/widgets/ProjectItem.cpp
+ ui/widgets/SubTaskProgressBar.h
+ ui/widgets/SubTaskProgressBar.cpp
ui/widgets/VersionListView.cpp
ui/widgets/VersionListView.h
ui/widgets/VersionSelectWidget.cpp
@@ -982,6 +1035,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/widgets/CustomCommands.ui
ui/widgets/InfoFrame.ui
ui/widgets/ModFilterWidget.ui
+ ui/widgets/SubTaskProgressBar.ui
ui/widgets/ThemeCustomizationWidget.ui
ui/dialogs/CopyInstanceDialog.ui
ui/dialogs/ProfileSetupDialog.ui
@@ -1155,6 +1209,12 @@ if(INSTALL_BUNDLE STREQUAL "full")
CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${RESOURCES_DEST_DIR}/qt.conf\" \" \")"
COMPONENT Runtime
)
+ # add qtlogging.ini as a config file
+ install(
+ FILES "qtlogging.ini"
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/${RESOURCES_DEST_DIR}
+ COMPONENT Runtime
+ )
# Bundle plugins
# Image formats
install(
diff --git a/launcher/InstanceCreationTask.h b/launcher/InstanceCreationTask.h
index 03ee1a7a..380fdf8a 100644
--- a/launcher/InstanceCreationTask.h
+++ b/launcher/InstanceCreationTask.h
@@ -34,7 +34,7 @@ class InstanceCreationTask : public InstanceTask {
QString getError() const { return m_error_message; }
protected:
- void setError(QString message) { m_error_message = message; };
+ void setError(const QString& message) { m_error_message = message; };
protected:
bool m_abort = false;
diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp
index 080828a8..352848f0 100644
--- a/launcher/InstanceImportTask.cpp
+++ b/launcher/InstanceImportTask.cpp
@@ -41,6 +41,7 @@
#include "MMCZip.h"
#include "NullInstance.h"
+#include "QObjectPtr.h"
#include "icons/IconList.h"
#include "icons/IconUtils.h"
@@ -98,6 +99,7 @@ void InstanceImportTask::executeTask()
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
+ connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propogateStepProgress);
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
@@ -259,7 +261,7 @@ void InstanceImportTask::extractFinished()
void InstanceImportTask::processFlame()
{
- FlameCreationTask* inst_creation_task = nullptr;
+ shared_qobject_ptr<FlameCreationTask> inst_creation_task = nullptr;
if (!m_extra_info.isEmpty()) {
auto pack_id_it = m_extra_info.constFind("pack_id");
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
@@ -274,10 +276,10 @@ void InstanceImportTask::processFlame()
if (original_instance_id_it != m_extra_info.constEnd())
original_instance_id = original_instance_id_it.value();
- inst_creation_task = new FlameCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
+ inst_creation_task = makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
} else {
// FIXME: Find a way to get IDs in directly imported ZIPs
- inst_creation_task = new FlameCreationTask(m_stagingPath, m_globalSettings, m_parent, {}, {});
+ inst_creation_task = makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, QString(), QString());
}
inst_creation_task->setName(*this);
@@ -285,18 +287,19 @@ void InstanceImportTask::processFlame()
inst_creation_task->setGroup(m_instGroup);
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
- connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] {
+ connect(inst_creation_task.get(), &Task::succeeded, this, [this, inst_creation_task] {
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
emitSucceeded();
});
- connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
- connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress);
- connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus);
- connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater);
+ connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed);
+ connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress);
+ connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress);
+ connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus);
+ connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails);
- connect(this, &Task::aborted, inst_creation_task, &InstanceCreationTask::abort);
- connect(inst_creation_task, &Task::aborted, this, &Task::abort);
- connect(inst_creation_task, &Task::abortStatusChanged, this, &Task::setAbortable);
+ connect(this, &Task::aborted, inst_creation_task.get(), &InstanceCreationTask::abort);
+ connect(inst_creation_task.get(), &Task::aborted, this, &Task::abort);
+ connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
inst_creation_task->start();
}
@@ -382,7 +385,9 @@ void InstanceImportTask::processModrinth()
});
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress);
+ connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress);
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus);
+ connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails);
connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater);
connect(this, &Task::aborted, inst_creation_task, &InstanceCreationTask::abort);
diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp
index 5b33d678..b4c520cd 100644
--- a/launcher/InstanceList.cpp
+++ b/launcher/InstanceList.cpp
@@ -797,7 +797,9 @@ class InstanceStaging : public Task {
connect(child, &Task::aborted, this, &InstanceStaging::childAborted);
connect(child, &Task::abortStatusChanged, this, &InstanceStaging::setAbortable);
connect(child, &Task::status, this, &InstanceStaging::setStatus);
+ connect(child, &Task::details, this, &InstanceStaging::setDetails);
connect(child, &Task::progress, this, &InstanceStaging::setProgress);
+ connect(child, &Task::stepProgress, this, &InstanceStaging::propogateStepProgress);
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded);
}
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..d70f6d00 100644
--- a/launcher/LoggedProcess.cpp
+++ b/launcher/LoggedProcess.cpp
@@ -44,12 +44,8 @@ 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)));
-#else
- connect(this, SIGNAL(error(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError)));
-#endif
+ connect(this, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &LoggedProcess::on_exit);
+ connect(this, &QProcess::errorOccurred, this, &LoggedProcess::on_error);
connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange);
}
diff --git a/launcher/MMCTime.cpp b/launcher/MMCTime.cpp
index 70bc4135..1702ec06 100644
--- a/launcher/MMCTime.cpp
+++ b/launcher/MMCTime.cpp
@@ -18,6 +18,8 @@
#include <MMCTime.h>
#include <QObject>
+#include <QDateTime>
+#include <QTextStream>
QString Time::prettifyDuration(int64_t duration) {
int seconds = (int) (duration % 60);
@@ -36,3 +38,65 @@ QString Time::prettifyDuration(int64_t duration) {
}
return QObject::tr("%1d %2h %3min").arg(days).arg(hours).arg(minutes);
}
+
+QString Time::humanReadableDuration(double duration, int precision) {
+
+ using days = std::chrono::duration<int, std::ratio<86400>>;
+
+ QString outStr;
+ QTextStream os(&outStr);
+
+ bool neg = false;
+ if (duration < 0) {
+ neg = true; // flag
+ duration *= -1; // invert
+ }
+
+ auto std_duration = std::chrono::duration<double>(duration);
+ auto d = std::chrono::duration_cast<days>(std_duration);
+ std_duration -= d;
+ auto h = std::chrono::duration_cast<std::chrono::hours>(std_duration);
+ std_duration -= h;
+ auto m = std::chrono::duration_cast<std::chrono::minutes>(std_duration);
+ std_duration -= m;
+ auto s = std::chrono::duration_cast<std::chrono::seconds>(std_duration);
+ std_duration -= s;
+ auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(std_duration);
+
+ auto dc = d.count();
+ auto hc = h.count();
+ auto mc = m.count();
+ auto sc = s.count();
+ auto msc = ms.count();
+
+ if (neg) {
+ os << '-';
+ }
+ if (dc) {
+ os << dc << QObject::tr("days");
+ }
+ if (hc) {
+ if (dc)
+ os << " ";
+ os << qSetFieldWidth(2) << hc << QObject::tr("h"); // hours
+ }
+ if (mc) {
+ if (dc || hc)
+ os << " ";
+ os << qSetFieldWidth(2) << mc << QObject::tr("m"); // minutes
+ }
+ if (dc || hc || mc || sc) {
+ if (dc || hc || mc)
+ os << " ";
+ os << qSetFieldWidth(2) << sc << QObject::tr("s"); // seconds
+ }
+ if ((msc && (precision > 0)) || !(dc || hc || mc || sc)) {
+ if (dc || hc || mc || sc)
+ os << " ";
+ os << qSetFieldWidth(0) << qSetRealNumberPrecision(precision) << msc << QObject::tr("ms"); // miliseconds
+ }
+
+ os.flush();
+
+ return outStr;
+} \ No newline at end of file
diff --git a/launcher/MMCTime.h b/launcher/MMCTime.h
index 10ff2ffe..6a5780b4 100644
--- a/launcher/MMCTime.h
+++ b/launcher/MMCTime.h
@@ -22,4 +22,13 @@ namespace Time {
QString prettifyDuration(int64_t duration);
+/**
+ * @brief Returns a string with short form time duration ie. `2days 1h3m4s56.0ms`.
+ * miliseconds are only included if `precision` is greater than 0.
+ *
+ * @param duration a number of seconds as floating point
+ * @param precision number of decmial points to display on fractons of a second, defualts to 0.
+ * @return QString
+ */
+QString humanReadableDuration(double duration, int precision = 0);
}
diff --git a/launcher/ResourceDownloadTask.cpp b/launcher/ResourceDownloadTask.cpp
index 98bcf259..61b918aa 100644
--- a/launcher/ResourceDownloadTask.cpp
+++ b/launcher/ResourceDownloadTask.cpp
@@ -53,6 +53,7 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack pack,
m_filesNetJob->addNetAction(Net::Download::makeFile(m_pack_version.downloadUrl, 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::stepProgress, this, &ResourceDownloadTask::propogateStepProgress);
connect(m_filesNetJob.get(), &NetJob::failed, this, &ResourceDownloadTask::downloadFailed);
addTask(m_filesNetJob);
diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp
index d109d63d..e08e6fdc 100644
--- a/launcher/StringUtils.cpp
+++ b/launcher/StringUtils.cpp
@@ -33,10 +33,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
+
#include "StringUtils.h"
+#include <QRegularExpression>
#include <QUuid>
+#include <cmath>
/// If you're wondering where these came from exactly, then know you're not the only one =D
@@ -113,6 +115,69 @@ int StringUtils::naturalCompare(const QString& s1, const QString& s2, Qt::CaseSe
return QString::compare(s1, s2, cs);
}
+QString StringUtils::truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_limit)
+{
+ auto display_options = QUrl::RemoveUserInfo | QUrl::RemoveFragment | QUrl::NormalizePathSegments;
+ auto str_url = url.toDisplayString(display_options);
+
+ if (str_url.length() <= max_len)
+ return str_url;
+
+ auto url_path_parts = url.path().split('/');
+ QString last_path_segment = url_path_parts.takeLast();
+
+ if (url_path_parts.size() >= 1 && url_path_parts.first().isEmpty())
+ url_path_parts.removeFirst(); // drop empty first segment (from leading / )
+
+ if (url_path_parts.size() >= 1)
+ url_path_parts.removeLast(); // drop the next to last path segment
+
+ auto url_template = QStringLiteral("%1://%2/%3%4");
+
+ auto url_compact = url_path_parts.isEmpty()
+ ? url_template.arg(url.scheme(), url.host(), QStringList({ "...", last_path_segment }).join('/'), url.query())
+ : url_template.arg(url.scheme(), url.host(),
+ QStringList({ url_path_parts.join('/'), "...", last_path_segment }).join('/'), url.query());
+
+ // remove url parts one by one if it's still too long
+ while (url_compact.length() > max_len && url_path_parts.size() >= 1) {
+ url_path_parts.removeLast(); // drop the next to last path segment
+ url_compact = url_path_parts.isEmpty()
+ ? url_template.arg(url.scheme(), url.host(), QStringList({ "...", last_path_segment }).join('/'), url.query())
+ : url_template.arg(url.scheme(), url.host(),
+ QStringList({ url_path_parts.join('/'), "...", last_path_segment }).join('/'), url.query());
+ }
+
+ if ((url_compact.length() >= max_len) && hard_limit) {
+ // still too long, truncate normaly
+ url_compact = QString(str_url);
+ auto to_remove = url_compact.length() - max_len + 3;
+ url_compact.remove(url_compact.length() - to_remove - 1, to_remove);
+ url_compact.append("...");
+ }
+
+ return url_compact;
+}
+
+static const QStringList s_units_si{ "KB", "MB", "GB", "TB" };
+static const QStringList s_units_kibi{ "KiB", "MiB", "GiB", "TiB" };
+
+QString StringUtils::humanReadableFileSize(double bytes, bool use_si, int decimal_points)
+{
+ const QStringList units = use_si ? s_units_si : s_units_kibi;
+ const int scale = use_si ? 1000 : 1024;
+
+ int u = -1;
+ double r = pow(10, decimal_points);
+
+ do {
+ bytes /= scale;
+ u++;
+ } while (round(abs(bytes) * r) / r >= scale && u < units.length() - 1);
+
+ return QString::number(bytes, 'f', 2) + " " + units[u];
+}
+
QString StringUtils::getRandomAlphaNumeric()
{
return QUuid::createUuid().toString(QUuid::Id128);
diff --git a/launcher/StringUtils.h b/launcher/StringUtils.h
index 36c8cf8f..f90a6ac7 100644
--- a/launcher/StringUtils.h
+++ b/launcher/StringUtils.h
@@ -37,6 +37,7 @@
#pragma once
#include <QString>
+#include <QUrl>
namespace StringUtils {
@@ -66,5 +67,16 @@ inline QString fromStdString(string s)
int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs);
+/**
+ * @brief Truncate a url while keeping its readability py placing the `...` in the middle of the path
+ * @param url Url to truncate
+ * @param max_len max lenght of url in charaters
+ * @param hard_limit if truncating the path can't get the url short enough, truncate it normaly.
+ */
+QString truncateUrlHumanFriendly(QUrl &url, int max_len, bool hard_limit = false);
+
+QString humanReadableFileSize(double bytes, bool use_si = false, int decimal_points = 1);
+
+
QString getRandomAlphaNumeric();
} // namespace StringUtils
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..b4c55b3d 100644
--- a/launcher/java/JavaChecker.cpp
+++ b/launcher/java/JavaChecker.cpp
@@ -87,15 +87,11 @@ 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)));
-#else
- connect(process.get(), SIGNAL(error(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError)));
-#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(), QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &JavaChecker::finished);
+ connect(process.get(), &QProcess::errorOccurred, this, &JavaChecker::error);
+ 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..77c8a18e 100644
--- a/launcher/launch/steps/Update.cpp
+++ b/launcher/launch/steps/Update.cpp
@@ -26,9 +26,11 @@ 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::stepProgress, this, &Update::propogateStepProgress);
+ connect(m_updateTask.get(), &Task::status, this, &Update::setStatus);
+ connect(m_updateTask.get(), &Task::details, this, &Update::setDetails);
emit progressReportingRequest();
return;
}
diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp
index 35bef05e..2c624a36 100644
--- a/launcher/minecraft/MinecraftInstance.cpp
+++ b/launcher/minecraft/MinecraftInstance.cpp
@@ -1109,79 +1109,79 @@ JavaVersion MinecraftInstance::getJavaVersion()
return JavaVersion(settings()->get("JavaVersion").toString());
}
-std::shared_ptr<ModFolderModel> MinecraftInstance::loaderModList() const
+std::shared_ptr<ModFolderModel> MinecraftInstance::loaderModList()
{
if (!m_loader_mod_list)
{
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
- m_loader_mod_list.reset(new ModFolderModel(modsRoot(), shared_from_this(), is_indexed));
+ m_loader_mod_list.reset(new ModFolderModel(modsRoot(), this, is_indexed));
m_loader_mod_list->disableInteraction(isRunning());
connect(this, &BaseInstance::runningStatusChanged, m_loader_mod_list.get(), &ModFolderModel::disableInteraction);
}
return m_loader_mod_list;
}
-std::shared_ptr<ModFolderModel> MinecraftInstance::coreModList() const
+std::shared_ptr<ModFolderModel> MinecraftInstance::coreModList()
{
if (!m_core_mod_list)
{
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
- m_core_mod_list.reset(new ModFolderModel(coreModsDir(), shared_from_this(), is_indexed));
+ m_core_mod_list.reset(new ModFolderModel(coreModsDir(), this, is_indexed));
m_core_mod_list->disableInteraction(isRunning());
connect(this, &BaseInstance::runningStatusChanged, m_core_mod_list.get(), &ModFolderModel::disableInteraction);
}
return m_core_mod_list;
}
-std::shared_ptr<ModFolderModel> MinecraftInstance::nilModList() const
+std::shared_ptr<ModFolderModel> MinecraftInstance::nilModList()
{
if (!m_nil_mod_list)
{
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
- m_nil_mod_list.reset(new ModFolderModel(nilModsDir(), shared_from_this(), is_indexed, false));
+ m_nil_mod_list.reset(new ModFolderModel(nilModsDir(), this, is_indexed, false));
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<ResourcePackFolderModel> MinecraftInstance::resourcePackList() const
+std::shared_ptr<ResourcePackFolderModel> MinecraftInstance::resourcePackList()
{
if (!m_resource_pack_list)
{
- m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir(), shared_from_this()));
+ m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir(), this));
}
return m_resource_pack_list;
}
-std::shared_ptr<TexturePackFolderModel> MinecraftInstance::texturePackList() const
+std::shared_ptr<TexturePackFolderModel> MinecraftInstance::texturePackList()
{
if (!m_texture_pack_list)
{
- m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir(), shared_from_this()));
+ m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir(), this));
}
return m_texture_pack_list;
}
-std::shared_ptr<ShaderPackFolderModel> MinecraftInstance::shaderPackList() const
+std::shared_ptr<ShaderPackFolderModel> MinecraftInstance::shaderPackList()
{
if (!m_shader_pack_list)
{
- m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir(), shared_from_this()));
+ m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir(), this));
}
return m_shader_pack_list;
}
-std::shared_ptr<WorldList> MinecraftInstance::worldList() const
+std::shared_ptr<WorldList> MinecraftInstance::worldList()
{
if (!m_world_list)
{
- m_world_list.reset(new WorldList(worldDir(), shared_from_this()));
+ m_world_list.reset(new WorldList(worldDir(), this));
}
return m_world_list;
}
-std::shared_ptr<GameOptions> MinecraftInstance::gameOptionsModel() const
+std::shared_ptr<GameOptions> MinecraftInstance::gameOptionsModel()
{
if (!m_game_options)
{
diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h
index a75fa481..068b3008 100644
--- a/launcher/minecraft/MinecraftInstance.h
+++ b/launcher/minecraft/MinecraftInstance.h
@@ -115,14 +115,14 @@ public:
std::shared_ptr<PackProfile> getPackProfile() const;
////// Mod Lists //////
- std::shared_ptr<ModFolderModel> loaderModList() const;
- std::shared_ptr<ModFolderModel> coreModList() const;
- std::shared_ptr<ModFolderModel> nilModList() const;
- std::shared_ptr<ResourcePackFolderModel> resourcePackList() const;
- std::shared_ptr<TexturePackFolderModel> texturePackList() const;
- std::shared_ptr<ShaderPackFolderModel> shaderPackList() const;
- std::shared_ptr<WorldList> worldList() const;
- std::shared_ptr<GameOptions> gameOptionsModel() const;
+ std::shared_ptr<ModFolderModel> loaderModList();
+ std::shared_ptr<ModFolderModel> coreModList();
+ std::shared_ptr<ModFolderModel> nilModList();
+ std::shared_ptr<ResourcePackFolderModel> resourcePackList();
+ std::shared_ptr<TexturePackFolderModel> texturePackList();
+ std::shared_ptr<ShaderPackFolderModel> shaderPackList();
+ std::shared_ptr<WorldList> worldList();
+ std::shared_ptr<GameOptions> gameOptionsModel();
////// Launch stuff //////
Task::Ptr createUpdateTask(Net::Mode mode) override;
diff --git a/launcher/minecraft/MinecraftLoadAndCheck.cpp b/launcher/minecraft/MinecraftLoadAndCheck.cpp
index d72bc7be..1c3f6fb7 100644
--- a/launcher/minecraft/MinecraftLoadAndCheck.cpp
+++ b/launcher/minecraft/MinecraftLoadAndCheck.cpp
@@ -22,6 +22,7 @@ void MinecraftLoadAndCheck::executeTask()
connect(m_task.get(), &Task::failed, this, &MinecraftLoadAndCheck::subtaskFailed);
connect(m_task.get(), &Task::aborted, this, [this]{ subtaskFailed(tr("Aborted")); });
connect(m_task.get(), &Task::progress, this, &MinecraftLoadAndCheck::progress);
+ connect(m_task.get(), &Task::stepProgress, this, &MinecraftLoadAndCheck::propogateStepProgress);
connect(m_task.get(), &Task::status, this, &MinecraftLoadAndCheck::setStatus);
}
diff --git a/launcher/minecraft/MinecraftUpdate.cpp b/launcher/minecraft/MinecraftUpdate.cpp
index 07ad4882..35430bb0 100644
--- a/launcher/minecraft/MinecraftUpdate.cpp
+++ b/launcher/minecraft/MinecraftUpdate.cpp
@@ -100,7 +100,9 @@ void MinecraftUpdate::next()
disconnect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
disconnect(task.get(), &Task::aborted, this, &Task::abort);
disconnect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
+ disconnect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propogateStepProgress);
disconnect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
+ disconnect(task.get(), &Task::details, this, &MinecraftUpdate::setDetails);
}
if(m_currentTask == m_tasks.size())
{
@@ -118,7 +120,9 @@ void MinecraftUpdate::next()
connect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
connect(task.get(), &Task::aborted, this, &Task::abort);
connect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
+ connect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propogateStepProgress);
connect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
+ connect(task.get(), &Task::details, this, &MinecraftUpdate::setDetails);
// if the task is already running, do not start it again
if(!task->isRunning())
{
diff --git a/launcher/minecraft/WorldList.cpp b/launcher/minecraft/WorldList.cpp
index 62e55cd4..0feee299 100644
--- a/launcher/minecraft/WorldList.cpp
+++ b/launcher/minecraft/WorldList.cpp
@@ -45,7 +45,7 @@
#include <QFileSystemWatcher>
#include <QDebug>
-WorldList::WorldList(const QString &dir, std::shared_ptr<const BaseInstance> instance)
+WorldList::WorldList(const QString &dir, BaseInstance* instance)
: QAbstractListModel(), m_instance(instance), m_dir(dir)
{
FS::ensureFolderPathExists(m_dir.absolutePath());
@@ -53,8 +53,7 @@ WorldList::WorldList(const QString &dir, std::shared_ptr<const BaseInstance> ins
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/WorldList.h b/launcher/minecraft/WorldList.h
index 10fb4e3c..96b64193 100644
--- a/launcher/minecraft/WorldList.h
+++ b/launcher/minecraft/WorldList.h
@@ -50,7 +50,7 @@ public:
IconFileRole
};
- WorldList(const QString &dir, std::shared_ptr<const BaseInstance> instance);
+ WorldList(const QString &dir, BaseInstance* instance);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
@@ -128,7 +128,7 @@ signals:
void changed();
protected:
- std::shared_ptr<const BaseInstance> m_instance;
+ BaseInstance* m_instance;
QFileSystemWatcher *m_watcher;
bool is_watching;
QDir m_dir;
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<QNetworkReply::NetworkError>::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<QNetworkReply::NetworkError>::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<AccountTask> 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<AccountTask> 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<AccountTask> 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<AccountTask> 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/auth/steps/XboxUserStep.cpp b/launcher/minecraft/auth/steps/XboxUserStep.cpp
index 53069597..842eb60f 100644
--- a/launcher/minecraft/auth/steps/XboxUserStep.cpp
+++ b/launcher/minecraft/auth/steps/XboxUserStep.cpp
@@ -38,6 +38,10 @@ void XboxUserStep::perform() {
QNetworkRequest request = QNetworkRequest(QUrl("https://user.auth.xboxlive.com/user/authenticate"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Accept", "application/json");
+ // set contract-verison header (prevent err 400 bad-request?)
+ // https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/reference/live/rest/additional/httpstandardheaders
+ request.setRawHeader("x-xbl-contract-version", "1");
+
auto *requestor = new AuthRequest(this);
connect(requestor, &AuthRequest::finished, this, &XboxUserStep::onRequestDone);
requestor->post(request, xbox_auth_data.toUtf8());
diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp
index 59e078f1..02e77b30 100644
--- a/launcher/minecraft/mod/ModFolderModel.cpp
+++ b/launcher/minecraft/mod/ModFolderModel.cpp
@@ -54,7 +54,7 @@
#include "minecraft/mod/tasks/LocalModParseTask.h"
#include "minecraft/mod/tasks/ModFolderLoadTask.h"
-ModFolderModel::ModFolderModel(const QString& dir, std::shared_ptr<const BaseInstance> instance, bool is_indexed, bool create_dir)
+ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir)
: ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed)
{
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME , SortType::VERSION, SortType::DATE, SortType::PROVIDER};
diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h
index c1f9a2b6..6ccaba23 100644
--- a/launcher/minecraft/mod/ModFolderModel.h
+++ b/launcher/minecraft/mod/ModFolderModel.h
@@ -76,7 +76,7 @@ public:
Enable,
Toggle
};
- ModFolderModel(const QString &dir, std::shared_ptr<const BaseInstance> instance, bool is_indexed = false, bool create_dir = true);
+ ModFolderModel(const QString &dir, BaseInstance* instance, bool is_indexed = false, bool create_dir = true);
virtual QString id() const override { return "mods"; }
diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp
index ba64497b..838fca53 100644
--- a/launcher/minecraft/mod/ResourceFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourceFolderModel.cpp
@@ -19,7 +19,7 @@
#include "settings/Setting.h"
#include "tasks/Task.h"
-ResourceFolderModel::ResourceFolderModel(QDir dir, std::shared_ptr<const BaseInstance> instance, QObject* parent, bool create_dir)
+ResourceFolderModel::ResourceFolderModel(QDir dir, BaseInstance* instance, QObject* parent, bool create_dir)
: QAbstractListModel(parent), m_dir(dir), m_instance(instance), m_watcher(this)
{
if (create_dir) {
diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h
index 54627c80..e1dc685b 100644
--- a/launcher/minecraft/mod/ResourceFolderModel.h
+++ b/launcher/minecraft/mod/ResourceFolderModel.h
@@ -29,7 +29,7 @@ class QSortFilterProxyModel;
class ResourceFolderModel : public QAbstractListModel {
Q_OBJECT
public:
- ResourceFolderModel(QDir, std::shared_ptr<const BaseInstance>, QObject* parent = nullptr, bool create_dir = true);
+ ResourceFolderModel(QDir, BaseInstance* instance, QObject* parent = nullptr, bool create_dir = true);
~ResourceFolderModel() override;
virtual QString id() const { return "resource"; }
@@ -203,7 +203,7 @@ class ResourceFolderModel : public QAbstractListModel {
bool m_can_interact = true;
QDir m_dir;
- std::shared_ptr<const BaseInstance> m_instance;
+ BaseInstance* m_instance;
QFileSystemWatcher m_watcher;
bool m_is_watching = false;
diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp
index 7ec5668c..8a7b1049 100644
--- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp
@@ -47,7 +47,7 @@
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
#include "minecraft/mod/tasks/LocalResourcePackParseTask.h"
-ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, std::shared_ptr<const BaseInstance> instance)
+ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstance* instance)
: ResourceFolderModel(QDir(dir), instance)
{
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE};
diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.h b/launcher/minecraft/mod/ResourcePackFolderModel.h
index bbec9619..531d8192 100644
--- a/launcher/minecraft/mod/ResourcePackFolderModel.h
+++ b/launcher/minecraft/mod/ResourcePackFolderModel.h
@@ -18,7 +18,7 @@ public:
NUM_COLUMNS
};
- explicit ResourcePackFolderModel(const QString &dir, std::shared_ptr<const BaseInstance> instance);
+ explicit ResourcePackFolderModel(const QString &dir, BaseInstance* instance);
virtual QString id() const override { return "resourcepacks"; }
diff --git a/launcher/minecraft/mod/ShaderPackFolderModel.h b/launcher/minecraft/mod/ShaderPackFolderModel.h
index e010f6ed..f8249962 100644
--- a/launcher/minecraft/mod/ShaderPackFolderModel.h
+++ b/launcher/minecraft/mod/ShaderPackFolderModel.h
@@ -6,7 +6,7 @@ class ShaderPackFolderModel : public ResourceFolderModel {
Q_OBJECT
public:
- explicit ShaderPackFolderModel(const QString& dir, std::shared_ptr<const BaseInstance> instance)
+ explicit ShaderPackFolderModel(const QString& dir, BaseInstance* instance)
: ResourceFolderModel(QDir(dir), instance)
{}
diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp
index c88f8f37..76145b3b 100644
--- a/launcher/minecraft/mod/TexturePackFolderModel.cpp
+++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp
@@ -42,7 +42,7 @@
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
#include "minecraft/mod/tasks/LocalTexturePackParseTask.h"
-TexturePackFolderModel::TexturePackFolderModel(const QString& dir, std::shared_ptr<const BaseInstance> instance)
+TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* instance)
: ResourceFolderModel(QDir(dir), instance)
{
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE };
diff --git a/launcher/minecraft/mod/TexturePackFolderModel.h b/launcher/minecraft/mod/TexturePackFolderModel.h
index ce9c06c4..71a8bdd1 100644
--- a/launcher/minecraft/mod/TexturePackFolderModel.h
+++ b/launcher/minecraft/mod/TexturePackFolderModel.h
@@ -64,6 +64,7 @@ public:
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
[[nodiscard]] int columnCount(const QModelIndex &parent) const override;
+ explicit TexturePackFolderModel(const QString &dir, BaseInstance* instance);
[[nodiscard]] Task* createUpdateTask() override;
[[nodiscard]] Task* createParseTask(Resource&) override;
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<QNetworkReply>(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<QNetworkReply::NetworkError>::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<QNetworkReply>(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<QNetworkReply::NetworkError>::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<QSslError>& 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<QSslError>& 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<QNetworkReply>(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<QNetworkReply::NetworkError>::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<QSslError>& 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<QSslError>& 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<QNetworkReply>(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<QNetworkReply::NetworkError>::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<QSslError>& 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<QSslError>& errors);
void downloadFinished();
};
diff --git a/launcher/minecraft/update/AssetUpdateTask.cpp b/launcher/minecraft/update/AssetUpdateTask.cpp
index 8ccb0e1d..31fd5eb1 100644
--- a/launcher/minecraft/update/AssetUpdateTask.cpp
+++ b/launcher/minecraft/update/AssetUpdateTask.cpp
@@ -45,6 +45,7 @@ void AssetUpdateTask::executeTask()
connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetIndexFailed);
connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); });
connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress);
+ connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propogateStepProgress);
qDebug() << m_inst->name() << ": Starting asset index download";
downloadJob->start();
@@ -83,6 +84,7 @@ void AssetUpdateTask::assetIndexFinished()
connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetsFailed);
connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); });
connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress);
+ connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propogateStepProgress);
downloadJob->start();
return;
}
diff --git a/launcher/minecraft/update/FMLLibrariesTask.cpp b/launcher/minecraft/update/FMLLibrariesTask.cpp
index 96fd3ba3..75e5c572 100644
--- a/launcher/minecraft/update/FMLLibrariesTask.cpp
+++ b/launcher/minecraft/update/FMLLibrariesTask.cpp
@@ -75,6 +75,7 @@ void FMLLibrariesTask::executeTask()
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);
+ connect(dljob.get(), &NetJob::stepProgress, this, &FMLLibrariesTask::propogateStepProgress);
downloadJob.reset(dljob);
downloadJob->start();
}
diff --git a/launcher/minecraft/update/LibrariesTask.cpp b/launcher/minecraft/update/LibrariesTask.cpp
index b9410111..415b9a66 100644
--- a/launcher/minecraft/update/LibrariesTask.cpp
+++ b/launcher/minecraft/update/LibrariesTask.cpp
@@ -70,6 +70,8 @@ void LibrariesTask::executeTask()
connect(downloadJob.get(), &NetJob::failed, this, &LibrariesTask::jarlibFailed);
connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); });
connect(downloadJob.get(), &NetJob::progress, this, &LibrariesTask::progress);
+ connect(downloadJob.get(), &NetJob::stepProgress, this, &LibrariesTask::propogateStepProgress);
+
downloadJob->start();
}
diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h
index 40f1efc4..8d0223f9 100644
--- a/launcher/modplatform/ModIndex.h
+++ b/launcher/modplatform/ModIndex.h
@@ -23,6 +23,7 @@
#include <QString>
#include <QVariant>
#include <QVector>
+#include <memory>
class QIODevice;
@@ -83,6 +84,8 @@ struct ExtraPackData {
};
struct IndexedPack {
+ using Ptr = std::shared_ptr<IndexedPack>;
+
QVariant addonId;
ResourceProvider provider;
QString name;
diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp
index 4bd8b7f2..96cea7b7 100644
--- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp
+++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp
@@ -683,6 +683,7 @@ void PackInstallTask::installConfigs()
abortable = true;
setProgress(current, total);
});
+ connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress);
connect(jobPtr.get(), &NetJob::aborted, [&]{
abortable = false;
jobPtr.reset();
@@ -846,9 +847,11 @@ void PackInstallTask::downloadMods()
});
connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total)
{
+ setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
abortable = true;
setProgress(current, total);
});
+ connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress);
connect(jobPtr.get(), &NetJob::aborted, [&]
{
abortable = false;
diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp
index d3a737bb..83db642e 100644
--- a/launcher/modplatform/flame/FileResolvingTask.cpp
+++ b/launcher/modplatform/flame/FileResolvingTask.cpp
@@ -35,7 +35,29 @@ void Flame::FileResolvingTask::executeTask()
QByteArray data = Json::toText(object);
auto dl = Net::Upload::makeByteArray(QUrl("https://api.curseforge.com/v1/mods/files"), result.get(), data);
m_dljob->addNetAction(dl);
- connect(m_dljob.get(), &NetJob::finished, this, &Flame::FileResolvingTask::netJobFinished);
+
+ auto step_progress = std::make_shared<TaskStepProgress>();
+ connect(m_dljob.get(), &NetJob::finished, this, [this, step_progress]() {
+ step_progress->state = TaskStepState::Succeeded;
+ stepProgress(*step_progress);
+ netJobFinished();
+ });
+ connect(m_dljob.get(), &NetJob::failed, this, [this, step_progress](QString reason) {
+ step_progress->state = TaskStepState::Failed;
+ stepProgress(*step_progress);
+ emitFailed(reason);
+ });
+ connect(m_dljob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propogateStepProgress);
+ connect(m_dljob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) {
+ qDebug() << "Resolve slug progress" << current << total;
+ step_progress->update(current, total);
+ stepProgress(*step_progress);
+ });
+ connect(m_dljob.get(), &NetJob::status, this, [this, step_progress](QString status) {
+ step_progress->status = status;
+ stepProgress(*step_progress);
+ });
+
m_dljob->start();
}
@@ -44,7 +66,7 @@ void Flame::FileResolvingTask::netJobFinished()
setProgress(1, 3);
// job to check modrinth for blocked projects
m_checkJob.reset(new NetJob("Modrinth check", m_network));
- blockedProjects = QMap<File *,QByteArray *>();
+ blockedProjects = QMap<File*, std::shared_ptr<QByteArray>>();
QJsonDocument doc;
QJsonArray array;
@@ -71,8 +93,8 @@ void Flame::FileResolvingTask::netJobFinished()
auto hash = out.hash;
if(!hash.isEmpty()) {
auto url = QString("https://api.modrinth.com/v2/version_file/%1?algorithm=sha1").arg(hash);
- auto output = new QByteArray();
- auto dl = Net::Download::makeByteArray(QUrl(url), output);
+ auto output = std::make_shared<QByteArray>();
+ auto dl = Net::Download::makeByteArray(QUrl(url), output.get());
QObject::connect(dl.get(), &Net::Download::succeeded, [&out]() {
out.resolved = true;
});
@@ -82,7 +104,27 @@ void Flame::FileResolvingTask::netJobFinished()
}
}
}
- connect(m_checkJob.get(), &NetJob::finished, this, &Flame::FileResolvingTask::modrinthCheckFinished);
+ auto step_progress = std::make_shared<TaskStepProgress>();
+ connect(m_checkJob.get(), &NetJob::finished, this, [this, step_progress]() {
+ step_progress->state = TaskStepState::Succeeded;
+ stepProgress(*step_progress);
+ modrinthCheckFinished();
+ });
+ connect(m_checkJob.get(), &NetJob::failed, this, [this, step_progress](QString reason) {
+ step_progress->state = TaskStepState::Failed;
+ stepProgress(*step_progress);
+ emitFailed(reason);
+ });
+ connect(m_checkJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propogateStepProgress);
+ connect(m_checkJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) {
+ qDebug() << "Resolve slug progress" << current << total;
+ step_progress->update(current, total);
+ stepProgress(*step_progress);
+ });
+ connect(m_checkJob.get(), &NetJob::status, this, [this, step_progress](QString status) {
+ step_progress->status = status;
+ stepProgress(*step_progress);
+ });
m_checkJob->start();
}
@@ -95,7 +137,6 @@ void Flame::FileResolvingTask::modrinthCheckFinished() {
auto &out = *it;
auto bytes = blockedProjects[out];
if (!out->resolved) {
- delete bytes;
continue;
}
@@ -112,11 +153,9 @@ void Flame::FileResolvingTask::modrinthCheckFinished() {
} else {
out->resolved = false;
}
-
- delete bytes;
}
//copy to an output list and filter out projects found on modrinth
- auto block = new QList<File *>();
+ auto block = std::make_shared<QList<File*>>();
auto it = blockedProjects.keys();
std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File *f) {
return !f->resolved;
@@ -124,32 +163,48 @@ void Flame::FileResolvingTask::modrinthCheckFinished() {
//Display not found mods early
if (!block->empty()) {
//blocked mods found, we need the slug for displaying.... we need another job :D !
- auto slugJob = new NetJob("Slug Job", m_network);
- auto slugs = QVector<QByteArray>(block->size());
- auto index = 0;
- for (auto fileInfo: *block) {
- auto projectId = fileInfo->projectId;
- slugs[index] = QByteArray();
+ m_slugJob.reset(new NetJob("Slug Job", m_network));
+ int index = 0;
+ for (auto mod : *block) {
+ auto projectId = mod->projectId;
+ auto output = std::make_shared<QByteArray>();
auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId);
- auto dl = Net::Download::makeByteArray(url, &slugs[index]);
- slugJob->addNetAction(dl);
- index++;
- }
- connect(slugJob, &NetJob::succeeded, this, [slugs, this, slugJob, block]() {
- slugJob->deleteLater();
- auto index = 0;
- for (const auto &slugResult: slugs) {
- auto json = QJsonDocument::fromJson(slugResult);
+ auto dl = Net::Download::makeByteArray(url, output.get());
+ qDebug() << "Fetching url slug for file:" << mod->fileName;
+ QObject::connect(dl.get(), &Net::Download::succeeded, [block, index, output]() {
+ auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done
+ auto json = QJsonDocument::fromJson(*output);
auto base = Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json),"data"),"links"),
"websiteUrl");
- auto mod = block->at(index);
auto link = QString("%1/download/%2").arg(base, QString::number(mod->fileId));
mod->websiteUrl = link;
- index++;
- }
+ });
+ m_slugJob->addNetAction(dl);
+ index++;
+ }
+ auto step_progress = std::make_shared<TaskStepProgress>();
+ connect(m_slugJob.get(), &NetJob::succeeded, this, [this, step_progress]() {
+ step_progress->state = TaskStepState::Succeeded;
+ stepProgress(*step_progress);
emitSucceeded();
});
- slugJob->start();
+ connect(m_slugJob.get(), &NetJob::failed, this, [this, step_progress](QString reason) {
+ step_progress->state = TaskStepState::Failed;
+ stepProgress(*step_progress);
+ emitFailed(reason);
+ });
+ connect(m_slugJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propogateStepProgress);
+ connect(m_slugJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) {
+ qDebug() << "Resolve slug progress" << current << total;
+ step_progress->update(current, total);
+ stepProgress(*step_progress);
+ });
+ connect(m_slugJob.get(), &NetJob::status, this, [this, step_progress](QString status) {
+ step_progress->status = status;
+ stepProgress(*step_progress);
+ });
+
+ m_slugJob->start();
} else {
emitSucceeded();
}
diff --git a/launcher/modplatform/flame/FileResolvingTask.h b/launcher/modplatform/flame/FileResolvingTask.h
index 8fc17ea9..c280827a 100644
--- a/launcher/modplatform/flame/FileResolvingTask.h
+++ b/launcher/modplatform/flame/FileResolvingTask.h
@@ -1,41 +1,37 @@
#pragma once
-#include "tasks/Task.h"
-#include "net/NetJob.h"
#include "PackManifest.h"
+#include "net/NetJob.h"
+#include "tasks/Task.h"
-namespace Flame
-{
-class FileResolvingTask : public Task
-{
+namespace Flame {
+class FileResolvingTask : public Task {
Q_OBJECT
-public:
- explicit FileResolvingTask(const shared_qobject_ptr<QNetworkAccessManager>& network, Flame::Manifest &toProcess);
- virtual ~FileResolvingTask() {};
+ public:
+ explicit FileResolvingTask(const shared_qobject_ptr<QNetworkAccessManager>& network, Flame::Manifest& toProcess);
+ virtual ~FileResolvingTask(){};
bool canAbort() const override { return true; }
bool abort() override;
- const Flame::Manifest &getResults() const
- {
- return m_toProcess;
- }
+ const Flame::Manifest& getResults() const { return m_toProcess; }
-protected:
+ protected:
virtual void executeTask() override;
-protected slots:
+ protected slots:
void netJobFinished();
-private: /* data */
+ private: /* data */
shared_qobject_ptr<QNetworkAccessManager> m_network;
Flame::Manifest m_toProcess;
- std::shared_ptr<QByteArray> result;
+ std::shared_ptr<QByteArray> result;
NetJob::Ptr m_dljob;
- NetJob::Ptr m_checkJob;
+ NetJob::Ptr m_checkJob;
+ NetJob::Ptr m_slugJob;
void modrinthCheckFinished();
- QMap<File *, QByteArray *> blockedProjects;
+ QMap<File*, std::shared_ptr<QByteArray>> blockedProjects;
};
-}
+} // namespace Flame
diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp
index 5ef9a409..92590a08 100644
--- a/launcher/modplatform/flame/FlameAPI.cpp
+++ b/launcher/modplatform/flame/FlameAPI.cpp
@@ -38,14 +38,14 @@ auto FlameAPI::getModFileChangelog(int modId, int fileId) -> QString
QEventLoop lock;
QString changelog;
- auto* netJob = new NetJob(QString("Flame::FileChangelog"), APPLICATION->network());
- auto* response = new QByteArray();
+ auto netJob = makeShared<NetJob>(QString("Flame::FileChangelog"), APPLICATION->network());
+ auto response = std::make_shared<QByteArray>();
netJob->addNetAction(Net::Download::makeByteArray(
QString("https://api.curseforge.com/v1/mods/%1/files/%2/changelog")
.arg(QString::fromStdString(std::to_string(modId)), QString::fromStdString(std::to_string(fileId))),
- response));
+ response.get()));
- QObject::connect(netJob, &NetJob::succeeded, [netJob, response, &changelog] {
+ QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &changelog] {
QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
if (parse_error.error != QJsonParseError::NoError) {
@@ -60,10 +60,7 @@ auto FlameAPI::getModFileChangelog(int modId, int fileId) -> QString
changelog = Json::ensureString(doc.object(), "data");
});
- QObject::connect(netJob, &NetJob::finished, [response, &lock] {
- delete response;
- lock.quit();
- });
+ QObject::connect(netJob.get(), &NetJob::finished, [&lock] { lock.quit(); });
netJob->start();
lock.exec();
@@ -76,13 +73,12 @@ auto FlameAPI::getModDescription(int modId) -> QString
QEventLoop lock;
QString description;
- auto* netJob = new NetJob(QString("Flame::ModDescription"), APPLICATION->network());
- auto* response = new QByteArray();
+ auto netJob = makeShared<NetJob>(QString("Flame::ModDescription"), APPLICATION->network());
+ auto response = std::make_shared<QByteArray>();
netJob->addNetAction(Net::Download::makeByteArray(
- QString("https://api.curseforge.com/v1/mods/%1/description")
- .arg(QString::number(modId)), response));
+ QString("https://api.curseforge.com/v1/mods/%1/description").arg(QString::number(modId)), response.get()));
- QObject::connect(netJob, &NetJob::succeeded, [netJob, response, &description] {
+ QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &description] {
QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
if (parse_error.error != QJsonParseError::NoError) {
@@ -97,10 +93,7 @@ auto FlameAPI::getModDescription(int modId) -> QString
description = Json::ensureString(doc.object(), "data");
});
- QObject::connect(netJob, &NetJob::finished, [response, &lock] {
- delete response;
- lock.quit();
- });
+ QObject::connect(netJob.get(), &NetJob::finished, [&lock] { lock.quit(); });
netJob->start();
lock.exec();
@@ -118,13 +111,13 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe
QEventLoop loop;
- auto netJob = new NetJob(QString("Flame::GetLatestVersion(%1)").arg(args.pack.name), APPLICATION->network());
- auto response = new QByteArray();
+ auto netJob = makeShared<NetJob>(QString("Flame::GetLatestVersion(%1)").arg(args.pack.name), APPLICATION->network());
+ auto response = std::make_shared<QByteArray>();
ModPlatform::IndexedVersion ver;
- netJob->addNetAction(Net::Download::makeByteArray(versions_url, response));
+ netJob->addNetAction(Net::Download::makeByteArray(versions_url, response.get()));
- QObject::connect(netJob, &NetJob::succeeded, [response, args, &ver] {
+ QObject::connect(netJob.get(), &NetJob::succeeded, [response, args, &ver] {
QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
if (parse_error.error != QJsonParseError::NoError) {
@@ -158,11 +151,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe
}
});
- QObject::connect(netJob, &NetJob::finished, [response, netJob, &loop] {
- netJob->deleteLater();
- delete response;
- loop.quit();
- });
+ QObject::connect(netJob.get(), &NetJob::finished, [&loop] { loop.quit(); });
netJob->start();
diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
index 964b559c..dae93d1c 100644
--- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
+++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
@@ -35,6 +35,7 @@
#include "FlameInstanceCreationTask.h"
+#include "modplatform/flame/FileResolvingTask.h"
#include "modplatform/flame/FlameAPI.h"
#include "modplatform/flame/PackManifest.h"
@@ -382,7 +383,8 @@ bool FlameCreationTask::createInstance()
});
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::progress, this, &FlameCreationTask::setProgress);
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::status, this, &FlameCreationTask::setStatus);
-
+ connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::stepProgress, this, &FlameCreationTask::propogateStepProgress);
+ connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::details, this, &FlameCreationTask::setDetails);
m_mod_id_resolver->start();
loop.exec();
@@ -452,7 +454,7 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop)
void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
{
- m_files_job.reset(new NetJob(tr("Mod download"), APPLICATION->network()));
+ m_files_job.reset(new NetJob(tr("Mod Download Flame"), APPLICATION->network()));
for (const auto& result : m_mod_id_resolver->getResults().files) {
QString filename = result.fileName;
if (!result.required) {
@@ -496,7 +498,11 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
m_files_job.reset();
setError(reason);
});
- connect(m_files_job.get(), &NetJob::progress, this, &FlameCreationTask::setProgress);
+ connect(m_files_job.get(), &NetJob::progress, this, [this](qint64 current, qint64 total){
+ setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
+ setProgress(current, total);
+ });
+ connect(m_files_job.get(), &NetJob::stepProgress, this, &FlameCreationTask::propogateStepProgress);
connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit);
setStatus(tr("Downloading mods..."));
diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.cpp b/launcher/modplatform/helpers/NetworkResourceAPI.cpp
index 010ac15e..a3c592fd 100644
--- a/launcher/modplatform/helpers/NetworkResourceAPI.cpp
+++ b/launcher/modplatform/helpers/NetworkResourceAPI.cpp
@@ -24,7 +24,7 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&
netJob->addNetAction(Net::Download::makeByteArray(QUrl(search_url), response));
- QObject::connect(netJob.get(), &NetJob::succeeded, [=]{
+ QObject::connect(netJob.get(), &NetJob::succeeded, [this, response, callbacks]{
QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
if (parse_error.error != QJsonParseError::NoError) {
@@ -40,16 +40,20 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&
callbacks.on_succeed(doc);
});
- QObject::connect(netJob.get(), &NetJob::failed, [=](QString reason){
+ QObject::connect(netJob.get(), &NetJob::failed, [&netJob, callbacks](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.get(), &NetJob::aborted, [=]{
+ QObject::connect(netJob.get(), &NetJob::aborted, [callbacks]{
callbacks.on_abort();
});
+ QObject::connect(netJob.get(), &NetJob::finished, [response] {
+ delete response;
+ });
+
return netJob;
}
@@ -88,7 +92,7 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi
netJob->addNetAction(Net::Download::makeByteArray(versions_url, response));
- QObject::connect(netJob.get(), &NetJob::succeeded, [=] {
+ QObject::connect(netJob.get(), &NetJob::succeeded, [response, callbacks, args] {
QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
if (parse_error.error != QJsonParseError::NoError) {
diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp
index 8d45fc5c..36c142ac 100644
--- a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp
+++ b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp
@@ -81,6 +81,7 @@ void PackInstallTask::downloadPack()
connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded);
connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed);
connect(netJobContainer.get(), &NetJob::progress, this, &PackInstallTask::onDownloadProgress);
+ connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress);
connect(netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::onDownloadAborted);
netJobContainer->start();
diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp
index 6814e645..bb8227aa 100644
--- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp
+++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp
@@ -11,6 +11,7 @@
#include "net/ChecksumValidator.h"
+#include "net/NetJob.h"
#include "settings/INISettingsObject.h"
#include "ui/dialogs/CustomMessageBox.h"
@@ -223,7 +224,7 @@ bool ModrinthCreationTask::createInstance()
instance.setName(name());
instance.saveNow();
- m_files_job.reset(new NetJob(tr("Mod download"), APPLICATION->network()));
+ m_files_job.reset(new NetJob(tr("Mod Download Modrinth"), APPLICATION->network()));
auto root_modpack_path = FS::PathCombine(m_stagingPath, ".minecraft");
auto root_modpack_url = QUrl::fromLocalFile(root_modpack_path);
@@ -262,7 +263,11 @@ bool ModrinthCreationTask::createInstance()
setError(reason);
});
connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit);
- connect(m_files_job.get(), &NetJob::progress, [&](qint64 current, qint64 total) { setProgress(current, total); });
+ connect(m_files_job.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
+ setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
+ setProgress(current, total);
+ });
+ connect(m_files_job.get(), &NetJob::stepProgress, this, &ModrinthCreationTask::propogateStepProgress);
setStatus(tr("Downloading mods..."));
m_files_job->start();
diff --git a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp
index 8fd43d21..f07ca24a 100644
--- a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp
+++ b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp
@@ -50,6 +50,7 @@ void Technic::SingleZipPackInstallTask::executeTask()
auto job = m_filesNetJob.get();
connect(job, &NetJob::succeeded, this, &Technic::SingleZipPackInstallTask::downloadSucceeded);
connect(job, &NetJob::progress, this, &Technic::SingleZipPackInstallTask::downloadProgressChanged);
+ connect(job, &NetJob::stepProgress, this, &Technic::SingleZipPackInstallTask::propogateStepProgress);
connect(job, &NetJob::failed, this, &Technic::SingleZipPackInstallTask::downloadFailed);
m_filesNetJob->start();
}
diff --git a/launcher/modplatform/technic/SolderPackInstallTask.cpp b/launcher/modplatform/technic/SolderPackInstallTask.cpp
index 77c503f0..c26d6a5a 100644
--- a/launcher/modplatform/technic/SolderPackInstallTask.cpp
+++ b/launcher/modplatform/technic/SolderPackInstallTask.cpp
@@ -127,6 +127,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded()
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &Technic::SolderPackInstallTask::downloadSucceeded);
connect(m_filesNetJob.get(), &NetJob::progress, this, &Technic::SolderPackInstallTask::downloadProgressChanged);
+ connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &Technic::SolderPackInstallTask::propogateStepProgress);
connect(m_filesNetJob.get(), &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);
connect(m_filesNetJob.get(), &NetJob::aborted, this, &Technic::SolderPackInstallTask::downloadAborted);
m_filesNetJob->start();
diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp
index e8a1d0b0..7f8d3a06 100644
--- a/launcher/net/Download.cpp
+++ b/launcher/net/Download.cpp
@@ -4,6 +4,7 @@
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
+ * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
* 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
@@ -36,17 +37,23 @@
*/
#include "Download.h"
+#include <QUrl>
#include <QDateTime>
#include <QFileInfo>
#include "ByteArraySink.h"
#include "ChecksumValidator.h"
-#include "FileSystem.h"
#include "MetaCacheSink.h"
-#include "BuildConfig.h"
#include "Application.h"
+#include "BuildConfig.h"
+
+#include "net/Logging.h"
+#include "net/NetAction.h"
+
+#include "MMCTime.h"
+#include "StringUtils.h"
namespace Net {
@@ -54,6 +61,7 @@ auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Down
{
auto dl = makeShared<Download>();
dl->m_url = url;
+ dl->setObjectName(QString("CACHE:") + url.toString());
dl->m_options = options;
auto md5Node = new ChecksumValidator(QCryptographicHash::Md5);
auto cachedNode = new MetaCacheSink(entry, md5Node, options.testFlag(Option::MakeEternal));
@@ -65,6 +73,7 @@ auto Download::makeByteArray(QUrl url, QByteArray* output, Options options) -> D
{
auto dl = makeShared<Download>();
dl->m_url = url;
+ dl->setObjectName(QString("BYTES:") + url.toString());
dl->m_options = options;
dl->m_sink.reset(new ByteArraySink(output));
return dl;
@@ -74,6 +83,7 @@ auto Download::makeFile(QUrl url, QString path, Options options) -> Download::Pt
{
auto dl = makeShared<Download>();
dl->m_url = url;
+ dl->setObjectName(QString("FILE:") + url.toString());
dl->m_options = options;
dl->m_sink.reset(new FileSink(path));
return dl;
@@ -86,10 +96,10 @@ void Download::addValidator(Validator* v)
void Download::executeTask()
{
- setStatus(tr("Downloading %1").arg(m_url.toString()));
+ setStatus(tr("Downloading %1").arg(StringUtils::truncateUrlHumanFriendly(m_url, 80)));
if (getState() == Task::State::AbortedByUser) {
- qWarning() << "Attempt to start an aborted Download:" << m_url.toString();
+ qCWarning(taskDownloadLogC) << getUid().toString() << "Attempt to start an aborted Download:" << m_url.toString();
emitAborted();
return;
}
@@ -99,10 +109,10 @@ void Download::executeTask()
switch (m_state) {
case State::Succeeded:
emit succeeded();
- qDebug() << "Download cache hit " << m_url.toString();
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Download cache hit " << m_url.toString();
return;
case State::Running:
- qDebug() << "Downloading " << m_url.toString();
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Downloading " << m_url.toString();
break;
case State::Inactive:
case State::Failed:
@@ -124,15 +134,21 @@ void Download::executeTask()
request.setRawHeader("Authorization", token.toUtf8());
}
- QNetworkReply* rep = m_network->get(request);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+ request.setTransferTimeout();
+#endif
+ m_last_progress_time = m_clock.now();
+ m_last_progress_bytes = 0;
+
+ QNetworkReply* rep = m_network->get(request);
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<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &Download::downloadError);
#endif
connect(rep, &QNetworkReply::sslErrors, this, &Download::sslErrors);
connect(rep, &QNetworkReply::readyRead, this, &Download::downloadReadyRead);
@@ -140,13 +156,39 @@ void Download::executeTask()
void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
+ auto now = m_clock.now();
+ auto elapsed = now - m_last_progress_time;
+
+ // use milliseconds for speed precision
+ auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed);
+ auto bytes_received_since = bytesReceived - m_last_progress_bytes;
+ auto dl_speed_bps = (double)bytes_received_since / elapsed_ms.count() * 1000;
+ auto remaing_time_s = (bytesTotal - bytesReceived) / dl_speed_bps;
+
+ //: Current amount of bytes downloaded, out of the total amount of bytes in the download
+ QString dl_progress =
+ tr("%1 / %2").arg(StringUtils::humanReadableFileSize(bytesReceived)).arg(StringUtils::humanReadableFileSize(bytesTotal));
+
+ QString dl_speed_str;
+ if (elapsed_ms.count() > 0) {
+ auto str_eta = bytesTotal > 0 ? Time::humanReadableDuration(remaing_time_s) : tr("unknown");
+ //: Download speed, in bytes per second (remaining download time in parenthesis)
+ dl_speed_str =
+ tr("%1 /s (%2)").arg(StringUtils::humanReadableFileSize(dl_speed_bps)).arg(str_eta);
+ } else {
+ //: Download speed at 0 bytes per second
+ dl_speed_str = tr("0 B/s");
+ }
+
+ setDetails(dl_progress + "\n" + dl_speed_str);
+
setProgress(bytesReceived, bytesTotal);
}
void Download::downloadError(QNetworkReply::NetworkError error)
{
if (error == QNetworkReply::OperationCanceledError) {
- qCritical() << "Aborted " << m_url.toString();
+ qCCritical(taskDownloadLogC) << getUid().toString() << "Aborted " << m_url.toString();
m_state = State::AbortedByUser;
} else {
if (m_options & Option::AcceptLocalFiles) {
@@ -156,7 +198,7 @@ void Download::downloadError(QNetworkReply::NetworkError error)
}
}
// error happened during download.
- qCritical() << "Failed " << m_url.toString() << " with reason " << error;
+ qCCritical(taskDownloadLogC) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error;
m_state = State::Failed;
}
}
@@ -165,9 +207,10 @@ void Download::sslErrors(const QList<QSslError>& errors)
{
int i = 1;
for (auto error : errors) {
- qCritical() << "Download" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString();
+ qCCritical(taskDownloadLogC) << getUid().toString() << "Download" << m_url.toString() << "SSL Error #" << i << " : "
+ << error.errorString();
auto cert = error.certificate();
- qCritical() << "Certificate in question:\n" << cert.toText();
+ qCCritical(taskDownloadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText();
i++;
}
}
@@ -210,17 +253,17 @@ auto Download::handleRedirect() -> bool
*/
redirect = QUrl(redirectStr, QUrl::TolerantMode);
if (!redirect.isValid()) {
- qWarning() << "Failed to parse redirect URL:" << redirectStr;
+ qCWarning(taskDownloadLogC) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr;
downloadError(QNetworkReply::ProtocolFailure);
return false;
}
- qDebug() << "Fixed location header:" << redirect;
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Fixed location header:" << redirect;
} else {
- qDebug() << "Location header:" << redirect;
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Location header:" << redirect;
}
m_url = QUrl(redirect.toString());
- qDebug() << "Following redirect to " << m_url.toString();
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Following redirect to " << m_url.toString();
startAction(m_network);
return true;
@@ -230,26 +273,26 @@ void Download::downloadFinished()
{
// handle HTTP redirection first
if (handleRedirect()) {
- qDebug() << "Download redirected:" << m_url.toString();
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Download redirected:" << m_url.toString();
return;
}
// if the download failed before this point ...
if (m_state == State::Succeeded) // pretend to succeed so we continue processing :)
{
- qDebug() << "Download failed but we are allowed to proceed:" << m_url.toString();
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed but we are allowed to proceed:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit succeeded();
return;
} else if (m_state == State::Failed) {
- qDebug() << "Download failed in previous step:" << m_url.toString();
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed in previous step:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit failed("");
return;
} else if (m_state == State::AbortedByUser) {
- qDebug() << "Download aborted in previous step:" << m_url.toString();
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Download aborted in previous step:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit aborted();
@@ -259,14 +302,14 @@ void Download::downloadFinished()
// make sure we got all the remaining data, if any
auto data = m_reply->readAll();
if (data.size()) {
- qDebug() << "Writing extra" << data.size() << "bytes";
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Writing extra" << data.size() << "bytes";
m_state = m_sink->write(data);
}
// otherwise, finalize the whole graph
m_state = m_sink->finalize(*m_reply.get());
if (m_state != State::Succeeded) {
- qDebug() << "Download failed to finalize:" << m_url.toString();
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed to finalize:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit failed("");
@@ -274,7 +317,7 @@ void Download::downloadFinished()
}
m_reply.reset();
- qDebug() << "Download succeeded:" << m_url.toString();
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Download succeeded:" << m_url.toString();
emit succeeded();
}
@@ -284,11 +327,11 @@ void Download::downloadReadyRead()
auto data = m_reply->readAll();
m_state = m_sink->write(data);
if (m_state == State::Failed) {
- qCritical() << "Failed to process response chunk";
+ qCCritical(taskDownloadLogC) << getUid().toString() << "Failed to process response chunk";
}
// qDebug() << "Download" << m_url.toString() << "gained" << data.size() << "bytes";
} else {
- qCritical() << "Cannot write download data! illegal status " << m_status;
+ qCCritical(taskDownloadLogC) << getUid().toString() << "Cannot write download data! illegal status " << m_status;
}
}
diff --git a/launcher/net/Download.h b/launcher/net/Download.h
index 7e1df322..920164a3 100644
--- a/launcher/net/Download.h
+++ b/launcher/net/Download.h
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
- * PolyMC - Minecraft Launcher
+ * Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
* 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
@@ -22,6 +23,7 @@
* 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
*
@@ -36,6 +38,8 @@
#pragma once
+#include <chrono>
+
#include "HttpMetaCache.h"
#include "NetAction.h"
#include "Sink.h"
@@ -70,7 +74,7 @@ class Download : public NetAction {
protected slots:
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;
void downloadError(QNetworkReply::NetworkError error) override;
- void sslErrors(const QList<QSslError>& errors);
+ void sslErrors(const QList<QSslError>& errors) override;
void downloadFinished() override;
void downloadReadyRead() override;
@@ -80,6 +84,10 @@ class Download : public NetAction {
private:
std::unique_ptr<Sink> m_sink;
Options m_options;
+
+ std::chrono::steady_clock m_clock;
+ std::chrono::time_point<std::chrono::steady_clock> m_last_progress_time;
+ qint64 m_last_progress_bytes;
};
} // namespace Net
diff --git a/launcher/net/FileSink.cpp b/launcher/net/FileSink.cpp
index ba0caf6c..1ecb21fd 100644
--- a/launcher/net/FileSink.cpp
+++ b/launcher/net/FileSink.cpp
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
- * PolyMC - Minecraft Launcher
+ * Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
@@ -37,6 +37,8 @@
#include "FileSystem.h"
+#include "net/Logging.h"
+
namespace Net {
Task::State FileSink::init(QNetworkRequest& request)
@@ -48,14 +50,14 @@ Task::State FileSink::init(QNetworkRequest& request)
// create a new save file and open it for writing
if (!FS::ensureFilePathExists(m_filename)) {
- qCritical() << "Could not create folder for " + m_filename;
+ qCCritical(taskNetLogC) << "Could not create folder for " + m_filename;
return Task::State::Failed;
}
wroteAnyData = false;
m_output_file.reset(new QSaveFile(m_filename));
if (!m_output_file->open(QIODevice::WriteOnly)) {
- qCritical() << "Could not open " + m_filename + " for writing";
+ qCCritical(taskNetLogC) << "Could not open " + m_filename + " for writing";
return Task::State::Failed;
}
@@ -67,7 +69,7 @@ Task::State FileSink::init(QNetworkRequest& request)
Task::State FileSink::write(QByteArray& data)
{
if (!writeAllValidators(data) || m_output_file->write(data) != data.size()) {
- qCritical() << "Failed writing into " + m_filename;
+ qCCritical(taskNetLogC) << "Failed writing into " + m_filename;
m_output_file->cancelWriting();
m_output_file.reset();
wroteAnyData = false;
@@ -106,7 +108,7 @@ Task::State FileSink::finalize(QNetworkReply& reply)
// nothing went wrong...
if (!m_output_file->commit()) {
- qCritical() << "Failed to commit changes to " << m_filename;
+ qCCritical(taskNetLogC) << "Failed to commit changes to " << m_filename;
m_output_file->cancelWriting();
return Task::State::Failed;
}
diff --git a/launcher/net/FileSink.h b/launcher/net/FileSink.h
index dffbdca6..40134b5f 100644
--- a/launcher/net/FileSink.h
+++ b/launcher/net/FileSink.h
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
- * PolyMC - Minecraft Launcher
+ * Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
diff --git a/launcher/net/HttpMetaCache.cpp b/launcher/net/HttpMetaCache.cpp
index 0d7ca769..689dbac9 100644
--- a/launcher/net/HttpMetaCache.cpp
+++ b/launcher/net/HttpMetaCache.cpp
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
- * PolyMC - Minecraft Launcher
+ * Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
@@ -44,6 +44,8 @@
#include <QDebug>
+#include "net/Logging.h"
+
auto MetaEntry::getFullPath() -> QString
{
// FIXME: make local?
@@ -55,7 +57,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()
@@ -124,7 +126,7 @@ auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString ex
// Get rid of old entries, to prevent cache problems
auto current_time = QDateTime::currentSecsSinceEpoch();
if (entry->isExpired(current_time - ( file_last_changed / 1000 ))) {
- qWarning() << "Removing cache entry because of old age!";
+ qCWarning(taskNetLogC) << "[HttpMetaCache]" << "Removing cache entry because of old age!";
selected_base.entry_list.remove(resource_path);
return staleEntry(base, resource_path);
}
@@ -137,12 +139,12 @@ auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString ex
auto HttpMetaCache::updateEntry(MetaEntryPtr stale_entry) -> bool
{
if (!m_entries.contains(stale_entry->m_baseId)) {
- qCritical() << "Cannot add entry with unknown base: " << stale_entry->m_baseId.toLocal8Bit();
+ qCCritical(taskHttpMetaCacheLogC) << "Cannot add entry with unknown base: " << stale_entry->m_baseId.toLocal8Bit();
return false;
}
if (stale_entry->m_stale) {
- qCritical() << "Cannot add stale entry: " << stale_entry->getFullPath().toLocal8Bit();
+ qCCritical(taskHttpMetaCacheLogC) << "Cannot add stale entry: " << stale_entry->getFullPath().toLocal8Bit();
return false;
}
@@ -166,10 +168,10 @@ void HttpMetaCache::evictAll()
{
for (QString& base : m_entries.keys()) {
EntryMap& map = m_entries[base];
- qDebug() << "Evicting base" << base;
+ qCDebug(taskHttpMetaCacheLogC) << "Evicting base" << base;
for (MetaEntryPtr entry : map.entry_list) {
if (!evictEntry(entry))
- qWarning() << "Unexpected missing cache entry" << entry->m_basePath;
+ qCWarning(taskHttpMetaCacheLogC) << "Unexpected missing cache entry" << entry->m_basePath;
}
}
}
@@ -267,7 +269,7 @@ void HttpMetaCache::SaveNow()
if (m_index_file.isNull())
return;
- qDebug() << "[HttpMetaCache]" << "Saving metacache with" << m_entries.size() << "entries";
+ qCDebug(taskHttpMetaCacheLogC) << "Saving metacache with" << m_entries.size() << "entries";
QJsonObject toplevel;
Json::writeString(toplevel, "version", "1");
@@ -302,6 +304,6 @@ void HttpMetaCache::SaveNow()
try {
Json::write(toplevel, m_index_file);
} catch (const Exception& e) {
- qWarning() << e.what();
+ qCWarning(taskHttpMetaCacheLogC) << "Error writing cache:" << e.what();
}
}
diff --git a/launcher/net/HttpMetaCache.h b/launcher/net/HttpMetaCache.h
index 37f4b49a..0dcb5668 100644
--- a/launcher/net/HttpMetaCache.h
+++ b/launcher/net/HttpMetaCache.h
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
- * PolyMC - Minecraft Launcher
+ * Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
diff --git a/launcher/net/Logging.cpp b/launcher/net/Logging.cpp
new file mode 100644
index 00000000..a9b9db7c
--- /dev/null
+++ b/launcher/net/Logging.cpp
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "net/Logging.h"
+
+Q_LOGGING_CATEGORY(taskNetLogC, "launcher.task.net")
+Q_LOGGING_CATEGORY(taskDownloadLogC, "launcher.task.net.download")
+Q_LOGGING_CATEGORY(taskUploadLogC, "launcher.task.net.upload")
+Q_LOGGING_CATEGORY(taskMetaCacheLogC, "launcher.task.net.metacache")
+Q_LOGGING_CATEGORY(taskHttpMetaCacheLogC, "launcher.task.net.metacache.http")
diff --git a/launcher/net/Logging.h b/launcher/net/Logging.h
new file mode 100644
index 00000000..b692e707
--- /dev/null
+++ b/launcher/net/Logging.h
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <QLoggingCategory>
+
+Q_DECLARE_LOGGING_CATEGORY(taskNetLogC)
+Q_DECLARE_LOGGING_CATEGORY(taskDownloadLogC)
+Q_DECLARE_LOGGING_CATEGORY(taskUploadLogC)
+Q_DECLARE_LOGGING_CATEGORY(taskMetaCacheLogC)
+Q_DECLARE_LOGGING_CATEGORY(taskHttpMetaCacheLogC)
diff --git a/launcher/net/MetaCacheSink.cpp b/launcher/net/MetaCacheSink.cpp
index c730fdbf..e203bc06 100644
--- a/launcher/net/MetaCacheSink.cpp
+++ b/launcher/net/MetaCacheSink.cpp
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
- * PolyMC - Minecraft Launcher
+ * Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
@@ -39,6 +39,8 @@
#include <QRegularExpression>
#include "Application.h"
+#include "net/Logging.h"
+
namespace Net {
/** Maximum time to hold a cache entry
@@ -97,11 +99,11 @@ Task::State MetaCacheSink::finalizeCache(QNetworkReply & reply)
{ // Cache lifetime
if (m_is_eternal) {
- qDebug() << "[MetaCache] Adding eternal cache entry:" << m_entry->getFullPath();
+ qCDebug(taskMetaCacheLogC) << "Adding eternal cache entry:" << m_entry->getFullPath();
m_entry->makeEternal(true);
} else if (reply.hasRawHeader("Cache-Control")) {
auto cache_control_header = reply.rawHeader("Cache-Control");
- // qDebug() << "[MetaCache] Parsing 'Cache-Control' header with" << cache_control_header;
+ qCDebug(taskMetaCacheLogC) << "Parsing 'Cache-Control' header with" << cache_control_header;
QRegularExpression max_age_expr("max-age=([0-9]+)");
qint64 max_age = max_age_expr.match(cache_control_header).captured(1).toLongLong();
@@ -109,7 +111,7 @@ Task::State MetaCacheSink::finalizeCache(QNetworkReply & reply)
} else if (reply.hasRawHeader("Expires")) {
auto expires_header = reply.rawHeader("Expires");
- // qDebug() << "[MetaCache] Parsing 'Expires' header with" << expires_header;
+ qCDebug(taskMetaCacheLogC) << "Parsing 'Expires' header with" << expires_header;
qint64 max_age = QDateTime::fromString(expires_header).toSecsSinceEpoch() - QDateTime::currentSecsSinceEpoch();
m_entry->setMaximumAge(max_age);
@@ -119,7 +121,7 @@ Task::State MetaCacheSink::finalizeCache(QNetworkReply & reply)
if (reply.hasRawHeader("Age")) {
auto age_header = reply.rawHeader("Age");
- // qDebug() << "[MetaCache] Parsing 'Age' header with" << age_header;
+ qCDebug(taskMetaCacheLogC) << "Parsing 'Age' header with" << age_header;
qint64 current_age = age_header.toLongLong();
m_entry->setCurrentAge(current_age);
diff --git a/launcher/net/MetaCacheSink.h b/launcher/net/MetaCacheSink.h
index f5948085..f9f7d233 100644
--- a/launcher/net/MetaCacheSink.h
+++ b/launcher/net/MetaCacheSink.h
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
- * PolyMC - Minecraft Launcher
+ * Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
diff --git a/launcher/net/NetAction.h b/launcher/net/NetAction.h
index 38fe058b..ab9322c2 100644
--- a/launcher/net/NetAction.h
+++ b/launcher/net/NetAction.h
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
- * PolyMC - Minecraft Launcher
+ * Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
+ * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
* 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
@@ -44,7 +45,7 @@
class NetAction : public Task {
Q_OBJECT
protected:
- explicit NetAction() : Task() {};
+ explicit NetAction() : Task(){};
public:
using Ptr = shared_qobject_ptr<NetAction>;
@@ -61,6 +62,17 @@ class NetAction : public Task {
virtual void downloadFinished() = 0;
virtual void downloadReadyRead() = 0;
+ virtual void sslErrors(const QList<QSslError>& 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<QNetworkAccessManager> network)
{
@@ -69,7 +81,7 @@ class NetAction : public Task {
}
protected:
- void executeTask() override {};
+ void executeTask() override{};
public:
shared_qobject_ptr<QNetworkAccessManager> m_network;
diff --git a/launcher/net/NetJob.cpp b/launcher/net/NetJob.cpp
index 4bcd40b5..3869316e 100644
--- a/launcher/net/NetJob.cpp
+++ b/launcher/net/NetJob.cpp
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
- * PolyMC - Minecraft Launcher
+ * Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
* 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
diff --git a/launcher/net/NetJob.h b/launcher/net/NetJob.h
index cd5d5e48..764cec18 100644
--- a/launcher/net/NetJob.h
+++ b/launcher/net/NetJob.h
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
- * PolyMC - Minecraft Launcher
+ * Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
+ * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
* 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
diff --git a/launcher/net/PasteUpload.cpp b/launcher/net/PasteUpload.cpp
index d9e785c5..595279a3 100644
--- a/launcher/net/PasteUpload.cpp
+++ b/launcher/net/PasteUpload.cpp
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
- * PolyMC - Minecraft Launcher
+ * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Lenny McLennington <lenny@sneed.church>
* Copyright (C) 2022 Swirl <swurl@swurl.xyz>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
@@ -47,6 +47,8 @@
#include <QFile>
#include <QUrlQuery>
+#include "net/Logging.h"
+
std::array<PasteUpload::PasteTypeInfo, 4> PasteUpload::PasteTypes = {
{{"0x0.st", "https://0x0.st", ""},
{"hastebin", "https://hst.sh", "/documents"},
@@ -147,7 +149,7 @@ void PasteUpload::executeTask()
void PasteUpload::downloadError(QNetworkReply::NetworkError error)
{
// error happened during download.
- qCritical() << "Network error: " << error;
+ qCCritical(taskUploadLogC) << getUid().toString() << "Network error: " << error;
emitFailed(m_reply->errorString());
}
@@ -166,7 +168,7 @@ void PasteUpload::downloadFinished()
{
QString reasonPhrase = m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
emitFailed(tr("Error: %1 returned unexpected status code %2 %3").arg(m_uploadUrl).arg(statusCode).arg(reasonPhrase));
- qCritical() << m_uploadUrl << " returned unexpected status code " << statusCode << " with body: " << data;
+ qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned unexpected status code " << statusCode << " with body: " << data;
m_reply.reset();
return;
}
@@ -187,7 +189,7 @@ void PasteUpload::downloadFinished()
else
{
emitFailed(tr("Error: %1 returned a malformed response body").arg(m_uploadUrl));
- qCritical() << m_uploadUrl << " returned malformed response body: " << data;
+ qCCritical(taskUploadLogC) << getUid().toString() << getUid().toString() << m_uploadUrl << " returned malformed response body: " << data;
return;
}
break;
@@ -206,15 +208,15 @@ void PasteUpload::downloadFinished()
{
QString error = jsonObj["error"].toString();
emitFailed(tr("Error: %1 returned an error: %2").arg(m_uploadUrl, error));
- qCritical() << m_uploadUrl << " returned error: " << error;
- qCritical() << "Response body: " << data;
+ qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned error: " << error;
+ qCCritical(taskUploadLogC) << getUid().toString() << "Response body: " << data;
return;
}
}
else
{
emitFailed(tr("Error: %1 returned a malformed response body").arg(m_uploadUrl));
- qCritical() << m_uploadUrl << " returned malformed response body: " << data;
+ qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned malformed response body: " << data;
return;
}
break;
@@ -234,16 +236,16 @@ void PasteUpload::downloadFinished()
QString error = jsonObj["error"].toString();
QString message = (jsonObj.contains("message") && jsonObj["message"].isString()) ? jsonObj["message"].toString() : "none";
emitFailed(tr("Error: %1 returned an error code: %2\nError message: %3").arg(m_uploadUrl, error, message));
- qCritical() << m_uploadUrl << " returned error: " << error;
- qCritical() << "Error message: " << message;
- qCritical() << "Response body: " << data;
+ qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned error: " << error;
+ qCCritical(taskUploadLogC) << getUid().toString() << "Error message: " << message;
+ qCCritical(taskUploadLogC) << getUid().toString() << "Response body: " << data;
return;
}
}
else
{
emitFailed(tr("Error: %1 returned a malformed response body").arg(m_uploadUrl));
- qCritical() << m_uploadUrl << " returned malformed response body: " << data;
+ qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned malformed response body: " << data;
return;
}
break;
diff --git a/launcher/net/PasteUpload.h b/launcher/net/PasteUpload.h
index eb315c2b..b72ab5b0 100644
--- a/launcher/net/PasteUpload.h
+++ b/launcher/net/PasteUpload.h
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
- * PolyMC - Minecraft Launcher
+ * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Lenny McLennington <lenny@sneed.church>
*
* This program is free software: you can redistribute it and/or modify
diff --git a/launcher/net/Upload.cpp b/launcher/net/Upload.cpp
index ccf43c2d..4f9553ed 100644
--- a/launcher/net/Upload.cpp
+++ b/launcher/net/Upload.cpp
@@ -4,6 +4,7 @@
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
+ * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
* 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
@@ -42,6 +43,8 @@
#include "BuildConfig.h"
#include "Application.h"
+#include "net/Logging.h"
+
namespace Net {
bool Upload::abort()
@@ -60,11 +63,11 @@ namespace Net {
void Upload::downloadError(QNetworkReply::NetworkError error) {
if (error == QNetworkReply::OperationCanceledError) {
- qCritical() << "Aborted " << m_url.toString();
+ qCCritical(taskUploadLogC) << getUid().toString() << "Aborted " << m_url.toString();
m_state = State::AbortedByUser;
} else {
// error happened during download.
- qCritical() << "Failed " << m_url.toString() << " with reason " << error;
+ qCCritical(taskUploadLogC) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error;
m_state = State::Failed;
}
}
@@ -72,9 +75,9 @@ namespace Net {
void Upload::sslErrors(const QList<QSslError> &errors) {
int i = 1;
for (const auto& error : errors) {
- qCritical() << "Upload" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString();
+ qCCritical(taskUploadLogC) << getUid().toString() << "Upload" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString();
auto cert = error.certificate();
- qCritical() << "Certificate in question:\n" << cert.toText();
+ qCCritical(taskUploadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText();
i++;
}
}
@@ -117,17 +120,17 @@ namespace Net {
*/
redirect = QUrl(redirectStr, QUrl::TolerantMode);
if (!redirect.isValid()) {
- qWarning() << "Failed to parse redirect URL:" << redirectStr;
+ qCWarning(taskUploadLogC) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr;
downloadError(QNetworkReply::ProtocolFailure);
return false;
}
- qDebug() << "Fixed location header:" << redirect;
+ qCDebug(taskUploadLogC) << getUid().toString() << "Fixed location header:" << redirect;
} else {
- qDebug() << "Location header:" << redirect;
+ qCDebug(taskUploadLogC) << getUid().toString() << "Location header:" << redirect;
}
m_url = QUrl(redirect.toString());
- qDebug() << "Following redirect to " << m_url.toString();
+ qCDebug(taskUploadLogC) << getUid().toString() << "Following redirect to " << m_url.toString();
startAction(m_network);
return true;
}
@@ -136,25 +139,25 @@ namespace Net {
// handle HTTP redirection first
// very unlikely for post requests, still can happen
if (handleRedirect()) {
- qDebug() << "Upload redirected:" << m_url.toString();
+ qCDebug(taskUploadLogC) << getUid().toString() << "Upload redirected:" << m_url.toString();
return;
}
// if the download failed before this point ...
if (m_state == State::Succeeded) {
- qDebug() << "Upload failed but we are allowed to proceed:" << m_url.toString();
+ qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed but we are allowed to proceed:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit succeeded();
return;
} else if (m_state == State::Failed) {
- qDebug() << "Upload failed in previous step:" << m_url.toString();
+ qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed in previous step:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit failed("");
return;
} else if (m_state == State::AbortedByUser) {
- qDebug() << "Upload aborted in previous step:" << m_url.toString();
+ qCDebug(taskUploadLogC) << getUid().toString() << "Upload aborted in previous step:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit aborted();
@@ -164,21 +167,21 @@ namespace Net {
// make sure we got all the remaining data, if any
auto data = m_reply->readAll();
if (data.size()) {
- qDebug() << "Writing extra" << data.size() << "bytes";
+ qCDebug(taskUploadLogC) << getUid().toString() << "Writing extra" << data.size() << "bytes";
m_state = m_sink->write(data);
}
// otherwise, finalize the whole graph
m_state = m_sink->finalize(*m_reply.get());
if (m_state != State::Succeeded) {
- qDebug() << "Upload failed to finalize:" << m_url.toString();
+ qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed to finalize:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit failed("");
return;
}
m_reply.reset();
- qDebug() << "Upload succeeded:" << m_url.toString();
+ qCDebug(taskUploadLogC) << getUid().toString() << "Upload succeeded:" << m_url.toString();
emit succeeded();
}
@@ -193,7 +196,7 @@ namespace Net {
setStatus(tr("Uploading %1").arg(m_url.toString()));
if (m_state == State::AbortedByUser) {
- qWarning() << "Attempt to start an aborted Upload:" << m_url.toString();
+ qCWarning(taskUploadLogC) << getUid().toString() << "Attempt to start an aborted Upload:" << m_url.toString();
emit aborted();
return;
}
@@ -202,10 +205,10 @@ namespace Net {
switch (m_state) {
case State::Succeeded:
emitSucceeded();
- qDebug() << "Upload cache hit " << m_url.toString();
+ qCDebug(taskUploadLogC) << getUid().toString() << "Upload cache hit " << m_url.toString();
return;
case State::Running:
- qDebug() << "Uploading " << m_url.toString();
+ qCDebug(taskUploadLogC) << getUid().toString() << "Uploading " << m_url.toString();
break;
case State::Inactive:
case State::Failed:
@@ -232,9 +235,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<QNetworkReply::NetworkError>::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..e8f0ea40 100644
--- a/launcher/net/Upload.h
+++ b/launcher/net/Upload.h
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
- * PolyMC - Minecraft Launcher
+ * Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
* 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
@@ -54,7 +55,7 @@ namespace Net {
protected slots:
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;
void downloadError(QNetworkReply::NetworkError error) override;
- void sslErrors(const QList<QSslError> & errors);
+ void sslErrors(const QList<QSslError> & errors) override;
void downloadFinished() override;
void downloadReadyRead() override;
diff --git a/launcher/qtlogging.ini b/launcher/qtlogging.ini
new file mode 100644
index 00000000..c12d1e10
--- /dev/null
+++ b/launcher/qtlogging.ini
@@ -0,0 +1,16 @@
+[Rules]
+*.debug=true
+# prevent log spam and strange bugs
+# qt.qpa.drawing in particular causes theme artifacts on MacOS
+qt.*.debug=false
+# don't log credentials by default
+launcher.auth.credentials.debug=false
+# remove the debug lines, other log levels still get through
+launcher.task.net.download.debug=false
+# enable or disable whole catageries
+launcher.task.net=true
+launcher.task=false
+launcher.task.net.upload=true
+launcher.task.net.metacache=false
+launcher.task.net.metacache.http=true
+
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<QNetworkReply::NetworkError>::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<QNetworkReply::NetworkError>::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/tasks/ConcurrentTask.cpp b/launcher/tasks/ConcurrentTask.cpp
index 3cc37b2a..5ee14505 100644
--- a/launcher/tasks/ConcurrentTask.cpp
+++ b/launcher/tasks/ConcurrentTask.cpp
@@ -1,11 +1,49 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
+ * Copyright (c) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ * 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 "ConcurrentTask.h"
-#include <QDebug>
#include <QCoreApplication>
+#include <QDebug>
+#include "tasks/Task.h"
ConcurrentTask::ConcurrentTask(QObject* parent, QString task_name, int max_concurrent)
: Task(parent), m_name(task_name), m_total_max_size(max_concurrent)
-{ setObjectName(task_name); }
+{
+ setObjectName(task_name);
+}
ConcurrentTask::~ConcurrentTask()
{
@@ -15,14 +53,9 @@ ConcurrentTask::~ConcurrentTask()
}
}
-auto ConcurrentTask::getStepProgress() const -> qint64
+auto ConcurrentTask::getStepProgress() const -> TaskStepProgressList
{
- return m_stepProgress;
-}
-
-auto ConcurrentTask::getStepTotalProgress() const -> qint64
-{
- return m_stepTotalProgress;
+ return m_task_progress.values();
}
void ConcurrentTask::addTask(Task::Ptr task)
@@ -32,11 +65,9 @@ void ConcurrentTask::addTask(Task::Ptr task)
void ConcurrentTask::executeTask()
{
- // Start the least amount of tasks needed, but at least one
- int num_starts = qMax(1, qMin(m_total_max_size, m_queue.size()));
- for (int i = 0; i < num_starts; i++) {
- QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection);
- }
+ // Start one task, startNext handles starting the up to the m_total_max_size
+ // while tracking the number currently being done
+ QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection);
}
bool ConcurrentTask::abort()
@@ -97,18 +128,22 @@ void ConcurrentTask::startNext()
Task::Ptr next = m_queue.dequeue();
- connect(next.get(), &Task::succeeded, this, [this, next] { subTaskSucceeded(next); });
+ connect(next.get(), &Task::succeeded, this, [this, next]() { subTaskSucceeded(next); });
connect(next.get(), &Task::failed, this, [this, next](QString msg) { subTaskFailed(next, msg); });
- connect(next.get(), &Task::status, this, &ConcurrentTask::subTaskStatus);
- connect(next.get(), &Task::stepStatus, this, &ConcurrentTask::subTaskStatus);
+ connect(next.get(), &Task::status, this, [this, next](QString msg) { subTaskStatus(next, msg); });
+ connect(next.get(), &Task::details, this, [this, next](QString msg) { subTaskDetails(next, msg); });
+ connect(next.get(), &Task::stepProgress, this, [this, next](TaskStepProgress const& tp) { subTaskStepProgress(next, tp); });
- connect(next.get(), &Task::progress, this, &ConcurrentTask::subTaskProgress);
+ connect(next.get(), &Task::progress, this, [this, next](qint64 current, qint64 total) { subTaskProgress(next, current, total); });
m_doing.insert(next.get(), next);
+ auto task_progress = std::make_shared<TaskStepProgress>(next->getUid());
+ m_task_progress.insert(next->getUid(), task_progress);
- setStepStatus(next->isMultiStep() ? next->getStepStatus() : next->getStatus());
updateState();
+ updateStepProgress(*task_progress.get(), Operation::ADDED);
+
QCoreApplication::processEvents();
@@ -123,12 +158,17 @@ void ConcurrentTask::startNext()
void ConcurrentTask::subTaskSucceeded(Task::Ptr task)
{
m_done.insert(task.get(), task);
+ m_succeeded.insert(task.get(), task);
+
m_doing.remove(task.get());
+ auto task_progress = m_task_progress.value(task->getUid());
+ task_progress->state = TaskStepState::Succeeded;
disconnect(task.get(), 0, this, 0);
+ emit stepProgress(*task_progress);
updateState();
-
+ updateStepProgress(*task_progress, Operation::REMOVED);
startNext();
}
@@ -139,27 +179,123 @@ void ConcurrentTask::subTaskFailed(Task::Ptr task, const QString& msg)
m_doing.remove(task.get());
+ auto task_progress = m_task_progress.value(task->getUid());
+ task_progress->state = TaskStepState::Failed;
+
disconnect(task.get(), 0, this, 0);
+ emit stepProgress(*task_progress);
updateState();
-
+ updateStepProgress(*task_progress, Operation::REMOVED);
startNext();
}
-void ConcurrentTask::subTaskStatus(const QString& msg)
+void ConcurrentTask::subTaskStatus(Task::Ptr task, const QString& msg)
+{
+ auto task_progress = m_task_progress.value(task->getUid());
+ task_progress->status = msg;
+ task_progress->state = TaskStepState::Running;
+
+ emit stepProgress(*task_progress);
+
+ if (totalSize() == 1) {
+ setStatus(msg);
+ }
+}
+
+void ConcurrentTask::subTaskDetails(Task::Ptr task, const QString& msg)
{
- setStepStatus(msg);
+ auto task_progress = m_task_progress.value(task->getUid());
+ task_progress->details = msg;
+ task_progress->state = TaskStepState::Running;
+
+ emit stepProgress(*task_progress);
+
+ if (totalSize() == 1) {
+ setDetails(msg);
+ }
}
-void ConcurrentTask::subTaskProgress(qint64 current, qint64 total)
+void ConcurrentTask::subTaskProgress(Task::Ptr task, qint64 current, qint64 total)
{
- m_stepProgress = current;
- m_stepTotalProgress = total;
+ auto task_progress = m_task_progress.value(task->getUid());
+
+ task_progress->update(current, total);
+
+ emit stepProgress(*task_progress);
+ updateStepProgress(*task_progress, Operation::CHANGED);
+ updateState();
+
+ if (totalSize() == 1) {
+ setProgress(task_progress->current, task_progress->total);
+ }
+}
+
+void ConcurrentTask::subTaskStepProgress(Task::Ptr task, TaskStepProgress const& task_progress)
+{
+ Operation op = Operation::ADDED;
+
+ if (!m_task_progress.contains(task_progress.uid)) {
+ m_task_progress.insert(task_progress.uid, std::make_shared<TaskStepProgress>(task_progress));
+ op = Operation::ADDED;
+ emit stepProgress(task_progress);
+ updateStepProgress(task_progress, op);
+ } else {
+ auto tp = m_task_progress.value(task_progress.uid);
+
+ tp->old_current = tp->current;
+ tp->old_total = tp->total;
+
+ tp->current = task_progress.current;
+ tp->total = task_progress.total;
+ tp->status = task_progress.status;
+ tp->details = task_progress.details;
+
+ op = Operation::CHANGED;
+ emit stepProgress(*tp.get());
+ updateStepProgress(*tp.get(), op);
+ }
+
+}
+
+void ConcurrentTask::updateStepProgress(TaskStepProgress const& changed_progress, Operation op)
+{
+
+ switch (op) {
+ case Operation::ADDED:
+ m_stepProgress += changed_progress.current;
+ m_stepTotalProgress += changed_progress.total;
+ break;
+ case Operation::REMOVED:
+ m_stepProgress -= changed_progress.current;
+ m_stepTotalProgress -= changed_progress.total;
+ break;
+ case Operation::CHANGED:
+ m_stepProgress -= changed_progress.old_current;
+ m_stepTotalProgress -= changed_progress.old_total;
+ m_stepProgress += changed_progress.current;
+ m_stepTotalProgress += changed_progress.total;
+ break;
+ }
+
}
void ConcurrentTask::updateState()
{
- setProgress(m_done.count(), totalSize());
- setStatus(tr("Executing %1 task(s) (%2 out of %3 are done)")
- .arg(QString::number(m_doing.count()), QString::number(m_done.count()), QString::number(totalSize())));
+ if (totalSize() > 1) {
+ setProgress(m_done.count(), totalSize());
+ setStatus(tr("Executing %1 task(s) (%2 out of %3 are done)")
+ .arg(QString::number(m_doing.count()), QString::number(m_done.count()), QString::number(totalSize())));
+ } else {
+ setProgress(m_stepProgress, m_stepTotalProgress);
+ QString status = tr("Please wait...");
+ if (m_queue.size() > 0) {
+ status = tr("Waiting for a task to start...");
+ } else if (m_doing.size() > 0) {
+ status = tr("Executing 1 task:");
+ } else if (m_done.size() > 0) {
+ status = tr("Task finished.");
+ }
+ setStatus(status);
+ }
}
diff --git a/launcher/tasks/ConcurrentTask.h b/launcher/tasks/ConcurrentTask.h
index d074d2e2..6325fc9e 100644
--- a/launcher/tasks/ConcurrentTask.h
+++ b/launcher/tasks/ConcurrentTask.h
@@ -1,13 +1,51 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
+ * Copyright (c) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ * 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 <QHash>
#include <QQueue>
#include <QSet>
+#include <QUuid>
+#include <memory>
#include "tasks/Task.h"
class ConcurrentTask : public Task {
Q_OBJECT
-public:
+ public:
using Ptr = shared_qobject_ptr<ConcurrentTask>;
explicit ConcurrentTask(QObject* parent = nullptr, QString task_name = "", int max_concurrent = 6);
@@ -15,15 +53,12 @@ public:
bool canAbort() const override { return true; }
- inline auto isMultiStep() const -> bool override { return m_queue.size() > 1; };
- auto getStepProgress() const -> qint64 override;
- auto getStepTotalProgress() const -> qint64 override;
-
- inline auto getStepStatus() const -> QString override { return m_step_status; }
+ inline auto isMultiStep() const -> bool override { return totalSize() > 1; };
+ auto getStepProgress() const -> TaskStepProgressList override;
void addTask(Task::Ptr task);
-public slots:
+ public slots:
bool abort() override;
/** Resets the internal state of the task.
@@ -31,26 +66,28 @@ public slots:
*/
void clear();
-protected
-slots:
+ protected slots:
void executeTask() override;
virtual void startNext();
void subTaskSucceeded(Task::Ptr);
- void subTaskFailed(Task::Ptr, const QString &msg);
- void subTaskStatus(const QString &msg);
- void subTaskProgress(qint64 current, qint64 total);
+ void subTaskFailed(Task::Ptr, const QString& msg);
+ void subTaskStatus(Task::Ptr task, const QString& msg);
+ void subTaskDetails(Task::Ptr task, const QString& msg);
+ void subTaskProgress(Task::Ptr task, qint64 current, qint64 total);
+ void subTaskStepProgress(Task::Ptr task, TaskStepProgress const& task_step_progress);
-protected:
+ protected:
// NOTE: This is not thread-safe.
[[nodiscard]] unsigned int totalSize() const { return m_queue.size() + m_doing.size() + m_done.size(); }
- void setStepStatus(QString status) { m_step_status = status; emit stepStatus(status); };
+ enum class Operation { ADDED, REMOVED, CHANGED };
+ void updateStepProgress(TaskStepProgress const& changed_progress, Operation);
virtual void updateState();
-protected:
+ protected:
QString m_name;
QString m_step_status;
@@ -59,6 +96,9 @@ protected:
QHash<Task*, Task::Ptr> m_doing;
QHash<Task*, Task::Ptr> m_done;
QHash<Task*, Task::Ptr> m_failed;
+ QHash<Task*, Task::Ptr> m_succeeded;
+
+ QHash<QUuid, std::shared_ptr<TaskStepProgress>> m_task_progress;
int m_total_max_size;
diff --git a/launcher/tasks/MultipleOptionsTask.cpp b/launcher/tasks/MultipleOptionsTask.cpp
index 034499df..89187a26 100644
--- a/launcher/tasks/MultipleOptionsTask.cpp
+++ b/launcher/tasks/MultipleOptionsTask.cpp
@@ -1,3 +1,37 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ * 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 "MultipleOptionsTask.h"
#include <QDebug>
diff --git a/launcher/tasks/MultipleOptionsTask.h b/launcher/tasks/MultipleOptionsTask.h
index db7d4d9a..a344343e 100644
--- a/launcher/tasks/MultipleOptionsTask.h
+++ b/launcher/tasks/MultipleOptionsTask.h
@@ -1,3 +1,37 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ * 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 "SequentialTask.h"
diff --git a/launcher/tasks/SequentialTask.cpp b/launcher/tasks/SequentialTask.cpp
index b2f86328..abf7536b 100644
--- a/launcher/tasks/SequentialTask.cpp
+++ b/launcher/tasks/SequentialTask.cpp
@@ -1,3 +1,38 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
+ * Copyright (c) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ * 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 "SequentialTask.h"
#include <QDebug>
diff --git a/launcher/tasks/SequentialTask.h b/launcher/tasks/SequentialTask.h
index 5eace96e..cec3b2be 100644
--- a/launcher/tasks/SequentialTask.h
+++ b/launcher/tasks/SequentialTask.h
@@ -1,3 +1,38 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
+ * Copyright (c) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ * 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 "ConcurrentTask.h"
diff --git a/launcher/tasks/Task.cpp b/launcher/tasks/Task.cpp
index 9ea1bb26..29c55cd4 100644
--- a/launcher/tasks/Task.cpp
+++ b/launcher/tasks/Task.cpp
@@ -2,6 +2,7 @@
/*
* PolyMC - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
+ * Copyright (c) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
* 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
@@ -37,8 +38,11 @@
#include <QDebug>
+Q_LOGGING_CATEGORY(taskLogC, "launcher.task")
+
Task::Task(QObject *parent, bool show_debug) : QObject(parent), m_show_debug(show_debug)
{
+ m_uid = QUuid::createUuid();
setAutoDelete(false);
}
@@ -51,11 +55,23 @@ void Task::setStatus(const QString &new_status)
}
}
+void Task::setDetails(const QString& new_details)
+{
+ if (m_details != new_details)
+ {
+ m_details = new_details;
+ emit details(m_details);
+ }
+}
+
void Task::setProgress(qint64 current, qint64 total)
{
- m_progress = current;
- m_progressTotal = total;
- emit progress(m_progress, m_progressTotal);
+ if ((m_progress != current) || (m_progressTotal != total)) {
+ m_progress = current;
+ m_progressTotal = total;
+
+ emit progress(m_progress, m_progressTotal);
+ }
}
void Task::start()
@@ -65,35 +81,35 @@ void Task::start()
case State::Inactive:
{
if (m_show_debug)
- qDebug() << "Task" << describe() << "starting for the first time";
+ qCDebug(taskLogC) << "Task" << describe() << "starting for the first time";
break;
}
case State::AbortedByUser:
{
if (m_show_debug)
- qDebug() << "Task" << describe() << "restarting for after being aborted by user";
+ qCDebug(taskLogC) << "Task" << describe() << "restarting for after being aborted by user";
break;
}
case State::Failed:
{
if (m_show_debug)
- qDebug() << "Task" << describe() << "restarting for after failing at first";
+ qCDebug(taskLogC) << "Task" << describe() << "restarting for after failing at first";
break;
}
case State::Succeeded:
{
if (m_show_debug)
- qDebug() << "Task" << describe() << "restarting for after succeeding at first";
+ qCDebug(taskLogC) << "Task" << describe() << "restarting for after succeeding at first";
break;
}
case State::Running:
{
if (m_show_debug)
- qWarning() << "The launcher tried to start task" << describe() << "while it was already running!";
+ qCWarning(taskLogC) << "The launcher tried to start task" << describe() << "while it was already running!";
return;
}
}
- // NOTE: only fall thorugh to here in end states
+ // NOTE: only fall through to here in end states
m_state = State::Running;
emit started();
executeTask();
@@ -104,12 +120,12 @@ void Task::emitFailed(QString reason)
// Don't fail twice.
if (!isRunning())
{
- qCritical() << "Task" << describe() << "failed while not running!!!!: " << reason;
+ qCCritical(taskLogC) << "Task" << describe() << "failed while not running!!!!: " << reason;
return;
}
m_state = State::Failed;
m_failReason = reason;
- qCritical() << "Task" << describe() << "failed: " << reason;
+ qCCritical(taskLogC) << "Task" << describe() << "failed: " << reason;
emit failed(reason);
emit finished();
}
@@ -119,13 +135,13 @@ void Task::emitAborted()
// Don't abort twice.
if (!isRunning())
{
- qCritical() << "Task" << describe() << "aborted while not running!!!!";
+ qCCritical(taskLogC) << "Task" << describe() << "aborted while not running!!!!";
return;
}
m_state = State::AbortedByUser;
m_failReason = "Aborted.";
if (m_show_debug)
- qDebug() << "Task" << describe() << "aborted.";
+ qCDebug(taskLogC) << "Task" << describe() << "aborted.";
emit aborted();
emit finished();
}
@@ -135,16 +151,21 @@ void Task::emitSucceeded()
// Don't succeed twice.
if (!isRunning())
{
- qCritical() << "Task" << describe() << "succeeded while not running!!!!";
+ qCCritical(taskLogC) << "Task" << describe() << "succeeded while not running!!!!";
return;
}
m_state = State::Succeeded;
if (m_show_debug)
- qDebug() << "Task" << describe() << "succeeded";
+ qCDebug(taskLogC) << "Task" << describe() << "succeeded";
emit succeeded();
emit finished();
}
+void Task::propogateStepProgress(TaskStepProgress const& task_progress)
+{
+ emit stepProgress(task_progress);
+}
+
QString Task::describe()
{
QString outStr;
@@ -159,6 +180,7 @@ QString Task::describe()
{
out << name;
}
+ out << " ID: " << m_uid.toString(QUuid::WithoutBraces);
out << QChar(')');
out.flush();
return outStr;
diff --git a/launcher/tasks/Task.h b/launcher/tasks/Task.h
index 3d607dca..6d8bbbb4 100644
--- a/launcher/tasks/Task.h
+++ b/launcher/tasks/Task.h
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
- * PolyMC - Minecraft Launcher
+ * PrismLauncher - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
+ * Copyright (c) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
* 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
@@ -36,9 +37,54 @@
#pragma once
#include <QRunnable>
+#include <QUuid>
+#include <QLoggingCategory>
#include "QObjectPtr.h"
+Q_DECLARE_LOGGING_CATEGORY(taskLogC)
+
+enum class TaskStepState {
+ Waiting,
+ Running,
+ Failed,
+ Succeeded
+};
+
+Q_DECLARE_METATYPE(TaskStepState)
+
+struct TaskStepProgress {
+ QUuid uid;
+ qint64 current = 0;
+ qint64 total = -1;
+
+ qint64 old_current = 0;
+ qint64 old_total = -1;
+
+ QString status = "";
+ QString details = "";
+ TaskStepState state = TaskStepState::Waiting;
+ TaskStepProgress() {
+ this->uid = QUuid::createUuid();
+ }
+ TaskStepProgress(QUuid uid) {
+ this->uid = uid;
+ }
+ bool isDone() const { return (state == TaskStepState::Failed) || (state == TaskStepState::Succeeded); }
+ void update(qint64 current, qint64 total) {
+ this->old_current = this->current;
+ this->old_total = this->total;
+
+ this->current = current;
+ this->total = total;
+ this->state = TaskStepState::Running;
+ }
+};
+
+Q_DECLARE_METATYPE(TaskStepProgress)
+
+typedef QList<std::shared_ptr<TaskStepProgress>> TaskStepProgressList;
+
class Task : public QObject, public QRunnable {
Q_OBJECT
public:
@@ -73,12 +119,15 @@ class Task : public QObject, public QRunnable {
auto getState() const -> State { return m_state; }
QString getStatus() { return m_status; }
- virtual auto getStepStatus() const -> QString { return m_status; }
+ QString getDetails() { return m_details; }
qint64 getProgress() { return m_progress; }
qint64 getTotalProgress() { return m_progressTotal; }
- virtual auto getStepProgress() const -> qint64 { return 0; }
- virtual auto getStepTotalProgress() const -> qint64 { return 100; }
+ virtual auto getStepProgress() const -> TaskStepProgressList { return {}; }
+
+
+
+ QUuid getUid() { return m_uid; }
protected:
void logWarning(const QString& line);
@@ -94,7 +143,8 @@ class Task : public QObject, public QRunnable {
void aborted();
void failed(QString reason);
void status(QString status);
- void stepStatus(QString status);
+ void details(QString details);
+ void stepProgress(TaskStepProgress const& task_progress);
/** Emitted when the canAbort() status has changed.
*/
@@ -117,8 +167,11 @@ class Task : public QObject, public QRunnable {
virtual void emitAborted();
virtual void emitFailed(QString reason = "");
+ virtual void propogateStepProgress(TaskStepProgress const& task_progress);
+
public slots:
void setStatus(const QString& status);
+ void setDetails(const QString& details);
void setProgress(qint64 current, qint64 total);
protected:
@@ -126,6 +179,7 @@ class Task : public QObject, public QRunnable {
QStringList m_Warnings;
QString m_failReason = "";
QString m_status;
+ QString m_details;
int m_progress = 0;
int m_progressTotal = 100;
@@ -135,4 +189,6 @@ class Task : public QObject, public QRunnable {
private:
// Change using setAbortStatus
bool m_can_abort = false;
+ QUuid m_uid;
+
};
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<LaunchTask> 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<int, QProcess::ExitStatus>::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<LaunchTask> 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<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &JVisualVM::profilerFinished);
profiler->start();
m_profilerProcess = profiler;
diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp
index ba453df6..fdfae597 100644
--- a/launcher/ui/dialogs/BlockedModsDialog.cpp
+++ b/launcher/ui/dialogs/BlockedModsDialog.cpp
@@ -195,7 +195,7 @@ void BlockedModsDialog::watchPath(QString path, bool watch_recursive)
auto to_watch = QFileInfo(path);
auto to_watch_path = to_watch.canonicalFilePath();
if (m_watcher.directories().contains(to_watch_path))
- return; // don't watch the same path twice (no loops!)
+ return; // don't watch the same path twice (no loops!)
qDebug() << "[Blocked Mods Dialog] Adding Watch Path:" << path;
m_watcher.addPath(to_watch_path);
@@ -203,10 +203,9 @@ void BlockedModsDialog::watchPath(QString path, bool watch_recursive)
if (!to_watch.isDir() || !watch_recursive)
return;
-
QDirIterator it(to_watch_path, QDir::Filter::Dirs | QDir::Filter::NoDotAndDotDot, QDirIterator::NoIteratorFlags);
while (it.hasNext()) {
- QString watch_dir = QDir(it.next()).canonicalPath(); // resolve symlinks and relative paths
+ QString watch_dir = QDir(it.next()).canonicalPath(); // resolve symlinks and relative paths
watchPath(watch_dir, watch_recursive);
}
}
@@ -302,11 +301,35 @@ bool BlockedModsDialog::checkValidPath(QString path)
{
const QFileInfo file = QFileInfo(path);
const QString filename = file.fileName();
- QString laxFilename(filename);
- laxFilename.replace('+', ' ');
- auto compare = [](QString fsfilename, QString metadataFilename) {
- return metadataFilename.compare(fsfilename, Qt::CaseInsensitive) == 0;
+ auto compare = [](QString fsFilename, QString metadataFilename) {
+ return metadataFilename.compare(fsFilename, Qt::CaseInsensitive) == 0;
+ };
+
+ // super lax compare (but not fuzzy)
+ // convert to lowercase
+ // convert all speratores to whitespace
+ // simplify sequence of internal whitespace to a single space
+ // efectivly compare two strings ignoring all separators and case
+ auto laxCompare = [](QString fsfilename, QString metadataFilename) {
+ // allowed character seperators
+ QList<QChar> allowedSeperators = { '-', '+', '.' , '_'};
+
+ // copy in lowercase
+ auto fsName = fsfilename.toLower();
+ auto metaName = metadataFilename.toLower();
+
+ // replace all potential allowed seperatores with whitespace
+ for (auto sep : allowedSeperators) {
+ fsName = fsName.replace(sep, ' ');
+ metaName = metaName.replace(sep, ' ');
+ }
+
+ // remove extraneous whitespace
+ fsName = fsName.simplified();
+ metaName = metaName.simplified();
+
+ return fsName.compare(metaName) == 0;
};
for (auto& mod : m_mods) {
@@ -314,7 +337,7 @@ bool BlockedModsDialog::checkValidPath(QString path)
qDebug() << "[Blocked Mods Dialog] Name match found:" << mod.name << "| From path:" << path;
return true;
}
- if (compare(laxFilename, mod.name)) {
+ if (laxCompare(filename, mod.name)) {
qDebug() << "[Blocked Mods Dialog] Lax name match found:" << mod.name << "| From path:" << path;
return true;
}
diff --git a/launcher/ui/dialogs/NewInstanceDialog.cpp b/launcher/ui/dialogs/NewInstanceDialog.cpp
index 64ed7673..aafaf220 100644
--- a/launcher/ui/dialogs/NewInstanceDialog.cpp
+++ b/launcher/ui/dialogs/NewInstanceDialog.cpp
@@ -99,7 +99,7 @@ NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString
// 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 = new PageContainer(this, {}, this);
m_container->setSizePolicy(QSizePolicy::Policy::Preferred, QSizePolicy::Policy::Expanding);
m_container->layout()->setContentsMargins(0, 0, 0, 0);
ui->verticalLayout->insertWidget(2, m_container);
diff --git a/launcher/ui/dialogs/ProgressDialog.cpp b/launcher/ui/dialogs/ProgressDialog.cpp
index da73a449..246a0fd4 100644
--- a/launcher/ui/dialogs/ProgressDialog.cpp
+++ b/launcher/ui/dialogs/ProgressDialog.cpp
@@ -1,29 +1,69 @@
-/* Copyright 2013-2021 MultiMC Contributors
+/// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PrismLaucher - Minecraft Launcher
+ * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
- * 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
+ * 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.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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.
*
- * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * 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 "ProgressDialog.h"
#include "ui_ProgressDialog.h"
+#include <limits>
#include <QDebug>
#include <QKeyEvent>
#include "tasks/Task.h"
+#include "ui/widgets/SubTaskProgressBar.h"
+
+
+// map a value in a numeric range of an arbitrary type to between 0 and INT_MAX
+// for getting the best precision out of the qt progress bar
+template<typename T, std::enable_if_t<std::is_arithmetic_v<T>, bool> = true>
+std::tuple<int, int> map_int_zero_max(T current, T range_max, T range_min)
+{
+ int int_max = std::numeric_limits<int>::max();
+
+ auto type_range = range_max - range_min;
+ double percentage = static_cast<double>(current - range_min) / static_cast<double>(type_range);
+ int mapped_current = percentage * int_max;
+
+ return {mapped_current, int_max};
+}
+
+
ProgressDialog::ProgressDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ProgressDialog)
{
ui->setupUi(this);
+ ui->taskProgressScrollArea->setHidden(true);
this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
setAttribute(Qt::WidgetAttribute::WA_QuitOnClose, true);
setSkipButton(false);
@@ -54,10 +94,24 @@ ProgressDialog::~ProgressDialog()
}
void ProgressDialog::updateSize()
-{
+{
+ QSize lastSize = this->size();
QSize qSize = QSize(480, minimumSizeHint().height());
- resize(qSize);
- setFixedSize(qSize);
+
+ // if the current window is too small
+ if ((lastSize != qSize) && (lastSize.height() < qSize.height()))
+ {
+ resize(qSize);
+
+ // keep the dialog in the center after a resize
+ this->move(
+ this->parentWidget()->x() + (this->parentWidget()->width() - this->width()) / 2,
+ this->parentWidget()->y() + (this->parentWidget()->height() - this->height()) / 2
+ );
+ }
+
+ setMinimumSize(qSize);
+
}
int ProgressDialog::execWithTask(Task* task)
@@ -79,17 +133,15 @@ int ProgressDialog::execWithTask(Task* task)
connect(task, &Task::failed, this, &ProgressDialog::onTaskFailed);
connect(task, &Task::succeeded, this, &ProgressDialog::onTaskSucceeded);
connect(task, &Task::status, this, &ProgressDialog::changeStatus);
- connect(task, &Task::stepStatus, this, &ProgressDialog::changeStatus);
+ connect(task, &Task::details, this, &ProgressDialog::changeStatus);
+ connect(task, &Task::stepProgress, this, &ProgressDialog::changeStepProgress);
connect(task, &Task::progress, this, &ProgressDialog::changeProgress);
-
connect(task, &Task::aborted, this, &ProgressDialog::hide);
connect(task, &Task::abortStatusChanged, ui->skipButton, &QPushButton::setEnabled);
m_is_multi_step = task->isMultiStep();
- if (!m_is_multi_step) {
- ui->globalStatusLabel->setHidden(true);
- ui->globalProgressBar->setHidden(true);
- }
+ ui->taskProgressScrollArea->setHidden(!m_is_multi_step);
+ updateSize();
// It's a good idea to start the task after we entered the dialog's event loop :^)
if (!task->isRunning()) {
@@ -149,23 +201,53 @@ void ProgressDialog::onTaskSucceeded()
void ProgressDialog::changeStatus(const QString& status)
{
ui->globalStatusLabel->setText(task->getStatus());
- ui->statusLabel->setText(task->getStepStatus());
+ ui->globalStatusDetailsLabel->setText(task->getDetails());
updateSize();
}
+void ProgressDialog::addTaskProgress(TaskStepProgress const& progress)
+{
+ SubTaskProgressBar* task_bar = new SubTaskProgressBar(this);
+ taskProgress.insert(progress.uid, task_bar);
+ ui->taskProgressLayout->addWidget(task_bar);
+}
+
+void ProgressDialog::changeStepProgress(TaskStepProgress const& task_progress)
+{
+ m_is_multi_step = true;
+ if(ui->taskProgressScrollArea->isHidden()) {
+ ui->taskProgressScrollArea->setHidden(false);
+ updateSize();
+ }
+
+ if (!taskProgress.contains(task_progress.uid))
+ addTaskProgress(task_progress);
+ auto task_bar = taskProgress.value(task_progress.uid);
+
+
+ auto const [mapped_current, mapped_total] = map_int_zero_max<qint64>(task_progress.current, task_progress.total, 0);
+ if (task_progress.total <= 0) {
+ task_bar->setRange(0, 0);
+ } else {
+ task_bar->setRange(0, mapped_total);
+ }
+
+ task_bar->setValue(mapped_current);
+ task_bar->setStatus(task_progress.status);
+ task_bar->setDetails(task_progress.details);
+
+ if (task_progress.isDone()) {
+ task_bar->setVisible(false);
+ }
+
+}
+
void ProgressDialog::changeProgress(qint64 current, qint64 total)
{
ui->globalProgressBar->setMaximum(total);
ui->globalProgressBar->setValue(current);
- if (!m_is_multi_step) {
- ui->taskProgressBar->setMaximum(total);
- ui->taskProgressBar->setValue(current);
- } else {
- ui->taskProgressBar->setMaximum(task->getStepProgress());
- ui->taskProgressBar->setValue(task->getStepTotalProgress());
- }
}
void ProgressDialog::keyPressEvent(QKeyEvent* e)
diff --git a/launcher/ui/dialogs/ProgressDialog.h b/launcher/ui/dialogs/ProgressDialog.h
index 0b4b78a4..fc9a0fbc 100644
--- a/launcher/ui/dialogs/ProgressDialog.h
+++ b/launcher/ui/dialogs/ProgressDialog.h
@@ -1,22 +1,50 @@
-/* Copyright 2013-2021 MultiMC Contributors
+/// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PrismLaucher - Minecraft Launcher
+ * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
- * 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
+ * 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.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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.
*
- * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * 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 <QDialog>
#include <memory>
+#include <QHash>
+#include <QUuid>
+
+#include "QObjectPtr.h"
+#include "tasks/Task.h"
+
+#include "ui/widgets/SubTaskProgressBar.h"
class Task;
class SequentialTask;
@@ -52,6 +80,7 @@ slots:
void changeStatus(const QString &status);
void changeProgress(qint64 current, qint64 total);
+ void changeStepProgress(TaskStepProgress const& task_progress);
private
@@ -64,6 +93,7 @@ protected:
private:
bool handleImmediateResult(QDialog::DialogCode &result);
+ void addTaskProgress(TaskStepProgress const& progress);
private:
Ui::ProgressDialog *ui;
@@ -71,4 +101,8 @@ private:
Task *task;
bool m_is_multi_step = false;
+ QHash<QUuid, SubTaskProgressBar*> taskProgress;
+
+
};
+
diff --git a/launcher/ui/dialogs/ProgressDialog.ui b/launcher/ui/dialogs/ProgressDialog.ui
index 34ab71e3..a4d08124 100644
--- a/launcher/ui/dialogs/ProgressDialog.ui
+++ b/launcher/ui/dialogs/ProgressDialog.ui
@@ -2,75 +2,135 @@
<ui version="4.0">
<class>ProgressDialog</class>
<widget class="QDialog" name="ProgressDialog">
- <property name="minimumSize">
- <size>
- <width>400</width>
- <height>0</height>
- </size>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>480</width>
+ <height>210</height>
+ </rect>
</property>
- <property name="maximumSize">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>1</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
<size>
- <width>600</width>
- <height>16777215</height>
+ <width>480</width>
+ <height>210</height>
</size>
</property>
<property name="windowTitle">
<string>Please wait...</string>
</property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="4" column="0">
- <widget class="QPushButton" name="skipButton">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <property name="sizeGripEnabled">
+ <bool>true</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
+ <item>
+ <widget class="QLabel" name="globalStatusLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>15</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Global Task Status...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="globalStatusDetailsLabel">
+ <property name="text">
+ <string>Global Status Details...</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QProgressBar" name="globalProgressBar">
+ <property name="enabled">
+ <bool>true</bool>
</property>
- <property name="text">
- <string>Skip</string>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>24</height>
+ </size>
</property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="globalStatusLabel">
- <property name="text">
- <string>Global Task Status...</string>
+ <property name="value">
+ <number>24</number>
</property>
</widget>
</item>
- <item row="2" column="0">
- <widget class="QLabel" name="statusLabel">
+ <item>
+ <widget class="QScrollArea" name="taskProgressScrollArea">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+ <sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="text">
- <string>Task Status...</string>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>100</height>
+ </size>
</property>
- <property name="wordWrap">
- <bool>true</bool>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
</property>
- </widget>
- </item>
- <item row="3" column="0">
- <widget class="QProgressBar" name="taskProgressBar">
- <property name="value">
- <number>24</number>
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAsNeeded</enum>
+ </property>
+ <property name="sizeAdjustPolicy">
+ <enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
- <property name="textVisible">
- <bool>false</bool>
+ <property name="widgetResizable">
+ <bool>true</bool>
</property>
+ <widget class="QWidget" name="taskProgressContainer">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>464</width>
+ <height>96</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="taskProgressLayout">
+ <property name="spacing">
+ <number>2</number>
+ </property>
+ </layout>
+ </widget>
</widget>
</item>
- <item row="1" column="0">
- <widget class="QProgressBar" name="globalProgressBar">
- <property name="enabled">
- <bool>true</bool>
+ <item>
+ <widget class="QPushButton" name="skipButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
- <property name="value">
- <number>24</number>
+ <property name="text">
+ <string>Skip</string>
</property>
</widget>
</item>
diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp
index edb7d063..d2a8d33e 100644
--- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp
+++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp
@@ -89,7 +89,7 @@ void ResourceDownloadDialog::reject()
// won't work with subclasses if we put it in this ctor.
void ResourceDownloadDialog::initializeContainer()
{
- m_container = new PageContainer(this);
+ m_container = new PageContainer(this, {}, this);
m_container->setSizePolicy(QSizePolicy::Policy::Preferred, QSizePolicy::Policy::Expanding);
m_container->layout()->setContentsMargins(0, 0, 0, 0);
m_vertical_layout.addWidget(m_container);
diff --git a/launcher/ui/pages/global/MinecraftPage.cpp b/launcher/ui/pages/global/MinecraftPage.cpp
index cc597fe0..eca3e865 100644
--- a/launcher/ui/pages/global/MinecraftPage.cpp
+++ b/launcher/ui/pages/global/MinecraftPage.cpp
@@ -46,7 +46,6 @@
MinecraftPage::MinecraftPage(QWidget *parent) : QWidget(parent), ui(new Ui::MinecraftPage)
{
ui->setupUi(this);
- ui->tabWidget->tabBar()->hide();
loadSettings();
updateCheckboxStuff();
}
diff --git a/launcher/ui/pages/global/MinecraftPage.ui b/launcher/ui/pages/global/MinecraftPage.ui
index 640f436d..103881b5 100644
--- a/launcher/ui/pages/global/MinecraftPage.ui
+++ b/launcher/ui/pages/global/MinecraftPage.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>936</width>
- <height>1134</height>
+ <height>541</height>
</rect>
</property>
<property name="sizePolicy">
@@ -39,7 +39,7 @@
</property>
<widget class="QWidget" name="minecraftTab">
<attribute name="title">
- <string notr="true">Minecraft</string>
+ <string notr="true">General</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
@@ -112,22 +112,29 @@
</widget>
</item>
<item>
- <widget class="QGroupBox" name="nativeLibWorkaroundGroupBox">
+ <widget class="QGroupBox" name="gameTimeGroupBox">
<property name="title">
- <string>Native library workarounds</string>
+ <string>Game time</string>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_5">
+ <layout class="QVBoxLayout" name="verticalLayout_6">
<item>
- <widget class="QCheckBox" name="useNativeGLFWCheck">
+ <widget class="QCheckBox" name="showGameTime">
<property name="text">
- <string>Use system installation of &amp;GLFW</string>
+ <string>Show time spent &amp;playing instances</string>
</property>
</widget>
</item>
<item>
- <widget class="QCheckBox" name="useNativeOpenALCheck">
+ <widget class="QCheckBox" name="showGlobalGameTime">
<property name="text">
- <string>Use system installation of &amp;OpenAL</string>
+ <string>Show time spent playing across &amp;all instances</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="recordGameTime">
+ <property name="text">
+ <string>&amp;Record time spent playing instances</string>
</property>
</widget>
</item>
@@ -135,38 +142,28 @@
</widget>
</item>
<item>
- <widget class="QGroupBox" name="perfomanceGroupBox">
+ <widget class="QGroupBox" name="groupBox">
<property name="title">
- <string>Performance</string>
+ <string>Miscellaneous</string>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_2">
- <item>
- <widget class="QCheckBox" name="enableFeralGamemodeCheck">
- <property name="toolTip">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable Feral Interactive's GameMode, to potentially improve gaming performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
- </property>
- <property name="text">
- <string>Enable Feral GameMode</string>
- </property>
- </widget>
- </item>
+ <layout class="QVBoxLayout" name="verticalLayout">
<item>
- <widget class="QCheckBox" name="enableMangoHud">
+ <widget class="QCheckBox" name="closeAfterLaunchCheck">
<property name="toolTip">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable MangoHud's advanced performance overlay.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The launcher will automatically reopen when the game crashes or exits.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
- <string>Enable MangoHud</string>
+ <string>&amp;Close the launcher after game window opens</string>
</property>
</widget>
</item>
<item>
- <widget class="QCheckBox" name="useDiscreteGpuCheck">
+ <widget class="QCheckBox" name="quitAfterGameStopCheck">
<property name="toolTip">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use the discrete GPU instead of the primary GPU.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The launcher will automatically quit after the game exits or crashes.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
- <string>Use discrete GPU</string>
+ <string>&amp;Quit the launcher after game window closes</string>
</property>
</widget>
</item>
@@ -174,29 +171,42 @@
</widget>
</item>
<item>
- <widget class="QGroupBox" name="gameTimeGroupBox">
+ <spacer name="verticalSpacerMinecraft">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab">
+ <attribute name="title">
+ <string>Tweaks</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_12">
+ <item>
+ <widget class="QGroupBox" name="nativeLibWorkaroundGroupBox">
<property name="title">
- <string>Game time</string>
+ <string>Native library workarounds</string>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_6">
+ <layout class="QVBoxLayout" name="verticalLayout_11">
<item>
- <widget class="QCheckBox" name="showGameTime">
- <property name="text">
- <string>Show time spent &amp;playing instances</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QCheckBox" name="showGlobalGameTime">
+ <widget class="QCheckBox" name="useNativeGLFWCheck">
<property name="text">
- <string>Show time spent playing across &amp;all instances</string>
+ <string>Use system installation of &amp;GLFW</string>
</property>
</widget>
</item>
<item>
- <widget class="QCheckBox" name="recordGameTime">
+ <widget class="QCheckBox" name="useNativeOpenALCheck">
<property name="text">
- <string>&amp;Record time spent playing instances</string>
+ <string>Use system installation of &amp;OpenAL</string>
</property>
</widget>
</item>
@@ -204,28 +214,38 @@
</widget>
</item>
<item>
- <widget class="QGroupBox" name="groupBox">
+ <widget class="QGroupBox" name="perfomanceGroupBox">
<property name="title">
- <string>Miscellaneous</string>
+ <string>Performance</string>
</property>
- <layout class="QVBoxLayout" name="verticalLayout">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
<item>
- <widget class="QCheckBox" name="closeAfterLaunchCheck">
+ <widget class="QCheckBox" name="enableFeralGamemodeCheck">
<property name="toolTip">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The launcher will automatically reopen when the game crashes or exits.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable Feral Interactive's GameMode, to potentially improve gaming performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
- <string>&amp;Close the launcher after game window opens</string>
+ <string>Enable Feral GameMode</string>
</property>
</widget>
</item>
<item>
- <widget class="QCheckBox" name="quitAfterGameStopCheck">
+ <widget class="QCheckBox" name="enableMangoHud">
<property name="toolTip">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The launcher will automatically quit after the game exits or crashes.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable MangoHud's advanced performance overlay.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
- <string>&amp;Quit the launcher after game window closes</string>
+ <string>Enable MangoHud</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="useDiscreteGpuCheck">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use the discrete GPU instead of the primary GPU.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Use discrete GPU</string>
</property>
</widget>
</item>
@@ -233,14 +253,14 @@
</widget>
</item>
<item>
- <spacer name="verticalSpacerMinecraft">
+ <spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
- <width>0</width>
- <height>0</height>
+ <width>20</width>
+ <height>40</height>
</size>
</property>
</spacer>
@@ -255,11 +275,6 @@
<tabstop>maximizedCheckBox</tabstop>
<tabstop>windowWidthSpinBox</tabstop>
<tabstop>windowHeightSpinBox</tabstop>
- <tabstop>useNativeGLFWCheck</tabstop>
- <tabstop>useNativeOpenALCheck</tabstop>
- <tabstop>enableFeralGamemodeCheck</tabstop>
- <tabstop>enableMangoHud</tabstop>
- <tabstop>useDiscreteGpuCheck</tabstop>
</tabstops>
<resources/>
<connections/>
diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp
index dc983d9a..d0701a7a 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
{
@@ -41,6 +39,37 @@ class NoBigComboBoxStyle : public QProxyStyle {
return QProxyStyle::styleHint(hint, option, widget, returnData);
}
// clang-format on
+
+ /**
+ * 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)
+ {
+ static QHash<QStyle*, NoBigComboBoxStyle*> s_singleton_instances_ = {};
+ static std::mutex s_singleton_instances_mutex_;
+
+ std::lock_guard<std::mutex> 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:
+ NoBigComboBoxStyle(QStyle* style) : QProxyStyle(style) {}
+
};
ManagedPackPage* ManagedPackPage::createPage(BaseInstance* inst, QString type, QWidget* parent)
@@ -62,8 +91,10 @@ 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 = NoBigComboBoxStyle::getInstance(ui->versionsComboBox->style());
+ ui->versionsComboBox->setStyle(comboStyle);
+ }
ui->reloadButton->setVisible(false);
connect(ui->reloadButton, &QPushButton::clicked, this, [this](bool){
diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp
index 7fff3b93..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);
@@ -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();
diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp
index 3ffe6cb0..afd8b292 100644
--- a/launcher/ui/pages/modplatform/ModModel.cpp
+++ b/launcher/ui/pages/modplatform/ModModel.cpp
@@ -36,7 +36,7 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments()
ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& entry)
{
- auto& pack = m_packs[entry.row()];
+ auto& pack = *m_packs[entry.row()];
auto profile = static_cast<MinecraftInstance const&>(m_base_instance).getPackProfile();
Q_ASSERT(profile);
@@ -51,7 +51,7 @@ ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& en
ResourceAPI::ProjectInfoArgs ModModel::createInfoArguments(QModelIndex& entry)
{
- auto& pack = m_packs[entry.row()];
+ auto& pack = *m_packs[entry.row()];
return { pack };
}
diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h
index c3b58cd6..4ea55efa 100644
--- a/launcher/ui/pages/modplatform/ModPage.h
+++ b/launcher/ui/pages/modplatform/ModPage.h
@@ -41,8 +41,6 @@ class ModPage : public ResourcePage {
return page;
}
- ~ModPage() override = default;
-
//: The plural version of 'mod'
[[nodiscard]] inline QString resourcesString() const override { return tr("mods"); }
//: The singular version of 'mods'
diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp
index db7d26f8..472aa851 100644
--- a/launcher/ui/pages/modplatform/ResourceModel.cpp
+++ b/launcher/ui/pages/modplatform/ResourceModel.cpp
@@ -9,6 +9,7 @@
#include <QMessageBox>
#include <QPixmapCache>
#include <QUrl>
+#include <memory>
#include "Application.h"
#include "BuildConfig.h"
@@ -45,16 +46,16 @@ auto ResourceModel::data(const QModelIndex& index, int role) const -> QVariant
auto pack = m_packs.at(pos);
switch (role) {
case Qt::ToolTipRole: {
- if (pack.description.length() > 100) {
+ if (pack->description.length() > 100) {
// some magic to prevent to long tooltips and replace html linebreaks
- QString edit = pack.description.left(97);
+ QString edit = pack->description.left(97);
edit = edit.left(edit.lastIndexOf("<br>")).left(edit.lastIndexOf(" ")).append("...");
return edit;
}
- return pack.description;
+ return pack->description;
}
case Qt::DecorationRole: {
- if (auto icon_or_none = const_cast<ResourceModel*>(this)->getIcon(const_cast<QModelIndex&>(index), pack.logoUrl);
+ if (auto icon_or_none = const_cast<ResourceModel*>(this)->getIcon(const_cast<QModelIndex&>(index), pack->logoUrl);
icon_or_none.has_value())
return icon_or_none.value();
@@ -64,16 +65,16 @@ auto ResourceModel::data(const QModelIndex& index, int role) const -> QVariant
return QSize(0, 58);
case Qt::UserRole: {
QVariant v;
- v.setValue(pack);
+ v.setValue(*pack);
return v;
}
// Custom data
case UserDataTypes::TITLE:
- return pack.name;
+ return pack->name;
case UserDataTypes::DESCRIPTION:
- return pack.description;
+ return pack->description;
case UserDataTypes::SELECTED:
- return pack.isAnyVersionSelected();
+ return pack->isAnyVersionSelected();
default:
break;
}
@@ -102,7 +103,7 @@ bool ResourceModel::setData(const QModelIndex& index, const QVariant& value, int
if (pos >= m_packs.size() || pos < 0 || !index.isValid())
return false;
- m_packs[pos] = value.value<ModPlatform::IndexedPack>();
+ m_packs[pos] = std::make_shared<ModPlatform::IndexedPack>(value.value<ModPlatform::IndexedPack>());
emit dataChanged(index, index);
return true;
@@ -161,7 +162,7 @@ void ResourceModel::loadEntry(QModelIndex& entry)
if (!hasActiveInfoJob())
m_current_info_job.clear();
- if (!pack.versionsLoaded) {
+ if (!pack->versionsLoaded) {
auto args{ createVersionsArguments(entry) };
auto callbacks{ createVersionsCallbacks(entry) };
@@ -177,7 +178,7 @@ void ResourceModel::loadEntry(QModelIndex& entry)
runInfoJob(job);
}
- if (!pack.extraDataLoaded) {
+ if (!pack->extraDataLoaded) {
auto args{ createInfoArguments(entry) };
auto callbacks{ createInfoCallbacks(entry) };
@@ -229,7 +230,7 @@ void ResourceModel::clearData()
void ResourceModel::runSearchJob(Task::Ptr ptr)
{
- m_current_search_job = ptr;
+ m_current_search_job.reset(ptr); // clean up first
m_current_search_job->start();
}
void ResourceModel::runInfoJob(Task::Ptr ptr)
@@ -326,15 +327,15 @@ void ResourceModel::loadIndexedPackVersions(ModPlatform::IndexedPack&, QJsonArra
void ResourceModel::searchRequestSucceeded(QJsonDocument& doc)
{
- QList<ModPlatform::IndexedPack> newList;
+ QList<ModPlatform::IndexedPack::Ptr> newList;
auto packs = documentToArray(doc);
for (auto packRaw : packs) {
auto packObj = packRaw.toObject();
- ModPlatform::IndexedPack pack;
+ ModPlatform::IndexedPack::Ptr pack = std::make_shared<ModPlatform::IndexedPack>();
try {
- loadIndexedPack(pack, packObj);
+ loadIndexedPack(*pack, packObj);
newList.append(pack);
} catch (const JSONValidationError& e) {
qWarning() << "Error while loading resource from " << debugName() << ": " << e.cause();
diff --git a/launcher/ui/pages/modplatform/ResourceModel.h b/launcher/ui/pages/modplatform/ResourceModel.h
index 46a02d6e..1ec42cda 100644
--- a/launcher/ui/pages/modplatform/ResourceModel.h
+++ b/launcher/ui/pages/modplatform/ResourceModel.h
@@ -123,7 +123,7 @@ class ResourceModel : public QAbstractListModel {
QSet<QUrl> m_currently_running_icon_actions;
QSet<QUrl> m_failed_icon_actions;
- QList<ModPlatform::IndexedPack> m_packs;
+ QList<ModPlatform::IndexedPack::Ptr> 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?
diff --git a/launcher/ui/pages/modplatform/ResourcePackModel.cpp b/launcher/ui/pages/modplatform/ResourcePackModel.cpp
index 3df9a787..18c14bf8 100644
--- a/launcher/ui/pages/modplatform/ResourcePackModel.cpp
+++ b/launcher/ui/pages/modplatform/ResourcePackModel.cpp
@@ -22,13 +22,13 @@ ResourceAPI::SearchArgs ResourcePackResourceModel::createSearchArguments()
ResourceAPI::VersionSearchArgs ResourcePackResourceModel::createVersionsArguments(QModelIndex& entry)
{
auto& pack = m_packs[entry.row()];
- return { pack };
+ return { *pack };
}
ResourceAPI::ProjectInfoArgs ResourcePackResourceModel::createInfoArguments(QModelIndex& entry)
{
auto& pack = m_packs[entry.row()];
- return { pack };
+ return { *pack };
}
void ResourcePackResourceModel::searchWithTerm(const QString& term, unsigned int sort)
diff --git a/launcher/ui/pages/modplatform/ResourcePackPage.h b/launcher/ui/pages/modplatform/ResourcePackPage.h
index c01c89c4..8c5cf08b 100644
--- a/launcher/ui/pages/modplatform/ResourcePackPage.h
+++ b/launcher/ui/pages/modplatform/ResourcePackPage.h
@@ -31,8 +31,6 @@ class ResourcePackResourcePage : public ResourcePage {
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'
diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp
index bbd465bc..f75bb886 100644
--- a/launcher/ui/pages/modplatform/ResourcePage.cpp
+++ b/launcher/ui/pages/modplatform/ResourcePage.cpp
@@ -83,6 +83,8 @@ ResourcePage::ResourcePage(ResourceDownloadDialog* parent, BaseInstance& base_in
ResourcePage::~ResourcePage()
{
delete m_ui;
+ if (m_model)
+ delete m_model;
}
void ResourcePage::retranslate()
diff --git a/launcher/ui/pages/modplatform/ShaderPackModel.cpp b/launcher/ui/pages/modplatform/ShaderPackModel.cpp
index 2101b394..aabd3be6 100644
--- a/launcher/ui/pages/modplatform/ShaderPackModel.cpp
+++ b/launcher/ui/pages/modplatform/ShaderPackModel.cpp
@@ -22,13 +22,13 @@ ResourceAPI::SearchArgs ShaderPackResourceModel::createSearchArguments()
ResourceAPI::VersionSearchArgs ShaderPackResourceModel::createVersionsArguments(QModelIndex& entry)
{
auto& pack = m_packs[entry.row()];
- return { pack };
+ return { *pack };
}
ResourceAPI::ProjectInfoArgs ShaderPackResourceModel::createInfoArguments(QModelIndex& entry)
{
auto& pack = m_packs[entry.row()];
- return { pack };
+ return { *pack };
}
void ShaderPackResourceModel::searchWithTerm(const QString& term, unsigned int sort)
diff --git a/launcher/ui/pages/modplatform/ShaderPackPage.h b/launcher/ui/pages/modplatform/ShaderPackPage.h
index 972419a8..9039c4d9 100644
--- a/launcher/ui/pages/modplatform/ShaderPackPage.h
+++ b/launcher/ui/pages/modplatform/ShaderPackPage.h
@@ -31,8 +31,6 @@ class ShaderPackResourcePage : public ResourcePage {
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'
diff --git a/launcher/ui/widgets/PageContainer.cpp b/launcher/ui/widgets/PageContainer.cpp
index 0a06a351..b9b17b42 100644
--- a/launcher/ui/widgets/PageContainer.cpp
+++ b/launcher/ui/widgets/PageContainer.cpp
@@ -87,7 +87,9 @@ PageContainer::PageContainer(BasePageProvider *pageProvider, QString defaultId,
auto pages = pageProvider->getPages();
for (auto page : pages)
{
- page->stackIndex = m_pageStack->addWidget(dynamic_cast<QWidget *>(page));
+ auto widget = dynamic_cast<QWidget *>(page);
+ widget->setParent(this);
+ page->stackIndex = m_pageStack->addWidget(widget);
page->listIndex = counter;
page->setParentContainer(this);
counter++;
diff --git a/launcher/ui/widgets/ProgressWidget.cpp b/launcher/ui/widgets/ProgressWidget.cpp
index f736af08..9181de7f 100644
--- a/launcher/ui/widgets/ProgressWidget.cpp
+++ b/launcher/ui/widgets/ProgressWidget.cpp
@@ -51,6 +51,7 @@ void ProgressWidget::watch(const Task* task)
connect(m_task, &Task::finished, this, &ProgressWidget::handleTaskFinish);
connect(m_task, &Task::status, this, &ProgressWidget::handleTaskStatus);
+ // TODO: should we connect &Task::details
connect(m_task, &Task::progress, this, &ProgressWidget::handleTaskProgress);
connect(m_task, &Task::destroyed, this, &ProgressWidget::taskDestroyed);
diff --git a/launcher/ui/widgets/SubTaskProgressBar.cpp b/launcher/ui/widgets/SubTaskProgressBar.cpp
new file mode 100644
index 00000000..84ea5f20
--- /dev/null
+++ b/launcher/ui/widgets/SubTaskProgressBar.cpp
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PrismLaucher - Minecraft Launcher
+ * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "SubTaskProgressBar.h"
+#include "ui_SubTaskProgressBar.h"
+
+unique_qobject_ptr<SubTaskProgressBar> SubTaskProgressBar::create(QWidget* parent)
+{
+ auto progress_bar = new SubTaskProgressBar(parent);
+ return unique_qobject_ptr<SubTaskProgressBar>(progress_bar);
+}
+
+SubTaskProgressBar::SubTaskProgressBar(QWidget* parent)
+ : ui(new Ui::SubTaskProgressBar)
+{
+ ui->setupUi(this);
+}
+SubTaskProgressBar::~SubTaskProgressBar()
+{
+ delete ui;
+}
+
+void SubTaskProgressBar::setRange(int min, int max)
+{
+ ui->progressBar->setRange(min, max);
+}
+
+void SubTaskProgressBar::setValue(int value)
+{
+ ui->progressBar->setValue(value);
+}
+
+void SubTaskProgressBar::setStatus(QString status)
+{
+ ui->statusLabel->setText(status);
+}
+
+void SubTaskProgressBar::setDetails(QString details)
+{
+ ui->statusDetailsLabel->setText(details);
+}
+
diff --git a/launcher/ui/widgets/SubTaskProgressBar.h b/launcher/ui/widgets/SubTaskProgressBar.h
new file mode 100644
index 00000000..8f8aeea2
--- /dev/null
+++ b/launcher/ui/widgets/SubTaskProgressBar.h
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PrismLaucher - Minecraft Launcher
+ * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include <QWidget>
+#include "QObjectPtr.h"
+
+namespace Ui {
+class SubTaskProgressBar;
+}
+
+class SubTaskProgressBar : public QWidget
+{
+ Q_OBJECT
+
+public:
+ static unique_qobject_ptr<SubTaskProgressBar> create(QWidget* parent = nullptr);
+
+ SubTaskProgressBar(QWidget* parent = nullptr);
+ ~SubTaskProgressBar();
+
+ void setRange(int min, int max);
+ void setValue(int value);
+ void setStatus(QString status);
+ void setDetails(QString details);
+
+
+
+private:
+ Ui::SubTaskProgressBar* ui;
+
+};
diff --git a/launcher/ui/widgets/SubTaskProgressBar.ui b/launcher/ui/widgets/SubTaskProgressBar.ui
new file mode 100644
index 00000000..5431eab6
--- /dev/null
+++ b/launcher/ui/widgets/SubTaskProgressBar.ui
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SubTaskProgressBar</class>
+ <widget class="QWidget" name="SubTaskProgressBar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>312</width>
+ <height>86</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
+ <property name="spacing">
+ <number>8</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="statusLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>8</pointsize>
+ </font>
+ </property>
+ <property name="text">
+ <string>Sub Task Status...</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="statusDetailsLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>8</pointsize>
+ </font>
+ </property>
+ <property name="text">
+ <string>Status Details</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QProgressBar" name="progressBar">
+ <property name="font">
+ <font>
+ <pointsize>8</pointsize>
+ </font>
+ </property>
+ <property name="value">
+ <number>24</number>
+ </property>
+ <property name="textVisible">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
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;
}
}
diff --git a/program_info/org.prismlauncher.PrismLauncher.desktop.in b/program_info/org.prismlauncher.PrismLauncher.desktop.in
index f08f2ba4..20fabe9d 100644
--- a/program_info/org.prismlauncher.PrismLauncher.desktop.in
+++ b/program_info/org.prismlauncher.PrismLauncher.desktop.in
@@ -8,6 +8,6 @@ Exec=@Launcher_APP_BINARY_NAME@
StartupNotify=true
Icon=org.prismlauncher.PrismLauncher
Categories=Game;ActionGame;AdventureGame;Simulation;
-Keywords=game;minecraft;launcher;mc;multimc;polymc;
+Keywords=game;minecraft;mc;
StartupWMClass=PrismLauncher
MimeType=application/zip;application/x-modrinth-modpack+zip
diff --git a/program_info/org.prismlauncher.PrismLauncher.metainfo.xml.in b/program_info/org.prismlauncher.PrismLauncher.metainfo.xml.in
index 96708960..a482f0e3 100644
--- a/program_info/org.prismlauncher.PrismLauncher.metainfo.xml.in
+++ b/program_info/org.prismlauncher.PrismLauncher.metainfo.xml.in
@@ -10,7 +10,7 @@
<url type="homepage">https://prismlauncher.org/</url>
<url type="help">https://prismlauncher.org/wiki/</url>
<url type="bugtracker">https://github.com/PrismLauncher/PrismLauncher/issues</url>
- <url type="contact">https://discord.gg/prismlauncher</url>
+ <url type="contact">https://prismlauncher.org/discord</url>
<url type="vcs-browser">https://github.com/PrismLauncher/PrismLauncher</url>
<url type="contribute">https://github.com/PrismLauncher/PrismLauncher/blob/develop/CONTRIBUTING.md</url>
<url type="translate">https://hosted.weblate.org/projects/prismlauncher/launcher</url>
diff --git a/program_info/win_install.nsi.in b/program_info/win_install.nsi.in
index f8918c58..1d902d5d 100644
--- a/program_info/win_install.nsi.in
+++ b/program_info/win_install.nsi.in
@@ -283,6 +283,7 @@ Section "@Launcher_DisplayName@"
File "@Launcher_APP_BINARY_NAME@.exe"
File "@Launcher_APP_BINARY_NAME@_filelink.exe"
File "qt.conf"
+ File "qtlogging.ini"
File *.dll
File /r "iconengines"
File /r "imageformats"
diff --git a/tests/ResourceFolderModel_test.cpp b/tests/ResourceFolderModel_test.cpp
index 054d81c4..962d89f1 100644
--- a/tests/ResourceFolderModel_test.cpp
+++ b/tests/ResourceFolderModel_test.cpp
@@ -90,9 +90,7 @@ slots:
QEventLoop loop;
- InstancePtr instance;
-
- ModFolderModel m(tempDir.path(), instance, true);
+ ModFolderModel m(tempDir.path(), nullptr, true);
connect(&m, &ModFolderModel::updateFinished, &loop, &QEventLoop::quit);
@@ -116,8 +114,7 @@ slots:
QString folder = source + '/';
QTemporaryDir tempDir;
QEventLoop loop;
- InstancePtr instance;
- ModFolderModel m(tempDir.path(), instance, true);
+ ModFolderModel m(tempDir.path(), nullptr, true);
connect(&m, &ModFolderModel::updateFinished, &loop, &QEventLoop::quit);
@@ -140,8 +137,7 @@ slots:
void test_addFromWatch()
{
QString source = QFINDTESTDATA("testdata/ResourceFolderModel");
- InstancePtr instance;
- ModFolderModel model(source, instance);
+ ModFolderModel model(source, nullptr);
QCOMPARE(model.size(), 0);
@@ -161,9 +157,7 @@ slots:
QString file_mod = QFINDTESTDATA("testdata/ResourceFolderModel/supercoolmod.jar");
QTemporaryDir tmp;
- InstancePtr instance;
-
- ResourceFolderModel model(QDir(tmp.path()), instance);
+ ResourceFolderModel model(QDir(tmp.path()), nullptr);
QCOMPARE(model.size(), 0);
@@ -214,8 +208,7 @@ slots:
QString file_mod = QFINDTESTDATA("testdata/ResourceFolderModel/supercoolmod.jar");
QTemporaryDir tmp;
- InstancePtr instance;
- ResourceFolderModel model(tmp.path(), instance);
+ ResourceFolderModel model(tmp.path(), nullptr);
QCOMPARE(model.size(), 0);
diff --git a/tests/ResourceModel_test.cpp b/tests/ResourceModel_test.cpp
index 716bf853..c0d9cd95 100644
--- a/tests/ResourceModel_test.cpp
+++ b/tests/ResourceModel_test.cpp
@@ -75,9 +75,9 @@ class ResourceModelTest : public QObject {
auto search_json = DummyResourceAPI::searchRequestResult();
auto processed_response = model->documentToArray(search_json).first().toObject();
- QVERIFY(processed_pack.addonId.toString() == Json::requireString(processed_response, "project_id"));
- QVERIFY(processed_pack.description == Json::requireString(processed_response, "description"));
- QVERIFY(processed_pack.authors.first().name == Json::requireString(processed_response, "author"));
+ QVERIFY(processed_pack->addonId.toString() == Json::requireString(processed_response, "project_id"));
+ QVERIFY(processed_pack->description == Json::requireString(processed_response, "description"));
+ QVERIFY(processed_pack->authors.first().name == Json::requireString(processed_response, "author"));
}
};
diff --git a/tests/Task_test.cpp b/tests/Task_test.cpp
index 95eb4a30..dabe5da2 100644
--- a/tests/Task_test.cpp
+++ b/tests/Task_test.cpp
@@ -69,8 +69,9 @@ class BigConcurrentTaskThread : public QThread {
auto sub_tasks = new BasicTask::Ptr[s_num_tasks];
for (unsigned i = 0; i < s_num_tasks; i++) {
- sub_tasks[i] = makeShared<BasicTask>(false);
- big_task.addTask(sub_tasks[i]);
+ auto sub_task = makeShared<BasicTask>(false);
+ sub_tasks[i] = sub_task;
+ big_task.addTask(sub_task);
}
big_task.run();
@@ -99,7 +100,7 @@ class TaskTest : public QObject {
t.setStatus(status);
QCOMPARE(t.getStatus(), status);
- QCOMPARE(t.getStepStatus(), status);
+ QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty());
}
void test_SetStatus_MultiStep(){
@@ -111,7 +112,7 @@ class TaskTest : public QObject {
QCOMPARE(t.getStatus(), status);
// Even though it is multi step, it does not override the getStepStatus method,
// so it should remain the same.
- QCOMPARE(t.getStepStatus(), status);
+ QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty());
}
void test_SetProgress(){