diff --git a/.clang-tidy b/.clang-tidy index ef5166da4..6c489fb50 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -7,17 +7,17 @@ Checks: # ^ Without unused-parameters the readability-identifier-naming check doesn't cause any warnings. CheckOptions: - - { key: readability-identifier-naming.ClassCase, value: PascalCase } - - { key: readability-identifier-naming.EnumCase, value: PascalCase } - - { key: readability-identifier-naming.FunctionCase, value: camelCase } - - { key: readability-identifier-naming.GlobalVariableCase, value: camelCase } - - { key: readability-identifier-naming.GlobalFunctionCase, value: camelCase } - - { key: readability-identifier-naming.GlobalConstantCase, value: SCREAMING_SNAKE_CASE } - - { key: readability-identifier-naming.MacroDefinitionCase, value: SCREAMING_SNAKE_CASE } - - { key: readability-identifier-naming.ClassMemberCase, value: camelCase } - - { key: readability-identifier-naming.PrivateMemberPrefix, value: m_ } - - { key: readability-identifier-naming.ProtectedMemberPrefix, value: m_ } - - { key: readability-identifier-naming.PrivateStaticMemberPrefix, value: s_ } + - { key: readability-identifier-naming.ClassCase, value: PascalCase } + - { key: readability-identifier-naming.EnumCase, value: PascalCase } + - { key: readability-identifier-naming.FunctionCase, value: camelCase } + - { key: readability-identifier-naming.GlobalVariableCase, value: camelCase } + - { key: readability-identifier-naming.GlobalFunctionCase, value: camelCase } + - { key: readability-identifier-naming.GlobalConstantCase, value: SCREAMING_SNAKE_CASE } + - { key: readability-identifier-naming.MacroDefinitionCase, value: SCREAMING_SNAKE_CASE } + - { key: readability-identifier-naming.ClassMemberCase, value: camelCase } + - { key: readability-identifier-naming.PrivateMemberPrefix, value: m_ } + - { key: readability-identifier-naming.ProtectedMemberPrefix, value: m_ } + - { key: readability-identifier-naming.PrivateStaticMemberPrefix, value: s_ } - { key: readability-identifier-naming.ProtectedStaticMemberPrefix, value: s_ } - - { key: readability-identifier-naming.PublicStaticConstantCase, value: SCREAMING_SNAKE_CASE } - - { key: readability-identifier-naming.EnumConstantCase, value: SCREAMING_SNAKE_CASE } \ No newline at end of file + - { key: readability-identifier-naming.PublicStaticConstantCase, value: SCREAMING_SNAKE_CASE } + - { key: readability-identifier-naming.EnumConstantCase, value: PascalCase } diff --git a/.github/ISSUE_TEMPLATE/rfc.yml b/.github/ISSUE_TEMPLATE/rfc.yml index fa7cdbe61..4d285047e 100644 --- a/.github/ISSUE_TEMPLATE/rfc.yml +++ b/.github/ISSUE_TEMPLATE/rfc.yml @@ -44,8 +44,8 @@ body: attributes: label: Unresolved Questions description: | - Are there any portions of your proposal which need to be discussed with the community before the RFC can proceed? - Be careful here -- an RFC with a lot of remaining questions is likely to be stalled. + Are there any portions of your proposal which need to be discussed with the community before the RFC can proceed? + Be careful here -- an RFC with a lot of remaining questions is likely to be stalled. If your RFC is mostly unresolved questions and not too much substance, it may not be ready. placeholder: Do a lot of users care about the cat? validations: diff --git a/.github/actions/package/linux/action.yml b/.github/actions/package/linux/action.yml index 3cc49fa02..bf299befb 100644 --- a/.github/actions/package/linux/action.yml +++ b/.github/actions/package/linux/action.yml @@ -27,6 +27,18 @@ runs: using: composite steps: + - name: Cleanup Qt installation on Linux + shell: bash + run: | + rm -rf "$QT_PLUGIN_PATH"/printsupport + rm -rf "$QT_PLUGIN_PATH"/sqldrivers + rm -rf "$QT_PLUGIN_PATH"/help + rm -rf "$QT_PLUGIN_PATH"/designer + rm -rf "$QT_PLUGIN_PATH"/qmltooling + rm -rf "$QT_PLUGIN_PATH"/qmlls + rm -rf "$QT_PLUGIN_PATH"/qmllint + rm -rf "$QT_PLUGIN_PATH"/platformthemes/libqgtk3.so + - name: Setup build variables shell: bash run: | @@ -52,32 +64,50 @@ runs: - name: Package AppImage shell: bash env: - VERSION: ${{ inputs.version }} + VERSION: ${{ github.ref_type == 'tag' && github.ref_name || inputs.version }} BUILD_DIR: build INSTALL_APPIMAGE_DIR: install-appdir GPG_PRIVATE_KEY: ${{ inputs.gpg-private-key }} run: | - cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr - - cp ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.{metainfo,appdata}.xml + cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }} if [ '${{ inputs.gpg-private-key-id }}' != '' ]; then echo "$GPG_PRIVATE_KEY" > privkey.asc gpg --import privkey.asc - gpg --export --armor 9C7A2C9B62603299 > pubkey.asc + gpg --export --armor ${{ inputs.gpg-private-key-id }} > pubkey.asc else echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY fi - appimagetool -s deploy "$INSTALL_APPIMAGE_DIR"/usr/share/applications/*.desktop - cp ~/bin/AppImageUpdate.AppImage "$INSTALL_APPIMAGE_DIR"/usr/bin/ - # FIXME(@getchoo): Validate AppStream information when https://github.com/probonopd/go-appimage/pull/379 is merged + sharun lib4bin \ + --hard-links \ + --with-hooks \ + --dst-dir "$INSTALL_APPIMAGE_DIR" \ + "$INSTALL_APPIMAGE_DIR"/bin/* "$QT_PLUGIN_PATH"/*/*.so + + cp ~/bin/AppImageUpdate.AppImage "$INSTALL_APPIMAGE_DIR"/bin/ + # FIXME(@getchoo): gamemode doesn't seem to be very portable with DBus. Find a way to make it work! + find "$INSTALL_APPIMAGE_DIR" -name '*gamemode*' -exec rm {} + + + #makes the launcher use portals for file picking + echo "QT_QPA_PLATFORMTHEME=xdgdesktopportal" > "$INSTALL_APPIMAGE_DIR"/.env + ln -s org.prismlauncher.PrismLauncher.metainfo.xml "$INSTALL_APPIMAGE_DIR"/share/metainfo/org.prismlauncher.PrismLauncher.appdata.xml + ln -s share/applications/org.prismlauncher.PrismLauncher.desktop "$INSTALL_APPIMAGE_DIR" + ln -s share/icons/hicolor/256x256/apps/org.prismlauncher.PrismLauncher.png "$INSTALL_APPIMAGE_DIR" + mv "$INSTALL_APPIMAGE_DIR"/{sharun,AppRun} + ls -la "$INSTALL_APPIMAGE_DIR" + + if [[ "${{ github.ref_type }}" == "tag" ]]; then + APPIMAGE_DEST="PrismLauncher-Linux-$APPIMAGE_ARCH.AppImage" + else + APPIMAGE_DEST="PrismLauncher-Linux-$VERSION-${{ inputs.build-type }}-$APPIMAGE_ARCH.AppImage" + fi + mkappimage \ - --no-appstream \ --updateinformation "gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-$APPIMAGE_ARCH.AppImage.zsync" \ "$INSTALL_APPIMAGE_DIR" \ - "PrismLauncher-Linux-$VERSION-${{ inputs.build-type }}-$APPIMAGE_ARCH.AppImage" + "$APPIMAGE_DEST" - name: Package portable tarball shell: bash @@ -89,11 +119,16 @@ runs: cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - #the linked cmark .so is of the version that ubuntu uses, so without this it breaks on most updated distros - mkdir ${{ env.INSTALL_PORTABLE_DIR }}/lib - cp /lib/$APPIMAGE_ARCH-linux-gnu/libcmark.so.0.* ${{ env.INSTALL_PORTABLE_DIR }}/lib + sharun lib4bin \ + --with-hooks \ + --hard-links \ + --dst-dir "$INSTALL_PORTABLE_DIR" \ + "$INSTALL_PORTABLE_DIR"/bin/* "$QT_PLUGIN_PATH"/*/*.so - for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt + # FIXME(@getchoo): gamemode doesn't seem to be very portable with DBus. Find a way to make it work! + find "$INSTALL_PORTABLE_DIR" -name '*gamemode*' -exec rm {} + + + for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f -o -type l); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt cd ${{ env.INSTALL_PORTABLE_DIR }} tar -czf ../PrismLauncher-portable.tar.gz * @@ -107,10 +142,10 @@ runs: uses: actions/upload-artifact@v6 with: name: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-${{ env.APPIMAGE_ARCH }}.AppImage - path: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-${{ env.APPIMAGE_ARCH }}.AppImage + path: PrismLauncher-${{ runner.os }}-*${{ env.APPIMAGE_ARCH }}.AppImage - name: Upload AppImage Zsync uses: actions/upload-artifact@v6 with: name: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-${{ env.APPIMAGE_ARCH }}.AppImage.zsync - path: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-${{ env.APPIMAGE_ARCH }}.AppImage.zsync + path: PrismLauncher-${{ runner.os }}-*${{ env.APPIMAGE_ARCH }}.AppImage.zsync diff --git a/.github/actions/package/windows/action.yml b/.github/actions/package/windows/action.yml index f49f6b9c9..74036154a 100644 --- a/.github/actions/package/windows/action.yml +++ b/.github/actions/package/windows/action.yml @@ -54,13 +54,13 @@ runs: Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt - name: Emit warning for unsigned builds - if: ${{ github.ref_name != 'develop' || inputs.azure-client-id == '' }} + if: ${{ env.CI_HAS_ACCESS_TO_AZURE == '' || inputs.azure-client-id == '' }} shell: pwsh run: | ":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY - name: Login to Azure - if: ${{ github.ref_name == 'develop' && inputs.azure-client-id != '' }} + if: ${{ env.CI_HAS_ACCESS_TO_AZURE != '' && inputs.azure-client-id != '' }} uses: azure/login@v2 with: client-id: ${{ inputs.azure-client-id }} @@ -68,18 +68,19 @@ runs: subscription-id: ${{ inputs.azure-subscription-id }} - name: Sign executables - if: ${{ github.ref_name == 'develop' && inputs.azure-client-id != '' }} - uses: azure/trusted-signing-action@v0 + if: ${{ env.CI_HAS_ACCESS_TO_AZURE != '' && inputs.azure-client-id != '' }} + uses: azure/artifact-signing-action@v1 with: endpoint: https://eus.codesigning.azure.net/ trusted-signing-account-name: PrismLauncher certificate-profile-name: PrismLauncher - - files: | - ${{ github.workspace }}\install\prismlauncher.exe - ${{ github.workspace }}\install\prismlauncher_filelink.exe - ${{ github.workspace }}\install\prismlauncher_updater.exe - + files-folder: ${{ github.workspace }}\install\ + files-folder-filter: dll,exe + files-folder-recurse: true + files-folder-depth: 2 + # recommended in https://github.com/Azure/artifact-signing-action#timestamping-1 + timestamp-rfc3161: "http://timestamp.acs.microsoft.com" + timestamp-digest: "SHA256" # TODO(@getchoo): Is this all really needed??? # https://github.com/Azure/trusted-signing-action/blob/fc390cf8ed0f14e248a542af1d838388a47c7a7c/docs/OIDC.md exclude-environment-credential: true @@ -140,8 +141,8 @@ runs: makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi" - name: Sign installer - if: ${{ github.ref_name == 'develop' && inputs.azure-client-id != '' }} - uses: azure/trusted-signing-action@v0 + if: ${{ env.CI_HAS_ACCESS_TO_AZURE != '' && inputs.azure-client-id != '' }} + uses: azure/artifact-signing-action@v1 with: endpoint: https://eus.codesigning.azure.net/ trusted-signing-account-name: PrismLauncher @@ -150,6 +151,9 @@ runs: files: | ${{ github.workspace }}\PrismLauncher-Setup.exe + # recommended in https://github.com/Azure/artifact-signing-action#timestamping-1 + timestamp-rfc3161: "http://timestamp.acs.microsoft.com" + timestamp-digest: "SHA256" # TODO(@getchoo): Is this all really needed??? # https://github.com/Azure/trusted-signing-action/blob/fc390cf8ed0f14e248a542af1d838388a47c7a7c/docs/OIDC.md exclude-environment-credential: true diff --git a/.github/actions/setup-dependencies/action.yml b/.github/actions/setup-dependencies/action.yml index 4c19474b7..a8ecba583 100644 --- a/.github/actions/setup-dependencies/action.yml +++ b/.github/actions/setup-dependencies/action.yml @@ -21,7 +21,6 @@ inputs: qt-version: description: Version of Qt to use required: true - default: 6.9.3 outputs: build-type: @@ -78,6 +77,5 @@ runs: with: aqtversion: "==3.1.*" version: ${{ inputs.qt-version }} - arch: ${{ inputs.qt-architecture }} modules: qtimageformats qtnetworkauth cache: ${{ inputs.build-type == 'Debug' }} diff --git a/.github/actions/setup-dependencies/linux/action.yml b/.github/actions/setup-dependencies/linux/action.yml index 67a1a9826..fa5af702b 100644 --- a/.github/actions/setup-dependencies/linux/action.yml +++ b/.github/actions/setup-dependencies/linux/action.yml @@ -1,4 +1,5 @@ name: Setup Linux dependencies +description: Install and setup dependencies for building Prism Launcher runs: using: composite @@ -12,29 +13,7 @@ runs: dpkg-dev \ ninja-build extra-cmake-modules pkg-config scdoc \ cmark gamemode-dev libarchive-dev libcmark-dev libqrencode-dev zlib1g-dev \ - libxcb-cursor-dev - - # TODO(@getchoo): Install with the above when all targets use Ubuntu 24.04 - - name: Install tomlplusplus - if: ${{ runner.arch == 'ARM64' }} - shell: bash - run: | - sudo apt-get -y install libtomlplusplus-dev - - # FIXME(@getchoo): THIS IS HORRIBLE TO DO! - # Install tomlplusplus from Ubuntu 24.04, since it never got backported to 22.04 - # I've done too much to continue keeping this as a submodule.... - - name: Install tomlplusplus from 24.04 - if: ${{ runner.arch != 'ARM64' }} - shell: bash - run: | - deb_arch="$(dpkg-architecture -q DEB_HOST_ARCH)" - curl -Lo libtomlplusplus-dev.deb http://mirrors.kernel.org/ubuntu/pool/universe/t/tomlplusplus/libtomlplusplus-dev_3.4.0+ds-0.2build1_"$deb_arch".deb - curl -Lo libtomlplusplus3t64.deb http://mirrors.kernel.org/ubuntu/pool/universe/t/tomlplusplus/libtomlplusplus3t64_3.4.0+ds-0.2build1_"$deb_arch".deb - sudo dpkg -i libtomlplusplus3t64.deb - sudo dpkg -i libtomlplusplus-dev.deb - rm *.deb - sudo apt-get install -f + libxcb-cursor-dev libtomlplusplus-dev - name: Setup AppImage tooling shell: bash @@ -56,19 +35,20 @@ runs: ;; esac + gh release download \ + --repo VHSgunzo/sharun \ + --pattern "sharun-$APPIMAGE_ARCH-aio" \ + --output ~/bin/sharun + + # FIXME!: revert this to probonopd/go-appimage once https://github.com/probonopd/go-appimage/pull/377 is merged! gh release download continuous \ - --repo probonopd/go-appimage \ - --pattern "appimagetool-*-$APPIMAGE_ARCH.AppImage" \ - --output ~/bin/appimagetool - gh release download continuous \ - --repo probonopd/go-appimage \ + --repo DioEgizio/go-appimage \ --pattern "mkappimage-*-$APPIMAGE_ARCH.AppImage" \ --output ~/bin/mkappimage - chmod +x ~/bin/appimagetool ~/bin/mkappimage - echo "$HOME/bin" >> "$GITHUB_PATH" gh release download \ --repo AppImageCommunity/AppImageUpdate \ --pattern "AppImageUpdate-$APPIMAGE_ARCH.AppImage" \ --output ~/bin/AppImageUpdate.AppImage - chmod +x ~/bin/AppImageUpdate.AppImage + chmod +x ~/bin/* + echo "$HOME/bin" >> "$GITHUB_PATH" diff --git a/.github/actions/setup-dependencies/macos/action.yml b/.github/actions/setup-dependencies/macos/action.yml index 0e6d48621..a90544be0 100644 --- a/.github/actions/setup-dependencies/macos/action.yml +++ b/.github/actions/setup-dependencies/macos/action.yml @@ -14,7 +14,7 @@ runs: shell: bash run: | brew update - brew install ninja extra-cmake-modules temurin@17 + brew install ninja extra-cmake-modules temurin@17 mono - name: Set JAVA_HOME shell: bash @@ -44,4 +44,4 @@ runs: - name: Setup vcpkg environment shell: bash run: | - echo "CMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" >> "$GITHUB_ENV" + echo "VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> "$GITHUB_ENV" diff --git a/.github/actions/setup-dependencies/windows/action.yml b/.github/actions/setup-dependencies/windows/action.yml index b250b0088..e530a9ce6 100644 --- a/.github/actions/setup-dependencies/windows/action.yml +++ b/.github/actions/setup-dependencies/windows/action.yml @@ -1,4 +1,5 @@ name: Setup Windows Dependencies +description: Install and setup dependencies for building Prism Launcher inputs: build-type: @@ -57,7 +58,7 @@ runs: if: ${{ inputs.msystem == '' }} shell: bash run: | - echo "CMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" >> "$GITHUB_ENV" + echo "VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> "$GITHUB_ENV" - name: Setup MSYS2 (MinGW) if: ${{ inputs.msystem != '' }} diff --git a/.github/workflows/blocked-prs.yml b/.github/workflows/blocked-prs.yml index ecbaf755d..e6b37f188 100644 --- a/.github/workflows/blocked-prs.yml +++ b/.github/workflows/blocked-prs.yml @@ -70,17 +70,16 @@ jobs: ' <<< "$PR_JSON")" } >> "$GITHUB_ENV" - - name: Find Blocked/Stacked PRs in body id: pr_ids run: | prs=$( jq -c ' - .prBody as $body + .prBody as $body | ( - $body | - reduce ( - . | scan("blocked (?:by|on):? #([0-9]+)") + $body | + reduce ( + . | scan("[Bb]locked (?:[Bb]y|[Oo]n):? #([0-9]+)") | map({ "type": "Blocked on", "number": ( . | tonumber ) @@ -88,17 +87,17 @@ jobs: ) as $i ([]; . + [$i[]]) ) as $bprs | ( - $body | - reduce ( - . | scan("stacked on:? #([0-9]+)") + $body | + reduce ( + . | scan("[Ss]tacked [Oo]n:? #([0-9]+)") | map({ "type": "Stacked on", "number": ( . | tonumber ) }) ) as $i ([]; . + [$i[]]) ) as $sprs - | ($bprs + $sprs) as $prs - | { + | ($bprs + $sprs) as $prs + | { "blocking": $prs, "numBlocking": ( $prs | length), } @@ -126,7 +125,7 @@ jobs: "number": .number, "merged": .merged, "state": (if .state == "open" then "Open" elif .merged then "Merged" else "Closed" end), - "labels": (reduce .labels[].name as $l ([]; . + [$l])), + "labels": (reduce .labels[].name as $l ([]; . + [$l])), "basePrUrl": .html_url, "baseRepoName": .head.repo.name, "baseRepoOwner": .head.repo.owner.login, @@ -143,7 +142,7 @@ jobs: echo "current_blocking=$(jq -c 'map( select( (.type == "Stacked on" and (.merged | not)) or - (.type == "Blocked on" and (.state == "Open")) + (.type == "Blocked on" and (.state == "Open")) ) | .number )' <<< "$blocked_pr_data" )"; } >> "$GITHUB_OUTPUT" @@ -252,4 +251,3 @@ jobs: --body "$COMMENT_BODY" \ --create-if-none \ --edit-last - diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5eef33805..68bb03dd3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,33 +5,8 @@ concurrency: cancel-in-progress: true on: - push: - branches: - - "develop" - - "release-*" - paths: - # File types - - "**.cpp" - - "**.h" - - "**.java" - - "**.ui" - - # Directories - - "buildconfig/**" - - "cmake/**" - - "launcher/**" - - "libraries/**" - - "program_info/**" - - "tests/**" - - # Files - - "CMakeLists.txt" - - "COPYING.md" - - # Workflows - - ".github/workflows/build.yml" - - ".github/actions/package/**" - - ".github/actions/setup-dependencies/**" + merge_group: + types: [checks_requested] pull_request: paths: # File types @@ -62,6 +37,9 @@ on: description: Type of build (Debug or Release) type: string default: Debug + environment: + description: Deployment environment to run under + type: string workflow_dispatch: inputs: build-type: @@ -73,6 +51,8 @@ jobs: build: name: Build (${{ matrix.artifact-name }}) + environment: ${{ inputs.environment || '' }} + permissions: # Required for Azure Trusted Signing id-token: write @@ -83,17 +63,15 @@ jobs: fail-fast: false matrix: include: - - os: ubuntu-22.04 + - os: ubuntu-24.04 artifact-name: Linux cmake-preset: linux + qt-version: 6.10.2 - # NOTE(@getchoo): Yes, we're intentionally using 24.04 here!!! - # - # It's not really documented anywhere AFAICT, but upstream Qt binaries - # *for the same version* are compiled against 24.04 on ARM, and *not* 22.04 like x64 - os: ubuntu-24.04-arm artifact-name: Linux-aarch64 cmake-preset: linux + qt-version: 6.10.2 - os: windows-2022 artifact-name: Windows-MinGW-w64 @@ -112,16 +90,19 @@ jobs: cmake-preset: windows_msvc # TODO(@getchoo): This is the default in setup-dependencies/windows. Why isn't it working?!?! vcvars-arch: amd64 + qt-version: 6.10.2 - os: windows-11-arm artifact-name: Windows-MSVC-arm64 cmake-preset: windows_msvc vcvars-arch: arm64 + qt-version: 6.10.2 - - os: macos-14 + - os: macos-26 artifact-name: macOS cmake-preset: macos_universal macosx-deployment-target: 12.0 + qt-version: 6.9.3 runs-on: ${{ matrix.os }} @@ -155,7 +136,7 @@ jobs: artifact-name: ${{ matrix.artifact-name }} msystem: ${{ matrix.msystem }} vcvars-arch: ${{ matrix.vcvars-arch }} - qt-architecture: ${{ matrix.qt-architecture }} + qt-version: ${{ matrix.qt-version }} ## # BUILD @@ -171,7 +152,7 @@ jobs: - name: Run tests run: | - ctest --preset "$CMAKE_PRESET" --build-config "$BUILD_TYPE" + ctest --preset "$CMAKE_PRESET" --build-config "$BUILD_TYPE" --extra-verbose --output-on-failure ## # PACKAGE @@ -214,6 +195,8 @@ jobs: - name: Package (Windows) if: ${{ runner.os == 'Windows' }} uses: ./.github/actions/package/windows + env: + CI_HAS_ACCESS_TO_AZURE: ${{ vars.CI_HAS_ACCESS_TO_AZURE || '' }} with: version: ${{ steps.short-version.outputs.version }} build-type: ${{ steps.setup-dependencies.outputs.build-type }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 17b722118..7be97d500 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -5,33 +5,8 @@ concurrency: cancel-in-progress: true on: - push: - branches: - - "develop" - - "release-*" - paths: - # File types - - "**.cpp" - - "**.h" - - "**.java" - - "**.ui" - - # Directories - - "buildconfig/**" - - "cmake/**" - - "launcher/**" - - "libraries/**" - - "program_info/**" - - "tests/**" - - # Files - - "CMakeLists.txt" - - "COPYING.md" - - # Workflows - - ".github/codeql/**" - - ".github/workflows/codeql.yml" - - ".github/actions/setup-dependencies/**" + merge_group: + types: [checks_requested] pull_request: paths: # File types @@ -79,11 +54,16 @@ jobs: uses: ./.github/actions/setup-dependencies with: build-type: Debug + qt-version: 6.4.3 - name: Configure and Build run: | - cmake --preset linux + cmake --preset linux -DLauncher_USE_PCH=OFF cmake --build --preset linux --config Debug + - name: Run tests + run: | + ctest --preset linux --build-config Debug --extra-verbose --output-on-failure + - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v4 diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml deleted file mode 100644 index e457538ff..000000000 --- a/.github/workflows/flatpak.yml +++ /dev/null @@ -1,105 +0,0 @@ -name: Flatpak - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -on: - push: - branches: - - "develop" - - "release-*" - # We don't do anything with these artifacts on releases. They go to Flathub - tags-ignore: - - "*" - paths: - # File types - - "**.cpp" - - "**.h" - - "**.java" - - "**.ui" - - # Build files - - "flatpak/**" - - # Directories - - "buildconfig/**" - - "cmake/**" - - "launcher/**" - - "libraries/**" - - "program_info/**" - - "tests/**" - - # Files - - "CMakeLists.txt" - - "COPYING.md" - - # Workflows - - ".github/workflows/flatpak.yml" - pull_request: - paths: - # File types - - "**.cpp" - - "**.h" - - "**.java" - - "**.ui" - - # Build files - - "flatpak/**" - - # Directories - - "buildconfig/**" - - "cmake/**" - - "launcher/**" - - "libraries/**" - - "program_info/**" - - "tests/**" - - # Files - - "CMakeLists.txt" - - "COPYING.md" - - # Workflows - - ".github/workflows/flatpak.yml" - workflow_dispatch: - -permissions: - contents: read - -jobs: - build: - name: Build (${{ matrix.arch }}) - - strategy: - fail-fast: false - matrix: - include: - - os: ubuntu-22.04 - arch: x86_64 - - - os: ubuntu-22.04-arm - arch: aarch64 - - runs-on: ${{ matrix.os }} - - container: - image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.10 - options: --privileged - - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - submodules: true - - - name: Set short version - shell: bash - run: | - echo "VERSION=${GITHUB_SHA::7}" >> "$GITHUB_ENV" - - - name: Build Flatpak - uses: flatpak/flatpak-github-actions/flatpak-builder@v6 - with: - bundle: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-Flatpak.flatpak - manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml - arch: ${{ matrix.arch }} diff --git a/.github/workflows/merge-blocking-pr.yml b/.github/workflows/merge-blocking-pr.yml index d37c33761..182e40984 100644 --- a/.github/workflows/merge-blocking-pr.yml +++ b/.github/workflows/merge-blocking-pr.yml @@ -38,7 +38,7 @@ jobs: gh -R ${{ github.repository }} pr list --label 'blocked' --json 'number,body' \ | jq -c --argjson pr "$PR_NUMBER" ' reduce ( .[] | select( - .body | + .body | scan("(?:blocked (?:by|on)|stacked on):? #([0-9]+)") | map(tonumber) | any(.[]; . == $pr) @@ -47,7 +47,7 @@ jobs: ) { echo "deps=$blocked_prs" - echo "numdeps=$(jq -r '. | length' <<< "$blocked_prs")" + echo "numdeps=$(jq -r '. | length' <<< "$blocked_prs")" } >> "$GITHUB_OUTPUT" - name: Trigger Blocked PR Workflows for Dependants diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index dbf8e8f52..2035668f4 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -86,9 +86,6 @@ jobs: - os: ubuntu-22.04-arm system: aarch64-linux - - os: macos-15-intel - system: x86_64-darwin - - os: macos-14 system: aarch64-darwin diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9e5445c66..bd22fe05c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,6 +11,7 @@ jobs: uses: ./.github/workflows/build.yml with: build-type: Release + environment: Release secrets: inherit create_release: @@ -34,6 +35,7 @@ jobs: run: | mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }} mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz + mv PrismLauncher-Linux-aarch64-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-aarch64-Qt6-Portable-${{ env.VERSION }}.tar.gz mv PrismLauncher-*.AppImage/PrismLauncher-*-x86_64.AppImage PrismLauncher-Linux-x86_64.AppImage mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*-x86_64.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync mv PrismLauncher-*.AppImage/PrismLauncher-*-aarch64.AppImage PrismLauncher-Linux-aarch64.AppImage @@ -88,6 +90,7 @@ jobs: name: Prism Launcher ${{ env.VERSION }} draft: true prerelease: false + fail_on_unmatched_files: true files: | PrismLauncher-Linux-x86_64.AppImage PrismLauncher-Linux-x86_64.AppImage.zsync diff --git a/.gitmodules b/.gitmodules index f2d71ef29..42c566fa8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ [submodule "libraries/libnbtplusplus"] path = libraries/libnbtplusplus url = https://github.com/PrismLauncher/libnbtplusplus.git -[submodule "flatpak/shared-modules"] - path = flatpak/shared-modules - url = https://github.com/flathub/shared-modules.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 197839893..c83beb089 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,9 @@ cmake_minimum_required(VERSION 3.22) # minimum version required by Qt -project(Launcher) +project(Launcher LANGUAGES C CXX) +if(APPLE) + enable_language(OBJC OBJCXX) +endif() string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD) if(IS_IN_SOURCE_BUILD) @@ -27,6 +30,9 @@ set(CMAKE_C_STANDARD_REQUIRED true) set(CMAKE_CXX_STANDARD 20) set(CMAKE_C_STANDARD 11) include(GenerateExportHeader) + +add_compile_definitions($<$>:QT_NO_DEBUG>) + if(MSVC) # /GS Adds buffer security checks, default on but incuded anyway to mirror gcc's fstack-protector flag # /permissive- specify standards-conforming compiler behavior, also enabled by Qt6, default on with std:c++20 @@ -79,19 +85,6 @@ else() if(WIN32) set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,8388608 ${CMAKE_EXE_LINKER_FLAGS}") - # Emit PDBs for WinDbg, etc. - if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") - set(CMAKE_EXE_LINKER_FLAGS "-Wl,--pdb= ${CMAKE_EXE_LINKER_FLAGS}") - - foreach(lang C CXX) - set("CMAKE_${lang}_FLAGS" "-gcodeview ${CMAKE_${lang}_FLAGS}") - - # Force-enabling this to use generator expressions like TARGET_PDB_FILE - # (and because we can actually emit PDBs) - set("CMAKE_${lang}_LINKER_SUPPORTS_PDB" ON) - endforeach() - endif() - # -ffunction-sections and -fdata-sections help reduce binary size # -mguard=cf enables Control Flow Guard # TODO: Look into -gc-sections to further reduce binary size @@ -101,8 +94,8 @@ else() endif() endif() -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_WARN_DEPRECATED_UP_TO=0x060200") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_UP_TO=0x060000") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_WARN_DEPRECATED_UP_TO=0x060400") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_UP_TO=0x060400") # set CXXFLAGS for build targets set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}") @@ -116,30 +109,24 @@ endif() option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF) # If this is a Debug build turn on address sanitiser -if ((CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") AND DEBUG_ADDRESS_SANITIZER) +if (DEBUG_ADDRESS_SANITIZER) message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off") - if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") - # using clang with clang-cl front end - message(STATUS "Address Sanitizer available on Clang MSVC frontend") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-") - else() - # AppleClang and Clang - message(STATUS "Address Sanitizer available on Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=null") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=null") - endif() - elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - # GCC - message(STATUS "Address Sanitizer available on GCC") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover") - link_libraries("asan") - elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - message(STATUS "Address Sanitizer available on MSVC") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-") + + set(USE_ASAN_COMPILE_OPTIONS $,$>) + if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") + message(STATUS "Using Address Sanitizer compile options for MSVC frontend") + add_compile_options( + $<${USE_ASAN_COMPILE_OPTIONS}:/fsanitize=address> + $<${USE_ASAN_COMPILE_OPTIONS}:/Oy-> + ) + elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + message(STATUS "Using Address Sanitizer compile options for GCC/Clang") + add_compile_options( + $<${USE_ASAN_COMPILE_OPTIONS}:-fsanitize=address,undefined> + $<${USE_ASAN_COMPILE_OPTIONS}:-fno-omit-frame-pointer> + $<${USE_ASAN_COMPILE_OPTIONS}:-fno-sanitize-recover=null> + ) + link_libraries("asan" "ubsan") else() message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}") endif() @@ -192,7 +179,7 @@ set(Launcher_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" CACHE S ######## Set version numbers ######## set(Launcher_VERSION_MAJOR 10) set(Launcher_VERSION_MINOR 0) -set(Launcher_VERSION_PATCH 0) +set(Launcher_VERSION_PATCH 5) set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}") set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}.0") @@ -232,6 +219,8 @@ set(Launcher_SUBREDDIT_URL "https://prismlauncher.org/reddit" CACHE STRING "URL # Builds set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against") +option(Launcher_USE_PCH "Use precompiled headers where possible" ON) + # Java downloader set(Launcher_ENABLE_JAVA_DOWNLOADER_DEFAULT ON) @@ -297,7 +286,7 @@ set(Launcher_BUILD_TIMESTAMP "${TODAY}") # Find the required Qt parts if(Launcher_QT_VERSION_MAJOR EQUAL 6) set(QT_VERSION_MAJOR 6) - find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml NetworkAuth OpenGL) + find_package(Qt6 6.4 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml NetworkAuth OpenGL) find_package(Qt6 COMPONENTS DBus) list(APPEND Launcher_QT_DBUS Qt6::DBus) else() @@ -337,7 +326,7 @@ if(NOT LibArchive_FOUND) pkg_check_modules(libarchive REQUIRED IMPORTED_TARGET libarchive) endif() -find_package(tomlplusplus 3.2.0 REQUIRED) +find_package(tomlplusplus 3.2.0) # fallback to pkgconfig, important especially as many distros package toml++ built with meson if(NOT tomlplusplus_FOUND) find_package(PkgConfig REQUIRED) @@ -379,7 +368,7 @@ if(UNIX AND APPLE) # Mac bundle settings set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_DisplayName}") set(MACOSX_BUNDLE_INFO_STRING "${Launcher_DisplayName}: A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.") - set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.prismlauncher.${Launcher_Name}") + set(MACOSX_BUNDLE_GUI_IDENTIFIER "${Launcher_AppID}") set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}") @@ -399,6 +388,52 @@ if(UNIX AND APPLE) # Add the icon install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns) + find_program(ACTOOL_EXE actool DOC "Path to the apple asset catalog compiler") + if(ACTOOL_EXE) + execute_process( + COMMAND xcodebuild -version + OUTPUT_VARIABLE XCODE_VERSION_OUTPUT + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + string(REGEX MATCH "Xcode ([0-9]+\.[0-9]+)" XCODE_VERSION_MATCH "${XCODE_VERSION_OUTPUT}") + if(XCODE_VERSION_MATCH) + set(XCODE_VERSION ${CMAKE_MATCH_1}) + else() + set(XCODE_VERSION 0.0) + endif() + + if(XCODE_VERSION VERSION_GREATER_EQUAL 26.0) + set(ASSETS_OUT_DIR "${CMAKE_BINARY_DIR}/program_info") + set(GENERATED_ASSETS_CAR "${ASSETS_OUT_DIR}/Assets.car") + set(ICON_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_Branding_MAC_ICON}") + + add_custom_command( + OUTPUT "${GENERATED_ASSETS_CAR}" + COMMAND ${ACTOOL_EXE} "${ICON_SOURCE}" + --compile "${ASSETS_OUT_DIR}" + --output-partial-info-plist /dev/null + --app-icon PrismLauncher + --enable-on-demand-resources NO + --target-device mac + --minimum-deployment-target ${CMAKE_OSX_DEPLOYMENT_TARGET} + --platform macosx + DEPENDS "${ICON_SOURCE}" + COMMENT "Compiling asset catalog (${ICON_SOURCE})" + VERBATIM + ) + add_custom_target(compile_assets ALL DEPENDS "${GENERATED_ASSETS_CAR}") + install(FILES "${GENERATED_ASSETS_CAR}" DESTINATION "${RESOURCES_DEST_DIR}") + else() + message(WARNING "Xcode ${XCODE_VERSION} is too old. Minimum required version is 26.0. Not compiling liquid glass icons.") + endif() + + else() + message(WARNING "actool not found. Cannot compile macOS app icons.\n" + "Install Xcode command line tools: 'xcode-select --install'") + endif() + + elseif(UNIX) include(KDEInstallDirs) diff --git a/CMakePresets.json b/CMakePresets.json index 613fada07..5e9ee8b39 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,214 +1,221 @@ { - "$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json", - "version": 8, - "cmakeMinimumRequired": { - "major": 3, - "minor": 28 - }, - "configurePresets": [ - { - "name": "base", - "hidden": true, - "binaryDir": "build", - "installDir": "install", - "generator": "Ninja Multi-Config", - "cacheVariables": { - "Launcher_BUILD_ARTIFACT": "$penv{ARTIFACT_NAME}", - "Launcher_BUILD_PLATFORM": "$penv{BUILD_PLATFORM}", - "Launcher_ENABLE_JAVA_DOWNLOADER": "ON", - "ENABLE_LTO": "ON" - } - }, - { - "name": "linux", - "displayName": "Linux", - "inherits": [ - "base" - ], - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Linux" - } - }, - { - "name": "macos", - "displayName": "macOS", - "inherits": [ - "base" - ], - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Darwin" - } - }, - { - "name": "macos_universal", - "displayName": "macOS (Universal Binary)", - "inherits": [ - "macos" - ], - "cacheVariables": { - "CMAKE_OSX_ARCHITECTURES": "x86_64;arm64", - "VCPKG_TARGET_TRIPLET": "universal-osx" - } - }, - { - "name": "windows_mingw", - "displayName": "Windows (MinGW)", - "inherits": [ - "base" - ], - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Windows" - } - }, - { - "name": "windows_msvc", - "displayName": "Windows (MSVC)", - "inherits": [ - "base" - ], - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Windows" - } - } - ], - "buildPresets": [ - { - "name": "linux", - "displayName": "Linux", - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Linux" - }, - "configurePreset": "linux" - }, - { - "name": "macos", - "displayName": "macOS", - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Darwin" - }, + "$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json", + "version": 8, + "cmakeMinimumRequired": { + "major": 3, + "minor": 28 + }, + "configurePresets": [ + { + "name": "base", + "hidden": true, + "binaryDir": "build", + "installDir": "install", + "generator": "Ninja Multi-Config", + "cacheVariables": { + "Launcher_BUILD_ARTIFACT": "$penv{ARTIFACT_NAME}", + "Launcher_BUILD_PLATFORM": "$penv{BUILD_PLATFORM}", + "Launcher_ENABLE_JAVA_DOWNLOADER": "ON", + "ENABLE_LTO": "ON" + } + }, + { + "name": "linux", + "displayName": "Linux", + "inherits": [ + "base" + ], + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "macos", + "displayName": "macOS", + "inherits": [ + "base" + ], + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + }, + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "$penv{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + } + }, + { + "name": "macos_universal", + "displayName": "macOS (Universal Binary)", + "inherits": [ + "macos" + ], + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "$penv{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "CMAKE_OSX_ARCHITECTURES": "x86_64;arm64", + "VCPKG_TARGET_TRIPLET": "universal-osx" + } + }, + { + "name": "windows_mingw", + "displayName": "Windows (MinGW)", + "inherits": [ + "base" + ], + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "windows_msvc", + "displayName": "Windows (MSVC)", + "inherits": [ + "base" + ], + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "$penv{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + } + } + ], + "buildPresets": [ + { + "name": "linux", + "displayName": "Linux", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + }, + "configurePreset": "linux" + }, + { + "name": "macos", + "displayName": "macOS", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + }, "configurePreset": "macos" - }, - { - "name": "macos_universal", - "displayName": "macOS (Universal Binary)", - "inherits": [ - "macos" - ], - "configurePreset": "macos_universal" - }, - { - "name": "windows_mingw", - "displayName": "Windows (MinGW)", - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Windows" - }, - "configurePreset": "windows_mingw" - }, - { - "name": "windows_msvc", - "displayName": "Windows (MSVC)", - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Windows" - }, - "configurePreset": "windows_msvc" - } - ], - "testPresets": [ - { - "name": "base", - "hidden": true, - "output": { - "outputOnFailure": true - }, - "execution": { - "noTestsAction": "error" - }, - "filter": { - "exclude": { - "name": "^example64|example$" - } - } - }, - { - "name": "linux", - "displayName": "Linux", - "inherits": [ - "base" - ], - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Linux" - }, - "configurePreset": "linux" - }, - { - "name": "macos", - "displayName": "macOS", - "inherits": [ - "base" - ], - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Darwin" - }, - "configurePreset": "macos" - }, - { - "name": "macos_universal", - "displayName": "macOS (Universal Binary)", - "inherits": [ - "base" - ], - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Darwin" - }, - "configurePreset": "macos_universal" - }, - { - "name": "windows_mingw", - "displayName": "Windows (MinGW)", - "inherits": [ - "base" - ], - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Windows" - }, - "configurePreset": "windows_mingw" - }, - { - "name": "windows_msvc", - "displayName": "Windows (MSVC)", - "inherits": [ - "base" - ], - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Windows" - }, - "configurePreset": "windows_msvc" - } - ] + }, + { + "name": "macos_universal", + "displayName": "macOS (Universal Binary)", + "inherits": [ + "macos" + ], + "configurePreset": "macos_universal" + }, + { + "name": "windows_mingw", + "displayName": "Windows (MinGW)", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + "configurePreset": "windows_mingw" + }, + { + "name": "windows_msvc", + "displayName": "Windows (MSVC)", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + "configurePreset": "windows_msvc" + } + ], + "testPresets": [ + { + "name": "base", + "hidden": true, + "output": { + "outputOnFailure": true + }, + "execution": { + "noTestsAction": "error" + }, + "filter": { + "exclude": { + "name": "^example64|example$" + } + } + }, + { + "name": "linux", + "displayName": "Linux", + "inherits": [ + "base" + ], + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + }, + "configurePreset": "linux" + }, + { + "name": "macos", + "displayName": "macOS", + "inherits": [ + "base" + ], + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + }, + "configurePreset": "macos" + }, + { + "name": "macos_universal", + "displayName": "macOS (Universal Binary)", + "inherits": [ + "base" + ], + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + }, + "configurePreset": "macos_universal" + }, + { + "name": "windows_mingw", + "displayName": "Windows (MinGW)", + "inherits": [ + "base" + ], + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + "configurePreset": "windows_mingw" + }, + { + "name": "windows_msvc", + "displayName": "Windows (MSVC)", + "inherits": [ + "base" + ], + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + "configurePreset": "windows_msvc" + } + ] } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fdc79faf2..8adfaec0e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,7 +13,8 @@ Please also follow the project's conventions for C++: - Public, private or protected `static const` class data members should be formatted as `SCREAMING_SNAKE_CASE`: `MAX_VALUE`. - Class function members should be formatted as `camelCase` without a prefix: `incrementCounter`. - Global functions and non-`const` global variables should be formatted as `camelCase` without a prefix: `globalData`. -- `const` global variables, macros, and enum constants should be formatted as `SCREAMING_SNAKE_CASE`: `LIGHT_GRAY`. +- `const` global variables and macros should be formatted as `SCREAMING_SNAKE_CASE`: `LIGHT_GRAY`. +- enum constants should be formatted as `PascalCase`: `CamelusBactrianus` - Avoid inventing acronyms or abbreviations especially for a name of multiple words - like `tp` for `texturePack`. - Avoid using `[[nodiscard]]` unless ignoring the return value is likely to cause a bug in cases such as: - A function allocates memory or another resource and the caller needs to clean it up. @@ -30,7 +31,7 @@ Here is what these conventions with the formatting configuration look like: constexpr double PI = 3.14159; -enum class PizzaToppings { HAM_AND_PINEAPPLE, OREO_AND_KETCHUP }; +enum class PizzaToppings { HamAndPineapple, OreoAndKetchup }; struct Person { QString name; diff --git a/COPYING.md b/COPYING.md index fb33844f7..52f29f2e6 100644 --- a/COPYING.md +++ b/COPYING.md @@ -1,7 +1,7 @@ ## Prism Launcher Prism Launcher - Minecraft Launcher - Copyright (C) 2022-2025 Prism Launcher Contributors + Copyright (C) 2022-2026 Prism Launcher Contributors 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 diff --git a/README.md b/README.md index 850370a29..ac6cfd96b 100644 --- a/README.md +++ b/README.md @@ -27,17 +27,13 @@ Please understand that these builds are not intended for most users. There may b There are development builds available through: - [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) (includes builds from pull requests opened by contributors) -- [nightly.link](https://nightly.link/PrismLauncher/PrismLauncher/workflows/build/develop) (this will always point only to the latest version of develop) +- [nightly.link](https://prismlauncher.org/nightly) (this will always point only to the latest version of develop) These have debug information in the binaries, so their file sizes are relatively larger. Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**. -For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**, respectively, you can use these packages for the latest development versions: - -[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?label=MPR&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git)
[![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?label=COPR&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?label=Gentoo&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher) - -These packages are also available to all the distributions based on the ones mentioned above. +On Linux, we also offer our own [Flatpak nightly repository](https://github.com/PrismLauncher/flatpak). Most software centers are able to install it by opening [this link](https://flatpak.prismlauncher.org/prismlauncher-nightly.flatpakref). ## Community & Support @@ -61,12 +57,7 @@ The translation effort for Prism Launcher is hosted on [Weblate](https://hosted. ## Building -If you want to build Prism Launcher yourself, check the build instructions: - -- [Windows](https://prismlauncher.org/wiki/development/build-instructions/windows/) -- [Linux](https://prismlauncher.org/wiki/development/build-instructions/linux/) -- [MacOS](https://prismlauncher.org/wiki/development/build-instructions/macos/) -- [OpenBSD](https://prismlauncher.org/wiki/development/build-instructions/openbsd/) +If you want to build Prism Launcher yourself, check the [build instructions](https://prismlauncher.org/wiki/development/build-instructions). ## Sponsors & Partners diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index 3637e7369..0a1ac334d 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -33,7 +33,6 @@ * limitations under the License. */ -#include #include #include "BuildConfig.h" diff --git a/cmake/MacOSXBundleInfo.plist.in b/cmake/MacOSXBundleInfo.plist.in index 3a8c8fbfe..0814f7ef0 100644 --- a/cmake/MacOSXBundleInfo.plist.in +++ b/cmake/MacOSXBundleInfo.plist.in @@ -21,7 +21,9 @@ CFBundleGetInfoString ${MACOSX_BUNDLE_INFO_STRING} CFBundleIconFile - ${MACOSX_BUNDLE_ICON_FILE} + ${Launcher_Name} + CFBundleIconName + ${Launcher_Name} CFBundleIdentifier ${MACOSX_BUNDLE_GUI_IDENTIFIER} CFBundleInfoDictionaryVersion @@ -42,6 +44,8 @@ LSRequiresCarbon + LSApplicationCategoryType + public.app-category.games NSHumanReadableCopyright ${MACOSX_BUNDLE_COPYRIGHT} SUPublicEDKey diff --git a/flake.lock b/flake.lock index 27b7ddbb6..730f36a14 100644 --- a/flake.lock +++ b/flake.lock @@ -18,18 +18,15 @@ }, "nixpkgs": { "locked": { - "lastModified": 1765472234, - "narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b", - "type": "github" + "lastModified": 1766473571, + "narHash": "sha256-QvjEJNgMVuOootbR+DEfbiW+zSK57U32CE0jmVdcNjQ=", + "rev": "76701a179d3a98b07653e2b0409847499b2a07d3", + "type": "tarball", + "url": "https://releases.nixos.org/nixos/25.11/nixos-25.11.2403.76701a179d3a/nixexprs.tar.xz" }, "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" + "type": "tarball", + "url": "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz" } }, "root": { diff --git a/flake.nix b/flake.nix index 594a82d91..6594db522 100644 --- a/flake.nix +++ b/flake.nix @@ -9,7 +9,7 @@ }; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs.url = "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz"; libnbtplusplus = { url = "github:PrismLauncher/libnbtplusplus"; diff --git a/flatpak/cmark.yml b/flatpak/cmark.yml deleted file mode 100644 index d5078baab..000000000 --- a/flatpak/cmark.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: cmark -buildsystem: cmake-ninja -builddir: true -config-opts: - - -DCMAKE_TESTS=OFF -sources: - - type: archive - url: https://github.com/commonmark/cmark/archive/0.31.1.tar.gz - sha256: 3da93db5469c30588cfeb283d9d62edfc6ded9eb0edc10a4f5bbfb7d722ea802 - x-checker-data: - type: anitya - project-id: 9159 - stable-only: true - url-template: https://github.com/commonmark/cmark/archive/$version.tar.gz diff --git a/flatpak/flite.json b/flatpak/flite.json deleted file mode 100644 index 1bf280af1..000000000 --- a/flatpak/flite.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "flite", - "config-opts": [ - "--enable-shared", - "--with-audio=pulseaudio" - ], - "no-parallel-make": true, - "sources": [ - { - "type": "git", - "url": "https://github.com/festvox/flite.git", - "tag": "v2.2", - "commit": "e9e2e37c329dbe98bfeb27a1828ef9a71fa84f88", - "x-checker-data": { - "type": "git", - "tag-pattern": "^v([\\d.]+)$" - } - } - ] -} diff --git a/flatpak/libdecor.json b/flatpak/libdecor.json deleted file mode 100644 index 1652a2f04..000000000 --- a/flatpak/libdecor.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "libdecor", - "buildsystem": "meson", - "config-opts": [ - "-Ddemo=false" - ], - "sources": [ - { - "type": "git", - "url": "https://gitlab.freedesktop.org/libdecor/libdecor.git", - "commit": "c2bd8ad6fa42c0cb17553ce77ad8a87d1f543b1f" - } - ], - "cleanup": [ - "/include", - "/lib/pkgconfig" - ] -} diff --git a/flatpak/org.prismlauncher.PrismLauncher.yml b/flatpak/org.prismlauncher.PrismLauncher.yml deleted file mode 100644 index 3de68e228..000000000 --- a/flatpak/org.prismlauncher.PrismLauncher.yml +++ /dev/null @@ -1,154 +0,0 @@ -id: org.prismlauncher.PrismLauncher -runtime: org.kde.Platform -runtime-version: '6.10' -sdk: org.kde.Sdk -sdk-extensions: - - org.freedesktop.Sdk.Extension.openjdk17 - -command: prismlauncher -finish-args: - - --share=ipc - - --socket=x11 - - --socket=wayland - - --device=all - - --share=network - - --socket=pulseaudio - # for Discord RPC mods - - --filesystem=xdg-run/app/com.discordapp.Discord:create - # Mod drag&drop - - --filesystem=xdg-download:ro - # FTBApp import - - --filesystem=~/.ftba:ro - # Userspace visibility for manual hugepages configuration - # Required for -XX:+UseLargePages - - --filesystem=/sys/kernel/mm/hugepages:ro - # Userspace visibility for transparent hugepages configuration - # Required for -XX:+UseTransparentHugePages - - --filesystem=/sys/kernel/mm/transparent_hugepage:ro - -modules: - - cmark.yml - - tomlplusplus.yml - - # 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 - - # Text to Speech in the game - - flite.json - - - name: prismlauncher - buildsystem: cmake-ninja - builddir: true - config-opts: - - -DLauncher_BUILD_PLATFORM=flatpak - # This allows us to manage and update Java independently of this Flatpak - - -DLauncher_ENABLE_JAVA_DOWNLOADER=ON - - -DCMAKE_BUILD_TYPE=RelWithDebInfo - build-options: - env: - JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17 - JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac - run-tests: true - sources: - - type: dir - path: ../ - - - name: glfw - buildsystem: cmake-ninja - config-opts: - - -DCMAKE_BUILD_TYPE=RelWithDebInfo - - -DBUILD_SHARED_LIBS:BOOL=ON - - -DGLFW_BUILD_WAYLAND:BOOL=ON - - -DGLFW_BUILD_DOCS:BOOL=OFF - sources: - - type: git - url: https://github.com/glfw/glfw.git - commit: 7b6aead9fb88b3623e3b3725ebb42670cbe4c579 # 3.4 - - type: patch - path: patches/0009-Defer-setting-cursor-position-until-the-cursor-is-lo.patch - cleanup: - - /include - - /lib/cmake - - /lib/pkgconfig - - - name: xrandr - buildsystem: autotools - sources: - - type: archive - url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.3.tar.xz - sha256: f8dd7566adb74147fab9964680b6bbadee87cf406a7fcff51718a5e6949b841c - 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 - buildsystem: meson - config-opts: - - -Dwith-sd-bus-provider=no-daemon - - -Dwith-examples=false - post-install: - # gamemoderun is installed for users who want to use wrapper commands - # post-install is running inside the build dir, we need it from the source though - - install -Dm755 ../data/gamemoderun -t /app/bin - sources: - - type: archive - dest-filename: gamemode.tar.gz - url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.8.2 - sha256: 2886d4ce543c78bd2a364316d5e7fd59ef06b71de63f896b37c6d3dc97658f60 - 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 - buildsystem: simple - build-commands: - - install -Dm755 prime-run /app/bin/prime-run - - mv /app/bin/prismlauncher /app/bin/prismrun - - install -Dm755 prismlauncher /app/bin/prismlauncher - sources: - - type: file - path: prime-run - - type: file - path: prismlauncher diff --git a/flatpak/patches/0009-Defer-setting-cursor-position-until-the-cursor-is-lo.patch b/flatpak/patches/0009-Defer-setting-cursor-position-until-the-cursor-is-lo.patch deleted file mode 100644 index 70cec9981..000000000 --- a/flatpak/patches/0009-Defer-setting-cursor-position-until-the-cursor-is-lo.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 9997ae55a47de469ea26f8437c30b51483abda5f Mon Sep 17 00:00:00 2001 -From: Dan Klishch -Date: Sat, 30 Sep 2023 23:38:05 -0400 -Subject: Defer setting cursor position until the cursor is locked - ---- - src/wl_platform.h | 3 +++ - src/wl_window.c | 14 ++++++++++++-- - 2 files changed, 15 insertions(+), 2 deletions(-) - -diff --git a/src/wl_platform.h b/src/wl_platform.h -index ca34f66e..cd1f227f 100644 ---- a/src/wl_platform.h -+++ b/src/wl_platform.h -@@ -403,6 +403,9 @@ typedef struct _GLFWwindowWayland - int scaleSize; - int compositorPreferredScale; - -+ double askedCursorPosX, askedCursorPosY; -+ GLFWbool didAskForSetCursorPos; -+ - struct zwp_relative_pointer_v1* relativePointer; - struct zwp_locked_pointer_v1* lockedPointer; - struct zwp_confined_pointer_v1* confinedPointer; -diff --git a/src/wl_window.c b/src/wl_window.c -index 1de26558..0df16747 100644 ---- a/src/wl_window.c -+++ b/src/wl_window.c -@@ -2586,8 +2586,9 @@ void _glfwGetCursorPosWayland(_GLFWwindow* window, double* xpos, double* ypos) - - void _glfwSetCursorPosWayland(_GLFWwindow* window, double x, double y) - { -- _glfwInputError(GLFW_FEATURE_UNAVAILABLE, -- "Wayland: The platform does not support setting the cursor position"); -+ window->wl.didAskForSetCursorPos = true; -+ window->wl.askedCursorPosX = x; -+ window->wl.askedCursorPosY = y; - } - - void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode) -@@ -2819,6 +2820,15 @@ static const struct zwp_relative_pointer_v1_listener relativePointerListener = - static void lockedPointerHandleLocked(void* userData, - struct zwp_locked_pointer_v1* lockedPointer) - { -+ _GLFWwindow* window = userData; -+ -+ if (window->wl.didAskForSetCursorPos) -+ { -+ window->wl.didAskForSetCursorPos = false; -+ zwp_locked_pointer_v1_set_cursor_position_hint(window->wl.lockedPointer, -+ wl_fixed_from_double(window->wl.askedCursorPosX), -+ wl_fixed_from_double(window->wl.askedCursorPosY)); -+ } - } - - static void lockedPointerHandleUnlocked(void* userData, --- -2.42.0 - diff --git a/flatpak/prime-run b/flatpak/prime-run deleted file mode 100644 index 946c28dd5..000000000 --- a/flatpak/prime-run +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -export __NV_PRIME_RENDER_OFFLOAD=1 __VK_LAYER_NV_optimus=NVIDIA_only __GLX_VENDOR_LIBRARY_NAME=nvidia -exec "$@" diff --git a/flatpak/prismlauncher b/flatpak/prismlauncher deleted file mode 100644 index 039d890d2..000000000 --- a/flatpak/prismlauncher +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -# discord RPC -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"; -done - -export PATH="${PATH}${PATH:+:}/usr/lib/extensions/vulkan/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin" -export VK_LAYER_PATH="/usr/lib/extensions/vulkan/share/vulkan/implicit_layer.d/" - -exec /app/bin/prismrun "$@" diff --git a/flatpak/shared-modules b/flatpak/shared-modules deleted file mode 160000 index 73f08ed2c..000000000 --- a/flatpak/shared-modules +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 73f08ed2c3187f6648ca04ebef030930a6c9f0be diff --git a/flatpak/tomlplusplus.yml b/flatpak/tomlplusplus.yml deleted file mode 100644 index 0afaf6678..000000000 --- a/flatpak/tomlplusplus.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: tomlplusplus -buildsystem: cmake-ninja -sources: - - type: archive - url: https://github.com/marzer/tomlplusplus/archive/v3.4.0.tar.gz - sha256: 8517f65938a4faae9ccf8ebb36631a38c1cadfb5efa85d9a72e15b9e97d25155 diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 601ccaeff..c2519c634 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -50,6 +50,7 @@ #include "tools/GenericProfiler.h" #include "ui/InstanceWindow.h" #include "ui/MainWindow.h" +#include "ui/ToolTipFilter.h" #include "ui/ViewLogWindow.h" #include "ui/dialogs/ProgressDialog.h" @@ -371,25 +372,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) } QString origcwdPath = QDir::currentPath(); -#if defined(Q_OS_LINUX) - const QString binFilePath = applicationFilePath(); - const bool isAppImage = binFilePath.startsWith("/tmp/.mount_"); - // Yes, this can technically trigger the logic below if someone makes an AppImage with an actual launcher exe named "ld-linux" - // Please don't :) - const bool executedFromLinker = QFileInfo(binFilePath).fileName().startsWith("ld-linux"); - - // NOTE(@getchoo): In order for `go-appimage` to generate self-contained AppImages, it executes apps from a bundled linker at - // /lib64 - // This is not the path to our actual binary, which we want - QString binPath; - if (isAppImage && executedFromLinker) { - binPath = FS::PathCombine(applicationDirPath(), "../usr/bin"); - } else { - binPath = applicationDirPath(); - } -#else QString binPath = applicationDirPath(); -#endif { // Root path is used for updates and portable data @@ -620,25 +603,25 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) { qInfo() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME + ", " + QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); - qInfo() << "Version : " << BuildConfig.printableVersionString(); - qInfo() << "Platform : " << BuildConfig.BUILD_PLATFORM; - qInfo() << "Git commit : " << BuildConfig.GIT_COMMIT; - qInfo() << "Git refspec : " << BuildConfig.GIT_REFSPEC; - qInfo() << "Compiled for : " << BuildConfig.systemID(); - qInfo() << "Compiled by : " << BuildConfig.compilerID(); - qInfo() << "Build Artifact : " << BuildConfig.BUILD_ARTIFACT; - qInfo() << "Updates Enabled : " << (updaterEnabled() ? "Yes" : "No"); + qInfo() << "Version :" << BuildConfig.printableVersionString(); + qInfo() << "Platform :" << BuildConfig.BUILD_PLATFORM; + qInfo() << "Git commit :" << BuildConfig.GIT_COMMIT; + qInfo() << "Git refspec :" << BuildConfig.GIT_REFSPEC; + qInfo() << "Compiled for :" << BuildConfig.systemID(); + qInfo() << "Compiled by :" << BuildConfig.compilerID(); + qInfo() << "Build Artifact :" << BuildConfig.BUILD_ARTIFACT; + qInfo() << "Updates Enabled :" << (updaterEnabled() ? "Yes" : "No"); if (adjustedBy.size()) { - qInfo() << "Work dir before adjustment : " << origcwdPath; - qInfo() << "Work dir after adjustment : " << QDir::currentPath(); - qInfo() << "Adjusted by : " << adjustedBy; + qInfo() << "Work dir before adjustment :" << origcwdPath; + qInfo() << "Work dir after adjustment :" << QDir::currentPath(); + qInfo() << "Adjusted by :" << adjustedBy; } else { - qInfo() << "Work dir : " << QDir::currentPath(); + qInfo() << "Work dir :" << QDir::currentPath(); } - qInfo() << "Binary path : " << binPath; - qInfo() << "Application root path : " << m_rootPath; + qInfo() << "Binary path :" << binPath; + qInfo() << "Application root path :" << m_rootPath; if (!m_instanceIdToLaunch.isEmpty()) { - qInfo() << "ID of instance to launch : " << m_instanceIdToLaunch; + qInfo() << "ID of instance to launch :" << m_instanceIdToLaunch; } if (!m_serverToJoin.isEmpty()) { qInfo() << "Address of server to join :" << m_serverToJoin; @@ -702,8 +685,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) QFontInfo consoleFontInfo(consoleFont); QString resolvedDefaultMonospace = consoleFontInfo.family(); QFont resolvedFont(resolvedDefaultMonospace); - qDebug() << "Detected default console font:" << resolvedDefaultMonospace - << ", substitutions:" << resolvedFont.substitutions().join(','); + qDebug().nospace() << "Detected default console font: " << resolvedDefaultMonospace + << ", substitutions: " << resolvedFont.substitutions().join(','); m_settings->registerSetting("ConsoleFont", resolvedDefaultMonospace); m_settings->registerSetting("ConsoleFontSize", defaultSize); @@ -1005,7 +988,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) // instance path: check for problems with '!' in instance path and warn the user in the log // and remember that we have to show him a dialog when the gui starts (if it does so) QString instDir = m_settings->get("InstanceDir").toString(); - qInfo() << "Instance path : " << instDir; + qInfo() << "Instance path :" << instDir; if (FS::checkProblemticPathJava(QDir(instDir))) { qWarning() << "Your instance path contains \'!\' and this is known to cause java problems!"; } @@ -1217,6 +1200,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) } } + if (qgetenv("XDG_CURRENT_DESKTOP") == "gamescope") { + installEventFilter(new ToolTipFilter); + } + if (createSetupWizard()) { return; } diff --git a/launcher/AssertHelpers.h b/launcher/AssertHelpers.h new file mode 100644 index 000000000..0b1cdb742 --- /dev/null +++ b/launcher/AssertHelpers.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2025 Octol1ttle + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#if defined(ASSERT_NEVER) +#error ASSERT_NEVER already defined +#else +#define ASSERT_NEVER(cond) (Q_ASSERT((cond) == false), (cond)) +#endif diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 02fc1321a..de647455a 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -26,13 +26,13 @@ set(CORE_SOURCES NullInstance.h MMCZip.h MMCZip.cpp - archive/ArchiveReader.cpp - archive/ArchiveReader.h - archive/ArchiveWriter.cpp - archive/ArchiveWriter.h - archive/ExportToZipTask.cpp + archive/ArchiveReader.cpp + archive/ArchiveReader.h + archive/ArchiveWriter.cpp + archive/ArchiveWriter.h + archive/ExportToZipTask.cpp archive/ExportToZipTask.h - archive/ExtractZipTask.cpp + archive/ExtractZipTask.cpp archive/ExtractZipTask.h StringUtils.h StringUtils.cpp @@ -102,6 +102,9 @@ set(CORE_SOURCES MMCTime.cpp MTPixmapCache.h + + # Assertion helper + AssertHelpers.h ) if (UNIX AND NOT CYGWIN AND NOT APPLE) set(CORE_SOURCES @@ -261,6 +264,8 @@ set(MINECRAFT_SOURCES minecraft/launch/ClaimAccount.h minecraft/launch/CreateGameFolders.cpp minecraft/launch/CreateGameFolders.h + minecraft/launch/EnsureOfflineLibraries.cpp + minecraft/launch/EnsureOfflineLibraries.h minecraft/launch/ModMinecraftJar.cpp minecraft/launch/ModMinecraftJar.h minecraft/launch/ExtractNatives.cpp @@ -346,6 +351,7 @@ set(MINECRAFT_SOURCES minecraft/mod/TexturePackFolderModel.h minecraft/mod/TexturePackFolderModel.cpp minecraft/mod/ShaderPackFolderModel.h + minecraft/mod/ShaderPackFolderModel.cpp minecraft/mod/tasks/ResourceFolderLoadTask.h minecraft/mod/tasks/ResourceFolderLoadTask.cpp minecraft/mod/tasks/LocalModParseTask.h @@ -621,10 +627,10 @@ set(PRISMUPDATER_SOURCES # Zip MMCZip.h MMCZip.cpp - archive/ArchiveReader.cpp - archive/ArchiveReader.h - archive/ArchiveWriter.cpp - archive/ArchiveWriter.h + archive/ArchiveReader.cpp + archive/ArchiveReader.h + archive/ArchiveWriter.cpp + archive/ArchiveWriter.h # Time MMCTime.h @@ -842,6 +848,8 @@ SET(LAUNCHER_SOURCES ui/InstanceWindow.cpp ui/ViewLogWindow.h ui/ViewLogWindow.cpp + ui/ToolTipFilter.h + ui/ToolTipFilter.cpp # FIXME: maybe find a better home for this. FileIgnoreProxy.cpp @@ -1300,6 +1308,19 @@ endif() include(CompilerWarnings) +######## Precompiled Headers ########### + +if(${Launcher_USE_PCH}) + message(STATUS "Using precompiled headers for applicable launcher targets") + set(PRECOMPILED_HEADERS + include/base.pch.hpp + include/qtcore.pch.hpp + include/qtgui.pch.hpp + ) +endif() + +####### Targets ######## + # Add executable add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES}) set_project_warnings(Launcher_logic @@ -1308,6 +1329,11 @@ set_project_warnings(Launcher_logic "${Launcher_GCC_WARNINGS}") target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION) + +if(${Launcher_USE_PCH}) + target_precompile_headers(Launcher_logic PRIVATE ${PRECOMPILED_HEADERS}) +endif() + target_link_libraries(Launcher_logic systeminfo Launcher_murmur2 @@ -1389,6 +1415,11 @@ endif() target_link_libraries(Launcher_logic) add_executable(${Launcher_Name} MACOSX_BUNDLE WIN32 main.cpp ${LAUNCHER_RCS}) + +if(${Launcher_USE_PCH}) + target_precompile_headers(${Launcher_Name} REUSE_FROM Launcher_logic) +endif() + target_link_libraries(${Launcher_Name} Launcher_logic) if(DEFINED Launcher_APP_BINARY_NAME) @@ -1412,14 +1443,19 @@ install(TARGETS ${Launcher_Name} ) # Deploy PDBs -if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) - install(FILES $ DESTINATION ${BINARY_DEST_DIR}) +if(CMAKE_CXX_LINKER_SUPPORTS_PDB) + install(FILES $ DESTINATION ${BINARY_DEST_DIR} OPTIONAL) endif() if(Launcher_BUILD_UPDATER) # Updater add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI}) target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + + if(${Launcher_USE_PCH}) + target_precompile_headers(prism_updater_logic PRIVATE ${PRECOMPILED_HEADERS}) + endif() + target_link_libraries(prism_updater_logic ${ZLIB_LIBRARIES} systeminfo @@ -1440,6 +1476,10 @@ if(Launcher_BUILD_UPDATER) target_sources("${Launcher_Name}_updater" PRIVATE updater/prismupdater/updater.exe.manifest) target_link_libraries("${Launcher_Name}_updater" prism_updater_logic) + if(${Launcher_USE_PCH}) + target_precompile_headers("${Launcher_Name}_updater" REUSE_FROM prism_updater_logic) + endif() + if(DEFINED Launcher_APP_BINARY_NAME) set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater") endif() @@ -1455,8 +1495,8 @@ if(Launcher_BUILD_UPDATER) ) # Deploy PDBs - if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) - install(FILES $ DESTINATION ${BINARY_DEST_DIR}) + if(CMAKE_CXX_LINKER_SUPPORTS_PDB) + install(FILES $ DESTINATION ${BINARY_DEST_DIR} OPTIONAL) endif() endif() @@ -1469,6 +1509,11 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER)) "${Launcher_GCC_WARNINGS}") target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + + if(${Launcher_USE_PCH}) + target_precompile_headers(filelink_logic PRIVATE ${PRECOMPILED_HEADERS}) + endif() + target_link_libraries(filelink_logic systeminfo BuildConfig @@ -1480,8 +1525,12 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER)) ) add_executable("${Launcher_Name}_filelink" WIN32 filelink/filelink_main.cpp) - target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest) + + if(${Launcher_USE_PCH}) + target_precompile_headers("${Launcher_Name}_filelink" REUSE_FROM filelink_logic) + endif() + # HACK: Fix manifest issues with Ninja in release mode (and only release mode) and MSVC # I have no idea why this works or why it's needed. UPDATE THIS IF YOU EDIT THE MANIFEST!!! -@getchoo # Thank you 2018 CMake mailing list thread https://cmake.cmake.narkive.com/LnotZXus/conflicting-msvc-manifests @@ -1506,8 +1555,8 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER)) ) # Deploy PDBs - if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) - install(FILES $ DESTINATION ${BINARY_DEST_DIR}) + if(CMAKE_CXX_LINKER_SUPPORTS_PDB) + install(FILES $ DESTINATION ${BINARY_DEST_DIR} OPTIONAL) endif() endif() @@ -1567,6 +1616,49 @@ if(WIN32 OR (UNIX AND APPLE)) SCRIPT ${QT_DEPLOY_SCRIPT} COMPONENT bundle ) + # FIXME: remove this crap once we stop using msys2 + if(MINGW) + # i've not found a solution better than injecting the config vars like this... + # with install(CODE" for everything everything just breaks + install(CODE " + set(QT_PLUGINS_DIR \"${QT_PLUGINS_DIR}\") + set(QT_LIBS_DIR \"${QT_LIBS_DIR}\") + set(QT_LIBEXECS_DIR \"${QT_LIBEXECS_DIR}\") + set(CMAKE_SYSTEM_LIBRARY_PATH \"${CMAKE_SYSTEM_LIBRARY_PATH}\") + set(CMAKE_INSTALL_PREFIX \"${CMAKE_INSTALL_PREFIX}\") + " + COMPONENT bundle) + + install(CODE [[ + file(GLOB QT_IMAGEFORMAT_DLLS "${QT_PLUGINS_DIR}/imageformats/*.dll") + set(CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL objdump) + file(GET_RUNTIME_DEPENDENCIES + RESOLVED_DEPENDENCIES_VAR imageformatdeps + LIBRARIES ${QT_IMAGEFORMAT_DLLS} + DIRECTORIES + ${CMAKE_SYSTEM_LIBRARY_PATH} + ${QT_PLUGINS_DIR} + ${QT_LIBS_DIR} + ${QT_LIBEXECS_DIR} + PRE_EXCLUDE_REGEXES + "^(api-ms-win|ext-ms)-.*\\.dll$" + # FIXME: Why aren't these caught by the below regex??? + "^azure.*\\.dll$" + "^vcruntime.*\\.dll$" + POST_EXCLUDE_REGEXES + "system32" + ) + foreach(_lib ${imageformatdeps}) + file(INSTALL + DESTINATION ${CMAKE_INSTALL_PREFIX} + TYPE SHARED_LIBRARY + FOLLOW_SYMLINK_CHAIN + FILES ${_lib} + ) + endforeach() + ]] + COMPONENT bundle) + endif() # Add qt.conf - this makes Qt stop looking for things outside the bundle install( @@ -1580,3 +1672,15 @@ if(WIN32 OR (UNIX AND APPLE)) COMPONENT bundle ) endif() + +find_program(CLANG_FORMAT clang-format OPTIONAL) +if(CLANG_FORMAT) + message(STATUS "Creating clang-format target") + add_custom_target( + clang-format + COMMAND ${CLANG_FORMAT} -i --style=file:${CMAKE_SOURCE_DIR}/.clang-format ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${PRISMUPDATER_SOURCES} ${LINKEXE_SOURCES} ${PRECOMPILED_HEADERS} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + ) +else() + message(WARNING "Unable to find `clang-format`. Not creating custom target") +endif() diff --git a/launcher/DesktopServices.cpp b/launcher/DesktopServices.cpp index b926dbca5..841c1399c 100644 --- a/launcher/DesktopServices.cpp +++ b/launcher/DesktopServices.cpp @@ -2,7 +2,6 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 dada513 - * Copyright (C) 2025 Seth Flynn * * 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 @@ -76,15 +75,6 @@ bool isFlatpak() #endif } -bool isSelfContained() -{ -#ifdef Q_OS_LINUX - return QFileInfo(QCoreApplication::applicationFilePath()).fileName().startsWith("ld-linux"); -#else - return false; -#endif -} - bool isSnap() { #ifdef Q_OS_LINUX diff --git a/launcher/DesktopServices.h b/launcher/DesktopServices.h index 5deb25872..6c6208e82 100644 --- a/launcher/DesktopServices.h +++ b/launcher/DesktopServices.h @@ -37,11 +37,6 @@ bool openUrl(const QUrl& url); */ bool isFlatpak(); -/** - * Determine whether the launcher is running in a self-contained Linux bundle - */ -bool isSelfContained(); - /** * Determine whether the launcher is running in a Snap environment */ diff --git a/launcher/FastFileIconProvider.cpp b/launcher/FastFileIconProvider.cpp index f2b6f4425..1dbab27ba 100644 --- a/launcher/FastFileIconProvider.cpp +++ b/launcher/FastFileIconProvider.cpp @@ -44,4 +44,4 @@ QIcon FastFileIconProvider::icon(const QFileInfo& info) const } return QApplication::style()->standardIcon(icon); -} \ No newline at end of file +} diff --git a/launcher/FastFileIconProvider.h b/launcher/FastFileIconProvider.h index 208534044..7799b7879 100644 --- a/launcher/FastFileIconProvider.h +++ b/launcher/FastFileIconProvider.h @@ -23,4 +23,4 @@ class FastFileIconProvider : public QFileIconProvider { public: QIcon icon(const QFileInfo& info) const override; -}; \ No newline at end of file +}; diff --git a/launcher/FileIgnoreProxy.cpp b/launcher/FileIgnoreProxy.cpp index cebe82eda..445c2a881 100644 --- a/launcher/FileIgnoreProxy.cpp +++ b/launcher/FileIgnoreProxy.cpp @@ -266,7 +266,21 @@ bool FileIgnoreProxy::filterAcceptsRow(int sourceRow, const QModelIndex& sourceP bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const { - return m_ignoreFiles.contains(fileInfo.fileName()) || m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath())); + if (m_ignoreFiles.contains(fileInfo.fileName())) { + return true; + } + + for (const auto& suffix : m_ignoreFilesSuffixes) { + if (fileInfo.fileName().endsWith(suffix)) { + return true; + } + } + + if (m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath()))) { + return true; + } + + return false; } bool FileIgnoreProxy::filterFile(const QFileInfo& file) const diff --git a/launcher/FileIgnoreProxy.h b/launcher/FileIgnoreProxy.h index 5184fc354..0f149ecb6 100644 --- a/launcher/FileIgnoreProxy.h +++ b/launcher/FileIgnoreProxy.h @@ -66,6 +66,7 @@ class FileIgnoreProxy : public QSortFilterProxyModel { // list of file names that need to be removed completely from model inline QStringList& ignoreFilesWithName() { return m_ignoreFiles; } + inline QStringList& ignoreFilesWithSuffix() { return m_ignoreFilesSuffixes; } // list of relative paths that need to be removed completely from model inline SeparatorPrefixTree<'/'>& ignoreFilesWithPath() { return m_ignoreFilePaths; } @@ -85,5 +86,6 @@ class FileIgnoreProxy : public QSortFilterProxyModel { const QString m_root; SeparatorPrefixTree<'/'> m_blocked; QStringList m_ignoreFiles; + QStringList m_ignoreFilesSuffixes; SeparatorPrefixTree<'/'> m_ignoreFilePaths; }; diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index ab7c73493..9ca1c5fa6 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -4,7 +4,6 @@ * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 TheKodeToad * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> - * Copyright (C) 2025 Seth Flynn * * 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 @@ -433,7 +432,7 @@ void create_link::make_link_list(const QString& offset) link_file(src, ""); } else { if (m_debug) - qDebug() << "linking recursively:" << src << "to" << dst << ", max_depth:" << m_max_depth; + qDebug().nospace() << "linking recursively: " << src << " to " << dst << ", max_depth: " << m_max_depth; QDir src_dir(src); QDirIterator source_it(src, QDir::Filter::Files | QDir::Filter::Hidden, QDirIterator::Subdirectories); @@ -593,7 +592,7 @@ void create_link::runPrivileged(const QString& offset) } ExternalLinkFileProcess* linkFileProcess = new ExternalLinkFileProcess(serverName, m_useHardLinks, this); - connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [this, gotResults]() { emit finishedPrivileged(gotResults); }); + connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [this, &gotResults]() { emit finishedPrivileged(gotResults); }); connect(linkFileProcess, &ExternalLinkFileProcess::finished, linkFileProcess, &QObject::deleteLater); linkFileProcess->start(); @@ -773,34 +772,6 @@ QString ResolveExecutable(QString path) return pathInfo.absoluteFilePath(); } -std::unique_ptr createProcess(const QString& program, const QStringList& arguments) -{ - qDebug() << "Creating process for" << program; - auto proc = std::unique_ptr(new QProcess()); - -#if defined(Q_OS_LINUX) - if (DesktopServices::isSelfContained()) { - const auto linkerPath = QCoreApplication::applicationFilePath(); - qDebug() << "Wrapping" << program << "with self-contained linker at" << linkerPath; - - QStringList wrappedArguments; - wrappedArguments << "--inhibit-cache" << program; - wrappedArguments += arguments; - - proc->setProgram(linkerPath); - proc->setArguments(wrappedArguments); - } else { - proc->setProgram(program); - proc->setArguments(arguments); - } -#else - proc->setProgram(program); - proc->setArguments(arguments); -#endif - - return proc; -} - /** * Normalize path * @@ -1129,17 +1100,17 @@ QString createShortcut(QString destination, QString target, QStringList args, QS hres = ppf->Save(wsz, TRUE); if (FAILED(hres)) { qWarning() << "IPresistFile->Save() failed"; - qWarning() << "hres = " << hres; + qWarning() << "hres =" << hres; } ppf->Release(); } else { qWarning() << "Failed to query IPersistFile interface from IShellLink instance"; - qWarning() << "hres = " << hres; + qWarning() << "hres =" << hres; } psl->Release(); } else { qWarning() << "Failed to create IShellLink instance"; - qWarning() << "hres = " << hres; + qWarning() << "hres =" << hres; } // go away COM, nobody likes you @@ -1428,14 +1399,14 @@ bool win_ioctl_clone(const std::wstring& src_path, const std::wstring& dst_path, ULONG fs_flags; if (!GetVolumeInformationByHandleW(hSourceFile, nullptr, 0, nullptr, nullptr, &fs_flags, nullptr, 0)) { ec = std::error_code(GetLastError(), std::system_category()); - qDebug() << "Failed to get Filesystem information for " << src_path.c_str(); + qDebug() << "Failed to get Filesystem information for" << src_path.c_str(); CloseHandle(hSourceFile); return false; } if (!(fs_flags & FILE_SUPPORTS_BLOCK_REFCOUNTING)) { SetLastError(ERROR_NOT_CAPABLE); ec = std::error_code(GetLastError(), std::system_category()); - qWarning() << "Filesystem at " << src_path.c_str() << " does not support reflink"; + qWarning() << "Filesystem at" << src_path.c_str() << "does not support reflink"; CloseHandle(hSourceFile); return false; } diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index db8545fd2..f2676b147 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -4,7 +4,6 @@ * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 TheKodeToad * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> - * Copyright (C) 2025 Seth Flynn * * 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 @@ -43,13 +42,11 @@ #include -#include #include #include #include #include #include -#include #include namespace FS { @@ -336,14 +333,6 @@ QString pathTruncate(const QString& path, int depth); */ QString ResolveExecutable(QString path); -/** - * Create a QProcess instance - * - * This wrapper is currently only required for wrapping binaries called in - * self-contained AppImages (like those created by `go-appimage`) - */ -std::unique_ptr createProcess(const QString& program, const QStringList& arguments); - /** * Normalize path * diff --git a/launcher/GZip.cpp b/launcher/GZip.cpp index 29c71c012..dc786e10e 100644 --- a/launcher/GZip.cpp +++ b/launcher/GZip.cpp @@ -215,4 +215,4 @@ QString GZip::readGzFileByBlocks(QFile* source, std::function(FS::PathCombine(m_origInstance->gameRoot(), "saves"), FS::PathCombine(staging_mc_dir, "saves")); - savesCopy->followSymlinks(true); (*savesCopy)(true); setProgress(0, savesCopy->totalCopied()); connect(savesCopy.get(), &FS::copy::fileCopied, [this](QString src) { setProgress(m_progress + 1, m_progressTotal); }); @@ -126,11 +125,11 @@ void InstanceCopyTask::executeTask() return !there_were_errors; } FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath); - folderCopy.followSymlinks(false).matcher(m_matcher); + folderCopy.matcher(m_matcher); folderCopy(true); setProgress(0, folderCopy.totalCopied()); - connect(&folderCopy, &FS::copy::fileCopied, [this](QString src) { setProgress(m_progress + 1, m_progressTotal); }); + connect(&folderCopy, &FS::copy::fileCopied, [this]() { setProgress(m_progress + 1, m_progressTotal); }); return folderCopy(); }); connect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &InstanceCopyTask::copyFinished); @@ -197,4 +196,4 @@ bool InstanceCopyTask::abort() return true; } return false; -} \ No newline at end of file +} diff --git a/launcher/InstanceCreationTask.cpp b/launcher/InstanceCreationTask.cpp index bd3514798..94c229128 100644 --- a/launcher/InstanceCreationTask.cpp +++ b/launcher/InstanceCreationTask.cpp @@ -25,7 +25,7 @@ void InstanceCreationTask::executeTask() qWarning() << "Instance creation failed!"; if (!m_error_message.isEmpty()) { - qWarning() << "Reason: " << m_error_message; + qWarning() << "Reason:" << m_error_message; emitFailed(tr("Error while creating new instance:\n%1").arg(m_error_message)); } else { emitFailed(tr("Error while creating new instance.")); diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 151a8c765..cd168dee2 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -150,22 +150,15 @@ void InstanceImportTask::processZipPack() extractDir.cd("minecraft"); m_modpackType = ModpackType::Technic; stop = true; - } else { - QFileInfo fileInfo(fileName); - if (fileInfo.fileName() == "instance.cfg") { - qDebug() << "MultiMC:" << true; - m_modpackType = ModpackType::MultiMC; - root = cleanPath(fileInfo.path()); - stop = true; - return true; - } - if (fileInfo.fileName() == "manifest.json") { - qDebug() << "Flame:" << true; - m_modpackType = ModpackType::Flame; - root = cleanPath(fileInfo.path()); - stop = true; - return true; - } + } else if (fileName == "manifest.json") { + qDebug() << "Flame:" << true; + m_modpackType = ModpackType::Flame; + stop = true; + } else if (QFileInfo fileInfo(fileName); fileInfo.fileName() == "instance.cfg") { + qDebug() << "MultiMC:" << true; + m_modpackType = ModpackType::MultiMC; + root = cleanPath(fileInfo.path()); + stop = true; } QCoreApplication::processEvents(); return true; @@ -271,7 +264,7 @@ bool installIcon(QString root, QString instIconKey) if (iconList->iconFileExists(instIconKey)) { iconList->deleteIcon(instIconKey); } - iconList->installIcon(importIconPath, instIconKey + ".png"); + iconList->installIcon(importIconPath, instIconKey + "." + QFileInfo(importIconPath).suffix()); return true; } return false; diff --git a/launcher/Json.cpp b/launcher/Json.cpp index dd7287e00..688f9dae7 100644 --- a/launcher/Json.cpp +++ b/launcher/Json.cpp @@ -101,6 +101,21 @@ QJsonArray requireArray(const QJsonDocument& doc, const QString& what) return doc.array(); } +QJsonDocument parseUntilGarbage(const QByteArray& json, QJsonParseError* error, QString* garbage) +{ + auto doc = QJsonDocument::fromJson(json, error); + if (error->error == QJsonParseError::GarbageAtEnd) { + qsizetype offset = error->offset; + QByteArray validJson = json.left(offset); + doc = QJsonDocument::fromJson(validJson, error); + + if (garbage) + *garbage = json.right(json.size() - offset); + } + + return doc; +} + void writeString(QJsonObject& to, const QString& key, const QString& value) { if (!value.isEmpty()) { diff --git a/launcher/Json.h b/launcher/Json.h index 8a994d7bc..7a50af167 100644 --- a/launcher/Json.h +++ b/launcher/Json.h @@ -107,6 +107,9 @@ QJsonArray toJsonArray(const QList& container) ////////////////// READING //////////////////// +// Attempt to parse JSON up until garbage is encountered +QJsonDocument parseUntilGarbage(const QByteArray& json, QJsonParseError* error = nullptr, QString* garbage = nullptr); + /// @throw JsonException template T requireIsType(const QJsonValue& value, const QString& what = "Value"); diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index cbea045fc..183afefaa 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -220,7 +220,10 @@ void LaunchController::login() if (tries > 0 && tries % 3 == 0) { auto result = QMessageBox::question(m_parentWidget, tr("Continue launch?"), - tr("It looks like we couldn't launch after %1 tries. Do you want to continue trying?").arg(tries)); + tr("It looks like we couldn't launch after %1 tries. Usually this can be fixed by logging out and " + "logging back in your Microsoft account. If that doesn't work, Minecraft authentication servers " + "may be having an outage or you may need a VPN in your region. Do you want to continue trying?") + .arg(tries)); if (result == QMessageBox::No) { emitAborted(); @@ -266,7 +269,7 @@ void LaunchController::login() } /* fallthrough */ case AccountState::Online: { - if (!m_session->wants_online) { + if (!m_session->wants_online && m_accountToUse->accountType() != AccountType::Offline) { // we ask the user for a player name bool ok = false; QString name; diff --git a/launcher/Launcher.in b/launcher/Launcher.in index 706d7022b..0e84bdd9d 100755 --- a/launcher/Launcher.in +++ b/launcher/Launcher.in @@ -18,89 +18,19 @@ LAUNCHER_NAME=@Launcher_APP_BINARY_NAME@ LAUNCHER_DIR="$(dirname "$(readlink -f "$0")")" echo "Launcher Dir: ${LAUNCHER_DIR}" -# Set up env. -# Pass our custom variables separately so that the launcher can remove them for child processes -export LAUNCHER_LD_LIBRARY_PATH="${LAUNCHER_DIR}/lib@LIB_SUFFIX@" -export LAUNCHER_LD_PRELOAD="" -export LAUNCHER_QT_PLUGIN_PATH="${LAUNCHER_DIR}/plugins" -export LAUNCHER_QT_FONTPATH="${LAUNCHER_DIR}/fonts" +# Makes the launcher use portals for file picking +export QT_QPA_PLATFORMTHEME=xdgdesktopportal -export LD_LIBRARY_PATH="$LAUNCHER_LD_LIBRARY_PATH:$LD_LIBRARY_PATH" -export LD_PRELOAD="$LAUNCHER_LD_PRELOAD:$LD_PRELOAD" -export QT_PLUGIN_PATH="$LAUNCHER_QT_PLUGIN_PATH:$QT_PLUGIN_PATH" -export QT_FONTPATH="$LAUNCHER_QT_FONTPATH:$QT_FONTPATH" +# Just to be sure... +chmod +x "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}" -# Detect missing dependencies... -DEPS_LIST=`ldd "${LAUNCHER_DIR}"/plugins/*/*.so 2>/dev/null | grep "not found" | sort -u | awk -vORS=", " '{ print $1 }'` -if [ "x$DEPS_LIST" = "x" ]; then - # We have all our dependencies. Run the launcher. - echo "No missing dependencies found." +ARGS=("${LAUNCHER_DIR}/${LAUNCHER_NAME}" "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}") - # Just to be sure... - chmod +x "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}" - - ARGS=("${LAUNCHER_DIR}/${LAUNCHER_NAME}" "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}") - - if [ -f portable.txt ]; then - ARGS+=("-d" "${LAUNCHER_DIR}") - fi - - ARGS+=("$@") - - # Run the launcher - exec -a "${ARGS[@]}" - - # Run the launcher in valgrind - # valgrind --log-file="valgrind.log" --leak-check=full --track-origins=yes "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}" -d "${LAUNCHER_DIR}" "$@" - - # Run the launcher with callgrind, delay instrumentation - # valgrind --log-file="valgrind.log" --tool=callgrind --instr-atstart=no "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}" -d "${LAUNCHER_DIR}" "$@" - # use callgrind_control -i on/off to profile actions - - # Exit with launcher's exit code. - # exit $? -else - # apt - if which apt-file &>/dev/null; then - LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u` - COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do apt-file -l search $LIBRARY; done` - COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'` - INSTALL_CMD="sudo apt-get install $COMMAND_LIBS" - # pacman - elif which pkgfile &>/dev/null; then - LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u` - COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do pkgfile $LIBRARY; done` - COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'` - INSTALL_CMD="sudo pacman -S $COMMAND_LIBS" - # dnf - elif which dnf &>/dev/null; then - LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u` - COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do dnf whatprovides -q $LIBRARY; done` - COMMAND_LIBS=`echo "$COMMAND_LIBS" | grep -v 'Repo' | sort -u | awk -vORS=" " '{ print $1 }'` - INSTALL_CMD="sudo dnf install $COMMAND_LIBS" - # yum - elif which yum &>/dev/null; then - LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u` - COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do yum whatprovides $LIBRARY; done` - COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'` - INSTALL_CMD="sudo yum install $COMMAND_LIBS" - # zypper - elif which zypper &>/dev/null; then - LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u` - COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do zypper wp $LIBRARY; done` - COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'` - INSTALL_CMD="sudo zypper install $COMMAND_LIBS" - # emerge - elif which pfl &>/dev/null; then - LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u` - COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do pfl $LIBRARY; done` - COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'` - INSTALL_CMD="sudo emerge $COMMAND_LIBS" - fi - - MESSAGE="Error: The launcher is missing the following libraries that it needs to work correctly:\n\t${DEPS_LIST}\nPlease install them from your distribution's package manager." - MESSAGE="$MESSAGE\n\nHint (please apply common sense): $INSTALL_CMD\n" - - printerror "$MESSAGE" - exit 1 +if [ -f portable.txt ]; then + ARGS+=("-d" "${LAUNCHER_DIR}") fi + +ARGS+=("$@") + +# Run the launcher +exec -a "${ARGS[@]}" diff --git a/launcher/MMCTime.cpp b/launcher/MMCTime.cpp index 1765fd844..252e6ac57 100644 --- a/launcher/MMCTime.cpp +++ b/launcher/MMCTime.cpp @@ -16,7 +16,6 @@ */ #include -#include #include #include @@ -99,4 +98,4 @@ QString Time::humanReadableDuration(double duration, int precision) os.flush(); return outStr; -} \ No newline at end of file +} diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 8e4e433ed..5e962d12a 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -56,18 +56,18 @@ bool mergeZipFiles(ArchiveWriter& into, QFileInfo from, QSet& contained return r.parse([&into, &contained, &filter, from](ArchiveReader::File* f) { auto filename = f->filename(); if (filter && !filter(filename)) { - qDebug() << "Skipping file " << filename << " from " << from.fileName() << " - filtered"; + qDebug() << "Skipping file" << filename << "from" << from.fileName() << "- filtered"; f->skip(); return true; } if (contained.contains(filename)) { - qDebug() << "Skipping already contained file " << filename << " from " << from.fileName(); + qDebug() << "Skipping already contained file" << filename << "from" << from.fileName(); f->skip(); return true; } contained.insert(filename); if (!into.addFile(f)) { - qCritical() << "Failed to copy data of " << filename << " into the jar"; + qCritical() << "Failed to copy data of" << filename << "into the jar"; return false; } return true; @@ -149,7 +149,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QListfileinfo().fileName() << "to the jar."; return false; } - qDebug() << "Adding folder " << filename.fileName() << " from " << filename.absoluteFilePath(); + qDebug() << "Adding folder" << filename.fileName() << "from" << filename.absoluteFilePath(); } else { // Make sure we do not continue launching when something is missing or undefined... zipOut.close(); @@ -321,7 +321,7 @@ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, Q for (const auto& e : entries) { if (excludeFilter && excludeFilter(e)) { QString relativeFilePath = rootDirectory.relativeFilePath(e.absoluteFilePath()); - qDebug() << "Skipping file " << relativeFilePath; + qDebug() << "Skipping file" << relativeFilePath; continue; } diff --git a/launcher/Markdown.cpp b/launcher/Markdown.cpp index 426067bf6..6f6d34828 100644 --- a/launcher/Markdown.cpp +++ b/launcher/Markdown.cpp @@ -28,4 +28,4 @@ QString markdownToHTML(const QString& markdown) free(buffer); return htmlStr; -} \ No newline at end of file +} diff --git a/launcher/Markdown.h b/launcher/Markdown.h index f91a016bd..57a2e5437 100644 --- a/launcher/Markdown.h +++ b/launcher/Markdown.h @@ -21,4 +21,4 @@ #include #include -QString markdownToHTML(const QString& markdown); \ No newline at end of file +QString markdownToHTML(const QString& markdown); diff --git a/launcher/MessageLevel.h b/launcher/MessageLevel.h index 006184780..cff098664 100644 --- a/launcher/MessageLevel.h +++ b/launcher/MessageLevel.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include diff --git a/launcher/PSaveFile.h b/launcher/PSaveFile.h index ba6154ad8..67a57a1a2 100644 --- a/launcher/PSaveFile.h +++ b/launcher/PSaveFile.h @@ -68,4 +68,4 @@ class PSaveFile : public QSaveFile { }; #else #define PSaveFile QSaveFile -#endif \ No newline at end of file +#endif diff --git a/launcher/QVariantUtils.h b/launcher/QVariantUtils.h index 91f2ad29c..23fe82573 100644 --- a/launcher/QVariantUtils.h +++ b/launcher/QVariantUtils.h @@ -66,4 +66,4 @@ inline QVariant fromList(QList val) return variantList; } -} // namespace QVariantUtils \ No newline at end of file +} // namespace QVariantUtils diff --git a/launcher/ResourceDownloadTask.cpp b/launcher/ResourceDownloadTask.cpp index 875a7f02d..2919ddd19 100644 --- a/launcher/ResourceDownloadTask.cpp +++ b/launcher/ResourceDownloadTask.cpp @@ -103,8 +103,8 @@ void ResourceDownloadTask::downloadSucceeded() void ResourceDownloadTask::downloadFailed(QString reason) { - emitFailed(reason); m_filesNetJob.reset(); + emitFailed(reason); } void ResourceDownloadTask::downloadProgressChanged(qint64 current, qint64 total) diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index b9e875482..a79cfb5c5 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -35,7 +35,6 @@ */ #include "StringUtils.h" -#include #include #include @@ -232,4 +231,4 @@ QString StringUtils::htmlListPatch(QString htmlStr) pos = htmlStr.indexOf(s_ulMatcher, pos); } return htmlStr; -} \ No newline at end of file +} diff --git a/launcher/archive/ArchiveReader.cpp b/launcher/archive/ArchiveReader.cpp index d29963c6d..1f87d8237 100644 --- a/launcher/archive/ArchiveReader.cpp +++ b/launcher/archive/ArchiveReader.cpp @@ -57,7 +57,7 @@ QByteArray ArchiveReader::File::readAll(int* outStatus) data.append(static_cast(buff), static_cast(size)); } if (status != ARCHIVE_EOF && status != ARCHIVE_OK) { - qWarning() << "libarchive read error: " << archive_error_string(m_archive.get()); + qWarning() << "libarchive read error:" << archive_error_string(m_archive.get()); } if (outStatus) { *outStatus = status; @@ -151,7 +151,7 @@ bool ArchiveReader::File::writeFile(archive* out, QString targetFileName, bool n auto r = archive_write_finish_entry(out); if (r < ARCHIVE_OK) qCritical() << "Failed to finish writing entry:" << archive_error_string(out); - return (r > ARCHIVE_WARN); + return (r >= ARCHIVE_WARN); } bool ArchiveReader::parse(std::function doStuff) @@ -180,6 +180,7 @@ bool ArchiveReader::parse(std::function doStuff) archive_read_close(a); return true; } + bool ArchiveReader::parse(std::function doStuff) { return parse([doStuff](File* f, bool&) { return doStuff(f); }); diff --git a/launcher/archive/ArchiveReader.h b/launcher/archive/ArchiveReader.h index 379006278..aaeba6095 100644 --- a/launcher/archive/ArchiveReader.h +++ b/launcher/archive/ArchiveReader.h @@ -69,4 +69,4 @@ class ArchiveReader { QStringList m_fileNames = {}; }; -} // namespace MMCZip \ No newline at end of file +} // namespace MMCZip diff --git a/launcher/archive/ArchiveWriter.cpp b/launcher/archive/ArchiveWriter.cpp index a546f2bb7..8436e5486 100644 --- a/launcher/archive/ArchiveWriter.cpp +++ b/launcher/archive/ArchiveWriter.cpp @@ -167,14 +167,14 @@ bool ArchiveWriter::addFile(const QString& fileName, const QString& fileDest) } if (archive_write_header(m_archive, entry) != ARCHIVE_OK) { - qCritical() << "Failed to write header for: " << fileDest << "-" << archive_error_string(m_archive); + qCritical() << "Failed to write header for:" << fileDest << "-" << archive_error_string(m_archive); return false; } if (fileInfo.isFile() && !fileInfo.isSymLink()) { QFile file(fileInfo.absoluteFilePath()); if (!file.open(QIODevice::ReadOnly)) { - qCritical() << "Failed to open file: " << fileInfo.filePath(); + qCritical() << "Failed to open file:" << fileInfo.filePath(); return false; } @@ -185,12 +185,12 @@ bool ArchiveWriter::addFile(const QString& fileName, const QString& fileDest) while (!file.atEnd()) { auto bytesRead = file.read(buffer.data(), chunkSize); if (bytesRead < 0) { - qCritical() << "Read error in file: " << fileInfo.filePath(); + qCritical() << "Read error in file:" << fileInfo.filePath(); return false; } if (archive_write_data(m_archive, buffer.constData(), bytesRead) < 0) { - qCritical() << "Write error in archive for: " << fileDest; + qCritical() << "Write error in archive for:" << fileDest; return false; } } @@ -216,12 +216,12 @@ bool ArchiveWriter::addFile(const QString& fileDest, const QByteArray& data) archive_entry_set_size(entry, data.size()); if (archive_write_header(m_archive, entry) != ARCHIVE_OK) { - qCritical() << "Failed to write header for: " << fileDest << "-" << archive_error_string(m_archive); + qCritical() << "Failed to write header for:" << fileDest << "-" << archive_error_string(m_archive); return false; } if (archive_write_data(m_archive, data.constData(), data.size()) < 0) { - qCritical() << "Write error in archive for: " << fileDest << "-" << archive_error_string(m_archive); + qCritical() << "Write error in archive for:" << fileDest << "-" << archive_error_string(m_archive); return false; } return true; diff --git a/launcher/archive/ArchiveWriter.h b/launcher/archive/ArchiveWriter.h index 807bb297c..50858b517 100644 --- a/launcher/archive/ArchiveWriter.h +++ b/launcher/archive/ArchiveWriter.h @@ -43,4 +43,4 @@ class ArchiveWriter { QString m_filename; QString m_format = "zip"; }; -} // namespace MMCZip \ No newline at end of file +} // namespace MMCZip diff --git a/launcher/archive/ExportToZipTask.cpp b/launcher/archive/ExportToZipTask.cpp index 9df9539be..bd3bc9032 100644 --- a/launcher/archive/ExportToZipTask.cpp +++ b/launcher/archive/ExportToZipTask.cpp @@ -97,4 +97,4 @@ bool ExportToZipTask::abort() } return false; } -} // namespace MMCZip \ No newline at end of file +} // namespace MMCZip diff --git a/launcher/archive/ExportToZipTask.h b/launcher/archive/ExportToZipTask.h index 21ad1db48..0c8329c93 100644 --- a/launcher/archive/ExportToZipTask.h +++ b/launcher/archive/ExportToZipTask.h @@ -69,4 +69,4 @@ class ExportToZipTask : public Task { QFuture m_buildZipFuture; QFutureWatcher m_buildZipWatcher; }; -} // namespace MMCZip \ No newline at end of file +} // namespace MMCZip diff --git a/launcher/archive/ExtractZipTask.cpp b/launcher/archive/ExtractZipTask.cpp index b958640c9..acbcd39d5 100644 --- a/launcher/archive/ExtractZipTask.cpp +++ b/launcher/archive/ExtractZipTask.cpp @@ -132,4 +132,4 @@ bool ExtractZipTask::abort() return false; } -} // namespace MMCZip \ No newline at end of file +} // namespace MMCZip diff --git a/launcher/archive/ExtractZipTask.h b/launcher/archive/ExtractZipTask.h index 284d873fe..03c391aee 100644 --- a/launcher/archive/ExtractZipTask.h +++ b/launcher/archive/ExtractZipTask.h @@ -50,4 +50,4 @@ class ExtractZipTask : public Task { QFuture m_zipFuture; QFutureWatcher m_zipWatcher; }; -} // namespace MMCZip \ No newline at end of file +} // namespace MMCZip diff --git a/launcher/filelink/FileLink.cpp b/launcher/filelink/FileLink.cpp index 1494fa8cc..343f31eaa 100644 --- a/launcher/filelink/FileLink.cpp +++ b/launcher/filelink/FileLink.cpp @@ -115,7 +115,7 @@ void FileLinkApp::joinServer(QString server) qDebug() << ("The connection was closed by the peer. "); break; default: - qDebug() << "The following error occurred: " << socket.errorString(); + qDebug() << "The following error occurred:" << socket.errorString(); } }); diff --git a/launcher/icons/IconList.cpp b/launcher/icons/IconList.cpp index fb80f89da..ad48665d4 100644 --- a/launcher/icons/IconList.cpp +++ b/launcher/icons/IconList.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include "icons/IconUtils.h" @@ -146,8 +147,7 @@ void IconList::directoryChanged(const QString& path) { QDir newDir(path); if (m_dir.absolutePath() != newDir.absolutePath()) { - if (!path.startsWith(m_dir.absolutePath())) - m_dir.setPath(path); + m_dir.setPath(path); m_dir.refresh(); if (m_isWatching) stopWatching(); @@ -169,7 +169,7 @@ void IconList::directoryChanged(const QString& path) QSet toAdd = newSet - currentSet; for (const QString& removedPath : toRemove) { - qDebug() << "Removing icon " << removedPath; + qDebug() << "Removing icon" << removedPath; QFileInfo removedFile(removedPath); QString relativePath = m_dir.relativeFilePath(removedFile.absoluteFilePath()); QString key = QFileInfo(relativePath).completeBaseName(); @@ -191,7 +191,7 @@ void IconList::directoryChanged(const QString& path) } for (const QString& addedPath : toAdd) { - qDebug() << "Adding icon " << addedPath; + qDebug() << "Adding icon" << addedPath; QFileInfo addfile(addedPath); QString relativePath = m_dir.relativeFilePath(addfile.absoluteFilePath()); @@ -209,7 +209,7 @@ void IconList::directoryChanged(const QString& path) void IconList::fileChanged(const QString& path) { - qDebug() << "Checking icon " << path; + qDebug() << "Checking icon" << path; QFileInfo checkfile(path); if (!checkfile.exists()) return; @@ -217,7 +217,13 @@ void IconList::fileChanged(const QString& path) int idx = getIconIndex(key); if (idx == -1) return; - QIcon icon(path); + QIcon icon; + // special handling for jpg and jpeg to go through pixmap to keep the size constant + if (path.endsWith(".jpg") || path.endsWith(".jpeg")) { + icon.addPixmap(QPixmap(path)); + } else { + icon.addFile(path); + } if (icon.availableSizes().empty()) return; @@ -240,9 +246,9 @@ void IconList::startWatching() FS::ensureFolderPathExists(abs_path); m_isWatching = addPathRecursively(abs_path); if (m_isWatching) { - qDebug() << "Started watching " << abs_path; + qDebug() << "Started watching" << abs_path; } else { - qDebug() << "Failed to start watching " << abs_path; + qDebug() << "Failed to start watching" << abs_path; } } @@ -395,7 +401,14 @@ bool IconList::addThemeIcon(const QString& key) bool IconList::addIcon(const QString& key, const QString& name, const QString& path, const IconType type) { // replace the icon even? is the input valid? - QIcon icon(path); + QIcon icon; + // special handling for jpg and jpeg to go through pixmap to keep the size constant + if (path.endsWith(".jpg") || path.endsWith(".jpeg")) { + icon.addPixmap(QPixmap(path)); + } else { + icon.addFile(path); + } + if (icon.isNull()) return false; auto iter = m_nameIndex.find(key); @@ -474,4 +487,4 @@ QString IconList::iconDirectory(const QString& key) const } } return getDirectory(); -} \ No newline at end of file +} diff --git a/launcher/include/base.pch.hpp b/launcher/include/base.pch.hpp new file mode 100644 index 000000000..c858857f9 --- /dev/null +++ b/launcher/include/base.pch.hpp @@ -0,0 +1,17 @@ +#pragma once +#ifndef PRISM_PRECOMPILED_BASE_HEADERS_H +#define PRISM_PRECOMPILED_BASE_HEADERS_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#endif // PRISM_PRECOMPILED_BASE_HEADERS_H diff --git a/launcher/include/qtcore.pch.hpp b/launcher/include/qtcore.pch.hpp new file mode 100644 index 000000000..d7c24ddb6 --- /dev/null +++ b/launcher/include/qtcore.pch.hpp @@ -0,0 +1,59 @@ +#pragma once +#ifndef PRISM_PRECOMPILED_QTCORE_HEADERS_H +#define PRISM_PRECOMPILED_QTCORE_HEADERS_H + +#include +#include +#include +#include + +#include + +#include + +// collections +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include + +#include +#include +#include + +#endif // PRISM_PRECOMPILED_QTCORE_HEADERS_H diff --git a/launcher/include/qtgui.pch.hpp b/launcher/include/qtgui.pch.hpp new file mode 100644 index 000000000..fb57cb33a --- /dev/null +++ b/launcher/include/qtgui.pch.hpp @@ -0,0 +1,47 @@ +#pragma once +#ifndef PRISM_PRECOMPILED_QTGUI_HEADERS_H +#define PRISM_PRECOMPILED_QTGUI_HEADERS_H + +#include + +#include + +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#endif // PRISM_PRECOMPILED_GUI_HEADERS_H diff --git a/launcher/java/JavaMetadata.h b/launcher/java/JavaMetadata.h index 2e569ee39..0757a6935 100644 --- a/launcher/java/JavaMetadata.h +++ b/launcher/java/JavaMetadata.h @@ -61,4 +61,4 @@ DownloadType parseDownloadType(QString javaDownload); QString downloadTypeToString(DownloadType javaDownload); MetadataPtr parseJavaMeta(const QJsonObject& libObj); -} // namespace Java \ No newline at end of file +} // namespace Java diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index 4f7a0ae3f..25fe1caa8 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -483,7 +483,8 @@ QList JavaUtils::FindJavaPaths() QString asdfDataDir = qEnvironmentVariable("ASDF_DATA_DIR", FS::PathCombine(home, ".asdf")); scanJavaDirs(FS::PathCombine(asdfDataDir, "installs/java")); // javas downloaded by gradle (toolchains) - scanJavaDirs(FS::PathCombine(home, ".gradle/jdks")); + QString gradleUserHome = qEnvironmentVariable("GRADLE_USER_HOME", FS::PathCombine(home, ".gradle")); + scanJavaDirs(FS::PathCombine(gradleUserHome, "jdks")); javas.append(getMinecraftJavaBundle()); javas.append(getPrismJavaBundle()); diff --git a/launcher/java/download/ArchiveDownloadTask.cpp b/launcher/java/download/ArchiveDownloadTask.cpp index 92357930b..c60908cec 100644 --- a/launcher/java/download/ArchiveDownloadTask.cpp +++ b/launcher/java/download/ArchiveDownloadTask.cpp @@ -114,4 +114,4 @@ bool ArchiveDownloadTask::abort() aborted = m_task->abort(); return aborted; }; -} // namespace Java \ No newline at end of file +} // namespace Java diff --git a/launcher/java/download/ArchiveDownloadTask.h b/launcher/java/download/ArchiveDownloadTask.h index 4cd919543..cfcdf9dcf 100644 --- a/launcher/java/download/ArchiveDownloadTask.h +++ b/launcher/java/download/ArchiveDownloadTask.h @@ -42,4 +42,4 @@ class ArchiveDownloadTask : public Task { QString m_checksum_hash; Task::Ptr m_task; }; -} // namespace Java \ No newline at end of file +} // namespace Java diff --git a/launcher/java/download/ManifestDownloadTask.cpp b/launcher/java/download/ManifestDownloadTask.cpp index 04d28a5cc..28c6a1831 100644 --- a/launcher/java/download/ManifestDownloadTask.cpp +++ b/launcher/java/download/ManifestDownloadTask.cpp @@ -61,7 +61,7 @@ void ManifestDownloadTask::executeTask() QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*files, &parse_error); if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response at " << parse_error.offset << ". Reason: " << parse_error.errorString(); + qWarning() << "Error while parsing JSON response at" << parse_error.offset << "reason:" << parse_error.errorString(); qWarning() << *files; emitFailed(parse_error.errorString()); return; diff --git a/launcher/java/download/ManifestDownloadTask.h b/launcher/java/download/ManifestDownloadTask.h index 0f65b343c..e68c8236f 100644 --- a/launcher/java/download/ManifestDownloadTask.h +++ b/launcher/java/download/ManifestDownloadTask.h @@ -43,4 +43,4 @@ class ManifestDownloadTask : public Task { QString m_checksum_hash; Task::Ptr m_task; }; -} // namespace Java \ No newline at end of file +} // namespace Java diff --git a/launcher/java/download/SymlinkTask.cpp b/launcher/java/download/SymlinkTask.cpp index 843c7caa9..9bbd50c63 100644 --- a/launcher/java/download/SymlinkTask.cpp +++ b/launcher/java/download/SymlinkTask.cpp @@ -78,4 +78,4 @@ void SymlinkTask::executeTask() } } -} // namespace Java \ No newline at end of file +} // namespace Java diff --git a/launcher/java/download/SymlinkTask.h b/launcher/java/download/SymlinkTask.h index 88cb20dd7..e38323eae 100644 --- a/launcher/java/download/SymlinkTask.h +++ b/launcher/java/download/SymlinkTask.h @@ -33,4 +33,4 @@ class SymlinkTask : public Task { QString m_path; Task::Ptr m_task; }; -} // namespace Java \ No newline at end of file +} // namespace Java diff --git a/launcher/launch/LaunchTask.cpp b/launcher/launch/LaunchTask.cpp index 9985e0f4f..eb1a8b37a 100644 --- a/launcher/launch/LaunchTask.cpp +++ b/launcher/launch/LaunchTask.cpp @@ -76,6 +76,7 @@ void LaunchTask::executeTask() if (!m_steps.size()) { state = LaunchTask::Finished; emitSucceeded(); + return; } state = LaunchTask::Running; onStepFinished(); diff --git a/launcher/launch/steps/LookupServerAddress.cpp b/launcher/launch/steps/LookupServerAddress.cpp index 4b67b3092..fdd9fc545 100644 --- a/launcher/launch/steps/LookupServerAddress.cpp +++ b/launcher/launch/steps/LookupServerAddress.cpp @@ -87,6 +87,6 @@ void LookupServerAddress::resolve(const QString& address, quint16 port) m_output->address = address; m_output->port = port; - emitSucceeded(); m_dnsLookup->deleteLater(); + emitSucceeded(); } diff --git a/launcher/logs/AnonymizeLog.cpp b/launcher/logs/AnonymizeLog.cpp index e5021a616..b808b35a3 100644 --- a/launcher/logs/AnonymizeLog.cpp +++ b/launcher/logs/AnonymizeLog.cpp @@ -65,4 +65,4 @@ void anonymizeLog(QString& log) for (auto rule : anonymizeRules) { log.replace(rule.reg, rule.with); } -} \ No newline at end of file +} diff --git a/launcher/logs/AnonymizeLog.h b/launcher/logs/AnonymizeLog.h index 2409ecee7..215d1e468 100644 --- a/launcher/logs/AnonymizeLog.h +++ b/launcher/logs/AnonymizeLog.h @@ -37,4 +37,4 @@ #include -void anonymizeLog(QString& log); \ No newline at end of file +void anonymizeLog(QString& log); diff --git a/launcher/macsandbox/SecurityBookmarkFileAccess.h b/launcher/macsandbox/SecurityBookmarkFileAccess.h index 5bddf0e31..69b344af9 100644 --- a/launcher/macsandbox/SecurityBookmarkFileAccess.h +++ b/launcher/macsandbox/SecurityBookmarkFileAccess.h @@ -42,7 +42,8 @@ class SecurityBookmarkFileAccess { bool m_readOnly; NSURL* securityScopedBookmarkToNSURL(QByteArray& bookmark, bool& isStale); -public: + + public: /// \param readOnly A boolean indicating whether the bookmark should be read-only. SecurityBookmarkFileAccess(bool readOnly = false); ~SecurityBookmarkFileAccess(); @@ -86,4 +87,4 @@ public: bool isAccessingPath(const QString& path); }; -#endif //FILEACCESS_H +#endif // FILEACCESS_H diff --git a/launcher/minecraft/Component.cpp b/launcher/minecraft/Component.cpp index 5f114e942..6a8bb27c0 100644 --- a/launcher/minecraft/Component.cpp +++ b/launcher/minecraft/Component.cpp @@ -479,6 +479,7 @@ void Component::clearUpdateAction() QDebug operator<<(QDebug d, const Component& comp) { - d << "Component(" << comp.m_uid << " : " << comp.m_cachedVersion << ")"; + QDebugStateSaver saver(d); + d.nospace() << "Component(" << comp.m_uid << " : " << comp.m_cachedVersion << ")"; return d; } diff --git a/launcher/minecraft/ComponentUpdateTask.cpp b/launcher/minecraft/ComponentUpdateTask.cpp index 56db20205..bdfdcfbcc 100644 --- a/launcher/minecraft/ComponentUpdateTask.cpp +++ b/launcher/minecraft/ComponentUpdateTask.cpp @@ -521,7 +521,7 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly) // change a version of something that exists for (auto& change : toChange) { // FIXME: this should not work directly with the component list - qCDebug(instanceProfileResolveC) << "Setting version of " << change.uid << "to" << change.equalsVersion; + qCDebug(instanceProfileResolveC) << "Setting version of" << change.uid << "to" << change.equalsVersion; auto component = componentIndex[change.uid]; component->setVersion(change.equalsVersion); } @@ -744,7 +744,7 @@ void ComponentUpdateTask::remoteLoadFailed(size_t taskIndex, const QString& msg) qCWarning(instanceProfileResolveC) << "Got multiple results from remote load task" << taskIndex; return; } - qCDebug(instanceProfileResolveC) << "Remote task" << taskIndex << "failed: " << msg; + qCDebug(instanceProfileResolveC) << "Remote task" << taskIndex << "failed:" << msg; d->remoteLoadSuccessful = false; taskSlot.succeeded = false; taskSlot.finished = true; @@ -773,8 +773,9 @@ void ComponentUpdateTask::checkIfAllFinished() .arg(component->getName(), component->m_version)); } } + d->remoteLoadStatusList.clear(); + auto allErrors = allErrorsList.join("\n"); emitFailed(tr("Component metadata update task failed while downloading from remote server:\n%1").arg(allErrors)); - d->remoteLoadStatusList.clear(); } } diff --git a/launcher/minecraft/Logging.cpp b/launcher/minecraft/Logging.cpp index 92596de3e..8b6304205 100644 --- a/launcher/minecraft/Logging.cpp +++ b/launcher/minecraft/Logging.cpp @@ -19,7 +19,6 @@ */ #include "minecraft/Logging.h" -#include Q_LOGGING_CATEGORY(instanceProfileC, "launcher.instance.profile") Q_LOGGING_CATEGORY(instanceProfileResolveC, "launcher.instance.profile.resolve") diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index da57e9354..f8924579a 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -40,13 +40,6 @@ #include "BuildConfig.h" #include "Json.h" #include "QObjectPtr.h" -#include "minecraft/launch/AutoInstallJava.h" -#include "minecraft/launch/CreateGameFolders.h" -#include "minecraft/launch/ExtractNatives.h" -#include "minecraft/launch/PrintInstanceInfo.h" -#include "minecraft/update/AssetUpdateTask.h" -#include "minecraft/update/FMLLibrariesTask.h" -#include "minecraft/update/LibrariesTask.h" #include "settings/Setting.h" #include "settings/SettingsObject.h" @@ -63,13 +56,23 @@ #include "launch/steps/QuitAfterGameStop.h" #include "launch/steps/TextPrint.h" +#include "minecraft/launch/AutoInstallJava.h" #include "minecraft/launch/ClaimAccount.h" +#include "minecraft/launch/CreateGameFolders.h" +#include "minecraft/launch/EnsureOfflineLibraries.h" +#include "minecraft/launch/ExtractNatives.h" #include "minecraft/launch/LauncherPartLaunch.h" #include "minecraft/launch/ModMinecraftJar.h" +#include "minecraft/launch/PrintInstanceInfo.h" #include "minecraft/launch/ReconstructAssets.h" #include "minecraft/launch/ScanModFolders.h" #include "minecraft/launch/VerifyJavaInstall.h" +#include "minecraft/update/AssetUpdateTask.h" +#include "minecraft/update/FMLLibrariesTask.h" +#include "minecraft/update/FoldersTask.h" +#include "minecraft/update/LibrariesTask.h" + #include "java/JavaUtils.h" #include "icons/IconList.h" @@ -84,13 +87,13 @@ #include "AssetsUtils.h" #include "MinecraftLoadAndCheck.h" #include "PackProfile.h" -#include "minecraft/update/FoldersTask.h" #include "tools/BaseProfiler.h" #include #include #include +#include #include #ifdef Q_OS_LINUX @@ -483,7 +486,7 @@ QStringList MinecraftInstance::getNativeJars() return nativeJars; } -static QString replaceTokensIn(const QString &text, const QMap &with) +static QString replaceTokensIn(const QString& text, const QMap& with) { // TODO: does this still work?? QString result; @@ -505,7 +508,6 @@ static QString replaceTokensIn(const QString &text, const QMap return result; } - QStringList MinecraftInstance::extraArguments() { auto list = BaseInstance::extraArguments(); @@ -520,7 +522,7 @@ QStringList MinecraftInstance::extraArguments() if (!addn.isEmpty()) { QMap tokenMapping = makeProfileVarMapping(m_components->getProfile()); - for (const QString &item : addn) { + for (const QString& item : addn) { list.append(replaceTokensIn(item, tokenMapping)); } } @@ -588,6 +590,16 @@ QStringList MinecraftInstance::javaArguments() "minecraft.exe.heapdump"); #endif + // LWJGL2 reads `LWJGL_DISABLE_XRANDR` to force disable xrandr usage and fall back to xf86videomode. + // It *SHOULD* check for the executable to exist before trying to use it for queries but it doesnt, + // so WE can and force disable xrandr if it is not available. +#ifdef Q_OS_LINUX + // LWJGL2 is "org.lwjgl" LWJGL3 is "org.lwjgl3" + if (m_components->getComponent("org.lwjgl") != nullptr && QStandardPaths::findExecutable("xrandr").isEmpty()) { + args << QString("-DLWJGL_DISABLE_XRANDR=true"); + } +#endif + int min = settings()->get("MinMemAlloc").toInt(); int max = settings()->get("MaxMemAlloc").toInt(); if (min < max) { @@ -753,7 +765,6 @@ QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, Mine } } - QMap tokenMapping = makeProfileVarMapping(profile); // yggdrasil! @@ -910,21 +921,13 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr out << "Libraries:"; QStringList jars, nativeJars; profile->getLibraryFiles(runtimeContext(), jars, nativeJars, getLocalLibraryPath(), binRoot()); - auto printLibFile = [&out](const QString& path) { - QFileInfo info(path); - if (info.exists()) { - out << " " + path; - } else { - out << " " + path + " (missing)"; - } - }; for (auto file : jars) { - printLibFile(file); + out << " " + file; } out << ""; out << "Native libraries:"; for (auto file : nativeJars) { - printLibFile(file); + out << " " + file; } out << ""; } @@ -1163,6 +1166,8 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt for (auto t : createUpdateTask()) { process->appendStep(makeShared(pptr, t)); } + } else { + process->appendStep(makeShared(pptr, this)); } // if there are any jar mods diff --git a/launcher/minecraft/MinecraftLoadAndCheck.cpp b/launcher/minecraft/MinecraftLoadAndCheck.cpp index c0a82e61e..149e7cf19 100644 --- a/launcher/minecraft/MinecraftLoadAndCheck.cpp +++ b/launcher/minecraft/MinecraftLoadAndCheck.cpp @@ -43,4 +43,4 @@ bool MinecraftLoadAndCheck::abort() return status; } return Task::abort(); -} \ No newline at end of file +} diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 74c8d5ef8..7ffebddb5 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -38,7 +38,6 @@ */ #include -#include #include #include #include @@ -168,6 +167,7 @@ static bool savePackProfile(const QString& filename, const ComponentContainer& c } if (!outFile.commit()) { qCCritical(instanceProfileC) << "Couldn't save" << outFile.fileName() << "because:" << outFile.errorString(); + return false; } return true; } @@ -230,9 +230,8 @@ static PackProfile::Result loadPackProfile(PackProfile* parent, void PackProfile::saveNow() { - if (saveIsScheduled()) { + if (saveIsScheduled() && save_internal()) { d->m_saveTimer.stop(); - save_internal(); } } @@ -280,12 +279,15 @@ QString PackProfile::patchFilePathForUid(const QString& uid) const return patchesPattern().arg(uid); } -void PackProfile::save_internal() +bool PackProfile::save_internal() { qDebug() << d->m_instance->name() << "|" << "Component list save performed now"; auto filename = componentsFilePath(); - savePackProfile(filename, d->components); - d->dirty = false; + if (savePackProfile(filename, d->components)) { + d->dirty = false; + return true; + } + return false; } PackProfile::Result PackProfile::load() @@ -364,7 +366,7 @@ void PackProfile::updateSucceeded() void PackProfile::updateFailed(const QString& error) { - qCDebug(instanceProfileC) << d->m_instance->name() << "|" << "Component list update/resolve task failed " << "Reason:" << error; + qCDebug(instanceProfileC) << d->m_instance->name() << "|" << "Component list update/resolve task failed. Reason:" << error; d->m_updateTask.reset(); invalidateLaunchProfile(); } @@ -952,7 +954,7 @@ std::shared_ptr PackProfile::getProfile() const } d->m_profile = profile; } catch (const Exception& error) { - qCWarning(instanceProfileC) << d->m_instance->name() << "|" << "Couldn't apply profile patches because: " << error.cause(); + qCWarning(instanceProfileC) << d->m_instance->name() << "|" << "Couldn't apply profile patches because:" << error.cause(); } } return d->m_profile; diff --git a/launcher/minecraft/PackProfile.h b/launcher/minecraft/PackProfile.h index d812dfa48..70dd04514 100644 --- a/launcher/minecraft/PackProfile.h +++ b/launcher/minecraft/PackProfile.h @@ -175,7 +175,7 @@ class PackProfile : public QAbstractListModel { QString patchesPattern() const; private slots: - void save_internal(); + bool save_internal(); void updateSucceeded(); void updateFailed(const QString& error); void componentDataChanged(); diff --git a/launcher/minecraft/ProfileUtils.cpp b/launcher/minecraft/ProfileUtils.cpp index a79f89529..b0b9122e3 100644 --- a/launcher/minecraft/ProfileUtils.cpp +++ b/launcher/minecraft/ProfileUtils.cpp @@ -55,7 +55,7 @@ bool readOverrideOrders(QString path, PatchOrder& order) return false; } if (!orderFile.open(QFile::ReadOnly)) { - qCritical() << "Couldn't open" << orderFile.fileName() << " for reading:" << orderFile.errorString(); + qCritical() << "Couldn't open" << orderFile.fileName() << "for reading:" << orderFile.errorString(); qWarning() << "Ignoring overridden order"; return false; } diff --git a/launcher/minecraft/World.cpp b/launcher/minecraft/World.cpp index bdbe721e3..fca45fb99 100644 --- a/launcher/minecraft/World.cpp +++ b/launcher/minecraft/World.cpp @@ -405,7 +405,7 @@ void World::loadFromLevelDat(QByteArray data) try { valPtr = &levelData->at("Data"); } catch (const std::out_of_range& e) { - qWarning() << "Unable to read NBT tags from " << m_folderName << ":" << e.what(); + qWarning().nospace() << "Unable to read NBT tags from " << m_folderName << ": " << e.what(); m_isValid = false; return; } diff --git a/launcher/minecraft/World.h b/launcher/minecraft/World.h index cca931826..bb4baa33d 100644 --- a/launcher/minecraft/World.h +++ b/launcher/minecraft/World.h @@ -87,7 +87,7 @@ class World { QString m_iconFile; QDateTime m_levelDatTime; QDateTime m_lastPlayed; - int64_t m_size; + int64_t m_size = 0; int64_t m_randomSeed = 0; GameType m_gameType; bool m_isValid = false; diff --git a/launcher/minecraft/WorldList.cpp b/launcher/minecraft/WorldList.cpp index 059feabde..4aa0f7532 100644 --- a/launcher/minecraft/WorldList.cpp +++ b/launcher/minecraft/WorldList.cpp @@ -36,7 +36,6 @@ #include "WorldList.h" #include -#include #include #include #include @@ -65,9 +64,9 @@ void WorldList::startWatching() update(); m_isWatching = m_watcher->addPath(m_dir.absolutePath()); if (m_isWatching) { - qDebug() << "Started watching " << m_dir.absolutePath(); + qDebug() << "Started watching" << m_dir.absolutePath(); } else { - qDebug() << "Failed to start watching " << m_dir.absolutePath(); + qDebug() << "Failed to start watching" << m_dir.absolutePath(); } } @@ -78,9 +77,9 @@ void WorldList::stopWatching() } m_isWatching = !m_watcher->removePath(m_dir.absolutePath()); if (!m_isWatching) { - qDebug() << "Stopped watching " << m_dir.absolutePath(); + qDebug() << "Stopped watching" << m_dir.absolutePath(); } else { - qDebug() << "Failed to stop watching " << m_dir.absolutePath(); + qDebug() << "Failed to stop watching" << m_dir.absolutePath(); } } @@ -352,7 +351,7 @@ Qt::DropActions WorldList::supportedDropActions() const void WorldList::installWorld(QFileInfo filename) { - qDebug() << "installing: " << filename.absoluteFilePath(); + qDebug() << "installing:" << filename.absoluteFilePath(); World w(filename); if (!w.isValid()) { return; diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index 3cbbb2a74..d86014a34 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -574,7 +574,7 @@ void AccountList::fillQueue() if (m_defaultAccount && m_defaultAccount->shouldRefresh()) { auto idToRefresh = m_defaultAccount->internalId(); m_refreshQueue.push_back(idToRefresh); - qDebug() << "AccountList: Queued default account with internal ID " << idToRefresh << " to refresh first"; + qDebug() << "AccountList: Queued default account with internal ID" << idToRefresh << "to refresh first"; } for (int i = 0; i < count(); i++) { @@ -598,7 +598,7 @@ void AccountList::requestRefresh(QString accountId) m_refreshQueue.removeAt(index); } m_refreshQueue.push_front(accountId); - qDebug() << "AccountList: Pushed account with internal ID " << accountId << " to the front of the queue"; + qDebug() << "AccountList: Pushed account with internal ID" << accountId << "to the front of the queue"; if (!isActive()) { tryNext(); } @@ -610,7 +610,7 @@ void AccountList::queueRefresh(QString accountId) return; } m_refreshQueue.push_back(accountId); - qDebug() << "AccountList: Queued account with internal ID " << accountId << " to refresh"; + qDebug() << "AccountList: Queued account with internal ID" << accountId << "to refresh"; } void AccountList::tryNext() @@ -626,13 +626,13 @@ void AccountList::tryNext() connect(m_currentTask.get(), &Task::succeeded, this, &AccountList::authSucceeded); connect(m_currentTask.get(), &Task::failed, this, &AccountList::authFailed); m_currentTask->start(); - qDebug() << "RefreshSchedule: Processing account " << account->accountDisplayString() << " with internal ID " + qDebug() << "RefreshSchedule: Processing account" << account->accountDisplayString() << "with internal ID" << accountId; return; } } } - qDebug() << "RefreshSchedule: Account with with internal ID " << accountId << " not found."; + qDebug() << "RefreshSchedule: Account with internal ID" << accountId << "not found."; } // if we get here, no account needed refreshing. Schedule refresh in an hour. m_refreshTimer->start(1000 * 3600); @@ -647,7 +647,7 @@ void AccountList::authSucceeded() void AccountList::authFailed(QString reason) { - qDebug() << "RefreshSchedule: Background account refresh failed: " << reason; + qDebug() << "RefreshSchedule: Background account refresh failed:" << reason; m_currentTask.reset(); m_nextTimer->start(1000 * 20); } @@ -669,7 +669,7 @@ void AccountList::beginActivity() void AccountList::endActivity() { if (m_activityCount == 0) { - qWarning() << m_name << " - Activity count would become below zero"; + qWarning() << "Activity count would become below zero"; return; } bool deactivating = m_activityCount == 1; diff --git a/launcher/minecraft/auth/AccountList.h b/launcher/minecraft/auth/AccountList.h index d3be6740e..2f1276312 100644 --- a/launcher/minecraft/auth/AccountList.h +++ b/launcher/minecraft/auth/AccountList.h @@ -111,7 +111,6 @@ class AccountList : public QAbstractListModel { void endActivity(); private: - const char* m_name; uint32_t m_activityCount = 0; signals: void listChanged(); diff --git a/launcher/minecraft/auth/AuthFlow.cpp b/launcher/minecraft/auth/AuthFlow.cpp index 287831b2f..cea171b33 100644 --- a/launcher/minecraft/auth/AuthFlow.cpp +++ b/launcher/minecraft/auth/AuthFlow.cpp @@ -69,6 +69,7 @@ void AuthFlow::nextStep() } m_currentStep = m_steps.front(); qDebug() << "AuthFlow:" << m_currentStep->describe(); + setStatus(m_currentStep->describe()); m_steps.pop_front(); connect(m_currentStep.get(), &AuthStep::finished, this, &AuthFlow::stepFinished); @@ -92,7 +93,9 @@ bool AuthFlow::changeState(AccountTaskState newState, QString reason) return true; } case AccountTaskState::STATE_WORKING: { - setStatus(m_currentStep ? m_currentStep->describe() : tr("Working...")); + if (!m_currentStep) { + setStatus(tr("Preparing to log in...")); + } m_data->accountState = AccountState::Working; return true; } @@ -148,8 +151,8 @@ bool AuthFlow::changeState(AccountTaskState newState, QString reason) } bool AuthFlow::abort() { - emitAborted(); if (m_currentStep) m_currentStep->abort(); + emitAborted(); return true; -} \ No newline at end of file +} diff --git a/launcher/minecraft/auth/AuthSession.cpp b/launcher/minecraft/auth/AuthSession.cpp index 3657befec..a74eabb7a 100644 --- a/launcher/minecraft/auth/AuthSession.cpp +++ b/launcher/minecraft/auth/AuthSession.cpp @@ -39,4 +39,4 @@ void AuthSession::MakeDemo(QString name, QString u) access_token = "0"; player_name = name; status = PlayableOnline; // needs online to download the assets -}; \ No newline at end of file +}; diff --git a/launcher/minecraft/auth/Parsers.cpp b/launcher/minecraft/auth/Parsers.cpp index ba77d3e31..08c780694 100644 --- a/launcher/minecraft/auth/Parsers.cpp +++ b/launcher/minecraft/auth/Parsers.cpp @@ -75,7 +75,7 @@ bool getBool(QJsonValue value, bool& out) "Message":"", "Redirect":"https://start.ui.xboxlive.com/AddChildToFamily" } -// 2148916233 = missing XBox account +// 2148916233 = missing Xbox account // 2148916238 = child account not linked to a family */ @@ -86,7 +86,7 @@ bool parseXTokenResponse(QByteArray& data, Token& output, QString name) QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error) { - qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString(); + qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON:" << jsonError.errorString(); return false; } @@ -123,7 +123,7 @@ bool parseXTokenResponse(QByteArray& data, Token& output, QString name) for (auto iter = obj_.begin(); iter != obj_.end(); iter++) { QString claim; if (!getString(obj_.value(iter.key()), claim)) { - qWarning() << "display claim " << iter.key() << " is not a string..."; + qWarning() << "display claim" << iter.key() << "is not a string..."; return false; } output.extra[iter.key()] = claim; @@ -148,7 +148,7 @@ bool parseMinecraftProfile(QByteArray& data, MinecraftProfile& output) QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error) { - qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString(); + qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON:" << jsonError.errorString(); return false; } @@ -290,7 +290,7 @@ bool parseMinecraftProfileMojang(QByteArray& data, MinecraftProfile& output) QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error) { - qWarning() << "Failed to parse response as JSON: " << jsonError.errorString(); + qWarning() << "Failed to parse response as JSON:" << jsonError.errorString(); return false; } @@ -331,7 +331,7 @@ bool parseMinecraftProfileMojang(QByteArray& data, MinecraftProfile& output) doc = QJsonDocument::fromJson(texturePayload, &jsonError); if (jsonError.error) { - qWarning() << "Failed to parse response as JSON: " << jsonError.errorString(); + qWarning() << "Failed to parse response as JSON:" << jsonError.errorString(); return false; } @@ -400,7 +400,7 @@ bool parseMinecraftEntitlements(QByteArray& data, MinecraftEntitlement& output) QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error) { - qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString(); + qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON:" << jsonError.errorString(); return false; } @@ -463,7 +463,7 @@ bool parseMojangResponse(QByteArray& data, Token& output) qCDebug(authCredentials()) << data; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error) { - qWarning() << "Failed to parse response from api.minecraftservices.com/launcher/login as JSON: " << jsonError.errorString(); + qWarning() << "Failed to parse response from api.minecraftservices.com/launcher/login as JSON:" << jsonError.errorString(); return false; } diff --git a/launcher/minecraft/auth/steps/EntitlementsStep.cpp b/launcher/minecraft/auth/steps/EntitlementsStep.cpp index 5b9809c52..11219bf17 100644 --- a/launcher/minecraft/auth/steps/EntitlementsStep.cpp +++ b/launcher/minecraft/auth/steps/EntitlementsStep.cpp @@ -34,6 +34,7 @@ void EntitlementsStep::perform() m_response.reset(new QByteArray()); m_request = Net::Download::makeByteArray(url, m_response); m_request->addHeaderProxy(new Net::RawHeaderProxy(headers)); + m_request->enableAutoRetry(true); m_task.reset(new NetJob("EntitlementsStep", APPLICATION->network())); m_task->setAskRetry(false); diff --git a/launcher/minecraft/auth/steps/GetSkinStep.cpp b/launcher/minecraft/auth/steps/GetSkinStep.cpp index e067bc34c..b9e1dbcdb 100644 --- a/launcher/minecraft/auth/steps/GetSkinStep.cpp +++ b/launcher/minecraft/auth/steps/GetSkinStep.cpp @@ -18,6 +18,7 @@ void GetSkinStep::perform() m_response.reset(new QByteArray()); m_request = Net::Download::makeByteArray(url, m_response); + m_request->enableAutoRetry(true); m_task.reset(new NetJob("GetSkinStep", APPLICATION->network())); m_task->setAskRetry(false); diff --git a/launcher/minecraft/auth/steps/LauncherLoginStep.cpp b/launcher/minecraft/auth/steps/LauncherLoginStep.cpp index 954f013af..f91c2c595 100644 --- a/launcher/minecraft/auth/steps/LauncherLoginStep.cpp +++ b/launcher/minecraft/auth/steps/LauncherLoginStep.cpp @@ -14,7 +14,7 @@ LauncherLoginStep::LauncherLoginStep(AccountData* data) : AuthStep(data) {} QString LauncherLoginStep::describe() { - return tr("Accessing Mojang services."); + return tr("Fetching Minecraft access token"); } void LauncherLoginStep::perform() @@ -39,6 +39,7 @@ void LauncherLoginStep::perform() m_response.reset(new QByteArray()); m_request = Net::Upload::makeByteArray(url, m_response, requestBody.toUtf8()); m_request->addHeaderProxy(new Net::RawHeaderProxy(headers)); + m_request->enableAutoRetry(true); m_task.reset(new NetJob("LauncherLoginStep", APPLICATION->network())); m_task->setAskRetry(false); @@ -69,5 +70,5 @@ void LauncherLoginStep::onRequestDone() emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to parse the Minecraft access token response.")); return; } - emit finished(AccountTaskState::STATE_WORKING, tr("")); + emit finished(AccountTaskState::STATE_WORKING, tr("Got Minecraft access token")); } diff --git a/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp b/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp index 7a4722f21..aaf527cd4 100644 --- a/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp +++ b/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp @@ -69,6 +69,7 @@ void MSADeviceCodeStep::perform() m_response.reset(new QByteArray()); m_request = Net::Upload::makeByteArray(url, m_response, payload); m_request->addHeaderProxy(new Net::RawHeaderProxy(headers)); + m_request->enableAutoRetry(true); m_task.reset(new NetJob("MSADeviceCodeStep", APPLICATION->network())); m_task->setAskRetry(false); @@ -120,12 +121,13 @@ void MSADeviceCodeStep::deviceAuthorizationFinished() return; } if (!m_request->wasSuccessful() || m_request->error() != QNetworkReply::NoError) { + qWarning() << "Device authorization failed:" << *m_response; emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Failed to retrieve device authorization")); - qDebug() << *m_response; return; } if (rsp.device_code.isEmpty() || rsp.user_code.isEmpty() || rsp.verification_uri.isEmpty() || rsp.expires_in == 0) { + qWarning() << "Device authorization failed: required fields missing"; emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Device authorization failed: required fields missing")); return; } @@ -272,5 +274,5 @@ void MSADeviceCodeStep::authenticationFinished() m_data->msaToken.extra = rsp.extra; m_data->msaToken.refresh_token = rsp.refresh_token; m_data->msaToken.token = rsp.access_token; - emit finished(AccountTaskState::STATE_WORKING, tr("Got")); + emit finished(AccountTaskState::STATE_WORKING, tr("Got MSA token")); } diff --git a/launcher/minecraft/auth/steps/MSAStep.cpp b/launcher/minecraft/auth/steps/MSAStep.cpp index aa972be71..29bab69d5 100644 --- a/launcher/minecraft/auth/steps/MSAStep.cpp +++ b/launcher/minecraft/auth/steps/MSAStep.cpp @@ -56,13 +56,14 @@ bool isSchemeHandlerRegistered() process.waitForFinished(); QString output = process.readAllStandardOutput().trimmed(); - return output.contains(BuildConfig.LAUNCHER_APP_BINARY_NAME); + return output.contains(APPLICATION->desktopFileName()); #elif defined(Q_OS_WIN) QString regPath = QString("HKEY_CURRENT_USER\\Software\\Classes\\%1").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); QSettings settings(regPath, QSettings::NativeFormat); - return settings.contains("shell/open/command/."); + const QString registeredRunCommand = settings.value("shell/open/command/.").toString().replace("\\", "/"); + return registeredRunCommand.contains(QCoreApplication::applicationFilePath()); #endif return true; } @@ -80,6 +81,33 @@ class CustomOAuthOobReplyHandler : public QOAuthOobReplyHandler { disconnect(APPLICATION, &Application::oauthReplyRecieved, this, &QOAuthOobReplyHandler::callbackReceived); } QString callback() const override { return BuildConfig.LAUNCHER_APP_BINARY_NAME + "://oauth/microsoft"; } + + protected: + void networkReplyFinished(QNetworkReply* reply) override + { + if (reply->error() != QNetworkReply::NoError) { + qWarning() << "OAuth2 request failed:" << reply->readAll(); + } + + QOAuthOobReplyHandler::networkReplyFinished(reply); + } +}; + +class LoggingOAuthHttpServerReplyHandler final : public QOAuthHttpServerReplyHandler { + Q_OBJECT + + public: + explicit LoggingOAuthHttpServerReplyHandler(QObject* parent = nullptr) : QOAuthHttpServerReplyHandler(parent) {} + + protected: + void networkReplyFinished(QNetworkReply* reply) override + { + if (reply->error() != QNetworkReply::NoError) { + qWarning() << "OAuth2 request failed:" << reply->readAll(); + } + + QOAuthHttpServerReplyHandler::networkReplyFinished(reply); + } }; MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(silent) @@ -88,7 +116,7 @@ MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(sile if (QCoreApplication::applicationFilePath().startsWith("/tmp/.mount_") || APPLICATION->isPortable() || !isSchemeHandlerRegistered()) { - auto replyHandler = new QOAuthHttpServerReplyHandler(this); + auto replyHandler = new LoggingOAuthHttpServerReplyHandler(this); replyHandler->setCallbackText(QString(R"XXX(