aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yml2
-rw-r--r--.github/workflows/winget.yml2
-rw-r--r--CMakeLists.txt19
-rw-r--r--README.md73
-rw-r--r--buildconfig/BuildConfig.cpp.in4
-rw-r--r--flake.lock31
-rw-r--r--flake.nix7
-rw-r--r--launcher/Application.cpp23
-rw-r--r--launcher/ApplicationMessage.cpp8
-rw-r--r--launcher/ApplicationMessage.h4
-rw-r--r--launcher/CMakeLists.txt4
-rw-r--r--launcher/InstanceImportTask.cpp74
-rw-r--r--launcher/icons/IconList.cpp19
-rw-r--r--launcher/icons/IconList.h1
-rw-r--r--launcher/minecraft/mod/ResourceFolderModel.cpp7
-rw-r--r--launcher/minecraft/mod/ResourceFolderModel.h2
-rw-r--r--launcher/modplatform/flame/FlameInstanceCreationTask.cpp4
-rw-r--r--launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp50
-rw-r--r--launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h4
-rw-r--r--launcher/net/NetJob.cpp4
-rw-r--r--launcher/tasks/ConcurrentTask.cpp36
-rw-r--r--launcher/tasks/ConcurrentTask.h9
-rw-r--r--launcher/tasks/MultipleOptionsTask.cpp4
-rw-r--r--launcher/tasks/SequentialTask.cpp4
-rw-r--r--launcher/ui/MainWindow.cpp2
-rw-r--r--launcher/ui/dialogs/IconPickerDialog.cpp7
-rw-r--r--launcher/ui/dialogs/IconPickerDialog.h1
-rw-r--r--launcher/ui/dialogs/ImportResourcePackDialog.cpp1
-rw-r--r--launcher/ui/pages/instance/NotesPage.ui6
-rw-r--r--launcher/ui/pages/instance/OtherLogsPage.ui3
-rw-r--r--launcher/ui/pages/instance/VersionPage.ui3
-rw-r--r--launcher/ui/widgets/ModListView.cpp1
-rw-r--r--launcher/ui/widgets/VariableSizedImageObject.cpp2
-rw-r--r--nix/default.nix14
-rw-r--r--program_info/prismlauncher.6.scd18
35 files changed, 256 insertions, 197 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1ba5d0e4..f415741d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -165,7 +165,7 @@ jobs:
- name: Retrieve ccache cache (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
- uses: actions/cache@v3.0.11
+ uses: actions/cache@v3.2.1
with:
path: '${{ github.workspace }}\.ccache'
key: ${{ matrix.os }}-mingw-w64
diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml
index 5c34040f..ef9561cf 100644
--- a/.github/workflows/winget.yml
+++ b/.github/workflows/winget.yml
@@ -11,5 +11,5 @@ jobs:
with:
identifier: PrismLauncher.PrismLauncher
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 }}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2bdd4811..de9b6fe1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -28,19 +28,28 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_C_STANDARD 11)
include(GenerateExportHeader)
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
- 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
# This implicitly selects an entrypoint specific to the subsystem selected
# qtmain/QtEntryPointLib provides the correct entrypoint (wWinMain) for gui programs
# 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
+ # /LTCG allows for linking wholy optimizated programs
# /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
- 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
# Note, CMake 3.25 replaces this with CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
@@ -129,7 +138,7 @@ set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL th
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
######## Set version numbers ########
-set(Launcher_VERSION_MAJOR 6)
+set(Launcher_VERSION_MAJOR 7)
set(Launcher_VERSION_MINOR 0)
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
diff --git a/README.md b/README.md
index f02b5695..8765da93 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,15 @@
-<p align="left">
+<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="/program_info/org.prismlauncher.PrismLauncher.logo-darkmode.svg">
<source media="(prefers-color-scheme: light)" srcset="/program_info/org.prismlauncher.PrismLauncher.logo.svg">
- <img alt="Prism Launcher" src="/program_info/org.prismlauncher.PrismLauncher.logo.svg" width="50%">
+ <img alt="Prism Launcher" src="/program_info/org.prismlauncher.PrismLauncher.logo.svg" width="40%">
</picture>
</p>
-
-Prism Launcher is a custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.
-
-This is a **fork** of the MultiMC Launcher and is not endorsed by MultiMC.
-
+<p align="center">
+ Prism Launcher is a custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.<br />
+ <br />This is a <b>fork</b> of the MultiMC Launcher and is <b>not</b> endorsed by it.
+</p>
## Installation
@@ -18,7 +17,7 @@ This is a **fork** of the MultiMC Launcher and is not endorsed by MultiMC.
<img src="https://repology.org/badge/vertical-allrepos/prismlauncher.svg" alt="Packaging status" align="right">
</a>
-- All downloads and instructions for Prism Launcher can be found on our [Website](https://prismlauncher.org/download/).
+- All downloads and instructions for Prism Launcher can be found on our [Website](https://prismlauncher.org/download).
- Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions).
### Development Builds
@@ -29,44 +28,33 @@ Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS*
For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**, respectively, you can use these packages for the latest development versions:
-[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?style=flat-square&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git/) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?style=flat-square&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git/) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?style=flat-square&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git) [![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?style=flat-square&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?style=flat-square&logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?style=flat-square&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher)
+[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?label=MPR&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git)<br />[![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?label=CORP&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?label=Gentoo&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher)
+
+These packages are also availiable to all the distributions based on the ones mentioned above.
## Community & Support
-Feel free to create a GitHub issue if you find a bug or want to suggest a new feature. We have multiple community spaces where other community members can help you.
+Feel free to create a GitHub issue if you find a bug or want to suggest a new feature. We have multiple community spaces where other community members can help you:
-#### Join our Discord server:
-[![Prism Launcher Discord server](https://discordapp.com/api/guilds/1031648380885147709/widget.png?style=banner2)](https://discord.gg/prismlauncher)
+- **Our Discord server:**
-#### Join our Matrix space:
-[![PrismLauncher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge&logo=matrix)](https://matrix.to/#/#prismlauncher:matrix.org)
+[![Prism Launcher Discord server](https://discordapp.com/api/guilds/1031648380885147709/widget.png?style=banner3)](https://discord.gg/prismlauncher)
-#### Join our Subreddit:
-[![r/PrismLauncher](https://img.shields.io/reddit/subreddit-subscribers/prismlauncher?style=for-the-badge&logo=reddit)](https://www.reddit.com/r/PrismLauncher/)
+- **Our Matrix space:**
-## Building
+[![PrismLauncher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge&label=Matrix%20Space&logo=matrix&color=purple)](https://matrix.to/#/#prismlauncher:matrix.org)
-If you want to build Prism Launcher yourself, check the [Build Instructions](https://prismlauncher.org/wiki/development/build-instructions/).
+- **Our Subreddit:**
+
+[![r/PrismLauncher](https://img.shields.io/reddit/subreddit-subscribers/prismlauncher?style=for-the-badge&logo=reddit)](https://www.reddit.com/r/PrismLauncher/)
## Translations
The translation effort for PrismLauncher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at <https://github.com/PrismLauncher/Translations>
-## Forking/Redistributing/Custom builds policy
-
-We don't care what you do with your fork/custom build as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy:
-
-- Make it clear that your fork is not PrismLauncher and is not endorsed by or affiliated with the PrismLauncher project (<https://prismlauncher.org>).
-- Go through [CMakeLists.txt](CMakeLists.txt) and change PrismLauncher's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled).
-
-If you have any questions or want any clarification on the above conditions please make an issue and ask us.
-
-Be aware that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions:
-
-- [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
-- [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions)
+## Building
-If you do not agree with these terms and conditions, then remove the associated API keys from the [CMakeLists.txt](CMakeLists.txt) file by setting them to an empty string (`""`).
+If you want to build Prism Launcher yourself, check the [Build Instructions](https://prismlauncher.org/wiki/development/build-instructions/).
## Sponsors & Partners
@@ -84,7 +72,7 @@ Thanks to Weblate for hosting our translation efforts.
<img src="https://hosted.weblate.org/widgets/prismlauncher/-/open-graph.png" alt="Translation status" width="300" />
</a>
-Thanks to Netlify for providing us their excellent web services, as part of their [Open Source program](https://www.netlify.com/open-source/)
+Thanks to Netlify for providing us their excellent web services, as part of their [Open Source program](https://www.netlify.com/open-source/).
<a href="https://www.netlify.com"> <img src="https://www.netlify.com/v3/img/components/netlify-color-accent.svg" alt="Deploys by Netlify" /> </a>
@@ -92,11 +80,24 @@ Thanks to the awesome people over at [MacStadium](https://www.macstadium.com/),
<a href="https://www.macstadium.com"><img src="https://uploads-ssl.webflow.com/5ac3c046c82724970fc60918/5c019d917bba312af7553b49_MacStadium-developerlogo.png" alt="Powered by MacStadium" width="300"></a>
+## Forking/Redistributing/Custom builds policy
-## License
+We don't care what you do with your fork/custom build as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy:
-All launcher code is available under the GPL-3.0-only license.
+- Make it clear that your fork is not PrismLauncher and is not endorsed by or affiliated with the PrismLauncher project (<https://prismlauncher.org>).
+- Go through [CMakeLists.txt](CMakeLists.txt) and change PrismLauncher's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled).
-![https://github.com/PrismLauncher/PrismLauncher/blob/develop/LICENSE](https://img.shields.io/github/license/PrismLauncher/PrismLauncher?style=for-the-badge&logo=gnu&color=C4282D)
+If you have any questions or want any clarification on the above conditions please make an issue and ask us.
+
+Be aware that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions:
+
+- [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
+- [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions)
+
+If you do not agree with these terms and conditions, then remove the associated API keys from the [CMakeLists.txt](CMakeLists.txt) file by setting them to an empty string (`""`).
+
+## License ![https://github.com/PrismLauncher/PrismLauncher/blob/develop/LICENSE](https://img.shields.io/github/license/PrismLauncher/PrismLauncher?label=License&logo=gnu&color=C4282D)
+
+All launcher code is available under the GPL-3.0-only license.
The logo and related assets are under the CC BY-SA 4.0 license.
diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in
index 1262ce8e..35373189 100644
--- a/buildconfig/BuildConfig.cpp.in
+++ b/buildconfig/BuildConfig.cpp.in
@@ -76,7 +76,9 @@ Config::Config()
// Assume that builds outside of Git repos are "stable"
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_TAG = versionString();
diff --git a/flake.lock b/flake.lock
index 7c0bb2f8..051e1664 100644
--- a/flake.lock
+++ b/flake.lock
@@ -3,11 +3,11 @@
"flake-compat": {
"flake": false,
"locked": {
- "lastModified": 1650374568,
- "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=",
+ "lastModified": 1668681692,
+ "narHash": "sha256-Ht91NGdewz8IQLtWZ9LCeNXMSXHUss+9COoqu6JLmXU=",
"owner": "edolstra",
"repo": "flake-compat",
- "rev": "b4a34015c698c7793d592d66adbab377907a2be8",
+ "rev": "009399224d5e398d03b22badca40a37ac85412a1",
"type": "github"
},
"original": {
@@ -34,11 +34,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1666057921,
- "narHash": "sha256-VpQqtXdj6G7cH//SvoprjR7XT3KS7p+tCVebGK1N6tE=",
+ "lastModified": 1671417167,
+ "narHash": "sha256-JkHam6WQOwZN1t2C2sbp1TqMv3TVRjzrdoejqfefwrM=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "88eab1e431cabd0ed621428d8b40d425a07af39f",
+ "rev": "bb31220cca6d044baa6dc2715b07497a2a7c4bc7",
"type": "github"
},
"original": {
@@ -52,24 +52,7 @@
"inputs": {
"flake-compat": "flake-compat",
"libnbtplusplus": "libnbtplusplus",
- "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"
+ "nixpkgs": "nixpkgs"
}
}
},
diff --git a/flake.nix b/flake.nix
index b1e07c91..5615a758 100644
--- a/flake.nix
+++ b/flake.nix
@@ -5,10 +5,9 @@
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
flake-compat = { url = "github:edolstra/flake-compat"; 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
# User-friendly version number.
version = builtins.substring 0 8 self.lastModifiedDate;
@@ -23,8 +22,8 @@
pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system});
packagesFn = pkgs: rec {
- prismlauncher-qt5 = pkgs.libsForQt5.callPackage ./nix { inherit version self libnbtplusplus tomlplusplus; };
- prismlauncher = pkgs.qt6Packages.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; };
};
in
{
diff --git a/launcher/Application.cpp b/launcher/Application.cpp
index 3f313ee4..ff34a168 100644
--- a/launcher/Application.cpp
+++ b/launcher/Application.cpp
@@ -146,19 +146,12 @@ static const QLatin1String liveCheckFile("live.check");
PixmapCache* PixmapCache::s_instance = nullptr;
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)
{
- const char *levels = "DWCFIS";
- const QString format("%1 %2 %3\n");
-
- 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);
+ QString out = qFormatLogMessage(type, context, msg);
+ out += QChar::LineFeed;
APPLICATION->logFile->write(out.toUtf8());
APPLICATION->logFile->flush();
@@ -431,6 +424,14 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
return;
}
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.";
}
diff --git a/launcher/ApplicationMessage.cpp b/launcher/ApplicationMessage.cpp
index ca276b89..700e43ce 100644
--- a/launcher/ApplicationMessage.cpp
+++ b/launcher/ApplicationMessage.cpp
@@ -47,8 +47,8 @@ void ApplicationMessage::parse(const QByteArray & input) {
args.clear();
auto parsedArgs = root.value("args").toObject();
- for(auto iter = parsedArgs.begin(); iter != parsedArgs.end(); iter++) {
- args[iter.key()] = iter.value().toString();
+ for(auto iter = parsedArgs.constBegin(); iter != parsedArgs.constEnd(); iter++) {
+ args.insert(iter.key(), iter.value().toString());
}
}
@@ -56,8 +56,8 @@ QByteArray ApplicationMessage::serialize() {
QJsonObject root;
root.insert("command", command);
QJsonObject outArgs;
- for (auto iter = args.begin(); iter != args.end(); iter++) {
- outArgs[iter.key()] = iter.value();
+ for (auto iter = args.constBegin(); iter != args.constEnd(); iter++) {
+ outArgs.insert(iter.key(), iter.value());
}
root.insert("args", outArgs);
diff --git a/launcher/ApplicationMessage.h b/launcher/ApplicationMessage.h
index 745bdead..d66456eb 100644
--- a/launcher/ApplicationMessage.h
+++ b/launcher/ApplicationMessage.h
@@ -1,12 +1,12 @@
#pragma once
#include <QString>
-#include <QMap>
+#include <QHash>
#include <QByteArray>
struct ApplicationMessage {
QString command;
- QMap<QString, QString> args;
+ QHash<QString, QString> args;
QByteArray serialize();
void parse(const QByteArray & input);
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index 439feb44..a0d92b6e 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -1166,6 +1166,8 @@ if(INSTALL_BUNDLE STREQUAL "full")
CONFIGURATIONS Debug RelWithDebInfo ""
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
+ PATTERN "*qopensslbackend*" EXCLUDE
+ PATTERN "*qcertonlybackend*" EXCLUDE
)
install(
DIRECTORY "${QT_PLUGINS_DIR}/tls"
@@ -1175,6 +1177,8 @@ if(INSTALL_BUNDLE STREQUAL "full")
REGEX "dd\\." EXCLUDE
REGEX "_debug\\." EXCLUDE
REGEX "\\.dSYM" EXCLUDE
+ PATTERN "*qopensslbackend*" EXCLUDE
+ PATTERN "*qcertonlybackend*" EXCLUDE
)
endif()
configure_file(
diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp
index b97870da..6b3fd296 100644
--- a/launcher/InstanceImportTask.cpp
+++ b/launcher/InstanceImportTask.cpp
@@ -257,20 +257,26 @@ void InstanceImportTask::extractAborted()
void InstanceImportTask::processFlame()
{
- auto pack_id_it = m_extra_info.constFind("pack_id");
- Q_ASSERT(pack_id_it != m_extra_info.constEnd());
- auto pack_id = pack_id_it.value();
-
- auto pack_version_id_it = m_extra_info.constFind("pack_version_id");
- Q_ASSERT(pack_version_id_it != m_extra_info.constEnd());
- auto pack_version_id = pack_version_id_it.value();
-
- QString original_instance_id;
- auto original_instance_id_it = m_extra_info.constFind("original_instance_id");
- if (original_instance_id_it != m_extra_info.constEnd())
- 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);
+ FlameCreationTask* inst_creation_task = nullptr;
+ if (!m_extra_info.isEmpty()) {
+ auto pack_id_it = m_extra_info.constFind("pack_id");
+ Q_ASSERT(pack_id_it != m_extra_info.constEnd());
+ auto pack_id = pack_id_it.value();
+
+ auto pack_version_id_it = m_extra_info.constFind("pack_version_id");
+ Q_ASSERT(pack_version_id_it != m_extra_info.constEnd());
+ auto pack_version_id = pack_version_id_it.value();
+
+ QString original_instance_id;
+ auto original_instance_id_it = m_extra_info.constFind("original_instance_id");
+ if (original_instance_id_it != m_extra_info.constEnd())
+ original_instance_id = original_instance_id_it.value();
+
+ inst_creation_task = new FlameCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
+ } 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->setIcon(m_instIcon);
@@ -335,21 +341,33 @@ void InstanceImportTask::processMultiMC()
void InstanceImportTask::processModrinth()
{
- auto pack_id_it = m_extra_info.constFind("pack_id");
- Q_ASSERT(pack_id_it != m_extra_info.constEnd());
- auto pack_id = pack_id_it.value();
-
- QString pack_version_id;
- auto pack_version_id_it = m_extra_info.constFind("pack_version_id");
- if (pack_version_id_it != m_extra_info.constEnd())
- pack_version_id = pack_version_id_it.value();
-
- QString original_instance_id;
- auto original_instance_id_it = m_extra_info.constFind("original_instance_id");
- if (original_instance_id_it != m_extra_info.constEnd())
- original_instance_id = original_instance_id_it.value();
+ ModrinthCreationTask* inst_creation_task = nullptr;
+ if (!m_extra_info.isEmpty()) {
+ auto pack_id_it = m_extra_info.constFind("pack_id");
+ Q_ASSERT(pack_id_it != m_extra_info.constEnd());
+ auto pack_id = pack_id_it.value();
+
+ QString pack_version_id;
+ auto pack_version_id_it = m_extra_info.constFind("pack_version_id");
+ if (pack_version_id_it != m_extra_info.constEnd())
+ pack_version_id = pack_version_id_it.value();
+
+ QString original_instance_id;
+ auto original_instance_id_it = m_extra_info.constFind("original_instance_id");
+ if (original_instance_id_it != m_extra_info.constEnd())
+ original_instance_id = original_instance_id_it.value();
+
+ 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);
+ }
- auto* inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
+ // 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->setIcon(m_instIcon);
diff --git a/launcher/icons/IconList.cpp b/launcher/icons/IconList.cpp
index 01043ad2..1dfc6432 100644
--- a/launcher/icons/IconList.cpp
+++ b/launcher/icons/IconList.cpp
@@ -354,15 +354,18 @@ const MMCIcon *IconList::icon(const QString &key) const
bool IconList::deleteIcon(const QString &key)
{
- int iconIdx = getIconIndex(key);
- if (iconIdx == -1)
+ if (!iconFileExists(key))
return false;
- auto &iconEntry = icons[iconIdx];
- if (iconEntry.has(IconType::FileBased))
- {
- return QFile::remove(iconEntry.m_images[IconType::FileBased].filename);
- }
- return false;
+
+ return QFile::remove(icon(key)->getFilePath());
+}
+
+bool IconList::trashIcon(const QString &key)
+{
+ if (!iconFileExists(key))
+ return false;
+
+ return FS::trash(icon(key)->getFilePath(), nullptr);
}
bool IconList::addThemeIcon(const QString& key)
diff --git a/launcher/icons/IconList.h b/launcher/icons/IconList.h
index f9f49e7f..97141e4a 100644
--- a/launcher/icons/IconList.h
+++ b/launcher/icons/IconList.h
@@ -52,6 +52,7 @@ public:
bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type);
void saveIcon(const QString &key, const QString &path, const char * format) const;
bool deleteIcon(const QString &key);
+ bool trashIcon(const QString &key);
bool iconFileExists(const QString &key) const;
void installIcons(const QStringList &iconFiles);
diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp
index 0310c8f6..a52c5db3 100644
--- a/launcher/minecraft/mod/ResourceFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourceFolderModel.cpp
@@ -20,6 +20,7 @@ ResourceFolderModel::ResourceFolderModel(QDir dir, QObject* parent) : QAbstractL
m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &ResourceFolderModel::directoryChanged);
+ connect(&m_helper_thread_task, &ConcurrentTask::finished, this, [this]{ m_helper_thread_task.clear(); });
}
ResourceFolderModel::~ResourceFolderModel()
@@ -275,7 +276,11 @@ void ResourceFolderModel::resolveResource(Resource* res)
connect(
task, &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()
diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h
index fe283b04..f1bc2dd7 100644
--- a/launcher/minecraft/mod/ResourceFolderModel.h
+++ b/launcher/minecraft/mod/ResourceFolderModel.h
@@ -10,6 +10,7 @@
#include "Resource.h"
#include "tasks/Task.h"
+#include "tasks/ConcurrentTask.h"
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.
QMap<QString, int> m_resources_index;
+ ConcurrentTask m_helper_thread_task;
QMap<int, Task::Ptr> m_active_parse_tasks;
std::atomic<int> m_next_resolution_ticket = 0;
};
diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
index 729268d7..1d441f09 100644
--- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
+++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
@@ -361,7 +361,9 @@ bool FlameCreationTask::createInstance()
FS::deletePath(jarmodsPath);
}
- instance.setManagedPack("flame", m_managed_id, m_pack.name, m_managed_version_id, m_pack.version);
+ // 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.setName(name());
m_mod_id_resolver = new Flame::FileResolvingTask(APPLICATION->network(), m_pack);
diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp
index 1c0e8979..c5a27c9d 100644
--- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp
+++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp
@@ -202,14 +202,14 @@ bool ModrinthCreationTask::createInstance()
auto components = instance.getPackProfile();
components->buildingFromScratch();
- components->setComponentVersion("net.minecraft", minecraftVersion, true);
+ components->setComponentVersion("net.minecraft", m_minecraft_version, true);
- if (!fabricVersion.isEmpty())
- components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion);
- if (!quiltVersion.isEmpty())
- components->setComponentVersion("org.quiltmc.quilt-loader", quiltVersion);
- if (!forgeVersion.isEmpty())
- components->setComponentVersion("net.minecraftforge", forgeVersion);
+ if (!m_fabric_version.isEmpty())
+ components->setComponentVersion("net.fabricmc.fabric-loader", m_fabric_version);
+ if (!m_quilt_version.isEmpty())
+ components->setComponentVersion("org.quiltmc.quilt-loader", m_quilt_version);
+ if (!m_forge_version.isEmpty())
+ components->setComponentVersion("net.minecraftforge", m_forge_version);
if (m_instIcon != "default") {
instance.setIconKey(m_instIcon);
@@ -217,7 +217,9 @@ bool ModrinthCreationTask::createInstance()
instance.setIconKey("modrinth");
}
- instance.setManagedPack("modrinth", m_managed_id, m_managed_name, m_managed_version_id, version());
+ // 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.setName(name());
instance.saveNow();
@@ -277,7 +279,7 @@ bool ModrinthCreationTask::createInstance()
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 {
auto doc = Json::requireDocument(index_path);
@@ -289,7 +291,7 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, std::vector<
throw JSONValidationError("Unknown game: " + game);
}
- if (set_managed_info) {
+ if (set_internal_data) {
if (m_managed_version_id.isEmpty())
m_managed_version_id = Json::ensureString(obj, "versionId", {}, "Managed ID");
m_managed_name = Json::ensureString(obj, "name", {}, "Managed Name");
@@ -365,19 +367,21 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, std::vector<
files.push_back(file);
}
- auto dependencies = Json::requireObject(obj, "dependencies", "modrinth.index.json");
- for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) {
- QString name = it.key();
- if (name == "minecraft") {
- minecraftVersion = Json::requireString(*it, "Minecraft version");
- } else if (name == "fabric-loader") {
- fabricVersion = Json::requireString(*it, "Fabric Loader version");
- } else if (name == "quilt-loader") {
- quiltVersion = Json::requireString(*it, "Quilt Loader version");
- } else if (name == "forge") {
- forgeVersion = Json::requireString(*it, "Forge version");
- } else {
- throw JSONValidationError("Unknown dependency type: " + name);
+ if (set_internal_data) {
+ auto dependencies = Json::requireObject(obj, "dependencies", "modrinth.index.json");
+ for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) {
+ QString name = it.key();
+ if (name == "minecraft") {
+ m_minecraft_version = Json::requireString(*it, "Minecraft version");
+ } else if (name == "fabric-loader") {
+ m_fabric_version = Json::requireString(*it, "Fabric Loader version");
+ } else if (name == "quilt-loader") {
+ m_quilt_version = Json::requireString(*it, "Quilt Loader version");
+ } else if (name == "forge") {
+ m_forge_version = Json::requireString(*it, "Forge version");
+ } else {
+ throw JSONValidationError("Unknown dependency type: " + name);
+ }
}
}
} else {
diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h
index 122fc5ce..6de24fd4 100644
--- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h
+++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h
@@ -37,12 +37,12 @@ class ModrinthCreationTask final : public InstanceCreationTask {
bool createInstance() override;
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:
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;
std::vector<Modrinth::File> m_files;
diff --git a/launcher/net/NetJob.cpp b/launcher/net/NetJob.cpp
index 8ced1b7e..9b5d4f1b 100644
--- a/launcher/net/NetJob.cpp
+++ b/launcher/net/NetJob.cpp
@@ -123,7 +123,7 @@ auto NetJob::getFailedFiles() -> QList<QString>
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)")
- .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())));
}
diff --git a/launcher/tasks/ConcurrentTask.cpp b/launcher/tasks/ConcurrentTask.cpp
index ce08a6a2..a890013e 100644
--- a/launcher/tasks/ConcurrentTask.cpp
+++ b/launcher/tasks/ConcurrentTask.cpp
@@ -27,18 +27,13 @@ auto ConcurrentTask::getStepTotalProgress() const -> qint64
void ConcurrentTask::addTask(Task::Ptr task)
{
- if (!isRunning())
- m_queue.append(task);
- else
- qWarning() << "Tried to add a task to a running concurrent task!";
+ m_queue.append(task);
}
void ConcurrentTask::executeTask()
{
- m_total_size = m_queue.size();
-
// 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++) {
QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection);
}
@@ -73,6 +68,20 @@ bool ConcurrentTask::abort()
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()
{
if (m_aborted || m_doing.count() > m_total_max_size)
@@ -101,9 +110,14 @@ void ConcurrentTask::startNext()
setStepStatus(next->isMultiStep() ? next->getStepStatus() : next->getStatus());
updateState();
- QCoreApplication::processEvents();
+ QMetaObject::invokeMethod(next.get(), &Task::start, Qt::QueuedConnection);
- next->start();
+ // Allow going up the number of concurrent tasks in case of tasks being added in the middle of a running task.
+ int num_starts = m_total_max_size - m_doing.size();
+ for (int i = 0; i < num_starts; i++)
+ QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection);
+
+ QCoreApplication::processEvents();
}
void ConcurrentTask::subTaskSucceeded(Task::Ptr task)
@@ -145,7 +159,7 @@ void ConcurrentTask::subTaskProgress(qint64 current, qint64 total)
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)")
- .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())));
}
diff --git a/launcher/tasks/ConcurrentTask.h b/launcher/tasks/ConcurrentTask.h
index f1279d32..b46919fb 100644
--- a/launcher/tasks/ConcurrentTask.h
+++ b/launcher/tasks/ConcurrentTask.h
@@ -24,6 +24,11 @@ public:
public slots:
bool abort() override;
+ /** Resets the internal state of the task.
+ * This allows the same task to be re-used.
+ */
+ void clear();
+
protected
slots:
void executeTask() override;
@@ -36,6 +41,9 @@ slots:
void subTaskProgress(qint64 current, qint64 total);
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); };
virtual void updateState();
@@ -51,7 +59,6 @@ protected:
QHash<Task*, Task::Ptr> m_failed;
int m_total_max_size;
- int m_total_size;
qint64 m_stepProgress = 0;
qint64 m_stepTotalProgress = 100;
diff --git a/launcher/tasks/MultipleOptionsTask.cpp b/launcher/tasks/MultipleOptionsTask.cpp
index 5ad6181f..034499df 100644
--- a/launcher/tasks/MultipleOptionsTask.cpp
+++ b/launcher/tasks/MultipleOptionsTask.cpp
@@ -22,6 +22,6 @@ void MultipleOptionsTask::startNext()
void MultipleOptionsTask::updateState()
{
- setProgress(m_done.count(), m_total_size);
- setStatus(tr("Attempting task %1 out of %2").arg(QString::number(m_doing.count() + m_done.count()), QString::number(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(totalSize())));
}
diff --git a/launcher/tasks/SequentialTask.cpp b/launcher/tasks/SequentialTask.cpp
index a34137cb..b2f86328 100644
--- a/launcher/tasks/SequentialTask.cpp
+++ b/launcher/tasks/SequentialTask.cpp
@@ -17,6 +17,6 @@ void SequentialTask::startNext()
void SequentialTask::updateState()
{
- setProgress(m_done.count(), m_total_size);
- setStatus(tr("Executing task %1 out of %2").arg(QString::number(m_doing.count() + m_done.count()), QString::number(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(totalSize())));
}
diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp
index c8a1fddc..e913849d 100644
--- a/launcher/ui/MainWindow.cpp
+++ b/launcher/ui/MainWindow.cpp
@@ -1684,7 +1684,7 @@ InstanceView
background-image: url(:/backgrounds/%1);
background-attachment: fixed;
background-clip: padding;
- background-position: bottom left;
+ background-position: bottom right;
background-repeat: none;
background-color:palette(base);
})")
diff --git a/launcher/ui/dialogs/IconPickerDialog.cpp b/launcher/ui/dialogs/IconPickerDialog.cpp
index fcb645db..5131686a 100644
--- a/launcher/ui/dialogs/IconPickerDialog.cpp
+++ b/launcher/ui/dialogs/IconPickerDialog.cpp
@@ -47,7 +47,6 @@ IconPickerDialog::IconPickerDialog(QWidget *parent)
contentsWidget->setUniformItemSizes(true);
contentsWidget->setTextElideMode(Qt::ElideRight);
contentsWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
- contentsWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
contentsWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
contentsWidget->setItemDelegate(new ListViewDelegate());
@@ -63,7 +62,7 @@ IconPickerDialog::IconPickerDialog(QWidget *parent)
// NOTE: ResetRole forces the button to be on the left, while the OK/Cancel ones are on the right. We win.
auto buttonAdd = ui->buttonBox->addButton(tr("Add Icon"), QDialogButtonBox::ResetRole);
- auto buttonRemove = ui->buttonBox->addButton(tr("Remove Icon"), QDialogButtonBox::ResetRole);
+ buttonRemove = ui->buttonBox->addButton(tr("Remove Icon"), QDialogButtonBox::ResetRole);
connect(buttonAdd, SIGNAL(clicked(bool)), SLOT(addNewIcon()));
connect(buttonRemove, SIGNAL(clicked(bool)), SLOT(removeSelectedIcon()));
@@ -111,6 +110,9 @@ void IconPickerDialog::addNewIcon()
void IconPickerDialog::removeSelectedIcon()
{
+ if (APPLICATION->icons()->trashIcon(selectedIconKey))
+ return;
+
APPLICATION->icons()->deleteIcon(selectedIconKey);
}
@@ -129,6 +131,7 @@ void IconPickerDialog::selectionChanged(QItemSelection selected, QItemSelection
if (!key.isEmpty()) {
selectedIconKey = key;
}
+ buttonRemove->setEnabled(APPLICATION->icons()->iconFileExists(selectedIconKey));
}
int IconPickerDialog::execWithSelection(QString selection)
diff --git a/launcher/ui/dialogs/IconPickerDialog.h b/launcher/ui/dialogs/IconPickerDialog.h
index 9af6a678..c93f565f 100644
--- a/launcher/ui/dialogs/IconPickerDialog.h
+++ b/launcher/ui/dialogs/IconPickerDialog.h
@@ -37,6 +37,7 @@ protected:
private:
Ui::IconPickerDialog *ui;
+ QPushButton *buttonRemove;
private
slots:
diff --git a/launcher/ui/dialogs/ImportResourcePackDialog.cpp b/launcher/ui/dialogs/ImportResourcePackDialog.cpp
index e807e926..e8902656 100644
--- a/launcher/ui/dialogs/ImportResourcePackDialog.cpp
+++ b/launcher/ui/dialogs/ImportResourcePackDialog.cpp
@@ -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.
contentsWidget->setUniformItemSizes(false);
contentsWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
- contentsWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
contentsWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
contentsWidget->setItemDelegate(new ListViewDelegate());
diff --git a/launcher/ui/pages/instance/NotesPage.ui b/launcher/ui/pages/instance/NotesPage.ui
index 67cb261c..4b506da7 100644
--- a/launcher/ui/pages/instance/NotesPage.ui
+++ b/launcher/ui/pages/instance/NotesPage.ui
@@ -17,17 +17,11 @@
<property name="topMargin">
<number>0</number>
</property>
- <property name="rightMargin">
- <number>0</number>
- </property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTextEdit" name="noteEditor">
- <property name="verticalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOn</enum>
- </property>
<property name="tabChangesFocus">
<bool>true</bool>
</property>
diff --git a/launcher/ui/pages/instance/OtherLogsPage.ui b/launcher/ui/pages/instance/OtherLogsPage.ui
index 77f3e647..3fdb023f 100644
--- a/launcher/ui/pages/instance/OtherLogsPage.ui
+++ b/launcher/ui/pages/instance/OtherLogsPage.ui
@@ -48,9 +48,6 @@
<property name="enabled">
<bool>false</bool>
</property>
- <property name="verticalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOn</enum>
- </property>
<property name="readOnly">
<bool>true</bool>
</property>
diff --git a/launcher/ui/pages/instance/VersionPage.ui b/launcher/ui/pages/instance/VersionPage.ui
index 74b9568a..4cd50885 100644
--- a/launcher/ui/pages/instance/VersionPage.ui
+++ b/launcher/ui/pages/instance/VersionPage.ui
@@ -28,9 +28,6 @@
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="ModListView" name="packageView">
- <property name="verticalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOn</enum>
- </property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
diff --git a/launcher/ui/widgets/ModListView.cpp b/launcher/ui/widgets/ModListView.cpp
index c8ccd292..d1860f57 100644
--- a/launcher/ui/widgets/ModListView.cpp
+++ b/launcher/ui/widgets/ModListView.cpp
@@ -31,7 +31,6 @@ ModListView::ModListView ( QWidget* parent )
setSelectionMode ( QAbstractItemView::ExtendedSelection );
setHeaderHidden ( false );
setSelectionBehavior(QAbstractItemView::SelectRows);
- setVerticalScrollBarPolicy ( Qt::ScrollBarAlwaysOn );
setHorizontalScrollBarPolicy ( Qt::ScrollBarAsNeeded );
setDropIndicatorShown(true);
setDragEnabled(true);
diff --git a/launcher/ui/widgets/VariableSizedImageObject.cpp b/launcher/ui/widgets/VariableSizedImageObject.cpp
index e57f7e95..991b4a04 100644
--- a/launcher/ui/widgets/VariableSizedImageObject.cpp
+++ b/launcher/ui/widgets/VariableSizedImageObject.cpp
@@ -101,7 +101,7 @@ void VariableSizedImageObject::loadImage(QTextDocument* doc, const QUrl& source,
auto full_entry_path = entry->getFullPath();
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;
// If we flushed, don't proceed.
diff --git a/nix/default.nix b/nix/default.nix
index 6050fd37..82ba9c7d 100644
--- a/nix/default.nix
+++ b/nix/default.nix
@@ -2,7 +2,7 @@
, stdenv
, cmake
, jdk8
-, jdk
+, jdk17
, zlib
, file
, wrapQtAppsHook
@@ -16,15 +16,15 @@
, glfw
, openal
, extra-cmake-modules
+, tomlplusplus
, ghc_filesystem
, msaClientID ? ""
-, jdks ? [ jdk jdk8 ]
+, jdks ? [ jdk17 jdk8 ]
# flake
, self
, version
, libnbtplusplus
-, tomlplusplus
}:
stdenv.mkDerivation rec {
@@ -33,13 +33,14 @@ stdenv.mkDerivation rec {
src = lib.cleanSource self;
- nativeBuildInputs = [ extra-cmake-modules cmake file jdk wrapQtAppsHook ];
+ nativeBuildInputs = [ extra-cmake-modules cmake file jdk17 wrapQtAppsHook ];
buildInputs = [
qtbase
qtsvg
zlib
quazip
ghc_filesystem
+ tomlplusplus
] ++ lib.optional (lib.versionAtLeast qtbase.version "6") qtwayland;
cmakeFlags = lib.optionals (msaClientID != "") [ "-DLauncher_MSA_CLIENT_ID=${msaClientID}" ]
@@ -52,11 +53,6 @@ stdenv.mkDerivation rec {
ln -s ${libnbtplusplus}/* source/libraries/libnbtplusplus
chmod -R +r+w 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 =
diff --git a/program_info/prismlauncher.6.scd b/program_info/prismlauncher.6.scd
index f979e457..e1ebfff3 100644
--- a/program_info/prismlauncher.6.scd
+++ b/program_info/prismlauncher.6.scd
@@ -41,6 +41,24 @@ Here are the current features of Prism Launcher.
*-a, --profile*=PROFILE
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
*0*