diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bbfe4ab44..c710d54bd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -586,33 +586,3 @@ jobs: with: bundle: "Prism Launcher.flatpak" manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml - - nix: - runs-on: ubuntu-latest - strategy: - matrix: - package: - - prismlauncher - - prismlauncher-qt5 - steps: - - name: Clone repository - if: inputs.build_type == 'Debug' - uses: actions/checkout@v3 - with: - submodules: 'true' - - name: Install nix - if: inputs.build_type == 'Debug' - uses: cachix/install-nix-action@v22 - with: - install_url: https://nixos.org/nix/install - extra_nix_config: | - auto-optimise-store = true - experimental-features = nix-command flakes - - uses: cachix/cachix-action@v12 - if: inputs.build_type == 'Debug' - with: - name: prismlauncher - authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - name: Build - if: inputs.build_type == 'Debug' - run: nix build .#${{ matrix.package }} --print-build-logs diff --git a/CMakeLists.txt b/CMakeLists.txt index 41634f68a..350189c87 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -318,6 +318,8 @@ add_subdirectory(program_info) ####################################### Install layout ####################################### +set(Launcher_ENABLE_UPDATER NO) + if(NOT (UNIX AND APPLE)) # Install "portable.txt" if selected component is "portable" install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_Portable_File}" DESTINATION "." COMPONENT portable EXCLUDE_FROM_ALL) @@ -342,9 +344,9 @@ if(UNIX AND APPLE) set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns) - set(MACOSX_BUNDLE_COPYRIGHT "© 2022 ${Launcher_Copyright_Mac}") - set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=") - set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml") + set(MACOSX_BUNDLE_COPYRIGHT "© 2022-2023 ${Launcher_Copyright_Mac}") + set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=" CACHE STRING "Public key for Sparkle update feed") + set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml" CACHE STRING "URL for Sparkle update feed") set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0/Sparkle-2.1.0.tar.xz" CACHE STRING "URL to Sparkle release archive") set(MACOSX_SPARKLE_SHA256 "bf6ac1caa9f8d321d5784859c88da874f28412f37fb327bc21b7b14c5d61ef94" CACHE STRING "SHA256 checksum for Sparkle release archive") @@ -353,8 +355,12 @@ if(UNIX AND APPLE) # directories to look for dependencies set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${MACOSX_SPARKLE_DIR}) + if(NOT MACOSX_SPARKLE_UPDATE_PUBLIC_KEY STREQUAL "" AND NOT MACOSX_SPARKLE_UPDATE_FEED_URL STREQUAL "") + set(Launcher_ENABLE_UPDATER YES) + endif() + # install as bundle - set(INSTALL_BUNDLE "full") + set(INSTALL_BUNDLE "full" CACHE STRING "Use fixup_bundle to bundle dependencies") # Add the icon install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns) @@ -367,7 +373,7 @@ elseif(UNIX) set(JARS_DEST_DIR "share/${Launcher_Name}") # install as bundle with no dependencies included - set(INSTALL_BUNDLE "nodeps") + set(INSTALL_BUNDLE "nodeps" CACHE STRING "Use fixup_bundle to bundle dependencies") # Set RPATH SET(Launcher_BINARY_RPATH "$ORIGIN/") @@ -401,7 +407,7 @@ elseif(WIN32) set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) # install as bundle - set(INSTALL_BUNDLE "full") + set(INSTALL_BUNDLE "full" CACHE STRING "Use fixup_bundle to bundle dependencies") else() message(FATAL_ERROR "Platform not supported") endif() diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b2795ce49..072916772 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,7 @@ In an effort to ensure that the code you contribute is actually compatible with This can be done by appending `-s` to your `git commit` call, or by manually appending the following text to your commit message: -``` +```text Signed-off-by: Author name @@ -27,7 +27,7 @@ Signed-off-by: Author name By signing off your work, you agree to the terms below: -``` +```text Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: @@ -62,7 +62,6 @@ As a bonus, you can also [cryptographically sign your commits][gh-signing-commit [gh-signing-commits]: https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits [gh-vigilant-mode]: https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits - ## Backporting to Release Branches We use [automated backports](https://github.com/PrismLauncher/PrismLauncher/blob/develop/.github/workflows/backport.yml) to merge specific contributions from develop into `release` branches. diff --git a/README.md b/README.md index 993f02f5d..641622b5c 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Feel free to create a GitHub issue if you find a bug or want to suggest a new fe - **Our Matrix space:** -[![PrismLauncher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge&label=Matrix%20Space&logo=matrix&color=purple)](https://prismlauncher.org/matrix) +[![Prism Launcher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge&label=Matrix%20Space&logo=matrix&color=purple)](https://prismlauncher.org/matrix) - **Our Subreddit:** @@ -50,7 +50,7 @@ Feel free to create a GitHub issue if you find a bug or want to suggest a new fe ## 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 +The translation effort for Prism Launcher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at ## Building @@ -82,14 +82,16 @@ Thanks to the awesome people over at [MacStadium](https://www.macstadium.com/), ## 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: +You are free to fork, redistribute and provide custom builds 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 (). -- 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). +- Make it clear that your fork is not Prism Launcher and is not endorsed by or affiliated with the Prism Launcher project (). +- Go through [CMakeLists.txt](CMakeLists.txt) and change Prism Launcher'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: +If you are just building Prism Launcher for your distribution, please make sure to set the `Launcher_BUILD_PLATFORM` to a slug representing your distribution. Examples are `archlinux`, `fedora` and `nixpkgs`. + +Note 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) diff --git a/flake.lock b/flake.lock index 370250c4b..af7ecae7c 100644 --- a/flake.lock +++ b/flake.lock @@ -76,11 +76,11 @@ "libnbtplusplus": { "flake": false, "locked": { - "lastModified": 1650031308, - "narHash": "sha256-TvVOjkUobYJD9itQYueELJX3wmecvEdCbJ0FinW2mL4=", + "lastModified": 1690036783, + "narHash": "sha256-A5kTgICnx+Qdq3Fir/bKTfdTt/T1NQP2SC+nhN1ENug=", "owner": "PrismLauncher", "repo": "libnbtplusplus", - "rev": "2203af7eeb48c45398139b583615134efd8d407f", + "rev": "a5e8fd52b8bf4ab5d5bcc042b2a247867589985f", "type": "github" }, "original": { @@ -91,11 +91,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1689413807, - "narHash": "sha256-exuzOvOhGAEKWQKwDuZAL4N8a1I837hH5eocaTcIbLc=", + "lastModified": 1690630721, + "narHash": "sha256-Y04onHyBQT4Erfr2fc82dbJTfXGYrf4V0ysLUYnPOP8=", "owner": "nixos", "repo": "nixpkgs", - "rev": "46ed466081b9cad1125b11f11a2af5cc40b942c7", + "rev": "d2b52322f35597c62abf56de91b0236746b2a03d", "type": "github" }, "original": { @@ -138,11 +138,11 @@ ] }, "locked": { - "lastModified": 1689328505, - "narHash": "sha256-9B3+OeUn1a/CvzE3GW6nWNwS5J7PDHTyHGlpL3wV5oA=", + "lastModified": 1690628027, + "narHash": "sha256-OTSbA2hM6VmxyZ/4siYPANffMBzIsKu04GLjXcv8ST0=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "5e28316db471d1ac234beb70031b635437421dd6", + "rev": "1e2443dd3f669eb65433b2fc26a3065e05a7dc9c", "type": "github" }, "original": { diff --git a/flatpak/org.prismlauncher.PrismLauncher.yml b/flatpak/org.prismlauncher.PrismLauncher.yml index a9b9762f5..46b6da36a 100644 --- a/flatpak/org.prismlauncher.PrismLauncher.yml +++ b/flatpak/org.prismlauncher.PrismLauncher.yml @@ -18,6 +18,8 @@ finish-args: - --filesystem=xdg-run/app/com.discordapp.Discord:create # Mod drag&drop - --filesystem=xdg-download:ro + # FTBApp import + - --filesystem=~/.ftba:ro cleanup: - /lib/libGLU* diff --git a/garnix.yaml b/garnix.yaml new file mode 100644 index 000000000..3bf145248 --- /dev/null +++ b/garnix.yaml @@ -0,0 +1,6 @@ +builds: + exclude: [] + include: + - "checks.x86_64-linux.*" + - "devShells.*.*" + - "packages.*.*" diff --git a/launcher/Application.cpp b/launcher/Application.cpp index bbea34c76..d6f1af27c 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -131,7 +131,7 @@ #include "MangoHud.h" #endif -#ifdef Q_OS_MAC +#if defined(Q_OS_MAC) && defined(SPARKLE_ENABLED) #include "updater/MacSparkleUpdater.h" #endif @@ -281,7 +281,16 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) } else { - QDir foo(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "..")); + QDir foo; + if (DesktopServices::isSnap()) + { + foo = QDir(getenv("SNAP_USER_COMMON")); + } + else + { + foo = QDir(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "..")); + } + dataPath = foo.absolutePath(); adjustedBy = "Persistent data path"; @@ -631,9 +640,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("ShowGlobalGameTime", true); m_settings->registerSetting("RecordGameTime", true); - // Minecraft launch method - m_settings->registerSetting("MCLaunchMethod", "LauncherPart"); - // Minecraft mods m_settings->registerSetting("ModMetadataDisabled", false); @@ -707,7 +713,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) QUrl metaUrl(m_settings->get("MetaURLOverride").toString()); // get rid of invalid meta urls - if (!metaUrl.isValid() || metaUrl.scheme() != "http" || metaUrl.scheme() != "https") + if (!metaUrl.isValid() || (metaUrl.scheme() != "http" && metaUrl.scheme() != "https")) m_settings->reset("MetaURLOverride"); } @@ -779,7 +785,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) if(BuildConfig.UPDATER_ENABLED) { qDebug() << "Initializing updater"; -#ifdef Q_OS_MAC +#if defined(Q_OS_MAC) && defined(SPARKLE_ENABLED) m_updater.reset(new MacSparkleUpdater()); #endif qDebug() << "<> Updater started."; @@ -1189,7 +1195,17 @@ QIcon Application::getThemedIcon(const QString& name) return QIcon::fromTheme(name); } -bool Application::openJsonEditor(const QString &filename) +QList Application::getValidCatPacks() +{ + return m_themeManager->getValidCatPacks(); +} + +QString Application::getCatPack(QString catName) +{ + return m_themeManager->getCatPack(catName); +} + +bool Application::openJsonEditor(const QString& filename) { const QString file = QDir::current().absoluteFilePath(filename); if (m_settings->get("JsonEditor").toString().isEmpty()) diff --git a/launcher/Application.h b/launcher/Application.h index 527c536b3..c0a980b2e 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -48,6 +48,7 @@ #include #include "minecraft/launch/MinecraftServerTarget.h" +#include "ui/themes/CatPack.h" class LaunchController; class LocalPeer; @@ -126,9 +127,11 @@ public: void setApplicationTheme(const QString& name); - shared_qobject_ptr updater() { - return m_updater; - } + QList getValidCatPacks(); + + QString getCatPack(QString catName = ""); + + shared_qobject_ptr updater() { return m_updater; } void triggerUpdateCheck(); diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 7cba97b4f..824cf80eb 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -262,8 +262,6 @@ set(MINECRAFT_SOURCES minecraft/launch/CreateGameFolders.h minecraft/launch/ModMinecraftJar.cpp minecraft/launch/ModMinecraftJar.h - minecraft/launch/DirectJavaLaunch.cpp - minecraft/launch/DirectJavaLaunch.h minecraft/launch/ExtractNatives.cpp minecraft/launch/ExtractNatives.h minecraft/launch/LauncherPartLaunch.cpp @@ -501,6 +499,11 @@ set(FTB_SOURCES modplatform/legacy_ftb/PrivatePackManager.cpp modplatform/legacy_ftb/PackHelpers.h + + modplatform/import_ftb/PackInstallTask.h + modplatform/import_ftb/PackInstallTask.cpp + modplatform/import_ftb/PackHelpers.h + modplatform/import_ftb/PackHelpers.cpp ) set(FLAME_SOURCES @@ -668,7 +671,7 @@ set(LOGIC_SOURCES ${ATLAUNCHER_SOURCES} ) -if(APPLE) +if(APPLE AND Launcher_ENABLE_UPDATER) set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES}) endif() @@ -761,6 +764,8 @@ SET(LAUNCHER_SOURCES ui/themes/SystemTheme.h ui/themes/ThemeManager.cpp ui/themes/ThemeManager.h + ui/themes/CatPack.cpp + ui/themes/CatPack.h # Processes LaunchController.h @@ -870,6 +875,11 @@ SET(LAUNCHER_SOURCES ui/pages/modplatform/legacy_ftb/ListModel.h ui/pages/modplatform/legacy_ftb/ListModel.cpp + ui/pages/modplatform/import_ftb/ImportFTBPage.cpp + ui/pages/modplatform/import_ftb/ImportFTBPage.h + ui/pages/modplatform/import_ftb/ListModel.h + ui/pages/modplatform/import_ftb/ListModel.cpp + ui/pages/modplatform/flame/FlameModel.cpp ui/pages/modplatform/flame/FlameModel.h ui/pages/modplatform/flame/FlamePage.cpp @@ -1044,6 +1054,7 @@ qt_wrap_ui(LAUNCHER_UI ui/pages/modplatform/ResourcePage.ui ui/pages/modplatform/flame/FlamePage.ui ui/pages/modplatform/legacy_ftb/Page.ui + ui/pages/modplatform/import_ftb/ImportFTBPage.ui ui/pages/modplatform/ImportPage.ui ui/pages/modplatform/modrinth/ModrinthPage.ui ui/pages/modplatform/technic/TechnicPage.ui @@ -1139,17 +1150,23 @@ if(APPLE) set(CMAKE_MACOSX_RPATH 1) set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/") - file(DOWNLOAD ${MACOSX_SPARKLE_DOWNLOAD_URL} ${CMAKE_BINARY_DIR}/Sparkle.tar.xz EXPECTED_HASH SHA256=${MACOSX_SPARKLE_SHA256}) - file(ARCHIVE_EXTRACT INPUT ${CMAKE_BINARY_DIR}/Sparkle.tar.xz DESTINATION ${CMAKE_BINARY_DIR}/frameworks/Sparkle) + if(Launcher_ENABLE_UPDATER) + file(DOWNLOAD ${MACOSX_SPARKLE_DOWNLOAD_URL} ${CMAKE_BINARY_DIR}/Sparkle.tar.xz EXPECTED_HASH SHA256=${MACOSX_SPARKLE_SHA256}) + file(ARCHIVE_EXTRACT INPUT ${CMAKE_BINARY_DIR}/Sparkle.tar.xz DESTINATION ${CMAKE_BINARY_DIR}/frameworks/Sparkle) + + find_library(SPARKLE_FRAMEWORK Sparkle "${CMAKE_BINARY_DIR}/frameworks/Sparkle") + add_compile_definitions(SPARKLE_ENABLED) + endif() - find_library(SPARKLE_FRAMEWORK Sparkle "${CMAKE_BINARY_DIR}/frameworks/Sparkle") target_link_libraries(Launcher_logic "-framework AppKit" "-framework Carbon" "-framework Foundation" "-framework ApplicationServices" ) - target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK}) + if(Launcher_ENABLE_UPDATER) + target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK}) + endif() endif() target_link_libraries(Launcher_logic) @@ -1211,7 +1228,7 @@ if(WIN32) ) endif() -if (UNIX AND APPLE) +if (UNIX AND APPLE AND Launcher_ENABLE_UPDATER) # Add Sparkle updater # It has to be copied here instead of just allowing fixup_bundle to install it, otherwise essential parts of # the framework aren't installed diff --git a/launcher/DesktopServices.cpp b/launcher/DesktopServices.cpp index 2984a1b4f..81362f099 100644 --- a/launcher/DesktopServices.cpp +++ b/launcher/DesktopServices.cpp @@ -118,7 +118,7 @@ bool openDirectory(const QString &path, bool ensureExists) return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); }; #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) - if(!isFlatpak()) + if(!isSandbox()) { return IndirectOpen(f); } @@ -139,7 +139,7 @@ bool openFile(const QString &path) return QDesktopServices::openUrl(QUrl::fromLocalFile(path)); }; #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) - if(!isFlatpak()) + if(!isSandbox()) { return IndirectOpen(f); } @@ -157,7 +157,7 @@ bool openFile(const QString &application, const QString &path, const QString &wo qDebug() << "Opening file" << path << "using" << application; #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave - if(!isFlatpak()) + if(!isSandbox()) { return IndirectOpen([&]() { @@ -177,7 +177,7 @@ bool run(const QString &application, const QStringList &args, const QString &wor { qDebug() << "Running" << application << "with args" << args.join(' '); #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) - if(!isFlatpak()) + if(!isSandbox()) { // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave return IndirectOpen([&]() @@ -202,7 +202,7 @@ bool openUrl(const QUrl &url) return QDesktopServices::openUrl(url); }; #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) - if(!isFlatpak()) + if(!isSandbox()) { return IndirectOpen(f); } @@ -224,4 +224,18 @@ bool isFlatpak() #endif } +bool isSnap() +{ +#ifdef Q_OS_LINUX + return getenv("SNAP"); +#else + return false; +#endif +} + +bool isSandbox() +{ + return isSnap() || isFlatpak(); +} + } diff --git a/launcher/DesktopServices.h b/launcher/DesktopServices.h index 21c9cae0b..b1948cc2b 100644 --- a/launcher/DesktopServices.h +++ b/launcher/DesktopServices.h @@ -34,5 +34,18 @@ namespace DesktopServices */ bool openUrl(const QUrl &url); + /** + * Determine whether the launcher is running in a Flatpak environment + */ bool isFlatpak(); + + /** + * Determine whether the launcher is running in a Snap environment + */ + bool isSnap(); + + /** + * Determine whether the launcher is running in a sandboxed (Flatpak or Snap) environment + */ + bool isSandbox(); } diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 352848f02..d6a96deb1 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -99,7 +99,7 @@ void InstanceImportTask::executeTask() connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded); connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged); - connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propogateStepProgress); + connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress); connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed); connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted); @@ -293,7 +293,7 @@ void InstanceImportTask::processFlame() }); connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed); connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress); - connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress); + connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress); connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus); connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails); @@ -385,7 +385,7 @@ void InstanceImportTask::processModrinth() }); connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed); connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress); - connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress); + connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress); connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus); connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails); connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater); diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index b4c520cd9..0485db19e 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -799,7 +799,7 @@ class InstanceStaging : public Task { connect(child, &Task::status, this, &InstanceStaging::setStatus); connect(child, &Task::details, this, &InstanceStaging::setDetails); connect(child, &Task::progress, this, &InstanceStaging::setProgress); - connect(child, &Task::stepProgress, this, &InstanceStaging::propogateStepProgress); + connect(child, &Task::stepProgress, this, &InstanceStaging::propagateStepProgress); connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded); } diff --git a/launcher/ResourceDownloadTask.cpp b/launcher/ResourceDownloadTask.cpp index 06c03c779..8bb9b64e9 100644 --- a/launcher/ResourceDownloadTask.cpp +++ b/launcher/ResourceDownloadTask.cpp @@ -54,7 +54,7 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, m_filesNetJob->addNetAction(Net::Download::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename()))); connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded); connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged); - connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propogateStepProgress); + connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propagateStepProgress); connect(m_filesNetJob.get(), &NetJob::failed, this, &ResourceDownloadTask::downloadFailed); addTask(m_filesNetJob); diff --git a/launcher/launch/steps/Update.cpp b/launcher/launch/steps/Update.cpp index 77c8a18ea..8df2bc67c 100644 --- a/launcher/launch/steps/Update.cpp +++ b/launcher/launch/steps/Update.cpp @@ -28,7 +28,7 @@ void Update::executeTask() { connect(m_updateTask.get(), &Task::finished, this, &Update::updateFinished); connect(m_updateTask.get(), &Task::progress, this, &Update::setProgress); - connect(m_updateTask.get(), &Task::stepProgress, this, &Update::propogateStepProgress); + connect(m_updateTask.get(), &Task::stepProgress, this, &Update::propagateStepProgress); connect(m_updateTask.get(), &Task::status, this, &Update::setStatus); connect(m_updateTask.get(), &Task::details, this, &Update::setDetails); emit progressReportingRequest(); diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index b4b0b4aa3..7291efb46 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -61,7 +61,6 @@ #include "launch/steps/QuitAfterGameStop.h" #include "minecraft/launch/LauncherPartLaunch.h" -#include "minecraft/launch/DirectJavaLaunch.h" #include "minecraft/launch/ModMinecraftJar.h" #include "minecraft/launch/ClaimAccount.h" #include "minecraft/launch/ReconstructAssets.h" @@ -167,10 +166,6 @@ void MinecraftInstance::loadSpecificSettings() m_settings->registerOverride(global_settings->getSetting("MaxMemAlloc"), memorySetting); m_settings->registerOverride(global_settings->getSetting("PermGen"), memorySetting); - // Minecraft launch method - auto launchMethodOverride = m_settings->registerSetting("OverrideMCLaunchMethod", false); - m_settings->registerOverride(global_settings->getSetting("MCLaunchMethod"), launchMethodOverride); - // Native library workarounds auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false); m_settings->registerOverride(global_settings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride); @@ -858,7 +853,7 @@ QMap MinecraftInstance::createCensorFilterFromSession(AuthSess { addToFilter(sessionRef.session, tr("")); } - if (sessionRef.access_token != "offline") { + if (sessionRef.access_token != "0") { addToFilter(sessionRef.access_token, tr("")); } if(sessionRef.client_token.size()) { @@ -1005,15 +1000,6 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt process->appendStep(makeShared(pptr)); } - // check launch method - QStringList validMethods = {"LauncherPart", "DirectJava"}; - QString method = launchMethod(); - if(!validMethods.contains(method)) - { - process->appendStep(makeShared(pptr, "Selected launch method \"" + method + "\" is not valid.\n", MessageLevel::Fatal)); - return process; - } - // create the .minecraft folder and server-resource-packs (workaround for Minecraft bug MCL-3732) { process->appendStep(makeShared(pptr)); @@ -1087,23 +1073,11 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt { // actually launch the game - auto method = launchMethod(); - if(method == "LauncherPart") - { - auto step = makeShared(pptr); - step->setWorkingDirectory(gameRoot()); - step->setAuthSession(session); - step->setServerToJoin(serverToJoin); - process->appendStep(step); - } - else if (method == "DirectJava") - { - auto step = makeShared(pptr); - step->setWorkingDirectory(gameRoot()); - step->setAuthSession(session); - step->setServerToJoin(serverToJoin); - process->appendStep(step); - } + auto step = makeShared(pptr); + step->setWorkingDirectory(gameRoot()); + step->setAuthSession(session); + step->setServerToJoin(serverToJoin); + process->appendStep(step); } // run post-exit command if that's needed @@ -1126,11 +1100,6 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt return m_launchProcess; } -QString MinecraftInstance::launchMethod() -{ - return settings()->get("MCLaunchMethod").toString(); -} - JavaVersion MinecraftInstance::getJavaVersion() { return JavaVersion(settings()->get("JavaVersion").toString()); diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index 277b94528..879be633a 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -166,8 +166,6 @@ public: protected: QMap createCensorFilterFromSession(AuthSessionPtr session); - QStringList validLaunchMethods(); - QString launchMethod(); protected: // data std::shared_ptr m_components; diff --git a/launcher/minecraft/MinecraftLoadAndCheck.cpp b/launcher/minecraft/MinecraftLoadAndCheck.cpp index 1c3f6fb71..2a3698798 100644 --- a/launcher/minecraft/MinecraftLoadAndCheck.cpp +++ b/launcher/minecraft/MinecraftLoadAndCheck.cpp @@ -22,7 +22,7 @@ void MinecraftLoadAndCheck::executeTask() connect(m_task.get(), &Task::failed, this, &MinecraftLoadAndCheck::subtaskFailed); connect(m_task.get(), &Task::aborted, this, [this]{ subtaskFailed(tr("Aborted")); }); connect(m_task.get(), &Task::progress, this, &MinecraftLoadAndCheck::progress); - connect(m_task.get(), &Task::stepProgress, this, &MinecraftLoadAndCheck::propogateStepProgress); + connect(m_task.get(), &Task::stepProgress, this, &MinecraftLoadAndCheck::propagateStepProgress); connect(m_task.get(), &Task::status, this, &MinecraftLoadAndCheck::setStatus); } diff --git a/launcher/minecraft/MinecraftUpdate.cpp b/launcher/minecraft/MinecraftUpdate.cpp index 35430bb0f..236d0224b 100644 --- a/launcher/minecraft/MinecraftUpdate.cpp +++ b/launcher/minecraft/MinecraftUpdate.cpp @@ -100,7 +100,7 @@ void MinecraftUpdate::next() disconnect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed); disconnect(task.get(), &Task::aborted, this, &Task::abort); disconnect(task.get(), &Task::progress, this, &MinecraftUpdate::progress); - disconnect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propogateStepProgress); + disconnect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propagateStepProgress); disconnect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus); disconnect(task.get(), &Task::details, this, &MinecraftUpdate::setDetails); } @@ -120,7 +120,7 @@ void MinecraftUpdate::next() connect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed); connect(task.get(), &Task::aborted, this, &Task::abort); connect(task.get(), &Task::progress, this, &MinecraftUpdate::progress); - connect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propogateStepProgress); + connect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propagateStepProgress); connect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus); connect(task.get(), &Task::details, this, &MinecraftUpdate::setDetails); // if the task is already running, do not start it again diff --git a/launcher/minecraft/auth/AccountData.cpp b/launcher/minecraft/auth/AccountData.cpp index 44f7e2563..0b78cb0c1 100644 --- a/launcher/minecraft/auth/AccountData.cpp +++ b/launcher/minecraft/auth/AccountData.cpp @@ -374,6 +374,10 @@ bool AccountData::resumeStateFromV3(QJsonObject data) { } yggdrasilToken = tokenFromJSONV3(data, "ygg"); + // versions before 7.2 used "offline" as the offline token + if (yggdrasilToken.token == "offline") + yggdrasilToken.token = "0"; + minecraftProfile = profileFromJSONV3(data, "profile"); if(!entitlementFromJSONV3(data, minecraftEntitlement)) { if(minecraftProfile.validity != Katabasis::Validity::None) { diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index d7b061e5e..5d279af1c 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -94,7 +94,7 @@ MinecraftAccountPtr MinecraftAccount::createOffline(const QString &username) { auto account = makeShared(); account->data.type = AccountType::Offline; - account->data.yggdrasilToken.token = "offline"; + account->data.yggdrasilToken.token = "0"; account->data.yggdrasilToken.validity = Katabasis::Validity::Certain; account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc(); account->data.yggdrasilToken.extra["userName"] = username; diff --git a/launcher/minecraft/launch/DirectJavaLaunch.cpp b/launcher/minecraft/launch/DirectJavaLaunch.cpp deleted file mode 100644 index ca55cd2ed..000000000 --- a/launcher/minecraft/launch/DirectJavaLaunch.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "DirectJavaLaunch.h" - -#include - -#include -#include -#include -#include - -#include "Application.h" - -#ifdef Q_OS_LINUX -#include "gamemode_client.h" -#endif - -DirectJavaLaunch::DirectJavaLaunch(LaunchTask *parent) : LaunchStep(parent) -{ - connect(&m_process, &LoggedProcess::log, this, &DirectJavaLaunch::logLines); - connect(&m_process, &LoggedProcess::stateChanged, this, &DirectJavaLaunch::on_state); -} - -void DirectJavaLaunch::executeTask() -{ - auto instance = m_parent->instance(); - std::shared_ptr minecraftInstance = std::dynamic_pointer_cast(instance); - QStringList args = minecraftInstance->javaArguments(); - - args.append("-Djava.library.path=" + minecraftInstance->getNativePath()); - - auto classPathEntries = minecraftInstance->getClassPath(); - args.append("-cp"); - QString classpath; -#ifdef Q_OS_WIN32 - classpath = classPathEntries.join(';'); -#else - classpath = classPathEntries.join(':'); -#endif - args.append(classpath); - args.append(minecraftInstance->getMainClass()); - - QString allArgs = args.join(", "); - emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::Launcher); - - auto javaPath = FS::ResolveExecutable(instance->settings()->get("JavaPath").toString()); - - m_process.setProcessEnvironment(instance->createLaunchEnvironment()); - - // make detachable - this will keep the process running even if the object is destroyed - m_process.setDetachable(true); - - auto mcArgs = minecraftInstance->processMinecraftArgs(m_session, m_serverToJoin); - args.append(mcArgs); - - QString wrapperCommandStr = instance->getWrapperCommand().trimmed(); - if(!wrapperCommandStr.isEmpty()) - { - auto wrapperArgs = Commandline::splitArgs(wrapperCommandStr); - auto wrapperCommand = wrapperArgs.takeFirst(); - auto realWrapperCommand = QStandardPaths::findExecutable(wrapperCommand); - if (realWrapperCommand.isEmpty()) - { - const char *reason = QT_TR_NOOP("The wrapper command \"%1\" couldn't be found."); - emit logLine(QString(reason).arg(wrapperCommand), MessageLevel::Fatal); - emitFailed(tr(reason).arg(wrapperCommand)); - return; - } - emit logLine("Wrapper command is:\n" + wrapperCommandStr + "\n\n", MessageLevel::Launcher); - args.prepend(javaPath); - m_process.start(wrapperCommand, wrapperArgs + args); - } - else - { - m_process.start(javaPath, args); - } - -#ifdef Q_OS_LINUX - if (instance->settings()->get("EnableFeralGamemode").toBool() && APPLICATION->capabilities() & Application::SupportsGameMode) - { - auto pid = m_process.processId(); - if (pid) - { - gamemode_request_start_for(pid); - } - } -#endif -} - -void DirectJavaLaunch::on_state(LoggedProcess::State state) -{ - switch(state) - { - case LoggedProcess::FailedToStart: - { - //: Error message displayed if instance can't start - const char *reason = QT_TR_NOOP("Could not launch Minecraft!"); - emit logLine(reason, MessageLevel::Fatal); - emitFailed(tr(reason)); - return; - } - case LoggedProcess::Aborted: - case LoggedProcess::Crashed: - { - m_parent->setPid(-1); - emitFailed(tr("Game crashed.")); - return; - } - case LoggedProcess::Finished: - { - m_parent->setPid(-1); - // if the exit code wasn't 0, report this as a crash - auto exitCode = m_process.exitCode(); - if(exitCode != 0) - { - emitFailed(tr("Game crashed.")); - return; - } - //FIXME: make this work again - // m_postlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(exitCode)); - // run post-exit - emitSucceeded(); - break; - } - case LoggedProcess::Running: - emit logLine(QString("Minecraft process ID: %1\n\n").arg(m_process.processId()), MessageLevel::Launcher); - m_parent->setPid(m_process.processId()); - m_parent->instance()->setLastLaunch(); - break; - default: - break; - } -} - -void DirectJavaLaunch::setWorkingDirectory(const QString &wd) -{ - m_process.setWorkingDirectory(wd); -} - -void DirectJavaLaunch::proceed() -{ - // nil -} - -bool DirectJavaLaunch::abort() -{ - auto state = m_process.state(); - if (state == LoggedProcess::Running || state == LoggedProcess::Starting) - { - m_process.kill(); - } - return true; -} - diff --git a/launcher/minecraft/launch/DirectJavaLaunch.h b/launcher/minecraft/launch/DirectJavaLaunch.h deleted file mode 100644 index 58b119b8c..000000000 --- a/launcher/minecraft/launch/DirectJavaLaunch.h +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include "MinecraftServerTarget.h" - -class DirectJavaLaunch: public LaunchStep -{ - Q_OBJECT -public: - explicit DirectJavaLaunch(LaunchTask *parent); - virtual ~DirectJavaLaunch() {}; - - virtual void executeTask(); - virtual bool abort(); - virtual void proceed(); - virtual bool canAbort() const - { - return true; - } - void setWorkingDirectory(const QString &wd); - void setAuthSession(AuthSessionPtr session) - { - m_session = session; - } - - void setServerToJoin(MinecraftServerTargetPtr serverToJoin) - { - m_serverToJoin = std::move(serverToJoin); - } - -private slots: - void on_state(LoggedProcess::State state); - -private: - LoggedProcess m_process; - QString m_command; - AuthSessionPtr m_session; - MinecraftServerTargetPtr m_serverToJoin; -}; - diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp index 264019f84..60389753e 100644 --- a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp @@ -1,9 +1,9 @@ #include "LocalModParseTask.h" +#include #include #include #include -#include #include #include #include @@ -369,12 +369,11 @@ ModDetails ReadQuiltModInfo(QByteArray contents) details.icon_file = icon.toString(); } } - } return details; } -ModDetails ReadForgeInfo(QString fileName) +ModDetails ReadForgeInfo(QByteArray contents) { ModDetails details; // Read the data @@ -382,7 +381,7 @@ ModDetails ReadForgeInfo(QString fileName) details.mod_id = "Forge"; details.homeurl = "http://www.minecraftforge.net/forum/"; INIFile ini; - if (!ini.loadFile(fileName)) + if (!ini.loadFile(contents)) return details; QString major = ini.get("forge.major.number", "0").toString(); @@ -554,7 +553,7 @@ bool processZIP(Mod& mod, ProcessingLevel level) return false; } - details = ReadForgeInfo(file.getFileName()); + details = ReadForgeInfo(file.readAll()); file.close(); zip.close(); diff --git a/launcher/minecraft/update/AssetUpdateTask.cpp b/launcher/minecraft/update/AssetUpdateTask.cpp index 31fd5eb11..fda85ba8a 100644 --- a/launcher/minecraft/update/AssetUpdateTask.cpp +++ b/launcher/minecraft/update/AssetUpdateTask.cpp @@ -45,7 +45,7 @@ void AssetUpdateTask::executeTask() connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetIndexFailed); connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress); - connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propogateStepProgress); + connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propagateStepProgress); qDebug() << m_inst->name() << ": Starting asset index download"; downloadJob->start(); @@ -84,7 +84,7 @@ void AssetUpdateTask::assetIndexFinished() connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetsFailed); connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress); - connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propogateStepProgress); + connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propagateStepProgress); downloadJob->start(); return; } diff --git a/launcher/minecraft/update/FMLLibrariesTask.cpp b/launcher/minecraft/update/FMLLibrariesTask.cpp index 75e5c5720..d9fa0595d 100644 --- a/launcher/minecraft/update/FMLLibrariesTask.cpp +++ b/launcher/minecraft/update/FMLLibrariesTask.cpp @@ -75,7 +75,7 @@ void FMLLibrariesTask::executeTask() connect(dljob.get(), &NetJob::failed, this, &FMLLibrariesTask::fmllibsFailed); connect(dljob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); connect(dljob.get(), &NetJob::progress, this, &FMLLibrariesTask::progress); - connect(dljob.get(), &NetJob::stepProgress, this, &FMLLibrariesTask::propogateStepProgress); + connect(dljob.get(), &NetJob::stepProgress, this, &FMLLibrariesTask::propagateStepProgress); downloadJob.reset(dljob); downloadJob->start(); } diff --git a/launcher/minecraft/update/LibrariesTask.cpp b/launcher/minecraft/update/LibrariesTask.cpp index 415b9a660..9d1c02957 100644 --- a/launcher/minecraft/update/LibrariesTask.cpp +++ b/launcher/minecraft/update/LibrariesTask.cpp @@ -70,7 +70,7 @@ void LibrariesTask::executeTask() connect(downloadJob.get(), &NetJob::failed, this, &LibrariesTask::jarlibFailed); connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); connect(downloadJob.get(), &NetJob::progress, this, &LibrariesTask::progress); - connect(downloadJob.get(), &NetJob::stepProgress, this, &LibrariesTask::propogateStepProgress); + connect(downloadJob.get(), &NetJob::stepProgress, this, &LibrariesTask::propagateStepProgress); downloadJob->start(); } diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 22ea02da2..82a51c274 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -684,7 +684,7 @@ void PackInstallTask::installConfigs() abortable = true; setProgress(current, total); }); - connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress); + connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress); connect(jobPtr.get(), &NetJob::aborted, [&]{ abortable = false; jobPtr.reset(); @@ -852,7 +852,7 @@ void PackInstallTask::downloadMods() abortable = true; setProgress(current, total); }); - connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress); + connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress); connect(jobPtr.get(), &NetJob::aborted, [&] { abortable = false; diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp index 34bd401d3..ae168bbd6 100644 --- a/launcher/modplatform/flame/FileResolvingTask.cpp +++ b/launcher/modplatform/flame/FileResolvingTask.cpp @@ -52,7 +52,7 @@ void Flame::FileResolvingTask::executeTask() stepProgress(*step_progress); emitFailed(reason); }); - connect(m_dljob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propogateStepProgress); + connect(m_dljob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress); connect(m_dljob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) { qDebug() << "Resolve slug progress" << current << total; step_progress->update(current, total); @@ -118,7 +118,7 @@ void Flame::FileResolvingTask::netJobFinished() stepProgress(*step_progress); emitFailed(reason); }); - connect(m_checkJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propogateStepProgress); + connect(m_checkJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress); connect(m_checkJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) { qDebug() << "Resolve slug progress" << current << total; step_progress->update(current, total); @@ -195,7 +195,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished() stepProgress(*step_progress); emitFailed(reason); }); - connect(m_slugJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propogateStepProgress); + connect(m_slugJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress); connect(m_slugJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) { qDebug() << "Resolve slug progress" << current << total; step_progress->update(current, total); diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index b57db288a..e17cf1c2e 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -57,15 +57,11 @@ #include #include +#include "meta/Index.h" +#include "meta/VersionList.h" #include "minecraft/World.h" #include "minecraft/mod/tasks/LocalResourceParse.h" - -const static QMap forgemap = { { "1.2.5", "3.4.9.171" }, - { "1.4.2", "6.0.1.355" }, - { "1.4.7", "6.6.2.534" }, - { "1.5.2", "7.8.1.737" } }; - static const FlameAPI api; bool FlameCreationTask::abort() @@ -259,6 +255,56 @@ bool FlameCreationTask::updateInstance() return false; } +QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType, QString loaderVersion, QString mcVersion) +{ + if (loaderVersion == "recommended") { + auto vlist = APPLICATION->metadataIndex()->get(uid); + if (!vlist) { + setError(tr("Failed to get local metadata index for %1").arg(uid)); + return {}; + } + + if (!vlist->isLoaded()) { + QEventLoop loadVersionLoop; + auto task = vlist->getLoadTask(); + connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit); + if (!task->isRunning()) + task->start(); + + loadVersionLoop.exec(); + } + + for (auto version : vlist->versions()) { + // first recommended build we find, we use. + if (!version->isRecommended()) + continue; + auto reqs = version->requiredSet(); + + // filter by minecraft version, if the loader depends on a certain version. + // not all mod loaders depend on a given Minecraft version, so we won't do this + // filtering for those loaders. + if (loaderType == "forge") { + auto iter = std::find_if(reqs.begin(), reqs.end(), [mcVersion](const Meta::Require& req) { + return req.uid == "net.minecraft" && req.equalsVersion == mcVersion; + }); + if (iter == reqs.end()) + continue; + } + return version->descriptor(); + } + + setError(tr("Failed to find version for %1 loader").arg(loaderType)); + return {}; + } + + if (loaderVersion.isEmpty()) { + emitFailed(tr("No loader version set for modpack!")); + return {}; + } + + return loaderVersion; +} + bool FlameCreationTask::createInstance() { QEventLoop loop; @@ -297,22 +343,29 @@ bool FlameCreationTask::createInstance() } } - QString forgeVersion; - QString fabricVersion; - // TODO: is Quilt relevant here? + QString loaderType; + QString loaderUid; + QString loaderVersion; + for (auto& loader : m_pack.minecraft.modLoaders) { auto id = loader.id; if (id.startsWith("forge-")) { id.remove("forge-"); - forgeVersion = id; - continue; - } - if (id.startsWith("fabric-")) { + loaderType = "forge"; + loaderUid = "net.minecraftforge"; + } else if (loaderType == "fabric") { id.remove("fabric-"); - fabricVersion = id; + loaderType = "fabric"; + loaderUid = "net.fabricmc.fabric-loader"; + } else if (loaderType == "quilt") { + id.remove("quilt-"); + loaderType = "quilt"; + loaderUid = "org.quiltmc.quilt-loader"; + } else { + logWarning(tr("Unknown mod loader in manifest: %1").arg(id)); continue; } - logWarning(tr("Unknown mod loader in manifest: %1").arg(id)); + loaderVersion = id; } QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); @@ -329,19 +382,12 @@ bool FlameCreationTask::createInstance() auto components = instance.getPackProfile(); components->buildingFromScratch(); components->setComponentVersion("net.minecraft", mcVersion, true); - if (!forgeVersion.isEmpty()) { - // FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata. - if (forgeVersion == "recommended") { - if (forgemap.contains(mcVersion)) { - forgeVersion = forgemap[mcVersion]; - } else { - logWarning(tr("Could not map recommended Forge version for Minecraft %1").arg(mcVersion)); - } - } - components->setComponentVersion("net.minecraftforge", forgeVersion); + if (!loaderType.isEmpty()) { + auto version = getVersionForLoader(loaderUid, loaderType, loaderVersion, mcVersion); + if (version.isEmpty()) + return false; + components->setComponentVersion(loaderUid, version); } - if (!fabricVersion.isEmpty()) - components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion); if (m_instIcon != "default") { instance.setIconKey(m_instIcon); @@ -386,7 +432,7 @@ bool FlameCreationTask::createInstance() }); connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::progress, this, &FlameCreationTask::setProgress); connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::status, this, &FlameCreationTask::setStatus); - connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::stepProgress, this, &FlameCreationTask::propogateStepProgress); + connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::stepProgress, this, &FlameCreationTask::propagateStepProgress); connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::details, this, &FlameCreationTask::setDetails); m_mod_id_resolver->start(); @@ -502,11 +548,11 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) m_files_job.reset(); setError(reason); }); - connect(m_files_job.get(), &NetJob::progress, this, [this](qint64 current, qint64 total){ + connect(m_files_job.get(), &NetJob::progress, this, [this](qint64 current, qint64 total) { setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); setProgress(current, total); }); - connect(m_files_job.get(), &NetJob::stepProgress, this, &FlameCreationTask::propogateStepProgress); + connect(m_files_job.get(), &NetJob::stepProgress, this, &FlameCreationTask::propagateStepProgress); connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit); setStatus(tr("Downloading mods...")); @@ -545,7 +591,6 @@ void FlameCreationTask::copyBlockedMods(QList const& blocked_mods) setAbortable(true); } - void FlameCreationTask::validateZIPResouces() { qDebug() << "Validating whether resources stored as .zip are in the right place"; @@ -569,7 +614,7 @@ void FlameCreationTask::validateZIPResouces() return localPath; }; - auto installWorld = [this](QString worldPath){ + auto installWorld = [this](QString worldPath) { qDebug() << "Installing World from" << worldPath; QFileInfo worldFileInfo(worldPath); World w(worldFileInfo); @@ -586,29 +631,29 @@ void FlameCreationTask::validateZIPResouces() QString worldPath; switch (type) { - case PackedResourceType::Mod : + case PackedResourceType::Mod: validatePath(fileName, targetFolder, "mods"); break; - case PackedResourceType::ResourcePack : + case PackedResourceType::ResourcePack: validatePath(fileName, targetFolder, "resourcepacks"); break; - case PackedResourceType::TexturePack : + case PackedResourceType::TexturePack: validatePath(fileName, targetFolder, "texturepacks"); break; - case PackedResourceType::DataPack : + case PackedResourceType::DataPack: validatePath(fileName, targetFolder, "datapacks"); break; - case PackedResourceType::ShaderPack : + case PackedResourceType::ShaderPack: // in theroy flame API can't do this but who knows, that *may* change ? // better to handle it if it *does* occure in the future validatePath(fileName, targetFolder, "shaderpacks"); break; - case PackedResourceType::WorldSave : + case PackedResourceType::WorldSave: worldPath = validatePath(fileName, targetFolder, "saves"); installWorld(worldPath); break; - case PackedResourceType::UNKNOWN : - default : + case PackedResourceType::UNKNOWN: + default: qDebug() << "Can't Identify" << fileName << "at" << localPath << ", leaving it where it is."; break; } diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.h b/launcher/modplatform/flame/FlameInstanceCreationTask.h index 0ae4735bf..603d3693e 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.h +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.h @@ -57,10 +57,7 @@ class FlameCreationTask final : public InstanceCreationTask { QString id, QString version_id, QString original_instance_id = {}) - : InstanceCreationTask() - , m_parent(parent) - , m_managed_id(std::move(id)) - , m_managed_version_id(std::move(version_id)) + : InstanceCreationTask(), m_parent(parent), m_managed_id(std::move(id)), m_managed_version_id(std::move(version_id)) { setStagingPath(staging_path); setParentSettings(global_settings); @@ -78,6 +75,7 @@ class FlameCreationTask final : public InstanceCreationTask { void setupDownloadJob(QEventLoop&); void copyBlockedMods(QList const& blocked_mods); void validateZIPResouces(); + QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion); private: QWidget* m_parent = nullptr; diff --git a/launcher/modplatform/flame/FlamePackExportTask.cpp b/launcher/modplatform/flame/FlamePackExportTask.cpp index ac0da2142..f5f3af372 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.cpp +++ b/launcher/modplatform/flame/FlamePackExportTask.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "Json.h" #include "MMCZip.h" @@ -64,20 +65,11 @@ void FlamePackExportTask::executeTask() bool FlamePackExportTask::abort() { - if (task != nullptr) { + if (task) { task->abort(); - task = nullptr; emitAborted(); return true; } - - if (buildZipFuture.isRunning()) { - buildZipFuture.cancel(); - // NOTE: Here we don't do `emitAborted()` because it will be done when `buildZipFuture` actually cancels, which may not occur - // immediately. - return true; - } - return false; } @@ -166,7 +158,7 @@ void FlamePackExportTask::collectHashes() stepProgress(*progressStep); emitFailed(reason); }); - connect(hashingTask.get(), &Task::stepProgress, this, &FlamePackExportTask::propogateStepProgress); + connect(hashingTask.get(), &Task::stepProgress, this, &FlamePackExportTask::propagateStepProgress); connect(hashingTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { progressStep->update(current, total); @@ -336,89 +328,40 @@ void FlamePackExportTask::buildZip() setStatus(tr("Adding files...")); setProgress(4, 5); - buildZipFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { - QuaZip zip(output); - if (!zip.open(QuaZip::mdCreate)) { - QFile::remove(output); - return BuildZipResult(tr("Could not create file")); - } + auto zipTask = makeShared(output, gameRoot, files, "overrides/", true); + zipTask->addExtraFile("manifest.json", generateIndex()); + zipTask->addExtraFile("modlist.html", generateHTML()); - if (buildZipFuture.isCanceled()) - return BuildZipResult(); + QStringList exclude; + std::transform(resolvedFiles.keyBegin(), resolvedFiles.keyEnd(), std::back_insert_iterator(exclude), + [this](QString file) { return gameRoot.relativeFilePath(file); }); + zipTask->setExcludeFiles(exclude); - QuaZipFile indexFile(&zip); - if (!indexFile.open(QIODevice::WriteOnly, QuaZipNewInfo("manifest.json"))) { - QFile::remove(output); - return BuildZipResult(tr("Could not create index")); - } - indexFile.write(generateIndex()); - - QuaZipFile modlist(&zip); - if (!modlist.open(QIODevice::WriteOnly, QuaZipNewInfo("modlist.html"))) { - QFile::remove(output); - return BuildZipResult(tr("Could not create index")); - } - QString content = ""; - for (auto mod : resolvedFiles) { - if (mod.isMod) { - content += QString(TEMPLATE) - .replace("{name}", mod.name.toHtmlEscaped()) - .replace("{url}", ModPlatform::getMetaURL(ModPlatform::ResourceProvider::FLAME, mod.addonId).toHtmlEscaped()) - .replace("{authors}", !mod.authors.isEmpty() ? QString(" (by %1)").arg(mod.authors).toHtmlEscaped() : ""); - } - } - content = "
    " + content + "
"; - modlist.write(content.toUtf8()); - - auto progressStep = std::make_shared(); - - size_t progress = 0; - for (const QFileInfo& file : files) { - if (buildZipFuture.isCanceled()) { - QFile::remove(output); - progressStep->state = TaskStepState::Failed; - stepProgress(*progressStep); - return BuildZipResult(); - } - progressStep->update(progress, files.length()); - stepProgress(*progressStep); - - const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath()); - if (!resolvedFiles.contains(file.absoluteFilePath()) && - !JlCompress::compressFile(&zip, file.absoluteFilePath(), "overrides/" + relative)) { - QFile::remove(output); - return BuildZipResult(tr("Could not read and compress %1").arg(relative)); - } - progress++; - } - - zip.close(); - - if (zip.getZipError() != 0) { - QFile::remove(output); - progressStep->state = TaskStepState::Failed; - stepProgress(*progressStep); - return BuildZipResult(tr("A zip error occurred")); - } + auto progressStep = std::make_shared(); + connect(zipTask.get(), &Task::finished, this, [this, progressStep] { progressStep->state = TaskStepState::Succeeded; stepProgress(*progressStep); - return BuildZipResult(); }); - connect(&buildZipWatcher, &QFutureWatcher::finished, this, &FlamePackExportTask::finish); - buildZipWatcher.setFuture(buildZipFuture); -} -void FlamePackExportTask::finish() -{ - if (buildZipFuture.isCanceled()) - emitAborted(); - else { - const BuildZipResult result = buildZipFuture.result(); - if (result.has_value()) - emitFailed(result.value()); - else - emitSucceeded(); - } + connect(zipTask.get(), &Task::succeeded, this, &FlamePackExportTask::emitSucceeded); + connect(zipTask.get(), &Task::aborted, this, &FlamePackExportTask::emitAborted); + connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) { + progressStep->state = TaskStepState::Failed; + stepProgress(*progressStep); + emitFailed(reason); + }); + connect(zipTask.get(), &Task::stepProgress, this, &FlamePackExportTask::propagateStepProgress); + + connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { + progressStep->update(current, total); + stepProgress(*progressStep); + }); + connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) { + progressStep->status = status; + stepProgress(*progressStep); + }); + task.reset(zipTask); + zipTask->start(); } QByteArray FlamePackExportTask::generateIndex() @@ -471,3 +414,18 @@ QByteArray FlamePackExportTask::generateIndex() return QJsonDocument(obj).toJson(QJsonDocument::Compact); } + +QByteArray FlamePackExportTask::generateHTML() +{ + QString content = ""; + for (auto mod : resolvedFiles) { + if (mod.isMod) { + content += QString(TEMPLATE) + .replace("{name}", mod.name.toHtmlEscaped()) + .replace("{url}", ModPlatform::getMetaURL(ModPlatform::ResourceProvider::FLAME, mod.addonId).toHtmlEscaped()) + .replace("{authors}", !mod.authors.isEmpty() ? QString(" (by %1)").arg(mod.authors).toHtmlEscaped() : ""); + } + } + content = "
    " + content + "
"; + return content.toUtf8(); +} \ No newline at end of file diff --git a/launcher/modplatform/flame/FlamePackExportTask.h b/launcher/modplatform/flame/FlamePackExportTask.h index 3dee0a7ea..d3dc6281e 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.h +++ b/launcher/modplatform/flame/FlamePackExportTask.h @@ -19,8 +19,6 @@ #pragma once -#include -#include #include "BaseInstance.h" #include "MMCZip.h" #include "minecraft/MinecraftInstance.h" @@ -52,7 +50,6 @@ class FlamePackExportTask : public Task { const QString output; const MMCZip::FilterFunction filter; - typedef std::optional BuildZipResult; struct ResolvedFile { int addonId; int version; @@ -76,15 +73,13 @@ class FlamePackExportTask : public Task { QMap pendingHashes{}; QMap resolvedFiles{}; Task::Ptr task; - QFuture buildZipFuture; - QFutureWatcher buildZipWatcher; void collectFiles(); void collectHashes(); void makeApiRequest(); void getProjectsInfo(); void buildZip(); - void finish(); QByteArray generateIndex(); + QByteArray generateHTML(); }; diff --git a/launcher/modplatform/import_ftb/PackHelpers.cpp b/launcher/modplatform/import_ftb/PackHelpers.cpp new file mode 100644 index 000000000..4a1bbef96 --- /dev/null +++ b/launcher/modplatform/import_ftb/PackHelpers.cpp @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "modplatform/import_ftb/PackHelpers.h" + +#include +#include +#include + +#include "FileSystem.h" +#include "Json.h" + +namespace FTBImportAPP { + +Modpack parseDirectory(QString path) +{ + Modpack modpack{ path }; + auto instanceFile = QFileInfo(FS::PathCombine(path, "instance.json")); + if (!instanceFile.exists() || !instanceFile.isFile()) + return {}; + try { + auto doc = Json::requireDocument(instanceFile.absoluteFilePath(), "FTB_APP instance JSON file"); + const auto root = doc.object(); + modpack.uuid = Json::requireString(root, "uuid", "uuid"); + modpack.id = Json::requireInteger(root, "id", "id"); + modpack.versionId = Json::requireInteger(root, "versionId", "versionId"); + modpack.name = Json::requireString(root, "name", "name"); + modpack.version = Json::requireString(root, "version", "version"); + modpack.mcVersion = Json::requireString(root, "mcVersion", "mcVersion"); + modpack.jvmArgs = Json::ensureVariant(root, "jvmArgs", {}, "jvmArgs"); + } catch (const Exception& e) { + qDebug() << "Couldn't load ftb instance json: " << e.cause(); + return {}; + } + auto versionsFile = QFileInfo(FS::PathCombine(path, "version.json")); + if (!versionsFile.exists() || !versionsFile.isFile()) + return {}; + try { + auto doc = Json::requireDocument(versionsFile.absoluteFilePath(), "FTB_APP version JSON file"); + const auto root = doc.object(); + auto targets = Json::requireArray(root, "targets", "targets"); + + for (auto target : targets) { + auto obj = Json::requireObject(target, "target"); + auto name = Json::requireString(obj, "name", "name"); + auto version = Json::requireString(obj, "version", "version"); + if (name == "forge") { + modpack.loaderType = ResourceAPI::Forge; + modpack.version = version; + break; + } else if (name == "fabric") { + modpack.loaderType = ResourceAPI::Fabric; + modpack.version = version; + break; + } else if (name == "quilt") { + modpack.loaderType = ResourceAPI::Quilt; + modpack.version = version; + break; + } + } + } catch (const Exception& e) { + qDebug() << "Couldn't load ftb version json: " << e.cause(); + return {}; + } + auto iconFile = QFileInfo(FS::PathCombine(path, "folder.jpg")); + if (iconFile.exists() && iconFile.isFile()) { + modpack.icon = QIcon(iconFile.absoluteFilePath()); + } + return modpack; +} + +} // namespace FTBImportAPP diff --git a/launcher/modplatform/import_ftb/PackHelpers.h b/launcher/modplatform/import_ftb/PackHelpers.h new file mode 100644 index 000000000..8ea4f3faf --- /dev/null +++ b/launcher/modplatform/import_ftb/PackHelpers.h @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include +#include +#include +#include +#include "modplatform/ResourceAPI.h" + +namespace FTBImportAPP { + +struct Modpack { + QString path; + + // json data + QString uuid; + int id; + int versionId; + QString name; + QString version; + QString mcVersion; + // not needed for instance creation + QVariant jvmArgs; + + std::optional loaderType; + QString loaderVersion; + + QIcon icon; +}; + +typedef QList ModpackList; + +Modpack parseDirectory(QString path); + +} // namespace FTBImportAPP + +// We need it for the proxy model +Q_DECLARE_METATYPE(FTBImportAPP::Modpack) diff --git a/launcher/modplatform/import_ftb/PackInstallTask.cpp b/launcher/modplatform/import_ftb/PackInstallTask.cpp new file mode 100644 index 000000000..b5e424d12 --- /dev/null +++ b/launcher/modplatform/import_ftb/PackInstallTask.cpp @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PackInstallTask.h" + +#include + +#include "BaseInstance.h" +#include "FileSystem.h" +#include "minecraft/MinecraftInstance.h" +#include "minecraft/PackProfile.h" +#include "modplatform/ResourceAPI.h" +#include "modplatform/import_ftb/PackHelpers.h" +#include "settings/INISettingsObject.h" + +namespace FTBImportAPP { + +void PackInstallTask::executeTask() +{ + setStatus(tr("Copying files...")); + setAbortable(false); + progress(1, 2); + + m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] { + FS::copy folderCopy(m_pack.path, FS::PathCombine(m_stagingPath, ".minecraft")); + folderCopy.followSymlinks(true); + return folderCopy(); + }); + connect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &PackInstallTask::copySettings); + connect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &PackInstallTask::emitAborted); + m_copyFutureWatcher.setFuture(m_copyFuture); +} + +void PackInstallTask::copySettings() +{ + setStatus(tr("Copying settings...")); + progress(2, 2); + QString instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg"); + auto instanceSettings = std::make_shared(instanceConfigPath); + instanceSettings->suspendSave(); + MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath); + instance.settings()->set("InstanceType", "OneSix"); + + if (m_pack.jvmArgs.isValid() && !m_pack.jvmArgs.toString().isEmpty()) { + instance.settings()->set("OverrideJavaArgs", true); + instance.settings()->set("JvmArgs", m_pack.jvmArgs.toString()); + } + + auto components = instance.getPackProfile(); + components->buildingFromScratch(); + components->setComponentVersion("net.minecraft", m_pack.mcVersion, true); + + auto modloader = m_pack.loaderType; + if (modloader.has_value()) + switch (modloader.value()) { + case ResourceAPI::Forge: { + components->setComponentVersion("net.minecraftforge", m_pack.version, true); + break; + } + case ResourceAPI::Fabric: { + components->setComponentVersion("net.fabricmc.fabric-loader", m_pack.version, true); + break; + } + case ResourceAPI::Quilt: { + components->setComponentVersion("org.quiltmc.quilt-loader", m_pack.version, true); + break; + } + case ResourceAPI::Cauldron: + break; + case ResourceAPI::LiteLoader: + break; + } + components->saveNow(); + + instance.setName(name()); + if (m_instIcon == "default") + m_instIcon = "ftb_logo"; + instance.setIconKey(m_instIcon); + instanceSettings->resumeSave(); + + emitSucceeded(); +} + +} // namespace FTBImportAPP diff --git a/launcher/modplatform/import_ftb/PackInstallTask.h b/launcher/modplatform/import_ftb/PackInstallTask.h new file mode 100644 index 000000000..842e4b35e --- /dev/null +++ b/launcher/modplatform/import_ftb/PackInstallTask.h @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#include "InstanceTask.h" +#include "PackHelpers.h" + +namespace FTBImportAPP { + +class PackInstallTask : public InstanceTask { + Q_OBJECT + + public: + explicit PackInstallTask(const Modpack& pack) : m_pack(pack) {} + virtual ~PackInstallTask() = default; + + protected: + virtual void executeTask() override; + + private slots: + void copySettings(); + + private: + QFuture m_copyFuture; + QFutureWatcher m_copyFutureWatcher; + + const Modpack m_pack; +}; + +} // namespace FTBImportAPP diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp index a4c78397b..1afe57830 100644 --- a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp +++ b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp @@ -81,7 +81,7 @@ void PackInstallTask::downloadPack() connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip); connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed); - connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress); + connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress); connect(netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::emitAborted); netJobContainer->start(); diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index 76f072773..bd0b828c6 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -267,7 +267,7 @@ bool ModrinthCreationTask::createInstance() setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); setProgress(current, total); }); - connect(m_files_job.get(), &NetJob::stepProgress, this, &ModrinthCreationTask::propogateStepProgress); + connect(m_files_job.get(), &NetJob::stepProgress, this, &ModrinthCreationTask::propagateStepProgress); setStatus(tr("Downloading mods...")); m_files_job->start(); diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp index 30fe566da..7bf296398 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp @@ -55,20 +55,11 @@ void ModrinthPackExportTask::executeTask() bool ModrinthPackExportTask::abort() { - if (task != nullptr) { + if (task) { task->abort(); - task = nullptr; emitAborted(); return true; } - - if (buildZipFuture.isRunning()) { - buildZipFuture.cancel(); - // NOTE: Here we don't do `emitAborted()` because it will be done when `buildZipFuture` actually cancels, which may not occur - // immediately. - return true; - } - return false; } @@ -205,63 +196,36 @@ void ModrinthPackExportTask::buildZip() { setStatus(tr("Adding files...")); - buildZipFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { - QuaZip zip(output); - if (!zip.open(QuaZip::mdCreate)) { - QFile::remove(output); - return BuildZipResult(tr("Could not create file")); - } + auto zipTask = makeShared(output, gameRoot, files, "overrides/", true); + zipTask->addExtraFile("modrinth.index.json", generateIndex()); - if (buildZipFuture.isCanceled()) - return BuildZipResult(); + zipTask->setExcludeFiles(resolvedFiles.keys()); - QuaZipFile indexFile(&zip); - if (!indexFile.open(QIODevice::WriteOnly, QuaZipNewInfo("modrinth.index.json"))) { - QFile::remove(output); - return BuildZipResult(tr("Could not create index")); - } - indexFile.write(generateIndex()); - - size_t progress = 0; - for (const QFileInfo& file : files) { - if (buildZipFuture.isCanceled()) { - QFile::remove(output); - return BuildZipResult(); - } - - setProgress(progress, files.length()); - const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath()); - if (!resolvedFiles.contains(relative) && !JlCompress::compressFile(&zip, file.absoluteFilePath(), "overrides/" + relative)) { - QFile::remove(output); - return BuildZipResult(tr("Could not read and compress %1").arg(relative)); - } - progress++; - } - - zip.close(); - - if (zip.getZipError() != 0) { - QFile::remove(output); - return BuildZipResult(tr("A zip error occurred")); - } - - return BuildZipResult(); + auto progressStep = std::make_shared(); + connect(zipTask.get(), &Task::finished, this, [this, progressStep] { + progressStep->state = TaskStepState::Succeeded; + stepProgress(*progressStep); }); - connect(&buildZipWatcher, &QFutureWatcher::finished, this, &ModrinthPackExportTask::finish); - buildZipWatcher.setFuture(buildZipFuture); -} -void ModrinthPackExportTask::finish() -{ - if (buildZipFuture.isCanceled()) - emitAborted(); - else { - const BuildZipResult result = buildZipFuture.result(); - if (result.has_value()) - emitFailed(result.value()); - else - emitSucceeded(); - } + connect(zipTask.get(), &Task::succeeded, this, &ModrinthPackExportTask::emitSucceeded); + connect(zipTask.get(), &Task::aborted, this, &ModrinthPackExportTask::emitAborted); + connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) { + progressStep->state = TaskStepState::Failed; + stepProgress(*progressStep); + emitFailed(reason); + }); + connect(zipTask.get(), &Task::stepProgress, this, &ModrinthPackExportTask::propagateStepProgress); + + connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { + progressStep->update(current, total); + stepProgress(*progressStep); + }); + connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) { + progressStep->status = status; + stepProgress(*progressStep); + }); + task.reset(zipTask); + zipTask->start(); } QByteArray ModrinthPackExportTask::generateIndex() diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.h b/launcher/modplatform/modrinth/ModrinthPackExportTask.h index 96f292c1b..1f9e0eb77 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.h +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.h @@ -56,22 +56,17 @@ class ModrinthPackExportTask : public Task { const QString output; const MMCZip::FilterFunction filter; - typedef std::optional BuildZipResult; - ModrinthAPI api; QFileInfoList files; QMap pendingHashes; QMap resolvedFiles; Task::Ptr task; - QFuture buildZipFuture; - QFutureWatcher buildZipWatcher; void collectFiles(); void collectHashes(); void makeApiRequest(); void parseApiResponse(const std::shared_ptr response); void buildZip(); - void finish(); QByteArray generateIndex(); }; diff --git a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp index f07ca24af..ab91c4668 100644 --- a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp +++ b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp @@ -50,7 +50,7 @@ void Technic::SingleZipPackInstallTask::executeTask() auto job = m_filesNetJob.get(); connect(job, &NetJob::succeeded, this, &Technic::SingleZipPackInstallTask::downloadSucceeded); connect(job, &NetJob::progress, this, &Technic::SingleZipPackInstallTask::downloadProgressChanged); - connect(job, &NetJob::stepProgress, this, &Technic::SingleZipPackInstallTask::propogateStepProgress); + connect(job, &NetJob::stepProgress, this, &Technic::SingleZipPackInstallTask::propagateStepProgress); connect(job, &NetJob::failed, this, &Technic::SingleZipPackInstallTask::downloadFailed); m_filesNetJob->start(); } diff --git a/launcher/modplatform/technic/SolderPackInstallTask.cpp b/launcher/modplatform/technic/SolderPackInstallTask.cpp index 6a05d17ae..cc1d261e2 100644 --- a/launcher/modplatform/technic/SolderPackInstallTask.cpp +++ b/launcher/modplatform/technic/SolderPackInstallTask.cpp @@ -126,7 +126,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded() connect(m_filesNetJob.get(), &NetJob::succeeded, this, &Technic::SolderPackInstallTask::downloadSucceeded); connect(m_filesNetJob.get(), &NetJob::progress, this, &Technic::SolderPackInstallTask::downloadProgressChanged); - connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &Technic::SolderPackInstallTask::propogateStepProgress); + connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &Technic::SolderPackInstallTask::propagateStepProgress); connect(m_filesNetJob.get(), &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed); connect(m_filesNetJob.get(), &NetJob::aborted, this, &Technic::SolderPackInstallTask::downloadAborted); m_filesNetJob->start(); diff --git a/launcher/settings/INIFile.cpp b/launcher/settings/INIFile.cpp index d16256b96..4fb11ed35 100644 --- a/launcher/settings/INIFile.cpp +++ b/launcher/settings/INIFile.cpp @@ -37,11 +37,12 @@ #include "settings/INIFile.h" #include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include #include @@ -71,6 +72,7 @@ bool INIFile::saveFile(QString fileName) return true; } + QString unescape(QString orig) { QString out; @@ -185,6 +187,19 @@ bool INIFile::loadFile(QString fileName) return true; } +bool INIFile::loadFile(QByteArray data) +{ + QTemporaryFile file; + if (!file.open()) + return false; + file.write(data); + file.flush(); + file.close(); + auto loaded = loadFile(file.fileName()); + file.remove(); + return loaded; +} + QVariant INIFile::get(QString key, QVariant def) const { if (!this->contains(key)) diff --git a/launcher/settings/INIFile.h b/launcher/settings/INIFile.h index 0d5c05ebb..4ee543cf1 100644 --- a/launcher/settings/INIFile.h +++ b/launcher/settings/INIFile.h @@ -50,6 +50,7 @@ public: explicit INIFile(); bool loadFile(QString fileName); + bool loadFile(QByteArray data); bool saveFile(QString fileName); QVariant get(QString key, QVariant def) const; diff --git a/launcher/tasks/Task.cpp b/launcher/tasks/Task.cpp index 29c55cd48..fd82ec001 100644 --- a/launcher/tasks/Task.cpp +++ b/launcher/tasks/Task.cpp @@ -161,7 +161,7 @@ void Task::emitSucceeded() emit finished(); } -void Task::propogateStepProgress(TaskStepProgress const& task_progress) +void Task::propagateStepProgress(TaskStepProgress const& task_progress) { emit stepProgress(task_progress); } diff --git a/launcher/tasks/Task.h b/launcher/tasks/Task.h index 6d8bbbb46..57177697e 100644 --- a/launcher/tasks/Task.h +++ b/launcher/tasks/Task.h @@ -167,7 +167,7 @@ class Task : public QObject, public QRunnable { virtual void emitAborted(); virtual void emitFailed(QString reason = ""); - virtual void propogateStepProgress(TaskStepProgress const& task_progress); + virtual void propagateStepProgress(TaskStepProgress const& task_progress); public slots: void setStatus(const QString& status); diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index da572fc34..e342e833e 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1342,16 +1342,10 @@ void MainWindow::on_actionExportInstanceFlamePack_triggered() if (m_selectedInstance) { auto instance = dynamic_cast(m_selectedInstance.get()); if (instance) { - QString errorMsg; - if (instance->getPackProfile()->getComponent("org.quiltmc.quilt-loader")) { - errorMsg = tr("Quilt is currently not supported by CurseForge modpacks."); - } else if (auto cmp = instance->getPackProfile()->getComponent("net.minecraft"); - cmp && cmp->getVersionFile() && cmp->getVersionFile()->type == "snapshot") { - errorMsg = tr("Snapshots are currently not supported by CurseForge modpacks."); - } - if (!errorMsg.isEmpty()) { - QMessageBox msgBox; - msgBox.setText(errorMsg); + if (auto cmp = instance->getPackProfile()->getComponent("net.minecraft"); + cmp && cmp->getVersionFile() && cmp->getVersionFile()->type == "snapshot") { + QMessageBox msgBox(this); + msgBox.setText("Snapshots are currently not supported by CurseForge modpacks."); msgBox.exec(); return; } diff --git a/launcher/ui/dialogs/AboutDialog.cpp b/launcher/ui/dialogs/AboutDialog.cpp index 88739463f..b1734eff3 100644 --- a/launcher/ui/dialogs/AboutDialog.cpp +++ b/launcher/ui/dialogs/AboutDialog.cpp @@ -170,7 +170,7 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDia QString urlText("

%1

"); ui->urlLabel->setText(urlText.arg(BuildConfig.LAUNCHER_GIT)); - QString copyText("© 2022 %1"); + QString copyText("© 2022-2023 %1"); ui->copyLabel->setText(copyText.arg(BuildConfig.LAUNCHER_COPYRIGHT)); connect(ui->closeButton, SIGNAL(clicked()), SLOT(close())); diff --git a/launcher/ui/dialogs/ExportInstanceDialog.cpp b/launcher/ui/dialogs/ExportInstanceDialog.cpp index d1a69b932..d6a503ccd 100644 --- a/launcher/ui/dialogs/ExportInstanceDialog.cpp +++ b/launcher/ui/dialogs/ExportInstanceDialog.cpp @@ -70,6 +70,8 @@ ExportInstanceDialog::ExportInstanceDialog(InstancePtr instance, QWidget* parent auto prefix = QDir(instance->instanceRoot()).relativeFilePath(instance->gameRoot()); proxyModel->ignoreFilesWithPath().insert({ FS::PathCombine(prefix, "logs"), FS::PathCombine(prefix, "crash-reports") }); proxyModel->ignoreFilesWithName().append({ ".DS_Store", "thumbs.db", "Thumbs.db" }); + proxyModel->ignoreFilesWithPath().insert( + { FS::PathCombine(prefix, ".cache"), FS::PathCombine(prefix, ".fabric"), FS::PathCombine(prefix, ".quilt") }); loadPackIgnore(); ui->treeView->setModel(proxyModel); diff --git a/launcher/ui/dialogs/ExportPackDialog.cpp b/launcher/ui/dialogs/ExportPackDialog.cpp index 2abe2805b..ad8db5ffb 100644 --- a/launcher/ui/dialogs/ExportPackDialog.cpp +++ b/launcher/ui/dialogs/ExportPackDialog.cpp @@ -61,7 +61,7 @@ ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPla // use the game root - everything outside cannot be exported const QDir root(instance->gameRoot()); proxy = new FileIgnoreProxy(instance->gameRoot(), this); - proxy->ignoreFilesWithPath().insert({ "logs", "crash-reports" }); + proxy->ignoreFilesWithPath().insert({ "logs", "crash-reports", ".cache", ".fabric", ".quilt" }); proxy->ignoreFilesWithName().append({ ".DS_Store", "thumbs.db", "Thumbs.db" }); proxy->setSourceModel(model); diff --git a/launcher/ui/dialogs/NewInstanceDialog.cpp b/launcher/ui/dialogs/NewInstanceDialog.cpp index 7b9bb944c..76b139fc7 100644 --- a/launcher/ui/dialogs/NewInstanceDialog.cpp +++ b/launcher/ui/dialogs/NewInstanceDialog.cpp @@ -33,38 +33,37 @@ * limitations under the License. */ -#include "Application.h" #include "NewInstanceDialog.h" +#include "Application.h" +#include "ui/pages/modplatform/import_ftb/ImportFTBPage.h" #include "ui_NewInstanceDialog.h" #include +#include #include #include -#include -#include "VersionSelectDialog.h" -#include "ProgressDialog.h" #include "IconPickerDialog.h" +#include "ProgressDialog.h" +#include "VersionSelectDialog.h" +#include +#include #include #include -#include #include -#include #include -#include "ui/widgets/PageContainer.h" #include "ui/pages/modplatform/CustomPage.h" -#include "ui/pages/modplatform/atlauncher/AtlPage.h" -#include "ui/pages/modplatform/legacy_ftb/Page.h" -#include "ui/pages/modplatform/flame/FlamePage.h" #include "ui/pages/modplatform/ImportPage.h" +#include "ui/pages/modplatform/atlauncher/AtlPage.h" +#include "ui/pages/modplatform/flame/FlamePage.h" +#include "ui/pages/modplatform/legacy_ftb/Page.h" #include "ui/pages/modplatform/modrinth/ModrinthPage.h" #include "ui/pages/modplatform/technic/TechnicPage.h" +#include "ui/widgets/PageContainer.h" - - -NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString & url, QWidget *parent) +NewInstanceDialog::NewInstanceDialog(const QString& initialGroup, const QString& url, QWidget* parent) : QDialog(parent), ui(new Ui::NewInstanceDialog) { ui->setupUi(this); @@ -168,6 +167,7 @@ QList NewInstanceDialog::getPages() if (APPLICATION->capabilities() & Application::SupportsFlame) pages.append(new FlamePage(this)); pages.append(new LegacyFTB::Page(this)); + pages.append(new FTBImportAPP::ImportFTBPage(this)); pages.append(new ModrinthPage(this)); pages.append(new TechnicPage(this)); diff --git a/launcher/ui/instanceview/InstanceView.cpp b/launcher/ui/instanceview/InstanceView.cpp index 1911dd59a..05f0004d1 100644 --- a/launcher/ui/instanceview/InstanceView.cpp +++ b/launcher/ui/instanceview/InstanceView.cpp @@ -48,7 +48,6 @@ #include #include "VisualGroup.h" -#include "ui/themes/ThemeManager.h" #include #include @@ -504,7 +503,7 @@ void InstanceView::setPaintCat(bool visible) { m_catVisible = visible; if (visible) - m_catPixmap.load(QString(":/backgrounds/%1").arg(ThemeManager::getCatImage())); + m_catPixmap.load(APPLICATION->getCatPack()); else m_catPixmap = QPixmap(); } diff --git a/launcher/ui/pages/global/APIPage.ui b/launcher/ui/pages/global/APIPage.ui index 40b89d914..492741ba4 100644 --- a/launcher/ui/pages/global/APIPage.ui +++ b/launcher/ui/pages/global/APIPage.ui @@ -30,7 +30,7 @@ - Services + Services diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index 6749cbe41..561cf79b7 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -58,7 +58,7 @@ - &PermGen: + &PermGen: permGenSpinBox diff --git a/launcher/ui/pages/global/LauncherPage.h b/launcher/ui/pages/global/LauncherPage.h index 33f66f1be..e06d98971 100644 --- a/launcher/ui/pages/global/LauncherPage.h +++ b/launcher/ui/pages/global/LauncherPage.h @@ -62,7 +62,7 @@ public: QString displayName() const override { - return "Launcher"; + return tr("Launcher"); } QIcon icon() const override { diff --git a/launcher/ui/pages/global/MinecraftPage.ui b/launcher/ui/pages/global/MinecraftPage.ui index cd0f4fafd..3f81f831c 100644 --- a/launcher/ui/pages/global/MinecraftPage.ui +++ b/launcher/ui/pages/global/MinecraftPage.ui @@ -39,7 +39,7 @@ - General + General diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 4548b2e72..a7f61979b 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -116,7 +116,7 @@ - PermGen: + PermGen: diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp new file mode 100644 index 000000000..5c9ff63b2 --- /dev/null +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ImportFTBPage.h" +#include "ui_ImportFTBPage.h" + +#include +#include "FileSystem.h" +#include "ListModel.h" +#include "modplatform/import_ftb/PackInstallTask.h" +#include "ui/dialogs/NewInstanceDialog.h" + +namespace FTBImportAPP { + +ImportFTBPage::ImportFTBPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), dialog(dialog), ui(new Ui::ImportFTBPage) +{ + ui->setupUi(this); + + { + listModel = new ListModel(this); + + ui->modpackList->setModel(listModel); + ui->modpackList->setSortingEnabled(true); + ui->modpackList->header()->hide(); + ui->modpackList->setIndentation(0); + ui->modpackList->setIconSize(QSize(42, 42)); + } + + connect(ui->modpackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &ImportFTBPage::onPublicPackSelectionChanged); + + ui->modpackList->selectionModel()->reset(); +} + +ImportFTBPage::~ImportFTBPage() +{ + delete ui; +} + +void ImportFTBPage::openedImpl() +{ + if (!initialized) { + listModel->update(); + initialized = true; + } + suggestCurrent(); +} + +void ImportFTBPage::retranslate() +{ + ui->retranslateUi(this); +} + +void ImportFTBPage::suggestCurrent() +{ + if (!isOpened) + return; + + if (selected.path.isEmpty()) { + dialog->setSuggestedPack(); + return; + } + + dialog->setSuggestedPack(selected.name, new PackInstallTask(selected)); + QString editedLogoName = QString("ftb_%1").arg(selected.id); + dialog->setSuggestedIconFromFile(FS::PathCombine(selected.path, "folder.jpg"), editedLogoName); +} + +void ImportFTBPage::onPublicPackSelectionChanged(QModelIndex now, QModelIndex prev) +{ + if (!now.isValid()) { + onPackSelectionChanged(); + return; + } + Modpack selectedPack = listModel->data(now, Qt::UserRole).value(); + onPackSelectionChanged(&selectedPack); +} + +void ImportFTBPage::onPackSelectionChanged(Modpack* pack) +{ + if (pack) { + selected = *pack; + suggestCurrent(); + return; + } + if (isOpened) + dialog->setSuggestedPack(); +} + +} // namespace FTBImportAPP diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.h b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.h new file mode 100644 index 000000000..54c49f7b7 --- /dev/null +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.h @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include "modplatform/import_ftb/PackHelpers.h" +#include "ui/pages/BasePage.h" +#include "ui/pages/modplatform/import_ftb/ListModel.h" + +class NewInstanceDialog; + +namespace FTBImportAPP { +namespace Ui { +class ImportFTBPage; +} + +class ImportFTBPage : public QWidget, public BasePage { + Q_OBJECT + + public: + explicit ImportFTBPage(NewInstanceDialog* dialog, QWidget* parent = 0); + virtual ~ImportFTBPage(); + QString displayName() const override { return tr("FTB App Import"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("ftb_logo"); } + QString id() const override { return "import_ftb"; } + QString helpPage() const override { return "FTB-platform"; } + bool shouldDisplay() const override { return true; } + void openedImpl() override; + void retranslate() override; + + private: + void suggestCurrent(); + void onPackSelectionChanged(Modpack* pack = nullptr); + private slots: + void onPublicPackSelectionChanged(QModelIndex first, QModelIndex second); + + private: + bool initialized = false; + Modpack selected; + ListModel* listModel = nullptr; + + NewInstanceDialog* dialog = nullptr; + Ui::ImportFTBPage* ui = nullptr; +}; + +} // namespace FTBImportAPP diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui new file mode 100644 index 000000000..32d548b0d --- /dev/null +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui @@ -0,0 +1,28 @@ + + + FTBImportAPP::ImportFTBPage + + + + 0 + 0 + 1461 + 1011 + + + + + + + + 16777215 + 16777215 + + + + + + + + + diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp new file mode 100644 index 000000000..dc78f451c --- /dev/null +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ListModel.h" +#include +#include +#include +#include +#include +#include "FileSystem.h" +#include "modplatform/import_ftb/PackHelpers.h" + +namespace FTBImportAPP { + +QString getPath() +{ + QString partialPath; +#if defined(Q_OS_OSX) + partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support"); +#elif defined(Q_OS_WIN32) + partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); +#else + partialPath = QDir::homePath(); +#endif + return FS::PathCombine(partialPath, ".ftba"); +} + +const QString ListModel::FTB_APP_PATH = getPath(); + +void ListModel::update() +{ + beginResetModel(); + modpacks.clear(); + + QString instancesPath = FS::PathCombine(FTB_APP_PATH, "instances"); + if (auto instancesInfo = QFileInfo(instancesPath); instancesInfo.exists() && instancesInfo.isDir()) { + QDirIterator directoryIterator(instancesPath, QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable | QDir::Hidden, + QDirIterator::FollowSymlinks); + while (directoryIterator.hasNext()) { + auto modpack = parseDirectory(directoryIterator.next()); + if (!modpack.path.isEmpty()) + modpacks.append(modpack); + } + } else { + qDebug() << "Couldn't find ftb instances folder: " << instancesPath; + } + + endResetModel(); +} + +QVariant ListModel::data(const QModelIndex& index, int role) const +{ + int pos = index.row(); + if (pos >= modpacks.size() || pos < 0 || !index.isValid()) { + return QVariant(); + } + + auto pack = modpacks.at(pos); + if (role == Qt::DisplayRole) { + return pack.name; + } else if (role == Qt::DecorationRole) { + return pack.icon; + } else if (role == Qt::UserRole) { + QVariant v; + v.setValue(pack); + return v; + } else if (role == Qt::ToolTipRole) { + return tr("Minecraft %1").arg(pack.mcVersion); + } + + return QVariant(); +} +} // namespace FTBImportAPP \ No newline at end of file diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.h b/launcher/ui/pages/modplatform/import_ftb/ListModel.h new file mode 100644 index 000000000..c67aa8963 --- /dev/null +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.h @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include "modplatform/import_ftb/PackHelpers.h" + +namespace FTBImportAPP { + +class ListModel : public QAbstractListModel { + Q_OBJECT + + public: + ListModel(QObject* parent) : QAbstractListModel(parent) {} + virtual ~ListModel() = default; + + int rowCount(const QModelIndex& parent) const { return modpacks.size(); } + int columnCount(const QModelIndex& parent) const { return 1; } + QVariant data(const QModelIndex& index, int role) const; + + void update(); + + static const QString FTB_APP_PATH; + + private: + ModpackList modpacks; +}; +} // namespace FTBImportAPP \ No newline at end of file diff --git a/launcher/ui/setupwizard/ThemeWizardPage.cpp b/launcher/ui/setupwizard/ThemeWizardPage.cpp index c3e0a5241..1c3369219 100644 --- a/launcher/ui/setupwizard/ThemeWizardPage.cpp +++ b/launcher/ui/setupwizard/ThemeWizardPage.cpp @@ -61,7 +61,7 @@ void ThemeWizardPage::updateIcons() void ThemeWizardPage::updateCat() { qDebug() << "Setting Cat"; - ui->catImagePreviewButton->setIcon(QIcon(QString(R"(:/backgrounds/%1)").arg(ThemeManager::getCatImage()))); + ui->catImagePreviewButton->setIcon(QIcon(QString(R"(%1)").arg(APPLICATION->getCatPack()))); } void ThemeWizardPage::retranslate() diff --git a/launcher/ui/themes/CatPack.cpp b/launcher/ui/themes/CatPack.cpp new file mode 100644 index 000000000..f0d8ddd55 --- /dev/null +++ b/launcher/ui/themes/CatPack.cpp @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ui/themes/CatPack.h" +#include +#include +#include +#include "FileSystem.h" +#include "Json.h" + +QString BasicCatPack::path() +{ + const auto now = QDate::currentDate(); + const auto birthday = QDate(now.year(), 11, 30); + const auto xmas = QDate(now.year(), 12, 25); + const auto halloween = QDate(now.year(), 10, 31); + + QString cat = QString(":/backgrounds/%1").arg(m_id); + if (std::abs(now.daysTo(xmas)) <= 4) { + cat += "-xmas"; + } else if (std::abs(now.daysTo(halloween)) <= 4) { + cat += "-spooky"; + } else if (std::abs(now.daysTo(birthday)) <= 12) { + cat += "-bday"; + } + return cat; +} + +JsonCatPack::PartialDate partialDate(QJsonObject date) +{ + auto month = Json::ensureInteger(date, "month", 1); + if (month > 12) + month = 12; + else if (month <= 0) + month = 1; + auto day = Json::ensureInteger(date, "day", 1); + if (day > 31) + day = 31; + else if (day <= 0) + day = 1; + return { month, day }; +}; + +JsonCatPack::JsonCatPack(QFileInfo& manifestInfo) : BasicCatPack(manifestInfo.dir().dirName()) +{ + QString path = manifestInfo.path(); + auto doc = Json::requireDocument(manifestInfo.absoluteFilePath(), "CatPack JSON file"); + const auto root = doc.object(); + m_name = Json::requireString(root, "name", "Catpack name"); + m_defaultPath = FS::PathCombine(path, Json::requireString(root, "default", "Default Cat")); + auto variants = Json::ensureArray(root, "variants", QJsonArray(), "Catpack Variants"); + for (auto v : variants) { + auto variant = Json::ensureObject(v, QJsonObject(), "Cat variant"); + m_variants << Variant{ FS::PathCombine(path, Json::requireString(variant, "path", "Variant path")), + partialDate(Json::requireObject(variant, "startTime", "Variant startTime")), + partialDate(Json::requireObject(variant, "endTime", "Variant endTime")) }; + } +} + +QDate ensureDay(int year, int month, int day) +{ + QDate date(year, month, 1); + if (day > date.daysInMonth()) + day = date.daysInMonth(); + return QDate(year, month, day); +} + +QString JsonCatPack::path() +{ + const QDate now = QDate::currentDate(); + for (auto var : m_variants) { + QDate startDate = ensureDay(now.year(), var.startTime.month, var.startTime.day); + QDate endDate = ensureDay(now.year(), var.endTime.month, var.endTime.day); + if (startDate > endDate) { // it's spans over multiple years + if (endDate <= now) // end date is in the past so jump one year into the future for endDate + endDate = endDate.addYears(1); + else // end date is in the future so jump one year into the past for startDate + startDate = startDate.addYears(-1); + } + + if (startDate >= now && now >= endDate) + return var.path; + } + return m_defaultPath; +} diff --git a/launcher/ui/themes/CatPack.h b/launcher/ui/themes/CatPack.h new file mode 100644 index 000000000..b03a19f03 --- /dev/null +++ b/launcher/ui/themes/CatPack.h @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +class CatPack { + public: + virtual ~CatPack() {} + virtual QString id() = 0; + virtual QString name() = 0; + virtual QString path() = 0; +}; + +class BasicCatPack : public CatPack { + public: + BasicCatPack(QString id, QString name) : m_id(id), m_name(name) {} + BasicCatPack(QString id) : BasicCatPack(id, id) {} + virtual QString id() { return m_id; }; + virtual QString name() { return m_name; }; + virtual QString path(); + + protected: + QString m_id; + QString m_name; +}; + +class FileCatPack : public BasicCatPack { + public: + FileCatPack(QString id, QFileInfo& fileInfo) : BasicCatPack(id), m_path(fileInfo.absoluteFilePath()) {} + FileCatPack(QFileInfo& fileInfo) : FileCatPack(fileInfo.baseName(), fileInfo) {} + virtual QString path() { return m_path; } + + private: + QString m_path; +}; + +class JsonCatPack : public BasicCatPack { + public: + struct PartialDate { + int month; + int day; + }; + struct Variant { + QString path; + PartialDate startTime; + PartialDate endTime; + }; + JsonCatPack(QFileInfo& manifestInfo); + virtual QString path(); + + private: + QString m_defaultPath; + QList m_variants; +}; diff --git a/launcher/ui/themes/ThemeManager.cpp b/launcher/ui/themes/ThemeManager.cpp index b50c6157f..321f7db4a 100644 --- a/launcher/ui/themes/ThemeManager.cpp +++ b/launcher/ui/themes/ThemeManager.cpp @@ -21,7 +21,10 @@ #include #include #include +#include +#include "Exception.h" #include "ui/themes/BrightTheme.h" +#include "ui/themes/CatPack.h" #include "ui/themes/CustomTheme.h" #include "ui/themes/DarkTheme.h" #include "ui/themes/SystemTheme.h" @@ -32,6 +35,7 @@ ThemeManager::ThemeManager(MainWindow* mainWindow) { m_mainWindow = mainWindow; initializeThemes(); + initializeCatPacks(); } /// @brief Adds the Theme to the list of themes @@ -40,7 +44,10 @@ ThemeManager::ThemeManager(MainWindow* mainWindow) QString ThemeManager::addTheme(std::unique_ptr theme) { QString id = theme->id(); - m_themes.emplace(id, std::move(theme)); + if (m_themes.find(id) == m_themes.end()) + m_themes.emplace(id, std::move(theme)); + else + themeWarningLog() << "Theme(" << id << ") not added to prevent id duplication"; return id; } @@ -77,7 +84,7 @@ void ThemeManager::initializeThemes() QString themeFolder = QDir("./themes/").absoluteFilePath(""); themeDebugLog() << "Theme Folder Path: " << themeFolder; - QDirIterator directoryIterator(themeFolder, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); + QDirIterator directoryIterator(themeFolder, QDir::Dirs | QDir::NoDotAndDotDot); while (directoryIterator.hasNext()) { QDir dir(directoryIterator.next()); QFileInfo themeJson(dir.absoluteFilePath("theme.json")); @@ -111,6 +118,16 @@ QList ThemeManager::getValidApplicationThemes() return ret; } +QList ThemeManager::getValidCatPacks() +{ + QList ret; + ret.reserve(m_catPacks.size()); + for (auto&& [id, theme] : m_catPacks) { + ret.append(theme.get()); + } + return ret; +} + void ThemeManager::setIconTheme(const QString& name) { QIcon::setThemeName(name); @@ -137,19 +154,74 @@ void ThemeManager::setApplicationTheme(const QString& name, bool initial) } } -QString ThemeManager::getCatImage(QString catName) +QString ThemeManager::getCatPack(QString catName) { - QDateTime now = QDateTime::currentDateTime(); - QDateTime birthday(QDate(now.date().year(), 11, 30), QTime(0, 0)); - QDateTime xmas(QDate(now.date().year(), 12, 25), QTime(0, 0)); - QDateTime halloween(QDate(now.date().year(), 10, 31), QTime(0, 0)); - QString cat = !catName.isEmpty() ? catName : APPLICATION->settings()->get("BackgroundCat").toString(); - if (std::abs(now.daysTo(xmas)) <= 4) { - cat += "-xmas"; - } else if (std::abs(now.daysTo(halloween)) <= 4) { - cat += "-spooky"; - } else if (std::abs(now.daysTo(birthday)) <= 12) { - cat += "-bday"; + auto catIter = m_catPacks.find(!catName.isEmpty() ? catName : APPLICATION->settings()->get("BackgroundCat").toString()); + if (catIter != m_catPacks.end()) { + auto& catPack = catIter->second; + themeDebugLog() << "applying catpack" << catPack->id(); + return catPack->path(); + } else { + themeWarningLog() << "Tried to get invalid catPack:" << catName; + } + + return m_catPacks.begin()->second->path(); +} + +QString ThemeManager::addCatPack(std::unique_ptr catPack) +{ + QString id = catPack->id(); + if (m_catPacks.find(id) == m_catPacks.end()) + m_catPacks.emplace(id, std::move(catPack)); + else + themeWarningLog() << "CatPack(" << id << ") not added to prevent id duplication"; + return id; +} + +void ThemeManager::initializeCatPacks() +{ + QList> defaultCats{ { "kitteh", QObject::tr("Background Cat (from MultiMC)") }, + { "rory", QObject::tr("Rory ID 11 (drawn by Ashtaka)") }, + { "rory-flat", QObject::tr("Rory ID 11 (flat edition, drawn by Ashtaka)") }, + { "teawie", QObject::tr("Teawie (drawn by SympathyTea)") } }; + for (auto [id, name] : defaultCats) { + addCatPack(std::unique_ptr(new BasicCatPack(id, name))); + } + QDir catpacksDir("catpacks"); + QString catpacksFolder = catpacksDir.absoluteFilePath(""); + themeDebugLog() << "CatPacks Folder Path:" << catpacksFolder; + + QStringList supportedImageFormats; + for (auto format : QImageReader::supportedImageFormats()) { + supportedImageFormats.append("*." + format); + } + auto loadFiles = [this, supportedImageFormats](QDir dir) { + // Load image files directly + QDirIterator ImageFileIterator(dir.absoluteFilePath(""), supportedImageFormats, QDir::Files); + while (ImageFileIterator.hasNext()) { + QFile customCatFile(ImageFileIterator.next()); + QFileInfo customCatFileInfo(customCatFile); + themeDebugLog() << "Loading CatPack from:" << customCatFileInfo.absoluteFilePath(); + addCatPack(std::unique_ptr(new FileCatPack(customCatFileInfo))); + } + }; + + loadFiles(catpacksDir); + + QDirIterator directoryIterator(catpacksFolder, QDir::Dirs | QDir::NoDotAndDotDot); + while (directoryIterator.hasNext()) { + QDir dir(directoryIterator.next()); + QFileInfo manifest(dir.absoluteFilePath("catpack.json")); + if (manifest.isFile()) { + try { + // Load background manifest + themeDebugLog() << "Loading background manifest from:" << manifest.absoluteFilePath(); + addCatPack(std::unique_ptr(new JsonCatPack(manifest))); + } catch (const Exception& e) { + themeWarningLog() << "Couldn't load catpack json:" << e.cause(); + } + } else { + loadFiles(dir); + } } - return cat; } diff --git a/launcher/ui/themes/ThemeManager.h b/launcher/ui/themes/ThemeManager.h index d2a6fb70a..1ce8c6f49 100644 --- a/launcher/ui/themes/ThemeManager.h +++ b/launcher/ui/themes/ThemeManager.h @@ -20,6 +20,7 @@ #include #include "ui/MainWindow.h" +#include "ui/themes/CatPack.h" #include "ui/themes/ITheme.h" inline auto themeDebugLog() @@ -40,18 +41,20 @@ class ThemeManager { void applyCurrentlySelectedTheme(bool initial = false); void setApplicationTheme(const QString& name, bool initial = false); - /// - /// Returns the cat based on selected cat and with events (Birthday, XMas, etc.) - /// - /// Optional, if you need a specific cat. - /// - static QString getCatImage(QString catName = ""); + /// @brief Returns the background based on selected and with events (Birthday, XMas, etc.) + /// @param catName Optional, if you need a specific background. + /// @return + QString getCatPack(QString catName = ""); + QList getValidCatPacks(); private: std::map> m_themes; + std::map> m_catPacks; MainWindow* m_mainWindow; void initializeThemes(); + void initializeCatPacks(); QString addTheme(std::unique_ptr theme); ITheme* getTheme(QString themeId); + QString addCatPack(std::unique_ptr catPack); }; diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.cpp b/launcher/ui/widgets/ThemeCustomizationWidget.cpp index 3bfcd8213..291f8ed9e 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.cpp +++ b/launcher/ui/widgets/ThemeCustomizationWidget.cpp @@ -95,9 +95,14 @@ void ThemeCustomizationWidget::applyWidgetTheme(int index) { emit currentWidgetThemeChanged(index); } -void ThemeCustomizationWidget::applyCatTheme(int index) { +void ThemeCustomizationWidget::applyCatTheme(int index) +{ auto settings = APPLICATION->settings(); - settings->set("BackgroundCat", m_catOptions[index].first); + auto originalCat = settings->get("BackgroundCat").toString(); + auto newCat = ui->backgroundCatComboBox->currentData().toString(); + if (originalCat != newCat) { + settings->set("BackgroundCat", newCat); + } emit currentCatChanged(index); } @@ -135,10 +140,10 @@ void ThemeCustomizationWidget::loadSettings() } auto cat = settings->get("BackgroundCat").toString(); - for (auto& catFromList : m_catOptions) { - QIcon catIcon = QIcon(QString(":/backgrounds/%1").arg(ThemeManager::getCatImage(catFromList.first))); - ui->backgroundCatComboBox->addItem(catIcon, catFromList.second); - if (cat == catFromList.first) { + for (auto& catFromList : APPLICATION->getValidCatPacks()) { + QIcon catIcon = QIcon(QString("%1").arg(catFromList->path())); + ui->backgroundCatComboBox->addItem(catIcon, catFromList->name(), catFromList->id()); + if (cat == catFromList->id()) { ui->backgroundCatComboBox->setCurrentIndex(ui->backgroundCatComboBox->count() - 1); } } diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.h b/launcher/ui/widgets/ThemeCustomizationWidget.h index 6c33c3c50..af47c788a 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.h +++ b/launcher/ui/widgets/ThemeCustomizationWidget.h @@ -53,25 +53,17 @@ class ThemeCustomizationWidget : public QWidget { private: Ui::ThemeCustomizationWidget* ui; - //TODO finish implementing - QList> m_iconThemeOptions{ - { "pe_colored", QObject::tr("Simple (Colored Icons)") }, - { "pe_light", QObject::tr("Simple (Light Icons)") }, - { "pe_dark", QObject::tr("Simple (Dark Icons)") }, - { "pe_blue", QObject::tr("Simple (Blue Icons)") }, - { "breeze_light", QObject::tr("Breeze Light") }, - { "breeze_dark", QObject::tr("Breeze Dark") }, - { "OSX", QObject::tr("OSX") }, - { "iOS", QObject::tr("iOS") }, - { "flat", QObject::tr("Flat") }, - { "flat_white", QObject::tr("Flat (White)") }, - { "multimc", QObject::tr("Legacy") }, - { "custom", QObject::tr("Custom") } - }; - QList> m_catOptions{ - { "kitteh", QObject::tr("Background Cat (from MultiMC)") }, - { "rory", QObject::tr("Rory ID 11 (drawn by Ashtaka)") }, - { "rory-flat", QObject::tr("Rory ID 11 (flat edition, drawn by Ashtaka)") }, - { "teawie", QObject::tr("Teawie (drawn by SympathyTea)") } - }; + // TODO finish implementing + QList> m_iconThemeOptions{ { "pe_colored", QObject::tr("Simple (Colored Icons)") }, + { "pe_light", QObject::tr("Simple (Light Icons)") }, + { "pe_dark", QObject::tr("Simple (Dark Icons)") }, + { "pe_blue", QObject::tr("Simple (Blue Icons)") }, + { "breeze_light", QObject::tr("Breeze Light") }, + { "breeze_dark", QObject::tr("Breeze Dark") }, + { "OSX", QObject::tr("OSX") }, + { "iOS", QObject::tr("iOS") }, + { "flat", QObject::tr("Flat") }, + { "flat_white", QObject::tr("Flat (White)") }, + { "multimc", QObject::tr("Legacy") }, + { "custom", QObject::tr("Custom") } }; }; diff --git a/libraries/README.md b/libraries/README.md index 4da11314d..e75a381ee 100644 --- a/libraries/README.md +++ b/libraries/README.md @@ -61,7 +61,7 @@ The `standard` and `legacy` launchers are available. Example (some parts have been censored): -``` +```text mod legacyjavafixer-1.0 mainClass net.minecraft.launchwrapper.Launch param --username diff --git a/libraries/libnbtplusplus b/libraries/libnbtplusplus index 2203af7ee..a5e8fd52b 160000 --- a/libraries/libnbtplusplus +++ b/libraries/libnbtplusplus @@ -1 +1 @@ -Subproject commit 2203af7eeb48c45398139b583615134efd8d407f +Subproject commit a5e8fd52b8bf4ab5d5bcc042b2a247867589985f diff --git a/nix/NIX.md b/nix/NIX.md index 980d20e87..aa945acc6 100644 --- a/nix/NIX.md +++ b/nix/NIX.md @@ -53,7 +53,8 @@ home.packages = [ pkgs.prismlauncher ]; ### Without flakes-enabled nix -#### Using channels +
+Using channels ```sh nix-channel --add https://github.com/PrismLauncher/PrismLauncher/archive/master.tar.gz prismlauncher @@ -61,7 +62,10 @@ nix-channel --update prismlauncher nix-env -iA prismlauncher ``` -#### Using the overlay +
+ +
+Using the overlay ```nix # In your configuration.nix: @@ -74,6 +78,8 @@ nix-env -iA prismlauncher } ``` +
+ ## Running ad-hoc If you're on a flakes-enabled nix you can run the launcher in one-line diff --git a/nix/default.nix b/nix/default.nix index 7bad1440c..47172927a 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -24,9 +24,9 @@ # Supported systems. systems = [ "x86_64-linux" - "x86_64-darwin" "aarch64-linux" - # Disabled due to qtbase being currently broken for "aarch64-darwin." + # Disabled due to our packages not supporting darwin yet. + # "x86_64-darwin" # "aarch64-darwin" ]; }