Compare commits
41 Commits
develop
...
release-6.
Author | SHA1 | Date | |
---|---|---|---|
|
39bba9cbfa | ||
|
104c231740 | ||
|
bc376b22c7 | ||
|
54e03d602f | ||
|
d8a84d2aa3 | ||
|
a7ff74365d | ||
|
68c884f20d | ||
|
b7e96bdf62 | ||
|
16b48866f4 | ||
|
4827f7e317 | ||
|
92f6a34624 | ||
|
c5ce8bfb3e | ||
|
6429088472 | ||
|
bf9885dd7e | ||
|
d16b6fe634 | ||
|
4539d58d7d | ||
|
a8611a56fc | ||
|
c3d64aa984 | ||
|
3fc63fa196 | ||
|
4438684ce6 | ||
|
699fce4482 | ||
|
49060beae7 | ||
|
de561e4fd3 | ||
|
1de301752f | ||
|
cb8c389303 | ||
|
e1e0166c95 | ||
|
6f8e1ccf89 | ||
|
3751856a4e | ||
|
5203e72199 | ||
|
3b1ab3c974 | ||
|
3476bbebd9 | ||
|
45870497c6 | ||
|
bcf506488f | ||
|
040774d67b | ||
|
94410352f5 | ||
|
2a819f1ca0 | ||
|
25c63dd1e0 | ||
|
d7223972d8 | ||
|
0a6c1238eb | ||
|
c11575f5f5 | ||
|
783761ca2e |
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:
|
||||||
##
|
##
|
||||||
|
2
.github/workflows/winget.yml
vendored
2
.github/workflows/winget.yml
vendored
@ -11,5 +11,5 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
identifier: PrismLauncher.PrismLauncher
|
identifier: PrismLauncher.PrismLauncher
|
||||||
version: ${{ github.event.release.tag_name }}
|
version: ${{ github.event.release.tag_name }}
|
||||||
installers-regex: 'PrismLauncher-Windows-Setup-.+\.exe$'
|
installers-regex: 'PrismLauncher-Windows-MSVC(:?-arm64|-Legacy)?-Setup-.+\.exe$'
|
||||||
token: ${{ secrets.WINGET_TOKEN }}
|
token: ${{ secrets.WINGET_TOKEN }}
|
||||||
|
@ -28,19 +28,28 @@ set(CMAKE_CXX_STANDARD 17)
|
|||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
include(GenerateExportHeader)
|
include(GenerateExportHeader)
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
# Use /W4 as /Wall includes unnesserey warnings such as added padding to structs
|
|
||||||
# /permissive- specify standards-conforming compiler behavior, also enabled by Qt6, default on with std:c++20
|
|
||||||
# /GS Adds buffer security checks, default on but incuded anyway to mirror gcc's fstack-protector flag
|
# /GS Adds buffer security checks, default on but incuded anyway to mirror gcc's fstack-protector flag
|
||||||
set(CMAKE_CXX_FLAGS "/W4 /permissive- /GS ${CMAKE_CXX_FLAGS}")
|
# /permissive- specify standards-conforming compiler behavior, also enabled by Qt6, default on with std:c++20
|
||||||
|
# Use /W4 as /Wall includes unnesserey warnings such as added padding to structs
|
||||||
|
set(CMAKE_CXX_FLAGS "/GS /permissive- /W4 ${CMAKE_CXX_FLAGS}")
|
||||||
|
|
||||||
# LINK accepts /SUBSYSTEM whics sets if we are a WINDOWS (gui) or a CONSOLE programs
|
# LINK accepts /SUBSYSTEM whics sets if we are a WINDOWS (gui) or a CONSOLE programs
|
||||||
# This implicitly selects an entrypoint specific to the subsystem selected
|
# This implicitly selects an entrypoint specific to the subsystem selected
|
||||||
# qtmain/QtEntryPointLib provides the correct entrypoint (wWinMain) for gui programs
|
# qtmain/QtEntryPointLib provides the correct entrypoint (wWinMain) for gui programs
|
||||||
# Additinaly LINK autodetects we use a GUI so we can omit /SUBSYSTEM
|
# Additinaly LINK autodetects we use a GUI so we can omit /SUBSYSTEM
|
||||||
# This allows tests to still use have console without using seperate linker flags
|
# This allows tests to still use have console without using seperate linker flags
|
||||||
|
# /LTCG allows for linking wholy optimizated programs
|
||||||
# /MANIFEST:NO disables generating a manifest file, we instead provide our own
|
# /MANIFEST:NO disables generating a manifest file, we instead provide our own
|
||||||
# /STACK sets the stack reserve size, ATL's pack list needs 3-4 MiB as of November 2022, provide 8 MiB
|
# /STACK sets the stack reserve size, ATL's pack list needs 3-4 MiB as of November 2022, provide 8 MiB
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "/MANIFEST:NO /STACK:8388608 ${CMAKE_EXE_LINKER_FLAGS}")
|
set(CMAKE_EXE_LINKER_FLAGS "/LTCG /MANIFEST:NO /STACK:8388608 ${CMAKE_EXE_LINKER_FLAGS}")
|
||||||
|
|
||||||
|
# /GL enables whole program optimizations
|
||||||
|
# /Gw helps reduce binary size
|
||||||
|
# /Gy allows the compiler to package individual functions
|
||||||
|
# /guard:cf enables control flow guard
|
||||||
|
foreach(lang C CXX)
|
||||||
|
set("CMAKE_${lang}_FLAGS_RELEASE" "/GL /Gw /Gy /guard:cf")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
# See https://github.com/ccache/ccache/issues/1040
|
# See https://github.com/ccache/ccache/issues/1040
|
||||||
# Note, CMake 3.25 replaces this with CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
|
# Note, CMake 3.25 replaces this with CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
|
||||||
@ -130,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 0)
|
set(Launcher_VERSION_MINOR 3)
|
||||||
|
|
||||||
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")
|
||||||
@ -199,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)
|
||||||
@ -259,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")
|
||||||
@ -366,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();
|
||||||
|
31
flake.lock
31
flake.lock
@ -3,11 +3,11 @@
|
|||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1650374568,
|
"lastModified": 1668681692,
|
||||||
"narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=",
|
"narHash": "sha256-Ht91NGdewz8IQLtWZ9LCeNXMSXHUss+9COoqu6JLmXU=",
|
||||||
"owner": "edolstra",
|
"owner": "edolstra",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "b4a34015c698c7793d592d66adbab377907a2be8",
|
"rev": "009399224d5e398d03b22badca40a37ac85412a1",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -34,11 +34,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1666057921,
|
"lastModified": 1671417167,
|
||||||
"narHash": "sha256-VpQqtXdj6G7cH//SvoprjR7XT3KS7p+tCVebGK1N6tE=",
|
"narHash": "sha256-JkHam6WQOwZN1t2C2sbp1TqMv3TVRjzrdoejqfefwrM=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "88eab1e431cabd0ed621428d8b40d425a07af39f",
|
"rev": "bb31220cca6d044baa6dc2715b07497a2a7c4bc7",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -52,24 +52,7 @@
|
|||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
"libnbtplusplus": "libnbtplusplus",
|
"libnbtplusplus": "libnbtplusplus",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs"
|
||||||
"tomlplusplus": "tomlplusplus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tomlplusplus": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1666091090,
|
|
||||||
"narHash": "sha256-djpMCFPvkJcfynV8WnsYdtwLq+J7jpV1iM4C6TojiyM=",
|
|
||||||
"owner": "marzer",
|
|
||||||
"repo": "tomlplusplus",
|
|
||||||
"rev": "1e4a3833d013aee08f58c5b31c69f709afc69f73",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "marzer",
|
|
||||||
"repo": "tomlplusplus",
|
|
||||||
"type": "github"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -5,10 +5,9 @@
|
|||||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||||
flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
|
flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
|
||||||
libnbtplusplus = { url = "github:PrismLauncher/libnbtplusplus"; flake = false; };
|
libnbtplusplus = { url = "github:PrismLauncher/libnbtplusplus"; flake = false; };
|
||||||
tomlplusplus = { url = "github:marzer/tomlplusplus"; flake = false; };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, libnbtplusplus, tomlplusplus, ... }:
|
outputs = { self, nixpkgs, libnbtplusplus, ... }:
|
||||||
let
|
let
|
||||||
# User-friendly version number.
|
# User-friendly version number.
|
||||||
version = builtins.substring 0 8 self.lastModifiedDate;
|
version = builtins.substring 0 8 self.lastModifiedDate;
|
||||||
@ -23,8 +22,8 @@
|
|||||||
pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system});
|
pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system});
|
||||||
|
|
||||||
packagesFn = pkgs: rec {
|
packagesFn = pkgs: rec {
|
||||||
prismlauncher-qt5 = pkgs.libsForQt5.callPackage ./nix { inherit version self libnbtplusplus tomlplusplus; };
|
prismlauncher-qt5 = pkgs.libsForQt5.callPackage ./nix { inherit version self libnbtplusplus; };
|
||||||
prismlauncher = pkgs.qt6Packages.callPackage ./nix { inherit version self libnbtplusplus tomlplusplus; };
|
prismlauncher = pkgs.qt6Packages.callPackage ./nix { inherit version self libnbtplusplus; };
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -47,8 +47,8 @@ void ApplicationMessage::parse(const QByteArray & input) {
|
|||||||
args.clear();
|
args.clear();
|
||||||
|
|
||||||
auto parsedArgs = root.value("args").toObject();
|
auto parsedArgs = root.value("args").toObject();
|
||||||
for(auto iter = parsedArgs.begin(); iter != parsedArgs.end(); iter++) {
|
for(auto iter = parsedArgs.constBegin(); iter != parsedArgs.constEnd(); iter++) {
|
||||||
args[iter.key()] = iter.value().toString();
|
args.insert(iter.key(), iter.value().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,8 +56,8 @@ QByteArray ApplicationMessage::serialize() {
|
|||||||
QJsonObject root;
|
QJsonObject root;
|
||||||
root.insert("command", command);
|
root.insert("command", command);
|
||||||
QJsonObject outArgs;
|
QJsonObject outArgs;
|
||||||
for (auto iter = args.begin(); iter != args.end(); iter++) {
|
for (auto iter = args.constBegin(); iter != args.constEnd(); iter++) {
|
||||||
outArgs[iter.key()] = iter.value();
|
outArgs.insert(iter.key(), iter.value());
|
||||||
}
|
}
|
||||||
root.insert("args", outArgs);
|
root.insert("args", outArgs);
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QMap>
|
#include <QHash>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
|
||||||
struct ApplicationMessage {
|
struct ApplicationMessage {
|
||||||
QString command;
|
QString command;
|
||||||
QMap<QString, QString> args;
|
QHash<QString, QString> args;
|
||||||
|
|
||||||
QByteArray serialize();
|
QByteArray serialize();
|
||||||
void parse(const QByteArray & input);
|
void parse(const QByteArray & input);
|
||||||
|
@ -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
|
||||||
@ -1166,6 +1174,8 @@ if(INSTALL_BUNDLE STREQUAL "full")
|
|||||||
CONFIGURATIONS Debug RelWithDebInfo ""
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
DESTINATION ${PLUGIN_DEST_DIR}
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
COMPONENT Runtime
|
COMPONENT Runtime
|
||||||
|
PATTERN "*qopensslbackend*" EXCLUDE
|
||||||
|
PATTERN "*qcertonlybackend*" EXCLUDE
|
||||||
)
|
)
|
||||||
install(
|
install(
|
||||||
DIRECTORY "${QT_PLUGINS_DIR}/tls"
|
DIRECTORY "${QT_PLUGINS_DIR}/tls"
|
||||||
@ -1175,6 +1185,8 @@ if(INSTALL_BUNDLE STREQUAL "full")
|
|||||||
REGEX "dd\\." EXCLUDE
|
REGEX "dd\\." EXCLUDE
|
||||||
REGEX "_debug\\." EXCLUDE
|
REGEX "_debug\\." EXCLUDE
|
||||||
REGEX "\\.dSYM" EXCLUDE
|
REGEX "\\.dSYM" EXCLUDE
|
||||||
|
PATTERN "*qopensslbackend*" EXCLUDE
|
||||||
|
PATTERN "*qcertonlybackend*" EXCLUDE
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
configure_file(
|
configure_file(
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -257,6 +257,8 @@ void InstanceImportTask::extractAborted()
|
|||||||
|
|
||||||
void InstanceImportTask::processFlame()
|
void InstanceImportTask::processFlame()
|
||||||
{
|
{
|
||||||
|
FlameCreationTask* inst_creation_task = nullptr;
|
||||||
|
if (!m_extra_info.isEmpty()) {
|
||||||
auto pack_id_it = m_extra_info.constFind("pack_id");
|
auto pack_id_it = m_extra_info.constFind("pack_id");
|
||||||
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
|
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
|
||||||
auto pack_id = pack_id_it.value();
|
auto pack_id = pack_id_it.value();
|
||||||
@ -270,7 +272,11 @@ void InstanceImportTask::processFlame()
|
|||||||
if (original_instance_id_it != m_extra_info.constEnd())
|
if (original_instance_id_it != m_extra_info.constEnd())
|
||||||
original_instance_id = original_instance_id_it.value();
|
original_instance_id = original_instance_id_it.value();
|
||||||
|
|
||||||
auto* inst_creation_task = new FlameCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
inst_creation_task = new 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->setName(*this);
|
inst_creation_task->setName(*this);
|
||||||
inst_creation_task->setIcon(m_instIcon);
|
inst_creation_task->setIcon(m_instIcon);
|
||||||
@ -335,6 +341,8 @@ void InstanceImportTask::processMultiMC()
|
|||||||
|
|
||||||
void InstanceImportTask::processModrinth()
|
void InstanceImportTask::processModrinth()
|
||||||
{
|
{
|
||||||
|
ModrinthCreationTask* inst_creation_task = nullptr;
|
||||||
|
if (!m_extra_info.isEmpty()) {
|
||||||
auto pack_id_it = m_extra_info.constFind("pack_id");
|
auto pack_id_it = m_extra_info.constFind("pack_id");
|
||||||
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
|
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
|
||||||
auto pack_id = pack_id_it.value();
|
auto pack_id = pack_id_it.value();
|
||||||
@ -349,7 +357,17 @@ void InstanceImportTask::processModrinth()
|
|||||||
if (original_instance_id_it != m_extra_info.constEnd())
|
if (original_instance_id_it != m_extra_info.constEnd())
|
||||||
original_instance_id = original_instance_id_it.value();
|
original_instance_id = original_instance_id_it.value();
|
||||||
|
|
||||||
auto* inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
||||||
|
} else {
|
||||||
|
QString pack_id;
|
||||||
|
if (!m_sourceUrl.isEmpty()) {
|
||||||
|
QRegularExpression regex(R"(data\/(.*)\/versions)");
|
||||||
|
pack_id = regex.match(m_sourceUrl.toString()).captured(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Find a way to get the ID in directly imported ZIPs
|
||||||
|
inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id);
|
||||||
|
}
|
||||||
|
|
||||||
inst_creation_task->setName(*this);
|
inst_creation_task->setName(*this);
|
||||||
inst_creation_task->setIcon(m_instIcon);
|
inst_creation_task->setIcon(m_instIcon);
|
||||||
|
@ -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"([a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]+)*)")) };
|
||||||
|
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"));
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ ResourceFolderModel::ResourceFolderModel(QDir dir, QObject* parent) : QAbstractL
|
|||||||
m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
|
m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
|
||||||
|
|
||||||
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &ResourceFolderModel::directoryChanged);
|
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &ResourceFolderModel::directoryChanged);
|
||||||
|
connect(&m_helper_thread_task, &ConcurrentTask::finished, this, [this]{ m_helper_thread_task.clear(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceFolderModel::~ResourceFolderModel()
|
ResourceFolderModel::~ResourceFolderModel()
|
||||||
@ -259,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;
|
||||||
|
|
||||||
@ -269,13 +270,17 @@ 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);
|
||||||
|
|
||||||
QThreadPool::globalInstance()->start(task);
|
m_helper_thread_task.addTask(task);
|
||||||
|
|
||||||
|
if (!m_helper_thread_task.isRunning()) {
|
||||||
|
QThreadPool::globalInstance()->start(&m_helper_thread_task);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceFolderModel::onUpdateSucceeded()
|
void ResourceFolderModel::onUpdateSucceeded()
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "Resource.h"
|
#include "Resource.h"
|
||||||
|
|
||||||
#include "tasks/Task.h"
|
#include "tasks/Task.h"
|
||||||
|
#include "tasks/ConcurrentTask.h"
|
||||||
|
|
||||||
class QSortFilterProxyModel;
|
class QSortFilterProxyModel;
|
||||||
|
|
||||||
@ -197,6 +198,7 @@ class ResourceFolderModel : public QAbstractListModel {
|
|||||||
// Represents the relationship between a resource's internal ID and it's row position on the model.
|
// Represents the relationship between a resource's internal ID and it's row position on the model.
|
||||||
QMap<QString, int> m_resources_index;
|
QMap<QString, int> m_resources_index;
|
||||||
|
|
||||||
|
ConcurrentTask m_helper_thread_task;
|
||||||
QMap<int, Task::Ptr> m_active_parse_tasks;
|
QMap<int, Task::Ptr> m_active_parse_tasks;
|
||||||
std::atomic<int> m_next_resolution_ticket = 0;
|
std::atomic<int> m_next_resolution_ticket = 0;
|
||||||
};
|
};
|
||||||
|
@ -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");
|
||||||
|
@ -361,6 +361,8 @@ bool FlameCreationTask::createInstance()
|
|||||||
FS::deletePath(jarmodsPath);
|
FS::deletePath(jarmodsPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't add managed info to packs without an ID (most likely imported from ZIP)
|
||||||
|
if (!m_managed_id.isEmpty())
|
||||||
instance.setManagedPack("flame", m_managed_id, m_pack.name, m_managed_version_id, m_pack.version);
|
instance.setManagedPack("flame", m_managed_id, m_pack.name, m_managed_version_id, m_pack.version);
|
||||||
instance.setName(name());
|
instance.setName(name());
|
||||||
|
|
||||||
|
@ -202,14 +202,14 @@ bool ModrinthCreationTask::createInstance()
|
|||||||
|
|
||||||
auto components = instance.getPackProfile();
|
auto components = instance.getPackProfile();
|
||||||
components->buildingFromScratch();
|
components->buildingFromScratch();
|
||||||
components->setComponentVersion("net.minecraft", minecraftVersion, true);
|
components->setComponentVersion("net.minecraft", m_minecraft_version, true);
|
||||||
|
|
||||||
if (!fabricVersion.isEmpty())
|
if (!m_fabric_version.isEmpty())
|
||||||
components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion);
|
components->setComponentVersion("net.fabricmc.fabric-loader", m_fabric_version);
|
||||||
if (!quiltVersion.isEmpty())
|
if (!m_quilt_version.isEmpty())
|
||||||
components->setComponentVersion("org.quiltmc.quilt-loader", quiltVersion);
|
components->setComponentVersion("org.quiltmc.quilt-loader", m_quilt_version);
|
||||||
if (!forgeVersion.isEmpty())
|
if (!m_forge_version.isEmpty())
|
||||||
components->setComponentVersion("net.minecraftforge", forgeVersion);
|
components->setComponentVersion("net.minecraftforge", m_forge_version);
|
||||||
|
|
||||||
if (m_instIcon != "default") {
|
if (m_instIcon != "default") {
|
||||||
instance.setIconKey(m_instIcon);
|
instance.setIconKey(m_instIcon);
|
||||||
@ -217,16 +217,27 @@ bool ModrinthCreationTask::createInstance()
|
|||||||
instance.setIconKey("modrinth");
|
instance.setIconKey("modrinth");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't add managed info to packs without an ID (most likely imported from ZIP)
|
||||||
|
if (!m_managed_id.isEmpty())
|
||||||
instance.setManagedPack("modrinth", m_managed_id, m_managed_name, m_managed_version_id, version());
|
instance.setManagedPack("modrinth", m_managed_id, m_managed_name, m_managed_version_id, version());
|
||||||
instance.setName(name());
|
instance.setName(name());
|
||||||
instance.saveNow();
|
instance.saveNow();
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@ -234,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();
|
||||||
@ -277,7 +288,7 @@ bool ModrinthCreationTask::createInstance()
|
|||||||
return ended_well;
|
return ended_well;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ModrinthCreationTask::parseManifest(const QString& index_path, std::vector<Modrinth::File>& files, bool set_managed_info, bool show_optional_dialog)
|
bool ModrinthCreationTask::parseManifest(const QString& index_path, std::vector<Modrinth::File>& files, bool set_internal_data, bool show_optional_dialog)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
auto doc = Json::requireDocument(index_path);
|
auto doc = Json::requireDocument(index_path);
|
||||||
@ -289,7 +300,7 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, std::vector<
|
|||||||
throw JSONValidationError("Unknown game: " + game);
|
throw JSONValidationError("Unknown game: " + game);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (set_managed_info) {
|
if (set_internal_data) {
|
||||||
if (m_managed_version_id.isEmpty())
|
if (m_managed_version_id.isEmpty())
|
||||||
m_managed_version_id = Json::ensureString(obj, "versionId", {}, "Managed ID");
|
m_managed_version_id = Json::ensureString(obj, "versionId", {}, "Managed ID");
|
||||||
m_managed_name = Json::ensureString(obj, "name", {}, "Managed Name");
|
m_managed_name = Json::ensureString(obj, "name", {}, "Managed Name");
|
||||||
@ -365,21 +376,23 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, std::vector<
|
|||||||
files.push_back(file);
|
files.push_back(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (set_internal_data) {
|
||||||
auto dependencies = Json::requireObject(obj, "dependencies", "modrinth.index.json");
|
auto dependencies = Json::requireObject(obj, "dependencies", "modrinth.index.json");
|
||||||
for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) {
|
for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) {
|
||||||
QString name = it.key();
|
QString name = it.key();
|
||||||
if (name == "minecraft") {
|
if (name == "minecraft") {
|
||||||
minecraftVersion = Json::requireString(*it, "Minecraft version");
|
m_minecraft_version = Json::requireString(*it, "Minecraft version");
|
||||||
} else if (name == "fabric-loader") {
|
} else if (name == "fabric-loader") {
|
||||||
fabricVersion = Json::requireString(*it, "Fabric Loader version");
|
m_fabric_version = Json::requireString(*it, "Fabric Loader version");
|
||||||
} else if (name == "quilt-loader") {
|
} else if (name == "quilt-loader") {
|
||||||
quiltVersion = Json::requireString(*it, "Quilt Loader version");
|
m_quilt_version = Json::requireString(*it, "Quilt Loader version");
|
||||||
} else if (name == "forge") {
|
} else if (name == "forge") {
|
||||||
forgeVersion = Json::requireString(*it, "Forge version");
|
m_forge_version = Json::requireString(*it, "Forge version");
|
||||||
} else {
|
} else {
|
||||||
throw JSONValidationError("Unknown dependency type: " + name);
|
throw JSONValidationError("Unknown dependency type: " + name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw JSONValidationError(QStringLiteral("Unknown format version: %s").arg(formatVersion));
|
throw JSONValidationError(QStringLiteral("Unknown format version: %s").arg(formatVersion));
|
||||||
}
|
}
|
||||||
|
@ -37,12 +37,12 @@ class ModrinthCreationTask final : public InstanceCreationTask {
|
|||||||
bool createInstance() override;
|
bool createInstance() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool parseManifest(const QString&, std::vector<Modrinth::File>&, bool set_managed_info = true, bool show_optional_dialog = true);
|
bool parseManifest(const QString&, std::vector<Modrinth::File>&, bool set_internal_data = true, bool show_optional_dialog = true);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWidget* m_parent = nullptr;
|
QWidget* m_parent = nullptr;
|
||||||
|
|
||||||
QString minecraftVersion, fabricVersion, quiltVersion, forgeVersion;
|
QString m_minecraft_version, m_fabric_version, m_quilt_version, m_forge_version;
|
||||||
QString m_managed_id, m_managed_version_id, m_managed_name;
|
QString m_managed_id, m_managed_version_id, m_managed_name;
|
||||||
|
|
||||||
std::vector<Modrinth::File> m_files;
|
std::vector<Modrinth::File> m_files;
|
||||||
|
@ -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-"))
|
||||||
|
@ -123,7 +123,7 @@ auto NetJob::getFailedFiles() -> QList<QString>
|
|||||||
|
|
||||||
void NetJob::updateState()
|
void NetJob::updateState()
|
||||||
{
|
{
|
||||||
emit progress(m_done.count(), m_total_size);
|
emit progress(m_done.count(), totalSize());
|
||||||
setStatus(tr("Executing %1 task(s) (%2 out of %3 are done)")
|
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(m_total_size)));
|
.arg(QString::number(m_doing.count()), QString::number(m_done.count()), QString::number(totalSize())));
|
||||||
}
|
}
|
||||||
|
@ -27,18 +27,13 @@ auto ConcurrentTask::getStepTotalProgress() const -> qint64
|
|||||||
|
|
||||||
void ConcurrentTask::addTask(Task::Ptr task)
|
void ConcurrentTask::addTask(Task::Ptr task)
|
||||||
{
|
{
|
||||||
if (!isRunning())
|
|
||||||
m_queue.append(task);
|
m_queue.append(task);
|
||||||
else
|
|
||||||
qWarning() << "Tried to add a task to a running concurrent task!";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConcurrentTask::executeTask()
|
void ConcurrentTask::executeTask()
|
||||||
{
|
{
|
||||||
m_total_size = m_queue.size();
|
|
||||||
|
|
||||||
// Start the least amount of tasks needed, but at least one
|
// Start the least amount of tasks needed, but at least one
|
||||||
int num_starts = std::max(1, std::min(m_total_max_size, m_total_size));
|
int num_starts = qMax(1, qMin(m_total_max_size, m_queue.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);
|
||||||
}
|
}
|
||||||
@ -73,6 +68,20 @@ bool ConcurrentTask::abort()
|
|||||||
return suceedeed;
|
return suceedeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConcurrentTask::clear()
|
||||||
|
{
|
||||||
|
Q_ASSERT(!isRunning());
|
||||||
|
|
||||||
|
m_done.clear();
|
||||||
|
m_failed.clear();
|
||||||
|
m_queue.clear();
|
||||||
|
|
||||||
|
m_aborted = false;
|
||||||
|
|
||||||
|
m_progress = 0;
|
||||||
|
m_stepProgress = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void ConcurrentTask::startNext()
|
void ConcurrentTask::startNext()
|
||||||
{
|
{
|
||||||
if (m_aborted || m_doing.count() > m_total_max_size)
|
if (m_aborted || m_doing.count() > m_total_max_size)
|
||||||
@ -103,7 +112,12 @@ void ConcurrentTask::startNext()
|
|||||||
|
|
||||||
QCoreApplication::processEvents();
|
QCoreApplication::processEvents();
|
||||||
|
|
||||||
next->start();
|
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.
|
||||||
|
int num_starts = qMin(m_queue.size(), m_total_max_size - m_doing.size());
|
||||||
|
for (int i = 0; i < num_starts; i++)
|
||||||
|
QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConcurrentTask::subTaskSucceeded(Task::Ptr task)
|
void ConcurrentTask::subTaskSucceeded(Task::Ptr task)
|
||||||
@ -145,7 +159,7 @@ void ConcurrentTask::subTaskProgress(qint64 current, qint64 total)
|
|||||||
|
|
||||||
void ConcurrentTask::updateState()
|
void ConcurrentTask::updateState()
|
||||||
{
|
{
|
||||||
setProgress(m_done.count(), m_total_size);
|
setProgress(m_done.count(), totalSize());
|
||||||
setStatus(tr("Executing %1 task(s) (%2 out of %3 are done)")
|
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(m_total_size)));
|
.arg(QString::number(m_doing.count()), QString::number(m_done.count()), QString::number(totalSize())));
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,11 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
bool abort() override;
|
bool abort() override;
|
||||||
|
|
||||||
|
/** Resets the internal state of the task.
|
||||||
|
* This allows the same task to be re-used.
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
protected
|
protected
|
||||||
slots:
|
slots:
|
||||||
void executeTask() override;
|
void executeTask() override;
|
||||||
@ -36,6 +41,9 @@ slots:
|
|||||||
void subTaskProgress(qint64 current, qint64 total);
|
void subTaskProgress(qint64 current, qint64 total);
|
||||||
|
|
||||||
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); };
|
void setStepStatus(QString status) { m_step_status = status; emit stepStatus(status); };
|
||||||
|
|
||||||
virtual void updateState();
|
virtual void updateState();
|
||||||
@ -51,7 +59,6 @@ protected:
|
|||||||
QHash<Task*, Task::Ptr> m_failed;
|
QHash<Task*, Task::Ptr> m_failed;
|
||||||
|
|
||||||
int m_total_max_size;
|
int m_total_max_size;
|
||||||
int m_total_size;
|
|
||||||
|
|
||||||
qint64 m_stepProgress = 0;
|
qint64 m_stepProgress = 0;
|
||||||
qint64 m_stepTotalProgress = 100;
|
qint64 m_stepTotalProgress = 100;
|
||||||
|
@ -22,6 +22,6 @@ void MultipleOptionsTask::startNext()
|
|||||||
|
|
||||||
void MultipleOptionsTask::updateState()
|
void MultipleOptionsTask::updateState()
|
||||||
{
|
{
|
||||||
setProgress(m_done.count(), m_total_size);
|
setProgress(m_done.count(), totalSize());
|
||||||
setStatus(tr("Attempting task %1 out of %2").arg(QString::number(m_doing.count() + m_done.count()), QString::number(m_total_size)));
|
setStatus(tr("Attempting task %1 out of %2").arg(QString::number(m_doing.count() + m_done.count()), QString::number(totalSize())));
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,6 @@ void SequentialTask::startNext()
|
|||||||
|
|
||||||
void SequentialTask::updateState()
|
void SequentialTask::updateState()
|
||||||
{
|
{
|
||||||
setProgress(m_done.count(), m_total_size);
|
setProgress(m_done.count(), totalSize());
|
||||||
setStatus(tr("Executing task %1 out of %2").arg(QString::number(m_doing.count() + m_done.count()), QString::number(m_total_size)));
|
setStatus(tr("Executing task %1 out of %2").arg(QString::number(m_doing.count() + m_done.count()), QString::number(totalSize())));
|
||||||
}
|
}
|
||||||
|
@ -1683,7 +1683,7 @@ InstanceView
|
|||||||
background-image: url(:/backgrounds/%1);
|
background-image: url(:/backgrounds/%1);
|
||||||
background-attachment: fixed;
|
background-attachment: fixed;
|
||||||
background-clip: padding;
|
background-clip: padding;
|
||||||
background-position: bottom left;
|
background-position: bottom right;
|
||||||
background-repeat: none;
|
background-repeat: none;
|
||||||
background-color:palette(base);
|
background-color:palette(base);
|
||||||
})")
|
})")
|
||||||
|
@ -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 *);
|
|
||||||
|
|
||||||
}
|
|
@ -47,7 +47,6 @@ IconPickerDialog::IconPickerDialog(QWidget *parent)
|
|||||||
contentsWidget->setUniformItemSizes(true);
|
contentsWidget->setUniformItemSizes(true);
|
||||||
contentsWidget->setTextElideMode(Qt::ElideRight);
|
contentsWidget->setTextElideMode(Qt::ElideRight);
|
||||||
contentsWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
contentsWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||||
contentsWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
|
||||||
contentsWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
contentsWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
contentsWidget->setItemDelegate(new ListViewDelegate());
|
contentsWidget->setItemDelegate(new ListViewDelegate());
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ ImportResourcePackDialog::ImportResourcePackDialog(QWidget* parent) : QDialog(pa
|
|||||||
// NOTE: We can't have uniform sizes because the text may wrap if it's too long. If we set this, it will cut off the wrapped text.
|
// NOTE: We can't have uniform sizes because the text may wrap if it's too long. If we set this, it will cut off the wrapped text.
|
||||||
contentsWidget->setUniformItemSizes(false);
|
contentsWidget->setUniformItemSizes(false);
|
||||||
contentsWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
contentsWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||||
contentsWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
|
||||||
contentsWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
contentsWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
contentsWidget->setItemDelegate(new ListViewDelegate());
|
contentsWidget->setItemDelegate(new ListViewDelegate());
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <QListView>
|
#include <QListView>
|
||||||
#include <QProxyStyle>
|
#include <QProxyStyle>
|
||||||
|
#include <QStyleFactory>
|
||||||
|
|
||||||
#include <HoeDown.h>
|
#include <HoeDown.h>
|
||||||
|
|
||||||
@ -60,6 +61,9 @@ ManagedPackPage::ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_wi
|
|||||||
|
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
// 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()));
|
ui->versionsComboBox->setStyle(new NoBigComboBoxStyle(ui->versionsComboBox->style()));
|
||||||
|
|
||||||
ui->reloadButton->setVisible(false);
|
ui->reloadButton->setVisible(false);
|
||||||
@ -223,17 +227,16 @@ void ModrinthManagedPackPage::parseManagedPack()
|
|||||||
ui->versionsComboBox->blockSignals(false);
|
ui->versionsComboBox->blockSignals(false);
|
||||||
|
|
||||||
for (auto version : m_pack.versions) {
|
for (auto version : m_pack.versions) {
|
||||||
QString name;
|
QString name = version.version;
|
||||||
|
|
||||||
if (!version.name.contains(version.version))
|
if (!version.name.contains(version.version))
|
||||||
name = QString("%1 — %2").arg(version.name, version.version);
|
name = QString("%1 — %2").arg(version.name, version.version);
|
||||||
else
|
|
||||||
name = version.name;
|
|
||||||
|
|
||||||
// NOTE: the id from version isn't the same id in the modpack format spec...
|
// NOTE: the id from version isn't the same id in the modpack format spec...
|
||||||
// e.g. HexMC's 4.4.0 has versionId 4.0.0 in the modpack index..............
|
// e.g. HexMC's 4.4.0 has versionId 4.0.0 in the modpack index..............
|
||||||
if (version.version == m_inst->getManagedPackVersionName())
|
if (version.version == m_inst->getManagedPackVersionName())
|
||||||
name.append(tr(" (Current)"));
|
name = tr("%1 (Current)").arg(name);
|
||||||
|
|
||||||
|
|
||||||
ui->versionsComboBox->addItem(name, QVariant(version.id));
|
ui->versionsComboBox->addItem(name, QVariant(version.id));
|
||||||
}
|
}
|
||||||
@ -370,12 +373,10 @@ void FlameManagedPackPage::parseManagedPack()
|
|||||||
ui->versionsComboBox->blockSignals(false);
|
ui->versionsComboBox->blockSignals(false);
|
||||||
|
|
||||||
for (auto version : m_pack.versions) {
|
for (auto version : m_pack.versions) {
|
||||||
QString name;
|
QString name = version.version;
|
||||||
|
|
||||||
name = version.version;
|
|
||||||
|
|
||||||
if (version.fileId == m_inst->getManagedPackVersionID().toInt())
|
if (version.fileId == m_inst->getManagedPackVersionID().toInt())
|
||||||
name.append(tr(" (Current)"));
|
name = tr("%1 (Current)").arg(name);
|
||||||
|
|
||||||
ui->versionsComboBox->addItem(name, QVariant(version.fileId));
|
ui->versionsComboBox->addItem(name, QVariant(version.fileId));
|
||||||
}
|
}
|
||||||
|
@ -17,17 +17,11 @@
|
|||||||
<property name="topMargin">
|
<property name="topMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTextEdit" name="noteEditor">
|
<widget class="QTextEdit" name="noteEditor">
|
||||||
<property name="verticalScrollBarPolicy">
|
|
||||||
<enum>Qt::ScrollBarAlwaysOn</enum>
|
|
||||||
</property>
|
|
||||||
<property name="tabChangesFocus">
|
<property name="tabChangesFocus">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
@ -48,9 +48,6 @@
|
|||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="verticalScrollBarPolicy">
|
|
||||||
<enum>Qt::ScrollBarAlwaysOn</enum>
|
|
||||||
</property>
|
|
||||||
<property name="readOnly">
|
<property name="readOnly">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
@ -28,9 +28,6 @@
|
|||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="ModListView" name="packageView">
|
<widget class="ModListView" name="packageView">
|
||||||
<property name="verticalScrollBarPolicy">
|
|
||||||
<enum>Qt::ScrollBarAlwaysOn</enum>
|
|
||||||
</property>
|
|
||||||
<property name="horizontalScrollBarPolicy">
|
<property name="horizontalScrollBarPolicy">
|
||||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||||
</property>
|
</property>
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -6,19 +6,14 @@
|
|||||||
|
|
||||||
void ITheme::apply(bool)
|
void ITheme::apply(bool)
|
||||||
{
|
{
|
||||||
|
APPLICATION->setStyleSheet(QString());
|
||||||
QApplication::setStyle(QStyleFactory::create(qtTheme()));
|
QApplication::setStyle(QStyleFactory::create(qtTheme()));
|
||||||
if(hasColorScheme())
|
if (hasColorScheme()) {
|
||||||
{
|
|
||||||
QApplication::setPalette(colorScheme());
|
QApplication::setPalette(colorScheme());
|
||||||
}
|
}
|
||||||
if (hasStyleSheet())
|
if (hasStyleSheet())
|
||||||
{
|
|
||||||
APPLICATION->setStyleSheet(appStyleSheet());
|
APPLICATION->setStyleSheet(appStyleSheet());
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
APPLICATION->setStyleSheet(QString());
|
|
||||||
}
|
|
||||||
QDir::setSearchPaths("theme", searchPaths());
|
QDir::setSearchPaths("theme", searchPaths());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,6 @@ ModListView::ModListView ( QWidget* parent )
|
|||||||
setSelectionMode ( QAbstractItemView::ExtendedSelection );
|
setSelectionMode ( QAbstractItemView::ExtendedSelection );
|
||||||
setHeaderHidden ( false );
|
setHeaderHidden ( false );
|
||||||
setSelectionBehavior(QAbstractItemView::SelectRows);
|
setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
setVerticalScrollBarPolicy ( Qt::ScrollBarAlwaysOn );
|
|
||||||
setHorizontalScrollBarPolicy ( Qt::ScrollBarAsNeeded );
|
setHorizontalScrollBarPolicy ( Qt::ScrollBarAsNeeded );
|
||||||
setDropIndicatorShown(true);
|
setDropIndicatorShown(true);
|
||||||
setDragEnabled(true);
|
setDragEnabled(true);
|
||||||
|
@ -101,7 +101,7 @@ void VariableSizedImageObject::loadImage(QTextDocument* doc, const QUrl& source,
|
|||||||
|
|
||||||
auto full_entry_path = entry->getFullPath();
|
auto full_entry_path = entry->getFullPath();
|
||||||
auto source_url = source;
|
auto source_url = source;
|
||||||
connect(job, &NetJob::succeeded, [this, doc, full_entry_path, source_url, posInDocument] {
|
connect(job, &NetJob::succeeded, this, [this, doc, full_entry_path, source_url, posInDocument] {
|
||||||
qDebug() << "Loaded resource at" << full_entry_path;
|
qDebug() << "Loaded resource at" << full_entry_path;
|
||||||
|
|
||||||
// If we flushed, don't proceed.
|
// If we flushed, don't proceed.
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
, stdenv
|
, stdenv
|
||||||
, cmake
|
, cmake
|
||||||
, jdk8
|
, jdk8
|
||||||
, jdk
|
, jdk17
|
||||||
, zlib
|
, zlib
|
||||||
, file
|
, file
|
||||||
, wrapQtAppsHook
|
, wrapQtAppsHook
|
||||||
@ -16,15 +16,15 @@
|
|||||||
, glfw
|
, glfw
|
||||||
, openal
|
, openal
|
||||||
, extra-cmake-modules
|
, extra-cmake-modules
|
||||||
|
, tomlplusplus
|
||||||
, ghc_filesystem
|
, ghc_filesystem
|
||||||
, msaClientID ? ""
|
, msaClientID ? ""
|
||||||
, jdks ? [ jdk jdk8 ]
|
, jdks ? [ jdk17 jdk8 ]
|
||||||
|
|
||||||
# flake
|
# flake
|
||||||
, self
|
, self
|
||||||
, version
|
, version
|
||||||
, libnbtplusplus
|
, libnbtplusplus
|
||||||
, tomlplusplus
|
|
||||||
}:
|
}:
|
||||||
|
|
||||||
stdenv.mkDerivation rec {
|
stdenv.mkDerivation rec {
|
||||||
@ -33,13 +33,14 @@ stdenv.mkDerivation rec {
|
|||||||
|
|
||||||
src = lib.cleanSource self;
|
src = lib.cleanSource self;
|
||||||
|
|
||||||
nativeBuildInputs = [ extra-cmake-modules cmake file jdk wrapQtAppsHook ];
|
nativeBuildInputs = [ extra-cmake-modules cmake file jdk17 wrapQtAppsHook ];
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
qtbase
|
qtbase
|
||||||
qtsvg
|
qtsvg
|
||||||
zlib
|
zlib
|
||||||
quazip
|
quazip
|
||||||
ghc_filesystem
|
ghc_filesystem
|
||||||
|
tomlplusplus
|
||||||
] ++ lib.optional (lib.versionAtLeast qtbase.version "6") qtwayland;
|
] ++ lib.optional (lib.versionAtLeast qtbase.version "6") qtwayland;
|
||||||
|
|
||||||
cmakeFlags = lib.optionals (msaClientID != "") [ "-DLauncher_MSA_CLIENT_ID=${msaClientID}" ]
|
cmakeFlags = lib.optionals (msaClientID != "") [ "-DLauncher_MSA_CLIENT_ID=${msaClientID}" ]
|
||||||
@ -52,11 +53,6 @@ stdenv.mkDerivation rec {
|
|||||||
ln -s ${libnbtplusplus}/* source/libraries/libnbtplusplus
|
ln -s ${libnbtplusplus}/* source/libraries/libnbtplusplus
|
||||||
chmod -R +r+w source/libraries/libnbtplusplus
|
chmod -R +r+w source/libraries/libnbtplusplus
|
||||||
chown -R $USER: source/libraries/libnbtplusplus
|
chown -R $USER: source/libraries/libnbtplusplus
|
||||||
rm -rf source/libraries/tomlplusplus
|
|
||||||
mkdir source/libraries/tomlplusplus
|
|
||||||
ln -s ${tomlplusplus}/* source/libraries/tomlplusplus
|
|
||||||
chmod -R +r+w source/libraries/tomlplusplus
|
|
||||||
chown -R $USER: source/libraries/tomlplusplus
|
|
||||||
'';
|
'';
|
||||||
|
|
||||||
postInstall =
|
postInstall =
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user