diff options
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}") @@ -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(); @@ -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" } } }, @@ -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* |