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..b21f3221e 100644 --- a/.github/actions/package/linux/action.yml +++ b/.github/actions/package/linux/action.yml @@ -52,32 +52,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 +107,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 +130,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..753ea19fe 100644 --- a/.github/actions/setup-dependencies/linux/action.yml +++ b/.github/actions/setup-dependencies/linux/action.yml @@ -12,29 +12,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 +34,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..43769f4fe 100644 --- a/.github/actions/setup-dependencies/windows/action.yml +++ b/.github/actions/setup-dependencies/windows/action.yml @@ -57,7 +57,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..a6ed96863 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 --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..cd4987cc7 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..ce19db037 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,7 @@ These have debug information in the binaries, so their file sizes are relatively 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(