Merge branch 'develop' into feature/java-downloader
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
This commit is contained in:
		
							
								
								
									
										32
									
								
								.github/workflows/backport.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								.github/workflows/backport.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | name: Backport | ||||||
|  | on: | ||||||
|  |   pull_request_target: | ||||||
|  |     types: [closed, labeled] | ||||||
|  |  | ||||||
|  | # WARNING: | ||||||
|  | # When extending this action, be aware that $GITHUB_TOKEN allows write access to | ||||||
|  | # the GitHub repository. This means that it should not evaluate user input in a | ||||||
|  | # way that allows code injection. | ||||||
|  |  | ||||||
|  | permissions: | ||||||
|  |   contents: read | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   backport: | ||||||
|  |     permissions: | ||||||
|  |       contents: write # for korthout/backport-action to create branch | ||||||
|  |       pull-requests: write # for korthout/backport-action to create PR to backport | ||||||
|  |     name: Backport Pull Request | ||||||
|  |     if: github.repository_owner == 'PrismLauncher' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name)) | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/checkout@v3 | ||||||
|  |         with: | ||||||
|  |           ref: ${{ github.event.pull_request.head.sha }} | ||||||
|  |       - name: Create backport PRs | ||||||
|  |         uses: korthout/backport-action@v1.3.1 | ||||||
|  |         with: | ||||||
|  |           # Config README: https://github.com/korthout/backport-action#backport-action | ||||||
|  |           pull_description: |- | ||||||
|  |             Bot-based backport to `${target_branch}`, triggered by a label in #${pull_number}. | ||||||
|  |  | ||||||
							
								
								
									
										40
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										40
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -264,23 +264,23 @@ jobs: | |||||||
|       - name: Configure CMake (macOS) |       - name: Configure CMake (macOS) | ||||||
|         if: runner.os == 'macOS' && matrix.qt_ver == 6 |         if: runner.os == 'macOS' && matrix.qt_ver == 6 | ||||||
|         run: | |         run: | | ||||||
|           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja |           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja | ||||||
|  |  | ||||||
|       - name: Configure CMake (macOS-Legacy) |       - name: Configure CMake (macOS-Legacy) | ||||||
|         if: runner.os == 'macOS' && matrix.qt_ver == 5 |         if: runner.os == 'macOS' && matrix.qt_ver == 5 | ||||||
|         run: | |         run: | | ||||||
|           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja |           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja | ||||||
|  |  | ||||||
|       - name: Configure CMake (Windows MinGW-w64) |       - name: Configure CMake (Windows MinGW-w64) | ||||||
|         if: runner.os == 'Windows' && matrix.msystem != '' |         if: runner.os == 'Windows' && matrix.msystem != '' | ||||||
|         shell: msys2 {0} |         shell: msys2 {0} | ||||||
|         run: | |         run: | | ||||||
|           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja |           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja | ||||||
|  |  | ||||||
|       - name: Configure CMake (Windows MSVC) |       - name: Configure CMake (Windows MSVC) | ||||||
|         if: runner.os == 'Windows' && matrix.msystem == '' |         if: runner.os == 'Windows' && matrix.msystem == '' | ||||||
|         run: | |         run: | | ||||||
|           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON |           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON | ||||||
|           # https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix) |           # https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix) | ||||||
|           if ("${{ env.CCACHE_VAR }}") |           if ("${{ env.CCACHE_VAR }}") | ||||||
|           { |           { | ||||||
| @@ -295,7 +295,7 @@ jobs: | |||||||
|       - name: Configure CMake (Linux) |       - name: Configure CMake (Linux) | ||||||
|         if: runner.os == 'Linux' |         if: runner.os == 'Linux' | ||||||
|         run: | |         run: | | ||||||
|           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja |           cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja | ||||||
|  |  | ||||||
|       ## |       ## | ||||||
|       # BUILD |       # BUILD | ||||||
| @@ -586,33 +586,3 @@ jobs: | |||||||
|         with: |         with: | ||||||
|           bundle: "Prism Launcher.flatpak" |           bundle: "Prism Launcher.flatpak" | ||||||
|           manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml  |           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 |  | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -19,3 +19,6 @@ | |||||||
| [submodule "libraries/cmark"] | [submodule "libraries/cmark"] | ||||||
| 	path = libraries/cmark | 	path = libraries/cmark | ||||||
| 	url = https://github.com/commonmark/cmark.git | 	url = https://github.com/commonmark/cmark.git | ||||||
|  | [submodule "flatpak/shared-modules"] | ||||||
|  | 	path = flatpak/shared-modules | ||||||
|  | 	url = https://github.com/flathub/shared-modules.git | ||||||
|   | |||||||
| @@ -178,7 +178,7 @@ set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}. | |||||||
| set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0") | set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0") | ||||||
|  |  | ||||||
| # Build platform. | # Build platform. | ||||||
| set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.") | set(Launcher_BUILD_PLATFORM "unknown" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.") | ||||||
|  |  | ||||||
| # Channel list URL | # Channel list URL | ||||||
| set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.") | set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.") | ||||||
|   | |||||||
| @@ -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: | This can be done by appending `-s` to your `git commit` call, or by manually appending the following text to your commit message: | ||||||
|  |  | ||||||
| ``` | ```text | ||||||
| <commit message> | <commit message> | ||||||
|  |  | ||||||
| Signed-off-by: Author name <Author email> | Signed-off-by: Author name <Author email> | ||||||
| @@ -27,7 +27,7 @@ Signed-off-by: Author name <Author email> | |||||||
|  |  | ||||||
| By signing off your work, you agree to the terms below: | By signing off your work, you agree to the terms below: | ||||||
|  |  | ||||||
| ``` | ```text | ||||||
| Developer's Certificate of Origin 1.1 | Developer's Certificate of Origin 1.1 | ||||||
|  |  | ||||||
| By making a contribution to this project, I certify that: | By making a contribution to this project, I certify that: | ||||||
| @@ -61,3 +61,9 @@ 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-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 | [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. | ||||||
|  |  | ||||||
|  | This is done when pull requests are merged and have labels such as `backport release-7.x` - which should be added along with the milestone for the release. | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								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:** | - **Our Matrix space:** | ||||||
|  |  | ||||||
| [](https://prismlauncher.org/matrix) | [](https://prismlauncher.org/matrix) | ||||||
|  |  | ||||||
| - **Our Subreddit:** | - **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 | ## Translations | ||||||
|  |  | ||||||
| The translation effort for PrismLauncher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at <https://github.com/PrismLauncher/Translations> | 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 <https://github.com/PrismLauncher/Translations> | ||||||
|  |  | ||||||
| ## Building | ## Building | ||||||
|  |  | ||||||
| @@ -82,14 +82,16 @@ Thanks to the awesome people over at [MacStadium](https://www.macstadium.com/), | |||||||
|  |  | ||||||
| ## Forking/Redistributing/Custom builds policy | ## 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 (<https://prismlauncher.org>). | - Make it clear that your fork is not Prism Launcher and is not endorsed by or affiliated with the Prism Launcher project (<https://prismlauncher.org>). | ||||||
| - Go through [CMakeLists.txt](CMakeLists.txt) and change PrismLauncher's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled). | - 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. | 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) | - [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) | - [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions) | ||||||
|   | |||||||
| @@ -65,7 +65,7 @@ Config::Config() | |||||||
|     MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@"; |     MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@"; | ||||||
|     MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@"; |     MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@"; | ||||||
|  |  | ||||||
|     if (BUILD_PLATFORM == "macOS" && !MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty()) |     if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty()) | ||||||
|     { |     { | ||||||
|         UPDATER_ENABLED = true; |         UPDATER_ENABLED = true; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -68,7 +68,7 @@ class Config { | |||||||
|  |  | ||||||
|     bool UPDATER_ENABLED = false; |     bool UPDATER_ENABLED = false; | ||||||
|  |  | ||||||
|     /// A short string identifying this build's platform. For example, "lin64" or "win32". |     /// A short string identifying this build's platform or distribution. | ||||||
|     QString BUILD_PLATFORM; |     QString BUILD_PLATFORM; | ||||||
|  |  | ||||||
|     /// A string containing the build timestamp |     /// A string containing the build timestamp | ||||||
|   | |||||||
							
								
								
									
										24
									
								
								flake.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										24
									
								
								flake.lock
									
									
									
										generated
									
									
									
								
							| @@ -21,11 +21,11 @@ | |||||||
|         "nixpkgs-lib": "nixpkgs-lib" |         "nixpkgs-lib": "nixpkgs-lib" | ||||||
|       }, |       }, | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1688254665, |         "lastModified": 1688466019, | ||||||
|         "narHash": "sha256-8FHEgBrr7gYNiS/NzCxIO3m4hvtLRW9YY1nYo1ivm3o=", |         "narHash": "sha256-VeM2akYrBYMsb4W/MmBo1zmaMfgbL4cH3Pu8PGyIwJ0=", | ||||||
|         "owner": "hercules-ci", |         "owner": "hercules-ci", | ||||||
|         "repo": "flake-parts", |         "repo": "flake-parts", | ||||||
|         "rev": "267149c58a14d15f7f81b4d737308421de9d7152", |         "rev": "8e8d955c22df93dbe24f19ea04f47a74adbdc5ec", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
| @@ -76,11 +76,11 @@ | |||||||
|     "libnbtplusplus": { |     "libnbtplusplus": { | ||||||
|       "flake": false, |       "flake": false, | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1650031308, |         "lastModified": 1690036783, | ||||||
|         "narHash": "sha256-TvVOjkUobYJD9itQYueELJX3wmecvEdCbJ0FinW2mL4=", |         "narHash": "sha256-A5kTgICnx+Qdq3Fir/bKTfdTt/T1NQP2SC+nhN1ENug=", | ||||||
|         "owner": "PrismLauncher", |         "owner": "PrismLauncher", | ||||||
|         "repo": "libnbtplusplus", |         "repo": "libnbtplusplus", | ||||||
|         "rev": "2203af7eeb48c45398139b583615134efd8d407f", |         "rev": "a5e8fd52b8bf4ab5d5bcc042b2a247867589985f", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
| @@ -91,11 +91,11 @@ | |||||||
|     }, |     }, | ||||||
|     "nixpkgs": { |     "nixpkgs": { | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1688221086, |         "lastModified": 1690026219, | ||||||
|         "narHash": "sha256-cdW6qUL71cNWhHCpMPOJjlw0wzSRP0pVlRn2vqX/VVg=", |         "narHash": "sha256-oOduRk/kzQxOBknZXTLSEYd7tk+GoKvr8wV6Ab+t4AU=", | ||||||
|         "owner": "nixos", |         "owner": "nixos", | ||||||
|         "repo": "nixpkgs", |         "repo": "nixpkgs", | ||||||
|         "rev": "cd99c2b3c9f160cd004318e0697f90bbd5960825", |         "rev": "f465da166263bc0d4b39dfd4ca28b777c92d4b73", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
| @@ -138,11 +138,11 @@ | |||||||
|         ] |         ] | ||||||
|       }, |       }, | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1688386108, |         "lastModified": 1689668210, | ||||||
|         "narHash": "sha256-Vffto9QaVonzYAcPlAzd0soqWYpPpKk60dfNLSIXcFA=", |         "narHash": "sha256-XAATwDkaUxH958yXLs1lcEOmU6pSEIkatY3qjqk8X0E=", | ||||||
|         "owner": "cachix", |         "owner": "cachix", | ||||||
|         "repo": "pre-commit-hooks.nix", |         "repo": "pre-commit-hooks.nix", | ||||||
|         "rev": "42587d3414d1747999a5f71e92a83cf6547b62da", |         "rev": "eb433bff05b285258be76513add6f6c57b441775", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								flatpak/libdecor.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								flatpak/libdecor.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | { | ||||||
|  |   "name": "libdecor", | ||||||
|  |   "buildsystem": "meson", | ||||||
|  |   "config-opts": [ | ||||||
|  |     "-Ddemo=false" | ||||||
|  |   ], | ||||||
|  |   "sources": [ | ||||||
|  |     { | ||||||
|  |       "type": "git", | ||||||
|  |       "url": "https://gitlab.freedesktop.org/libdecor/libdecor.git", | ||||||
|  |       "commit": "73260393a97291c887e1074ab7f318e031be0ac6" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "type": "patch", | ||||||
|  |       "path": "patches/weird_libdecor.patch" | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   "cleanup": [ | ||||||
|  |     "/include", | ||||||
|  |     "/lib/pkgconfig" | ||||||
|  |   ] | ||||||
|  | } | ||||||
| @@ -5,13 +5,6 @@ sdk: org.kde.Sdk | |||||||
| sdk-extensions: | sdk-extensions: | ||||||
|   - org.freedesktop.Sdk.Extension.openjdk17 |   - org.freedesktop.Sdk.Extension.openjdk17 | ||||||
|   - org.freedesktop.Sdk.Extension.openjdk8 |   - org.freedesktop.Sdk.Extension.openjdk8 | ||||||
| add-extensions: |  | ||||||
|   com.valvesoftware.Steam.Utility.gamescope: |  | ||||||
|     version: stable |  | ||||||
|     add-ld-path: lib |  | ||||||
|     no-autodownload: true |  | ||||||
|     autodelete: false |  | ||||||
|     directory: utils/gamescope |  | ||||||
|  |  | ||||||
| command: prismlauncher | command: prismlauncher | ||||||
| finish-args: | finish-args: | ||||||
| @@ -26,12 +19,22 @@ finish-args: | |||||||
|     # Mod drag&drop |     # Mod drag&drop | ||||||
|   - --filesystem=xdg-download:ro |   - --filesystem=xdg-download:ro | ||||||
|  |  | ||||||
|  | cleanup: | ||||||
|  |   - /lib/libGLU* | ||||||
|  |  | ||||||
| modules: | modules: | ||||||
|  |   # Might be needed by some Controller mods (see https://github.com/isXander/Controlify/issues/31) | ||||||
|  |   - shared-modules/libusb/libusb.json | ||||||
|  |  | ||||||
|  |   # Needed for proper Wayland support | ||||||
|  |   - libdecor.json | ||||||
|  |  | ||||||
|   - name: prismlauncher |   - name: prismlauncher | ||||||
|     buildsystem: cmake-ninja |     buildsystem: cmake-ninja | ||||||
|  |     builddir: true | ||||||
|     config-opts: |     config-opts: | ||||||
|       - -DLauncher_BUILD_PLATFORM=flatpak |       - -DLauncher_BUILD_PLATFORM=flatpak | ||||||
|       - -DCMAKE_BUILD_TYPE=Debug |       - -DCMAKE_BUILD_TYPE=RelWithDebInfo | ||||||
|       - -DLauncher_QT_VERSION_MAJOR=5 |       - -DLauncher_QT_VERSION_MAJOR=5 | ||||||
|     build-options: |     build-options: | ||||||
|       env: |       env: | ||||||
| @@ -40,7 +43,7 @@ modules: | |||||||
|     sources: |     sources: | ||||||
|       - type: dir |       - type: dir | ||||||
|         path: ../ |         path: ../ | ||||||
|     builddir: true |  | ||||||
|   - name: openjdk |   - name: openjdk | ||||||
|     buildsystem: simple |     buildsystem: simple | ||||||
|     build-commands: |     build-commands: | ||||||
| @@ -49,14 +52,45 @@ modules: | |||||||
|       - mv /app/jre /app/jdk/17 |       - mv /app/jre /app/jdk/17 | ||||||
|       - /usr/lib/sdk/openjdk8/install.sh |       - /usr/lib/sdk/openjdk8/install.sh | ||||||
|       - mv /app/jre /app/jdk/8 |       - mv /app/jre /app/jdk/8 | ||||||
|     cleanup: [/jre] |     cleanup: | ||||||
|  |       - /jre | ||||||
|  |  | ||||||
|  |   - name: glfw | ||||||
|  |     buildsystem: cmake-ninja | ||||||
|  |     config-opts: | ||||||
|  |       - -DCMAKE_BUILD_TYPE=RelWithDebInfo | ||||||
|  |       - -DBUILD_SHARED_LIBS:BOOL=ON | ||||||
|  |       - -DGLFW_USE_WAYLAND=ON | ||||||
|  |     sources: | ||||||
|  |       - type: git | ||||||
|  |         url: https://github.com/glfw/glfw.git | ||||||
|  |         commit: 3fa2360720eeba1964df3c0ecf4b5df8648a8e52 | ||||||
|  |       - type: patch | ||||||
|  |         path: patches/0003-Don-t-crash-on-calls-to-focus-or-icon.patch | ||||||
|  |       - type: patch | ||||||
|  |         path: patches/0005-Add-warning-about-being-an-unofficial-patch.patch | ||||||
|  |       - type: patch | ||||||
|  |         path: patches/0007-Platform-Prefer-Wayland-over-X11.patch | ||||||
|  |     cleanup: | ||||||
|  |       - /include | ||||||
|  |       - /lib/cmake | ||||||
|  |       - /lib/pkgconfig | ||||||
|  |  | ||||||
|   - name: xrandr |   - name: xrandr | ||||||
|     buildsystem: autotools |     buildsystem: autotools | ||||||
|     sources: |     sources: | ||||||
|       - type: archive |       - type: archive | ||||||
|         url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.1.tar.xz |         url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.2.tar.xz | ||||||
|         sha256: 7bc76daf9d72f8aff885efad04ce06b90488a1a169d118dea8a2b661832e8762 |         sha256: c8bee4790d9058bacc4b6246456c58021db58a87ddda1a9d0139bf5f18f1f240 | ||||||
|     cleanup: [/share/man, /bin/xkeystone] |         x-checker-data: | ||||||
|  |           type: anitya | ||||||
|  |           project-id: 14957 | ||||||
|  |           stable-only: true | ||||||
|  |           url-template: https://xorg.freedesktop.org/archive/individual/app/xrandr-$version.tar.xz | ||||||
|  |     cleanup: | ||||||
|  |       - /share/man | ||||||
|  |       - /bin/xkeystone | ||||||
|  |  | ||||||
|   - name: gamemode |   - name: gamemode | ||||||
|     buildsystem: meson |     buildsystem: meson | ||||||
|     config-opts: |     config-opts: | ||||||
| @@ -67,19 +101,56 @@ modules: | |||||||
|       # post-install is running inside the build dir, we need it from the source though |       # post-install is running inside the build dir, we need it from the source though | ||||||
|       - install -Dm755 ../data/gamemoderun -t /app/bin |       - install -Dm755 ../data/gamemoderun -t /app/bin | ||||||
|     sources: |     sources: | ||||||
|       - type: git |       - type: archive | ||||||
|         url: https://github.com/FeralInteractive/gamemode |         archive-type: tar-gzip | ||||||
|         tag: "1.7" |         url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.7 | ||||||
|         commit: 4dc99dff76218718763a6b07fc1900fa6d1dafd9 |         sha256: 57ce73ba605d1cf12f8d13725006a895182308d93eba0f69f285648449641803 | ||||||
|  |         x-checker-data: | ||||||
|  |           type: json | ||||||
|  |           url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest | ||||||
|  |           version-query: .tag_name | ||||||
|  |           url-query: .tarball_url | ||||||
|  |           timestamp-query: .published_at | ||||||
|  |     cleanup: | ||||||
|  |       - /include | ||||||
|  |       - /lib/pkgconfig | ||||||
|  |       - /lib/libgamemodeauto.a | ||||||
|  |  | ||||||
|  |   - name: glxinfo | ||||||
|  |     buildsystem: meson | ||||||
|  |     config-opts: | ||||||
|  |       - --bindir=/app/mesa-demos | ||||||
|  |       - -Degl=disabled | ||||||
|  |       - -Dglut=disabled | ||||||
|  |       - -Dosmesa=disabled | ||||||
|  |       - -Dvulkan=disabled | ||||||
|  |       - -Dwayland=disabled | ||||||
|  |     post-install: | ||||||
|  |       - mv -v /app/mesa-demos/glxinfo /app/bin | ||||||
|  |     sources: | ||||||
|  |       - type: archive | ||||||
|  |         url: https://archive.mesa3d.org/demos/mesa-demos-9.0.0.tar.xz | ||||||
|  |         sha256: 3046a3d26a7b051af7ebdd257a5f23bfeb160cad6ed952329cdff1e9f1ed496b | ||||||
|  |         x-checker-data: | ||||||
|  |           type: anitya | ||||||
|  |           project-id: 16781 | ||||||
|  |           stable-only: true | ||||||
|  |           url-template: https://archive.mesa3d.org/demos/mesa-demos-$version.tar.xz | ||||||
|  |     cleanup: | ||||||
|  |       - /include | ||||||
|  |       - /mesa-demos | ||||||
|  |       - /share | ||||||
|  |     modules: | ||||||
|  |       - shared-modules/glu/glu-9.json | ||||||
|  |  | ||||||
|   - name: enhance |   - name: enhance | ||||||
|     buildsystem: simple |     buildsystem: simple | ||||||
|     build-commands: |     build-commands: | ||||||
|       - mkdir -p /app/utils/gamescope |  | ||||||
|       - install -Dm755 prime-run /app/bin/prime-run |       - install -Dm755 prime-run /app/bin/prime-run | ||||||
|       - mv /app/bin/prismlauncher /app/bin/prismrun |       - mv /app/bin/prismlauncher /app/bin/prismrun | ||||||
|       - install -Dm755 prismlauncher /app/bin/prismlauncher |       - install -Dm755 prismlauncher /app/bin/prismlauncher | ||||||
|     sources: |     sources: | ||||||
|       - type: file |       - type: file | ||||||
|         path: ../flatpak/prime-run |         path: prime-run | ||||||
|       - type: file |       - type: file | ||||||
|         path: ../flatpak/prismlauncher |         path: prismlauncher | ||||||
|   | |||||||
| @@ -0,0 +1,24 @@ | |||||||
|  | diff --git a/src/wl_window.c b/src/wl_window.c | ||||||
|  | index 52d3b9eb..4ac4eb5d 100644 | ||||||
|  | --- a/src/wl_window.c | ||||||
|  | +++ b/src/wl_window.c | ||||||
|  | @@ -2117,8 +2117,7 @@ void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title) | ||||||
|  |  void _glfwSetWindowIconWayland(_GLFWwindow* window, | ||||||
|  |                                 int count, const GLFWimage* images) | ||||||
|  |  { | ||||||
|  | -    _glfwInputError(GLFW_FEATURE_UNAVAILABLE, | ||||||
|  | -                    "Wayland: The platform does not support setting the window icon"); | ||||||
|  | +    fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the window icon\n"); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos) | ||||||
|  | @@ -2361,8 +2360,7 @@ void _glfwRequestWindowAttentionWayland(_GLFWwindow* window) | ||||||
|  |   | ||||||
|  |  void _glfwFocusWindowWayland(_GLFWwindow* window) | ||||||
|  |  { | ||||||
|  | -    _glfwInputError(GLFW_FEATURE_UNAVAILABLE, | ||||||
|  | -                    "Wayland: The platform does not support setting the input focus"); | ||||||
|  | +    fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the input focus\n"); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  void _glfwSetWindowMonitorWayland(_GLFWwindow* window, | ||||||
| @@ -0,0 +1,17 @@ | |||||||
|  | diff --git a/src/init.c b/src/init.c | ||||||
|  | index 06dbb3f2..a7c6da86 100644 | ||||||
|  | --- a/src/init.c | ||||||
|  | +++ b/src/init.c | ||||||
|  | @@ -449,6 +449,12 @@ GLFWAPI int glfwInit(void) | ||||||
|  |      _glfw.initialized = GLFW_TRUE; | ||||||
|  |   | ||||||
|  |      glfwDefaultWindowHints(); | ||||||
|  | + | ||||||
|  | +    fprintf(stderr, "!!! Patched GLFW from https://github.com/Admicos/minecraft-wayland\n" | ||||||
|  | +         "!!! If any issues with the window, or some issues with rendering, occur, " | ||||||
|  | +         "first try with the built-in GLFW, and if that solves the issue, report there first.\n" | ||||||
|  | +         "!!! Use outside Minecraft is untested, and things might break.\n"); | ||||||
|  | + | ||||||
|  |      return GLFW_TRUE; | ||||||
|  |  } | ||||||
|  |   | ||||||
							
								
								
									
										20
									
								
								flatpak/patches/0007-Platform-Prefer-Wayland-over-X11.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								flatpak/patches/0007-Platform-Prefer-Wayland-over-X11.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | diff --git a/src/platform.c b/src/platform.c | ||||||
|  | index c5966ae7..3e7442f9 100644 | ||||||
|  | --- a/src/platform.c | ||||||
|  | +++ b/src/platform.c | ||||||
|  | @@ -49,12 +49,12 @@ static const struct | ||||||
|  |  #if defined(_GLFW_COCOA) | ||||||
|  |      { GLFW_PLATFORM_COCOA, _glfwConnectCocoa }, | ||||||
|  |  #endif | ||||||
|  | -#if defined(_GLFW_X11) | ||||||
|  | -    { GLFW_PLATFORM_X11, _glfwConnectX11 }, | ||||||
|  | -#endif | ||||||
|  |  #if defined(_GLFW_WAYLAND) | ||||||
|  |      { GLFW_PLATFORM_WAYLAND, _glfwConnectWayland }, | ||||||
|  |  #endif | ||||||
|  | +#if defined(_GLFW_X11) | ||||||
|  | +    { GLFW_PLATFORM_X11, _glfwConnectX11 }, | ||||||
|  | +#endif | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  GLFWbool _glfwSelectPlatform(int desiredID, _GLFWplatform* platform) | ||||||
							
								
								
									
										40
									
								
								flatpak/patches/weird_libdecor.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								flatpak/patches/weird_libdecor.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | diff --git a/src/libdecor.c b/src/libdecor.c | ||||||
|  | index a9c1106..1aa38b3 100644 | ||||||
|  | --- a/src/libdecor.c | ||||||
|  | +++ b/src/libdecor.c | ||||||
|  | @@ -1391,22 +1391,32 @@ calculate_priority(const struct libdecor_plugin_description *plugin_description) | ||||||
|  |  static bool | ||||||
|  |  check_symbol_conflicts(const struct libdecor_plugin_description *plugin_description) | ||||||
|  |  { | ||||||
|  | +	bool ret = true; | ||||||
|  |  	char * const *symbol; | ||||||
|  | +	void* main_prog = dlopen(NULL, RTLD_LAZY); | ||||||
|  | +	if (!main_prog) { | ||||||
|  | +		fprintf(stderr, "Plugin \"%s\" couldn't check conflicting symbols: \"%s\".\n", | ||||||
|  | +					plugin_description->description, dlerror()); | ||||||
|  | +		return false; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  |   | ||||||
|  |  	symbol = plugin_description->conflicting_symbols; | ||||||
|  |  	while (*symbol) { | ||||||
|  |  		dlerror(); | ||||||
|  | -		dlsym (RTLD_DEFAULT, *symbol); | ||||||
|  | +		dlsym (main_prog, *symbol); | ||||||
|  |  		if (!dlerror()) { | ||||||
|  |  			fprintf(stderr, "Plugin \"%s\" uses conflicting symbol \"%s\".\n", | ||||||
|  |  					plugin_description->description, *symbol); | ||||||
|  | -			return false; | ||||||
|  | +			ret = false; | ||||||
|  | +			break; | ||||||
|  |  		} | ||||||
|  |   | ||||||
|  |  		symbol++; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	return true; | ||||||
|  | +	dlclose(main_prog); | ||||||
|  | +	return ret; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static struct plugin_loader * | ||||||
| @@ -5,7 +5,7 @@ for i in {0..9}; do | |||||||
|     test -S "$XDG_RUNTIME_DIR"/discord-ipc-"$i" || ln -sf {app/com.discordapp.Discord,"$XDG_RUNTIME_DIR"}/discord-ipc-"$i"; |     test -S "$XDG_RUNTIME_DIR"/discord-ipc-"$i" || ln -sf {app/com.discordapp.Discord,"$XDG_RUNTIME_DIR"}/discord-ipc-"$i"; | ||||||
| done | done | ||||||
|  |  | ||||||
| export PATH="${PATH}${PATH:+:}/app/utils/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin" | export PATH="${PATH}${PATH:+:}/usr/lib/extensions/vulkan/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin" | ||||||
| export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}${LD_LIBRARY_PATH:+:}/usr/lib/extensions/vulkan/MangoHud/\$LIB/" | export VK_LAYER_PATH="/usr/lib/extensions/vulkan/share/vulkan/implicit_layer.d/" | ||||||
|  |  | ||||||
| exec /app/bin/prismrun "$@" | exec /app/bin/prismrun "$@" | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								flatpak/shared-modules
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								flatpak/shared-modules
									
									
									
									
									
										Submodule
									
								
							 Submodule flatpak/shared-modules added at 45094ca570
									
								
							
							
								
								
									
										6
									
								
								garnix.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								garnix.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | builds: | ||||||
|  |   exclude: [] | ||||||
|  |   include: | ||||||
|  |     - "checks.x86_64-linux.*" | ||||||
|  |     - "devShells.*.*" | ||||||
|  |     - "packages.*.*" | ||||||
| @@ -6,9 +6,10 @@ | |||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  *  Copyright (C) 2022 Lenny McLennington <lenny@sneed.church> |  *  Copyright (C) 2022 Lenny McLennington <lenny@sneed.church> | ||||||
|  *  Copyright (C) 2022 Tayou <tayou@gmx.net> |  *  Copyright (C) 2022 Tayou <git@tayou.org> | ||||||
|  *  Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> |  *  Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> | ||||||
|  *  Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> |  *  Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> | ||||||
|  |  *  Copyright (C) 2023 seth <getchoo at tuta dot io> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
| @@ -475,6 +476,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) | |||||||
|  |  | ||||||
|         qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT; |         qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT; | ||||||
|         qDebug() << "Version                    : " << BuildConfig.printableVersionString(); |         qDebug() << "Version                    : " << BuildConfig.printableVersionString(); | ||||||
|  |         qDebug() << "Platform                   : " << BuildConfig.BUILD_PLATFORM; | ||||||
|         qDebug() << "Git commit                 : " << BuildConfig.GIT_COMMIT; |         qDebug() << "Git commit                 : " << BuildConfig.GIT_COMMIT; | ||||||
|         qDebug() << "Git refspec                : " << BuildConfig.GIT_REFSPEC; |         qDebug() << "Git refspec                : " << BuildConfig.GIT_REFSPEC; | ||||||
|         if (adjustedBy.size()) |         if (adjustedBy.size()) | ||||||
| @@ -609,6 +611,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) | |||||||
|         m_settings->registerSetting("IgnoreJavaCompatibility", false); |         m_settings->registerSetting("IgnoreJavaCompatibility", false); | ||||||
|         m_settings->registerSetting("IgnoreJavaWizard", false); |         m_settings->registerSetting("IgnoreJavaWizard", false); | ||||||
|  |  | ||||||
|  |         // Mod loader settings | ||||||
|  |         m_settings->registerSetting("DisableQuiltBeacon", false); | ||||||
|  |  | ||||||
|         // Native library workarounds |         // Native library workarounds | ||||||
|         m_settings->registerSetting("UseNativeOpenAL", false); |         m_settings->registerSetting("UseNativeOpenAL", false); | ||||||
|         m_settings->registerSetting("UseNativeGLFW", false); |         m_settings->registerSetting("UseNativeGLFW", false); | ||||||
| @@ -1181,7 +1186,17 @@ QIcon Application::getThemedIcon(const QString& name) | |||||||
|     return QIcon::fromTheme(name); |     return QIcon::fromTheme(name); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool Application::openJsonEditor(const QString &filename) | QList<CatPack*> 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); |     const QString file = QDir::current().absoluteFilePath(filename); | ||||||
|     if (m_settings->get("JsonEditor").toString().isEmpty()) |     if (m_settings->get("JsonEditor").toString().isEmpty()) | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
| /* | /* | ||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  *  Copyright (C) 2022 Tayou <tayou@gmx.net> |  *  Copyright (C) 2022 Tayou <git@tayou.org> | ||||||
|  *  Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> |  *  Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
| @@ -48,6 +48,7 @@ | |||||||
| #include <BaseInstance.h> | #include <BaseInstance.h> | ||||||
|  |  | ||||||
| #include "minecraft/launch/MinecraftServerTarget.h" | #include "minecraft/launch/MinecraftServerTarget.h" | ||||||
|  | #include "ui/themes/CatPack.h" | ||||||
|  |  | ||||||
| class LaunchController; | class LaunchController; | ||||||
| class LocalPeer; | class LocalPeer; | ||||||
| @@ -126,9 +127,11 @@ public: | |||||||
|  |  | ||||||
|     void setApplicationTheme(const QString& name); |     void setApplicationTheme(const QString& name); | ||||||
|  |  | ||||||
|     shared_qobject_ptr<ExternalUpdater> updater() { |     QList<CatPack*> getValidCatPacks(); | ||||||
|         return m_updater; |  | ||||||
|     } |     QString getCatPack(QString catName = ""); | ||||||
|  |  | ||||||
|  |     shared_qobject_ptr<ExternalUpdater> updater() { return m_updater; } | ||||||
|  |  | ||||||
|     void triggerUpdateCheck(); |     void triggerUpdateCheck(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -15,16 +15,15 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <memory> |  | ||||||
| #include <QString> |  | ||||||
| #include <QMetaType> | #include <QMetaType> | ||||||
|  | #include <QString> | ||||||
|  | #include <memory> | ||||||
|  |  | ||||||
| /*! | /*! | ||||||
|  * An abstract base class for versions. |  * An abstract base class for versions. | ||||||
|  */ |  */ | ||||||
| class BaseVersion | class BaseVersion { | ||||||
| { |    public: | ||||||
| public: |  | ||||||
|     using Ptr = std::shared_ptr<BaseVersion>; |     using Ptr = std::shared_ptr<BaseVersion>; | ||||||
|     virtual ~BaseVersion() {} |     virtual ~BaseVersion() {} | ||||||
|     /*! |     /*! | ||||||
| @@ -45,14 +44,8 @@ public: | |||||||
|      */ |      */ | ||||||
|     virtual QString typeString() const = 0; |     virtual QString typeString() const = 0; | ||||||
|  |  | ||||||
|     virtual bool operator<(BaseVersion &a) |     virtual bool operator<(BaseVersion& a) { return name() < a.name(); }; | ||||||
|     { |     virtual bool operator>(BaseVersion& a) { return name() > a.name(); }; | ||||||
|         return name() < a.name(); |  | ||||||
|     }; |  | ||||||
|     virtual bool operator>(BaseVersion &a) |  | ||||||
|     { |  | ||||||
|         return name() > a.name(); |  | ||||||
|     }; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| Q_DECLARE_METATYPE(BaseVersion::Ptr) | Q_DECLARE_METATYPE(BaseVersion::Ptr) | ||||||
|   | |||||||
| @@ -763,6 +763,8 @@ SET(LAUNCHER_SOURCES | |||||||
|     ui/themes/SystemTheme.h |     ui/themes/SystemTheme.h | ||||||
|     ui/themes/ThemeManager.cpp |     ui/themes/ThemeManager.cpp | ||||||
|     ui/themes/ThemeManager.h |     ui/themes/ThemeManager.h | ||||||
|  |     ui/themes/CatPack.cpp | ||||||
|  |     ui/themes/CatPack.h | ||||||
|  |  | ||||||
|     # Processes |     # Processes | ||||||
|     LaunchController.h |     LaunchController.h | ||||||
|   | |||||||
| @@ -99,7 +99,7 @@ void InstanceImportTask::executeTask() | |||||||
|  |  | ||||||
|         connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded); |         connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded); | ||||||
|         connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged); |         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::failed, this, &InstanceImportTask::downloadFailed); | ||||||
|         connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted); |         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::failed, this, &InstanceImportTask::emitFailed); | ||||||
|     connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress); |     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::status, this, &InstanceImportTask::setStatus); | ||||||
|     connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails); |     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::failed, this, &InstanceImportTask::emitFailed); | ||||||
|     connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress); |     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::status, this, &InstanceImportTask::setStatus); | ||||||
|     connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails); |     connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails); | ||||||
|     connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater); |     connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater); | ||||||
|   | |||||||
| @@ -799,7 +799,7 @@ class InstanceStaging : public Task { | |||||||
|         connect(child, &Task::status, this, &InstanceStaging::setStatus); |         connect(child, &Task::status, this, &InstanceStaging::setStatus); | ||||||
|         connect(child, &Task::details, this, &InstanceStaging::setDetails); |         connect(child, &Task::details, this, &InstanceStaging::setDetails); | ||||||
|         connect(child, &Task::progress, this, &InstanceStaging::setProgress); |         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); |         connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -390,7 +390,10 @@ void LaunchController::launchInstance() | |||||||
|     m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher)); |     m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher)); | ||||||
|  |  | ||||||
|     // Prepend Version |     // Prepend Version | ||||||
|     m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), BuildConfig.LAUNCHER_DISPLAYNAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher)); |     { | ||||||
|  |         auto versionString = QString("%1 version: %2 (%3)").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString(), BuildConfig.BUILD_PLATFORM); | ||||||
|  |         m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), versionString + "\n\n", MessageLevel::Launcher)); | ||||||
|  |     } | ||||||
|     m_launcher->start(); |     m_launcher->start(); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,8 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  |  *  Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
| @@ -33,56 +34,50 @@ | |||||||
|  *      limitations under the License. |  *      limitations under the License. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | #include "MMCZip.h" | ||||||
| #include <quazip/quazip.h> | #include <quazip/quazip.h> | ||||||
| #include <quazip/quazipdir.h> | #include <quazip/quazipdir.h> | ||||||
| #include <quazip/quazipfile.h> | #include <quazip/quazipfile.h> | ||||||
| #include "MMCZip.h" |  | ||||||
| #include "FileSystem.h" | #include "FileSystem.h" | ||||||
|  |  | ||||||
| #include <QCoreApplication> | #include <QCoreApplication> | ||||||
| #include <QDebug> | #include <QDebug> | ||||||
|  | #include <QtConcurrentRun> | ||||||
|  |  | ||||||
|  | namespace MMCZip { | ||||||
| // ours | // ours | ||||||
| bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained, const FilterFunction filter) | bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet<QString>& contained, const FilterFunction filter) | ||||||
| { | { | ||||||
|     QuaZip modZip(from.filePath()); |     QuaZip modZip(from.filePath()); | ||||||
|     modZip.open(QuaZip::mdUnzip); |     modZip.open(QuaZip::mdUnzip); | ||||||
|  |  | ||||||
|     QuaZipFile fileInsideMod(&modZip); |     QuaZipFile fileInsideMod(&modZip); | ||||||
|     QuaZipFile zipOutFile(into); |     QuaZipFile zipOutFile(into); | ||||||
|     for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile()) |     for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile()) { | ||||||
|     { |  | ||||||
|         QString filename = modZip.getCurrentFileName(); |         QString filename = modZip.getCurrentFileName(); | ||||||
|         if (filter && !filter(filename)) |         if (filter && !filter(filename)) { | ||||||
|         { |             qDebug() << "Skipping file " << filename << " from " << from.fileName() << " - filtered"; | ||||||
|             qDebug() << "Skipping file " << filename << " from " |  | ||||||
|                         << from.fileName() << " - filtered"; |  | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|         if (contained.contains(filename)) |         if (contained.contains(filename)) { | ||||||
|         { |             qDebug() << "Skipping already contained file " << filename << " from " << from.fileName(); | ||||||
|             qDebug() << "Skipping already contained file " << filename << " from " |  | ||||||
|                         << from.fileName(); |  | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|         contained.insert(filename); |         contained.insert(filename); | ||||||
|  |  | ||||||
|         if (!fileInsideMod.open(QIODevice::ReadOnly)) |         if (!fileInsideMod.open(QIODevice::ReadOnly)) { | ||||||
|         { |  | ||||||
|             qCritical() << "Failed to open " << filename << " from " << from.fileName(); |             qCritical() << "Failed to open " << filename << " from " << from.fileName(); | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         QuaZipNewInfo info_out(fileInsideMod.getActualFileName()); |         QuaZipNewInfo info_out(fileInsideMod.getActualFileName()); | ||||||
|  |  | ||||||
|         if (!zipOutFile.open(QIODevice::WriteOnly, info_out)) |         if (!zipOutFile.open(QIODevice::WriteOnly, info_out)) { | ||||||
|         { |  | ||||||
|             qCritical() << "Failed to open " << filename << " in the jar"; |             qCritical() << "Failed to open " << filename << " in the jar"; | ||||||
|             fileInsideMod.close(); |             fileInsideMod.close(); | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|         if (!JlCompress::copyData(fileInsideMod, zipOutFile)) |         if (!JlCompress::copyData(fileInsideMod, zipOutFile)) { | ||||||
|         { |  | ||||||
|             zipOutFile.close(); |             zipOutFile.close(); | ||||||
|             fileInsideMod.close(); |             fileInsideMod.close(); | ||||||
|             qCritical() << "Failed to copy data of " << filename << " into the jar"; |             qCritical() << "Failed to copy data of " << filename << " into the jar"; | ||||||
| @@ -94,10 +89,11 @@ bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &containe | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, bool followSymlinks) | bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool followSymlinks) | ||||||
| { | { | ||||||
|     QDir directory(dir); |     QDir directory(dir); | ||||||
|     if (!directory.exists()) return false; |     if (!directory.exists()) | ||||||
|  |         return false; | ||||||
|  |  | ||||||
|     for (auto e : files) { |     for (auto e : files) { | ||||||
|         auto filePath = directory.relativeFilePath(e.absoluteFilePath()); |         auto filePath = directory.relativeFilePath(e.absoluteFilePath()); | ||||||
| @@ -109,17 +105,18 @@ bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, boo | |||||||
|                 srcPath = e.canonicalFilePath(); |                 srcPath = e.canonicalFilePath(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         if( !JlCompress::compressFile(zip, srcPath, filePath)) return false; |         if (!JlCompress::compressFile(zip, srcPath, filePath)) | ||||||
|  |             return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks) | bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks) | ||||||
| { | { | ||||||
|     QuaZip zip(fileCompressed); |     QuaZip zip(fileCompressed); | ||||||
|     QDir().mkpath(QFileInfo(fileCompressed).absolutePath()); |     QDir().mkpath(QFileInfo(fileCompressed).absolutePath()); | ||||||
|     if(!zip.open(QuaZip::mdCreate)) { |     if (!zip.open(QuaZip::mdCreate)) { | ||||||
|         QFile::remove(fileCompressed); |         QFile::remove(fileCompressed); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| @@ -127,7 +124,7 @@ bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList | |||||||
|     auto result = compressDirFiles(&zip, dir, files, followSymlinks); |     auto result = compressDirFiles(&zip, dir, files, followSymlinks); | ||||||
|  |  | ||||||
|     zip.close(); |     zip.close(); | ||||||
|     if(zip.getZipError()!=0) { |     if (zip.getZipError() != 0) { | ||||||
|         QFile::remove(fileCompressed); |         QFile::remove(fileCompressed); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| @@ -136,11 +133,10 @@ bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList | |||||||
| } | } | ||||||
|  |  | ||||||
| // ours | // ours | ||||||
| bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods) | bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods) | ||||||
| { | { | ||||||
|     QuaZip zipOut(targetJarPath); |     QuaZip zipOut(targetJarPath); | ||||||
|     if (!zipOut.open(QuaZip::mdCreate)) |     if (!zipOut.open(QuaZip::mdCreate)) { | ||||||
|     { |  | ||||||
|         QFile::remove(targetJarPath); |         QFile::remove(targetJarPath); | ||||||
|         qCritical() << "Failed to open the minecraft.jar for modding"; |         qCritical() << "Failed to open the minecraft.jar for modding"; | ||||||
|         return false; |         return false; | ||||||
| @@ -151,37 +147,29 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const | |||||||
|  |  | ||||||
|     // Modify the jar |     // Modify the jar | ||||||
|     // This needs to be done in reverse-order to ensure we respect the loading order of components |     // This needs to be done in reverse-order to ensure we respect the loading order of components | ||||||
|     for (auto i = mods.crbegin(); i != mods.crend(); i++) |     for (auto i = mods.crbegin(); i != mods.crend(); i++) { | ||||||
|     { |  | ||||||
|         const auto* mod = *i; |         const auto* mod = *i; | ||||||
|         // do not merge disabled mods. |         // do not merge disabled mods. | ||||||
|         if (!mod->enabled()) |         if (!mod->enabled()) | ||||||
|             continue; |             continue; | ||||||
|         if (mod->type() == ResourceType::ZIPFILE) |         if (mod->type() == ResourceType::ZIPFILE) { | ||||||
|         { |             if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles)) { | ||||||
|             if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles)) |  | ||||||
|             { |  | ||||||
|                 zipOut.close(); |                 zipOut.close(); | ||||||
|                 QFile::remove(targetJarPath); |                 QFile::remove(targetJarPath); | ||||||
|                 qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; |                 qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|         } |         } else if (mod->type() == ResourceType::SINGLEFILE) { | ||||||
|         else if (mod->type() == ResourceType::SINGLEFILE) |  | ||||||
|         { |  | ||||||
|             // FIXME: buggy - does not work with addedFiles |             // FIXME: buggy - does not work with addedFiles | ||||||
|             auto filename = mod->fileinfo(); |             auto filename = mod->fileinfo(); | ||||||
|             if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) |             if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) { | ||||||
|             { |  | ||||||
|                 zipOut.close(); |                 zipOut.close(); | ||||||
|                 QFile::remove(targetJarPath); |                 QFile::remove(targetJarPath); | ||||||
|                 qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; |                 qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|             addedFiles.insert(filename.fileName()); |             addedFiles.insert(filename.fileName()); | ||||||
|         } |         } else if (mod->type() == ResourceType::FOLDER) { | ||||||
|         else if (mod->type() == ResourceType::FOLDER) |  | ||||||
|         { |  | ||||||
|             // untested, but seems to be unused / not possible to reach |             // untested, but seems to be unused / not possible to reach | ||||||
|             // FIXME: buggy - does not work with addedFiles |             // FIXME: buggy - does not work with addedFiles | ||||||
|             auto filename = mod->fileinfo(); |             auto filename = mod->fileinfo(); | ||||||
| @@ -190,25 +178,21 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const | |||||||
|             dir.cdUp(); |             dir.cdUp(); | ||||||
|             QString parent_dir = dir.absolutePath(); |             QString parent_dir = dir.absolutePath(); | ||||||
|             auto files = QFileInfoList(); |             auto files = QFileInfoList(); | ||||||
|             MMCZip::collectFileListRecursively(what_to_zip, nullptr, &files, nullptr); |             collectFileListRecursively(what_to_zip, nullptr, &files, nullptr); | ||||||
|  |  | ||||||
|             for (auto e : files) { |             for (auto e : files) { | ||||||
|                 if (addedFiles.contains(e.filePath())) |                 if (addedFiles.contains(e.filePath())) | ||||||
|                     files.removeAll(e); |                     files.removeAll(e); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (!MMCZip::compressDirFiles(&zipOut, parent_dir, files)) |             if (!compressDirFiles(&zipOut, parent_dir, files)) { | ||||||
|             { |  | ||||||
|                 zipOut.close(); |                 zipOut.close(); | ||||||
|                 QFile::remove(targetJarPath); |                 QFile::remove(targetJarPath); | ||||||
|                 qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; |                 qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|             qDebug() << "Adding folder " << filename.fileName() << " from " |             qDebug() << "Adding folder " << filename.fileName() << " from " << filename.absoluteFilePath(); | ||||||
|                      << filename.absoluteFilePath(); |         } else { | ||||||
|         } |  | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             // Make sure we do not continue launching when something is missing or undefined... |             // Make sure we do not continue launching when something is missing or undefined... | ||||||
|             zipOut.close(); |             zipOut.close(); | ||||||
|             QFile::remove(targetJarPath); |             QFile::remove(targetJarPath); | ||||||
| @@ -217,8 +201,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key){return !key.contains("META-INF");})) |     if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key) { return !key.contains("META-INF"); })) { | ||||||
|     { |  | ||||||
|         zipOut.close(); |         zipOut.close(); | ||||||
|         QFile::remove(targetJarPath); |         QFile::remove(targetJarPath); | ||||||
|         qCritical() << "Failed to insert minecraft.jar contents."; |         qCritical() << "Failed to insert minecraft.jar contents."; | ||||||
| @@ -227,8 +210,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const | |||||||
|  |  | ||||||
|     // Recompress the jar |     // Recompress the jar | ||||||
|     zipOut.close(); |     zipOut.close(); | ||||||
|     if (zipOut.getZipError() != 0) |     if (zipOut.getZipError() != 0) { | ||||||
|     { |  | ||||||
|         QFile::remove(targetJarPath); |         QFile::remove(targetJarPath); | ||||||
|         qCritical() << "Failed to finalize minecraft.jar!"; |         qCritical() << "Failed to finalize minecraft.jar!"; | ||||||
|         return false; |         return false; | ||||||
| @@ -237,7 +219,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const | |||||||
| } | } | ||||||
|  |  | ||||||
| // ours | // ours | ||||||
| QString MMCZip::findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root) | QString findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root) | ||||||
| { | { | ||||||
|     QuaZipDir rootDir(zip, root); |     QuaZipDir rootDir(zip, root); | ||||||
|     for (auto&& fileName : rootDir.entryList(QDir::Files)) { |     for (auto&& fileName : rootDir.entryList(QDir::Files)) { | ||||||
| @@ -261,27 +243,23 @@ QString MMCZip::findFolderOfFileInZip(QuaZip* zip, const QString& what, const QS | |||||||
| } | } | ||||||
|  |  | ||||||
| // ours | // ours | ||||||
| bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root) | bool findFilesInZip(QuaZip* zip, const QString& what, QStringList& result, const QString& root) | ||||||
| { | { | ||||||
|     QuaZipDir rootDir(zip, root); |     QuaZipDir rootDir(zip, root); | ||||||
|     for(auto fileName: rootDir.entryList(QDir::Files)) |     for (auto fileName : rootDir.entryList(QDir::Files)) { | ||||||
|     { |         if (fileName == what) { | ||||||
|         if(fileName == what) |  | ||||||
|         { |  | ||||||
|             result.append(root); |             result.append(root); | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     for(auto fileName: rootDir.entryList(QDir::Dirs)) |     for (auto fileName : rootDir.entryList(QDir::Dirs)) { | ||||||
|     { |  | ||||||
|         findFilesInZip(zip, what, result, root + fileName); |         findFilesInZip(zip, what, result, root + fileName); | ||||||
|     } |     } | ||||||
|     return !result.isEmpty(); |     return !result.isEmpty(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| // ours | // ours | ||||||
| std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target) | std::optional<QStringList> extractSubDir(QuaZip* zip, const QString& subdir, const QString& target) | ||||||
| { | { | ||||||
|     auto target_top_dir = QUrl::fromLocalFile(target); |     auto target_top_dir = QUrl::fromLocalFile(target); | ||||||
|  |  | ||||||
| @@ -289,16 +267,13 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su | |||||||
|  |  | ||||||
|     qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target; |     qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target; | ||||||
|     auto numEntries = zip->getEntriesCount(); |     auto numEntries = zip->getEntriesCount(); | ||||||
|     if(numEntries < 0) { |     if (numEntries < 0) { | ||||||
|         qWarning() << "Failed to enumerate files in archive"; |         qWarning() << "Failed to enumerate files in archive"; | ||||||
|         return std::nullopt; |         return std::nullopt; | ||||||
|     } |     } else if (numEntries == 0) { | ||||||
|     else if(numEntries == 0) { |  | ||||||
|         qDebug() << "Extracting empty archives seems odd..."; |         qDebug() << "Extracting empty archives seems odd..."; | ||||||
|         return extracted; |         return extracted; | ||||||
|     } |     } else if (!zip->goToFirstFile()) { | ||||||
|     else if (!zip->goToFirstFile()) |  | ||||||
|     { |  | ||||||
|         qWarning() << "Failed to seek to first file in zip"; |         qWarning() << "Failed to seek to first file in zip"; | ||||||
|         return std::nullopt; |         return std::nullopt; | ||||||
|     } |     } | ||||||
| @@ -334,7 +309,8 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (!target_top_dir.isParentOf(QUrl::fromLocalFile(target_file_path))) { |         if (!target_top_dir.isParentOf(QUrl::fromLocalFile(target_file_path))) { | ||||||
|             qWarning() << "Extracting" << relative_file_name << "was cancelled, because it was effectively outside of the target path" << target; |             qWarning() << "Extracting" << relative_file_name << "was cancelled, because it was effectively outside of the target path" | ||||||
|  |                        << target; | ||||||
|             return std::nullopt; |             return std::nullopt; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -345,7 +321,8 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         extracted.append(target_file_path); |         extracted.append(target_file_path); | ||||||
|         QFile::setPermissions(target_file_path, QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser); |         QFile::setPermissions(target_file_path, | ||||||
|  |                               QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser); | ||||||
|  |  | ||||||
|         qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path; |         qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path; | ||||||
|     } while (zip->goToNextFile()); |     } while (zip->goToNextFile()); | ||||||
| @@ -354,66 +331,66 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su | |||||||
| } | } | ||||||
|  |  | ||||||
| // ours | // ours | ||||||
| bool MMCZip::extractRelFile(QuaZip *zip, const QString &file, const QString &target) | bool extractRelFile(QuaZip* zip, const QString& file, const QString& target) | ||||||
| { | { | ||||||
|     return JlCompress::extractFile(zip, file, target); |     return JlCompress::extractFile(zip, file, target); | ||||||
| } | } | ||||||
|  |  | ||||||
| // ours | // ours | ||||||
| std::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString dir) | std::optional<QStringList> extractDir(QString fileCompressed, QString dir) | ||||||
| { | { | ||||||
|     QuaZip zip(fileCompressed); |     QuaZip zip(fileCompressed); | ||||||
|     if (!zip.open(QuaZip::mdUnzip)) |     if (!zip.open(QuaZip::mdUnzip)) { | ||||||
|     { |  | ||||||
|         // check if this is a minimum size empty zip file... |         // check if this is a minimum size empty zip file... | ||||||
|         QFileInfo fileInfo(fileCompressed); |         QFileInfo fileInfo(fileCompressed); | ||||||
|         if(fileInfo.size() == 22) { |         if (fileInfo.size() == 22) { | ||||||
|             return QStringList(); |             return QStringList(); | ||||||
|         } |         } | ||||||
|         qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();; |         qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError(); | ||||||
|  |         ; | ||||||
|         return std::nullopt; |         return std::nullopt; | ||||||
|     } |     } | ||||||
|     return MMCZip::extractSubDir(&zip, "", dir); |     return extractSubDir(&zip, "", dir); | ||||||
| } | } | ||||||
|  |  | ||||||
| // ours | // ours | ||||||
| std::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString subdir, QString dir) | std::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QString dir) | ||||||
| { | { | ||||||
|     QuaZip zip(fileCompressed); |     QuaZip zip(fileCompressed); | ||||||
|     if (!zip.open(QuaZip::mdUnzip)) |     if (!zip.open(QuaZip::mdUnzip)) { | ||||||
|     { |  | ||||||
|         // check if this is a minimum size empty zip file... |         // check if this is a minimum size empty zip file... | ||||||
|         QFileInfo fileInfo(fileCompressed); |         QFileInfo fileInfo(fileCompressed); | ||||||
|         if(fileInfo.size() == 22) { |         if (fileInfo.size() == 22) { | ||||||
|             return QStringList(); |             return QStringList(); | ||||||
|         } |         } | ||||||
|         qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();; |         qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError(); | ||||||
|  |         ; | ||||||
|         return std::nullopt; |         return std::nullopt; | ||||||
|     } |     } | ||||||
|     return MMCZip::extractSubDir(&zip, subdir, dir); |     return extractSubDir(&zip, subdir, dir); | ||||||
| } | } | ||||||
|  |  | ||||||
| // ours | // ours | ||||||
| bool MMCZip::extractFile(QString fileCompressed, QString file, QString target) | bool extractFile(QString fileCompressed, QString file, QString target) | ||||||
| { | { | ||||||
|     QuaZip zip(fileCompressed); |     QuaZip zip(fileCompressed); | ||||||
|     if (!zip.open(QuaZip::mdUnzip)) |     if (!zip.open(QuaZip::mdUnzip)) { | ||||||
|     { |  | ||||||
|         // check if this is a minimum size empty zip file... |         // check if this is a minimum size empty zip file... | ||||||
|         QFileInfo fileInfo(fileCompressed); |         QFileInfo fileInfo(fileCompressed); | ||||||
|         if(fileInfo.size() == 22) { |         if (fileInfo.size() == 22) { | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|         qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError(); |         qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError(); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|     return MMCZip::extractRelFile(&zip, file, target); |     return extractRelFile(&zip, file, target); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool MMCZip::collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList *files, | bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter) | ||||||
|                                         MMCZip::FilterFunction excludeFilter) { | { | ||||||
|     QDir rootDirectory(rootDir); |     QDir rootDirectory(rootDir); | ||||||
|     if (!rootDirectory.exists()) return false; |     if (!rootDirectory.exists()) | ||||||
|  |         return false; | ||||||
|  |  | ||||||
|     QDir directory; |     QDir directory; | ||||||
|     if (subDir == nullptr) |     if (subDir == nullptr) | ||||||
| @@ -421,25 +398,107 @@ bool MMCZip::collectFileListRecursively(const QString& rootDir, const QString& s | |||||||
|     else |     else | ||||||
|         directory = QDir(subDir); |         directory = QDir(subDir); | ||||||
|  |  | ||||||
|     if (!directory.exists()) return false;  // shouldn't ever happen |     if (!directory.exists()) | ||||||
|  |         return false;  // shouldn't ever happen | ||||||
|  |  | ||||||
|     // recurse directories |     // recurse directories | ||||||
|     QFileInfoList entries = directory.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden); |     QFileInfoList entries = directory.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden); | ||||||
|     for (const auto& e: entries) { |     for (const auto& e : entries) { | ||||||
|         if (!collectFileListRecursively(rootDir, e.filePath(), files, excludeFilter)) |         if (!collectFileListRecursively(rootDir, e.filePath(), files, excludeFilter)) | ||||||
|             return false; |             return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // collect files |     // collect files | ||||||
|     entries = directory.entryInfoList(QDir::Files); |     entries = directory.entryInfoList(QDir::Files); | ||||||
|     for (const auto& e: entries) { |     for (const auto& e : entries) { | ||||||
|         QString relativeFilePath = rootDirectory.relativeFilePath(e.absoluteFilePath()); |         QString relativeFilePath = rootDirectory.relativeFilePath(e.absoluteFilePath()); | ||||||
|         if (excludeFilter && excludeFilter(relativeFilePath)) { |         if (excludeFilter && excludeFilter(relativeFilePath)) { | ||||||
|             qDebug() << "Skipping file " << relativeFilePath; |             qDebug() << "Skipping file " << relativeFilePath; | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         files->append(e);  // we want the original paths for MMCZip::compressDirFiles |         files->append(e);  // we want the original paths for compressDirFiles | ||||||
|     } |     } | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void ExportToZipTask::executeTask() | ||||||
|  | { | ||||||
|  |     setStatus("Adding files..."); | ||||||
|  |     setProgress(0, m_files.length()); | ||||||
|  |     m_build_zip_future = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return exportZip(); }); | ||||||
|  |     connect(&m_build_zip_watcher, &QFutureWatcher<ZipResult>::finished, this, &ExportToZipTask::finish); | ||||||
|  |     m_build_zip_watcher.setFuture(m_build_zip_future); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | auto ExportToZipTask::exportZip() -> ZipResult | ||||||
|  | { | ||||||
|  |     if (!m_dir.exists()) { | ||||||
|  |         return ZipResult(tr("Folder doesn't exist")); | ||||||
|  |     } | ||||||
|  |     if (!m_output.isOpen() && !m_output.open(QuaZip::mdCreate)) { | ||||||
|  |         return ZipResult(tr("Could not create file")); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (auto fileName : m_extra_files.keys()) { | ||||||
|  |         if (m_build_zip_future.isCanceled()) | ||||||
|  |             return ZipResult(); | ||||||
|  |         QuaZipFile indexFile(&m_output); | ||||||
|  |         if (!indexFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileName))) { | ||||||
|  |             return ZipResult(tr("Could not create:") + fileName); | ||||||
|  |         } | ||||||
|  |         indexFile.write(m_extra_files[fileName]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (const QFileInfo& file : m_files) { | ||||||
|  |         if (m_build_zip_future.isCanceled()) | ||||||
|  |             return ZipResult(); | ||||||
|  |  | ||||||
|  |         auto absolute = file.absoluteFilePath(); | ||||||
|  |         auto relative = m_dir.relativeFilePath(absolute); | ||||||
|  |         setStatus("Compresing: " + relative); | ||||||
|  |         setProgress(m_progress + 1, m_progressTotal); | ||||||
|  |         if (m_follow_symlinks) { | ||||||
|  |             if (file.isSymLink()) | ||||||
|  |                 absolute = file.symLinkTarget(); | ||||||
|  |             else | ||||||
|  |                 absolute = file.canonicalFilePath(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!m_exclude_files.contains(relative) && !JlCompress::compressFile(&m_output, absolute, m_destination_prefix + relative)) { | ||||||
|  |             return ZipResult(tr("Could not read and compress %1").arg(relative)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     m_output.close(); | ||||||
|  |     if (m_output.getZipError() != 0) { | ||||||
|  |         return ZipResult(tr("A zip error occurred")); | ||||||
|  |     } | ||||||
|  |     return ZipResult(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ExportToZipTask::finish() | ||||||
|  | { | ||||||
|  |     if (m_build_zip_future.isCanceled()) { | ||||||
|  |         QFile::remove(m_output_path); | ||||||
|  |         emitAborted(); | ||||||
|  |     } else if (auto result = m_build_zip_future.result(); result.has_value()) { | ||||||
|  |         QFile::remove(m_output_path); | ||||||
|  |         emitFailed(result.value()); | ||||||
|  |     } else { | ||||||
|  |         emitSucceeded(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool ExportToZipTask::abort() | ||||||
|  | { | ||||||
|  |     if (m_build_zip_future.isRunning()) { | ||||||
|  |         m_build_zip_future.cancel(); | ||||||
|  |         // NOTE: Here we don't do `emitAborted()` because it will be done when `m_build_zip_future` actually cancels, which may not occur | ||||||
|  |         // immediately. | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace MMCZip | ||||||
| @@ -1,7 +1,8 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  |  *  Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
| @@ -35,26 +36,30 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <QString> | #include <quazip.h> | ||||||
| #include <QFileInfo> |  | ||||||
| #include <QSet> |  | ||||||
| #include "minecraft/mod/Mod.h" |  | ||||||
| #include <functional> |  | ||||||
|  |  | ||||||
| #include <quazip/JlCompress.h> | #include <quazip/JlCompress.h> | ||||||
|  | #include <QDir> | ||||||
|  | #include <QFileInfo> | ||||||
|  | #include <QFuture> | ||||||
|  | #include <QFutureWatcher> | ||||||
|  | #include <QHash> | ||||||
|  | #include <QSet> | ||||||
|  | #include <QString> | ||||||
|  | #include <functional> | ||||||
|  | #include <memory> | ||||||
| #include <optional> | #include <optional> | ||||||
|  | #include "minecraft/mod/Mod.h" | ||||||
|  | #include "tasks/Task.h" | ||||||
|  |  | ||||||
| namespace MMCZip | namespace MMCZip { | ||||||
| { | using FilterFunction = std::function<bool(const QString&)>; | ||||||
|     using FilterFunction = std::function<bool(const QString &)>; |  | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|  * Merge two zip files, using a filter function |  * Merge two zip files, using a filter function | ||||||
|  */ |  */ | ||||||
|     bool mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained, | bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet<QString>& contained, const FilterFunction filter = nullptr); | ||||||
|                                             const FilterFunction filter = nullptr); |  | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|  * Compress directory, by providing a list of files to compress |  * Compress directory, by providing a list of files to compress | ||||||
|  * \param zip target archive |  * \param zip target archive | ||||||
|  * \param dir directory that will be compressed (to compress with relative paths) |  * \param dir directory that will be compressed (to compress with relative paths) | ||||||
| @@ -62,9 +67,9 @@ namespace MMCZip | |||||||
|  * \param followSymlinks should follow symlinks when compressing file data |  * \param followSymlinks should follow symlinks when compressing file data | ||||||
|  * \return true for success or false for failure |  * \return true for success or false for failure | ||||||
|  */ |  */ | ||||||
|     bool compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, bool followSymlinks = false); | bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool followSymlinks = false); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|  * Compress directory, by providing a list of files to compress |  * Compress directory, by providing a list of files to compress | ||||||
|  * \param fileCompressed target archive file |  * \param fileCompressed target archive file | ||||||
|  * \param dir directory that will be compressed (to compress with relative paths) |  * \param dir directory that will be compressed (to compress with relative paths) | ||||||
| @@ -72,47 +77,47 @@ namespace MMCZip | |||||||
|  * \param followSymlinks should follow symlinks when compressing file data |  * \param followSymlinks should follow symlinks when compressing file data | ||||||
|  * \return true for success or false for failure |  * \return true for success or false for failure | ||||||
|  */ |  */ | ||||||
|     bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false); | bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|  * take a source jar, add mods to it, resulting in target jar |  * take a source jar, add mods to it, resulting in target jar | ||||||
|  */ |  */ | ||||||
|     bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods); | bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|  * Find a single file in archive by file name (not path) |  * Find a single file in archive by file name (not path) | ||||||
|  * |  * | ||||||
|  * \param ignore_paths paths to skip when recursing the search |  * \param ignore_paths paths to skip when recursing the search | ||||||
|  * |  * | ||||||
|  * \return the path prefix where the file is |  * \return the path prefix where the file is | ||||||
|  */ |  */ | ||||||
|     QString findFolderOfFileInZip(QuaZip * zip, const QString & what, const QStringList& ignore_paths = {}, const QString &root = QString("")); | QString findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths = {}, const QString& root = QString("")); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|  * Find a multiple files of the same name in archive by file name |  * Find a multiple files of the same name in archive by file name | ||||||
|  * If a file is found in a path, no deeper paths are searched |  * If a file is found in a path, no deeper paths are searched | ||||||
|  * |  * | ||||||
|  * \return true if anything was found |  * \return true if anything was found | ||||||
|  */ |  */ | ||||||
|     bool findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root = QString()); | bool findFilesInZip(QuaZip* zip, const QString& what, QStringList& result, const QString& root = QString()); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|  * Extract a subdirectory from an archive |  * Extract a subdirectory from an archive | ||||||
|  */ |  */ | ||||||
|     std::optional<QStringList> extractSubDir(QuaZip *zip, const QString & subdir, const QString &target); | std::optional<QStringList> extractSubDir(QuaZip* zip, const QString& subdir, const QString& target); | ||||||
|  |  | ||||||
|     bool extractRelFile(QuaZip *zip, const QString & file, const QString &target); | bool extractRelFile(QuaZip* zip, const QString& file, const QString& target); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|  * Extract a whole archive. |  * Extract a whole archive. | ||||||
|  * |  * | ||||||
|  * \param fileCompressed The name of the archive. |  * \param fileCompressed The name of the archive. | ||||||
|  * \param dir The directory to extract to, the current directory if left empty. |  * \param dir The directory to extract to, the current directory if left empty. | ||||||
|  * \return The list of the full paths of the files extracted, empty on failure. |  * \return The list of the full paths of the files extracted, empty on failure. | ||||||
|  */ |  */ | ||||||
|     std::optional<QStringList> extractDir(QString fileCompressed, QString dir); | std::optional<QStringList> extractDir(QString fileCompressed, QString dir); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|  * Extract a subdirectory from an archive |  * Extract a subdirectory from an archive | ||||||
|  * |  * | ||||||
|  * \param fileCompressed The name of the archive. |  * \param fileCompressed The name of the archive. | ||||||
| @@ -120,9 +125,9 @@ namespace MMCZip | |||||||
|  * \param dir The directory to extract to, the current directory if left empty. |  * \param dir The directory to extract to, the current directory if left empty. | ||||||
|  * \return The list of the full paths of the files extracted, empty on failure. |  * \return The list of the full paths of the files extracted, empty on failure. | ||||||
|  */ |  */ | ||||||
|     std::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QString dir); | std::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QString dir); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|  * Extract a single file from an archive into a directory |  * Extract a single file from an archive into a directory | ||||||
|  * |  * | ||||||
|  * \param fileCompressed The name of the archive. |  * \param fileCompressed The name of the archive. | ||||||
| @@ -130,9 +135,9 @@ namespace MMCZip | |||||||
|  * \param dir The directory to extract to, the current directory if left empty. |  * \param dir The directory to extract to, the current directory if left empty. | ||||||
|  * \return true for success or false for failure |  * \return true for success or false for failure | ||||||
|  */ |  */ | ||||||
|     bool extractFile(QString fileCompressed, QString file, QString dir); | bool extractFile(QString fileCompressed, QString file, QString dir); | ||||||
|  |  | ||||||
|     /** | /** | ||||||
|  * Populate a QFileInfoList with a directory tree recursively, while allowing to excludeFilter what shouldn't be included. |  * Populate a QFileInfoList with a directory tree recursively, while allowing to excludeFilter what shouldn't be included. | ||||||
|  * \param rootDir directory to start off |  * \param rootDir directory to start off | ||||||
|  * \param subDir subdirectory, should be nullptr for first invocation |  * \param subDir subdirectory, should be nullptr for first invocation | ||||||
| @@ -140,5 +145,48 @@ namespace MMCZip | |||||||
|  * \param excludeFilter function to excludeFilter which files shouldn't be included (returning true means to excude) |  * \param excludeFilter function to excludeFilter which files shouldn't be included (returning true means to excude) | ||||||
|  * \return true for success or false for failure |  * \return true for success or false for failure | ||||||
|  */ |  */ | ||||||
|     bool collectFileListRecursively(const QString &rootDir, const QString &subDir, QFileInfoList *files, FilterFunction excludeFilter); | bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter); | ||||||
| } |  | ||||||
|  | class ExportToZipTask : public Task { | ||||||
|  |    public: | ||||||
|  |     ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) | ||||||
|  |         : m_output_path(outputPath) | ||||||
|  |         , m_output(outputPath) | ||||||
|  |         , m_dir(dir) | ||||||
|  |         , m_files(files) | ||||||
|  |         , m_destination_prefix(destinationPrefix) | ||||||
|  |         , m_follow_symlinks(followSymlinks) | ||||||
|  |     { | ||||||
|  |         setAbortable(true); | ||||||
|  |     }; | ||||||
|  |     ExportToZipTask(QString outputPath, QString dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) | ||||||
|  |         : ExportToZipTask(outputPath, QDir(dir), files, destinationPrefix, followSymlinks){}; | ||||||
|  |  | ||||||
|  |     virtual ~ExportToZipTask() = default; | ||||||
|  |  | ||||||
|  |     void setExcludeFiles(QStringList excludeFiles) { m_exclude_files = excludeFiles; } | ||||||
|  |     void addExtraFile(QString fileName, QByteArray data) { m_extra_files.insert(fileName, data); } | ||||||
|  |  | ||||||
|  |     typedef std::optional<QString> ZipResult; | ||||||
|  |  | ||||||
|  |    protected: | ||||||
|  |     virtual void executeTask() override; | ||||||
|  |     bool abort() override; | ||||||
|  |  | ||||||
|  |     ZipResult exportZip(); | ||||||
|  |     void finish(); | ||||||
|  |  | ||||||
|  |    private: | ||||||
|  |     QString m_output_path; | ||||||
|  |     QuaZip m_output; | ||||||
|  |     QDir m_dir; | ||||||
|  |     QFileInfoList m_files; | ||||||
|  |     QString m_destination_prefix; | ||||||
|  |     bool m_follow_symlinks; | ||||||
|  |     QStringList m_exclude_files; | ||||||
|  |     QHash<QString, QByteArray> m_extra_files; | ||||||
|  |  | ||||||
|  |     QFuture<ZipResult> m_build_zip_future; | ||||||
|  |     QFutureWatcher<ZipResult> m_build_zip_watcher; | ||||||
|  | }; | ||||||
|  | }  // namespace MMCZip | ||||||
|   | |||||||
| @@ -54,7 +54,7 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, | |||||||
|     m_filesNetJob->addNetAction(Net::Download::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename()))); |     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::succeeded, this, &ResourceDownloadTask::downloadSucceeded); | ||||||
|     connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged); |     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); |     connect(m_filesNetJob.get(), &NetJob::failed, this, &ResourceDownloadTask::downloadFailed); | ||||||
|  |  | ||||||
|     addTask(m_filesNetJob); |     addTask(m_filesNetJob); | ||||||
|   | |||||||
| @@ -1,29 +1,64 @@ | |||||||
|  | // SPDX-License-Identifier: GPL-3.0-only | ||||||
|  | /* | ||||||
|  |  *  Prism Launcher - Minecraft Launcher | ||||||
|  |  *  Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> | ||||||
|  |  * | ||||||
|  |  *  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 <https://www.gnu.org/licenses/>. | ||||||
|  |  */ | ||||||
|  |  | ||||||
| #include "JavaInstall.h" | #include "JavaInstall.h" | ||||||
|  |  | ||||||
|  | #include "BaseVersion.h" | ||||||
| #include "StringUtils.h" | #include "StringUtils.h" | ||||||
|  |  | ||||||
| bool JavaInstall::operator<(const JavaInstall &rhs) | bool JavaInstall::operator<(const JavaInstall& rhs) | ||||||
| { | { | ||||||
|     auto archCompare = StringUtils::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive); |     auto archCompare = StringUtils::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive); | ||||||
|     if(archCompare != 0) |     if (archCompare != 0) | ||||||
|         return archCompare < 0; |         return archCompare < 0; | ||||||
|     if(id < rhs.id) |     if (id < rhs.id) { | ||||||
|     { |  | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|     if(id > rhs.id) |     if (id > rhs.id) { | ||||||
|     { |  | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|     return StringUtils::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0; |     return StringUtils::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool JavaInstall::operator==(const JavaInstall &rhs) | bool JavaInstall::operator==(const JavaInstall& rhs) | ||||||
| { | { | ||||||
|     return arch == rhs.arch && id == rhs.id && path == rhs.path; |     return arch == rhs.arch && id == rhs.id && path == rhs.path; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool JavaInstall::operator>(const JavaInstall &rhs) | bool JavaInstall::operator>(const JavaInstall& rhs) | ||||||
| { | { | ||||||
|     return (!operator<(rhs)) && (!operator==(rhs)); |     return (!operator<(rhs)) && (!operator==(rhs)); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | bool JavaInstall::operator<(BaseVersion& a) | ||||||
|  | { | ||||||
|  |     try { | ||||||
|  |         return operator<(dynamic_cast<JavaInstall&>(a)); | ||||||
|  |     } catch (const std::bad_cast& e) { | ||||||
|  |         return BaseVersion::operator<(a); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool JavaInstall::operator>(BaseVersion& a) | ||||||
|  | { | ||||||
|  |     try { | ||||||
|  |         return operator>(dynamic_cast<JavaInstall&>(a)); | ||||||
|  |     } catch (const std::bad_cast& e) { | ||||||
|  |         return BaseVersion::operator>(a); | ||||||
|  |     } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,33 +1,40 @@ | |||||||
|  | // SPDX-License-Identifier: GPL-3.0-only | ||||||
|  | /* | ||||||
|  |  *  Prism Launcher - Minecraft Launcher | ||||||
|  |  *  Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> | ||||||
|  |  * | ||||||
|  |  *  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 <https://www.gnu.org/licenses/>. | ||||||
|  |  */ | ||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "BaseVersion.h" | #include "BaseVersion.h" | ||||||
| #include "JavaVersion.h" | #include "JavaVersion.h" | ||||||
|  |  | ||||||
| struct JavaInstall : public BaseVersion | struct JavaInstall : public BaseVersion { | ||||||
| { |     JavaInstall() {} | ||||||
|     JavaInstall(){} |     JavaInstall(QString id, QString arch, QString path) : id(id), arch(arch), path(path) {} | ||||||
|     JavaInstall(QString id, QString arch, QString path) |     virtual QString descriptor() { return id.toString(); } | ||||||
|     : id(id), arch(arch), path(path) |  | ||||||
|     { |  | ||||||
|     } |  | ||||||
|     virtual QString descriptor() |  | ||||||
|     { |  | ||||||
|         return id.toString(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     virtual QString name() |     virtual QString name() { return id.toString(); } | ||||||
|     { |  | ||||||
|         return id.toString(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     virtual QString typeString() const |     virtual QString typeString() const { return arch; } | ||||||
|     { |  | ||||||
|         return arch; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bool operator<(const JavaInstall & rhs); |     virtual bool operator<(BaseVersion& a) override; | ||||||
|     bool operator==(const JavaInstall & rhs); |     virtual bool operator>(BaseVersion& a) override; | ||||||
|     bool operator>(const JavaInstall & rhs); |     bool operator<(const JavaInstall& rhs); | ||||||
|  |     bool operator==(const JavaInstall& rhs); | ||||||
|  |     bool operator>(const JavaInstall& rhs); | ||||||
|  |  | ||||||
|     JavaVersion id; |     JavaVersion id; | ||||||
|     QString arch; |     QString arch; | ||||||
|   | |||||||
| @@ -28,7 +28,7 @@ void Update::executeTask() | |||||||
|     { |     { | ||||||
|         connect(m_updateTask.get(), &Task::finished, this, &Update::updateFinished); |         connect(m_updateTask.get(), &Task::finished, this, &Update::updateFinished); | ||||||
|         connect(m_updateTask.get(), &Task::progress, this, &Update::setProgress); |         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::status, this, &Update::setStatus); | ||||||
|         connect(m_updateTask.get(), &Task::details, this, &Update::setDetails); |         connect(m_updateTask.get(), &Task::details, this, &Update::setDetails); | ||||||
|         emit progressReportingRequest(); |         emit progressReportingRequest(); | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ | |||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  *  Copyright (C) 2022 Jamie Mansfield <jmansfield@cadixdev.org> |  *  Copyright (C) 2022 Jamie Mansfield <jmansfield@cadixdev.org> | ||||||
|  *  Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me> |  *  Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me> | ||||||
|  |  *  Copyright (c) 2023 seth <getchoo at tuta dot io> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
| @@ -186,6 +187,10 @@ void MinecraftInstance::loadSpecificSettings() | |||||||
|         m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride); |         m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride); | ||||||
|         m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride); |         m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride); | ||||||
|  |  | ||||||
|  |         // Mod loader specific options | ||||||
|  |         auto modLoaderSettings = m_settings->registerSetting("OverrideModLoaderSettings", false); | ||||||
|  |         m_settings->registerOverride(global_settings->getSetting("DisableQuiltBeacon"), modLoaderSettings); | ||||||
|  |  | ||||||
|         m_settings->set("InstanceType", "OneSix"); |         m_settings->set("InstanceType", "OneSix"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -391,6 +396,12 @@ QStringList MinecraftInstance::extraArguments() | |||||||
|         agent->library()->getApplicableFiles(runtimeContext(), jar, temp1, temp2, temp3, getLocalLibraryPath()); |         agent->library()->getApplicableFiles(runtimeContext(), jar, temp1, temp2, temp3, getLocalLibraryPath()); | ||||||
|         list.append("-javaagent:"+jar[0]+(agent->argument().isEmpty() ? "" : "="+agent->argument())); |         list.append("-javaagent:"+jar[0]+(agent->argument().isEmpty() ? "" : "="+agent->argument())); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     { | ||||||
|  |         const auto loaders = version->getModLoaders(); | ||||||
|  |         if (loaders.has_value() && loaders.value() & ResourceAPI::Quilt && settings()->get("DisableQuiltBeacon").toBool()) | ||||||
|  |             list.append("-Dloader.disable_beacon=true"); | ||||||
|  |     } | ||||||
|     return list; |     return list; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -832,7 +843,7 @@ QMap<QString, QString> MinecraftInstance::createCensorFilterFromSession(AuthSess | |||||||
|     { |     { | ||||||
|         addToFilter(sessionRef.session, tr("<SESSION ID>")); |         addToFilter(sessionRef.session, tr("<SESSION ID>")); | ||||||
|     } |     } | ||||||
|     if (sessionRef.access_token != "offline") { |     if (sessionRef.access_token != "0") { | ||||||
|         addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>")); |         addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>")); | ||||||
|     } |     } | ||||||
|     if(sessionRef.client_token.size()) { |     if(sessionRef.client_token.size()) { | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ void MinecraftLoadAndCheck::executeTask() | |||||||
|     connect(m_task.get(), &Task::failed, this, &MinecraftLoadAndCheck::subtaskFailed); |     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::aborted, this, [this]{ subtaskFailed(tr("Aborted")); }); | ||||||
|     connect(m_task.get(), &Task::progress, this, &MinecraftLoadAndCheck::progress); |     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); |     connect(m_task.get(), &Task::status, this, &MinecraftLoadAndCheck::setStatus); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -100,7 +100,7 @@ void MinecraftUpdate::next() | |||||||
|         disconnect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed); |         disconnect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed); | ||||||
|         disconnect(task.get(), &Task::aborted, this, &Task::abort); |         disconnect(task.get(), &Task::aborted, this, &Task::abort); | ||||||
|         disconnect(task.get(), &Task::progress, this, &MinecraftUpdate::progress); |         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::status, this, &MinecraftUpdate::setStatus); | ||||||
|         disconnect(task.get(), &Task::details, this, &MinecraftUpdate::setDetails); |         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::failed, this, &MinecraftUpdate::subtaskFailed); | ||||||
|     connect(task.get(), &Task::aborted, this, &Task::abort); |     connect(task.get(), &Task::aborted, this, &Task::abort); | ||||||
|     connect(task.get(), &Task::progress, this, &MinecraftUpdate::progress); |     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::status, this, &MinecraftUpdate::setStatus); | ||||||
|     connect(task.get(), &Task::details, this, &MinecraftUpdate::setDetails); |     connect(task.get(), &Task::details, this, &MinecraftUpdate::setDetails); | ||||||
|     // if the task is already running, do not start it again |     // if the task is already running, do not start it again | ||||||
|   | |||||||
| @@ -374,6 +374,10 @@ bool AccountData::resumeStateFromV3(QJsonObject data) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     yggdrasilToken = tokenFromJSONV3(data, "ygg"); |     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"); |     minecraftProfile = profileFromJSONV3(data, "profile"); | ||||||
|     if(!entitlementFromJSONV3(data, minecraftEntitlement)) { |     if(!entitlementFromJSONV3(data, minecraftEntitlement)) { | ||||||
|         if(minecraftProfile.validity != Katabasis::Validity::None) { |         if(minecraftProfile.validity != Katabasis::Validity::None) { | ||||||
|   | |||||||
| @@ -26,6 +26,7 @@ bool AuthSession::MakeOffline(QString offline_playername) | |||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|     session = "-"; |     session = "-"; | ||||||
|  |     access_token = "0"; | ||||||
|     player_name = offline_playername; |     player_name = offline_playername; | ||||||
|     status = PlayableOffline; |     status = PlayableOffline; | ||||||
|     return true; |     return true; | ||||||
|   | |||||||
| @@ -37,6 +37,7 @@ | |||||||
|  |  | ||||||
| #include "MinecraftAccount.h" | #include "MinecraftAccount.h" | ||||||
|  |  | ||||||
|  | #include <QCryptographicHash> | ||||||
| #include <QUuid> | #include <QUuid> | ||||||
| #include <QJsonObject> | #include <QJsonObject> | ||||||
| #include <QJsonArray> | #include <QJsonArray> | ||||||
| @@ -93,14 +94,14 @@ MinecraftAccountPtr MinecraftAccount::createOffline(const QString &username) | |||||||
| { | { | ||||||
|     auto account = makeShared<MinecraftAccount>(); |     auto account = makeShared<MinecraftAccount>(); | ||||||
|     account->data.type = AccountType::Offline; |     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.validity = Katabasis::Validity::Certain; | ||||||
|     account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc(); |     account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc(); | ||||||
|     account->data.yggdrasilToken.extra["userName"] = username; |     account->data.yggdrasilToken.extra["userName"] = username; | ||||||
|     account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")); |     account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")); | ||||||
|     account->data.minecraftEntitlement.ownsMinecraft = true; |     account->data.minecraftEntitlement.ownsMinecraft = true; | ||||||
|     account->data.minecraftEntitlement.canPlayMinecraft = true; |     account->data.minecraftEntitlement.canPlayMinecraft = true; | ||||||
|     account->data.minecraftProfile.id = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")); |     account->data.minecraftProfile.id = uuidFromUsername(username).toString().remove(QRegularExpression("[{}-]")); | ||||||
|     account->data.minecraftProfile.name = username; |     account->data.minecraftProfile.name = username; | ||||||
|     account->data.minecraftProfile.validity = Katabasis::Validity::Certain; |     account->data.minecraftProfile.validity = Katabasis::Validity::Certain; | ||||||
|     return account; |     return account; | ||||||
| @@ -334,3 +335,32 @@ void MinecraftAccount::incrementUses() | |||||||
|         qWarning() << "Profile" << data.profileId() << "is now in use."; |         qWarning() << "Profile" << data.profileId() << "is now in use."; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | QUuid MinecraftAccount::uuidFromUsername(QString username) { | ||||||
|  |     auto input = QString("OfflinePlayer:%1").arg(username).toUtf8(); | ||||||
|  |  | ||||||
|  |     // basically a reimplementation of Java's UUID#nameUUIDFromBytes | ||||||
|  |     QByteArray digest = QCryptographicHash::hash(input, QCryptographicHash::Md5); | ||||||
|  |  | ||||||
|  | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) | ||||||
|  |     auto bOr = [](QByteArray& array, int index, char value) { | ||||||
|  |         array[index] = array.at(index) | value; | ||||||
|  |     }; | ||||||
|  |     auto bAnd = [](QByteArray& array, int index, char value) { | ||||||
|  |         array[index] = array.at(index) & value; | ||||||
|  |     }; | ||||||
|  | #else | ||||||
|  |     auto bOr = [](QByteArray& array, qsizetype index, char value) { | ||||||
|  |         array[index] |= value; | ||||||
|  |     }; | ||||||
|  |     auto bAnd = [](QByteArray& array, qsizetype index, char value) { | ||||||
|  |         array[index] &= value; | ||||||
|  |     }; | ||||||
|  | #endif | ||||||
|  |     bAnd(digest, 6, (char) 0x0f); // clear version | ||||||
|  |     bOr(digest, 6, (char) 0x30); // set to version 3 | ||||||
|  |     bAnd(digest, 8, (char) 0x3f); // clear variant | ||||||
|  |     bOr(digest, 8, (char) 0x80); // set to IETF variant | ||||||
|  |  | ||||||
|  |     return QUuid::fromRfc4122(digest); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -98,6 +98,8 @@ public: /* construction */ | |||||||
|     static MinecraftAccountPtr loadFromJsonV2(const QJsonObject &json); |     static MinecraftAccountPtr loadFromJsonV2(const QJsonObject &json); | ||||||
|     static MinecraftAccountPtr loadFromJsonV3(const QJsonObject &json); |     static MinecraftAccountPtr loadFromJsonV3(const QJsonObject &json); | ||||||
|  |  | ||||||
|  |     static QUuid uuidFromUsername(QString username); | ||||||
|  |  | ||||||
|     //! Saves a MinecraftAccount to a JSON object and returns it. |     //! Saves a MinecraftAccount to a JSON object and returns it. | ||||||
|     QJsonObject saveToJson() const; |     QJsonObject saveToJson() const; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -45,7 +45,7 @@ void AssetUpdateTask::executeTask() | |||||||
|     connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetIndexFailed); |     connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetIndexFailed); | ||||||
|     connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); |     connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); | ||||||
|     connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress); |     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"; |     qDebug() << m_inst->name() << ": Starting asset index download"; | ||||||
|     downloadJob->start(); |     downloadJob->start(); | ||||||
| @@ -84,7 +84,7 @@ void AssetUpdateTask::assetIndexFinished() | |||||||
|         connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetsFailed); |         connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetsFailed); | ||||||
|         connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); |         connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); | ||||||
|         connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress); |         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(); |         downloadJob->start(); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -75,7 +75,7 @@ void FMLLibrariesTask::executeTask() | |||||||
|     connect(dljob.get(), &NetJob::failed, this, &FMLLibrariesTask::fmllibsFailed); |     connect(dljob.get(), &NetJob::failed, this, &FMLLibrariesTask::fmllibsFailed); | ||||||
|     connect(dljob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); |     connect(dljob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); | ||||||
|     connect(dljob.get(), &NetJob::progress, this, &FMLLibrariesTask::progress); |     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.reset(dljob); | ||||||
|     downloadJob->start(); |     downloadJob->start(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -70,7 +70,7 @@ void LibrariesTask::executeTask() | |||||||
|     connect(downloadJob.get(), &NetJob::failed, this, &LibrariesTask::jarlibFailed); |     connect(downloadJob.get(), &NetJob::failed, this, &LibrariesTask::jarlibFailed); | ||||||
|     connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); |     connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); | ||||||
|     connect(downloadJob.get(), &NetJob::progress, this, &LibrariesTask::progress); |     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(); |     downloadJob->start(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -684,7 +684,7 @@ void PackInstallTask::installConfigs() | |||||||
|         abortable = true; |         abortable = true; | ||||||
|         setProgress(current, total); |         setProgress(current, total); | ||||||
|     }); |     }); | ||||||
|     connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress); |     connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress); | ||||||
|     connect(jobPtr.get(), &NetJob::aborted, [&]{ |     connect(jobPtr.get(), &NetJob::aborted, [&]{ | ||||||
|         abortable = false; |         abortable = false; | ||||||
|         jobPtr.reset(); |         jobPtr.reset(); | ||||||
| @@ -852,7 +852,7 @@ void PackInstallTask::downloadMods() | |||||||
|         abortable = true; |         abortable = true; | ||||||
|         setProgress(current, total); |         setProgress(current, total); | ||||||
|     }); |     }); | ||||||
|     connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress); |     connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress); | ||||||
|     connect(jobPtr.get(), &NetJob::aborted, [&] |     connect(jobPtr.get(), &NetJob::aborted, [&] | ||||||
|     { |     { | ||||||
|         abortable = false; |         abortable = false; | ||||||
|   | |||||||
| @@ -52,7 +52,7 @@ void Flame::FileResolvingTask::executeTask() | |||||||
|         stepProgress(*step_progress); |         stepProgress(*step_progress); | ||||||
|         emitFailed(reason); |         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) { |     connect(m_dljob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) { | ||||||
|         qDebug() << "Resolve slug progress" << current << total; |         qDebug() << "Resolve slug progress" << current << total; | ||||||
|         step_progress->update(current, total); |         step_progress->update(current, total); | ||||||
| @@ -118,7 +118,7 @@ void Flame::FileResolvingTask::netJobFinished() | |||||||
|         stepProgress(*step_progress); |         stepProgress(*step_progress); | ||||||
|         emitFailed(reason); |         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) { |     connect(m_checkJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) { | ||||||
|         qDebug() << "Resolve slug progress" << current << total; |         qDebug() << "Resolve slug progress" << current << total; | ||||||
|         step_progress->update(current, total); |         step_progress->update(current, total); | ||||||
| @@ -195,7 +195,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished() | |||||||
|             stepProgress(*step_progress); |             stepProgress(*step_progress); | ||||||
|             emitFailed(reason); |             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) { |         connect(m_slugJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) { | ||||||
|             qDebug() << "Resolve slug progress" << current << total; |             qDebug() << "Resolve slug progress" << current << total; | ||||||
|             step_progress->update(current, total); |             step_progress->update(current, total); | ||||||
|   | |||||||
| @@ -57,15 +57,11 @@ | |||||||
| #include <QDebug> | #include <QDebug> | ||||||
| #include <QFileInfo> | #include <QFileInfo> | ||||||
|  |  | ||||||
|  | #include "meta/Index.h" | ||||||
|  | #include "meta/VersionList.h" | ||||||
| #include "minecraft/World.h" | #include "minecraft/World.h" | ||||||
| #include "minecraft/mod/tasks/LocalResourceParse.h" | #include "minecraft/mod/tasks/LocalResourceParse.h" | ||||||
|  |  | ||||||
|  |  | ||||||
| const static QMap<QString, QString> 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; | static const FlameAPI api; | ||||||
|  |  | ||||||
| bool FlameCreationTask::abort() | bool FlameCreationTask::abort() | ||||||
| @@ -259,6 +255,56 @@ bool FlameCreationTask::updateInstance() | |||||||
|     return false; |     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() | bool FlameCreationTask::createInstance() | ||||||
| { | { | ||||||
|     QEventLoop loop; |     QEventLoop loop; | ||||||
| @@ -297,22 +343,29 @@ bool FlameCreationTask::createInstance() | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     QString forgeVersion; |     QString loaderType; | ||||||
|     QString fabricVersion; |     QString loaderUid; | ||||||
|     // TODO: is Quilt relevant here? |     QString loaderVersion; | ||||||
|  |  | ||||||
|     for (auto& loader : m_pack.minecraft.modLoaders) { |     for (auto& loader : m_pack.minecraft.modLoaders) { | ||||||
|         auto id = loader.id; |         auto id = loader.id; | ||||||
|         if (id.startsWith("forge-")) { |         if (id.startsWith("forge-")) { | ||||||
|             id.remove("forge-"); |             id.remove("forge-"); | ||||||
|             forgeVersion = id; |             loaderType = "forge"; | ||||||
|             continue; |             loaderUid = "net.minecraftforge"; | ||||||
|         } |         } else if (loaderType == "fabric") { | ||||||
|         if (id.startsWith("fabric-")) { |  | ||||||
|             id.remove("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; |             continue; | ||||||
|         } |         } | ||||||
|         logWarning(tr("Unknown mod loader in manifest: %1").arg(id)); |         loaderVersion = id; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); |     QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); | ||||||
| @@ -329,19 +382,12 @@ bool FlameCreationTask::createInstance() | |||||||
|     auto components = instance.getPackProfile(); |     auto components = instance.getPackProfile(); | ||||||
|     components->buildingFromScratch(); |     components->buildingFromScratch(); | ||||||
|     components->setComponentVersion("net.minecraft", mcVersion, true); |     components->setComponentVersion("net.minecraft", mcVersion, true); | ||||||
|     if (!forgeVersion.isEmpty()) { |     if (!loaderType.isEmpty()) { | ||||||
|         // FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata. |         auto version = getVersionForLoader(loaderUid, loaderType, loaderVersion, mcVersion); | ||||||
|         if (forgeVersion == "recommended") { |         if (version.isEmpty()) | ||||||
|             if (forgemap.contains(mcVersion)) { |             return false; | ||||||
|                 forgeVersion = forgemap[mcVersion]; |         components->setComponentVersion(loaderUid, version); | ||||||
|             } else { |  | ||||||
|                 logWarning(tr("Could not map recommended Forge version for Minecraft %1").arg(mcVersion)); |  | ||||||
|     } |     } | ||||||
|         } |  | ||||||
|         components->setComponentVersion("net.minecraftforge", forgeVersion); |  | ||||||
|     } |  | ||||||
|     if (!fabricVersion.isEmpty()) |  | ||||||
|         components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion); |  | ||||||
|  |  | ||||||
|     if (m_instIcon != "default") { |     if (m_instIcon != "default") { | ||||||
|         instance.setIconKey(m_instIcon); |         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::progress, this, &FlameCreationTask::setProgress); | ||||||
|     connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::status, this, &FlameCreationTask::setStatus); |     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); |     connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::details, this, &FlameCreationTask::setDetails); | ||||||
|     m_mod_id_resolver->start(); |     m_mod_id_resolver->start(); | ||||||
|  |  | ||||||
| @@ -502,11 +548,11 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) | |||||||
|         m_files_job.reset(); |         m_files_job.reset(); | ||||||
|         setError(reason); |         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)); |         setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); | ||||||
|         setProgress(current, 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); |     connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit); | ||||||
|  |  | ||||||
|     setStatus(tr("Downloading mods...")); |     setStatus(tr("Downloading mods...")); | ||||||
| @@ -545,7 +591,6 @@ void FlameCreationTask::copyBlockedMods(QList<BlockedMod> const& blocked_mods) | |||||||
|     setAbortable(true); |     setAbortable(true); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| void FlameCreationTask::validateZIPResouces() | void FlameCreationTask::validateZIPResouces() | ||||||
| { | { | ||||||
|     qDebug() << "Validating whether resources stored as .zip are in the right place"; |     qDebug() << "Validating whether resources stored as .zip are in the right place"; | ||||||
| @@ -569,7 +614,7 @@ void FlameCreationTask::validateZIPResouces() | |||||||
|             return localPath; |             return localPath; | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         auto installWorld = [this](QString worldPath){ |         auto installWorld = [this](QString worldPath) { | ||||||
|             qDebug() << "Installing World from" << worldPath; |             qDebug() << "Installing World from" << worldPath; | ||||||
|             QFileInfo worldFileInfo(worldPath); |             QFileInfo worldFileInfo(worldPath); | ||||||
|             World w(worldFileInfo); |             World w(worldFileInfo); | ||||||
| @@ -586,29 +631,29 @@ void FlameCreationTask::validateZIPResouces() | |||||||
|         QString worldPath; |         QString worldPath; | ||||||
|  |  | ||||||
|         switch (type) { |         switch (type) { | ||||||
|             case PackedResourceType::Mod : |             case PackedResourceType::Mod: | ||||||
|                 validatePath(fileName, targetFolder, "mods"); |                 validatePath(fileName, targetFolder, "mods"); | ||||||
|                 break; |                 break; | ||||||
|             case PackedResourceType::ResourcePack : |             case PackedResourceType::ResourcePack: | ||||||
|                 validatePath(fileName, targetFolder, "resourcepacks"); |                 validatePath(fileName, targetFolder, "resourcepacks"); | ||||||
|                 break; |                 break; | ||||||
|             case PackedResourceType::TexturePack : |             case PackedResourceType::TexturePack: | ||||||
|                 validatePath(fileName, targetFolder, "texturepacks"); |                 validatePath(fileName, targetFolder, "texturepacks"); | ||||||
|                 break; |                 break; | ||||||
|             case PackedResourceType::DataPack : |             case PackedResourceType::DataPack: | ||||||
|                 validatePath(fileName, targetFolder, "datapacks"); |                 validatePath(fileName, targetFolder, "datapacks"); | ||||||
|                 break; |                 break; | ||||||
|             case PackedResourceType::ShaderPack : |             case PackedResourceType::ShaderPack: | ||||||
|                 // in theroy flame API can't do this but who knows, that *may* change ? |                 // 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 |                 // better to handle it if it *does* occure in the future | ||||||
|                 validatePath(fileName, targetFolder, "shaderpacks"); |                 validatePath(fileName, targetFolder, "shaderpacks"); | ||||||
|                 break; |                 break; | ||||||
|             case PackedResourceType::WorldSave : |             case PackedResourceType::WorldSave: | ||||||
|                 worldPath = validatePath(fileName, targetFolder, "saves"); |                 worldPath = validatePath(fileName, targetFolder, "saves"); | ||||||
|                 installWorld(worldPath); |                 installWorld(worldPath); | ||||||
|                 break; |                 break; | ||||||
|             case PackedResourceType::UNKNOWN : |             case PackedResourceType::UNKNOWN: | ||||||
|             default : |             default: | ||||||
|                 qDebug() << "Can't Identify" << fileName << "at" << localPath << ", leaving it where it is."; |                 qDebug() << "Can't Identify" << fileName << "at" << localPath << ", leaving it where it is."; | ||||||
|                 break; |                 break; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -57,10 +57,7 @@ class FlameCreationTask final : public InstanceCreationTask { | |||||||
|                       QString id, |                       QString id, | ||||||
|                       QString version_id, |                       QString version_id, | ||||||
|                       QString original_instance_id = {}) |                       QString original_instance_id = {}) | ||||||
|         : InstanceCreationTask() |         : InstanceCreationTask(), m_parent(parent), m_managed_id(std::move(id)), m_managed_version_id(std::move(version_id)) | ||||||
|         , m_parent(parent) |  | ||||||
|         , m_managed_id(std::move(id)) |  | ||||||
|         , m_managed_version_id(std::move(version_id)) |  | ||||||
|     { |     { | ||||||
|         setStagingPath(staging_path); |         setStagingPath(staging_path); | ||||||
|         setParentSettings(global_settings); |         setParentSettings(global_settings); | ||||||
| @@ -78,6 +75,7 @@ class FlameCreationTask final : public InstanceCreationTask { | |||||||
|     void setupDownloadJob(QEventLoop&); |     void setupDownloadJob(QEventLoop&); | ||||||
|     void copyBlockedMods(QList<BlockedMod> const& blocked_mods); |     void copyBlockedMods(QList<BlockedMod> const& blocked_mods); | ||||||
|     void validateZIPResouces(); |     void validateZIPResouces(); | ||||||
|  |     QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion); | ||||||
|  |  | ||||||
|    private: |    private: | ||||||
|     QWidget* m_parent = nullptr; |     QWidget* m_parent = nullptr; | ||||||
|   | |||||||
| @@ -166,7 +166,7 @@ void FlamePackExportTask::collectHashes() | |||||||
|         stepProgress(*progressStep); |         stepProgress(*progressStep); | ||||||
|         emitFailed(reason); |         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) { |     connect(hashingTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { | ||||||
|         progressStep->update(current, total); |         progressStep->update(current, total); | ||||||
|   | |||||||
| @@ -81,7 +81,7 @@ void PackInstallTask::downloadPack() | |||||||
|  |  | ||||||
|     connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip); |     connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip); | ||||||
|     connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed); |     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); |     connect(netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::emitAborted); | ||||||
|  |  | ||||||
|     netJobContainer->start(); |     netJobContainer->start(); | ||||||
|   | |||||||
| @@ -267,7 +267,7 @@ bool ModrinthCreationTask::createInstance() | |||||||
|         setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); |         setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); | ||||||
|         setProgress(current, 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...")); |     setStatus(tr("Downloading mods...")); | ||||||
|     m_files_job->start(); |     m_files_job->start(); | ||||||
|   | |||||||
| @@ -50,7 +50,7 @@ void Technic::SingleZipPackInstallTask::executeTask() | |||||||
|     auto job = m_filesNetJob.get(); |     auto job = m_filesNetJob.get(); | ||||||
|     connect(job, &NetJob::succeeded, this, &Technic::SingleZipPackInstallTask::downloadSucceeded); |     connect(job, &NetJob::succeeded, this, &Technic::SingleZipPackInstallTask::downloadSucceeded); | ||||||
|     connect(job, &NetJob::progress, this, &Technic::SingleZipPackInstallTask::downloadProgressChanged); |     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); |     connect(job, &NetJob::failed, this, &Technic::SingleZipPackInstallTask::downloadFailed); | ||||||
|     m_filesNetJob->start(); |     m_filesNetJob->start(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -126,7 +126,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded() | |||||||
|  |  | ||||||
|     connect(m_filesNetJob.get(), &NetJob::succeeded, this, &Technic::SolderPackInstallTask::downloadSucceeded); |     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::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::failed, this, &Technic::SolderPackInstallTask::downloadFailed); | ||||||
|     connect(m_filesNetJob.get(), &NetJob::aborted, this, &Technic::SolderPackInstallTask::downloadAborted); |     connect(m_filesNetJob.get(), &NetJob::aborted, this, &Technic::SolderPackInstallTask::downloadAborted); | ||||||
|     m_filesNetJob->start(); |     m_filesNetJob->start(); | ||||||
|   | |||||||
| @@ -161,7 +161,7 @@ void Task::emitSucceeded() | |||||||
|     emit finished(); |     emit finished(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void Task::propogateStepProgress(TaskStepProgress const& task_progress) | void Task::propagateStepProgress(TaskStepProgress const& task_progress) | ||||||
| { | { | ||||||
|     emit stepProgress(task_progress); |     emit stepProgress(task_progress); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -167,7 +167,7 @@ class Task : public QObject, public QRunnable { | |||||||
|     virtual void emitAborted(); |     virtual void emitAborted(); | ||||||
|     virtual void emitFailed(QString reason = ""); |     virtual void emitFailed(QString reason = ""); | ||||||
|  |  | ||||||
|     virtual void propogateStepProgress(TaskStepProgress const& task_progress); |     virtual void propagateStepProgress(TaskStepProgress const& task_progress); | ||||||
|  |  | ||||||
|    public slots: |    public slots: | ||||||
|     void setStatus(const QString& status); |     void setStatus(const QString& status); | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ | |||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  *  Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> |  *  Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> | ||||||
|  |  *  Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
| @@ -41,6 +42,9 @@ | |||||||
| #include <QFileSystemModel> | #include <QFileSystemModel> | ||||||
| #include <QMessageBox> | #include <QMessageBox> | ||||||
| #include "FileIgnoreProxy.h" | #include "FileIgnoreProxy.h" | ||||||
|  | #include "QObjectPtr.h" | ||||||
|  | #include "ui/dialogs/CustomMessageBox.h" | ||||||
|  | #include "ui/dialogs/ProgressDialog.h" | ||||||
| #include "ui_ExportInstanceDialog.h" | #include "ui_ExportInstanceDialog.h" | ||||||
|  |  | ||||||
| #include <FileSystem.h> | #include <FileSystem.h> | ||||||
| @@ -66,13 +70,15 @@ ExportInstanceDialog::ExportInstanceDialog(InstancePtr instance, QWidget* parent | |||||||
|     auto prefix = QDir(instance->instanceRoot()).relativeFilePath(instance->gameRoot()); |     auto prefix = QDir(instance->instanceRoot()).relativeFilePath(instance->gameRoot()); | ||||||
|     proxyModel->ignoreFilesWithPath().insert({ FS::PathCombine(prefix, "logs"), FS::PathCombine(prefix, "crash-reports") }); |     proxyModel->ignoreFilesWithPath().insert({ FS::PathCombine(prefix, "logs"), FS::PathCombine(prefix, "crash-reports") }); | ||||||
|     proxyModel->ignoreFilesWithName().append({ ".DS_Store", "thumbs.db", "Thumbs.db" }); |     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(); |     loadPackIgnore(); | ||||||
|  |  | ||||||
|     ui->treeView->setModel(proxyModel); |     ui->treeView->setModel(proxyModel); | ||||||
|     ui->treeView->setRootIndex(proxyModel->mapFromSource(model->index(root))); |     ui->treeView->setRootIndex(proxyModel->mapFromSource(model->index(root))); | ||||||
|     ui->treeView->sortByColumn(0, Qt::AscendingOrder); |     ui->treeView->sortByColumn(0, Qt::AscendingOrder); | ||||||
|  |  | ||||||
|     connect(proxyModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(rowsInserted(QModelIndex,int,int))); |     connect(proxyModel, SIGNAL(rowsInserted(QModelIndex, int, int)), SLOT(rowsInserted(QModelIndex, int, int))); | ||||||
|  |  | ||||||
|     model->setFilter(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Hidden); |     model->setFilter(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Hidden); | ||||||
|     model->setRootPath(root); |     model->setRootPath(root); | ||||||
| @@ -92,32 +98,26 @@ void SaveIcon(InstancePtr m_instance) | |||||||
|     auto iconKey = m_instance->iconKey(); |     auto iconKey = m_instance->iconKey(); | ||||||
|     auto iconList = APPLICATION->icons(); |     auto iconList = APPLICATION->icons(); | ||||||
|     auto mmcIcon = iconList->icon(iconKey); |     auto mmcIcon = iconList->icon(iconKey); | ||||||
|     if(!mmcIcon || mmcIcon->isBuiltIn()) { |     if (!mmcIcon || mmcIcon->isBuiltIn()) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     auto path = mmcIcon->getFilePath(); |     auto path = mmcIcon->getFilePath(); | ||||||
|     if(!path.isNull()) { |     if (!path.isNull()) { | ||||||
|         QFileInfo inInfo (path); |         QFileInfo inInfo(path); | ||||||
|         FS::copy(path, FS::PathCombine(m_instance->instanceRoot(), inInfo.fileName())) (); |         FS::copy(path, FS::PathCombine(m_instance->instanceRoot(), inInfo.fileName()))(); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     auto & image = mmcIcon->m_images[mmcIcon->type()]; |     auto& image = mmcIcon->m_images[mmcIcon->type()]; | ||||||
|     auto & icon = image.icon; |     auto& icon = image.icon; | ||||||
|     auto sizes = icon.availableSizes(); |     auto sizes = icon.availableSizes(); | ||||||
|     if(sizes.size() == 0) |     if (sizes.size() == 0) { | ||||||
|     { |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     auto areaOf = [](QSize size) |     auto areaOf = [](QSize size) { return size.width() * size.height(); }; | ||||||
|     { |  | ||||||
|         return size.width() * size.height(); |  | ||||||
|     }; |  | ||||||
|     QSize largest = sizes[0]; |     QSize largest = sizes[0]; | ||||||
|     // find variant with largest area |     // find variant with largest area | ||||||
|     for(auto size: sizes) |     for (auto size : sizes) { | ||||||
|     { |         if (areaOf(largest) < areaOf(size)) { | ||||||
|         if(areaOf(largest) < areaOf(size)) |  | ||||||
|         { |  | ||||||
|             largest = size; |             largest = size; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -125,16 +125,15 @@ void SaveIcon(InstancePtr m_instance) | |||||||
|     pixmap.save(FS::PathCombine(m_instance->instanceRoot(), iconKey + ".png")); |     pixmap.save(FS::PathCombine(m_instance->instanceRoot(), iconKey + ".png")); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool ExportInstanceDialog::doExport() | void ExportInstanceDialog::doExport() | ||||||
| { | { | ||||||
|     auto name = FS::RemoveInvalidFilenameChars(m_instance->name()); |     auto name = FS::RemoveInvalidFilenameChars(m_instance->name()); | ||||||
|  |  | ||||||
|     const QString output = QFileDialog::getSaveFileName( |     const QString output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(m_instance->name()), | ||||||
|         this, tr("Export %1").arg(m_instance->name()), |  | ||||||
|                                                         FS::PathCombine(QDir::homePath(), name + ".zip"), "Zip (*.zip)", nullptr); |                                                         FS::PathCombine(QDir::homePath(), name + ".zip"), "Zip (*.zip)", nullptr); | ||||||
|     if (output.isEmpty()) |     if (output.isEmpty()) { | ||||||
|     { |         QDialog::done(QDialog::Rejected); | ||||||
|         return false; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     SaveIcon(m_instance); |     SaveIcon(m_instance); | ||||||
| @@ -143,46 +142,40 @@ bool ExportInstanceDialog::doExport() | |||||||
|     if (!MMCZip::collectFileListRecursively(m_instance->instanceRoot(), nullptr, &files, |     if (!MMCZip::collectFileListRecursively(m_instance->instanceRoot(), nullptr, &files, | ||||||
|                                             std::bind(&FileIgnoreProxy::filterFile, proxyModel, std::placeholders::_1))) { |                                             std::bind(&FileIgnoreProxy::filterFile, proxyModel, std::placeholders::_1))) { | ||||||
|         QMessageBox::warning(this, tr("Error"), tr("Unable to export instance")); |         QMessageBox::warning(this, tr("Error"), tr("Unable to export instance")); | ||||||
|         return false; |         QDialog::done(QDialog::Rejected); | ||||||
|  |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!MMCZip::compressDirFiles(output, m_instance->instanceRoot(), files, true)) |     auto task = makeShared<MMCZip::ExportToZipTask>(output, m_instance->instanceRoot(), files, "", true); | ||||||
|     { |  | ||||||
|         QMessageBox::warning(this, tr("Error"), tr("Unable to export instance")); |     connect(task.get(), &Task::failed, this, | ||||||
|         return false; |             [this, output](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); | ||||||
|     } |     connect(task.get(), &Task::finished, this, [task] { task->deleteLater(); }); | ||||||
|     return true; |  | ||||||
|  |     ProgressDialog progress(this); | ||||||
|  |     progress.setSkipButton(true, tr("Abort")); | ||||||
|  |     auto result = progress.execWithTask(task.get()); | ||||||
|  |     QDialog::done(result); | ||||||
| } | } | ||||||
|  |  | ||||||
| void ExportInstanceDialog::done(int result) | void ExportInstanceDialog::done(int result) | ||||||
| { | { | ||||||
|     savePackIgnore(); |     savePackIgnore(); | ||||||
|     if (result == QDialog::Accepted) |     if (result == QDialog::Accepted) { | ||||||
|     { |         doExport(); | ||||||
|         if (doExport()) |  | ||||||
|         { |  | ||||||
|             QDialog::done(QDialog::Accepted); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     QDialog::done(result); |     QDialog::done(result); | ||||||
| } | } | ||||||
|  |  | ||||||
| void ExportInstanceDialog::rowsInserted(QModelIndex parent, int top, int bottom) | void ExportInstanceDialog::rowsInserted(QModelIndex parent, int top, int bottom) | ||||||
| { | { | ||||||
|     //WARNING: possible off-by-one? |     // WARNING: possible off-by-one? | ||||||
|     for(int i = top; i < bottom; i++) |     for (int i = top; i < bottom; i++) { | ||||||
|     { |  | ||||||
|         auto node = proxyModel->index(i, 0, parent); |         auto node = proxyModel->index(i, 0, parent); | ||||||
|         if(proxyModel->shouldExpand(node)) |         if (proxyModel->shouldExpand(node)) { | ||||||
|         { |  | ||||||
|             auto expNode = node.parent(); |             auto expNode = node.parent(); | ||||||
|             if(!expNode.isValid()) |             if (!expNode.isValid()) { | ||||||
|             { |  | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|             ui->treeView->expand(node); |             ui->treeView->expand(node); | ||||||
| @@ -199,8 +192,7 @@ void ExportInstanceDialog::loadPackIgnore() | |||||||
| { | { | ||||||
|     auto filename = ignoreFileName(); |     auto filename = ignoreFileName(); | ||||||
|     QFile ignoreFile(filename); |     QFile ignoreFile(filename); | ||||||
|     if(!ignoreFile.open(QIODevice::ReadOnly)) |     if (!ignoreFile.open(QIODevice::ReadOnly)) { | ||||||
|     { |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     auto data = ignoreFile.readAll(); |     auto data = ignoreFile.readAll(); | ||||||
| @@ -216,12 +208,9 @@ void ExportInstanceDialog::savePackIgnore() | |||||||
| { | { | ||||||
|     auto data = proxyModel->blockedPaths().toStringList().join('\n').toUtf8(); |     auto data = proxyModel->blockedPaths().toStringList().join('\n').toUtf8(); | ||||||
|     auto filename = ignoreFileName(); |     auto filename = ignoreFileName(); | ||||||
|     try |     try { | ||||||
|     { |  | ||||||
|         FS::write(filename, data); |         FS::write(filename, data); | ||||||
|     } |     } catch (const Exception& e) { | ||||||
|     catch (const Exception &e) |  | ||||||
|     { |  | ||||||
|         qWarning() << e.cause(); |         qWarning() << e.cause(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
| /* | /* | ||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> |  *  Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> | ||||||
|  |  *  Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
| @@ -38,39 +39,37 @@ | |||||||
| #include <QDialog> | #include <QDialog> | ||||||
| #include <QModelIndex> | #include <QModelIndex> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include "FileIgnoreProxy.h" |  | ||||||
| #include "FastFileIconProvider.h" | #include "FastFileIconProvider.h" | ||||||
|  | #include "FileIgnoreProxy.h" | ||||||
|  |  | ||||||
| class BaseInstance; | class BaseInstance; | ||||||
| typedef std::shared_ptr<BaseInstance> InstancePtr; | typedef std::shared_ptr<BaseInstance> InstancePtr; | ||||||
|  |  | ||||||
| namespace Ui | namespace Ui { | ||||||
| { |  | ||||||
| class ExportInstanceDialog; | class ExportInstanceDialog; | ||||||
| } | } | ||||||
|  |  | ||||||
| class ExportInstanceDialog : public QDialog | class ExportInstanceDialog : public QDialog { | ||||||
| { |  | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
|  |  | ||||||
| public: |    public: | ||||||
|     explicit ExportInstanceDialog(InstancePtr instance, QWidget *parent = 0); |     explicit ExportInstanceDialog(InstancePtr instance, QWidget* parent = 0); | ||||||
|     ~ExportInstanceDialog(); |     ~ExportInstanceDialog(); | ||||||
|  |  | ||||||
|     virtual void done(int result); |     virtual void done(int result); | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     bool doExport(); |     void doExport(); | ||||||
|     void loadPackIgnore(); |     void loadPackIgnore(); | ||||||
|     void savePackIgnore(); |     void savePackIgnore(); | ||||||
|     QString ignoreFileName(); |     QString ignoreFileName(); | ||||||
|  |  | ||||||
| private: |    private: | ||||||
|     Ui::ExportInstanceDialog *ui; |     Ui::ExportInstanceDialog* ui; | ||||||
|     InstancePtr m_instance; |     InstancePtr m_instance; | ||||||
|     FileIgnoreProxy * proxyModel; |     FileIgnoreProxy* proxyModel; | ||||||
|     FastFileIconProvider icons; |     FastFileIconProvider icons; | ||||||
|  |  | ||||||
| private slots: |    private slots: | ||||||
|     void rowsInserted(QModelIndex parent, int top, int bottom); |     void rowsInserted(QModelIndex parent, int top, int bottom); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -61,7 +61,7 @@ ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPla | |||||||
|     // use the game root - everything outside cannot be exported |     // use the game root - everything outside cannot be exported | ||||||
|     const QDir root(instance->gameRoot()); |     const QDir root(instance->gameRoot()); | ||||||
|     proxy = new FileIgnoreProxy(instance->gameRoot(), this); |     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->ignoreFilesWithName().append({ ".DS_Store", "thumbs.db", "Thumbs.db" }); | ||||||
|     proxy->setSourceModel(model); |     proxy->setSourceModel(model); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -48,7 +48,6 @@ | |||||||
| #include <QAccessible> | #include <QAccessible> | ||||||
|  |  | ||||||
| #include "VisualGroup.h" | #include "VisualGroup.h" | ||||||
| #include "ui/themes/ThemeManager.h" |  | ||||||
| #include <QDebug> | #include <QDebug> | ||||||
|  |  | ||||||
| #include <Application.h> | #include <Application.h> | ||||||
| @@ -504,7 +503,7 @@ void InstanceView::setPaintCat(bool visible) | |||||||
| { | { | ||||||
|     m_catVisible = visible; |     m_catVisible = visible; | ||||||
|     if (visible) |     if (visible) | ||||||
|         m_catPixmap.load(QString(":/backgrounds/%1").arg(ThemeManager::getCatImage())); |         m_catPixmap.load(APPLICATION->getCatPack()); | ||||||
|     else |     else | ||||||
|         m_catPixmap = QPixmap(); |         m_catPixmap = QPixmap(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,7 +1,8 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  |  *  Copyright (C) 2023 Tayou <git@tayou.org> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
| @@ -35,22 +36,18 @@ | |||||||
|  |  | ||||||
| #include "VisualGroup.h" | #include "VisualGroup.h" | ||||||
|  |  | ||||||
|  | #include <QApplication> | ||||||
|  | #include <QDebug> | ||||||
| #include <QModelIndex> | #include <QModelIndex> | ||||||
| #include <QPainter> | #include <QPainter> | ||||||
| #include <QtMath> | #include <QtMath> | ||||||
| #include <QApplication> | #include <utility> | ||||||
| #include <QDebug> |  | ||||||
|  |  | ||||||
| #include "InstanceView.h" | #include "InstanceView.h" | ||||||
|  |  | ||||||
| VisualGroup::VisualGroup(const QString &text, InstanceView *view) : view(view), text(text), collapsed(false) | VisualGroup::VisualGroup(QString text, InstanceView* view) : view(view), text(std::move(text)), collapsed(false) {} | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| VisualGroup::VisualGroup(const VisualGroup *other) | VisualGroup::VisualGroup(const VisualGroup* other) : view(other->view), text(other->text), collapsed(other->collapsed) {} | ||||||
|     : view(other->view), text(other->text), collapsed(other->collapsed) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void VisualGroup::update() | void VisualGroup::update() | ||||||
| { | { | ||||||
| @@ -64,13 +61,11 @@ void VisualGroup::update() | |||||||
|     int positionInRow = 0; |     int positionInRow = 0; | ||||||
|     int currentRow = 0; |     int currentRow = 0; | ||||||
|     int offsetFromTop = 0; |     int offsetFromTop = 0; | ||||||
|     for (auto item: temp_items) |     for (auto item : temp_items) { | ||||||
|     { |         if (positionInRow == itemsPerRow) { | ||||||
|         if(positionInRow == itemsPerRow) |  | ||||||
|         { |  | ||||||
|             rows[currentRow].height = maxRowHeight; |             rows[currentRow].height = maxRowHeight; | ||||||
|             rows[currentRow].top = offsetFromTop; |             rows[currentRow].top = offsetFromTop; | ||||||
|             currentRow ++; |             currentRow++; | ||||||
|             offsetFromTop += maxRowHeight + 5; |             offsetFromTop += maxRowHeight + 5; | ||||||
|             positionInRow = 0; |             positionInRow = 0; | ||||||
|             maxRowHeight = 0; |             maxRowHeight = 0; | ||||||
| @@ -83,8 +78,7 @@ void VisualGroup::update() | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
|         auto itemHeight = view->itemDelegate()->sizeHint(viewItemOption, item).height(); |         auto itemHeight = view->itemDelegate()->sizeHint(viewItemOption, item).height(); | ||||||
|         if(itemHeight > maxRowHeight) |         if (itemHeight > maxRowHeight) { | ||||||
|         { |  | ||||||
|             maxRowHeight = itemHeight; |             maxRowHeight = itemHeight; | ||||||
|         } |         } | ||||||
|         rows[currentRow].items.append(item); |         rows[currentRow].items.append(item); | ||||||
| @@ -94,16 +88,13 @@ void VisualGroup::update() | |||||||
|     rows[currentRow].top = offsetFromTop; |     rows[currentRow].top = offsetFromTop; | ||||||
| } | } | ||||||
|  |  | ||||||
| QPair<int, int> VisualGroup::positionOf(const QModelIndex &index) const | QPair<int, int> VisualGroup::positionOf(const QModelIndex& index) const | ||||||
| { | { | ||||||
|     int y = 0; |     int y = 0; | ||||||
|     for (auto & row: rows) |     for (auto& row : rows) { | ||||||
|     { |         for (auto x = 0; x < row.items.size(); x++) { | ||||||
|         for(auto x = 0; x < row.items.size(); x++) |             if (row.items[x] == index) { | ||||||
|         { |                 return qMakePair(x, y); | ||||||
|             if(row.items[x] == index) |  | ||||||
|             { |  | ||||||
|                 return qMakePair(x,y); |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         y++; |         y++; | ||||||
| @@ -112,186 +103,102 @@ QPair<int, int> VisualGroup::positionOf(const QModelIndex &index) const | |||||||
|     return qMakePair(0, 0); |     return qMakePair(0, 0); | ||||||
| } | } | ||||||
|  |  | ||||||
| int VisualGroup::rowTopOf(const QModelIndex &index) const | int VisualGroup::rowTopOf(const QModelIndex& index) const | ||||||
| { | { | ||||||
|     auto position = positionOf(index); |     auto position = positionOf(index); | ||||||
|     return rows[position.second].top; |     return rows[position.second].top; | ||||||
| } | } | ||||||
|  |  | ||||||
| int VisualGroup::rowHeightOf(const QModelIndex &index) const | int VisualGroup::rowHeightOf(const QModelIndex& index) const | ||||||
| { | { | ||||||
|     auto position = positionOf(index); |     auto position = positionOf(index); | ||||||
|     return rows[position.second].height; |     return rows[position.second].height; | ||||||
| } | } | ||||||
|  |  | ||||||
| VisualGroup::HitResults VisualGroup::hitScan(const QPoint &pos) const | VisualGroup::HitResults VisualGroup::hitScan(const QPoint& pos) const | ||||||
| { | { | ||||||
|     VisualGroup::HitResults results = VisualGroup::NoHit; |     VisualGroup::HitResults results = VisualGroup::NoHit; | ||||||
|     int y_start = verticalPosition(); |     int y_start = verticalPosition(); | ||||||
|     int body_start = y_start + headerHeight(); |     int body_start = y_start + headerHeight(); | ||||||
|     int body_end = body_start + contentHeight() + 5; // FIXME: wtf is this 5? |     int body_end = body_start + contentHeight(); | ||||||
|     int y = pos.y(); |     int y = pos.y(); | ||||||
|     // int x = pos.x(); |     // int x = pos.x(); | ||||||
|     if (y < y_start) |     if (y < y_start) { | ||||||
|     { |  | ||||||
|         results = VisualGroup::NoHit; |         results = VisualGroup::NoHit; | ||||||
|     } |     } else if (y < body_start) { | ||||||
|     else if (y < body_start) |  | ||||||
|     { |  | ||||||
|         results = VisualGroup::HeaderHit; |         results = VisualGroup::HeaderHit; | ||||||
|         int collapseSize = headerHeight() - 4; |         int collapseSize = headerHeight() - 4; | ||||||
|  |  | ||||||
|         // the icon |         // the icon | ||||||
|         QRect iconRect = QRect(view->m_leftMargin + 2, 2 + y_start, collapseSize, collapseSize); |         QRect iconRect = QRect(view->m_leftMargin + 2, 2 + y_start, view->width() - 4, collapseSize); | ||||||
|         if (iconRect.contains(pos)) |         if (iconRect.contains(pos)) { | ||||||
|         { |  | ||||||
|             results |= VisualGroup::CheckboxHit; |             results |= VisualGroup::CheckboxHit; | ||||||
|         } |         } | ||||||
|     } |     } else if (y < body_end) { | ||||||
|     else if (y < body_end) |  | ||||||
|     { |  | ||||||
|         results |= VisualGroup::BodyHit; |         results |= VisualGroup::BodyHit; | ||||||
|     } |     } | ||||||
|     return results; |     return results; | ||||||
| } | } | ||||||
|  |  | ||||||
| void VisualGroup::drawHeader(QPainter *painter, const QStyleOptionViewItem &option) | void VisualGroup::drawHeader(QPainter* painter, const QStyleOptionViewItem& option) const | ||||||
| { | { | ||||||
|     painter->setRenderHint(QPainter::Antialiasing); |     QRect optRect = option.rect; | ||||||
|  |     optRect.setTop(optRect.top() + 7); | ||||||
|     const QRect optRect = option.rect; |  | ||||||
|     QFont font(QApplication::font()); |     QFont font(QApplication::font()); | ||||||
|     font.setBold(true); |     font.setBold(true); | ||||||
|     const QFontMetrics fontMetrics = QFontMetrics(font); |     const QFontMetrics fontMetrics = QFontMetrics(font); | ||||||
|  |  | ||||||
|     QColor outlineColor = option.palette.text().color(); |  | ||||||
|     outlineColor.setAlphaF(0.35); |  | ||||||
|  |  | ||||||
|     //BEGIN: top left corner |  | ||||||
|     { |  | ||||||
|         painter->save(); |  | ||||||
|         painter->setPen(outlineColor); |  | ||||||
|         const QPointF topLeft(optRect.topLeft()); |  | ||||||
|         QRectF arc(topLeft, QSizeF(4, 4)); |  | ||||||
|         arc.translate(0.5, 0.5); |  | ||||||
|         painter->drawArc(arc, 1440, 1440); |  | ||||||
|         painter->restore(); |  | ||||||
|     } |  | ||||||
|     //END: top left corner |  | ||||||
|  |  | ||||||
|     //BEGIN: left vertical line |  | ||||||
|     { |  | ||||||
|         QPoint start(optRect.topLeft()); |  | ||||||
|         start.ry() += 3; |  | ||||||
|         QPoint verticalGradBottom(optRect.topLeft()); |  | ||||||
|         verticalGradBottom.ry() += fontMetrics.height() + 5; |  | ||||||
|         QLinearGradient gradient(start, verticalGradBottom); |  | ||||||
|         gradient.setColorAt(0, outlineColor); |  | ||||||
|         gradient.setColorAt(1, Qt::transparent); |  | ||||||
|         painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); |  | ||||||
|     } |  | ||||||
|     //END: left vertical line |  | ||||||
|  |  | ||||||
|     //BEGIN: horizontal line |  | ||||||
|     { |  | ||||||
|         QPoint start(optRect.topLeft()); |  | ||||||
|         start.rx() += 3; |  | ||||||
|         QPoint horizontalGradTop(optRect.topLeft()); |  | ||||||
|         horizontalGradTop.rx() += optRect.width() - 6; |  | ||||||
|         painter->fillRect(QRect(start, QSize(optRect.width() - 6, 1)), outlineColor); |  | ||||||
|     } |  | ||||||
|     //END: horizontal line |  | ||||||
|  |  | ||||||
|     //BEGIN: top right corner |  | ||||||
|     { |  | ||||||
|         painter->save(); |  | ||||||
|         painter->setPen(outlineColor); |  | ||||||
|         QPointF topRight(optRect.topRight()); |  | ||||||
|         topRight.rx() -= 4; |  | ||||||
|         QRectF arc(topRight, QSizeF(4, 4)); |  | ||||||
|         arc.translate(0.5, 0.5); |  | ||||||
|         painter->drawArc(arc, 0, 1440); |  | ||||||
|         painter->restore(); |  | ||||||
|     } |  | ||||||
|     //END: top right corner |  | ||||||
|  |  | ||||||
|     //BEGIN: right vertical line |  | ||||||
|     { |  | ||||||
|         QPoint start(optRect.topRight()); |  | ||||||
|         start.ry() += 3; |  | ||||||
|         QPoint verticalGradBottom(optRect.topRight()); |  | ||||||
|         verticalGradBottom.ry() += fontMetrics.height() + 5; |  | ||||||
|         QLinearGradient gradient(start, verticalGradBottom); |  | ||||||
|         gradient.setColorAt(0, outlineColor); |  | ||||||
|         gradient.setColorAt(1, Qt::transparent); |  | ||||||
|         painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); |  | ||||||
|     } |  | ||||||
|     //END: right vertical line |  | ||||||
|  |  | ||||||
|     //BEGIN: checkboxy thing |  | ||||||
|     { |  | ||||||
|         painter->save(); |  | ||||||
|         painter->setRenderHint(QPainter::Antialiasing, false); |  | ||||||
|     painter->setFont(font); |     painter->setFont(font); | ||||||
|         QColor penColor(option.palette.text().color()); |  | ||||||
|  |     QPen pen; | ||||||
|  |     pen.setWidth(2); | ||||||
|  |     QColor penColor = option.palette.text().color(); | ||||||
|     penColor.setAlphaF(0.6); |     penColor.setAlphaF(0.6); | ||||||
|         painter->setPen(penColor); |     pen.setColor(penColor); | ||||||
|         QRect iconSubRect(option.rect); |     painter->setPen(pen); | ||||||
|         iconSubRect.setTop(iconSubRect.top() + 7); |     painter->setRenderHint(QPainter::Antialiasing); | ||||||
|         iconSubRect.setLeft(iconSubRect.left() + 7); |  | ||||||
|  |  | ||||||
|         int sizing = fontMetrics.height(); |     // sizes and offsets, to keep things consistent below | ||||||
|         int even = ( (sizing - 1) % 2 ); |     int arrowOffsetLeft = fontMetrics.height() / 2 + 7; | ||||||
|  |     int textOffsetLeft = arrowOffsetLeft * 2; | ||||||
|  |     int arrowSize = 6; | ||||||
|  |     int centerHeight = optRect.top() + fontMetrics.height() / 2; | ||||||
|  |  | ||||||
|         iconSubRect.setHeight(sizing - even); |     // BEGIN: arrow | ||||||
|         iconSubRect.setWidth(sizing - even); |  | ||||||
|         painter->drawRect(iconSubRect); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         /* |  | ||||||
|         if(collapsed) |  | ||||||
|             painter->drawText(iconSubRect, Qt::AlignHCenter | Qt::AlignVCenter, "+"); |  | ||||||
|         else |  | ||||||
|             painter->drawText(iconSubRect, Qt::AlignHCenter | Qt::AlignVCenter, "-"); |  | ||||||
|         */ |  | ||||||
|         painter->setBrush(option.palette.text()); |  | ||||||
|         painter->fillRect(iconSubRect.x(), iconSubRect.y() + iconSubRect.height() / 2, |  | ||||||
|                           iconSubRect.width(), 2, penColor); |  | ||||||
|         if (collapsed) |  | ||||||
|     { |     { | ||||||
|             painter->fillRect(iconSubRect.x() + iconSubRect.width() / 2, iconSubRect.y(), 2, |         QPolygon arrowPolygon; | ||||||
|                               iconSubRect.height(), penColor); |         if (collapsed) { | ||||||
|  |             arrowPolygon << QPoint(arrowOffsetLeft - arrowSize / 2, centerHeight - arrowSize) | ||||||
|  |                          << QPoint(arrowOffsetLeft + arrowSize / 2, centerHeight) | ||||||
|  |                          << QPoint(arrowOffsetLeft - arrowSize / 2, centerHeight + arrowSize); | ||||||
|  |             painter->drawPolyline(arrowPolygon); | ||||||
|  |         } else { | ||||||
|  |             arrowPolygon << QPoint(arrowOffsetLeft - arrowSize, centerHeight - arrowSize / 2) | ||||||
|  |                          << QPoint(arrowOffsetLeft, centerHeight + arrowSize / 2) | ||||||
|  |                          << QPoint(arrowOffsetLeft + arrowSize, centerHeight - arrowSize / 2); | ||||||
|  |             painter->drawPolyline(arrowPolygon); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         painter->restore(); |  | ||||||
|     } |     } | ||||||
|     //END: checkboxy thing |     // END: arrow | ||||||
|  |  | ||||||
|     //BEGIN: text |     // BEGIN: text | ||||||
|     { |     { | ||||||
|         QRect textRect(option.rect); |         QRect textRect(optRect); | ||||||
|         textRect.setTop(textRect.top() + 7); |         textRect.setTop(textRect.top()); | ||||||
|         textRect.setLeft(textRect.left() + 7 + fontMetrics.height() + 7); |         textRect.setLeft(textOffsetLeft); | ||||||
|         textRect.setHeight(fontMetrics.height()); |         textRect.setHeight(fontMetrics.height()); | ||||||
|         textRect.setRight(textRect.right() - 7); |         textRect.setRight(textRect.right() - 7); | ||||||
|  |  | ||||||
|         painter->save(); |         painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, !text.isEmpty() ? text : QObject::tr("Ungrouped")); | ||||||
|         painter->setFont(font); |  | ||||||
|         QColor penColor(option.palette.text().color()); |  | ||||||
|         penColor.setAlphaF(0.6); |  | ||||||
|         painter->setPen(penColor); |  | ||||||
|         painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, text); |  | ||||||
|         painter->restore(); |  | ||||||
|     } |     } | ||||||
|     //END: text |     // END: text | ||||||
| } | } | ||||||
|  |  | ||||||
| int VisualGroup::totalHeight() const | int VisualGroup::totalHeight() const | ||||||
| { | { | ||||||
|     return headerHeight() + 5 + contentHeight(); // FIXME: wtf is that '5'? |     return headerHeight() + contentHeight(); | ||||||
| } | } | ||||||
|  |  | ||||||
| int VisualGroup::headerHeight() const | int VisualGroup::headerHeight() | ||||||
| { | { | ||||||
|     QFont font(QApplication::font()); |     QFont font(QApplication::font()); | ||||||
|     font.setBold(true); |     font.setBold(true); | ||||||
| @@ -311,8 +218,7 @@ int VisualGroup::headerHeight() const | |||||||
|  |  | ||||||
| int VisualGroup::contentHeight() const | int VisualGroup::contentHeight() const | ||||||
| { | { | ||||||
|     if (collapsed) |     if (collapsed) { | ||||||
|     { |  | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|     auto last = rows[numRows() - 1]; |     auto last = rows[numRows() - 1]; | ||||||
| @@ -321,7 +227,7 @@ int VisualGroup::contentHeight() const | |||||||
|  |  | ||||||
| int VisualGroup::numRows() const | int VisualGroup::numRows() const | ||||||
| { | { | ||||||
|     return rows.size(); |     return (int)rows.size(); | ||||||
| } | } | ||||||
|  |  | ||||||
| int VisualGroup::verticalPosition() const | int VisualGroup::verticalPosition() const | ||||||
| @@ -332,11 +238,9 @@ int VisualGroup::verticalPosition() const | |||||||
| QList<QModelIndex> VisualGroup::items() const | QList<QModelIndex> VisualGroup::items() const | ||||||
| { | { | ||||||
|     QList<QModelIndex> indices; |     QList<QModelIndex> indices; | ||||||
|     for (int i = 0; i < view->model()->rowCount(); ++i) |     for (int i = 0; i < view->model()->rowCount(); ++i) { | ||||||
|     { |  | ||||||
|         const QModelIndex index = view->model()->index(i, 0); |         const QModelIndex index = view->model()->index(i, 0); | ||||||
|         if (index.data(InstanceViewRoles::GroupRole).toString() == text) |         if (index.data(InstanceViewRoles::GroupRole).toString() == text) { | ||||||
|         { |  | ||||||
|             indices.append(index); |             indices.append(index); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,4 +1,24 @@ | |||||||
| /* Copyright 2013-2021 MultiMC Contributors | // SPDX-License-Identifier: GPL-3.0-only | ||||||
|  | /* | ||||||
|  |  *  Prism Launcher - Minecraft Launcher | ||||||
|  |  *  Copyright (C) 2023 Tayou <git@tayou.org> | ||||||
|  |  * | ||||||
|  |  *  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 <https://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  * 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"); |  *      Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  *      you may not use this file except in compliance with the License. |  *      you may not use this file except in compliance with the License. | ||||||
| @@ -42,8 +62,8 @@ struct VisualRow | |||||||
| struct VisualGroup | struct VisualGroup | ||||||
| { | { | ||||||
| /* constructors */ | /* constructors */ | ||||||
|     VisualGroup(const QString &text, InstanceView *view); |     VisualGroup(QString text, InstanceView *view); | ||||||
|     VisualGroup(const VisualGroup *other); |     explicit VisualGroup(const VisualGroup *other); | ||||||
|  |  | ||||||
| /* data */ | /* data */ | ||||||
|     InstanceView *view = nullptr; |     InstanceView *view = nullptr; | ||||||
| @@ -58,13 +78,13 @@ struct VisualGroup | |||||||
|     void update(); |     void update(); | ||||||
|  |  | ||||||
|     /// draw the header at y-position. |     /// draw the header at y-position. | ||||||
|     void drawHeader(QPainter *painter, const QStyleOptionViewItem &option); |     void drawHeader(QPainter *painter, const QStyleOptionViewItem &option) const; | ||||||
|  |  | ||||||
|     /// height of the group, in total. includes a small bit of padding. |     /// height of the group, in total. includes a small bit of padding. | ||||||
|     int totalHeight() const; |     int totalHeight() const; | ||||||
|  |  | ||||||
|     /// height of the group header, in pixels |     /// height of the group header, in pixels | ||||||
|     int headerHeight() const; |     static int headerHeight() ; | ||||||
|  |  | ||||||
|     /// height of the group content, in pixels |     /// height of the group content, in pixels | ||||||
|     int contentHeight() const; |     int contentHeight() const; | ||||||
|   | |||||||
| @@ -30,7 +30,7 @@ | |||||||
|      </property> |      </property> | ||||||
|      <widget class="QWidget" name="tab"> |      <widget class="QWidget" name="tab"> | ||||||
|       <attribute name="title"> |       <attribute name="title"> | ||||||
|        <string notr="true">Services</string> |        <string>Services</string> | ||||||
|       </attribute> |       </attribute> | ||||||
|       <layout class="QVBoxLayout" name="verticalLayout_2"> |       <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||||
|        <item> |        <item> | ||||||
|   | |||||||
| @@ -159,19 +159,6 @@ void AccountListPage::on_actionAddMojang_triggered() | |||||||
|  |  | ||||||
| void AccountListPage::on_actionAddMicrosoft_triggered() | void AccountListPage::on_actionAddMicrosoft_triggered() | ||||||
| { | { | ||||||
|     if(BuildConfig.BUILD_PLATFORM == "osx64") { |  | ||||||
|         CustomMessageBox::selectable( |  | ||||||
|             this, |  | ||||||
|             tr("Microsoft Accounts not available"), |  | ||||||
|             //: %1 refers to the launcher itself |  | ||||||
|             tr( |  | ||||||
|                 "Microsoft accounts are only usable on macOS 10.13 or newer, with fully updated %1.\n\n" |  | ||||||
|                 "Please update both your operating system and %1." |  | ||||||
|             ).arg(BuildConfig.LAUNCHER_DISPLAYNAME), |  | ||||||
|             QMessageBox::Warning |  | ||||||
|         )->exec(); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     MinecraftAccountPtr account = MSALoginDialog::newAccount( |     MinecraftAccountPtr account = MSALoginDialog::newAccount( | ||||||
|         this, |         this, | ||||||
|         tr("Please enter your Mojang account email and password to add your account.") |         tr("Please enter your Mojang account email and password to add your account.") | ||||||
|   | |||||||
| @@ -58,7 +58,7 @@ | |||||||
|           <item row="2" column="0"> |           <item row="2" column="0"> | ||||||
|            <widget class="QLabel" name="labelPermGen"> |            <widget class="QLabel" name="labelPermGen"> | ||||||
|             <property name="text"> |             <property name="text"> | ||||||
|              <string notr="true">&PermGen:</string> |              <string>&PermGen:</string> | ||||||
|             </property> |             </property> | ||||||
|             <property name="buddy"> |             <property name="buddy"> | ||||||
|              <cstring>permGenSpinBox</cstring> |              <cstring>permGenSpinBox</cstring> | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ | |||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> |  *  Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> | ||||||
|  *  Copyright (c) 2022 dada513 <dada513@protonmail.com> |  *  Copyright (c) 2022 dada513 <dada513@protonmail.com> | ||||||
|  *  Copyright (C) 2022 Tayou <tayou@gmx.net> |  *  Copyright (C) 2022 Tayou <git@tayou.org> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
|   | |||||||
| @@ -62,7 +62,7 @@ public: | |||||||
|  |  | ||||||
|     QString displayName() const override |     QString displayName() const override | ||||||
|     { |     { | ||||||
|         return "Launcher"; |         return tr("Launcher"); | ||||||
|     } |     } | ||||||
|     QIcon icon() const override |     QIcon icon() const override | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
| /* | /* | ||||||
|  *  PolyMC - Minecraft Launcher |  *  PolyMC - Minecraft Launcher | ||||||
|  *  Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> |  *  Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> | ||||||
|  |  *  Copyright (C) 2023 seth <getchoo at tuta dot io> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
| @@ -99,6 +100,9 @@ void MinecraftPage::applySettings() | |||||||
|     // Miscellaneous |     // Miscellaneous | ||||||
|     s->set("CloseAfterLaunch", ui->closeAfterLaunchCheck->isChecked()); |     s->set("CloseAfterLaunch", ui->closeAfterLaunchCheck->isChecked()); | ||||||
|     s->set("QuitAfterGameStop", ui->quitAfterGameStopCheck->isChecked()); |     s->set("QuitAfterGameStop", ui->quitAfterGameStopCheck->isChecked()); | ||||||
|  |  | ||||||
|  |     // Mod loader settings | ||||||
|  |     s->set("DisableQuiltBeacon", ui->disableQuiltBeaconCheckBox->isChecked()); | ||||||
| } | } | ||||||
|  |  | ||||||
| void MinecraftPage::loadSettings() | void MinecraftPage::loadSettings() | ||||||
| @@ -137,6 +141,8 @@ void MinecraftPage::loadSettings() | |||||||
|  |  | ||||||
|     ui->closeAfterLaunchCheck->setChecked(s->get("CloseAfterLaunch").toBool()); |     ui->closeAfterLaunchCheck->setChecked(s->get("CloseAfterLaunch").toBool()); | ||||||
|     ui->quitAfterGameStopCheck->setChecked(s->get("QuitAfterGameStop").toBool()); |     ui->quitAfterGameStopCheck->setChecked(s->get("QuitAfterGameStop").toBool()); | ||||||
|  |  | ||||||
|  |     ui->disableQuiltBeaconCheckBox->setChecked(s->get("DisableQuiltBeacon").toBool()); | ||||||
| } | } | ||||||
|  |  | ||||||
| void MinecraftPage::retranslate() | void MinecraftPage::retranslate() | ||||||
|   | |||||||
| @@ -39,7 +39,7 @@ | |||||||
|      </property> |      </property> | ||||||
|      <widget class="QWidget" name="minecraftTab"> |      <widget class="QWidget" name="minecraftTab"> | ||||||
|       <attribute name="title"> |       <attribute name="title"> | ||||||
|        <string notr="true">General</string> |        <string>General</string> | ||||||
|       </attribute> |       </attribute> | ||||||
|       <layout class="QVBoxLayout" name="verticalLayout_3"> |       <layout class="QVBoxLayout" name="verticalLayout_3"> | ||||||
|        <item> |        <item> | ||||||
| @@ -190,6 +190,25 @@ | |||||||
|        <string>Tweaks</string> |        <string>Tweaks</string> | ||||||
|       </attribute> |       </attribute> | ||||||
|       <layout class="QVBoxLayout" name="verticalLayout_12"> |       <layout class="QVBoxLayout" name="verticalLayout_12"> | ||||||
|  |        <item> | ||||||
|  |         <widget class="QGroupBox" name="modLoaderSettingsGroupBox"> | ||||||
|  |          <property name="title"> | ||||||
|  |           <string>Mod loader settings</string> | ||||||
|  |          </property> | ||||||
|  |          <layout class="QVBoxLayout" name="verticalLayout_13"> | ||||||
|  |           <item> | ||||||
|  |            <widget class="QCheckBox" name="disableQuiltBeaconCheckBox"> | ||||||
|  |             <property name="text"> | ||||||
|  |              <string>Disable Quilt Loader Beacon</string> | ||||||
|  |             </property> | ||||||
|  |             <property name="toolTip"> | ||||||
|  |              <string>Disable Quilt loader's beacon for counting monthly active users</string> | ||||||
|  |             </property> | ||||||
|  |            </widget> | ||||||
|  |           </item> | ||||||
|  |          </layout> | ||||||
|  |         </widget> | ||||||
|  |        </item> | ||||||
|        <item> |        <item> | ||||||
|         <widget class="QGroupBox" name="nativeLibWorkaroundGroupBox"> |         <widget class="QGroupBox" name="nativeLibWorkaroundGroupBox"> | ||||||
|          <property name="title"> |          <property name="title"> | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ | |||||||
|  *  PolyMC - Minecraft Launcher |  *  PolyMC - Minecraft Launcher | ||||||
|  *  Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> |  *  Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> | ||||||
|  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> |  *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> | ||||||
|  |  *  Copyright (C) 2023 seth <getchoo at tuta dot io> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
| @@ -51,9 +52,9 @@ | |||||||
| #include "minecraft/auth/AccountList.h" | #include "minecraft/auth/AccountList.h" | ||||||
|  |  | ||||||
| #include "JavaDownloader.h" | #include "JavaDownloader.h" | ||||||
|  | #include "FileSystem.h" | ||||||
| #include "java/JavaInstallList.h" | #include "java/JavaInstallList.h" | ||||||
| #include "java/JavaUtils.h" | #include "java/JavaUtils.h" | ||||||
| #include "FileSystem.h" |  | ||||||
|  |  | ||||||
| InstanceSettingsPage::InstanceSettingsPage(BaseInstance *inst, QWidget *parent) | InstanceSettingsPage::InstanceSettingsPage(BaseInstance *inst, QWidget *parent) | ||||||
|     : QWidget(parent), ui(new Ui::InstanceSettingsPage), m_instance(inst) |     : QWidget(parent), ui(new Ui::InstanceSettingsPage), m_instance(inst) | ||||||
| @@ -281,6 +282,14 @@ void InstanceSettingsPage::applySettings() | |||||||
|         m_settings->reset("InstanceAccountId"); |         m_settings->reset("InstanceAccountId"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     bool overrideModLoaderSettings = ui->modLoaderSettingsGroupBox->isChecked(); | ||||||
|  |     m_settings->set("OverrideModLoaderSettings", overrideModLoaderSettings); | ||||||
|  |     if (overrideModLoaderSettings) { | ||||||
|  |         m_settings->set("DisableQuiltBeacon", ui->disableQuiltBeaconCheckBox->isChecked()); | ||||||
|  |     } else { | ||||||
|  |         m_settings->reset("DisableQuiltBeacon"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // FIXME: This should probably be called by a signal instead |     // FIXME: This should probably be called by a signal instead | ||||||
|     m_instance->updateRuntimeContext(); |     m_instance->updateRuntimeContext(); | ||||||
| } | } | ||||||
| @@ -381,6 +390,10 @@ void InstanceSettingsPage::loadSettings() | |||||||
|  |  | ||||||
|     ui->instanceAccountGroupBox->setChecked(m_settings->get("UseAccountForInstance").toBool()); |     ui->instanceAccountGroupBox->setChecked(m_settings->get("UseAccountForInstance").toBool()); | ||||||
|     updateAccountsMenu(); |     updateAccountsMenu(); | ||||||
|  |  | ||||||
|  |     // Mod loader specific settings | ||||||
|  |     ui->modLoaderSettingsGroupBox->setChecked(m_settings->get("OverrideModLoaderSettings").toBool()); | ||||||
|  |     ui->disableQuiltBeaconCheckBox->setChecked(m_settings->get("DisableQuiltBeacon").toBool()); | ||||||
| } | } | ||||||
|  |  | ||||||
| void InstanceSettingsPage::on_javaDownloadBtn_clicked() | void InstanceSettingsPage::on_javaDownloadBtn_clicked() | ||||||
|   | |||||||
| @@ -123,7 +123,7 @@ | |||||||
|           <item row="2" column="0"> |           <item row="2" column="0"> | ||||||
|            <widget class="QLabel" name="labelPermGen"> |            <widget class="QLabel" name="labelPermGen"> | ||||||
|             <property name="text"> |             <property name="text"> | ||||||
|              <string notr="true">PermGen:</string> |              <string>PermGen:</string> | ||||||
|             </property> |             </property> | ||||||
|            </widget> |            </widget> | ||||||
|           </item> |           </item> | ||||||
| @@ -548,6 +548,31 @@ | |||||||
|        <string>Miscellaneous</string> |        <string>Miscellaneous</string> | ||||||
|       </attribute> |       </attribute> | ||||||
|       <layout class="QVBoxLayout" name="verticalLayout_9"> |       <layout class="QVBoxLayout" name="verticalLayout_9"> | ||||||
|  |        <item> | ||||||
|  |         <widget class="QGroupBox" name="modLoaderSettingsGroupBox"> | ||||||
|  |          <property name="checkable"> | ||||||
|  |           <bool>true</bool> | ||||||
|  |          </property> | ||||||
|  |          <property name="checked"> | ||||||
|  |           <bool>false</bool> | ||||||
|  |          </property> | ||||||
|  |          <property name="title"> | ||||||
|  |           <string>Mod loader settings</string> | ||||||
|  |          </property> | ||||||
|  |          <layout class="QVBoxLayout" name="VerticalLayout_16"> | ||||||
|  |           <item> | ||||||
|  |            <widget class="QCheckBox" name="disableQuiltBeaconCheckBox"> | ||||||
|  |             <property name="text"> | ||||||
|  |              <string>Disable Quilt Loader Beacon</string> | ||||||
|  |             </property> | ||||||
|  |             <property name="toolTip"> | ||||||
|  |              <string>Disable Quilt loader's beacon for counting monthly active users</string> | ||||||
|  |             </property> | ||||||
|  |            </widget> | ||||||
|  |           </item> | ||||||
|  |          </layout> | ||||||
|  |         </widget> | ||||||
|  |        </item> | ||||||
|        <item> |        <item> | ||||||
|         <widget class="QGroupBox" name="gameTimeGroupBox"> |         <widget class="QGroupBox" name="gameTimeGroupBox"> | ||||||
|          <property name="enabled"> |          <property name="enabled"> | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Tayou <tayou@gmx.net> |  *  Copyright (C) 2022 Tayou <git@tayou.org> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
| @@ -61,7 +61,7 @@ void ThemeWizardPage::updateIcons() | |||||||
| void ThemeWizardPage::updateCat() | void ThemeWizardPage::updateCat() | ||||||
| { | { | ||||||
|     qDebug() << "Setting Cat"; |     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() | void ThemeWizardPage::retranslate() | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Tayou <tayou@gmx.net> |  *  Copyright (C) 2022 Tayou <git@tayou.org> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
|   | |||||||
							
								
								
									
										117
									
								
								launcher/ui/themes/CatPack.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								launcher/ui/themes/CatPack.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,117 @@ | |||||||
|  | // SPDX-License-Identifier: GPL-3.0-only | ||||||
|  | /* | ||||||
|  |  *  Prism Launcher - Minecraft Launcher | ||||||
|  |  *  Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> | ||||||
|  |  * | ||||||
|  |  *  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 <https://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  * 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 <QDate> | ||||||
|  | #include <QDir> | ||||||
|  | #include <QFileInfo> | ||||||
|  | #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; | ||||||
|  | } | ||||||
							
								
								
									
										91
									
								
								launcher/ui/themes/CatPack.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								launcher/ui/themes/CatPack.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | |||||||
|  | // SPDX-License-Identifier: GPL-3.0-only | ||||||
|  | /* | ||||||
|  |  *  Prism Launcher - Minecraft Launcher | ||||||
|  |  *  Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> | ||||||
|  |  * | ||||||
|  |  *  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 <https://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  * 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 <QDate> | ||||||
|  | #include <QFileInfo> | ||||||
|  | #include <QList> | ||||||
|  | #include <QString> | ||||||
|  |  | ||||||
|  | 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<Variant> m_variants; | ||||||
|  | }; | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Tayou <tayou@gmx.net> |  *  Copyright (C) 2022 Tayou <git@tayou.org> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Tayou <tayou@gmx.net> |  *  Copyright (C) 2022 Tayou <git@tayou.org> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Tayou <tayou@gmx.net> |  *  Copyright (C) 2022 Tayou <git@tayou.org> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Tayou <tayou@gmx.net> |  *  Copyright (C) 2022 Tayou <git@tayou.org> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Tayou <tayou@gmx.net> |  *  Copyright (C) 2022 Tayou <git@tayou.org> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Tayou <tayou@gmx.net> |  *  Copyright (C) 2022 Tayou <git@tayou.org> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Tayou <tayou@gmx.net> |  *  Copyright (C) 2022 Tayou <git@tayou.org> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
| @@ -21,7 +21,10 @@ | |||||||
| #include <QDir> | #include <QDir> | ||||||
| #include <QDirIterator> | #include <QDirIterator> | ||||||
| #include <QIcon> | #include <QIcon> | ||||||
|  | #include <QImageReader> | ||||||
|  | #include "Exception.h" | ||||||
| #include "ui/themes/BrightTheme.h" | #include "ui/themes/BrightTheme.h" | ||||||
|  | #include "ui/themes/CatPack.h" | ||||||
| #include "ui/themes/CustomTheme.h" | #include "ui/themes/CustomTheme.h" | ||||||
| #include "ui/themes/DarkTheme.h" | #include "ui/themes/DarkTheme.h" | ||||||
| #include "ui/themes/SystemTheme.h" | #include "ui/themes/SystemTheme.h" | ||||||
| @@ -32,6 +35,7 @@ ThemeManager::ThemeManager(MainWindow* mainWindow) | |||||||
| { | { | ||||||
|     m_mainWindow = mainWindow; |     m_mainWindow = mainWindow; | ||||||
|     initializeThemes(); |     initializeThemes(); | ||||||
|  |     initializeCatPacks(); | ||||||
| } | } | ||||||
|  |  | ||||||
| /// @brief Adds the Theme to the list of themes | /// @brief Adds the Theme to the list of themes | ||||||
| @@ -40,7 +44,10 @@ ThemeManager::ThemeManager(MainWindow* mainWindow) | |||||||
| QString ThemeManager::addTheme(std::unique_ptr<ITheme> theme) | QString ThemeManager::addTheme(std::unique_ptr<ITheme> theme) | ||||||
| { | { | ||||||
|     QString id = theme->id(); |     QString id = theme->id(); | ||||||
|  |     if (m_themes.find(id) == m_themes.end()) | ||||||
|         m_themes.emplace(id, std::move(theme)); |         m_themes.emplace(id, std::move(theme)); | ||||||
|  |     else | ||||||
|  |         themeWarningLog() << "Theme(" << id << ") not added to prevent id duplication"; | ||||||
|     return id; |     return id; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -77,7 +84,7 @@ void ThemeManager::initializeThemes() | |||||||
|         QString themeFolder = QDir("./themes/").absoluteFilePath(""); |         QString themeFolder = QDir("./themes/").absoluteFilePath(""); | ||||||
|         themeDebugLog() << "Theme Folder Path: " << themeFolder; |         themeDebugLog() << "Theme Folder Path: " << themeFolder; | ||||||
|  |  | ||||||
|         QDirIterator directoryIterator(themeFolder, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); |         QDirIterator directoryIterator(themeFolder, QDir::Dirs | QDir::NoDotAndDotDot); | ||||||
|         while (directoryIterator.hasNext()) { |         while (directoryIterator.hasNext()) { | ||||||
|             QDir dir(directoryIterator.next()); |             QDir dir(directoryIterator.next()); | ||||||
|             QFileInfo themeJson(dir.absoluteFilePath("theme.json")); |             QFileInfo themeJson(dir.absoluteFilePath("theme.json")); | ||||||
| @@ -111,6 +118,16 @@ QList<ITheme*> ThemeManager::getValidApplicationThemes() | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | QList<CatPack*> ThemeManager::getValidCatPacks() | ||||||
|  | { | ||||||
|  |     QList<CatPack*> ret; | ||||||
|  |     ret.reserve(m_catPacks.size()); | ||||||
|  |     for (auto&& [id, theme] : m_catPacks) { | ||||||
|  |         ret.append(theme.get()); | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
| void ThemeManager::setIconTheme(const QString& name) | void ThemeManager::setIconTheme(const QString& name) | ||||||
| { | { | ||||||
|     QIcon::setThemeName(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(); |     auto catIter = m_catPacks.find(!catName.isEmpty() ? catName : APPLICATION->settings()->get("BackgroundCat").toString()); | ||||||
|     QDateTime birthday(QDate(now.date().year(), 11, 30), QTime(0, 0)); |     if (catIter != m_catPacks.end()) { | ||||||
|     QDateTime xmas(QDate(now.date().year(), 12, 25), QTime(0, 0)); |         auto& catPack = catIter->second; | ||||||
|     QDateTime halloween(QDate(now.date().year(), 10, 31), QTime(0, 0)); |         themeDebugLog() << "applying catpack" << catPack->id(); | ||||||
|     QString cat = !catName.isEmpty() ? catName : APPLICATION->settings()->get("BackgroundCat").toString(); |         return catPack->path(); | ||||||
|     if (std::abs(now.daysTo(xmas)) <= 4) { |     } else { | ||||||
|         cat += "-xmas"; |         themeWarningLog() << "Tried to get invalid catPack:" << catName; | ||||||
|     } else if (std::abs(now.daysTo(halloween)) <= 4) { |     } | ||||||
|         cat += "-spooky"; |  | ||||||
|     } else if (std::abs(now.daysTo(birthday)) <= 12) { |     return m_catPacks.begin()->second->path(); | ||||||
|         cat += "-bday"; | } | ||||||
|  |  | ||||||
|  | QString ThemeManager::addCatPack(std::unique_ptr<CatPack> 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<std::pair<QString, QString>> 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<CatPack>(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<CatPack>(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<CatPack>(new JsonCatPack(manifest))); | ||||||
|  |             } catch (const Exception& e) { | ||||||
|  |                 themeWarningLog() << "Couldn't load catpack json:" << e.cause(); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             loadFiles(dir); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     return cat; |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Tayou <tayou@gmx.net> |  *  Copyright (C) 2022 Tayou <git@tayou.org> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
| @@ -20,6 +20,7 @@ | |||||||
| #include <QString> | #include <QString> | ||||||
|  |  | ||||||
| #include "ui/MainWindow.h" | #include "ui/MainWindow.h" | ||||||
|  | #include "ui/themes/CatPack.h" | ||||||
| #include "ui/themes/ITheme.h" | #include "ui/themes/ITheme.h" | ||||||
|  |  | ||||||
| inline auto themeDebugLog() | inline auto themeDebugLog() | ||||||
| @@ -40,18 +41,20 @@ class ThemeManager { | |||||||
|     void applyCurrentlySelectedTheme(bool initial = false); |     void applyCurrentlySelectedTheme(bool initial = false); | ||||||
|     void setApplicationTheme(const QString& name, bool initial = false); |     void setApplicationTheme(const QString& name, bool initial = false); | ||||||
|  |  | ||||||
|     /// <summary> |     /// @brief Returns the background based on selected and with events (Birthday, XMas, etc.) | ||||||
|     /// Returns the cat based on selected cat and with events (Birthday, XMas, etc.) |     /// @param catName Optional, if you need a specific background. | ||||||
|     /// </summary> |     /// @return | ||||||
|     /// <param name="catName">Optional, if you need a specific cat.</param> |     QString getCatPack(QString catName = ""); | ||||||
|     /// <returns></returns> |     QList<CatPack*> getValidCatPacks(); | ||||||
|     static QString getCatImage(QString catName = ""); |  | ||||||
|  |  | ||||||
|    private: |    private: | ||||||
|     std::map<QString, std::unique_ptr<ITheme>> m_themes; |     std::map<QString, std::unique_ptr<ITheme>> m_themes; | ||||||
|  |     std::map<QString, std::unique_ptr<CatPack>> m_catPacks; | ||||||
|     MainWindow* m_mainWindow; |     MainWindow* m_mainWindow; | ||||||
|  |  | ||||||
|     void initializeThemes(); |     void initializeThemes(); | ||||||
|  |     void initializeCatPacks(); | ||||||
|     QString addTheme(std::unique_ptr<ITheme> theme); |     QString addTheme(std::unique_ptr<ITheme> theme); | ||||||
|     ITheme* getTheme(QString themeId); |     ITheme* getTheme(QString themeId); | ||||||
|  |     QString addCatPack(std::unique_ptr<CatPack> catPack); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Tayou <tayou@gmx.net> |  *  Copyright (C) 2022 Tayou <git@tayou.org> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
| @@ -95,9 +95,14 @@ void ThemeCustomizationWidget::applyWidgetTheme(int index) { | |||||||
|     emit currentWidgetThemeChanged(index); |     emit currentWidgetThemeChanged(index); | ||||||
| } | } | ||||||
|  |  | ||||||
| void ThemeCustomizationWidget::applyCatTheme(int index) { | void ThemeCustomizationWidget::applyCatTheme(int index) | ||||||
|  | { | ||||||
|     auto settings = APPLICATION->settings(); |     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); |     emit currentCatChanged(index); | ||||||
| } | } | ||||||
| @@ -135,10 +140,10 @@ void ThemeCustomizationWidget::loadSettings() | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     auto cat = settings->get("BackgroundCat").toString(); |     auto cat = settings->get("BackgroundCat").toString(); | ||||||
|     for (auto& catFromList : m_catOptions) { |     for (auto& catFromList : APPLICATION->getValidCatPacks()) { | ||||||
|         QIcon catIcon = QIcon(QString(":/backgrounds/%1").arg(ThemeManager::getCatImage(catFromList.first))); |         QIcon catIcon = QIcon(QString("%1").arg(catFromList->path())); | ||||||
|         ui->backgroundCatComboBox->addItem(catIcon, catFromList.second); |         ui->backgroundCatComboBox->addItem(catIcon, catFromList->name(), catFromList->id()); | ||||||
|         if (cat == catFromList.first) { |         if (cat == catFromList->id()) { | ||||||
|             ui->backgroundCatComboBox->setCurrentIndex(ui->backgroundCatComboBox->count() - 1); |             ui->backgroundCatComboBox->setCurrentIndex(ui->backgroundCatComboBox->count() - 1); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| // SPDX-License-Identifier: GPL-3.0-only | // SPDX-License-Identifier: GPL-3.0-only | ||||||
| /* | /* | ||||||
|  *  Prism Launcher - Minecraft Launcher |  *  Prism Launcher - Minecraft Launcher | ||||||
|  *  Copyright (C) 2022 Tayou <tayou@gmx.net> |  *  Copyright (C) 2022 Tayou <git@tayou.org> | ||||||
|  * |  * | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  *  it under the terms of the GNU General Public License as published by |  *  it under the terms of the GNU General Public License as published by | ||||||
| @@ -53,9 +53,8 @@ class ThemeCustomizationWidget : public QWidget { | |||||||
|    private: |    private: | ||||||
|     Ui::ThemeCustomizationWidget* ui; |     Ui::ThemeCustomizationWidget* ui; | ||||||
|  |  | ||||||
|     //TODO finish implementing |     // TODO finish implementing | ||||||
|     QList<std::pair<QString, QString>> m_iconThemeOptions{  |     QList<std::pair<QString, QString>> m_iconThemeOptions{ { "pe_colored", QObject::tr("Simple (Colored Icons)") }, | ||||||
|         { "pe_colored",     QObject::tr("Simple (Colored Icons)") },  |  | ||||||
|                                                            { "pe_light", QObject::tr("Simple (Light Icons)") }, |                                                            { "pe_light", QObject::tr("Simple (Light Icons)") }, | ||||||
|                                                            { "pe_dark", QObject::tr("Simple (Dark Icons)") }, |                                                            { "pe_dark", QObject::tr("Simple (Dark Icons)") }, | ||||||
|                                                            { "pe_blue", QObject::tr("Simple (Blue Icons)") }, |                                                            { "pe_blue", QObject::tr("Simple (Blue Icons)") }, | ||||||
| @@ -66,12 +65,5 @@ class ThemeCustomizationWidget : public QWidget { | |||||||
|                                                            { "flat", QObject::tr("Flat") }, |                                                            { "flat", QObject::tr("Flat") }, | ||||||
|                                                            { "flat_white", QObject::tr("Flat (White)") }, |                                                            { "flat_white", QObject::tr("Flat (White)") }, | ||||||
|                                                            { "multimc", QObject::tr("Legacy") }, |                                                            { "multimc", QObject::tr("Legacy") }, | ||||||
|         { "custom",         QObject::tr("Custom") }  |                                                            { "custom", QObject::tr("Custom") } }; | ||||||
|     }; |  | ||||||
|     QList<std::pair<QString, QString>> 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)") } |  | ||||||
|     }; |  | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -61,7 +61,7 @@ The `standard` and `legacy` launchers are available. | |||||||
|  |  | ||||||
| Example (some parts have been censored): | Example (some parts have been censored): | ||||||
|  |  | ||||||
| ``` | ```text | ||||||
| mod legacyjavafixer-1.0 | mod legacyjavafixer-1.0 | ||||||
| mainClass net.minecraft.launchwrapper.Launch | mainClass net.minecraft.launchwrapper.Launch | ||||||
| param --username | param --username | ||||||
|   | |||||||
 Submodule libraries/libnbtplusplus updated: 2203af7eeb...a5e8fd52b8
									
								
							
							
								
								
									
										10
									
								
								nix/NIX.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								nix/NIX.md
									
									
									
									
									
								
							| @@ -53,7 +53,8 @@ home.packages = [ pkgs.prismlauncher ]; | |||||||
|  |  | ||||||
| ### Without flakes-enabled nix | ### Without flakes-enabled nix | ||||||
|  |  | ||||||
| #### Using channels | <details> | ||||||
|  | <summary>Using channels</summary> | ||||||
|  |  | ||||||
| ```sh | ```sh | ||||||
| nix-channel --add https://github.com/PrismLauncher/PrismLauncher/archive/master.tar.gz prismlauncher | 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 | nix-env -iA prismlauncher | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| #### Using the overlay | </details> | ||||||
|  |  | ||||||
|  | <details> | ||||||
|  | <summary>Using the overlay</summary> | ||||||
|  |  | ||||||
| ```nix | ```nix | ||||||
| # In your configuration.nix: | # In your configuration.nix: | ||||||
| @@ -74,6 +78,8 @@ nix-env -iA prismlauncher | |||||||
| } | } | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|  | </details> | ||||||
|  |  | ||||||
| ## Running ad-hoc | ## Running ad-hoc | ||||||
|  |  | ||||||
| If you're on a flakes-enabled nix you can run the launcher in one-line | If you're on a flakes-enabled nix you can run the launcher in one-line | ||||||
|   | |||||||
| @@ -24,9 +24,9 @@ | |||||||
|   # Supported systems. |   # Supported systems. | ||||||
|   systems = [ |   systems = [ | ||||||
|     "x86_64-linux" |     "x86_64-linux" | ||||||
|     "x86_64-darwin" |  | ||||||
|     "aarch64-linux" |     "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" |     # "aarch64-darwin" | ||||||
|   ]; |   ]; | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 TheKodeToad
					TheKodeToad