diff --git a/.clang-tidy b/.clang-tidy index 11ddc9cfc..436dcf244 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,4 +1,5 @@ Checks: - modernize-use-using + - readability-avoid-const-params-in-decls SystemHeaders: false diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index f9cf4fb7d..e5443439d 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -24,7 +24,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha }} - name: Create backport PRs - uses: korthout/backport-action@v2.0.0 + uses: korthout/backport-action@v2.1.1 with: # Config README: https://github.com/korthout/backport-action#backport-action pull_description: |- diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8077ea59a..4fb2d6794 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -594,7 +594,7 @@ jobs: flatpak: runs-on: ubuntu-latest container: - image: bilelmoussaoui/flatpak-github-actions:kde-5.15-22.08 + image: bilelmoussaoui/flatpak-github-actions:kde-5.15-23.08 options: --privileged steps: - name: Checkout diff --git a/README.md b/README.md index 61bf4c20b..b32132d49 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,18 @@ - 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). +- Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) tab (this also includes the pull requests status). ### Development Builds -There are development builds available [here](https://github.com/PrismLauncher/PrismLauncher/actions). These have debug information in the binaries, so their file sizes are relatively larger. +Please understand that these builds are not intended for most users. There may be bugs, and other instabilities. You have been warned. + +There are development builds available through: + +- [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) (includes builds from pull requests opened by contribuitors) +- [nightly.link](https://nightly.link/PrismLauncher/PrismLauncher/workflows/trigger_builds/develop) (this will always point only to the latest version of develop) + +These have debug information in the binaries, so their file sizes are relatively larger. Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**. diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake index c9962c443..51d2fb13a 100644 --- a/cmake/CompilerWarnings.cmake +++ b/cmake/CompilerWarnings.cmake @@ -68,6 +68,8 @@ function( /w14906 # string literal cast to 'LPWSTR' /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied /permissive- # standards conformance mode for MSVC compiler. + + /we4062 # forbid omitting a possible value of an enum in a switch statement ) endif() @@ -93,6 +95,8 @@ function( # in a lot of noise. This warning is only notifying us that clang is emulating the GCC behaviour # instead of the exact standard wording so we can safely ignore it -Wno-gnu-zero-variadic-macro-arguments + + -Werror=switch # forbid omitting a possible value of an enum in a switch statement ) endif() @@ -104,6 +108,8 @@ function( -Wduplicated-branches # warn if if / else branches have duplicated code -Wlogical-op # warn about logical operations being used where bitwise were probably wanted -Wuseless-cast # warn if you perform a cast to the same type + + -Werror=switch # forbid omitting a possible value of an enum in a switch statement ) endif() @@ -128,6 +134,8 @@ function( -Woverloaded-virtual -Wuseless-cast -Wextra-semi + + -Werror=switch # forbid omitting a possible value of an enum in a switch statement ) target_compile_options( diff --git a/flake.lock b/flake.lock index 7cf901f77..3422af0ac 100644 --- a/flake.lock +++ b/flake.lock @@ -21,11 +21,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1696343447, - "narHash": "sha256-B2xAZKLkkeRFG5XcHHSXXcP7To9Xzr59KXeZiRf4vdQ=", + "lastModified": 1698882062, + "narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "c9afaba3dfa4085dbd2ccb38dfade5141e33d9d4", + "rev": "8c9fa2545007b49a5db5f650ae91f227672c3877", "type": "github" }, "original": { @@ -76,11 +76,11 @@ "libnbtplusplus": { "flake": false, "locked": { - "lastModified": 1690036783, - "narHash": "sha256-A5kTgICnx+Qdq3Fir/bKTfdTt/T1NQP2SC+nhN1ENug=", + "lastModified": 1699286814, + "narHash": "sha256-yy0q+bky80LtK1GWzz7qpM+aAGrOqLuewbid8WT1ilk=", "owner": "PrismLauncher", "repo": "libnbtplusplus", - "rev": "a5e8fd52b8bf4ab5d5bcc042b2a247867589985f", + "rev": "23b955121b8217c1c348a9ed2483167a6f3ff4ad", "type": "github" }, "original": { @@ -106,11 +106,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1698336494, - "narHash": "sha256-sO72WDBKyijYD1GcKPlGsycKbMBiTJMBCnmOxLAs880=", + "lastModified": 1700108881, + "narHash": "sha256-+Lqybl8kj0+nD/IlAWPPG/RDTa47gff9nbei0u7BntE=", "owner": "nixos", "repo": "nixpkgs", - "rev": "808c0d8c53c7ae50f82aca8e7df263225cf235bf", + "rev": "7414e9ee0b3e9903c24d3379f577a417f0aae5f1", "type": "github" }, "original": { @@ -123,11 +123,11 @@ "nixpkgs-lib": { "locked": { "dir": "lib", - "lastModified": 1696019113, - "narHash": "sha256-X3+DKYWJm93DRSdC5M6K5hLqzSya9BjibtBsuARoPco=", + "lastModified": 1698611440, + "narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "f5892ddac112a1e9b3612c39af1b72987ee5783a", + "rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735", "type": "github" }, "original": { @@ -153,11 +153,11 @@ ] }, "locked": { - "lastModified": 1698227354, - "narHash": "sha256-Fi5H9jbaQLmLw9qBi/mkR33CoFjNbobo5xWdX4tKz1Q=", + "lastModified": 1700064067, + "narHash": "sha256-1ZWNDzhu8UlVCK7+DUN9dVQfiHX1bv6OQP9VxstY/gs=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "bd38df3d508dfcdff52cd243d297f218ed2257bf", + "rev": "e558068cba67b23b4fbc5537173dbb43748a17e8", "type": "github" }, "original": { diff --git a/flatpak/org.prismlauncher.PrismLauncher.yml b/flatpak/org.prismlauncher.PrismLauncher.yml index 46b6da36a..89727751e 100644 --- a/flatpak/org.prismlauncher.PrismLauncher.yml +++ b/flatpak/org.prismlauncher.PrismLauncher.yml @@ -1,6 +1,6 @@ id: org.prismlauncher.PrismLauncher runtime: org.kde.Platform -runtime-version: "5.15-22.08" +runtime-version: "5.15-23.08" sdk: org.kde.Sdk sdk-extensions: - org.freedesktop.Sdk.Extension.openjdk17 @@ -113,6 +113,9 @@ modules: version-query: .tag_name url-query: .tarball_url timestamp-query: .published_at + # from https://github.com/flathub/net.gaijin.WarThunder/blob/7ea6f7a9f84b9c77150c003a7059dc03f8dcbc7f/gamemode.patch + - type: patch + path: patches/gamemode.patch cleanup: - /include - /lib/pkgconfig diff --git a/flatpak/patches/gamemode.patch b/flatpak/patches/gamemode.patch new file mode 100644 index 000000000..3cc0d7412 --- /dev/null +++ b/flatpak/patches/gamemode.patch @@ -0,0 +1,12 @@ +diff -ruN a/common/common-pidfds.c b/common/common-pidfds.c +--- a/common/common-pidfds.c 2021-02-18 20:00:12.000000000 +0100 ++++ b/common/common-pidfds.c 2023-09-07 08:57:42.954362763 +0200 +@@ -58,6 +58,8 @@ + { + return (int)syscall(__NR_pidfd_open, pid, flags); + } ++#else ++#include + #endif + + /* pidfd functions */ diff --git a/garnix.yaml b/garnix.yaml index 3bf145248..6cf8f7214 100644 --- a/garnix.yaml +++ b/garnix.yaml @@ -1,5 +1,6 @@ builds: - exclude: [] + exclude: + - "*.x86_64-darwin.*" include: - "checks.x86_64-linux.*" - "devShells.*.*" diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 661c6c5be..be252f1c5 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -644,6 +644,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) // Minecraft mods m_settings->registerSetting("ModMetadataDisabled", false); + m_settings->registerSetting("ModDependenciesDisabled", false); // Minecraft offline player name m_settings->registerSetting("LastOfflinePlayerName", ""); diff --git a/launcher/DataMigrationTask.h b/launcher/DataMigrationTask.h index 6cc23b1a8..aba9f2399 100644 --- a/launcher/DataMigrationTask.h +++ b/launcher/DataMigrationTask.h @@ -18,7 +18,7 @@ class DataMigrationTask : public Task { Q_OBJECT public: - explicit DataMigrationTask(QObject* parent, const QString& sourcePath, const QString& targetPath, const IPathMatcher::Ptr pathmatcher); + explicit DataMigrationTask(QObject* parent, const QString& sourcePath, const QString& targetPath, IPathMatcher::Ptr pathmatcher); ~DataMigrationTask() override = default; protected: diff --git a/launcher/DesktopServices.cpp b/launcher/DesktopServices.cpp index 004e5e085..17eb7c2df 100644 --- a/launcher/DesktopServices.cpp +++ b/launcher/DesktopServices.cpp @@ -108,12 +108,9 @@ bool openDirectory(const QString& path, [[maybe_unused]] bool ensureExists) #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) if (!isSandbox()) { return IndirectOpen(f); - } else { - return f(); } -#else - return f(); #endif + return f(); } bool openFile(const QString& path) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 5bb10b069..d0b25e957 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -957,6 +957,8 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri << "\n"; stream << "Type=Application" << "\n"; + stream << "Categories=Game;ActionGame;AdventureGame;Simulation" + << "\n"; stream << "Exec=\"" << target.toLocal8Bit() << "\"" << argstring.toLocal8Bit() << "\n"; stream << "Name=" << name.toLocal8Bit() << "\n"; if (!icon.isEmpty()) { diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 21ad834b4..3a23e6a03 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -60,7 +60,7 @@ #include -InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent, QMap&& extra_info) +InstanceImportTask::InstanceImportTask(const QUrl& sourceUrl, QWidget* parent, QMap&& extra_info) : m_sourceUrl(sourceUrl), m_extra_info(extra_info), m_parent(parent) {} diff --git a/launcher/InstanceImportTask.h b/launcher/InstanceImportTask.h index d6cda7d0d..075f57a73 100644 --- a/launcher/InstanceImportTask.h +++ b/launcher/InstanceImportTask.h @@ -51,7 +51,7 @@ class FileResolvingTask; class InstanceImportTask : public InstanceTask { Q_OBJECT public: - explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr, QMap&& extra_info = {}); + explicit InstanceImportTask(const QUrl& sourceUrl, QWidget* parent = nullptr, QMap&& extra_info = {}); bool abort() override; diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index 8f0cbba79..0db487c48 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -823,6 +823,9 @@ void InstanceList::on_InstFolderChanged([[maybe_unused]] const Setting& setting, } m_instDir = newInstDir; m_groupsLoaded = false; + beginRemoveRows(QModelIndex(), 0, count()); + m_instances.erase(m_instances.begin(), m_instances.end()); + endRemoveRows(); emit instancesChanged(); } } diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index 9fb385777..8a8fa00a8 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -36,6 +36,7 @@ #include "LaunchController.h" #include "Application.h" +#include "minecraft/auth/AccountData.h" #include "minecraft/auth/AccountList.h" #include "ui/InstanceWindow.h" @@ -89,7 +90,7 @@ void LaunchController::decideAccount() // Tell the user they need to log in at least one account in order to play. auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"), tr("In order to play Minecraft, you must have at least one Microsoft " - "account which owns Minecraft logged in." + "account which owns Minecraft logged in. " "Would you like to open the account manager to add an account now?"), QMessageBox::Information, QMessageBox::Yes | QMessageBox::No) ->exec(); @@ -161,7 +162,7 @@ void LaunchController::login() m_accountToUse->fillSession(m_session); // Launch immediately in true offline mode - if (m_accountToUse->isOffline()) { + if (m_accountToUse->accountType() == AccountType::Offline) { launchInstance(); return; } diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index c3acd8eb6..b81106a59 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -51,7 +51,7 @@ namespace MMCZip { // ours -bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet& contained, const FilterFunction filter) +bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet& contained, const FilterFunction& filter) { QuaZip modZip(from.filePath()); modZip.open(QuaZip::mdUnzip); diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h index fc06f17ef..a50af97e6 100644 --- a/launcher/MMCZip.h +++ b/launcher/MMCZip.h @@ -60,7 +60,7 @@ using FilterFunction = std::function; /** * Merge two zip files, using a filter function */ -bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet& contained, const FilterFunction filter = nullptr); +bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet& contained, const FilterFunction& filter = nullptr); /** * Compress directory, by providing a list of files to compress diff --git a/launcher/MTPixmapCache.h b/launcher/MTPixmapCache.h index 1a3e52160..b6bd13045 100644 --- a/launcher/MTPixmapCache.h +++ b/launcher/MTPixmapCache.h @@ -5,6 +5,7 @@ #include #include #include +#include #define GET_TYPE() \ Qt::ConnectionType type; \ @@ -100,10 +101,14 @@ class PixmapCache final : public QObject { */ bool _markCacheMissByEviciton() { + static constexpr uint maxInt = static_cast(std::numeric_limits::max()); + static constexpr uint step = 10240; + static constexpr int oneSecond = 1000; + auto now = QTime::currentTime(); if (!m_last_cache_miss_by_eviciton.isNull()) { auto diff = m_last_cache_miss_by_eviciton.msecsTo(now); - if (diff < 1000) { // less than a second ago + if (diff < oneSecond) { // less than a second ago ++m_consecutive_fast_evicitons; } else { m_consecutive_fast_evicitons = 0; @@ -111,11 +116,17 @@ class PixmapCache final : public QObject { } m_last_cache_miss_by_eviciton = now; if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) { - // double the cache size - auto newSize = _cacheLimit() * 2; - qDebug() << m_consecutive_fast_evicitons << "pixmap cache misses by eviction happened too fast, doubling cache size to" - << newSize; - _setCacheLimit(newSize); + // increase the cache size + uint newSize = _cacheLimit() + step; + if (newSize >= maxInt) { // increase it until you overflow :D + newSize = maxInt; + qDebug() << m_consecutive_fast_evicitons + << tr("pixmap cache misses by eviction happened too fast, doing nothing as the cache size reached it's limit"); + } else { + qDebug() << m_consecutive_fast_evicitons + << tr("pixmap cache misses by eviction happened too fast, increasing cache size to") << static_cast(newSize); + } + _setCacheLimit(static_cast(newSize)); m_consecutive_fast_evicitons = 0; return true; } diff --git a/launcher/RecursiveFileSystemWatcher.h b/launcher/RecursiveFileSystemWatcher.h index ec3ed804e..7f96f5cd0 100644 --- a/launcher/RecursiveFileSystemWatcher.h +++ b/launcher/RecursiveFileSystemWatcher.h @@ -13,7 +13,7 @@ class RecursiveFileSystemWatcher : public QObject { QDir rootDir() const { return m_root; } // WARNING: setting this to true may be bad for performance - void setWatchFiles(const bool watchFiles); + void setWatchFiles(bool watchFiles); bool watchFiles() const { return m_watchFiles; } void setMatcher(IPathMatcher::Ptr matcher) { m_matcher = matcher; } diff --git a/launcher/ResourceDownloadTask.h b/launcher/ResourceDownloadTask.h index 2baddf8a8..f686e819a 100644 --- a/launcher/ResourceDownloadTask.h +++ b/launcher/ResourceDownloadTask.h @@ -32,7 +32,7 @@ class ResourceDownloadTask : public SequentialTask { public: explicit ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion version, - const std::shared_ptr packs, + std::shared_ptr packs, bool is_indexed = true, QString custom_target_folder = {}); const QString& getFilename() const { return m_pack_version.fileName; } diff --git a/launcher/VersionProxyModel.h b/launcher/VersionProxyModel.h index 57c711d58..0863a7c80 100644 --- a/launcher/VersionProxyModel.h +++ b/launcher/VersionProxyModel.h @@ -28,7 +28,7 @@ class VersionProxyModel : public QAbstractProxyModel { const FilterMap& filters() const; const QString& search() const; - void setFilter(const BaseVersionList::ModelRoles column, Filter* filter); + void setFilter(BaseVersionList::ModelRoles column, Filter* filter); void setSearch(const QString& search); void clearFilters(); QModelIndex getRecommended() const; diff --git a/launcher/icons/IconList.h b/launcher/icons/IconList.h index 8afd05574..c51826057 100644 --- a/launcher/icons/IconList.h +++ b/launcher/icons/IconList.h @@ -67,7 +67,7 @@ class IconList : public QAbstractListModel { virtual Qt::ItemFlags flags(const QModelIndex& index) const override; bool addThemeIcon(const QString& key); - bool addIcon(const QString& key, const QString& name, const QString& path, const IconType type); + bool addIcon(const QString& key, const QString& name, const QString& path, IconType type); void saveIcon(const QString& key, const QString& path, const char* format) const; bool deleteIcon(const QString& key); bool trashIcon(const QString& key); diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index 10bbb1f1b..074bf54df 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -34,6 +34,7 @@ */ #include +#include #include #include @@ -440,18 +441,26 @@ QStringList getMinecraftJavaBundle() { QString partialPath; QString executable = "java"; + QStringList processpaths; #if defined(Q_OS_OSX) partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support"); #elif defined(Q_OS_WIN32) partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); executable += "w.exe"; + + // add the microsoft store version of the launcher to the search. the current path is: + // C:\Users\USERNAME\AppData\Local\Packages\Microsoft.4297127D64EC6_8wekyb3d8bbwe\LocalCache\Local\runtime + auto minecraftMSStorePath = + FS::PathCombine(QFileInfo(partialPath).absolutePath(), "Local", "Packages", "Microsoft.4297127D64EC6_8wekyb3d8bbwe"); + minecraftMSStorePath = FS::PathCombine(minecraftMSStorePath, "LocalCache", "Local", "runtime"); + processpaths << minecraftMSStorePath; #else partialPath = QDir::homePath(); #endif - auto minecraftPath = FS::PathCombine(partialPath, ".minecraft", "runtime"); - QStringList javas; - QStringList processpaths{ minecraftPath }; + auto minecraftDataPath = FS::PathCombine(partialPath, ".minecraft", "runtime"); + processpaths << minecraftDataPath; + QStringList javas; while (!processpaths.isEmpty()) { auto dirPath = processpaths.takeFirst(); QDir dir(dirPath); diff --git a/launcher/meta/Index.h b/launcher/meta/Index.h index 41fdfcea9..2c650ce2f 100644 --- a/launcher/meta/Index.h +++ b/launcher/meta/Index.h @@ -55,6 +55,6 @@ class Index : public QAbstractListModel, public BaseEntity { QVector m_lists; QHash m_uids; - void connectVersionList(const int row, const VersionList::Ptr& list); + void connectVersionList(int row, const VersionList::Ptr& list); }; } // namespace Meta diff --git a/launcher/meta/Version.h b/launcher/meta/Version.h index 07dcafb01..24da12d6d 100644 --- a/launcher/meta/Version.h +++ b/launcher/meta/Version.h @@ -64,7 +64,7 @@ class Version : public QObject, public BaseVersion, public BaseEntity { public: // for usage by format parsers only void setType(const QString& type); - void setTime(const qint64 time); + void setTime(qint64 time); void setRequires(const Meta::RequireSet& reqs, const Meta::RequireSet& conflicts); void setVolatile(bool volatile_); void setRecommended(bool recommended); diff --git a/launcher/meta/VersionList.h b/launcher/meta/VersionList.h index 5e587f204..2c5624701 100644 --- a/launcher/meta/VersionList.h +++ b/launcher/meta/VersionList.h @@ -79,7 +79,7 @@ class VersionList : public BaseVersionList, public BaseEntity { Version::Ptr m_recommended; - void setupAddedVersion(const int row, const Version::Ptr& version); + void setupAddedVersion(int row, const Version::Ptr& version); }; } // namespace Meta Q_DECLARE_METATYPE(Meta::VersionList::Ptr) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 170561f11..4229f73eb 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -565,43 +565,6 @@ QProcessEnvironment MinecraftInstance::createEnvironment() for (auto it = variables.begin(); it != variables.end(); ++it) { env.insert(it.key(), it.value()); } - return env; -} - -QProcessEnvironment MinecraftInstance::createLaunchEnvironment() -{ - // prepare the process environment - QProcessEnvironment env = createEnvironment(); - -#ifdef Q_OS_LINUX - if (settings()->get("EnableMangoHud").toBool() && APPLICATION->capabilities() & Application::SupportsMangoHud) { - auto preloadList = env.value("LD_PRELOAD").split(QLatin1String(":")); - auto libPaths = env.value("LD_LIBRARY_PATH").split(QLatin1String(":")); - - auto mangoHudLibString = MangoHud::getLibraryString(); - if (!mangoHudLibString.isEmpty()) { - QFileInfo mangoHudLib(mangoHudLibString); - - // dlsym variant is only needed for OpenGL and not included in the vulkan layer - preloadList << "libMangoHud_dlsym.so" << mangoHudLib.fileName(); - libPaths << mangoHudLib.absolutePath(); - } - - env.insert("LD_PRELOAD", preloadList.join(QLatin1String(":"))); - env.insert("LD_LIBRARY_PATH", libPaths.join(QLatin1String(":"))); - env.insert("MANGOHUD", "1"); - } - - if (settings()->get("UseDiscreteGpu").toBool()) { - // Open Source Drivers - env.insert("DRI_PRIME", "1"); - // Proprietary Nvidia Drivers - env.insert("__NV_PRIME_RENDER_OFFLOAD", "1"); - env.insert("__VK_LAYER_NV_optimus", "NVIDIA_only"); - env.insert("__GLX_VENDOR_LIBRARY_NAME", "nvidia"); - } -#endif - // custom env auto insertEnv = [&env](QMap envMap) { @@ -618,7 +581,53 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment() insertEnv(APPLICATION->settings()->get("Env").toMap()); else insertEnv(settings()->get("Env").toMap()); + return env; +} +QProcessEnvironment MinecraftInstance::createLaunchEnvironment() +{ + // prepare the process environment + QProcessEnvironment env = createEnvironment(); + +#ifdef Q_OS_LINUX + if (settings()->get("EnableMangoHud").toBool() && APPLICATION->capabilities() & Application::SupportsMangoHud) { + QStringList preloadList; + if (auto value = env.value("LD_PRELOAD"); !value.isEmpty()) + preloadList = value.split(QLatin1String(":")); + QStringList libPaths; + if (auto value = env.value("LD_LIBRARY_PATH"); !value.isEmpty()) + libPaths = value.split(QLatin1String(":")); + + auto mangoHudLibString = MangoHud::getLibraryString(); + if (!mangoHudLibString.isEmpty()) { + QFileInfo mangoHudLib(mangoHudLibString); + QString libPath = mangoHudLib.absolutePath(); + auto appendLib = [libPath, &preloadList](QString fileName) { + if (QFileInfo(FS::PathCombine(libPath, fileName)).exists()) + preloadList << fileName; + }; + + // dlsym variant is only needed for OpenGL and not included in the vulkan layer + appendLib("libMangoHud_dlsym.so"); + appendLib("libMangoHud_opengl.so"); + appendLib(mangoHudLib.fileName()); + libPaths << libPath; + } + + env.insert("LD_PRELOAD", preloadList.join(QLatin1String(":"))); + env.insert("LD_LIBRARY_PATH", libPaths.join(QLatin1String(":"))); + env.insert("MANGOHUD", "1"); + } + + if (settings()->get("UseDiscreteGpu").toBool()) { + // Open Source Drivers + env.insert("DRI_PRIME", "1"); + // Proprietary Nvidia Drivers + env.insert("__NV_PRIME_RENDER_OFFLOAD", "1"); + env.insert("__VK_LAYER_NV_optimus", "NVIDIA_only"); + env.insert("__GLX_VENDOR_LIBRARY_NAME", "nvidia"); + } +#endif return env; } diff --git a/launcher/minecraft/OneSixVersionFormat.h b/launcher/minecraft/OneSixVersionFormat.h index 9bdc4a4a3..9024d41e4 100644 --- a/launcher/minecraft/OneSixVersionFormat.h +++ b/launcher/minecraft/OneSixVersionFormat.h @@ -9,7 +9,7 @@ class OneSixVersionFormat { public: // version files / profile patches - static VersionFilePtr versionFileFromJson(const QJsonDocument& doc, const QString& filename, const bool requireOrder); + static VersionFilePtr versionFileFromJson(const QJsonDocument& doc, const QString& filename, bool requireOrder); static QJsonDocument versionFileToJson(const VersionFilePtr& patch); // libraries diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 9e706ae0a..180f8aa30 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -430,7 +430,7 @@ bool PackProfile::remove(const int index) return true; } -bool PackProfile::remove(const QString id) +bool PackProfile::remove(const QString& id) { int i = 0; for (auto patch : d->components) { diff --git a/launcher/minecraft/PackProfile.h b/launcher/minecraft/PackProfile.h index e72b6ebfe..e58e9ae9a 100644 --- a/launcher/minecraft/PackProfile.h +++ b/launcher/minecraft/PackProfile.h @@ -89,13 +89,13 @@ class PackProfile : public QAbstractListModel { enum MoveDirection { MoveUp, MoveDown }; /// move component file # up or down the list - void move(const int index, const MoveDirection direction); + void move(int index, MoveDirection direction); /// remove component file # - including files/records - bool remove(const int index); + bool remove(int index); /// remove component file by id - including files/records - bool remove(const QString id); + bool remove(const QString& id); bool customize(int index); diff --git a/launcher/minecraft/ProfileUtils.cpp b/launcher/minecraft/ProfileUtils.cpp index d56ed14bd..f81d6cb7f 100644 --- a/launcher/minecraft/ProfileUtils.cpp +++ b/launcher/minecraft/ProfileUtils.cpp @@ -140,7 +140,7 @@ VersionFilePtr parseJsonFile(const QFileInfo& fileInfo, const bool requireOrder) return guardedParseJson(doc, fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), requireOrder); } -bool saveJsonFile(const QJsonDocument doc, const QString& filename) +bool saveJsonFile(const QJsonDocument& doc, const QString& filename) { auto data = doc.toJson(); QSaveFile jsonFile(filename); diff --git a/launcher/minecraft/ProfileUtils.h b/launcher/minecraft/ProfileUtils.h index edabe0bf0..11e44485f 100644 --- a/launcher/minecraft/ProfileUtils.h +++ b/launcher/minecraft/ProfileUtils.h @@ -47,10 +47,10 @@ bool readOverrideOrders(QString path, PatchOrder& order); bool writeOverrideOrders(QString path, const PatchOrder& order); /// Parse a version file in JSON format -VersionFilePtr parseJsonFile(const QFileInfo& fileInfo, const bool requireOrder); +VersionFilePtr parseJsonFile(const QFileInfo& fileInfo, bool requireOrder); /// Save a JSON file (in any format) -bool saveJsonFile(const QJsonDocument doc, const QString& filename); +bool saveJsonFile(const QJsonDocument& doc, const QString& filename); /// Remove LWJGL from a patch file. This is applied to all Mojang-like profile files. void removeLwjglFromPatch(VersionFilePtr patch); diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index b141909a9..c3c09003c 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -283,9 +283,15 @@ QVariant AccountList::data(const QModelIndex& index, int role) const return account->accountDisplayString(); case TypeColumn: { - auto typeStr = account->typeString(); - typeStr[0] = typeStr[0].toUpper(); - return typeStr; + switch (account->accountType()) { + case AccountType::MSA: { + return tr("MSA", "Account type"); + } + case AccountType::Offline: { + return tr("Offline", "Account type"); + } + } + return tr("Unknown", "Account type"); } case StatusColumn: { diff --git a/launcher/minecraft/auth/AccountList.h b/launcher/minecraft/auth/AccountList.h index 051d8f958..039730739 100644 --- a/launcher/minecraft/auth/AccountList.h +++ b/launcher/minecraft/auth/AccountList.h @@ -75,7 +75,7 @@ class AccountList : public QAbstractListModel { virtual Qt::ItemFlags flags(const QModelIndex& index) const override; virtual bool setData(const QModelIndex& index, const QVariant& value, int role) override; - void addAccount(const MinecraftAccountPtr account); + void addAccount(MinecraftAccountPtr account); void removeAccount(QModelIndex index); int findAccountByProfileId(const QString& profileId) const; MinecraftAccountPtr getAccountByProfileName(const QString& profileName) const; diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index 545d06aed..ecee93d98 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -52,6 +52,7 @@ #include "flows/MSA.h" #include "flows/Offline.h" +#include "minecraft/auth/AccountData.h" MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) { @@ -185,7 +186,7 @@ void MinecraftAccount::authFailed(QString reason) // NOTE: this doesn't do much. There was an error of some sort. } break; case AccountTaskState::STATE_FAILED_HARD: { - if (isMSA()) { + if (accountType() == AccountType::MSA) { data.msaToken.token = QString(); data.msaToken.refresh_token = QString(); data.msaToken.validity = Katabasis::Validity::None; diff --git a/launcher/minecraft/auth/MinecraftAccount.h b/launcher/minecraft/auth/MinecraftAccount.h index c4e9756a9..f773b3bc9 100644 --- a/launcher/minecraft/auth/MinecraftAccount.h +++ b/launcher/minecraft/auth/MinecraftAccount.h @@ -118,9 +118,7 @@ class MinecraftAccount : public QObject, public Usable { bool isActive() const; - bool isMSA() const { return data.type == AccountType::MSA; } - - bool isOffline() const { return data.type == AccountType::Offline; } + [[nodiscard]] AccountType accountType() const noexcept { return data.type; } bool ownsMinecraft() const { return data.minecraftEntitlement.ownsMinecraft; } diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index a5f1489dd..fc543202f 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -37,9 +37,9 @@ #include "ModFolderModel.h" #include -#include #include #include +#include #include #include #include @@ -65,8 +65,8 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider" }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider") }); m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER }; - m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, - QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; + m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, + QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true, true, true }; } @@ -131,6 +131,11 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const } return {}; } + case Qt::SizeHintRole: + if (column == ImageColumn) { + return QSize(32, 32); + } + return {}; case Qt::CheckStateRole: switch (column) { case ActiveColumn: diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 0503b660b..9157f35f0 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -45,7 +46,7 @@ ResourceFolderModel::~ResourceFolderModel() QCoreApplication::processEvents(); } -bool ResourceFolderModel::startWatching(const QStringList paths) +bool ResourceFolderModel::startWatching(const QStringList& paths) { if (m_is_watching) return false; @@ -64,7 +65,7 @@ bool ResourceFolderModel::startWatching(const QStringList paths) return m_is_watching; } -bool ResourceFolderModel::stopWatching(const QStringList paths) +bool ResourceFolderModel::stopWatching(const QStringList& paths) { if (!m_is_watching) return false; @@ -460,9 +461,9 @@ bool ResourceFolderModel::setData(const QModelIndex& index, [[maybe_unused]] con if (role == Qt::CheckStateRole) { if (m_instance != nullptr && m_instance->isRunning()) { auto response = - CustomMessageBox::selectable(nullptr, "Confirm toggle", - "If you enable/disable this resource while the game is running it may crash your game.\n" - "Are you sure you want to do this?", + CustomMessageBox::selectable(nullptr, tr("Confirm toggle"), + tr("If you enable/disable this resource while the game is running it may crash your game.\n" + "Are you sure you want to do this?"), QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ->exec(); @@ -516,36 +517,22 @@ void ResourceFolderModel::setupHeaderAction(QAction* act, int column) act->setText(columnNames().at(column)); } -void ResourceFolderModel::saveHiddenColumn(int column, bool hidden) +void ResourceFolderModel::saveColumns(QTreeView* tree) { - auto const setting_name = QString("UI/%1_Page/HiddenColumns").arg(id()); + auto const setting_name = QString("UI/%1_Page/Columns").arg(id()); auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name) : m_instance->settings()->registerSetting(setting_name); - auto hiddenColumns = setting->get().toStringList(); - auto name = columnNames(false).at(column); - auto index = hiddenColumns.indexOf(name); - if (index >= 0 && !hidden) { - hiddenColumns.removeAt(index); - } else if (index < 0 && hidden) { - hiddenColumns.append(name); - } - setting->set(hiddenColumns); + setting->set(tree->header()->saveState()); } -void ResourceFolderModel::loadHiddenColumns(QTreeView* tree) +void ResourceFolderModel::loadColumns(QTreeView* tree) { - auto const setting_name = QString("UI/%1_Page/HiddenColumns").arg(id()); + auto const setting_name = QString("UI/%1_Page/Columns").arg(id()); auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name) : m_instance->settings()->registerSetting(setting_name); - auto hiddenColumns = setting->get().toStringList(); - auto col_names = columnNames(false); - for (auto col_name : hiddenColumns) { - auto index = col_names.indexOf(col_name); - if (index >= 0) - tree->setColumnHidden(index, true); - } + tree->header()->restoreState(setting->get().toByteArray()); } QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree) @@ -570,7 +557,7 @@ QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree) if (m_column_resize_modes.at(c) == QHeaderView::ResizeToContents) tree->resizeColumnToContents(c); } - saveHiddenColumn(col, !toggled); + saveColumns(tree); }); menu->addAction(act); diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 60b8879c0..d764280b6 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -39,14 +39,14 @@ class ResourceFolderModel : public QAbstractListModel { * Returns whether starting to watch all the paths was successful. * If one or more fails, it returns false. */ - bool startWatching(const QStringList paths); + bool startWatching(const QStringList& paths); /** Stops watching the paths for changes. * * Returns whether stopping to watch all the paths was successful. * If one or more fails, it returns false. */ - bool stopWatching(const QStringList paths); + bool stopWatching(const QStringList& paths); /* Helper methods for subclasses, using a predetermined list of paths. */ virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); } @@ -117,8 +117,8 @@ class ResourceFolderModel : public QAbstractListModel { [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; void setupHeaderAction(QAction* act, int column); - void saveHiddenColumn(int column, bool hidden); - void loadHiddenColumns(QTreeView* tree); + void saveColumns(QTreeView* tree); + void loadColumns(QTreeView* tree); QMenu* createHeaderContextMenu(QTreeView* tree); /** This creates a proxy model to filter / sort the model for a UI. @@ -201,8 +201,7 @@ class ResourceFolderModel : public QAbstractListModel { QList m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE }; QStringList m_column_names = { "Enable", "Name", "Last Modified" }; QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified") }; - QList m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Stretch, - QHeaderView::ResizeToContents }; + QList m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive }; QList m_columnsHideable = { false, false, true }; QDir m_dir; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index f27431576..693b8af05 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -52,8 +52,8 @@ ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstanc m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified" }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified") }); m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE }; - m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, - QHeaderView::ResizeToContents }; + m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, + QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true, true }; } @@ -117,6 +117,11 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const } return m_resources[row]->internal_id(); } + case Qt::SizeHintRole: + if (column == ImageColumn) { + return QSize(32, 32); + } + return {}; case Qt::CheckStateRole: switch (column) { case ActiveColumn: diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index 5c5f2b7c1..f210501c7 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -47,8 +47,7 @@ TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified" }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified") }); m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE }; - m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, - QHeaderView::ResizeToContents }; + m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true }; } @@ -104,6 +103,11 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const } return {}; } + case Qt::SizeHintRole: + if (column == ImageColumn) { + return QSize(32, 32); + } + return {}; case Qt::CheckStateRole: if (column == ActiveColumn) { return m_resources[row]->enabled() ? Qt::Checked : Qt::Unchecked; diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index df8c690af..efc17298e 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -137,10 +137,11 @@ Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptrpack->provider == m_flame_provider.name ? m_flame_provider : m_modrinth_provider; auto responseInfo = std::make_shared(); auto info = provider.api->getProject(pDep->pack->addonId.toString(), responseInfo); - QObject::connect(info.get(), &NetJob::succeeded, [responseInfo, provider, pDep] { + QObject::connect(info.get(), &NetJob::succeeded, [this, responseInfo, provider, pDep] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*responseInfo, &parse_error); if (parse_error.error != QJsonParseError::NoError) { + removePack(pDep->pack->addonId); qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset << " reason: " << parse_error.errorString(); qDebug() << *responseInfo; @@ -151,6 +152,7 @@ Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptrloadIndexedPack(*pDep->pack, obj); } catch (const JSONValidationError& e) { + removePack(pDep->pack->addonId); qDebug() << doc; qWarning() << "Error while reading mod info: " << e.cause(); } @@ -211,11 +213,13 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen pDep->pack->versionsLoaded = true; } catch (const JSONValidationError& e) { + removePack(dep.addonId); qDebug() << doc; qWarning() << "Error while reading mod version: " << e.cause(); return; } if (level == 0) { + removePack(dep.addonId); qWarning() << "Dependency cycle exceeded"; return; } @@ -238,7 +242,7 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen return tasks; } -void GetModDependenciesTask::removePack(const QVariant addonId) +void GetModDependenciesTask::removePack(const QVariant& addonId) { auto pred = [addonId](const std::shared_ptr& v) { return v->pack->addonId == addonId; }; #if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0) diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.h b/launcher/minecraft/mod/tasks/GetModDependenciesTask.h index 2580f8077..e88204bdc 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.h +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.h @@ -65,13 +65,13 @@ class GetModDependenciesTask : public SequentialTask { QHash getRequiredBy(); protected slots: - Task::Ptr prepareDependencyTask(const ModPlatform::Dependency&, const ModPlatform::ResourceProvider, int); + Task::Ptr prepareDependencyTask(const ModPlatform::Dependency&, ModPlatform::ResourceProvider, int); QList getDependenciesForVersion(const ModPlatform::IndexedVersion&, - const ModPlatform::ResourceProvider providerName); + ModPlatform::ResourceProvider providerName); void prepare(); Task::Ptr getProjectInfoTask(std::shared_ptr pDep); - ModPlatform::Dependency getOverride(const ModPlatform::Dependency&, const ModPlatform::ResourceProvider providerName); - void removePack(const QVariant addonId); + ModPlatform::Dependency getOverride(const ModPlatform::Dependency&, ModPlatform::ResourceProvider providerName); + void removePack(const QVariant& addonId); private: QList> m_pack_dependencies; diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index e99ce3a56..bb4f18983 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -208,17 +208,15 @@ Task::Ptr FlameAPI::getFile(const QString& addonId, const QString& fileId, std:: return netJob; } -// https://docs.curseforge.com/?python#tocS_ModsSearchSortField -static QList s_sorts = { { 1, "Featured", QObject::tr("Sort by Featured") }, - { 2, "Popularity", QObject::tr("Sort by Popularity") }, - { 3, "LastUpdated", QObject::tr("Sort by Last Updated") }, - { 4, "Name", QObject::tr("Sort by Name") }, - { 5, "Author", QObject::tr("Sort by Author") }, - { 6, "TotalDownloads", QObject::tr("Sort by Downloads") }, - { 7, "Category", QObject::tr("Sort by Category") }, - { 8, "GameVersion", QObject::tr("Sort by Game Version") } }; - QList FlameAPI::getSortingMethods() const { - return s_sorts; + // https://docs.curseforge.com/?python#tocS_ModsSearchSortField + return { { 1, "Featured", QObject::tr("Sort by Featured") }, + { 2, "Popularity", QObject::tr("Sort by Popularity") }, + { 3, "LastUpdated", QObject::tr("Sort by Last Updated") }, + { 4, "Name", QObject::tr("Sort by Name") }, + { 5, "Author", QObject::tr("Sort by Author") }, + { 6, "TotalDownloads", QObject::tr("Sort by Downloads") }, + { 7, "Category", QObject::tr("Sort by Category") }, + { 8, "GameVersion", QObject::tr("Sort by Game Version") } }; } diff --git a/launcher/modplatform/modrinth/ModrinthAPI.cpp b/launcher/modplatform/modrinth/ModrinthAPI.cpp index f453f5cb9..9777c2cfd 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.cpp +++ b/launcher/modplatform/modrinth/ModrinthAPI.cpp @@ -111,14 +111,12 @@ Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, std::shared_ptr s_sorts = { { 1, "relevance", QObject::tr("Sort by Relevance") }, - { 2, "downloads", QObject::tr("Sort by Downloads") }, - { 3, "follows", QObject::tr("Sort by Follows") }, - { 4, "newest", QObject::tr("Sort by Last Updated") }, - { 5, "updated", QObject::tr("Sort by Newest") } }; - QList ModrinthAPI::getSortingMethods() const { - return s_sorts; + // https://docs.modrinth.com/api-spec/#tag/projects/operation/searchProjects + return { { 1, "relevance", QObject::tr("Sort by Relevance") }, + { 2, "downloads", QObject::tr("Sort by Downloads") }, + { 3, "follows", QObject::tr("Sort by Follows") }, + { 4, "newest", QObject::tr("Sort by Newest") }, + { 5, "updated", QObject::tr("Sort by Last Updated") } }; } diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.h b/launcher/modplatform/modrinth/ModrinthPackExportTask.h index 33c42e817..81c2f25bf 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.h +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.h @@ -68,7 +68,7 @@ class ModrinthPackExportTask : public Task { void collectFiles(); void collectHashes(); void makeApiRequest(); - void parseApiResponse(const std::shared_ptr response); + void parseApiResponse(std::shared_ptr response); void buildZip(); QByteArray generateIndex(); diff --git a/launcher/modplatform/technic/TechnicPackProcessor.h b/launcher/modplatform/technic/TechnicPackProcessor.h index 466bce596..08e117fd8 100644 --- a/launcher/modplatform/technic/TechnicPackProcessor.h +++ b/launcher/modplatform/technic/TechnicPackProcessor.h @@ -33,6 +33,6 @@ class TechnicPackProcessor : public QObject { const QString& instIcon, const QString& stagingPath, const QString& minecraftVersion = QString(), - const bool isSolder = false); + bool isSolder = false); }; } // namespace Technic diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 1da982dad..7b4d1c8a5 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1198,17 +1198,27 @@ void MainWindow::on_actionViewCentralModsFolder_triggered() void MainWindow::on_actionViewIconThemeFolder_triggered() { - DesktopServices::openDirectory(APPLICATION->themeManager()->getIconThemesFolder().path()); + DesktopServices::openDirectory(APPLICATION->themeManager()->getIconThemesFolder().path(), true); } void MainWindow::on_actionViewWidgetThemeFolder_triggered() { - DesktopServices::openDirectory(APPLICATION->themeManager()->getApplicationThemesFolder().path()); + DesktopServices::openDirectory(APPLICATION->themeManager()->getApplicationThemesFolder().path(), true); } void MainWindow::on_actionViewCatPackFolder_triggered() { - DesktopServices::openDirectory(APPLICATION->themeManager()->getCatPacksFolder().path()); + DesktopServices::openDirectory(APPLICATION->themeManager()->getCatPacksFolder().path(), true); +} + +void MainWindow::on_actionViewIconsFolder_triggered() +{ + DesktopServices::openDirectory(APPLICATION->icons()->getDirectory(), true); +} + +void MainWindow::on_actionViewLogsFolder_triggered() +{ + DesktopServices::openDirectory("logs", true); } void MainWindow::refreshInstances() diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index 0b7287404..d2e154643 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -117,6 +117,8 @@ class MainWindow : public QMainWindow { void on_actionViewIconThemeFolder_triggered(); void on_actionViewWidgetThemeFolder_triggered(); void on_actionViewCatPackFolder_triggered(); + void on_actionViewIconsFolder_triggered(); + void on_actionViewLogsFolder_triggered(); void on_actionViewSelectedInstFolder_triggered(); diff --git a/launcher/ui/MainWindow.ui b/launcher/ui/MainWindow.ui index 91b2c2703..1ee3a5632 100644 --- a/launcher/ui/MainWindow.ui +++ b/launcher/ui/MainWindow.ui @@ -194,6 +194,9 @@ + + + @@ -545,10 +548,10 @@ .. - View &Instance Folder + &Instances - Open the instance folder in a file browser. + Open the instances folder in a file browser. @@ -557,7 +560,7 @@ .. - View Launcher &Root Folder + Launcher &Root Open the launcher's root folder in a file browser. @@ -569,12 +572,36 @@ .. - View &Central Mods Folder + &Central Mods Open the central mods folder in a file browser. + + + + .. + + + Instance Icons + + + Open the instance icons folder in a file browser. + + + + + + .. + + + Logs + + + Open the logs folder in a file browser. + + Themes @@ -718,10 +745,10 @@ .. - View &Widget Themes Folder + &Widget Themes - View Widget Theme Folder + Open the widget themes folder in a file browser. @@ -730,18 +757,22 @@ .. - View I&con Theme Folder + I&con Theme - View Icon Theme Folder + Open the icon theme folder in a file browser. - + + .. - View Cat Packs Folder + Cat Packs + + + Open the cat packs folder in a file browser. diff --git a/launcher/ui/dialogs/AboutDialog.cpp b/launcher/ui/dialogs/AboutDialog.cpp index f5eaff7a1..cee01e821 100644 --- a/launcher/ui/dialogs/AboutDialog.cpp +++ b/launcher/ui/dialogs/AboutDialog.cpp @@ -101,7 +101,7 @@ QString getCreditsHtml() stream << "

" << QObject::tr("With thanks to", "About Credits") << "

\n"; stream << QString("

Boba %1

\n").arg(getWebsite("https://bobaonline.neocities.org/")); stream << QString("

Davi Rafael %1

\n").arg(getWebsite("https://auti.one/")); - stream << QString("

Fulmine %1

\n").arg(getWebsite("https://www.fulmine.xyz/")); + stream << QString("

Fulmine %1

\n").arg(getWebsite("https://fulmine.xyz/")); stream << QString("

ely %1

\n").arg(getGitHub("elyrodso")); stream << QString("

gon sawa %1

\n").arg(getGitHub("gonsawa")); stream << QString("

Pankakes

\n"); diff --git a/launcher/ui/dialogs/ChooseProviderDialog.h b/launcher/ui/dialogs/ChooseProviderDialog.h index be9735b5c..51e7c98c6 100644 --- a/launcher/ui/dialogs/ChooseProviderDialog.h +++ b/launcher/ui/dialogs/ChooseProviderDialog.h @@ -13,7 +13,6 @@ enum class ResourceProvider; class Mod; class NetJob; -class ModUpdateDialog; class ChooseProviderDialog : public QDialog { Q_OBJECT diff --git a/launcher/ui/dialogs/MSALoginDialog.cpp b/launcher/ui/dialogs/MSALoginDialog.cpp index 74fff9fd3..7df423412 100644 --- a/launcher/ui/dialogs/MSALoginDialog.cpp +++ b/launcher/ui/dialogs/MSALoginDialog.cpp @@ -105,8 +105,16 @@ void MSALoginDialog::showVerificationUriAndCode(const QUrl& uri, const QString& QString urlString = uri.toString(); QString linkString = QString("%2").arg(urlString, urlString); - ui->label->setText( - tr("

Please open up %1 in a browser and put in the code %2 to proceed with login.

").arg(linkString, code)); + if (urlString == "https://www.microsoft.com/link" && !code.isEmpty()) { + urlString += QString("?otc=%1").arg(code); + DesktopServices::openUrl(urlString); + ui->label->setText(tr("

Please login in the opened browser. If no browser was opened, please open up %1 in " + "a browser and put in the code %2 to proceed with login.

") + .arg(linkString, code)); + } else { + ui->label->setText( + tr("

Please open up %1 in a browser and put in the code %2 to proceed with login.

").arg(linkString, code)); + } ui->actionButton->setVisible(true); connect(ui->actionButton, &QPushButton::clicked, [=]() { DesktopServices::openUrl(uri); diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 1a70ea59a..cbaaf88b9 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -39,7 +39,8 @@ static std::optional mcLoaders(BaseInstance* inst) ModUpdateDialog::ModUpdateDialog(QWidget* parent, BaseInstance* instance, const std::shared_ptr mods, - QList& search_for) + QList& search_for, + bool includeDeps) : ReviewMessageBox(parent, tr("Confirm mods to update"), "") , m_parent(parent) , m_mod_model(mods) @@ -47,6 +48,7 @@ ModUpdateDialog::ModUpdateDialog(QWidget* parent, , m_second_try_metadata( new ConcurrentTask(nullptr, "Second Metadata Search", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt())) , m_instance(instance) + , m_include_deps(includeDeps) { ReviewMessageBox::setGeometry(0, 0, 800, 600); @@ -186,7 +188,7 @@ void ModUpdateDialog::checkCandidates() } } - { // dependencies + if (m_include_deps && !APPLICATION->settings()->get("ModDependenciesDisabled").toBool()) { // dependencies auto depTask = makeShared(this, m_instance, m_mod_model.get(), selectedVers); connect(depTask.get(), &Task::failed, this, diff --git a/launcher/ui/dialogs/ModUpdateDialog.h b/launcher/ui/dialogs/ModUpdateDialog.h index b79aa4943..de5ab46a5 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.h +++ b/launcher/ui/dialogs/ModUpdateDialog.h @@ -16,10 +16,12 @@ class ConcurrentTask; class ModUpdateDialog final : public ReviewMessageBox { Q_OBJECT public: + explicit ModUpdateDialog(QWidget* parent, BaseInstance* instance, std::shared_ptr mod_model, QList& search_for); explicit ModUpdateDialog(QWidget* parent, BaseInstance* instance, - const std::shared_ptr mod_model, - QList& search_for); + std::shared_ptr mod_model, + QList& search_for, + bool includeDeps); void checkCandidates(); @@ -61,4 +63,5 @@ class ModUpdateDialog final : public ReviewMessageBox { bool m_no_updates = false; bool m_aborted = false; + bool m_include_deps = false; }; diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index dc7cfff06..1431ea92c 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -270,13 +270,15 @@ QList ModDownloadDialog::getPages() GetModDependenciesTask::Ptr ModDownloadDialog::getModDependenciesTask() { - if (auto model = dynamic_cast(getBaseModel().get()); model) { - QList> selectedVers; - for (auto& selected : getTasks()) { - selectedVers.append(std::make_shared(selected->getPack(), selected->getVersion())); - } + if (!APPLICATION->settings()->get("ModDependenciesDisabled").toBool()) { // dependencies + if (auto model = dynamic_cast(getBaseModel().get()); model) { + QList> selectedVers; + for (auto& selected : getTasks()) { + selectedVers.append(std::make_shared(selected->getPack(), selected->getVersion())); + } - return makeShared(this, m_instance, model, selectedVers); + return makeShared(this, m_instance, model, selectedVers); + } } return nullptr; } diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.h b/launcher/ui/dialogs/ResourceDownloadDialog.h index e9d2cfbe6..a6efca138 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.h +++ b/launcher/ui/dialogs/ResourceDownloadDialog.h @@ -50,7 +50,7 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider { public: using DownloadTaskPtr = shared_qobject_ptr; - ResourceDownloadDialog(QWidget* parent, const std::shared_ptr base_model); + ResourceDownloadDialog(QWidget* parent, std::shared_ptr base_model); void initializeContainer(); void connectButtons(); diff --git a/launcher/ui/instanceview/InstanceView.cpp b/launcher/ui/instanceview/InstanceView.cpp index 7530fdfba..e665097f4 100644 --- a/launcher/ui/instanceview/InstanceView.cpp +++ b/launcher/ui/instanceview/InstanceView.cpp @@ -278,12 +278,14 @@ void InstanceView::mousePressEvent(QMouseEvent* event) m_pressedAlreadySelected = selectionModel()->isSelected(m_pressedIndex); m_pressedPosition = geometryPos; - VisualGroup::HitResults hitResult; - m_pressedCategory = categoryAt(geometryPos, hitResult); - if (m_pressedCategory && hitResult & VisualGroup::CheckboxHit) { - setState(m_pressedCategory->collapsed ? ExpandingState : CollapsingState); - event->accept(); - return; + if (event->button() == Qt::LeftButton) { + VisualGroup::HitResults hitResult; + m_pressedCategory = categoryAt(geometryPos, hitResult); + if (m_pressedCategory && hitResult & VisualGroup::CheckboxHit) { + setState(m_pressedCategory->collapsed ? ExpandingState : CollapsingState); + event->accept(); + return; + } } if (index.isValid() && (index.flags() & Qt::ItemIsEnabled)) { @@ -366,10 +368,7 @@ void InstanceView::mouseReleaseEvent(QMouseEvent* event) VisualGroup::HitResults hitResult; - bool click = - (index == m_pressedIndex && index.isValid()) || (m_pressedCategory && m_pressedCategory == categoryAt(geometryPos, hitResult)); - - if (click && m_pressedCategory) { + if (event->button() == Qt::LeftButton && m_pressedCategory != nullptr && m_pressedCategory == categoryAt(geometryPos, hitResult)) { if (state() == ExpandingState) { m_pressedCategory->collapsed = false; emit groupStateChanged(m_pressedCategory->text, false); @@ -397,7 +396,7 @@ void InstanceView::mouseReleaseEvent(QMouseEvent* event) setState(NoState); - if (click) { + if (index == m_pressedIndex && index.isValid()) { if (event->button() == Qt::LeftButton) { emit clicked(index); } diff --git a/launcher/ui/instanceview/InstanceView.h b/launcher/ui/instanceview/InstanceView.h index 3d4d56208..30be411a8 100644 --- a/launcher/ui/instanceview/InstanceView.h +++ b/launcher/ui/instanceview/InstanceView.h @@ -65,7 +65,7 @@ class InstanceView : public QAbstractItemView { /// get the model index at the specified visual point virtual QModelIndex indexAt(const QPoint& point) const override; QString groupNameAt(const QPoint& point); - void setSelection(const QRect& rect, const QItemSelectionModel::SelectionFlags commands) override; + void setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags commands) override; virtual int horizontalOffset() const override; virtual int verticalOffset() const override; diff --git a/launcher/ui/pages/global/APIPage.ui b/launcher/ui/pages/global/APIPage.ui index 492741ba4..93591e440 100644 --- a/launcher/ui/pages/global/APIPage.ui +++ b/launcher/ui/pages/global/APIPage.ui @@ -209,6 +209,9 @@ <html><head/><body><p>Note: you only need to set this to access private data. Read the <a href="https://docs.modrinth.com/api-spec/#section/Authentication">documentation</a> for more information.</p></body></html> + + true + diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index 3a21cdd9b..abd8fa228 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -35,6 +35,7 @@ */ #include "AccountListPage.h" +#include "minecraft/auth/AccountData.h" #include "ui_AccountListPage.h" #include @@ -216,7 +217,7 @@ void AccountListPage::updateButtonStates() QModelIndex selected = selection.first(); MinecraftAccountPtr account = selected.data(AccountList::PointerRole).value(); accountIsReady = !account->isActive(); - accountIsOnline = !account->isOffline(); + accountIsOnline = account->accountType() != AccountType::Offline; } ui->actionRemove->setEnabled(accountIsReady); ui->actionSetDefault->setEnabled(accountIsReady); @@ -231,6 +232,7 @@ void AccountListPage::updateButtonStates() ui->actionNoDefault->setEnabled(true); ui->actionNoDefault->setChecked(false); } + ui->listView->resizeColumnToContents(3); } void AccountListPage::on_actionUploadSkin_triggered() diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index 5a547637c..fd16572d3 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -84,7 +84,7 @@ MiB - 128 + 8 1048576 @@ -106,7 +106,7 @@ MiB - 128 + 8 1048576 @@ -128,7 +128,7 @@ MiB - 64 + 4 999999999 diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index 6d8c65ec5..d15883db5 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -223,6 +223,7 @@ void LauncherPage::applySettings() // Mods s->set("ModMetadataDisabled", ui->metadataDisableBtn->isChecked()); + s->set("ModDependenciesDisabled", ui->dependenciesDisableBtn->isChecked()); } void LauncherPage::loadSettings() { @@ -278,6 +279,7 @@ void LauncherPage::loadSettings() // Mods ui->metadataDisableBtn->setChecked(s->get("ModMetadataDisabled").toBool()); ui->metadataWarningLabel->setHidden(!ui->metadataDisableBtn->isChecked()); + ui->dependenciesDisableBtn->setChecked(s->get("ModDependenciesDisabled").toBool()); } void LauncherPage::refreshFontPreview() diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index 250a8bc88..83897f7a7 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -186,6 +186,16 @@ + + + + Disable the automatic detection, installation, and updating of mod dependencies. + + + Disable automatic mod dependency management + + + diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index 1a8fafa9b..48a71b809 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -42,6 +42,7 @@ #include "minecraft/mod/ResourceFolderModel.h" #include "ui/GuiUtil.h" +#include #include #include #include @@ -95,7 +96,8 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared connect(viewHeader, &QHeaderView::customContextMenuRequested, this, &ExternalResourcesPage::ShowHeaderContextMenu); - m_model->loadHiddenColumns(ui->treeView); + m_model->loadColumns(ui->treeView); + connect(ui->treeView->header(), &QHeaderView::sectionResized, this, [this] { m_model->saveColumns(ui->treeView); }); } ExternalResourcesPage::~ExternalResourcesPage() @@ -252,9 +254,9 @@ void ExternalResourcesPage::removeItem() void ExternalResourcesPage::removeItems(const QItemSelection& selection) { if (m_instance != nullptr && m_instance->isRunning()) { - auto response = CustomMessageBox::selectable(this, "Confirm Delete", - "If you remove this resource while the game is running it may crash your game.\n" - "Are you sure you want to do this?", + auto response = CustomMessageBox::selectable(this, tr("Confirm Delete"), + tr("If you remove this resource while the game is running it may crash your game.\n" + "Are you sure you want to do this?"), QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ->exec(); @@ -273,9 +275,9 @@ void ExternalResourcesPage::enableItem() void ExternalResourcesPage::disableItem() { if (m_instance != nullptr && m_instance->isRunning()) { - auto response = CustomMessageBox::selectable(this, "Confirm disable", - "If you disable this resource while the game is running it may crash your game.\n" - "Are you sure you want to do this?", + auto response = CustomMessageBox::selectable(this, tr("Confirm disable"), + tr("If you disable this resource while the game is running it may crash your game.\n" + "Are you sure you want to do this?"), QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ->exec(); diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.ui b/launcher/ui/pages/instance/ExternalResourcesPage.ui index ba703f77d..ff08e12d2 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.ui +++ b/launcher/ui/pages/instance/ExternalResourcesPage.ui @@ -70,6 +70,9 @@ + + true + Actions @@ -146,17 +149,6 @@ Download a new resource - - - false - - - Check for &Updates - - - Try to check or update all selected resources (all resources if none are selected) - - false @@ -168,15 +160,15 @@ Go to mods home page - + - false + true - Remove metadata + Check for &Updates - Remove mod's metadata + Try to check or update all selected resources (all resources if none are selected) diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index ad348cc0b..3b06ac865 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -155,7 +155,7 @@ MiB - 128 + 8 1048576 @@ -177,7 +177,7 @@ MiB - 128 + 8 1048576 @@ -199,7 +199,7 @@ MiB - 64 + 4 999999999 diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 625d37933..ce1cc3a90 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -84,50 +84,71 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr connect(ui->actionDownloadItem, &QAction::triggered, this, &ModFolderPage::installMods); + // update menu + auto updateMenu = ui->actionUpdateItem->menu(); + if (updateMenu) { + updateMenu->clear(); + } else { + updateMenu = new QMenu(this); + } + + auto update = updateMenu->addAction(tr("Check for Updates")); + update->setToolTip(tr("Try to check or update all selected mods (all mods if none are selected)")); + connect(update, &QAction::triggered, this, &ModFolderPage::updateMods); + + auto updateWithDeps = updateMenu->addAction(tr("Verify Dependencies")); + updateWithDeps->setToolTip( + tr("Try to update and check for missing dependencies all selected mods (all mods if none are selected)")); + connect(updateWithDeps, &QAction::triggered, this, [this] { updateMods(true); }); + + auto depsDisabled = APPLICATION->settings()->getSetting("ModDependenciesDisabled"); + updateWithDeps->setVisible(!depsDisabled->get().toBool()); + connect(depsDisabled.get(), &Setting::SettingChanged, this, + [updateWithDeps](const Setting& setting, QVariant value) { updateWithDeps->setVisible(!value.toBool()); }); + + auto actionRemoveItemMetadata = updateMenu->addAction(tr("Reset update metadata")); + actionRemoveItemMetadata->setToolTip(tr("Remove mod's metadata")); + connect(actionRemoveItemMetadata, &QAction::triggered, this, &ModFolderPage::deleteModMetadata); + actionRemoveItemMetadata->setEnabled(false); + + ui->actionUpdateItem->setMenu(updateMenu); + ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected mods (all mods if none are selected)")); - ui->actionsToolbar->insertActionAfter(ui->actionAddItem, ui->actionUpdateItem); connect(ui->actionUpdateItem, &QAction::triggered, this, &ModFolderPage::updateMods); + ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionUpdateItem); ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page")); ui->actionsToolbar->addAction(ui->actionVisitItemPage); connect(ui->actionVisitItemPage, &QAction::triggered, this, &ModFolderPage::visitModPages); - ui->actionRemoveItemMetadata->setToolTip(tr("Remove mod's metadata")); - ui->actionsToolbar->insertActionAfter(ui->actionRemoveItem, ui->actionRemoveItemMetadata); - connect(ui->actionRemoveItemMetadata, &QAction::triggered, this, &ModFolderPage::deleteModMetadata); - auto check_allow_update = [this] { return ui->treeView->selectionModel()->hasSelection() || !m_model->empty(); }; - connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this, check_allow_update] { - ui->actionUpdateItem->setEnabled(check_allow_update()); + connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, + [this, check_allow_update, actionRemoveItemMetadata] { + ui->actionUpdateItem->setEnabled(check_allow_update()); - auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); - auto mods_list = m_model->selectedMods(selection); - auto selected = std::count_if(mods_list.cbegin(), mods_list.cend(), - [](Mod* v) { return v->metadata() != nullptr || v->homeurl().size() != 0; }); - if (selected <= 1) { - ui->actionVisitItemPage->setText(tr("Visit mod's page")); - ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page")); + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + auto mods_list = m_model->selectedMods(selection); + auto selected = std::count_if(mods_list.cbegin(), mods_list.cend(), + [](Mod* v) { return v->metadata() != nullptr || v->homeurl().size() != 0; }); + if (selected <= 1) { + ui->actionVisitItemPage->setText(tr("Visit mod's page")); + ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page")); - ui->actionRemoveItemMetadata->setToolTip(tr("Remove mod's metadata")); - } else { - ui->actionVisitItemPage->setText(tr("Visit mods' pages")); - ui->actionVisitItemPage->setToolTip(tr("Go to the pages of the selected mods")); + } else { + ui->actionVisitItemPage->setText(tr("Visit mods' pages")); + ui->actionVisitItemPage->setToolTip(tr("Go to the pages of the selected mods")); + } + ui->actionVisitItemPage->setEnabled(selected != 0); + actionRemoveItemMetadata->setEnabled(selected != 0); + }); - ui->actionRemoveItemMetadata->setToolTip(tr("Remove mods' metadata")); - } - ui->actionVisitItemPage->setEnabled(selected != 0); - ui->actionRemoveItemMetadata->setEnabled(selected != 0); - }); + auto updateButtons = [this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); }; + connect(mods.get(), &ModFolderModel::rowsInserted, this, updateButtons); - connect(mods.get(), &ModFolderModel::rowsInserted, this, - [this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); }); + connect(mods.get(), &ModFolderModel::rowsRemoved, this, updateButtons); - connect(mods.get(), &ModFolderModel::rowsRemoved, this, - [this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); }); - - connect(mods.get(), &ModFolderModel::updateFinished, this, - [this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); }); + connect(mods.get(), &ModFolderModel::updateFinished, this, updateButtons); } } @@ -150,9 +171,9 @@ bool ModFolderPage::onSelectionChanged(const QModelIndex& current, [[maybe_unuse void ModFolderPage::removeItems(const QItemSelection& selection) { if (m_instance != nullptr && m_instance->isRunning()) { - auto response = CustomMessageBox::selectable(this, "Confirm Delete", - "If you remove mods while the game is running it may crash your game.\n" - "Are you sure you want to do this?", + auto response = CustomMessageBox::selectable(this, tr("Confirm Delete"), + tr("If you remove mods while the game is running it may crash your game.\n" + "Are you sure you want to do this?"), QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ->exec(); @@ -204,7 +225,7 @@ void ModFolderPage::installMods() } } -void ModFolderPage::updateMods() +void ModFolderPage::updateMods(bool includeDeps) { if (m_instance->typeName() != "Minecraft") return; // this is a null instance or a legacy instance @@ -214,6 +235,22 @@ void ModFolderPage::updateMods() QMessageBox::critical(this, tr("Error"), tr("Please install a mod loader first!")); return; } + if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) { + QMessageBox::critical(this, tr("Error"), tr("Mod updates are unavailable when metadata is disabled!")); + return; + } + if (m_instance != nullptr && m_instance->isRunning()) { + auto response = + CustomMessageBox::selectable(this, tr("Confirm Update"), + tr("If you update mods while the game is running may cause mod duplication and game crashes.\n" + "The old files may not be deleted as they are in use.\n" + "Are you sure you want to do this?"), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); auto mods_list = m_model->selectedMods(selection); @@ -221,7 +258,7 @@ void ModFolderPage::updateMods() if (use_all) mods_list = m_model->allMods(); - ModUpdateDialog update_dialog(this, m_instance, m_model, mods_list); + ModUpdateDialog update_dialog(this, m_instance, m_model, mods_list, includeDeps); update_dialog.checkCandidates(); if (update_dialog.aborted()) { diff --git a/launcher/ui/pages/instance/ModFolderPage.h b/launcher/ui/pages/instance/ModFolderPage.h index 0c654d0d1..4672350c6 100644 --- a/launcher/ui/pages/instance/ModFolderPage.h +++ b/launcher/ui/pages/instance/ModFolderPage.h @@ -64,7 +64,7 @@ class ModFolderPage : public ExternalResourcesPage { void deleteModMetadata(); void installMods(); - void updateMods(); + void updateMods(bool includeDeps = false); void visitModPages(); protected: diff --git a/launcher/ui/pages/instance/OtherLogsPage.h b/launcher/ui/pages/instance/OtherLogsPage.h index 4b3b122b0..de42f5a23 100644 --- a/launcher/ui/pages/instance/OtherLogsPage.h +++ b/launcher/ui/pages/instance/OtherLogsPage.h @@ -65,7 +65,7 @@ class OtherLogsPage : public QWidget, public BasePage { private slots: void populateSelectLogBox(); - void on_selectLogBox_currentIndexChanged(const int index); + void on_selectLogBox_currentIndexChanged(int index); void on_btnReload_clicked(); void on_btnPaste_clicked(); void on_btnCopy_clicked(); @@ -78,7 +78,7 @@ class OtherLogsPage : public QWidget, public BasePage { void findPreviousActivated(); private: - void setControlsEnabled(const bool enabled); + void setControlsEnabled(bool enabled); private: Ui::OtherLogsPage* ui; diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h index 5a43e49a6..f3660dd48 100644 --- a/launcher/ui/pages/modplatform/ModPage.h +++ b/launcher/ui/pages/modplatform/ModPage.h @@ -49,9 +49,7 @@ class ModPage : public ResourcePage { [[nodiscard]] QMap urlHandlers() const override; - void addResourceToPage(ModPlatform::IndexedPack::Ptr, - ModPlatform::IndexedVersion&, - const std::shared_ptr) override; + void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, std::shared_ptr) override; virtual auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, diff --git a/launcher/ui/pages/modplatform/ResourceModel.h b/launcher/ui/pages/modplatform/ResourceModel.h index ecf4f8f79..12db49080 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.h +++ b/launcher/ui/pages/modplatform/ResourceModel.h @@ -88,7 +88,7 @@ class ResourceModel : public QAbstractListModel { void addPack(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion& version, - const std::shared_ptr packs, + std::shared_ptr packs, bool is_indexed = false, QString custom_target_folder = {}); void removePack(const QString& rem); diff --git a/launcher/ui/pages/modplatform/ResourcePage.h b/launcher/ui/pages/modplatform/ResourcePage.h index 7bec0a375..235b44412 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.h +++ b/launcher/ui/pages/modplatform/ResourcePage.h @@ -78,7 +78,7 @@ class ResourcePage : public QWidget, public BasePage { void addResourceToDialog(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&); void removeResourceFromDialog(const QString& pack_name); virtual void removeResourceFromPage(const QString& name); - virtual void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, const std::shared_ptr); + virtual void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, std::shared_ptr); QList selectedPacks() { return m_model->selectedPacks(); } bool hasSelectedPacks() { return !(m_model->selectedPacks().isEmpty()); } diff --git a/launcher/ui/pages/modplatform/ShaderPackPage.h b/launcher/ui/pages/modplatform/ShaderPackPage.h index fcf6d4a7c..c29317e15 100644 --- a/launcher/ui/pages/modplatform/ShaderPackPage.h +++ b/launcher/ui/pages/modplatform/ShaderPackPage.h @@ -38,9 +38,7 @@ class ShaderPackResourcePage : public ResourcePage { [[nodiscard]] bool supportsFiltering() const override { return false; }; - void addResourceToPage(ModPlatform::IndexedPack::Ptr, - ModPlatform::IndexedVersion&, - const std::shared_ptr) override; + void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, std::shared_ptr) override; [[nodiscard]] QMap urlHandlers() const override; diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.h b/launcher/ui/pages/modplatform/flame/FlameModel.h index 5a07ef6bb..9b6d70fec 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.h +++ b/launcher/ui/pages/modplatform/flame/FlameModel.h @@ -38,7 +38,7 @@ class ListModel : public QAbstractListModel { void fetchMore(const QModelIndex& parent) override; void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback); - void searchWithTerm(const QString& term, const int sort); + void searchWithTerm(const QString& term, int sort); [[nodiscard]] bool hasActiveSearchJob() const { return jobPtr && jobPtr->isRunning(); } [[nodiscard]] Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? jobPtr : nullptr; } diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h index 2a9d62261..514ee4484 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h @@ -71,7 +71,7 @@ class ModpackListModel : public QAbstractListModel { /* Ask the API for more information */ void fetchMore(const QModelIndex& parent) override; void refresh(); - void searchWithTerm(const QString& term, const int sort); + void searchWithTerm(const QString& term, int sort); [[nodiscard]] bool hasActiveSearchJob() const { return jobPtr && jobPtr->isRunning(); } [[nodiscard]] Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? jobPtr : nullptr; } diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index d2d921918..bd6b6b118 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -80,7 +80,7 @@ void JavaSettingsWidget::setupUi() m_minMemSpinBox = new QSpinBox(m_memoryGroupBox); m_minMemSpinBox->setObjectName(QStringLiteral("minMemSpinBox")); m_minMemSpinBox->setSuffix(QStringLiteral(" MiB")); - m_minMemSpinBox->setMinimum(128); + m_minMemSpinBox->setMinimum(8); m_minMemSpinBox->setMaximum(1048576); m_minMemSpinBox->setSingleStep(128); m_labelMinMem->setBuddy(m_minMemSpinBox); @@ -93,7 +93,7 @@ void JavaSettingsWidget::setupUi() m_maxMemSpinBox = new QSpinBox(m_memoryGroupBox); m_maxMemSpinBox->setObjectName(QStringLiteral("maxMemSpinBox")); m_maxMemSpinBox->setSuffix(QStringLiteral(" MiB")); - m_maxMemSpinBox->setMinimum(128); + m_maxMemSpinBox->setMinimum(8); m_maxMemSpinBox->setMaximum(1048576); m_maxMemSpinBox->setSingleStep(128); m_labelMaxMem->setBuddy(m_maxMemSpinBox); @@ -112,7 +112,7 @@ void JavaSettingsWidget::setupUi() m_permGenSpinBox = new QSpinBox(m_memoryGroupBox); m_permGenSpinBox->setObjectName(QStringLiteral("permGenSpinBox")); m_permGenSpinBox->setSuffix(QStringLiteral(" MiB")); - m_permGenSpinBox->setMinimum(64); + m_permGenSpinBox->setMinimum(4); m_permGenSpinBox->setMaximum(1048576); m_permGenSpinBox->setSingleStep(8); m_gridLayout_2->addWidget(m_permGenSpinBox, 2, 1, 1, 1); diff --git a/launcher/ui/widgets/LanguageSelectionWidget.cpp b/launcher/ui/widgets/LanguageSelectionWidget.cpp index 37d053478..481547b5b 100644 --- a/launcher/ui/widgets/LanguageSelectionWidget.cpp +++ b/launcher/ui/widgets/LanguageSelectionWidget.cpp @@ -76,7 +76,7 @@ void LanguageSelectionWidget::languageRowChanged(const QModelIndex& current, con translations->updateLanguage(key); } -void LanguageSelectionWidget::languageSettingChanged(const Setting&, const QVariant) +void LanguageSelectionWidget::languageSettingChanged(const Setting&, const QVariant&) { auto translations = APPLICATION->translations(); auto index = translations->selectedIndex(); diff --git a/launcher/ui/widgets/LanguageSelectionWidget.h b/launcher/ui/widgets/LanguageSelectionWidget.h index 5e86a288f..f034853dd 100644 --- a/launcher/ui/widgets/LanguageSelectionWidget.h +++ b/launcher/ui/widgets/LanguageSelectionWidget.h @@ -34,7 +34,7 @@ class LanguageSelectionWidget : public QWidget { protected slots: void languageRowChanged(const QModelIndex& current, const QModelIndex& previous); - void languageSettingChanged(const Setting&, const QVariant); + void languageSettingChanged(const Setting&, const QVariant&); private: QVBoxLayout* verticalLayout = nullptr; diff --git a/launcher/ui/widgets/ModListView.cpp b/launcher/ui/widgets/ModListView.cpp index c72d4c522..a38c7c86a 100644 --- a/launcher/ui/widgets/ModListView.cpp +++ b/launcher/ui/widgets/ModListView.cpp @@ -48,14 +48,14 @@ void ModListView::setModel(QAbstractItemModel* model) return; } if (!string.size()) { - head->setSectionResizeMode(0, QHeaderView::ResizeToContents); + head->setSectionResizeMode(0, QHeaderView::Interactive); head->setSectionResizeMode(1, QHeaderView::Stretch); for (int i = 2; i < head->count(); i++) - head->setSectionResizeMode(i, QHeaderView::ResizeToContents); + head->setSectionResizeMode(i, QHeaderView::Interactive); } else { head->setSectionResizeMode(0, QHeaderView::Stretch); for (int i = 1; i < head->count(); i++) - head->setSectionResizeMode(i, QHeaderView::ResizeToContents); + head->setSectionResizeMode(i, QHeaderView::Interactive); } }