Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
54e03d602f | |||
d8a84d2aa3 | |||
a7ff74365d | |||
68c884f20d | |||
b7e96bdf62 | |||
16b48866f4 | |||
4827f7e317 | |||
92f6a34624 | |||
c5ce8bfb3e | |||
6429088472 | |||
bf9885dd7e | |||
d16b6fe634 | |||
4539d58d7d | |||
a8611a56fc | |||
c3d64aa984 | |||
3fc63fa196 | |||
4438684ce6 | |||
699fce4482 | |||
49060beae7 | |||
de561e4fd3 | |||
1de301752f |
5
.github/workflows/build.yml
vendored
5
.github/workflows/build.yml
vendored
@ -61,7 +61,7 @@ jobs:
|
|||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: windows
|
qt_host: windows
|
||||||
qt_arch: ''
|
qt_arch: ''
|
||||||
qt_version: '6.4.0'
|
qt_version: '6.4.2'
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_tools: ''
|
qt_tools: ''
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ jobs:
|
|||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: windows
|
qt_host: windows
|
||||||
qt_arch: 'win64_msvc2019_arm64'
|
qt_arch: 'win64_msvc2019_arm64'
|
||||||
qt_version: '6.4.0'
|
qt_version: '6.4.2'
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_tools: ''
|
qt_tools: ''
|
||||||
|
|
||||||
@ -105,6 +105,7 @@ jobs:
|
|||||||
INSTALL_APPIMAGE_DIR: "install-appdir"
|
INSTALL_APPIMAGE_DIR: "install-appdir"
|
||||||
BUILD_DIR: "build"
|
BUILD_DIR: "build"
|
||||||
CCACHE_VAR: ""
|
CCACHE_VAR: ""
|
||||||
|
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
##
|
##
|
||||||
|
@ -139,7 +139,7 @@ set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRIN
|
|||||||
|
|
||||||
######## Set version numbers ########
|
######## Set version numbers ########
|
||||||
set(Launcher_VERSION_MAJOR 6)
|
set(Launcher_VERSION_MAJOR 6)
|
||||||
set(Launcher_VERSION_MINOR 1)
|
set(Launcher_VERSION_MINOR 2)
|
||||||
|
|
||||||
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
||||||
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
|
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
|
||||||
@ -208,9 +208,15 @@ set(Launcher_BUILD_TIMESTAMP "${TODAY}")
|
|||||||
|
|
||||||
################################ 3rd Party Libs ################################
|
################################ 3rd Party Libs ################################
|
||||||
|
|
||||||
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
# Successive configurations of cmake without cleaning the build dir will cause zlib fallback to fail due to cached values
|
||||||
|
# Record when fallback triggered and skip this find_package
|
||||||
|
if(NOT Launcher_FORCE_BUNDLED_LIBS AND NOT FORCE_BUNDLED_ZLIB)
|
||||||
find_package(ZLIB QUIET)
|
find_package(ZLIB QUIET)
|
||||||
endif()
|
endif()
|
||||||
|
if(NOT ZLIB_FOUND)
|
||||||
|
set(FORCE_BUNDLED_ZLIB TRUE CACHE BOOL "")
|
||||||
|
mark_as_advanced(FORCE_BUNDLED_ZLIB)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Find the required Qt parts
|
# Find the required Qt parts
|
||||||
include(QtVersionlessBackport)
|
include(QtVersionlessBackport)
|
||||||
@ -268,6 +274,8 @@ if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
|||||||
find_package(ghc_filesystem QUIET)
|
find_package(ghc_filesystem QUIET)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
include(ECMQtDeclareLoggingCategory)
|
||||||
|
|
||||||
####################################### Program Info #######################################
|
####################################### Program Info #######################################
|
||||||
|
|
||||||
set(Launcher_APP_BINARY_NAME "prismlauncher" CACHE STRING "Name of the Launcher binary")
|
set(Launcher_APP_BINARY_NAME "prismlauncher" CACHE STRING "Name of the Launcher binary")
|
||||||
@ -375,13 +383,24 @@ add_subdirectory(libraries/systeminfo) # system information library
|
|||||||
add_subdirectory(libraries/hoedown) # markdown parser
|
add_subdirectory(libraries/hoedown) # markdown parser
|
||||||
add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
|
add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
|
||||||
add_subdirectory(libraries/javacheck) # java compatibility checker
|
add_subdirectory(libraries/javacheck) # java compatibility checker
|
||||||
if(NOT ZLIB_FOUND)
|
if(FORCE_BUNDLED_ZLIB)
|
||||||
message(STATUS "Using bundled zlib")
|
message(STATUS "Using bundled zlib")
|
||||||
|
|
||||||
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) # Suppress cmake warnings and allow INTERPROCEDURAL_OPTIMIZATION for zlib
|
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) # Suppress cmake warnings and allow INTERPROCEDURAL_OPTIMIZATION for zlib
|
||||||
set(SKIP_INSTALL_ALL ON)
|
set(SKIP_INSTALL_ALL ON)
|
||||||
add_subdirectory(libraries/zlib EXCLUDE_FROM_ALL)
|
add_subdirectory(libraries/zlib EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib" "${CMAKE_CURRENT_BINARY_DIR}/libraries/zlib" CACHE STRING "")
|
# On OS where unistd.h exists, zlib's generated header defines `Z_HAVE_UNISTD_H`, while the included header does not.
|
||||||
|
# We cannot safely undo the rename on those systems, and they generally have packages for zlib anyway.
|
||||||
|
check_include_file(unistd.h NEED_GENERATED_ZCONF)
|
||||||
|
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h.included" AND NOT NEED_GENERATED_ZCONF)
|
||||||
|
# zlib's cmake script renames a file, dirtying the submodule, see https://github.com/madler/zlib/issues/162
|
||||||
|
message(STATUS "Undoing Rename")
|
||||||
|
message(STATUS " ${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h")
|
||||||
|
file(RENAME "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h.included" "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/libraries/zlib" "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib" CACHE STRING "" FORCE)
|
||||||
set_target_properties(zlibstatic PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INCLUDE_DIR}")
|
set_target_properties(zlibstatic PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INCLUDE_DIR}")
|
||||||
add_library(ZLIB::ZLIB ALIAS zlibstatic)
|
add_library(ZLIB::ZLIB ALIAS zlibstatic)
|
||||||
set(ZLIB_LIBRARY ZLIB::ZLIB CACHE STRING "zlib library name")
|
set(ZLIB_LIBRARY ZLIB::ZLIB CACHE STRING "zlib library name")
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
## Prism Launcher
|
## Prism Launcher
|
||||||
|
|
||||||
Prism Launcher - Minecraft Launcher
|
Prism Launcher - Minecraft Launcher
|
||||||
Copyright (C) 2022 Prism Launcher Contributors
|
Copyright (C) 2022-2023 Prism Launcher Contributors
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -76,7 +76,9 @@ Config::Config()
|
|||||||
|
|
||||||
// Assume that builds outside of Git repos are "stable"
|
// Assume that builds outside of Git repos are "stable"
|
||||||
if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND")
|
if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND")
|
||||||
|| GIT_TAG == QStringLiteral("GITDIR-NOTFOUND"))
|
|| GIT_TAG == QStringLiteral("GITDIR-NOTFOUND")
|
||||||
|
|| GIT_REFSPEC == QStringLiteral("")
|
||||||
|
|| GIT_TAG == QStringLiteral("GIT-NOTFOUND"))
|
||||||
{
|
{
|
||||||
GIT_REFSPEC = "refs/heads/stable";
|
GIT_REFSPEC = "refs/heads/stable";
|
||||||
GIT_TAG = versionString();
|
GIT_TAG = versionString();
|
||||||
|
@ -62,11 +62,6 @@
|
|||||||
#include "ui/pages/global/APIPage.h"
|
#include "ui/pages/global/APIPage.h"
|
||||||
#include "ui/pages/global/CustomCommandsPage.h"
|
#include "ui/pages/global/CustomCommandsPage.h"
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
#include "ui/WinDarkmode.h"
|
|
||||||
#include <versionhelpers.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "ui/setupwizard/SetupWizard.h"
|
#include "ui/setupwizard/SetupWizard.h"
|
||||||
#include "ui/setupwizard/LanguageWizardPage.h"
|
#include "ui/setupwizard/LanguageWizardPage.h"
|
||||||
#include "ui/setupwizard/JavaWizardPage.h"
|
#include "ui/setupwizard/JavaWizardPage.h"
|
||||||
@ -146,19 +141,12 @@ static const QLatin1String liveCheckFile("live.check");
|
|||||||
PixmapCache* PixmapCache::s_instance = nullptr;
|
PixmapCache* PixmapCache::s_instance = nullptr;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
/** This is used so that we can output to the log file in addition to the CLI. */
|
||||||
void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||||
{
|
{
|
||||||
const char *levels = "DWCFIS";
|
QString out = qFormatLogMessage(type, context, msg);
|
||||||
const QString format("%1 %2 %3\n");
|
out += QChar::LineFeed;
|
||||||
|
|
||||||
qint64 msecstotal = APPLICATION->timeSinceStart();
|
|
||||||
qint64 seconds = msecstotal / 1000;
|
|
||||||
qint64 msecs = msecstotal % 1000;
|
|
||||||
QString foo;
|
|
||||||
char buf[1025] = {0};
|
|
||||||
::snprintf(buf, 1024, "%5lld.%03lld", seconds, msecs);
|
|
||||||
|
|
||||||
QString out = format.arg(buf).arg(levels[type]).arg(msg);
|
|
||||||
|
|
||||||
APPLICATION->logFile->write(out.toUtf8());
|
APPLICATION->logFile->write(out.toUtf8());
|
||||||
APPLICATION->logFile->flush();
|
APPLICATION->logFile->flush();
|
||||||
@ -431,6 +419,14 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
qInstallMessageHandler(appDebugOutput);
|
qInstallMessageHandler(appDebugOutput);
|
||||||
|
|
||||||
|
qSetMessagePattern(
|
||||||
|
"%{time process}" " "
|
||||||
|
"%{if-debug}D%{endif}" "%{if-info}I%{endif}" "%{if-warning}W%{endif}" "%{if-critical}C%{endif}" "%{if-fatal}F%{endif}"
|
||||||
|
" " "|" " "
|
||||||
|
"%{if-category}[%{category}]: %{endif}"
|
||||||
|
"%{message}");
|
||||||
|
|
||||||
qDebug() << "<> Log initialized.";
|
qDebug() << "<> Log initialized.";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1352,16 +1348,7 @@ MainWindow* Application::showMainWindow(bool minimized)
|
|||||||
m_mainWindow = new MainWindow();
|
m_mainWindow = new MainWindow();
|
||||||
m_mainWindow->restoreState(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowState").toByteArray()));
|
m_mainWindow->restoreState(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowState").toByteArray()));
|
||||||
m_mainWindow->restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowGeometry").toByteArray()));
|
m_mainWindow->restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowGeometry").toByteArray()));
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
if (IsWindows10OrGreater())
|
|
||||||
{
|
|
||||||
if (QString::compare(settings()->get("ApplicationTheme").toString(), "dark") == 0) {
|
|
||||||
WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), true);
|
|
||||||
} else {
|
|
||||||
WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if(minimized)
|
if(minimized)
|
||||||
{
|
{
|
||||||
m_mainWindow->showMinimized();
|
m_mainWindow->showMinimized();
|
||||||
@ -1538,7 +1525,8 @@ QString Application::getJarPath(QString jarFile)
|
|||||||
FS::PathCombine(m_rootPath, "share/" + BuildConfig.LAUNCHER_APP_BINARY_NAME),
|
FS::PathCombine(m_rootPath, "share/" + BuildConfig.LAUNCHER_APP_BINARY_NAME),
|
||||||
#endif
|
#endif
|
||||||
FS::PathCombine(m_rootPath, "jars"),
|
FS::PathCombine(m_rootPath, "jars"),
|
||||||
FS::PathCombine(applicationDirPath(), "jars")
|
FS::PathCombine(applicationDirPath(), "jars"),
|
||||||
|
FS::PathCombine(applicationDirPath(), "..", "jars") // from inside build dir, for debuging
|
||||||
};
|
};
|
||||||
for(QString p : potentialPaths)
|
for(QString p : potentialPaths)
|
||||||
{
|
{
|
||||||
|
@ -551,6 +551,24 @@ set(ATLAUNCHER_SOURCES
|
|||||||
modplatform/atlauncher/ATLShareCode.h
|
modplatform/atlauncher/ATLShareCode.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
######## Logging categories ########
|
||||||
|
|
||||||
|
ecm_qt_declare_logging_category(CORE_SOURCES
|
||||||
|
HEADER Logging.h
|
||||||
|
IDENTIFIER authCredentials
|
||||||
|
CATEGORY_NAME "launcher.auth.credentials"
|
||||||
|
DEFAULT_SEVERITY Warning
|
||||||
|
DESCRIPTION "Secrets and credentials for debugging purposes"
|
||||||
|
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}"
|
||||||
|
DESTINATION "${KDE_INSTALL_LOGGINGCATEGORIESDIR}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
################################ COMPILE ################################
|
################################ COMPILE ################################
|
||||||
|
|
||||||
set(LOGIC_SOURCES
|
set(LOGIC_SOURCES
|
||||||
@ -905,16 +923,6 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/instanceview/VisualGroup.h
|
ui/instanceview/VisualGroup.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
set(LAUNCHER_SOURCES
|
|
||||||
${LAUNCHER_SOURCES}
|
|
||||||
|
|
||||||
# GUI - dark titlebar for Windows 10/11
|
|
||||||
ui/WinDarkmode.h
|
|
||||||
ui/WinDarkmode.cpp
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
qt_wrap_ui(LAUNCHER_UI
|
qt_wrap_ui(LAUNCHER_UI
|
||||||
ui/setupwizard/PasteWizardPage.ui
|
ui/setupwizard/PasteWizardPage.ui
|
||||||
ui/pages/global/AccountListPage.ui
|
ui/pages/global/AccountListPage.ui
|
||||||
|
@ -56,6 +56,7 @@
|
|||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
#include <shobjidl.h>
|
#include <shobjidl.h>
|
||||||
#include <sys/utime.h>
|
#include <sys/utime.h>
|
||||||
|
#include <versionhelpers.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <winnls.h>
|
#include <winnls.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -234,6 +235,10 @@ bool trash(QString path, QString *pathInTrash = nullptr)
|
|||||||
// FIXME: Figure out trash in Flatpak. Qt seemingly doesn't use the Trash portal
|
// FIXME: Figure out trash in Flatpak. Qt seemingly doesn't use the Trash portal
|
||||||
if (DesktopServices::isFlatpak())
|
if (DesktopServices::isFlatpak())
|
||||||
return false;
|
return false;
|
||||||
|
#if defined Q_OS_WIN32
|
||||||
|
if (IsWindowsServer())
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
return QFile::moveToTrash(path, pathInTrash);
|
return QFile::moveToTrash(path, pathInTrash);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,8 @@
|
|||||||
#include "minecraft/ParseUtils.h"
|
#include "minecraft/ParseUtils.h"
|
||||||
#include <minecraft/MojangVersionFormat.h>
|
#include <minecraft/MojangVersionFormat.h>
|
||||||
|
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
using namespace Json;
|
using namespace Json;
|
||||||
|
|
||||||
static void readString(const QJsonObject &root, const QString &key, QString &variable)
|
static void readString(const QJsonObject &root, const QString &key, QString &variable)
|
||||||
@ -121,6 +123,15 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc
|
|||||||
out->uid = root.value("fileId").toString();
|
out->uid = root.value("fileId").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QRegularExpression valid_uid_regex{ QRegularExpression::anchoredPattern(QStringLiteral(R"(\w+(?:\.\w+)*)")) };
|
||||||
|
if (!valid_uid_regex.match(out->uid).hasMatch()) {
|
||||||
|
qCritical() << "The component's 'uid' contains illegal characters! UID:" << out->uid;
|
||||||
|
out->addProblem(
|
||||||
|
ProblemSeverity::Error,
|
||||||
|
QObject::tr("The component's 'uid' contains illegal characters! This can cause security issues.")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
out->version = root.value("version").toString();
|
out->version = root.value("version").toString();
|
||||||
|
|
||||||
MojangVersionFormat::readVersionProperties(root, out.get());
|
MojangVersionFormat::readVersionProperties(root, out.get());
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "Parsers.h"
|
#include "Parsers.h"
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
#include "Logging.h"
|
||||||
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
@ -75,9 +76,7 @@ bool getBool(QJsonValue value, bool & out) {
|
|||||||
|
|
||||||
bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output, QString name) {
|
bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output, QString name) {
|
||||||
qDebug() << "Parsing" << name <<":";
|
qDebug() << "Parsing" << name <<":";
|
||||||
#ifndef NDEBUG
|
qCDebug(authCredentials()) << data;
|
||||||
qDebug() << data;
|
|
||||||
#endif
|
|
||||||
QJsonParseError jsonError;
|
QJsonParseError jsonError;
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
|
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
|
||||||
if(jsonError.error) {
|
if(jsonError.error) {
|
||||||
@ -137,9 +136,7 @@ bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output, QString na
|
|||||||
|
|
||||||
bool parseMinecraftProfile(QByteArray & data, MinecraftProfile &output) {
|
bool parseMinecraftProfile(QByteArray & data, MinecraftProfile &output) {
|
||||||
qDebug() << "Parsing Minecraft profile...";
|
qDebug() << "Parsing Minecraft profile...";
|
||||||
#ifndef NDEBUG
|
qCDebug(authCredentials()) << data;
|
||||||
qDebug() << data;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QJsonParseError jsonError;
|
QJsonParseError jsonError;
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
|
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
|
||||||
@ -275,9 +272,7 @@ decoded base64 "value":
|
|||||||
|
|
||||||
bool parseMinecraftProfileMojang(QByteArray & data, MinecraftProfile &output) {
|
bool parseMinecraftProfileMojang(QByteArray & data, MinecraftProfile &output) {
|
||||||
qDebug() << "Parsing Minecraft profile...";
|
qDebug() << "Parsing Minecraft profile...";
|
||||||
#ifndef NDEBUG
|
qCDebug(authCredentials()) << data;
|
||||||
qDebug() << data;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QJsonParseError jsonError;
|
QJsonParseError jsonError;
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
|
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
|
||||||
@ -389,9 +384,7 @@ bool parseMinecraftProfileMojang(QByteArray & data, MinecraftProfile &output) {
|
|||||||
|
|
||||||
bool parseMinecraftEntitlements(QByteArray & data, MinecraftEntitlement &output) {
|
bool parseMinecraftEntitlements(QByteArray & data, MinecraftEntitlement &output) {
|
||||||
qDebug() << "Parsing Minecraft entitlements...";
|
qDebug() << "Parsing Minecraft entitlements...";
|
||||||
#ifndef NDEBUG
|
qCDebug(authCredentials()) << data;
|
||||||
qDebug() << data;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QJsonParseError jsonError;
|
QJsonParseError jsonError;
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
|
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
|
||||||
@ -424,9 +417,7 @@ bool parseMinecraftEntitlements(QByteArray & data, MinecraftEntitlement &output)
|
|||||||
|
|
||||||
bool parseRolloutResponse(QByteArray & data, bool& result) {
|
bool parseRolloutResponse(QByteArray & data, bool& result) {
|
||||||
qDebug() << "Parsing Rollout response...";
|
qDebug() << "Parsing Rollout response...";
|
||||||
#ifndef NDEBUG
|
qCDebug(authCredentials()) << data;
|
||||||
qDebug() << data;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QJsonParseError jsonError;
|
QJsonParseError jsonError;
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
|
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
|
||||||
@ -455,9 +446,7 @@ bool parseRolloutResponse(QByteArray & data, bool& result) {
|
|||||||
bool parseMojangResponse(QByteArray & data, Katabasis::Token &output) {
|
bool parseMojangResponse(QByteArray & data, Katabasis::Token &output) {
|
||||||
QJsonParseError jsonError;
|
QJsonParseError jsonError;
|
||||||
qDebug() << "Parsing Mojang response...";
|
qDebug() << "Parsing Mojang response...";
|
||||||
#ifndef NDEBUG
|
qCDebug(authCredentials()) << data;
|
||||||
qDebug() << data;
|
|
||||||
#endif
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
|
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
|
||||||
if(jsonError.error) {
|
if(jsonError.error) {
|
||||||
qWarning() << "Failed to parse response from api.minecraftservices.com/launcher/login as JSON: " << jsonError.errorString();
|
qWarning() << "Failed to parse response from api.minecraftservices.com/launcher/login as JSON: " << jsonError.errorString();
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
|
||||||
|
#include "Logging.h"
|
||||||
#include "minecraft/auth/AuthRequest.h"
|
#include "minecraft/auth/AuthRequest.h"
|
||||||
#include "minecraft/auth/Parsers.h"
|
#include "minecraft/auth/Parsers.h"
|
||||||
|
|
||||||
@ -41,9 +42,7 @@ void EntitlementsStep::onRequestDone(
|
|||||||
auto requestor = qobject_cast<AuthRequest *>(QObject::sender());
|
auto requestor = qobject_cast<AuthRequest *>(QObject::sender());
|
||||||
requestor->deleteLater();
|
requestor->deleteLater();
|
||||||
|
|
||||||
#ifndef NDEBUG
|
qCDebug(authCredentials()) << data;
|
||||||
qDebug() << data;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// TODO: check presence of same entitlementsRequestId?
|
// TODO: check presence of same entitlementsRequestId?
|
||||||
// TODO: validate JWTs?
|
// TODO: validate JWTs?
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
|
|
||||||
|
#include "Logging.h"
|
||||||
|
#include "minecraft/auth/AccountTask.h"
|
||||||
#include "minecraft/auth/AuthRequest.h"
|
#include "minecraft/auth/AuthRequest.h"
|
||||||
#include "minecraft/auth/Parsers.h"
|
#include "minecraft/auth/Parsers.h"
|
||||||
#include "minecraft/auth/AccountTask.h"
|
|
||||||
#include "net/NetUtils.h"
|
#include "net/NetUtils.h"
|
||||||
|
|
||||||
LauncherLoginStep::LauncherLoginStep(AccountData* data) : AuthStep(data) {
|
LauncherLoginStep::LauncherLoginStep(AccountData* data) : AuthStep(data) {
|
||||||
@ -51,14 +52,10 @@ void LauncherLoginStep::onRequestDone(
|
|||||||
auto requestor = qobject_cast<AuthRequest *>(QObject::sender());
|
auto requestor = qobject_cast<AuthRequest *>(QObject::sender());
|
||||||
requestor->deleteLater();
|
requestor->deleteLater();
|
||||||
|
|
||||||
#ifndef NDEBUG
|
qCDebug(authCredentials()) << data;
|
||||||
qDebug() << data;
|
|
||||||
#endif
|
|
||||||
if (error != QNetworkReply::NoError) {
|
if (error != QNetworkReply::NoError) {
|
||||||
qWarning() << "Reply error:" << error;
|
qWarning() << "Reply error:" << error;
|
||||||
#ifndef NDEBUG
|
qCDebug(authCredentials()) << data;
|
||||||
qDebug() << data;
|
|
||||||
#endif
|
|
||||||
if (Net::isApplicationError(error)) {
|
if (Net::isApplicationError(error)) {
|
||||||
emit finished(
|
emit finished(
|
||||||
AccountTaskState::STATE_FAILED_SOFT,
|
AccountTaskState::STATE_FAILED_SOFT,
|
||||||
@ -76,9 +73,7 @@ void LauncherLoginStep::onRequestDone(
|
|||||||
|
|
||||||
if(!Parsers::parseMojangResponse(data, m_data->yggdrasilToken)) {
|
if(!Parsers::parseMojangResponse(data, m_data->yggdrasilToken)) {
|
||||||
qWarning() << "Could not parse login_with_xbox response...";
|
qWarning() << "Could not parse login_with_xbox response...";
|
||||||
#ifndef NDEBUG
|
qCDebug(authCredentials()) << data;
|
||||||
qDebug() << data;
|
|
||||||
#endif
|
|
||||||
emit finished(
|
emit finished(
|
||||||
AccountTaskState::STATE_FAILED_SOFT,
|
AccountTaskState::STATE_FAILED_SOFT,
|
||||||
tr("Failed to parse the Minecraft access token response.")
|
tr("Failed to parse the Minecraft access token response.")
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
#include "minecraft/auth/Parsers.h"
|
#include "minecraft/auth/Parsers.h"
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "Logging.h"
|
||||||
|
|
||||||
using OAuth2 = Katabasis::DeviceFlow;
|
using OAuth2 = Katabasis::DeviceFlow;
|
||||||
using Activity = Katabasis::Activity;
|
using Activity = Katabasis::Activity;
|
||||||
@ -117,14 +118,12 @@ void MSAStep::onOAuthActivityChanged(Katabasis::Activity activity) {
|
|||||||
// Succeeded or did not invalidate tokens
|
// Succeeded or did not invalidate tokens
|
||||||
emit hideVerificationUriAndCode();
|
emit hideVerificationUriAndCode();
|
||||||
QVariantMap extraTokens = m_oauth2->extraTokens();
|
QVariantMap extraTokens = m_oauth2->extraTokens();
|
||||||
#ifndef NDEBUG
|
|
||||||
if (!extraTokens.isEmpty()) {
|
if (!extraTokens.isEmpty()) {
|
||||||
qDebug() << "Extra tokens in response:";
|
qCDebug(authCredentials()) << "Extra tokens in response:";
|
||||||
foreach (QString key, extraTokens.keys()) {
|
foreach (QString key, extraTokens.keys()) {
|
||||||
qDebug() << "\t" << key << ":" << extraTokens.value(key);
|
qCDebug(authCredentials()) << "\t" << key << ":" << extraTokens.value(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
emit finished(AccountTaskState::STATE_WORKING, tr("Got "));
|
emit finished(AccountTaskState::STATE_WORKING, tr("Got "));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
|
|
||||||
|
#include "Logging.h"
|
||||||
#include "minecraft/auth/AuthRequest.h"
|
#include "minecraft/auth/AuthRequest.h"
|
||||||
#include "minecraft/auth/Parsers.h"
|
#include "minecraft/auth/Parsers.h"
|
||||||
#include "net/NetUtils.h"
|
#include "net/NetUtils.h"
|
||||||
@ -40,9 +41,7 @@ void MinecraftProfileStep::onRequestDone(
|
|||||||
auto requestor = qobject_cast<AuthRequest *>(QObject::sender());
|
auto requestor = qobject_cast<AuthRequest *>(QObject::sender());
|
||||||
requestor->deleteLater();
|
requestor->deleteLater();
|
||||||
|
|
||||||
#ifndef NDEBUG
|
qCDebug(authCredentials()) << data;
|
||||||
qDebug() << data;
|
|
||||||
#endif
|
|
||||||
if (error == QNetworkReply::ContentNotFoundError) {
|
if (error == QNetworkReply::ContentNotFoundError) {
|
||||||
// NOTE: Succeed even if we do not have a profile. This is a valid account state.
|
// NOTE: Succeed even if we do not have a profile. This is a valid account state.
|
||||||
if(m_data->type == AccountType::Mojang) {
|
if(m_data->type == AccountType::Mojang) {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
|
|
||||||
|
#include "Logging.h"
|
||||||
#include "minecraft/auth/AuthRequest.h"
|
#include "minecraft/auth/AuthRequest.h"
|
||||||
#include "minecraft/auth/Parsers.h"
|
#include "minecraft/auth/Parsers.h"
|
||||||
#include "net/NetUtils.h"
|
#include "net/NetUtils.h"
|
||||||
@ -43,9 +44,7 @@ void MinecraftProfileStepMojang::onRequestDone(
|
|||||||
auto requestor = qobject_cast<AuthRequest *>(QObject::sender());
|
auto requestor = qobject_cast<AuthRequest *>(QObject::sender());
|
||||||
requestor->deleteLater();
|
requestor->deleteLater();
|
||||||
|
|
||||||
#ifndef NDEBUG
|
qCDebug(authCredentials()) << data;
|
||||||
qDebug() << data;
|
|
||||||
#endif
|
|
||||||
if (error == QNetworkReply::ContentNotFoundError) {
|
if (error == QNetworkReply::ContentNotFoundError) {
|
||||||
// NOTE: Succeed even if we do not have a profile. This is a valid account state.
|
// NOTE: Succeed even if we do not have a profile. This is a valid account state.
|
||||||
if(m_data->type == AccountType::Mojang) {
|
if(m_data->type == AccountType::Mojang) {
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <QJsonParseError>
|
#include <QJsonParseError>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
#include "Logging.h"
|
||||||
#include "minecraft/auth/AuthRequest.h"
|
#include "minecraft/auth/AuthRequest.h"
|
||||||
#include "minecraft/auth/Parsers.h"
|
#include "minecraft/auth/Parsers.h"
|
||||||
#include "net/NetUtils.h"
|
#include "net/NetUtils.h"
|
||||||
@ -58,9 +59,7 @@ void XboxAuthorizationStep::onRequestDone(
|
|||||||
auto requestor = qobject_cast<AuthRequest *>(QObject::sender());
|
auto requestor = qobject_cast<AuthRequest *>(QObject::sender());
|
||||||
requestor->deleteLater();
|
requestor->deleteLater();
|
||||||
|
|
||||||
#ifndef NDEBUG
|
qCDebug(authCredentials()) << data;
|
||||||
qDebug() << data;
|
|
||||||
#endif
|
|
||||||
if (error != QNetworkReply::NoError) {
|
if (error != QNetworkReply::NoError) {
|
||||||
qWarning() << "Reply error:" << error;
|
qWarning() << "Reply error:" << error;
|
||||||
if (Net::isApplicationError(error)) {
|
if (Net::isApplicationError(error)) {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
#include <QUrlQuery>
|
#include <QUrlQuery>
|
||||||
|
|
||||||
|
#include "Logging.h"
|
||||||
#include "minecraft/auth/AuthRequest.h"
|
#include "minecraft/auth/AuthRequest.h"
|
||||||
#include "minecraft/auth/Parsers.h"
|
#include "minecraft/auth/Parsers.h"
|
||||||
#include "net/NetUtils.h"
|
#include "net/NetUtils.h"
|
||||||
@ -56,9 +56,7 @@ void XboxProfileStep::onRequestDone(
|
|||||||
|
|
||||||
if (error != QNetworkReply::NoError) {
|
if (error != QNetworkReply::NoError) {
|
||||||
qWarning() << "Reply error:" << error;
|
qWarning() << "Reply error:" << error;
|
||||||
#ifndef NDEBUG
|
qCDebug(authCredentials()) << data;
|
||||||
qDebug() << data;
|
|
||||||
#endif
|
|
||||||
if (Net::isApplicationError(error)) {
|
if (Net::isApplicationError(error)) {
|
||||||
emit finished(
|
emit finished(
|
||||||
AccountTaskState::STATE_FAILED_SOFT,
|
AccountTaskState::STATE_FAILED_SOFT,
|
||||||
@ -74,9 +72,7 @@ void XboxProfileStep::onRequestDone(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NDEBUG
|
qCDebug(authCredentials()) << "XBox profile: " << data;
|
||||||
qDebug() << "XBox profile: " << data;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
emit finished(AccountTaskState::STATE_WORKING, tr("Got Xbox profile"));
|
emit finished(AccountTaskState::STATE_WORKING, tr("Got Xbox profile"));
|
||||||
}
|
}
|
||||||
|
@ -260,7 +260,7 @@ void ResourceFolderModel::resolveResource(Resource* res)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto task = createParseTask(*res);
|
Task::Ptr task{ createParseTask(*res) };
|
||||||
if (!task)
|
if (!task)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -270,11 +270,11 @@ void ResourceFolderModel::resolveResource(Resource* res)
|
|||||||
m_active_parse_tasks.insert(ticket, task);
|
m_active_parse_tasks.insert(ticket, task);
|
||||||
|
|
||||||
connect(
|
connect(
|
||||||
task, &Task::succeeded, this, [=] { onParseSucceeded(ticket, res->internal_id()); }, Qt::ConnectionType::QueuedConnection);
|
task.get(), &Task::succeeded, this, [=] { onParseSucceeded(ticket, res->internal_id()); }, Qt::ConnectionType::QueuedConnection);
|
||||||
connect(
|
connect(
|
||||||
task, &Task::failed, this, [=] { onParseFailed(ticket, res->internal_id()); }, Qt::ConnectionType::QueuedConnection);
|
task.get(), &Task::failed, this, [=] { onParseFailed(ticket, res->internal_id()); }, Qt::ConnectionType::QueuedConnection);
|
||||||
connect(
|
connect(
|
||||||
task, &Task::finished, this, [=] { m_active_parse_tasks.remove(ticket); }, Qt::ConnectionType::QueuedConnection);
|
task.get(), &Task::finished, this, [=] { m_active_parse_tasks.remove(ticket); }, Qt::ConnectionType::QueuedConnection);
|
||||||
|
|
||||||
m_helper_thread_task.addTask(task);
|
m_helper_thread_task.addTask(task);
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// NEW format
|
// NEW format
|
||||||
// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/6f62b37cea040daf350dc253eae6326dd9c822c3
|
// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/c8d8f1929aff9979e322af79a59ce81f3e02db6a
|
||||||
|
|
||||||
// OLD format:
|
// OLD format:
|
||||||
// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/5bf6a2d05145ec79387acc0d45c958642fb049fc
|
// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/5bf6a2d05145ec79387acc0d45c958642fb049fc
|
||||||
@ -73,10 +73,11 @@ ModDetails ReadMCModInfo(QByteArray contents)
|
|||||||
version = Json::ensureString(val, "").toInt();
|
version = Json::ensureString(val, "").toInt();
|
||||||
|
|
||||||
if (version != 2) {
|
if (version != 2) {
|
||||||
qCritical() << "BAD stuff happened to mod json:";
|
qWarning() << QString(R"(The value of 'modListVersion' is "%1" (expected "2")! The file may be corrupted.)").arg(version);
|
||||||
qCritical() << contents;
|
qWarning() << "The contents of 'mcmod.info' are as follows:";
|
||||||
return {};
|
qWarning() << contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto arrVal = jsonDoc.object().value("modlist");
|
auto arrVal = jsonDoc.object().value("modlist");
|
||||||
if (arrVal.isUndefined()) {
|
if (arrVal.isUndefined()) {
|
||||||
arrVal = jsonDoc.object().value("modList");
|
arrVal = jsonDoc.object().value("modList");
|
||||||
|
@ -225,10 +225,19 @@ bool ModrinthCreationTask::createInstance()
|
|||||||
|
|
||||||
m_files_job = new NetJob(tr("Mod download"), APPLICATION->network());
|
m_files_job = new NetJob(tr("Mod download"), APPLICATION->network());
|
||||||
|
|
||||||
|
auto root_modpack_path = FS::PathCombine(m_stagingPath, ".minecraft");
|
||||||
|
auto root_modpack_url = QUrl::fromLocalFile(root_modpack_path);
|
||||||
|
|
||||||
for (auto file : m_files) {
|
for (auto file : m_files) {
|
||||||
auto path = FS::PathCombine(m_stagingPath, ".minecraft", file.path);
|
auto file_path = FS::PathCombine(root_modpack_path, file.path);
|
||||||
qDebug() << "Will try to download" << file.downloads.front() << "to" << path;
|
if (!root_modpack_url.isParentOf(QUrl::fromLocalFile(file_path))) {
|
||||||
auto dl = Net::Download::makeFile(file.downloads.dequeue(), path);
|
// This means we somehow got out of the root folder, so abort here to prevent exploits
|
||||||
|
setError(tr("One of the files has a path that leads to an arbitrary location (%1). This is a security risk and isn't allowed.").arg(file.path));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Will try to download" << file.downloads.front() << "to" << file_path;
|
||||||
|
auto dl = Net::Download::makeFile(file.downloads.dequeue(), file_path);
|
||||||
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
||||||
m_files_job->addNetAction(dl);
|
m_files_job->addNetAction(dl);
|
||||||
|
|
||||||
@ -236,8 +245,8 @@ bool ModrinthCreationTask::createInstance()
|
|||||||
// FIXME: This really needs to be put into a ConcurrentTask of
|
// FIXME: This really needs to be put into a ConcurrentTask of
|
||||||
// MultipleOptionsTask's , once those exist :)
|
// MultipleOptionsTask's , once those exist :)
|
||||||
auto param = dl.toWeakRef();
|
auto param = dl.toWeakRef();
|
||||||
connect(dl.get(), &NetAction::failed, [this, &file, path, param] {
|
connect(dl.get(), &NetAction::failed, [this, &file, file_path, param] {
|
||||||
auto ndl = Net::Download::makeFile(file.downloads.dequeue(), path);
|
auto ndl = Net::Download::makeFile(file.downloads.dequeue(), file_path);
|
||||||
ndl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
ndl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
||||||
m_files_job->addNetAction(ndl);
|
m_files_job->addNetAction(ndl);
|
||||||
if (auto shared = param.lock()) shared->succeeded();
|
if (auto shared = param.lock()) shared->succeeded();
|
||||||
|
@ -172,7 +172,7 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const
|
|||||||
auto libraryObject = Json::ensureObject(library, {}, "");
|
auto libraryObject = Json::ensureObject(library, {}, "");
|
||||||
auto libraryName = Json::ensureString(libraryObject, "name", "", "");
|
auto libraryName = Json::ensureString(libraryObject, "name", "", "");
|
||||||
|
|
||||||
if (libraryName.startsWith("net.minecraftforge:forge:") && libraryName.contains('-'))
|
if ((libraryName.startsWith("net.minecraftforge:forge:") || libraryName.startsWith("net.minecraftforge:fmlloader:")) && libraryName.contains('-'))
|
||||||
{
|
{
|
||||||
QString libraryVersion = libraryName.section(':', 2);
|
QString libraryVersion = libraryName.section(':', 2);
|
||||||
if (!libraryVersion.startsWith("1.7.10-"))
|
if (!libraryVersion.startsWith("1.7.10-"))
|
||||||
|
@ -110,14 +110,14 @@ void ConcurrentTask::startNext()
|
|||||||
setStepStatus(next->isMultiStep() ? next->getStepStatus() : next->getStatus());
|
setStepStatus(next->isMultiStep() ? next->getStepStatus() : next->getStatus());
|
||||||
updateState();
|
updateState();
|
||||||
|
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
|
||||||
QMetaObject::invokeMethod(next.get(), &Task::start, Qt::QueuedConnection);
|
QMetaObject::invokeMethod(next.get(), &Task::start, Qt::QueuedConnection);
|
||||||
|
|
||||||
// Allow going up the number of concurrent tasks in case of tasks being added in the middle of a running task.
|
// Allow going up the number of concurrent tasks in case of tasks being added in the middle of a running task.
|
||||||
int num_starts = m_total_max_size - m_doing.size();
|
int num_starts = qMin(m_queue.size(), m_total_max_size - m_doing.size());
|
||||||
for (int i = 0; i < num_starts; i++)
|
for (int i = 0; i < num_starts; i++)
|
||||||
QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection);
|
QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection);
|
||||||
|
|
||||||
QCoreApplication::processEvents();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConcurrentTask::subTaskSucceeded(Task::Ptr task)
|
void ConcurrentTask::subTaskSucceeded(Task::Ptr task)
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
#include <QWidget>
|
|
||||||
|
|
||||||
#include "WinDarkmode.h"
|
|
||||||
|
|
||||||
namespace WinDarkmode {
|
|
||||||
|
|
||||||
/* See https://github.com/statiolake/neovim-qt/commit/da8eaba7f0e38b6b51f3bacd02a8cc2d1f7a34d8 */
|
|
||||||
void setDarkWinTitlebar(WId winid, bool darkmode)
|
|
||||||
{
|
|
||||||
HWND hwnd = reinterpret_cast<HWND>(winid);
|
|
||||||
BOOL dark = (BOOL) darkmode;
|
|
||||||
|
|
||||||
HMODULE hUxtheme = LoadLibraryExW(L"uxtheme.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
|
||||||
HMODULE hUser32 = GetModuleHandleW(L"user32.dll");
|
|
||||||
fnAllowDarkModeForWindow AllowDarkModeForWindow
|
|
||||||
= reinterpret_cast<fnAllowDarkModeForWindow>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(133)));
|
|
||||||
fnSetPreferredAppMode SetPreferredAppMode
|
|
||||||
= reinterpret_cast<fnSetPreferredAppMode>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(135)));
|
|
||||||
fnSetWindowCompositionAttribute SetWindowCompositionAttribute
|
|
||||||
= reinterpret_cast<fnSetWindowCompositionAttribute>(GetProcAddress(hUser32, "SetWindowCompositionAttribute"));
|
|
||||||
|
|
||||||
SetPreferredAppMode(AllowDark);
|
|
||||||
AllowDarkModeForWindow(hwnd, dark);
|
|
||||||
WINDOWCOMPOSITIONATTRIBDATA data = {
|
|
||||||
WCA_USEDARKMODECOLORS,
|
|
||||||
&dark,
|
|
||||||
sizeof(dark)
|
|
||||||
};
|
|
||||||
SetWindowCompositionAttribute(hwnd, &data);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <dwmapi.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace WinDarkmode {
|
|
||||||
|
|
||||||
void setDarkWinTitlebar(WId winid, bool darkmode);
|
|
||||||
|
|
||||||
enum PreferredAppMode {
|
|
||||||
Default,
|
|
||||||
AllowDark,
|
|
||||||
ForceDark,
|
|
||||||
ForceLight,
|
|
||||||
Max
|
|
||||||
};
|
|
||||||
|
|
||||||
enum WINDOWCOMPOSITIONATTRIB {
|
|
||||||
WCA_UNDEFINED = 0,
|
|
||||||
WCA_NCRENDERING_ENABLED = 1,
|
|
||||||
WCA_NCRENDERING_POLICY = 2,
|
|
||||||
WCA_TRANSITIONS_FORCEDISABLED = 3,
|
|
||||||
WCA_ALLOW_NCPAINT = 4,
|
|
||||||
WCA_CAPTION_BUTTON_BOUNDS = 5,
|
|
||||||
WCA_NONCLIENT_RTL_LAYOUT = 6,
|
|
||||||
WCA_FORCE_ICONIC_REPRESENTATION = 7,
|
|
||||||
WCA_EXTENDED_FRAME_BOUNDS = 8,
|
|
||||||
WCA_HAS_ICONIC_BITMAP = 9,
|
|
||||||
WCA_THEME_ATTRIBUTES = 10,
|
|
||||||
WCA_NCRENDERING_EXILED = 11,
|
|
||||||
WCA_NCADORNMENTINFO = 12,
|
|
||||||
WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
|
|
||||||
WCA_VIDEO_OVERLAY_ACTIVE = 14,
|
|
||||||
WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
|
|
||||||
WCA_DISALLOW_PEEK = 16,
|
|
||||||
WCA_CLOAK = 17,
|
|
||||||
WCA_CLOAKED = 18,
|
|
||||||
WCA_ACCENT_POLICY = 19,
|
|
||||||
WCA_FREEZE_REPRESENTATION = 20,
|
|
||||||
WCA_EVER_UNCLOAKED = 21,
|
|
||||||
WCA_VISUAL_OWNER = 22,
|
|
||||||
WCA_HOLOGRAPHIC = 23,
|
|
||||||
WCA_EXCLUDED_FROM_DDA = 24,
|
|
||||||
WCA_PASSIVEUPDATEMODE = 25,
|
|
||||||
WCA_USEDARKMODECOLORS = 26,
|
|
||||||
WCA_LAST = 27
|
|
||||||
};
|
|
||||||
|
|
||||||
struct WINDOWCOMPOSITIONATTRIBDATA {
|
|
||||||
WINDOWCOMPOSITIONATTRIB Attrib;
|
|
||||||
PVOID pvData;
|
|
||||||
SIZE_T cbData;
|
|
||||||
};
|
|
||||||
|
|
||||||
using fnAllowDarkModeForWindow = BOOL (WINAPI *)(HWND hWnd, BOOL allow);
|
|
||||||
using fnSetPreferredAppMode = PreferredAppMode (WINAPI *)(PreferredAppMode appMode);
|
|
||||||
using fnSetWindowCompositionAttribute = BOOL (WINAPI *)(HWND hwnd, WINDOWCOMPOSITIONATTRIBDATA *);
|
|
||||||
|
|
||||||
}
|
|
@ -428,6 +428,10 @@ void ModPage::updateUi()
|
|||||||
text += "<hr>";
|
text += "<hr>";
|
||||||
|
|
||||||
HoeDown h;
|
HoeDown h;
|
||||||
|
|
||||||
|
// hoedown bug: it doesn't handle markdown surrounded by block tags (like center, div) so strip them
|
||||||
|
current.extraData.body.remove(QRegularExpression("<[^>]*(?:center|div)\\W*>"));
|
||||||
|
|
||||||
ui->packDescription->setHtml(text + (current.extraData.body.isEmpty() ? current.description : h.process(current.extraData.body.toUtf8())));
|
ui->packDescription->setHtml(text + (current.extraData.body.isEmpty() ? current.description : h.process(current.extraData.body.toUtf8())));
|
||||||
ui->packDescription->flush();
|
ui->packDescription->flush();
|
||||||
}
|
}
|
||||||
|
@ -28,14 +28,6 @@
|
|||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
#include <windows.h>
|
|
||||||
// this is needed for versionhelpers.h, it is also included in WinDarkmode, but we can't rely on that.
|
|
||||||
// Ultimately this should be included in versionhelpers, but that is outside of the project.
|
|
||||||
#include "ui/WinDarkmode.h"
|
|
||||||
#include <versionhelpers.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ThemeManager::ThemeManager(MainWindow* mainWindow)
|
ThemeManager::ThemeManager(MainWindow* mainWindow)
|
||||||
{
|
{
|
||||||
m_mainWindow = mainWindow;
|
m_mainWindow = mainWindow;
|
||||||
@ -140,15 +132,6 @@ void ThemeManager::setApplicationTheme(const QString& name, bool initial)
|
|||||||
auto& theme = themeIter->second;
|
auto& theme = themeIter->second;
|
||||||
themeDebugLog() << "applying theme" << theme->name();
|
themeDebugLog() << "applying theme" << theme->name();
|
||||||
theme->apply(initial);
|
theme->apply(initial);
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
if (m_mainWindow && IsWindows10OrGreater()) {
|
|
||||||
if (QString::compare(theme->id(), "dark") == 0) {
|
|
||||||
WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), true);
|
|
||||||
} else {
|
|
||||||
WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
themeWarningLog() << "Tried to set invalid theme:" << name;
|
themeWarningLog() << "Tried to set invalid theme:" << name;
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,15 @@ set( katabasis_PUBLIC
|
|||||||
include/katabasis/RequestParameter.h
|
include/katabasis/RequestParameter.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ecm_qt_declare_logging_category(katabasis_PRIVATE
|
||||||
|
HEADER KatabasisLogging.h # NOTE: this won't be in src/, but CMAKE_BINARY_DIR/src isn't included by default so this should be fine
|
||||||
|
IDENTIFIER katabasisCredentials
|
||||||
|
CATEGORY_NAME "katabasis.credentials"
|
||||||
|
DEFAULT_SEVERITY Warning
|
||||||
|
DESCRIPTION "Secrets and credentials from Katabasis"
|
||||||
|
EXPORT "Katabasis"
|
||||||
|
)
|
||||||
|
|
||||||
add_library( Katabasis STATIC ${katabasis_PRIVATE} ${katabasis_PUBLIC} )
|
add_library( Katabasis STATIC ${katabasis_PRIVATE} ${katabasis_PUBLIC} )
|
||||||
target_link_libraries(Katabasis Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Network)
|
target_link_libraries(Katabasis Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Network)
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QLoggingCategory>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
@ -19,9 +19,11 @@
|
|||||||
#include "katabasis/PollServer.h"
|
#include "katabasis/PollServer.h"
|
||||||
#include "katabasis/Globals.h"
|
#include "katabasis/Globals.h"
|
||||||
|
|
||||||
|
#include "KatabasisLogging.h"
|
||||||
#include "JsonResponse.h"
|
#include "JsonResponse.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// ref: https://tools.ietf.org/html/rfc8628#section-3.2
|
// ref: https://tools.ietf.org/html/rfc8628#section-3.2
|
||||||
// Exception: Google sign-in uses "verification_url" instead of "*_uri" - we'll accept both.
|
// Exception: Google sign-in uses "verification_url" instead of "*_uri" - we'll accept both.
|
||||||
bool hasMandatoryDeviceAuthParams(const QVariantMap& params)
|
bool hasMandatoryDeviceAuthParams(const QVariantMap& params)
|
||||||
@ -333,9 +335,7 @@ QString DeviceFlow::refreshToken() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DeviceFlow::setRefreshToken(const QString &v) {
|
void DeviceFlow::setRefreshToken(const QString &v) {
|
||||||
#ifndef NDEBUG
|
qCDebug(katabasisCredentials) << "new refresh token:" << v;
|
||||||
qDebug() << "DeviceFlow::setRefreshToken" << v << "...";
|
|
||||||
#endif
|
|
||||||
token_.refresh_token = v;
|
token_.refresh_token = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,38 +19,49 @@
|
|||||||
<p>Features:</p>
|
<p>Features:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Easily install game modifications, such as Fabric, Forge and Quilt</li>
|
<li>Easily install game modifications, such as Fabric, Forge and Quilt</li>
|
||||||
<li>Control your Java settings</li>
|
<li>Easily install and update modpacks from the Launcher</li>
|
||||||
|
<li>Control your Java settings, and enable Mangohud or Gamemode with a toggle</li>
|
||||||
<li>Manage worlds and resource packs from the launcher</li>
|
<li>Manage worlds and resource packs from the launcher</li>
|
||||||
<li>See logs and other details easily</li>
|
<li>See logs and other details easily through a dashboard</li>
|
||||||
<li>Kill Minecraft in case of a crash/freeze</li>
|
<li>Kill Minecraft in case of a crash/freeze</li>
|
||||||
<li>Isolate Minecraft instances to keep everything clean</li>
|
<li>Isolate Minecraft instances to keep everything clean</li>
|
||||||
<li>Install and update mods directly from the launcher</li>
|
<li>Install and update mods directly from the launcher</li>
|
||||||
|
<li>Customize the launcher with themes, and more</li>
|
||||||
|
<li>And cat :3</li>
|
||||||
</ul>
|
</ul>
|
||||||
</description>
|
</description>
|
||||||
<screenshots>
|
<screenshots>
|
||||||
<screenshot type="default">
|
<screenshot type="default">
|
||||||
<caption>The main Prism Launcher window</caption>
|
<caption>The main Prism Launcher window</caption>
|
||||||
<image type="source" width="976" height="764">https://prismlauncher.org/img/screenshots/LauncherDark.png</image>
|
<image type="source" width="1030" height="764">https://prismlauncher.org/img/screenshots/LauncherDark.png</image>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<caption>Modpack installation</caption>
|
<caption>Modpack installation</caption>
|
||||||
<image type="source" width="1103" height="954">https://prismlauncher.org/img/screenshots/ModpackInstallDark.png</image>
|
<image type="source" width="1126" height="850">https://prismlauncher.org/img/screenshots/ModpackInstallDark.png</image>
|
||||||
|
</screenshot>
|
||||||
|
<screenshot>
|
||||||
|
<caption>Modpack updating</caption>
|
||||||
|
<image type="source" width="930" height="677">https://prismlauncher.org/img/screenshots/ModpackUpdateDark.png</image>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<caption>Mod installation</caption>
|
<caption>Mod installation</caption>
|
||||||
<image type="source" width="1036" height="700">https://prismlauncher.org/img/screenshots/ModInstallDark.png</image>
|
<image type="source" width="848" height="558">https://prismlauncher.org/img/screenshots/ModInstallDark.png</image>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<caption>Mod updating</caption>
|
<caption>Mod updating</caption>
|
||||||
<image type="source" width="930" height="858">https://prismlauncher.org/img/screenshots/ModUpdateDark.png</image>
|
<image type="source" width="860" height="748">https://prismlauncher.org/img/screenshots/ModUpdateDark.png</image>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<caption>Instance management</caption>
|
<caption>Instance management</caption>
|
||||||
<image type="source" width="1083" height="735">https://prismlauncher.org/img/screenshots/PropertiesDark.png</image>
|
<image type="source" width="960" height="659">https://prismlauncher.org/img/screenshots/PropertiesDark.png</image>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<caption>Cat :)</caption>
|
<caption>Cat :3</caption>
|
||||||
<image type="source" width="931" height="759">https://prismlauncher.org/img/screenshots/LauncherCatDark.png</image>
|
<image type="source" width="1042" height="754">https://prismlauncher.org/img/screenshots/LauncherCatDark.png</image>
|
||||||
|
</screenshot>
|
||||||
|
<screenshot>
|
||||||
|
<caption>Customization</caption>
|
||||||
|
<image type="source" width="1040" height="752">https://prismlauncher.org/img/screenshots/CustomizeDark.png</image>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
</screenshots>
|
</screenshots>
|
||||||
<releases>
|
<releases>
|
||||||
|
@ -41,6 +41,24 @@ Here are the current features of Prism Launcher.
|
|||||||
*-a, --profile*=PROFILE
|
*-a, --profile*=PROFILE
|
||||||
Use the account specified by PROFILE (only valid in combination with --launch).
|
Use the account specified by PROFILE (only valid in combination with --launch).
|
||||||
|
|
||||||
|
# ENVIRONMENT
|
||||||
|
|
||||||
|
The behavior of the launcher can be customized by the following environment
|
||||||
|
variables, besides other common Qt variables:
|
||||||
|
|
||||||
|
*QT_LOGGING_RULES*
|
||||||
|
Specifies which logging categories are shown in the logs. One can
|
||||||
|
enable/disable multiple categories by separating them with a semicolon (;).
|
||||||
|
|
||||||
|
The specific syntax, and alternatives to this setting, can be found at
|
||||||
|
https://doc.qt.io/qt-6/qloggingcategory.html#configuring-categories.
|
||||||
|
|
||||||
|
*QT_MESSAGE_PATTERN*
|
||||||
|
Specifies the format in which the console output will be shown.
|
||||||
|
|
||||||
|
Available options, as well as syntax, can be viewed at
|
||||||
|
https://doc.qt.io/qt-6/qtglobal.html#qSetMessagePattern.
|
||||||
|
|
||||||
# EXIT STATUS
|
# EXIT STATUS
|
||||||
|
|
||||||
*0*
|
*0*
|
||||||
|
@ -1,16 +1,23 @@
|
|||||||
#include <QTest>
|
#include <QTest>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
#include <tasks/ConcurrentTask.h>
|
#include <tasks/ConcurrentTask.h>
|
||||||
#include <tasks/MultipleOptionsTask.h>
|
#include <tasks/MultipleOptionsTask.h>
|
||||||
#include <tasks/SequentialTask.h>
|
#include <tasks/SequentialTask.h>
|
||||||
#include <tasks/Task.h>
|
#include <tasks/Task.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
/* Does nothing. Only used for testing. */
|
/* Does nothing. Only used for testing. */
|
||||||
class BasicTask : public Task {
|
class BasicTask : public Task {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
friend class TaskTest;
|
friend class TaskTest;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BasicTask(bool show_debug_log = true) : Task(nullptr, show_debug_log) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void executeTask() override
|
void executeTask() override
|
||||||
{
|
{
|
||||||
@ -30,6 +37,57 @@ class BasicTask_MultiStep : public Task {
|
|||||||
void executeTask() override {};
|
void executeTask() override {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class BigConcurrentTask : public ConcurrentTask {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
void startNext() override
|
||||||
|
{
|
||||||
|
// This is here only to help fill the stack a bit more quickly (if there's an issue, of course :^))
|
||||||
|
// Each tasks thus adds 1024 * 4 bytes to the stack, at the very least.
|
||||||
|
[[maybe_unused]] volatile std::array<uint32_t, 1024> some_data_on_the_stack {};
|
||||||
|
|
||||||
|
ConcurrentTask::startNext();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BigConcurrentTaskThread : public QThread {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
BigConcurrentTask big_task;
|
||||||
|
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
QTimer deadline;
|
||||||
|
deadline.setInterval(10000);
|
||||||
|
connect(&deadline, &QTimer::timeout, this, [this]{ passed_the_deadline = true; });
|
||||||
|
deadline.start();
|
||||||
|
|
||||||
|
// NOTE: Arbitrary value that manages to trigger a problem when there is one.
|
||||||
|
// Considering each tasks, in a problematic state, adds 1024 * 4 bytes to the stack,
|
||||||
|
// this number is enough to fill up 16 MiB of stack, more than enough to cause a problem.
|
||||||
|
static const unsigned s_num_tasks = 1 << 12;
|
||||||
|
auto sub_tasks = new BasicTask::Ptr[s_num_tasks];
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < s_num_tasks; i++) {
|
||||||
|
sub_tasks[i] = new BasicTask(false);
|
||||||
|
big_task.addTask(sub_tasks[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
big_task.run();
|
||||||
|
|
||||||
|
while (!big_task.isFinished() && !passed_the_deadline)
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
|
||||||
|
emit finished();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool passed_the_deadline = false;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void finished();
|
||||||
|
};
|
||||||
|
|
||||||
class TaskTest : public QObject {
|
class TaskTest : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@ -183,6 +241,22 @@ class TaskTest : public QObject {
|
|||||||
return t.isFinished();
|
return t.isFinished();
|
||||||
}, 1000), "Task didn't finish as it should.");
|
}, 1000), "Task didn't finish as it should.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_stackOverflowInConcurrentTask()
|
||||||
|
{
|
||||||
|
QEventLoop loop;
|
||||||
|
|
||||||
|
auto thread = new BigConcurrentTaskThread;
|
||||||
|
|
||||||
|
connect(thread, &BigConcurrentTaskThread::finished, &loop, &QEventLoop::quit);
|
||||||
|
|
||||||
|
thread->start();
|
||||||
|
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
QVERIFY(!thread->passed_the_deadline);
|
||||||
|
thread->deleteLater();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
QTEST_GUILESS_MAIN(TaskTest)
|
QTEST_GUILESS_MAIN(TaskTest)
|
||||||
|
Reference in New Issue
Block a user