Merge branch 'PrismLauncher:develop' into refresh_cats

This commit is contained in:
Sajjaad Farzad 2023-12-24 16:03:13 -08:00 committed by GitHub
commit b0a4af7412
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 345 additions and 187 deletions

View File

@ -24,7 +24,7 @@ jobs:
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
- name: Create backport PRs - name: Create backport PRs
uses: korthout/backport-action@v2.1.1 uses: korthout/backport-action@v2.3.0
with: with:
# Config README: https://github.com/korthout/backport-action#backport-action # Config README: https://github.com/korthout/backport-action#backport-action
pull_description: |- pull_description: |-

View File

@ -61,7 +61,7 @@ jobs:
qt_ver: 6 qt_ver: 6
qt_host: windows qt_host: windows
qt_arch: '' qt_arch: ''
qt_version: '6.6.0' qt_version: '6.6.1'
qt_modules: 'qt5compat qtimageformats' qt_modules: 'qt5compat qtimageformats'
qt_tools: '' qt_tools: ''
@ -73,7 +73,7 @@ jobs:
qt_ver: 6 qt_ver: 6
qt_host: windows qt_host: windows
qt_arch: 'win64_msvc2019_arm64' qt_arch: 'win64_msvc2019_arm64'
qt_version: '6.6.0' qt_version: '6.6.1'
qt_modules: 'qt5compat qtimageformats' qt_modules: 'qt5compat qtimageformats'
qt_tools: '' qt_tools: ''
@ -83,7 +83,7 @@ jobs:
qt_ver: 6 qt_ver: 6
qt_host: mac qt_host: mac
qt_arch: '' qt_arch: ''
qt_version: '6.6.0' qt_version: '6.6.1'
qt_modules: 'qt5compat qtimageformats' qt_modules: 'qt5compat qtimageformats'
qt_tools: '' qt_tools: ''
@ -517,70 +517,70 @@ jobs:
- name: Upload binary tarball (macOS) - name: Upload binary tarball (macOS)
if: runner.os == 'macOS' if: runner.os == 'macOS'
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }} name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher.tar.gz path: PrismLauncher.tar.gz
- name: Upload binary zip (Windows) - name: Upload binary zip (Windows)
if: runner.os == 'Windows' if: runner.os == 'Windows'
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }} name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
path: ${{ env.INSTALL_DIR }}/** path: ${{ env.INSTALL_DIR }}/**
- name: Upload binary zip (Windows, portable) - name: Upload binary zip (Windows, portable)
if: runner.os == 'Windows' if: runner.os == 'Windows'
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: PrismLauncher-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }} name: PrismLauncher-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: ${{ env.INSTALL_PORTABLE_DIR }}/** path: ${{ env.INSTALL_PORTABLE_DIR }}/**
- name: Upload installer (Windows) - name: Upload installer (Windows)
if: runner.os == 'Windows' if: runner.os == 'Windows'
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }} name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher-Setup.exe path: PrismLauncher-Setup.exe
- name: Upload binary tarball (Linux, Qt 5) - name: Upload binary tarball (Linux, Qt 5)
if: runner.os == 'Linux' && matrix.qt_ver != 6 if: runner.os == 'Linux' && matrix.qt_ver != 6
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: PrismLauncher-${{ runner.os }}-Qt5-${{ env.VERSION }}-${{ inputs.build_type }} name: PrismLauncher-${{ runner.os }}-Qt5-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher.tar.gz path: PrismLauncher.tar.gz
- name: Upload binary tarball (Linux, portable, Qt 5) - name: Upload binary tarball (Linux, portable, Qt 5)
if: runner.os == 'Linux' && matrix.qt_ver != 6 if: runner.os == 'Linux' && matrix.qt_ver != 6
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: PrismLauncher-${{ runner.os }}-Qt5-Portable-${{ env.VERSION }}-${{ inputs.build_type }} name: PrismLauncher-${{ runner.os }}-Qt5-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher-portable.tar.gz path: PrismLauncher-portable.tar.gz
- name: Upload binary tarball (Linux, Qt 6) - name: Upload binary tarball (Linux, Qt 6)
if: runner.os == 'Linux' && matrix.qt_ver !=5 if: runner.os == 'Linux' && matrix.qt_ver !=5
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: PrismLauncher-${{ runner.os }}-Qt6-${{ env.VERSION }}-${{ inputs.build_type }} name: PrismLauncher-${{ runner.os }}-Qt6-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher.tar.gz path: PrismLauncher.tar.gz
- name: Upload binary tarball (Linux, portable, Qt 6) - name: Upload binary tarball (Linux, portable, Qt 6)
if: runner.os == 'Linux' && matrix.qt_ver != 5 if: runner.os == 'Linux' && matrix.qt_ver != 5
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: PrismLauncher-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }} name: PrismLauncher-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher-portable.tar.gz path: PrismLauncher-portable.tar.gz
- name: Upload AppImage (Linux) - name: Upload AppImage (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 5 if: runner.os == 'Linux' && matrix.qt_ver != 5
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
- name: Upload AppImage Zsync (Linux) - name: Upload AppImage Zsync (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 5 if: runner.os == 'Linux' && matrix.qt_ver != 5
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync
path: PrismLauncher-Linux-x86_64.AppImage.zsync path: PrismLauncher-Linux-x86_64.AppImage.zsync

View File

@ -13,7 +13,7 @@ jobs:
submodules: 'true' submodules: 'true'
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v2 uses: github/codeql-action/init@v3
with: with:
config-file: ./.github/codeql/codeql-config.yml config-file: ./.github/codeql/codeql-config.yml
queries: security-and-quality queries: security-and-quality
@ -32,4 +32,4 @@ jobs:
cmake --build build cmake --build build
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2 uses: github/codeql-action/analyze@v3

View File

@ -32,7 +32,7 @@ jobs:
submodules: "true" submodules: "true"
path: "PrismLauncher-source" path: "PrismLauncher-source"
- name: Download artifacts - name: Download artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
- name: Grab and store version - name: Grab and store version
run: | run: |
tag_name=$(echo ${{ github.ref }} | grep -oE "[^/]+$") tag_name=$(echo ${{ github.ref }} | grep -oE "[^/]+$")

View File

@ -17,7 +17,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@6a9a9e84a173d90b3ffb42c5ddaf9ea033fad011 # v23 - uses: cachix/install-nix-action@7ac1ec25491415c381d9b62f0657c7a028df52a7 # v24
- uses: DeterminateSystems/update-flake-lock@v20 - uses: DeterminateSystems/update-flake-lock@v20
with: with:

30
flake.lock generated
View File

@ -21,11 +21,11 @@
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": "nixpkgs-lib"
}, },
"locked": { "locked": {
"lastModified": 1698882062, "lastModified": 1701473968,
"narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=", "narHash": "sha256-YcVE5emp1qQ8ieHUnxt1wCZCC3ZfAS+SRRWZ2TMda7E=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "8c9fa2545007b49a5db5f650ae91f227672c3877", "rev": "34fed993f1674c8d06d58b37ce1e0fe5eebcb9f5",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -91,11 +91,11 @@
}, },
"nix-filter": { "nix-filter": {
"locked": { "locked": {
"lastModified": 1694857738, "lastModified": 1701697642,
"narHash": "sha256-bxxNyLHjhu0N8T3REINXQ2ZkJco0ABFPn6PIe2QUfqo=", "narHash": "sha256-L217WytWZHSY8GW9Gx1A64OnNctbuDbfslaTEofXXRw=",
"owner": "numtide", "owner": "numtide",
"repo": "nix-filter", "repo": "nix-filter",
"rev": "41fd48e00c22b4ced525af521ead8792402de0ea", "rev": "c843418ecfd0344ecb85844b082ff5675e02c443",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -106,11 +106,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1700108881, "lastModified": 1703134684,
"narHash": "sha256-+Lqybl8kj0+nD/IlAWPPG/RDTa47gff9nbei0u7BntE=", "narHash": "sha256-SQmng1EnBFLzS7WSRyPM9HgmZP2kLJcPAz+Ug/nug6o=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "7414e9ee0b3e9903c24d3379f577a417f0aae5f1", "rev": "d6863cbcbbb80e71cecfc03356db1cda38919523",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -123,11 +123,11 @@
"nixpkgs-lib": { "nixpkgs-lib": {
"locked": { "locked": {
"dir": "lib", "dir": "lib",
"lastModified": 1698611440, "lastModified": 1701253981,
"narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=", "narHash": "sha256-ztaDIyZ7HrTAfEEUt9AtTDNoCYxUdSd6NrRHaYOIxtk=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735", "rev": "e92039b55bcd58469325ded85d4f58dd5a4eaf58",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -153,11 +153,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1700064067, "lastModified": 1702456155,
"narHash": "sha256-1ZWNDzhu8UlVCK7+DUN9dVQfiHX1bv6OQP9VxstY/gs=", "narHash": "sha256-I2XhXGAecdGlqi6hPWYT83AQtMgL+aa3ulA85RAEgOk=",
"owner": "cachix", "owner": "cachix",
"repo": "pre-commit-hooks.nix", "repo": "pre-commit-hooks.nix",
"rev": "e558068cba67b23b4fbc5537173dbb43748a17e8", "rev": "007a45d064c1c32d04e1b8a0de5ef00984c419bc",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -1,6 +1,6 @@
id: org.prismlauncher.PrismLauncher id: org.prismlauncher.PrismLauncher
runtime: org.kde.Platform runtime: org.kde.Platform
runtime-version: "5.15-23.08" runtime-version: 5.15-23.08
sdk: org.kde.Sdk sdk: org.kde.Sdk
sdk-extensions: sdk-extensions:
- org.freedesktop.Sdk.Extension.openjdk17 - org.freedesktop.Sdk.Extension.openjdk17
@ -104,18 +104,15 @@ modules:
- install -Dm755 ../data/gamemoderun -t /app/bin - install -Dm755 ../data/gamemoderun -t /app/bin
sources: sources:
- type: archive - type: archive
archive-type: tar-gzip dest-filename: gamemode.tar.gz
url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.7 url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.8.1
sha256: 57ce73ba605d1cf12f8d13725006a895182308d93eba0f69f285648449641803 sha256: 969cf85b5ca3944f3e315cd73a0ee9bea4f9c968cd7d485e9f4745bc1e679c4e
x-checker-data: x-checker-data:
type: json type: json
url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest
version-query: .tag_name version-query: .tag_name
url-query: .tarball_url url-query: .tarball_url
timestamp-query: .published_at timestamp-query: .published_at
# from https://github.com/flathub/net.gaijin.WarThunder/blob/7ea6f7a9f84b9c77150c003a7059dc03f8dcbc7f/gamemode.patch
- type: patch
path: patches/gamemode.patch
cleanup: cleanup:
- /include - /include
- /lib/pkgconfig - /lib/pkgconfig

View File

@ -1,12 +0,0 @@
diff -ruN a/common/common-pidfds.c b/common/common-pidfds.c
--- a/common/common-pidfds.c 2021-02-18 20:00:12.000000000 +0100
+++ b/common/common-pidfds.c 2023-09-07 08:57:42.954362763 +0200
@@ -58,6 +58,8 @@
{
return (int)syscall(__NR_pidfd_open, pid, flags);
}
+#else
+#include <sys/pidfd.h>
#endif
/* pidfd functions */

@ -1 +1 @@
Subproject commit 45094ca570be383d06df729b6972830ec63bd3df Subproject commit 55a8e460c6343229597a13e973ba4855c27a1c4c

View File

@ -132,6 +132,15 @@
#include "gamemode_client.h" #include "gamemode_client.h"
#endif #endif
#if defined(Q_OS_LINUX)
#include <sys/statvfs.h>
#endif
#if defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
#include <sys/mount.h>
#include <sys/types.h>
#endif
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
#if defined(SPARKLE_ENABLED) #if defined(SPARKLE_ENABLED)
#include "updater/MacSparkleUpdater.h" #include "updater/MacSparkleUpdater.h"
@ -659,6 +668,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// The cat // The cat
m_settings->registerSetting("TheCat", false); m_settings->registerSetting("TheCat", false);
m_settings->registerSetting("StatusBarVisible", true);
m_settings->registerSetting("ToolbarsLocked", false); m_settings->registerSetting("ToolbarsLocked", false);
m_settings->registerSetting("InstSortMode", "Name"); m_settings->registerSetting("InstSortMode", "Name");
@ -988,6 +999,37 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
} }
} }
// notify user if /tmp is mounted with `noexec` (#1693)
{
bool is_tmp_noexec = false;
#if defined(Q_OS_LINUX)
struct statvfs tmp_stat;
statvfs("/tmp", &tmp_stat);
is_tmp_noexec = tmp_stat.f_flag & ST_NOEXEC;
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
struct statfs tmp_stat;
statfs("/tmp", &tmp_stat);
is_tmp_noexec = tmp_stat.f_flags & MNT_NOEXEC;
#endif
if (is_tmp_noexec) {
auto infoMsg =
tr("Your /tmp directory is currently mounted with the 'noexec' flag enabled.\n"
"Some versions of Minecraft may not launch.\n");
auto msgBox = new QMessageBox(QMessageBox::Information, tr("Incompatible system configuration"), infoMsg, QMessageBox::Ok);
msgBox->setDefaultButton(QMessageBox::Ok);
msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->setMinimumWidth(460);
msgBox->adjustSize();
msgBox->open();
}
}
if (createSetupWizard()) { if (createSetupWizard()) {
return; return;
} }
@ -1471,6 +1513,17 @@ InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString pa
auto& window = extras.window; auto& window = extras.window;
if (window) { if (window) {
// If the window is minimized on macOS or Windows, activate and bring it up
#ifdef Q_OS_MACOS
if (window->isMinimized()) {
window->setWindowState(window->windowState() & ~Qt::WindowMinimized);
}
#elif defined(Q_OS_WIN)
if (window->isMinimized()) {
window->showNormal();
}
#endif
window->raise(); window->raise();
window->activateWindow(); window->activateWindow();
} else { } else {
@ -1478,6 +1531,7 @@ InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString pa
m_openWindows++; m_openWindows++;
connect(window, &InstanceWindow::isClosing, this, &Application::on_windowClose); connect(window, &InstanceWindow::isClosing, this, &Application::on_windowClose);
} }
if (!page.isEmpty()) { if (!page.isEmpty()) {
window->selectPage(page); window->selectPage(page);
} }

View File

@ -64,6 +64,8 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
m_settings->registerSetting("lastLaunchTime", 0); m_settings->registerSetting("lastLaunchTime", 0);
m_settings->registerSetting("totalTimePlayed", 0); m_settings->registerSetting("totalTimePlayed", 0);
if (m_settings->get("totalTimePlayed").toLongLong() < 0)
m_settings->reset("totalTimePlayed");
m_settings->registerSetting("lastTimePlayed", 0); m_settings->registerSetting("lastTimePlayed", 0);
m_settings->registerSetting("linkedInstances", "[]"); m_settings->registerSetting("linkedInstances", "[]");

View File

@ -142,9 +142,8 @@ void InstanceCopyTask::copyFinished()
if (!m_keepPlaytime) { if (!m_keepPlaytime) {
inst->resetTimePlayed(); inst->resetTimePlayed();
} }
if (m_useLinks)
inst->addLinkedInstanceId(m_origInstance->id());
if (m_useLinks) { if (m_useLinks) {
inst->addLinkedInstanceId(m_origInstance->id());
auto allowed_symlinks_file = QFileInfo(FS::PathCombine(inst->gameRoot(), "allowed_symlinks.txt")); auto allowed_symlinks_file = QFileInfo(FS::PathCombine(inst->gameRoot(), "allowed_symlinks.txt"));
QByteArray allowed_symlinks; QByteArray allowed_symlinks;

View File

@ -306,7 +306,6 @@ void ResourceFolderModel::applyUpdates(QSet<QString>& current_set, QSet<QString>
auto removed_it = m_resources.begin() + removed_index; auto removed_it = m_resources.begin() + removed_index;
Q_ASSERT(removed_it != m_resources.end()); Q_ASSERT(removed_it != m_resources.end());
Q_ASSERT(removed_set.contains(removed_it->get()->internal_id()));
if ((*removed_it)->isResolving()) { if ((*removed_it)->isResolving()) {
auto ticket = (*removed_it)->resolutionTicket(); auto ticket = (*removed_it)->resolutionTicket();

View File

@ -182,7 +182,9 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
ResourceAPI::DependencySearchArgs args = { dep, m_version, m_loaderType }; ResourceAPI::DependencySearchArgs args = { dep, m_version, m_loaderType };
ResourceAPI::DependencySearchCallbacks callbacks; ResourceAPI::DependencySearchCallbacks callbacks;
callbacks.on_fail = [](QString reason, int) {
qCritical() << tr("A network error occurred. Could not load project dependencies:%1").arg(reason);
};
callbacks.on_succeed = [dep, provider, pDep, level, this](auto& doc, [[maybe_unused]] auto& pack) { callbacks.on_succeed = [dep, provider, pDep, level, this](auto& doc, [[maybe_unused]] auto& pack) {
try { try {
QJsonArray arr; QJsonArray arr;
@ -283,4 +285,4 @@ QHash<QString, QStringList> GetModDependenciesTask::getRequiredBy()
rby[addonId.toString()] = req; rby[addonId.toString()] = req;
} }
return rby; return rby;
} }

View File

@ -149,6 +149,7 @@ void EnsureMetadataTask::executeTask()
if (m_current_task) if (m_current_task)
m_current_task.reset(); m_current_task.reset();
}); });
connect(project_task.get(), &Task::failed, this, &EnsureMetadataTask::emitFailed);
m_current_task = project_task; m_current_task = project_task;
project_task->start(); project_task->start();

View File

@ -96,6 +96,7 @@ class ResourceAPI {
}; };
struct VersionSearchCallbacks { struct VersionSearchCallbacks {
std::function<void(QJsonDocument&, ModPlatform::IndexedPack)> on_succeed; std::function<void(QJsonDocument&, ModPlatform::IndexedPack)> on_succeed;
std::function<void(QString const& reason, int network_error_code)> on_fail;
}; };
struct ProjectInfoArgs { struct ProjectInfoArgs {
@ -118,6 +119,7 @@ class ResourceAPI {
struct DependencySearchCallbacks { struct DependencySearchCallbacks {
std::function<void(QJsonDocument&, const ModPlatform::Dependency&)> on_succeed; std::function<void(QJsonDocument&, const ModPlatform::Dependency&)> on_succeed;
std::function<void(QString const& reason, int network_error_code)> on_fail;
}; };
public: public:

View File

@ -119,7 +119,6 @@ void Flame::FileResolvingTask::netJobFinished()
connect(m_checkJob.get(), &NetJob::failed, this, [this, step_progress](QString reason) { connect(m_checkJob.get(), &NetJob::failed, this, [this, step_progress](QString reason) {
step_progress->state = TaskStepState::Failed; step_progress->state = TaskStepState::Failed;
stepProgress(*step_progress); stepProgress(*step_progress);
emitFailed(reason);
}); });
connect(m_checkJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress); connect(m_checkJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress);
connect(m_checkJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) { connect(m_checkJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) {

View File

@ -24,7 +24,7 @@ bool FlameCheckUpdate::abort()
return true; return true;
} }
ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info) ModPlatform::IndexedPack FlameCheckUpdate::getProjectInfo(ModPlatform::IndexedVersion& ver_info)
{ {
ModPlatform::IndexedPack pack; ModPlatform::IndexedPack pack;
@ -57,6 +57,7 @@ ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info)
} }
}); });
connect(get_project_job, &NetJob::failed, this, &FlameCheckUpdate::emitFailed);
QObject::connect(get_project_job, &NetJob::finished, [&loop, get_project_job] { QObject::connect(get_project_job, &NetJob::finished, [&loop, get_project_job] {
get_project_job->deleteLater(); get_project_job->deleteLater();
loop.quit(); loop.quit();
@ -68,7 +69,7 @@ ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info)
return pack; return pack;
} }
ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId) ModPlatform::IndexedVersion FlameCheckUpdate::getFileInfo(int addonId, int fileId)
{ {
ModPlatform::IndexedVersion ver; ModPlatform::IndexedVersion ver;
@ -100,7 +101,7 @@ ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId)
qDebug() << doc; qDebug() << doc;
} }
}); });
connect(get_file_info_job, &NetJob::failed, this, &FlameCheckUpdate::emitFailed);
QObject::connect(get_file_info_job, &NetJob::finished, [&loop, get_file_info_job] { QObject::connect(get_file_info_job, &NetJob::finished, [&loop, get_file_info_job] {
get_file_info_job->deleteLater(); get_file_info_job->deleteLater();
loop.quit(); loop.quit();

View File

@ -22,6 +22,9 @@ class FlameCheckUpdate : public CheckUpdateTask {
void executeTask() override; void executeTask() override;
private: private:
ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info);
ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId);
NetJob* m_net_job = nullptr; NetJob* m_net_job = nullptr;
bool m_was_aborted = false; bool m_was_aborted = false;

View File

@ -227,6 +227,7 @@ bool FlameCreationTask::updateInstance()
m_files_to_remove.append(old_minecraft_dir.absoluteFilePath(relative_path)); m_files_to_remove.append(old_minecraft_dir.absoluteFilePath(relative_path));
} }
}); });
connect(job.get(), &Task::failed, this, [](QString reason) { qCritical() << "Failed to get files: " << reason; });
connect(job.get(), &Task::finished, &loop, &QEventLoop::quit); connect(job.get(), &Task::finished, &loop, &QEventLoop::quit);
m_process_update_file_info_job = job; m_process_update_file_info_job = job;
@ -427,6 +428,9 @@ bool FlameCreationTask::createInstance()
// Don't add managed info to packs without an ID (most likely imported from ZIP) // Don't add managed info to packs without an ID (most likely imported from ZIP)
if (!m_managed_id.isEmpty()) if (!m_managed_id.isEmpty())
instance.setManagedPack("flame", m_managed_id, m_pack.name, m_managed_version_id, m_pack.version); instance.setManagedPack("flame", m_managed_id, m_pack.name, m_managed_version_id, m_pack.version);
else
instance.setManagedPack("flame", "", name(), "", "");
instance.setName(name()); instance.setName(name());
m_mod_id_resolver.reset(new Flame::FileResolvingTask(APPLICATION->network(), m_pack)); m_mod_id_resolver.reset(new Flame::FileResolvingTask(APPLICATION->network(), m_pack));

View File

@ -323,6 +323,7 @@ void FlamePackExportTask::getProjectsInfo()
} }
buildZip(); buildZip();
}); });
connect(projTask.get(), &Task::failed, this, &FlamePackExportTask::emitFailed);
task.reset(projTask); task.reset(projTask);
task->start(); task->start();
} }

View File

@ -102,6 +102,13 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi
callbacks.on_succeed(doc, args.pack); callbacks.on_succeed(doc, args.pack);
}); });
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);
});
return netJob; return netJob;
} }
@ -146,6 +153,12 @@ Task::Ptr NetworkResourceAPI::getDependencyVersion(DependencySearchArgs&& args,
callbacks.on_succeed(doc, args.dependency); callbacks.on_succeed(doc, args.dependency);
}); });
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);
});
return netJob; return netJob;
} }

View File

@ -72,9 +72,7 @@ void ModrinthCheckUpdate::executeTask()
auto response = std::make_shared<QByteArray>(); auto response = std::make_shared<QByteArray>();
auto job = api.latestVersions(hashes, best_hash_type, m_game_versions, m_loaders, response); auto job = api.latestVersions(hashes, best_hash_type, m_game_versions, m_loaders, response);
QEventLoop lock; connect(job.get(), &Task::succeeded, this, [this, response, mappings, best_hash_type, job] {
connect(job.get(), &Task::succeeded, this, [this, response, &mappings, best_hash_type, job] {
QJsonParseError parse_error{}; QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
if (parse_error.error != QJsonParseError::NoError) { if (parse_error.error != QJsonParseError::NoError) {
@ -82,7 +80,7 @@ void ModrinthCheckUpdate::executeTask()
<< " reason: " << parse_error.errorString(); << " reason: " << parse_error.errorString();
qWarning() << *response; qWarning() << *response;
failed(parse_error.errorString()); emitFailed(parse_error.errorString());
return; return;
} }
@ -167,19 +165,17 @@ void ModrinthCheckUpdate::executeTask()
m_deps.append(std::make_shared<GetModDependenciesTask::PackDependency>(pack, project_ver)); m_deps.append(std::make_shared<GetModDependenciesTask::PackDependency>(pack, project_ver));
} }
} catch (Json::JsonException& e) { } catch (Json::JsonException& e) {
failed(e.cause() + " : " + e.what()); emitFailed(e.cause() + " : " + e.what());
return;
} }
emitSucceeded();
}); });
connect(job.get(), &Task::finished, &lock, &QEventLoop::quit); connect(job.get(), &Task::failed, this, &ModrinthCheckUpdate::emitFailed);
setStatus(tr("Waiting for the API response from Modrinth...")); setStatus(tr("Waiting for the API response from Modrinth..."));
setProgress(1, 3); setProgress(1, 3);
m_net_job = qSharedPointerObjectCast<NetJob, Task>(job); m_net_job = qSharedPointerObjectCast<NetJob, Task>(job);
job->start(); job->start();
lock.exec();
emitSucceeded();
} }

View File

@ -226,6 +226,9 @@ bool ModrinthCreationTask::createInstance()
// Don't add managed info to packs without an ID (most likely imported from ZIP) // Don't add managed info to packs without an ID (most likely imported from ZIP)
if (!m_managed_id.isEmpty()) if (!m_managed_id.isEmpty())
instance.setManagedPack("modrinth", m_managed_id, m_managed_name, m_managed_version_id, version()); instance.setManagedPack("modrinth", m_managed_id, m_managed_name, m_managed_version_id, version());
else
instance.setManagedPack("modrinth", "", name(), "", "");
instance.setName(name()); instance.setName(name());
instance.saveNow(); instance.saveNow();
@ -289,7 +292,7 @@ bool ModrinthCreationTask::createInstance()
// Only change the name if it didn't use a custom name, so that the previous custom name // Only change the name if it didn't use a custom name, so that the previous custom name
// is preserved, but if we're using the original one, we update the version string. // is preserved, but if we're using the original one, we update the version string.
// NOTE: This needs to come before the copyManagedPack call! // NOTE: This needs to come before the copyManagedPack call!
if (inst->name().contains(inst->getManagedPackVersionName())) { if (inst->name().contains(inst->getManagedPackVersionName()) && inst->name() != instance.name()) {
if (askForChangingInstanceName(m_parent, inst->name(), instance.name()) == InstanceNameChange::ShouldChange) if (askForChangingInstanceName(m_parent, inst->name(), instance.name()) == InstanceNameChange::ShouldChange)
inst->setName(instance.name()); inst->setName(instance.name());
} }

View File

@ -36,6 +36,7 @@
*/ */
#include "NetJob.h" #include "NetJob.h"
#include "tasks/ConcurrentTask.h"
#if defined(LAUNCHER_APPLICATION) #if defined(LAUNCHER_APPLICATION)
#include "Application.h" #include "Application.h"
#endif #endif
@ -56,18 +57,15 @@ auto NetJob::addNetAction(NetAction::Ptr action) -> bool
return true; return true;
} }
void NetJob::startNext() void NetJob::executeNextSubTask()
{ {
if (m_queue.isEmpty() && m_doing.isEmpty()) { // We're finished, check for failures and retry if we can (up to 3 times)
// We're finished, check for failures and retry if we can (up to 3 times) if (isRunning() && m_queue.isEmpty() && m_doing.isEmpty() && !m_failed.isEmpty() && m_try < 3) {
if (!m_failed.isEmpty() && m_try < 3) { m_try += 1;
m_try += 1; while (!m_failed.isEmpty())
while (!m_failed.isEmpty()) m_queue.enqueue(m_failed.take(*m_failed.keyBegin()));
m_queue.enqueue(m_failed.take(*m_failed.keyBegin()));
}
} }
ConcurrentTask::executeNextSubTask();
ConcurrentTask::startNext();
} }
auto NetJob::size() const -> int auto NetJob::size() const -> int

View File

@ -55,8 +55,6 @@ class NetJob : public ConcurrentTask {
explicit NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network); explicit NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network);
~NetJob() override = default; ~NetJob() override = default;
void startNext() override;
auto size() const -> int; auto size() const -> int;
auto canAbort() const -> bool override; auto canAbort() const -> bool override;
@ -69,6 +67,9 @@ class NetJob : public ConcurrentTask {
// Qt can't handle auto at the start for some reason? // Qt can't handle auto at the start for some reason?
bool abort() override; bool abort() override;
protected slots:
void executeNextSubTask() override;
protected: protected:
void updateState() override; void updateState() override;

View File

@ -5,6 +5,7 @@
qt.*.debug=false qt.*.debug=false
# don't log credentials by default # don't log credentials by default
launcher.auth.credentials.debug=false launcher.auth.credentials.debug=false
katabasis.*.debug=false
# remove the debug lines, other log levels still get through # remove the debug lines, other log levels still get through
launcher.task.net.download.debug=false launcher.task.net.download.debug=false
# enable or disable whole catageries # enable or disable whole catageries

View File

@ -58,14 +58,14 @@ void ImgurUpload::init()
QNetworkReply* ImgurUpload::getReply(QNetworkRequest& request) QNetworkReply* ImgurUpload::getReply(QNetworkRequest& request)
{ {
auto file = new QFile(m_fileInfo.absoluteFilePath()); auto file = new QFile(m_fileInfo.absoluteFilePath(), this);
if (!file->open(QFile::ReadOnly)) { if (!file->open(QFile::ReadOnly)) {
emitFailed(); emitFailed();
return nullptr; return nullptr;
} }
QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType, this);
file->setParent(multipart); file->setParent(multipart);
QHttpPart filePart; QHttpPart filePart;
filePart.setBodyDevice(file); filePart.setBodyDevice(file);

View File

@ -54,6 +54,7 @@ bool INIFile::saveFile(QString fileName)
insert("ConfigVersion", "1.2"); insert("ConfigVersion", "1.2");
QSettings _settings_obj{ fileName, QSettings::Format::IniFormat }; QSettings _settings_obj{ fileName, QSettings::Format::IniFormat };
_settings_obj.setFallbacksEnabled(false); _settings_obj.setFallbacksEnabled(false);
_settings_obj.clear();
for (Iterator iter = begin(); iter != end(); iter++) for (Iterator iter = begin(); iter != end(); iter++)
_settings_obj.setValue(iter.key(), iter.value()); _settings_obj.setValue(iter.key(), iter.value());

View File

@ -35,7 +35,6 @@
*/ */
#include "ConcurrentTask.h" #include "ConcurrentTask.h"
#include <QCoreApplication>
#include <QDebug> #include <QDebug>
#include "tasks/Task.h" #include "tasks/Task.h"
@ -47,9 +46,9 @@ ConcurrentTask::ConcurrentTask(QObject* parent, QString task_name, int max_concu
ConcurrentTask::~ConcurrentTask() ConcurrentTask::~ConcurrentTask()
{ {
for (auto task : m_queue) { for (auto task : m_doing) {
if (task) if (task)
task->deleteLater(); task->disconnect(this);
} }
} }
@ -65,15 +64,13 @@ void ConcurrentTask::addTask(Task::Ptr task)
void ConcurrentTask::executeTask() void ConcurrentTask::executeTask()
{ {
// Start one task, startNext handles starting the up to the m_total_max_size for (auto i = 0; i < m_total_max_size; i++)
// while tracking the number currently being done QMetaObject::invokeMethod(this, &ConcurrentTask::executeNextSubTask, Qt::QueuedConnection);
QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection);
} }
bool ConcurrentTask::abort() bool ConcurrentTask::abort()
{ {
m_queue.clear(); m_queue.clear();
m_aborted = true;
if (m_doing.isEmpty()) { if (m_doing.isEmpty()) {
// Don't call emitAborted() here, we want to bypass the 'is the task running' check // Don't call emitAborted() here, we want to bypass the 'is the task running' check
@ -108,29 +105,36 @@ void ConcurrentTask::clear()
m_failed.clear(); m_failed.clear();
m_queue.clear(); m_queue.clear();
m_aborted = false;
m_progress = 0; m_progress = 0;
m_stepProgress = 0; m_stepProgress = 0;
} }
void ConcurrentTask::startNext() void ConcurrentTask::executeNextSubTask()
{ {
if (m_aborted || m_doing.count() > m_total_max_size) if (!isRunning()) {
return; return;
}
if (m_queue.isEmpty() && m_doing.isEmpty() && !wasSuccessful()) { if (m_doing.count() >= m_total_max_size) {
emitSucceeded(); return;
}
if (m_queue.isEmpty()) {
if (m_doing.isEmpty()) {
if (m_failed.isEmpty())
emitSucceeded();
else
emitFailed(tr("One or more subtasks failed"));
}
return; return;
} }
if (m_queue.isEmpty()) startSubTask(m_queue.dequeue());
return; }
Task::Ptr next = m_queue.dequeue();
void ConcurrentTask::startSubTask(Task::Ptr next)
{
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::failed, this, [this, next](QString msg) { subTaskFailed(next, msg); });
// this should never happen but if it does, it's better to fail the task than get stuck
connect(next.get(), &Task::aborted, this, [this, next] { subTaskFailed(next, "Aborted"); }); connect(next.get(), &Task::aborted, this, [this, next] { subTaskFailed(next, "Aborted"); });
connect(next.get(), &Task::status, this, [this, next](QString msg) { subTaskStatus(next, msg); }); connect(next.get(), &Task::status, this, [this, next](QString msg) { subTaskStatus(next, msg); });
@ -140,55 +144,42 @@ void ConcurrentTask::startNext()
connect(next.get(), &Task::progress, this, [this, next](qint64 current, qint64 total) { subTaskProgress(next, current, total); }); connect(next.get(), &Task::progress, this, [this, next](qint64 current, qint64 total) { subTaskProgress(next, current, total); });
m_doing.insert(next.get(), next); m_doing.insert(next.get(), next);
qsizetype num_starts = qMin(m_queue.size(), m_total_max_size - m_doing.size());
auto task_progress = std::make_shared<TaskStepProgress>(next->getUid()); auto task_progress = std::make_shared<TaskStepProgress>(next->getUid());
m_task_progress.insert(next->getUid(), task_progress); m_task_progress.insert(next->getUid(), task_progress);
updateState(); updateState();
updateStepProgress(*task_progress.get(), Operation::ADDED); updateStepProgress(*task_progress.get(), Operation::ADDED);
QCoreApplication::processEvents();
QMetaObject::invokeMethod(next.get(), &Task::start, Qt::QueuedConnection); QMetaObject::invokeMethod(next.get(), &Task::start, Qt::QueuedConnection);
}
// Allow going up the number of concurrent tasks in case of tasks being added in the middle of a running task. void ConcurrentTask::subTaskFinished(Task::Ptr task, TaskStepState state)
for (int i = 0; i < num_starts; i++) {
QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection); m_done.insert(task.get(), task);
(state == TaskStepState::Succeeded ? m_succeeded : m_failed).insert(task.get(), task);
m_doing.remove(task.get());
auto task_progress = m_task_progress.value(task->getUid());
task_progress->state = state;
disconnect(task.get(), 0, this, 0);
emit stepProgress(*task_progress);
updateState();
updateStepProgress(*task_progress, Operation::REMOVED);
QMetaObject::invokeMethod(this, &ConcurrentTask::executeNextSubTask, Qt::QueuedConnection);
} }
void ConcurrentTask::subTaskSucceeded(Task::Ptr task) void ConcurrentTask::subTaskSucceeded(Task::Ptr task)
{ {
m_done.insert(task.get(), task); subTaskFinished(task, TaskStepState::Succeeded);
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();
} }
void ConcurrentTask::subTaskFailed(Task::Ptr task, [[maybe_unused]] const QString& msg) void ConcurrentTask::subTaskFailed(Task::Ptr task, [[maybe_unused]] const QString& msg)
{ {
m_done.insert(task.get(), task); subTaskFinished(task, TaskStepState::Failed);
m_failed.insert(task.get(), task);
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(Task::Ptr task, const QString& msg) void ConcurrentTask::subTaskStatus(Task::Ptr task, const QString& msg)

View File

@ -72,10 +72,11 @@ class ConcurrentTask : public Task {
protected slots: protected slots:
void executeTask() override; void executeTask() override;
virtual void startNext(); virtual void executeNextSubTask();
void subTaskSucceeded(Task::Ptr); void subTaskSucceeded(Task::Ptr);
void subTaskFailed(Task::Ptr, const QString& msg); virtual void subTaskFailed(Task::Ptr, const QString& msg);
void subTaskFinished(Task::Ptr, TaskStepState);
void subTaskStatus(Task::Ptr task, const QString& msg); void subTaskStatus(Task::Ptr task, const QString& msg);
void subTaskDetails(Task::Ptr task, const QString& msg); void subTaskDetails(Task::Ptr task, const QString& msg);
void subTaskProgress(Task::Ptr task, qint64 current, qint64 total); void subTaskProgress(Task::Ptr task, qint64 current, qint64 total);
@ -90,6 +91,8 @@ class ConcurrentTask : public Task {
virtual void updateState(); virtual void updateState();
void startSubTask(Task::Ptr task);
protected: protected:
QString m_name; QString m_name;
QString m_step_status; QString m_step_status;
@ -107,6 +110,4 @@ class ConcurrentTask : public Task {
qint64 m_stepProgress = 0; qint64 m_stepProgress = 0;
qint64 m_stepTotalProgress = 100; qint64 m_stepTotalProgress = 100;
bool m_aborted = false;
}; };

View File

@ -36,9 +36,9 @@
#include <QDebug> #include <QDebug>
MultipleOptionsTask::MultipleOptionsTask(QObject* parent, const QString& task_name) : SequentialTask(parent, task_name) {} MultipleOptionsTask::MultipleOptionsTask(QObject* parent, const QString& task_name) : ConcurrentTask(parent, task_name, 1) {}
void MultipleOptionsTask::startNext() void MultipleOptionsTask::executeNextSubTask()
{ {
if (m_done.size() != m_failed.size()) { if (m_done.size() != m_failed.size()) {
emitSucceeded(); emitSucceeded();
@ -51,7 +51,7 @@ void MultipleOptionsTask::startNext()
return; return;
} }
ConcurrentTask::startNext(); ConcurrentTask::executeNextSubTask();
} }
void MultipleOptionsTask::updateState() void MultipleOptionsTask::updateState()

View File

@ -34,18 +34,18 @@
*/ */
#pragma once #pragma once
#include "SequentialTask.h" #include "ConcurrentTask.h"
/* This task type will attempt to do run each of it's subtasks in sequence, /* This task type will attempt to do run each of it's subtasks in sequence,
* until one of them succeeds. When that happens, the remaining tasks will not run. * until one of them succeeds. When that happens, the remaining tasks will not run.
* */ * */
class MultipleOptionsTask : public SequentialTask { class MultipleOptionsTask : public ConcurrentTask {
Q_OBJECT Q_OBJECT
public: public:
explicit MultipleOptionsTask(QObject* parent = nullptr, const QString& task_name = ""); explicit MultipleOptionsTask(QObject* parent = nullptr, const QString& task_name = "");
~MultipleOptionsTask() override = default; ~MultipleOptionsTask() override = default;
private slots: private slots:
void startNext() override; void executeNextSubTask() override;
void updateState() override; void updateState() override;
}; };

View File

@ -36,18 +36,15 @@
#include "SequentialTask.h" #include "SequentialTask.h"
#include <QDebug> #include <QDebug>
#include "tasks/ConcurrentTask.h"
SequentialTask::SequentialTask(QObject* parent, QString task_name) : ConcurrentTask(parent, task_name, 1) {} SequentialTask::SequentialTask(QObject* parent, QString task_name) : ConcurrentTask(parent, task_name, 1) {}
void SequentialTask::startNext() void SequentialTask::subTaskFailed(Task::Ptr task, const QString& msg)
{ {
if (m_failed.size() > 0) { emitFailed(msg);
emitFailed(tr("One of the tasks failed!")); qWarning() << msg;
qWarning() << m_failed.constBegin()->get()->failReason(); ConcurrentTask::subTaskFailed(task, msg);
return;
}
ConcurrentTask::startNext();
} }
void SequentialTask::updateState() void SequentialTask::updateState()

View File

@ -50,7 +50,9 @@ class SequentialTask : public ConcurrentTask {
explicit SequentialTask(QObject* parent = nullptr, QString task_name = ""); explicit SequentialTask(QObject* parent = nullptr, QString task_name = "");
~SequentialTask() override = default; ~SequentialTask() override = default;
protected slots:
virtual void subTaskFailed(Task::Ptr, const QString& msg) override;
protected: protected:
void startNext() override;
void updateState() override; void updateState() override;
}; };

View File

@ -186,6 +186,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
ui->instanceToolBar->addContextMenuAction(ui->newsToolBar->toggleViewAction()); ui->instanceToolBar->addContextMenuAction(ui->newsToolBar->toggleViewAction());
ui->instanceToolBar->addContextMenuAction(ui->instanceToolBar->toggleViewAction()); ui->instanceToolBar->addContextMenuAction(ui->instanceToolBar->toggleViewAction());
ui->instanceToolBar->addContextMenuAction(ui->actionToggleStatusBar);
ui->instanceToolBar->addContextMenuAction(ui->actionLockToolbars); ui->instanceToolBar->addContextMenuAction(ui->actionLockToolbars);
} }
@ -319,6 +320,14 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
setCatBackground(cat_enable); setCatBackground(cat_enable);
} }
// Togglable status bar
{
bool statusBarVisible = APPLICATION->settings()->get("StatusBarVisible").toBool();
ui->actionToggleStatusBar->setChecked(statusBarVisible);
connect(ui->actionToggleStatusBar, &QAction::toggled, this, &MainWindow::setStatusBarVisibility);
setStatusBarVisibility(statusBarVisible);
}
// Lock toolbars // Lock toolbars
{ {
bool toolbarsLocked = APPLICATION->settings()->get("ToolbarsLocked").toBool(); bool toolbarsLocked = APPLICATION->settings()->get("ToolbarsLocked").toBool();
@ -451,10 +460,16 @@ QMenu* MainWindow::createPopupMenu()
QMenu* filteredMenu = QMainWindow::createPopupMenu(); QMenu* filteredMenu = QMainWindow::createPopupMenu();
filteredMenu->removeAction(ui->mainToolBar->toggleViewAction()); filteredMenu->removeAction(ui->mainToolBar->toggleViewAction());
filteredMenu->addAction(ui->actionToggleStatusBar);
filteredMenu->addAction(ui->actionLockToolbars); filteredMenu->addAction(ui->actionLockToolbars);
return filteredMenu; return filteredMenu;
} }
void MainWindow::setStatusBarVisibility(bool state)
{
statusBar()->setVisible(state);
APPLICATION->settings()->set("StatusBarVisible", state);
}
void MainWindow::lockToolbars(bool state) void MainWindow::lockToolbars(bool state)
{ {
ui->mainToolBar->setMovable(!state); ui->mainToolBar->setMovable(!state);

View File

@ -205,6 +205,8 @@ class MainWindow : public QMainWindow {
void globalSettingsClosed(); void globalSettingsClosed();
void setStatusBarVisibility(bool);
void lockToolbars(bool); void lockToolbars(bool);
#ifndef Q_OS_MAC #ifndef Q_OS_MAC

View File

@ -176,6 +176,7 @@
<addaction name="actionChangeTheme"/> <addaction name="actionChangeTheme"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionCAT"/> <addaction name="actionCAT"/>
<addaction name="actionToggleStatusBar"/>
<addaction name="actionLockToolbars"/> <addaction name="actionLockToolbars"/>
<addaction name="separator"/> <addaction name="separator"/>
</widget> </widget>
@ -257,6 +258,14 @@
<string>It's a fluffy kitty :3</string> <string>It's a fluffy kitty :3</string>
</property> </property>
</action> </action>
<action name="actionToggleStatusBar">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Status Bar</string>
</property>
</action>
<action name="actionLockToolbars"> <action name="actionLockToolbars">
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>

View File

@ -15,7 +15,6 @@
#pragma once #pragma once
#include <net/NetJob.h>
#include <QDialog> #include <QDialog>
namespace Ui { namespace Ui {
@ -31,7 +30,4 @@ class AboutDialog : public QDialog {
private: private:
Ui::AboutDialog* ui; Ui::AboutDialog* ui;
NetJob::Ptr netJob;
QByteArray dataSink;
}; };

View File

@ -328,6 +328,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool
connect(modrinth_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) { connect(modrinth_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) {
onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::MODRINTH); onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::MODRINTH);
}); });
connect(modrinth_task.get(), &EnsureMetadataTask::failed,
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
if (modrinth_task->getHashingTask()) if (modrinth_task->getHashingTask())
seq.addTask(modrinth_task->getHashingTask()); seq.addTask(modrinth_task->getHashingTask());
@ -341,6 +343,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool
connect(flame_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) { connect(flame_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) {
onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::FLAME); onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::FLAME);
}); });
connect(flame_task.get(), &EnsureMetadataTask::failed,
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
if (flame_task->getHashingTask()) if (flame_task->getHashingTask())
seq.addTask(flame_task->getHashingTask()); seq.addTask(flame_task->getHashingTask());
@ -394,6 +398,8 @@ void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::R
auto task = makeShared<EnsureMetadataTask>(mod, index_dir, next(first_choice)); auto task = makeShared<EnsureMetadataTask>(mod, index_dir, next(first_choice));
connect(task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); connect(task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); });
connect(task.get(), &EnsureMetadataTask::metadataFailed, [this](Mod* candidate) { onMetadataFailed(candidate, false); }); connect(task.get(), &EnsureMetadataTask::metadataFailed, [this](Mod* candidate) { onMetadataFailed(candidate, false); });
connect(task.get(), &EnsureMetadataTask::failed,
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
m_second_try_metadata->addTask(task); m_second_try_metadata->addTask(task);
} else { } else {

View File

@ -158,13 +158,14 @@ void VisualGroup::drawHeader(QPainter* painter, const QStyleOptionViewItem& opti
painter->setRenderHint(QPainter::Antialiasing); painter->setRenderHint(QPainter::Antialiasing);
// sizes and offsets, to keep things consistent below // sizes and offsets, to keep things consistent below
int arrowOffsetLeft = fontMetrics.height() / 2 + 7; const int arrowOffsetLeft = fontMetrics.height() / 2 + 7;
int textOffsetLeft = arrowOffsetLeft * 2; const int textOffsetLeft = arrowOffsetLeft * 2;
int arrowSize = 6; const int centerHeight = optRect.top() + fontMetrics.height() / 2;
int centerHeight = optRect.top() + fontMetrics.height() / 2; const QString& textToDraw = text.isEmpty() ? QObject::tr("Ungrouped") : text;
// BEGIN: arrow // BEGIN: arrow
{ {
constexpr int arrowSize = 6;
QPolygon arrowPolygon; QPolygon arrowPolygon;
if (collapsed) { if (collapsed) {
arrowPolygon << QPoint(arrowOffsetLeft - arrowSize / 2, centerHeight - arrowSize) arrowPolygon << QPoint(arrowOffsetLeft - arrowSize / 2, centerHeight - arrowSize)
@ -188,9 +189,26 @@ void VisualGroup::drawHeader(QPainter* painter, const QStyleOptionViewItem& opti
textRect.setHeight(fontMetrics.height()); textRect.setHeight(fontMetrics.height());
textRect.setRight(textRect.right() - 7); textRect.setRight(textRect.right() - 7);
painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, !text.isEmpty() ? text : QObject::tr("Ungrouped")); painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, textToDraw);
} }
// END: text // END: text
// BEGIN: horizontal line
{
penColor.setAlphaF(0.05);
pen.setColor(penColor);
painter->setPen(pen);
// startPoint is left + arrow + text + space
const int startPoint =
optRect.left() + fontMetrics.height() + fontMetrics.size(Qt::AlignLeft | Qt::AlignVCenter, textToDraw).width() + 20;
painter->setRenderHint(QPainter::Antialiasing, false);
QPolygon polygon;
// for some reason the height (yPos) doesn't look centered, so we are adding 1 to the center height
const int lineHeight = centerHeight + 1;
polygon << QPoint(startPoint, lineHeight) << QPoint(optRect.right() - 3, lineHeight);
painter->drawPolyline(polygon);
}
// END: horizontal line
} }
int VisualGroup::totalHeight() const int VisualGroup::totalHeight() const

View File

@ -131,6 +131,22 @@ ManagedPackPage::~ManagedPackPage()
void ManagedPackPage::openedImpl() void ManagedPackPage::openedImpl()
{ {
if (m_inst->getManagedPackID().isEmpty()) {
ui->packVersion->hide();
ui->packVersionLabel->hide();
ui->packOrigin->hide();
ui->packOriginLabel->hide();
ui->versionsComboBox->hide();
ui->updateButton->hide();
ui->updateToVersionLabel->hide();
ui->updateFromFileButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
ui->packName->setText(m_inst->name());
ui->changelogTextBrowser->setText(tr("This is a local modpack.\n"
"This can be updated only using a file in %1 format\n")
.arg(displayName()));
return;
}
ui->packName->setText(m_inst->getManagedPackName()); ui->packName->setText(m_inst->getManagedPackName());
ui->packVersion->setText(m_inst->getManagedPackVersionName()); ui->packVersion->setText(m_inst->getManagedPackVersionName());
ui->packOrigin->setText(tr("Website: <a href=%1>%2</a> | Pack ID: %3 | Version ID: %4") ui->packOrigin->setText(tr("Website: <a href=%1>%2</a> | Pack ID: %3 | Version ID: %4")
@ -355,6 +371,8 @@ void ModrinthManagedPackPage::update()
void ModrinthManagedPackPage::updateFromFile() void ModrinthManagedPackPage::updateFromFile()
{ {
auto output = QFileDialog::getOpenFileUrl(this, tr("Choose update file"), QDir::homePath(), "Modrinth pack (*.mrpack *.zip)"); auto output = QFileDialog::getOpenFileUrl(this, tr("Choose update file"), QDir::homePath(), "Modrinth pack (*.mrpack *.zip)");
if (output.isEmpty())
return;
QMap<QString, QString> extra_info; QMap<QString, QString> extra_info;
extra_info.insert("pack_id", m_inst->getManagedPackID()); extra_info.insert("pack_id", m_inst->getManagedPackID());
extra_info.insert("pack_version_id", QString()); extra_info.insert("pack_version_id", QString());
@ -472,7 +490,7 @@ void FlameManagedPackPage::parseManagedPack()
QString FlameManagedPackPage::url() const QString FlameManagedPackPage::url() const
{ {
// FIXME: We should display the websiteUrl field, but this requires doing the API request first :( // FIXME: We should display the websiteUrl field, but this requires doing the API request first :(
return {}; return "https://www.curseforge.com/projects/" + m_inst->getManagedPackID();
} }
void FlameManagedPackPage::suggestVersion() void FlameManagedPackPage::suggestVersion()
@ -519,6 +537,8 @@ void FlameManagedPackPage::update()
void FlameManagedPackPage::updateFromFile() void FlameManagedPackPage::updateFromFile()
{ {
auto output = QFileDialog::getOpenFileUrl(this, tr("Choose update file"), QDir::homePath(), "CurseForge pack (*.zip)"); auto output = QFileDialog::getOpenFileUrl(this, tr("Choose update file"), QDir::homePath(), "CurseForge pack (*.zip)");
if (output.isEmpty())
return;
QMap<QString, QString> extra_info; QMap<QString, QString> extra_info;
extra_info.insert("pack_id", m_inst->getManagedPackID()); extra_info.insert("pack_id", m_inst->getManagedPackID());

View File

@ -242,7 +242,7 @@ void ModFolderPage::updateMods(bool includeDeps)
if (m_instance != nullptr && m_instance->isRunning()) { if (m_instance != nullptr && m_instance->isRunning()) {
auto response = auto response =
CustomMessageBox::selectable(this, tr("Confirm Update"), CustomMessageBox::selectable(this, tr("Confirm Update"),
tr("If you update mods while the game is running may cause mod duplication and game crashes.\n" tr("Updating mods while the game is running may cause mod duplication and game crashes.\n"
"The old files may not be deleted as they are in use.\n" "The old files may not be deleted as they are in use.\n"
"Are you sure you want to do this?"), "Are you sure you want to do this?"),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)

View File

@ -295,13 +295,6 @@ void VersionPage::on_actionRemove_triggered()
m_container->refreshContainer(); m_container->refreshContainer();
} }
void VersionPage::on_actionInstall_mods_triggered()
{
if (m_container) {
m_container->selectPage("mods");
}
}
void VersionPage::on_actionAdd_to_Minecraft_jar_triggered() void VersionPage::on_actionAdd_to_Minecraft_jar_triggered()
{ {
auto list = GuiUtil::BrowseForFiles("jarmod", tr("Select jar mods"), tr("Minecraft.jar mods (*.zip *.jar)"), auto list = GuiUtil::BrowseForFiles("jarmod", tr("Select jar mods"), tr("Minecraft.jar mods (*.zip *.jar)"),

View File

@ -80,7 +80,6 @@ class VersionPage : public QMainWindow, public BasePage {
void on_actionAdd_Agents_triggered(); void on_actionAdd_Agents_triggered();
void on_actionRevert_triggered(); void on_actionRevert_triggered();
void on_actionEdit_triggered(); void on_actionEdit_triggered();
void on_actionInstall_mods_triggered();
void on_actionCustomize_triggered(); void on_actionCustomize_triggered();
void on_actionDownload_All_triggered(); void on_actionDownload_All_triggered();

View File

@ -207,6 +207,10 @@ void ResourceModel::loadEntry(QModelIndex& entry)
return; return;
versionRequestSucceeded(doc, pack, entry); versionRequestSucceeded(doc, pack, entry);
}; };
if (!callbacks.on_fail)
callbacks.on_fail = [](QString reason, int) {
QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load project versions:%1").arg(reason));
};
if (auto job = m_api->getProjectVersions(std::move(args), std::move(callbacks)); job) if (auto job = m_api->getProjectVersions(std::move(args), std::move(callbacks)); job)
runInfoJob(job); runInfoJob(job);
@ -230,6 +234,12 @@ void ResourceModel::loadEntry(QModelIndex& entry)
return; return;
QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load project info:%1").arg(reason)); QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load project info:%1").arg(reason));
}; };
if (!callbacks.on_abort)
callbacks.on_abort = [this] {
if (!s_running_models.constFind(this).value())
return;
qCritical() << tr("The request was aborted for an unknown reason");
};
if (auto job = m_api->getProjectInfo(std::move(args), std::move(callbacks)); job) if (auto job = m_api->getProjectInfo(std::move(args), std::move(callbacks)); job)
runInfoJob(job); runInfoJob(job);

View File

@ -170,6 +170,10 @@ void ListModel::performPaginatedSearch()
callbacks.on_fail = [this](QString reason) { searchRequestFailed(reason); }; callbacks.on_fail = [this](QString reason) { searchRequestFailed(reason); };
callbacks.on_succeed = [this](auto& doc, auto& pack) { searchRequestForOneSucceeded(doc); }; callbacks.on_succeed = [this](auto& doc, auto& pack) { searchRequestForOneSucceeded(doc); };
callbacks.on_abort = [this] {
qCritical() << "Search task aborted by an unknown reason!";
searchRequestFailed("Abborted");
};
static const FlameAPI api; static const FlameAPI api;
if (auto job = api.getProjectInfo({ projectId }, std::move(callbacks)); job) { if (auto job = api.getProjectInfo({ projectId }, std::move(callbacks)); job) {
jobPtr = job; jobPtr = job;

View File

@ -34,6 +34,7 @@
*/ */
#include "FlamePage.h" #include "FlamePage.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui_FlamePage.h" #include "ui_FlamePage.h"
#include <QKeyEvent> #include <QKeyEvent>
@ -193,6 +194,8 @@ void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelInde
suggestCurrent(); suggestCurrent();
}); });
QObject::connect(netJob, &NetJob::finished, this, [response, netJob] { netJob->deleteLater(); }); QObject::connect(netJob, &NetJob::finished, this, [response, netJob] { netJob->deleteLater(); });
connect(netJob, &NetJob::failed,
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
netJob->start(); netJob->start();
} else { } else {
for (auto version : current.versions) { for (auto version : current.versions) {

View File

@ -140,6 +140,10 @@ void ModpackListModel::performPaginatedSearch()
callbacks.on_fail = [this](QString reason) { searchRequestFailed(reason); }; callbacks.on_fail = [this](QString reason) { searchRequestFailed(reason); };
callbacks.on_succeed = [this](auto& doc, auto& pack) { searchRequestForOneSucceeded(doc); }; callbacks.on_succeed = [this](auto& doc, auto& pack) { searchRequestForOneSucceeded(doc); };
callbacks.on_abort = [this] {
qCritical() << "Search task aborted by an unknown reason!";
searchRequestFailed("Abborted");
};
static const ModrinthAPI api; static const ModrinthAPI api;
if (auto job = api.getProjectInfo({ projectId }, std::move(callbacks)); job) { if (auto job = api.getProjectInfo({ projectId }, std::move(callbacks)); job) {
jobPtr = job; jobPtr = job;

View File

@ -35,6 +35,7 @@
*/ */
#include "ModrinthPage.h" #include "ModrinthPage.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui_ModrinthPage.h" #include "ui_ModrinthPage.h"
#include "ModrinthModel.h" #include "ModrinthModel.h"
@ -182,6 +183,8 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
suggestCurrent(); suggestCurrent();
}); });
QObject::connect(netJob, &NetJob::finished, this, [response, netJob] { netJob->deleteLater(); }); QObject::connect(netJob, &NetJob::finished, this, [response, netJob] { netJob->deleteLater(); });
connect(netJob, &NetJob::failed,
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
netJob->start(); netJob->start();
} else } else
updateUI(); updateUI();
@ -235,6 +238,8 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
suggestCurrent(); suggestCurrent();
}); });
QObject::connect(netJob, &NetJob::finished, this, [response, netJob] { netJob->deleteLater(); }); QObject::connect(netJob, &NetJob::finished, this, [response, netJob] { netJob->deleteLater(); });
connect(netJob, &NetJob::failed,
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
netJob->start(); netJob->start();
} else { } else {

View File

@ -34,6 +34,7 @@
*/ */
#include "TechnicPage.h" #include "TechnicPage.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui/widgets/ProjectItem.h" #include "ui/widgets/ProjectItem.h"
#include "ui_TechnicPage.h" #include "ui_TechnicPage.h"
@ -208,6 +209,8 @@ void TechnicPage::suggestCurrent()
metadataLoaded(); metadataLoaded();
}); });
connect(jobPtr.get(), &NetJob::failed,
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
jobPtr = netJob; jobPtr = netJob;
jobPtr->start(); jobPtr->start();
@ -258,6 +261,8 @@ void TechnicPage::metadataLoaded()
netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(url), response)); netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(url), response));
QObject::connect(netJob.get(), &NetJob::succeeded, this, &TechnicPage::onSolderLoaded); QObject::connect(netJob.get(), &NetJob::succeeded, this, &TechnicPage::onSolderLoaded);
connect(jobPtr.get(), &NetJob::failed,
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
jobPtr = netJob; jobPtr = netJob;
jobPtr->start(); jobPtr->start();

View File

@ -102,14 +102,7 @@ void VariableSizedImageObject::loadImage(QTextDocument* doc, const QUrl& source,
auto full_entry_path = entry->getFullPath(); auto full_entry_path = entry->getFullPath();
auto source_url = source; auto source_url = source;
connect(job, &NetJob::succeeded, this, [this, doc, full_entry_path, source_url, posInDocument] { auto loadImage = [this, doc, full_entry_path, source_url, posInDocument](const QImage& image) {
qDebug() << "Loaded resource at" << full_entry_path;
// If we flushed, don't proceed.
if (!m_fetching_images.contains(source_url))
return;
QImage image(full_entry_path);
doc->addResource(QTextDocument::ImageResource, source_url, image); doc->addResource(QTextDocument::ImageResource, source_url, image);
parseImage(doc, image, posInDocument); parseImage(doc, image, posInDocument);
@ -121,6 +114,23 @@ void VariableSizedImageObject::loadImage(QTextDocument* doc, const QUrl& source,
doc->setPageSize(size); doc->setPageSize(size);
m_fetching_images.remove(source_url); m_fetching_images.remove(source_url);
};
connect(job, &NetJob::succeeded, this, [this, full_entry_path, source_url, loadImage] {
qDebug() << "Loaded resource at:" << full_entry_path;
// If we flushed, don't proceed.
if (!m_fetching_images.contains(source_url))
return;
QImage image(full_entry_path);
loadImage(image);
});
connect(job, &NetJob::failed, this, [this, full_entry_path, source_url, loadImage](QString reason) {
qWarning() << "Failed resource at:" << full_entry_path << " because:" << reason;
// If we flushed, don't proceed.
if (!m_fetching_images.contains(source_url))
return;
loadImage(QImage());
}); });
connect(job, &NetJob::finished, job, &NetJob::deleteLater); connect(job, &NetJob::finished, job, &NetJob::deleteLater);

View File

@ -37,13 +37,13 @@ class BasicTask_MultiStep : public Task {
class BigConcurrentTask : public ConcurrentTask { class BigConcurrentTask : public ConcurrentTask {
Q_OBJECT Q_OBJECT
void startNext() override void executeNextSubTask() override
{ {
// This is here only to help fill the stack a bit more quickly (if there's an issue, of course :^)) // This is here only to help fill the stack a bit more quickly (if there's an issue, of course :^))
// Each tasks thus adds 1024 * 4 bytes to the stack, at the very least. // Each tasks thus adds 1024 * 4 bytes to the stack, at the very least.
[[maybe_unused]] volatile std::array<uint32_t, 1024> some_data_on_the_stack{}; [[maybe_unused]] volatile std::array<uint32_t, 1024> some_data_on_the_stack{};
ConcurrentTask::startNext(); ConcurrentTask::executeNextSubTask();
} }
}; };
@ -71,11 +71,14 @@ class BigConcurrentTaskThread : public QThread {
quit(); quit();
}); });
m_deadline.start(); if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, &BigConcurrentTaskThread::start_timer, Qt::QueuedConnection);
}
big_task.run(); big_task.run();
exec(); exec();
} }
void start_timer() { m_deadline.start(); }
public: public:
bool passed_the_deadline = false; bool passed_the_deadline = false;