Compare commits

..

2 Commits
develop ... 9.0

Author SHA1 Message Date
Alexandru Ionut Tripon
e8c9722a53
Merge pull request #2939 from PrismLauncher/backport-2938-to-release-9.x
[Backport release-9.x] fix sparkle signature
2024-10-21 00:08:40 +03:00
Trial97
9a810dfa6e fix sparkle signature
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 0586d38e03de6890bf5697c7e1a965aac9bd36fb)
2024-10-20 21:07:55 +00:00
722 changed files with 10112 additions and 20175 deletions

View File

@ -1,23 +1,5 @@
Checks:
- modernize-use-using
- readability-avoid-const-params-in-decls
- misc-unused-parameters,
- readability-identifier-naming
# ^ Without unused-parameters the readability-identifier-naming check doesn't cause any warnings.
CheckOptions:
- { key: readability-identifier-naming.ClassCase, value: PascalCase }
- { key: readability-identifier-naming.EnumCase, value: PascalCase }
- { key: readability-identifier-naming.FunctionCase, value: camelCase }
- { key: readability-identifier-naming.GlobalVariableCase, value: camelCase }
- { key: readability-identifier-naming.GlobalFunctionCase, value: camelCase }
- { key: readability-identifier-naming.GlobalConstantCase, value: SCREAMING_SNAKE_CASE }
- { key: readability-identifier-naming.MacroDefinitionCase, value: SCREAMING_SNAKE_CASE }
- { key: readability-identifier-naming.ClassMemberCase, value: camelCase }
- { key: readability-identifier-naming.PrivateMemberPrefix, value: m_ }
- { key: readability-identifier-naming.ProtectedMemberPrefix, value: m_ }
- { key: readability-identifier-naming.PrivateStaticMemberPrefix, value: s_ }
- { key: readability-identifier-naming.ProtectedStaticMemberPrefix, value: s_ }
- { key: readability-identifier-naming.PublicStaticConstantCase, value: SCREAMING_SNAKE_CASE }
- { key: readability-identifier-naming.EnumConstantCase, value: SCREAMING_SNAKE_CASE }
SystemHeaders: false

2
.envrc
View File

@ -1,2 +1,2 @@
use nix
use flake
watch_file nix/*.nix

View File

@ -5,9 +5,3 @@ bbb3b3e6f6e3c0f95873f22e6d0a4aaf350f49d9
# (nix) alejandra -> nixfmt
4c81d8c53d09196426568c4a31a4e752ed05397a
# reformat codebase
1d468ac35ad88d8c77cc83f25e3704d9bd7df01b
# format a part of codebase
5c8481a118c8fefbfe901001d7828eaf6866eac4

View File

@ -1,103 +0,0 @@
# This file incorporates work covered by the following copyright and
# permission notice
#
# Copyright (c) 2003-2025 Eelco Dolstra and the Nixpkgs/NixOS contributors
#
# 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.
name: Get merge commit
description: Get a merge commit of a given pull request
inputs:
repository:
description: Repository containing the pull request
required: false
pull-request-id:
description: ID of a pull request
required: true
outputs:
merge-commit-sha:
description: Git SHA of a merge commit
value: ${{ steps.query.outputs.merge-commit-sha }}
runs:
using: composite
steps:
- name: Wait for GitHub to report merge commit
id: query
shell: bash
env:
GITHUB_REPO: ${{ inputs.repository || github.repository }}
PR_ID: ${{ inputs.pull-request-id }}
# https://github.com/NixOS/nixpkgs/blob/8f77f3600f1ee775b85dc2c72fd842768e486ec9/ci/get-merge-commit.sh
run: |
set -euo pipefail
log() {
echo "$@" >&2
}
# Retry the API query this many times
retryCount=5
# Start with 5 seconds, but double every retry
retryInterval=5
while true; do
log "Checking whether the pull request can be merged"
prInfo=$(gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/$GITHUB_REPO/pulls/$PR_ID")
# Non-open PRs won't have their mergeability computed no matter what
state=$(jq -r .state <<<"$prInfo")
if [[ "$state" != open ]]; then
log "PR is not open anymore"
exit 1
fi
mergeable=$(jq -r .mergeable <<<"$prInfo")
if [[ "$mergeable" == "null" ]]; then
if ((retryCount == 0)); then
log "Not retrying anymore. It's likely that GitHub is having internal issues: check https://www.githubstatus.com/"
exit 3
else
((retryCount -= 1)) || true
# null indicates that GitHub is still computing whether it's mergeable
# Wait a couple seconds before trying again
log "GitHub is still computing whether this PR can be merged, waiting $retryInterval seconds before trying again ($retryCount retries left)"
sleep "$retryInterval"
((retryInterval *= 2)) || true
fi
else
break
fi
done
if [[ "$mergeable" == "true" ]]; then
echo "merge-commit-sha=$(jq -r .merge_commit_sha <<<"$prInfo")" >> "$GITHUB_OUTPUT"
else
echo "# 🚨 The PR has a merge conflict!" >> "$GITHUB_STEP_SUMMARY"
exit 2
fi

View File

@ -1,124 +0,0 @@
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
cmake-preset:
description: Base CMake preset previously used for the build
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: Package AppImage
shell: bash
env:
VERSION: ${{ inputs.version }}
BUILD_DIR: build
INSTALL_APPIMAGE_DIR: install-appdir
GPG_PRIVATE_KEY: ${{ inputs.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/${{ inputs.qt-version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
cp /usr/lib/x86_64-linux-gnu/libssl.so.* ${{ 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 [ '${{ inputs.gpg-private-key-id }}' != '' ]; then
export SIGN=1
export SIGN_KEY=${{ inputs.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 portable tarball
shell: bash
env:
BUILD_DIR: build
CMAKE_PRESET: ${{ inputs.cmake-preset }}
INSTALL_PORTABLE_DIR: install-portable
run: |
cmake --preset "$CMAKE_PRESET" -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_PORTABLE_DIR }} -DINSTALL_BUNDLE=full
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.* ${{ env.INSTALL_PORTABLE_DIR }}/lib
cp /usr/lib/x86_64-linux-gnu/libssl.so.* ${{ env.INSTALL_PORTABLE_DIR }}/lib
cp /usr/lib/x86_64-linux-gnu/libffi.so.*.* ${{ 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 *
- name: Upload binary tarball
uses: actions/upload-artifact@v4
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@v4
with:
name: PrismLauncher-${{ inputs.artifact-name }}-${{ inputs.version }}-${{ inputs.build-type }}-x86_64.AppImage
path: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-x86_64.AppImage
- name: Upload AppImage Zsync
uses: actions/upload-artifact@v4
with:
name: PrismLauncher-${{ inputs.artifact-name }}-${{ inputs.version }}-${{ inputs.build-type }}-x86_64.AppImage.zsync
path: PrismLauncher-Linux-x86_64.AppImage.zsync

View File

@ -1,121 +0,0 @@
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 }}
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: Make Sparkle signature
shell: bash
run: |
if [ '${{ inputs.sparkle-ed25519-key }}' != '' ]; then
echo '${{ inputs.sparkle-ed25519-key }}' > ed25519-priv.pem
signature=$(/opt/homebrew/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: Upload binary tarball
uses: actions/upload-artifact@v4
with:
name: PrismLauncher-${{ inputs.artifact-name }}-${{ inputs.version }}-${{ inputs.build-type }}
path: PrismLauncher.zip

View File

@ -1,143 +0,0 @@
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: true
default: false
windows-codesign-cert:
description: Certificate for signing Windows builds
required: false
windows-codesign-password:
description: Password for signing Windows builds
required: false
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 }}
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: Fetch codesign certificate
shell: bash # yes, we are not using MSYS2 or PowerShell here
run: |
echo '${{ inputs.windows-codesign-cert }}' | base64 --decode > codesign.pfx
- name: Sign executable
shell: pwsh
env:
INSTALL_DIR: install
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 '${{ inputs.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 (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 }} --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 }} --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
shell: pwsh
run: |
if (Get-Content ./codesign.pfx){
SignTool sign /fd sha256 /td sha256 /f codesign.pfx /p '${{ inputs.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: Upload binary zip
uses: actions/upload-artifact@v4
with:
name: PrismLauncher-${{ inputs.artifact-name }}-${{ inputs.version }}-${{ inputs.build-type }}
path: install/**
- name: Upload portable zip
uses: actions/upload-artifact@v4
with:
name: PrismLauncher-${{ inputs.artifact-name }}-Portable-${{ inputs.version }}-${{ inputs.build-type }}
path: install-portable/**
- name: Upload installer
uses: actions/upload-artifact@v4
with:
name: PrismLauncher-${{ inputs.artifact-name }}-Setup-${{ inputs.version }}-${{ inputs.build-type }}
path: PrismLauncher-Setup.exe

View File

@ -1,78 +0,0 @@
name: Setup 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: false
qt-architecture:
description: Qt architecture
required: false
qt-version:
description: Version of Qt to use
required: true
default: 6.8.1
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
- 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.18
with:
variant: ${{ runner.os == 'Windows' && 'sccache' || 'ccache' }}
create-symlink: ${{ runner.os != 'Windows' }}
key: ${{ runner.os }}-qt${{ inputs.qt_ver }}-${{ inputs.architecture }}
- name: Use ccache on debug builds
if: ${{ inputs.build-type == 'Debug' }}
shell: bash
env:
# Only use sccache on MSVC
CCACHE_VARIANT: ${{ (runner.os == 'Windows' && inputs.msystem == '') && 'sccache' || 'ccache' }}
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 }}
arch: ${{ inputs.qt-architecture }}
modules: qt5compat qtimageformats qtnetworkauth
cache: ${{ inputs.build-type == 'Debug' }}

View File

@ -1,26 +0,0 @@
name: Setup Linux dependencies
runs:
using: composite
steps:
- name: Install host dependencies
shell: bash
run: |
sudo apt-get -y update
sudo apt-get -y install ninja-build extra-cmake-modules scdoc appstream libxcb-cursor-dev
- name: Setup AppImage tooling
shell: bash
run: |
declare -A appimage_deps
appimage_deps["https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20250213-2/linuxdeploy-x86_64.AppImage"]="4648f278ab3ef31f819e67c30d50f462640e5365a77637d7e6f2ad9fd0b4522a linuxdeploy-x86_64.AppImage"
appimage_deps["https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/1-alpha-20250213-1/linuxdeploy-plugin-qt-x86_64.AppImage"]="15106be885c1c48a021198e7e1e9a48ce9d02a86dd0a1848f00bdbf3c1c92724 linuxdeploy-plugin-qt-x86_64.AppImage"
appimage_deps["https://github.com/AppImageCommunity/AppImageUpdate/releases/download/2.0.0-alpha-1-20241225/AppImageUpdate-x86_64.AppImage"]="f1747cf60058e99f1bb9099ee9787d16c10241313b7acec81810ea1b1e568c11 AppImageUpdate-x86_64.AppImage"
for url in "${!appimage_deps[@]}"; do
curl -LO "$url"
sha256sum -c - <<< "${appimage_deps[$url]}"
done
sudo apt -y install libopengl0

View File

@ -1,16 +0,0 @@
name: Setup macOS dependencies
runs:
using: composite
steps:
- name: Install dependencies
shell: bash
run: |
brew update
brew install ninja extra-cmake-modules temurin@17
- name: Set JAVA_HOME
shell: bash
run: |
echo "JAVA_HOME=$(/usr/libexec/java_home -v 17)" >> "$GITHUB_ENV"

View File

@ -1,66 +0,0 @@
name: Setup Windows Dependencies
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 MSYS2 (MinGW-64)
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-5compat:p
qt6-networkauth:p
cmark:p
quazip-qt6:p
- name: Retrieve ccache cache (MinGW)
if: ${{ inputs.msystem != '' && inputs.build-type == 'Debug' }}
uses: actions/cache@v4.2.3
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

View File

@ -25,7 +25,7 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Create backport PRs
uses: korthout/backport-action@v3.2.0
uses: korthout/backport-action@v3.1.0
with:
# Config README: https://github.com/korthout/backport-action#backport-action
pull_description: |-

View File

@ -1,239 +0,0 @@
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
jobs:
blocked_status:
name: Check Blocked Status
runs-on: ubuntu-latest
steps:
- name: Generate token
id: generate-token
uses: actions/create-github-app-token@v2
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("blocked (?:by|on):? #([0-9]+)")
| map({
"type": "Blocked on",
"number": ( . | tonumber )
})
) as $i ([]; . + [$i[]])
) as $bprs
| (
$body |
reduce (
. | scan("stacked on:? #([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,
"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(.[].merged; .)' <<< "$blocked_pr_data")";
echo "current_blocking=$(jq -c 'map( select( .merged | not ) | .number )' <<< "$blocked_pr_data" )";
} >> "$GITHUB_OUTPUT"
- name: Add 'blocked' Label is Missing
id: label_blocked
if: (fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0) && !contains(fromJSON(env.JOB_DATA).prLabels, '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 '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 '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 '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 ' "Blocking PR #" + (.number | tostring) + " is " + (if .merged then "" else "not yet " end) + "merged"' <<< "$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 .merged 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 '<h3>PR Dependencies :pushpin:</h3>' > "$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 .merged then ":heavy_check_mark: Merged" else ":x: Not Merged" 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<<EOF';
cat "${COMMENT_PATH}";
echo 'EOF';
} >> "$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

View File

@ -1,195 +1,699 @@
name: Build
on:
push:
branches-ignore:
- "renovate/**"
paths:
# File types
- "**.cpp"
- "**.h"
- "**.java"
# Directories
- "buildconfig/"
- "cmake/"
- "launcher/"
- "libraries/"
- "program_info/"
- "tests/"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/workflows/build.yml"
pull_request:
paths:
# File types
- "**.cpp"
- "**.h"
# Directories
- "buildconfig/"
- "cmake/"
- "launcher/"
- "libraries/"
- "program_info/"
- "tests/"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/workflows/build.yml"
workflow_call:
inputs:
build-type:
description: Type of build (Debug or Release)
build_type:
description: Type of build (Debug, Release, RelWithDebInfo, MinSizeRel)
type: string
default: Debug
workflow_dispatch:
inputs:
build-type:
description: Type of build (Debug or Release)
is_qt_cached:
description: Enable Qt caching or not
type: string
default: Debug
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
CACHIX_AUTH_TOKEN:
description: Private token for authenticating against Cachix cache
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
jobs:
build:
name: Build (${{ matrix.artifact-name }})
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: "qtnetworkauth"
- os: ubuntu-20.04
qt_ver: 6
qt_host: linux
qt_arch: ""
qt_version: "6.2.4"
qt_modules: "qt5compat qtimageformats qtnetworkauth"
- os: windows-2022
name: "Windows-MinGW-w64"
msystem: clang64
vcvars_arch: "amd64_x86"
- os: windows-2022
name: "Windows-MSVC"
msystem: ""
architecture: "x64"
vcvars_arch: "amd64"
qt_ver: 6
qt_host: windows
qt_arch: ""
qt_version: "6.7.3"
qt_modules: "qt5compat qtimageformats qtnetworkauth"
nscurl_tag: "v24.9.26.122"
nscurl_sha256: "AEE6C4BE3CB6455858E9C1EE4B3AFE0DB9960FA03FE99CCDEDC28390D57CCBB0"
- 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.3"
qt_modules: "qt5compat qtimageformats qtnetworkauth"
nscurl_tag: "v24.9.26.122"
nscurl_sha256: "AEE6C4BE3CB6455858E9C1EE4B3AFE0DB9960FA03FE99CCDEDC28390D57CCBB0"
- os: macos-14
name: macOS
macosx_deployment_target: 11.0
qt_ver: 6
qt_host: mac
qt_arch: ""
qt_version: "6.7.3"
qt_modules: "qt5compat qtimageformats qtnetworkauth"
- os: macos-14
name: macOS-Legacy
macosx_deployment_target: 10.13
qt_ver: 5
qt_host: mac
qt_version: "5.15.2"
qt_modules: "qtnetworkauth"
runs-on: ${{ matrix.os }}
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
steps:
##
# PREPARE
##
- name: Checkout
uses: actions/checkout@v4
with:
submodules: "true"
- name: "Setup MSYS2"
if: runner.os == 'Windows' && matrix.msystem != ''
uses: msys2/setup-msys2@v2
with:
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
qt6-networkauth: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.14
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.1.1
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
- name: Setup java (macOS)
if: runner.os == 'macOS'
uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: "17"
##
# 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_ENABLE_JAVA_DOWNLOADER=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_ENABLE_JAVA_DOWNLOADER=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="" -DCMAKE_OSX_ARCHITECTURES="x86_64" -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_ENABLE_JAVA_DOWNLOADER=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_ENABLE_JAVA_DOWNLOADER=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_ENABLE_JAVA_DOWNLOADER=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
##
# BUILD
##
- name: Build
if: runner.os != 'Windows'
run: |
cmake --build ${{ env.BUILD_DIR }}
- name: Build (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != ''
shell: msys2 {0}
run: |
cmake --build ${{ env.BUILD_DIR }}
- name: Build (Windows MSVC)
if: runner.os == 'Windows' && matrix.msystem == ''
run: |
cmake --build ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }}
##
# TEST
##
- 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
echo '${{ secrets.SPARKLE_ED25519_KEY }}' > ed25519-priv.pem
signature=$(/opt/homebrew/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: |
if ('${{ matrix.nscurl_tag }}') {
New-Item -Name NSISPlugins -ItemType Directory
Invoke-Webrequest https://github.com/negrutiu/nsis-nscurl/releases/download/${{ matrix.nscurl_tag }}/NScurl.zip -OutFile NSISPlugins\NScurl.zip
$nscurl_hash = Get-FileHash NSISPlugins\NScurl.zip -Algorithm Sha256 | Select-Object -ExpandProperty Hash
if ( $nscurl_hash -ne "${{ matrix.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 (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
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 *
##
# 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
with:
name: PrismLauncher-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: ${{ env.INSTALL_PORTABLE_DIR }}/**
- 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
- 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
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
flatpak:
runs-on: ubuntu-latest
container:
image: bilelmoussaoui/flatpak-github-actions:kde-6.7
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
nix:
name: Nix (${{ matrix.system }})
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
artifact-name: Linux
base-cmake-preset: linux
system: x86_64-linux
- os: windows-2022
artifact-name: Windows-MinGW-w64
base-cmake-preset: windows_mingw
msystem: CLANG64
vcvars-arch: amd64_x86
- os: windows-11-arm
artifact-name: Windows-MinGW-arm64
base-cmake-preset: windows_mingw
msystem: CLANGARM64
vcvars-arch: arm64
- os: windows-2022
artifact-name: Windows-MSVC
base-cmake-preset: windows_msvc
# TODO(@getchoo): This is the default in setup-dependencies/windows. Why isn't it working?!?!
vcvars-arch: amd64
- os: windows-2022
artifact-name: Windows-MSVC-arm64
base-cmake-preset: windows_msvc_arm64_cross
vcvars-arch: amd64_arm64
qt-architecture: win64_msvc2022_arm64_cross_compiled
- os: macos-13
system: x86_64-darwin
- os: macos-14
artifact-name: macOS
base-cmake-preset: ${{ (inputs.build-type || 'Debug') == 'Debug' && 'macos_universal' || 'macos' }}
macosx-deployment-target: 11.0
system: aarch64-darwin
runs-on: ${{ matrix.os }}
defaults:
run:
shell: ${{ matrix.msystem != '' && 'msys2 {0}' || 'bash' }}
env:
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx-deployment-target }}
steps:
##
# SETUP
##
- name: Checkout
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v30
# For PRs
- name: Setup Nix Magic Cache
uses: DeterminateSystems/magic-nix-cache-action@v8
# For in-tree builds
- name: Setup Cachix
uses: cachix/cachix-action@v15
with:
submodules: true
name: prismlauncher
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Setup dependencies
id: setup-dependencies
uses: ./.github/actions/setup-dependencies
with:
build-type: ${{ inputs.build-type || 'Debug' }}
msystem: ${{ matrix.msystem }}
vcvars-arch: ${{ matrix.vcvars-arch }}
qt-architecture: ${{ matrix.qt-architecture }}
##
# BUILD
##
- name: Get CMake preset
id: cmake-preset
env:
BASE_CMAKE_PRESET: ${{ matrix.base-cmake-preset }}
PRESET_TYPE: ${{ (inputs.build-type || 'Debug') == 'Debug' && 'debug' || 'ci' }}
- name: Run flake checks
run: |
echo preset="$BASE_CMAKE_PRESET"_"$PRESET_TYPE" >> "$GITHUB_OUTPUT"
nix flake check --print-build-logs --show-trace
- name: Run CMake workflow
env:
CMAKE_PRESET: ${{ steps.cmake-preset.outputs.preset }}
- name: Build debug package
if: ${{ inputs.build_type == 'Debug' }}
run: |
cmake --workflow --preset "$CMAKE_PRESET"
nix build --print-build-logs .#prismlauncher-debug
##
# PACKAGE
##
- name: Get short version
id: short-version
shell: bash
- name: Build release package
if: ${{ inputs.build_type != 'Debug' }}
run: |
echo "version=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
- name: Package (Linux)
if: ${{ runner.os == 'Linux' }}
uses: ./.github/actions/package/linux
with:
version: ${{ steps.short-version.outputs.version }}
build-type: ${{ steps.setup-dependencies.outputs.build-type }}
cmake-preset: ${{ steps.cmake-preset.outputs.preset }}
qt-version: ${{ steps.setup-dependencies.outputs.qt-version }}
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
gpg-private-key-id: ${{ secrets.GPG_PRIVATE_KEY_ID }}
- 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
with:
version: ${{ steps.short-version.outputs.version }}
build-type: ${{ steps.setup-dependencies.outputs.build-type }}
artifact-name: ${{ matrix.artifact-name }}
msystem: ${{ matrix.msystem }}
windows-codesign-cert: ${{ secrets.WINDOWS_CODESIGN_CERT }}
windows-codesign-password: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
nix build --print-build-logs .#prismlauncher

View File

@ -1,50 +1,6 @@
name: "CodeQL Code Scanning"
on:
push:
paths:
# File types
- "**.cpp"
- "**.h"
- "**.java"
# Directories
- "buildconfig/"
- "cmake/"
- "launcher/"
- "libraries/"
- "program_info/"
- "tests/"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/codeql"
- ".github/workflows/codeql.yml"
pull_request:
paths:
# File types
- "**.cpp"
- "**.h"
# Directories
- "buildconfig/"
- "cmake/"
- "launcher/"
- "libraries/"
- "program_info/"
- "tests/"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/codeql"
- ".github/workflows/codeql.yml"
workflow_dispatch:
on: [ push, pull_request, workflow_dispatch ]
jobs:
CodeQL:
@ -54,7 +10,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: "true"
submodules: 'true'
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
@ -63,15 +19,17 @@ jobs:
queries: security-and-quality
languages: cpp, java
- name: Setup dependencies
uses: ./.github/actions/setup-dependencies
with:
build-type: Debug
- 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 libqt5networkauth5 libqt5networkauth5-dev
- name: Configure and Build
run: |
cmake --preset linux_debug
cmake --build --preset linux_debug
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr -DLauncher_QT_VERSION_MAJOR=5 -G Ninja
cmake --build build
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3

View File

@ -1,95 +0,0 @@
name: Flatpak
on:
push:
# We don't do anything with these artifacts on releases. They go to Flathub
tags-ignore:
- "*"
paths:
# File types
- "**.cpp"
- "**.h"
- "**.java"
# Build files
- "flatpak/"
# Directories
- "buildconfig/"
- "cmake/"
- "launcher/"
- "libraries/"
- "program_info/"
- "tests/"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/workflows/flatpak.yml"
pull_request:
paths:
# File types
- "**.cpp"
- "**.h"
# Build files
- "flatpak/"
# Directories
- "buildconfig/"
- "cmake/"
- "launcher/"
- "libraries/"
- "program_info/"
- "tests/"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/workflows/flatpak.yml"
workflow_dispatch:
permissions:
contents: read
jobs:
build:
name: Build (${{ matrix.arch }})
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
arch: x86_64
- os: ubuntu-22.04-arm
arch: aarch64
runs-on: ${{ matrix.os }}
container:
image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.8
options: --privileged
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: true
- name: Set short version
shell: bash
run: |
echo "VERSION=${GITHUB_SHA::7}" >> "$GITHUB_ENV"
- name: Build Flatpak
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
with:
bundle: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-Flatpak.flatpak
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
arch: ${{ matrix.arch }}

View File

@ -1,62 +0,0 @@
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
jobs:
update-blocked-status:
name: Update Blocked Status
runs-on: ubuntu-latest
# a pr that was a `blocking:<id>` 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, 'blocking') }}
steps:
- name: Generate token
id: generate-token
uses: actions/create-github-app-token@v2
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 '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")

View File

@ -1,143 +0,0 @@
name: Nix
on:
push:
tags:
- "*"
paths:
# File types
- "**.cpp"
- "**.h"
- "**.java"
# Build files
- "**.nix"
- "nix/"
- "flake.lock"
# Directories
- "buildconfig/"
- "cmake/"
- "launcher/"
- "libraries/"
- "program_info/"
- "tests/"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/workflows/nix.yml"
pull_request_target:
paths:
# File types
- "**.cpp"
- "**.h"
# Build files
- "**.nix"
- "nix/"
- "flake.lock"
# Directories
- "buildconfig/"
- "cmake/"
- "launcher/"
- "libraries/"
- "program_info/"
- "tests/"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/workflows/nix.yml"
workflow_dispatch:
permissions:
contents: read
env:
DEBUG: ${{ github.ref_type != 'tag' }}
USE_DETERMINATE: ${{ github.event_name == 'pull_request' }}
jobs:
build:
name: Build (${{ matrix.system }})
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
system: x86_64-linux
- os: ubuntu-22.04-arm
system: aarch64-linux
- os: macos-13
system: x86_64-darwin
- os: macos-14
system: aarch64-darwin
runs-on: ${{ matrix.os }}
permissions:
id-token: write
steps:
- name: Get merge commit
if: ${{ github.event_name == 'pull_request_target' }}
id: merge-commit
uses: PrismLauncher/PrismLauncher/.github/actions/get-merge-commit@develop
with:
pull-request-id: ${{ github.event.number }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ steps.merge-commit.outputs.merge-commit-sha || github.sha }}
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v17
with:
determinate: ${{ env.USE_DETERMINATE }}
# For PRs
- name: Setup Nix Magic Cache
if: ${{ env.USE_DETERMINATE == 'true' }}
uses: DeterminateSystems/flakehub-cache-action@v1
# For in-tree builds
- name: Setup Cachix
if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}
uses: cachix/cachix-action@v16
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"

View File

@ -1,29 +0,0 @@
name: Stale
on:
schedule:
# run weekly on sunday
- cron: "0 0 * * 0"
workflow_dispatch:
jobs:
label:
name: Label issues and PRs
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v9
with:
days-before-stale: 60
days-before-close: -1 # Don't close anything
exempt-issue-labels: rfc,nostale,help wanted
exempt-all-milestones: true
exempt-all-assignees: true
operations-per-run: 1000
stale-issue-label: inactive
stale-pr-label: inactive

43
.github/workflows/trigger_builds.yml vendored Normal file
View File

@ -0,0 +1,43 @@
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 }}
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}

View File

@ -10,8 +10,21 @@ jobs:
name: Build Release
uses: ./.github/workflows/build.yml
with:
build-type: Release
secrets: inherit
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 }}
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
create_release:
needs: build_release
@ -34,8 +47,10 @@ 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-macOS*/PrismLauncher.zip PrismLauncher-macOS-${{ env.VERSION }}.zip
tar --exclude='.git' -czf PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }}
@ -76,15 +91,13 @@ jobs:
draft: true
prerelease: false
files: |
PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
PrismLauncher-Linux-x86_64.AppImage
PrismLauncher-Linux-x86_64.AppImage.zsync
PrismLauncher-Linux-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
@ -92,4 +105,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-${{ env.VERSION }}.tar.gz

View File

@ -17,7 +17,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31
- uses: cachix/install-nix-action@08dcb3a5e62fa31e2da3d490afc4176ef55ecd72 # v30
- uses: DeterminateSystems/update-flake-lock@v24
with:

View File

@ -1,21 +1,13 @@
name: Publish
name: Publish to WinGet
on:
release:
types: [ released ]
permissions:
contents: read
types: [released]
jobs:
winget:
name: Winget
publish:
runs-on: windows-latest
steps:
- name: Publish on Winget
uses: vedantmgoyal2009/winget-releaser@v2
- uses: vedantmgoyal2009/winget-releaser@v2
with:
identifier: PrismLauncher.PrismLauncher
version: ${{ github.event.release.tag_name }}

8
.gitignore vendored
View File

@ -14,7 +14,6 @@ CMakeLists.txt.user.*
CMakeSettings.json
/CMakeFiles
CMakeCache.txt
CMakeUserPresets.json
/.project
/.settings
/.idea
@ -22,7 +21,6 @@ CMakeUserPresets.json
/.vs
cmake-build-*/
Debug
compile_commands.json
# Build dirs
build
@ -49,12 +47,8 @@ run/
# Nix/NixOS
.direnv/
## Used when manually invoking stdenv phases
outputs/
## Regular artifacts
.pre-commit-config.yaml
result
result-*
repl-result-*
# Flatpak
.flatpak-builder

6
.gitmodules vendored
View File

@ -4,6 +4,9 @@
[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
@ -19,6 +22,3 @@
[submodule "flatpak/shared-modules"]
path = flatpak/shared-modules
url = https://github.com/flathub/shared-modules.git
[submodule "libraries/qt-qrcodegenerator/QR-Code-generator"]
path = libraries/qt-qrcodegenerator/QR-Code-generator
url = https://github.com/nayuki/QR-Code-generator

View File

@ -78,18 +78,13 @@ else()
# 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}")
# -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
foreach(lang C CXX)
set("CMAKE_${lang}_FLAGS_RELEASE" "-ffunction-sections -fdata-sections -mguard=cf")
endforeach()
endif()
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_WARN_DEPRECATED_UP_TO=0x060200")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_UP_TO=0x060000")
# 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")
# Fix aarch64 build for toml++
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0")
@ -97,12 +92,6 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0")
# set CXXFLAGS for build targets
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
# 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 STREQUAL "Ninja" AND CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
endif()
option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF)
# If this is a Debug build turn on address sanitiser
@ -117,14 +106,14 @@ if ((CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebI
else()
# AppleClang and Clang
message(STATUS "Address Sanitizer available on Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=null")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=null")
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 -fsanitize=undefined -fno-sanitize-recover")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover")
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")
@ -191,13 +180,12 @@ set(Launcher_LOGIN_CALLBACK_URL "https://prismlauncher.org/successful-login" CAC
set(Launcher_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" CACHE STRING "URL for FML Libraries.")
######## Set version numbers ########
set(Launcher_VERSION_MAJOR 10)
set(Launcher_VERSION_MAJOR 9)
set(Launcher_VERSION_MINOR 0)
set(Launcher_VERSION_PATCH 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")
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")
# 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.")
@ -308,11 +296,23 @@ endif()
# Find the required Qt parts
include(QtVersionlessBackport)
if(Launcher_QT_VERSION_MAJOR EQUAL 6)
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
set(QT_VERSION_MAJOR 5)
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml NetworkAuth)
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)
set(QT_VERSION_MAJOR 6)
find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml Core5Compat NetworkAuth OpenGL)
find_package(Qt6 COMPONENTS DBus)
list(APPEND Launcher_QT_DBUS Qt6::DBus)
find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml Core5Compat NetworkAuth)
list(APPEND Launcher_QT_LIBS Qt6::Core5Compat)
if(NOT Launcher_FORCE_BUNDLED_LIBS)
@ -326,16 +326,29 @@ else()
message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported")
endif()
if(Launcher_QT_VERSION_MAJOR EQUAL 6)
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()
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)
endif()
if(NOT Launcher_FORCE_BUNDLED_LIBS)
# Find toml++
find_package(tomlplusplus 3.2.0 QUIET)
# Find ghc_filesystem
find_package(ghc_filesystem QUIET)
# Find cmark
find_package(cmark QUIET)
endif()
@ -384,8 +397,8 @@ 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.6.4/Sparkle-2.6.4.tar.xz" CACHE STRING "URL to Sparkle release archive")
set(MACOSX_SPARKLE_SHA256 "50612a06038abc931f16011d7903b8326a362c1074dabccb718404ce8e585f0b" CACHE STRING "SHA256 checksum for Sparkle release archive")
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_DIR "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
# directories to look for dependencies
@ -475,7 +488,6 @@ 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
add_subdirectory(libraries/qt-qrcodegenerator) # qr code generator
if(FORCE_BUNDLED_ZLIB)
message(STATUS "Using bundled zlib")
@ -531,6 +543,12 @@ else()
endif()
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 ###############################

View File

@ -1,14 +0,0 @@
{
"$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
"version": 8,
"cmakeMinimumRequired": {
"major": 3,
"minor": 28
},
"include": [
"cmake/linuxPreset.json",
"cmake/macosPreset.json",
"cmake/windowsMinGWPreset.json",
"cmake/windowsMSVCPreset.json"
]
}

View File

@ -2,59 +2,16 @@
## Code formatting
All files are formatted with `clang-format` using the configuration in `.clang-format`. Ensure it is run on changed files before committing!
Try to follow the existing formatting.
If there is no existing formatting, you may use `clang-format` with our included `.clang-format` configuration.
Please also follow the project's conventions for C++:
In general, in order of importance:
- 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, macros, and enum constants should be formatted as `SCREAMING_SNAKE_CASE`: `LIGHT_GRAY`.
- Avoid inventing acronyms or abbreviations especially for a name of multiple words - like `tp` for `texturePack`.
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 { HAM_AND_PINEAPPLE, OREO_AND_KETCHUP };
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.
- 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.
## Signing your work

View File

@ -1,7 +1,7 @@
## Prism Launcher
Prism Launcher - Minecraft Launcher
Copyright (C) 2022-2025 Prism Launcher Contributors
Copyright (C) 2022-2024 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 6
## Qt 5/6
Copyright (C) 2022 The Qt Company Ltd and other contributors.
Contact: https://www.qt.io/licensing
@ -362,6 +362,28 @@
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 <s.schuemann@pobox.com>
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 <uri_herrera@nitrux.in> and others
@ -403,12 +425,3 @@
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
## qt-qrcodegenerator (`libraries/qt-qrcodegenerator`)
Copyright © 2024 Project Nayuki. (MIT License)
https://www.nayuki.io/page/qr-code-generator-library
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.

View File

@ -34,8 +34,8 @@
*/
#include <qstringliteral.h>
#include <QObject>
#include "BuildConfig.h"
#include <QObject>
const Config BuildConfig;
@ -49,7 +49,7 @@ Config::Config()
LAUNCHER_DOMAIN = "@Launcher_Domain@";
LAUNCHER_CONFIGFILE = "@Launcher_ConfigFile@";
LAUNCHER_GIT = "@Launcher_Git@";
LAUNCHER_APPID = "@Launcher_AppID@";
LAUNCHER_DESKTOPFILENAME = "@Launcher_DesktopFileName@";
LAUNCHER_SVGFILENAME = "@Launcher_SVGFileName@";
USER_AGENT = "@Launcher_UserAgent@";
@ -58,7 +58,6 @@ Config::Config()
// 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@";
@ -75,13 +74,14 @@ 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
#cmakedefine01 Launcher_ENABLE_JAVA_DOWNLOADER
JAVA_DOWNLOADER_ENABLED = Launcher_ENABLE_JAVA_DOWNLOADER;
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
@ -89,19 +89,27 @@ Config::Config()
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()) {
}
else if (!GIT_COMMIT.isEmpty())
{
VERSION_CHANNEL = GIT_COMMIT.mid(0, 8);
} else {
}
else
{
VERSION_CHANNEL = "unknown";
}
@ -128,7 +136,7 @@ Config::Config()
QString Config::versionString() const
{
return QString("%1.%2.%3").arg(VERSION_MAJOR).arg(VERSION_MINOR).arg(VERSION_PATCH);
return QString("%1.%2").arg(VERSION_MAJOR).arg(VERSION_MINOR);
}
QString Config::printableVersionString() const
@ -136,7 +144,8 @@ 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;
@ -153,3 +162,4 @@ QString Config::systemID() const
{
return QStringLiteral("%1 %2 %3").arg(COMPILER_TARGET_SYSTEM, COMPILER_TARGET_SYSTEM_VERSION, COMPILER_TARGET_SYSTEM_PROCESSOR);
}

View File

@ -52,15 +52,13 @@ class Config {
QString LAUNCHER_DOMAIN;
QString LAUNCHER_CONFIGFILE;
QString LAUNCHER_GIT;
QString LAUNCHER_APPID;
QString LAUNCHER_DESKTOPFILENAME;
QString LAUNCHER_SVGFILENAME;
/// The major version number.
int VERSION_MAJOR;
/// The minor version number.
int VERSION_MINOR;
/// The patch version number.
int VERSION_PATCH;
/**
* The version channel

View File

@ -8,8 +8,6 @@
<string>A Minecraft mod wants to access your microphone.</string>
<key>NSDownloadsFolderUsageDescription</key>
<string>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.</string>
<key>NSLocalNetworkUsageDescription</key>
<string>Minecraft uses the local network to find and connect to LAN servers.</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSHighResolutionCapable</key>

View File

@ -1,81 +0,0 @@
{
"$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
"version": 8,
"configurePresets": [
{
"name": "base",
"hidden": true,
"binaryDir": "build",
"installDir": "install",
"cacheVariables": {
"Launcher_BUILD_PLATFORM": "custom"
}
},
{
"name": "base_debug",
"hidden": true,
"inherits": [
"base"
],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "base_release",
"hidden": true,
"inherits": [
"base"
],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"ENABLE_LTO": "ON"
}
},
{
"name": "base_ci",
"hidden": true,
"inherits": [
"base_release"
],
"cacheVariables": {
"Launcher_BUILD_PLATFORM": "official",
"Launcher_FORCE_BUNDLED_LIBS": "ON"
}
}
],
"testPresets": [
{
"name": "base",
"hidden": true,
"output": {
"outputOnFailure": true
},
"execution": {
"noTestsAction": "error"
},
"filter": {
"exclude": {
"name": "^example64|example$"
}
}
},
{
"name": "base_debug",
"hidden": true,
"inherits": [
"base"
],
"output": {
"debug": true
}
},
{
"name": "base_release",
"hidden": true,
"inherits": [
"base"
]
}
]
}

View File

@ -1,180 +0,0 @@
{
"$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
"version": 8,
"include": [
"commonPresets.json"
],
"configurePresets": [
{
"name": "linux_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
},
"generator": "Ninja",
"cacheVariables": {
"Launcher_BUILD_ARTIFACT": "Linux-Qt6",
"Launcher_ENABLE_JAVA_DOWNLOADER": "ON"
}
},
{
"name": "linux_debug",
"inherits": [
"base_debug",
"linux_base"
],
"displayName": "Linux (Debug)"
},
{
"name": "linux_release",
"inherits": [
"base_release",
"linux_base"
],
"displayName": "Linux (Release)"
},
{
"name": "linux_ci",
"inherits": [
"base_ci",
"linux_base"
],
"displayName": "Linux (CI)",
"cacheVariables": {
"Launcher_BUILD_ARTIFACT": "Linux-Qt6"
},
"installDir": "/usr"
}
],
"buildPresets": [
{
"name": "linux_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
}
},
{
"name": "linux_debug",
"inherits": [
"linux_base"
],
"displayName": "Linux (Debug)",
"configurePreset": "linux_debug"
},
{
"name": "linux_release",
"inherits": [
"linux_base"
],
"displayName": "Linux (Release)",
"configurePreset": "linux_release"
},
{
"name": "linux_ci",
"inherits": [
"linux_base"
],
"displayName": "Linux (CI)",
"configurePreset": "linux_ci"
}
],
"testPresets": [
{
"name": "linux_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
}
},
{
"name": "linux_debug",
"inherits": [
"base_debug",
"linux_base"
],
"displayName": "Linux (Debug)",
"configurePreset": "linux_debug"
},
{
"name": "linux_release",
"inherits": [
"base_release",
"linux_base"
],
"displayName": "Linux (Release)",
"configurePreset": "linux_release"
},
{
"name": "linux_ci",
"inherits": [
"base_release",
"linux_base"
],
"displayName": "Linux (CI)",
"configurePreset": "linux_ci"
}
],
"workflowPresets": [
{
"name": "linux_debug",
"displayName": "Linux (Debug)",
"steps": [
{
"type": "configure",
"name": "linux_debug"
},
{
"type": "build",
"name": "linux_debug"
},
{
"type": "test",
"name": "linux_debug"
}
]
},
{
"name": "linux",
"displayName": "Linux (Release)",
"steps": [
{
"type": "configure",
"name": "linux_release"
},
{
"type": "build",
"name": "linux_release"
},
{
"type": "test",
"name": "linux_release"
}
]
},
{
"name": "linux_ci",
"displayName": "Linux (CI)",
"steps": [
{
"type": "configure",
"name": "linux_ci"
},
{
"type": "build",
"name": "linux_ci"
},
{
"type": "test",
"name": "linux_ci"
}
]
}
]
}

View File

@ -1,272 +0,0 @@
{
"$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
"version": 8,
"include": [
"commonPresets.json"
],
"configurePresets": [
{
"name": "macos_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Darwin"
},
"generator": "Ninja"
},
{
"name": "macos_universal_base",
"hidden": true,
"inherits": [
"macos_base"
],
"cacheVariables": {
"CMAKE_OSX_ARCHITECTURES": "x86_64;arm64",
"Launcher_BUILD_ARTIFACT": "macOS-Qt6"
}
},
{
"name": "macos_debug",
"inherits": [
"base_debug",
"macos_base"
],
"displayName": "macOS (Debug)"
},
{
"name": "macos_release",
"inherits": [
"base_release",
"macos_base"
],
"displayName": "macOS (Release)"
},
{
"name": "macos_universal_debug",
"inherits": [
"base_debug",
"macos_universal_base"
],
"displayName": "macOS (Universal Binary, Debug)"
},
{
"name": "macos_universal_release",
"inherits": [
"base_release",
"macos_universal_base"
],
"displayName": "macOS (Universal Binary, Release)"
},
{
"name": "macos_ci",
"inherits": [
"base_ci",
"macos_universal_base"
],
"displayName": "macOS (CI)",
"cacheVariables": {
"Launcher_BUILD_ARTIFACT": "macOS-Qt6"
}
}
],
"buildPresets": [
{
"name": "macos_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Darwin"
}
},
{
"name": "macos_debug",
"inherits": [
"macos_base"
],
"displayName": "macOS (Debug)",
"configurePreset": "macos_debug"
},
{
"name": "macos_release",
"inherits": [
"macos_base"
],
"displayName": "macOS (Release)",
"configurePreset": "macos_release"
},
{
"name": "macos_universal_debug",
"inherits": [
"macos_base"
],
"displayName": "macOS (Universal Binary, Debug)",
"configurePreset": "macos_universal_debug"
},
{
"name": "macos_universal_release",
"inherits": [
"macos_base"
],
"displayName": "macOS (Universal Binary, Release)",
"configurePreset": "macos_universal_release"
},
{
"name": "macos_ci",
"inherits": [
"macos_base"
],
"displayName": "macOS (CI)",
"configurePreset": "macos_ci"
}
],
"testPresets": [
{
"name": "macos_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Darwin"
}
},
{
"name": "macos_debug",
"inherits": [
"base_debug",
"macos_base"
],
"displayName": "MacOS (Debug)",
"configurePreset": "macos_debug"
},
{
"name": "macos_release",
"inherits": [
"base_release",
"macos_base"
],
"displayName": "macOS (Release)",
"configurePreset": "macos_release"
},
{
"name": "macos_universal_debug",
"inherits": [
"base_debug",
"macos_base"
],
"displayName": "MacOS (Universal Binary, Debug)",
"configurePreset": "macos_universal_debug"
},
{
"name": "macos_universal_release",
"inherits": [
"base_release",
"macos_base"
],
"displayName": "macOS (Universal Binary, Release)",
"configurePreset": "macos_universal_release"
},
{
"name": "macos_ci",
"inherits": [
"base_release",
"macos_base"
],
"displayName": "macOS (CI)",
"configurePreset": "macos_ci"
}
],
"workflowPresets": [
{
"name": "macos_debug",
"displayName": "macOS (Debug)",
"steps": [
{
"type": "configure",
"name": "macos_debug"
},
{
"type": "build",
"name": "macos_debug"
},
{
"type": "test",
"name": "macos_debug"
}
]
},
{
"name": "macos",
"displayName": "macOS (Release)",
"steps": [
{
"type": "configure",
"name": "macos_release"
},
{
"type": "build",
"name": "macos_release"
},
{
"type": "test",
"name": "macos_release"
}
]
},
{
"name": "macos_universal_debug",
"displayName": "macOS (Universal Binary, Debug)",
"steps": [
{
"type": "configure",
"name": "macos_universal_debug"
},
{
"type": "build",
"name": "macos_universal_debug"
},
{
"type": "test",
"name": "macos_universal_debug"
}
]
},
{
"name": "macos_universal",
"displayName": "macOS (Universal Binary, Release)",
"steps": [
{
"type": "configure",
"name": "macos_universal_release"
},
{
"type": "build",
"name": "macos_universal_release"
},
{
"type": "test",
"name": "macos_universal_release"
}
]
},
{
"name": "macos_ci",
"displayName": "macOS (CI)",
"steps": [
{
"type": "configure",
"name": "macos_ci"
},
{
"type": "build",
"name": "macos_ci"
},
{
"type": "test",
"name": "macos_ci"
}
]
}
]
}

View File

@ -1,311 +0,0 @@
{
"$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
"version": 8,
"include": [
"commonPresets.json"
],
"configurePresets": [
{
"name": "windows_msvc_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"cacheVariables": {
"Launcher_BUILD_ARTIFACT": "Windows-MSVC-Qt6"
}
},
{
"name": "windows_msvc_arm64_cross_base",
"hidden": true,
"inherits": [
"windows_msvc_base"
],
"architecture": "arm64",
"cacheVariables": {
"Launcher_BUILD_ARTIFACT": "Windows-MSVC-arm64-Qt6"
}
},
{
"name": "windows_msvc_debug",
"inherits": [
"base_debug",
"windows_msvc_base"
],
"displayName": "Windows MSVC (Debug)",
"generator": "Ninja"
},
{
"name": "windows_msvc_release",
"inherits": [
"base_release",
"windows_msvc_base"
],
"displayName": "Windows MSVC (Release)"
},
{
"name": "windows_msvc_arm64_cross_debug",
"inherits": [
"base_debug",
"windows_msvc_arm64_cross_base"
],
"displayName": "Windows MSVC (ARM64 cross, Debug)"
},
{
"name": "windows_msvc_arm64_cross_release",
"inherits": [
"base_release",
"windows_msvc_arm64_cross_base"
],
"displayName": "Windows MSVC (ARM64 cross, Release)"
},
{
"name": "windows_msvc_ci",
"inherits": [
"base_ci",
"windows_msvc_base"
],
"displayName": "Windows MSVC (CI)",
"cacheVariables": {
"Launcher_BUILD_ARTIFACT": "Windows-MSVC-Qt6"
}
},
{
"name": "windows_msvc_arm64_cross_ci",
"inherits": [
"base_ci",
"windows_msvc_arm64_cross_base"
],
"displayName": "Windows MSVC (ARM64 cross, CI)",
"cacheVariables": {
"Launcher_BUILD_ARTIFACT": "Windows-MSVC-arm64-Qt6"
}
}
],
"buildPresets": [
{
"name": "windows_msvc_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "windows_msvc_debug",
"inherits": [
"windows_msvc_base"
],
"displayName": "Windows MSVC (Debug)",
"configurePreset": "windows_msvc_debug",
"configuration": "Debug"
},
{
"name": "windows_msvc_release",
"inherits": [
"windows_msvc_base"
],
"displayName": "Windows MSVC (Release)",
"configurePreset": "windows_msvc_release",
"configuration": "Release",
"nativeToolOptions": [
"/p:UseMultiToolTask=true",
"/p:EnforceProcessCountAcrossBuilds=true"
]
},
{
"name": "windows_msvc_arm64_cross_debug",
"inherits": [
"windows_msvc_base"
],
"displayName": "Windows MSVC (ARM64 cross, Debug)",
"configurePreset": "windows_msvc_arm64_cross_debug",
"configuration": "Debug",
"nativeToolOptions": [
"/p:UseMultiToolTask=true",
"/p:EnforceProcessCountAcrossBuilds=true"
]
},
{
"name": "windows_msvc_arm64_cross_release",
"inherits": [
"windows_msvc_base"
],
"displayName": "Windows MSVC (ARM64 cross, Release)",
"configurePreset": "windows_msvc_arm64_cross_release",
"configuration": "Release",
"nativeToolOptions": [
"/p:UseMultiToolTask=true",
"/p:EnforceProcessCountAcrossBuilds=true"
]
},
{
"name": "windows_msvc_ci",
"inherits": [
"windows_msvc_base"
],
"displayName": "Windows MSVC (CI)",
"configurePreset": "windows_msvc_ci",
"configuration": "Release",
"nativeToolOptions": [
"/p:UseMultiToolTask=true",
"/p:EnforceProcessCountAcrossBuilds=true"
]
},
{
"name": "windows_msvc_arm64_cross_ci",
"inherits": [
"windows_msvc_base"
],
"displayName": "Windows MSVC (ARM64 cross, CI)",
"configurePreset": "windows_msvc_arm64_cross_ci",
"configuration": "Release",
"nativeToolOptions": [
"/p:UseMultiToolTask=true",
"/p:EnforceProcessCountAcrossBuilds=true"
]
}
],
"testPresets": [
{
"name": "windows_msvc_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "windows_msvc_debug",
"inherits": [
"base_debug",
"windows_msvc_base"
],
"displayName": "Windows MSVC (Debug)",
"configurePreset": "windows_msvc_debug",
"configuration": "Debug"
},
{
"name": "windows_msvc_release",
"inherits": [
"base_release",
"windows_msvc_base"
],
"displayName": "Windows MSVC (Release)",
"configurePreset": "windows_msvc_release",
"configuration": "Release"
},
{
"name": "windows_msvc_ci",
"inherits": [
"base_release",
"windows_msvc_base"
],
"displayName": "Windows MSVC (CI)",
"configurePreset": "windows_msvc_ci",
"configuration": "Release"
}
],
"workflowPresets": [
{
"name": "windows_msvc_debug",
"displayName": "Windows MSVC (Debug)",
"steps": [
{
"type": "configure",
"name": "windows_msvc_debug"
},
{
"type": "build",
"name": "windows_msvc_debug"
},
{
"type": "test",
"name": "windows_msvc_debug"
}
]
},
{
"name": "windows_msvc",
"displayName": "Windows MSVC (Release)",
"steps": [
{
"type": "configure",
"name": "windows_msvc_release"
},
{
"type": "build",
"name": "windows_msvc_release"
},
{
"type": "test",
"name": "windows_msvc_release"
}
]
},
{
"name": "windows_msvc_arm64_cross_debug",
"displayName": "Windows MSVC (ARM64 cross, Debug)",
"steps": [
{
"type": "configure",
"name": "windows_msvc_arm64_cross_debug"
},
{
"type": "build",
"name": "windows_msvc_arm64_cross_debug"
}
]
},
{
"name": "windows_msvc_arm64_cross",
"displayName": "Windows MSVC (ARM64 cross, Release)",
"steps": [
{
"type": "configure",
"name": "windows_msvc_arm64_cross_release"
},
{
"type": "build",
"name": "windows_msvc_arm64_cross_release"
}
]
},
{
"name": "windows_msvc_ci",
"displayName": "Windows MSVC (CI)",
"steps": [
{
"type": "configure",
"name": "windows_msvc_ci"
},
{
"type": "build",
"name": "windows_msvc_ci"
},
{
"type": "test",
"name": "windows_msvc_ci"
}
]
},
{
"name": "windows_msvc_arm64_cross_ci",
"displayName": "Windows MSVC (ARM64 cross, CI)",
"steps": [
{
"type": "configure",
"name": "windows_msvc_arm64_cross_ci"
},
{
"type": "build",
"name": "windows_msvc_arm64_cross_ci"
}
]
}
]
}

View File

@ -1,183 +0,0 @@
{
"$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
"version": 8,
"include": [
"commonPresets.json"
],
"configurePresets": [
{
"name": "windows_mingw_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"generator": "Ninja",
"cacheVariables": {
"Launcher_BUILD_ARTIFACT": "Windows-MinGW-w64-Qt6"
}
},
{
"name": "windows_mingw_debug",
"inherits": [
"base_debug",
"windows_mingw_base"
],
"displayName": "Windows MinGW (Debug)"
},
{
"name": "windows_mingw_release",
"inherits": [
"base_release",
"windows_mingw_base"
],
"displayName": "Windows MinGW (Release)"
},
{
"name": "windows_mingw_ci",
"inherits": [
"base_ci",
"windows_mingw_base"
],
"displayName": "Windows MinGW (CI)",
"cacheVariables": {
"Launcher_BUILD_ARTIFACT": "Windows-MinGW-w64-Qt6"
}
}
],
"buildPresets": [
{
"name": "windows_mingw_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "windows_mingw_debug",
"inherits": [
"windows_mingw_base"
],
"displayName": "Windows MinGW (Debug)",
"configurePreset": "windows_mingw_debug"
},
{
"name": "windows_mingw_release",
"inherits": [
"windows_mingw_base"
],
"displayName": "Windows MinGW (Release)",
"configurePreset": "windows_mingw_release"
},
{
"name": "windows_mingw_ci",
"inherits": [
"windows_mingw_base"
],
"displayName": "Windows MinGW (CI)",
"configurePreset": "windows_mingw_ci"
}
],
"testPresets": [
{
"name": "windows_mingw_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"filter": {
"exclude": {
"name": "^example64|example$"
}
}
},
{
"name": "windows_mingw_debug",
"inherits": [
"base_debug",
"windows_mingw_base"
],
"displayName": "Windows MinGW (Debug)",
"configurePreset": "windows_mingw_debug"
},
{
"name": "windows_mingw_release",
"inherits": [
"base_release",
"windows_mingw_base"
],
"displayName": "Windows MinGW (Release)",
"configurePreset": "windows_mingw_release"
},
{
"name": "windows_mingw_ci",
"inherits": [
"base_release",
"windows_mingw_base"
],
"displayName": "Windows MinGW (CI)",
"configurePreset": "windows_mingw_ci"
}
],
"workflowPresets": [
{
"name": "windows_mingw_debug",
"displayName": "Windows MinGW (Debug)",
"steps": [
{
"type": "configure",
"name": "windows_mingw_debug"
},
{
"type": "build",
"name": "windows_mingw_debug"
},
{
"type": "test",
"name": "windows_mingw_debug"
}
]
},
{
"name": "windows_mingw",
"displayName": "Windows MinGW (Release)",
"steps": [
{
"type": "configure",
"name": "windows_mingw_release"
},
{
"type": "build",
"name": "windows_mingw_release"
},
{
"type": "test",
"name": "windows_mingw_release"
}
]
},
{
"name": "windows_mingw_ci",
"displayName": "Windows MinGW (CI)",
"steps": [
{
"type": "configure",
"name": "windows_mingw_ci"
},
{
"type": "build",
"name": "windows_mingw_ci"
},
{
"type": "test",
"name": "windows_mingw_ci"
}
]
}
]
}

View File

@ -1,4 +1,9 @@
(import (fetchTarball {
url = "https://github.com/edolstra/flake-compat/archive/ff81ac966bb2cae68946d5ed5fc4994f96d0ffec.tar.gz";
sha256 = "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=";
}) { src = ./.; }).defaultNix
(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

64
flake.lock generated
View File

@ -1,13 +1,29 @@
{
"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"
}
},
"libnbtplusplus": {
"flake": false,
"locked": {
"lastModified": 1744811532,
"narHash": "sha256-qhmjaRkt+O7A+gu6HjUkl7QzOEb4r8y8vWZMG2R/C6o=",
"lastModified": 1699286814,
"narHash": "sha256-yy0q+bky80LtK1GWzz7qpM+aAGrOqLuewbid8WT1ilk=",
"owner": "PrismLauncher",
"repo": "libnbtplusplus",
"rev": "531449ba1c930c98e0bcf5d332b237a8566f9d78",
"rev": "23b955121b8217c1c348a9ed2483167a6f3ff4ad",
"type": "github"
},
"original": {
@ -16,13 +32,28 @@
"type": "github"
}
},
"nix-filter": {
"locked": {
"lastModified": 1710156097,
"narHash": "sha256-1Wvk8UP7PXdf8bCCaEoMnOT1qe5/Duqgj+rL8sRQsSM=",
"owner": "numtide",
"repo": "nix-filter",
"rev": "3342559a24e85fc164b295c3444e8a139924675b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "nix-filter",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1745526057,
"narHash": "sha256-ITSpPDwvLBZBnPRS2bUcHY3gZSwis/uTe255QgMtTLA=",
"lastModified": 1729256560,
"narHash": "sha256-/uilDXvCIEs3C9l73JTACm4quuHUsIHcns1c+cHUJwA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "f771eb401a46846c1aebd20552521b233dd7e18b",
"rev": "4c2fcb090b1f3e5b47eaa7bd33913b574a11e0a0",
"type": "github"
},
"original": {
@ -32,27 +63,12 @@
"type": "github"
}
},
"qt-qrcodegenerator": {
"flake": false,
"locked": {
"lastModified": 1737616857,
"narHash": "sha256-6SugPt0lp1Gz7nV23FLmsmpfzgFItkSw7jpGftsDPWc=",
"owner": "nayuki",
"repo": "QR-Code-generator",
"rev": "2c9044de6b049ca25cb3cd1649ed7e27aa055138",
"type": "github"
},
"original": {
"owner": "nayuki",
"repo": "QR-Code-generator",
"type": "github"
}
},
"root": {
"inputs": {
"flake-compat": "flake-compat",
"libnbtplusplus": "libnbtplusplus",
"nixpkgs": "nixpkgs",
"qt-qrcodegenerator": "qt-qrcodegenerator"
"nix-filter": "nix-filter",
"nixpkgs": "nixpkgs"
}
}
},

162
flake.nix
View File

@ -16,8 +16,25 @@
flake = false;
};
qt-qrcodegenerator = {
url = "github:nayuki/QR-Code-generator";
nix-filter.url = "github:numtide/nix-filter";
/*
Inputs below this are optional and can be removed
```
{
inputs.prismlauncher = {
url = "github:PrismLauncher/PrismLauncher";
inputs = {
flake-compat.follows = "";
};
};
}
```
*/
flake-compat = {
url = "github:edolstra/flake-compat";
flake = false;
};
};
@ -27,9 +44,9 @@
self,
nixpkgs,
libnbtplusplus,
qt-qrcodegenerator,
nix-filter,
...
}:
let
inherit (nixpkgs) lib;
@ -41,142 +58,45 @@
forAllSystems = lib.genAttrs systems;
nixpkgsFor = forAllSystems (system: nixpkgs.legacyPackages.${system});
in
{
checks = forAllSystems (
system:
let
pkgs = nixpkgsFor.${system};
llvm = pkgs.llvmPackages_19;
checks' = nixpkgsFor.${system}.callPackage ./nix/checks.nix { inherit self; };
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
'';
}
lib.filterAttrs (_: lib.isDerivation) checks'
);
devShells = forAllSystems (
system:
let
pkgs = nixpkgsFor.${system};
llvm = pkgs.llvmPackages_19;
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 = pkgs.mkShell {
name = "prism-launcher";
inputsFrom = [ packages'.prismlauncher-unwrapped ];
packages = with pkgs; [
inputsFrom = [ self.packages.${system}.prismlauncher-unwrapped ];
buildInputs = with pkgs; [
ccache
llvm.clang-tools
ninja
];
cmakeBuildType = "Debug";
cmakeFlags = [ "-GNinja" ] ++ packages'.prismlauncher.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: {
overlays.default =
final: prev:
let
version = builtins.substring 0 8 self.lastModifiedDate or "dirty";
in
{
prismlauncher-unwrapped = prev.callPackage ./nix/unwrapped.nix {
inherit
libnbtplusplus
qt-qrcodegenerator
nix-filter
self
version
;
};
@ -185,7 +105,6 @@
packages = forAllSystems (
system:
let
pkgs = nixpkgsFor.${system};
@ -198,7 +117,6 @@
default = prismPackages.prismlauncher;
};
in
# Only output them if they're available on the current system
lib.filterAttrs (_: lib.meta.availableOn pkgs.stdenv.hostPlatform) packages
);
@ -206,18 +124,16 @@
# 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};
prismPackages = self.packages.${system};
legacyPackages = self.legacyPackages.${system};
in
{
prismlauncher-debug = packages'.prismlauncher.override {
prismlauncher-unwrapped = legacyPackages'.prismlauncher-unwrapped-debug;
prismlauncher-debug = prismPackages.prismlauncher.override {
prismlauncher-unwrapped = legacyPackages.prismlauncher-unwrapped-debug;
};
prismlauncher-unwrapped-debug = packages'.prismlauncher-unwrapped.overrideAttrs {
prismlauncher-unwrapped-debug = prismPackages.prismlauncher-unwrapped.overrideAttrs {
cmakeBuildType = "Debug";
dontStrip = true;
};

View File

@ -1,20 +0,0 @@
{
"name": "flite",
"config-opts": [
"--enable-shared",
"--with-audio=pulseaudio"
],
"no-parallel-make": true,
"sources": [
{
"type": "git",
"url": "https://github.com/festvox/flite.git",
"tag": "v2.2",
"commit": "e9e2e37c329dbe98bfeb27a1828ef9a71fa84f88",
"x-checker-data": {
"type": "git",
"tag-pattern": "^v([\\d.]+)$"
}
}
]
}

View File

@ -8,7 +8,11 @@
{
"type": "git",
"url": "https://gitlab.freedesktop.org/libdecor/libdecor.git",
"commit": "c2bd8ad6fa42c0cb17553ce77ad8a87d1f543b1f"
"commit": "73260393a97291c887e1074ab7f318e031be0ac6"
},
{
"type": "patch",
"path": "patches/weird_libdecor.patch"
}
],
"cleanup": [

View File

@ -1,6 +1,6 @@
id: org.prismlauncher.PrismLauncher
runtime: org.kde.Platform
runtime-version: '6.8'
runtime-version: 6.7
sdk: org.kde.Sdk
sdk-extensions:
- org.freedesktop.Sdk.Extension.openjdk17
@ -19,12 +19,6 @@ finish-args:
- --filesystem=xdg-download:ro
# FTBApp import
- --filesystem=~/.ftba:ro
# Userspace visibility for manual hugepages configuration
# Required for -XX:+UseLargePages
- --filesystem=/sys/kernel/mm/hugepages:ro
# Userspace visibility for transparent hugepages configuration
# Required for -XX:+UseTransparentHugePages
- --filesystem=/sys/kernel/mm/transparent_hugepage:ro
modules:
# Might be needed by some Controller mods (see https://github.com/isXander/Controlify/issues/31)
@ -33,16 +27,11 @@ modules:
# Needed for proper Wayland support
- libdecor.json
# Text to Speech in the game
- flite.json
- name: prismlauncher
buildsystem: cmake-ninja
builddir: true
config-opts:
- -DLauncher_BUILD_PLATFORM=flatpak
# This allows us to manage and update Java independently of this Flatpak
- -DLauncher_ENABLE_JAVA_DOWNLOADER=ON
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
build-options:
env:
@ -58,14 +47,18 @@ modules:
config-opts:
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
- -DBUILD_SHARED_LIBS:BOOL=ON
- -DGLFW_BUILD_WAYLAND:BOOL=ON
- -DGLFW_USE_WAYLAND:BOOL=ON
- -DGLFW_BUILD_DOCS:BOOL=OFF
sources:
- type: git
url: https://github.com/glfw/glfw.git
commit: 7b6aead9fb88b3623e3b3725ebb42670cbe4c579 # 3.4
commit: 3fa2360720eeba1964df3c0ecf4b5df8648a8e52
- type: patch
path: patches/0009-Defer-setting-cursor-position-until-the-cursor-is-lo.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
@ -75,8 +68,8 @@ modules:
buildsystem: autotools
sources:
- type: archive
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.3.tar.xz
sha256: f8dd7566adb74147fab9964680b6bbadee87cf406a7fcff51718a5e6949b841c
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.2.tar.xz
sha256: c8bee4790d9058bacc4b6246456c58021db58a87ddda1a9d0139bf5f18f1f240
x-checker-data:
type: anitya
project-id: 14957
@ -98,8 +91,8 @@ modules:
sources:
- type: archive
dest-filename: gamemode.tar.gz
url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.8.2
sha256: 2886d4ce543c78bd2a364316d5e7fd59ef06b71de63f896b37c6d3dc97658f60
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

View File

@ -0,0 +1,24 @@
diff --git a/src/wl_window.c b/src/wl_window.c
index 52d3b9eb..4ac4eb5d 100644
--- a/src/wl_window.c
+++ b/src/wl_window.c
@@ -2117,8 +2117,7 @@ void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title)
void _glfwSetWindowIconWayland(_GLFWwindow* window,
int count, const GLFWimage* images)
{
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
- "Wayland: The platform does not support setting the window icon");
+ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the window icon\n");
}
void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos)
@@ -2361,8 +2360,7 @@ void _glfwRequestWindowAttentionWayland(_GLFWwindow* window)
void _glfwFocusWindowWayland(_GLFWwindow* window)
{
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
- "Wayland: The platform does not support setting the input focus");
+ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the input focus\n");
}
void _glfwSetWindowMonitorWayland(_GLFWwindow* window,

View File

@ -0,0 +1,17 @@
diff --git a/src/init.c b/src/init.c
index 06dbb3f2..a7c6da86 100644
--- a/src/init.c
+++ b/src/init.c
@@ -449,6 +449,12 @@ GLFWAPI int glfwInit(void)
_glfw.initialized = GLFW_TRUE;
glfwDefaultWindowHints();
+
+ fprintf(stderr, "!!! Patched GLFW from https://github.com/Admicos/minecraft-wayland\n"
+ "!!! If any issues with the window, or some issues with rendering, occur, "
+ "first try with the built-in GLFW, and if that solves the issue, report there first.\n"
+ "!!! Use outside Minecraft is untested, and things might break.\n");
+
return GLFW_TRUE;
}

View File

@ -0,0 +1,20 @@
diff --git a/src/platform.c b/src/platform.c
index c5966ae7..3e7442f9 100644
--- a/src/platform.c
+++ b/src/platform.c
@@ -49,12 +49,12 @@ static const struct
#if defined(_GLFW_COCOA)
{ GLFW_PLATFORM_COCOA, _glfwConnectCocoa },
#endif
-#if defined(_GLFW_X11)
- { GLFW_PLATFORM_X11, _glfwConnectX11 },
-#endif
#if defined(_GLFW_WAYLAND)
{ GLFW_PLATFORM_WAYLAND, _glfwConnectWayland },
#endif
+#if defined(_GLFW_X11)
+ { GLFW_PLATFORM_X11, _glfwConnectX11 },
+#endif
};
GLFWbool _glfwSelectPlatform(int desiredID, _GLFWplatform* platform)

View File

@ -1,59 +0,0 @@
From 9997ae55a47de469ea26f8437c30b51483abda5f Mon Sep 17 00:00:00 2001
From: Dan Klishch <danilklishch@gmail.com>
Date: Sat, 30 Sep 2023 23:38:05 -0400
Subject: Defer setting cursor position until the cursor is locked
---
src/wl_platform.h | 3 +++
src/wl_window.c | 14 ++++++++++++--
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/src/wl_platform.h b/src/wl_platform.h
index ca34f66e..cd1f227f 100644
--- a/src/wl_platform.h
+++ b/src/wl_platform.h
@@ -403,6 +403,9 @@ typedef struct _GLFWwindowWayland
int scaleSize;
int compositorPreferredScale;
+ double askedCursorPosX, askedCursorPosY;
+ GLFWbool didAskForSetCursorPos;
+
struct zwp_relative_pointer_v1* relativePointer;
struct zwp_locked_pointer_v1* lockedPointer;
struct zwp_confined_pointer_v1* confinedPointer;
diff --git a/src/wl_window.c b/src/wl_window.c
index 1de26558..0df16747 100644
--- a/src/wl_window.c
+++ b/src/wl_window.c
@@ -2586,8 +2586,9 @@ void _glfwGetCursorPosWayland(_GLFWwindow* window, double* xpos, double* ypos)
void _glfwSetCursorPosWayland(_GLFWwindow* window, double x, double y)
{
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
- "Wayland: The platform does not support setting the cursor position");
+ window->wl.didAskForSetCursorPos = true;
+ window->wl.askedCursorPosX = x;
+ window->wl.askedCursorPosY = y;
}
void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode)
@@ -2819,6 +2820,15 @@ static const struct zwp_relative_pointer_v1_listener relativePointerListener =
static void lockedPointerHandleLocked(void* userData,
struct zwp_locked_pointer_v1* lockedPointer)
{
+ _GLFWwindow* window = userData;
+
+ if (window->wl.didAskForSetCursorPos)
+ {
+ window->wl.didAskForSetCursorPos = false;
+ zwp_locked_pointer_v1_set_cursor_position_hint(window->wl.lockedPointer,
+ wl_fixed_from_double(window->wl.askedCursorPosX),
+ wl_fixed_from_double(window->wl.askedCursorPosY));
+ }
}
static void lockedPointerHandleUnlocked(void* userData,
--
2.42.0

View File

@ -0,0 +1,40 @@
diff --git a/src/libdecor.c b/src/libdecor.c
index a9c1106..1aa38b3 100644
--- a/src/libdecor.c
+++ b/src/libdecor.c
@@ -1391,22 +1391,32 @@ calculate_priority(const struct libdecor_plugin_description *plugin_description)
static bool
check_symbol_conflicts(const struct libdecor_plugin_description *plugin_description)
{
+ bool ret = true;
char * const *symbol;
+ void* main_prog = dlopen(NULL, RTLD_LAZY);
+ if (!main_prog) {
+ fprintf(stderr, "Plugin \"%s\" couldn't check conflicting symbols: \"%s\".\n",
+ plugin_description->description, dlerror());
+ return false;
+ }
+
symbol = plugin_description->conflicting_symbols;
while (*symbol) {
dlerror();
- dlsym (RTLD_DEFAULT, *symbol);
+ dlsym (main_prog, *symbol);
if (!dlerror()) {
fprintf(stderr, "Plugin \"%s\" uses conflicting symbol \"%s\".\n",
plugin_description->description, *symbol);
- return false;
+ ret = false;
+ break;
}
symbol++;
}
- return true;
+ dlclose(main_prog);
+ return ret;
}
static struct plugin_loader *

@ -1 +1 @@
Subproject commit 73f08ed2c3187f6648ca04ebef030930a6c9f0be
Subproject commit f2b0c16a2a217a1822ce5a6538ba8f755ed1dd32

View File

@ -48,7 +48,6 @@
#include "net/PasteUpload.h"
#include "pathmatcher/MultiMatcher.h"
#include "pathmatcher/SimplePrefixMatcher.h"
#include "tasks/Task.h"
#include "tools/GenericProfiler.h"
#include "ui/InstanceWindow.h"
#include "ui/MainWindow.h"
@ -59,6 +58,8 @@
#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/ExternalToolsPage.h"
#include "ui/pages/global/JavaPage.h"
#include "ui/pages/global/LanguagePage.h"
@ -96,7 +97,6 @@
#include <QList>
#include <QNetworkAccessManager>
#include <QStringList>
#include <QStringLiteral>
#include <QStyleFactory>
#include <QTranslator>
#include <QWindow>
@ -128,7 +128,6 @@
#include <stdlib.h>
#include <sys.h>
#include <QStringLiteral>
#include "SysInfo.h"
#ifdef Q_OS_LINUX
@ -155,16 +154,10 @@
#endif
#if defined Q_OS_WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <QStyleHints>
#include "console/WindowsConsole.h"
#include "WindowsConsole.h"
#endif
#include "console/Console.h"
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
@ -172,63 +165,6 @@ 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. */
@ -237,24 +173,11 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt
static std::mutex loggerMutex;
const std::lock_guard<std::mutex> 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;
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);
}
@ -295,25 +218,15 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// attach the parent console if stdout not already captured
if (AttachWindowsConsole()) {
consoleAttached = true;
if (auto err = EnableAnsiSupport(); !err) {
isANSIColorConsole = true;
} else {
std::cout << "Error setting up ansi console" << err.message() << std::endl;
}
}
#else
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_APPID);
m_startTime = QDateTime::currentDateTime();
setDesktopFileName(BuildConfig.LAUNCHER_DESKTOPFILENAME);
startTime = QDateTime::currentDateTime();
// Don't quit on hiding the last window
this->setQuitOnLastWindowClosed(false);
@ -329,7 +242,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
{ { "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" },
{ { "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" } });
@ -345,10 +257,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_serverToJoin = parser.value("server");
m_worldToJoin = parser.value("world");
m_profileToUse = parser.value("profile");
if (parser.isSet("offline")) {
m_offline = true;
m_offlineName = parser.value("offline");
}
m_liveCheck = parser.isSet("alive");
m_instanceIdToShowWindowOf = parser.value("show");
@ -363,9 +271,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
}
// error if --launch is missing with --server or --profile
if ((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty() || !m_profileToUse.isEmpty() || m_offline) &&
m_instanceIdToLaunch.isEmpty()) {
std::cerr << "--server, --profile and --offline can only be used in combination with --launch!" << std::endl;
if (((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty()) || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) {
std::cerr << "--server and --profile can only be used in combination with --launch!" << std::endl;
m_status = Application::Failed;
return;
}
@ -462,20 +369,19 @@ 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";
sentMessage = m_peerInstance->sendMessage(activate.serialize(), timeout);
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());
sentMessage = m_peerInstance->sendMessage(import.serialize(), timeout);
m_peerInstance->sendMessage(import.serialize(), timeout);
}
}
} else {
@ -491,20 +397,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
if (!m_profileToUse.isEmpty()) {
launch.args["profile"] = m_profileToUse;
}
if (m_offline) {
launch.args["offline_enabled"] = "true";
launch.args["offline_name"] = m_offlineName;
m_peerInstance->sendMessage(launch.serialize(), timeout);
}
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);
}
}
}
@ -535,14 +431,27 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
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}");
bool foundLoggingRules = false;
auto logRulesFile = QStringLiteral("qtlogging.ini");
auto logRulesPath = FS::PathCombine(dataPath, logRulesFile);
qInfo() << "Testing" << logRulesPath << "...";
qDebug() << "Testing" << logRulesPath << "...";
foundLoggingRules = QFile::exists(logRulesPath);
// search the dataPath()
@ -550,7 +459,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
if (!foundLoggingRules && !isPortable() && dirParam.isEmpty() && dataDirEnv.isEmpty()) {
logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile));
if (!logRulesPath.isEmpty()) {
qInfo() << "Found" << logRulesPath << "...";
qDebug() << "Found" << logRulesPath << "...";
foundLoggingRules = true;
}
}
@ -561,28 +470,28 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
#else
logRulesPath = FS::PathCombine(m_rootPath, logRulesFile);
#endif
qInfo() << "Testing" << logRulesPath << "...";
qDebug() << "Testing" << logRulesPath << "...";
foundLoggingRules = QFile::exists(logRulesPath);
}
if (foundLoggingRules) {
// load and set logging rules
qInfo() << "Loading logging rules from:" << logRulesPath;
qDebug() << "Loading logging rules from:" << logRulesPath;
QSettings loggingRules(logRulesPath, QSettings::IniFormat);
loggingRules.beginGroup("Rules");
QStringList rule_names = loggingRules.childKeys();
QStringList rules;
qInfo() << "Setting log rules:";
qDebug() << "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);
qInfo() << " " << rule;
qDebug() << " " << rule;
}
auto rules_str = rules.join("\n");
QLoggingCategory::setFilterRules(rules_str);
}
qInfo() << "<> Log initialized.";
qDebug() << "<> Log initialized.";
}
{
@ -599,33 +508,33 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
}
{
qInfo() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME + ", " + QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", "));
qInfo() << "Version : " << BuildConfig.printableVersionString();
qInfo() << "Platform : " << BuildConfig.BUILD_PLATFORM;
qInfo() << "Git commit : " << BuildConfig.GIT_COMMIT;
qInfo() << "Git refspec : " << BuildConfig.GIT_REFSPEC;
qInfo() << "Compiled for : " << BuildConfig.systemID();
qInfo() << "Compiled by : " << BuildConfig.compilerID();
qInfo() << "Build Artifact : " << BuildConfig.BUILD_ARTIFACT;
qInfo() << "Updates Enabled : " << (updaterEnabled() ? "Yes" : "No");
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");
if (adjustedBy.size()) {
qInfo() << "Work dir before adjustment : " << origcwdPath;
qInfo() << "Work dir after adjustment : " << QDir::currentPath();
qInfo() << "Adjusted by : " << adjustedBy;
qDebug() << "Work dir before adjustment : " << origcwdPath;
qDebug() << "Work dir after adjustment : " << QDir::currentPath();
qDebug() << "Adjusted by : " << adjustedBy;
} else {
qInfo() << "Work dir : " << QDir::currentPath();
qDebug() << "Work dir : " << QDir::currentPath();
}
qInfo() << "Binary path : " << binPath;
qInfo() << "Application root path : " << m_rootPath;
qDebug() << "Binary path : " << binPath;
qDebug() << "Application root path : " << m_rootPath;
if (!m_instanceIdToLaunch.isEmpty()) {
qInfo() << "ID of instance to launch : " << m_instanceIdToLaunch;
qDebug() << "ID of instance to launch : " << m_instanceIdToLaunch;
}
if (!m_serverToJoin.isEmpty()) {
qInfo() << "Address of server to join :" << m_serverToJoin;
qDebug() << "Address of server to join :" << m_serverToJoin;
} else if (!m_worldToJoin.isEmpty()) {
qInfo() << "Name of the world to join :" << m_worldToJoin;
qDebug() << "Name of the world to join :" << m_worldToJoin;
}
qInfo() << "<> Paths set.";
qDebug() << "<> Paths set.";
}
if (m_liveCheck) {
@ -696,7 +605,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
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");
@ -790,9 +698,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
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
@ -884,6 +790,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_globalSettingsProvider->addPage<MinecraftPage>();
m_globalSettingsProvider->addPage<JavaPage>();
m_globalSettingsProvider->addPage<LanguagePage>();
m_globalSettingsProvider->addPage<CustomCommandsPage>();
m_globalSettingsProvider->addPage<EnvironmentVariablesPage>();
m_globalSettingsProvider->addPage<ProxyPage>();
m_globalSettingsProvider->addPage<ExternalToolsPage>();
m_globalSettingsProvider->addPage<AccountListPage>();
@ -892,7 +800,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
PixmapCache::setInstance(new PixmapCache(this));
qInfo() << "<> Settings loaded.";
qDebug() << "<> Settings loaded.";
}
#ifndef QT_NO_ACCESSIBILITY
@ -908,7 +816,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);
qInfo() << "<> Network done.";
qDebug() << "<> Network done.";
}
// load translations
@ -916,8 +824,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);
qInfo() << "Your language is" << bcp47Name;
qInfo() << "<> Translations loaded.";
qDebug() << "Your language is" << bcp47Name;
qDebug() << "<> Translations loaded.";
}
// Instance icons
@ -927,8 +835,8 @@ 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,
[this](const Setting&, QVariant value) { m_icons->directoryChanged(value.toString()); });
qInfo() << "<> Instance icons initialized.";
[&](const Setting&, QVariant value) { m_icons->directoryChanged(value.toString()); });
qDebug() << "<> Instance icons initialized.";
}
// Themes
@ -940,25 +848,25 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// instance path: check for problems with '!' in instance path and warn the user in the log
// and remember that we have to show him a dialog when the gui starts (if it does so)
QString instDir = InstDirSetting->get().toString();
qInfo() << "Instance path : " << instDir;
qDebug() << "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));
connect(InstDirSetting.get(), &Setting::SettingChanged, m_instances.get(), &InstanceList::on_InstFolderChanged);
qInfo() << "Loading Instances...";
qDebug() << "Loading Instances...";
m_instances->loadList();
qInfo() << "<> Instances loaded.";
qDebug() << "<> Instances loaded.";
}
// and accounts
{
m_accounts.reset(new AccountList(this));
qInfo() << "Loading accounts...";
qDebug() << "Loading accounts...";
m_accounts->setListFilePath("accounts.json", true);
m_accounts->loadList();
m_accounts->fillQueue();
qInfo() << "<> Accounts loaded.";
qDebug() << "<> Accounts loaded.";
}
// init the http meta cache
@ -979,7 +887,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_metacache->addBase("meta", QDir("meta").absolutePath());
m_metacache->addBase("java", QDir("cache/java").absolutePath());
m_metacache->Load();
qInfo() << "<> Cache initialized.";
qDebug() << "<> Cache initialized.";
}
// now we have network, download translation updates
@ -1162,11 +1070,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
bool Application::createSetupWizard()
{
bool javaRequired = [this]() {
if (BuildConfig.JAVA_DOWNLOADER_ENABLED && settings()->get("AutomaticJavaDownload").toBool()) {
return false;
}
bool ignoreJavaWizard = settings()->get("IgnoreJavaWizard").toBool();
bool javaRequired = [&]() {
bool ignoreJavaWizard = m_settings->get("IgnoreJavaWizard").toBool();
if (ignoreJavaWizard) {
return false;
}
@ -1178,10 +1083,13 @@ bool Application::createSetupWizard()
}
QString currentJavaPath = settings()->get("JavaPath").toString();
QString actualPath = FS::ResolveExecutable(currentJavaPath);
return actualPath.isNull();
if (actualPath.isNull()) {
return true;
}
return false;
}();
bool askjava = BuildConfig.JAVA_DOWNLOADER_ENABLED && !javaRequired && !settings()->get("AutomaticJavaDownload").toBool() &&
!settings()->get("AutomaticJavaSwitch").toBool() && !settings()->get("UserAskedAboutAutomaticJavaDownload").toBool();
bool askjava = BuildConfig.JAVA_DOWNLOADER_ENABLED && !javaRequired && !m_settings->get("AutomaticJavaDownload").toBool() &&
!m_settings->get("AutomaticJavaSwitch").toBool() && !m_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());
@ -1193,16 +1101,8 @@ bool Application::createSetupWizard()
// set default theme after going into theme wizard
if (!validIcons)
settings()->set("IconTheme", QString("pe_colored"));
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);
}
if (!validWidgets)
settings()->set("ApplicationTheme", QString("system"));
m_themeManager->applyCurrentlySelectedTheme(true);
@ -1269,9 +1169,6 @@ bool Application::event(QEvent* event)
#endif
if (event->type() == QEvent::FileOpen) {
if (!m_mainWindow) {
showMainWindow(false);
}
auto ev = static_cast<QFileOpenEvent*>(event);
m_mainWindow->processURLs({ ev->url() });
}
@ -1312,7 +1209,7 @@ void Application::performMainStartupAction()
qDebug() << " Launching with account" << m_profileToUse;
}
launch(inst, !m_offline, false, targetToJoin, accountToUse, m_offlineName);
launch(inst, true, false, targetToJoin, accountToUse);
return;
}
}
@ -1405,17 +1302,12 @@ 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;
if (!id.isEmpty()) {
@ -1445,7 +1337,7 @@ void Application::messageReceived(const QByteArray& message)
}
}
launch(instance, !offline, false, serverObject, accountObject, offlineName);
launch(instance, true, false, serverObject, accountObject);
} else {
qWarning() << "Received invalid message" << message;
}
@ -1483,17 +1375,11 @@ bool Application::openJsonEditor(const QString& filename)
}
}
bool Application::launch(InstancePtr instance,
bool online,
bool demo,
MinecraftTarget::Ptr targetToJoin,
MinecraftAccountPtr accountToUse,
const QString& offlineName)
bool Application::launch(InstancePtr instance, bool online, bool demo, MinecraftTarget::Ptr targetToJoin, MinecraftAccountPtr accountToUse)
{
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) {
@ -1509,7 +1395,6 @@ bool Application::launch(InstancePtr instance,
controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get());
controller->setTargetToJoin(targetToJoin);
controller->setAccountToUse(accountToUse);
controller->setOfflineName(offlineName);
if (window) {
controller->setParentWidget(window);
} else if (m_mainWindow) {
@ -1519,7 +1404,7 @@ bool Application::launch(InstancePtr instance,
connect(controller.get(), &LaunchController::failed, this, &Application::controllerFailed);
connect(controller.get(), &LaunchController::aborted, this, [this] { controllerFailed(tr("Aborted")); });
addRunningInstance();
QMetaObject::invokeMethod(controller.get(), &Task::start, Qt::QueuedConnection);
controller->start();
return true;
} else if (instance->isRunning()) {
showInstanceWindow(instance, "console");
@ -1537,11 +1422,9 @@ bool Application::kill(InstancePtr instance)
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;
locker.unlock();
if (controller) {
return controller->abort();
}
@ -1595,14 +1478,12 @@ void Application::controllerSucceeded()
if (!controller)
return;
auto id = controller->id();
QMutexLocker locker(&m_instanceExtrasMutex);
auto& extras = m_instanceExtras[id];
// on success, do...
if (controller->instance()->settings()->get("AutoCloseConsole").toBool()) {
if (extras.window) {
QMetaObject::invokeMethod(extras.window, &QWidget::close, Qt::QueuedConnection);
extras.window->close();
}
}
extras.controller.reset();
@ -1622,7 +1503,6 @@ void Application::controllerFailed(const QString& error)
if (!controller)
return;
auto id = controller->id();
QMutexLocker locker(&m_instanceExtrasMutex);
auto& extras = m_instanceExtras[id];
// on failure, do... nothing
@ -1680,7 +1560,6 @@ InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString pa
if (!instance)
return nullptr;
auto id = instance->id();
QMutexLocker locker(&m_instanceExtrasMutex);
auto& extras = m_instanceExtras[id];
auto& window = extras.window;
@ -1718,7 +1597,6 @@ void Application::on_windowClose()
m_openWindows--;
auto instWindow = qobject_cast<InstanceWindow*>(QObject::sender());
if (instWindow) {
QMutexLocker locker(&m_instanceExtrasMutex);
auto& extras = m_instanceExtras[instWindow->instanceId()];
extras.window = nullptr;
if (extras.controller) {
@ -1966,7 +1844,7 @@ bool Application::handleDataMigration(const QString& currentData,
matcher->add(std::make_shared<SimplePrefixMatcher>("themes/"));
ProgressDialog diag;
DataMigrationTask task(oldData, currentData, matcher);
DataMigrationTask task(nullptr, oldData, currentData, matcher);
if (diag.execWithTask(&task)) {
qDebug() << "<> Migration succeeded";
setDoNotMigrate();
@ -2005,31 +1883,3 @@ 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;
}

View File

@ -42,7 +42,6 @@
#include <QDebug>
#include <QFlag>
#include <QIcon>
#include <QMutex>
#include <QUrl>
#include <memory>
@ -112,7 +111,7 @@ class Application : public QApplication {
std::shared_ptr<SettingsObject> settings() const { return m_settings; }
qint64 timeSinceStart() const { return m_startTime.msecsTo(QDateTime::currentDateTime()); }
qint64 timeSinceStart() const { return startTime.msecsTo(QDateTime::currentDateTime()); }
QIcon getThemedIcon(const QString& name);
@ -211,8 +210,7 @@ class Application : public QApplication {
bool online = true,
bool demo = false,
MinecraftTarget::Ptr targetToJoin = nullptr,
MinecraftAccountPtr accountToUse = nullptr,
const QString& offlineName = QString());
MinecraftAccountPtr accountToUse = nullptr);
bool kill(InstancePtr instance);
void closeCurrentWindow();
@ -237,7 +235,7 @@ class Application : public QApplication {
bool shouldExitNow() const;
private:
QDateTime m_startTime;
QDateTime startTime;
shared_qobject_ptr<QNetworkAccessManager> m_network;
@ -280,7 +278,6 @@ class Application : public QApplication {
shared_qobject_ptr<LaunchController> controller;
};
std::map<QString, InstanceXtras> m_instanceExtras;
mutable QMutex m_instanceExtrasMutex;
// main state variables
size_t m_openWindows = 0;
@ -302,19 +299,8 @@ class Application : public QApplication {
QString m_serverToJoin;
QString m_worldToJoin;
QString m_profileToUse;
bool m_offline = false;
QString m_offlineName;
bool m_liveCheck = false;
QList<QUrl> m_urlsToImport;
QString m_instanceIdToShowWindowOf;
std::unique_ptr<QFile> logFile;
public:
void addQSavePath(QString);
void removeQSavePath(QString);
bool checkQSavePath(QString);
private:
QHash<QString, int> m_qsaveResources;
mutable QMutex m_qsaveResourcesMutex;
};

View File

@ -42,8 +42,8 @@
#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonObject>
#include <QRegularExpression>
#include "Application.h"
#include "settings/INISettingsObject.h"
#include "settings/OverrideSetting.h"
#include "settings/Setting.h"
@ -174,12 +174,6 @@ void BaseInstance::copyManagedPack(BaseInstance& other)
m_settings->set("ManagedPackName", other.getManagedPackName());
m_settings->set("ManagedPackVersionID", other.getManagedPackVersionID());
m_settings->set("ManagedPackVersionName", other.getManagedPackVersionName());
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", "");
}
}
int BaseInstance::getConsoleMaxLines() const
@ -392,12 +386,6 @@ 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);
}
QString BaseInstance::name() const
{
return m_settings->get("name").toString();
@ -423,8 +411,3 @@ void BaseInstance::updateRuntimeContext()
{
// NOOP
}
bool BaseInstance::isLegacy()
{
return traits().contains("legacyLaunch") || traits().contains("alphaLaunch");
}

View File

@ -126,9 +126,6 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
QString name() const;
void setName(QString val);
/// Sync name and rename instance dir accordingly; returns true if successful
bool syncInstanceDirName(const QString& newRoot) const;
/// Value used for instance window titles
QString windowTitle() const;
@ -151,6 +148,9 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
void setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version);
void copyManagedPack(BaseInstance& other);
/// guess log level from a line of game log
virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString& line, MessageLevel::Enum level) { return level; }
virtual QStringList extraArguments();
/// Traits. Normally inside the version, depends on instance implementation.
@ -195,10 +195,15 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
virtual QProcessEnvironment createEnvironment() = 0;
virtual QProcessEnvironment createLaunchEnvironment() = 0;
/*!
* Returns a matcher that can maps relative paths within the instance to whether they are 'log files'
*/
virtual IPathMatcher::Ptr getLogFileMatcher() = 0;
/*!
* Returns the root folder to use for looking up log files
*/
virtual QStringList getLogFileSearchPaths() = 0;
virtual QString getLogFileRoot() = 0;
virtual QString getStatusbarDescription() = 0;
@ -264,8 +269,6 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
bool removeLinkedInstanceId(const QString& id);
bool isLinkedToInstanceId(const QString& id) const;
bool isLegacy();
protected:
void changeStatus(Status newStatus);

View File

@ -21,8 +21,6 @@ set(CORE_SOURCES
BaseVersion.h
BaseInstance.h
BaseInstance.cpp
InstanceDirUpdate.h
InstanceDirUpdate.cpp
NullInstance.h
MMCZip.h
MMCZip.cpp
@ -32,7 +30,6 @@ set(CORE_SOURCES
StringUtils.cpp
QVariantUtils.h
RuntimeContext.h
PSaveFile.h
# Basic instance manipulation tasks (derived from InstanceTask)
InstanceCreationTask.h
@ -99,7 +96,7 @@ set(CORE_SOURCES
MTPixmapCache.h
)
if (UNIX AND NOT CYGWIN AND NOT APPLE)
set(CORE_SOURCES
set(CORE_SOURCES
${CORE_SOURCES}
# MangoHud
@ -175,8 +172,6 @@ set(LAUNCH_SOURCES
launch/LogModel.h
launch/TaskStepWrapper.cpp
launch/TaskStepWrapper.h
logs/LogParser.cpp
logs/LogParser.h
)
# Old update system
@ -349,12 +344,13 @@ set(MINECRAFT_SOURCES
minecraft/mod/TexturePackFolderModel.h
minecraft/mod/TexturePackFolderModel.cpp
minecraft/mod/ShaderPackFolderModel.h
minecraft/mod/tasks/ResourceFolderLoadTask.h
minecraft/mod/tasks/ResourceFolderLoadTask.cpp
minecraft/mod/tasks/BasicFolderLoadTask.h
minecraft/mod/tasks/ModFolderLoadTask.h
minecraft/mod/tasks/ModFolderLoadTask.cpp
minecraft/mod/tasks/LocalModParseTask.h
minecraft/mod/tasks/LocalModParseTask.cpp
minecraft/mod/tasks/LocalResourceUpdateTask.h
minecraft/mod/tasks/LocalResourceUpdateTask.cpp
minecraft/mod/tasks/LocalModUpdateTask.h
minecraft/mod/tasks/LocalModUpdateTask.cpp
minecraft/mod/tasks/LocalDataPackParseTask.h
minecraft/mod/tasks/LocalDataPackParseTask.cpp
minecraft/mod/tasks/LocalResourcePackParseTask.h
@ -591,8 +587,8 @@ set(ATLAUNCHER_SOURCES
)
set(LINKEXE_SOURCES
console/WindowsConsole.h
console/WindowsConsole.cpp
WindowsConsole.cpp
WindowsConsole.h
filelink/FileLink.h
filelink/FileLink.cpp
@ -661,14 +657,6 @@ 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
@ -796,9 +784,6 @@ SET(LAUNCHER_SOURCES
SysInfo.h
SysInfo.cpp
# console utils
console/Console.h
# GUI - general utilities
DesktopServices.h
DesktopServices.cpp
@ -825,8 +810,7 @@ SET(LAUNCHER_SOURCES
resources/flat/flat.qrc
resources/flat_white/flat_white.qrc
resources/documents/documents.qrc
resources/shaders/shaders.qrc
"${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_LogoQRC}"
../${Launcher_Branding_LogoQRC}
# Icons
icons/MMCIcon.h
@ -924,6 +908,7 @@ 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
@ -933,22 +918,21 @@ 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
@ -1046,6 +1030,8 @@ SET(LAUNCHER_SOURCES
ui/dialogs/CopyInstanceDialog.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
@ -1082,21 +1068,14 @@ SET(LAUNCHER_SOURCES
ui/dialogs/BlockedModsDialog.h
ui/dialogs/ChooseProviderDialog.h
ui/dialogs/ChooseProviderDialog.cpp
ui/dialogs/ResourceUpdateDialog.cpp
ui/dialogs/ResourceUpdateDialog.h
ui/dialogs/ModUpdateDialog.cpp
ui/dialogs/ModUpdateDialog.h
ui/dialogs/InstallLoaderDialog.cpp
ui/dialogs/InstallLoaderDialog.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
@ -1106,12 +1085,14 @@ SET(LAUNCHER_SOURCES
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/JavaWizardWidget.cpp
ui/widgets/JavaWizardWidget.h
ui/widgets/JavaSettingsWidget.cpp
ui/widgets/JavaSettingsWidget.h
ui/widgets/LabeledToolButton.cpp
ui/widgets/LabeledToolButton.h
ui/widgets/LanguageSelectionWidget.cpp
@ -1147,10 +1128,6 @@ SET(LAUNCHER_SOURCES
ui/widgets/WideBar.cpp
ui/widgets/ThemeCustomizationWidget.h
ui/widgets/ThemeCustomizationWidget.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
@ -1167,7 +1144,7 @@ SET(LAUNCHER_SOURCES
)
if (NOT Apple)
set(LAUNCHER_SOURCES
set(LAUNCHER_SOURCES
${LAUNCHER_SOURCES}
ui/dialogs/UpdateAvailableDialog.h
@ -1177,8 +1154,8 @@ endif()
if(WIN32)
set(LAUNCHER_SOURCES
console/WindowsConsole.h
console/WindowsConsole.cpp
WindowsConsole.cpp
WindowsConsole.h
${LAUNCHER_SOURCES}
)
endif()
@ -1194,6 +1171,7 @@ qt_wrap_ui(LAUNCHER_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
@ -1201,6 +1179,7 @@ qt_wrap_ui(LAUNCHER_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
@ -1223,8 +1202,6 @@ qt_wrap_ui(LAUNCHER_UI
ui/widgets/ModFilterWidget.ui
ui/widgets/SubTaskProgressBar.ui
ui/widgets/ThemeCustomizationWidget.ui
ui/widgets/MinecraftSettingsWidget.ui
ui/widgets/JavaSettingsWidget.ui
ui/dialogs/CopyInstanceDialog.ui
ui/dialogs/ProfileSetupDialog.ui
ui/dialogs/ProgressDialog.ui
@ -1240,10 +1217,12 @@ qt_wrap_ui(LAUNCHER_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
)
@ -1268,8 +1247,7 @@ qt_add_resources(LAUNCHER_RESOURCES
resources/iOS/iOS.qrc
resources/flat/flat.qrc
resources/documents/documents.qrc
resources/shaders/shaders.qrc
"${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_LogoQRC}"
../${Launcher_Branding_LogoQRC}
)
qt_wrap_ui(PRISMUPDATER_UI
@ -1302,7 +1280,7 @@ target_link_libraries(Launcher_logic
qdcss
BuildConfig
Qt${QT_VERSION_MAJOR}::Widgets
qrcode
ghcFilesystem::ghc_filesystem
)
if (UNIX AND NOT CYGWIN AND NOT APPLE)
@ -1319,8 +1297,6 @@ target_link_libraries(Launcher_logic
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
@ -1329,10 +1305,6 @@ target_link_libraries(Launcher_logic
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/")
@ -1389,6 +1361,7 @@ if(Launcher_BUILD_UPDATER)
${ZLIB_LIBRARIES}
systeminfo
BuildConfig
ghcFilesystem::ghc_filesystem
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Network
@ -1427,6 +1400,7 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
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

View File

@ -12,10 +12,13 @@
#include <QtConcurrent>
DataMigrationTask::DataMigrationTask(const QString& sourcePath, const QString& targetPath, const IPathMatcher::Ptr pathMatcher)
: Task(), m_sourcePath(sourcePath), m_targetPath(targetPath), m_pathMatcher(pathMatcher), m_copy(sourcePath, targetPath)
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)
{
m_copy.matcher(m_pathMatcher).whitelist(true);
m_copy.matcher(m_pathMatcher.get()).whitelist(true);
}
void DataMigrationTask::executeTask()
@ -24,7 +27,7 @@ void DataMigrationTask::executeTask()
// 1. Scan
// Check how many files we gotta copy
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] {
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] {
return m_copy(true); // dry run to collect amount of files
});
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::dryRunFinished);
@ -37,7 +40,11 @@ void DataMigrationTask::dryRunFinished()
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::dryRunFinished);
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::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;
}
@ -53,7 +60,7 @@ void DataMigrationTask::dryRunFinished()
setProgress(m_copy.totalCopied(), m_toCopy);
setStatus(tr("Copying %1…").arg(shortenedName));
});
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] {
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] {
return m_copy(false); // actually copy now
});
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::copyFinished);
@ -71,7 +78,11 @@ void DataMigrationTask::copyFinished()
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::copyFinished);
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::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;
}

View File

@ -18,7 +18,7 @@
class DataMigrationTask : public Task {
Q_OBJECT
public:
explicit DataMigrationTask(const QString& sourcePath, const QString& targetPath, IPathMatcher::Ptr pathmatcher);
explicit DataMigrationTask(QObject* parent, const QString& sourcePath, const QString& targetPath, IPathMatcher::Ptr pathmatcher);
~DataMigrationTask() override = default;
protected:

View File

@ -40,11 +40,12 @@
#include <QFileSystemModel>
#include <QSortFilterProxyModel>
#include <QStack>
#include <algorithm>
#include "FileSystem.h"
#include "SeparatorPrefixTree.h"
#include "StringUtils.h"
FileIgnoreProxy::FileIgnoreProxy(QString root, QObject* parent) : QSortFilterProxyModel(parent), m_root(root) {}
FileIgnoreProxy::FileIgnoreProxy(QString root, QObject* parent) : QSortFilterProxyModel(parent), root(root) {}
// NOTE: Sadly, we have to do sorting ourselves.
bool FileIgnoreProxy::lessThan(const QModelIndex& left, const QModelIndex& right) const
{
@ -103,10 +104,10 @@ QVariant FileIgnoreProxy::data(const QModelIndex& index, int role) const
if (index.column() == 0 && role == Qt::CheckStateRole) {
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
auto blockedPath = relPath(fsm->filePath(sourceIndex));
auto cover = m_blocked.cover(blockedPath);
auto cover = blocked.cover(blockedPath);
if (!cover.isNull()) {
return QVariant(Qt::Unchecked);
} else if (m_blocked.exists(blockedPath)) {
} else if (blocked.exists(blockedPath)) {
return QVariant(Qt::PartiallyChecked);
} else {
return QVariant(Qt::Checked);
@ -129,7 +130,7 @@ bool FileIgnoreProxy::setData(const QModelIndex& index, const QVariant& value, i
QString FileIgnoreProxy::relPath(const QString& path) const
{
return QDir(m_root).relativeFilePath(path);
return QDir(root).relativeFilePath(path);
}
bool FileIgnoreProxy::setFilterState(QModelIndex index, Qt::CheckState state)
@ -145,18 +146,18 @@ bool FileIgnoreProxy::setFilterState(QModelIndex index, Qt::CheckState state)
bool changed = false;
if (state == Qt::Unchecked) {
// blocking a path
auto& node = m_blocked.insert(blockedPath);
auto& node = blocked.insert(blockedPath);
// get rid of all blocked nodes below
node.clear();
changed = true;
} else if (state == Qt::Checked || state == Qt::PartiallyChecked) {
if (!m_blocked.remove(blockedPath)) {
auto cover = m_blocked.cover(blockedPath);
if (!blocked.remove(blockedPath)) {
auto cover = blocked.cover(blockedPath);
qDebug() << "Blocked by cover" << cover;
// uncover
m_blocked.remove(cover);
blocked.remove(cover);
// block all contents, except for any cover
QModelIndex rootIndex = fsm->index(FS::PathCombine(m_root, cover));
QModelIndex rootIndex = fsm->index(FS::PathCombine(root, cover));
QModelIndex doing = rootIndex;
int row = 0;
QStack<QModelIndex> todo;
@ -178,7 +179,7 @@ bool FileIgnoreProxy::setFilterState(QModelIndex index, Qt::CheckState state)
todo.push(node);
} else {
// or just block this one.
m_blocked.insert(relpath);
blocked.insert(relpath);
}
row++;
}
@ -228,7 +229,7 @@ bool FileIgnoreProxy::shouldExpand(QModelIndex index)
return false;
}
auto blockedPath = relPath(fsm->filePath(sourceIndex));
auto found = m_blocked.find(blockedPath);
auto found = blocked.find(blockedPath);
if (found) {
return !found->leaf();
}
@ -238,8 +239,8 @@ bool FileIgnoreProxy::shouldExpand(QModelIndex index)
void FileIgnoreProxy::setBlockedPaths(QStringList paths)
{
beginResetModel();
m_blocked.clear();
m_blocked.insert(paths);
blocked.clear();
blocked.insert(paths);
endResetModel();
}
@ -269,28 +270,7 @@ bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const
return m_ignoreFiles.contains(fileInfo.fileName()) || m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath()));
}
bool FileIgnoreProxy::filterFile(const QFileInfo& file) const
bool FileIgnoreProxy::filterFile(const QString& fileName) const
{
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();
}
return blocked.covers(fileName) || ignoreFile(QFileInfo(QDir(root), fileName));
}

View File

@ -61,19 +61,15 @@ class FileIgnoreProxy : public QSortFilterProxyModel {
void setBlockedPaths(QStringList paths);
inline const SeparatorPrefixTree<'/'>& blockedPaths() const { return m_blocked; }
inline SeparatorPrefixTree<'/'>& blockedPaths() { return m_blocked; }
inline const SeparatorPrefixTree<'/'>& blockedPaths() const { return blocked; }
inline SeparatorPrefixTree<'/'>& blockedPaths() { return blocked; }
// list of file names that need to be removed completely from model
inline QStringList& ignoreFilesWithName() { return m_ignoreFiles; }
// list of relative paths that need to be removed completely from model
inline SeparatorPrefixTree<'/'>& ignoreFilesWithPath() { return m_ignoreFilePaths; }
bool filterFile(const QFileInfo& fileName) const;
void loadBlockedPathsFromFile(const QString& fileName);
void saveBlockedPathsToFile(const QString& fileName);
bool filterFile(const QString& fileName) const;
protected:
bool filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const;
@ -82,8 +78,8 @@ class FileIgnoreProxy : public QSortFilterProxyModel {
bool ignoreFile(QFileInfo file) const;
private:
const QString m_root;
SeparatorPrefixTree<'/'> m_blocked;
const QString root;
SeparatorPrefixTree<'/'> blocked;
QStringList m_ignoreFiles;
SeparatorPrefixTree<'/'> m_ignoreFilePaths;
};

View File

@ -45,6 +45,7 @@
#include <QDirIterator>
#include <QFile>
#include <QFileInfo>
#include <QSaveFile>
#include <QStandardPaths>
#include <QStorageInfo>
#include <QTextStream>
@ -53,7 +54,6 @@
#include <system_error>
#include "DesktopServices.h"
#include "PSaveFile.h"
#include "StringUtils.h"
#if defined Q_OS_WIN32
@ -77,8 +77,24 @@
#include <utime.h>
#endif
// Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header
#ifdef __APPLE__
#include <Availability.h> // 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(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#define GHC_USE_STD_FS
#include <filesystem>
namespace fs = std::filesystem;
#endif // MacOS min version check
#endif // Other OSes version check
#ifndef GHC_USE_STD_FS
#include <ghc/filesystem.hpp>
namespace fs = ghc::filesystem;
#endif
// clone
#if defined(Q_OS_LINUX)
@ -175,8 +191,8 @@ void ensureExists(const QDir& dir)
void write(const QString& filename, const QByteArray& data)
{
ensureExists(QFileInfo(filename).dir());
PSaveFile file(filename);
if (!file.open(PSaveFile::WriteOnly)) {
QSaveFile file(filename);
if (!file.open(QSaveFile::WriteOnly)) {
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
}
if (data.size() != file.write(data)) {
@ -197,8 +213,8 @@ void appendSafe(const QString& filename, const QByteArray& data)
buffer = QByteArray();
}
buffer.append(data);
PSaveFile file(filename);
if (!file.open(PSaveFile::WriteOnly)) {
QSaveFile file(filename);
if (!file.open(QSaveFile::WriteOnly)) {
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
}
if (buffer.size() != file.write(buffer)) {
@ -325,7 +341,7 @@ bool copy::operator()(const QString& offset, bool dryRun)
opt |= copy_opts::overwrite_existing;
// Function that'll do the actual copying
auto copy_file = [this, dryRun, src, dst, opt, &err](QString src_path, QString relative_dst_path) {
auto copy_file = [&](QString src_path, QString relative_dst_path) {
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
return;
@ -412,7 +428,7 @@ void create_link::make_link_list(const QString& offset)
m_recursive = true;
// Function that'll do the actual linking
auto link_file = [this, dst](QString src_path, QString relative_dst_path) {
auto link_file = [&](QString src_path, QString relative_dst_path) {
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist)) {
qDebug() << "path" << relative_dst_path << "in black list or not in whitelist";
return;
@ -507,7 +523,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, [this, &gotResults]() {
connect(&m_linkServer, &QLocalServer::newConnection, this, [&]() {
qDebug() << "Client connected, sending out pairs";
// construct block of data to send
QByteArray block;
@ -589,7 +605,7 @@ void create_link::runPrivileged(const QString& offset)
}
ExternalLinkFileProcess* linkFileProcess = new ExternalLinkFileProcess(serverName, m_useHardLinks, this);
connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [this, gotResults]() { emit finishedPrivileged(gotResults); });
connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [&]() { emit finishedPrivileged(gotResults); });
connect(linkFileProcess, &ExternalLinkFileProcess::finished, linkFileProcess, &QObject::deleteLater);
linkFileProcess->start();
@ -679,6 +695,9 @@ 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;
@ -687,6 +706,7 @@ bool trash(QString path, QString* pathInTrash)
return false;
#endif
return QFile::moveToTrash(path, pathInTrash);
#endif
}
QString PathCombine(const QString& path1, const QString& path2)
@ -720,7 +740,11 @@ 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(".");
@ -740,7 +764,11 @@ 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();
@ -922,7 +950,7 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
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";
@ -943,7 +971,8 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
if (!args.empty())
argstring = " \"" + args.join("\" \"") + "\"";
stream << "#!/bin/bash" << "\n";
stream << "#!/bin/bash"
<< "\n";
stream << "\"" << target << "\" " << argstring << "\n";
stream.flush();
@ -987,9 +1016,12 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
if (!args.empty())
argstring = " '" + args.join("' '") + "'";
stream << "[Desktop Entry]" << "\n";
stream << "Type=Application" << "\n";
stream << "Categories=Game;ActionGame;AdventureGame;Simulation" << "\n";
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()) {
@ -1267,7 +1299,7 @@ bool clone::operator()(const QString& offset, bool dryRun)
std::error_code err;
// Function that'll do the actual cloneing
auto cloneFile = [this, dryRun, dst, &err](QString src_path, QString relative_dst_path) {
auto cloneFile = [&](QString src_path, QString relative_dst_path) {
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
return;

View File

@ -115,7 +115,7 @@ class copy : public QObject {
m_followSymlinks = follow;
return *this;
}
copy& matcher(IPathMatcher::Ptr filter)
copy& matcher(const IPathMatcher* filter)
{
m_matcher = filter;
return *this;
@ -147,7 +147,7 @@ class copy : public QObject {
private:
bool m_followSymlinks = true;
IPathMatcher::Ptr m_matcher = nullptr;
const IPathMatcher* m_matcher = nullptr;
bool m_whitelist = false;
bool m_overwrite = false;
QDir m_src;
@ -209,7 +209,7 @@ class create_link : public QObject {
m_useHardLinks = useHard;
return *this;
}
create_link& matcher(IPathMatcher::Ptr filter)
create_link& matcher(const IPathMatcher* filter)
{
m_matcher = filter;
return *this;
@ -260,7 +260,7 @@ class create_link : public QObject {
private:
bool m_useHardLinks = false;
IPathMatcher::Ptr m_matcher = nullptr;
const IPathMatcher* m_matcher = nullptr;
bool m_whitelist = false;
bool m_recursive = true;
@ -488,7 +488,7 @@ class clone : public QObject {
m_src.setPath(src);
m_dst.setPath(dst);
}
clone& matcher(IPathMatcher::Ptr filter)
clone& matcher(const IPathMatcher* filter)
{
m_matcher = filter;
return *this;
@ -514,7 +514,7 @@ class clone : public QObject {
bool operator()(const QString& offset, bool dryRun = false);
private:
IPathMatcher::Ptr m_matcher = nullptr;
const IPathMatcher* m_matcher = nullptr;
bool m_whitelist = false;
QDir m_src;
QDir m_dst;

View File

@ -36,8 +36,6 @@
#include "GZip.h"
#include <zlib.h>
#include <QByteArray>
#include <QDebug>
#include <QFile>
bool GZip::unzip(const QByteArray& compressedBytes, QByteArray& uncompressedBytes)
{
@ -138,81 +136,3 @@ bool GZip::zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes)
}
return true;
}
int inf(QFile* source, std::function<bool(const QByteArray&)> 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<Bytef*>(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; /* and fall through */
case Z_DATA_ERROR:
case Z_MEM_ERROR:
(void)inflateEnd(&strm);
return ret;
}
have = CHUNK - strm.avail_out;
if (!handleBlock(QByteArray(reinterpret_cast<const char*>(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<bool(const QByteArray&)> handleBlock)
{
auto ret = inf(source, handleBlock);
return zerr(ret);
}

View File

@ -1,11 +1,8 @@
#pragma once
#include <QByteArray>
#include <QFile>
namespace GZip {
bool unzip(const QByteArray& compressedBytes, QByteArray& uncompressedBytes);
bool zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes);
QString readGzFileByBlocks(QFile* source, std::function<bool(const QByteArray&)> handleBlock);
} // namespace GZip
class GZip {
public:
static bool unzip(const QByteArray& compressedBytes, QByteArray& uncompressedBytes);
static bool zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes);
};

View File

@ -43,7 +43,7 @@ void InstanceCopyTask::executeTask()
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] {
if (m_useClone) {
FS::clone folderClone(m_origInstance->instanceRoot(), m_stagingPath);
folderClone.matcher(m_matcher);
folderClone.matcher(m_matcher.get());
folderClone(true);
setProgress(0, folderClone.totalCloned());
@ -72,7 +72,7 @@ void InstanceCopyTask::executeTask()
}
FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath);
int depth = m_linkRecursively ? -1 : 0; // we need to at least link the top level instead of the instance folder
folderLink.linkRecursively(true).setMaxDepth(depth).useHardLinks(m_useHardLinks).matcher(m_matcher);
folderLink.linkRecursively(true).setMaxDepth(depth).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
folderLink(true);
setProgress(0, m_progressTotal + folderLink.totalToLink());
@ -91,7 +91,7 @@ void InstanceCopyTask::executeTask()
QEventLoop loop;
bool got_priv_results = false;
connect(&folderLink, &FS::create_link::finishedPrivileged, this, [&got_priv_results, &loop](bool gotResults) {
connect(&folderLink, &FS::create_link::finishedPrivileged, this, [&](bool gotResults) {
if (!gotResults) {
qDebug() << "Privileged run exited without results!";
}
@ -127,7 +127,7 @@ void InstanceCopyTask::executeTask()
return !there_were_errors;
}
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
folderCopy.followSymlinks(false).matcher(m_matcher);
folderCopy.followSymlinks(false).matcher(m_matcher.get());
folderCopy(true);
setProgress(0, folderCopy.totalCopied());

View File

@ -28,7 +28,7 @@ class InstanceCopyTask : public InstanceTask {
InstancePtr m_origInstance;
QFuture<bool> m_copyFuture;
QFutureWatcher<bool> m_copyFutureWatcher;
IPathMatcher::Ptr m_matcher;
std::unique_ptr<IPathMatcher> m_matcher;
bool m_keepPlaytime;
bool m_useLinks = false;
bool m_useHardLinks = false;

View File

@ -61,6 +61,6 @@ void InstanceCreationTask::executeTask()
return;
}
}
if (!m_abort)
emitSucceeded();
}

View File

@ -1,126 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "InstanceDirUpdate.h"
#include <QCheckBox>
#include "Application.h"
#include "FileSystem.h"
#include "InstanceList.h"
#include "ui/dialogs/CustomMessageBox.h"
QString askToUpdateInstanceDirName(InstancePtr instance, const QString& oldName, const QString& newName, QWidget* parent)
{
if (oldName == newName)
return QString();
QString renamingMode = APPLICATION->settings()->get("InstRenamingMode").toString();
if (renamingMode == "MetadataOnly")
return QString();
auto oldRoot = instance->instanceRoot();
auto newDirName = FS::DirNameFromString(newName, QFileInfo(oldRoot).dir().absolutePath());
auto newRoot = FS::PathCombine(QFileInfo(oldRoot).dir().absolutePath(), newDirName);
if (oldRoot == newRoot)
return QString();
if (oldRoot == FS::PathCombine(QFileInfo(oldRoot).dir().absolutePath(), newName))
return QString();
// Check for conflict
if (QDir(newRoot).exists()) {
QMessageBox::warning(parent, QObject::tr("Cannot rename instance"),
QObject::tr("New instance root (%1) already exists. <br />Only the metadata will be renamed.").arg(newRoot));
return QString();
}
// Ask if we should rename
if (renamingMode == "AskEverytime") {
auto checkBox = new QCheckBox(QObject::tr("&Remember my choice"), parent);
auto dialog =
CustomMessageBox::selectable(parent, QObject::tr("Rename instance folder"),
QObject::tr("Would you also like to rename the instance folder?\n\n"
"Old name: %1\n"
"New name: %2")
.arg(oldName, newName),
QMessageBox::Question, QMessageBox::No | QMessageBox::Yes, QMessageBox::NoButton, checkBox);
auto res = dialog->exec();
if (checkBox->isChecked()) {
if (res == QMessageBox::Yes)
APPLICATION->settings()->set("InstRenamingMode", "PhysicalDir");
else
APPLICATION->settings()->set("InstRenamingMode", "MetadataOnly");
}
if (res == QMessageBox::No)
return QString();
}
// Check for linked instances
if (!checkLinkedInstances(instance->id(), parent, QObject::tr("Renaming")))
return QString();
// Now we can confirm that a renaming is happening
if (!instance->syncInstanceDirName(newRoot)) {
QMessageBox::warning(parent, QObject::tr("Cannot rename instance"),
QObject::tr("An error occurred when performing the following renaming operation: <br/>"
" - Old instance root: %1<br/>"
" - New instance root: %2<br/>"
"Only the metadata is renamed.")
.arg(oldRoot, newRoot));
return QString();
}
return newRoot;
}
bool checkLinkedInstances(const QString& id, QWidget* parent, const QString& verb)
{
auto linkedInstances = APPLICATION->instances()->getLinkedInstancesById(id);
if (!linkedInstances.empty()) {
auto response = CustomMessageBox::selectable(parent, QObject::tr("There are linked instances"),
QObject::tr("The following instance(s) might reference files in this instance:\n\n"
"%1\n\n"
"%2 it could break the other instance(s), \n\n"
"Do you wish to proceed?",
nullptr, linkedInstances.count())
.arg(linkedInstances.join("\n"))
.arg(verb),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec();
if (response != QMessageBox::Yes)
return false;
}
return true;
}

View File

@ -1,43 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "BaseInstance.h"
/// Update instanceRoot to make it sync with name/id; return newRoot if a directory rename happened
QString askToUpdateInstanceDirName(InstancePtr instance, const QString& oldName, const QString& newName, QWidget* parent);
/// Check if there are linked instances, and display a warning; return true if the operation should proceed
bool checkLinkedInstances(const QString& id, QWidget* parent, const QString& verb);

View File

@ -69,10 +69,9 @@ bool InstanceImportTask::abort()
if (!canAbort())
return false;
bool wasAborted = false;
if (m_task)
wasAborted = m_task->abort();
return wasAborted;
if (task)
task->abort();
return Task::abort();
}
void InstanceImportTask::executeTask()
@ -105,7 +104,7 @@ void InstanceImportTask::downloadFromUrl()
connect(filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
connect(filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::emitFailed);
connect(filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::emitAborted);
m_task.reset(filesNetJob);
task.reset(filesNetJob);
filesNetJob->start();
}
@ -194,7 +193,7 @@ void InstanceImportTask::processZipPack()
stepProgress(*progressStep);
});
connect(zipTask.get(), &Task::succeeded, this, &InstanceImportTask::extractFinished, Qt::QueuedConnection);
connect(zipTask.get(), &Task::succeeded, this, &InstanceImportTask::extractFinished);
connect(zipTask.get(), &Task::aborted, this, &InstanceImportTask::emitAborted);
connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) {
progressStep->state = TaskStepState::Failed;
@ -211,13 +210,12 @@ void InstanceImportTask::processZipPack()
progressStep->status = status;
stepProgress(*progressStep);
});
m_task.reset(zipTask);
task.reset(zipTask);
zipTask->start();
}
void InstanceImportTask::extractFinished()
{
setAbortable(false);
QDir extractDir(m_stagingPath);
qDebug() << "Fixing permissions for extracted pack files...";
@ -291,11 +289,8 @@ void InstanceImportTask::processFlame()
inst_creation_task->setGroup(m_instGroup);
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
auto weak = inst_creation_task.toWeakRef();
connect(inst_creation_task.get(), &Task::succeeded, this, [this, weak] {
if (auto sp = weak.lock()) {
setOverride(sp->shouldOverride(), sp->originalInstanceID());
}
connect(inst_creation_task.get(), &Task::succeeded, this, [this, inst_creation_task] {
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
emitSucceeded();
});
connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed);
@ -304,12 +299,11 @@ void InstanceImportTask::processFlame()
connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus);
connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails);
connect(inst_creation_task.get(), &Task::aborted, this, &InstanceImportTask::emitAborted);
connect(this, &Task::aborted, inst_creation_task.get(), &InstanceCreationTask::abort);
connect(inst_creation_task.get(), &Task::aborted, this, &Task::abort);
connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
m_task.reset(inst_creation_task);
setAbortable(true);
m_task->start();
inst_creation_task->start();
}
void InstanceImportTask::processTechnic()
@ -356,7 +350,7 @@ void InstanceImportTask::processMultiMC()
void InstanceImportTask::processModrinth()
{
shared_qobject_ptr<ModrinthCreationTask> inst_creation_task = nullptr;
ModrinthCreationTask* inst_creation_task = nullptr;
if (!m_extra_info.isEmpty()) {
auto pack_id_it = m_extra_info.constFind("pack_id");
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
@ -373,16 +367,16 @@ void InstanceImportTask::processModrinth()
original_instance_id = original_instance_id_it.value();
inst_creation_task =
makeShared<ModrinthCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
} else {
QString pack_id;
if (!m_sourceUrl.isEmpty()) {
static const QRegularExpression s_regex(R"(data\/([^\/]*)\/versions)");
pack_id = s_regex.match(m_sourceUrl.toString()).captured(1);
QRegularExpression regex(R"(data\/([^\/]*)\/versions)");
pack_id = regex.match(m_sourceUrl.toString()).captured(1);
}
// FIXME: Find a way to get the ID in directly imported ZIPs
inst_creation_task = makeShared<ModrinthCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id);
inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id);
}
inst_creation_task->setName(*this);
@ -390,23 +384,20 @@ void InstanceImportTask::processModrinth()
inst_creation_task->setGroup(m_instGroup);
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
auto weak = inst_creation_task.toWeakRef();
connect(inst_creation_task.get(), &Task::succeeded, this, [this, weak] {
if (auto sp = weak.lock()) {
setOverride(sp->shouldOverride(), sp->originalInstanceID());
}
connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] {
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
emitSucceeded();
});
connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed);
connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress);
connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus);
connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails);
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress);
connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus);
connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails);
connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater);
connect(inst_creation_task.get(), &Task::aborted, this, &InstanceImportTask::emitAborted);
connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
connect(this, &Task::aborted, inst_creation_task, &InstanceCreationTask::abort);
connect(inst_creation_task, &Task::aborted, this, &Task::abort);
connect(inst_creation_task, &Task::abortStatusChanged, this, &Task::setAbortable);
m_task.reset(inst_creation_task);
setAbortable(true);
m_task->start();
inst_creation_task->start();
}

View File

@ -40,13 +40,16 @@
#include <QUrl>
#include "InstanceTask.h"
#include <memory>
#include <optional>
class QuaZip;
class InstanceImportTask : public InstanceTask {
Q_OBJECT
public:
explicit InstanceImportTask(const QUrl& sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {});
virtual ~InstanceImportTask() = default;
bool abort() override;
protected:
@ -67,7 +70,7 @@ class InstanceImportTask : public InstanceTask {
private: /* data */
QUrl m_sourceUrl;
QString m_archivePath;
Task::Ptr m_task;
Task::Ptr task;
enum class ModpackType {
Unknown,
MultiMC,

View File

@ -428,7 +428,7 @@ static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr>&
QList<InstanceId> InstanceList::discoverInstances()
{
qInfo() << "Discovering instances in" << m_instDir;
qDebug() << "Discovering instances in" << m_instDir;
QList<InstanceId> out;
QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden, QDirIterator::FollowSymlinks);
while (iter.hasNext()) {
@ -447,9 +447,13 @@ QList<InstanceId> InstanceList::discoverInstances()
}
auto id = dirInfo.fileName();
out.append(id);
qInfo() << "Found instance ID" << id;
qDebug() << "Found instance ID" << id;
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
instanceSet = QSet<QString>(out.begin(), out.end());
#else
instanceSet = out.toSet();
#endif
m_instancesProbed = true;
return out;
}
@ -464,7 +468,7 @@ InstanceList::InstListError InstanceList::loadList()
if (existingIds.contains(id)) {
auto instPair = existingIds[id];
existingIds.remove(id);
qInfo() << "Should keep and soft-reload" << id;
qDebug() << "Should keep and soft-reload" << id;
} else {
InstancePtr instPtr = loadInstance(id);
if (instPtr) {
@ -483,7 +487,7 @@ InstanceList::InstListError InstanceList::loadList()
int front_bookmark = -1;
int back_bookmark = -1;
int currentItem = -1;
auto removeNow = [this, &front_bookmark, &back_bookmark, &currentItem]() {
auto removeNow = [&]() {
beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark);
m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1);
endRemoveRows();

View File

@ -43,8 +43,11 @@ class InstancePageProvider : protected QObject, public BasePageProvider {
values.append(new ServersPage(onesix));
// values.append(new GameOptionsPage(onesix.get()));
values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots")));
values.append(new InstanceSettingsPage(onesix));
values.append(new OtherLogsPage(inst));
values.append(new InstanceSettingsPage(onesix.get()));
auto logMatcher = inst->getLogFileMatcher();
if (logMatcher) {
values.append(new OtherLogsPage(inst->getLogFileRoot(), logMatcher));
}
return values;
}

View File

@ -41,9 +41,7 @@
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget* parent)
{
static const QRegularExpression s_memRegex("-Xm[sx]");
static const QRegularExpression s_versionRegex("-version:.*");
if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(s_memRegex) || jvmargs.contains("-XX-MaxHeapSize") ||
if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegularExpression("-Xm[sx]")) || jvmargs.contains("-XX-MaxHeapSize") ||
jvmargs.contains("-XX:InitialHeapSize")) {
auto warnStr = QObject::tr(
"You tried to manually set a JVM memory option (using \"-XX:PermSize\", \"-XX-MaxHeapSize\", \"-XX:InitialHeapSize\", \"-Xmx\" "
@ -54,7 +52,7 @@ bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget* parent)
return false;
}
// block lunacy with passing required version to the JVM
if (jvmargs.contains(s_versionRegex)) {
if (jvmargs.contains(QRegularExpression("-version:.*"))) {
auto warnStr = QObject::tr(
"You tried to pass required Java version argument to the JVM (using \"-version:xxx\"). This is not safe and will not be "
"allowed.\n"
@ -118,7 +116,7 @@ void JavaCommon::TestCheck::run()
emit finished();
return;
}
checker.reset(new JavaChecker(m_path, "", 0, 0, 0, 0));
checker.reset(new JavaChecker(m_path, "", 0, 0, 0, 0, this));
connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinished);
checker->start();
}
@ -130,7 +128,7 @@ void JavaCommon::TestCheck::checkFinished(const JavaChecker::Result& result)
emit finished();
return;
}
checker.reset(new JavaChecker(m_path, m_args, m_maxMem, m_maxMem, result.javaVersion.requiresPermGen() ? m_permGen : 0, 0));
checker.reset(new JavaChecker(m_path, m_args, m_maxMem, m_maxMem, result.javaVersion.requiresPermGen() ? m_permGen : 0, 0, this));
connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinishedWithArgs);
checker->start();
}

View File

@ -24,7 +24,7 @@ class TestCheck : public QObject {
TestCheck(QWidget* parent, QString path, QString args, int minMem, int maxMem, int permGen)
: m_parent(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen)
{}
virtual ~TestCheck() = default;
virtual ~TestCheck() {};
void run();

View File

@ -188,10 +188,10 @@ T ensureIsType(const QJsonObject& parent, const QString& key, const T default_ =
}
template <typename T>
QList<T> requireIsArrayOf(const QJsonDocument& doc)
QVector<T> requireIsArrayOf(const QJsonDocument& doc)
{
const QJsonArray array = requireArray(doc);
QList<T> out;
QVector<T> out;
for (const QJsonValue val : array) {
out.append(requireIsType<T>(val, "Document"));
}
@ -199,10 +199,10 @@ QList<T> requireIsArrayOf(const QJsonDocument& doc)
}
template <typename T>
QList<T> ensureIsArrayOf(const QJsonValue& value, const QString& what = "Value")
QVector<T> ensureIsArrayOf(const QJsonValue& value, const QString& what = "Value")
{
const QJsonArray array = ensureIsType<QJsonArray>(value, QJsonArray(), what);
QList<T> out;
QVector<T> out;
for (const QJsonValue val : array) {
out.append(requireIsType<T>(val, what));
}
@ -210,7 +210,7 @@ QList<T> ensureIsArrayOf(const QJsonValue& value, const QString& what = "Value")
}
template <typename T>
QList<T> ensureIsArrayOf(const QJsonValue& value, const QList<T> default_, const QString& what = "Value")
QVector<T> ensureIsArrayOf(const QJsonValue& value, const QVector<T> default_, const QString& what = "Value")
{
if (value.isUndefined()) {
return default_;
@ -220,7 +220,7 @@ QList<T> ensureIsArrayOf(const QJsonValue& value, const QList<T> default_, const
/// @throw JsonException
template <typename T>
QList<T> requireIsArrayOf(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__")
QVector<T> requireIsArrayOf(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__")
{
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
if (!parent.contains(key)) {
@ -230,9 +230,9 @@ QList<T> requireIsArrayOf(const QJsonObject& parent, const QString& key, const Q
}
template <typename T>
QList<T> ensureIsArrayOf(const QJsonObject& parent,
QVector<T> ensureIsArrayOf(const QJsonObject& parent,
const QString& key,
const QList<T>& default_ = QList<T>(),
const QVector<T>& default_ = QVector<T>(),
const QString& what = "__placeholder__")
{
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');

View File

@ -43,7 +43,6 @@
#include "ui/InstanceWindow.h"
#include "ui/MainWindow.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui/dialogs/MSALoginDialog.h"
#include "ui/dialogs/ProfileSelectDialog.h"
#include "ui/dialogs/ProfileSetupDialog.h"
#include "ui/dialogs/ProgressDialog.h"
@ -62,7 +61,7 @@
#include "launch/steps/TextPrint.h"
#include "tasks/Task.h"
LaunchController::LaunchController() : Task() {}
LaunchController::LaunchController(QObject* parent) : Task(parent) {}
void LaunchController::executeTask()
{
@ -182,8 +181,7 @@ void LaunchController::login()
auto name = askOfflineName("Player", m_demo, ok);
if (ok) {
m_session = std::make_shared<AuthSession>();
static const QRegularExpression s_removeChars("[{}-]");
m_session->MakeDemo(name, MinecraftAccount::uuidFromUsername(name).toString().remove(s_removeChars));
m_session->MakeDemo(name, MinecraftAccount::uuidFromUsername(name).toString().remove(QRegularExpression("[{}-]")));
launchInstance();
return;
}
@ -201,7 +199,8 @@ void LaunchController::login()
m_accountToUse->shouldRefresh()) {
// Force account refresh on the account used to launch the instance updating the AccountState
// only on first try and if it is not meant to be offline
m_accountToUse->refresh();
auto accounts = APPLICATION->accounts();
accounts->requestRefresh(m_accountToUse->internalId());
}
while (tryagain) {
if (tries > 0 && tries % 3 == 0) {
@ -220,34 +219,13 @@ void LaunchController::login()
m_session->demo = m_demo;
m_accountToUse->fillSession(m_session);
MinecraftAccountPtr accountToCheck;
if (m_accountToUse->ownsMinecraft())
accountToCheck = m_accountToUse;
else if (const MinecraftAccountPtr defaultAccount = APPLICATION->accounts()->defaultAccount();
defaultAccount != nullptr && defaultAccount->ownsMinecraft()) {
accountToCheck = defaultAccount;
} else {
for (int i = 0; i < APPLICATION->accounts()->count(); i++) {
MinecraftAccountPtr account = APPLICATION->accounts()->at(i);
if (account->ownsMinecraft())
accountToCheck = account;
}
}
if (accountToCheck == nullptr) {
if (!m_session->demo)
m_session->demo = askPlayDemo();
if (m_session->demo)
// Launch immediately in true offline mode
if (m_accountToUse->accountType() == AccountType::Offline) {
launchInstance();
else
emitFailed(tr("Launch cancelled - account does not own Minecraft."));
return;
}
switch (accountToCheck->accountState()) {
switch (m_accountToUse->accountState()) {
case AccountState::Offline: {
m_session->wants_online = false;
}
@ -256,19 +234,16 @@ void LaunchController::login()
if (!m_session->wants_online) {
// we ask the user for a player name
bool ok = false;
QString name;
if (m_offlineName.isEmpty()) {
name = askOfflineName(m_session->player_name, m_session->demo, ok);
auto name = askOfflineName(m_session->player_name, m_session->demo, ok);
if (!ok) {
tryagain = false;
break;
}
} else {
name = m_offlineName;
}
m_session->MakeOffline(name);
// offline flavored game from here :3
} else if (m_accountToUse == accountToCheck && !m_accountToUse->hasProfile()) {
}
if (m_accountToUse->ownsMinecraft()) {
if (!m_accountToUse->hasProfile()) {
// Now handle setting up a profile name here...
ProfileSetupDialog dialog(m_accountToUse, m_parentWidget);
if (dialog.exec() == QDialog::Accepted) {
@ -279,18 +254,26 @@ void LaunchController::login()
return;
}
}
if (m_accountToUse->accountType() == AccountType::Offline)
m_session->wants_online = false;
// we own Minecraft, there is a profile, it's all ready to go!
launchInstance();
return;
} else {
// play demo ?
if (!m_session->demo) {
m_session->demo = askPlayDemo();
}
if (m_session->demo) { // play demo here
launchInstance();
} else {
emitFailed(tr("Launch cancelled - account does not own Minecraft."));
}
}
return;
}
case AccountState::Errored:
// This means some sort of soft error that we can fix with a refresh ... so let's refresh.
case AccountState::Unchecked: {
accountToCheck->refresh();
m_accountToUse->refresh();
}
/* fallthrough */
case AccountState::Working: {
@ -299,19 +282,19 @@ void LaunchController::login()
if (m_online) {
progDialog.setSkipButton(true, tr("Play Offline"));
}
auto task = accountToCheck->currentTask();
auto task = m_accountToUse->currentTask();
progDialog.execWithTask(task.get());
continue;
}
case AccountState::Expired: {
if (reauthenticateAccount(accountToCheck))
continue;
auto errorString = tr("The account has expired and needs to be logged into manually again.");
QMessageBox::warning(m_parentWidget, tr("Account refresh failed"), errorString, QMessageBox::StandardButton::Ok,
QMessageBox::StandardButton::Ok);
emitFailed(errorString);
return;
}
case AccountState::Disabled: {
auto errorString = tr("The launcher's client identification has changed. Please remove '%1' and try again.")
.arg(accountToCheck->profileName());
auto errorString = tr("The launcher's client identification has changed. Please remove this account and add it again.");
QMessageBox::warning(m_parentWidget, tr("Client identification changed"), errorString, QMessageBox::StandardButton::Ok,
QMessageBox::StandardButton::Ok);
emitFailed(errorString);
@ -319,9 +302,8 @@ void LaunchController::login()
}
case AccountState::Gone: {
auto errorString =
tr("'%1' no longer exists on the servers. It may have been migrated, in which case please add the new account "
"you migrated this one to.")
.arg(accountToCheck->profileName());
tr("The account no longer exists on the servers. It may have been migrated, in which case please add the new account "
"you migrated this one to.");
QMessageBox::warning(m_parentWidget, tr("Account gone"), errorString, QMessageBox::StandardButton::Ok,
QMessageBox::StandardButton::Ok);
emitFailed(errorString);
@ -332,38 +314,6 @@ void LaunchController::login()
emitFailed(tr("Failed to launch."));
}
bool LaunchController::reauthenticateAccount(MinecraftAccountPtr account)
{
auto button = QMessageBox::warning(
m_parentWidget, tr("Account refresh failed"),
tr("'%1' has expired and needs to be reauthenticated. Do you want to reauthenticate this account?").arg(account->profileName()),
QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, QMessageBox::StandardButton::Yes);
if (button == QMessageBox::StandardButton::Yes) {
auto accounts = APPLICATION->accounts();
bool isDefault = accounts->defaultAccount() == account;
accounts->removeAccount(accounts->index(accounts->findAccountByProfileId(account->profileId())));
if (account->accountType() == AccountType::MSA) {
auto newAccount = MSALoginDialog::newAccount(m_parentWidget);
if (newAccount != nullptr) {
accounts->addAccount(newAccount);
if (isDefault)
accounts->setDefaultAccount(newAccount);
if (m_accountToUse == account) {
m_accountToUse = nullptr;
decideAccount();
}
return true;
}
}
}
emitFailed(tr("The account has expired and needs to be reauthenticated"));
return false;
}
void LaunchController::launchInstance()
{
Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL");

View File

@ -47,7 +47,7 @@ class LaunchController : public Task {
public:
void executeTask() override;
LaunchController();
LaunchController(QObject* parent = nullptr);
virtual ~LaunchController() = default;
void setInstance(InstancePtr instance) { m_instance = instance; }
@ -56,8 +56,6 @@ class LaunchController : public Task {
void setOnline(bool online) { m_online = online; }
void setOfflineName(const QString& offlineName) { m_offlineName = offlineName; }
void setDemo(bool demo) { m_demo = demo; }
void setProfiler(BaseProfilerFactory* profiler) { m_profiler = profiler; }
@ -78,7 +76,6 @@ class LaunchController : public Task {
void decideAccount();
bool askPlayDemo();
QString askOfflineName(QString playerName, bool demo, bool& ok);
bool reauthenticateAccount(MinecraftAccountPtr account);
private slots:
void readyForLaunch();
@ -90,7 +87,6 @@ class LaunchController : public Task {
private:
BaseProfilerFactory* m_profiler = nullptr;
bool m_online = true;
QString m_offlineName;
bool m_demo = false;
InstancePtr m_instance;
QWidget* m_parentWidget = nullptr;

View File

@ -39,16 +39,8 @@ if [ "x$DEPS_LIST" = "x" ]; then
# Just to be sure...
chmod +x "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}"
ARGS=("${LAUNCHER_DIR}/${LAUNCHER_NAME}" "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}")
if [ -f portable.txt ]; then
ARGS+=("-d" "${LAUNCHER_DIR}")
fi
ARGS+=("$@")
# Run the launcher
exec -a "${ARGS[@]}"
exec -a "${LAUNCHER_DIR}/${LAUNCHER_NAME}" "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}" -d "${LAUNCHER_DIR}" "$@"
# Run the launcher in valgrind
# valgrind --log-file="valgrind.log" --leak-check=full --track-origins=yes "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}" -d "${LAUNCHER_DIR}" "$@"

View File

@ -378,7 +378,7 @@ std::optional<QStringList> extractDir(QString fileCompressed, QString dir)
if (fileInfo.size() == 22) {
return QStringList();
}
qWarning() << "Could not open archive for unpacking:" << fileCompressed << "Error:" << zip.getZipError();
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();
;
return std::nullopt;
}
@ -395,7 +395,7 @@ std::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QS
if (fileInfo.size() == 22) {
return QStringList();
}
qWarning() << "Could not open archive for unpacking:" << fileCompressed << "Error:" << zip.getZipError();
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();
;
return std::nullopt;
}
@ -412,13 +412,13 @@ bool extractFile(QString fileCompressed, QString file, QString target)
if (fileInfo.size() == 22) {
return true;
}
qWarning() << "Could not open archive for unpacking:" << fileCompressed << "Error:" << zip.getZipError();
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();
return false;
}
return extractRelFile(&zip, file, target);
}
bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFileFunction excludeFilter)
bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter)
{
QDir rootDirectory(rootDir);
if (!rootDirectory.exists())
@ -443,8 +443,8 @@ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, Q
// collect files
entries = directory.entryInfoList(QDir::Files);
for (const auto& e : entries) {
if (excludeFilter && excludeFilter(e)) {
QString relativeFilePath = rootDirectory.relativeFilePath(e.absoluteFilePath());
if (excludeFilter && excludeFilter(relativeFilePath)) {
qDebug() << "Skipping file " << relativeFilePath;
continue;
}
@ -577,7 +577,7 @@ auto ExtractZipTask::extractZip() -> ZipResult
auto relative_file_name = QDir::fromNativeSeparators(file_name.mid(m_subdirectory.size()));
auto original_name = relative_file_name;
setStatus("Unpacking: " + relative_file_name);
setStatus("Unziping: " + relative_file_name);
// Fix subdirs/files ending with a / getting transformed into absolute paths
if (relative_file_name.startsWith('/'))

View File

@ -56,7 +56,6 @@
namespace MMCZip {
using FilterFunction = std::function<bool(const QString&)>;
using FilterFileFunction = std::function<bool(const QFileInfo&)>;
/**
* Merge two zip files, using a filter function
@ -150,7 +149,7 @@ bool extractFile(QString fileCompressed, QString file, QString dir);
* \param excludeFilter function to excludeFilter which files shouldn't be included (returning true means to excude)
* \return true for success or false for failure
*/
bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFileFunction excludeFilter);
bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter);
#if defined(LAUNCHER_APPLICATION)
class ExportToZipTask : public Task {

View File

@ -2,22 +2,19 @@
MessageLevel::Enum MessageLevel::getLevel(const QString& levelName)
{
QString name = levelName.toUpper();
if (name == "LAUNCHER")
if (levelName == "Launcher")
return MessageLevel::Launcher;
else if (name == "TRACE")
return MessageLevel::Trace;
else if (name == "DEBUG")
else if (levelName == "Debug")
return MessageLevel::Debug;
else if (name == "INFO")
else if (levelName == "Info")
return MessageLevel::Info;
else if (name == "MESSAGE")
else if (levelName == "Message")
return MessageLevel::Message;
else if (name == "WARNING" || name == "WARN")
else if (levelName == "Warning")
return MessageLevel::Warning;
else if (name == "ERROR")
else if (levelName == "Error")
return MessageLevel::Error;
else if (name == "FATAL")
else if (levelName == "Fatal")
return MessageLevel::Fatal;
// Skip PrePost, it's not exposed to !![]!
// Also skip StdErr and StdOut

View File

@ -12,7 +12,6 @@ enum Enum {
StdOut, /**< Undetermined stderr messages */
StdErr, /**< Undetermined stdout messages */
Launcher, /**< Launcher Messages */
Trace, /**< Trace Messages */
Debug, /**< Debug Messages */
Info, /**< Info Messages */
Message, /**< Standard Messages */

View File

@ -57,7 +57,8 @@ class NullInstance : public BaseInstance {
QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); }
QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); }
QMap<QString, QString> getVariables() override { return QMap<QString, QString>(); }
QStringList getLogFileSearchPaths() override { return {}; }
IPathMatcher::Ptr getLogFileMatcher() override { return nullptr; }
QString getLogFileRoot() override { return instanceRoot(); }
QString typeName() const override { return "Null"; }
bool canExport() const override { return false; }
bool canEdit() const override { return false; }

View File

@ -1,71 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <QFileInfo>
#include <QSaveFile>
#include "Application.h"
#if defined(LAUNCHER_APPLICATION)
/* PSaveFile
* A class that mimics QSaveFile for Windows.
*
* When reading resources, we need to avoid accessing temporary files
* generated by QSaveFile. If we start reading such a file, we may
* inadvertently keep it open while QSaveFile is trying to remove it,
* or we might detect the file just before it is removed, leading to
* race conditions and errors.
*
* Unfortunately, QSaveFile doesn't provide a way to retrieve the
* temporary file name or to set a specific template for the temporary
* file name it uses. By default, QSaveFile appends a `.XXXXXX` suffix
* to the original file name, where the `XXXXXX` part is dynamically
* generated to ensure uniqueness.
*
* This class acts like a lock by adding and removing the target file
* name into/from a global string set, helping to manage access to
* files during critical operations.
*
* Note: Please do not use the `setFileName` function directly, as it
* is not virtual and cannot be overridden.
*/
class PSaveFile : public QSaveFile {
public:
PSaveFile(const QString& name) : QSaveFile(name) { addPath(name); }
PSaveFile(const QString& name, QObject* parent) : QSaveFile(name, parent) { addPath(name); }
virtual ~PSaveFile()
{
if (auto app = APPLICATION_DYN) {
app->removeQSavePath(m_absoluteFilePath);
}
}
private:
void addPath(const QString& path)
{
m_absoluteFilePath = QFileInfo(path).absoluteFilePath() + "."; // add dot for tmp files only
if (auto app = APPLICATION_DYN) {
app->addQSavePath(m_absoluteFilePath);
}
}
QString m_absoluteFilePath;
};
#else
#define PSaveFile QSaveFile
#endif

View File

@ -33,7 +33,7 @@ class shared_qobject_ptr : public QSharedPointer<T> {
{}
void reset() { QSharedPointer<T>::reset(); }
void reset(T* other)
void reset(T*&& other)
{
shared_qobject_ptr<T> t(other);
this->swap(t);

View File

@ -1,6 +1,7 @@
#include "RecursiveFileSystemWatcher.h"
#include <QDebug>
#include <QRegularExpression>
RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject* parent) : QObject(parent), m_watcher(new QFileSystemWatcher(this))
{

View File

@ -35,9 +35,9 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
QString custom_target_folder)
: m_pack(std::move(pack)), m_pack_version(std::move(version)), m_pack_model(packs), m_custom_target_folder(custom_target_folder)
{
if (is_indexed) {
m_update_task.reset(new LocalResourceUpdateTask(m_pack_model->indexDir(), *m_pack, m_pack_version));
connect(m_update_task.get(), &LocalResourceUpdateTask::hasOldResource, this, &ResourceDownloadTask::hasOldResource);
if (auto model = dynamic_cast<ModFolderModel*>(m_pack_model.get()); model && is_indexed) {
m_update_task.reset(new LocalModUpdateTask(model->indexDir(), *m_pack, m_pack_version));
connect(m_update_task.get(), &LocalModUpdateTask::hasOldMod, this, &ResourceDownloadTask::hasOldResource);
addTask(m_update_task);
}
@ -91,8 +91,12 @@ void ResourceDownloadTask::downloadSucceeded()
m_filesNetJob.reset();
auto name = std::get<0>(to_delete);
auto filename = std::get<1>(to_delete);
if (!name.isEmpty() && filename != m_pack_version.fileName)
m_pack_model->uninstallResource(filename, true);
if (!name.isEmpty() && filename != m_pack_version.fileName) {
if (auto model = dynamic_cast<ModFolderModel*>(m_pack_model.get()); model)
model->uninstallMod(filename, true);
else
m_pack_model->uninstallResource(filename);
}
}
void ResourceDownloadTask::downloadFailed(QString reason)

View File

@ -22,7 +22,7 @@
#include "net/NetJob.h"
#include "tasks/SequentialTask.h"
#include "minecraft/mod/tasks/LocalResourceUpdateTask.h"
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
#include "modplatform/ModIndex.h"
class ResourceFolderModel;
@ -50,7 +50,7 @@ class ResourceDownloadTask : public SequentialTask {
QString m_custom_target_folder;
NetJob::Ptr m_filesNetJob;
LocalResourceUpdateTask::Ptr m_update_task;
LocalModUpdateTask::Ptr m_update_task;
void downloadProgressChanged(qint64 current, qint64 total);
void downloadFailed(QString reason);

View File

@ -53,7 +53,7 @@ static inline QChar getNextChar(const QString& s, int location)
int StringUtils::naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs)
{
int l1 = 0, l2 = 0;
while (l1 <= s1.size() && l2 <= s2.size()) {
while (l1 <= s1.count() && l2 <= s2.count()) {
// skip spaces, tabs and 0's
QChar c1 = getNextChar(s1, l1);
while (c1.isSpace())
@ -213,10 +213,11 @@ QPair<QString, QString> StringUtils::splitFirst(const QString& s, const QRegular
return qMakePair(left, right);
}
static const QRegularExpression ulMatcher("<\\s*/\\s*ul\\s*>");
QString StringUtils::htmlListPatch(QString htmlStr)
{
static const QRegularExpression s_ulMatcher("<\\s*/\\s*ul\\s*>");
int pos = htmlStr.indexOf(s_ulMatcher);
int pos = htmlStr.indexOf(ulMatcher);
int imgPos;
while (pos != -1) {
pos = htmlStr.indexOf(">", pos) + 1; // Get the size of the </ul> tag. Add one for zeroeth index
@ -229,7 +230,7 @@ QString StringUtils::htmlListPatch(QString htmlStr)
if (textBetween.isEmpty())
htmlStr.insert(pos, "<br>");
pos = htmlStr.indexOf(s_ulMatcher, pos);
pos = htmlStr.indexOf(ulMatcher, pos);
}
return htmlStr;
}

View File

@ -81,9 +81,9 @@ QString getSupportedJavaArchitecture()
if (arch == "arm64")
return "mac-os-arm64";
if (arch.contains("64"))
return "mac-os-x64";
return "mac-os-64";
if (arch.contains("86"))
return "mac-os-x86";
return "mac-os-86";
// Unknown, maybe something new, appending arch
return "mac-os-" + arch;
} else if (sys == "linux") {

View File

@ -1,6 +1,7 @@
#include "Version.h"
#include <QDebug>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include <QUrl>
@ -78,7 +79,7 @@ void Version::parse()
if (m_string.isEmpty())
return;
auto classChange = [&currentSection](QChar lastChar, QChar currentChar) {
auto classChange = [&](QChar lastChar, QChar currentChar) {
if (lastChar.isNull())
return false;
if (lastChar.isDigit() != currentChar.isDigit())

View File

@ -72,14 +72,22 @@ class Version {
}
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
auto numPart = QStringView{ m_fullString }.left(cutoff);
#else
auto numPart = m_fullString.leftRef(cutoff);
#endif
if (!numPart.isEmpty()) {
m_isNull = false;
m_numPart = numPart.toInt();
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
auto stringPart = QStringView{ m_fullString }.mid(cutoff);
#else
auto stringPart = m_fullString.midRef(cutoff);
#endif
if (!stringPart.isEmpty()) {
m_isNull = false;

View File

@ -193,8 +193,8 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
if (value.toBool()) {
return tr("Recommended");
} else if (hasLatest) {
auto latest = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
if (latest.toBool()) {
auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
if (value.toBool()) {
return tr("Latest");
}
}
@ -203,7 +203,9 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
}
}
case Qt::DecorationRole: {
if (column == Name && hasRecommended) {
switch (column) {
case Name: {
if (hasRecommended) {
auto recommenced = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
if (recommenced.toBool()) {
return APPLICATION->getThemedIcon("star");
@ -223,8 +225,12 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
}
return pixmap;
}
}
default: {
return QVariant();
}
}
}
default: {
if (roles.contains((BaseVersionList::ModelRoles)role)) {
return sourceModel()->data(parentIndex, role);
@ -295,6 +301,7 @@ void VersionProxyModel::sourceDataChanged(const QModelIndex& source_top_left, co
void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw)
{
auto replacing = dynamic_cast<BaseVersionList*>(replacingRaw);
beginResetModel();
m_columns.clear();
if (!replacing) {
@ -341,6 +348,8 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw)
hasLatest = true;
}
filterModel->setSourceModel(replacing);
endResetModel();
}
QModelIndex VersionProxyModel::getRecommended() const

View File

@ -16,18 +16,13 @@
*
*/
#include "WindowsConsole.h"
#include <system_error>
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <fcntl.h>
#include <io.h>
#include <stdio.h>
#include <cstddef>
#include <windows.h>
#include <iostream>
void RedirectHandle(DWORD handle, FILE* stream, const char* mode)
@ -131,29 +126,3 @@ bool AttachWindowsConsole()
return false;
}
std::error_code EnableAnsiSupport()
{
// ref: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
// Using `CreateFileW("CONOUT$", ...)` to retrieve the console handle works correctly even if STDOUT and/or STDERR are redirected
HANDLE console_handle = CreateFileW(L"CONOUT$", FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
if (console_handle == INVALID_HANDLE_VALUE) {
return std::error_code(GetLastError(), std::system_category());
}
// ref: https://docs.microsoft.com/en-us/windows/console/getconsolemode
DWORD console_mode;
if (0 == GetConsoleMode(console_handle, &console_mode)) {
return std::error_code(GetLastError(), std::system_category());
}
// VT processing not already enabled?
if ((console_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0) {
// https://docs.microsoft.com/en-us/windows/console/setconsolemode
if (0 == SetConsoleMode(console_handle, console_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) {
return std::error_code(GetLastError(), std::system_category());
}
}
return {};
}

View File

@ -21,8 +21,5 @@
#pragma once
#include <system_error>
void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr);
bool AttachWindowsConsole();
std::error_code EnableAnsiSupport();

View File

@ -1,33 +0,0 @@
#pragma once
#include <QString>
#include <ostream>
#if defined Q_OS_WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#else
#include <unistd.h>
#include <cstdio>
#endif
namespace console {
inline bool isConsole()
{
#if defined Q_OS_WIN32
DWORD procIDs[2];
DWORD maxCount = 2;
DWORD result = GetConsoleProcessList((LPDWORD)procIDs, maxCount);
return result > 1;
#else
if (isatty(fileno(stdout))) {
return true;
}
return false;
#endif
}
} // namespace console

View File

@ -37,14 +37,27 @@
#include <sys.h>
#if defined Q_OS_WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include "console/WindowsConsole.h"
#include "WindowsConsole.h"
#endif
// Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header
#ifdef __APPLE__
#include <Availability.h> // 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(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#define GHC_USE_STD_FS
#include <filesystem>
namespace fs = std::filesystem;
#endif // MacOS min version check
#endif // Other OSes version check
#ifndef GHC_USE_STD_FS
#include <ghc/filesystem.hpp>
namespace fs = ghc::filesystem;
#endif
FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv), socket(new QLocalSocket(this))
{
@ -91,11 +104,11 @@ void FileLinkApp::joinServer(QString server)
in.setDevice(&socket);
connect(&socket, &QLocalSocket::connected, this, []() { qDebug() << "connected to server"; });
connect(&socket, &QLocalSocket::connected, this, [&]() { qDebug() << "connected to server"; });
connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs);
connect(&socket, &QLocalSocket::errorOccurred, this, [this](QLocalSocket::LocalSocketError socketError) {
connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError) {
m_status = Failed;
switch (socketError) {
case QLocalSocket::ServerNotFoundError:
@ -119,7 +132,7 @@ void FileLinkApp::joinServer(QString server)
}
});
connect(&socket, &QLocalSocket::disconnected, this, [this]() {
connect(&socket, &QLocalSocket::disconnected, this, [&]() {
qDebug() << "disconnected from server, should exit";
m_status = Succeeded;
exit();

View File

@ -37,6 +37,7 @@
#include "IconList.h"
#include <FileSystem.h>
#include <QDebug>
#include <QEventLoop>
#include <QFileSystemWatcher>
#include <QMap>
#include <QMimeData>
@ -46,24 +47,24 @@
#define MAX_SIZE 1024
IconList::IconList(const QStringList& builtinPaths, const QString& path, QObject* parent) : QAbstractListModel(parent)
IconList::IconList(const QStringList& builtinPaths, QString path, QObject* parent) : QAbstractListModel(parent)
{
QSet<QString> builtinNames;
// add builtin icons
for (const auto& builtinPath : builtinPaths) {
QDir instanceIcons(builtinPath);
auto fileInfoList = instanceIcons.entryInfoList(QDir::Files, QDir::Name);
for (const auto& fileInfo : fileInfoList) {
builtinNames.insert(fileInfo.baseName());
for (auto& builtinPath : builtinPaths) {
QDir instance_icons(builtinPath);
auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name);
for (auto file_info : file_info_list) {
builtinNames.insert(file_info.completeBaseName());
}
}
for (const auto& builtinName : builtinNames) {
for (auto& builtinName : builtinNames) {
addThemeIcon(builtinName);
}
m_watcher.reset(new QFileSystemWatcher());
m_isWatching = false;
is_watching = false;
connect(m_watcher.get(), &QFileSystemWatcher::directoryChanged, this, &IconList::directoryChanged);
connect(m_watcher.get(), &QFileSystemWatcher::fileChanged, this, &IconList::fileChanged);
@ -76,129 +77,91 @@ IconList::IconList(const QStringList& builtinPaths, const QString& path, QObject
void IconList::sortIconList()
{
qDebug() << "Sorting icon list...";
std::sort(m_icons.begin(), m_icons.end(), [](const MMCIcon& a, const MMCIcon& b) {
bool aIsSubdir = a.m_key.contains(QDir::separator());
bool bIsSubdir = b.m_key.contains(QDir::separator());
if (aIsSubdir != bIsSubdir) {
return !aIsSubdir; // root-level icons come first
}
return a.m_key.localeAwareCompare(b.m_key) < 0;
});
std::sort(icons.begin(), icons.end(), [](const MMCIcon& a, const MMCIcon& b) { return a.m_key.localeAwareCompare(b.m_key) < 0; });
reindex();
}
// Helper function to add directories recursively
bool IconList::addPathRecursively(const QString& path)
{
QDir dir(path);
if (!dir.exists())
return false;
// Add the directory itself
bool watching = m_watcher->addPath(path);
// Add all subdirectories
QFileInfoList entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
for (const QFileInfo& entry : entries) {
if (addPathRecursively(entry.absoluteFilePath())) {
watching = true;
}
}
return watching;
}
QStringList IconList::getIconFilePaths() const
{
QStringList iconFiles{};
QStringList directories{ m_dir.absolutePath() };
while (!directories.isEmpty()) {
QString first = directories.takeFirst();
QDir dir(first);
for (QFileInfo& fileInfo : dir.entryInfoList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot, QDir::Name)) {
if (fileInfo.isDir())
directories.push_back(fileInfo.absoluteFilePath());
else
iconFiles.push_back(fileInfo.absoluteFilePath());
}
}
return iconFiles;
}
QString formatName(const QDir& iconsDir, const QFileInfo& iconFile)
{
if (iconFile.dir() == iconsDir)
return iconFile.baseName();
constexpr auto delimiter = " » ";
QString relativePathWithoutExtension = iconsDir.relativeFilePath(iconFile.dir().path()) + QDir::separator() + iconFile.baseName();
return relativePathWithoutExtension.replace(QDir::separator(), delimiter);
}
/// Split into a separate function because the preprocessing impedes readability
QSet<QString> toStringSet(const QList<QString>& list)
{
QSet<QString> set(list.begin(), list.end());
return set;
}
void IconList::directoryChanged(const QString& path)
{
QDir newDir(path);
if (m_dir.absolutePath() != newDir.absolutePath()) {
if (!path.startsWith(m_dir.absolutePath()))
QDir new_dir(path);
if (m_dir.absolutePath() != new_dir.absolutePath()) {
m_dir.setPath(path);
m_dir.refresh();
if (m_isWatching)
if (is_watching)
stopWatching();
startWatching();
}
if (!m_dir.exists() && !FS::ensureFolderPathExists(m_dir.absolutePath()))
if (!m_dir.exists())
if (!FS::ensureFolderPathExists(m_dir.absolutePath()))
return;
m_dir.refresh();
const QStringList newFileNamesList = getIconFilePaths();
const QSet<QString> newSet = toStringSet(newFileNamesList);
QSet<QString> currentSet;
for (const MMCIcon& it : m_icons) {
auto new_list = m_dir.entryList(QDir::Files, QDir::Name);
for (auto it = new_list.begin(); it != new_list.end(); it++) {
QString& foo = (*it);
foo = m_dir.filePath(foo);
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QSet<QString> new_set(new_list.begin(), new_list.end());
#else
auto new_set = new_list.toSet();
#endif
QList<QString> current_list;
for (auto& it : icons) {
if (!it.has(IconType::FileBased))
continue;
QFileInfo icon(it.getFilePath());
currentSet.insert(icon.absoluteFilePath());
current_list.push_back(it.m_images[IconType::FileBased].filename);
}
QSet<QString> toRemove = currentSet - newSet;
QSet<QString> toAdd = newSet - currentSet;
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QSet<QString> current_set(current_list.begin(), current_list.end());
#else
QSet<QString> current_set = current_list.toSet();
#endif
for (const QString& removedPath : toRemove) {
qDebug() << "Removing icon " << removedPath;
QFileInfo removedFile(removedPath);
QString relativePath = m_dir.relativeFilePath(removedFile.absoluteFilePath());
QString key = QFileInfo(relativePath).completeBaseName();
QSet<QString> to_remove = current_set;
to_remove -= new_set;
QSet<QString> to_add = new_set;
to_add -= current_set;
for (auto remove : to_remove) {
qDebug() << "Removing " << remove;
QFileInfo rmfile(remove);
QString key = rmfile.completeBaseName();
QString suffix = rmfile.suffix();
// The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well
if (!IconUtils::isIconSuffix(suffix))
key = rmfile.fileName();
int idx = getIconIndex(key);
if (idx == -1)
continue;
m_icons[idx].remove(FileBased);
if (m_icons[idx].type() == ToBeDeleted) {
icons[idx].remove(IconType::FileBased);
if (icons[idx].type() == IconType::ToBeDeleted) {
beginRemoveRows(QModelIndex(), idx, idx);
m_icons.remove(idx);
icons.remove(idx);
reindex();
endRemoveRows();
} else {
dataChanged(index(idx), index(idx));
}
m_watcher->removePath(removedPath);
m_watcher->removePath(remove);
emit iconUpdated(key);
}
for (const QString& addedPath : toAdd) {
qDebug() << "Adding icon " << addedPath;
for (auto add : to_add) {
qDebug() << "Adding " << add;
QFileInfo addfile(addedPath);
QString relativePath = m_dir.relativeFilePath(addfile.absoluteFilePath());
QString key = QFileInfo(relativePath).completeBaseName();
QString name = formatName(m_dir, addfile);
QFileInfo addfile(add);
QString key = addfile.completeBaseName();
if (addIcon(key, name, addfile.filePath(), IconType::FileBased)) {
m_watcher->addPath(addedPath);
QString suffix = addfile.suffix();
// The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well
if (!IconUtils::isIconSuffix(suffix))
key = addfile.fileName();
if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased)) {
m_watcher->addPath(add);
emit iconUpdated(key);
}
}
@ -208,24 +171,24 @@ void IconList::directoryChanged(const QString& path)
void IconList::fileChanged(const QString& path)
{
qDebug() << "Checking icon " << path;
qDebug() << "Checking " << path;
QFileInfo checkfile(path);
if (!checkfile.exists())
return;
QString key = m_dir.relativeFilePath(checkfile.absoluteFilePath());
QString key = checkfile.completeBaseName();
int idx = getIconIndex(key);
if (idx == -1)
return;
QIcon icon(path);
if (icon.availableSizes().empty())
if (!icon.availableSizes().size())
return;
m_icons[idx].m_images[IconType::FileBased].icon = icon;
icons[idx].m_images[IconType::FileBased].icon = icon;
dataChanged(index(idx), index(idx));
emit iconUpdated(key);
}
void IconList::SettingChanged(const Setting& setting, const QVariant& value)
void IconList::SettingChanged(const Setting& setting, QVariant value)
{
if (setting.id() != "IconsDir")
return;
@ -237,8 +200,8 @@ void IconList::startWatching()
{
auto abs_path = m_dir.absolutePath();
FS::ensureFolderPathExists(abs_path);
m_isWatching = addPathRecursively(abs_path);
if (m_isWatching) {
is_watching = m_watcher->addPath(abs_path);
if (is_watching) {
qDebug() << "Started watching " << abs_path;
} else {
qDebug() << "Failed to start watching " << abs_path;
@ -249,7 +212,7 @@ void IconList::stopWatching()
{
m_watcher->removePaths(m_watcher->files());
m_watcher->removePaths(m_watcher->directories());
m_isWatching = false;
is_watching = false;
}
QStringList IconList::mimeTypes() const
@ -279,7 +242,7 @@ bool IconList::dropMimeData(const QMimeData* data,
if (data->hasUrls()) {
auto urls = data->urls();
QStringList iconFiles;
for (const auto& url : urls) {
for (auto url : urls) {
// only local files may be dropped...
if (!url.isLocalFile())
continue;
@ -300,33 +263,33 @@ Qt::ItemFlags IconList::flags(const QModelIndex& index) const
QVariant IconList::data(const QModelIndex& index, int role) const
{
if (!index.isValid())
return {};
return QVariant();
int row = index.row();
if (row < 0 || row >= m_icons.size())
return {};
if (row < 0 || row >= icons.size())
return QVariant();
switch (role) {
case Qt::DecorationRole:
return m_icons[row].icon();
return icons[row].icon();
case Qt::DisplayRole:
return m_icons[row].name();
return icons[row].name();
case Qt::UserRole:
return m_icons[row].m_key;
return icons[row].m_key;
default:
return {};
return QVariant();
}
}
int IconList::rowCount(const QModelIndex& parent) const
{
return parent.isValid() ? 0 : m_icons.size();
return parent.isValid() ? 0 : icons.size();
}
void IconList::installIcons(const QStringList& iconFiles)
{
for (const QString& file : iconFiles)
for (QString file : iconFiles)
installIcon(file, {});
}
@ -349,13 +312,12 @@ bool IconList::iconFileExists(const QString& key) const
return iconEntry && iconEntry->has(IconType::FileBased);
}
/// Returns the icon with the given key or nullptr if it doesn't exist.
const MMCIcon* IconList::icon(const QString& key) const
{
int iconIdx = getIconIndex(key);
if (iconIdx == -1)
return nullptr;
return &m_icons[iconIdx];
return &icons[iconIdx];
}
bool IconList::deleteIcon(const QString& key)
@ -370,22 +332,22 @@ bool IconList::trashIcon(const QString& key)
bool IconList::addThemeIcon(const QString& key)
{
auto iter = m_nameIndex.find(key);
if (iter != m_nameIndex.end()) {
auto& oldOne = m_icons[*iter];
auto iter = name_index.find(key);
if (iter != name_index.end()) {
auto& oldOne = icons[*iter];
oldOne.replace(Builtin, key);
dataChanged(index(*iter), index(*iter));
return true;
}
// add a new icon
beginInsertRows(QModelIndex(), m_icons.size(), m_icons.size());
beginInsertRows(QModelIndex(), icons.size(), icons.size());
{
MMCIcon mmc_icon;
mmc_icon.m_name = key;
mmc_icon.m_key = key;
mmc_icon.replace(Builtin, key);
m_icons.push_back(mmc_icon);
m_nameIndex[key] = m_icons.size() - 1;
icons.push_back(mmc_icon);
name_index[key] = icons.size() - 1;
}
endInsertRows();
return true;
@ -397,22 +359,22 @@ bool IconList::addIcon(const QString& key, const QString& name, const QString& p
QIcon icon(path);
if (icon.isNull())
return false;
auto iter = m_nameIndex.find(key);
if (iter != m_nameIndex.end()) {
auto& oldOne = m_icons[*iter];
auto iter = name_index.find(key);
if (iter != name_index.end()) {
auto& oldOne = icons[*iter];
oldOne.replace(type, icon, path);
dataChanged(index(*iter), index(*iter));
return true;
}
// add a new icon
beginInsertRows(QModelIndex(), m_icons.size(), m_icons.size());
beginInsertRows(QModelIndex(), icons.size(), icons.size());
{
MMCIcon mmc_icon;
mmc_icon.m_name = name;
mmc_icon.m_key = key;
mmc_icon.replace(type, icon, path);
m_icons.push_back(mmc_icon);
m_nameIndex[key] = m_icons.size() - 1;
icons.push_back(mmc_icon);
name_index[key] = icons.size() - 1;
}
endInsertRows();
return true;
@ -427,32 +389,33 @@ void IconList::saveIcon(const QString& key, const QString& path, const char* for
void IconList::reindex()
{
m_nameIndex.clear();
for (int i = 0; i < m_icons.size(); i++) {
m_nameIndex[m_icons[i].m_key] = i;
emit iconUpdated(m_icons[i].m_key); // prevents incorrect indices with proxy model
name_index.clear();
int i = 0;
for (auto& iter : icons) {
name_index[iter.m_key] = i;
i++;
}
}
QIcon IconList::getIcon(const QString& key) const
{
int iconIndex = getIconIndex(key);
int icon_index = getIconIndex(key);
if (iconIndex != -1)
return m_icons[iconIndex].icon();
if (icon_index != -1)
return icons[icon_index].icon();
// Fallback for icons that don't exist.b
iconIndex = getIconIndex("grass");
// Fallback for icons that don't exist.
icon_index = getIconIndex("grass");
if (iconIndex != -1)
return m_icons[iconIndex].icon();
return {};
if (icon_index != -1)
return icons[icon_index].icon();
return QIcon();
}
int IconList::getIconIndex(const QString& key) const
{
auto iter = m_nameIndex.find(key == "default" ? "grass" : key);
if (iter != m_nameIndex.end())
auto iter = name_index.find(key == "default" ? "grass" : key);
if (iter != name_index.end())
return *iter;
return -1;
@ -462,15 +425,3 @@ QString IconList::getDirectory() const
{
return m_dir.absolutePath();
}
/// Returns the directory of the icon with the given key or the default directory if it's a builtin icon.
QString IconList::iconDirectory(const QString& key) const
{
for (const auto& mmcIcon : m_icons) {
if (mmcIcon.m_key == key && mmcIcon.has(IconType::FileBased)) {
QFileInfo iconFile(mmcIcon.getFilePath());
return iconFile.dir().path();
}
}
return getDirectory();
}

View File

@ -51,7 +51,7 @@ class QFileSystemWatcher;
class IconList : public QAbstractListModel {
Q_OBJECT
public:
explicit IconList(const QStringList& builtinPaths, const QString& path, QObject* parent = 0);
explicit IconList(const QStringList& builtinPaths, QString path, QObject* parent = 0);
virtual ~IconList() {};
QIcon getIcon(const QString& key) const;
@ -72,7 +72,6 @@ class IconList : public QAbstractListModel {
bool deleteIcon(const QString& key);
bool trashIcon(const QString& key);
bool iconFileExists(const QString& key) const;
QString iconDirectory(const QString& key) const;
void installIcons(const QStringList& iconFiles);
void installIcon(const QString& file, const QString& name);
@ -92,20 +91,18 @@ class IconList : public QAbstractListModel {
IconList& operator=(const IconList&) = delete;
void reindex();
void sortIconList();
bool addPathRecursively(const QString& path);
QStringList getIconFilePaths() const;
public slots:
void directoryChanged(const QString& path);
protected slots:
void fileChanged(const QString& path);
void SettingChanged(const Setting& setting, const QVariant& value);
void SettingChanged(const Setting& setting, QVariant value);
private:
shared_qobject_ptr<QFileSystemWatcher> m_watcher;
bool m_isWatching;
QMap<QString, int> m_nameIndex;
QList<MMCIcon> m_icons;
bool is_watching;
QMap<QString, int> name_index;
QVector<MMCIcon> icons;
QDir m_dir;
};

View File

@ -44,8 +44,8 @@
#include "FileSystem.h"
#include "java/JavaUtils.h"
JavaChecker::JavaChecker(QString path, QString args, int minMem, int maxMem, int permGen, int id)
: Task(), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen), m_id(id)
JavaChecker::JavaChecker(QString path, QString args, int minMem, int maxMem, int permGen, int id, QObject* parent)
: Task(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen), m_id(id)
{}
void JavaChecker::executeTask()
@ -137,7 +137,11 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
QMap<QString, QString> results;
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QStringList lines = m_stdout.split("\n", Qt::SkipEmptyParts);
#else
QStringList lines = m_stdout.split("\n", QString::SkipEmptyParts);
#endif
for (QString line : lines) {
line = line.trimmed();
// NOTE: workaround for GH-4125, where garbage is getting printed into stdout on bedrock linux
@ -145,7 +149,11 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
continue;
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
auto parts = line.split('=', Qt::SkipEmptyParts);
#else
auto parts = line.split('=', QString::SkipEmptyParts);
#endif
if (parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty()) {
continue;
} else {
@ -163,7 +171,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
auto os_arch = results["os.arch"];
auto java_version = results["java.version"];
auto java_vendor = results["java.vendor"];
bool is_64 = os_arch == "x86_64" || os_arch == "amd64" || os_arch == "aarch64" || os_arch == "arm64" || os_arch == "riscv64";
bool is_64 = os_arch == "x86_64" || os_arch == "amd64" || os_arch == "aarch64" || os_arch == "arm64";
result.validity = Result::Validity::Valid;
result.is_64bit = is_64;

View File

@ -1,6 +1,7 @@
#pragma once
#include <QProcess>
#include <QTimer>
#include <memory>
#include "JavaVersion.h"
#include "QObjectPtr.h"
@ -25,7 +26,7 @@ class JavaChecker : public Task {
enum class Validity { Errored, ReturnedInvalidData, Valid } validity = Validity::Errored;
};
explicit JavaChecker(QString path, QString args, int minMem = 0, int maxMem = 0, int permGen = 0, int id = 0);
explicit JavaChecker(QString path, QString args, int minMem = 0, int maxMem = 0, int permGen = 0, int id = 0, QObject* parent = 0);
signals:
void checkFinished(const Result& result);

Some files were not shown because too many files have changed in this diff Show More