diff --git a/.clang-format b/.clang-format
index 114bcba505..005a1006bc 100644
--- a/.clang-format
+++ b/.clang-format
@@ -5,6 +5,7 @@ AllowShortIfStatementsOnASingleLine: false
ColumnLimit: 140
---
Language: Cpp
+AccessModifierOffset: -1
AlignConsecutiveMacros: None
AlignConsecutiveAssignments: None
BraceWrapping:
@@ -15,3 +16,4 @@ BraceWrapping:
BreakBeforeBraces: Custom
BreakConstructorInitializers: BeforeComma
Cpp11BracedListStyle: false
+QualifierAlignment: Left
diff --git a/.clang-tidy b/.clang-tidy
index 436dcf244f..c5eb095330 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,5 +1,32 @@
+FormatStyle: file
+
Checks:
- - modernize-use-using
- - readability-avoid-const-params-in-decls
+ "bugprone-*,clang-analyzer-*,cppcoreguidelines-*,hicpp-*,misc-*,modernize-*,performance-*,portability-*,readability-*,
+ -*-magic-numbers,
+ -*-non-private-member-variables-in-classes,
+ -*-special-member-functions,
+ -bugprone-easily-swappable-parameters,
+ -cppcoreguidelines-owning-memory,
+ -cppcoreguidelines-pro-type-static-cast-downcast,
+ -modernize-use-nodiscard,
+ -modernize-use-trailing-return-type,
+ -portability-avoid-pragma-once,
+ -readability-avoid-unconditional-preprocessor-if,
+ -readability-function-cognitive-complexity,
+ -readability-identifier-length,
+ -readability-redundant-access-specifiers"
-SystemHeaders: false
+CheckOptions:
+ misc-include-cleaner.MissingIncludes: false
+ readability-identifier-naming.DefaultCase: "camelBack"
+ readability-identifier-naming.NamespaceCase: "CamelCase"
+ readability-identifier-naming.ClassCase: "CamelCase"
+ readability-identifier-naming.ClassConstantCase: "CamelCase"
+ readability-identifier-naming.EnumCase: "CamelCase"
+ readability-identifier-naming.EnumConstantCase: "CamelCase"
+ readability-identifier-naming.MacroDefinitionCase: "UPPER_CASE"
+ readability-identifier-naming.ClassMemberPrefix: "m_"
+ readability-identifier-naming.StaticConstantPrefix: "s_"
+ readability-identifier-naming.StaticVariablePrefix: "s_"
+ readability-identifier-naming.GlobalConstantPrefix: "g_"
+ readability-implicit-bool-conversion.AllowPointerConditions: true
diff --git a/.editorconfig b/.editorconfig
index 56166b207f..03b99379b2 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,8 +1,22 @@
-# EditorConfig specs and documentation: https://EditorConfig.org
-
-# top-most EditorConfig file
-root = true
-
-# C++ Code Style settings
-[*.{c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}]
-cpp_generate_documentation_comments = doxygen_slash_star
+# EditorConfig specs and documentation: https://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+end_of_line = lf
+charset = utf-8
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.{yml,nix}]
+indent_size = 2
+
+# C++ Code Style settings
+[*.{c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}]
+cpp_generate_documentation_comments = doxygen_slash_star
+
+[CMakeLists.txt]
+ij_continuation_indent_size = 4
diff --git a/.envrc b/.envrc
index 190b5b2b3d..1d11c53545 100644
--- a/.envrc
+++ b/.envrc
@@ -1,2 +1,2 @@
-use flake
+use nix
watch_file nix/*.nix
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 2163db45bb..c7d36db271 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -2,3 +2,12 @@
# tabs -> spaces
bbb3b3e6f6e3c0f95873f22e6d0a4aaf350f49d9
+
+# (nix) alejandra -> nixfmt
+4c81d8c53d09196426568c4a31a4e752ed05397a
+
+# reformat codebase
+1d468ac35ad88d8c77cc83f25e3704d9bd7df01b
+
+# format a part of codebase
+5c8481a118c8fefbfe901001d7828eaf6866eac4
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index ea1fbfdd93..4ea328301b 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -1,6 +1,6 @@
name: Bug Report
description: File a bug report
-labels: [bug]
+labels: ["bug: unconfirmed", "status: needs triage"]
body:
- type: markdown
attributes:
diff --git a/.github/ISSUE_TEMPLATE/rfc.yml b/.github/ISSUE_TEMPLATE/rfc.yml
index fa7cdbe61b..5e6d68e657 100644
--- a/.github/ISSUE_TEMPLATE/rfc.yml
+++ b/.github/ISSUE_TEMPLATE/rfc.yml
@@ -1,7 +1,7 @@
# Template based on https://gitlab.archlinux.org/archlinux/rfcs/-/blob/0ba3b61e987e197f8d1901709409b8564958f78a/rfcs/0000-template.rst
name: Request for Comment (RFC)
description: Propose a larger change and start a discussion.
-labels: [rfc]
+labels: ["type: enhancement", "status: needs discussion", "status: needs triage"]
body:
- type: markdown
attributes:
@@ -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/ISSUE_TEMPLATE/suggestion.yml b/.github/ISSUE_TEMPLATE/suggestion.yml
index ddee86b656..18a202ae18 100644
--- a/.github/ISSUE_TEMPLATE/suggestion.yml
+++ b/.github/ISSUE_TEMPLATE/suggestion.yml
@@ -1,6 +1,6 @@
name: Suggestion
description: Make a suggestion
-labels: [enhancement]
+labels: ["type: enhancement", "status: needs triage"]
body:
- type: markdown
attributes:
diff --git a/.github/actions/package/linux/action.yml b/.github/actions/package/linux/action.yml
new file mode 100644
index 0000000000..2ce6ca9557
--- /dev/null
+++ b/.github/actions/package/linux/action.yml
@@ -0,0 +1,153 @@
+name: Package for Linux
+description: Create Linux packages for Prism Launcher
+
+inputs:
+ version:
+ description: Launcher version
+ required: true
+ build-type:
+ description: Type for the build
+ required: true
+ default: Debug
+ artifact-name:
+ description: Name of the uploaded artifact
+ required: true
+ default: Linux
+ qt-version:
+ description: Version of Qt to use
+ required: true
+ gpg-private-key:
+ description: Private key for AppImage signing
+ required: false
+ gpg-private-key-id:
+ description: ID for the gpg-private-key, to select the signing key
+ required: false
+
+runs:
+ using: composite
+
+ steps:
+ - name: Cleanup Qt installation on Linux
+ shell: bash
+ run: |
+ rm -rf "$QT_PLUGIN_PATH"/printsupport
+ rm -rf "$QT_PLUGIN_PATH"/sqldrivers
+ rm -rf "$QT_PLUGIN_PATH"/help
+ rm -rf "$QT_PLUGIN_PATH"/designer
+ rm -rf "$QT_PLUGIN_PATH"/qmltooling
+ rm -rf "$QT_PLUGIN_PATH"/qmlls
+ rm -rf "$QT_PLUGIN_PATH"/qmllint
+ rm -rf "$QT_PLUGIN_PATH"/platformthemes/libqgtk3.so
+
+ - name: Setup build variables
+ shell: bash
+ run: |
+ # Fixup architecture naming for AppImages
+ dpkg_arch="$(dpkg-architecture -q DEB_HOST_ARCH_CPU)"
+ case "$dpkg_arch" in
+ "amd64")
+ APPIMAGE_ARCH="x86_64"
+ ;;
+ "arm64")
+ APPIMAGE_ARCH="aarch64"
+ ;;
+ *)
+ echo "# 🚨 The Debian architecture \"$deb_arch\" is not recognized!" >> "$GITHUB_STEP_SUMMARY"
+ exit 1
+ ;;
+ esac
+ echo "APPIMAGE_ARCH=$APPIMAGE_ARCH" >> "$GITHUB_ENV"
+
+ # Used for the file paths of libraries
+ echo "DEB_HOST_MULTIARCH=$(dpkg-architecture -q DEB_HOST_MULTIARCH)" >> "$GITHUB_ENV"
+
+ - name: Package AppImage
+ shell: bash
+ env:
+ 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 }}
+
+ if [ '${{ inputs.gpg-private-key-id }}' != '' ]; then
+ echo "$GPG_PRIVATE_KEY" > privkey.asc
+ gpg --import privkey.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
+
+ 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 {} +
+
+ #disable OpenGL and Vulkan launcher features until https://github.com/VHSgunzo/sharun/issues/35
+ echo "PRISMLAUNCHER_DISABLE_GLVULKAN=1" >> "$INSTALL_APPIMAGE_DIR"/.env
+ #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 \
+ --updateinformation "gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-$APPIMAGE_ARCH.AppImage.zsync" \
+ "$INSTALL_APPIMAGE_DIR" \
+ "$APPIMAGE_DEST"
+
+ - name: Package portable tarball
+ shell: bash
+ env:
+ BUILD_DIR: build
+
+ INSTALL_PORTABLE_DIR: install-portable
+ run: |
+ 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
+
+ sharun lib4bin \
+ --with-hooks \
+ --hard-links \
+ --dst-dir "$INSTALL_PORTABLE_DIR" \
+ "$INSTALL_PORTABLE_DIR"/bin/* "$QT_PLUGIN_PATH"/*/*.so
+
+ # 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 *
+
+ - name: Upload binary tarball
+ uses: actions/upload-artifact@v7
+ with:
+ name: PrismLauncher-${{ inputs.artifact-name }}-Qt6-Portable-${{ inputs.version }}-${{ inputs.build-type }}
+ path: PrismLauncher-portable.tar.gz
+
+ - name: Upload AppImage
+ uses: actions/upload-artifact@v7
+ with:
+ name: 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@v7
+ with:
+ name: 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/macos/action.yml b/.github/actions/package/macos/action.yml
new file mode 100644
index 0000000000..1af01250f9
--- /dev/null
+++ b/.github/actions/package/macos/action.yml
@@ -0,0 +1,147 @@
+name: Package for macOS
+description: Create a macOS package for Prism Launcher
+
+inputs:
+ version:
+ description: Launcher version
+ required: true
+ build-type:
+ description: Type for the build
+ required: true
+ default: Debug
+ artifact-name:
+ description: Name of the uploaded artifact
+ required: true
+ default: macOS
+ apple-codesign-cert:
+ description: Certificate for signing macOS builds
+ required: false
+ apple-codesign-password:
+ description: Password for signing macOS builds
+ required: false
+ apple-codesign-id:
+ description: Certificate ID for signing macOS builds
+ required: false
+ apple-notarize-apple-id:
+ description: Apple ID used for notarizing macOS builds
+ required: false
+ apple-notarize-team-id:
+ description: Team ID used for notarizing macOS builds
+ required: false
+ apple-notarize-password:
+ description: Password used for notarizing macOS builds
+ required: false
+ sparkle-ed25519-key:
+ description: Private key for signing Sparkle updates
+ required: false
+
+runs:
+ using: composite
+
+ steps:
+ - name: Fetch codesign certificate
+ shell: bash
+ run: |
+ echo '${{ inputs.apple-codesign-cert }}' | base64 --decode > codesign.p12
+ if [ -n '${{ inputs.apple-codesign-id }}' ]; then
+ security create-keychain -p '${{ inputs.apple-codesign-password }}' build.keychain
+ security default-keychain -s build.keychain
+ security unlock-keychain -p '${{ inputs.apple-codesign-password }}' build.keychain
+ security import codesign.p12 -k build.keychain -P '${{ inputs.apple-codesign-password }}' -T /usr/bin/codesign
+ security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k '${{ inputs.apple-codesign-password }}' build.keychain
+ else
+ echo ":warning: Using ad-hoc code signing for macOS, as certificate was not present." >> $GITHUB_STEP_SUMMARY
+ fi
+
+ - name: Package
+ shell: bash
+ env:
+ BUILD_DIR: build
+ INSTALL_DIR: install
+ run: |
+ cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }}
+
+ cd ${{ env.INSTALL_DIR }}
+ chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher"
+
+ if [ -n '${{ inputs.apple-codesign-id }}' ]; then
+ APPLE_CODESIGN_ID='${{ inputs.apple-codesign-id }}'
+ ENTITLEMENTS_FILE='../program_info/App.entitlements'
+ else
+ APPLE_CODESIGN_ID='-'
+ ENTITLEMENTS_FILE='../program_info/AdhocSignedApp.entitlements'
+ fi
+
+ sudo codesign --sign "$APPLE_CODESIGN_ID" --deep --force --entitlements "$ENTITLEMENTS_FILE" --options runtime "PrismLauncher.app/Contents/MacOS/prismlauncher"
+ mv "PrismLauncher.app" "Prism Launcher.app"
+
+ - name: Notarize
+ shell: bash
+ env:
+ INSTALL_DIR: install
+ run: |
+ cd ${{ env.INSTALL_DIR }}
+
+ if [ -n '${{ inputs.apple-notarize-password }}' ]; then
+ ditto -c -k --sequesterRsrc --keepParent "Prism Launcher.app" ../PrismLauncher.zip
+ xcrun notarytool submit ../PrismLauncher.zip \
+ --wait --progress \
+ --apple-id '${{ inputs.apple-notarize-apple-id }}' \
+ --team-id '${{ inputs.apple-notarize-team-id }}' \
+ --password '${{ inputs.apple-notarize-password }}'
+
+ xcrun stapler staple "Prism Launcher.app"
+ else
+ echo ":warning: Skipping notarization as credentials are not present." >> $GITHUB_STEP_SUMMARY
+ fi
+ ditto -c -k --sequesterRsrc --keepParent "Prism Launcher.app" ../PrismLauncher.zip
+
+ - name: Create DMG
+ shell: bash
+ env:
+ INSTALL_DIR: install
+ run: |
+ cd ${{ env.INSTALL_DIR }}
+
+ mkdir -p src
+ cp -R "Prism Launcher.app" src/
+
+ ln -s /Applications src/
+
+ hdiutil create \
+ -volname "Prism Launcher ${{ inputs.version }}" \
+ -srcfolder src \
+ -ov -format ULMO \
+ "../PrismLauncher.dmg"
+
+ - name: Make Sparkle signature
+ shell: bash
+ run: |
+ if [ '${{ inputs.sparkle-ed25519-key }}' != '' ]; then
+ echo '${{ inputs.sparkle-ed25519-key }}' > ed25519-priv.pem
+ signature_zip=$(/opt/homebrew/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.zip -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
+ signature_dmg=$(/opt/homebrew/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.dmg -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
+ rm ed25519-priv.pem
+ cat >> $GITHUB_STEP_SUMMARY << EOF
+ ### Artifact Information :information_source:
+ - :memo: Sparkle Signature (ed25519): \`$signature_zip\` (ZIP)
+ - :memo: Sparkle Signature (ed25519): \`$signature_dmg\` (DMG)
+ EOF
+ else
+ cat >> $GITHUB_STEP_SUMMARY << EOF
+ ### Artifact Information :information_source:
+ - :warning: Sparkle Signature (ed25519): No private key available (likely a pull request or fork)
+ EOF
+ fi
+
+ - name: Upload binary tarball
+ uses: actions/upload-artifact@v7
+ with:
+ name: PrismLauncher-${{ inputs.artifact-name }}-${{ inputs.version }}-${{ inputs.build-type }}
+ path: PrismLauncher.zip
+
+ - name: Upload disk image
+ uses: actions/upload-artifact@v7
+ with:
+ name: PrismLauncher-${{ inputs.artifact-name }}-${{ inputs.version }}-${{ inputs.build-type }}.dmg
+ path: PrismLauncher.dmg
diff --git a/.github/actions/package/windows/action.yml b/.github/actions/package/windows/action.yml
new file mode 100644
index 0000000000..cd0eb7d917
--- /dev/null
+++ b/.github/actions/package/windows/action.yml
@@ -0,0 +1,186 @@
+name: Package for Windows
+description: Create a Windows package for Prism Launcher
+
+inputs:
+ version:
+ description: Launcher version
+ required: true
+ build-type:
+ description: Type for the build
+ required: true
+ default: Debug
+ artifact-name:
+ description: Name of the uploaded artifact
+ required: true
+ msystem:
+ description: MSYS2 subsystem to use
+ required: false
+ azure-client-id:
+ description: Client ID for the Azure Signer Application
+ required: true
+ azure-tenant-id:
+ description: Tenant ID for the Azure Signer Application
+ required: true
+ azure-subscription-id:
+ description: Subscription ID for the Azure Signer Application
+ required: true
+
+runs:
+ using: composite
+
+ steps:
+ - name: Package (MinGW)
+ if: ${{ inputs.msystem != '' }}
+ shell: msys2 {0}
+ env:
+ BUILD_DIR: build
+ INSTALL_DIR: install
+ run: |
+ cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }}
+ touch ${{ env.INSTALL_DIR }}/manifest.txt
+ for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_DIR }}/manifest.txt
+
+ - name: Package (MSVC)
+ if: ${{ inputs.msystem == '' }}
+ shell: pwsh
+ env:
+ BUILD_DIR: build
+ INSTALL_DIR: install
+ run: |
+ cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }}
+
+ cd ${{ github.workspace }}
+
+ 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: ${{ 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: ${{ env.CI_HAS_ACCESS_TO_AZURE != '' && inputs.azure-client-id != '' }}
+ uses: azure/login@v3
+ with:
+ client-id: ${{ inputs.azure-client-id }}
+ tenant-id: ${{ inputs.azure-tenant-id }}
+ subscription-id: ${{ inputs.azure-subscription-id }}
+
+ - name: Sign executables
+ 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-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
+ exclude-workload-identity-credential: true
+ exclude-managed-identity-credential: true
+ exclude-shared-token-cache-credential: true
+ exclude-visual-studio-credential: true
+ exclude-visual-studio-code-credential: true
+ exclude-azure-cli-credential: false
+ exclude-azure-powershell-credential: true
+ exclude-azure-developer-cli-credential: true
+ exclude-interactive-browser-credential: true
+
+ - name: Package (MinGW, portable)
+ if: ${{ inputs.msystem != '' }}
+ shell: msys2 {0}
+ env:
+ BUILD_DIR: build
+ INSTALL_DIR: install
+ INSTALL_PORTABLE_DIR: install-portable
+ run: |
+ cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
+ cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
+ for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
+
+ - name: Package (MSVC, portable)
+ if: ${{ inputs.msystem == '' }}
+ shell: pwsh
+ env:
+ BUILD_DIR: build
+ INSTALL_DIR: install
+ INSTALL_PORTABLE_DIR: install-portable
+ run: |
+ cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
+ cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
+
+ Get-ChildItem ${{ env.INSTALL_PORTABLE_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_PORTABLE_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
+
+ - name: Package (installer)
+ shell: pwsh
+ env:
+ BUILD_DIR: build
+ INSTALL_DIR: install
+
+ NSCURL_VERSION: "v24.9.26.122"
+ NSCURL_SHA256: "AEE6C4BE3CB6455858E9C1EE4B3AFE0DB9960FA03FE99CCDEDC28390D57CCBB0"
+ run: |
+ New-Item -Name NSISPlugins -ItemType Directory
+ Invoke-Webrequest https://github.com/negrutiu/nsis-nscurl/releases/download/"${{ env.NSCURL_VERSION }}"/NScurl.zip -OutFile NSISPlugins\NScurl.zip
+ $nscurl_hash = Get-FileHash NSISPlugins\NScurl.zip -Algorithm Sha256 | Select-Object -ExpandProperty Hash
+ if ( $nscurl_hash -ne "${{ env.nscurl_sha256 }}") {
+ echo "::error:: NSCurl.zip sha256 mismatch"
+ exit 1
+ }
+ Expand-Archive -Path NSISPlugins\NScurl.zip -DestinationPath NSISPlugins\NScurl
+
+ cd ${{ env.INSTALL_DIR }}
+ makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
+
+ - name: Sign installer
+ 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 }}\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
+ exclude-workload-identity-credential: true
+ exclude-managed-identity-credential: true
+ exclude-shared-token-cache-credential: true
+ exclude-visual-studio-credential: true
+ exclude-visual-studio-code-credential: true
+ exclude-azure-cli-credential: false
+ exclude-azure-powershell-credential: true
+ exclude-azure-developer-cli-credential: true
+ exclude-interactive-browser-credential: true
+
+ - name: Upload binary zip
+ uses: actions/upload-artifact@v7
+ with:
+ name: PrismLauncher-${{ inputs.artifact-name }}-${{ inputs.version }}-${{ inputs.build-type }}
+ path: install/**
+
+ - name: Upload portable zip
+ uses: actions/upload-artifact@v7
+ with:
+ name: PrismLauncher-${{ inputs.artifact-name }}-Portable-${{ inputs.version }}-${{ inputs.build-type }}
+ path: install-portable/**
+
+ - name: Upload installer
+ uses: actions/upload-artifact@v7
+ with:
+ name: PrismLauncher-${{ inputs.artifact-name }}-Setup-${{ inputs.version }}-${{ inputs.build-type }}
+ path: PrismLauncher-Setup.exe
diff --git a/.github/actions/setup-dependencies/action.yml b/.github/actions/setup-dependencies/action.yml
new file mode 100644
index 0000000000..7d403ed0af
--- /dev/null
+++ b/.github/actions/setup-dependencies/action.yml
@@ -0,0 +1,81 @@
+name: Setup Dependencies
+description: Install and setup dependencies for building Prism Launcher
+
+inputs:
+ build-type:
+ description: Type for the build
+ required: true
+ default: Debug
+ artifact-name:
+ description: Name of the uploaded artifact
+ required: true
+ msystem:
+ description: MSYS2 subsystem to use
+ required: false
+ vcvars-arch:
+ description: Visual Studio architecture to use
+ required: false
+ qt-architecture:
+ description: Qt architecture
+ required: false
+ qt-version:
+ description: Version of Qt to use
+ required: true
+
+outputs:
+ build-type:
+ description: Type of build used
+ value: ${{ inputs.build-type }}
+ qt-version:
+ description: Version of Qt used
+ value: ${{ inputs.qt-version }}
+
+runs:
+ using: composite
+
+ steps:
+ - name: Setup Linux dependencies
+ if: ${{ runner.os == 'Linux' }}
+ uses: ./.github/actions/setup-dependencies/linux
+
+ - name: Setup macOS dependencies
+ if: ${{ runner.os == 'macOS' }}
+ uses: ./.github/actions/setup-dependencies/macos
+ with:
+ build-type: ${{ inputs.build-type }}
+
+ - name: Setup Windows dependencies
+ if: ${{ runner.os == 'Windows' }}
+ uses: ./.github/actions/setup-dependencies/windows
+ with:
+ build-type: ${{ inputs.build-type }}
+ msystem: ${{ inputs.msystem }}
+ vcvars-arch: ${{ inputs.vcvars-arch }}
+
+ # TODO(@getchoo): Get this working on MSYS2!
+ - name: Setup ccache
+ if: ${{ (runner.os != 'Windows' || inputs.msystem == '') && inputs.build-type == 'Debug' }}
+ uses: hendrikmuhs/ccache-action@v1.2.22
+ with:
+ variant: sccache
+ create-symlink: ${{ runner.os != 'Windows' }}
+ key: ${{ runner.os }}-${{ runner.arch }}-${{ inputs.artifact-name }}-sccache
+
+ - name: Use ccache on debug builds
+ if: ${{ inputs.build-type == 'Debug' }}
+ shell: bash
+ env:
+ # Only use ccache on MSYS2
+ CCACHE_VARIANT: ${{ (runner.os == 'Windows' && inputs.msystem != '') && 'ccache' || 'sccache' }}
+ run: |
+ echo "CMAKE_C_COMPILER_LAUNCHER=$CCACHE_VARIANT" >> "$GITHUB_ENV"
+ echo "CMAKE_CXX_COMPILER_LAUNCHER=$CCACHE_VARIANT" >> "$GITHUB_ENV"
+
+ - name: Install Qt
+ if: ${{ inputs.msystem == '' }}
+ uses: jurplel/install-qt-action@v4
+ with:
+ aqtversion: "==3.1.*"
+ version: ${{ inputs.qt-version }}
+ 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
new file mode 100644
index 0000000000..fe7ee21425
--- /dev/null
+++ b/.github/actions/setup-dependencies/linux/action.yml
@@ -0,0 +1,54 @@
+name: Setup Linux dependencies
+description: Install and setup dependencies for building Prism Launcher
+
+runs:
+ using: composite
+
+ steps:
+ - name: Install host dependencies
+ shell: bash
+ run: |
+ sudo apt-get -y update
+ sudo apt-get -y install \
+ dpkg-dev \
+ ninja-build extra-cmake-modules pkg-config scdoc \
+ cmark gamemode-dev libarchive-dev libcmark-dev libqrencode-dev zlib1g-dev \
+ libxcb-cursor-dev libtomlplusplus-dev libvulkan-dev
+
+ - name: Setup AppImage tooling
+ shell: bash
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ # Determinate AppImage architecture to use
+ dpkg_arch="$(dpkg-architecture -q DEB_HOST_ARCH_CPU)"
+ case "$dpkg_arch" in
+ "amd64")
+ APPIMAGE_ARCH="x86_64"
+ ;;
+ "arm64")
+ APPIMAGE_ARCH="aarch64"
+ ;;
+ *)
+ echo "# 🚨 The Debian architecture \"$deb_arch\" is not recognized!" >> "$GITHUB_STEP_SUMMARY"
+ exit 1
+ ;;
+ 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 DioEgizio/go-appimage \
+ --pattern "mkappimage-*-$APPIMAGE_ARCH.AppImage" \
+ --output ~/bin/mkappimage
+
+ gh release download \
+ --repo AppImageCommunity/AppImageUpdate \
+ --pattern "AppImageUpdate-$APPIMAGE_ARCH.AppImage" \
+ --output ~/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
new file mode 100644
index 0000000000..a90544be00
--- /dev/null
+++ b/.github/actions/setup-dependencies/macos/action.yml
@@ -0,0 +1,47 @@
+name: Setup macOS dependencies
+
+inputs:
+ build-type:
+ description: Type for the build
+ required: true
+ default: Debug
+
+runs:
+ using: composite
+
+ steps:
+ - name: Install dependencies
+ shell: bash
+ run: |
+ brew update
+ brew install ninja extra-cmake-modules temurin@17 mono
+
+ - name: Set JAVA_HOME
+ shell: bash
+ run: |
+ echo "JAVA_HOME=$(/usr/libexec/java_home -v 17)" >> "$GITHUB_ENV"
+
+ - name: Setup vcpkg cache
+ if: ${{ inputs.build-type == 'Debug' }}
+ shell: bash
+ env:
+ USERNAME: ${{ github.repository_owner }}
+ GITHUB_TOKEN: ${{ github.token }}
+ FEED_URL: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
+ run: |
+ mono `vcpkg fetch nuget | tail -n 1` \
+ sources add \
+ -Source "$FEED_URL" \
+ -StorePasswordInClearText \
+ -Name GitHubPackages \
+ -UserName "$USERNAME" \
+ -Password "$GITHUB_TOKEN"
+ mono `vcpkg fetch nuget | tail -n 1` \
+ setapikey "$GITHUB_TOKEN" \
+ -Source "$FEED_URL"
+ echo "VCPKG_BINARY_SOURCES=clear;nuget,$FEED_URL,readwrite" >> "$GITHUB_ENV"
+
+ - name: Setup vcpkg environment
+ shell: bash
+ run: |
+ 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
new file mode 100644
index 0000000000..d2d0820d8a
--- /dev/null
+++ b/.github/actions/setup-dependencies/windows/action.yml
@@ -0,0 +1,108 @@
+name: Setup Windows Dependencies
+description: Install and setup dependencies for building Prism Launcher
+
+inputs:
+ build-type:
+ description: Type for the build
+ required: true
+ default: Debug
+ msystem:
+ description: MSYS2 subsystem to use
+ required: false
+ vcvars-arch:
+ description: Visual Studio architecture to use
+ required: true
+ default: amd64
+
+runs:
+ using: composite
+
+ steps:
+ # NOTE: Installed on MinGW as well for SignTool
+ - name: Enter VS Developer shell
+ if: ${{ runner.os == 'Windows' }}
+ uses: ilammy/msvc-dev-cmd@v1
+ with:
+ arch: ${{ inputs.vcvars-arch }}
+ vsversion: 2022
+
+ - name: Setup Java (MSVC)
+ uses: actions/setup-java@v5
+ with:
+ # NOTE(@getchoo): We should probably stay on Zulu.
+ # Temurin doesn't have Java 17 builds for WoA
+ distribution: zulu
+ java-version: 17
+
+ - name: Setup vcpkg cache (MSVC)
+ if: ${{ inputs.msystem == '' && inputs.build-type == 'Debug' }}
+ shell: pwsh
+ env:
+ USERNAME: ${{ github.repository_owner }}
+ GITHUB_TOKEN: ${{ github.token }}
+ FEED_URL: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
+ run: |
+ .$(vcpkg fetch nuget) `
+ sources add `
+ -Source "$env:FEED_URL" `
+ -StorePasswordInClearText `
+ -Name GitHubPackages `
+ -UserName "$env:USERNAME" `
+ -Password "$env:GITHUB_TOKEN"
+ .$(vcpkg fetch nuget) `
+ setapikey "$env:GITHUB_TOKEN" `
+ -Source "$env:FEED_URL"
+ "VCPKG_BINARY_SOURCES=clear;nuget,$env:FEED_URL,readwrite" | Out-File -Append $env:GITHUB_ENV
+
+ - name: Setup vcpkg environment (MSVC)
+ if: ${{ inputs.msystem == '' }}
+ shell: bash
+ run: |
+ echo "VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> "$GITHUB_ENV"
+
+ - name: Setup MSYS2 (MinGW)
+ if: ${{ inputs.msystem != '' }}
+ uses: msys2/setup-msys2@v2
+ with:
+ msystem: ${{ inputs.msystem }}
+ update: true
+ install: >-
+ git
+ pacboy: >-
+ toolchain:p
+ ccache:p
+ cmake:p
+ extra-cmake-modules:p
+ ninja:p
+ qt6-base:p
+ qt6-svg:p
+ qt6-imageformats:p
+ qt6-networkauth:p
+ cmark:p
+ qrencode:p
+ tomlplusplus:p
+ libarchive:p
+
+ - name: List pacman packages (MinGW)
+ if: ${{ inputs.msystem != '' }}
+ shell: msys2 {0}
+ run: |
+ pacman -Qe
+
+ - name: Retrieve ccache cache (MinGW)
+ if: ${{ inputs.msystem != '' && inputs.build-type == 'Debug' }}
+ uses: actions/cache@v5.0.4
+ with:
+ path: '${{ github.workspace }}\.ccache'
+ key: ${{ runner.os }}-mingw-w64-ccache-${{ github.run_id }}
+ restore-keys: |
+ ${{ runner.os }}-mingw-w64-ccache
+
+ - name: Setup ccache (MinGW)
+ if: ${{ inputs.msystem != '' && inputs.build-type == 'Debug' }}
+ shell: msys2 {0}
+ run: |
+ ccache --set-config=cache_dir='${{ github.workspace }}\.ccache'
+ ccache --set-config=max_size='500M'
+ ccache --set-config=compression=true
+ ccache -p # Show config
diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml
index 60bd86eecb..02fba2f68a 100644
--- a/.github/workflows/backport.yml
+++ b/.github/workflows/backport.yml
@@ -8,8 +8,7 @@ on:
# the GitHub repository. This means that it should not evaluate user input in a
# way that allows code injection.
-permissions:
- contents: read
+permissions: {}
jobs:
backport:
@@ -19,13 +18,13 @@ jobs:
actions: write # for korthout/backport-action to create PR with workflow changes
name: Backport Pull Request
if: github.repository_owner == 'PrismLauncher' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
- runs-on: ubuntu-latest
+ runs-on: ubuntu-slim
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Create backport PRs
- uses: korthout/backport-action@v2.5.0
+ uses: korthout/backport-action@v4.3.0
with:
# Config README: https://github.com/korthout/backport-action#backport-action
pull_description: |-
diff --git a/.github/workflows/blocked-prs.yml b/.github/workflows/blocked-prs.yml
new file mode 100644
index 0000000000..0010801540
--- /dev/null
+++ b/.github/workflows/blocked-prs.yml
@@ -0,0 +1,257 @@
+name: Blocked/Stacked Pull Requests Automation
+
+on:
+ pull_request_target:
+ types:
+ - opened
+ - reopened
+ - edited
+ - synchronize
+ workflow_dispatch:
+ inputs:
+ pr_id:
+ description: Local Pull Request number to work on
+ required: true
+ type: number
+
+permissions: {}
+
+jobs:
+ blocked_status:
+ name: Check Blocked Status
+ runs-on: ubuntu-slim
+
+ steps:
+ - name: Generate token
+ id: generate-token
+ uses: actions/create-github-app-token@v3
+ with:
+ app-id: ${{ vars.PULL_REQUEST_APP_ID }}
+ private-key: ${{ secrets.PULL_REQUEST_APP_PRIVATE_KEY }}
+
+ - name: Setup From Dispatch Event
+ if: github.event_name == 'workflow_dispatch'
+ id: dispatch_event_setup
+ env:
+ GH_TOKEN: ${{ steps.generate-token.outputs.token }}
+ PR_NUMBER: ${{ inputs.pr_id }}
+ run: |
+ # setup env for the rest of the workflow
+ OWNER=$(dirname "${{ github.repository }}")
+ REPO=$(basename "${{ github.repository }}")
+ PR_JSON=$(
+ gh api \
+ -H "Accept: application/vnd.github.raw+json" \
+ -H "X-GitHub-Api-Version: 2022-11-28" \
+ "/repos/$OWNER/$REPO/pulls/$PR_NUMBER"
+ )
+ echo "PR_JSON=$PR_JSON" >> "$GITHUB_ENV"
+
+ - name: Setup Environment
+ id: env_setup
+ env:
+ EVENT_PR_JSON: ${{ toJSON(github.event.pull_request) }}
+ run: |
+ # setup env for the rest of the workflow
+ PR_JSON=${PR_JSON:-"$EVENT_PR_JSON"}
+ {
+ echo "REPO=$(jq -r '.base.repo.name' <<< "$PR_JSON")"
+ echo "OWNER=$(jq -r '.base.repo.owner.login' <<< "$PR_JSON")"
+ echo "PR_NUMBER=$(jq -r '.number' <<< "$PR_JSON")"
+ echo "JOB_DATA=$(jq -c '
+ {
+ "repo": .base.repo.name,
+ "owner": .base.repo.owner.login,
+ "repoUrl": .base.repo.html_url,
+ "prNumber": .number,
+ "prHeadSha": .head.sha,
+ "prHeadLabel": .head.label,
+ "prBody": (.body // ""),
+ "prLabels": (reduce .labels[].name as $l ([]; . + [$l]))
+ }
+ ' <<< "$PR_JSON")"
+ } >> "$GITHUB_ENV"
+
+
+ - name: Find Blocked/Stacked PRs in body
+ id: pr_ids
+ run: |
+ prs=$(
+ jq -c '
+ .prBody as $body
+ | (
+ $body |
+ reduce (
+ . | scan("[Bb]locked (?:[Bb]y|[Oo]n):? #([0-9]+)")
+ | map({
+ "type": "Blocked on",
+ "number": ( . | tonumber )
+ })
+ ) as $i ([]; . + [$i[]])
+ ) as $bprs
+ | (
+ $body |
+ reduce (
+ . | scan("[Ss]tacked [Oo]n:? #([0-9]+)")
+ | map({
+ "type": "Stacked on",
+ "number": ( . | tonumber )
+ })
+ ) as $i ([]; . + [$i[]])
+ ) as $sprs
+ | ($bprs + $sprs) as $prs
+ | {
+ "blocking": $prs,
+ "numBlocking": ( $prs | length),
+ }
+ ' <<< "$JOB_DATA"
+ )
+ echo "prs=$prs" >> "$GITHUB_OUTPUT"
+
+ - name: Collect Blocked PR Data
+ id: blocking_data
+ if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0
+ env:
+ GH_TOKEN: ${{ steps.generate-token.outputs.token }}
+ BLOCKING_PRS: ${{ steps.pr_ids.outputs.prs }}
+ run: |
+ blocked_pr_data=$(
+ while read -r pr_data ; do
+ gh api \
+ -H "Accept: application/vnd.github+json" \
+ -H "X-GitHub-Api-Version: 2022-11-28" \
+ "/repos/$OWNER/$REPO/pulls/$(jq -r '.number' <<< "$pr_data")" \
+ | jq -c --arg type "$(jq -r '.type' <<< "$pr_data")" \
+ '
+ . | {
+ "type": $type,
+ "number": .number,
+ "merged": .merged,
+ "state": (if .state == "open" then "Open" elif .merged then "Merged" else "Closed" end),
+ "labels": (reduce .labels[].name as $l ([]; . + [$l])),
+ "basePrUrl": .html_url,
+ "baseRepoName": .head.repo.name,
+ "baseRepoOwner": .head.repo.owner.login,
+ "baseRepoUrl": .head.repo.html_url,
+ "baseSha": .head.sha,
+ "baseRefName": .head.ref,
+ }
+ '
+ done < <(jq -c '.blocking[]' <<< "$BLOCKING_PRS") | jq -c -s
+ )
+ {
+ echo "data=$blocked_pr_data";
+ echo "all_merged=$(jq -r 'all(.[] | (.type == "Stacked on" and .merged) or (.type == "Blocked on" and (.state != "Open")); .)' <<< "$blocked_pr_data")";
+ echo "current_blocking=$(jq -c 'map(
+ select(
+ (.type == "Stacked on" and (.merged | not)) or
+ (.type == "Blocked on" and (.state == "Open"))
+ ) | .number
+ )' <<< "$blocked_pr_data" )";
+ } >> "$GITHUB_OUTPUT"
+
+ - name: Add 'blocked' Label if Missing
+ id: label_blocked
+ if: "(fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0) && !contains(fromJSON(env.JOB_DATA).prLabels, 'status: blocked') && !fromJSON(steps.blocking_data.outputs.all_merged)"
+ continue-on-error: true
+ env:
+ GH_TOKEN: ${{ steps.generate-token.outputs.token }}
+ run: |
+ gh -R ${{ github.repository }} issue edit --add-label 'status: blocked' "$PR_NUMBER"
+
+ - name: Remove 'blocked' Label if All Dependencies Are Merged
+ id: unlabel_blocked
+ if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 && fromJSON(steps.blocking_data.outputs.all_merged)
+ continue-on-error: true
+ env:
+ GH_TOKEN: ${{ steps.generate-token.outputs.token }}
+ run: |
+ gh -R ${{ github.repository }} issue edit --remove-label 'status: blocked' "$PR_NUMBER"
+
+ - name: Apply 'blocking' Label to Unmerged Dependencies
+ id: label_blocking
+ if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0
+ continue-on-error: true
+ env:
+ GH_TOKEN: ${{ steps.generate-token.outputs.token }}
+ BLOCKING_ISSUES: ${{ steps.blocking_data.outputs.current_blocking }}
+ run: |
+ while read -r pr ; do
+ gh -R ${{ github.repository }} issue edit --add-label 'status: blocking' "$pr" || true
+ done < <(jq -c '.[]' <<< "$BLOCKING_ISSUES")
+
+ - name: Apply Blocking PR Status Check
+ id: blocked_check
+ if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0
+ continue-on-error: true
+ env:
+ GH_TOKEN: ${{ steps.generate-token.outputs.token }}
+ BLOCKING_DATA: ${{ steps.blocking_data.outputs.data }}
+ run: |
+ pr_head_sha=$(jq -r '.prHeadSha' <<< "$JOB_DATA")
+ # create commit Status, overwrites previous identical context
+ while read -r pr_data ; do
+ DESC=$(
+ jq -r 'if .type == "Stacked on" then
+ "Stacked PR #" + (.number | tostring) + " is " + (if .merged then "" else "not yet " end) + "merged"
+ else
+ "Blocking PR #" + (.number | tostring) + " is " + (if .state == "Open" then "" else "not yet " end) + "merged or closed"
+ end ' <<< "$pr_data"
+ )
+ gh api \
+ --method POST \
+ -H "Accept: application/vnd.github+json" \
+ -H "X-GitHub-Api-Version: 2022-11-28" \
+ "/repos/${OWNER}/${REPO}/statuses/${pr_head_sha}" \
+ -f "state=$(jq -r 'if (.type == "Stacked on" and .merged) or (.type == "Blocked on" and (.state != "Open")) then "success" else "failure" end' <<< "$pr_data")" \
+ -f "target_url=$(jq -r '.basePrUrl' <<< "$pr_data" )" \
+ -f "description=$DESC" \
+ -f "context=ci/blocking-pr-check:$(jq '.number' <<< "$pr_data")"
+ done < <(jq -c '.[]' <<< "$BLOCKING_DATA")
+
+ - name: Context Comment
+ id: generate-comment
+ if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0
+ continue-on-error: true
+ env:
+ BLOCKING_DATA: ${{ steps.blocking_data.outputs.data }}
+ run: |
+ COMMENT_PATH="$(pwd)/temp_comment_file.txt"
+ echo '
PR Dependencies :pushpin:
' > "$COMMENT_PATH"
+ echo >> "$COMMENT_PATH"
+ pr_head_label=$(jq -r '.prHeadLabel' <<< "$JOB_DATA")
+ while read -r pr_data ; do
+ base_pr=$(jq -r '.number' <<< "$pr_data")
+ base_ref_name=$(jq -r '.baseRefName' <<< "$pr_data")
+ base_repo_owner=$(jq -r '.baseRepoOwner' <<< "$pr_data")
+ base_repo_name=$(jq -r '.baseRepoName' <<< "$pr_data")
+ compare_url="https://github.com/$base_repo_owner/$base_repo_name/compare/$base_ref_name...$pr_head_label"
+ status=$(jq -r '
+ if .type == "Stacked on" then
+ if .merged then ":heavy_check_mark: Merged" else ":x: Not Merged (" + .state + ")" end
+ else
+ if .state != "Open" then ":white_check_mark: " + .state else ":x: Open" end
+ end
+ ' <<< "$pr_data")
+ type=$(jq -r '.type' <<< "$pr_data")
+ echo " - $type #$base_pr $status [(compare)]($compare_url)" >> "$COMMENT_PATH"
+ done < <(jq -c '.[]' <<< "$BLOCKING_DATA")
+
+ {
+ echo 'body<> "$GITHUB_OUTPUT"
+
+ - name: 💬 PR Comment
+ if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0
+ continue-on-error: true
+ env:
+ GH_TOKEN: ${{ steps.generate-token.outputs.token }}
+ COMMENT_BODY: ${{ steps.generate-comment.outputs.body }}
+ run: |
+ gh -R ${{ github.repository }} issue comment "$PR_NUMBER" \
+ --body "$COMMENT_BODY" \
+ --create-if-none \
+ --edit-last
+
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index e502318a3b..0596906c8e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,626 +1,188 @@
name: Build
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
on:
+ merge_group:
+ types: [checks_requested]
+ pull_request:
workflow_call:
inputs:
- build_type:
- description: Type of build (Debug, Release, RelWithDebInfo, MinSizeRel)
+ build-type:
+ description: Type of build (Debug or Release)
type: string
default: Debug
- is_qt_cached:
- description: Enable Qt caching or not
+ environment:
+ description: Deployment environment to run under
+ type: string
+ workflow_dispatch:
+ inputs:
+ build-type:
+ description: Type of build (Debug or Release)
type: string
- default: true
- secrets:
- SPARKLE_ED25519_KEY:
- description: Private key for signing Sparkle updates
- required: false
- WINDOWS_CODESIGN_CERT:
- description: Certificate for signing Windows builds
- required: false
- WINDOWS_CODESIGN_PASSWORD:
- description: Password for signing Windows builds
- required: false
- APPLE_CODESIGN_CERT:
- description: Certificate for signing macOS builds
- required: false
- APPLE_CODESIGN_PASSWORD:
- description: Password for signing macOS builds
- required: false
- APPLE_CODESIGN_ID:
- description: Certificate ID for signing macOS builds
- required: false
- APPLE_NOTARIZE_APPLE_ID:
- description: Apple ID used for notarizing macOS builds
- required: false
- APPLE_NOTARIZE_TEAM_ID:
- description: Team ID used for notarizing macOS builds
- required: false
- APPLE_NOTARIZE_PASSWORD:
- description: Password used for notarizing macOS builds
- required: false
- GPG_PRIVATE_KEY:
- description: Private key for AppImage signing
- required: false
- GPG_PRIVATE_KEY_ID:
- description: ID for the GPG_PRIVATE_KEY, to select the signing key
- required: false
+ default: Debug
+
+permissions: {}
jobs:
build:
+ name: Build (${{ matrix.artifact-name }})
+
+ environment: ${{ inputs.environment || '' }}
+
+ permissions:
+ contents: read
+ # Required for Azure Trusted Signing
+ id-token: write
+ # Required for vcpkg binary cache
+ packages: write
+
strategy:
fail-fast: false
matrix:
include:
- - os: ubuntu-20.04
- qt_ver: 5
- qt_host: linux
- qt_arch: ""
- qt_version: "5.12.8"
- qt_modules: ""
-
- - os: ubuntu-20.04
- qt_ver: 6
- qt_host: linux
- qt_arch: ""
- qt_version: "6.2.4"
- qt_modules: "qt5compat qtimageformats"
+ - os: ubuntu-24.04
+ artifact-name: Linux
+ cmake-preset: linux
+ qt-version: 6.10.2
- - os: windows-2022
- name: "Windows-MinGW-w64"
- msystem: clang64
- vcvars_arch: "amd64_x86"
+ - os: ubuntu-24.04-arm
+ artifact-name: Linux-aarch64
+ cmake-preset: linux
+ qt-version: 6.10.2
- os: windows-2022
- name: "Windows-MSVC"
- msystem: ""
- architecture: "x64"
- vcvars_arch: "amd64"
- qt_ver: 6
- qt_host: windows
- qt_arch: ''
- qt_version: '6.7.0'
- qt_modules: 'qt5compat qtimageformats'
+ artifact-name: Windows-MinGW-w64
+ cmake-preset: windows_mingw
+ msystem: CLANG64
+ vcvars-arch: amd64_x86
+
+ - os: windows-11-arm
+ artifact-name: Windows-MinGW-arm64
+ cmake-preset: windows_mingw
+ msystem: CLANGARM64
+ vcvars-arch: arm64
- os: windows-2022
- name: "Windows-MSVC-arm64"
- msystem: ""
- architecture: "arm64"
- vcvars_arch: "amd64_arm64"
- qt_ver: 6
- qt_host: windows
- qt_arch: 'win64_msvc2019_arm64'
- qt_version: '6.7.0'
- qt_modules: 'qt5compat qtimageformats'
-
- - os: macos-12
- name: macOS
- macosx_deployment_target: 11.0
- qt_ver: 6
- qt_host: mac
- qt_arch: ''
- qt_version: '6.7.0'
- qt_modules: 'qt5compat qtimageformats'
-
- - os: macos-12
- name: macOS-Legacy
- macosx_deployment_target: 10.13
- qt_ver: 5
- qt_host: mac
- qt_version: "5.15.2"
- qt_modules: ""
+ artifact-name: Windows-MSVC
+ 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-26
+ artifact-name: macOS
+ cmake-preset: macos_universal
+ macosx-deployment-target: 12.0
+ qt-version: 6.9.3
runs-on: ${{ matrix.os }}
+ defaults:
+ run:
+ shell: ${{ matrix.msystem != '' && 'msys2 {0}' || 'bash' }}
+
env:
- MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }}
- INSTALL_DIR: "install"
- INSTALL_PORTABLE_DIR: "install-portable"
- INSTALL_APPIMAGE_DIR: "install-appdir"
- BUILD_DIR: "build"
- CCACHE_VAR: ""
- HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
+ ARTIFACT_NAME: ${{ matrix.artifact-name }}-Qt6
+ BUILD_PLATFORM: official
+ BUILD_TYPE: ${{ inputs.build-type || 'Debug' }}
+ CMAKE_PRESET: ${{ matrix.cmake-preset }}
+
+ MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx-deployment-target }}
steps:
##
- # PREPARE
+ # SETUP
##
+
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
- submodules: "true"
+ submodules: true
- - name: "Setup MSYS2"
- if: runner.os == 'Windows' && matrix.msystem != ''
- uses: msys2/setup-msys2@v2
+ - name: Setup dependencies
+ id: setup-dependencies
+ uses: ./.github/actions/setup-dependencies
with:
+ build-type: ${{ env.BUILD_TYPE }}
+ artifact-name: ${{ matrix.artifact-name }}
msystem: ${{ matrix.msystem }}
- update: true
- install: >-
- git
- mingw-w64-x86_64-binutils
- pacboy: >-
- toolchain:p
- cmake:p
- extra-cmake-modules:p
- ninja:p
- qt6-base:p
- qt6-svg:p
- qt6-imageformats:p
- quazip-qt6:p
- ccache:p
- qt6-5compat:p
- cmark:p
-
- - name: Force newer ccache
- if: runner.os == 'Windows' && matrix.msystem == '' && inputs.build_type == 'Debug'
- run: |
- choco install ccache --version 4.7.1
-
- - name: Setup ccache
- if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug'
- uses: hendrikmuhs/ccache-action@v1.2.13
- with:
- key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }}
-
- - name: Retrieve ccache cache (Windows MinGW-w64)
- if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
- uses: actions/cache@v4.0.2
- with:
- path: '${{ github.workspace }}\.ccache'
- key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
- restore-keys: |
- ${{ matrix.os }}-mingw-w64-ccache
-
- - name: Setup ccache (Windows MinGW-w64)
- if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
- shell: msys2 {0}
- run: |
- ccache --set-config=cache_dir='${{ github.workspace }}\.ccache'
- ccache --set-config=max_size='500M'
- ccache --set-config=compression=true
- ccache -p # Show config
- ccache -z # Zero stats
-
- - name: Use ccache on Debug builds only
- if: inputs.build_type == 'Debug'
- shell: bash
- run: |
- echo "CCACHE_VAR=ccache" >> $GITHUB_ENV
-
- - name: Set short version
- shell: bash
- run: |
- ver_short=`git rev-parse --short HEAD`
- echo "VERSION=$ver_short" >> $GITHUB_ENV
-
- - name: Install Dependencies (Linux)
- if: runner.os == 'Linux'
- run: |
- sudo apt-get -y update
- sudo apt-get -y install ninja-build extra-cmake-modules scdoc appstream
-
- - name: Install Dependencies (macOS)
- if: runner.os == 'macOS'
- run: |
- brew update
- brew install ninja extra-cmake-modules
-
- - name: Install host Qt (Windows MSVC arm64)
- if: runner.os == 'Windows' && matrix.architecture == 'arm64'
- uses: jurplel/install-qt-action@v3
- with:
- aqtversion: "==3.1.*"
- py7zrversion: ">=0.20.2"
- version: ${{ matrix.qt_version }}
- host: "windows"
- target: "desktop"
- arch: ""
- modules: ${{ matrix.qt_modules }}
- cache: ${{ inputs.is_qt_cached }}
- cache-key-prefix: host-qt-arm64-windows
- dir: ${{ github.workspace }}\HostQt
- set-env: false
-
- - name: Install Qt (macOS, Linux & Windows MSVC)
- if: matrix.msystem == ''
- uses: jurplel/install-qt-action@v3
- with:
- aqtversion: "==3.1.*"
- py7zrversion: ">=0.20.2"
- version: ${{ matrix.qt_version }}
- target: "desktop"
- arch: ${{ matrix.qt_arch }}
- modules: ${{ matrix.qt_modules }}
- tools: ${{ matrix.qt_tools }}
- cache: ${{ inputs.is_qt_cached }}
-
- - name: Install MSVC (Windows MSVC)
- if: runner.os == 'Windows' # We want this for MinGW builds as well, as we need SignTool
- uses: ilammy/msvc-dev-cmd@v1
- with:
- vsversion: 2022
- arch: ${{ matrix.vcvars_arch }}
-
- - name: Prepare AppImage (Linux)
- if: runner.os == 'Linux' && matrix.qt_ver != 5
- run: |
- wget "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage"
- wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage"
- wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage"
-
- wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage"
-
- sudo apt install libopengl0
-
- - name: Add QT_HOST_PATH var (Windows MSVC arm64)
- if: runner.os == 'Windows' && matrix.architecture == 'arm64'
- run: |
- echo "QT_HOST_PATH=${{ github.workspace }}\HostQt\Qt\${{ matrix.qt_version }}\msvc2019_64" >> $env:GITHUB_ENV
-
- ##
- # CONFIGURE
- ##
-
- - name: Configure CMake (macOS)
- if: runner.os == 'macOS' && matrix.qt_ver == 6
- run: |
- cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja
-
- - name: Configure CMake (macOS-Legacy)
- if: runner.os == 'macOS' && matrix.qt_ver == 5
- run: |
- cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja
-
- - name: Configure CMake (Windows MinGW-w64)
- if: runner.os == 'Windows' && matrix.msystem != ''
- shell: msys2 {0}
- run: |
- cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} -G Ninja
-
- - name: Configure CMake (Windows MSVC)
- if: runner.os == 'Windows' && matrix.msystem == ''
- run: |
- cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }}
- # https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
- if ("${{ env.CCACHE_VAR }}")
- {
- Copy-Item C:/ProgramData/chocolatey/lib/ccache/tools/ccache-4.7.1-windows-x86_64/ccache.exe -Destination C:/ProgramData/chocolatey/lib/ccache/tools/ccache-4.7.1-windows-x86_64/cl.exe
- echo "CLToolExe=cl.exe" >> $env:GITHUB_ENV
- echo "CLToolPath=C:/ProgramData/chocolatey/lib/ccache/tools/ccache-4.7.1-windows-x86_64/" >> $env:GITHUB_ENV
- echo "TrackFileAccess=false" >> $env:GITHUB_ENV
- }
- # Needed for ccache, but also speeds up compile
- echo "UseMultiToolTask=true" >> $env:GITHUB_ENV
-
- - name: Configure CMake (Linux)
- if: runner.os == 'Linux'
- run: |
- cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -G Ninja
+ vcvars-arch: ${{ matrix.vcvars-arch }}
+ qt-version: ${{ matrix.qt-version }}
##
# BUILD
##
- - name: Build
- if: runner.os != 'Windows'
+ - name: Configure project
run: |
- cmake --build ${{ env.BUILD_DIR }}
+ cmake --preset "$CMAKE_PRESET"
- - name: Build (Windows MinGW-w64)
- if: runner.os == 'Windows' && matrix.msystem != ''
- shell: msys2 {0}
+ - name: Run build
run: |
- cmake --build ${{ env.BUILD_DIR }}
+ cmake --build --preset "$CMAKE_PRESET" --config "$BUILD_TYPE"
- - name: Build (Windows MSVC)
- if: runner.os == 'Windows' && matrix.msystem == ''
+ - name: Run tests
run: |
- cmake --build ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }}
+ ctest --preset "$CMAKE_PRESET" --build-config "$BUILD_TYPE"
##
- # TEST
+ # PACKAGE
##
- - name: Test
- if: runner.os != 'Windows'
- run: |
- ctest -E "^example64|example$" --test-dir build --output-on-failure
-
- - name: Test (Windows MinGW-w64)
- if: runner.os == 'Windows' && matrix.msystem != ''
- shell: msys2 {0}
- run: |
- ctest -E "^example64|example$" --test-dir build --output-on-failure
-
- - name: Test (Windows MSVC)
- if: runner.os == 'Windows' && matrix.msystem == '' && matrix.architecture != 'arm64'
- run: |
- ctest -E "^example64|example$" --test-dir build --output-on-failure -C ${{ inputs.build_type }}
-
- ##
- # PACKAGE BUILDS
- ##
-
- - name: Fetch codesign certificate (macOS)
- if: runner.os == 'macOS'
- run: |
- echo '${{ secrets.APPLE_CODESIGN_CERT }}' | base64 --decode > codesign.p12
- if [ -n '${{ secrets.APPLE_CODESIGN_ID }}' ]; then
- security create-keychain -p '${{ secrets.APPLE_CODESIGN_PASSWORD }}' build.keychain
- security default-keychain -s build.keychain
- security unlock-keychain -p '${{ secrets.APPLE_CODESIGN_PASSWORD }}' build.keychain
- security import codesign.p12 -k build.keychain -P '${{ secrets.APPLE_CODESIGN_PASSWORD }}' -T /usr/bin/codesign
- security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k '${{ secrets.APPLE_CODESIGN_PASSWORD }}' build.keychain
- else
- echo ":warning: Using ad-hoc code signing for macOS, as certificate was not present." >> $GITHUB_STEP_SUMMARY
- fi
-
- - name: Package (macOS)
- if: runner.os == 'macOS'
- run: |
- cmake --install ${{ env.BUILD_DIR }}
-
- cd ${{ env.INSTALL_DIR }}
- chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher"
-
- if [ -n '${{ secrets.APPLE_CODESIGN_ID }}' ]; then
- APPLE_CODESIGN_ID='${{ secrets.APPLE_CODESIGN_ID }}'
- else
- APPLE_CODESIGN_ID='-'
- fi
-
- sudo codesign --sign "$APPLE_CODESIGN_ID" --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PrismLauncher.app/Contents/MacOS/prismlauncher"
- mv "PrismLauncher.app" "Prism Launcher.app"
-
- - name: Notarize (macOS)
- if: runner.os == 'macOS'
- run: |
- cd ${{ env.INSTALL_DIR }}
-
- if [ -n '${{ secrets.APPLE_NOTARIZE_PASSWORD }}' ]; then
- ditto -c -k --sequesterRsrc --keepParent "Prism Launcher.app" ../PrismLauncher.zip
- xcrun notarytool submit ../PrismLauncher.zip \
- --wait --progress \
- --apple-id '${{ secrets.APPLE_NOTARIZE_APPLE_ID }}' \
- --team-id '${{ secrets.APPLE_NOTARIZE_TEAM_ID }}' \
- --password '${{ secrets.APPLE_NOTARIZE_PASSWORD }}'
-
- xcrun stapler staple "Prism Launcher.app"
- else
- echo ":warning: Skipping notarization as credentials are not present." >> $GITHUB_STEP_SUMMARY
- fi
- ditto -c -k --sequesterRsrc --keepParent "Prism Launcher.app" ../PrismLauncher.zip
-
- - name: Make Sparkle signature (macOS)
- if: matrix.name == 'macOS'
- run: |
- if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then
- brew install openssl@3
- echo '${{ secrets.SPARKLE_ED25519_KEY }}' > ed25519-priv.pem
- signature=$(/usr/local/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.zip -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
- rm ed25519-priv.pem
- cat >> $GITHUB_STEP_SUMMARY << EOF
- ### Artifact Information :information_source:
- - :memo: Sparkle Signature (ed25519): \`$signature\`
- EOF
- else
- cat >> $GITHUB_STEP_SUMMARY << EOF
- ### Artifact Information :information_source:
- - :warning: Sparkle Signature (ed25519): No private key available (likely a pull request or fork)
- EOF
- fi
-
- - name: Package (Windows MinGW-w64)
- if: runner.os == 'Windows' && matrix.msystem != ''
- shell: msys2 {0}
- run: |
- cmake --install ${{ env.BUILD_DIR }}
- touch ${{ env.INSTALL_DIR }}/manifest.txt
- for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_DIR }}/manifest.txt
-
- - name: Package (Windows MSVC)
- if: runner.os == 'Windows' && matrix.msystem == ''
- run: |
- cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }}
-
- cd ${{ github.workspace }}
-
- 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: Fetch codesign certificate (Windows)
- if: runner.os == 'Windows'
- shell: bash # yes, we are not using MSYS2 or PowerShell here
- run: |
- echo '${{ secrets.WINDOWS_CODESIGN_CERT }}' | base64 --decode > codesign.pfx
-
- - name: Sign executable (Windows)
- if: runner.os == 'Windows'
- run: |
- if (Get-Content ./codesign.pfx){
- cd ${{ env.INSTALL_DIR }}
- # We ship the exact same executable for portable and non-portable editions, so signing just once is fine
- SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_updater.exe prismlauncher_filelink.exe
- } else {
- ":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
- }
-
- - name: Package (Windows MinGW-w64, portable)
- if: runner.os == 'Windows' && matrix.msystem != ''
- shell: msys2 {0}
- run: |
- cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
- cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
- for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
-
- - name: Package (Windows MSVC, portable)
- if: runner.os == 'Windows' && matrix.msystem == ''
- run: |
- cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
- cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
-
- Get-ChildItem ${{ env.INSTALL_PORTABLE_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_PORTABLE_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
-
- - name: Package (Windows, installer)
- if: runner.os == 'Windows'
- run: |
- cd ${{ env.INSTALL_DIR }}
- makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
-
- - name: Sign installer (Windows)
- if: runner.os == 'Windows'
- run: |
- if (Get-Content ./codesign.pfx){
- SignTool sign /fd sha256 /td sha256 /f codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com PrismLauncher-Setup.exe
- } else {
- ":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
- }
-
- - name: Package AppImage (Linux)
- if: runner.os == 'Linux' && matrix.qt_ver != 5
+ - name: Get short version
+ id: short-version
shell: bash
- env:
- GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
- run: |
- cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
-
- mv ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.metainfo.xml ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.appdata.xml
- export "NO_APPSTREAM=1" # we have to skip appstream checking because appstream on ubuntu 20.04 is outdated
-
- export OUTPUT="PrismLauncher-Linux-x86_64.AppImage"
-
- chmod +x linuxdeploy-*.AppImage
-
- mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib
- mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
-
- cp -r ${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
-
- cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
- cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
- cp /usr/lib/x86_64-linux-gnu/libOpenGL.so.0* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
-
- LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
- export LD_LIBRARY_PATH
-
- chmod +x AppImageUpdate-x86_64.AppImage
- cp AppImageUpdate-x86_64.AppImage ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin
-
- export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-x86_64.AppImage.zsync"
-
- if [ '${{ secrets.GPG_PRIVATE_KEY_ID }}' != '' ]; then
- export SIGN=1
- export SIGN_KEY=${{ secrets.GPG_PRIVATE_KEY_ID }}
- mkdir -p ~/.gnupg/
- echo "$GPG_PRIVATE_KEY" > ~/.gnupg/private.key
- gpg --import ~/.gnupg/private.key
- else
- echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY
- fi
-
- ./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.prismlauncher.PrismLauncher.svg
-
- mv "PrismLauncher-Linux-x86_64.AppImage" "PrismLauncher-Linux-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
-
- - name: Package (Linux, portable)
- if: runner.os == 'Linux'
run: |
- cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_PORTABLE_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -DINSTALL_BUNDLE=full -G Ninja
- cmake --install ${{ env.BUILD_DIR }}
- cmake --install ${{ env.BUILD_DIR }} --component portable
-
- mkdir ${{ env.INSTALL_PORTABLE_DIR }}/lib
- cp /lib/x86_64-linux-gnu/libbz2.so.1.0 ${{ env.INSTALL_PORTABLE_DIR }}/lib
- cp /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0 ${{ env.INSTALL_PORTABLE_DIR }}/lib
- cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_PORTABLE_DIR }}/lib
- cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_PORTABLE_DIR }}/lib
- cp /usr/lib/x86_64-linux-gnu/libffi.so.7 ${{ env.INSTALL_PORTABLE_DIR }}/lib
- mv ${{ env.INSTALL_PORTABLE_DIR }}/bin/*.so* ${{ env.INSTALL_PORTABLE_DIR }}/lib
-
- 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
- cd ${{ env.INSTALL_PORTABLE_DIR }}
- tar -czf ../PrismLauncher-portable.tar.gz *
+ echo "version=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
- ##
- # UPLOAD BUILDS
- ##
-
- - name: Upload binary tarball (macOS)
- if: runner.os == 'macOS'
- uses: actions/upload-artifact@v4
- with:
- name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
- path: PrismLauncher.zip
-
- - name: Upload binary zip (Windows)
- if: runner.os == 'Windows'
- uses: actions/upload-artifact@v4
- with:
- name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
- path: ${{ env.INSTALL_DIR }}/**
-
- - name: Upload binary zip (Windows, portable)
- if: runner.os == 'Windows'
- uses: actions/upload-artifact@v4
+ - name: Package (Linux)
+ if: ${{ runner.os == 'Linux' }}
+ uses: ./.github/actions/package/linux
with:
- name: PrismLauncher-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
- path: ${{ env.INSTALL_PORTABLE_DIR }}/**
+ version: ${{ steps.short-version.outputs.version }}
+ build-type: ${{ steps.setup-dependencies.outputs.build-type }}
+ artifact-name: ${{ matrix.artifact-name }}
+ qt-version: ${{ steps.setup-dependencies.outputs.qt-version }}
- - name: Upload installer (Windows)
- if: runner.os == 'Windows'
- uses: actions/upload-artifact@v4
- with:
- name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
- path: PrismLauncher-Setup.exe
+ gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
+ gpg-private-key-id: ${{ secrets.GPG_PRIVATE_KEY_ID }}
- - name: Upload binary tarball (Linux, portable, Qt 5)
- if: runner.os == 'Linux' && matrix.qt_ver != 6
- uses: actions/upload-artifact@v4
- with:
- name: PrismLauncher-${{ runner.os }}-Qt5-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
- path: PrismLauncher-portable.tar.gz
-
- - name: Upload binary tarball (Linux, portable, Qt 6)
- if: runner.os == 'Linux' && matrix.qt_ver != 5
- uses: actions/upload-artifact@v4
- with:
- name: PrismLauncher-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
- path: PrismLauncher-portable.tar.gz
-
- - name: Upload AppImage (Linux)
- if: runner.os == 'Linux' && matrix.qt_ver != 5
- uses: actions/upload-artifact@v4
- with:
- name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
- path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
-
- - name: Upload AppImage Zsync (Linux)
- if: runner.os == 'Linux' && matrix.qt_ver != 5
- uses: actions/upload-artifact@v4
+ - name: Package (macOS)
+ if: ${{ runner.os == 'macOS' }}
+ uses: ./.github/actions/package/macos
+ with:
+ version: ${{ steps.short-version.outputs.version }}
+ build-type: ${{ steps.setup-dependencies.outputs.build-type }}
+ artifact-name: ${{ matrix.artifact-name }}
+
+ apple-codesign-cert: ${{ secrets.APPLE_CODESIGN_CERT }}
+ apple-codesign-password: ${{ secrets.APPLE_CODESIGN_PASSWORD }}
+ apple-codesign-id: ${{ secrets.APPLE_CODESIGN_ID }}
+ apple-notarize-apple-id: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
+ apple-notarize-team-id: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
+ apple-notarize-password: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
+ sparkle-ed25519-key: ${{ secrets.SPARKLE_ED25519_KEY }}
+
+ - 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:
- name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync
- path: PrismLauncher-Linux-x86_64.AppImage.zsync
-
- - name: ccache stats (Windows MinGW-w64)
- if: runner.os == 'Windows' && matrix.msystem != ''
- shell: msys2 {0}
- run: |
- ccache -s
+ version: ${{ steps.short-version.outputs.version }}
+ build-type: ${{ steps.setup-dependencies.outputs.build-type }}
+ artifact-name: ${{ matrix.artifact-name }}
+ msystem: ${{ matrix.msystem }}
- flatpak:
- runs-on: ubuntu-latest
- container:
- image: bilelmoussaoui/flatpak-github-actions:kde-5.15-23.08
- options: --privileged
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- if: inputs.build_type == 'Debug'
- with:
- submodules: "true"
- - name: Build Flatpak (Linux)
- if: inputs.build_type == 'Debug'
- uses: flatpak/flatpak-github-actions/flatpak-builder@v6
- with:
- bundle: "Prism Launcher.flatpak"
- manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
+ azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ azure-subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml
new file mode 100644
index 0000000000..5251c149a4
--- /dev/null
+++ b/.github/workflows/clang-tidy.yml
@@ -0,0 +1,53 @@
+name: Clang-Tidy Code Scanning
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+on:
+ merge_group:
+ types: [checks_requested]
+ pull_request:
+
+permissions: {}
+
+jobs:
+ clang-tidy:
+ name: Run Clang-Tidy
+
+ runs-on: ubuntu-latest
+
+ permissions:
+ contents: read
+ security-events: write
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v6
+ with:
+ fetch-depth: 0 # Required for diffing later on
+ submodules: "true"
+
+ - name: Setup sccache
+ uses: hendrikmuhs/ccache-action@v1.2.22
+ with:
+ variant: sccache
+
+ - name: Install Nix
+ uses: cachix/install-nix-action@v31
+
+ - name: Run build
+ # TODO(@getchoo): Figure out how to make this work with PCH
+ run: |
+ nix develop --command bash -c '
+ cmake -B build -D Launcher_USE_PCH=OFF -D CMAKE_CXX_COMPILER_LAUNCHER=sccache && cmake --build build
+ '
+
+ # TODO: Use SARIF after https://github.com/psastras/sarif-rs/issues/638 is fixed
+ - name: Run clang-tidy-diff
+ env:
+ BASE_REF: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }}
+ run: |
+ nix develop --command bash -c '
+ clang-tidy -verify-config && git diff -U0 --no-color "$BASE_REF" | clang-tidy-diff.py -p1 -quiet -only-check-in-db
+ '
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index d40d7eb686..f9705bf53b 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -1,35 +1,52 @@
name: "CodeQL Code Scanning"
-on: [ push, pull_request, workflow_dispatch ]
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+on:
+ merge_group:
+ types: [checks_requested]
+ pull_request:
+ workflow_dispatch:
+
+permissions: {}
jobs:
CodeQL:
runs-on: ubuntu-latest
-
+
+ permissions:
+ contents: read
+ security-events: write
+
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
- submodules: 'true'
+ submodules: "true"
- name: Initialize CodeQL
- uses: github/codeql-action/init@v3
+ uses: github/codeql-action/init@v4
with:
config-file: ./.github/codeql/codeql-config.yml
queries: security-and-quality
languages: cpp, java
- - name: Install Dependencies
- run:
- sudo apt-get -y update
-
- sudo apt-get -y install ninja-build extra-cmake-modules scdoc qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
+ - name: Setup dependencies
+ uses: ./.github/actions/setup-dependencies
+ with:
+ build-type: Debug
+ qt-version: 6.4.3
- name: Configure and Build
run: |
- cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr -DLauncher_QT_VERSION_MAJOR=5 -G Ninja
+ cmake --preset linux -DLauncher_USE_PCH=OFF
+ cmake --build --preset linux --config Debug
- cmake --build build
+ - name: Run tests
+ run: |
+ ctest --preset linux --build-config Debug --extra-verbose --output-on-failure
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
+ uses: github/codeql-action/analyze@v4
diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml
new file mode 100644
index 0000000000..a5dcdc48a0
--- /dev/null
+++ b/.github/workflows/container.yml
@@ -0,0 +1,174 @@
+name: Development Container
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+on:
+ merge_group:
+ types: [checks_requested]
+ pull_request:
+ workflow_dispatch:
+
+permissions: {}
+
+env:
+ REGISTRY: ghcr.io
+
+jobs:
+ build:
+ name: Build (${{ matrix.arch }})
+
+ permissions:
+ contents: read
+ packages: write
+
+ outputs:
+ image-name: ${{ steps.image-name.outputs.image-name }}
+
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - arch: arm64
+ os: ubuntu-24.04-arm
+ - arch: amd64
+ os: ubuntu-24.04-arm
+
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ - name: Set image name
+ id: image-name
+ run: |
+ echo "image-name=${REGISTRY}/${GITHUB_REPOSITORY_OWNER,,}/devcontainer" >> "$GITHUB_OUTPUT"
+
+ - name: Install Podman
+ uses: redhat-actions/podman-install@main
+ # TODO(@getchoo): Always use this when the action properly supports ARM
+ if: ${{ runner.arch == 'X64' || runner.arch == 'X86' }}
+ with:
+ github-token: ${{ github.token }}
+
+ - name: Checkout repository
+ uses: actions/checkout@v6
+
+ - name: Determine metadata for image
+ id: image-metadata
+ uses: docker/metadata-action@v6
+ with:
+ images: |
+ ${{ steps.image-name.outputs.image-name }}
+ flavor: |
+ latest=false
+ tags: |
+ type=raw,value=latest,enable=${{ github.event.merge_group.base_ref == 'refs/heads/develop' }}
+
+ type=sha
+ type=sha,format=long
+ type=ref,event=branch
+ type=ref,event=tag
+
+ - name: Build image
+ id: build-image
+ uses: redhat-actions/buildah-build@v2
+ with:
+ containerfiles: |
+ ./Containerfile
+ tags: ${{ steps.image-metadata.outputs.tags }}
+ labels: ${{ steps.image-metadata.outputs.labels }}
+
+ - name: Push image
+ id: push-image
+ if: ${{ github.event_name != 'pull_request' }}
+ uses: redhat-actions/push-to-registry@v2
+ with:
+ tags: ${{ steps.build-image.outputs.tags }}
+ username: ${{ github.repository_owner }}
+ password: ${{ github.token }}
+ tls-verify: true
+
+ - name: Export image digest
+ if: ${{ github.event_name != 'pull_request' }}
+ env:
+ DIGEST: ${{ steps.push-image.outputs.digest }}
+ run: |
+ mkdir -p "$RUNNER_TEMP"/digests
+ touch "$RUNNER_TEMP"/digests/"${DIGEST#sha256:}"
+
+ - name: Upload digest artifact
+ if: ${{ github.event_name != 'pull_request' }}
+ uses: actions/upload-artifact@v7
+ with:
+ name: digests-${{ matrix.arch }}
+ path: ${{ runner.temp }}/digests/*
+ if-no-files-found: error
+ retention-days: 1
+
+ manifest:
+ name: Create manifest
+
+ needs: [ build ]
+ if: ${{ github.event_name != 'pull_request' }}
+
+ permissions:
+ contents: read
+ packages: write
+
+ runs-on: ubuntu-24.04
+
+ steps:
+ - name: Download digests
+ uses: actions/download-artifact@v8
+ with:
+ path: ${{ runner.temp }}/digests
+ pattern: digests-*
+ merge-multiple: true
+
+ - name: Install Podman
+ # TODO(@getchoo): Always use this when the action properly supports ARM
+ if: ${{ runner.arch == 'X64' || runner.arch == 'X86' }}
+ uses: redhat-actions/podman-install@main
+ with:
+ github-token: ${{ github.token }}
+
+ - name: Login to registry
+ uses: redhat-actions/podman-login@v1
+ with:
+ registry: ${{ env.REGISTRY }}
+ username: ${{ github.repository_owner }}
+ password: ${{ github.token }}
+
+ - name: Determine metadata for manifest
+ id: manifest-metadata
+ uses: docker/metadata-action@v6
+ with:
+ images: |
+ ${{ needs.build.outputs.image-name }}
+ flavor: |
+ latest=false
+ tags: |
+ type=raw,value=latest,enable=${{ github.event.merge_group.base_ref == 'refs/heads/develop' }}
+
+ type=sha
+ type=sha,format=long
+ type=ref,event=branch
+ type=ref,event=tag
+
+ - name: Create manifest list
+ working-directory: ${{ runner.temp }}/digests
+ env:
+ IMAGE_NAME: ${{ needs.build.outputs.image-name }}
+ run: |
+ while read -r tag; do
+ podman manifest create "$tag" \
+ $(printf "$IMAGE_NAME@sha256:%s " *)
+ done <<< "$DOCKER_METADATA_OUTPUT_TAGS"
+
+ - name: Push manifest
+ uses: redhat-actions/push-to-registry@v2
+ with:
+ tags: ${{ steps.manifest-metadata.outputs.tags }}
+ username: ${{ github.repository_owner }}
+ password: ${{ github.token }}
+ tls-verify: true
diff --git a/.github/workflows/merge-blocking-pr.yml b/.github/workflows/merge-blocking-pr.yml
new file mode 100644
index 0000000000..3542a470e0
--- /dev/null
+++ b/.github/workflows/merge-blocking-pr.yml
@@ -0,0 +1,64 @@
+name: Merged Blocking Pull Request Automation
+
+on:
+ pull_request_target:
+ types:
+ - closed
+ workflow_dispatch:
+ inputs:
+ pr_id:
+ description: Local Pull Request number to work on
+ required: true
+ type: number
+
+permissions: {}
+
+jobs:
+ update-blocked-status:
+ name: Update Blocked Status
+ runs-on: ubuntu-slim
+
+ # a pr that was a `blocking:` label was merged.
+ # find the open pr's it was blocked by and trigger a refresh of their state
+ if: "${{ github.event_name == 'workflow_dispatch' || github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'status: blocking') }}"
+
+ steps:
+ - name: Generate token
+ id: generate-token
+ uses: actions/create-github-app-token@v3
+ with:
+ app-id: ${{ vars.PULL_REQUEST_APP_ID }}
+ private-key: ${{ secrets.PULL_REQUEST_APP_PRIVATE_KEY }}
+
+ - name: Gather Dependent PRs
+ id: gather_deps
+ env:
+ GH_TOKEN: ${{ steps.generate-token.outputs.token }}
+ PR_NUMBER: ${{ inputs.pr_id || github.event.pull_request.number }}
+ run: |
+ blocked_prs=$(
+ gh -R ${{ github.repository }} pr list --label 'status: blocked' --json 'number,body' \
+ | jq -c --argjson pr "$PR_NUMBER" '
+ reduce ( .[] | select(
+ .body |
+ scan("(?:blocked (?:by|on)|stacked on):? #([0-9]+)") |
+ map(tonumber) |
+ any(.[]; . == $pr)
+ )) as $i ([]; . + [$i])
+ '
+ )
+ {
+ echo "deps=$blocked_prs"
+ echo "numdeps=$(jq -r '. | length' <<< "$blocked_prs")"
+ } >> "$GITHUB_OUTPUT"
+
+ - name: Trigger Blocked PR Workflows for Dependants
+ if: fromJSON(steps.gather_deps.outputs.numdeps) > 0
+ env:
+ GH_TOKEN: ${{ steps.generate-token.outputs.token }}
+ DEPS: ${{ steps.gather_deps.outputs.deps }}
+ run: |
+ while read -r pr ; do
+ gh -R ${{ github.repository }} workflow run 'blocked-prs.yml' -r "${{ github.ref_name }}" -f pr_id="$pr"
+ done < <(jq -c '.[].number' <<< "$DEPS")
+
diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml
new file mode 100644
index 0000000000..0fea44f081
--- /dev/null
+++ b/.github/workflows/nix.yml
@@ -0,0 +1,138 @@
+name: Nix
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+on:
+ push:
+ branches:
+ - "develop"
+ - "release-*"
+ tags:
+ - "*"
+ paths:
+ # File types
+ - "**.cpp"
+ - "**.h"
+ - "**.java"
+ - "**.ui"
+ - "**.md"
+
+ # Build files
+ - "**.nix"
+ - "nix/**"
+ - "flake.lock"
+
+ # Directories
+ - "buildconfig/**"
+ - "cmake/**"
+ - "launcher/**"
+ - "libraries/**"
+ - "program_info/**"
+ - "tests/**"
+
+ # Files
+ - "CMakeLists.txt"
+
+ # Workflows
+ - ".github/workflows/nix.yml"
+ pull_request:
+ paths:
+ # File types
+ - "**.cpp"
+ - "**.h"
+ - "**.java"
+ - "**.ui"
+ - "**.md"
+
+ # Build files
+ - "**.nix"
+ - "nix/**"
+ - "flake.lock"
+
+ # Directories
+ - "buildconfig/**"
+ - "cmake/**"
+ - "launcher/**"
+ - "libraries/**"
+ - "program_info/**"
+ - "tests/**"
+
+ # Files
+ - "CMakeLists.txt"
+
+ # Workflows
+ - ".github/workflows/nix.yml"
+ workflow_dispatch:
+
+permissions: {}
+
+env:
+ DEBUG: ${{ github.ref_type != 'tag' }}
+
+jobs:
+ build:
+ name: Build (${{ matrix.system }})
+
+ permissions:
+ contents: read
+
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - os: ubuntu-22.04
+ system: x86_64-linux
+
+ - os: ubuntu-22.04-arm
+ system: aarch64-linux
+
+ - os: macos-26
+ system: aarch64-darwin
+
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v6
+
+ - name: Install Nix
+ uses: cachix/install-nix-action@v31
+
+ # For PRs
+ - name: Setup Nix Magic Cache
+ if: ${{ github.event_name == 'pull_request' }}
+ uses: DeterminateSystems/magic-nix-cache-action@v13
+ with:
+ diagnostic-endpoint: ""
+ use-flakehub: false
+
+ # For in-tree builds
+ - name: Setup Cachix
+ if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}
+ uses: cachix/cachix-action@v17
+ with:
+ name: prismlauncher
+ authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
+
+ - name: Run Flake checks
+ run: |
+ nix flake check --print-build-logs --show-trace
+
+ - name: Build debug package
+ if: ${{ env.DEBUG == 'true' }}
+ run: |
+ nix build \
+ --no-link --print-build-logs --print-out-paths \
+ .#prismlauncher-debug >> "$GITHUB_STEP_SUMMARY"
+
+ - name: Build release package
+ if: ${{ env.DEBUG == 'false' }}
+ env:
+ TAG: ${{ github.ref_name }}
+ SYSTEM: ${{ matrix.system }}
+ run: |
+ nix build --no-link --print-out-paths .#prismlauncher \
+ | tee -a "$GITHUB_STEP_SUMMARY" \
+ | xargs cachix pin prismlauncher "$TAG"-"$SYSTEM"
diff --git a/.github/workflows/winget.yml b/.github/workflows/publish.yml
similarity index 55%
rename from .github/workflows/winget.yml
rename to .github/workflows/publish.yml
index eacf230997..1bb1c5b503 100644
--- a/.github/workflows/winget.yml
+++ b/.github/workflows/publish.yml
@@ -1,13 +1,23 @@
-name: Publish to WinGet
+name: Publish
+
on:
release:
- types: [released]
+ types: [ released ]
+
+permissions: {}
jobs:
- publish:
- runs-on: windows-latest
+ winget:
+ name: Winget
+
+ permissions:
+ contents: read
+
+ runs-on: ubuntu-slim
+
steps:
- - uses: vedantmgoyal2009/winget-releaser@v2
+ - name: Publish on Winget
+ uses: vedantmgoyal2009/winget-releaser@v2
with:
identifier: PrismLauncher.PrismLauncher
version: ${{ github.event.release.tag_name }}
diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/release.yml
similarity index 62%
rename from .github/workflows/trigger_release.yml
rename to .github/workflows/release.yml
index 134281b2c9..ecc23effac 100644
--- a/.github/workflows/trigger_release.yml
+++ b/.github/workflows/release.yml
@@ -5,39 +5,38 @@ on:
tags:
- "*"
+permissions: {}
+
jobs:
build_release:
name: Build Release
uses: ./.github/workflows/build.yml
+ permissions:
+ contents: read
+ # Required for Azure Trusted Signing
+ id-token: write
+ # Required for vcpkg binary cache
+ packages: write
with:
- build_type: Release
- is_qt_cached: false
- secrets:
- SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
- WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
- WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
- APPLE_CODESIGN_CERT: ${{ secrets.APPLE_CODESIGN_CERT }}
- APPLE_CODESIGN_PASSWORD: ${{ secrets.APPLE_CODESIGN_PASSWORD }}
- APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }}
- APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
- APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
- APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
- GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
- GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
+ build-type: Release
+ environment: Release
+ secrets: inherit
create_release:
needs: build_release
- runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ runs-on: ubuntu-slim
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
submodules: "true"
path: "PrismLauncher-source"
- name: Download artifacts
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@v8
- name: Grab and store version
run: |
tag_name=$(echo ${{ github.ref }} | grep -oE "[^/]+$")
@@ -46,11 +45,13 @@ 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-Qt5-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
- mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage
- mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
- mv PrismLauncher-macOS-Legacy*/PrismLauncher.zip PrismLauncher-macOS-Legacy-${{ env.VERSION }}.zip
+ 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
+ mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*-aarch64.AppImage.zsync PrismLauncher-Linux-aarch64.AppImage.zsync
mv PrismLauncher-macOS*/PrismLauncher.zip PrismLauncher-macOS-${{ env.VERSION }}.zip
+ mv PrismLauncher-macOS*/PrismLauncher.dmg PrismLauncher-macOS-${{ env.VERSION }}.dmg
tar --exclude='.git' -czf PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }}
@@ -80,6 +81,17 @@ jobs:
cd ..
done
+ for d in PrismLauncher-Windows-MinGW-arm64*; do
+ cd "${d}" || continue
+ INST="$(echo -n ${d} | grep -o Setup || true)"
+ PORT="$(echo -n ${d} | grep -o Portable || true)"
+ NAME="PrismLauncher-Windows-MinGW-arm64"
+ test -z "${PORT}" || NAME="${NAME}-Portable"
+ test -z "${INST}" || mv PrismLauncher-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
+ test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" *
+ cd ..
+ done
+
- name: Create release
id: create_release
uses: softprops/action-gh-release@v2
@@ -89,14 +101,20 @@ jobs:
name: Prism Launcher ${{ env.VERSION }}
draft: true
prerelease: false
+ fail_on_unmatched_files: true
files: |
- PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
PrismLauncher-Linux-x86_64.AppImage
PrismLauncher-Linux-x86_64.AppImage.zsync
+ PrismLauncher-Linux-aarch64.AppImage
+ PrismLauncher-Linux-aarch64.AppImage.zsync
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
+ PrismLauncher-Linux-aarch64-Qt6-Portable-${{ env.VERSION }}.tar.gz
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip
PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-MinGW-w64-Setup-${{ env.VERSION }}.exe
+ PrismLauncher-Windows-MinGW-arm64-${{ env.VERSION }}.zip
+ PrismLauncher-Windows-MinGW-arm64-Portable-${{ env.VERSION }}.zip
+ PrismLauncher-Windows-MinGW-arm64-Setup-${{ env.VERSION }}.exe
PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-arm64-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-arm64-Setup-${{ env.VERSION }}.exe
@@ -104,5 +122,5 @@ jobs:
PrismLauncher-Windows-MSVC-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-Setup-${{ env.VERSION }}.exe
PrismLauncher-macOS-${{ env.VERSION }}.zip
- PrismLauncher-macOS-Legacy-${{ env.VERSION }}.zip
+ PrismLauncher-macOS-${{ env.VERSION }}.dmg
PrismLauncher-${{ env.VERSION }}.tar.gz
diff --git a/.github/workflows/trigger_builds.yml b/.github/workflows/trigger_builds.yml
deleted file mode 100644
index 9efafc8cc2..0000000000
--- a/.github/workflows/trigger_builds.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-name: Build Application
-
-on:
- push:
- branches-ignore:
- - "renovate/**"
- paths-ignore:
- - "**.md"
- - "**/LICENSE"
- - "flake.lock"
- - "packages/**"
- - ".github/ISSUE_TEMPLATE/**"
- - ".markdownlint**"
- pull_request:
- paths-ignore:
- - "**.md"
- - "**/LICENSE"
- - "flake.lock"
- - "packages/**"
- - ".github/ISSUE_TEMPLATE/**"
- - ".markdownlint**"
- workflow_dispatch:
-
-jobs:
- build_debug:
- name: Build Debug
- uses: ./.github/workflows/build.yml
- with:
- build_type: Debug
- is_qt_cached: true
- secrets:
- SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
- WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
- WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
- APPLE_CODESIGN_CERT: ${{ secrets.APPLE_CODESIGN_CERT }}
- APPLE_CODESIGN_PASSWORD: ${{ secrets.APPLE_CODESIGN_PASSWORD }}
- APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }}
- APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
- APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
- APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
- GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
- GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
diff --git a/.github/workflows/update-flake.yml b/.github/workflows/update-flake.yml
index 855b105eab..1353166f14 100644
--- a/.github/workflows/update-flake.yml
+++ b/.github/workflows/update-flake.yml
@@ -6,25 +6,30 @@ on:
- cron: "0 0 * * 0"
workflow_dispatch:
-permissions:
- contents: write
- pull-requests: write
+permissions: {}
jobs:
update-flake:
if: github.repository == 'PrismLauncher/PrismLauncher'
- runs-on: ubuntu-latest
+
+ permissions:
+ contents: write
+ pull-requests: write
+
+ runs-on: ubuntu-slim
steps:
- - uses: actions/checkout@v4
- - uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26
+ - uses: actions/checkout@v6
+ - uses: cachix/install-nix-action@96951a368ba55167b55f1c916f7d416bac6505fe # v31
- - uses: DeterminateSystems/update-flake-lock@v21
+ - uses: DeterminateSystems/update-flake-lock@v28
with:
commit-msg: "chore(nix): update lockfile"
pr-title: "chore(nix): update lockfile"
pr-labels: |
- Linux
- packaging
- simple change
+ platform: Linux
+ area: packaging
+ complexity: low
+ priority: low
+ type: robot
changelog:omit
diff --git a/.gitignore b/.gitignore
index b5523f6857..00afabbfa3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@ CMakeLists.txt.user.*
CMakeSettings.json
/CMakeFiles
CMakeCache.txt
+CMakeUserPresets.json
/.project
/.settings
/.idea
@@ -21,6 +22,7 @@ CMakeCache.txt
/.vs
cmake-build-*/
Debug
+compile_commands.json
# Build dirs
build
@@ -47,8 +49,12 @@ run/
# Nix/NixOS
.direnv/
-.pre-commit-config.yaml
+## Used when manually invoking stdenv phases
+outputs/
+## Regular artifacts
result
+result-*
+repl-result-*
# Flatpak
.flatpak-builder
diff --git a/.gitmodules b/.gitmodules
index 0f437d2778..42c566fa8f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,24 +1,3 @@
-[submodule "libraries/quazip"]
- path = libraries/quazip
- url = https://github.com/stachenov/quazip.git
-[submodule "libraries/tomlplusplus"]
- path = libraries/tomlplusplus
- url = https://github.com/marzer/tomlplusplus.git
-[submodule "libraries/filesystem"]
- path = libraries/filesystem
- url = https://github.com/gulrak/filesystem
[submodule "libraries/libnbtplusplus"]
path = libraries/libnbtplusplus
url = https://github.com/PrismLauncher/libnbtplusplus.git
-[submodule "libraries/zlib"]
- path = libraries/zlib
- url = https://github.com/madler/zlib.git
-[submodule "libraries/extra-cmake-modules"]
- path = libraries/extra-cmake-modules
- url = https://github.com/KDE/extra-cmake-modules
-[submodule "libraries/cmark"]
- path = libraries/cmark
- url = https://github.com/commonmark/cmark.git
-[submodule "flatpak/shared-modules"]
- path = flatpak/shared-modules
- url = https://github.com/flathub/shared-modules.git
diff --git a/.markdownlintignore b/.markdownlintignore
index a8669d01d0..96f627ad9c 100644
--- a/.markdownlintignore
+++ b/.markdownlintignore
@@ -1,2 +1 @@
libraries/nbtplusplus
-libraries/quazip
diff --git a/BUILD.md b/BUILD.md
deleted file mode 100644
index a139039df8..0000000000
--- a/BUILD.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Build Instructions
-
-Full build instructions are available on [the website](https://prismlauncher.org/wiki/development/build-instructions/).
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 63408ec210..80977e06e1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,9 @@
-cmake_minimum_required(VERSION 3.15) # minimum version required by QuaZip
+cmake_minimum_required(VERSION 3.25) # Required for features like `CMAKE_MSVC_DEBUG_INFORMATION_FORMAT`
-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)
@@ -24,101 +27,106 @@ set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/jars)
######## Set compiler flags ########
set(CMAKE_CXX_STANDARD_REQUIRED true)
set(CMAKE_C_STANDARD_REQUIRED true)
-set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD 20)
set(CMAKE_C_STANDARD 11)
include(GenerateExportHeader)
-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
- # Use /W4 as /Wall includes unnesserey warnings such as added padding to structs
- set(CMAKE_CXX_FLAGS "/GS /permissive- /W4 ${CMAKE_CXX_FLAGS}")
-
- # /EHs Enables stack unwind semantics for standard C++ exceptions to ensure stackframes are unwound
- # and object deconstructors are called when an exception is caught.
- # without it memory leaks and a warning is printed
- # /EHc tells the compiler to assume that functions declared as extern "C" never throw a C++ exception
- # This appears to not always be a defualt compiler option in CMAKE
- set(CMAKE_CXX_FLAGS "/EHsc ${CMAKE_CXX_FLAGS}")
-
- # LINK accepts /SUBSYSTEM whics sets if we are a WINDOWS (gui) or a CONSOLE programs
- # This implicitly selects an entrypoint specific to the subsystem selected
- # qtmain/QtEntryPointLib provides the correct entrypoint (wWinMain) for gui programs
- # Additinaly LINK autodetects we use a GUI so we can omit /SUBSYSTEM
- # This allows tests to still use have console without using seperate linker flags
- # /LTCG allows for linking wholy optimizated programs
- # /MANIFEST:NO disables generating a manifest file, we instead provide our own
- # /STACK sets the stack reserve size, ATL's pack list needs 3-4 MiB as of November 2022, provide 8 MiB
- set(CMAKE_EXE_LINKER_FLAGS "/LTCG /MANIFEST:NO /STACK:8388608 ${CMAKE_EXE_LINKER_FLAGS}")
+
+add_compile_definitions($<$>:QT_NO_DEBUG>)
+add_compile_definitions(QT_WARN_DEPRECATED_UP_TO=0x060400)
+add_compile_definitions(QT_DISABLE_DEPRECATED_UP_TO=0x060400)
+
+if(CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
+ add_compile_options(
+ # /GS Adds buffer security checks, default on but included anyway to mirror gcc's fstack-protector flag
+ "$<$:/GS>"
+
+ # /Gw helps reduce binary size
+ # /Gy allows the compiler to package individual functions
+ # /guard:cf enables control flow guard
+ "$<$,$>:/Gw;/Gy;/guard:cf>"
+ )
+
+ add_link_options(
+ # /LTCG allows for linking wholy optimizated programs
+ # /MANIFEST:NO disables generating a manifest file, we instead provide our own
+ # /STACK sets the stack reserve size, ATL's pack list needs 3-4 MiB as of November 2022, provide 8 MiB
+ "$<$:/LTCG;/MANIFEST:NO;/STACK:8388608>"
+ )
# /GL enables whole program optimizations
- # /Gw helps reduce binary size
- # /Gy allows the compiler to package individual functions
- # /guard:cf enables control flow guard
- foreach(lang C CXX)
- set("CMAKE_${lang}_FLAGS_RELEASE" "/GL /Gw /Gy /guard:cf")
- endforeach()
+ # NOTE: With Clang, this is implemented as regular LTO and only used during linking
+ if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+ add_compile_options("$<$,$>:/GL>")
+ endif()
# See https://github.com/ccache/ccache/issues/1040
- # Note, CMake 3.25 replaces this with CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
- # See https://cmake.org/cmake/help/v3.25/variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT.html
- foreach(config DEBUG RELWITHDEBINFO)
- foreach(lang C CXX)
- set(flags_var "CMAKE_${lang}_FLAGS_${config}")
- string(REGEX REPLACE "/Z[Ii]" "/Z7" ${flags_var} "${${flags_var}}")
- endforeach()
- endforeach()
+ # TODO(@getchoo): Is sccache affected by this? Would be nice to use `ProgramDatabase`....
+ set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$:Embedded>")
if(CMAKE_MSVC_RUNTIME_LIBRARY STREQUAL "MultiThreadedDLL")
set(CMAKE_MAP_IMPORTED_CONFIG_DEBUG Release "")
set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release "")
endif()
else()
- set(CMAKE_CXX_FLAGS "-Wall -pedantic -fstack-protector-strong --param=ssp-buffer-size=4 ${CMAKE_CXX_FLAGS}")
+ add_compile_options("$<$:-fstack-protector-strong;--param=ssp-buffer-size=4>")
+
+ # Avoid re-defining _FORTIFY_SOURCE, as it can cause redefinition errors in setups that use it by default (i.e., package builds)
+ if(NOT (CMAKE_C_FLAGS MATCHES "-D_FORTIFY_SOURCE" OR CMAKE_CXX_FLAGS MATCHES "-D_FORTIFY_SOURCE"))
+ # NOTE: _FORTIFY_SOURCE requires optimizations in most newer versions of glibc
+ add_compile_options("$<$,$>:-D_FORTIFY_SOURCE=2>")
+ endif()
# ATL's pack list needs more than the default 1 Mib stack on windows
- if(WIN32)
- set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,8388608 ${CMAKE_EXE_LINKER_FLAGS}")
+ if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
+ add_link_options("$<$:-Wl,--stack,8388608>")
+
+ # -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
+ add_compile_options("$<$,$>:-ffunction-sections;-fdata-sections;-mguard=cf>")
endif()
endif()
-# Fix build with Qt 5.13
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y")
-
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_BEFORE=0x050C00")
+# Export compile commands for debug builds if we can (useful in LSPs like clangd)
+# https://cmake.org/cmake/help/v3.31/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html
+if(CMAKE_GENERATOR STREQUAL "Unix Makefiles" OR CMAKE_GENERATOR MATCHES "^Ninja")
+ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+endif()
-# Fix aarch64 build for toml++
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0")
+option(USE_CLANG_TIDY "Enable the use of clang-tidy during compilation" OFF)
-# set CXXFLAGS for build targets
-set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
+if(USE_CLANG_TIDY)
+ find_program(CLANG_TIDY clang-tidy OPTIONAL)
+ if(CLANG_TIDY)
+ message(STATUS "Using clang-tidy during compilation")
+ set(CLANG_TIDY_COMMAND "${CLANG_TIDY}" "--config-file=${CMAKE_SOURCE_DIR}/.clang-tidy")
+ set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_COMMAND})
+ else()
+ message(WARNING "Unable to find `clang-tidy`. Not using during compilation")
+ endif()
+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")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
- 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")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
- 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()
@@ -134,8 +142,9 @@ if(ENABLE_LTO)
if(ipo_supported)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL TRUE)
+ set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
if(CMAKE_BUILD_TYPE)
- if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
+ if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
message(STATUS "IPO / LTO enabled")
else()
message(STATUS "Not enabling IPO / LTO on debug builds")
@@ -150,20 +159,9 @@ endif()
option(BUILD_TESTING "Build the testing tree." ON)
-find_package(ECM QUIET NO_MODULE)
-if(NOT ECM_FOUND)
- if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/extra-cmake-modules/CMakeLists.txt")
- message(STATUS "Using bundled ECM")
- set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/libraries/extra-cmake-modules/modules;${CMAKE_MODULE_PATH}")
- else()
- message(FATAL_ERROR
- " Could not find ECM\n \n"
- " Either install ECM using the system package manager or clone submodules\n"
- " Submodules can be cloned with 'git submodule update --init --recursive'")
- endif()
-else()
- set(CMAKE_MODULE_PATH "${ECM_MODULE_PATH};${CMAKE_MODULE_PATH}")
-endif()
+find_package(ECM NO_MODULE REQUIRED)
+set(CMAKE_MODULE_PATH "${ECM_MODULE_PATH};${CMAKE_MODULE_PATH}")
+
include(CTest)
include(ECMAddTests)
if(BUILD_TESTING)
@@ -175,15 +173,19 @@ endif()
######## Set URLs ########
set(Launcher_NEWS_RSS_URL "https://prismlauncher.org/feed/feed.xml" CACHE STRING "URL to fetch Prism Launcher's news RSS feed from.")
set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'")
-set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
+set(Launcher_WIKI_URL "https://prismlauncher.org/wiki/" CACHE STRING "URL that gets opened when the user clicks 'Launcher Help'")
+set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help in a dialog window")
+set(Launcher_LOGIN_CALLBACK_URL "https://prismlauncher.org/successful-login" CACHE STRING "URL that gets opened when the user successfully logins.")
+set(Launcher_LEGACY_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" CACHE STRING "URL for legacy (<=1.5.2) FML Libraries.")
######## Set version numbers ########
-set(Launcher_VERSION_MAJOR 9)
+set(Launcher_VERSION_MAJOR 11)
set(Launcher_VERSION_MINOR 0)
+set(Launcher_VERSION_PATCH 0)
-set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
-set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
-set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0")
+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")
+set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},${Launcher_VERSION_PATCH},0")
# Build platform.
set(Launcher_BUILD_PLATFORM "unknown" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.")
@@ -205,6 +207,7 @@ set(Launcher_BUG_TRACKER_URL "https://github.com/PrismLauncher/PrismLauncher/iss
# Translations Platform URL
set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/prismlauncher/launcher/" CACHE STRING "URL for the translations platform.")
+set(Launcher_TRANSLATION_FILES_URL "https://i18n.prismlauncher.org/" CACHE STRING "URL for the translations files.")
# Matrix Space
set(Launcher_MATRIX_URL "https://prismlauncher.org/matrix" CACHE STRING "URL to the Matrix Space")
@@ -216,9 +219,23 @@ set(Launcher_DISCORD_URL "https://prismlauncher.org/discord" CACHE STRING "URL f
set(Launcher_SUBREDDIT_URL "https://prismlauncher.org/reddit" CACHE STRING "URL for the subreddit.")
# Builds
-set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
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)
+
+# Although we recommend enabling this, we cannot guarantee binary compatibility on
+# differing Linux/BSD/etc distributions. Downstream packagers should be explicitly opt-ing into this
+# feature if they know it will work with their distribution.
+if(UNIX AND NOT APPLE)
+ set(Launcher_ENABLE_JAVA_DOWNLOADER_DEFAULT OFF)
+endif()
+
+# Java downloader
+option(Launcher_ENABLE_JAVA_DOWNLOADER "Build the java downloader feature" ${Launcher_ENABLE_JAVA_DOWNLOADER_DEFAULT})
+
# Native libraries
if(UNIX AND APPLE)
set(Launcher_GLFW_LIBRARY_NAME "libglfw.dylib" CACHE STRING "Name of native glfw library")
@@ -268,75 +285,59 @@ set(Launcher_BUILD_TIMESTAMP "${TODAY}")
################################ 3rd Party Libs ################################
-# Successive configurations of cmake without cleaning the build dir will cause zlib fallback to fail due to cached values
-# Record when fallback triggered and skip this find_package
-if(NOT Launcher_FORCE_BUNDLED_LIBS AND NOT FORCE_BUNDLED_ZLIB)
- find_package(ZLIB QUIET)
-endif()
-if(NOT ZLIB_FOUND)
- set(FORCE_BUNDLED_ZLIB TRUE CACHE BOOL "")
- mark_as_advanced(FORCE_BUNDLED_ZLIB)
-endif()
-
# Find the required Qt parts
-include(QtVersionlessBackport)
-if(Launcher_QT_VERSION_MAJOR EQUAL 5)
- set(QT_VERSION_MAJOR 5)
- find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml)
-
- if(NOT Launcher_FORCE_BUNDLED_LIBS)
- find_package(QuaZip-Qt5 1.3 QUIET)
- endif()
- if (NOT QuaZip-Qt5_FOUND)
- set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE)
- set(FORCE_BUNDLED_QUAZIP 1)
- endif()
-
- # Qt 6 sets these by default. Notably causes Windows APIs to use UNICODE strings.
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUNICODE -D_UNICODE")
-elseif(Launcher_QT_VERSION_MAJOR EQUAL 6)
+if(Launcher_QT_VERSION_MAJOR EQUAL 6)
set(QT_VERSION_MAJOR 6)
- find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml Core5Compat)
- list(APPEND Launcher_QT_LIBS Qt6::Core5Compat)
-
- if(NOT Launcher_FORCE_BUNDLED_LIBS)
- find_package(QuaZip-Qt6 1.3 QUIET)
- endif()
- if (NOT QuaZip-Qt6_FOUND)
- set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE)
- set(FORCE_BUNDLED_QUAZIP 1)
- endif()
+ 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()
message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported")
endif()
-if(Launcher_QT_VERSION_MAJOR EQUAL 5)
- include(ECMQueryQt)
- ecm_query_qt(QT_PLUGINS_DIR QT_INSTALL_PLUGINS)
- ecm_query_qt(QT_LIBS_DIR QT_INSTALL_LIBS)
- ecm_query_qt(QT_LIBEXECS_DIR QT_INSTALL_LIBEXECS)
-else()
+if(Launcher_QT_VERSION_MAJOR EQUAL 6)
set(QT_PLUGINS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_PLUGINS})
set(QT_LIBS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBS})
set(QT_LIBEXECS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBEXECS})
endif()
-# NOTE: Qt 6 already sets this by default
-if (Qt5_POSITION_INDEPENDENT_CODE)
- SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
+find_package(cmark REQUIRED)
+
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(gamemode REQUIRED IMPORTED_TARGET gamemode)
endif()
-if(NOT Launcher_FORCE_BUNDLED_LIBS)
- # Find toml++
- find_package(tomlplusplus 3.2.0 QUIET)
+# Find libqrencode
+## NOTE(@getchoo): Never use pkg-config with MSVC since the vcpkg port makes our install bundle fail to find the dll
+if(MSVC)
+ find_path(LIBQRENCODE_INCLUDE_DIR qrencode.h REQUIRED)
+ find_library(LIBQRENCODE_LIBRARY_RELEASE qrencode REQUIRED)
+ find_library(LIBQRENCODE_LIBRARY_DEBUG qrencoded)
+ set(LIBQRENCODE_LIBRARIES optimized ${LIBQRENCODE_LIBRARY_RELEASE} debug ${LIBQRENCODE_LIBRARY_DEBUG})
+else()
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(libqrencode REQUIRED IMPORTED_TARGET libqrencode)
+endif()
- # Find ghc_filesystem
- find_package(ghc_filesystem QUIET)
+# Find libarchive through CMake packages, mainly for vcpkg
+find_package(LibArchive)
+# CMake packages aren't available in most distributions of libarchive, so fallback to pkg-config
+if(NOT LibArchive_FOUND)
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(libarchive REQUIRED IMPORTED_TARGET libarchive)
+endif()
- # Find cmark
- find_package(cmark QUIET)
+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)
+ pkg_check_modules(tomlplusplus REQUIRED IMPORTED_TARGET tomlplusplus>=3.2.0)
endif()
+find_package(ZLIB REQUIRED)
+
+
include(ECMQtDeclareLoggingCategory)
####################################### Program Info #######################################
@@ -350,7 +351,7 @@ set(Launcher_ENABLE_UPDATER NO)
set(Launcher_BUILD_UPDATER NO)
if (NOT APPLE AND (NOT Launcher_UPDATER_GITHUB_REPO STREQUAL "" AND NOT Launcher_BUILD_ARTIFACT STREQUAL ""))
- set(Launcher_BUILD_UPDATER YES)
+ set(Launcher_BUILD_UPDATER YES)
endif()
if(NOT (UNIX AND APPLE))
@@ -366,13 +367,10 @@ if(UNIX AND APPLE)
set(RESOURCES_DEST_DIR "${Launcher_Name}.app/Contents/Resources")
set(JARS_DEST_DIR "${Launcher_Name}.app/Contents/MacOS/jars")
- # Apps to bundle
- set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.app")
-
# 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}")
@@ -381,23 +379,63 @@ if(UNIX AND APPLE)
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=" CACHE STRING "Public key for Sparkle update feed")
set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml" CACHE STRING "URL for Sparkle update feed")
- set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.5.2/Sparkle-2.5.2.tar.xz" CACHE STRING "URL to Sparkle release archive")
- set(MACOSX_SPARKLE_SHA256 "572dd67ae398a466f19f343a449e1890bac1ef74885b4739f68f979a8a89884b" CACHE STRING "SHA256 checksum for Sparkle release archive")
+ set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.8.0/Sparkle-2.8.0.tar.xz" CACHE STRING "URL to Sparkle release archive")
+ set(MACOSX_SPARKLE_SHA256 "fd5681ee92bf238aaac2d08214ceaf0cc8976e452d7f882d80bac1e61581f3b1" CACHE STRING "SHA256 checksum for Sparkle release archive")
set(MACOSX_SPARKLE_DIR "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
- # directories to look for dependencies
- set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${MACOSX_SPARKLE_DIR})
-
if(NOT MACOSX_SPARKLE_UPDATE_PUBLIC_KEY STREQUAL "" AND NOT MACOSX_SPARKLE_UPDATE_FEED_URL STREQUAL "")
set(Launcher_ENABLE_UPDATER YES)
endif()
- # install as bundle
- set(INSTALL_BUNDLE "full" CACHE STRING "Use fixup_bundle to bundle dependencies")
-
# 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 ${Launcher_Name}
+ --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)
@@ -405,30 +443,20 @@ elseif(UNIX)
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
set(JARS_DEST_DIR "share/${Launcher_Name}")
- # install as bundle with no dependencies included
- set(INSTALL_BUNDLE "nodeps" CACHE STRING "Use fixup_bundle to bundle dependencies")
-
# Set RPATH
SET(Launcher_BINARY_RPATH "$ORIGIN/")
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${KDE_INSTALL_APPDIR})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${KDE_INSTALL_METAINFODIR})
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION "${KDE_INSTALL_ICONDIR}/hicolor/scalable/apps")
- install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
+ install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_PNG_256} DESTINATION "${KDE_INSTALL_ICONDIR}/hicolor/256x256/apps" RENAME "${Launcher_AppID}.png")
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "share/${Launcher_Name}")
- if (INSTALL_BUNDLE STREQUAL full)
- set(PLUGIN_DEST_DIR "plugins")
- set(BUNDLE_DEST_DIR ".")
- set(RESOURCES_DEST_DIR ".")
-
- # Apps to bundle
- set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${Launcher_APP_BINARY_NAME}")
-
- # directories to look for dependencies
- set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
- endif()
+ set(PLUGIN_DEST_DIR "plugins")
+ set(BUNDLE_DEST_DIR ".")
+ set(RESOURCES_DEST_DIR ".")
if(Launcher_ManPage)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")
@@ -444,15 +472,6 @@ elseif(WIN32)
set(PLUGIN_DEST_DIR ".")
set(RESOURCES_DEST_DIR ".")
set(JARS_DEST_DIR "jars")
-
- # Apps to bundle
- set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.exe")
-
- # directories to look for dependencies
- set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
-
- # install as bundle
- set(INSTALL_BUNDLE "full" CACHE STRING "Use fixup_bundle to bundle dependencies")
else()
message(FATAL_ERROR "Platform not supported")
endif()
@@ -469,69 +488,12 @@ option(NBT_USE_ZLIB "Build NBT library with zlib support" OFF)
option(NBT_BUILD_TESTS "Build NBT library tests" OFF) #FIXME: fix unit tests.
add_subdirectory(libraries/libnbtplusplus)
-add_subdirectory(libraries/systeminfo) # system information library
add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
add_subdirectory(libraries/javacheck) # java compatibility checker
-if(FORCE_BUNDLED_ZLIB)
- message(STATUS "Using bundled zlib")
-
- set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) # Suppress cmake warnings and allow INTERPROCEDURAL_OPTIMIZATION for zlib
- set(SKIP_INSTALL_ALL ON)
- add_subdirectory(libraries/zlib EXCLUDE_FROM_ALL)
-
- # On OS where unistd.h exists, zlib's generated header defines `Z_HAVE_UNISTD_H`, while the included header does not.
- # We cannot safely undo the rename on those systems, and they generally have packages for zlib anyway.
- check_include_file(unistd.h NEED_GENERATED_ZCONF)
- if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h.included" AND NOT NEED_GENERATED_ZCONF)
- # zlib's cmake script renames a file, dirtying the submodule, see https://github.com/madler/zlib/issues/162
- message(STATUS "Undoing Rename")
- message(STATUS " ${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h")
- file(RENAME "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h.included" "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h")
- endif()
-
- set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/libraries/zlib" "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib" CACHE STRING "" FORCE)
- set_target_properties(zlibstatic PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INCLUDE_DIR}")
- add_library(ZLIB::ZLIB ALIAS zlibstatic)
- set(ZLIB_LIBRARY ZLIB::ZLIB CACHE STRING "zlib library name")
- find_package(ZLIB REQUIRED)
-else()
- message(STATUS "Using system zlib")
-endif()
-if (FORCE_BUNDLED_QUAZIP)
- message(STATUS "Using bundled QuaZip")
- set(BUILD_SHARED_LIBS 0) # link statically to avoid conflicts.
- set(QUAZIP_INSTALL 0)
- add_subdirectory(libraries/quazip) # zip manipulation library
-else()
- message(STATUS "Using system QuaZip")
-endif()
add_subdirectory(libraries/rainbow) # Qt extension for colors
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
-if(NOT tomlplusplus_FOUND)
- message(STATUS "Using bundled tomlplusplus")
- add_subdirectory(libraries/tomlplusplus) # toml parser
-else()
- message(STATUS "Using system tomlplusplus")
-endif()
-if(NOT cmark_FOUND)
- message(STATUS "Using bundled cmark")
- set(BUILD_TESTING 0)
- set(BUILD_SHARED_LIBS 0)
- add_subdirectory(libraries/cmark EXCLUDE_FROM_ALL) # Markdown parser
- add_library(cmark::cmark ALIAS cmark)
-else()
- message(STATUS "Using system cmark")
-endif()
-add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much
-add_subdirectory(libraries/gamemode)
add_subdirectory(libraries/murmur2) # Hash for usage with the CurseForge API
-if (NOT ghc_filesystem_FOUND)
- message(STATUS "Using bundled ghc_filesystem")
- add_subdirectory(libraries/filesystem) # Implementation of std::filesystem for old C++, for usage in old macOS
-else()
- message(STATUS "Using system ghc_filesystem")
-endif()
add_subdirectory(libraries/qdcss) # css parser
############################### Built Artifacts ###############################
diff --git a/CMakePresets.json b/CMakePresets.json
new file mode 100644
index 0000000000..f8496acb64
--- /dev/null
+++ b/CMakePresets.json
@@ -0,0 +1,222 @@
+{
+ "$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,
+ "verbosity": "extra"
+ },
+ "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 072916772a..f4b12d08bc 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,17 +1,113 @@
# Contributions Guidelines
-## Code formatting
+## Restrictions on Generative AI Usage (AI Policy)
-Try to follow the existing formatting.
-If there is no existing formatting, you may use `clang-format` with our included `.clang-format` configuration.
+> [!NOTE]
+> The following is adapted from [matplotlib's contributing guide](https://matplotlib.org/devdocs/devel/contribute.html#generative-ai) and the [Linux Kernel policy guide](https://www.kernel.org/doc./html/next/process/coding-assistants.html)
-In general, in order of importance:
+We expect authentic engagement in our community.
-- Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
-- Prefer readability over dogma.
-- Keep to the existing formatting.
-- Indent with 4 space unless it's in a submodule.
-- Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
+- Do not post output from Large Language Models or similar generative AI as comments on GitHub or our discord server, as such comments tend to be formulaic and low-quality content.
+- If you use generative AI tools as an aid in developing code or documentation changes, ensure that you fully understand the proposed changes and can explain why they are the correct approach.
+
+Make sure you have added value based on your personal competency to your contributions.
+Just taking some input, feeding it to an AI and posting the result is not of value to the project.
+To preserve precious core developer capacity, we reserve the right to rigorously reject seemingly AI generated low-value contributions.
+
+### Signed-off-by and Developer Certificate of Origin
+
+AI agents MUST NOT add Signed-off-by tags. Only humans can legally certify the Developer Certificate of Origin (DCO). The human submitter is responsible for:
+
+- Reviewing all AI-generated code
+- Ensuring compliance with licensing requirements
+- Adding their own Signed-off-by tag to certify the DCO
+- Taking full responsibility for the contribution
+
+See [Signing your work](#signing-your-work) for more information.
+
+### Attribution
+
+When AI tools contribute to development, proper attribution helps track the evolving role of AI in the development process. Contributions should include an Assisted-by tag in the commit message with the following format:
+
+```text
+Assisted-by: AGENT_NAME:MODEL_VERSION [TOOL1] [TOOL2]
+```
+
+Where:
+
+- `AGENT_NAME` is the name of the AI tool or framework
+- `MODEL_VERSION` is the specific model version used
+- `[TOOL1] [TOOL2]` are optional specialized analysis tools used (e.g., coccinelle, sparse, smatch, clang-tidy)
+
+Basic development tools (git, gcc, make, editors) should not be listed.
+
+Example:
+
+```text
+Assisted-by: Claude:claude-3-opus coccinelle sparse
+```
+
+## Code style
+
+All files are formatted with `clang-format` using the configuration in `.clang-format`. Ensure it is run on changed files before committing!
+
+Please also follow the project's conventions for C++:
+
+- Class and type names should be formatted as `PascalCase`: `MyClass`.
+- Private or protected class data members should be formatted as `camelCase` prefixed with `m_`: `m_myCounter`.
+- Private or protected `static` class data members should be formatted as `camelCase` prefixed with `s_`: `s_instance`.
+- Public class data members should be formatted as `camelCase` without the prefix: `dateOfBirth`.
+- 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 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.
+ - A function has side effects and an error status is returned.
+ - A function is likely be mistaken for having side effects.
+- A plain getter is unlikely to cause confusion and adding `[[nodiscard]]` can create clutter and inconsistency.
+
+Most of these rules are included in the `.clang-tidy` file, so you can run `clang-tidy` to check for any violations.
+
+Here is what these conventions with the formatting configuration look like:
+
+```c++
+#define AWESOMENESS 10
+
+constexpr double PI = 3.14159;
+
+enum class PizzaToppings { HamAndPineapple, OreoAndKetchup };
+
+struct Person {
+ QString name;
+ QDateTime dateOfBirth;
+
+ long daysOld() const { return dateOfBirth.daysTo(QDateTime::currentDateTime()); }
+};
+
+class ImportantClass {
+ public:
+ void incrementCounter()
+ {
+ if (m_counter + 1 > MAX_COUNTER_VALUE)
+ throw std::runtime_error("Counter has reached limit!");
+
+ ++m_counter;
+ }
+
+ int counter() const { return m_counter; }
+
+ private:
+ static constexpr int MAX_COUNTER_VALUE = 100;
+ int m_counter;
+};
+
+ImportantClass importantClassInstance;
+```
+
+If you see any names which do not follow these conventions, it is preferred that you leave them be - renames increase the number of changes therefore make reviewing harder and make your PR more prone to conflicts. However, if you're refactoring a whole class anyway, it's fine.
## Signing your work
diff --git a/COPYING.md b/COPYING.md
index f14e2958e3..52f29f2e68 100644
--- a/COPYING.md
+++ b/COPYING.md
@@ -1,7 +1,7 @@
## Prism Launcher
Prism Launcher - Minecraft Launcher
- Copyright (C) 2022-2024 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
@@ -108,7 +108,7 @@
Information on third party licenses used in MinGW-w64 can be found in its COPYING.MinGW-w64-runtime.txt.
-## Qt 5/6
+## Qt 6
Copyright (C) 2022 The Qt Company Ltd and other contributors.
Contact: https://www.qt.io/licensing
@@ -212,30 +212,6 @@
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-## Quazip
-
- Copyright (C) 2005-2021 Sergey A. Tachenov
-
- The QuaZip library is licensed under the GNU Lesser General Public
- License V2.1 plus a static linking exception.
-
- The original ZIP/UNZIP package (MiniZip) is copyrighted by Gilles
- Vollant and contributors, see quazip/(un)zip.h files for details.
- Basically it's the zlib license.
-
- STATIC LINKING EXCEPTION
-
- The copyright holders give you permission to link this library with
- independent modules to produce an executable, regardless of the license
- terms of these independent modules, and to copy and distribute the
- resulting executable under terms of your choice, provided that you also
- meet, for each linked independent module, the terms and conditions of
- the license of that module. An independent module is a module which is
- not derived from or based on this library. If you modify this library,
- you must extend this exception to your version of the library.
-
- See COPYING file for the full LGPL text.
-
## launcher (`libraries/launcher`)
PolyMC - Minecraft Launcher
@@ -333,32 +309,6 @@
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-## O2 (Katabasis fork)
-
- Copyright (c) 2012, Akos Polster
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
## Gamemode
Copyright (c) 2017-2022, Feral Interactive
@@ -388,28 +338,6 @@
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
-## gulrak/filesystem
-
- Copyright (c) 2018, Steffen Schümann
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-
## Breeze icons
Copyright (C) 2014 Uri Herrera and others
@@ -451,3 +379,44 @@
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see .
+
+## libqrencode (`fukuchi/libqrencode`)
+
+ Copyright (C) 2020 libqrencode Authors
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+## vcpkg (`cmake/vcpkg-ports`)
+
+ MIT License
+
+ Copyright (c) Microsoft Corporation
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ software and associated documentation files (the "Software"), to deal in the Software
+ without restriction, including without limitation the rights to use, copy, modify,
+ merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be included in all copies
+ or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Containerfile b/Containerfile
new file mode 100644
index 0000000000..8ee4330ed6
--- /dev/null
+++ b/Containerfile
@@ -0,0 +1,74 @@
+ARG DEBIAN_VERSION=stable-slim
+
+FROM docker.io/library/debian:${DEBIAN_VERSION}
+
+ARG QT_ABI=gcc_64
+ARG QT_ARCH=
+ARG QT_HOST=linux
+ARG QT_TARGET=desktop
+ARG QT_VERSION=6.10.2
+
+ARG DEBIAN_FRONTEND=noninteractive
+
+RUN apt-get update \
+ && apt-get --assume-yes upgrade \
+ && apt-get --assume-yes autopurge
+
+# Use Adoptium for Java 17
+RUN apt-get --assume-yes --no-install-recommends install \
+ apt-transport-https ca-certificates curl gpg
+RUN curl -L https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor | tee /etc/apt/trusted.gpg.d/adoptium.gpg
+RUN echo "deb https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | tee /etc/apt/sources.list.d/adoptium.list
+RUN apt-get update
+
+# Install base dependencies
+RUN apt-get --assume-yes --no-install-recommends install \
+ # Compilers
+ clang lld llvm temurin-17-jdk \
+ # Build system
+ cmake ninja-build extra-cmake-modules pkg-config \
+ # Dependencies
+ cmark gamemode-dev libarchive-dev libcmark-dev libgamemode0 libgl1-mesa-dev libqrencode-dev libtomlplusplus-dev libvulkan-dev scdoc zlib1g-dev \
+ # Tooling
+ clang-format clang-tidy git
+
+# Use LLD by default for faster linking
+ENV CMAKE_LINKER_TYPE=lld
+
+# Prepare and install Qt
+## Setup UTF-8 locale (required, apparently)
+RUN apt-get --assume-yes --no-install-recommends install locales
+RUN echo "C.UTF-8 UTF-8" > /etc/locale.gen
+RUN locale-gen
+ENV LC_ALL=C.UTF-8
+
+## Some libraries are required for the official binaries
+RUN apt-get --assume-yes --no-install-recommends install \
+ libglib2.0-0t64 libxkbcommon0 python3-pip
+
+RUN pip3 install --break-system-packages aqtinstall
+RUN aqt install-qt \
+ ${QT_HOST} ${QT_TARGET} ${QT_VERSION} ${QT_ARCH} \
+ --outputdir /opt/qt \
+ --modules qtimageformats qtnetworkauth
+
+ENV PATH=/opt/qt/${QT_VERSION}/${QT_ABI}/bin:$PATH
+ENV QT_PLUGIN_PATH=/opt/qt/${QT_VERSION}/${QT_ABI}/plugins/
+
+## We don't use these. Nuke them
+RUN rm -rf \
+ "$QT_PLUGIN_PATH"/designer \
+ "$QT_PLUGIN_PATH"/help \
+ # "$QT_PLUGIN_PATH"/platformthemes/libqgtk3.so \
+ "$QT_PLUGIN_PATH"/printsupport \
+ "$QT_PLUGIN_PATH"/qmllint \
+ "$QT_PLUGIN_PATH"/qmlls \
+ "$QT_PLUGIN_PATH"/qmltooling \
+ "$QT_PLUGIN_PATH"/sqldrivers
+
+# Setup workspace
+RUN mkdir /work
+WORKDIR /work
+
+ENTRYPOINT ["bash"]
+CMD ["-i"]
diff --git a/README.md b/README.md
index b32132d49e..ac6cfd96b0 100644
--- a/README.md
+++ b/README.md
@@ -26,18 +26,14 @@ Please understand that these builds are not intended for most users. There may b
There are development builds available through:
-- [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) (includes builds from pull requests opened by contribuitors)
-- [nightly.link](https://nightly.link/PrismLauncher/PrismLauncher/workflows/trigger_builds/develop) (this will always point only to the latest version of develop)
+- [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) (includes builds from pull requests opened by contributors)
+- [nightly.link](https://prismlauncher.org/nightly) (this will always point only to the latest version of develop)
These have debug information in the binaries, so their file sizes are relatively larger.
Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**.
-For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**, respectively, you can use these packages for the latest development versions:
-
-[](https://aur.archlinux.org/packages/prismlauncher-git) [](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [](https://mpr.makedeb.org/packages/prismlauncher-git)
[](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [](https://build.opensuse.org/project/show/home:getchoo) [](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,7 +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](https://prismlauncher.org/wiki/development/build-instructions/).
+If you want to build Prism Launcher yourself, check the [build instructions](https://prismlauncher.org/wiki/development/build-instructions).
## Sponsors & Partners
@@ -71,7 +67,13 @@ We thank all the wonderful backers over at Open Collective! Support Prism Launch
Thanks to JetBrains for providing us a few licenses for all their products, as part of their [Open Source program](https://www.jetbrains.com/opensource/).
-[](https://www.jetbrains.com/opensource/)
+
+
+
+
+
+
+
Thanks to Weblate for hosting our translation efforts.
diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in
index b40cacb0f3..14d8236d89 100644
--- a/buildconfig/BuildConfig.cpp.in
+++ b/buildconfig/BuildConfig.cpp.in
@@ -33,9 +33,8 @@
* limitations under the License.
*/
-#include
-#include "BuildConfig.h"
#include
+#include "BuildConfig.h"
const Config BuildConfig;
@@ -49,15 +48,16 @@ Config::Config()
LAUNCHER_DOMAIN = "@Launcher_Domain@";
LAUNCHER_CONFIGFILE = "@Launcher_ConfigFile@";
LAUNCHER_GIT = "@Launcher_Git@";
- LAUNCHER_DESKTOPFILENAME = "@Launcher_DesktopFileName@";
+ LAUNCHER_APPID = "@Launcher_AppID@";
LAUNCHER_SVGFILENAME = "@Launcher_SVGFileName@";
+ LAUNCHER_ENVNAME = "@Launcher_ENVName@";
USER_AGENT = "@Launcher_UserAgent@";
- USER_AGENT_UNCACHED = USER_AGENT + " (Uncached)";
// Version information
VERSION_MAJOR = @Launcher_VERSION_MAJOR@;
VERSION_MINOR = @Launcher_VERSION_MINOR@;
+ VERSION_PATCH = @Launcher_VERSION_PATCH@;
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
BUILD_ARTIFACT = "@Launcher_BUILD_ARTIFACT@";
@@ -74,55 +74,53 @@ Config::Config()
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
- if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
- {
+ if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty()) {
UPDATER_ENABLED = true;
- } else if(!UPDATER_GITHUB_REPO.isEmpty() && !BUILD_ARTIFACT.isEmpty()) {
+ } else if (!UPDATER_GITHUB_REPO.isEmpty() && !BUILD_ARTIFACT.isEmpty()) {
UPDATER_ENABLED = true;
}
+#cmakedefine01 Launcher_ENABLE_JAVA_DOWNLOADER
+ JAVA_DOWNLOADER_ENABLED = Launcher_ENABLE_JAVA_DOWNLOADER;
+
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
GIT_TAG = "@Launcher_GIT_TAG@";
GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";
// Assume that builds outside of Git repos are "stable"
- if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND")
- || GIT_TAG == QStringLiteral("GITDIR-NOTFOUND")
- || GIT_REFSPEC == QStringLiteral("")
- || GIT_TAG == QStringLiteral("GIT-NOTFOUND"))
- {
+ if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND") || GIT_TAG == QStringLiteral("GITDIR-NOTFOUND") ||
+ GIT_REFSPEC == QStringLiteral("") || GIT_TAG == QStringLiteral("GIT-NOTFOUND")) {
GIT_REFSPEC = "refs/heads/stable";
GIT_TAG = versionString();
GIT_COMMIT = "";
}
- if (GIT_REFSPEC.startsWith("refs/heads/"))
- {
+ if (GIT_REFSPEC.startsWith("refs/heads/")) {
VERSION_CHANNEL = GIT_REFSPEC;
- VERSION_CHANNEL.remove("refs/heads/");
- }
- else if (!GIT_COMMIT.isEmpty())
- {
+ VERSION_CHANNEL.remove("refs/heads/");
+ } else if (!GIT_COMMIT.isEmpty()) {
VERSION_CHANNEL = GIT_COMMIT.mid(0, 8);
- }
- else
- {
+ } else {
VERSION_CHANNEL = "unknown";
}
NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@";
+ WIKI_URL = "@Launcher_WIKI_URL@";
HELP_URL = "@Launcher_HELP_URL@";
+ LOGIN_CALLBACK_URL = "@Launcher_LOGIN_CALLBACK_URL@";
IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@";
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
META_URL = "@Launcher_META_URL@";
+ LEGACY_FMLLIBS_BASE_URL = "@Launcher_LEGACY_FMLLIBS_BASE_URL@";
GLFW_LIBRARY_NAME = "@Launcher_GLFW_LIBRARY_NAME@";
OPENAL_LIBRARY_NAME = "@Launcher_OPENAL_LIBRARY_NAME@";
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@";
+ TRANSLATION_FILES_URL = "@Launcher_TRANSLATION_FILES_URL@";
MATRIX_URL = "@Launcher_MATRIX_URL@";
DISCORD_URL = "@Launcher_DISCORD_URL@";
SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@";
@@ -130,7 +128,7 @@ Config::Config()
QString Config::versionString() const
{
- return QString("%1.%2").arg(VERSION_MAJOR).arg(VERSION_MINOR);
+ return QString("%1.%2.%3").arg(VERSION_MAJOR).arg(VERSION_MINOR).arg(VERSION_PATCH);
}
QString Config::printableVersionString() const
@@ -138,8 +136,7 @@ QString Config::printableVersionString() const
QString vstr = versionString();
// If the build is not a main release, append the channel
- if(VERSION_CHANNEL != "stable" && GIT_TAG != vstr)
- {
+ if (VERSION_CHANNEL != "stable" && GIT_TAG != vstr) {
vstr += "-" + VERSION_CHANNEL;
}
return vstr;
@@ -156,4 +153,3 @@ QString Config::systemID() const
{
return QStringLiteral("%1 %2 %3").arg(COMPILER_TARGET_SYSTEM, COMPILER_TARGET_SYSTEM_VERSION, COMPILER_TARGET_SYSTEM_PROCESSOR);
}
-
diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h
index 77b6eef549..d430622bcd 100644
--- a/buildconfig/BuildConfig.h
+++ b/buildconfig/BuildConfig.h
@@ -52,13 +52,16 @@ class Config {
QString LAUNCHER_DOMAIN;
QString LAUNCHER_CONFIGFILE;
QString LAUNCHER_GIT;
- QString LAUNCHER_DESKTOPFILENAME;
+ QString LAUNCHER_APPID;
QString LAUNCHER_SVGFILENAME;
+ QString LAUNCHER_ENVNAME;
/// The major version number.
int VERSION_MAJOR;
/// The minor version number.
int VERSION_MINOR;
+ /// The patch version number.
+ int VERSION_PATCH;
/**
* The version channel
@@ -67,6 +70,7 @@ class Config {
QString VERSION_CHANNEL;
bool UPDATER_ENABLED = false;
+ bool JAVA_DOWNLOADER_ENABLED = false;
/// A short string identifying this build's platform or distribution.
QString BUILD_PLATFORM;
@@ -104,9 +108,6 @@ class Config {
/// User-Agent to use.
QString USER_AGENT;
- /// User-Agent to use for uncached requests.
- QString USER_AGENT_UNCACHED;
-
/// The git commit hash of this build
QString GIT_COMMIT;
@@ -128,10 +129,20 @@ class Config {
QString NEWS_OPEN_URL;
/**
- * URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help
+ * URL that gets opened when the user clicks 'Launcher Help'
+ */
+ QString WIKI_URL;
+
+ /**
+ * URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help in a dialog window
*/
QString HELP_URL;
+ /**
+ * URL that gets opened when the user succesfully logins.
+ */
+ QString LOGIN_CALLBACK_URL;
+
/**
* Client ID you can get from Imgur when you register an application
*/
@@ -161,14 +172,13 @@ class Config {
QString DISCORD_URL;
QString SUBREDDIT_URL;
- QString RESOURCE_BASE = "https://resources.download.minecraft.net/";
+ QString DEFAULT_RESOURCE_BASE = "https://resources.download.minecraft.net/";
QString LIBRARY_BASE = "https://libraries.minecraft.net/";
- QString AUTH_BASE = "https://authserver.mojang.com/";
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
- QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists
- QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists
+ QString LEGACY_FMLLIBS_BASE_URL;
+ QString TRANSLATION_FILES_URL;
- QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
+ QString FTB_API_BASE_URL = "https://api.feed-the-beast.com/v1/modpacks/public";
QString LEGACY_FTB_CDN_BASE_URL = "https://dist.creeper.host/FTB2/";
diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake
deleted file mode 100644
index 51d2fb13a8..0000000000
--- a/cmake/CompilerWarnings.cmake
+++ /dev/null
@@ -1,163 +0,0 @@
-#
-# Function to set compiler warnings with reasonable defaults at the project level.
-# Taken from https://github.com/aminya/project_options/blob/main/src/CompilerWarnings.cmake
-# under the folowing license:
-#
-# MIT License
-#
-# Copyright (c) 2022-2100 Amin Yahyaabadi
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-#
-
-include_guard()
-
-function(_set_project_warnings_add_target_link_option TARGET OPTIONS)
- target_link_options(${_project_name} INTERFACE ${OPTIONS})
-endfunction()
-
-# Set the compiler warnings
-#
-# https://clang.llvm.org/docs/DiagnosticsReference.html
-# https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md
-function(
- set_project_warnings
- _project_name
- MSVC_WARNINGS
- CLANG_WARNINGS
- GCC_WARNINGS
-)
- if("${MSVC_WARNINGS}" STREQUAL "")
- set(MSVC_WARNINGS
- /W4 # Baseline reasonable warnings
- /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data
- /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
- /w14263 # 'function': member function does not override any base class virtual member function
- /w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not
- # be destructed correctly
- /w14287 # 'operator': unsigned/negative constant mismatch
- /we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside
- # the for-loop scope
- /w14296 # 'operator': expression is always 'boolean_value'
- /w14311 # 'variable': pointer truncation from 'type1' to 'type2'
- /w14545 # expression before comma evaluates to a function which is missing an argument list
- /w14546 # function call before comma missing argument list
- /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect
- /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'?
- /w14555 # expression has no effect; expected expression with side- effect
- /w14619 # pragma warning: there is no warning number 'number'
- /w14640 # Enable warning on thread un-safe static member initialization
- /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior.
- /w14905 # wide string literal cast to 'LPSTR'
- /w14906 # string literal cast to 'LPWSTR'
- /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied
- /permissive- # standards conformance mode for MSVC compiler.
-
- /we4062 # forbid omitting a possible value of an enum in a switch statement
- )
- endif()
-
- if("${CLANG_WARNINGS}" STREQUAL "")
- set(CLANG_WARNINGS
- -Wall
- -Wextra # reasonable and standard
- -Wshadow # warn the user if a variable declaration shadows one from a parent context
- -Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps
- # catch hard to track down memory errors
- -Wold-style-cast # warn for c-style casts
- -Wcast-align # warn for potential performance problem casts
- -Wunused # warn on anything being unused
- -Woverloaded-virtual # warn if you overload (not override) a virtual function
- -Wpedantic # warn if non-standard C++ is used
- -Wconversion # warn on type conversions that may lose data
- -Wsign-conversion # warn on sign conversions
- -Wnull-dereference # warn if a null dereference is detected
- -Wdouble-promotion # warn if float is implicit promoted to double
- -Wformat=2 # warn on security issues around functions that format output (ie printf)
- -Wimplicit-fallthrough # warn on statements that fallthrough without an explicit annotation
- # -Wgnu-zero-variadic-macro-arguments (part of -pedantic) is triggered by every qCDebug() call and therefore results
- # in a lot of noise. This warning is only notifying us that clang is emulating the GCC behaviour
- # instead of the exact standard wording so we can safely ignore it
- -Wno-gnu-zero-variadic-macro-arguments
-
- -Werror=switch # forbid omitting a possible value of an enum in a switch statement
- )
- endif()
-
- if("${GCC_WARNINGS}" STREQUAL "")
- set(GCC_WARNINGS
- ${CLANG_WARNINGS}
- -Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist
- -Wduplicated-cond # warn if if / else chain has duplicated conditions
- -Wduplicated-branches # warn if if / else branches have duplicated code
- -Wlogical-op # warn about logical operations being used where bitwise were probably wanted
- -Wuseless-cast # warn if you perform a cast to the same type
-
- -Werror=switch # forbid omitting a possible value of an enum in a switch statement
- )
- endif()
-
- if(MSVC)
- set(PROJECT_WARNINGS_CXX ${MSVC_WARNINGS})
- elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
- set(PROJECT_WARNINGS_CXX ${CLANG_WARNINGS})
- elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
- set(PROJECT_WARNINGS_CXX ${GCC_WARNINGS})
- else()
- message(AUTHOR_WARNING "No compiler warnings set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'")
- # TODO support Intel compiler
- endif()
-
- # Add C warnings
- set(PROJECT_WARNINGS_C "${PROJECT_WARNINGS_CXX}")
- list(
- REMOVE_ITEM
- PROJECT_WARNINGS_C
- -Wnon-virtual-dtor
- -Wold-style-cast
- -Woverloaded-virtual
- -Wuseless-cast
- -Wextra-semi
-
- -Werror=switch # forbid omitting a possible value of an enum in a switch statement
- )
-
- target_compile_options(
- ${_project_name}
- INTERFACE # C++ warnings
- $<$:${PROJECT_WARNINGS_CXX}>
- # C warnings
- $<$:${PROJECT_WARNINGS_C}>
- )
-
- # If we are using the compiler as a linker driver pass the warnings to it
- # (most useful when using LTO or warnings as errors)
- if(CMAKE_CXX_LINK_EXECUTABLE MATCHES "^")
- _set_project_warnings_add_target_link_option(
- ${_project_name} "$<$:${PROJECT_WARNINGS_CXX}>"
- )
- endif()
-
- if(CMAKE_C_LINK_EXECUTABLE MATCHES "^")
- _set_project_warnings_add_target_link_option(
- ${_project_name} "$<$:${PROJECT_WARNINGS_C}>"
- )
- endif()
-
- endfunction()
diff --git a/cmake/ECMQueryQt.cmake b/cmake/ECMQueryQt.cmake
deleted file mode 100644
index 98eb50089e..0000000000
--- a/cmake/ECMQueryQt.cmake
+++ /dev/null
@@ -1,100 +0,0 @@
-# SPDX-FileCopyrightText: 2014 Rohan Garg
-# SPDX-FileCopyrightText: 2014 Alex Merry
-# SPDX-FileCopyrightText: 2014-2016 Aleix Pol
-# SPDX-FileCopyrightText: 2017 Friedrich W. H. Kossebau
-# SPDX-FileCopyrightText: 2022 Ahmad Samir
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#[=======================================================================[.rst:
-ECMQueryQt
----------------
-This module can be used to query the installation paths used by Qt.
-
-For Qt5 this uses ``qmake``, and for Qt6 this used ``qtpaths`` (the latter has built-in
-support to query the paths of a target platform when cross-compiling).
-
-This module defines the following function:
-::
-
- ecm_query_qt( [TRY])
-
-Passing ``TRY`` will result in the method not making the build fail if the executable
-used for querying has not been found, but instead simply print a warning message and
-return an empty string.
-
-Example usage:
-
-.. code-block:: cmake
-
- include(ECMQueryQt)
- ecm_query_qt(bin_dir QT_INSTALL_BINS)
-
-If the call succeeds ``${bin_dir}`` will be set to ``/path/to/bin/dir`` (e.g.
-``/usr/lib64/qt/bin/``).
-
-Since: 5.93
-#]=======================================================================]
-
-include(${CMAKE_CURRENT_LIST_DIR}/QtVersionOption.cmake)
-include(CheckLanguage)
-check_language(CXX)
-if (CMAKE_CXX_COMPILER)
- # Enable the CXX language to let CMake look for config files in library dirs.
- # See: https://gitlab.kitware.com/cmake/cmake/-/issues/23266
- enable_language(CXX)
-endif()
-
-if (QT_MAJOR_VERSION STREQUAL "5")
- # QUIET to accommodate the TRY option
- find_package(Qt${QT_MAJOR_VERSION}Core QUIET)
- if(TARGET Qt5::qmake)
- get_target_property(_qmake_executable_default Qt5::qmake LOCATION)
-
- set(QUERY_EXECUTABLE ${_qmake_executable_default}
- CACHE FILEPATH "Location of the Qt5 qmake executable")
- set(_exec_name_text "Qt5 qmake")
- set(_cli_option "-query")
- endif()
-elseif(QT_MAJOR_VERSION STREQUAL "6")
- # QUIET to accommodate the TRY option
- find_package(Qt6 COMPONENTS CoreTools QUIET CONFIG)
- if (TARGET Qt6::qtpaths)
- get_target_property(_qtpaths_executable Qt6::qtpaths LOCATION)
-
- set(QUERY_EXECUTABLE ${_qtpaths_executable}
- CACHE FILEPATH "Location of the Qt6 qtpaths executable")
- set(_exec_name_text "Qt6 qtpaths")
- set(_cli_option "--query")
- endif()
-endif()
-
-function(ecm_query_qt result_variable qt_variable)
- set(options TRY)
- set(oneValueArgs)
- set(multiValueArgs)
-
- cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
-
- if(NOT QUERY_EXECUTABLE)
- if(ARGS_TRY)
- set(${result_variable} "" PARENT_SCOPE)
- message(STATUS "No ${_exec_name_text} executable found. Can't check ${qt_variable}")
- return()
- else()
- message(FATAL_ERROR "No ${_exec_name_text} executable found. Can't check ${qt_variable} as required")
- endif()
- endif()
- execute_process(
- COMMAND ${QUERY_EXECUTABLE} ${_cli_option} "${qt_variable}"
- RESULT_VARIABLE return_code
- OUTPUT_VARIABLE output
- )
- if(return_code EQUAL 0)
- string(STRIP "${output}" output)
- file(TO_CMAKE_PATH "${output}" output_path)
- set(${result_variable} "${output_path}" PARENT_SCOPE)
- else()
- message(WARNING "Failed call: ${_command} \"${qt_variable}\"")
- message(FATAL_ERROR "${_exec_name_text} call failed: ${return_code}")
- endif()
-endfunction()
diff --git a/cmake/MacOSXBundleInfo.plist.in b/cmake/MacOSXBundleInfo.plist.in
index c439efe25d..eb40bacfd2 100644
--- a/cmake/MacOSXBundleInfo.plist.in
+++ b/cmake/MacOSXBundleInfo.plist.in
@@ -7,7 +7,9 @@
NSMicrophoneUsageDescription
A Minecraft mod wants to access your microphone.
NSDownloadsFolderUsageDescription
- Prism uses access to your Downloads folder to help you more quickly add mods that can't be automatically downloaded to your instance. You can change where Prism scans for downloaded mods in Settings or the prompt that appears.
+ ${Launcher_DisplayName} uses access to your Downloads folder to help you more quickly add mods that can't be automatically downloaded to your instance. You can change where ${Launcher_DisplayName} scans for downloaded mods in Settings or the prompt that appears.
+ NSLocalNetworkUsageDescription
+ Minecraft uses the local network to find and connect to LAN servers.
NSPrincipalClass
NSApplication
NSHighResolutionCapable
@@ -19,7 +21,9 @@
CFBundleGetInfoString
${MACOSX_BUNDLE_INFO_STRING}
CFBundleIconFile
- ${MACOSX_BUNDLE_ICON_FILE}
+ ${Launcher_Name}
+ CFBundleIconName
+ ${Launcher_Name}
CFBundleIdentifier
${MACOSX_BUNDLE_GUI_IDENTIFIER}
CFBundleInfoDictionaryVersion
@@ -40,6 +44,8 @@
LSRequiresCarbon
+ LSApplicationCategoryType
+ public.app-category.games
NSHumanReadableCopyright
${MACOSX_BUNDLE_COPYRIGHT}
SUPublicEDKey
@@ -55,7 +61,7 @@
mrpack
CFBundleTypeName
- Prism Launcher instance
+ ${Launcher_DisplayName} instance
CFBundleTypeOSTypes
TEXT
@@ -79,6 +85,15 @@
curseforge
+
+ CFBundleURLName
+ ${Launcher_Name}
+ CFBundleURLSchemes
+
+ prismlauncher
+ ${MACOSX_BUNDLE_EXECUTABLE_NAME}
+
+
diff --git a/cmake/QtVersionOption.cmake b/cmake/QtVersionOption.cmake
deleted file mode 100644
index 1390f9db60..0000000000
--- a/cmake/QtVersionOption.cmake
+++ /dev/null
@@ -1,38 +0,0 @@
-#.rst:
-# QtVersionOption
-# ---------------
-#
-# Adds a build option to select the major Qt version if necessary,
-# that is, if the major Qt version has not yet been determined otherwise
-# (e.g. by a corresponding find_package() call).
-#
-# This module is typically included by other modules requiring knowledge
-# about the major Qt version.
-#
-# ``QT_MAJOR_VERSION`` is defined to either be "5" or "6".
-#
-#
-# Since 5.82.0.
-
-#=============================================================================
-# SPDX-FileCopyrightText: 2021 Volker Krause
-#
-# SPDX-License-Identifier: BSD-3-Clause
-
-if (DEFINED QT_MAJOR_VERSION)
- return()
-endif()
-
-if (TARGET Qt5::Core)
- set(QT_MAJOR_VERSION 5)
-elseif (TARGET Qt6::Core)
- set(QT_MAJOR_VERSION 6)
-else()
- option(BUILD_WITH_QT6 "Build against Qt 6" OFF)
-
- if (BUILD_WITH_QT6)
- set(QT_MAJOR_VERSION 6)
- else()
- set(QT_MAJOR_VERSION 5)
- endif()
-endif()
diff --git a/cmake/QtVersionlessBackport.cmake b/cmake/QtVersionlessBackport.cmake
deleted file mode 100644
index 46792db580..0000000000
--- a/cmake/QtVersionlessBackport.cmake
+++ /dev/null
@@ -1,97 +0,0 @@
-#=============================================================================
-# Copyright 2005-2011 Kitware, Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# * Neither the name of Kitware, Inc. nor the names of its
-# contributors may be used to endorse or promote products derived
-# from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#=============================================================================
-
-# From Qt5CoreMacros.cmake
-
-function(qt_generate_moc)
- if(QT_VERSION_MAJOR EQUAL 5)
- qt5_generate_moc(${ARGV})
- elseif(QT_VERSION_MAJOR EQUAL 6)
- qt6_generate_moc(${ARGV})
- endif()
-endfunction()
-
-function(qt_wrap_cpp outfiles)
- if(QT_VERSION_MAJOR EQUAL 5)
- qt5_wrap_cpp("${outfiles}" ${ARGN})
- elseif(QT_VERSION_MAJOR EQUAL 6)
- qt6_wrap_cpp("${outfiles}" ${ARGN})
- endif()
- set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
-endfunction()
-
-function(qt_add_binary_resources)
- if(QT_VERSION_MAJOR EQUAL 5)
- qt5_add_binary_resources(${ARGV})
- elseif(QT_VERSION_MAJOR EQUAL 6)
- qt6_add_binary_resources(${ARGV})
- endif()
-endfunction()
-
-function(qt_add_resources outfiles)
- if(QT_VERSION_MAJOR EQUAL 5)
- qt5_add_resources("${outfiles}" ${ARGN})
- elseif(QT_VERSION_MAJOR EQUAL 6)
- qt6_add_resources("${outfiles}" ${ARGN})
- endif()
- set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
-endfunction()
-
-function(qt_add_big_resources outfiles)
- if(QT_VERSION_MAJOR EQUAL 5)
- qt5_add_big_resources(${outfiles} ${ARGN})
- elseif(QT_VERSION_MAJOR EQUAL 6)
- qt6_add_big_resources(${outfiles} ${ARGN})
- endif()
- set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
-endfunction()
-
-function(qt_import_plugins)
- if(QT_VERSION_MAJOR EQUAL 5)
- qt5_import_plugins(${ARGV})
- elseif(QT_VERSION_MAJOR EQUAL 6)
- qt6_import_plugins(${ARGV})
- endif()
-endfunction()
-
-
-# From Qt5WidgetsMacros.cmake
-
-function(qt_wrap_ui outfiles)
- if(QT_VERSION_MAJOR EQUAL 5)
- qt5_wrap_ui("${outfiles}" ${ARGN})
- elseif(QT_VERSION_MAJOR EQUAL 6)
- qt6_wrap_ui("${outfiles}" ${ARGN})
- endif()
- set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
-endfunction()
-
diff --git a/cmake/vcpkg-ports/vcpkg-tool-meson/README.md b/cmake/vcpkg-ports/vcpkg-tool-meson/README.md
new file mode 100644
index 0000000000..9047c80370
--- /dev/null
+++ b/cmake/vcpkg-ports/vcpkg-tool-meson/README.md
@@ -0,0 +1,3 @@
+The only difference between this and the upstream vcpkg port is the addition of `universal-osx.patch`. It's very annoying we need to bundle this entire tree to do that.
+
+-@getchoo
diff --git a/cmake/vcpkg-ports/vcpkg-tool-meson/adjust-args.patch b/cmake/vcpkg-ports/vcpkg-tool-meson/adjust-args.patch
new file mode 100644
index 0000000000..ad800aa66e
--- /dev/null
+++ b/cmake/vcpkg-ports/vcpkg-tool-meson/adjust-args.patch
@@ -0,0 +1,13 @@
+diff --git a/mesonbuild/cmake/toolchain.py b/mesonbuild/cmake/toolchain.py
+index 11a00be5d..89ae490ff 100644
+--- a/mesonbuild/cmake/toolchain.py
++++ b/mesonbuild/cmake/toolchain.py
+@@ -202,7 +202,7 @@ class CMakeToolchain:
+ @staticmethod
+ def is_cmdline_option(compiler: 'Compiler', arg: str) -> bool:
+ if compiler.get_argument_syntax() == 'msvc':
+- return arg.startswith('/')
++ return arg.startswith(('/','-'))
+ else:
+ if os.path.basename(compiler.get_exe()) == 'zig' and arg in {'ar', 'cc', 'c++', 'dlltool', 'lib', 'ranlib', 'objcopy', 'rc'}:
+ return True
diff --git a/cmake/vcpkg-ports/vcpkg-tool-meson/adjust-python-dep.patch b/cmake/vcpkg-ports/vcpkg-tool-meson/adjust-python-dep.patch
new file mode 100644
index 0000000000..0cbfe717de
--- /dev/null
+++ b/cmake/vcpkg-ports/vcpkg-tool-meson/adjust-python-dep.patch
@@ -0,0 +1,45 @@
+diff --git a/mesonbuild/dependencies/python.py b/mesonbuild/dependencies/python.py
+index 883a29a..d9a82af 100644
+--- a/mesonbuild/dependencies/python.py
++++ b/mesonbuild/dependencies/python.py
+@@ -232,8 +232,10 @@ class _PythonDependencyBase(_Base):
+ else:
+ if self.is_freethreaded:
+ libpath = Path('libs') / f'python{vernum}t.lib'
++ libpath = Path('libs') / f'..' / f'..' / f'..' / f'lib' / f'python{vernum}t.lib'
+ else:
+ libpath = Path('libs') / f'python{vernum}.lib'
++ libpath = Path('libs') / f'..' / f'..' / f'..' / f'lib' / f'python{vernum}.lib'
+ # For a debug build, pyconfig.h may force linking with
+ # pythonX_d.lib (see meson#10776). This cannot be avoided
+ # and won't work unless we also have a debug build of
+@@ -250,6 +252,8 @@ class _PythonDependencyBase(_Base):
+ vscrt = self.env.coredata.optstore.get_value('b_vscrt')
+ if vscrt in {'mdd', 'mtd', 'from_buildtype', 'static_from_buildtype'}:
+ vscrt_debug = True
++ if is_debug_build:
++ libpath = Path('libs') / f'..' / f'..' / f'..' / f'debug/lib' / f'python{vernum}_d.lib'
+ if is_debug_build and vscrt_debug and not self.variables.get('Py_DEBUG'):
+ mlog.warning(textwrap.dedent('''\
+ Using a debug build type with MSVC or an MSVC-compatible compiler
+@@ -350,9 +354,10 @@ class PythonSystemDependency(SystemDependency, _PythonDependencyBase):
+ self.is_found = True
+
+ # compile args
++ verdot = self.variables.get('py_version_short')
+ inc_paths = mesonlib.OrderedSet([
+ self.variables.get('INCLUDEPY'),
+- self.paths.get('include'),
++ self.paths.get('include') + f'/../../../include/python${verdot}',
+ self.paths.get('platinclude')])
+
+ self.compile_args += ['-I' + path for path in inc_paths if path]
+@@ -416,7 +421,7 @@ def python_factory(env: 'Environment', for_machine: 'MachineChoice',
+ candidates.append(functools.partial(wrap_in_pythons_pc_dir, pkg_name, env, kwargs, installation))
+ # We only need to check both, if a python install has a LIBPC. It might point to the wrong location,
+ # e.g. relocated / cross compilation, but the presence of LIBPC indicates we should definitely look for something.
+- if pkg_libdir is not None:
++ if True or pkg_libdir is not None:
+ candidates.append(functools.partial(PythonPkgConfigDependency, pkg_name, env, kwargs, installation))
+ else:
+ candidates.append(functools.partial(PkgConfigDependency, 'python3', env, kwargs))
diff --git a/cmake/vcpkg-ports/vcpkg-tool-meson/fix-libcpp-enable-assertions.patch b/cmake/vcpkg-ports/vcpkg-tool-meson/fix-libcpp-enable-assertions.patch
new file mode 100644
index 0000000000..394b064dc4
--- /dev/null
+++ b/cmake/vcpkg-ports/vcpkg-tool-meson/fix-libcpp-enable-assertions.patch
@@ -0,0 +1,52 @@
+From a16ec8b0fb6d7035b669a13edd4d97ff0c307a0b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Martin=20D=C3=B8rum?=
+Date: Fri, 2 May 2025 10:56:28 +0200
+Subject: [PATCH] cpp: fix _LIBCPP_ENABLE_ASSERTIONS warning
+
+libc++ deprecated _LIBCPP_ENABLE_ASSERTIONS from version 18.
+However, the libc++ shipped with Apple Clang backported that
+deprecation in version 17 already,
+which is the version which Apple currently ships for macOS.
+This PR changes the _LIBCPP_ENABLE_ASSERTIONS deprecation check
+to use version ">=17" on Apple Clang.
+---
+ mesonbuild/compilers/cpp.py | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py
+index 01b9bb9fa34f..f7dc150e8608 100644
+--- a/mesonbuild/compilers/cpp.py
++++ b/mesonbuild/compilers/cpp.py
+@@ -311,6 +311,9 @@ def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subpro
+ return libs
+ return []
+
++ def is_libcpp_enable_assertions_deprecated(self) -> bool:
++ return version_compare(self.version, ">=18")
++
+ def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]:
+ if disable:
+ return ['-DNDEBUG']
+@@ -323,7 +326,7 @@ def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]:
+ if self.language_stdlib_provider(env) == 'stdc++':
+ return ['-D_GLIBCXX_ASSERTIONS=1']
+ else:
+- if version_compare(self.version, '>=18'):
++ if self.is_libcpp_enable_assertions_deprecated():
+ return ['-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST']
+ elif version_compare(self.version, '>=15'):
+ return ['-D_LIBCPP_ENABLE_ASSERTIONS=1']
+@@ -343,7 +346,12 @@ class ArmLtdClangCPPCompiler(ClangCPPCompiler):
+
+
+ class AppleClangCPPCompiler(AppleCompilerMixin, AppleCPPStdsMixin, ClangCPPCompiler):
+- pass
++ def is_libcpp_enable_assertions_deprecated(self) -> bool:
++ # Upstream libc++ deprecated _LIBCPP_ENABLE_ASSERTIONS
++ # in favor of _LIBCPP_HARDENING_MODE from version 18 onwards,
++ # but Apple Clang 17's libc++ has back-ported that change.
++ # See: https://github.com/mesonbuild/meson/issues/14440
++ return version_compare(self.version, ">=17")
+
+
+ class EmscriptenCPPCompiler(EmscriptenMixin, ClangCPPCompiler):
diff --git a/cmake/vcpkg-ports/vcpkg-tool-meson/install.cmake b/cmake/vcpkg-ports/vcpkg-tool-meson/install.cmake
new file mode 100644
index 0000000000..84201aa1aa
--- /dev/null
+++ b/cmake/vcpkg-ports/vcpkg-tool-meson/install.cmake
@@ -0,0 +1,5 @@
+file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/tools/meson")
+file(INSTALL "${SOURCE_PATH}/meson.py"
+ "${SOURCE_PATH}/mesonbuild"
+ DESTINATION "${CURRENT_PACKAGES_DIR}/tools/meson"
+)
diff --git a/cmake/vcpkg-ports/vcpkg-tool-meson/meson-intl.patch b/cmake/vcpkg-ports/vcpkg-tool-meson/meson-intl.patch
new file mode 100644
index 0000000000..8f2a029de5
--- /dev/null
+++ b/cmake/vcpkg-ports/vcpkg-tool-meson/meson-intl.patch
@@ -0,0 +1,13 @@
+diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py
+--- a/mesonbuild/dependencies/misc.py
++++ b/mesonbuild/dependencies/misc.py
+@@ -593,7 +593,8 @@ iconv_factory = DependencyFactory(
+
+ packages['intl'] = intl_factory = DependencyFactory(
+ 'intl',
++ [DependencyMethods.BUILTIN, DependencyMethods.SYSTEM, DependencyMethods.CMAKE],
++ cmake_name='Intl',
+- [DependencyMethods.BUILTIN, DependencyMethods.SYSTEM],
+ builtin_class=IntlBuiltinDependency,
+ system_class=IntlSystemDependency,
+ )
diff --git a/cmake/vcpkg-ports/vcpkg-tool-meson/meson.template.in b/cmake/vcpkg-ports/vcpkg-tool-meson/meson.template.in
new file mode 100644
index 0000000000..df21b753b0
--- /dev/null
+++ b/cmake/vcpkg-ports/vcpkg-tool-meson/meson.template.in
@@ -0,0 +1,43 @@
+[binaries]
+cmake = ['@CMAKE_COMMAND@']
+ninja = ['@NINJA@']
+pkg-config = ['@PKGCONFIG@']
+@MESON_MT@
+@MESON_AR@
+@MESON_RC@
+@MESON_C@
+@MESON_C_LD@
+@MESON_CXX@
+@MESON_CXX_LD@
+@MESON_OBJC@
+@MESON_OBJC_LD@
+@MESON_OBJCPP@
+@MESON_OBJCPP_LD@
+@MESON_FC@
+@MESON_FC_LD@
+@MESON_WINDRES@
+@MESON_ADDITIONAL_BINARIES@
+[properties]
+cmake_toolchain_file = '@SCRIPTS@/buildsystems/vcpkg.cmake'
+@MESON_ADDITIONAL_PROPERTIES@
+[cmake]
+CMAKE_BUILD_TYPE = '@MESON_CMAKE_BUILD_TYPE@'
+VCPKG_TARGET_TRIPLET = '@TARGET_TRIPLET@'
+VCPKG_HOST_TRIPLET = '@_HOST_TRIPLET@'
+VCPKG_CHAINLOAD_TOOLCHAIN_FILE = '@VCPKG_CHAINLOAD_TOOLCHAIN_FILE@'
+VCPKG_CRT_LINKAGE = '@VCPKG_CRT_LINKAGE@'
+_VCPKG_INSTALLED_DIR = '@_VCPKG_INSTALLED_DIR@'
+@MESON_HOST_MACHINE@
+@MESON_BUILD_MACHINE@
+[built-in options]
+default_library = '@MESON_DEFAULT_LIBRARY@'
+werror = false
+@MESON_CFLAGS@
+@MESON_CXXFLAGS@
+@MESON_FCFLAGS@
+@MESON_OBJCFLAGS@
+@MESON_OBJCPPFLAGS@
+# b_vscrt
+@MESON_VSCRT_LINKAGE@
+# c_winlibs/cpp_winlibs
+@MESON_WINLIBS@
\ No newline at end of file
diff --git a/cmake/vcpkg-ports/vcpkg-tool-meson/portfile.cmake b/cmake/vcpkg-ports/vcpkg-tool-meson/portfile.cmake
new file mode 100644
index 0000000000..fdea886a7a
--- /dev/null
+++ b/cmake/vcpkg-ports/vcpkg-tool-meson/portfile.cmake
@@ -0,0 +1,45 @@
+# This port represents a dependency on the Meson build system.
+# In the future, it is expected that this port acquires and installs Meson.
+# Currently is used in ports that call vcpkg_find_acquire_program(MESON) in order to force rebuilds.
+
+set(VCPKG_POLICY_CMAKE_HELPER_PORT enabled)
+
+set(patches
+ meson-intl.patch
+ adjust-python-dep.patch
+ adjust-args.patch
+ remove-freebsd-pcfile-specialization.patch
+ fix-libcpp-enable-assertions.patch # https://github.com/mesonbuild/meson/pull/14548, Remove in 1.8.3
+ universal-osx.patch # NOTE(@getchoo): THIS IS THE ONLY CHANGE NEEDED FOR PRISM
+)
+set(scripts
+ vcpkg-port-config.cmake
+ vcpkg_configure_meson.cmake
+ vcpkg_install_meson.cmake
+ meson.template.in
+)
+set(to_hash
+ "${CMAKE_CURRENT_LIST_DIR}/vcpkg.json"
+ "${CMAKE_CURRENT_LIST_DIR}/portfile.cmake"
+)
+foreach(file IN LISTS patches scripts)
+ set(filepath "${CMAKE_CURRENT_LIST_DIR}/${file}")
+ list(APPEND to_hash "${filepath}")
+ file(COPY "${filepath}" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
+endforeach()
+
+set(meson_path_hash "")
+foreach(filepath IN LISTS to_hash)
+ file(SHA1 "${filepath}" to_append)
+ string(APPEND meson_path_hash "${to_append}")
+endforeach()
+string(SHA512 meson_path_hash "${meson_path_hash}")
+
+string(SUBSTRING "${meson_path_hash}" 0 6 MESON_SHORT_HASH)
+list(TRANSFORM patches REPLACE [[^(..*)$]] [["${CMAKE_CURRENT_LIST_DIR}/\0"]])
+list(JOIN patches "\n " PATCHES)
+configure_file("${CMAKE_CURRENT_LIST_DIR}/vcpkg-port-config.cmake" "${CURRENT_PACKAGES_DIR}/share/${PORT}/vcpkg-port-config.cmake" @ONLY)
+
+vcpkg_install_copyright(FILE_LIST "${VCPKG_ROOT_DIR}/LICENSE.txt")
+
+include("${CURRENT_PACKAGES_DIR}/share/${PORT}/vcpkg-port-config.cmake")
diff --git a/cmake/vcpkg-ports/vcpkg-tool-meson/remove-freebsd-pcfile-specialization.patch b/cmake/vcpkg-ports/vcpkg-tool-meson/remove-freebsd-pcfile-specialization.patch
new file mode 100644
index 0000000000..947345ccf8
--- /dev/null
+++ b/cmake/vcpkg-ports/vcpkg-tool-meson/remove-freebsd-pcfile-specialization.patch
@@ -0,0 +1,23 @@
+diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py
+index cc0450a52..13501466d 100644
+--- a/mesonbuild/modules/pkgconfig.py
++++ b/mesonbuild/modules/pkgconfig.py
+@@ -701,16 +701,8 @@ class PkgConfigModule(NewExtensionModule):
+ pcfile = filebase + '.pc'
+ pkgroot = pkgroot_name = kwargs['install_dir'] or default_install_dir
+ if pkgroot is None:
+- m = state.environment.machines.host
+- if m.is_freebsd():
+- pkgroot = os.path.join(_as_str(state.environment.coredata.optstore.get_value_for(OptionKey('prefix'))), 'libdata', 'pkgconfig')
+- pkgroot_name = os.path.join('{prefix}', 'libdata', 'pkgconfig')
+- elif m.is_haiku():
+- pkgroot = os.path.join(_as_str(state.environment.coredata.optstore.get_value_for(OptionKey('prefix'))), 'develop', 'lib', 'pkgconfig')
+- pkgroot_name = os.path.join('{prefix}', 'develop', 'lib', 'pkgconfig')
+- else:
+- pkgroot = os.path.join(_as_str(state.environment.coredata.optstore.get_value_for(OptionKey('libdir'))), 'pkgconfig')
+- pkgroot_name = os.path.join('{libdir}', 'pkgconfig')
++ pkgroot = os.path.join(_as_str(state.environment.coredata.optstore.get_value_for(OptionKey('libdir'))), 'pkgconfig')
++ pkgroot_name = os.path.join('{libdir}', 'pkgconfig')
+ relocatable = state.get_option('pkgconfig.relocatable')
+ self._generate_pkgconfig_file(state, deps, subdirs, name, description, url,
+ version, pcfile, conflicts, variables,
diff --git a/cmake/vcpkg-ports/vcpkg-tool-meson/universal-osx.patch b/cmake/vcpkg-ports/vcpkg-tool-meson/universal-osx.patch
new file mode 100644
index 0000000000..58b96d5ce4
--- /dev/null
+++ b/cmake/vcpkg-ports/vcpkg-tool-meson/universal-osx.patch
@@ -0,0 +1,16 @@
+diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py
+index f57957f0b..a72e72a0b 100644
+--- a/mesonbuild/compilers/detect.py
++++ b/mesonbuild/compilers/detect.py
+@@ -1472,6 +1472,11 @@ def _get_clang_compiler_defines(compiler: T.List[str], lang: str) -> T.Dict[str,
+ """
+ from .mixins.clang import clang_lang_map
+
++ # Filter out `-arch` flags passed to the compiler for Universal Binaries
++ # https://github.com/mesonbuild/meson/issues/5290
++ # https://github.com/mesonbuild/meson/issues/8206
++ compiler = [arg for i, arg in enumerate(compiler) if not (i > 0 and compiler[i - 1] == "-arch") and not arg == "-arch"]
++
+ def _try_obtain_compiler_defines(args: T.List[str]) -> str:
+ mlog.debug(f'Running command: {join_args(args)}')
+ p, output, error = Popen_safe(compiler + args, write='', stdin=subprocess.PIPE)
diff --git a/cmake/vcpkg-ports/vcpkg-tool-meson/vcpkg-port-config.cmake b/cmake/vcpkg-ports/vcpkg-tool-meson/vcpkg-port-config.cmake
new file mode 100644
index 0000000000..c0dee3a382
--- /dev/null
+++ b/cmake/vcpkg-ports/vcpkg-tool-meson/vcpkg-port-config.cmake
@@ -0,0 +1,62 @@
+include("${CURRENT_HOST_INSTALLED_DIR}/share/vcpkg-cmake-get-vars/vcpkg-port-config.cmake")
+# Overwrite builtin scripts
+include("${CMAKE_CURRENT_LIST_DIR}/vcpkg_configure_meson.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/vcpkg_install_meson.cmake")
+
+set(meson_short_hash @MESON_SHORT_HASH@)
+
+# Setup meson:
+set(program MESON)
+set(program_version @VERSION@)
+set(program_name meson)
+set(search_names meson meson.py)
+set(ref "${program_version}")
+set(path_to_search "${DOWNLOADS}/tools/meson-${program_version}-${meson_short_hash}")
+set(download_urls "https://github.com/mesonbuild/meson/archive/${ref}.tar.gz")
+set(download_filename "meson-${ref}.tar.gz")
+set(download_sha512 bd2e65f0863d9cb974e659ff502d773e937b8a60aaddfd7d81e34cd2c296c8e82bf214d790ac089ba441543059dfc2677ba95ed51f676df9da420859f404a907)
+
+find_program(SCRIPT_MESON NAMES ${search_names} PATHS "${path_to_search}" NO_DEFAULT_PATH) # NO_DEFAULT_PATH due top patching
+
+if(NOT SCRIPT_MESON)
+ vcpkg_download_distfile(archive_path
+ URLS ${download_urls}
+ SHA512 "${download_sha512}"
+ FILENAME "${download_filename}"
+ )
+ file(REMOVE_RECURSE "${path_to_search}")
+ file(REMOVE_RECURSE "${path_to_search}-tmp")
+ file(MAKE_DIRECTORY "${path_to_search}-tmp")
+ file(ARCHIVE_EXTRACT INPUT "${archive_path}"
+ DESTINATION "${path_to_search}-tmp"
+ #PATTERNS "**/mesonbuild/*" "**/*.py"
+ )
+ z_vcpkg_apply_patches(
+ SOURCE_PATH "${path_to_search}-tmp/meson-${ref}"
+ PATCHES
+ @PATCHES@
+ )
+ file(MAKE_DIRECTORY "${path_to_search}")
+ file(RENAME "${path_to_search}-tmp/meson-${ref}/meson.py" "${path_to_search}/meson.py")
+ file(RENAME "${path_to_search}-tmp/meson-${ref}/mesonbuild" "${path_to_search}/mesonbuild")
+ file(REMOVE_RECURSE "${path_to_search}-tmp")
+ set(SCRIPT_MESON "${path_to_search}/meson.py")
+endif()
+
+# Check required python version
+vcpkg_find_acquire_program(PYTHON3)
+vcpkg_execute_in_download_mode(
+ COMMAND "${PYTHON3}" --version
+ OUTPUT_VARIABLE version_contents
+ WORKING_DIRECTORY "${CURRENT_BUILDTREES_DIR}"
+)
+string(REGEX MATCH [[[0-9]+\.[0-9]+\.[0-9]+]] python_ver "${version_contents}")
+
+set(min_required 3.7)
+if(python_ver VERSION_LESS "${min_required}")
+ message(FATAL_ERROR "Found Python version '${python_ver} at ${PYTHON3}' is insufficient for meson. meson requires at least version '${min_required}'")
+else()
+ message(STATUS "Found Python version '${python_ver} at ${PYTHON3}'")
+endif()
+
+message(STATUS "Using meson: ${SCRIPT_MESON}")
diff --git a/cmake/vcpkg-ports/vcpkg-tool-meson/vcpkg.json b/cmake/vcpkg-ports/vcpkg-tool-meson/vcpkg.json
new file mode 100644
index 0000000000..04a0cbbec8
--- /dev/null
+++ b/cmake/vcpkg-ports/vcpkg-tool-meson/vcpkg.json
@@ -0,0 +1,11 @@
+{
+ "name": "vcpkg-tool-meson",
+ "version": "1.8.2",
+ "description": "Meson build system",
+ "homepage": "https://github.com/mesonbuild/meson",
+ "license": "Apache-2.0",
+ "supports": "native",
+ "dependencies": [
+ "vcpkg-cmake-get-vars"
+ ]
+}
diff --git a/cmake/vcpkg-ports/vcpkg-tool-meson/vcpkg_configure_meson.cmake b/cmake/vcpkg-ports/vcpkg-tool-meson/vcpkg_configure_meson.cmake
new file mode 100644
index 0000000000..6b00200d18
--- /dev/null
+++ b/cmake/vcpkg-ports/vcpkg-tool-meson/vcpkg_configure_meson.cmake
@@ -0,0 +1,480 @@
+function(z_vcpkg_meson_set_proglist_variables config_type)
+ if(VCPKG_TARGET_IS_WINDOWS)
+ set(proglist MT AR)
+ else()
+ set(proglist AR RANLIB STRIP NM OBJDUMP DLLTOOL MT)
+ endif()
+ foreach(prog IN LISTS proglist)
+ if(VCPKG_DETECTED_CMAKE_${prog})
+ if(meson_${prog})
+ string(TOUPPER "MESON_${meson_${prog}}" var_to_set)
+ set("${var_to_set}" "${meson_${prog}} = ['${VCPKG_DETECTED_CMAKE_${prog}}']" PARENT_SCOPE)
+ elseif(${prog} STREQUAL AR AND VCPKG_COMBINED_STATIC_LINKER_FLAGS_${config_type})
+ # Probably need to move AR somewhere else
+ string(TOLOWER "${prog}" proglower)
+ z_vcpkg_meson_convert_compiler_flags_to_list(ar_flags "${VCPKG_COMBINED_STATIC_LINKER_FLAGS_${config_type}}")
+ list(PREPEND ar_flags "${VCPKG_DETECTED_CMAKE_${prog}}")
+ z_vcpkg_meson_convert_list_to_python_array(ar_flags ${ar_flags})
+ set("MESON_AR" "${proglower} = ${ar_flags}" PARENT_SCOPE)
+ else()
+ string(TOUPPER "MESON_${prog}" var_to_set)
+ string(TOLOWER "${prog}" proglower)
+ set("${var_to_set}" "${proglower} = ['${VCPKG_DETECTED_CMAKE_${prog}}']" PARENT_SCOPE)
+ endif()
+ endif()
+ endforeach()
+ set(compilers "${arg_LANGUAGES}")
+ if(VCPKG_TARGET_IS_WINDOWS)
+ list(APPEND compilers RC)
+ endif()
+ set(meson_RC windres)
+ set(meson_Fortran fortran)
+ set(meson_CXX cpp)
+ foreach(prog IN LISTS compilers)
+ if(VCPKG_DETECTED_CMAKE_${prog}_COMPILER)
+ string(TOUPPER "MESON_${prog}" var_to_set)
+ if(meson_${prog})
+ if(VCPKG_COMBINED_${prog}_FLAGS_${config_type})
+ # Need compiler flags in prog vars for sanity check.
+ z_vcpkg_meson_convert_compiler_flags_to_list(${prog}flags "${VCPKG_COMBINED_${prog}_FLAGS_${config_type}}")
+ endif()
+ list(PREPEND ${prog}flags "${VCPKG_DETECTED_CMAKE_${prog}_COMPILER}")
+ list(FILTER ${prog}flags EXCLUDE REGEX "(-|/)nologo") # Breaks compiler detection otherwise
+ z_vcpkg_meson_convert_list_to_python_array(${prog}flags ${${prog}flags})
+ set("${var_to_set}" "${meson_${prog}} = ${${prog}flags}" PARENT_SCOPE)
+ if (DEFINED VCPKG_DETECTED_CMAKE_${prog}_COMPILER_ID
+ AND NOT VCPKG_DETECTED_CMAKE_${prog}_COMPILER_ID MATCHES "^(GNU|Intel)$"
+ AND VCPKG_DETECTED_CMAKE_LINKER)
+ string(TOUPPER "MESON_${prog}_LD" var_to_set)
+ set(${var_to_set} "${meson_${prog}}_ld = ['${VCPKG_DETECTED_CMAKE_LINKER}']" PARENT_SCOPE)
+ endif()
+ else()
+ if(VCPKG_COMBINED_${prog}_FLAGS_${config_type})
+ # Need compiler flags in prog vars for sanity check.
+ z_vcpkg_meson_convert_compiler_flags_to_list(${prog}flags "${VCPKG_COMBINED_${prog}_FLAGS_${config_type}}")
+ endif()
+ list(PREPEND ${prog}flags "${VCPKG_DETECTED_CMAKE_${prog}_COMPILER}")
+ list(FILTER ${prog}flags EXCLUDE REGEX "(-|/)nologo") # Breaks compiler detection otherwise
+ z_vcpkg_meson_convert_list_to_python_array(${prog}flags ${${prog}flags})
+ string(TOLOWER "${prog}" proglower)
+ set("${var_to_set}" "${proglower} = ${${prog}flags}" PARENT_SCOPE)
+ if (DEFINED VCPKG_DETECTED_CMAKE_${prog}_COMPILER_ID
+ AND NOT VCPKG_DETECTED_CMAKE_${prog}_COMPILER_ID MATCHES "^(GNU|Intel)$"
+ AND VCPKG_DETECTED_CMAKE_LINKER)
+ string(TOUPPER "MESON_${prog}_LD" var_to_set)
+ set(${var_to_set} "${proglower}_ld = ['${VCPKG_DETECTED_CMAKE_LINKER}']" PARENT_SCOPE)
+ endif()
+ endif()
+ endif()
+ endforeach()
+endfunction()
+
+function(z_vcpkg_meson_convert_compiler_flags_to_list out_var compiler_flags)
+ separate_arguments(cmake_list NATIVE_COMMAND "${compiler_flags}")
+ list(TRANSFORM cmake_list REPLACE ";" [[\\;]])
+ set("${out_var}" "${cmake_list}" PARENT_SCOPE)
+endfunction()
+
+function(z_vcpkg_meson_convert_list_to_python_array out_var)
+ z_vcpkg_function_arguments(flag_list 1)
+ vcpkg_list(REMOVE_ITEM flag_list "") # remove empty elements if any
+ vcpkg_list(JOIN flag_list "', '" flag_list)
+ set("${out_var}" "['${flag_list}']" PARENT_SCOPE)
+endfunction()
+
+# Generates the required compiler properties for meson
+function(z_vcpkg_meson_set_flags_variables config_type)
+ if(VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_MINGW)
+ set(libpath_flag /LIBPATH:)
+ else()
+ set(libpath_flag -L)
+ endif()
+ if(config_type STREQUAL "DEBUG")
+ set(path_suffix "/debug")
+ else()
+ set(path_suffix "")
+ endif()
+
+ set(includepath "-I${CURRENT_INSTALLED_DIR}/include")
+ set(libpath "${libpath_flag}${CURRENT_INSTALLED_DIR}${path_suffix}/lib")
+
+ foreach(lang IN LISTS arg_LANGUAGES)
+ z_vcpkg_meson_convert_compiler_flags_to_list(${lang}flags "${VCPKG_COMBINED_${lang}_FLAGS_${config_type}}")
+ if(lang MATCHES "^(C|CXX)$")
+ vcpkg_list(APPEND ${lang}flags "${includepath}")
+ endif()
+ z_vcpkg_meson_convert_list_to_python_array(${lang}flags ${${lang}flags})
+ set(lang_mapping "${lang}")
+ if(lang STREQUAL "Fortran")
+ set(lang_mapping "FC")
+ endif()
+ string(TOLOWER "${lang_mapping}" langlower)
+ if(lang STREQUAL "CXX")
+ set(langlower cpp)
+ endif()
+ set(MESON_${lang_mapping}FLAGS "${langlower}_args = ${${lang}flags}\n")
+ set(linker_flags "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_${config_type}}")
+ z_vcpkg_meson_convert_compiler_flags_to_list(linker_flags "${linker_flags}")
+ vcpkg_list(APPEND linker_flags "${libpath}")
+ z_vcpkg_meson_convert_list_to_python_array(linker_flags ${linker_flags})
+ string(APPEND MESON_${lang_mapping}FLAGS "${langlower}_link_args = ${linker_flags}\n")
+ set(MESON_${lang_mapping}FLAGS "${MESON_${lang_mapping}FLAGS}" PARENT_SCOPE)
+ endforeach()
+endfunction()
+
+function(z_vcpkg_get_build_and_host_system build_system host_system is_cross) #https://mesonbuild.com/Cross-compilation.html
+ set(build_unknown FALSE)
+ if(CMAKE_HOST_WIN32)
+ if(DEFINED ENV{PROCESSOR_ARCHITEW6432})
+ set(build_arch $ENV{PROCESSOR_ARCHITEW6432})
+ else()
+ set(build_arch $ENV{PROCESSOR_ARCHITECTURE})
+ endif()
+ if(build_arch MATCHES "(amd|AMD)64")
+ set(build_cpu_fam x86_64)
+ set(build_cpu x86_64)
+ elseif(build_arch MATCHES "(x|X)86")
+ set(build_cpu_fam x86)
+ set(build_cpu i686)
+ elseif(build_arch MATCHES "^(ARM|arm)64$")
+ set(build_cpu_fam aarch64)
+ set(build_cpu armv8)
+ elseif(build_arch MATCHES "^(ARM|arm)$")
+ set(build_cpu_fam arm)
+ set(build_cpu armv7hl)
+ else()
+ if(NOT DEFINED VCPKG_MESON_CROSS_FILE OR NOT DEFINED VCPKG_MESON_NATIVE_FILE)
+ message(WARNING "Unsupported build architecture ${build_arch}! Please set VCPKG_MESON_(CROSS|NATIVE)_FILE to a meson file containing the build_machine entry!")
+ endif()
+ set(build_unknown TRUE)
+ endif()
+ elseif(CMAKE_HOST_UNIX)
+ # at this stage, CMAKE_HOST_SYSTEM_PROCESSOR is not defined
+ execute_process(
+ COMMAND uname -m
+ OUTPUT_VARIABLE MACHINE
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ COMMAND_ERROR_IS_FATAL ANY)
+
+ # Show real machine architecture to visually understand whether we are in a native Apple Silicon terminal or running under Rosetta emulation
+ debug_message("Machine: ${MACHINE}")
+
+ if(MACHINE MATCHES "arm64|aarch64")
+ set(build_cpu_fam aarch64)
+ set(build_cpu armv8)
+ elseif(MACHINE MATCHES "armv7h?l")
+ set(build_cpu_fam arm)
+ set(build_cpu ${MACHINE})
+ elseif(MACHINE MATCHES "x86_64|amd64")
+ set(build_cpu_fam x86_64)
+ set(build_cpu x86_64)
+ elseif(MACHINE MATCHES "x86|i686")
+ set(build_cpu_fam x86)
+ set(build_cpu i686)
+ elseif(MACHINE MATCHES "i386")
+ set(build_cpu_fam x86)
+ set(build_cpu i386)
+ elseif(MACHINE MATCHES "loongarch64")
+ set(build_cpu_fam loongarch64)
+ set(build_cpu loongarch64)
+ else()
+ # https://github.com/mesonbuild/meson/blob/master/docs/markdown/Reference-tables.md#cpu-families
+ if(NOT DEFINED VCPKG_MESON_CROSS_FILE OR NOT DEFINED VCPKG_MESON_NATIVE_FILE)
+ message(WARNING "Unhandled machine: ${MACHINE}! Please set VCPKG_MESON_(CROSS|NATIVE)_FILE to a meson file containing the build_machine entry!")
+ endif()
+ set(build_unknown TRUE)
+ endif()
+ else()
+ if(NOT DEFINED VCPKG_MESON_CROSS_FILE OR NOT DEFINED VCPKG_MESON_NATIVE_FILE)
+ message(WARNING "Failed to detect the build architecture! Please set VCPKG_MESON_(CROSS|NATIVE)_FILE to a meson file containing the build_machine entry!")
+ endif()
+ set(build_unknown TRUE)
+ endif()
+
+ set(build "[build_machine]\n") # Machine the build is performed on
+ string(APPEND build "endian = 'little'\n")
+ if(CMAKE_HOST_WIN32)
+ string(APPEND build "system = 'windows'\n")
+ elseif(CMAKE_HOST_APPLE)
+ string(APPEND build "system = 'darwin'\n")
+ elseif(CYGWIN)
+ string(APPEND build "system = 'cygwin'\n")
+ elseif(CMAKE_HOST_UNIX)
+ string(APPEND build "system = 'linux'\n")
+ else()
+ set(build_unknown TRUE)
+ endif()
+
+ if(DEFINED build_cpu_fam)
+ string(APPEND build "cpu_family = '${build_cpu_fam}'\n")
+ endif()
+ if(DEFINED build_cpu)
+ string(APPEND build "cpu = '${build_cpu}'")
+ endif()
+ if(NOT build_unknown)
+ set(${build_system} "${build}" PARENT_SCOPE)
+ endif()
+
+ set(host_unkown FALSE)
+ if(VCPKG_TARGET_ARCHITECTURE MATCHES "(amd|AMD|x|X)64")
+ set(host_cpu_fam x86_64)
+ set(host_cpu x86_64)
+ elseif(VCPKG_TARGET_ARCHITECTURE MATCHES "(x|X)86")
+ set(host_cpu_fam x86)
+ set(host_cpu i686)
+ elseif(VCPKG_TARGET_ARCHITECTURE MATCHES "^(ARM|arm)64$")
+ set(host_cpu_fam aarch64)
+ set(host_cpu armv8)
+ elseif(VCPKG_TARGET_ARCHITECTURE MATCHES "^(ARM|arm)$")
+ set(host_cpu_fam arm)
+ set(host_cpu armv7hl)
+ elseif(VCPKG_TARGET_ARCHITECTURE MATCHES "loongarch64")
+ set(host_cpu_fam loongarch64)
+ set(host_cpu loongarch64)
+ elseif(VCPKG_TARGET_ARCHITECTURE MATCHES "wasm32")
+ set(host_cpu_fam wasm32)
+ set(host_cpu wasm32)
+ else()
+ if(NOT DEFINED VCPKG_MESON_CROSS_FILE OR NOT DEFINED VCPKG_MESON_NATIVE_FILE)
+ message(WARNING "Unsupported target architecture ${VCPKG_TARGET_ARCHITECTURE}! Please set VCPKG_MESON_(CROSS|NATIVE)_FILE to a meson file containing the host_machine entry!" )
+ endif()
+ set(host_unkown TRUE)
+ endif()
+
+ set(host "[host_machine]\n") # host=target in vcpkg.
+ string(APPEND host "endian = 'little'\n")
+ if(NOT VCPKG_CMAKE_SYSTEM_NAME OR VCPKG_TARGET_IS_MINGW OR VCPKG_TARGET_IS_UWP)
+ set(meson_system_name "windows")
+ else()
+ string(TOLOWER "${VCPKG_CMAKE_SYSTEM_NAME}" meson_system_name)
+ endif()
+ string(APPEND host "system = '${meson_system_name}'\n")
+ string(APPEND host "cpu_family = '${host_cpu_fam}'\n")
+ string(APPEND host "cpu = '${host_cpu}'")
+ if(NOT host_unkown)
+ set(${host_system} "${host}" PARENT_SCOPE)
+ endif()
+
+ if(NOT build_cpu_fam MATCHES "${host_cpu_fam}"
+ OR VCPKG_TARGET_IS_ANDROID OR VCPKG_TARGET_IS_IOS OR VCPKG_TARGET_IS_UWP
+ OR (VCPKG_TARGET_IS_MINGW AND NOT CMAKE_HOST_WIN32))
+ set(${is_cross} TRUE PARENT_SCOPE)
+ endif()
+endfunction()
+
+function(z_vcpkg_meson_setup_extra_windows_variables config_type)
+ ## b_vscrt
+ if(VCPKG_CRT_LINKAGE STREQUAL "static")
+ set(crt_type "mt")
+ else()
+ set(crt_type "md")
+ endif()
+ if(config_type STREQUAL "DEBUG")
+ set(crt_type "${crt_type}d")
+ endif()
+ set(MESON_VSCRT_LINKAGE "b_vscrt = '${crt_type}'" PARENT_SCOPE)
+ ## winlibs
+ separate_arguments(c_winlibs NATIVE_COMMAND "${VCPKG_DETECTED_CMAKE_C_STANDARD_LIBRARIES}")
+ separate_arguments(cpp_winlibs NATIVE_COMMAND "${VCPKG_DETECTED_CMAKE_CXX_STANDARD_LIBRARIES}")
+ z_vcpkg_meson_convert_list_to_python_array(c_winlibs ${c_winlibs})
+ z_vcpkg_meson_convert_list_to_python_array(cpp_winlibs ${cpp_winlibs})
+ set(MESON_WINLIBS "c_winlibs = ${c_winlibs}\n")
+ string(APPEND MESON_WINLIBS "cpp_winlibs = ${cpp_winlibs}")
+ set(MESON_WINLIBS "${MESON_WINLIBS}" PARENT_SCOPE)
+endfunction()
+
+function(z_vcpkg_meson_setup_variables config_type)
+ set(meson_var_list VSCRT_LINKAGE WINLIBS MT AR RC C C_LD CXX CXX_LD OBJC OBJC_LD OBJCXX OBJCXX_LD FC FC_LD WINDRES CFLAGS CXXFLAGS OBJCFLAGS OBJCXXFLAGS FCFLAGS SHARED_LINKER_FLAGS)
+ foreach(var IN LISTS meson_var_list)
+ set(MESON_${var} "")
+ endforeach()
+
+ if(VCPKG_TARGET_IS_WINDOWS)
+ z_vcpkg_meson_setup_extra_windows_variables("${config_type}")
+ endif()
+
+ z_vcpkg_meson_set_proglist_variables("${config_type}")
+ z_vcpkg_meson_set_flags_variables("${config_type}")
+
+ foreach(var IN LISTS meson_var_list)
+ set(MESON_${var} "${MESON_${var}}" PARENT_SCOPE)
+ endforeach()
+endfunction()
+
+function(vcpkg_generate_meson_cmd_args)
+ cmake_parse_arguments(PARSE_ARGV 0 arg
+ ""
+ "OUTPUT;CONFIG"
+ "OPTIONS;LANGUAGES;ADDITIONAL_BINARIES;ADDITIONAL_PROPERTIES"
+ )
+
+ if(NOT arg_LANGUAGES)
+ set(arg_LANGUAGES C CXX)
+ endif()
+
+ vcpkg_list(JOIN arg_ADDITIONAL_BINARIES "\n" MESON_ADDITIONAL_BINARIES)
+ vcpkg_list(JOIN arg_ADDITIONAL_PROPERTIES "\n" MESON_ADDITIONAL_PROPERTIES)
+
+ set(buildtype "${arg_CONFIG}")
+
+ if(NOT VCPKG_CHAINLOAD_TOOLCHAIN_FILE)
+ z_vcpkg_select_default_vcpkg_chainload_toolchain()
+ endif()
+ vcpkg_list(APPEND VCPKG_CMAKE_CONFIGURE_OPTIONS "-DVCPKG_LANGUAGES=${arg_LANGUAGES}")
+ vcpkg_cmake_get_vars(cmake_vars_file)
+ debug_message("Including cmake vars from: ${cmake_vars_file}")
+ include("${cmake_vars_file}")
+
+ vcpkg_list(APPEND arg_OPTIONS --backend ninja --wrap-mode nodownload -Doptimization=plain)
+
+ z_vcpkg_get_build_and_host_system(MESON_HOST_MACHINE MESON_BUILD_MACHINE IS_CROSS)
+
+ if(arg_CONFIG STREQUAL "DEBUG")
+ set(suffix "dbg")
+ else()
+ string(SUBSTRING "${arg_CONFIG}" 0 3 suffix)
+ string(TOLOWER "${suffix}" suffix)
+ endif()
+ set(meson_input_file_${buildtype} "${CURRENT_BUILDTREES_DIR}/meson-${TARGET_TRIPLET}-${suffix}.log")
+
+ if(IS_CROSS)
+ # VCPKG_CROSSCOMPILING is not used since it regresses a lot of ports in x64-windows-x triplets
+ # For consistency this should proably be changed in the future?
+ vcpkg_list(APPEND arg_OPTIONS --native "${SCRIPTS}/buildsystems/meson/none.txt")
+ vcpkg_list(APPEND arg_OPTIONS --cross "${meson_input_file_${buildtype}}")
+ else()
+ vcpkg_list(APPEND arg_OPTIONS --native "${meson_input_file_${buildtype}}")
+ endif()
+
+ # User provided cross/native files
+ if(VCPKG_MESON_NATIVE_FILE)
+ vcpkg_list(APPEND arg_OPTIONS --native "${VCPKG_MESON_NATIVE_FILE}")
+ endif()
+ if(VCPKG_MESON_NATIVE_FILE_${buildtype})
+ vcpkg_list(APPEND arg_OPTIONS --native "${VCPKG_MESON_NATIVE_FILE_${buildtype}}")
+ endif()
+ if(VCPKG_MESON_CROSS_FILE)
+ vcpkg_list(APPEND arg_OPTIONS --cross "${VCPKG_MESON_CROSS_FILE}")
+ endif()
+ if(VCPKG_MESON_CROSS_FILE_${buildtype})
+ vcpkg_list(APPEND arg_OPTIONS --cross "${VCPKG_MESON_CROSS_FILE_${buildtype}}")
+ endif()
+
+ vcpkg_list(APPEND arg_OPTIONS --libdir lib) # else meson install into an architecture describing folder
+ vcpkg_list(APPEND arg_OPTIONS --pkgconfig.relocatable)
+
+ if(arg_CONFIG STREQUAL "RELEASE")
+ vcpkg_list(APPEND arg_OPTIONS -Ddebug=false --prefix "${CURRENT_PACKAGES_DIR}")
+ vcpkg_list(APPEND arg_OPTIONS "--pkg-config-path;['${CURRENT_INSTALLED_DIR}/lib/pkgconfig','${CURRENT_INSTALLED_DIR}/share/pkgconfig']")
+ if(VCPKG_TARGET_IS_WINDOWS)
+ vcpkg_list(APPEND arg_OPTIONS "-Dcmake_prefix_path=['${CURRENT_INSTALLED_DIR}','${CURRENT_INSTALLED_DIR}/debug','${CURRENT_INSTALLED_DIR}/share']")
+ else()
+ vcpkg_list(APPEND arg_OPTIONS "-Dcmake_prefix_path=['${CURRENT_INSTALLED_DIR}','${CURRENT_INSTALLED_DIR}/debug']")
+ endif()
+ elseif(arg_CONFIG STREQUAL "DEBUG")
+ vcpkg_list(APPEND arg_OPTIONS -Ddebug=true --prefix "${CURRENT_PACKAGES_DIR}/debug" --includedir ../include)
+ vcpkg_list(APPEND arg_OPTIONS "--pkg-config-path;['${CURRENT_INSTALLED_DIR}/debug/lib/pkgconfig','${CURRENT_INSTALLED_DIR}/share/pkgconfig']")
+ if(VCPKG_TARGET_IS_WINDOWS)
+ vcpkg_list(APPEND arg_OPTIONS "-Dcmake_prefix_path=['${CURRENT_INSTALLED_DIR}/debug','${CURRENT_INSTALLED_DIR}','${CURRENT_INSTALLED_DIR}/share']")
+ else()
+ vcpkg_list(APPEND arg_OPTIONS "-Dcmake_prefix_path=['${CURRENT_INSTALLED_DIR}/debug','${CURRENT_INSTALLED_DIR}']")
+ endif()
+ else()
+ message(FATAL_ERROR "Unknown configuration. Only DEBUG and RELEASE are valid values.")
+ endif()
+
+ # Allow overrides / additional configuration variables from triplets
+ if(DEFINED VCPKG_MESON_CONFIGURE_OPTIONS)
+ vcpkg_list(APPEND arg_OPTIONS ${VCPKG_MESON_CONFIGURE_OPTIONS})
+ endif()
+ if(DEFINED VCPKG_MESON_CONFIGURE_OPTIONS_${buildtype})
+ vcpkg_list(APPEND arg_OPTIONS ${VCPKG_MESON_CONFIGURE_OPTIONS_${buildtype}})
+ endif()
+
+ if(VCPKG_LIBRARY_LINKAGE STREQUAL "dynamic")
+ set(MESON_DEFAULT_LIBRARY shared)
+ else()
+ set(MESON_DEFAULT_LIBRARY static)
+ endif()
+ set(MESON_CMAKE_BUILD_TYPE "${cmake_build_type_${buildtype}}")
+ z_vcpkg_meson_setup_variables(${buildtype})
+ configure_file("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/meson.template.in" "${meson_input_file_${buildtype}}" @ONLY)
+ set("${arg_OUTPUT}" ${arg_OPTIONS} PARENT_SCOPE)
+endfunction()
+
+function(vcpkg_configure_meson)
+ # parse parameters such that semicolons in options arguments to COMMAND don't get erased
+ cmake_parse_arguments(PARSE_ARGV 0 arg
+ "NO_PKG_CONFIG"
+ "SOURCE_PATH"
+ "OPTIONS;OPTIONS_DEBUG;OPTIONS_RELEASE;LANGUAGES;ADDITIONAL_BINARIES;ADDITIONAL_NATIVE_BINARIES;ADDITIONAL_CROSS_BINARIES;ADDITIONAL_PROPERTIES"
+ )
+
+ if(DEFINED arg_ADDITIONAL_NATIVE_BINARIES OR DEFINED arg_ADDITIONAL_CROSS_BINARIES)
+ message(WARNING "Options ADDITIONAL_(NATIVE|CROSS)_BINARIES have been deprecated. Only use ADDITIONAL_BINARIES!")
+ endif()
+ vcpkg_list(APPEND arg_ADDITIONAL_BINARIES ${arg_ADDITIONAL_NATIVE_BINARIES} ${arg_ADDITIONAL_CROSS_BINARIES})
+ vcpkg_list(REMOVE_DUPLICATES arg_ADDITIONAL_BINARIES)
+
+ file(REMOVE_RECURSE "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel")
+ file(REMOVE_RECURSE "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg")
+
+ vcpkg_find_acquire_program(MESON)
+
+ get_filename_component(CMAKE_PATH "${CMAKE_COMMAND}" DIRECTORY)
+ vcpkg_add_to_path("${CMAKE_PATH}") # Make CMake invokeable for Meson
+
+ vcpkg_find_acquire_program(NINJA)
+
+ if(NOT arg_NO_PKG_CONFIG)
+ vcpkg_find_acquire_program(PKGCONFIG)
+ set(ENV{PKG_CONFIG} "${PKGCONFIG}")
+ endif()
+
+ vcpkg_find_acquire_program(PYTHON3)
+ get_filename_component(PYTHON3_DIR "${PYTHON3}" DIRECTORY)
+ vcpkg_add_to_path(PREPEND "${PYTHON3_DIR}")
+
+ set(buildtypes "")
+ if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
+ set(buildname "DEBUG")
+ set(cmake_build_type_${buildname} "Debug")
+ vcpkg_list(APPEND buildtypes "${buildname}")
+ set(path_suffix_${buildname} "debug/")
+ set(suffix_${buildname} "dbg")
+ endif()
+ if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release")
+ set(buildname "RELEASE")
+ set(cmake_build_type_${buildname} "Release")
+ vcpkg_list(APPEND buildtypes "${buildname}")
+ set(path_suffix_${buildname} "")
+ set(suffix_${buildname} "rel")
+ endif()
+
+ # configure build
+ foreach(buildtype IN LISTS buildtypes)
+ message(STATUS "Configuring ${TARGET_TRIPLET}-${suffix_${buildtype}}")
+ file(MAKE_DIRECTORY "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-${suffix_${buildtype}}")
+
+ vcpkg_generate_meson_cmd_args(
+ OUTPUT cmd_args
+ CONFIG ${buildtype}
+ LANGUAGES ${arg_LANGUAGES}
+ OPTIONS ${arg_OPTIONS} ${arg_OPTIONS_${buildtype}}
+ ADDITIONAL_BINARIES ${arg_ADDITIONAL_BINARIES}
+ ADDITIONAL_PROPERTIES ${arg_ADDITIONAL_PROPERTIES}
+ )
+
+ vcpkg_execute_required_process(
+ COMMAND ${MESON} setup ${cmd_args} ${arg_SOURCE_PATH}
+ WORKING_DIRECTORY "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-${suffix_${buildtype}}"
+ LOGNAME config-${TARGET_TRIPLET}-${suffix_${buildtype}}
+ SAVE_LOG_FILES
+ meson-logs/meson-log.txt
+ meson-info/intro-dependencies.json
+ meson-logs/install-log.txt
+ )
+
+ message(STATUS "Configuring ${TARGET_TRIPLET}-${suffix_${buildtype}} done")
+ endforeach()
+endfunction()
diff --git a/cmake/vcpkg-ports/vcpkg-tool-meson/vcpkg_install_meson.cmake b/cmake/vcpkg-ports/vcpkg-tool-meson/vcpkg_install_meson.cmake
new file mode 100644
index 0000000000..0351f271a4
--- /dev/null
+++ b/cmake/vcpkg-ports/vcpkg-tool-meson/vcpkg_install_meson.cmake
@@ -0,0 +1,71 @@
+function(vcpkg_install_meson)
+ cmake_parse_arguments(PARSE_ARGV 0 arg "ADD_BIN_TO_PATH" "" "")
+
+ vcpkg_find_acquire_program(NINJA)
+ unset(ENV{DESTDIR}) # installation directory was already specified with '--prefix' option
+
+ if(VCPKG_TARGET_IS_OSX)
+ vcpkg_backup_env_variables(VARS SDKROOT MACOSX_DEPLOYMENT_TARGET)
+ set(ENV{SDKROOT} "${VCPKG_DETECTED_CMAKE_OSX_SYSROOT}")
+ set(ENV{MACOSX_DEPLOYMENT_TARGET} "${VCPKG_DETECTED_CMAKE_OSX_DEPLOYMENT_TARGET}")
+ endif()
+
+ foreach(buildtype IN ITEMS "debug" "release")
+ if(DEFINED VCPKG_BUILD_TYPE AND NOT VCPKG_BUILD_TYPE STREQUAL buildtype)
+ continue()
+ endif()
+
+ if(buildtype STREQUAL "debug")
+ set(short_buildtype "dbg")
+ else()
+ set(short_buildtype "rel")
+ endif()
+
+ message(STATUS "Package ${TARGET_TRIPLET}-${short_buildtype}")
+ if(arg_ADD_BIN_TO_PATH)
+ vcpkg_backup_env_variables(VARS PATH)
+ if(buildtype STREQUAL "debug")
+ vcpkg_add_to_path(PREPEND "${CURRENT_INSTALLED_DIR}/debug/bin")
+ else()
+ vcpkg_add_to_path(PREPEND "${CURRENT_INSTALLED_DIR}/bin")
+ endif()
+ endif()
+ vcpkg_execute_required_process(
+ COMMAND "${NINJA}" install -v
+ WORKING_DIRECTORY "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-${short_buildtype}"
+ LOGNAME package-${TARGET_TRIPLET}-${short_buildtype}
+ )
+ if(arg_ADD_BIN_TO_PATH)
+ vcpkg_restore_env_variables(VARS PATH)
+ endif()
+ endforeach()
+
+ vcpkg_list(SET renamed_libs)
+ if(VCPKG_TARGET_IS_WINDOWS AND VCPKG_LIBRARY_LINKAGE STREQUAL static AND NOT VCPKG_TARGET_IS_MINGW)
+ # Meson names all static libraries lib.a which basically breaks the world
+ file(GLOB_RECURSE gen_libraries "${CURRENT_PACKAGES_DIR}*/**/lib*.a")
+ foreach(gen_library IN LISTS gen_libraries)
+ get_filename_component(libdir "${gen_library}" DIRECTORY)
+ get_filename_component(libname "${gen_library}" NAME)
+ string(REGEX REPLACE ".a$" ".lib" fixed_librawname "${libname}")
+ string(REGEX REPLACE "^lib" "" fixed_librawname "${fixed_librawname}")
+ file(RENAME "${gen_library}" "${libdir}/${fixed_librawname}")
+ # For cmake fixes.
+ string(REGEX REPLACE ".a$" "" origin_librawname "${libname}")
+ string(REGEX REPLACE ".lib$" "" fixed_librawname "${fixed_librawname}")
+ vcpkg_list(APPEND renamed_libs ${fixed_librawname})
+ set(${librawname}_old ${origin_librawname})
+ set(${librawname}_new ${fixed_librawname})
+ endforeach()
+ file(GLOB_RECURSE cmake_files "${CURRENT_PACKAGES_DIR}*/*.cmake")
+ foreach(cmake_file IN LISTS cmake_files)
+ foreach(current_lib IN LISTS renamed_libs)
+ vcpkg_replace_string("${cmake_file}" "${${current_lib}_old}" "${${current_lib}_new}" IGNORE_UNCHANGED)
+ endforeach()
+ endforeach()
+ endif()
+
+ if(VCPKG_TARGET_IS_OSX)
+ vcpkg_restore_env_variables(VARS SDKROOT MACOSX_DEPLOYMENT_TARGET)
+ endif()
+endfunction()
diff --git a/cmake/vcpkg-triplets/universal-osx.cmake b/cmake/vcpkg-triplets/universal-osx.cmake
new file mode 100644
index 0000000000..1c91a5650e
--- /dev/null
+++ b/cmake/vcpkg-triplets/universal-osx.cmake
@@ -0,0 +1,8 @@
+# See https://github.com/microsoft/vcpkg/discussions/19454
+# NOTE: Try to keep in sync with default arm64-osx definition
+set(VCPKG_TARGET_ARCHITECTURE x64)
+set(VCPKG_CRT_LINKAGE dynamic)
+set(VCPKG_LIBRARY_LINKAGE static)
+
+set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
+set(VCPKG_OSX_ARCHITECTURES "arm64;x86_64")
diff --git a/default.nix b/default.nix
index c7d0c267d2..5ecef55905 100644
--- a/default.nix
+++ b/default.nix
@@ -1,14 +1,4 @@
-(
- import
- (
- let
- lock = builtins.fromJSON (builtins.readFile ./flake.lock);
- in
- fetchTarball {
- url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
- sha256 = lock.nodes.flake-compat.locked.narHash;
- }
- )
- {src = ./.;}
-)
-.defaultNix
+(import (fetchTarball {
+ url = "https://github.com/edolstra/flake-compat/archive/ff81ac966bb2cae68946d5ed5fc4994f96d0ffec.tar.gz";
+ sha256 = "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=";
+}) { src = ./.; }).defaultNix
diff --git a/flake.lock b/flake.lock
index 740d5c43e7..eb53cb8443 100644
--- a/flake.lock
+++ b/flake.lock
@@ -1,88 +1,13 @@
{
"nodes": {
- "flake-compat": {
- "flake": false,
- "locked": {
- "lastModified": 1696426674,
- "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
- "owner": "edolstra",
- "repo": "flake-compat",
- "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
- "type": "github"
- },
- "original": {
- "owner": "edolstra",
- "repo": "flake-compat",
- "type": "github"
- }
- },
- "flake-parts": {
- "inputs": {
- "nixpkgs-lib": [
- "nixpkgs"
- ]
- },
- "locked": {
- "lastModified": 1714641030,
- "narHash": "sha256-yzcRNDoyVP7+SCNX0wmuDju1NUCt8Dz9+lyUXEI0dbI=",
- "owner": "hercules-ci",
- "repo": "flake-parts",
- "rev": "e5d10a24b66c3ea8f150e47dfdb0416ab7c3390e",
- "type": "github"
- },
- "original": {
- "owner": "hercules-ci",
- "repo": "flake-parts",
- "type": "github"
- }
- },
- "flake-utils": {
- "inputs": {
- "systems": "systems"
- },
- "locked": {
- "lastModified": 1710146030,
- "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
- "owner": "numtide",
- "repo": "flake-utils",
- "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
- "type": "github"
- },
- "original": {
- "owner": "numtide",
- "repo": "flake-utils",
- "type": "github"
- }
- },
- "gitignore": {
- "inputs": {
- "nixpkgs": [
- "pre-commit-hooks",
- "nixpkgs"
- ]
- },
- "locked": {
- "lastModified": 1709087332,
- "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
- "owner": "hercules-ci",
- "repo": "gitignore.nix",
- "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
- "type": "github"
- },
- "original": {
- "owner": "hercules-ci",
- "repo": "gitignore.nix",
- "type": "github"
- }
- },
"libnbtplusplus": {
"flake": false,
"locked": {
- "lastModified": 1699286814,
- "narHash": "sha256-yy0q+bky80LtK1GWzz7qpM+aAGrOqLuewbid8WT1ilk=",
+ "lastModified": 1772016279,
+ "narHash": "sha256-7itkptyjoRcXfGLwg1/jxajetZ3a4mDc66+w4X6yW8s=",
"owner": "PrismLauncher",
"repo": "libnbtplusplus",
- "rev": "23b955121b8217c1c348a9ed2483167a6f3ff4ad",
+ "rev": "687e43031df0dc641984b4256bcca50d5b3f7de3",
"type": "github"
},
"original": {
@@ -93,70 +18,21 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1715413075,
- "narHash": "sha256-FCi3R1MeS5bVp0M0xTheveP6hhcCYfW/aghSTPebYL4=",
- "owner": "nixos",
- "repo": "nixpkgs",
- "rev": "e4e7a43a9db7e22613accfeb1005cca1b2b1ee0d",
- "type": "github"
- },
- "original": {
- "owner": "nixos",
- "ref": "nixpkgs-unstable",
- "repo": "nixpkgs",
- "type": "github"
- }
- },
- "pre-commit-hooks": {
- "inputs": {
- "flake-compat": [
- "flake-compat"
- ],
- "flake-utils": "flake-utils",
- "gitignore": "gitignore",
- "nixpkgs": [
- "nixpkgs"
- ],
- "nixpkgs-stable": [
- "nixpkgs"
- ]
- },
- "locked": {
- "lastModified": 1714478972,
- "narHash": "sha256-q//cgb52vv81uOuwz1LaXElp3XAe1TqrABXODAEF6Sk=",
- "owner": "cachix",
- "repo": "pre-commit-hooks.nix",
- "rev": "2849da033884f54822af194400f8dff435ada242",
- "type": "github"
+ "lastModified": 1774709303,
+ "narHash": "sha256-D4ely1FsBcvtj/qSrNhSWpq+CUZKNiKwJIxpxnfy9o4=",
+ "rev": "8110df5ad7abf5d4c0f6fb0f8f978390e77f9685",
+ "type": "tarball",
+ "url": "https://releases.nixos.org/nixos/unstable/nixos-26.05pre971119.8110df5ad7ab/nixexprs.tar.xz"
},
"original": {
- "owner": "cachix",
- "repo": "pre-commit-hooks.nix",
- "type": "github"
+ "type": "tarball",
+ "url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"
}
},
"root": {
"inputs": {
- "flake-compat": "flake-compat",
- "flake-parts": "flake-parts",
"libnbtplusplus": "libnbtplusplus",
- "nixpkgs": "nixpkgs",
- "pre-commit-hooks": "pre-commit-hooks"
- }
- },
- "systems": {
- "locked": {
- "lastModified": 1681028828,
- "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
- "owner": "nix-systems",
- "repo": "default",
- "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
- "type": "github"
- },
- "original": {
- "owner": "nix-systems",
- "repo": "default",
- "type": "github"
+ "nixpkgs": "nixpkgs"
}
}
},
diff --git a/flake.nix b/flake.nix
index e16c766998..289e0ec1cc 100644
--- a/flake.nix
+++ b/flake.nix
@@ -2,52 +2,247 @@
description = "A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once (Fork of MultiMC)";
nixConfig = {
- extra-substituters = ["https://cache.garnix.io"];
- extra-trusted-public-keys = ["cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g="];
+ extra-substituters = [ "https://prismlauncher.cachix.org" ];
+ extra-trusted-public-keys = [
+ "prismlauncher.cachix.org-1:9/n/FGyABA2jLUVfY+DEp4hKds/rwO+SCOtbOkDzd+c="
+ ];
};
inputs = {
- nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
- flake-parts = {
- url = "github:hercules-ci/flake-parts";
- inputs.nixpkgs-lib.follows = "nixpkgs";
- };
- pre-commit-hooks = {
- url = "github:cachix/pre-commit-hooks.nix";
- inputs = {
- nixpkgs.follows = "nixpkgs";
- nixpkgs-stable.follows = "nixpkgs";
- flake-compat.follows = "flake-compat";
- };
- };
- flake-compat = {
- url = "github:edolstra/flake-compat";
- flake = false;
- };
+ nixpkgs.url = "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz";
+
libnbtplusplus = {
url = "github:PrismLauncher/libnbtplusplus";
flake = false;
};
};
- outputs = {
- flake-parts,
- pre-commit-hooks,
- ...
- } @ inputs:
- flake-parts.lib.mkFlake {inherit inputs;} {
- imports = [
- pre-commit-hooks.flakeModule
-
- ./nix/dev.nix
- ./nix/distribution.nix
- ];
-
- systems = [
- "x86_64-linux"
- "aarch64-linux"
- "x86_64-darwin"
- "aarch64-darwin"
- ];
+ outputs =
+ {
+ self,
+ nixpkgs,
+ libnbtplusplus,
+ }:
+
+ let
+ inherit (nixpkgs) lib;
+
+ # While we only officially support aarch and x86_64 on Linux and MacOS,
+ # we expose a reasonable amount of other systems for users who want to
+ # build for most exotic platforms
+ systems = lib.systems.flakeExposed;
+
+ forAllSystems = lib.genAttrs systems;
+ nixpkgsFor = forAllSystems (system: nixpkgs.legacyPackages.${system});
+ in
+
+ {
+ checks = forAllSystems (
+ system:
+
+ let
+ pkgs = nixpkgsFor.${system};
+ llvm = pkgs.llvmPackages_22;
+ in
+
+ {
+ formatting =
+ pkgs.runCommand "check-formatting"
+ {
+ nativeBuildInputs = with pkgs; [
+ deadnix
+ llvm.clang-tools
+ markdownlint-cli
+ nixfmt-rfc-style
+ statix
+ ];
+ }
+ ''
+ cd ${self}
+
+ echo "Running clang-format...."
+ clang-format --dry-run --style='file' --Werror */**.{c,cc,cpp,h,hh,hpp}
+
+ echo "Running deadnix..."
+ deadnix --fail
+
+ echo "Running markdownlint..."
+ markdownlint --dot .
+
+ echo "Running nixfmt..."
+ find -type f -name '*.nix' -exec nixfmt --check {} +
+
+ echo "Running statix"
+ statix check .
+
+ touch $out
+ '';
+ }
+ );
+
+ devShells = forAllSystems (
+ system:
+
+ let
+ pkgs = nixpkgsFor.${system};
+ llvm = pkgs.llvmPackages_22;
+ python = pkgs.python3;
+ mkShell = pkgs.mkShell.override { inherit (llvm) stdenv; };
+
+ packages' = self.packages.${system};
+
+ welcomeMessage = ''
+ Welcome to the Prism Launcher repository! 🌈
+
+ We just set some things up for you. To get building, you can run:
+
+ ```
+ $ cd "$cmakeBuildDir"
+ $ ninjaBuildPhase
+ $ ninjaInstallPhase
+ ```
+
+ Feel free to ask any questions in our Discord server or Matrix space:
+ - https://prismlauncher.org/discord
+ - https://matrix.to/#/#prismlauncher:matrix.org
+
+ And thanks for helping out :)
+ '';
+
+ # Re-use our package wrapper to wrap our development environment
+ qt-wrapper-env = packages'.prismlauncher.overrideAttrs (old: {
+ name = "qt-wrapper-env";
+
+ # Required to use script-based makeWrapper below
+ strictDeps = true;
+
+ # We don't need/want the unwrapped Prism package
+ paths = [ ];
+
+ nativeBuildInputs = old.nativeBuildInputs or [ ] ++ [
+ # Ensure the wrapper is script based so it can be sourced
+ pkgs.makeWrapper
+ ];
+
+ # Inspired by https://discourse.nixos.org/t/python-qt-woes/11808/10
+ buildCommand = ''
+ makeQtWrapper ${lib.getExe pkgs.runtimeShellPackage} "$out"
+ sed -i '/^exec/d' "$out"
+ '';
+ });
+ in
+
+ {
+ default = mkShell {
+ name = "prism-launcher";
+
+ inputsFrom = [ packages'.prismlauncher-unwrapped ];
+
+ packages = [
+ pkgs.ccache
+ llvm.clang-tools
+ python # NOTE(@getchoo): Required for run-clang-tidy, etc.
+
+ (pkgs.stdenvNoCC.mkDerivation {
+ pname = "clang-tidy-diff";
+ inherit (llvm.clang) version;
+
+ nativeBuildInputs = [
+ pkgs.installShellFiles
+ python.pkgs.wrapPython
+ ];
+
+ dontUnpack = true;
+ dontConfigure = true;
+ dontBuild = true;
+
+ postInstall = "installBin ${llvm.libclang.python}/share/clang/clang-tidy-diff.py";
+ postFixup = "wrapPythonPrograms";
+ })
+ ];
+
+ cmakeBuildType = "Debug";
+ cmakeFlags = [ "-GNinja" ] ++ packages'.prismlauncher-unwrapped.cmakeFlags;
+ dontFixCmake = true;
+
+ shellHook = ''
+ echo "Sourcing ${qt-wrapper-env}"
+ source ${qt-wrapper-env}
+
+ git submodule update --init --force
+
+ if [ ! -f compile_commands.json ]; then
+ cmakeConfigurePhase
+ cd ..
+ ln -s "$cmakeBuildDir"/compile_commands.json compile_commands.json
+ fi
+
+ echo ${lib.escapeShellArg welcomeMessage}
+ '';
+ };
+ }
+ );
+
+ formatter = forAllSystems (system: nixpkgsFor.${system}.nixfmt-rfc-style);
+
+ overlays.default =
+ final: prev:
+
+ let
+ llvm = final.llvmPackages_22 or prev.llvmPackages_22;
+ in
+
+ {
+ prismlauncher-unwrapped = prev.callPackage ./nix/unwrapped.nix {
+ inherit (llvm) stdenv;
+ inherit
+ libnbtplusplus
+ self
+ ;
+ };
+
+ prismlauncher = final.callPackage ./nix/wrapper.nix { };
+ };
+
+ packages = forAllSystems (
+ system:
+
+ let
+ pkgs = nixpkgsFor.${system};
+
+ # Build a scope from our overlay
+ prismPackages = lib.makeScope pkgs.newScope (final: self.overlays.default final pkgs);
+
+ # Grab our packages from it and set the default
+ packages = {
+ inherit (prismPackages) prismlauncher-unwrapped prismlauncher;
+ default = prismPackages.prismlauncher;
+ };
+ in
+
+ # Only output them if they're available on the current system
+ lib.filterAttrs (_: lib.meta.availableOn pkgs.stdenv.hostPlatform) packages
+ );
+
+ # We put these under legacyPackages as they are meant for CI, not end user consumption
+ legacyPackages = forAllSystems (
+ system:
+
+ let
+ packages' = self.packages.${system};
+ legacyPackages' = self.legacyPackages.${system};
+ in
+
+ {
+ prismlauncher-debug = packages'.prismlauncher.override {
+ prismlauncher-unwrapped = legacyPackages'.prismlauncher-unwrapped-debug;
+ };
+
+ prismlauncher-unwrapped-debug = packages'.prismlauncher-unwrapped.overrideAttrs {
+ cmakeBuildType = "Debug";
+ dontStrip = true;
+ };
+ }
+ );
};
}
diff --git a/flatpak/libdecor.json b/flatpak/libdecor.json
deleted file mode 100644
index 589310a35e..0000000000
--- a/flatpak/libdecor.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "name": "libdecor",
- "buildsystem": "meson",
- "config-opts": [
- "-Ddemo=false"
- ],
- "sources": [
- {
- "type": "git",
- "url": "https://gitlab.freedesktop.org/libdecor/libdecor.git",
- "commit": "73260393a97291c887e1074ab7f318e031be0ac6"
- },
- {
- "type": "patch",
- "path": "patches/weird_libdecor.patch"
- }
- ],
- "cleanup": [
- "/include",
- "/lib/pkgconfig"
- ]
-}
diff --git a/flatpak/org.prismlauncher.PrismLauncher.yml b/flatpak/org.prismlauncher.PrismLauncher.yml
deleted file mode 100644
index b4c6e81434..0000000000
--- a/flatpak/org.prismlauncher.PrismLauncher.yml
+++ /dev/null
@@ -1,161 +0,0 @@
-id: org.prismlauncher.PrismLauncher
-runtime: org.kde.Platform
-runtime-version: 5.15-23.08
-sdk: org.kde.Sdk
-sdk-extensions:
- - org.freedesktop.Sdk.Extension.openjdk21
- - org.freedesktop.Sdk.Extension.openjdk17
- - org.freedesktop.Sdk.Extension.openjdk8
-
-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
-
-cleanup:
- - /lib/libGLU*
-
-modules:
- # Might be needed by some Controller mods (see https://github.com/isXander/Controlify/issues/31)
- - shared-modules/libusb/libusb.json
-
- # Needed for proper Wayland support
- - libdecor.json
-
- - name: prismlauncher
- buildsystem: cmake-ninja
- builddir: true
- config-opts:
- - -DLauncher_BUILD_PLATFORM=flatpak
- - -DCMAKE_BUILD_TYPE=RelWithDebInfo
- - -DLauncher_QT_VERSION_MAJOR=5
- build-options:
- env:
- JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17
- JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac
- sources:
- - type: dir
- path: ../
-
- - name: openjdk
- buildsystem: simple
- build-commands:
- - mkdir -p /app/jdk/
- - /usr/lib/sdk/openjdk21/install.sh
- - mv /app/jre /app/jdk/21
- - /usr/lib/sdk/openjdk17/install.sh
- - mv /app/jre /app/jdk/17
- - /usr/lib/sdk/openjdk8/install.sh
- - mv /app/jre /app/jdk/8
- cleanup:
- - /jre
-
- - name: glfw
- buildsystem: cmake-ninja
- config-opts:
- - -DCMAKE_BUILD_TYPE=RelWithDebInfo
- - -DBUILD_SHARED_LIBS:BOOL=ON
- - -DGLFW_USE_WAYLAND=ON
- sources:
- - type: git
- url: https://github.com/glfw/glfw.git
- commit: 3fa2360720eeba1964df3c0ecf4b5df8648a8e52
- - type: patch
- path: patches/0003-Don-t-crash-on-calls-to-focus-or-icon.patch
- - type: patch
- path: patches/0005-Add-warning-about-being-an-unofficial-patch.patch
- - type: patch
- path: patches/0007-Platform-Prefer-Wayland-over-X11.patch
- cleanup:
- - /include
- - /lib/cmake
- - /lib/pkgconfig
-
- - name: xrandr
- buildsystem: autotools
- sources:
- - type: archive
- url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.2.tar.xz
- sha256: c8bee4790d9058bacc4b6246456c58021db58a87ddda1a9d0139bf5f18f1f240
- 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.1
- sha256: 969cf85b5ca3944f3e315cd73a0ee9bea4f9c968cd7d485e9f4745bc1e679c4e
- 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/0003-Don-t-crash-on-calls-to-focus-or-icon.patch b/flatpak/patches/0003-Don-t-crash-on-calls-to-focus-or-icon.patch
deleted file mode 100644
index 9130e856ca..0000000000
--- a/flatpak/patches/0003-Don-t-crash-on-calls-to-focus-or-icon.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-diff --git a/src/wl_window.c b/src/wl_window.c
-index 52d3b9eb..4ac4eb5d 100644
---- a/src/wl_window.c
-+++ b/src/wl_window.c
-@@ -2117,8 +2117,7 @@ void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title)
- void _glfwSetWindowIconWayland(_GLFWwindow* window,
- int count, const GLFWimage* images)
- {
-- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
-- "Wayland: The platform does not support setting the window icon");
-+ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the window icon\n");
- }
-
- void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos)
-@@ -2361,8 +2360,7 @@ void _glfwRequestWindowAttentionWayland(_GLFWwindow* window)
-
- void _glfwFocusWindowWayland(_GLFWwindow* window)
- {
-- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
-- "Wayland: The platform does not support setting the input focus");
-+ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the input focus\n");
- }
-
- void _glfwSetWindowMonitorWayland(_GLFWwindow* window,
diff --git a/flatpak/patches/0005-Add-warning-about-being-an-unofficial-patch.patch b/flatpak/patches/0005-Add-warning-about-being-an-unofficial-patch.patch
deleted file mode 100644
index b031d739fc..0000000000
--- a/flatpak/patches/0005-Add-warning-about-being-an-unofficial-patch.patch
+++ /dev/null
@@ -1,17 +0,0 @@
-diff --git a/src/init.c b/src/init.c
-index 06dbb3f2..a7c6da86 100644
---- a/src/init.c
-+++ b/src/init.c
-@@ -449,6 +449,12 @@ GLFWAPI int glfwInit(void)
- _glfw.initialized = GLFW_TRUE;
-
- glfwDefaultWindowHints();
-+
-+ fprintf(stderr, "!!! Patched GLFW from https://github.com/Admicos/minecraft-wayland\n"
-+ "!!! If any issues with the window, or some issues with rendering, occur, "
-+ "first try with the built-in GLFW, and if that solves the issue, report there first.\n"
-+ "!!! Use outside Minecraft is untested, and things might break.\n");
-+
- return GLFW_TRUE;
- }
-
diff --git a/flatpak/patches/0007-Platform-Prefer-Wayland-over-X11.patch b/flatpak/patches/0007-Platform-Prefer-Wayland-over-X11.patch
deleted file mode 100644
index 4eeb813090..0000000000
--- a/flatpak/patches/0007-Platform-Prefer-Wayland-over-X11.patch
+++ /dev/null
@@ -1,20 +0,0 @@
-diff --git a/src/platform.c b/src/platform.c
-index c5966ae7..3e7442f9 100644
---- a/src/platform.c
-+++ b/src/platform.c
-@@ -49,12 +49,12 @@ static const struct
- #if defined(_GLFW_COCOA)
- { GLFW_PLATFORM_COCOA, _glfwConnectCocoa },
- #endif
--#if defined(_GLFW_X11)
-- { GLFW_PLATFORM_X11, _glfwConnectX11 },
--#endif
- #if defined(_GLFW_WAYLAND)
- { GLFW_PLATFORM_WAYLAND, _glfwConnectWayland },
- #endif
-+#if defined(_GLFW_X11)
-+ { GLFW_PLATFORM_X11, _glfwConnectX11 },
-+#endif
- };
-
- GLFWbool _glfwSelectPlatform(int desiredID, _GLFWplatform* platform)
diff --git a/flatpak/patches/weird_libdecor.patch b/flatpak/patches/weird_libdecor.patch
deleted file mode 100644
index 3a400b820f..0000000000
--- a/flatpak/patches/weird_libdecor.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-diff --git a/src/libdecor.c b/src/libdecor.c
-index a9c1106..1aa38b3 100644
---- a/src/libdecor.c
-+++ b/src/libdecor.c
-@@ -1391,22 +1391,32 @@ calculate_priority(const struct libdecor_plugin_description *plugin_description)
- static bool
- check_symbol_conflicts(const struct libdecor_plugin_description *plugin_description)
- {
-+ bool ret = true;
- char * const *symbol;
-+ void* main_prog = dlopen(NULL, RTLD_LAZY);
-+ if (!main_prog) {
-+ fprintf(stderr, "Plugin \"%s\" couldn't check conflicting symbols: \"%s\".\n",
-+ plugin_description->description, dlerror());
-+ return false;
-+ }
-+
-
- symbol = plugin_description->conflicting_symbols;
- while (*symbol) {
- dlerror();
-- dlsym (RTLD_DEFAULT, *symbol);
-+ dlsym (main_prog, *symbol);
- if (!dlerror()) {
- fprintf(stderr, "Plugin \"%s\" uses conflicting symbol \"%s\".\n",
- plugin_description->description, *symbol);
-- return false;
-+ ret = false;
-+ break;
- }
-
- symbol++;
- }
-
-- return true;
-+ dlclose(main_prog);
-+ return ret;
- }
-
- static struct plugin_loader *
diff --git a/flatpak/prime-run b/flatpak/prime-run
deleted file mode 100644
index 946c28dd59..0000000000
--- 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 039d890d29..0000000000
--- 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 f2b0c16a2a..0000000000
--- a/flatpak/shared-modules
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit f2b0c16a2a217a1822ce5a6538ba8f755ed1dd32
diff --git a/garnix.yaml b/garnix.yaml
deleted file mode 100644
index 6cf8f72146..0000000000
--- a/garnix.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-builds:
- exclude:
- - "*.x86_64-darwin.*"
- include:
- - "checks.x86_64-linux.*"
- - "devShells.*.*"
- - "packages.*.*"
diff --git a/launcher/Application.cpp b/launcher/Application.cpp
index bb8751cccc..115b6489cc 100644
--- a/launcher/Application.cpp
+++ b/launcher/Application.cpp
@@ -44,12 +44,14 @@
#include "BuildConfig.h"
#include "DataMigrationTask.h"
+#include "java/JavaInstallList.h"
#include "net/PasteUpload.h"
-#include "pathmatcher/MultiMatcher.h"
-#include "pathmatcher/SimplePrefixMatcher.h"
-#include "settings/INIFile.h"
+#include "tasks/Task.h"
+#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"
#include "ui/instanceview/AccessibleInstanceView.h"
@@ -57,8 +59,7 @@
#include "ui/pages/BasePageProvider.h"
#include "ui/pages/global/APIPage.h"
#include "ui/pages/global/AccountListPage.h"
-#include "ui/pages/global/CustomCommandsPage.h"
-#include "ui/pages/global/EnvironmentVariablesPage.h"
+#include "ui/pages/global/AppearancePage.h"
#include "ui/pages/global/ExternalToolsPage.h"
#include "ui/pages/global/JavaPage.h"
#include "ui/pages/global/LanguagePage.h"
@@ -66,8 +67,10 @@
#include "ui/pages/global/MinecraftPage.h"
#include "ui/pages/global/ProxyPage.h"
+#include "ui/setupwizard/AutoJavaWizardPage.h"
#include "ui/setupwizard/JavaWizardPage.h"
#include "ui/setupwizard/LanguageWizardPage.h"
+#include "ui/setupwizard/LoginWizardPage.h"
#include "ui/setupwizard/PasteWizardPage.h"
#include "ui/setupwizard/SetupWizard.h"
#include "ui/setupwizard/ThemeWizardPage.h"
@@ -94,6 +97,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -105,8 +109,6 @@
#include "icons/IconList.h"
#include "net/HttpMetaCache.h"
-#include "java/JavaUtils.h"
-
#include "updater/ExternalUpdater.h"
#include "tools/JProfiler.h"
@@ -124,11 +126,11 @@
#include
#include
-#include
+#include "SysInfo.h"
#ifdef Q_OS_LINUX
#include
-#include "MangoHud.h"
+#include "LibraryUtils.h"
#include "gamemode_client.h"
#endif
@@ -150,9 +152,15 @@
#endif
#if defined Q_OS_WIN32
-#include "WindowsConsole.h"
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include
+#include
#endif
+#include "console/Console.h"
+
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
@@ -160,6 +168,63 @@ static const QLatin1String liveCheckFile("live.check");
PixmapCache* PixmapCache::s_instance = nullptr;
+static bool isANSIColorConsole;
+
+static QString defaultLogFormat = QStringLiteral(
+ "%{time process}"
+ " "
+ "%{if-debug}Debug:%{endif}"
+ "%{if-info}Info:%{endif}"
+ "%{if-warning}Warning:%{endif}"
+ "%{if-critical}Critical:%{endif}"
+ "%{if-fatal}Fatal:%{endif}"
+ " "
+ "%{if-category}[%{category}] %{endif}"
+ "%{message}"
+ " "
+ "(%{function}:%{line})");
+
+#define ansi_reset "\x1b[0m"
+#define ansi_bold "\x1b[1m"
+#define ansi_reset_bold "\x1b[22m"
+#define ansi_faint "\x1b[2m"
+#define ansi_italic "\x1b[3m"
+#define ansi_red_fg "\x1b[31m"
+#define ansi_green_fg "\x1b[32m"
+#define ansi_yellow_fg "\x1b[33m"
+#define ansi_blue_fg "\x1b[34m"
+#define ansi_purple_fg "\x1b[35m"
+#define ansi_inverse "\x1b[7m"
+
+// clang-format off
+static QString ansiLogFormat = QStringLiteral(
+ ansi_faint "%{time process}" ansi_reset
+ " "
+ "%{if-debug}" ansi_bold ansi_green_fg "D:" ansi_reset "%{endif}"
+ "%{if-info}" ansi_bold ansi_blue_fg "I:" ansi_reset "%{endif}"
+ "%{if-warning}" ansi_bold ansi_yellow_fg "W:" ansi_reset_bold "%{endif}"
+ "%{if-critical}" ansi_bold ansi_red_fg "C:" ansi_reset_bold "%{endif}"
+ "%{if-fatal}" ansi_bold ansi_inverse ansi_red_fg "F:" ansi_reset_bold "%{endif}"
+ " "
+ "%{if-category}" ansi_bold "[%{category}]" ansi_reset_bold " %{endif}"
+ "%{message}"
+ " "
+ ansi_reset ansi_faint "(%{function}:%{line})" ansi_reset
+);
+// clang-format on
+
+#undef ansi_inverse
+#undef ansi_purple_fg
+#undef ansi_blue_fg
+#undef ansi_yellow_fg
+#undef ansi_green_fg
+#undef ansi_red_fg
+#undef ansi_italic
+#undef ansi_faint
+#undef ansi_bold
+#undef ansi_reset_bold
+#undef ansi_reset
+
namespace {
/** This is used so that we can output to the log file in addition to the CLI. */
@@ -168,11 +233,27 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt
static std::mutex loggerMutex;
const std::lock_guard lock(loggerMutex); // synchronized, QFile logFile is not thread-safe
+ if (isANSIColorConsole) {
+ // ensure default is set for log file
+ qSetMessagePattern(defaultLogFormat);
+ }
+
QString out = qFormatLogMessage(type, context, msg);
- out += QChar::LineFeed;
+ if (APPLICATION->logModel) {
+ APPLICATION->logModel->append(MessageLevel::fromQtMsgType(type), out);
+ }
+ out += QChar::LineFeed;
APPLICATION->logFile->write(out.toUtf8());
APPLICATION->logFile->flush();
+
+ if (isANSIColorConsole) {
+ // format ansi for console;
+ qSetMessagePattern(ansiLogFormat);
+ out = qFormatLogMessage(type, context, msg);
+ out += QChar::LineFeed;
+ }
+
QTextStream(stderr) << out.toLocal8Bit();
fflush(stderr);
}
@@ -209,19 +290,17 @@ std::tuple read_lock_File(const Q
Application::Application(int& argc, char** argv) : QApplication(argc, argv)
{
-#if defined Q_OS_WIN32
- // attach the parent console if stdout not already captured
- if (AttachWindowsConsole()) {
- consoleAttached = true;
+ if (console::isConsole()) {
+ isANSIColorConsole = true;
}
-#endif
+
setOrganizationName(BuildConfig.LAUNCHER_NAME);
setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN);
setApplicationName(BuildConfig.LAUNCHER_NAME);
setApplicationDisplayName(QString("%1 %2").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString()));
setApplicationVersion(BuildConfig.printableVersionString() + "\n" + BuildConfig.GIT_COMMIT);
- setDesktopFileName(BuildConfig.LAUNCHER_DESKTOPFILENAME);
- startTime = QDateTime::currentDateTime();
+ setDesktopFileName(BuildConfig.LAUNCHER_APPID);
+ m_startTime = QDateTime::currentDateTime();
// Don't quit on hiding the last window
this->setQuitOnLastWindowClosed(false);
@@ -235,8 +314,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
{ { { "d", "dir" }, "Use a custom path as application root (use '.' for current directory)", "directory" },
{ { "l", "launch" }, "Launch the specified instance (by instance ID)", "instance" },
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
+ { { "w", "world" }, "Join the specified world on launch (only valid in combination with --launch)", "world" },
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" },
+ { { "o", "offline" }, "Launch offline, with given player name (only valid in combination with --launch)", "offline" },
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
+ { "show-window", "Show the main launcher window (useful in combination with --launch)" },
{ { "I", "import" }, "Import instance or resource from specified local path or URL", "url" },
{ "show", "Opens the window for the specified instance (by instance ID)", "show" } });
// Has to be positional for some OS to handle that properly
@@ -249,10 +331,16 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_instanceIdToLaunch = parser.value("launch");
m_serverToJoin = parser.value("server");
+ m_worldToJoin = parser.value("world");
m_profileToUse = parser.value("profile");
+ if (parser.isSet("offline")) {
+ m_launchOffline = true;
+ m_offlineName = parser.value("offline");
+ }
m_liveCheck = parser.isSet("alive");
m_instanceIdToShowWindowOf = parser.value("show");
+ m_showMainWindow = parser.isSet("show-window");
for (auto url : parser.values("import")) {
m_urlsToImport.append(normalizeImportUrl(url));
@@ -264,8 +352,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
}
// error if --launch is missing with --server or --profile
- if ((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) {
- std::cerr << "--server and --profile can only be used in combination with --launch!" << std::endl;
+ if ((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty() || !m_profileToUse.isEmpty() || m_launchOffline) &&
+ m_instanceIdToLaunch.isEmpty()) {
+ std::cerr << "--server, --profile and --offline can only be used in combination with --launch!" << std::endl;
m_status = Application::Failed;
return;
}
@@ -291,16 +380,21 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
QString adjustedBy;
QString dataPath;
// change folder
+ QString dataDirEnv;
QString dirParam = parser.value("dir");
if (!dirParam.isEmpty()) {
// the dir param. it makes multimc data path point to whatever the user specified
// on command line
adjustedBy = "Command line";
dataPath = dirParam;
+ } else if (dataDirEnv = QProcessEnvironment::systemEnvironment().value(QString("%1_DATA_DIR").arg(BuildConfig.LAUNCHER_NAME.toUpper()));
+ !dataDirEnv.isEmpty()) {
+ adjustedBy = "System environment";
+ dataPath = dataDirEnv;
} else {
QDir foo;
if (DesktopServices::isSnap()) {
- foo = QDir(getenv("SNAP_USER_COMMON"));
+ foo = QDir(qEnvironmentVariable("SNAP_USER_COMMON"));
} else {
foo = QDir(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), ".."));
}
@@ -357,19 +451,20 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_peerInstance = new LocalPeer(this, appID);
connect(m_peerInstance, &LocalPeer::messageReceived, this, &Application::messageReceived);
if (m_peerInstance->isClient()) {
+ bool sentMessage = false;
int timeout = 2000;
if (m_instanceIdToLaunch.isEmpty()) {
ApplicationMessage activate;
activate.command = "activate";
- m_peerInstance->sendMessage(activate.serialize(), timeout);
+ sentMessage = m_peerInstance->sendMessage(activate.serialize(), timeout);
if (!m_urlsToImport.isEmpty()) {
for (auto url : m_urlsToImport) {
ApplicationMessage import;
import.command = "import";
import.args.insert("url", url.toString());
- m_peerInstance->sendMessage(import.serialize(), timeout);
+ sentMessage = m_peerInstance->sendMessage(import.serialize(), timeout);
}
}
} else {
@@ -379,14 +474,26 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
if (!m_serverToJoin.isEmpty()) {
launch.args["server"] = m_serverToJoin;
+ } else if (!m_worldToJoin.isEmpty()) {
+ launch.args["world"] = m_worldToJoin;
}
if (!m_profileToUse.isEmpty()) {
launch.args["profile"] = m_profileToUse;
}
- m_peerInstance->sendMessage(launch.serialize(), timeout);
+ if (m_launchOffline) {
+ launch.args["offline_enabled"] = "true";
+ launch.args["offline_name"] = m_offlineName;
+ }
+ sentMessage = m_peerInstance->sendMessage(launch.serialize(), timeout);
+ }
+ if (sentMessage) {
+ m_status = Application::Succeeded;
+ return;
+ } else {
+ std::cerr << "Unable to redirect command to already running instance\n";
+ // C function not Qt function - event loop not started yet
+ ::exit(1);
}
- m_status = Application::Succeeded;
- return;
}
}
@@ -394,63 +501,48 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
{
static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "-%0.log";
static const QString logBase = FS::PathCombine("logs", baseLogFile);
- auto moveFile = [](const QString& oldName, const QString& newName) {
- QFile::remove(newName);
- QFile::copy(oldName, newName);
- QFile::remove(oldName);
- };
if (FS::ensureFolderPathExists("logs")) { // if this did not fail
for (auto i = 0; i <= 4; i++)
if (auto oldName = baseLogFile.arg(i);
QFile::exists(oldName)) // do not pointlessly delete new files if the old ones are not there
- moveFile(oldName, logBase.arg(i));
+ FS::move(oldName, logBase.arg(i));
}
for (auto i = 4; i > 0; i--)
- moveFile(logBase.arg(i - 1), logBase.arg(i));
+ FS::move(logBase.arg(i - 1), logBase.arg(i));
logFile = std::unique_ptr(new QFile(logBase.arg(0)));
if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
showFatalErrorMessage("The launcher data folder is not writable!",
- QString("The launcher couldn't create a log file - the data folder is not writable.\n"
+ QString("The launcher couldn't create a log file - %1.\n"
"\n"
"Make sure you have write permissions to the data folder.\n"
- "(%1)\n"
+ "(%2)\n"
"\n"
"The launcher cannot continue until you fix this problem.")
+ .arg(logFile->errorString())
.arg(dataPath));
return;
}
qInstallMessageHandler(appDebugOutput);
+ qSetMessagePattern(defaultLogFormat);
- qSetMessagePattern(
- "%{time process}"
- " "
- "%{if-debug}D%{endif}"
- "%{if-info}I%{endif}"
- "%{if-warning}W%{endif}"
- "%{if-critical}C%{endif}"
- "%{if-fatal}F%{endif}"
- " "
- "|"
- " "
- "%{if-category}[%{category}]: %{endif}"
- "%{message}");
+ logModel.reset(new LogModel(this));
bool foundLoggingRules = false;
auto logRulesFile = QStringLiteral("qtlogging.ini");
auto logRulesPath = FS::PathCombine(dataPath, logRulesFile);
- qDebug() << "Testing" << logRulesPath << "...";
+ qInfo() << "Testing" << logRulesPath << "...";
foundLoggingRules = QFile::exists(logRulesPath);
// search the dataPath()
// seach app data standard path
- if (!foundLoggingRules && !isPortable() && dirParam.isEmpty()) {
+ if (!foundLoggingRules && !isPortable() && dirParam.isEmpty() && dataDirEnv.isEmpty()) {
logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile));
if (!logRulesPath.isEmpty()) {
- qDebug() << "Found" << logRulesPath << "...";
+ qInfo() << "Found" << logRulesPath << "...";
foundLoggingRules = true;
}
}
@@ -461,28 +553,28 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
#else
logRulesPath = FS::PathCombine(m_rootPath, logRulesFile);
#endif
- qDebug() << "Testing" << logRulesPath << "...";
+ qInfo() << "Testing" << logRulesPath << "...";
foundLoggingRules = QFile::exists(logRulesPath);
}
if (foundLoggingRules) {
// load and set logging rules
- qDebug() << "Loading logging rules from:" << logRulesPath;
+ qInfo() << "Loading logging rules from:" << logRulesPath;
QSettings loggingRules(logRulesPath, QSettings::IniFormat);
loggingRules.beginGroup("Rules");
QStringList rule_names = loggingRules.childKeys();
QStringList rules;
- qDebug() << "Setting log rules:";
+ qInfo() << "Setting log rules:";
for (auto rule_name : rule_names) {
auto rule = QString("%1=%2").arg(rule_name).arg(loggingRules.value(rule_name).toString());
rules.append(rule);
- qDebug() << " " << rule;
+ qInfo() << " " << rule;
}
auto rules_str = rules.join("\n");
QLoggingCategory::setFilterRules(rules_str);
}
- qDebug() << "<> Log initialized.";
+ qInfo() << "<> Log initialized.";
}
{
@@ -499,31 +591,33 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
}
{
- qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME + ", " + QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", "));
- qDebug() << "Version : " << BuildConfig.printableVersionString();
- qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM;
- qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
- qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC;
- qDebug() << "Compiled for : " << BuildConfig.systemID();
- qDebug() << "Compiled by : " << BuildConfig.compilerID();
- qDebug() << "Build Artifact : " << BuildConfig.BUILD_ARTIFACT;
- qDebug() << "Updates Enabled : " << (updaterEnabled() ? "Yes" : "No");
+ 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");
if (adjustedBy.size()) {
- qDebug() << "Work dir before adjustment : " << origcwdPath;
- qDebug() << "Work dir after adjustment : " << QDir::currentPath();
- qDebug() << "Adjusted by : " << adjustedBy;
+ qInfo() << "Work dir before adjustment :" << origcwdPath;
+ qInfo() << "Work dir after adjustment :" << QDir::currentPath();
+ qInfo() << "Adjusted by :" << adjustedBy;
} else {
- qDebug() << "Work dir : " << QDir::currentPath();
+ qInfo() << "Work dir :" << QDir::currentPath();
}
- qDebug() << "Binary path : " << binPath;
- qDebug() << "Application root path : " << m_rootPath;
+ qInfo() << "Binary path :" << binPath;
+ qInfo() << "Application root path :" << m_rootPath;
if (!m_instanceIdToLaunch.isEmpty()) {
- qDebug() << "ID of instance to launch : " << m_instanceIdToLaunch;
+ qInfo() << "ID of instance to launch :" << m_instanceIdToLaunch;
}
if (!m_serverToJoin.isEmpty()) {
- qDebug() << "Address of server to join :" << m_serverToJoin;
+ qInfo() << "Address of server to join :" << m_serverToJoin;
+ } else if (!m_worldToJoin.isEmpty()) {
+ qInfo() << "Name of the world to join :" << m_worldToJoin;
}
- qDebug() << "<> Paths set.";
+ qInfo() << "<> Paths set.";
}
if (m_liveCheck) {
@@ -533,11 +627,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
if (check.write(payload) == payload.size()) {
check.close();
} else {
- qWarning() << "Could not write into" << liveCheckFile << "!";
+ qWarning() << "Could not write into" << liveCheckFile << "error:" << check.errorString();
check.remove(); // also closes file!
}
} else {
- qWarning() << "Could not open" << liveCheckFile << "for writing!";
+ qWarning() << "Could not open" << liveCheckFile << "for writing:" << check.errorString();
}
}
@@ -558,6 +652,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("NumberOfConcurrentTasks", 10);
m_settings->registerSetting("NumberOfConcurrentDownloads", 6);
+ m_settings->registerSetting("NumberOfManualRetries", 1);
+ m_settings->registerSetting("RequestTimeout", 60);
QString defaultMonospace;
int defaultSize = 11;
@@ -578,20 +674,37 @@ 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);
m_settings->registerSetting("ConsoleMaxLines", 100000);
m_settings->registerSetting("ConsoleOverflowStop", true);
+ logModel->setMaxLines(getConsoleMaxLines(settings()));
+ logModel->setStopOnOverflow(shouldStopOnConsoleOverflow(settings()));
+ logModel->setOverflowMessage(tr("Cannot display this log since the log length surpassed %1 lines.").arg(logModel->getMaxLines()));
+
// Folders
m_settings->registerSetting("InstanceDir", "instances");
m_settings->registerSetting({ "CentralModsDir", "ModsDir" }, "mods");
m_settings->registerSetting("IconsDir", "icons");
m_settings->registerSetting("DownloadsDir", QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
m_settings->registerSetting("DownloadsDirWatchRecursive", false);
+ m_settings->registerSetting("MoveModsFromDownloadsDir", false);
+ m_settings->registerSetting("SkinsDir", "skins");
+ m_settings->registerSetting("JavaDir", "java");
+
+#ifdef Q_OS_MACOS
+ // Folder security-scoped bookmarks
+ m_settings->registerSetting("InstanceDirBookmark", "");
+ m_settings->registerSetting("CentralModsDirBookmark", "");
+ m_settings->registerSetting("IconsDirBookmark", "");
+ m_settings->registerSetting("DownloadsDirBookmark", "");
+ m_settings->registerSetting("SkinsDirBookmark", "");
+ m_settings->registerSetting("JavaDirBookmark", "");
+#endif
// Editors
m_settings->registerSetting("JsonEditor", QString());
@@ -620,8 +733,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// Memory
m_settings->registerSetting({ "MinMemAlloc", "MinMemoryAlloc" }, 512);
- m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, suitableMaxMem());
+ m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, SysInfo::defaultMaxJvmMem());
m_settings->registerSetting("PermGen", 128);
+ m_settings->registerSetting("LowMemWarning", true);
// Java Settings
m_settings->registerSetting("JavaPath", "");
@@ -634,6 +748,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("JvmArgs", "");
m_settings->registerSetting("IgnoreJavaCompatibility", false);
m_settings->registerSetting("IgnoreJavaWizard", false);
+ auto defaultEnableAutoJava = m_settings->get("JavaPath").toString().isEmpty();
+ m_settings->registerSetting("AutomaticJavaSwitch", defaultEnableAutoJava);
+ m_settings->registerSetting("AutomaticJavaDownload", defaultEnableAutoJava);
+ m_settings->registerSetting("UserAskedAboutAutomaticJavaDownload", false);
// Legacy settings
m_settings->registerSetting("OnlineFixes", false);
@@ -659,6 +777,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// Minecraft mods
m_settings->registerSetting("ModMetadataDisabled", false);
m_settings->registerSetting("ModDependenciesDisabled", false);
+ m_settings->registerSetting("SkipModpackUpdatePrompt", false);
+ m_settings->registerSetting("ShowModIncompat", false);
// Minecraft offline player name
m_settings->registerSetting("LastOfflinePlayerName", "");
@@ -673,12 +793,15 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// The cat
m_settings->registerSetting("TheCat", false);
m_settings->registerSetting("CatOpacity", 100);
+ m_settings->registerSetting("CatFit", "fit");
m_settings->registerSetting("StatusBarVisible", true);
m_settings->registerSetting("ToolbarsLocked", false);
+ // Instance
m_settings->registerSetting("InstSortMode", "Name");
+ m_settings->registerSetting("InstRenamingMode", "AskEverytime");
m_settings->registerSetting("SelectedInstance", QString());
// Window state and geometry
@@ -696,10 +819,17 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("UpdateDialogGeometry", "");
+ m_settings->registerSetting("NewsGeometry", "");
+
m_settings->registerSetting("ModDownloadGeometry", "");
m_settings->registerSetting("RPDownloadGeometry", "");
m_settings->registerSetting("TPDownloadGeometry", "");
m_settings->registerSetting("ShaderDownloadGeometry", "");
+ m_settings->registerSetting("DataPackDownloadGeometry", "");
+
+ // data pack window
+ // in future, more pages may be added - so this name is chosen to avoid needing migration
+ m_settings->registerSetting("WorldManagementGeometry", "");
// HACK: This code feels so stupid is there a less stupid way of doing this?
{
@@ -725,20 +855,26 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
}
}
{
+ auto resetIfInvalid = [this](const Setting* setting) {
+ if (const QUrl url(setting->get().toString()); !url.isValid() || (url.scheme() != "http" && url.scheme() != "https")) {
+ m_settings->reset(setting->id());
+ }
+ };
+
// Meta URL
- m_settings->registerSetting("MetaURLOverride", "");
+ resetIfInvalid(m_settings->registerSetting("MetaURLOverride", "").get());
- QUrl metaUrl(m_settings->get("MetaURLOverride").toString());
+ // Resource URL
+ resetIfInvalid(m_settings->registerSetting({ "ResourceURLOverride", "ResourceURL" }, "").get());
- // get rid of invalid meta urls
- if (!metaUrl.isValid() || (metaUrl.scheme() != "http" && metaUrl.scheme() != "https"))
- m_settings->reset("MetaURLOverride");
+ // Legacy FML libs URL
+ resetIfInvalid(m_settings->registerSetting("LegacyFMLLibsURLOverride", "").get());
}
m_settings->registerSetting("CloseAfterLaunch", false);
m_settings->registerSetting("QuitAfterGameStop", false);
- m_settings->registerSetting("Env", QVariant(QMap()));
+ m_settings->registerSetting("Env", "{}");
// Custom Microsoft Authentication Client ID
m_settings->registerSetting("MSAClientIDOverride", "");
@@ -754,30 +890,33 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->set("FlameKeyOverride", flameKey);
m_settings->reset("CFKeyOverride");
}
+ m_settings->registerSetting("FallbackMRBlockedMods", true);
m_settings->registerSetting("ModrinthToken", "");
m_settings->registerSetting("UserAgentOverride", "");
// FTBApp instances
m_settings->registerSetting("FTBAppInstancesPath", "");
+ // Custom Technic Client ID
+ m_settings->registerSetting("TechnicClientID", "");
+
// Init page provider
{
- m_globalSettingsProvider = std::make_shared(tr("Settings"));
+ m_globalSettingsProvider = std::make_unique(tr("Settings"));
m_globalSettingsProvider->addPage();
+ m_globalSettingsProvider->addPage();
+ m_globalSettingsProvider->addPage();
m_globalSettingsProvider->addPage();
m_globalSettingsProvider->addPage();
- m_globalSettingsProvider->addPage();
- m_globalSettingsProvider->addPage();
- m_globalSettingsProvider->addPage();
- m_globalSettingsProvider->addPage();
- m_globalSettingsProvider->addPage();
m_globalSettingsProvider->addPage();
m_globalSettingsProvider->addPage();
+ m_globalSettingsProvider->addPage();
+ m_globalSettingsProvider->addPage();
}
PixmapCache::setInstance(new PixmapCache(this));
- qDebug() << "<> Settings loaded.";
+ qInfo() << "<> Settings loaded.";
}
#ifndef QT_NO_ACCESSIBILITY
@@ -793,7 +932,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
QString user = settings()->get("ProxyUser").toString();
QString pass = settings()->get("ProxyPass").toString();
updateProxySettings(proxyTypeStr, addr, port, user, pass);
- qDebug() << "<> Network done.";
+ qInfo() << "<> Network done.";
}
// load translations
@@ -801,8 +940,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_translations.reset(new TranslationsModel("translations"));
auto bcp47Name = m_settings->get("Language").toString();
m_translations->selectLanguage(bcp47Name);
- qDebug() << "Your language is" << bcp47Name;
- qDebug() << "<> Translations loaded.";
+ qInfo() << "Your language is" << bcp47Name;
+ qInfo() << "<> Translations loaded.";
}
// Instance icons
@@ -812,65 +951,75 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
":/icons/multimc/128x128/instances/", ":/icons/multimc/scalable/instances/" };
m_icons.reset(new IconList(instFolders, setting->get().toString()));
connect(setting.get(), &Setting::SettingChanged,
- [&](const Setting&, QVariant value) { m_icons->directoryChanged(value.toString()); });
- qDebug() << "<> Instance icons intialized.";
+ [this](const Setting&, QVariant value) { m_icons->directoryChanged(value.toString()); });
+ qInfo() << "<> Instance icons initialized.";
}
// Themes
m_themeManager = std::make_unique();
+#ifdef Q_OS_MACOS
+ // for macOS: getting directory settings will generate URL security-scoped bookmarks if needed and not present
+ // this facilitates a smooth transition from a non-sandboxed version of the launcher, that likely can access the directory,
+ // and a sandboxed version that can't access the directory without a bookmark
+ // this section can likely be removed once the sandboxed version has been released for a while and migrations aren't done anymore
+ {
+ m_settings->get("InstanceDir");
+ m_settings->get("CentralModsDir");
+ m_settings->get("IconsDir");
+ m_settings->get("DownloadsDir");
+ m_settings->get("SkinsDir");
+ m_settings->get("JavaDir");
+ }
+#endif
+
// initialize and load all instances
{
auto InstDirSetting = m_settings->getSetting("InstanceDir");
// 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 = InstDirSetting->get().toString();
- qDebug() << "Instance path : " << instDir;
+ QString instDir = m_settings->get("InstanceDir").toString();
+ qInfo() << "Instance path :" << instDir;
if (FS::checkProblemticPathJava(QDir(instDir))) {
qWarning() << "Your instance path contains \'!\' and this is known to cause java problems!";
}
- m_instances.reset(new InstanceList(m_settings, instDir, this));
+ m_instances.reset(new InstanceList(m_settings.get(), instDir, this));
connect(InstDirSetting.get(), &Setting::SettingChanged, m_instances.get(), &InstanceList::on_InstFolderChanged);
- qDebug() << "Loading Instances...";
+ qInfo() << "Loading Instances...";
m_instances->loadList();
- qDebug() << "<> Instances loaded.";
+ qInfo() << "<> Instances loaded.";
}
// and accounts
{
m_accounts.reset(new AccountList(this));
- qDebug() << "Loading accounts...";
+ qInfo() << "Loading accounts...";
m_accounts->setListFilePath("accounts.json", true);
m_accounts->loadList();
m_accounts->fillQueue();
- qDebug() << "<> Accounts loaded.";
+ qInfo() << "<> Accounts loaded.";
}
// init the http meta cache
{
m_metacache.reset(new HttpMetaCache("metacache"));
m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath());
- m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath());
- m_metacache->addBase("versions", QDir("versions").absolutePath());
m_metacache->addBase("libraries", QDir("libraries").absolutePath());
- m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath());
- m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
m_metacache->addBase("general", QDir("cache").absolutePath());
m_metacache->addBase("ATLauncherPacks", QDir("cache/ATLauncherPacks").absolutePath());
m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath());
- m_metacache->addBase("ModpacksCHPacks", QDir("cache/ModpacksCHPacks").absolutePath());
m_metacache->addBase("TechnicPacks", QDir("cache/TechnicPacks").absolutePath());
m_metacache->addBase("FlamePacks", QDir("cache/FlamePacks").absolutePath());
m_metacache->addBase("FlameMods", QDir("cache/FlameMods").absolutePath());
m_metacache->addBase("ModrinthPacks", QDir("cache/ModrinthPacks").absolutePath());
m_metacache->addBase("ModrinthModpacks", QDir("cache/ModrinthModpacks").absolutePath());
- m_metacache->addBase("root", QDir::currentPath());
m_metacache->addBase("translations", QDir("translations").absolutePath());
- m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
m_metacache->addBase("meta", QDir("meta").absolutePath());
+ m_metacache->addBase("java", QDir("cache/java").absolutePath());
+ m_metacache->addBase("feed", QDir("cache/feed").absolutePath());
m_metacache->Load();
- qDebug() << "<> Cache initialized.";
+ qInfo() << "<> Cache initialized.";
}
// now we have network, download translation updates
@@ -879,13 +1028,14 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// FIXME: what to do with these?
m_profilers.insert("jprofiler", std::shared_ptr(new JProfilerFactory()));
m_profilers.insert("jvisualvm", std::shared_ptr(new JVisualVMFactory()));
+ m_profilers.insert("generic", std::shared_ptr(new GenericProfilerFactory()));
for (auto profiler : m_profilers.values()) {
- profiler->registerSettings(m_settings);
+ profiler->registerSettings(m_settings.get());
}
// Create the MCEdit thing... why is this here?
{
- m_mcedit.reset(new MCEditTool(m_settings));
+ m_mcedit.reset(new MCEditTool(m_settings.get()));
}
#ifdef Q_OS_MACOS
@@ -947,8 +1097,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
[[fallthrough]];
default: {
qDebug() << "Exiting because update lockfile is present";
- QMetaObject::invokeMethod(
- this, []() { exit(1); }, Qt::QueuedConnection);
+ QMetaObject::invokeMethod(this, []() { exit(1); }, Qt::QueuedConnection);
return;
}
}
@@ -980,8 +1129,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
[[fallthrough]];
default: {
qDebug() << "Exiting because update lockfile is present";
- QMetaObject::invokeMethod(
- this, []() { exit(1); }, Qt::QueuedConnection);
+ QMetaObject::invokeMethod(this, []() { exit(1); }, Qt::QueuedConnection);
return;
}
}
@@ -993,7 +1141,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
"\n"
"You are now running %1 .\n"
"Check the Prism Launcher updater log at: \n"
- "%1\n"
+ "%2\n"
"for details.")
.arg(BuildConfig.printableVersionString())
.arg(update_log_path);
@@ -1009,7 +1157,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
}
// notify user if /tmp is mounted with `noexec` (#1693)
- {
+ QString jvmArgs = m_settings->get("JvmArgs").toString();
+ if (jvmArgs.indexOf("java.io.tmpdir") == -1) { /* java.io.tmpdir is a valid workaround, so don't annoy */
bool is_tmp_noexec = false;
#if defined(Q_OS_LINUX)
@@ -1029,7 +1178,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
if (is_tmp_noexec) {
auto infoMsg =
tr("Your /tmp directory is currently mounted with the 'noexec' flag enabled.\n"
- "Some versions of Minecraft may not launch.\n");
+ "Some versions of Minecraft may not launch.\n"
+ "\n"
+ "You may solve this issue by remounting /tmp as 'exec' or setting "
+ "the java.io.tmpdir JVM argument to a writeable directory in a "
+ "filesystem where the 'exec' flag is set (e.g., /home/user/.local/tmp)\n");
auto msgBox = new QMessageBox(QMessageBox::Information, tr("Incompatible system configuration"), infoMsg, QMessageBox::Ok);
msgBox->setDefaultButton(QMessageBox::Ok);
msgBox->setAttribute(Qt::WA_DeleteOnClose);
@@ -1039,6 +1192,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
}
}
+ if (qgetenv("XDG_CURRENT_DESKTOP") == "gamescope") {
+ installEventFilter(new ToolTipFilter);
+ }
+
if (createSetupWizard()) {
return;
}
@@ -1049,8 +1206,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
bool Application::createSetupWizard()
{
- bool javaRequired = [&]() {
- bool ignoreJavaWizard = m_settings->get("IgnoreJavaWizard").toBool();
+ bool javaRequired = [this]() {
+ if (BuildConfig.JAVA_DOWNLOADER_ENABLED && settings()->get("AutomaticJavaDownload").toBool()) {
+ return false;
+ }
+ bool ignoreJavaWizard = settings()->get("IgnoreJavaWizard").toBool();
if (ignoreJavaWizard) {
return false;
}
@@ -1062,24 +1222,31 @@ bool Application::createSetupWizard()
}
QString currentJavaPath = settings()->get("JavaPath").toString();
QString actualPath = FS::ResolveExecutable(currentJavaPath);
- if (actualPath.isNull()) {
- return true;
- }
- return false;
+ return actualPath.isNull();
}();
+ bool askjava = BuildConfig.JAVA_DOWNLOADER_ENABLED && !javaRequired && !settings()->get("AutomaticJavaDownload").toBool() &&
+ !settings()->get("AutomaticJavaSwitch").toBool() && !settings()->get("UserAskedAboutAutomaticJavaDownload").toBool();
bool languageRequired = settings()->get("Language").toString().isEmpty();
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
bool validWidgets = m_themeManager->isValidApplicationTheme(settings()->get("ApplicationTheme").toString());
bool validIcons = m_themeManager->isValidIconTheme(settings()->get("IconTheme").toString());
+ bool login = !m_accounts->anyAccountIsValid() && capabilities() & Application::SupportsMSA;
bool themeInterventionRequired = !validWidgets || !validIcons;
- bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
-
+ bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired || askjava || login;
if (wizardRequired) {
// set default theme after going into theme wizard
if (!validIcons)
settings()->set("IconTheme", QString("pe_colored"));
- if (!validWidgets)
- settings()->set("ApplicationTheme", QString("system"));
+ if (!validWidgets) {
+#if defined(Q_OS_WIN32) && QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
+ const QString style =
+ QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark ? QStringLiteral("dark") : QStringLiteral("bright");
+#else
+ const QString style = QStringLiteral("system");
+#endif
+
+ settings()->set("ApplicationTheme", style);
+ }
m_themeManager->applyCurrentlySelectedTheme(true);
@@ -1090,6 +1257,8 @@ bool Application::createSetupWizard()
if (javaRequired) {
m_setupWizard->addPage(new JavaWizardPage(m_setupWizard));
+ } else if (askjava) {
+ m_setupWizard->addPage(new AutoJavaWizardPage(m_setupWizard));
}
if (pasteInterventionRequired) {
@@ -1100,11 +1269,14 @@ bool Application::createSetupWizard()
m_setupWizard->addPage(new ThemeWizardPage(m_setupWizard));
}
+ if (login) {
+ m_setupWizard->addPage(new LoginWizardPage(m_setupWizard));
+ }
connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished);
m_setupWizard->show();
- return true;
}
- return false;
+
+ return wizardRequired || login;
}
bool Application::updaterEnabled()
@@ -1141,6 +1313,9 @@ bool Application::event(QEvent* event)
#endif
if (event->type() == QEvent::FileOpen) {
+ if (!m_mainWindow) {
+ showMainWindow(false);
+ }
auto ev = static_cast(event);
m_mainWindow->processURLs({ ev->url() });
}
@@ -1160,14 +1335,17 @@ void Application::performMainStartupAction()
if (!m_instanceIdToLaunch.isEmpty()) {
auto inst = instances()->getInstanceById(m_instanceIdToLaunch);
if (inst) {
- MinecraftServerTargetPtr serverToJoin = nullptr;
+ MinecraftTarget::Ptr targetToJoin = nullptr;
MinecraftAccountPtr accountToUse = nullptr;
qDebug() << "<> Instance" << m_instanceIdToLaunch << "launching";
if (!m_serverToJoin.isEmpty()) {
// FIXME: validate the server string
- serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(m_serverToJoin)));
+ targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(m_serverToJoin, false)));
qDebug() << " Launching with server" << m_serverToJoin;
+ } else if (!m_worldToJoin.isEmpty()) {
+ targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(m_worldToJoin, true)));
+ qDebug() << " Launching with world" << m_worldToJoin;
}
if (!m_profileToUse.isEmpty()) {
@@ -1178,8 +1356,11 @@ void Application::performMainStartupAction()
qDebug() << " Launching with account" << m_profileToUse;
}
- launch(inst, true, false, serverToJoin, accountToUse);
- return;
+ launch(inst, m_launchOffline ? LaunchMode::Offline : LaunchMode::Normal, targetToJoin, accountToUse, m_offlineName);
+
+ if (!m_showMainWindow) {
+ return;
+ }
}
}
if (!m_instanceIdToShowWindowOf.isEmpty()) {
@@ -1209,6 +1390,12 @@ void Application::performMainStartupAction()
qDebug() << "<> Updater started.";
}
+ { // delete instances tmp dirctory
+ auto instDir = m_settings->get("InstanceDir").toString();
+ const QString tempRoot = FS::PathCombine(instDir, ".tmp");
+ FS::deletePath(tempRoot);
+ }
+
if (!m_urlsToImport.isEmpty()) {
qDebug() << "<> Importing from url:" << m_urlsToImport;
m_mainWindow->processURLs(m_urlsToImport);
@@ -1226,30 +1413,27 @@ Application::~Application()
{
// Shut down logger by setting the logger function to nothing
qInstallMessageHandler(nullptr);
-
-#if defined Q_OS_WIN32
- // Detach from Windows console
- if (consoleAttached) {
- fclose(stdout);
- fclose(stdin);
- fclose(stderr);
- FreeConsole();
- }
-#endif
}
void Application::messageReceived(const QByteArray& message)
{
- if (status() != Initialized) {
- qDebug() << "Received message" << message << "while still initializing. It will be ignored.";
- return;
- }
-
ApplicationMessage received;
received.parse(message);
auto& command = received.command;
+ if (status() != Initialized) {
+ bool isLoginAtempt = false;
+ if (command == "import") {
+ QString url = received.args["url"];
+ isLoginAtempt = !url.isEmpty() && normalizeImportUrl(url).scheme() == BuildConfig.LAUNCHER_APP_BINARY_NAME;
+ }
+ if (!isLoginAtempt) {
+ qDebug() << "Received message" << message << "while still initializing. It will be ignored.";
+ return;
+ }
+ }
+
if (command == "activate") {
showMainWindow();
} else if (command == "import") {
@@ -1258,13 +1442,19 @@ void Application::messageReceived(const QByteArray& message)
qWarning() << "Received" << command << "message without a zip path/URL.";
return;
}
+ if (!m_mainWindow) {
+ showMainWindow(false);
+ }
m_mainWindow->processURLs({ normalizeImportUrl(url) });
} else if (command == "launch") {
QString id = received.args["id"];
QString server = received.args["server"];
+ QString world = received.args["world"];
QString profile = received.args["profile"];
+ bool offline = received.args["offline_enabled"] == "true";
+ QString offlineName = received.args["offline_name"];
- InstancePtr instance;
+ BaseInstance* instance;
if (!id.isEmpty()) {
instance = instances()->getInstanceById(id);
if (!instance) {
@@ -1276,11 +1466,12 @@ void Application::messageReceived(const QByteArray& message)
return;
}
- MinecraftServerTargetPtr serverObject = nullptr;
+ MinecraftTarget::Ptr serverObject = nullptr;
if (!server.isEmpty()) {
- serverObject = std::make_shared(MinecraftServerTarget::parse(server));
+ serverObject = std::make_shared(MinecraftTarget::parse(server, false));
+ } else if (!world.isEmpty()) {
+ serverObject = std::make_shared(MinecraftTarget::parse(world, true));
}
-
MinecraftAccountPtr accountObject;
if (!profile.isEmpty()) {
accountObject = accounts()->getAccountByProfileName(profile);
@@ -1291,31 +1482,28 @@ void Application::messageReceived(const QByteArray& message)
}
}
- launch(instance, true, false, serverObject, accountObject);
+ launch(instance, offline ? LaunchMode::Offline : LaunchMode::Normal, serverObject, accountObject, offlineName);
} else {
qWarning() << "Received invalid message" << message;
}
}
-std::shared_ptr Application::translations()
+TranslationsModel* Application::translations()
{
- return m_translations;
+ return m_translations.get();
}
-std::shared_ptr Application::javalist()
+JavaInstallList* Application::javalist()
{
if (!m_javalist) {
m_javalist.reset(new JavaInstallList());
}
- return m_javalist;
+ return m_javalist.get();
}
-QIcon Application::getThemedIcon(const QString& name)
+QIcon Application::logo()
{
- if (name == "logo") {
- return QIcon(":/" + BuildConfig.LAUNCHER_SVGFILENAME);
- }
- return QIcon::fromTheme(name);
+ return QIcon(":/" + BuildConfig.LAUNCHER_SVGFILENAME);
}
bool Application::openJsonEditor(const QString& filename)
@@ -1329,15 +1517,16 @@ bool Application::openJsonEditor(const QString& filename)
}
}
-bool Application::launch(InstancePtr instance,
- bool online,
- bool demo,
- MinecraftServerTargetPtr serverToJoin,
- MinecraftAccountPtr accountToUse)
+bool Application::launch(BaseInstance* instance,
+ LaunchMode mode,
+ MinecraftTarget::Ptr targetToJoin,
+ MinecraftAccountPtr accountToUse,
+ const QString& offlineName)
{
if (m_updateRunning) {
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
} else if (instance->canLaunch()) {
+ QMutexLocker locker(&m_instanceExtrasMutex);
auto& extras = m_instanceExtras[instance->id()];
auto window = extras.window;
if (window) {
@@ -1348,21 +1537,19 @@ bool Application::launch(InstancePtr instance,
auto& controller = extras.controller;
controller.reset(new LaunchController());
controller->setInstance(instance);
- controller->setOnline(online);
- controller->setDemo(demo);
+ controller->setLaunchMode(mode);
controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get());
- controller->setServerToJoin(serverToJoin);
+ controller->setTargetToJoin(targetToJoin);
controller->setAccountToUse(accountToUse);
+ controller->setOfflineName(offlineName);
if (window) {
controller->setParentWidget(window);
} else if (m_mainWindow) {
controller->setParentWidget(m_mainWindow);
}
- connect(controller.get(), &LaunchController::succeeded, this, &Application::controllerSucceeded);
- connect(controller.get(), &LaunchController::failed, this, &Application::controllerFailed);
- connect(controller.get(), &LaunchController::aborted, this, [this] { controllerFailed(tr("Aborted")); });
+ connect(controller.get(), &LaunchController::finished, this, &Application::controllerFinished);
addRunningInstance();
- controller->start();
+ QMetaObject::invokeMethod(controller.get(), &Task::start, Qt::QueuedConnection);
return true;
} else if (instance->isRunning()) {
showInstanceWindow(instance, "console");
@@ -1374,15 +1561,17 @@ bool Application::launch(InstancePtr instance,
return false;
}
-bool Application::kill(InstancePtr instance)
+bool Application::kill(BaseInstance* instance)
{
if (!instance->isRunning()) {
qWarning() << "Attempted to kill instance" << instance->id() << ", which isn't running.";
return false;
}
+ QMutexLocker locker(&m_instanceExtrasMutex);
auto& extras = m_instanceExtras[instance->id()];
// NOTE: copy of the shared pointer keeps it alive
- auto controller = extras.controller;
+ auto& controller = extras.controller;
+ locker.unlock();
if (controller) {
return controller->abort();
}
@@ -1430,18 +1619,21 @@ void Application::updateIsRunning(bool running)
m_updateRunning = running;
}
-void Application::controllerSucceeded()
+void Application::controllerFinished()
{
- auto controller = qobject_cast(QObject::sender());
+ auto controller = qobject_cast(sender());
if (!controller)
return;
auto id = controller->id();
- auto& extras = m_instanceExtras[id];
+ QMutexLocker locker(&m_instanceExtrasMutex);
+ auto& extras = m_instanceExtras.at(id);
+
+ const bool wasSuccessful = controller->wasSuccessful();
// on success, do...
- if (controller->instance()->settings()->get("AutoCloseConsole").toBool()) {
+ if (wasSuccessful && controller->instance()->settings()->get("AutoCloseConsole").toBool()) {
if (extras.window) {
- extras.window->close();
+ QMetaObject::invokeMethod(extras.window, &QWidget::close, Qt::QueuedConnection);
}
}
extras.controller.reset();
@@ -1449,28 +1641,8 @@ void Application::controllerSucceeded()
// quit when there are no more windows.
if (shouldExitNow()) {
- m_status = Status::Succeeded;
- exit(0);
- }
-}
-
-void Application::controllerFailed(const QString& error)
-{
- Q_UNUSED(error);
- auto controller = qobject_cast(QObject::sender());
- if (!controller)
- return;
- auto id = controller->id();
- auto& extras = m_instanceExtras[id];
-
- // on failure, do... nothing
- extras.controller.reset();
- subRunningInstance();
-
- // quit when there are no more windows.
- if (shouldExitNow()) {
- m_status = Status::Failed;
- exit(1);
+ m_status = wasSuccessful ? Succeeded : Failed;
+ exit(wasSuccessful ? 0 : 1);
}
}
@@ -1483,9 +1655,9 @@ void Application::ShowGlobalSettings(class QWidget* parent, QString open_page)
{
SettingsObject::Lock lock(APPLICATION->settings());
PageDialog dlg(m_globalSettingsProvider.get(), open_page, parent);
+ connect(&dlg, &PageDialog::applied, this, &Application::globalSettingsApplied);
dlg.exec();
}
- emit globalSettingsClosed();
}
MainWindow* Application::showMainWindow(bool minimized)
@@ -1496,8 +1668,8 @@ MainWindow* Application::showMainWindow(bool minimized)
m_mainWindow->activateWindow();
} else {
m_mainWindow = new MainWindow();
- m_mainWindow->restoreState(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowState").toByteArray()));
- m_mainWindow->restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowGeometry").toByteArray()));
+ m_mainWindow->restoreState(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowState").toString().toUtf8()));
+ m_mainWindow->restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowGeometry").toString().toUtf8()));
if (minimized) {
m_mainWindow->showMinimized();
@@ -1513,11 +1685,26 @@ MainWindow* Application::showMainWindow(bool minimized)
return m_mainWindow;
}
-InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString page)
+ViewLogWindow* Application::showLogWindow()
+{
+ if (m_viewLogWindow) {
+ m_viewLogWindow->setWindowState(m_viewLogWindow->windowState() & ~Qt::WindowMinimized);
+ m_viewLogWindow->raise();
+ m_viewLogWindow->activateWindow();
+ } else {
+ m_viewLogWindow = new ViewLogWindow();
+ connect(m_viewLogWindow, &ViewLogWindow::isClosing, this, &Application::on_windowClose);
+ m_openWindows++;
+ }
+ return m_viewLogWindow;
+}
+
+InstanceWindow* Application::showInstanceWindow(BaseInstance* instance, QString page)
{
if (!instance)
return nullptr;
auto id = instance->id();
+ QMutexLocker locker(&m_instanceExtrasMutex);
auto& extras = m_instanceExtras[id];
auto& window = extras.window;
@@ -1553,18 +1740,23 @@ InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString pa
void Application::on_windowClose()
{
m_openWindows--;
- auto instWindow = qobject_cast(QObject::sender());
+ auto instWindow = qobject_cast(sender());
if (instWindow) {
+ QMutexLocker locker(&m_instanceExtrasMutex);
auto& extras = m_instanceExtras[instWindow->instanceId()];
extras.window = nullptr;
if (extras.controller) {
extras.controller->setParentWidget(m_mainWindow);
}
}
- auto mainWindow = qobject_cast(QObject::sender());
+ auto mainWindow = qobject_cast(sender());
if (mainWindow) {
m_mainWindow = nullptr;
}
+ auto logWindow = qobject_cast(sender());
+ if (logWindow) {
+ m_viewLogWindow = nullptr;
+ }
// quit when there are no more windows.
if (shouldExitNow()) {
exit(0);
@@ -1619,22 +1811,22 @@ void Application::updateProxySettings(QString proxyTypeStr, QString addr, int po
qDebug() << proxyDesc;
}
-shared_qobject_ptr Application::metacache()
+HttpMetaCache* Application::metacache()
{
- return m_metacache;
+ return m_metacache.get();
}
-shared_qobject_ptr Application::network()
+QNetworkAccessManager* Application::network()
{
- return m_network;
+ return m_network.get();
}
-shared_qobject_ptr Application::metadataIndex()
+Meta::Index* Application::metadataIndex()
{
if (!m_metadataIndex) {
m_metadataIndex.reset(new Meta::Index());
}
- return m_metadataIndex;
+ return m_metadataIndex.get();
}
void Application::updateCapabilities()
@@ -1649,7 +1841,7 @@ void Application::updateCapabilities()
if (gamemode_query_status() >= 0)
m_capabilities |= SupportsGameMode;
- if (!MangoHud::getLibraryString().isEmpty())
+ if (!LibraryUtils::findMangoHud().isEmpty())
m_capabilities |= SupportsMangoHud;
#endif
}
@@ -1657,8 +1849,8 @@ void Application::updateCapabilities()
void Application::detectLibraries()
{
#ifdef Q_OS_LINUX
- m_detectedGLFWPath = MangoHud::findLibrary(BuildConfig.GLFW_LIBRARY_NAME);
- m_detectedOpenALPath = MangoHud::findLibrary(BuildConfig.OPENAL_LIBRARY_NAME);
+ m_detectedGLFWPath = LibraryUtils::find(BuildConfig.GLFW_LIBRARY_NAME);
+ m_detectedOpenALPath = LibraryUtils::find(BuildConfig.OPENAL_LIBRARY_NAME);
qDebug() << "Detected native libraries:" << m_detectedGLFWPath << m_detectedOpenALPath;
#endif
}
@@ -1669,8 +1861,7 @@ QString Application::getJarPath(QString jarFile)
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
FS::PathCombine(m_rootPath, "share", BuildConfig.LAUNCHER_NAME),
#endif
- FS::PathCombine(m_rootPath, "jars"),
- FS::PathCombine(applicationDirPath(), "jars"),
+ FS::PathCombine(m_rootPath, "jars"), FS::PathCombine(applicationDirPath(), "jars"),
FS::PathCombine(applicationDirPath(), "..", "jars") // from inside build dir, for debuging
};
for (QString p : potentialPaths) {
@@ -1720,31 +1911,6 @@ QString Application::getUserAgent()
return BuildConfig.USER_AGENT;
}
-QString Application::getUserAgentUncached()
-{
- QString uaOverride = m_settings->get("UserAgentOverride").toString();
- if (!uaOverride.isEmpty()) {
- uaOverride += " (Uncached)";
- return uaOverride.replace("$LAUNCHER_VER", BuildConfig.printableVersionString());
- }
-
- return BuildConfig.USER_AGENT_UNCACHED;
-}
-
-int Application::suitableMaxMem()
-{
- float totalRAM = (float)Sys::getSystemRam() / (float)Sys::mebibyte;
- int maxMemoryAlloc;
-
- // If totalRAM < 6GB, use (totalRAM / 1.5), else 4GB
- if (totalRAM < (4096 * 1.5))
- maxMemoryAlloc = (int)(totalRAM / 1.5);
- else
- maxMemoryAlloc = 4096;
-
- return maxMemoryAlloc;
-}
-
bool Application::handleDataMigration(const QString& currentData,
const QString& oldData,
const QString& name,
@@ -1790,7 +1956,9 @@ bool Application::handleDataMigration(const QString& currentData,
auto setDoNotMigrate = [&nomigratePath] {
QFile file(nomigratePath);
- file.open(QIODevice::WriteOnly);
+ if (!file.open(QIODevice::WriteOnly)) {
+ qWarning() << "setDoNotMigrate failed; Failed to open file" << file.fileName() << "for writing:" << file.errorString();
+ }
};
// create no-migrate file if user doesn't want to migrate
@@ -1802,22 +1970,23 @@ bool Application::handleDataMigration(const QString& currentData,
if (!currentExists) {
// Migrate!
- auto matcher = std::make_shared();
- matcher->add(std::make_shared(configFile));
- matcher->add(std::make_shared(
- BuildConfig.LAUNCHER_CONFIGFILE)); // it's possible that we already used that directory before
- matcher->add(std::make_shared("logs/"));
- matcher->add(std::make_shared("accounts.json"));
- matcher->add(std::make_shared("accounts/"));
- matcher->add(std::make_shared("assets/"));
- matcher->add(std::make_shared("icons/"));
- matcher->add(std::make_shared("instances/"));
- matcher->add(std::make_shared("libraries/"));
- matcher->add(std::make_shared("mods/"));
- matcher->add(std::make_shared("themes/"));
+ using namespace Filters;
+
+ QList filters;
+ filters.append(equals(configFile));
+ filters.append(equals(BuildConfig.LAUNCHER_CONFIGFILE)); // it's possible that we already used that directory before
+ filters.append(startsWith("logs/"));
+ filters.append(equals("accounts.json"));
+ filters.append(startsWith("accounts/"));
+ filters.append(startsWith("assets/"));
+ filters.append(startsWith("icons/"));
+ filters.append(startsWith("instances/"));
+ filters.append(startsWith("libraries/"));
+ filters.append(startsWith("mods/"));
+ filters.append(startsWith("themes/"));
ProgressDialog diag;
- DataMigrationTask task(nullptr, oldData, currentData, matcher);
+ DataMigrationTask task(oldData, currentData, any(std::move(filters)));
if (diag.execWithTask(&task)) {
qDebug() << "<> Migration succeeded";
setDoNotMigrate();
@@ -1842,7 +2011,7 @@ void Application::triggerUpdateCheck()
}
}
-QUrl Application::normalizeImportUrl(QString const& url)
+QUrl Application::normalizeImportUrl(const QString& url)
{
auto local_file = QFileInfo(url);
if (local_file.exists()) {
@@ -1851,3 +2020,36 @@ QUrl Application::normalizeImportUrl(QString const& url)
return QUrl::fromUserInput(url);
}
}
+
+const QString Application::javaPath()
+{
+ return m_settings->get("JavaDir").toString();
+}
+
+void Application::addQSavePath(QString path)
+{
+ QMutexLocker locker(&m_qsaveResourcesMutex);
+ m_qsaveResources[path] = m_qsaveResources.value(path, 0) + 1;
+}
+
+void Application::removeQSavePath(QString path)
+{
+ QMutexLocker locker(&m_qsaveResourcesMutex);
+ auto count = m_qsaveResources.value(path, 0) - 1;
+ if (count <= 0) {
+ m_qsaveResources.remove(path);
+ } else {
+ m_qsaveResources[path] = count;
+ }
+}
+
+bool Application::checkQSavePath(QString path)
+{
+ QMutexLocker locker(&m_qsaveResourcesMutex);
+ for (auto partialPath : m_qsaveResources.keys()) {
+ if (path.startsWith(partialPath) && m_qsaveResources.value(partialPath, 0) > 0) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/launcher/Application.h b/launcher/Application.h
index 7669e08ec3..936e13d711 100644
--- a/launcher/Application.h
+++ b/launcher/Application.h
@@ -37,23 +37,25 @@
#pragma once
+#include
+
#include
#include
#include
#include
#include
+#include
#include
-#include
-#include
+#include "QObjectPtr.h"
-#include "minecraft/launch/MinecraftServerTarget.h"
-#include "ui/themes/CatPack.h"
+#include "minecraft/auth/MinecraftAccount.h"
class LaunchController;
class LocalPeer;
class InstanceWindow;
class MainWindow;
+class ViewLogWindow;
class SetupWizard;
class GenericPageProvider;
class QFile;
@@ -72,6 +74,12 @@ class ITheme;
class MCEditTool;
class ThemeManager;
class IconTheme;
+class BaseInstance;
+
+class LogModel;
+
+struct MinecraftTarget;
+class MinecraftAccount;
namespace Meta {
class Index;
@@ -82,8 +90,13 @@ class Index;
#endif
#define APPLICATION (static_cast(QCoreApplication::instance()))
+// Used for checking if is a test
+#if defined(APPLICATION_DYN)
+#undef APPLICATION_DYN
+#endif
+#define APPLICATION_DYN (dynamic_cast(QCoreApplication::instance()))
+
class Application : public QApplication {
- // friends for the purpose of limiting access to deprecated stuff
Q_OBJECT
public:
enum Status { StartingUp, Failed, Succeeded, Initialized };
@@ -104,29 +117,29 @@ class Application : public QApplication {
bool event(QEvent* event) override;
- std::shared_ptr settings() const { return m_settings; }
+ SettingsObject* settings() const { return m_settings.get(); }
- qint64 timeSinceStart() const { return startTime.msecsTo(QDateTime::currentDateTime()); }
+ qint64 timeSinceStart() const { return m_startTime.msecsTo(QDateTime::currentDateTime()); }
- QIcon getThemedIcon(const QString& name);
+ QIcon logo();
ThemeManager* themeManager() { return m_themeManager.get(); }
- shared_qobject_ptr updater() { return m_updater; }
+ ExternalUpdater* updater() { return m_updater.get(); }
void triggerUpdateCheck();
- std::shared_ptr translations();
+ TranslationsModel* translations();
- std::shared_ptr javalist();
+ JavaInstallList* javalist();
- std::shared_ptr instances() const { return m_instances; }
+ InstanceList* instances() const { return m_instances.get(); }
- std::shared_ptr icons() const { return m_icons; }
+ IconList* icons() const { return m_icons.get(); }
MCEditTool* mcedit() const { return m_mcedit.get(); }
- shared_qobject_ptr accounts() const { return m_accounts; }
+ AccountList* accounts() const { return m_accounts.get(); }
Status status() const { return m_status; }
@@ -134,11 +147,11 @@ class Application : public QApplication {
void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
- shared_qobject_ptr network();
+ QNetworkAccessManager* network();
- shared_qobject_ptr metacache();
+ HttpMetaCache* metacache();
- shared_qobject_ptr metadataIndex();
+ Meta::Index* metadataIndex();
void updateCapabilities();
@@ -154,7 +167,6 @@ class Application : public QApplication {
QString getFlameAPIKey();
QString getModrinthAPIToken();
QString getUserAgent();
- QString getUserAgentUncached();
/// this is the root of the 'installation'. Used for automatic updates
const QString& root() { return m_rootPath; }
@@ -162,6 +174,9 @@ class Application : public QApplication {
/// the data path the application is using
const QString& dataRoot() { return m_dataPath; }
+ /// the java installed path the application is using
+ const QString javaPath();
+
bool isPortable() { return m_portable; }
const Capabilities capabilities() { return m_capabilities; }
@@ -172,45 +187,45 @@ class Application : public QApplication {
*/
bool openJsonEditor(const QString& filename);
- InstanceWindow* showInstanceWindow(InstancePtr instance, QString page = QString());
+ InstanceWindow* showInstanceWindow(BaseInstance* instance, QString page = QString());
MainWindow* showMainWindow(bool minimized = false);
+ ViewLogWindow* showLogWindow();
void updateIsRunning(bool running);
bool updatesAreAllowed();
void ShowGlobalSettings(class QWidget* parent, QString open_page = QString());
- int suitableMaxMem();
-
bool updaterEnabled();
QString updaterBinaryName();
- QUrl normalizeImportUrl(QString const& url);
+ QUrl normalizeImportUrl(const QString& url);
signals:
void updateAllowedChanged(bool status);
void globalSettingsAboutToOpen();
- void globalSettingsClosed();
+ void globalSettingsApplied();
int currentCatChanged(int index);
+ void oauthReplyRecieved(QVariantMap);
+
#ifdef Q_OS_MACOS
void clickedOnDock();
#endif
public slots:
- bool launch(InstancePtr instance,
- bool online = true,
- bool demo = false,
- MinecraftServerTargetPtr serverToJoin = nullptr,
- MinecraftAccountPtr accountToUse = nullptr);
- bool kill(InstancePtr instance);
+ bool launch(BaseInstance* instance,
+ LaunchMode mode = LaunchMode::Normal,
+ std::shared_ptr targetToJoin = nullptr,
+ shared_qobject_ptr accountToUse = nullptr,
+ const QString& offlineName = QString());
+ bool kill(BaseInstance* instance);
void closeCurrentWindow();
private slots:
void on_windowClose();
void messageReceived(const QByteArray& message);
- void controllerSucceeded();
- void controllerFailed(const QString& error);
+ void controllerFinished();
void setupWizardFinished(int status);
private:
@@ -227,22 +242,26 @@ class Application : public QApplication {
bool shouldExitNow() const;
private:
- QDateTime startTime;
+ QHash m_qsaveResources;
+ mutable QMutex m_qsaveResourcesMutex;
+
+ private:
+ QDateTime m_startTime;
- shared_qobject_ptr m_network;
+ std::unique_ptr m_network;
- shared_qobject_ptr m_updater;
- shared_qobject_ptr m_accounts;
+ std::unique_ptr m_updater;
+ std::unique_ptr m_accounts;
- shared_qobject_ptr m_metacache;
- shared_qobject_ptr m_metadataIndex;
+ std::unique_ptr m_metacache;
+ std::unique_ptr m_metadataIndex;
- std::shared_ptr m_settings;
- std::shared_ptr m_instances;
- std::shared_ptr m_icons;
- std::shared_ptr m_javalist;
- std::shared_ptr m_translations;
- std::shared_ptr m_globalSettingsProvider;
+ std::unique_ptr m_settings;
+ std::unique_ptr m_instances;
+ std::unique_ptr m_icons;
+ std::unique_ptr m_javalist;
+ std::unique_ptr m_translations;
+ std::unique_ptr m_globalSettingsProvider;
std::unique_ptr m_mcedit;
QSet m_features;
std::unique_ptr m_themeManager;
@@ -259,17 +278,13 @@ class Application : public QApplication {
Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive;
#endif
-#if defined Q_OS_WIN32
- // used on Windows to attach the standard IO streams
- bool consoleAttached = false;
-#endif
-
// FIXME: attach to instances instead.
struct InstanceXtras {
InstanceWindow* window = nullptr;
- shared_qobject_ptr controller;
+ std::unique_ptr controller;
};
std::map m_instanceExtras;
+ mutable QMutex m_instanceExtrasMutex;
// main state variables
size_t m_openWindows = 0;
@@ -279,6 +294,9 @@ class Application : public QApplication {
// main window, if any
MainWindow* m_mainWindow = nullptr;
+ // log window, if any
+ ViewLogWindow* m_viewLogWindow = nullptr;
+
// peer launcher instance connector - used to implement single instance launcher and signalling
LocalPeer* m_peerInstance = nullptr;
@@ -289,9 +307,19 @@ class Application : public QApplication {
QString m_detectedOpenALPath;
QString m_instanceIdToLaunch;
QString m_serverToJoin;
+ QString m_worldToJoin;
QString m_profileToUse;
+ bool m_launchOffline = false;
+ QString m_offlineName;
bool m_liveCheck = false;
QList m_urlsToImport;
QString m_instanceIdToShowWindowOf;
+ bool m_showMainWindow = false;
std::unique_ptr logFile;
+ std::unique_ptr logModel;
+
+ public:
+ void addQSavePath(QString);
+ void removeQSavePath(QString);
+ bool checkQSavePath(QString);
};
diff --git a/launcher/AssertHelpers.h b/launcher/AssertHelpers.h
new file mode 100644
index 0000000000..0b1cdb742c
--- /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/BaseInstaller.cpp b/launcher/BaseInstaller.cpp
index 1ff86ed403..96a3b5ebe8 100644
--- a/launcher/BaseInstaller.cpp
+++ b/launcher/BaseInstaller.cpp
@@ -16,6 +16,7 @@
#include
#include "BaseInstaller.h"
+#include "FileSystem.h"
#include "minecraft/MinecraftInstance.h"
BaseInstaller::BaseInstaller() {}
@@ -42,7 +43,7 @@ bool BaseInstaller::add(MinecraftInstance* to)
bool BaseInstaller::remove(MinecraftInstance* from)
{
- return QFile::remove(filename(from->instanceRoot()));
+ return FS::deletePath(filename(from->instanceRoot()));
}
QString BaseInstaller::filename(const QString& root) const
diff --git a/launcher/BaseInstaller.h b/launcher/BaseInstaller.h
index 6244ced7d1..1cf7d65f51 100644
--- a/launcher/BaseInstaller.h
+++ b/launcher/BaseInstaller.h
@@ -29,7 +29,7 @@ class BaseVersion;
class BaseInstaller {
public:
BaseInstaller();
- virtual ~BaseInstaller(){};
+ virtual ~BaseInstaller() {};
bool isApplied(MinecraftInstance* on);
virtual bool add(MinecraftInstance* to);
diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp
index cda44b454b..0080cc516f 100644
--- a/launcher/BaseInstance.cpp
+++ b/launcher/BaseInstance.cpp
@@ -42,8 +42,10 @@
#include
#include
#include
-#include
+#include "Application.h"
+#include "Json.h"
+#include "launch/LaunchTask.h"
#include "settings/INISettingsObject.h"
#include "settings/OverrideSetting.h"
#include "settings/Setting.h"
@@ -52,9 +54,26 @@
#include "Commandline.h"
#include "FileSystem.h"
-BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir) : QObject()
+int getConsoleMaxLines(SettingsObject* settings)
{
- m_settings = settings;
+ auto lineSetting = settings->getSetting("ConsoleMaxLines");
+ bool conversionOk = false;
+ int maxLines = lineSetting->get().toInt(&conversionOk);
+ if (!conversionOk) {
+ maxLines = lineSetting->defValue().toInt();
+ qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines;
+ }
+ return maxLines;
+}
+
+bool shouldStopOnConsoleOverflow(SettingsObject* settings)
+{
+ return settings->get("ConsoleOverflowStop").toBool();
+}
+
+BaseInstance::BaseInstance(SettingsObject* globalSettings, std::unique_ptr settings, const QString& rootDir) : QObject()
+{
+ m_settings = std::move(settings);
m_global_settings = globalSettings;
m_rootDir = rootDir;
@@ -69,6 +88,7 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
m_settings->registerSetting("lastTimePlayed", 0);
m_settings->registerSetting("linkedInstances", "[]");
+ m_settings->registerSetting("shortcuts", QString());
// Game time override
auto gameTimeOverride = m_settings->registerSetting("OverrideGameTime", false);
@@ -103,10 +123,13 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
m_settings->registerSetting("ManagedPackName", "");
m_settings->registerSetting("ManagedPackVersionID", "");
m_settings->registerSetting("ManagedPackVersionName", "");
+ m_settings->registerSetting("ManagedPackURL", "");
m_settings->registerSetting("Profiler", "");
}
+BaseInstance::~BaseInstance() {}
+
QString BaseInstance::getPreLaunchCommand()
{
return settings()->get("PreLaunchCommand").toString();
@@ -174,46 +197,35 @@ void BaseInstance::copyManagedPack(BaseInstance& other)
m_settings->set("ManagedPackName", other.getManagedPackName());
m_settings->set("ManagedPackVersionID", other.getManagedPackVersionID());
m_settings->set("ManagedPackVersionName", other.getManagedPackVersionName());
-}
-int BaseInstance::getConsoleMaxLines() const
-{
- auto lineSetting = m_settings->getSetting("ConsoleMaxLines");
- bool conversionOk = false;
- int maxLines = lineSetting->get().toInt(&conversionOk);
- if (!conversionOk) {
- maxLines = lineSetting->defValue().toInt();
- qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines;
+ if (APPLICATION->settings()->get("AutomaticJavaSwitch").toBool() && m_settings->get("AutomaticJava").toBool() &&
+ m_settings->get("OverrideJavaLocation").toBool()) {
+ m_settings->set("OverrideJavaLocation", false);
+ m_settings->set("JavaPath", "");
}
- return maxLines;
-}
-
-bool BaseInstance::shouldStopOnConsoleOverflow() const
-{
- return m_settings->get("ConsoleOverflowStop").toBool();
}
QStringList BaseInstance::getLinkedInstances() const
{
- return m_settings->get("linkedInstances").toStringList();
+ auto setting = m_settings->get("linkedInstances").toString();
+ return Json::toStringList(setting);
}
void BaseInstance::setLinkedInstances(const QStringList& list)
{
- auto linkedInstances = m_settings->get("linkedInstances").toStringList();
- m_settings->set("linkedInstances", list);
+ m_settings->set("linkedInstances", Json::fromStringList(list));
}
void BaseInstance::addLinkedInstanceId(const QString& id)
{
- auto linkedInstances = m_settings->get("linkedInstances").toStringList();
+ auto linkedInstances = getLinkedInstances();
linkedInstances.append(id);
setLinkedInstances(linkedInstances);
}
bool BaseInstance::removeLinkedInstanceId(const QString& id)
{
- auto linkedInstances = m_settings->get("linkedInstances").toStringList();
+ auto linkedInstances = getLinkedInstances();
int numRemoved = linkedInstances.removeAll(id);
setLinkedInstances(linkedInstances);
return numRemoved > 0;
@@ -221,7 +233,7 @@ bool BaseInstance::removeLinkedInstanceId(const QString& id)
bool BaseInstance::isLinkedToInstanceId(const QString& id) const
{
- auto linkedInstances = m_settings->get("linkedInstances").toStringList();
+ auto linkedInstances = getLinkedInstances();
return linkedInstances.contains(id);
}
@@ -269,13 +281,18 @@ void BaseInstance::setRunning(bool running)
m_isRunning = running;
- if (!m_settings->get("RecordGameTime").toBool()) {
- emit runningStatusChanged(running);
+ emit runningStatusChanged(running);
+}
+
+void BaseInstance::setMinecraftRunning(bool running)
+{
+ if (!settings()->get("RecordGameTime").toBool()) {
return;
}
if (running) {
m_timeStarted = QDateTime::currentDateTime();
+ setLastLaunch(m_timeStarted.toMSecsSinceEpoch());
} else {
QDateTime timeEnded = QDateTime::currentDateTime();
@@ -285,8 +302,6 @@ void BaseInstance::setRunning(bool running)
emit propertiesChanged(this);
}
-
- emit runningStatusChanged(running);
}
int64_t BaseInstance::totalTimePlayed() const
@@ -324,11 +339,11 @@ QString BaseInstance::instanceRoot() const
return m_rootDir;
}
-SettingsObjectPtr BaseInstance::settings()
+SettingsObject* BaseInstance::settings()
{
loadSpecificSettings();
- return m_settings;
+ return m_settings.get();
}
bool BaseInstance::canLaunch() const
@@ -383,6 +398,63 @@ void BaseInstance::setName(QString val)
emit propertiesChanged(this);
}
+bool BaseInstance::syncInstanceDirName(const QString& newRoot) const
+{
+ auto oldRoot = instanceRoot();
+ return oldRoot == newRoot || QFile::rename(oldRoot, newRoot);
+}
+
+void BaseInstance::registerShortcut(const ShortcutData& data)
+{
+ auto currentShortcuts = shortcuts();
+ currentShortcuts.append(data);
+ qDebug() << "Registering shortcut for instance" << id() << "with name" << data.name << "and path" << data.filePath;
+ setShortcuts(currentShortcuts);
+}
+
+void BaseInstance::setShortcuts(const QList& shortcuts)
+{
+ // FIXME: if no change, do not set. setting involves saving a file.
+ QJsonArray array;
+ for (const auto& elem : shortcuts) {
+ array.append(QJsonObject{ { "name", elem.name }, { "filePath", elem.filePath }, { "target", static_cast(elem.target) } });
+ }
+
+ QJsonDocument document;
+ document.setArray(array);
+ m_settings->set("shortcuts", QString::fromUtf8(document.toJson(QJsonDocument::Compact)));
+}
+
+QList BaseInstance::shortcuts() const
+{
+ auto data = m_settings->get("shortcuts").toString().toUtf8();
+ QJsonParseError parseError;
+ auto document = QJsonDocument::fromJson(data, &parseError);
+ if (parseError.error != QJsonParseError::NoError || !document.isArray())
+ return {};
+
+ QList results;
+ for (const auto& elem : document.array()) {
+ if (!elem.isObject())
+ continue;
+ auto dict = elem.toObject();
+ if (!dict.contains("name") || !dict.contains("filePath") || !dict.contains("target"))
+ continue;
+ int value = dict["target"].toInt(-1);
+ if (!dict["name"].isString() || !dict["filePath"].isString() || value < 0 || value >= 3)
+ continue;
+
+ QString shortcutName = dict["name"].toString();
+ QString filePath = dict["filePath"].toString();
+ if (!QDir(filePath).exists()) {
+ qWarning() << "Shortcut" << shortcutName << "for instance" << name() << "have non-existent path" << filePath;
+ continue;
+ }
+ results.append({ shortcutName, filePath, static_cast(value) });
+ }
+ return results;
+}
+
QString BaseInstance::name() const
{
return m_settings->get("name").toString();
@@ -399,12 +471,17 @@ QStringList BaseInstance::extraArguments()
return Commandline::splitArgs(settings()->get("JvmArgs").toString());
}
-shared_qobject_ptr BaseInstance::getLaunchTask()
+LaunchTask* BaseInstance::getLaunchTask()
{
- return m_launchProcess;
+ return m_launchProcess.get();
}
void BaseInstance::updateRuntimeContext()
{
// NOOP
}
+
+bool BaseInstance::isLegacy()
+{
+ return traits().contains("legacyLaunch") || traits().contains("alphaLaunch");
+}
diff --git a/launcher/BaseInstance.h b/launcher/BaseInstance.h
index f4ed9113ce..9280d2e1c7 100644
--- a/launcher/BaseInstance.h
+++ b/launcher/BaseInstance.h
@@ -38,7 +38,9 @@
#pragma once
#include
+#include
#include
+#include
#include
#include
#include
@@ -50,21 +52,31 @@
#include "BaseVersionList.h"
#include "MessageLevel.h"
#include "minecraft/auth/MinecraftAccount.h"
-#include "pathmatcher/IPathMatcher.h"
#include "settings/INIFile.h"
#include "net/Mode.h"
#include "RuntimeContext.h"
-#include "minecraft/launch/MinecraftServerTarget.h"
+#include "minecraft/launch/MinecraftTarget.h"
class QDir;
class Task;
class LaunchTask;
class BaseInstance;
-// pointer for lazy people
-using InstancePtr = std::shared_ptr;
+/// Shortcut saving target representations
+enum class ShortcutTarget { Desktop, Applications, Other };
+
+/// Shortcut data representation
+struct ShortcutData {
+ QString name;
+ QString filePath;
+ ShortcutTarget target = ShortcutTarget::Other;
+};
+
+/// Console settings
+int getConsoleMaxLines(SettingsObject* settings);
+bool shouldStopOnConsoleOverflow(SettingsObject* settings);
/*!
* \brief Base class for instances.
@@ -74,11 +86,11 @@ using InstancePtr = std::shared_ptr;
* To create a new instance type, create a new class inheriting from this class
* and implement the pure virtual functions.
*/
-class BaseInstance : public QObject, public std::enable_shared_from_this {
+class BaseInstance : public QObject {
Q_OBJECT
protected:
/// no-touchy!
- BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir);
+ BaseInstance(SettingsObject* globalSettings, std::unique_ptr settings, const QString& rootDir);
public: /* types */
enum class Status {
@@ -88,7 +100,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this shortcuts() const;
+ void setShortcuts(const QList& shortcuts);
+
/// Value used for instance window titles
QString windowTitle() const;
@@ -147,9 +168,6 @@ class BaseInstance : public QObject, public std::enable_shared_from_this createUpdateTask() = 0;
/// returns a valid launcher (task container)
- virtual shared_qobject_ptr createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0;
+ virtual LaunchTask* createLaunchTask(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) = 0;
/// returns the current launch task (if any)
- shared_qobject_ptr getLaunchTask();
+ LaunchTask* getLaunchTask();
/*!
* Create envrironment variables for running the instance
@@ -194,15 +212,10 @@ class BaseInstance : public QObject, public std::enable_shared_from_this);
+ void launchTaskChanged(LaunchTask*);
void runningStatusChanged(bool running);
@@ -295,10 +307,10 @@ class BaseInstance : public QObject, public std::enable_shared_from_this m_settings;
// InstanceFlags m_flags;
bool m_isRunning = false;
- shared_qobject_ptr m_launchProcess;
+ std::unique_ptr m_launchProcess;
QDateTime m_timeStarted;
RuntimeContext m_runtimeContext;
@@ -308,7 +320,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this;
virtual ~BaseVersion() {}
/*!
* A string used to identify this version in config files.
* This should be unique within the version list or shenanigans will occur.
*/
- virtual QString descriptor() = 0;
+ virtual QString descriptor() const = 0;
/*!
* The name of this version as it is displayed to the user.
* For example: "1.5.1"
*/
- virtual QString name() = 0;
+ virtual QString name() const = 0;
/*!
* This should return a string that describes
* the kind of version this is (Stable, Beta, Snapshot, whatever)
*/
virtual QString typeString() const = 0;
- virtual bool operator<(BaseVersion& a) { return name() < a.name(); }
- virtual bool operator>(BaseVersion& a) { return name() > a.name(); }
+ virtual bool operator<(BaseVersion& a) const { return name() < a.name(); }
+ virtual bool operator>(BaseVersion& a) const { return name() > a.name(); }
};
Q_DECLARE_METATYPE(BaseVersion::Ptr)
diff --git a/launcher/BaseVersionList.cpp b/launcher/BaseVersionList.cpp
index e11560d5ec..22077c9623 100644
--- a/launcher/BaseVersionList.cpp
+++ b/launcher/BaseVersionList.cpp
@@ -78,6 +78,14 @@ QVariant BaseVersionList::data(const QModelIndex& index, int role) const
case TypeRole:
return version->typeString();
+ case JavaMajorRole: {
+ auto major = version->name();
+ if (major.startsWith("java")) {
+ major = "Java " + major.mid(4);
+ }
+ return major;
+ }
+
default:
return QVariant();
}
@@ -110,6 +118,8 @@ QHash BaseVersionList::roleNames() const
roles.insert(TypeRole, "type");
roles.insert(BranchRole, "branch");
roles.insert(PathRole, "path");
- roles.insert(ArchitectureRole, "architecture");
+ roles.insert(JavaNameRole, "javaName");
+ roles.insert(CPUArchitectureRole, "architecture");
+ roles.insert(JavaMajorRole, "javaMajor");
return roles;
}
diff --git a/launcher/BaseVersionList.h b/launcher/BaseVersionList.h
index 231887c4ea..673d135628 100644
--- a/launcher/BaseVersionList.h
+++ b/launcher/BaseVersionList.h
@@ -48,7 +48,9 @@ class BaseVersionList : public QAbstractListModel {
TypeRole,
BranchRole,
PathRole,
- ArchitectureRole,
+ JavaNameRole,
+ JavaMajorRole,
+ CPUArchitectureRole,
SortRole
};
using RoleList = QList;
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index e93219015e..c2f5e70cfc 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -21,13 +21,24 @@ set(CORE_SOURCES
BaseVersion.h
BaseInstance.h
BaseInstance.cpp
+ InstanceDirUpdate.h
+ InstanceDirUpdate.cpp
NullInstance.h
MMCZip.h
MMCZip.cpp
+ archive/ArchiveReader.cpp
+ archive/ArchiveReader.h
+ archive/ArchiveWriter.cpp
+ archive/ArchiveWriter.h
+ archive/ExportToZipTask.cpp
+ archive/ExportToZipTask.h
+ archive/ExtractZipTask.cpp
+ archive/ExtractZipTask.h
StringUtils.h
StringUtils.cpp
QVariantUtils.h
RuntimeContext.h
+ PSaveFile.h
# Basic instance manipulation tasks (derived from InstanceTask)
InstanceCreationTask.h
@@ -51,7 +62,6 @@ set(CORE_SOURCES
# String filters
Filter.h
- Filter.cpp
# JSON parsing helpers
Json.h
@@ -65,9 +75,6 @@ set(CORE_SOURCES
# RW lock protected map
RWStorage.h
- # A variable that has an implicit default value and keeps track of changes
- DefaultVariable.h
-
# a smart pointer wrapper intended for safer use with Qt signal/slot mechanisms
QObjectPtr.h
@@ -92,32 +99,27 @@ set(CORE_SOURCES
MMCTime.cpp
MTPixmapCache.h
+
+ # Assertion helper
+ AssertHelpers.h
)
if (UNIX AND NOT CYGWIN AND NOT APPLE)
-set(CORE_SOURCES
+ set(CORE_SOURCES
${CORE_SOURCES}
- # MangoHud
- MangoHud.h
- MangoHud.cpp
+ # LibraryUtils
+ LibraryUtils.h
+ LibraryUtils.cpp
)
endif()
-set(PATHMATCHER_SOURCES
- # Path matchers
- pathmatcher/FSTreeMatcher.h
- pathmatcher/IPathMatcher.h
- pathmatcher/MultiMatcher.h
- pathmatcher/RegexpMatcher.h
- pathmatcher/SimplePrefixMatcher.h
-)
-
set(NET_SOURCES
# network stuffs
net/ByteArraySink.h
net/ChecksumValidator.h
net/Download.cpp
net/Download.h
+ net/DummySink.h
net/FileSink.cpp
net/FileSink.h
net/HttpMetaCache.cpp
@@ -126,7 +128,6 @@ set(NET_SOURCES
net/MetaCacheSink.h
net/Logging.h
net/Logging.cpp
- net/NetAction.h
net/NetJob.cpp
net/NetJob.h
net/NetUtils.h
@@ -139,7 +140,6 @@ set(NET_SOURCES
net/HeaderProxy.h
net/RawHeaderProxy.h
net/ApiHeaderProxy.h
- net/StaticHeaderProxy.h
net/ApiDownload.h
net/ApiDownload.cpp
net/ApiUpload.cpp
@@ -160,16 +160,20 @@ set(LAUNCH_SOURCES
launch/steps/PreLaunchCommand.h
launch/steps/TextPrint.cpp
launch/steps/TextPrint.h
- launch/steps/Update.cpp
- launch/steps/Update.h
launch/steps/QuitAfterGameStop.cpp
launch/steps/QuitAfterGameStop.h
+ launch/steps/PrintServers.cpp
+ launch/steps/PrintServers.h
launch/LaunchStep.cpp
launch/LaunchStep.h
launch/LaunchTask.cpp
launch/LaunchTask.h
launch/LogModel.cpp
launch/LogModel.h
+ launch/TaskStepWrapper.cpp
+ launch/TaskStepWrapper.h
+ logs/LogParser.cpp
+ logs/LogParser.h
)
# Old update system
@@ -205,33 +209,27 @@ set(ICONS_SOURCES
# Support for Minecraft instances and launch
set(MINECRAFT_SOURCES
+
+ # Logging
+ minecraft/Logging.h
+ minecraft/Logging.cpp
+
# Minecraft support
minecraft/auth/AccountData.cpp
minecraft/auth/AccountData.h
minecraft/auth/AccountList.cpp
minecraft/auth/AccountList.h
- minecraft/auth/AccountTask.cpp
- minecraft/auth/AccountTask.h
- minecraft/auth/AuthRequest.cpp
- minecraft/auth/AuthRequest.h
minecraft/auth/AuthSession.cpp
minecraft/auth/AuthSession.h
- minecraft/auth/AuthStep.cpp
minecraft/auth/AuthStep.h
minecraft/auth/MinecraftAccount.cpp
minecraft/auth/MinecraftAccount.h
minecraft/auth/Parsers.cpp
minecraft/auth/Parsers.h
- minecraft/auth/flows/AuthFlow.cpp
- minecraft/auth/flows/AuthFlow.h
- minecraft/auth/flows/MSA.cpp
- minecraft/auth/flows/MSA.h
- minecraft/auth/flows/Offline.cpp
- minecraft/auth/flows/Offline.h
+ minecraft/auth/AuthFlow.cpp
+ minecraft/auth/AuthFlow.h
- minecraft/auth/steps/OfflineStep.cpp
- minecraft/auth/steps/OfflineStep.h
minecraft/auth/steps/EntitlementsStep.cpp
minecraft/auth/steps/EntitlementsStep.h
minecraft/auth/steps/GetSkinStep.cpp
@@ -240,22 +238,19 @@ set(MINECRAFT_SOURCES
minecraft/auth/steps/LauncherLoginStep.h
minecraft/auth/steps/MinecraftProfileStep.cpp
minecraft/auth/steps/MinecraftProfileStep.h
+ minecraft/auth/steps/MSADeviceCodeStep.cpp
+ minecraft/auth/steps/MSADeviceCodeStep.h
minecraft/auth/steps/MSAStep.cpp
minecraft/auth/steps/MSAStep.h
minecraft/auth/steps/XboxAuthorizationStep.cpp
minecraft/auth/steps/XboxAuthorizationStep.h
- minecraft/auth/steps/XboxProfileStep.cpp
- minecraft/auth/steps/XboxProfileStep.h
minecraft/auth/steps/XboxUserStep.cpp
minecraft/auth/steps/XboxUserStep.h
- minecraft/gameoptions/GameOptions.h
- minecraft/gameoptions/GameOptions.cpp
-
minecraft/update/AssetUpdateTask.h
minecraft/update/AssetUpdateTask.cpp
- minecraft/update/FMLLibrariesTask.cpp
- minecraft/update/FMLLibrariesTask.h
+ minecraft/update/LegacyFMLLibrariesTask.cpp
+ minecraft/update/LegacyFMLLibrariesTask.h
minecraft/update/FoldersTask.cpp
minecraft/update/FoldersTask.h
minecraft/update/LibrariesTask.cpp
@@ -265,14 +260,18 @@ set(MINECRAFT_SOURCES
minecraft/launch/ClaimAccount.h
minecraft/launch/CreateGameFolders.cpp
minecraft/launch/CreateGameFolders.h
+ minecraft/launch/EnsureAvailableMemory.cpp
+ minecraft/launch/EnsureAvailableMemory.h
+ minecraft/launch/EnsureOfflineLibraries.cpp
+ minecraft/launch/EnsureOfflineLibraries.h
minecraft/launch/ModMinecraftJar.cpp
minecraft/launch/ModMinecraftJar.h
minecraft/launch/ExtractNatives.cpp
minecraft/launch/ExtractNatives.h
minecraft/launch/LauncherPartLaunch.cpp
minecraft/launch/LauncherPartLaunch.h
- minecraft/launch/MinecraftServerTarget.cpp
- minecraft/launch/MinecraftServerTarget.h
+ minecraft/launch/MinecraftTarget.cpp
+ minecraft/launch/MinecraftTarget.h
minecraft/launch/PrintInstanceInfo.cpp
minecraft/launch/PrintInstanceInfo.h
minecraft/launch/ReconstructAssets.cpp
@@ -281,6 +280,8 @@ set(MINECRAFT_SOURCES
minecraft/launch/ScanModFolders.h
minecraft/launch/VerifyJavaInstall.cpp
minecraft/launch/VerifyJavaInstall.h
+ minecraft/launch/AutoInstallJava.cpp
+ minecraft/launch/AutoInstallJava.h
minecraft/GradleSpecifier.h
minecraft/MinecraftInstance.cpp
@@ -295,8 +296,6 @@ set(MINECRAFT_SOURCES
minecraft/ComponentUpdateTask.h
minecraft/MinecraftLoadAndCheck.h
minecraft/MinecraftLoadAndCheck.cpp
- minecraft/MinecraftUpdate.h
- minecraft/MinecraftUpdate.cpp
minecraft/MojangVersionFormat.cpp
minecraft/MojangVersionFormat.h
minecraft/Rule.cpp
@@ -307,6 +306,8 @@ set(MINECRAFT_SOURCES
minecraft/ParseUtils.h
minecraft/ProfileUtils.cpp
minecraft/ProfileUtils.h
+ minecraft/ShortcutUtils.cpp
+ minecraft/ShortcutUtils.h
minecraft/Library.cpp
minecraft/Library.h
minecraft/MojangDownloadInfo.h
@@ -333,6 +334,8 @@ set(MINECRAFT_SOURCES
minecraft/mod/ResourceFolderModel.cpp
minecraft/mod/DataPack.h
minecraft/mod/DataPack.cpp
+ minecraft/mod/DataPackFolderModel.h
+ minecraft/mod/DataPackFolderModel.cpp
minecraft/mod/ResourcePack.h
minecraft/mod/ResourcePack.cpp
minecraft/mod/ResourcePackFolderModel.h
@@ -346,17 +349,15 @@ set(MINECRAFT_SOURCES
minecraft/mod/TexturePackFolderModel.h
minecraft/mod/TexturePackFolderModel.cpp
minecraft/mod/ShaderPackFolderModel.h
- minecraft/mod/tasks/BasicFolderLoadTask.h
- minecraft/mod/tasks/ModFolderLoadTask.h
- minecraft/mod/tasks/ModFolderLoadTask.cpp
+ minecraft/mod/ShaderPackFolderModel.cpp
+ minecraft/mod/tasks/ResourceFolderLoadTask.h
+ minecraft/mod/tasks/ResourceFolderLoadTask.cpp
minecraft/mod/tasks/LocalModParseTask.h
minecraft/mod/tasks/LocalModParseTask.cpp
- minecraft/mod/tasks/LocalModUpdateTask.h
- minecraft/mod/tasks/LocalModUpdateTask.cpp
+ minecraft/mod/tasks/LocalResourceUpdateTask.h
+ minecraft/mod/tasks/LocalResourceUpdateTask.cpp
minecraft/mod/tasks/LocalDataPackParseTask.h
minecraft/mod/tasks/LocalDataPackParseTask.cpp
- minecraft/mod/tasks/LocalResourcePackParseTask.h
- minecraft/mod/tasks/LocalResourcePackParseTask.cpp
minecraft/mod/tasks/LocalTexturePackParseTask.h
minecraft/mod/tasks/LocalTexturePackParseTask.cpp
minecraft/mod/tasks/LocalShaderPackParseTask.h
@@ -372,13 +373,17 @@ set(MINECRAFT_SOURCES
minecraft/AssetsUtils.h
minecraft/AssetsUtils.cpp
- # Minecraft services
- minecraft/services/CapeChange.cpp
- minecraft/services/CapeChange.h
- minecraft/services/SkinUpload.cpp
- minecraft/services/SkinUpload.h
- minecraft/services/SkinDelete.cpp
- minecraft/services/SkinDelete.h
+ # Minecraft skins
+ minecraft/skins/CapeChange.cpp
+ minecraft/skins/CapeChange.h
+ minecraft/skins/SkinUpload.cpp
+ minecraft/skins/SkinUpload.h
+ minecraft/skins/SkinDelete.cpp
+ minecraft/skins/SkinDelete.h
+ minecraft/skins/SkinModel.cpp
+ minecraft/skins/SkinModel.h
+ minecraft/skins/SkinList.cpp
+ minecraft/skins/SkinList.h
minecraft/Agent.h)
@@ -422,8 +427,6 @@ set(SETTINGS_SOURCES
set(JAVA_SOURCES
java/JavaChecker.h
java/JavaChecker.cpp
- java/JavaCheckerJob.h
- java/JavaCheckerJob.cpp
java/JavaInstall.h
java/JavaInstall.cpp
java/JavaInstallList.h
@@ -432,6 +435,20 @@ set(JAVA_SOURCES
java/JavaUtils.cpp
java/JavaVersion.h
java/JavaVersion.cpp
+
+ java/JavaMetadata.h
+ java/JavaMetadata.cpp
+ java/download/ArchiveDownloadTask.cpp
+ java/download/ArchiveDownloadTask.h
+ java/download/ManifestDownloadTask.cpp
+ java/download/ManifestDownloadTask.h
+ java/download/SymlinkTask.cpp
+ java/download/SymlinkTask.h
+
+ ui/java/InstallJavaDialog.h
+ ui/java/InstallJavaDialog.cpp
+ ui/java/VersionList.h
+ ui/java/VersionList.cpp
)
set(TRANSLATIONS_SOURCES
@@ -453,6 +470,8 @@ set(TOOLS_SOURCES
tools/JVisualVM.h
tools/MCEditTool.cpp
tools/MCEditTool.h
+ tools/GenericProfiler.cpp
+ tools/GenericProfiler.h
)
set(META_SOURCES
@@ -472,8 +491,11 @@ set(META_SOURCES
set(API_SOURCES
modplatform/ModIndex.h
modplatform/ModIndex.cpp
+ modplatform/ResourceType.h
+ modplatform/ResourceType.cpp
modplatform/ResourceAPI.h
+ modplatform/ResourceAPI.cpp
modplatform/EnsureMetadataTask.h
modplatform/EnsureMetadataTask.cpp
@@ -484,8 +506,6 @@ set(API_SOURCES
modplatform/flame/FlameAPI.cpp
modplatform/modrinth/ModrinthAPI.h
modplatform/modrinth/ModrinthAPI.cpp
- modplatform/helpers/NetworkResourceAPI.h
- modplatform/helpers/NetworkResourceAPI.cpp
modplatform/helpers/HashUtils.h
modplatform/helpers/HashUtils.cpp
modplatform/helpers/OverrideUtils.h
@@ -509,12 +529,15 @@ set(FTB_SOURCES
modplatform/import_ftb/PackInstallTask.cpp
modplatform/import_ftb/PackHelpers.h
modplatform/import_ftb/PackHelpers.cpp
+
+ modplatform/ftb/FTBPackInstallTask.h
+ modplatform/ftb/FTBPackInstallTask.cpp
+ modplatform/ftb/FTBPackManifest.h
+ modplatform/ftb/FTBPackManifest.cpp
)
set(FLAME_SOURCES
# Flame
- modplatform/flame/FlamePackIndex.cpp
- modplatform/flame/FlamePackIndex.h
modplatform/flame/FlameModIndex.cpp
modplatform/flame/FlameModIndex.h
modplatform/flame/PackManifest.h
@@ -532,8 +555,6 @@ set(FLAME_SOURCES
set(MODRINTH_SOURCES
modplatform/modrinth/ModrinthPackIndex.cpp
modplatform/modrinth/ModrinthPackIndex.h
- modplatform/modrinth/ModrinthPackManifest.cpp
- modplatform/modrinth/ModrinthPackManifest.h
modplatform/modrinth/ModrinthCheckUpdate.cpp
modplatform/modrinth/ModrinthCheckUpdate.h
modplatform/modrinth/ModrinthInstanceCreationTask.cpp
@@ -571,8 +592,8 @@ set(ATLAUNCHER_SOURCES
)
set(LINKEXE_SOURCES
- WindowsConsole.cpp
- WindowsConsole.h
+ console/WindowsConsole.h
+ console/WindowsConsole.cpp
filelink/FileLink.h
filelink/FileLink.cpp
@@ -592,7 +613,7 @@ set(PRISMUPDATER_SOURCES
updater/prismupdater/UpdaterDialogs.cpp
updater/prismupdater/GitHubRelease.h
updater/prismupdater/GitHubRelease.cpp
-
+
Json.h
Json.cpp
FileSystem.h
@@ -609,7 +630,11 @@ set(PRISMUPDATER_SOURCES
# Zip
MMCZip.h
MMCZip.cpp
-
+ archive/ArchiveReader.cpp
+ archive/ArchiveReader.h
+ archive/ArchiveWriter.cpp
+ archive/ArchiveWriter.h
+
# Time
MMCTime.h
MMCTime.cpp
@@ -624,7 +649,6 @@ set(PRISMUPDATER_SOURCES
net/HttpMetaCache.h
net/Logging.h
net/Logging.cpp
- net/NetAction.h
net/NetRequest.cpp
net/NetRequest.h
net/NetJob.cpp
@@ -642,6 +666,14 @@ set(PRISMUPDATER_SOURCES
)
+if(WIN32)
+ set(PRISMUPDATER_SOURCES
+ console/WindowsConsole.h
+ console/WindowsConsole.cpp
+ ${PRISMUPDATER_SOURCES}
+ )
+endif()
+
######## Logging categories ########
ecm_qt_declare_logging_category(CORE_SOURCES
@@ -653,6 +685,22 @@ ecm_qt_declare_logging_category(CORE_SOURCES
EXPORT "${Launcher_Name}"
)
+ecm_qt_export_logging_category(
+ IDENTIFIER instanceProfileC
+ CATEGORY_NAME "launcher.instance.profile"
+ DEFAULT_SEVERITY Debug
+ DESCRIPTION "Profile actions"
+ EXPORT "${Launcher_Name}"
+)
+
+ecm_qt_export_logging_category(
+ IDENTIFIER instanceProfileResolveC
+ CATEGORY_NAME "launcher.instance.profile.resolve"
+ DEFAULT_SEVERITY Debug
+ DESCRIPTION "Profile component resolusion actions"
+ EXPORT "${Launcher_Name}"
+)
+
ecm_qt_export_logging_category(
IDENTIFIER taskLogC
CATEGORY_NAME "launcher.task"
@@ -665,7 +713,7 @@ ecm_qt_export_logging_category(
IDENTIFIER taskNetLogC
CATEGORY_NAME "launcher.task.net"
DEFAULT_SEVERITY Debug
- DESCRIPTION "task network action"
+ DESCRIPTION "Task network action"
EXPORT "${Launcher_Name}"
)
@@ -673,14 +721,14 @@ ecm_qt_export_logging_category(
IDENTIFIER taskDownloadLogC
CATEGORY_NAME "launcher.task.net.download"
DEFAULT_SEVERITY Debug
- DESCRIPTION "task network download actions"
+ DESCRIPTION "Task network download actions"
EXPORT "${Launcher_Name}"
)
ecm_qt_export_logging_category(
IDENTIFIER taskUploadLogC
CATEGORY_NAME "launcher.task.net.upload"
DEFAULT_SEVERITY Debug
- DESCRIPTION "task network upload actions"
+ DESCRIPTION "Task network upload actions"
EXPORT "${Launcher_Name}"
)
@@ -713,7 +761,6 @@ endif()
set(LOGIC_SOURCES
${CORE_SOURCES}
- ${PATHMATCHER_SOURCES}
${NET_SOURCES}
${LAUNCH_SOURCES}
${UPDATE_SOURCES}
@@ -750,6 +797,13 @@ SET(LAUNCHER_SOURCES
DataMigrationTask.cpp
ApplicationMessage.h
ApplicationMessage.cpp
+ SysInfo.h
+ SysInfo.cpp
+ HardwareInfo.cpp
+ HardwareInfo.h
+
+ # console utils
+ console/Console.h
# GUI - general utilities
DesktopServices.h
@@ -763,41 +817,29 @@ SET(LAUNCHER_SOURCES
KonamiCode.h
KonamiCode.cpp
- # Bundled resources
- resources/backgrounds/backgrounds.qrc
- resources/multimc/multimc.qrc
- resources/pe_dark/pe_dark.qrc
- resources/pe_light/pe_light.qrc
- resources/pe_colored/pe_colored.qrc
- resources/pe_blue/pe_blue.qrc
- resources/breeze_dark/breeze_dark.qrc
- resources/breeze_light/breeze_light.qrc
- resources/OSX/OSX.qrc
- resources/iOS/iOS.qrc
- resources/flat/flat.qrc
- resources/flat_white/flat_white.qrc
- resources/documents/documents.qrc
- ../${Launcher_Branding_LogoQRC}
-
# Icons
icons/MMCIcon.h
icons/MMCIcon.cpp
icons/IconList.h
icons/IconList.cpp
+ # log utils
+ logs/AnonymizeLog.cpp
+ logs/AnonymizeLog.h
+
# GUI - windows
ui/GuiUtil.h
ui/GuiUtil.cpp
- ui/ColorCache.h
- ui/ColorCache.cpp
ui/MainWindow.h
ui/MainWindow.cpp
ui/InstanceWindow.h
ui/InstanceWindow.cpp
+ ui/ViewLogWindow.h
+ ui/ViewLogWindow.cpp
+ ui/ToolTipFilter.h
+ ui/ToolTipFilter.cpp
# FIXME: maybe find a better home for this.
- SkinUtils.cpp
- SkinUtils.h
FileIgnoreProxy.cpp
FileIgnoreProxy.h
FastFileIconProvider.cpp
@@ -813,8 +855,11 @@ SET(LAUNCHER_SOURCES
ui/setupwizard/LanguageWizardPage.h
ui/setupwizard/PasteWizardPage.cpp
ui/setupwizard/PasteWizardPage.h
- ui/setupwizard/ThemeWizardPage.cpp
ui/setupwizard/ThemeWizardPage.h
+ ui/setupwizard/AutoJavaWizardPage.cpp
+ ui/setupwizard/AutoJavaWizardPage.h
+ ui/setupwizard/LoginWizardPage.cpp
+ ui/setupwizard/LoginWizardPage.h
# GUI - themes
ui/themes/FusionTheme.cpp
@@ -837,8 +882,11 @@ SET(LAUNCHER_SOURCES
ui/themes/ThemeManager.h
ui/themes/CatPack.cpp
ui/themes/CatPack.h
+ ui/themes/CatPainter.cpp
+ ui/themes/CatPainter.h
# Processes
+ LaunchMode.h
LaunchController.h
LaunchController.cpp
@@ -857,12 +905,12 @@ SET(LAUNCHER_SOURCES
# GUI - instance pages
ui/pages/instance/ExternalResourcesPage.cpp
ui/pages/instance/ExternalResourcesPage.h
- ui/pages/instance/GameOptionsPage.cpp
- ui/pages/instance/GameOptionsPage.h
ui/pages/instance/VersionPage.cpp
ui/pages/instance/VersionPage.h
ui/pages/instance/ManagedPackPage.cpp
ui/pages/instance/ManagedPackPage.h
+ ui/pages/instance/DataPackPage.h
+ ui/pages/instance/DataPackPage.cpp
ui/pages/instance/TexturePackPage.h
ui/pages/instance/TexturePackPage.cpp
ui/pages/instance/ResourcePackPage.h
@@ -875,7 +923,6 @@ SET(LAUNCHER_SOURCES
ui/pages/instance/NotesPage.h
ui/pages/instance/LogPage.cpp
ui/pages/instance/LogPage.h
- ui/pages/instance/InstanceSettingsPage.cpp
ui/pages/instance/InstanceSettingsPage.h
ui/pages/instance/ScreenshotsPage.cpp
ui/pages/instance/ScreenshotsPage.h
@@ -885,24 +932,26 @@ SET(LAUNCHER_SOURCES
ui/pages/instance/ServersPage.h
ui/pages/instance/WorldListPage.cpp
ui/pages/instance/WorldListPage.h
+ ui/pages/instance/McClient.cpp
+ ui/pages/instance/McClient.h
+ ui/pages/instance/McResolver.cpp
+ ui/pages/instance/McResolver.h
+ ui/pages/instance/ServerPingTask.cpp
+ ui/pages/instance/ServerPingTask.h
# GUI - global settings pages
ui/pages/global/AccountListPage.cpp
ui/pages/global/AccountListPage.h
- ui/pages/global/CustomCommandsPage.cpp
- ui/pages/global/CustomCommandsPage.h
- ui/pages/global/EnvironmentVariablesPage.cpp
- ui/pages/global/EnvironmentVariablesPage.h
ui/pages/global/ExternalToolsPage.cpp
ui/pages/global/ExternalToolsPage.h
ui/pages/global/JavaPage.cpp
ui/pages/global/JavaPage.h
ui/pages/global/LanguagePage.cpp
ui/pages/global/LanguagePage.h
- ui/pages/global/MinecraftPage.cpp
ui/pages/global/MinecraftPage.h
ui/pages/global/LauncherPage.cpp
ui/pages/global/LauncherPage.h
+ ui/pages/global/AppearancePage.h
ui/pages/global/ProxyPage.cpp
ui/pages/global/ProxyPage.h
ui/pages/global/APIPage.cpp
@@ -932,6 +981,11 @@ SET(LAUNCHER_SOURCES
ui/pages/modplatform/ShaderPackPage.cpp
ui/pages/modplatform/ShaderPackModel.cpp
+ ui/pages/modplatform/DataPackPage.cpp
+ ui/pages/modplatform/DataPackModel.cpp
+
+ ui/pages/modplatform/ModpackProviderBasePage.h
+
ui/pages/modplatform/atlauncher/AtlFilterModel.cpp
ui/pages/modplatform/atlauncher/AtlFilterModel.h
ui/pages/modplatform/atlauncher/AtlListModel.cpp
@@ -943,6 +997,13 @@ SET(LAUNCHER_SOURCES
ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.cpp
ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.h
+ ui/pages/modplatform/ftb/FtbFilterModel.cpp
+ ui/pages/modplatform/ftb/FtbFilterModel.h
+ ui/pages/modplatform/ftb/FtbListModel.cpp
+ ui/pages/modplatform/ftb/FtbListModel.h
+ ui/pages/modplatform/ftb/FtbPage.cpp
+ ui/pages/modplatform/ftb/FtbPage.h
+
ui/pages/modplatform/legacy_ftb/Page.cpp
ui/pages/modplatform/legacy_ftb/Page.h
ui/pages/modplatform/legacy_ftb/ListModel.h
@@ -978,8 +1039,6 @@ SET(LAUNCHER_SOURCES
ui/pages/modplatform/OptionalModDialog.cpp
ui/pages/modplatform/OptionalModDialog.h
- ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp
- ui/pages/modplatform/modrinth/ModrinthResourceModels.h
ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp
ui/pages/modplatform/modrinth/ModrinthResourcePages.h
@@ -992,10 +1051,10 @@ SET(LAUNCHER_SOURCES
ui/dialogs/ProfileSetupDialog.h
ui/dialogs/CopyInstanceDialog.cpp
ui/dialogs/CopyInstanceDialog.h
+ ui/dialogs/CreateShortcutDialog.cpp
+ ui/dialogs/CreateShortcutDialog.h
ui/dialogs/CustomMessageBox.cpp
ui/dialogs/CustomMessageBox.h
- ui/dialogs/EditAccountDialog.cpp
- ui/dialogs/EditAccountDialog.h
ui/dialogs/ExportInstanceDialog.cpp
ui/dialogs/ExportInstanceDialog.h
ui/dialogs/ExportPackDialog.cpp
@@ -1008,8 +1067,8 @@ SET(LAUNCHER_SOURCES
ui/dialogs/ImportResourceDialog.h
ui/dialogs/MSALoginDialog.cpp
ui/dialogs/MSALoginDialog.h
- ui/dialogs/OfflineLoginDialog.cpp
- ui/dialogs/OfflineLoginDialog.h
+ ui/dialogs/NetworkJobFailedDialog.cpp
+ ui/dialogs/NetworkJobFailedDialog.h
ui/dialogs/NewComponentDialog.cpp
ui/dialogs/NewComponentDialog.h
ui/dialogs/NewInstanceDialog.cpp
@@ -1024,8 +1083,6 @@ SET(LAUNCHER_SOURCES
ui/dialogs/ReviewMessageBox.h
ui/dialogs/VersionSelectDialog.cpp
ui/dialogs/VersionSelectDialog.h
- ui/dialogs/SkinUploadDialog.cpp
- ui/dialogs/SkinUploadDialog.h
ui/dialogs/ResourceDownloadDialog.cpp
ui/dialogs/ResourceDownloadDialog.h
ui/dialogs/ScrollMessageBox.cpp
@@ -1034,32 +1091,40 @@ SET(LAUNCHER_SOURCES
ui/dialogs/BlockedModsDialog.h
ui/dialogs/ChooseProviderDialog.h
ui/dialogs/ChooseProviderDialog.cpp
- ui/dialogs/ModUpdateDialog.cpp
- ui/dialogs/ModUpdateDialog.h
+ ui/dialogs/ResourceUpdateDialog.cpp
+ ui/dialogs/ResourceUpdateDialog.h
ui/dialogs/InstallLoaderDialog.cpp
ui/dialogs/InstallLoaderDialog.h
+ ui/dialogs/ChooseOfflineNameDialog.cpp
+ ui/dialogs/ChooseOfflineNameDialog.h
+
+ ui/dialogs/skins/SkinManageDialog.cpp
+ ui/dialogs/skins/SkinManageDialog.h
+
+ ui/dialogs/skins/draw/SkinOpenGLWindow.h
+ ui/dialogs/skins/draw/SkinOpenGLWindow.cpp
+ ui/dialogs/skins/draw/Scene.h
+ ui/dialogs/skins/draw/Scene.cpp
+ ui/dialogs/skins/draw/BoxGeometry.h
+ ui/dialogs/skins/draw/BoxGeometry.cpp
# GUI - widgets
+ ui/widgets/CheckComboBox.cpp
+ ui/widgets/CheckComboBox.h
ui/widgets/Common.cpp
ui/widgets/Common.h
ui/widgets/CustomCommands.cpp
ui/widgets/CustomCommands.h
ui/widgets/EnvironmentVariables.cpp
ui/widgets/EnvironmentVariables.h
- ui/widgets/DropLabel.cpp
- ui/widgets/DropLabel.h
- ui/widgets/FocusLineEdit.cpp
- ui/widgets/FocusLineEdit.h
ui/widgets/IconLabel.cpp
ui/widgets/IconLabel.h
- ui/widgets/JavaSettingsWidget.cpp
- ui/widgets/JavaSettingsWidget.h
+ ui/widgets/JavaWizardWidget.cpp
+ ui/widgets/JavaWizardWidget.h
ui/widgets/LabeledToolButton.cpp
ui/widgets/LabeledToolButton.h
ui/widgets/LanguageSelectionWidget.cpp
ui/widgets/LanguageSelectionWidget.h
- ui/widgets/LineSeparator.cpp
- ui/widgets/LineSeparator.h
ui/widgets/LogView.cpp
ui/widgets/LogView.h
ui/widgets/InfoFrame.cpp
@@ -1087,8 +1152,12 @@ SET(LAUNCHER_SOURCES
ui/widgets/ProgressWidget.cpp
ui/widgets/WideBar.h
ui/widgets/WideBar.cpp
- ui/widgets/ThemeCustomizationWidget.h
- ui/widgets/ThemeCustomizationWidget.cpp
+ ui/widgets/AppearanceWidget.h
+ ui/widgets/AppearanceWidget.cpp
+ ui/widgets/MinecraftSettingsWidget.h
+ ui/widgets/MinecraftSettingsWidget.cpp
+ ui/widgets/JavaSettingsWidget.h
+ ui/widgets/JavaSettingsWidget.cpp
# GUI - instance group view
ui/instanceview/InstanceProxyModel.cpp
@@ -1104,8 +1173,15 @@ SET(LAUNCHER_SOURCES
ui/instanceview/VisualGroup.h
)
+if (APPLE)
+ set(LAUNCHER_SOURCES
+ ${LAUNCHER_SOURCES}
+ ui/themes/ThemeManager.mm
+ )
+endif()
+
if (NOT Apple)
-set(LAUNCHER_SOURCES
+ set(LAUNCHER_SOURCES
${LAUNCHER_SOURCES}
ui/dialogs/UpdateAvailableDialog.h
@@ -1113,10 +1189,19 @@ set(LAUNCHER_SOURCES
)
endif()
+if (APPLE)
+ set(LAUNCHER_SOURCES
+ ${LAUNCHER_SOURCES}
+
+ macsandbox/SecurityBookmarkFileAccess.h
+ macsandbox/SecurityBookmarkFileAccess.mm
+ )
+endif()
+
if(WIN32)
set(LAUNCHER_SOURCES
- WindowsConsole.cpp
- WindowsConsole.h
+ console/WindowsConsole.h
+ console/WindowsConsole.cpp
${LAUNCHER_SOURCES}
)
endif()
@@ -1124,21 +1209,19 @@ endif()
qt_wrap_ui(LAUNCHER_UI
ui/MainWindow.ui
ui/setupwizard/PasteWizardPage.ui
- ui/setupwizard/ThemeWizardPage.ui
+ ui/setupwizard/AutoJavaWizardPage.ui
+ ui/setupwizard/LoginWizardPage.ui
ui/pages/global/AccountListPage.ui
ui/pages/global/JavaPage.ui
ui/pages/global/LauncherPage.ui
ui/pages/global/APIPage.ui
ui/pages/global/ProxyPage.ui
- ui/pages/global/MinecraftPage.ui
ui/pages/global/ExternalToolsPage.ui
ui/pages/instance/ExternalResourcesPage.ui
ui/pages/instance/NotesPage.ui
ui/pages/instance/LogPage.ui
ui/pages/instance/ServersPage.ui
- ui/pages/instance/GameOptionsPage.ui
ui/pages/instance/OtherLogsPage.ui
- ui/pages/instance/InstanceSettingsPage.ui
ui/pages/instance/VersionPage.ui
ui/pages/instance/ManagedPackPage.ui
ui/pages/instance/WorldListPage.ui
@@ -1152,36 +1235,39 @@ qt_wrap_ui(LAUNCHER_UI
ui/pages/modplatform/import_ftb/ImportFTBPage.ui
ui/pages/modplatform/ImportPage.ui
ui/pages/modplatform/OptionalModDialog.ui
+ ui/pages/modplatform/ftb/FtbPage.ui
ui/pages/modplatform/modrinth/ModrinthPage.ui
ui/pages/modplatform/technic/TechnicPage.ui
- ui/widgets/InstanceCardWidget.ui
ui/widgets/CustomCommands.ui
ui/widgets/EnvironmentVariables.ui
ui/widgets/InfoFrame.ui
ui/widgets/ModFilterWidget.ui
ui/widgets/SubTaskProgressBar.ui
- ui/widgets/ThemeCustomizationWidget.ui
+ ui/widgets/AppearanceWidget.ui
+ ui/widgets/MinecraftSettingsWidget.ui
+ ui/widgets/JavaSettingsWidget.ui
ui/dialogs/CopyInstanceDialog.ui
+ ui/dialogs/CreateShortcutDialog.ui
ui/dialogs/ProfileSetupDialog.ui
ui/dialogs/ProgressDialog.ui
ui/dialogs/NewInstanceDialog.ui
+ ui/dialogs/NetworkJobFailedDialog.ui
ui/dialogs/NewComponentDialog.ui
ui/dialogs/NewsDialog.ui
ui/dialogs/ProfileSelectDialog.ui
- ui/dialogs/SkinUploadDialog.ui
ui/dialogs/ExportInstanceDialog.ui
ui/dialogs/ExportPackDialog.ui
ui/dialogs/ExportToModListDialog.ui
ui/dialogs/IconPickerDialog.ui
ui/dialogs/ImportResourceDialog.ui
ui/dialogs/MSALoginDialog.ui
- ui/dialogs/OfflineLoginDialog.ui
ui/dialogs/AboutDialog.ui
- ui/dialogs/EditAccountDialog.ui
ui/dialogs/ReviewMessageBox.ui
ui/dialogs/ScrollMessageBox.ui
ui/dialogs/BlockedModsDialog.ui
ui/dialogs/ChooseProviderDialog.ui
+ ui/dialogs/skins/SkinManageDialog.ui
+ ui/dialogs/ChooseOfflineNameDialog.ui
)
qt_wrap_ui(PRISM_UPDATE_UI
@@ -1204,8 +1290,10 @@ qt_add_resources(LAUNCHER_RESOURCES
resources/OSX/OSX.qrc
resources/iOS/iOS.qrc
resources/flat/flat.qrc
+ resources/flat_white/flat_white.qrc
resources/documents/documents.qrc
- ../${Launcher_Branding_LogoQRC}
+ resources/shaders/shaders.qrc
+ "${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_LogoQRC}"
)
qt_wrap_ui(PRISMUPDATER_UI
@@ -1219,34 +1307,56 @@ if(WIN32)
set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC})
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})
-if(BUILD_TESTING)
-target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_TEST)
-endif()
-set_project_warnings(Launcher_logic
- "${Launcher_MSVC_WARNINGS}"
- "${Launcher_CLANG_WARNINGS}"
- "${Launcher_GCC_WARNINGS}")
-target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
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
nbt++
${ZLIB_LIBRARIES}
- tomlplusplus::tomlplusplus
qdcss
BuildConfig
- Katabasis
Qt${QT_VERSION_MAJOR}::Widgets
- ghcFilesystem::ghc_filesystem
)
-if (UNIX AND NOT CYGWIN AND NOT APPLE)
+if(TARGET PkgConfig::libqrencode)
+ target_link_libraries(Launcher_logic PkgConfig::libqrencode)
+else()
+ target_include_directories(Launcher_logic PRIVATE ${LIBQRENCODE_INCLUDE_DIR})
+ target_link_libraries(Launcher_logic ${LIBQRENCODE_LIBRARIES})
+endif()
+
+if(TARGET PkgConfig::tomlplusplus)
+ target_link_libraries(Launcher_logic PkgConfig::tomlplusplus)
+else()
+ target_link_libraries(Launcher_logic tomlplusplus::tomlplusplus)
+endif()
+if(TARGET PkgConfig::libarchive)
+ target_link_libraries(Launcher_logic PkgConfig::libarchive)
+else()
+ target_link_libraries(Launcher_logic LibArchive::LibArchive)
+endif()
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_libraries(Launcher_logic
gamemode
)
@@ -1259,24 +1369,30 @@ target_link_libraries(Launcher_logic
Qt${QT_VERSION_MAJOR}::Concurrent
Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Widgets
+ Qt${QT_VERSION_MAJOR}::NetworkAuth
+ Qt${QT_VERSION_MAJOR}::OpenGL
+ ${Launcher_QT_DBUS}
${Launcher_QT_LIBS}
)
target_link_libraries(Launcher_logic
- QuaZip::QuaZip
cmark::cmark
LocalPeer
Launcher_rainbow
)
+if (TARGET ${Launcher_QT_DBUS})
+ add_compile_definitions(WITH_QTDBUS)
+endif()
+
if(APPLE)
set(CMAKE_MACOSX_RPATH 1)
set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/")
if(Launcher_ENABLE_UPDATER)
- file(DOWNLOAD ${MACOSX_SPARKLE_DOWNLOAD_URL} ${CMAKE_BINARY_DIR}/Sparkle.tar.xz EXPECTED_HASH SHA256=${MACOSX_SPARKLE_SHA256})
- file(ARCHIVE_EXTRACT INPUT ${CMAKE_BINARY_DIR}/Sparkle.tar.xz DESTINATION ${CMAKE_BINARY_DIR}/frameworks/Sparkle)
+ file(DOWNLOAD ${MACOSX_SPARKLE_DOWNLOAD_URL} ${CMAKE_BINARY_DIR}/Sparkle.tar.xz EXPECTED_HASH SHA256=${MACOSX_SPARKLE_SHA256})
+ file(ARCHIVE_EXTRACT INPUT ${CMAKE_BINARY_DIR}/Sparkle.tar.xz DESTINATION ${CMAKE_BINARY_DIR}/frameworks/Sparkle)
- find_library(SPARKLE_FRAMEWORK Sparkle "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
- add_compile_definitions(SPARKLE_ENABLED)
+ find_library(SPARKLE_FRAMEWORK Sparkle "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
+ add_compile_definitions(SPARKLE_ENABLED)
endif()
target_link_libraries(Launcher_logic
@@ -1286,13 +1402,16 @@ if(APPLE)
"-framework ApplicationServices"
)
if(Launcher_ENABLE_UPDATER)
- target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK})
+ target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK})
endif()
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)
@@ -1308,34 +1427,50 @@ if(DEFINED Launcher_APP_BINARY_DEFS)
endif()
install(TARGETS ${Launcher_Name}
+ RUNTIME_DEPENDENCY_SET LAUNCHER_DEPENDENCY_SET
BUNDLE DESTINATION "." COMPONENT Runtime
LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime
RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
)
+# Deploy PDBs
+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
- QuaZip::QuaZip
${ZLIB_LIBRARIES}
- systeminfo
BuildConfig
- ghcFilesystem::ghc_filesystem
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Network
${Launcher_QT_LIBS}
cmark::cmark
- Katabasis
)
+ if(TARGET PkgConfig::libarchive)
+ target_link_libraries(prism_updater_logic PkgConfig::libarchive)
+ else()
+ target_link_libraries(prism_updater_logic LibArchive::LibArchive)
+ endif()
add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp)
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()
@@ -1349,21 +1484,25 @@ if(Launcher_BUILD_UPDATER)
RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
)
+
+ # Deploy PDBs
+ if(CMAKE_CXX_LINKER_SUPPORTS_PDB)
+ install(FILES $ DESTINATION ${BINARY_DEST_DIR} OPTIONAL)
+ endif()
endif()
if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
# File link
add_library(filelink_logic STATIC ${LINKEXE_SOURCES})
- set_project_warnings(filelink_logic
- "${Launcher_MSVC_WARNINGS}"
- "${Launcher_CLANG_WARNINGS}"
- "${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
- ghcFilesystem::ghc_filesystem
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Network
@@ -1372,9 +1511,19 @@ 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
+ if(MSVC)
+ set_property(TARGET "${Launcher_Name}_filelink" PROPERTY LINK_FLAGS "/MANIFESTUAC:level='requireAdministrator'")
+ endif()
+
target_link_libraries("${Launcher_Name}_filelink" filelink_logic)
if(DEFINED Launcher_APP_BINARY_NAME)
@@ -1390,6 +1539,11 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
)
+
+ # Deploy PDBs
+ if(CMAKE_CXX_LINKER_SUPPORTS_PDB)
+ install(FILES $ DESTINATION ${BINARY_DEST_DIR} OPTIONAL)
+ endif()
endif()
if (UNIX AND APPLE AND Launcher_ENABLE_UPDATER)
@@ -1399,187 +1553,140 @@ if (UNIX AND APPLE AND Launcher_ENABLE_UPDATER)
install(DIRECTORY ${MACOSX_SPARKLE_DIR}/Sparkle.framework DESTINATION ${FRAMEWORK_DEST_DIR} USE_SOURCE_PERMISSIONS)
endif()
+# Set basic compiler warning/error flags for all targets
+get_property(Launcher_TARGETS DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY BUILDSYSTEM_TARGETS)
+foreach(target ${Launcher_TARGETS})
+ message(STATUS "Enabling all warnings as errors for target '${target}'")
+ if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
+ target_compile_options(${target} PRIVATE /W4 /WX /permissive-)
+ else()
+ target_compile_options(${target} PRIVATE -Wall -Wextra -Wpedantic -Werror)
+ endif()
+endforeach()
+
+# Disable some warnings in main launcher target due to being present in a lot of places. TODO: Fix them.
+if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
+ target_compile_options(Launcher_logic PRIVATE /wd4100) # C4100 - unused parameter
+ target_compile_options(${Launcher_Name} PRIVATE /wd4100) # C4100 - unused parameter
+else()
+ target_compile_options(Launcher_logic PRIVATE -Wno-unused-parameter -Wno-missing-field-initializers)
+ target_compile_options(${Launcher_Name} PRIVATE -Wno-unused-parameter -Wno-missing-field-initializers)
+endif()
+
#### The bundle mess! ####
-# Bundle utilities are used to complete the portable packages - they add all the libraries that would otherwise be missing on the target system.
+# Bundle utilities are used to complete packages for different platforms - they add all the libraries that would otherwise be missing on the target system.
# NOTE: it seems that this absolutely has to be here, and nowhere else.
-if(INSTALL_BUNDLE STREQUAL "full")
- # Add qt.conf - this makes Qt stop looking for things outside the bundle
- install(
- CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${RESOURCES_DEST_DIR}/qt.conf\" \" \")"
- COMPONENT Runtime
- )
- # add qtlogging.ini as a config file
- install(
- FILES "qtlogging.ini"
- DESTINATION ${CMAKE_INSTALL_PREFIX}/${RESOURCES_DEST_DIR}
- COMPONENT Runtime
- )
- # Bundle plugins
- # Image formats
- install(
- DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
- CONFIGURATIONS Debug RelWithDebInfo ""
- DESTINATION ${PLUGIN_DEST_DIR}
- COMPONENT Runtime
- REGEX "tga|tiff|mng" EXCLUDE
- )
- install(
- DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
- CONFIGURATIONS Release MinSizeRel
- DESTINATION ${PLUGIN_DEST_DIR}
- COMPONENT Runtime
- REGEX "tga|tiff|mng" EXCLUDE
- REGEX "d\\." EXCLUDE
- REGEX "_debug\\." EXCLUDE
- REGEX "\\.dSYM" EXCLUDE
+if(WIN32 OR (UNIX AND APPLE))
+ if(WIN32)
+ set(QT_DEPLOY_TOOL_OPTIONS "--no-opengl-sw --no-quick-import --no-system-d3d-compiler --no-system-dxc-compiler --skip-plugin-types generic,networkinformation")
+ endif()
+
+ qt_generate_deploy_script(
+ TARGET ${Launcher_Name}
+ OUTPUT_SCRIPT QT_DEPLOY_SCRIPT
+ CONTENT "
+ qt_deploy_runtime_dependencies(
+ EXECUTABLE ${BINARY_DEST_DIR}/$
+ BIN_DIR ${BINARY_DEST_DIR}
+ LIBEXEC_DIR ${LIBRARY_DEST_DIR}
+ LIB_DIR ${LIBRARY_DEST_DIR}
+ PLUGINS_DIR ${PLUGIN_DEST_DIR}
+ NO_OVERWRITE
+ NO_TRANSLATIONS
+ NO_COMPILER_RUNTIME
+ DEPLOY_TOOL_OPTIONS ${QT_DEPLOY_TOOL_OPTIONS}
+ )"
)
- # Icon engines
+
+ # Bundle our linked dependencies
install(
- DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
- CONFIGURATIONS Debug RelWithDebInfo ""
- DESTINATION ${PLUGIN_DEST_DIR}
- COMPONENT Runtime
- REGEX "fontawesome" EXCLUDE
+ RUNTIME_DEPENDENCY_SET LAUNCHER_DEPENDENCY_SET
+ COMPONENT bundle
+ DIRECTORIES
+ ${CMAKE_SYSTEM_LIBRARY_PATH}
+ ${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"
+ LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
+ RUNTIME DESTINATION ${BINARY_DEST_DIR}
+ FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR}
)
+ # Deploy Qt plugins
install(
- DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
- CONFIGURATIONS Release MinSizeRel
- DESTINATION ${PLUGIN_DEST_DIR}
- COMPONENT Runtime
- REGEX "fontawesome" EXCLUDE
- REGEX "d\\." EXCLUDE
- REGEX "_debug\\." EXCLUDE
- REGEX "\\.dSYM" EXCLUDE
+ SCRIPT ${QT_DEPLOY_SCRIPT}
+ COMPONENT bundle
)
- # Platform plugins
+ # 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(
- DIRECTORY "${QT_PLUGINS_DIR}/platforms"
- CONFIGURATIONS Debug RelWithDebInfo ""
- DESTINATION ${PLUGIN_DEST_DIR}
- COMPONENT Runtime
- REGEX "minimal|linuxfb|offscreen" EXCLUDE
+ CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${RESOURCES_DEST_DIR}/qt.conf\" \" \")"
+ COMPONENT bundle
)
+ # Add qtlogging.ini as a config file
install(
- DIRECTORY "${QT_PLUGINS_DIR}/platforms"
- CONFIGURATIONS Release MinSizeRel
- DESTINATION ${PLUGIN_DEST_DIR}
- COMPONENT Runtime
- REGEX "minimal|linuxfb|offscreen" EXCLUDE
- REGEX "[^2]d\\." EXCLUDE
- REGEX "_debug\\." EXCLUDE
- REGEX "\\.dSYM" EXCLUDE
+ FILES "qtlogging.ini"
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/${RESOURCES_DEST_DIR}
+ COMPONENT bundle
)
- # Style plugins
- if(EXISTS "${QT_PLUGINS_DIR}/styles")
- install(
- DIRECTORY "${QT_PLUGINS_DIR}/styles"
- CONFIGURATIONS Debug RelWithDebInfo ""
- DESTINATION ${PLUGIN_DEST_DIR}
- COMPONENT Runtime
- )
- install(
- DIRECTORY "${QT_PLUGINS_DIR}/styles"
- CONFIGURATIONS Release MinSizeRel
- DESTINATION ${PLUGIN_DEST_DIR}
- COMPONENT Runtime
- REGEX "d\\." EXCLUDE
- REGEX "_debug\\." EXCLUDE
- REGEX "\\.dSYM" EXCLUDE
- )
- endif()
- # TLS plugins (Qt 6 only)
- if(EXISTS "${QT_PLUGINS_DIR}/tls")
- install(
- DIRECTORY "${QT_PLUGINS_DIR}/tls"
- CONFIGURATIONS Debug RelWithDebInfo ""
- DESTINATION ${PLUGIN_DEST_DIR}
- COMPONENT Runtime
- PATTERN "*qcertonlybackend*" EXCLUDE
- )
- install(
- DIRECTORY "${QT_PLUGINS_DIR}/tls"
- CONFIGURATIONS Release MinSizeRel
- DESTINATION ${PLUGIN_DEST_DIR}
- COMPONENT Runtime
- REGEX "dd\\." EXCLUDE
- REGEX "_debug\\." EXCLUDE
- REGEX "\\.dSYM" EXCLUDE
- PATTERN "*qcertonlybackend*" EXCLUDE
- )
- endif()
- # Wayland support
- if(EXISTS "${QT_PLUGINS_DIR}/wayland-graphics-integration-client")
- install(
- DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-client"
- CONFIGURATIONS Debug RelWithDebInfo ""
- DESTINATION ${PLUGIN_DEST_DIR}
- COMPONENT Runtime
- )
- install(
- DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-client"
- CONFIGURATIONS Release MinSizeRel
- DESTINATION ${PLUGIN_DEST_DIR}
- COMPONENT Runtime
- REGEX "dd\\." EXCLUDE
- REGEX "_debug\\." EXCLUDE
- REGEX "\\.dSYM" EXCLUDE
- )
- endif()
- if(EXISTS "${QT_PLUGINS_DIR}/wayland-graphics-integration-server")
- install(
- DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-server"
- CONFIGURATIONS Debug RelWithDebInfo ""
- DESTINATION ${PLUGIN_DEST_DIR}
- COMPONENT Runtime
- )
- install(
- DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-server"
- CONFIGURATIONS Release MinSizeRel
- DESTINATION ${PLUGIN_DEST_DIR}
- COMPONENT Runtime
- REGEX "dd\\." EXCLUDE
- REGEX "_debug\\." EXCLUDE
- REGEX "\\.dSYM" EXCLUDE
- )
- endif()
- if(EXISTS "${QT_PLUGINS_DIR}/wayland-decoration-client")
- install(
- DIRECTORY "${QT_PLUGINS_DIR}/wayland-decoration-client"
- CONFIGURATIONS Debug RelWithDebInfo ""
- DESTINATION ${PLUGIN_DEST_DIR}
- COMPONENT Runtime
- )
- install(
- DIRECTORY "${QT_PLUGINS_DIR}/wayland-decoration-client"
- CONFIGURATIONS Release MinSizeRel
- DESTINATION ${PLUGIN_DEST_DIR}
- COMPONENT Runtime
- REGEX "dd\\." EXCLUDE
- REGEX "_debug\\." EXCLUDE
- REGEX "\\.dSYM" EXCLUDE
- )
- endif()
- if(EXISTS "${QT_PLUGINS_DIR}/wayland-shell-integration")
- install(
- DIRECTORY "${QT_PLUGINS_DIR}/wayland-shell-integration"
- CONFIGURATIONS Debug RelWithDebInfo ""
- DESTINATION ${PLUGIN_DEST_DIR}
- COMPONENT Runtime
- )
- install(
- DIRECTORY "${QT_PLUGINS_DIR}/wayland-shell-integration"
- CONFIGURATIONS Release MinSizeRel
- DESTINATION ${PLUGIN_DEST_DIR}
- COMPONENT Runtime
- REGEX "dd\\." EXCLUDE
- REGEX "_debug\\." EXCLUDE
- REGEX "\\.dSYM" EXCLUDE
- )
- endif()
- configure_file(
- "${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"
- "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake"
- @ONLY
+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}
)
- install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake" COMPONENT Runtime)
+else()
+ message(WARNING "Unable to find `clang-format`. Not creating custom target")
endif()
diff --git a/launcher/DataMigrationTask.cpp b/launcher/DataMigrationTask.cpp
index 27ce5f01b8..cab22089eb 100644
--- a/launcher/DataMigrationTask.cpp
+++ b/launcher/DataMigrationTask.cpp
@@ -12,13 +12,10 @@
#include
-DataMigrationTask::DataMigrationTask(QObject* parent,
- const QString& sourcePath,
- const QString& targetPath,
- const IPathMatcher::Ptr pathMatcher)
- : Task(parent), m_sourcePath(sourcePath), m_targetPath(targetPath), m_pathMatcher(pathMatcher), m_copy(sourcePath, targetPath)
+DataMigrationTask::DataMigrationTask(const QString& sourcePath, const QString& targetPath, Filter pathMatcher)
+ : Task(), m_sourcePath(sourcePath), m_targetPath(targetPath), m_pathMatcher(pathMatcher), m_copy(sourcePath, targetPath)
{
- m_copy.matcher(m_pathMatcher.get()).whitelist(true);
+ m_copy.matcher(m_pathMatcher).whitelist(true);
}
void DataMigrationTask::executeTask()
@@ -27,7 +24,7 @@ void DataMigrationTask::executeTask()
// 1. Scan
// Check how many files we gotta copy
- m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] {
+ m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] {
return m_copy(true); // dry run to collect amount of files
});
connect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &DataMigrationTask::dryRunFinished);
@@ -40,11 +37,7 @@ void DataMigrationTask::dryRunFinished()
disconnect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &DataMigrationTask::dryRunFinished);
disconnect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &DataMigrationTask::dryRunAborted);
-#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
if (!m_copyFuture.isValid() || !m_copyFuture.result()) {
-#else
- if (!m_copyFuture.result()) {
-#endif
emitFailed(tr("Failed to scan source path."));
return;
}
@@ -60,7 +53,7 @@ void DataMigrationTask::dryRunFinished()
setProgress(m_copy.totalCopied(), m_toCopy);
setStatus(tr("Copying %1…").arg(shortenedName));
});
- m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] {
+ m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] {
return m_copy(false); // actually copy now
});
connect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &DataMigrationTask::copyFinished);
@@ -70,7 +63,7 @@ void DataMigrationTask::dryRunFinished()
void DataMigrationTask::dryRunAborted()
{
- emitFailed(tr("Aborted"));
+ emitAborted();
}
void DataMigrationTask::copyFinished()
@@ -78,11 +71,7 @@ void DataMigrationTask::copyFinished()
disconnect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &DataMigrationTask::copyFinished);
disconnect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &DataMigrationTask::copyAborted);
-#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
if (!m_copyFuture.isValid() || !m_copyFuture.result()) {
-#else
- if (!m_copyFuture.result()) {
-#endif
emitFailed(tr("Some paths could not be copied!"));
return;
}
@@ -92,5 +81,5 @@ void DataMigrationTask::copyFinished()
void DataMigrationTask::copyAborted()
{
- emitFailed(tr("Aborted"));
+ emitAborted();
}
diff --git a/launcher/DataMigrationTask.h b/launcher/DataMigrationTask.h
index aba9f23996..9a2b0adb8a 100644
--- a/launcher/DataMigrationTask.h
+++ b/launcher/DataMigrationTask.h
@@ -5,7 +5,7 @@
#pragma once
#include "FileSystem.h"
-#include "pathmatcher/IPathMatcher.h"
+#include "Filter.h"
#include "tasks/Task.h"
#include
@@ -18,7 +18,7 @@
class DataMigrationTask : public Task {
Q_OBJECT
public:
- explicit DataMigrationTask(QObject* parent, const QString& sourcePath, const QString& targetPath, IPathMatcher::Ptr pathmatcher);
+ explicit DataMigrationTask(const QString& sourcePath, const QString& targetPath, Filter pathmatcher);
~DataMigrationTask() override = default;
protected:
@@ -33,7 +33,7 @@ class DataMigrationTask : public Task {
private:
const QString& m_sourcePath;
const QString& m_targetPath;
- const IPathMatcher::Ptr m_pathMatcher;
+ const Filter m_pathMatcher;
FS::copy m_copy;
int m_toCopy = 0;
diff --git a/launcher/DefaultVariable.h b/launcher/DefaultVariable.h
deleted file mode 100644
index b082091c74..0000000000
--- a/launcher/DefaultVariable.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#pragma once
-
-template
-class DefaultVariable {
- public:
- DefaultVariable(const T& value) { defaultValue = value; }
- DefaultVariable& operator=(const T& value)
- {
- currentValue = value;
- is_default = currentValue == defaultValue;
- is_explicit = true;
- return *this;
- }
- operator const T&() const { return is_default ? defaultValue : currentValue; }
- bool isDefault() const { return is_default; }
- bool isExplicit() const { return is_explicit; }
-
- private:
- T currentValue;
- T defaultValue;
- bool is_default = true;
- bool is_explicit = false;
-};
diff --git a/launcher/FastFileIconProvider.cpp b/launcher/FastFileIconProvider.cpp
index f2b6f44256..1dbab27ba6 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 2085340446..7799b78794 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 df06c3c752..445c2a881c 100644
--- a/launcher/FileIgnoreProxy.cpp
+++ b/launcher/FileIgnoreProxy.cpp
@@ -40,12 +40,11 @@
#include
#include
#include
-#include
#include "FileSystem.h"
#include "SeparatorPrefixTree.h"
#include "StringUtils.h"
-FileIgnoreProxy::FileIgnoreProxy(QString root, QObject* parent) : QSortFilterProxyModel(parent), root(root) {}
+FileIgnoreProxy::FileIgnoreProxy(QString root, QObject* parent) : QSortFilterProxyModel(parent), m_root(root) {}
// NOTE: Sadly, we have to do sorting ourselves.
bool FileIgnoreProxy::lessThan(const QModelIndex& left, const QModelIndex& right) const
{
@@ -104,10 +103,10 @@ QVariant FileIgnoreProxy::data(const QModelIndex& index, int role) const
if (index.column() == 0 && role == Qt::CheckStateRole) {
QFileSystemModel* fsm = qobject_cast(sourceModel());
auto blockedPath = relPath(fsm->filePath(sourceIndex));
- auto cover = blocked.cover(blockedPath);
+ auto cover = m_blocked.cover(blockedPath);
if (!cover.isNull()) {
return QVariant(Qt::Unchecked);
- } else if (blocked.exists(blockedPath)) {
+ } else if (m_blocked.exists(blockedPath)) {
return QVariant(Qt::PartiallyChecked);
} else {
return QVariant(Qt::Checked);
@@ -130,7 +129,7 @@ bool FileIgnoreProxy::setData(const QModelIndex& index, const QVariant& value, i
QString FileIgnoreProxy::relPath(const QString& path) const
{
- return QDir(root).relativeFilePath(path);
+ return QDir(m_root).relativeFilePath(path);
}
bool FileIgnoreProxy::setFilterState(QModelIndex index, Qt::CheckState state)
@@ -146,18 +145,18 @@ bool FileIgnoreProxy::setFilterState(QModelIndex index, Qt::CheckState state)
bool changed = false;
if (state == Qt::Unchecked) {
// blocking a path
- auto& node = blocked.insert(blockedPath);
+ auto& node = m_blocked.insert(blockedPath);
// get rid of all blocked nodes below
node.clear();
changed = true;
} else if (state == Qt::Checked || state == Qt::PartiallyChecked) {
- if (!blocked.remove(blockedPath)) {
- auto cover = blocked.cover(blockedPath);
+ if (!m_blocked.remove(blockedPath)) {
+ auto cover = m_blocked.cover(blockedPath);
qDebug() << "Blocked by cover" << cover;
// uncover
- blocked.remove(cover);
+ m_blocked.remove(cover);
// block all contents, except for any cover
- QModelIndex rootIndex = fsm->index(FS::PathCombine(root, cover));
+ QModelIndex rootIndex = fsm->index(FS::PathCombine(m_root, cover));
QModelIndex doing = rootIndex;
int row = 0;
QStack todo;
@@ -179,7 +178,7 @@ bool FileIgnoreProxy::setFilterState(QModelIndex index, Qt::CheckState state)
todo.push(node);
} else {
// or just block this one.
- blocked.insert(relpath);
+ m_blocked.insert(relpath);
}
row++;
}
@@ -229,7 +228,7 @@ bool FileIgnoreProxy::shouldExpand(QModelIndex index)
return false;
}
auto blockedPath = relPath(fsm->filePath(sourceIndex));
- auto found = blocked.find(blockedPath);
+ auto found = m_blocked.find(blockedPath);
if (found) {
return !found->leaf();
}
@@ -239,8 +238,8 @@ bool FileIgnoreProxy::shouldExpand(QModelIndex index)
void FileIgnoreProxy::setBlockedPaths(QStringList paths)
{
beginResetModel();
- blocked.clear();
- blocked.insert(paths);
+ m_blocked.clear();
+ m_blocked.insert(paths);
endResetModel();
}
@@ -267,10 +266,45 @@ 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 QString& fileName) const
+bool FileIgnoreProxy::filterFile(const QFileInfo& file) const
{
- return blocked.covers(fileName) || ignoreFile(QFileInfo(QDir(root), fileName));
+ return m_blocked.covers(relPath(file.absoluteFilePath())) || ignoreFile(file);
+}
+
+void FileIgnoreProxy::loadBlockedPathsFromFile(const QString& fileName)
+{
+ QFile ignoreFile(fileName);
+ if (!ignoreFile.open(QIODevice::ReadOnly)) {
+ return;
+ }
+ auto ignoreData = ignoreFile.readAll();
+ auto string = QString::fromUtf8(ignoreData);
+ setBlockedPaths(string.split('\n', Qt::SkipEmptyParts));
+}
+
+void FileIgnoreProxy::saveBlockedPathsToFile(const QString& fileName)
+{
+ auto ignoreData = blockedPaths().toStringList().join('\n').toUtf8();
+ try {
+ FS::write(fileName, ignoreData);
+ } catch (const Exception& e) {
+ qWarning() << e.cause();
+ }
}
diff --git a/launcher/FileIgnoreProxy.h b/launcher/FileIgnoreProxy.h
index e01a2651e7..0f149ecb67 100644
--- a/launcher/FileIgnoreProxy.h
+++ b/launcher/FileIgnoreProxy.h
@@ -61,15 +61,20 @@ class FileIgnoreProxy : public QSortFilterProxyModel {
void setBlockedPaths(QStringList paths);
- inline const SeparatorPrefixTree<'/'>& blockedPaths() const { return blocked; }
- inline SeparatorPrefixTree<'/'>& blockedPaths() { return blocked; }
+ inline const SeparatorPrefixTree<'/'>& blockedPaths() const { return m_blocked; }
+ inline SeparatorPrefixTree<'/'>& blockedPaths() { return m_blocked; }
// 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; }
- bool filterFile(const QString& fileName) const;
+ bool filterFile(const QFileInfo& fileName) const;
+
+ void loadBlockedPathsFromFile(const QString& fileName);
+
+ void saveBlockedPathsToFile(const QString& fileName);
protected:
bool filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const;
@@ -78,8 +83,9 @@ class FileIgnoreProxy : public QSortFilterProxyModel {
bool ignoreFile(QFileInfo file) const;
private:
- const QString root;
- SeparatorPrefixTree<'/'> blocked;
+ 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 70704e1d32..f53c9343e5 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -45,7 +45,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -54,15 +53,14 @@
#include
#include "DesktopServices.h"
+#include "PSaveFile.h"
#include "StringUtils.h"
#if defined Q_OS_WIN32
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
-#include
#include
#include
-#include
#include
#include
#include
@@ -77,24 +75,8 @@
#include
#endif
-// Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header
-
-#ifdef __APPLE__
-#include // for deployment target to support pre-catalina targets without std::fs
-#endif // __APPLE__
-
-#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
-#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
-#define GHC_USE_STD_FS
#include
namespace fs = std::filesystem;
-#endif // MacOS min version check
-#endif // Other OSes version check
-
-#ifndef GHC_USE_STD_FS
-#include
-namespace fs = ghc::filesystem;
-#endif
// clone
#if defined(Q_OS_LINUX)
@@ -123,6 +105,10 @@ namespace fs = ghc::filesystem;
#if defined(__MINGW32__)
+// Avoid re-defining structs retroactively added to MinGW
+// https://github.com/mingw-w64/mingw-w64/issues/90#issuecomment-2829284729
+#if __MINGW64_VERSION_MAJOR < 13
+
struct _DUPLICATE_EXTENTS_DATA {
HANDLE FileHandle;
LARGE_INTEGER SourceFileOffset;
@@ -132,6 +118,7 @@ struct _DUPLICATE_EXTENTS_DATA {
using DUPLICATE_EXTENTS_DATA = _DUPLICATE_EXTENTS_DATA;
using PDUPLICATE_EXTENTS_DATA = _DUPLICATE_EXTENTS_DATA*;
+#endif
struct _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER {
WORD ChecksumAlgorithm; // Checksum algorithm. e.g. CHECKSUM_TYPE_UNCHANGED, CHECKSUM_TYPE_NONE, CHECKSUM_TYPE_CRC32
@@ -191,8 +178,8 @@ void ensureExists(const QDir& dir)
void write(const QString& filename, const QByteArray& data)
{
ensureExists(QFileInfo(filename).dir());
- QSaveFile file(filename);
- if (!file.open(QSaveFile::WriteOnly)) {
+ PSaveFile file(filename);
+ if (!file.open(PSaveFile::WriteOnly)) {
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
}
if (data.size() != file.write(data)) {
@@ -213,8 +200,8 @@ void appendSafe(const QString& filename, const QByteArray& data)
buffer = QByteArray();
}
buffer.append(data);
- QSaveFile file(filename);
- if (!file.open(QSaveFile::WriteOnly)) {
+ PSaveFile file(filename);
+ if (!file.open(PSaveFile::WriteOnly)) {
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
}
if (buffer.size() != file.write(buffer)) {
@@ -276,6 +263,9 @@ bool ensureFolderPathExists(const QFileInfo folderPath)
{
QDir dir;
QString ensuredPath = folderPath.filePath();
+ if (folderPath.exists())
+ return true;
+
bool success = dir.mkpath(ensuredPath);
return success;
}
@@ -292,6 +282,9 @@ bool copyFileAttributes(QString src, QString dst)
if (attrs == INVALID_FILE_ATTRIBUTES)
return false;
return SetFileAttributesW(dst.toStdWString().c_str(), attrs);
+#else
+ Q_UNUSED(src);
+ Q_UNUSED(dst);
#endif
return true;
}
@@ -338,8 +331,8 @@ bool copy::operator()(const QString& offset, bool dryRun)
opt |= copy_opts::overwrite_existing;
// Function that'll do the actual copying
- auto copy_file = [&](QString src_path, QString relative_dst_path) {
- if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
+ auto copy_file = [this, dryRun, src, dst, opt, &err](QString src_path, QString relative_dst_path) {
+ if (m_matcher && (m_matcher(relative_dst_path) != m_whitelist))
return;
auto dst_path = PathCombine(dst, relative_dst_path);
@@ -425,8 +418,8 @@ void create_link::make_link_list(const QString& offset)
m_recursive = true;
// Function that'll do the actual linking
- auto link_file = [&](QString src_path, QString relative_dst_path) {
- if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist)) {
+ auto link_file = [this, dst](QString src_path, QString relative_dst_path) {
+ if (m_matcher && (m_matcher(relative_dst_path) != m_whitelist)) {
qDebug() << "path" << relative_dst_path << "in black list or not in whitelist";
return;
}
@@ -442,7 +435,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);
@@ -520,7 +513,7 @@ void create_link::runPrivileged(const QString& offset)
QString serverName = BuildConfig.LAUNCHER_APP_BINARY_NAME + "_filelink_server" + StringUtils::getRandomAlphaNumeric();
- connect(&m_linkServer, &QLocalServer::newConnection, this, [&]() {
+ connect(&m_linkServer, &QLocalServer::newConnection, this, [this, &gotResults]() {
qDebug() << "Client connected, sending out pairs";
// construct block of data to send
QByteArray block;
@@ -602,7 +595,7 @@ void create_link::runPrivileged(const QString& offset)
}
ExternalLinkFileProcess* linkFileProcess = new ExternalLinkFileProcess(serverName, m_useHardLinks, this);
- connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [&]() { emit finishedPrivileged(gotResults); });
+ connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [this, &gotResults]() { emit finishedPrivileged(gotResults); });
connect(linkFileProcess, &ExternalLinkFileProcess::finished, linkFileProcess, &QObject::deleteLater);
linkFileProcess->start();
@@ -647,6 +640,19 @@ void ExternalLinkFileProcess::runLinkFile()
qDebug() << "Process exited";
}
+bool moveByCopy(const QString& source, const QString& dest)
+{
+ if (!copy(source, dest)()) { // copy
+ qDebug() << "Copy of" << source << "to" << dest << "failed!";
+ return false;
+ }
+ if (!deletePath(source)) { // remove original
+ qDebug() << "Deletion of" << source << "failed!";
+ return false;
+ };
+ return true;
+}
+
bool move(const QString& source, const QString& dest)
{
std::error_code err;
@@ -654,13 +660,14 @@ bool move(const QString& source, const QString& dest)
ensureFilePathExists(dest);
fs::rename(StringUtils::toStdString(source), StringUtils::toStdString(dest), err);
- if (err) {
- qWarning() << "Failed to move file:" << QString::fromStdString(err.message());
- qDebug() << "Source file:" << source;
- qDebug() << "Destination file:" << dest;
+ if (err.value() != 0) {
+ if (moveByCopy(source, dest))
+ return true;
+ qDebug() << "Move of" << source << "to" << dest << "failed!";
+ qWarning() << "Failed to move file:" << QString::fromStdString(err.message()) << QString::number(err.value());
+ return false;
}
-
- return err.value() == 0;
+ return true;
}
bool deletePath(QString path)
@@ -678,9 +685,6 @@ bool deletePath(QString path)
bool trash(QString path, QString* pathInTrash)
{
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
- return false;
-#else
// FIXME: Figure out trash in Flatpak. Qt seemingly doesn't use the Trash portal
if (DesktopServices::isFlatpak())
return false;
@@ -689,7 +693,6 @@ bool trash(QString path, QString* pathInTrash)
return false;
#endif
return QFile::moveToTrash(path, pathInTrash);
-#endif
}
QString PathCombine(const QString& path1, const QString& path2)
@@ -723,11 +726,7 @@ int pathDepth(const QString& path)
QFileInfo info(path);
-#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
- auto parts = QDir::toNativeSeparators(info.path()).split(QDir::separator(), QString::SkipEmptyParts);
-#else
auto parts = QDir::toNativeSeparators(info.path()).split(QDir::separator(), Qt::SkipEmptyParts);
-#endif
int numParts = parts.length();
numParts -= parts.count(".");
@@ -747,11 +746,7 @@ QString pathTruncate(const QString& path, int depth)
return pathTruncate(trunc, depth);
}
-#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
- auto parts = QDir::toNativeSeparators(trunc).split(QDir::separator(), QString::SkipEmptyParts);
-#else
auto parts = QDir::toNativeSeparators(trunc).split(QDir::separator(), Qt::SkipEmptyParts);
-#endif
if (parts.startsWith(".") && !path.startsWith(".")) {
parts.removeFirst();
@@ -801,25 +796,68 @@ QString NormalizePath(QString path)
}
}
-static const QString BAD_PATH_CHARS = "\"?<>:;*|!+\r\n";
-static const QString BAD_FILENAME_CHARS = BAD_PATH_CHARS + "\\/";
+static const QString BAD_WIN_CHARS = "<>:\"|?*\r\n";
+static const QString BAD_NTFS_CHARS = "<>:\"|?*";
+static const QString BAD_HFS_CHARS = ":";
+
+static const QString BAD_FILENAME_CHARS = BAD_WIN_CHARS + "\\/";
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
{
for (int i = 0; i < string.length(); i++)
if (string.at(i) < ' ' || BAD_FILENAME_CHARS.contains(string.at(i)))
string[i] = replaceWith;
-
return string;
}
-QString RemoveInvalidPathChars(QString string, QChar replaceWith)
+QString RemoveInvalidPathChars(QString path, QChar replaceWith)
{
- for (int i = 0; i < string.length(); i++)
- if (string.at(i) < ' ' || BAD_PATH_CHARS.contains(string.at(i)))
- string[i] = replaceWith;
+ QString invalidChars;
+#ifdef Q_OS_WIN
+ invalidChars = BAD_WIN_CHARS;
+#endif
- return string;
+ // the null character is ignored in this check as it was not a problem until now
+ switch (statFS(path).fsType) {
+ case FilesystemType::FAT: // similar to NTFS
+ /* fallthrough */
+ case FilesystemType::NTFS:
+ /* fallthrough */
+ case FilesystemType::REFS: // similar to NTFS(should be available only on windows)
+ invalidChars += BAD_NTFS_CHARS;
+ break;
+ // case FilesystemType::EXT:
+ // case FilesystemType::EXT_2_OLD:
+ // case FilesystemType::EXT_2_3_4:
+ // case FilesystemType::XFS:
+ // case FilesystemType::BTRFS:
+ // case FilesystemType::NFS:
+ // case FilesystemType::ZFS:
+ case FilesystemType::APFS:
+ /* fallthrough */
+ case FilesystemType::HFS:
+ /* fallthrough */
+ case FilesystemType::HFSPLUS:
+ /* fallthrough */
+ case FilesystemType::HFSX:
+ invalidChars += BAD_HFS_CHARS;
+ break;
+ // case FilesystemType::FUSEBLK:
+ // case FilesystemType::F2FS:
+ // case FilesystemType::UNKNOWN:
+ default:
+ break;
+ }
+
+ if (invalidChars.size() != 0) {
+ for (int i = 0; i < path.length(); i++) {
+ if (path.at(i) < ' ' || invalidChars.contains(path.at(i))) {
+ path[i] = replaceWith;
+ }
+ }
+ }
+
+ return path;
}
QString DirNameFromString(QString string, QString inDir)
@@ -855,44 +893,70 @@ QString getDesktopDir()
return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
}
+QString getApplicationsDir()
+{
+ return QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation);
+}
+
+QString quoteArgs(const QStringList& args, const QString& wrap, const QString& escapeChar, bool wrapOnlyIfNeeded = false)
+{
+ QString result;
+
+ auto size = args.size();
+ for (int i = 0; i < size; ++i) {
+ QString arg = args[i];
+ arg.replace(wrap, escapeChar);
+
+ bool needsWrapping = !wrapOnlyIfNeeded || arg.contains(' ') || arg.contains('\t') || arg.contains(wrap);
+
+ if (needsWrapping)
+ result += wrap + arg + wrap;
+ else
+ result += arg;
+
+ if (i < size - 1)
+ result += ' ';
+ }
+
+ return result;
+}
+
// Cross-platform Shortcut creation
-bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon)
+QString createShortcut(QString destination, QString target, QStringList args, QString name, QString icon)
{
if (destination.isEmpty()) {
destination = PathCombine(getDesktopDir(), RemoveInvalidFilenameChars(name));
}
-#if defined(Q_OS_MACOS)
- // Create the Application
- QDir applicationDirectory =
- QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + "/" + BuildConfig.LAUNCHER_NAME + " Instances/";
-
- if (!applicationDirectory.mkpath(".")) {
- qWarning() << "Couldn't create application directory";
- return false;
+ if (!ensureFilePathExists(destination)) {
+ qWarning() << "Destination path can't be created!";
+ return QString();
}
-
- QDir application = applicationDirectory.path() + "/" + name + ".app/";
+#if defined(Q_OS_MACOS)
+ QDir application = destination + ".app/";
if (application.exists()) {
qWarning() << "Application already exists!";
- return false;
+ return QString();
}
if (!application.mkpath(".")) {
qWarning() << "Couldn't create application";
- return false;
+ return QString();
}
QDir content = application.path() + "/Contents/";
QDir resources = content.path() + "/Resources/";
QDir binaryDir = content.path() + "/MacOS/";
- QFile info = content.path() + "/Info.plist";
+ QFile info(content.path() + "/Info.plist");
if (!(content.mkpath(".") && resources.mkpath(".") && binaryDir.mkpath("."))) {
qWarning() << "Couldn't create directories within application";
- return false;
+ return QString();
+ }
+ if (!info.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qWarning() << "Failed to open file" << info.fileName() << "for writing:" << info.errorString();
+ return QString();
}
- info.open(QIODevice::WriteOnly | QIODevice::Text);
QFile(icon).rename(resources.path() + "/Icon.icns");
@@ -900,15 +964,15 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
QString exec = binaryDir.path() + "/Run.command";
QFile f(exec);
- f.open(QIODevice::WriteOnly | QIODevice::Text);
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qWarning() << "Failed to open file" << f.fileName() << "for writing:" << f.errorString();
+ return QString();
+ }
QTextStream stream(&f);
- QString argstring;
- if (!args.empty())
- argstring = " \"" + args.join("\" \"") + "\"";
+ auto argstring = quoteArgs(args, "\"", "\\\"");
- stream << "#!/bin/bash"
- << "\n";
+ stream << "#!/bin/bash" << "\n";
stream << "\"" << target << "\" " << argstring << "\n";
stream.flush();
@@ -940,25 +1004,23 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
"\n"
"";
- return true;
+ return application.path();
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
if (!destination.endsWith(".desktop")) // in case of isFlatpak destination is already populated
destination += ".desktop";
QFile f(destination);
- f.open(QIODevice::WriteOnly | QIODevice::Text);
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qWarning() << "Failed to open file" << f.fileName() << "for writing:" << f.errorString();
+ return QString();
+ }
QTextStream stream(&f);
- QString argstring;
- if (!args.empty())
- argstring = " '" + args.join("' '") + "'";
-
- stream << "[Desktop Entry]"
- << "\n";
- stream << "Type=Application"
- << "\n";
- stream << "Categories=Game;ActionGame;AdventureGame;Simulation"
- << "\n";
- stream << "Exec=\"" << target.toLocal8Bit() << "\"" << argstring.toLocal8Bit() << "\n";
+ auto argstring = quoteArgs(args, "'", "'\\''");
+
+ stream << "[Desktop Entry]" << "\n";
+ stream << "Type=Application" << "\n";
+ stream << "Categories=Game;ActionGame;AdventureGame;Simulation" << "\n";
+ stream << "Exec=\"" << target.toLocal8Bit() << "\" " << argstring.toLocal8Bit() << "\n";
stream << "Name=" << name.toLocal8Bit() << "\n";
if (!icon.isEmpty()) {
stream << "Icon=" << icon.toLocal8Bit() << "\n";
@@ -969,51 +1031,38 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther);
- return true;
+ return destination;
#elif defined(Q_OS_WIN)
QFileInfo targetInfo(target);
if (!targetInfo.exists()) {
qWarning() << "Target file does not exist!";
- return false;
+ return QString();
}
target = targetInfo.absoluteFilePath();
if (target.length() >= MAX_PATH) {
qWarning() << "Target file path is too long!";
- return false;
+ return QString();
}
if (!icon.isEmpty() && icon.length() >= MAX_PATH) {
qWarning() << "Icon path is too long!";
- return false;
+ return QString();
}
destination += ".lnk";
if (destination.length() >= MAX_PATH) {
qWarning() << "Destination path is too long!";
- return false;
- }
-
- QString argStr;
- int argCount = args.count();
- for (int i = 0; i < argCount; i++) {
- if (args[i].contains(' ')) {
- argStr.append('"').append(args[i]).append('"');
- } else {
- argStr.append(args[i]);
- }
-
- if (i < argCount - 1) {
- argStr.append(" ");
- }
+ return QString();
}
+ auto argStr = quoteArgs(args, "\"", "\\\"", true);
if (argStr.length() >= MAX_PATH) {
qWarning() << "Arguments string is too long!";
- return false;
+ return QString();
}
HRESULT hres;
@@ -1022,7 +1071,7 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
hres = CoInitialize(nullptr);
if (FAILED(hres)) {
qWarning() << "Failed to initialize COM!";
- return false;
+ return QString();
}
WCHAR wsz[MAX_PATH];
@@ -1060,26 +1109,28 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
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
CoUninitialize();
- return SUCCEEDED(hres);
+ if (SUCCEEDED(hres))
+ return destination;
+ return QString();
#else
qWarning("Desktop Shortcuts not supported on your platform!");
- return false;
+ return QString();
#endif
}
@@ -1235,8 +1286,8 @@ bool clone::operator()(const QString& offset, bool dryRun)
std::error_code err;
// Function that'll do the actual cloneing
- auto cloneFile = [&](QString src_path, QString relative_dst_path) {
- if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
+ auto cloneFile = [this, dryRun, dst, &err](QString src_path, QString relative_dst_path) {
+ if (m_matcher && (m_matcher(relative_dst_path) != m_whitelist))
return;
auto dst_path = PathCombine(dst, relative_dst_path);
@@ -1357,14 +1408,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;
}
@@ -1634,4 +1685,40 @@ QString getPathNameInLocal8bit(const QString& file)
}
#endif
+QString getUniqueResourceName(const QString& filePath)
+{
+ auto newFileName = filePath;
+ if (!newFileName.endsWith(".disabled")) {
+ return newFileName; // prioritize enabled mods
+ }
+ newFileName.chop(9);
+ if (!QFile::exists(newFileName)) {
+ return filePath;
+ }
+ QFileInfo fileInfo(filePath);
+ auto baseName = fileInfo.completeBaseName();
+ auto path = fileInfo.absolutePath();
+
+ int counter = 1;
+ do {
+ if (counter == 1) {
+ newFileName = FS::PathCombine(path, baseName + ".duplicate");
+ } else {
+ newFileName = FS::PathCombine(path, baseName + ".duplicate" + QString::number(counter));
+ }
+ counter++;
+ } while (QFile::exists(newFileName));
+
+ return newFileName;
+}
+bool removeFiles(QStringList listFile)
+{
+ bool ret = true;
+ // For each file
+ for (int i = 0; i < listFile.count(); i++) {
+ // Remove
+ ret = ret && QFile::remove(listFile.at(i));
+ }
+ return ret;
+}
} // namespace FS
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index 5496c3795c..f2676b1471 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -38,7 +38,7 @@
#pragma once
#include "Exception.h"
-#include "pathmatcher/IPathMatcher.h"
+#include "Filter.h"
#include
@@ -72,7 +72,7 @@ void appendSafe(const QString& filename, const QByteArray& data);
void append(const QString& filename, const QByteArray& data);
/**
- * read data from a file safely\
+ * read data from a file safely
*/
QByteArray read(const QString& filename);
@@ -115,9 +115,9 @@ class copy : public QObject {
m_followSymlinks = follow;
return *this;
}
- copy& matcher(const IPathMatcher* filter)
+ copy& matcher(Filter filter)
{
- m_matcher = filter;
+ m_matcher = std::move(filter);
return *this;
}
copy& whitelist(bool whitelist)
@@ -147,7 +147,7 @@ class copy : public QObject {
private:
bool m_followSymlinks = true;
- const IPathMatcher* m_matcher = nullptr;
+ Filter m_matcher = nullptr;
bool m_whitelist = false;
bool m_overwrite = false;
QDir m_src;
@@ -209,9 +209,9 @@ class create_link : public QObject {
m_useHardLinks = useHard;
return *this;
}
- create_link& matcher(const IPathMatcher* filter)
+ create_link& matcher(Filter filter)
{
- m_matcher = filter;
+ m_matcher = std::move(filter);
return *this;
}
create_link& whitelist(bool whitelist)
@@ -240,6 +240,7 @@ class create_link : public QObject {
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
int totalLinked() { return m_linked; }
+ int totalToLink() { return static_cast(m_links_to_make.size()); }
void runPrivileged() { runPrivileged(QString()); }
void runPrivileged(const QString& offset);
@@ -259,7 +260,7 @@ class create_link : public QObject {
private:
bool m_useHardLinks = false;
- const IPathMatcher* m_matcher = nullptr;
+ Filter m_matcher = nullptr;
bool m_whitelist = false;
bool m_recursive = true;
@@ -290,6 +291,8 @@ bool move(const QString& source, const QString& dest);
*/
bool deletePath(QString path);
+bool removeFiles(QStringList listFile);
+
/**
* Trash a folder / file
*/
@@ -352,14 +355,18 @@ bool checkProblemticPathJava(QDir folder);
// Get the Directory representing the User's Desktop
QString getDesktopDir();
+// Get the Directory representing the User's Applications directory
+QString getApplicationsDir();
+
// Overrides one folder with the contents of another, preserving items exclusive to the first folder
// Equivalent to doing QDir::rename, but allowing for overrides
bool overrideFolder(QString overwritten_path, QString override_path);
/**
* Creates a shortcut to the specified target file at the specified destination path.
+ * Returns null QString if creation failed; otherwise returns the path to the created shortcut.
*/
-bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon);
+QString createShortcut(QString destination, QString target, QStringList args, QString name, QString icon);
enum class FilesystemType {
FAT,
@@ -378,6 +385,7 @@ enum class FilesystemType {
HFSX,
FUSEBLK,
F2FS,
+ BCACHEFS,
UNKNOWN
};
@@ -406,6 +414,7 @@ static const QMap s_filesystem_type_names = { { Fil
{ FilesystemType::HFSX, { "HFSX" } },
{ FilesystemType::FUSEBLK, { "FUSEBLK" } },
{ FilesystemType::F2FS, { "F2FS" } },
+ { FilesystemType::BCACHEFS, { "BCACHEFS" } },
{ FilesystemType::UNKNOWN, { "UNKNOWN" } } };
/**
@@ -458,7 +467,7 @@ QString nearestExistentAncestor(const QString& path);
FilesystemInfo statFS(const QString& path);
static const QList s_clone_filesystems = { FilesystemType::BTRFS, FilesystemType::APFS, FilesystemType::ZFS,
- FilesystemType::XFS, FilesystemType::REFS };
+ FilesystemType::XFS, FilesystemType::REFS, FilesystemType::BCACHEFS };
/**
* @brief if the Filesystem is reflink/clone capable
@@ -485,9 +494,9 @@ class clone : public QObject {
m_src.setPath(src);
m_dst.setPath(dst);
}
- clone& matcher(const IPathMatcher* filter)
+ clone& matcher(Filter filter)
{
- m_matcher = filter;
+ m_matcher = std::move(filter);
return *this;
}
clone& whitelist(bool whitelist)
@@ -511,7 +520,7 @@ class clone : public QObject {
bool operator()(const QString& offset, bool dryRun = false);
private:
- const IPathMatcher* m_matcher = nullptr;
+ Filter m_matcher = nullptr;
bool m_whitelist = false;
QDir m_src;
QDir m_dst;
@@ -557,4 +566,6 @@ uintmax_t hardLinkCount(const QString& path);
QString getPathNameInLocal8bit(const QString& file);
#endif
+QString getUniqueResourceName(const QString& filePath);
+
} // namespace FS
diff --git a/launcher/Filter.cpp b/launcher/Filter.cpp
deleted file mode 100644
index fc1c423447..0000000000
--- a/launcher/Filter.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#include "Filter.h"
-
-Filter::~Filter() {}
-
-ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern) {}
-ContainsFilter::~ContainsFilter() {}
-bool ContainsFilter::accepts(const QString& value)
-{
- return value.contains(pattern);
-}
-
-ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern) {}
-ExactFilter::~ExactFilter() {}
-bool ExactFilter::accepts(const QString& value)
-{
- return value == pattern;
-}
-
-ExactIfPresentFilter::ExactIfPresentFilter(const QString& pattern) : pattern(pattern) {}
-bool ExactIfPresentFilter::accepts(const QString& value)
-{
- return value.isEmpty() || value == pattern;
-}
-
-RegexpFilter::RegexpFilter(const QString& regexp, bool invert) : invert(invert)
-{
- pattern.setPattern(regexp);
- pattern.optimize();
-}
-RegexpFilter::~RegexpFilter() {}
-bool RegexpFilter::accepts(const QString& value)
-{
- auto match = pattern.match(value);
- bool matched = match.hasMatch();
- return invert ? (!matched) : (matched);
-}
diff --git a/launcher/Filter.h b/launcher/Filter.h
index 089c844d4c..317f5b067d 100644
--- a/launcher/Filter.h
+++ b/launcher/Filter.h
@@ -3,49 +3,52 @@
#include
#include
-class Filter {
- public:
- virtual ~Filter();
- virtual bool accepts(const QString& value) = 0;
-};
-
-class ContainsFilter : public Filter {
- public:
- ContainsFilter(const QString& pattern);
- virtual ~ContainsFilter();
- bool accepts(const QString& value) override;
-
- private:
- QString pattern;
-};
-
-class ExactFilter : public Filter {
- public:
- ExactFilter(const QString& pattern);
- virtual ~ExactFilter();
- bool accepts(const QString& value) override;
-
- private:
- QString pattern;
-};
-
-class ExactIfPresentFilter : public Filter {
- public:
- ExactIfPresentFilter(const QString& pattern);
- ~ExactIfPresentFilter() override = default;
- bool accepts(const QString& value) override;
-
- private:
- QString pattern;
-};
-
-class RegexpFilter : public Filter {
- public:
- RegexpFilter(const QString& regexp, bool invert);
- virtual ~RegexpFilter();
- bool accepts(const QString& value) override;
-
- private:
- QRegularExpression pattern;
- bool invert = false;
-};
+using Filter = std::function;
+
+namespace Filters {
+inline Filter inverse(Filter filter)
+{
+ return [filter = std::move(filter)](const QString& src) { return !filter(src); };
+}
+
+inline Filter any(QList filters)
+{
+ return [filters = std::move(filters)](const QString& src) {
+ for (auto& filter : filters)
+ if (filter(src))
+ return true;
+
+ return false;
+ };
+}
+
+inline Filter equals(QString pattern)
+{
+ return [pattern = std::move(pattern)](const QString& src) { return src == pattern; };
+}
+
+inline Filter equalsAny(QStringList patterns = {})
+{
+ return [patterns = std::move(patterns)](const QString& src) { return patterns.isEmpty() || patterns.contains(src); };
+}
+
+inline Filter equalsOrEmpty(QString pattern)
+{
+ return [pattern = std::move(pattern)](const QString& src) { return src.isEmpty() || src == pattern; };
+}
+
+inline Filter contains(QString pattern)
+{
+ return [pattern = std::move(pattern)](const QString& src) { return src.contains(pattern); };
+}
+
+inline Filter startsWith(QString pattern)
+{
+ return [pattern = std::move(pattern)](const QString& src) { return src.startsWith(pattern); };
+}
+
+inline Filter regexp(QRegularExpression pattern)
+{
+ return [pattern = std::move(pattern)](const QString& src) { return pattern.match(src).hasMatch(); };
+}
+} // namespace Filters
diff --git a/launcher/GZip.cpp b/launcher/GZip.cpp
index 1c2539e085..201dcd572b 100644
--- a/launcher/GZip.cpp
+++ b/launcher/GZip.cpp
@@ -36,6 +36,8 @@
#include "GZip.h"
#include
#include
+#include
+#include
bool GZip::unzip(const QByteArray& compressedBytes, QByteArray& uncompressedBytes)
{
@@ -136,3 +138,82 @@ bool GZip::zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes)
}
return true;
}
+
+int inf(QFile* source, std::function handleBlock)
+{
+ constexpr auto CHUNK = 16384;
+ int ret;
+ unsigned have;
+ z_stream strm;
+ memset(&strm, 0, sizeof(strm));
+ char in[CHUNK];
+ unsigned char out[CHUNK];
+
+ ret = inflateInit2(&strm, (16 + MAX_WBITS));
+ if (ret != Z_OK)
+ return ret;
+
+ /* decompress until deflate stream ends or end of file */
+ do {
+ strm.avail_in = source->read(in, CHUNK);
+ if (source->error()) {
+ (void)inflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ if (strm.avail_in == 0)
+ break;
+ strm.next_in = reinterpret_cast(in);
+
+ /* run inflate() on input until output buffer not full */
+ do {
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ ret = inflate(&strm, Z_NO_FLUSH);
+ assert(ret != Z_STREAM_ERROR); /* state not clobbered */
+ switch (ret) {
+ case Z_NEED_DICT:
+ ret = Z_DATA_ERROR;
+ [[fallthrough]];
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ (void)inflateEnd(&strm);
+ return ret;
+ }
+ have = CHUNK - strm.avail_out;
+ if (!handleBlock(QByteArray(reinterpret_cast(out), have))) {
+ (void)inflateEnd(&strm);
+ return Z_OK;
+ }
+
+ } while (strm.avail_out == 0);
+
+ /* done when inflate() says it's done */
+ } while (ret != Z_STREAM_END);
+
+ /* clean up and return */
+ (void)inflateEnd(&strm);
+ return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
+}
+
+QString zerr(int ret)
+{
+ switch (ret) {
+ case Z_ERRNO:
+ return QObject::tr("error handling file");
+ case Z_STREAM_ERROR:
+ return QObject::tr("invalid compression level");
+ case Z_DATA_ERROR:
+ return QObject::tr("invalid or incomplete deflate data");
+ case Z_MEM_ERROR:
+ return QObject::tr("out of memory");
+ case Z_VERSION_ERROR:
+ return QObject::tr("zlib version mismatch!");
+ }
+ return {};
+}
+
+QString GZip::readGzFileByBlocks(QFile* source, std::function