Merge branch 'develop' into feature/images-for-resource-page
This commit is contained in:
commit
b28f682ad9
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@ -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: |
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.";
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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())
|
||||
{
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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};
|
||||
|
@ -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"; }
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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};
|
||||
|
@ -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"; }
|
||||
|
||||
|
@ -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)
|
||||
{}
|
||||
|
||||
|
@ -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 };
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -27,6 +27,7 @@ protected:
|
||||
|
||||
public slots:
|
||||
void downloadError(QNetworkReply::NetworkError);
|
||||
void sslErrors(const QList<QSslError>& errors);
|
||||
void downloadFinished();
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -22,5 +22,6 @@ protected:
|
||||
|
||||
public slots:
|
||||
void downloadError(QNetworkReply::NetworkError);
|
||||
void sslErrors(const QList<QSslError>& errors);
|
||||
void downloadFinished();
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -32,6 +32,7 @@ protected:
|
||||
public slots:
|
||||
|
||||
void downloadError(QNetworkReply::NetworkError);
|
||||
void sslErrors(const QList<QSslError>& errors);
|
||||
|
||||
void downloadFinished();
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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..."));
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
26
launcher/net/Logging.cpp
Normal file
26
launcher/net/Logging.cpp
Normal file
@ -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")
|
28
launcher/net/Logging.h
Normal file
28
launcher/net/Logging.h
Normal file
@ -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)
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
16
launcher/qtlogging.ini
Normal file
16
launcher/qtlogging.ini
Normal file
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 &)));
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
setStepStatus(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::subTaskProgress(qint64 current, qint64 total)
|
||||
void ConcurrentTask::subTaskDetails(Task::Ptr task, const QString& msg)
|
||||
{
|
||||
m_stepProgress = current;
|
||||
m_stepTotalProgress = total;
|
||||
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(Task::Ptr task, qint64 current, qint64 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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
@ -2,26 +2,129 @@
|
||||
<ui version="4.0">
|
||||
<class>ProgressDialog</class>
|
||||
<widget class="QDialog" name="ProgressDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>480</width>
|
||||
<height>210</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>400</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<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">
|
||||
<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="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>24</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="taskProgressScrollArea">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAsNeeded</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
</property>
|
||||
<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>
|
||||
<widget class="QPushButton" name="skipButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@ -31,49 +134,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="globalStatusLabel">
|
||||
<property name="text">
|
||||
<string>Global Task Status...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="statusLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Task Status...</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QProgressBar" name="taskProgressBar">
|
||||
<property name="value">
|
||||
<number>24</number>
|
||||
</property>
|
||||
<property name="textVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QProgressBar" name="globalProgressBar">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>24</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
@ -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);
|
||||
|
@ -46,7 +46,6 @@
|
||||
MinecraftPage::MinecraftPage(QWidget *parent) : QWidget(parent), ui(new Ui::MinecraftPage)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->tabWidget->tabBar()->hide();
|
||||
loadSettings();
|
||||
updateCheckboxStuff();
|
||||
}
|
||||
|
@ -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>
|
||||
@ -111,68 +111,6 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="nativeLibWorkaroundGroupBox">
|
||||
<property name="title">
|
||||
<string>Native library workarounds</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="useNativeGLFWCheck">
|
||||
<property name="text">
|
||||
<string>Use system installation of &GLFW</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="useNativeOpenALCheck">
|
||||
<property name="text">
|
||||
<string>Use system installation of &OpenAL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="perfomanceGroupBox">
|
||||
<property name="title">
|
||||
<string>Performance</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="enableFeralGamemodeCheck">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enable Feral Interactive's GameMode, to potentially improve gaming performance.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Feral GameMode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="enableMangoHud">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enable MangoHud's advanced performance overlay.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable MangoHud</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="useDiscreteGpuCheck">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Use the discrete GPU instead of the primary GPU.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use discrete GPU</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gameTimeGroupBox">
|
||||
<property name="title">
|
||||
@ -247,6 +185,88 @@
|
||||
</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>Native library workarounds</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_11">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="useNativeGLFWCheck">
|
||||
<property name="text">
|
||||
<string>Use system installation of &GLFW</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="useNativeOpenALCheck">
|
||||
<property name="text">
|
||||
<string>Use system installation of &OpenAL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="perfomanceGroupBox">
|
||||
<property name="title">
|
||||
<string>Performance</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="enableFeralGamemodeCheck">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enable Feral Interactive's GameMode, to potentially improve gaming performance.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Feral GameMode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="enableMangoHud">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enable MangoHud's advanced performance overlay.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable MangoHud</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="useDiscreteGpuCheck">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Use the discrete GPU instead of the primary GPU.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use discrete GPU</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@ -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/>
|
||||
|
@ -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){
|
||||
|
@ -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();
|
||||
|
@ -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 };
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user