Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into skin_selector
This commit is contained in:
commit
23134d4b72
41
.github/scripts/prepare_JREs.sh
vendored
41
.github/scripts/prepare_JREs.sh
vendored
@ -1,41 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
URL_JDK8="https://api.adoptium.net/v3/binary/version/jdk8u312-b07/linux/x64/jre/hotspot/normal/eclipse"
|
|
||||||
URL_JDK17="https://api.adoptium.net/v3/binary/latest/17/ga/linux/x64/jre/hotspot/normal/eclipse"
|
|
||||||
|
|
||||||
mkdir -p JREs
|
|
||||||
pushd JREs
|
|
||||||
|
|
||||||
wget --content-disposition "$URL_JDK8"
|
|
||||||
wget --content-disposition "$URL_JDK17"
|
|
||||||
|
|
||||||
for file in *;
|
|
||||||
do
|
|
||||||
mkdir temp
|
|
||||||
|
|
||||||
re='(OpenJDK([[:digit:]]+)U-jre_x64_linux_hotspot_([[:digit:]]+)(.*).tar.gz)'
|
|
||||||
if [[ $file =~ $re ]];
|
|
||||||
then
|
|
||||||
version_major=${BASH_REMATCH[2]}
|
|
||||||
version_trailing=${BASH_REMATCH[4]}
|
|
||||||
|
|
||||||
if [ $version_major = 17 ];
|
|
||||||
then
|
|
||||||
hyphen='-'
|
|
||||||
else
|
|
||||||
hyphen=''
|
|
||||||
fi
|
|
||||||
|
|
||||||
version_edit=$(echo $version_trailing | sed -e 's/_/+/g' | sed -e 's/b/-b/g')
|
|
||||||
dir_name=jdk$hyphen$version_major$version_edit-jre
|
|
||||||
mkdir jre$version_major
|
|
||||||
tar -xzf $file -C temp
|
|
||||||
pushd temp/$dir_name
|
|
||||||
cp -r . ../../jre$version_major
|
|
||||||
popd
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -rf temp
|
|
||||||
done
|
|
||||||
|
|
||||||
popd
|
|
3
.github/workflows/backport.yml
vendored
3
.github/workflows/backport.yml
vendored
@ -16,6 +16,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: write # for korthout/backport-action to create branch
|
contents: write # for korthout/backport-action to create branch
|
||||||
pull-requests: write # for korthout/backport-action to create PR to backport
|
pull-requests: write # for korthout/backport-action to create PR to backport
|
||||||
|
actions: write # for korthout/backport-action to create PR with workflow changes
|
||||||
name: Backport Pull Request
|
name: Backport Pull Request
|
||||||
if: github.repository_owner == 'PrismLauncher' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
|
if: github.repository_owner == 'PrismLauncher' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -24,7 +25,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
- name: Create backport PRs
|
- name: Create backport PRs
|
||||||
uses: korthout/backport-action@v2.1.1
|
uses: korthout/backport-action@v2.5.0
|
||||||
with:
|
with:
|
||||||
# Config README: https://github.com/korthout/backport-action#backport-action
|
# Config README: https://github.com/korthout/backport-action#backport-action
|
||||||
pull_description: |-
|
pull_description: |-
|
||||||
|
182
.github/workflows/build.yml
vendored
182
.github/workflows/build.yml
vendored
@ -21,8 +21,23 @@ on:
|
|||||||
WINDOWS_CODESIGN_PASSWORD:
|
WINDOWS_CODESIGN_PASSWORD:
|
||||||
description: Password for signing Windows builds
|
description: Password for signing Windows builds
|
||||||
required: false
|
required: false
|
||||||
CACHIX_AUTH_TOKEN:
|
APPLE_CODESIGN_CERT:
|
||||||
description: Private token for authenticating against Cachix cache
|
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
|
required: false
|
||||||
GPG_PRIVATE_KEY:
|
GPG_PRIVATE_KEY:
|
||||||
description: Private key for AppImage signing
|
description: Private key for AppImage signing
|
||||||
@ -39,6 +54,10 @@ jobs:
|
|||||||
include:
|
include:
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
qt_ver: 5
|
qt_ver: 5
|
||||||
|
qt_host: linux
|
||||||
|
qt_arch: ""
|
||||||
|
qt_version: "5.12.8"
|
||||||
|
qt_modules: ""
|
||||||
|
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
@ -46,7 +65,6 @@ jobs:
|
|||||||
qt_arch: ""
|
qt_arch: ""
|
||||||
qt_version: "6.2.4"
|
qt_version: "6.2.4"
|
||||||
qt_modules: "qt5compat qtimageformats"
|
qt_modules: "qt5compat qtimageformats"
|
||||||
qt_tools: ""
|
|
||||||
|
|
||||||
- os: windows-2022
|
- os: windows-2022
|
||||||
name: "Windows-MinGW-w64"
|
name: "Windows-MinGW-w64"
|
||||||
@ -61,9 +79,8 @@ jobs:
|
|||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: windows
|
qt_host: windows
|
||||||
qt_arch: ''
|
qt_arch: ''
|
||||||
qt_version: '6.6.0'
|
qt_version: '6.7.0'
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_tools: ''
|
|
||||||
|
|
||||||
- os: windows-2022
|
- os: windows-2022
|
||||||
name: "Windows-MSVC-arm64"
|
name: "Windows-MSVC-arm64"
|
||||||
@ -73,9 +90,8 @@ jobs:
|
|||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: windows
|
qt_host: windows
|
||||||
qt_arch: 'win64_msvc2019_arm64'
|
qt_arch: 'win64_msvc2019_arm64'
|
||||||
qt_version: '6.6.0'
|
qt_version: '6.7.0'
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_tools: ''
|
|
||||||
|
|
||||||
- os: macos-12
|
- os: macos-12
|
||||||
name: macOS
|
name: macOS
|
||||||
@ -83,9 +99,8 @@ jobs:
|
|||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: mac
|
qt_host: mac
|
||||||
qt_arch: ''
|
qt_arch: ''
|
||||||
qt_version: '6.6.0'
|
qt_version: '6.7.0'
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_tools: ''
|
|
||||||
|
|
||||||
- os: macos-12
|
- os: macos-12
|
||||||
name: macOS-Legacy
|
name: macOS-Legacy
|
||||||
@ -94,7 +109,6 @@ jobs:
|
|||||||
qt_host: mac
|
qt_host: mac
|
||||||
qt_version: "5.15.2"
|
qt_version: "5.15.2"
|
||||||
qt_modules: ""
|
qt_modules: ""
|
||||||
qt_tools: ""
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
@ -145,13 +159,13 @@ jobs:
|
|||||||
|
|
||||||
- name: Setup ccache
|
- name: Setup ccache
|
||||||
if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug'
|
if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug'
|
||||||
uses: hendrikmuhs/ccache-action@v1.2.10
|
uses: hendrikmuhs/ccache-action@v1.2.13
|
||||||
with:
|
with:
|
||||||
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }}
|
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }}
|
||||||
|
|
||||||
- name: Retrieve ccache cache (Windows MinGW-w64)
|
- name: Retrieve ccache cache (Windows MinGW-w64)
|
||||||
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
||||||
uses: actions/cache@v3.3.2
|
uses: actions/cache@v4.0.2
|
||||||
with:
|
with:
|
||||||
path: '${{ github.workspace }}\.ccache'
|
path: '${{ github.workspace }}\.ccache'
|
||||||
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
|
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
|
||||||
@ -192,11 +206,6 @@ jobs:
|
|||||||
brew update
|
brew update
|
||||||
brew install ninja extra-cmake-modules
|
brew install ninja extra-cmake-modules
|
||||||
|
|
||||||
- name: Install Qt (Linux)
|
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
|
||||||
run: |
|
|
||||||
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
|
|
||||||
|
|
||||||
- name: Install host Qt (Windows MSVC arm64)
|
- name: Install host Qt (Windows MSVC arm64)
|
||||||
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v3
|
||||||
@ -208,20 +217,18 @@ jobs:
|
|||||||
target: "desktop"
|
target: "desktop"
|
||||||
arch: ""
|
arch: ""
|
||||||
modules: ${{ matrix.qt_modules }}
|
modules: ${{ matrix.qt_modules }}
|
||||||
tools: ${{ matrix.qt_tools }}
|
|
||||||
cache: ${{ inputs.is_qt_cached }}
|
cache: ${{ inputs.is_qt_cached }}
|
||||||
cache-key-prefix: host-qt-arm64-windows
|
cache-key-prefix: host-qt-arm64-windows
|
||||||
dir: ${{ github.workspace }}\HostQt
|
dir: ${{ github.workspace }}\HostQt
|
||||||
set-env: false
|
set-env: false
|
||||||
|
|
||||||
- name: Install Qt (macOS, Linux, Qt 6 & Windows MSVC)
|
- name: Install Qt (macOS, Linux & Windows MSVC)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS' || (runner.os == 'Windows' && matrix.msystem == '')
|
if: matrix.msystem == ''
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v3
|
||||||
with:
|
with:
|
||||||
aqtversion: "==3.1.*"
|
aqtversion: "==3.1.*"
|
||||||
py7zrversion: ">=0.20.2"
|
py7zrversion: ">=0.20.2"
|
||||||
version: ${{ matrix.qt_version }}
|
version: ${{ matrix.qt_version }}
|
||||||
host: ${{ matrix.qt_host }}
|
|
||||||
target: "desktop"
|
target: "desktop"
|
||||||
arch: ${{ matrix.qt_arch }}
|
arch: ${{ matrix.qt_arch }}
|
||||||
modules: ${{ matrix.qt_modules }}
|
modules: ${{ matrix.qt_modules }}
|
||||||
@ -244,7 +251,6 @@ jobs:
|
|||||||
|
|
||||||
wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage"
|
wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage"
|
||||||
|
|
||||||
${{ github.workspace }}/.github/scripts/prepare_JREs.sh
|
|
||||||
sudo apt install libopengl0
|
sudo apt install libopengl0
|
||||||
|
|
||||||
- name: Add QT_HOST_PATH var (Windows MSVC arm64)
|
- name: Add QT_HOST_PATH var (Windows MSVC arm64)
|
||||||
@ -336,6 +342,20 @@ jobs:
|
|||||||
# PACKAGE BUILDS
|
# 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)
|
- name: Package (macOS)
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
run: |
|
run: |
|
||||||
@ -343,9 +363,34 @@ jobs:
|
|||||||
|
|
||||||
cd ${{ env.INSTALL_DIR }}
|
cd ${{ env.INSTALL_DIR }}
|
||||||
chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
||||||
sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "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"
|
mv "PrismLauncher.app" "Prism Launcher.app"
|
||||||
tar -czf ../PrismLauncher.tar.gz *
|
|
||||||
|
- 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)
|
- name: Make Sparkle signature (macOS)
|
||||||
if: matrix.name == 'macOS'
|
if: matrix.name == 'macOS'
|
||||||
@ -353,7 +398,7 @@ jobs:
|
|||||||
if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then
|
if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then
|
||||||
brew install openssl@3
|
brew install openssl@3
|
||||||
echo '${{ secrets.SPARKLE_ED25519_KEY }}' > ed25519-priv.pem
|
echo '${{ secrets.SPARKLE_ED25519_KEY }}' > ed25519-priv.pem
|
||||||
signature=$(/usr/local/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.tar.gz -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
|
signature=$(/usr/local/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.zip -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
|
||||||
rm ed25519-priv.pem
|
rm ed25519-priv.pem
|
||||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||||
### Artifact Information :information_source:
|
### Artifact Information :information_source:
|
||||||
@ -379,12 +424,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }}
|
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }}
|
||||||
|
|
||||||
cd ${{ env.INSTALL_DIR }}
|
|
||||||
if ("${{ matrix.qt_ver }}" -eq "5")
|
|
||||||
{
|
|
||||||
Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll
|
|
||||||
Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll
|
|
||||||
}
|
|
||||||
cd ${{ github.workspace }}
|
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
|
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
|
||||||
@ -437,26 +476,6 @@ jobs:
|
|||||||
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||||
}
|
}
|
||||||
|
|
||||||
- name: Package (Linux)
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
run: |
|
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }}
|
|
||||||
for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_DIR }}/manifest.txt
|
|
||||||
|
|
||||||
cd ${{ env.INSTALL_DIR }}
|
|
||||||
tar --owner root --group root -czf ../PrismLauncher.tar.gz *
|
|
||||||
|
|
||||||
- name: Package (Linux, portable)
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
run: |
|
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
|
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
|
||||||
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: Package AppImage (Linux)
|
- name: Package AppImage (Linux)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
shell: bash
|
shell: bash
|
||||||
@ -472,13 +491,9 @@ jobs:
|
|||||||
|
|
||||||
chmod +x linuxdeploy-*.AppImage
|
chmod +x linuxdeploy-*.AppImage
|
||||||
|
|
||||||
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-{8,17}-openjdk
|
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib
|
||||||
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
||||||
|
|
||||||
cp -r ${{ github.workspace }}/JREs/jre8/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk
|
|
||||||
|
|
||||||
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk
|
|
||||||
|
|
||||||
cp -r ${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ 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/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||||
@ -486,10 +501,6 @@ jobs:
|
|||||||
cp /usr/lib/x86_64-linux-gnu/libOpenGL.so.0* ${{ 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"
|
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
|
||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server"
|
|
||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64"
|
|
||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib/server"
|
|
||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
|
|
||||||
export LD_LIBRARY_PATH
|
export LD_LIBRARY_PATH
|
||||||
|
|
||||||
chmod +x AppImageUpdate-x86_64.AppImage
|
chmod +x AppImageUpdate-x86_64.AppImage
|
||||||
@ -511,76 +522,81 @@ jobs:
|
|||||||
|
|
||||||
mv "PrismLauncher-Linux-x86_64.AppImage" "PrismLauncher-Linux-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
|
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
|
# UPLOAD BUILDS
|
||||||
##
|
##
|
||||||
|
|
||||||
- name: Upload binary tarball (macOS)
|
- name: Upload binary tarball (macOS)
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PrismLauncher.tar.gz
|
path: PrismLauncher.zip
|
||||||
|
|
||||||
- name: Upload binary zip (Windows)
|
- name: Upload binary zip (Windows)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: ${{ env.INSTALL_DIR }}/**
|
path: ${{ env.INSTALL_DIR }}/**
|
||||||
|
|
||||||
- name: Upload binary zip (Windows, portable)
|
- name: Upload binary zip (Windows, portable)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: ${{ env.INSTALL_PORTABLE_DIR }}/**
|
path: ${{ env.INSTALL_PORTABLE_DIR }}/**
|
||||||
|
|
||||||
- name: Upload installer (Windows)
|
- name: Upload installer (Windows)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PrismLauncher-Setup.exe
|
path: PrismLauncher-Setup.exe
|
||||||
|
|
||||||
- name: Upload binary tarball (Linux, Qt 5)
|
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: PrismLauncher-${{ runner.os }}-Qt5-${{ env.VERSION }}-${{ inputs.build_type }}
|
|
||||||
path: PrismLauncher.tar.gz
|
|
||||||
|
|
||||||
- name: Upload binary tarball (Linux, portable, Qt 5)
|
- name: Upload binary tarball (Linux, portable, Qt 5)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ runner.os }}-Qt5-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ runner.os }}-Qt5-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PrismLauncher-portable.tar.gz
|
path: PrismLauncher-portable.tar.gz
|
||||||
|
|
||||||
- name: Upload binary tarball (Linux, Qt 6)
|
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver !=5
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: PrismLauncher-${{ runner.os }}-Qt6-${{ env.VERSION }}-${{ inputs.build_type }}
|
|
||||||
path: PrismLauncher.tar.gz
|
|
||||||
|
|
||||||
- name: Upload binary tarball (Linux, portable, Qt 6)
|
- name: Upload binary tarball (Linux, portable, Qt 6)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PrismLauncher-portable.tar.gz
|
path: PrismLauncher-portable.tar.gz
|
||||||
|
|
||||||
- name: Upload AppImage (Linux)
|
- name: Upload AppImage (Linux)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||||
path: 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)
|
- name: Upload AppImage Zsync (Linux)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync
|
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync
|
||||||
path: PrismLauncher-Linux-x86_64.AppImage.zsync
|
path: PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||||
|
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v3
|
||||||
with:
|
with:
|
||||||
config-file: ./.github/codeql/codeql-config.yml
|
config-file: ./.github/codeql/codeql-config.yml
|
||||||
queries: security-and-quality
|
queries: security-and-quality
|
||||||
@ -32,4 +32,4 @@ jobs:
|
|||||||
cmake --build build
|
cmake --build build
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v3
|
||||||
|
7
.github/workflows/trigger_builds.yml
vendored
7
.github/workflows/trigger_builds.yml
vendored
@ -32,6 +32,11 @@ jobs:
|
|||||||
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
||||||
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
||||||
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
APPLE_CODESIGN_CERT: ${{ secrets.APPLE_CODESIGN_CERT }}
|
||||||
|
APPLE_CODESIGN_PASSWORD: ${{ secrets.APPLE_CODESIGN_PASSWORD }}
|
||||||
|
APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }}
|
||||||
|
APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
|
||||||
|
APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
|
||||||
|
APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
|
||||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||||
|
23
.github/workflows/trigger_release.yml
vendored
23
.github/workflows/trigger_release.yml
vendored
@ -16,7 +16,12 @@ jobs:
|
|||||||
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
||||||
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
||||||
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
APPLE_CODESIGN_CERT: ${{ secrets.APPLE_CODESIGN_CERT }}
|
||||||
|
APPLE_CODESIGN_PASSWORD: ${{ secrets.APPLE_CODESIGN_PASSWORD }}
|
||||||
|
APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }}
|
||||||
|
APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
|
||||||
|
APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
|
||||||
|
APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
|
||||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||||
|
|
||||||
@ -32,7 +37,7 @@ jobs:
|
|||||||
submodules: "true"
|
submodules: "true"
|
||||||
path: "PrismLauncher-source"
|
path: "PrismLauncher-source"
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
- name: Grab and store version
|
- name: Grab and store version
|
||||||
run: |
|
run: |
|
||||||
tag_name=$(echo ${{ github.ref }} | grep -oE "[^/]+$")
|
tag_name=$(echo ${{ github.ref }} | grep -oE "[^/]+$")
|
||||||
@ -41,13 +46,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
|
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-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||||
mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
|
||||||
mv PrismLauncher-Linux-Qt5-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
|
mv PrismLauncher-Linux-Qt5-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
|
||||||
mv PrismLauncher-Linux-Qt5*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz
|
|
||||||
mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage
|
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-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||||
mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
mv PrismLauncher-macOS-Legacy*/PrismLauncher.zip PrismLauncher-macOS-Legacy-${{ env.VERSION }}.zip
|
||||||
mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
|
mv PrismLauncher-macOS*/PrismLauncher.zip PrismLauncher-macOS-${{ env.VERSION }}.zip
|
||||||
|
|
||||||
tar --exclude='.git' -czf PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }}
|
tar --exclude='.git' -czf PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }}
|
||||||
|
|
||||||
@ -79,7 +82,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
id: create_release
|
id: create_release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
tag_name: ${{ github.ref }}
|
tag_name: ${{ github.ref }}
|
||||||
@ -87,11 +90,9 @@ jobs:
|
|||||||
draft: true
|
draft: true
|
||||||
prerelease: false
|
prerelease: false
|
||||||
files: |
|
files: |
|
||||||
PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz
|
|
||||||
PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
|
PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
|
||||||
PrismLauncher-Linux-x86_64.AppImage
|
PrismLauncher-Linux-x86_64.AppImage
|
||||||
PrismLauncher-Linux-x86_64.AppImage.zsync
|
PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||||
PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
|
||||||
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||||
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip
|
||||||
@ -102,6 +103,6 @@ jobs:
|
|||||||
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MSVC-Portable-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MSVC-Portable-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MSVC-Setup-${{ env.VERSION }}.exe
|
PrismLauncher-Windows-MSVC-Setup-${{ env.VERSION }}.exe
|
||||||
PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
|
PrismLauncher-macOS-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
PrismLauncher-macOS-Legacy-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-${{ env.VERSION }}.tar.gz
|
PrismLauncher-${{ env.VERSION }}.tar.gz
|
||||||
|
4
.github/workflows/update-flake.yml
vendored
4
.github/workflows/update-flake.yml
vendored
@ -17,9 +17,9 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: cachix/install-nix-action@6a9a9e84a173d90b3ffb42c5ddaf9ea033fad011 # v23
|
- uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26
|
||||||
|
|
||||||
- uses: DeterminateSystems/update-flake-lock@v20
|
- uses: DeterminateSystems/update-flake-lock@v21
|
||||||
with:
|
with:
|
||||||
commit-msg: "chore(nix): update lockfile"
|
commit-msg: "chore(nix): update lockfile"
|
||||||
pr-title: "chore(nix): update lockfile"
|
pr-title: "chore(nix): update lockfile"
|
||||||
|
@ -178,7 +178,7 @@ set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL th
|
|||||||
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
||||||
|
|
||||||
######## Set version numbers ########
|
######## Set version numbers ########
|
||||||
set(Launcher_VERSION_MAJOR 8)
|
set(Launcher_VERSION_MAJOR 9)
|
||||||
set(Launcher_VERSION_MINOR 0)
|
set(Launcher_VERSION_MINOR 0)
|
||||||
|
|
||||||
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
||||||
@ -377,12 +377,12 @@ if(UNIX AND APPLE)
|
|||||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}")
|
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}")
|
||||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}")
|
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}")
|
||||||
set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns)
|
set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns)
|
||||||
set(MACOSX_BUNDLE_COPYRIGHT "© 2022-2023 ${Launcher_Copyright_Mac}")
|
set(MACOSX_BUNDLE_COPYRIGHT "${Launcher_Copyright_Mac}")
|
||||||
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=" CACHE STRING "Public key for Sparkle update feed")
|
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_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.1.0/Sparkle-2.1.0.tar.xz" CACHE STRING "URL to 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 "bf6ac1caa9f8d321d5784859c88da874f28412f37fb327bc21b7b14c5d61ef94" CACHE STRING "SHA256 checksum for 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")
|
set(MACOSX_SPARKLE_DIR "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
|
||||||
|
|
||||||
# directories to look for dependencies
|
# directories to look for dependencies
|
||||||
@ -417,7 +417,19 @@ elseif(UNIX)
|
|||||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
|
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
|
||||||
|
|
||||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "share/${Launcher_Name}")
|
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "share/${Launcher_Name}")
|
||||||
|
|
||||||
|
if (INSTALL_BUNDLE STREQUAL full)
|
||||||
|
set(PLUGIN_DEST_DIR "plugins")
|
||||||
|
set(BUNDLE_DEST_DIR ".")
|
||||||
|
set(RESOURCES_DEST_DIR ".")
|
||||||
|
|
||||||
|
# Apps to bundle
|
||||||
|
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${Launcher_APP_BINARY_NAME}")
|
||||||
|
|
||||||
|
# directories to look for dependencies
|
||||||
|
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
||||||
|
endif()
|
||||||
|
|
||||||
if(Launcher_ManPage)
|
if(Launcher_ManPage)
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")
|
||||||
endif()
|
endif()
|
||||||
@ -504,11 +516,10 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
if(NOT cmark_FOUND)
|
if(NOT cmark_FOUND)
|
||||||
message(STATUS "Using bundled cmark")
|
message(STATUS "Using bundled cmark")
|
||||||
set(CMARK_STATIC ON CACHE BOOL "Build static libcmark library" FORCE)
|
set(BUILD_TESTING 0)
|
||||||
set(CMARK_SHARED OFF CACHE BOOL "Build shared libcmark library" FORCE)
|
set(BUILD_SHARED_LIBS 0)
|
||||||
set(CMARK_TESTS OFF CACHE BOOL "Build cmark tests and enable testing" FORCE)
|
|
||||||
add_subdirectory(libraries/cmark EXCLUDE_FROM_ALL) # Markdown parser
|
add_subdirectory(libraries/cmark EXCLUDE_FROM_ALL) # Markdown parser
|
||||||
add_library(cmark::cmark ALIAS cmark_static)
|
add_library(cmark::cmark ALIAS cmark)
|
||||||
else()
|
else()
|
||||||
message(STATUS "Using system cmark")
|
message(STATUS "Using system cmark")
|
||||||
endif()
|
endif()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
## Prism Launcher
|
## Prism Launcher
|
||||||
|
|
||||||
Prism Launcher - Minecraft Launcher
|
Prism Launcher - Minecraft Launcher
|
||||||
Copyright (C) 2022-2023 Prism Launcher Contributors
|
Copyright (C) 2022-2024 Prism Launcher Contributors
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -436,7 +436,7 @@
|
|||||||
Copyright (C) 2007 Johann Ollivier Lapeyre <johann@oxygen-icons.org>
|
Copyright (C) 2007 Johann Ollivier Lapeyre <johann@oxygen-icons.org>
|
||||||
Copyright (C) 2007 Kenneth Wimer <kwwii@bootsplash.org>
|
Copyright (C) 2007 Kenneth Wimer <kwwii@bootsplash.org>
|
||||||
Copyright (C) 2007 Riccardo Iaconelli <riccardo@oxygen-icons.org>
|
Copyright (C) 2007 Riccardo Iaconelli <riccardo@oxygen-icons.org>
|
||||||
|
|
||||||
and others
|
and others
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
This library is free software; you can redistribute it and/or
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
<string>A Minecraft mod wants to access your camera.</string>
|
<string>A Minecraft mod wants to access your camera.</string>
|
||||||
<key>NSMicrophoneUsageDescription</key>
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
<string>A Minecraft mod wants to access your microphone.</string>
|
<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>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
<string>NSApplication</string>
|
<string>NSApplication</string>
|
||||||
<key>NSHighResolutionCapable</key>
|
<key>NSHighResolutionCapable</key>
|
||||||
|
68
flake.lock
generated
68
flake.lock
generated
@ -18,14 +18,16 @@
|
|||||||
},
|
},
|
||||||
"flake-parts": {
|
"flake-parts": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs-lib": "nixpkgs-lib"
|
"nixpkgs-lib": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1698882062,
|
"lastModified": 1714641030,
|
||||||
"narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=",
|
"narHash": "sha256-yzcRNDoyVP7+SCNX0wmuDju1NUCt8Dz9+lyUXEI0dbI=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "8c9fa2545007b49a5db5f650ae91f227672c3877",
|
"rev": "e5d10a24b66c3ea8f150e47dfdb0416ab7c3390e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -39,11 +41,11 @@
|
|||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1685518550,
|
"lastModified": 1710146030,
|
||||||
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
|
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
|
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -60,11 +62,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1660459072,
|
"lastModified": 1709087332,
|
||||||
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
|
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "gitignore.nix",
|
"repo": "gitignore.nix",
|
||||||
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
|
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -89,28 +91,13 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nix-filter": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1694857738,
|
|
||||||
"narHash": "sha256-bxxNyLHjhu0N8T3REINXQ2ZkJco0ABFPn6PIe2QUfqo=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "nix-filter",
|
|
||||||
"rev": "41fd48e00c22b4ced525af521ead8792402de0ea",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "nix-filter",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1700108881,
|
"lastModified": 1715413075,
|
||||||
"narHash": "sha256-+Lqybl8kj0+nD/IlAWPPG/RDTa47gff9nbei0u7BntE=",
|
"narHash": "sha256-FCi3R1MeS5bVp0M0xTheveP6hhcCYfW/aghSTPebYL4=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "7414e9ee0b3e9903c24d3379f577a417f0aae5f1",
|
"rev": "e4e7a43a9db7e22613accfeb1005cca1b2b1ee0d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -120,24 +107,6 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-lib": {
|
|
||||||
"locked": {
|
|
||||||
"dir": "lib",
|
|
||||||
"lastModified": 1698611440,
|
|
||||||
"narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"dir": "lib",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixos-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pre-commit-hooks": {
|
"pre-commit-hooks": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": [
|
"flake-compat": [
|
||||||
@ -153,11 +122,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1700064067,
|
"lastModified": 1714478972,
|
||||||
"narHash": "sha256-1ZWNDzhu8UlVCK7+DUN9dVQfiHX1bv6OQP9VxstY/gs=",
|
"narHash": "sha256-q//cgb52vv81uOuwz1LaXElp3XAe1TqrABXODAEF6Sk=",
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "pre-commit-hooks.nix",
|
"repo": "pre-commit-hooks.nix",
|
||||||
"rev": "e558068cba67b23b4fbc5537173dbb43748a17e8",
|
"rev": "2849da033884f54822af194400f8dff435ada242",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -171,7 +140,6 @@
|
|||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
"flake-parts": "flake-parts",
|
"flake-parts": "flake-parts",
|
||||||
"libnbtplusplus": "libnbtplusplus",
|
"libnbtplusplus": "libnbtplusplus",
|
||||||
"nix-filter": "nix-filter",
|
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"pre-commit-hooks": "pre-commit-hooks"
|
"pre-commit-hooks": "pre-commit-hooks"
|
||||||
}
|
}
|
||||||
|
19
flake.nix
19
flake.nix
@ -1,15 +1,24 @@
|
|||||||
{
|
{
|
||||||
description = "A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once (Fork of MultiMC)";
|
description = "A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once (Fork of MultiMC)";
|
||||||
|
|
||||||
|
nixConfig = {
|
||||||
|
extra-substituters = ["https://cache.garnix.io"];
|
||||||
|
extra-trusted-public-keys = ["cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g="];
|
||||||
|
};
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
flake-parts = {
|
||||||
nix-filter.url = "github:numtide/nix-filter";
|
url = "github:hercules-ci/flake-parts";
|
||||||
|
inputs.nixpkgs-lib.follows = "nixpkgs";
|
||||||
|
};
|
||||||
pre-commit-hooks = {
|
pre-commit-hooks = {
|
||||||
url = "github:cachix/pre-commit-hooks.nix";
|
url = "github:cachix/pre-commit-hooks.nix";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs = {
|
||||||
inputs.nixpkgs-stable.follows = "nixpkgs";
|
nixpkgs.follows = "nixpkgs";
|
||||||
inputs.flake-compat.follows = "flake-compat";
|
nixpkgs-stable.follows = "nixpkgs";
|
||||||
|
flake-compat.follows = "flake-compat";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
flake-compat = {
|
flake-compat = {
|
||||||
url = "github:edolstra/flake-compat";
|
url = "github:edolstra/flake-compat";
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
id: org.prismlauncher.PrismLauncher
|
id: org.prismlauncher.PrismLauncher
|
||||||
runtime: org.kde.Platform
|
runtime: org.kde.Platform
|
||||||
runtime-version: "5.15-23.08"
|
runtime-version: 5.15-23.08
|
||||||
sdk: org.kde.Sdk
|
sdk: org.kde.Sdk
|
||||||
sdk-extensions:
|
sdk-extensions:
|
||||||
|
- org.freedesktop.Sdk.Extension.openjdk21
|
||||||
- org.freedesktop.Sdk.Extension.openjdk17
|
- org.freedesktop.Sdk.Extension.openjdk17
|
||||||
- org.freedesktop.Sdk.Extension.openjdk8
|
- org.freedesktop.Sdk.Extension.openjdk8
|
||||||
|
|
||||||
@ -50,6 +51,8 @@ modules:
|
|||||||
buildsystem: simple
|
buildsystem: simple
|
||||||
build-commands:
|
build-commands:
|
||||||
- mkdir -p /app/jdk/
|
- mkdir -p /app/jdk/
|
||||||
|
- /usr/lib/sdk/openjdk21/install.sh
|
||||||
|
- mv /app/jre /app/jdk/21
|
||||||
- /usr/lib/sdk/openjdk17/install.sh
|
- /usr/lib/sdk/openjdk17/install.sh
|
||||||
- mv /app/jre /app/jdk/17
|
- mv /app/jre /app/jdk/17
|
||||||
- /usr/lib/sdk/openjdk8/install.sh
|
- /usr/lib/sdk/openjdk8/install.sh
|
||||||
@ -104,18 +107,15 @@ modules:
|
|||||||
- install -Dm755 ../data/gamemoderun -t /app/bin
|
- install -Dm755 ../data/gamemoderun -t /app/bin
|
||||||
sources:
|
sources:
|
||||||
- type: archive
|
- type: archive
|
||||||
archive-type: tar-gzip
|
dest-filename: gamemode.tar.gz
|
||||||
url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.7
|
url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.8.1
|
||||||
sha256: 57ce73ba605d1cf12f8d13725006a895182308d93eba0f69f285648449641803
|
sha256: 969cf85b5ca3944f3e315cd73a0ee9bea4f9c968cd7d485e9f4745bc1e679c4e
|
||||||
x-checker-data:
|
x-checker-data:
|
||||||
type: json
|
type: json
|
||||||
url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest
|
url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest
|
||||||
version-query: .tag_name
|
version-query: .tag_name
|
||||||
url-query: .tarball_url
|
url-query: .tarball_url
|
||||||
timestamp-query: .published_at
|
timestamp-query: .published_at
|
||||||
# from https://github.com/flathub/net.gaijin.WarThunder/blob/7ea6f7a9f84b9c77150c003a7059dc03f8dcbc7f/gamemode.patch
|
|
||||||
- type: patch
|
|
||||||
path: patches/gamemode.patch
|
|
||||||
cleanup:
|
cleanup:
|
||||||
- /include
|
- /include
|
||||||
- /lib/pkgconfig
|
- /lib/pkgconfig
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
diff -ruN a/common/common-pidfds.c b/common/common-pidfds.c
|
|
||||||
--- a/common/common-pidfds.c 2021-02-18 20:00:12.000000000 +0100
|
|
||||||
+++ b/common/common-pidfds.c 2023-09-07 08:57:42.954362763 +0200
|
|
||||||
@@ -58,6 +58,8 @@
|
|
||||||
{
|
|
||||||
return (int)syscall(__NR_pidfd_open, pid, flags);
|
|
||||||
}
|
|
||||||
+#else
|
|
||||||
+#include <sys/pidfd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* pidfd functions */
|
|
@ -1 +1 @@
|
|||||||
Subproject commit 45094ca570be383d06df729b6972830ec63bd3df
|
Subproject commit f2b0c16a2a217a1822ce5a6538ba8f755ed1dd32
|
@ -132,6 +132,15 @@
|
|||||||
#include "gamemode_client.h"
|
#include "gamemode_client.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(Q_OS_LINUX)
|
||||||
|
#include <sys/statvfs.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_MAC)
|
#if defined(Q_OS_MAC)
|
||||||
#if defined(SPARKLE_ENABLED)
|
#if defined(SPARKLE_ENABLED)
|
||||||
#include "updater/MacSparkleUpdater.h"
|
#include "updater/MacSparkleUpdater.h"
|
||||||
@ -216,6 +225,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
// Don't quit on hiding the last window
|
// Don't quit on hiding the last window
|
||||||
this->setQuitOnLastWindowClosed(false);
|
this->setQuitOnLastWindowClosed(false);
|
||||||
|
this->setQuitLockEnabled(false);
|
||||||
|
|
||||||
// Commandline parsing
|
// Commandline parsing
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
@ -299,7 +309,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
adjustedBy = "Persistent data path";
|
adjustedBy = "Persistent data path";
|
||||||
|
|
||||||
#ifndef Q_OS_MACOS
|
#ifndef Q_OS_MACOS
|
||||||
if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) {
|
if (auto portableUserData = FS::PathCombine(m_rootPath, "UserData"); QDir(portableUserData).exists()) {
|
||||||
|
dataPath = portableUserData;
|
||||||
|
adjustedBy = "Portable user data path";
|
||||||
|
m_portable = true;
|
||||||
|
} else if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) {
|
||||||
dataPath = m_rootPath;
|
dataPath = m_rootPath;
|
||||||
adjustedBy = "Portable data path";
|
adjustedBy = "Portable data path";
|
||||||
m_portable = true;
|
m_portable = true;
|
||||||
@ -485,8 +499,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << ", (c) 2022-2023 "
|
qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME + ", " + QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", "));
|
||||||
<< qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", "));
|
|
||||||
qDebug() << "Version : " << BuildConfig.printableVersionString();
|
qDebug() << "Version : " << BuildConfig.printableVersionString();
|
||||||
qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM;
|
qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM;
|
||||||
qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
|
qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
|
||||||
@ -632,10 +645,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
m_settings->registerSetting("UseNativeGLFW", false);
|
m_settings->registerSetting("UseNativeGLFW", false);
|
||||||
m_settings->registerSetting("CustomGLFWPath", "");
|
m_settings->registerSetting("CustomGLFWPath", "");
|
||||||
|
|
||||||
// Peformance related options
|
// Performance related options
|
||||||
m_settings->registerSetting("EnableFeralGamemode", false);
|
m_settings->registerSetting("EnableFeralGamemode", false);
|
||||||
m_settings->registerSetting("EnableMangoHud", false);
|
m_settings->registerSetting("EnableMangoHud", false);
|
||||||
m_settings->registerSetting("UseDiscreteGpu", false);
|
m_settings->registerSetting("UseDiscreteGpu", false);
|
||||||
|
m_settings->registerSetting("UseZink", false);
|
||||||
|
|
||||||
// Game time
|
// Game time
|
||||||
m_settings->registerSetting("ShowGameTime", true);
|
m_settings->registerSetting("ShowGameTime", true);
|
||||||
@ -659,6 +673,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
// The cat
|
// The cat
|
||||||
m_settings->registerSetting("TheCat", false);
|
m_settings->registerSetting("TheCat", false);
|
||||||
|
m_settings->registerSetting("CatOpacity", 100);
|
||||||
|
|
||||||
|
m_settings->registerSetting("StatusBarVisible", true);
|
||||||
|
|
||||||
m_settings->registerSetting("ToolbarsLocked", false);
|
m_settings->registerSetting("ToolbarsLocked", false);
|
||||||
|
|
||||||
@ -741,6 +758,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
m_settings->registerSetting("ModrinthToken", "");
|
m_settings->registerSetting("ModrinthToken", "");
|
||||||
m_settings->registerSetting("UserAgentOverride", "");
|
m_settings->registerSetting("UserAgentOverride", "");
|
||||||
|
|
||||||
|
// FTBApp instances
|
||||||
|
m_settings->registerSetting("FTBAppInstancesPath", "");
|
||||||
|
|
||||||
// Init page provider
|
// Init page provider
|
||||||
{
|
{
|
||||||
m_globalSettingsProvider = std::make_shared<GenericPageProvider>(tr("Settings"));
|
m_globalSettingsProvider = std::make_shared<GenericPageProvider>(tr("Settings"));
|
||||||
@ -989,6 +1009,37 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// notify user if /tmp is mounted with `noexec` (#1693)
|
||||||
|
{
|
||||||
|
bool is_tmp_noexec = false;
|
||||||
|
|
||||||
|
#if defined(Q_OS_LINUX)
|
||||||
|
|
||||||
|
struct statvfs tmp_stat;
|
||||||
|
statvfs("/tmp", &tmp_stat);
|
||||||
|
is_tmp_noexec = tmp_stat.f_flag & ST_NOEXEC;
|
||||||
|
|
||||||
|
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||||
|
|
||||||
|
struct statfs tmp_stat;
|
||||||
|
statfs("/tmp", &tmp_stat);
|
||||||
|
is_tmp_noexec = tmp_stat.f_flags & MNT_NOEXEC;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (is_tmp_noexec) {
|
||||||
|
auto infoMsg =
|
||||||
|
tr("Your /tmp directory is currently mounted with the 'noexec' flag enabled.\n"
|
||||||
|
"Some versions of Minecraft may not launch.\n");
|
||||||
|
auto msgBox = new QMessageBox(QMessageBox::Information, tr("Incompatible system configuration"), infoMsg, QMessageBox::Ok);
|
||||||
|
msgBox->setDefaultButton(QMessageBox::Ok);
|
||||||
|
msgBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
msgBox->setMinimumWidth(460);
|
||||||
|
msgBox->adjustSize();
|
||||||
|
msgBox->open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (createSetupWizard()) {
|
if (createSetupWizard()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1472,6 +1523,17 @@ InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString pa
|
|||||||
auto& window = extras.window;
|
auto& window = extras.window;
|
||||||
|
|
||||||
if (window) {
|
if (window) {
|
||||||
|
// If the window is minimized on macOS or Windows, activate and bring it up
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
if (window->isMinimized()) {
|
||||||
|
window->setWindowState(window->windowState() & ~Qt::WindowMinimized);
|
||||||
|
}
|
||||||
|
#elif defined(Q_OS_WIN)
|
||||||
|
if (window->isMinimized()) {
|
||||||
|
window->showNormal();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
window->raise();
|
window->raise();
|
||||||
window->activateWindow();
|
window->activateWindow();
|
||||||
} else {
|
} else {
|
||||||
@ -1479,6 +1541,7 @@ InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString pa
|
|||||||
m_openWindows++;
|
m_openWindows++;
|
||||||
connect(window, &InstanceWindow::isClosing, this, &Application::on_windowClose);
|
connect(window, &InstanceWindow::isClosing, this, &Application::on_windowClose);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!page.isEmpty()) {
|
if (!page.isEmpty()) {
|
||||||
window->selectPage(page);
|
window->selectPage(page);
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,8 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
|
|||||||
|
|
||||||
m_settings->registerSetting("lastLaunchTime", 0);
|
m_settings->registerSetting("lastLaunchTime", 0);
|
||||||
m_settings->registerSetting("totalTimePlayed", 0);
|
m_settings->registerSetting("totalTimePlayed", 0);
|
||||||
|
if (m_settings->get("totalTimePlayed").toLongLong() < 0)
|
||||||
|
m_settings->reset("totalTimePlayed");
|
||||||
m_settings->registerSetting("lastTimePlayed", 0);
|
m_settings->registerSetting("lastTimePlayed", 0);
|
||||||
|
|
||||||
m_settings->registerSetting("linkedInstances", "[]");
|
m_settings->registerSetting("linkedInstances", "[]");
|
||||||
|
@ -829,6 +829,8 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/themes/DarkTheme.h
|
ui/themes/DarkTheme.h
|
||||||
ui/themes/ITheme.cpp
|
ui/themes/ITheme.cpp
|
||||||
ui/themes/ITheme.h
|
ui/themes/ITheme.h
|
||||||
|
ui/themes/HintOverrideProxyStyle.cpp
|
||||||
|
ui/themes/HintOverrideProxyStyle.h
|
||||||
ui/themes/SystemTheme.cpp
|
ui/themes/SystemTheme.cpp
|
||||||
ui/themes/SystemTheme.h
|
ui/themes/SystemTheme.h
|
||||||
ui/themes/IconTheme.cpp
|
ui/themes/IconTheme.cpp
|
||||||
@ -1496,7 +1498,6 @@ if(INSTALL_BUNDLE STREQUAL "full")
|
|||||||
CONFIGURATIONS Debug RelWithDebInfo ""
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
DESTINATION ${PLUGIN_DEST_DIR}
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
COMPONENT Runtime
|
COMPONENT Runtime
|
||||||
PATTERN "*qopensslbackend*" EXCLUDE
|
|
||||||
PATTERN "*qcertonlybackend*" EXCLUDE
|
PATTERN "*qcertonlybackend*" EXCLUDE
|
||||||
)
|
)
|
||||||
install(
|
install(
|
||||||
@ -1507,10 +1508,78 @@ if(INSTALL_BUNDLE STREQUAL "full")
|
|||||||
REGEX "dd\\." EXCLUDE
|
REGEX "dd\\." EXCLUDE
|
||||||
REGEX "_debug\\." EXCLUDE
|
REGEX "_debug\\." EXCLUDE
|
||||||
REGEX "\\.dSYM" EXCLUDE
|
REGEX "\\.dSYM" EXCLUDE
|
||||||
PATTERN "*qopensslbackend*" EXCLUDE
|
|
||||||
PATTERN "*qcertonlybackend*" EXCLUDE
|
PATTERN "*qcertonlybackend*" EXCLUDE
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
# Wayland support
|
||||||
|
if(EXISTS "${QT_PLUGINS_DIR}/wayland-graphics-integration-client")
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-client"
|
||||||
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-client"
|
||||||
|
CONFIGURATIONS Release MinSizeRel
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
REGEX "dd\\." EXCLUDE
|
||||||
|
REGEX "_debug\\." EXCLUDE
|
||||||
|
REGEX "\\.dSYM" EXCLUDE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
if(EXISTS "${QT_PLUGINS_DIR}/wayland-graphics-integration-server")
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-server"
|
||||||
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-server"
|
||||||
|
CONFIGURATIONS Release MinSizeRel
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
REGEX "dd\\." EXCLUDE
|
||||||
|
REGEX "_debug\\." EXCLUDE
|
||||||
|
REGEX "\\.dSYM" EXCLUDE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
if(EXISTS "${QT_PLUGINS_DIR}/wayland-decoration-client")
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/wayland-decoration-client"
|
||||||
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/wayland-decoration-client"
|
||||||
|
CONFIGURATIONS Release MinSizeRel
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
REGEX "dd\\." EXCLUDE
|
||||||
|
REGEX "_debug\\." EXCLUDE
|
||||||
|
REGEX "\\.dSYM" EXCLUDE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
if(EXISTS "${QT_PLUGINS_DIR}/wayland-shell-integration")
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/wayland-shell-integration"
|
||||||
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/wayland-shell-integration"
|
||||||
|
CONFIGURATIONS Release MinSizeRel
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
REGEX "dd\\." EXCLUDE
|
||||||
|
REGEX "_debug\\." EXCLUDE
|
||||||
|
REGEX "\\.dSYM" EXCLUDE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
configure_file(
|
configure_file(
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"
|
"${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake"
|
"${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake"
|
||||||
|
@ -37,140 +37,33 @@
|
|||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
#include "FileSystem.h"
|
||||||
/**
|
|
||||||
* This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing.
|
|
||||||
*/
|
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool IndirectOpen(T callable, qint64* pid_forked = nullptr)
|
|
||||||
{
|
|
||||||
auto pid = fork();
|
|
||||||
if (pid_forked) {
|
|
||||||
if (pid > 0)
|
|
||||||
*pid_forked = pid;
|
|
||||||
else
|
|
||||||
*pid_forked = 0;
|
|
||||||
}
|
|
||||||
if (pid == -1) {
|
|
||||||
qWarning() << "IndirectOpen failed to fork: " << errno;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// child - do the stuff
|
|
||||||
if (pid == 0) {
|
|
||||||
// unset all this garbage so it doesn't get passed to the child process
|
|
||||||
qunsetenv("LD_PRELOAD");
|
|
||||||
qunsetenv("LD_LIBRARY_PATH");
|
|
||||||
qunsetenv("LD_DEBUG");
|
|
||||||
qunsetenv("QT_PLUGIN_PATH");
|
|
||||||
qunsetenv("QT_FONTPATH");
|
|
||||||
|
|
||||||
// open the URL
|
|
||||||
auto status = callable();
|
|
||||||
|
|
||||||
// detach from the parent process group.
|
|
||||||
setsid();
|
|
||||||
|
|
||||||
// die. now. do not clean up anything, it would just hang forever.
|
|
||||||
_exit(status ? 0 : 1);
|
|
||||||
} else {
|
|
||||||
// parent - assume it worked.
|
|
||||||
int status;
|
|
||||||
while (waitpid(pid, &status, 0)) {
|
|
||||||
if (WIFEXITED(status)) {
|
|
||||||
return WEXITSTATUS(status) == 0;
|
|
||||||
}
|
|
||||||
if (WIFSIGNALED(status)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace DesktopServices {
|
namespace DesktopServices {
|
||||||
bool openDirectory(const QString& path, [[maybe_unused]] bool ensureExists)
|
bool openPath(const QFileInfo& path, bool ensureFolderPathExists)
|
||||||
{
|
{
|
||||||
qDebug() << "Opening directory" << path;
|
qDebug() << "Opening path" << path;
|
||||||
QDir parentPath;
|
if (ensureFolderPathExists) {
|
||||||
QDir dir(path);
|
FS::ensureFolderPathExists(path);
|
||||||
if (ensureExists && !dir.exists()) {
|
|
||||||
parentPath.mkpath(dir.absolutePath());
|
|
||||||
}
|
}
|
||||||
auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); };
|
return openUrl(QUrl::fromLocalFile(QFileInfo(path).absoluteFilePath()));
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
|
||||||
if (!isSandbox()) {
|
|
||||||
return IndirectOpen(f);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return f();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool openFile(const QString& path)
|
bool openPath(const QString& path, bool ensureFolderPathExists)
|
||||||
{
|
{
|
||||||
qDebug() << "Opening file" << path;
|
return openPath(QFileInfo(path), ensureFolderPathExists);
|
||||||
auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(path)); };
|
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
|
||||||
if (!isSandbox()) {
|
|
||||||
return IndirectOpen(f);
|
|
||||||
} else {
|
|
||||||
return f();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
return f();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool openFile(const QString& application, const QString& path, const QString& workingDirectory, qint64* pid)
|
|
||||||
{
|
|
||||||
qDebug() << "Opening file" << path << "using" << application;
|
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
|
||||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
|
||||||
if (!isSandbox()) {
|
|
||||||
return IndirectOpen([&]() { return QProcess::startDetached(application, QStringList() << path, workingDirectory); }, pid);
|
|
||||||
} else {
|
|
||||||
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool run(const QString& application, const QStringList& args, const QString& workingDirectory, qint64* pid)
|
bool run(const QString& application, const QStringList& args, const QString& workingDirectory, qint64* pid)
|
||||||
{
|
{
|
||||||
qDebug() << "Running" << application << "with args" << args.join(' ');
|
qDebug() << "Running" << application << "with args" << args.join(' ');
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
|
||||||
if (!isSandbox()) {
|
|
||||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
|
||||||
return IndirectOpen([&]() { return QProcess::startDetached(application, args, workingDirectory); }, pid);
|
|
||||||
} else {
|
|
||||||
return QProcess::startDetached(application, args, workingDirectory, pid);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
return QProcess::startDetached(application, args, workingDirectory, pid);
|
return QProcess::startDetached(application, args, workingDirectory, pid);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool openUrl(const QUrl& url)
|
bool openUrl(const QUrl& url)
|
||||||
{
|
{
|
||||||
qDebug() << "Opening URL" << url.toString();
|
qDebug() << "Opening URL" << url.toString();
|
||||||
auto f = [&]() { return QDesktopServices::openUrl(url); };
|
return QDesktopServices::openUrl(url);
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
|
||||||
if (!isSandbox()) {
|
|
||||||
return IndirectOpen(f);
|
|
||||||
} else {
|
|
||||||
return f();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
return f();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isFlatpak()
|
bool isFlatpak()
|
||||||
@ -191,9 +84,4 @@ bool isSnap()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSandbox()
|
|
||||||
{
|
|
||||||
return isSnap() || isFlatpak();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace DesktopServices
|
} // namespace DesktopServices
|
||||||
|
@ -3,31 +3,30 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
|
class QFileInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This wraps around QDesktopServices and adds workarounds where needed
|
* This wraps around QDesktopServices and adds workarounds where needed
|
||||||
* Use this instead of QDesktopServices!
|
* Use this instead of QDesktopServices!
|
||||||
*/
|
*/
|
||||||
namespace DesktopServices {
|
namespace DesktopServices {
|
||||||
/**
|
/**
|
||||||
* Open a file in whatever application is applicable
|
* Open a path in whatever application is applicable.
|
||||||
|
* @param ensureFolderPathExists Make sure the path exists
|
||||||
*/
|
*/
|
||||||
bool openFile(const QString& path);
|
bool openPath(const QFileInfo& path, bool ensureFolderPathExists = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open a file in the specified application
|
* Open a path in whatever application is applicable.
|
||||||
|
* @param ensureFolderPathExists Make sure the path exists
|
||||||
*/
|
*/
|
||||||
bool openFile(const QString& application, const QString& path, const QString& workingDirectory = QString(), qint64* pid = 0);
|
bool openPath(const QString& path, bool ensureFolderPathExists = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run an application
|
* Run an application
|
||||||
*/
|
*/
|
||||||
bool run(const QString& application, const QStringList& args, const QString& workingDirectory = QString(), qint64* pid = 0);
|
bool run(const QString& application, const QStringList& args, const QString& workingDirectory = QString(), qint64* pid = 0);
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a directory
|
|
||||||
*/
|
|
||||||
bool openDirectory(const QString& path, bool ensureExists = false);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the URL, most likely in a browser. Maybe.
|
* Open the URL, most likely in a browser. Maybe.
|
||||||
*/
|
*/
|
||||||
@ -42,9 +41,4 @@ bool isFlatpak();
|
|||||||
* Determine whether the launcher is running in a Snap environment
|
* Determine whether the launcher is running in a Snap environment
|
||||||
*/
|
*/
|
||||||
bool isSnap();
|
bool isSnap();
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the launcher is running in a sandboxed (Flatpak or Snap) environment
|
|
||||||
*/
|
|
||||||
bool isSandbox();
|
|
||||||
} // namespace DesktopServices
|
} // namespace DesktopServices
|
||||||
|
@ -1,4 +1,37 @@
|
|||||||
// Licensed under the Apache-2.0 license. See README.md for details.
|
// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2024 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* 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
|
#pragma once
|
||||||
|
|
||||||
@ -8,12 +41,12 @@
|
|||||||
|
|
||||||
class Exception : public std::exception {
|
class Exception : public std::exception {
|
||||||
public:
|
public:
|
||||||
Exception(const QString& message) : std::exception(), m_message(message) { qCritical() << "Exception:" << message; }
|
Exception(const QString& message) : std::exception(), m_message(message.toUtf8()) { qCritical() << "Exception:" << message; }
|
||||||
Exception(const Exception& other) : std::exception(), m_message(other.cause()) {}
|
Exception(const Exception& other) : std::exception(), m_message(other.m_message) {}
|
||||||
virtual ~Exception() noexcept {}
|
virtual ~Exception() noexcept {}
|
||||||
const char* what() const noexcept { return m_message.toLatin1().constData(); }
|
const char* what() const noexcept { return m_message.constData(); }
|
||||||
QString cause() const { return m_message; }
|
QString cause() const { return QString::fromUtf8(m_message); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_message;
|
QByteArray m_message;
|
||||||
};
|
};
|
||||||
|
@ -272,15 +272,19 @@ bool ensureFilePathExists(QString filenamepath)
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ensureFolderPathExists(QString foldernamepath)
|
bool ensureFolderPathExists(const QFileInfo folderPath)
|
||||||
{
|
{
|
||||||
QFileInfo a(foldernamepath);
|
|
||||||
QDir dir;
|
QDir dir;
|
||||||
QString ensuredPath = a.filePath();
|
QString ensuredPath = folderPath.filePath();
|
||||||
bool success = dir.mkpath(ensuredPath);
|
bool success = dir.mkpath(ensuredPath);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ensureFolderPathExists(const QString folderPathName)
|
||||||
|
{
|
||||||
|
return ensureFolderPathExists(QFileInfo(folderPathName));
|
||||||
|
}
|
||||||
|
|
||||||
bool copyFileAttributes(QString src, QString dst)
|
bool copyFileAttributes(QString src, QString dst)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
@ -797,15 +801,24 @@ QString NormalizePath(QString path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString badFilenameChars = "\"\\/?<>:;*|!+\r\n";
|
static const QString BAD_PATH_CHARS = "\"?<>:;*|!+\r\n";
|
||||||
|
static const QString BAD_FILENAME_CHARS = BAD_PATH_CHARS + "\\/";
|
||||||
|
|
||||||
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
|
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < string.length(); i++) {
|
for (int i = 0; i < string.length(); i++)
|
||||||
if (badFilenameChars.contains(string[i])) {
|
if (string.at(i) < ' ' || BAD_FILENAME_CHARS.contains(string.at(i)))
|
||||||
string[i] = replaceWith;
|
string[i] = replaceWith;
|
||||||
}
|
|
||||||
}
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString RemoveInvalidPathChars(QString string, QChar replaceWith)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < string.length(); i++)
|
||||||
|
if (string.at(i) < ' ' || BAD_PATH_CHARS.contains(string.at(i)))
|
||||||
|
string[i] = replaceWith;
|
||||||
|
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1581,4 +1594,44 @@ uintmax_t hardLinkCount(const QString& path)
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
// returns 8.3 file format from long path
|
||||||
|
QString shortPathName(const QString& file)
|
||||||
|
{
|
||||||
|
auto input = file.toStdWString();
|
||||||
|
std::wstring output;
|
||||||
|
long length = GetShortPathNameW(input.c_str(), NULL, 0);
|
||||||
|
if (length == 0)
|
||||||
|
return {};
|
||||||
|
// NOTE: this resizing might seem weird...
|
||||||
|
// when GetShortPathNameW fails, it returns length including null character
|
||||||
|
// when it succeeds, it returns length excluding null character
|
||||||
|
// See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989(v=vs.85).aspx
|
||||||
|
output.resize(length);
|
||||||
|
if (GetShortPathNameW(input.c_str(), (LPWSTR)output.c_str(), length) == 0)
|
||||||
|
return {};
|
||||||
|
output.resize(length - 1);
|
||||||
|
QString ret = QString::fromStdWString(output);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the string survives roundtrip through local 8bit encoding...
|
||||||
|
bool fitsInLocal8bit(const QString& string)
|
||||||
|
{
|
||||||
|
return string == QString::fromLocal8Bit(string.toLocal8Bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString getPathNameInLocal8bit(const QString& file)
|
||||||
|
{
|
||||||
|
if (!fitsInLocal8bit(file)) {
|
||||||
|
auto path = shortPathName(file);
|
||||||
|
if (!path.isEmpty()) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
// in case shortPathName fails just return the path as is
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace FS
|
} // namespace FS
|
||||||
|
@ -91,7 +91,13 @@ bool ensureFilePathExists(QString filenamepath);
|
|||||||
* Creates all the folders in a path for the specified path
|
* Creates all the folders in a path for the specified path
|
||||||
* last segment of the path is treated as a folder name and is created!
|
* last segment of the path is treated as a folder name and is created!
|
||||||
*/
|
*/
|
||||||
bool ensureFolderPathExists(QString filenamepath);
|
bool ensureFolderPathExists(const QFileInfo folderPath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates all the folders in a path for the specified path
|
||||||
|
* last segment of the path is treated as a folder name and is created!
|
||||||
|
*/
|
||||||
|
bool ensureFolderPathExists(const QString folderPathName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copies a directory and it's contents from src to dest
|
* @brief Copies a directory and it's contents from src to dest
|
||||||
@ -336,6 +342,8 @@ QString NormalizePath(QString path);
|
|||||||
|
|
||||||
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith = '-');
|
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith = '-');
|
||||||
|
|
||||||
|
QString RemoveInvalidPathChars(QString string, QChar replaceWith = '-');
|
||||||
|
|
||||||
QString DirNameFromString(QString string, QString inDir = ".");
|
QString DirNameFromString(QString string, QString inDir = ".");
|
||||||
|
|
||||||
/// Checks if the a given Path contains "!"
|
/// Checks if the a given Path contains "!"
|
||||||
@ -545,4 +553,8 @@ bool canLink(const QString& src, const QString& dst);
|
|||||||
|
|
||||||
uintmax_t hardLinkCount(const QString& path);
|
uintmax_t hardLinkCount(const QString& path);
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
QString getPathNameInLocal8bit(const QString& file);
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace FS
|
} // namespace FS
|
||||||
|
@ -43,10 +43,10 @@ void InstanceCopyTask::executeTask()
|
|||||||
QFileInfo dotMCDir(FS::PathCombine(m_stagingPath, ".minecraft"));
|
QFileInfo dotMCDir(FS::PathCombine(m_stagingPath, ".minecraft"));
|
||||||
|
|
||||||
QString staging_mc_dir;
|
QString staging_mc_dir;
|
||||||
if (mcDir.exists() && !dotMCDir.exists())
|
if (dotMCDir.exists() && !mcDir.exists())
|
||||||
staging_mc_dir = mcDir.filePath();
|
|
||||||
else
|
|
||||||
staging_mc_dir = dotMCDir.filePath();
|
staging_mc_dir = dotMCDir.filePath();
|
||||||
|
else
|
||||||
|
staging_mc_dir = mcDir.filePath();
|
||||||
|
|
||||||
FS::copy savesCopy(FS::PathCombine(m_origInstance->gameRoot(), "saves"), FS::PathCombine(staging_mc_dir, "saves"));
|
FS::copy savesCopy(FS::PathCombine(m_origInstance->gameRoot(), "saves"), FS::PathCombine(staging_mc_dir, "saves"));
|
||||||
savesCopy.followSymlinks(true);
|
savesCopy.followSymlinks(true);
|
||||||
@ -142,9 +142,8 @@ void InstanceCopyTask::copyFinished()
|
|||||||
if (!m_keepPlaytime) {
|
if (!m_keepPlaytime) {
|
||||||
inst->resetTimePlayed();
|
inst->resetTimePlayed();
|
||||||
}
|
}
|
||||||
if (m_useLinks)
|
|
||||||
inst->addLinkedInstanceId(m_origInstance->id());
|
|
||||||
if (m_useLinks) {
|
if (m_useLinks) {
|
||||||
|
inst->addLinkedInstanceId(m_origInstance->id());
|
||||||
auto allowed_symlinks_file = QFileInfo(FS::PathCombine(inst->gameRoot(), "allowed_symlinks.txt"));
|
auto allowed_symlinks_file = QFileInfo(FS::PathCombine(inst->gameRoot(), "allowed_symlinks.txt"));
|
||||||
|
|
||||||
QByteArray allowed_symlinks;
|
QByteArray allowed_symlinks;
|
||||||
|
@ -164,8 +164,8 @@ void InstanceImportTask::processZipPack()
|
|||||||
} else if (technicFound) {
|
} else if (technicFound) {
|
||||||
// process as Technic pack
|
// process as Technic pack
|
||||||
qDebug() << "Technic:" << technicFound;
|
qDebug() << "Technic:" << technicFound;
|
||||||
extractDir.mkpath(".minecraft");
|
extractDir.mkpath("minecraft");
|
||||||
extractDir.cd(".minecraft");
|
extractDir.cd("minecraft");
|
||||||
m_modpackType = ModpackType::Technic;
|
m_modpackType = ModpackType::Technic;
|
||||||
} else {
|
} else {
|
||||||
QStringList paths_to_ignore{ "overrides/" };
|
QStringList paths_to_ignore{ "overrides/" };
|
||||||
|
@ -47,9 +47,6 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
class QuaZip;
|
class QuaZip;
|
||||||
namespace Flame {
|
|
||||||
class FileResolvingTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
class InstanceImportTask : public InstanceTask {
|
class InstanceImportTask : public InstanceTask {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -79,7 +76,6 @@ class InstanceImportTask : public InstanceTask {
|
|||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
NetJob::Ptr m_filesNetJob;
|
NetJob::Ptr m_filesNetJob;
|
||||||
shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver;
|
|
||||||
QUrl m_sourceUrl;
|
QUrl m_sourceUrl;
|
||||||
QString m_archivePath;
|
QString m_archivePath;
|
||||||
bool m_downloadRequired = false;
|
bool m_downloadRequired = false;
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
#include <QFileSystemWatcher>
|
#include <QFileSystemWatcher>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
@ -847,14 +848,16 @@ class InstanceStaging : public Task {
|
|||||||
const unsigned maxBackoff = 16;
|
const unsigned maxBackoff = 16;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InstanceStaging(InstanceList* parent, InstanceTask* child, QString stagingPath, InstanceName const& instanceName, QString groupName)
|
InstanceStaging(InstanceList* parent, InstanceTask* child, SettingsObjectPtr settings)
|
||||||
: m_parent(parent)
|
: m_parent(parent), backoff(minBackoff, maxBackoff)
|
||||||
, backoff(minBackoff, maxBackoff)
|
|
||||||
, m_stagingPath(std::move(stagingPath))
|
|
||||||
, m_instance_name(std::move(instanceName))
|
|
||||||
, m_groupName(std::move(groupName))
|
|
||||||
{
|
{
|
||||||
|
m_stagingPath = parent->getStagedInstancePath();
|
||||||
|
|
||||||
m_child.reset(child);
|
m_child.reset(child);
|
||||||
|
|
||||||
|
m_child->setStagingPath(m_stagingPath);
|
||||||
|
m_child->setParentSettings(std::move(settings));
|
||||||
|
|
||||||
connect(child, &Task::succeeded, this, &InstanceStaging::childSucceeded);
|
connect(child, &Task::succeeded, this, &InstanceStaging::childSucceeded);
|
||||||
connect(child, &Task::failed, this, &InstanceStaging::childFailed);
|
connect(child, &Task::failed, this, &InstanceStaging::childFailed);
|
||||||
connect(child, &Task::aborted, this, &InstanceStaging::childAborted);
|
connect(child, &Task::aborted, this, &InstanceStaging::childAborted);
|
||||||
@ -866,7 +869,7 @@ class InstanceStaging : public Task {
|
|||||||
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceeded);
|
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceeded);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~InstanceStaging(){};
|
virtual ~InstanceStaging() {}
|
||||||
|
|
||||||
// FIXME/TODO: add ability to abort during instance commit retries
|
// FIXME/TODO: add ability to abort during instance commit retries
|
||||||
bool abort() override
|
bool abort() override
|
||||||
@ -881,14 +884,22 @@ class InstanceStaging : public Task {
|
|||||||
bool canAbort() const override { return (m_child && m_child->canAbort()); }
|
bool canAbort() const override { return (m_child && m_child->canAbort()); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void executeTask() override { m_child->start(); }
|
virtual void executeTask() override
|
||||||
|
{
|
||||||
|
if (m_stagingPath.isNull()) {
|
||||||
|
emitFailed(tr("Could not create staging folder"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_child->start();
|
||||||
|
}
|
||||||
QStringList warnings() const override { return m_child->warnings(); }
|
QStringList warnings() const override { return m_child->warnings(); }
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void childSucceeded()
|
void childSucceeded()
|
||||||
{
|
{
|
||||||
unsigned sleepTime = backoff();
|
unsigned sleepTime = backoff();
|
||||||
if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get())) {
|
if (m_parent->commitStagedInstance(m_stagingPath, *m_child.get(), m_child->group(), *m_child.get())) {
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -897,7 +908,7 @@ class InstanceStaging : public Task {
|
|||||||
emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something."));
|
emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
qDebug() << "Failed to commit instance" << m_instance_name.name() << "Initiating backoff:" << sleepTime;
|
qDebug() << "Failed to commit instance" << m_child->name() << "Initiating backoff:" << sleepTime;
|
||||||
m_backoffTimer.start(sleepTime * 500);
|
m_backoffTimer.start(sleepTime * 500);
|
||||||
}
|
}
|
||||||
void childFailed(const QString& reason)
|
void childFailed(const QString& reason)
|
||||||
@ -906,7 +917,11 @@ class InstanceStaging : public Task {
|
|||||||
emitFailed(reason);
|
emitFailed(reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
void childAborted() { emitAborted(); }
|
void childAborted()
|
||||||
|
{
|
||||||
|
m_parent->destroyStagingPath(m_stagingPath);
|
||||||
|
emitAborted();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
InstanceList* m_parent;
|
InstanceList* m_parent;
|
||||||
@ -918,34 +933,35 @@ class InstanceStaging : public Task {
|
|||||||
ExponentialSeries backoff;
|
ExponentialSeries backoff;
|
||||||
QString m_stagingPath;
|
QString m_stagingPath;
|
||||||
unique_qobject_ptr<InstanceTask> m_child;
|
unique_qobject_ptr<InstanceTask> m_child;
|
||||||
InstanceName m_instance_name;
|
|
||||||
QString m_groupName;
|
|
||||||
QTimer m_backoffTimer;
|
QTimer m_backoffTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
Task* InstanceList::wrapInstanceTask(InstanceTask* task)
|
Task* InstanceList::wrapInstanceTask(InstanceTask* task)
|
||||||
{
|
{
|
||||||
auto stagingPath = getStagedInstancePath();
|
return new InstanceStaging(this, task, m_globalSettings);
|
||||||
task->setStagingPath(stagingPath);
|
|
||||||
task->setParentSettings(m_globalSettings);
|
|
||||||
return new InstanceStaging(this, task, stagingPath, *task, task->group());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString InstanceList::getStagedInstancePath()
|
QString InstanceList::getStagedInstancePath()
|
||||||
{
|
{
|
||||||
QString key = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
const QString tempRoot = FS::PathCombine(m_instDir, ".tmp");
|
||||||
QString tempDir = ".LAUNCHER_TEMP/";
|
|
||||||
QString relPath = FS::PathCombine(tempDir, key);
|
QString result;
|
||||||
QDir rootPath(m_instDir);
|
int tries = 0;
|
||||||
auto path = FS::PathCombine(m_instDir, relPath);
|
|
||||||
if (!rootPath.mkpath(relPath)) {
|
do {
|
||||||
return QString();
|
if (++tries > 256)
|
||||||
}
|
return {};
|
||||||
|
|
||||||
|
const QString key = QUuid::createUuid().toString(QUuid::Id128).left(6);
|
||||||
|
result = FS::PathCombine(tempRoot, key);
|
||||||
|
} while (QFileInfo::exists(result));
|
||||||
|
|
||||||
|
if (!QDir::current().mkpath(result))
|
||||||
|
return {};
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
auto tempPath = FS::PathCombine(m_instDir, tempDir);
|
SetFileAttributesA(tempRoot.toStdString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
|
||||||
SetFileAttributesA(tempPath.toStdString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
|
|
||||||
#endif
|
#endif
|
||||||
return path;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InstanceList::commitStagedInstance(const QString& path,
|
bool InstanceList::commitStagedInstance(const QString& path,
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
|
|
||||||
|
#include <QPushButton>
|
||||||
|
|
||||||
InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name)
|
InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name)
|
||||||
{
|
{
|
||||||
auto dialog =
|
auto dialog =
|
||||||
@ -27,16 +29,15 @@ ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name)
|
|||||||
"separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before "
|
"separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before "
|
||||||
"updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).")
|
"updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).")
|
||||||
.arg(original_version_name),
|
.arg(original_version_name),
|
||||||
QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort);
|
QMessageBox::Information, QMessageBox::Cancel);
|
||||||
info->setButtonText(QMessageBox::Ok, QObject::tr("Update existing instance"));
|
QAbstractButton* update = info->addButton(QObject::tr("Update existing instance"), QMessageBox::AcceptRole);
|
||||||
info->setButtonText(QMessageBox::Abort, QObject::tr("Create new instance"));
|
QAbstractButton* skip = info->addButton(QObject::tr("Create new instance"), QMessageBox::ResetRole);
|
||||||
info->setButtonText(QMessageBox::Reset, QObject::tr("Cancel"));
|
|
||||||
|
|
||||||
info->exec();
|
info->exec();
|
||||||
|
|
||||||
if (info->clickedButton() == info->button(QMessageBox::Ok))
|
if (info->clickedButton() == update)
|
||||||
return ShouldUpdate::Update;
|
return ShouldUpdate::Update;
|
||||||
if (info->clickedButton() == info->button(QMessageBox::Abort))
|
if (info->clickedButton() == skip)
|
||||||
return ShouldUpdate::SkipUpdating;
|
return ShouldUpdate::SkipUpdating;
|
||||||
return ShouldUpdate::Cancel;
|
return ShouldUpdate::Cancel;
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,6 @@
|
|||||||
#include "ui/InstanceWindow.h"
|
#include "ui/InstanceWindow.h"
|
||||||
#include "ui/MainWindow.h"
|
#include "ui/MainWindow.h"
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
#include "ui/dialogs/EditAccountDialog.h"
|
|
||||||
#include "ui/dialogs/ProfileSelectDialog.h"
|
#include "ui/dialogs/ProfileSelectDialog.h"
|
||||||
#include "ui/dialogs/ProfileSetupDialog.h"
|
#include "ui/dialogs/ProfileSetupDialog.h"
|
||||||
#include "ui/dialogs/ProgressDialog.h"
|
#include "ui/dialogs/ProgressDialog.h"
|
||||||
@ -144,6 +143,12 @@ void LaunchController::login()
|
|||||||
bool tryagain = true;
|
bool tryagain = true;
|
||||||
unsigned int tries = 0;
|
unsigned int tries = 0;
|
||||||
|
|
||||||
|
if (m_accountToUse->accountType() != AccountType::Offline && m_accountToUse->accountState() == AccountState::Offline) {
|
||||||
|
// 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
|
||||||
|
auto accounts = APPLICATION->accounts();
|
||||||
|
accounts->requestRefresh(m_accountToUse->internalId());
|
||||||
|
}
|
||||||
while (tryagain) {
|
while (tryagain) {
|
||||||
if (tries > 0 && tries % 3 == 0) {
|
if (tries > 0 && tries % 3 == 0) {
|
||||||
auto result =
|
auto result =
|
||||||
@ -250,12 +255,6 @@ void LaunchController::login()
|
|||||||
progDialog.execWithTask(task.get());
|
progDialog.execWithTask(task.get());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// FIXME: this is missing - the meaning is that the account is queued for refresh and we should wait for that
|
|
||||||
/*
|
|
||||||
case AccountState::Queued: {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
case AccountState::Expired: {
|
case AccountState::Expired: {
|
||||||
auto errorString = tr("The account has expired and needs to be logged into manually again.");
|
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::warning(m_parentWidget, tr("Account refresh failed"), errorString, QMessageBox::StandardButton::Ok,
|
||||||
|
@ -119,6 +119,7 @@ bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool follow
|
|||||||
bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks)
|
bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks)
|
||||||
{
|
{
|
||||||
QuaZip zip(fileCompressed);
|
QuaZip zip(fileCompressed);
|
||||||
|
zip.setUtf8Enabled(true);
|
||||||
QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
|
QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
|
||||||
if (!zip.open(QuaZip::mdCreate)) {
|
if (!zip.open(QuaZip::mdCreate)) {
|
||||||
QFile::remove(fileCompressed);
|
QFile::remove(fileCompressed);
|
||||||
@ -141,6 +142,7 @@ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files,
|
|||||||
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods)
|
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods)
|
||||||
{
|
{
|
||||||
QuaZip zipOut(targetJarPath);
|
QuaZip zipOut(targetJarPath);
|
||||||
|
zipOut.setUtf8Enabled(true);
|
||||||
if (!zipOut.open(QuaZip::mdCreate)) {
|
if (!zipOut.open(QuaZip::mdCreate)) {
|
||||||
QFile::remove(targetJarPath);
|
QFile::remove(targetJarPath);
|
||||||
qCritical() << "Failed to open the minecraft.jar for modding";
|
qCritical() << "Failed to open the minecraft.jar for modding";
|
||||||
@ -286,10 +288,13 @@ std::optional<QStringList> extractSubDir(QuaZip* zip, const QString& subdir, con
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
QString file_name = zip->getCurrentFileName();
|
QString file_name = zip->getCurrentFileName();
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
file_name = FS::RemoveInvalidPathChars(file_name);
|
||||||
|
#endif
|
||||||
if (!file_name.startsWith(subdir))
|
if (!file_name.startsWith(subdir))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto relative_file_name = QDir::fromNativeSeparators(file_name.remove(0, subdir.size()));
|
auto relative_file_name = QDir::fromNativeSeparators(file_name.mid(subdir.size()));
|
||||||
auto original_name = relative_file_name;
|
auto original_name = relative_file_name;
|
||||||
|
|
||||||
// Fix subdirs/files ending with a / getting transformed into absolute paths
|
// Fix subdirs/files ending with a / getting transformed into absolute paths
|
||||||
@ -463,7 +468,7 @@ auto ExportToZipTask::exportZip() -> ZipResult
|
|||||||
|
|
||||||
auto absolute = file.absoluteFilePath();
|
auto absolute = file.absoluteFilePath();
|
||||||
auto relative = m_dir.relativeFilePath(absolute);
|
auto relative = m_dir.relativeFilePath(absolute);
|
||||||
setStatus("Compresing: " + relative);
|
setStatus("Compressing: " + relative);
|
||||||
setProgress(m_progress + 1, m_progressTotal);
|
setProgress(m_progress + 1, m_progressTotal);
|
||||||
if (m_follow_symlinks) {
|
if (m_follow_symlinks) {
|
||||||
if (file.isSymLink())
|
if (file.isSymLink())
|
||||||
|
@ -154,7 +154,12 @@ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, Q
|
|||||||
#if defined(LAUNCHER_APPLICATION)
|
#if defined(LAUNCHER_APPLICATION)
|
||||||
class ExportToZipTask : public Task {
|
class ExportToZipTask : public Task {
|
||||||
public:
|
public:
|
||||||
ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false)
|
ExportToZipTask(QString outputPath,
|
||||||
|
QDir dir,
|
||||||
|
QFileInfoList files,
|
||||||
|
QString destinationPrefix = "",
|
||||||
|
bool followSymlinks = false,
|
||||||
|
bool utf8Enabled = false)
|
||||||
: m_output_path(outputPath)
|
: m_output_path(outputPath)
|
||||||
, m_output(outputPath)
|
, m_output(outputPath)
|
||||||
, m_dir(dir)
|
, m_dir(dir)
|
||||||
@ -163,9 +168,15 @@ class ExportToZipTask : public Task {
|
|||||||
, m_follow_symlinks(followSymlinks)
|
, m_follow_symlinks(followSymlinks)
|
||||||
{
|
{
|
||||||
setAbortable(true);
|
setAbortable(true);
|
||||||
|
m_output.setUtf8Enabled(utf8Enabled);
|
||||||
};
|
};
|
||||||
ExportToZipTask(QString outputPath, QString dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false)
|
ExportToZipTask(QString outputPath,
|
||||||
: ExportToZipTask(outputPath, QDir(dir), files, destinationPrefix, followSymlinks){};
|
QString dir,
|
||||||
|
QFileInfoList files,
|
||||||
|
QString destinationPrefix = "",
|
||||||
|
bool followSymlinks = false,
|
||||||
|
bool utf8Enabled = false)
|
||||||
|
: ExportToZipTask(outputPath, QDir(dir), files, destinationPrefix, followSymlinks, utf8Enabled){};
|
||||||
|
|
||||||
virtual ~ExportToZipTask() = default;
|
virtual ~ExportToZipTask() = default;
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
set(CMAKE_MODULE_PATH "@CMAKE_MODULE_PATH@")
|
set(CMAKE_MODULE_PATH "@CMAKE_MODULE_PATH@")
|
||||||
|
|
||||||
file(GLOB_RECURSE QTPLUGINS "${CMAKE_INSTALL_PREFIX}/@PLUGIN_DEST_DIR@/*@CMAKE_SHARED_LIBRARY_SUFFIX@")
|
file(GLOB_RECURSE QTPLUGINS "${CMAKE_INSTALL_PREFIX}/@PLUGIN_DEST_DIR@/*@CMAKE_SHARED_LIBRARY_SUFFIX@")
|
||||||
function(gp_resolved_file_type_override resolved_file type_var)
|
function(gp_resolved_file_type_override resolved_file type_var)
|
||||||
if(resolved_file MATCHES "^/(usr/)?lib/libQt")
|
if(resolved_file MATCHES "^/(usr/)?lib/libQt")
|
||||||
|
@ -55,6 +55,9 @@ void JavaChecker::performCheck()
|
|||||||
qDebug() << "Java checker library could not be found. Please check your installation.";
|
qDebug() << "Java checker library could not be found. Please check your installation.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
checkerJar = FS::getPathNameInLocal8bit(checkerJar);
|
||||||
|
#endif
|
||||||
|
|
||||||
QStringList args;
|
QStringList args;
|
||||||
|
|
||||||
|
@ -413,6 +413,8 @@ QList<QString> JavaUtils::FindJavaPaths()
|
|||||||
scanJavaDirs(FS::PathCombine(home, ".jdks"));
|
scanJavaDirs(FS::PathCombine(home, ".jdks"));
|
||||||
// javas downloaded by sdkman
|
// javas downloaded by sdkman
|
||||||
scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java"));
|
scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java"));
|
||||||
|
// javas downloaded by gradle (toolchains)
|
||||||
|
scanJavaDirs(FS::PathCombine(home, ".gradle/jdks"));
|
||||||
|
|
||||||
javas.append(getMinecraftJavaBundle());
|
javas.append(getMinecraftJavaBundle());
|
||||||
javas = addJavasFromEnv(javas);
|
javas = addJavasFromEnv(javas);
|
||||||
@ -439,26 +441,25 @@ QString JavaUtils::getJavaCheckPath()
|
|||||||
|
|
||||||
QStringList getMinecraftJavaBundle()
|
QStringList getMinecraftJavaBundle()
|
||||||
{
|
{
|
||||||
QString partialPath;
|
|
||||||
QString executable = "java";
|
QString executable = "java";
|
||||||
QStringList processpaths;
|
QStringList processpaths;
|
||||||
#if defined(Q_OS_OSX)
|
#if defined(Q_OS_OSX)
|
||||||
partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support");
|
processpaths << FS::PathCombine(QDir::homePath(), FS::PathCombine("Library", "Application Support", "minecraft", "runtime"));
|
||||||
#elif defined(Q_OS_WIN32)
|
#elif defined(Q_OS_WIN32)
|
||||||
partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", "");
|
|
||||||
executable += "w.exe";
|
executable += "w.exe";
|
||||||
|
|
||||||
|
auto appDataPath = QProcessEnvironment::systemEnvironment().value("APPDATA", "");
|
||||||
|
processpaths << FS::PathCombine(QFileInfo(appDataPath).absoluteFilePath(), ".minecraft", "runtime");
|
||||||
|
|
||||||
// add the microsoft store version of the launcher to the search. the current path is:
|
// add the microsoft store version of the launcher to the search. the current path is:
|
||||||
// C:\Users\USERNAME\AppData\Local\Packages\Microsoft.4297127D64EC6_8wekyb3d8bbwe\LocalCache\Local\runtime
|
// C:\Users\USERNAME\AppData\Local\Packages\Microsoft.4297127D64EC6_8wekyb3d8bbwe\LocalCache\Local\runtime
|
||||||
|
auto localAppDataPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", "");
|
||||||
auto minecraftMSStorePath =
|
auto minecraftMSStorePath =
|
||||||
FS::PathCombine(QFileInfo(partialPath).absolutePath(), "Local", "Packages", "Microsoft.4297127D64EC6_8wekyb3d8bbwe");
|
FS::PathCombine(QFileInfo(localAppDataPath).absoluteFilePath(), "Packages", "Microsoft.4297127D64EC6_8wekyb3d8bbwe");
|
||||||
minecraftMSStorePath = FS::PathCombine(minecraftMSStorePath, "LocalCache", "Local", "runtime");
|
processpaths << FS::PathCombine(minecraftMSStorePath, "LocalCache", "Local", "runtime");
|
||||||
processpaths << minecraftMSStorePath;
|
|
||||||
#else
|
#else
|
||||||
partialPath = QDir::homePath();
|
processpaths << FS::PathCombine(QDir::homePath(), ".minecraft", "runtime");
|
||||||
#endif
|
#endif
|
||||||
auto minecraftDataPath = FS::PathCombine(partialPath, ".minecraft", "runtime");
|
|
||||||
processpaths << minecraftDataPath;
|
|
||||||
|
|
||||||
QStringList javas;
|
QStringList javas;
|
||||||
while (!processpaths.isEmpty()) {
|
while (!processpaths.isEmpty()) {
|
||||||
|
@ -173,11 +173,12 @@ void MinecraftInstance::loadSpecificSettings()
|
|||||||
m_settings->registerOverride(global_settings->getSetting("UseNativeGLFW"), nativeLibraryWorkaroundsOverride);
|
m_settings->registerOverride(global_settings->getSetting("UseNativeGLFW"), nativeLibraryWorkaroundsOverride);
|
||||||
m_settings->registerOverride(global_settings->getSetting("CustomGLFWPath"), nativeLibraryWorkaroundsOverride);
|
m_settings->registerOverride(global_settings->getSetting("CustomGLFWPath"), nativeLibraryWorkaroundsOverride);
|
||||||
|
|
||||||
// Peformance related options
|
// Performance related options
|
||||||
auto performanceOverride = m_settings->registerSetting("OverridePerformance", false);
|
auto performanceOverride = m_settings->registerSetting("OverridePerformance", false);
|
||||||
m_settings->registerOverride(global_settings->getSetting("EnableFeralGamemode"), performanceOverride);
|
m_settings->registerOverride(global_settings->getSetting("EnableFeralGamemode"), performanceOverride);
|
||||||
m_settings->registerOverride(global_settings->getSetting("EnableMangoHud"), performanceOverride);
|
m_settings->registerOverride(global_settings->getSetting("EnableMangoHud"), performanceOverride);
|
||||||
m_settings->registerOverride(global_settings->getSetting("UseDiscreteGpu"), performanceOverride);
|
m_settings->registerOverride(global_settings->getSetting("UseDiscreteGpu"), performanceOverride);
|
||||||
|
m_settings->registerOverride(global_settings->getSetting("UseZink"), performanceOverride);
|
||||||
|
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
auto miscellaneousOverride = m_settings->registerSetting("OverrideMiscellaneous", false);
|
auto miscellaneousOverride = m_settings->registerSetting("OverrideMiscellaneous", false);
|
||||||
@ -292,10 +293,10 @@ QString MinecraftInstance::gameRoot() const
|
|||||||
QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
|
QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
|
||||||
QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft"));
|
QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft"));
|
||||||
|
|
||||||
if (mcDir.exists() && !dotMCDir.exists())
|
if (dotMCDir.exists() && !mcDir.exists())
|
||||||
return mcDir.filePath();
|
|
||||||
else
|
|
||||||
return dotMCDir.filePath();
|
return dotMCDir.filePath();
|
||||||
|
else
|
||||||
|
return mcDir.filePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString MinecraftInstance::binRoot() const
|
QString MinecraftInstance::binRoot() const
|
||||||
@ -594,9 +595,6 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
|
|||||||
QStringList preloadList;
|
QStringList preloadList;
|
||||||
if (auto value = env.value("LD_PRELOAD"); !value.isEmpty())
|
if (auto value = env.value("LD_PRELOAD"); !value.isEmpty())
|
||||||
preloadList = value.split(QLatin1String(":"));
|
preloadList = value.split(QLatin1String(":"));
|
||||||
QStringList libPaths;
|
|
||||||
if (auto value = env.value("LD_LIBRARY_PATH"); !value.isEmpty())
|
|
||||||
libPaths = value.split(QLatin1String(":"));
|
|
||||||
|
|
||||||
auto mangoHudLibString = MangoHud::getLibraryString();
|
auto mangoHudLibString = MangoHud::getLibraryString();
|
||||||
if (!mangoHudLibString.isEmpty()) {
|
if (!mangoHudLibString.isEmpty()) {
|
||||||
@ -604,18 +602,16 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
|
|||||||
QString libPath = mangoHudLib.absolutePath();
|
QString libPath = mangoHudLib.absolutePath();
|
||||||
auto appendLib = [libPath, &preloadList](QString fileName) {
|
auto appendLib = [libPath, &preloadList](QString fileName) {
|
||||||
if (QFileInfo(FS::PathCombine(libPath, fileName)).exists())
|
if (QFileInfo(FS::PathCombine(libPath, fileName)).exists())
|
||||||
preloadList << fileName;
|
preloadList << FS::PathCombine(libPath, fileName);
|
||||||
};
|
};
|
||||||
|
|
||||||
// dlsym variant is only needed for OpenGL and not included in the vulkan layer
|
// dlsym variant is only needed for OpenGL and not included in the vulkan layer
|
||||||
appendLib("libMangoHud_dlsym.so");
|
appendLib("libMangoHud_dlsym.so");
|
||||||
appendLib("libMangoHud_opengl.so");
|
appendLib("libMangoHud_opengl.so");
|
||||||
appendLib(mangoHudLib.fileName());
|
appendLib(mangoHudLib.fileName());
|
||||||
libPaths << libPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
env.insert("LD_PRELOAD", preloadList.join(QLatin1String(":")));
|
env.insert("LD_PRELOAD", preloadList.join(QLatin1String(":")));
|
||||||
env.insert("LD_LIBRARY_PATH", libPaths.join(QLatin1String(":")));
|
|
||||||
env.insert("MANGOHUD", "1");
|
env.insert("MANGOHUD", "1");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -627,6 +623,13 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
|
|||||||
env.insert("__VK_LAYER_NV_optimus", "NVIDIA_only");
|
env.insert("__VK_LAYER_NV_optimus", "NVIDIA_only");
|
||||||
env.insert("__GLX_VENDOR_LIBRARY_NAME", "nvidia");
|
env.insert("__GLX_VENDOR_LIBRARY_NAME", "nvidia");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (settings()->get("UseZink").toBool()) {
|
||||||
|
// taken from https://wiki.archlinux.org/title/OpenGL#OpenGL_over_Vulkan_(Zink)
|
||||||
|
env.insert("__GLX_VENDOR_LIBRARY_NAME", "mesa");
|
||||||
|
env.insert("MESA_LOADER_DRIVER_OVERRIDE", "zink");
|
||||||
|
env.insert("GALLIUM_DRIVER", "zink");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
@ -662,8 +665,12 @@ QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, Mine
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (serverToJoin && !serverToJoin->address.isEmpty()) {
|
if (serverToJoin && !serverToJoin->address.isEmpty()) {
|
||||||
args_pattern += " --server " + serverToJoin->address;
|
if (profile->hasTrait("feature:is_quick_play_multiplayer")) {
|
||||||
args_pattern += " --port " + QString::number(serverToJoin->port);
|
args_pattern += " --quickPlayMultiplayer " + serverToJoin->address + ':' + QString::number(serverToJoin->port);
|
||||||
|
} else {
|
||||||
|
args_pattern += " --server " + serverToJoin->address;
|
||||||
|
args_pattern += " --port " + QString::number(serverToJoin->port);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap<QString, QString> token_mapping;
|
QMap<QString, QString> token_mapping;
|
||||||
|
@ -157,20 +157,6 @@ void MojangVersionFormat::readVersionProperties(const QJsonObject& in, VersionFi
|
|||||||
Bits::readString(in, "id", out->minecraftVersion);
|
Bits::readString(in, "id", out->minecraftVersion);
|
||||||
Bits::readString(in, "mainClass", out->mainClass);
|
Bits::readString(in, "mainClass", out->mainClass);
|
||||||
Bits::readString(in, "minecraftArguments", out->minecraftArguments);
|
Bits::readString(in, "minecraftArguments", out->minecraftArguments);
|
||||||
if (out->minecraftArguments.isEmpty()) {
|
|
||||||
QString processArguments;
|
|
||||||
Bits::readString(in, "processArguments", processArguments);
|
|
||||||
QString toCompare = processArguments.toLower();
|
|
||||||
if (toCompare == "legacy") {
|
|
||||||
out->minecraftArguments = " ${auth_player_name} ${auth_session}";
|
|
||||||
} else if (toCompare == "username_session") {
|
|
||||||
out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}";
|
|
||||||
} else if (toCompare == "username_session_version") {
|
|
||||||
out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}";
|
|
||||||
} else if (!toCompare.isEmpty()) {
|
|
||||||
out->addProblem(ProblemSeverity::Error, QObject::tr("processArguments is set to unknown value '%1'").arg(processArguments));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Bits::readString(in, "type", out->type);
|
Bits::readString(in, "type", out->type);
|
||||||
|
|
||||||
Bits::readString(in, "assets", out->assets);
|
Bits::readString(in, "assets", out->assets);
|
||||||
|
@ -52,8 +52,6 @@
|
|||||||
#include <FileSystem.h>
|
#include <FileSystem.h>
|
||||||
#include <QSaveFile>
|
#include <QSaveFile>
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
enum AccountListVersion { MojangMSA = 3 };
|
enum AccountListVersion { MojangMSA = 3 };
|
||||||
|
|
||||||
AccountList::AccountList(QObject* parent) : QAbstractListModel(parent)
|
AccountList::AccountList(QObject* parent) : QAbstractListModel(parent)
|
||||||
|
@ -126,7 +126,35 @@ bool XboxAuthorizationStep::processSTSError(QNetworkReply::NetworkError error, Q
|
|||||||
emit finished(
|
emit finished(
|
||||||
AccountTaskState::STATE_FAILED_SOFT,
|
AccountTaskState::STATE_FAILED_SOFT,
|
||||||
tr("This Microsoft account is underaged and is not linked to a family.\n\nPlease set up your account according to %1.")
|
tr("This Microsoft account is underaged and is not linked to a family.\n\nPlease set up your account according to %1.")
|
||||||
.arg("<a href=\"https://help.minecraft.net/hc/en-us/articles/4403181904525\">help.minecraft.net</a>"));
|
.arg("<a href=\"https://help.minecraft.net/hc/en-us/articles/4408968616077\">help.minecraft.net</a>"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// the following codes where copied from: https://github.com/PrismarineJS/prismarine-auth/pull/44
|
||||||
|
case 2148916236: {
|
||||||
|
emit finished(AccountTaskState::STATE_FAILED_SOFT,
|
||||||
|
tr("This Microsoft account requires proof of age to play. Please login to %1 to provide proof of age.")
|
||||||
|
.arg("<a href=\"https://login.live.com/login.srf\">login.live.com</a>"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 2148916237:
|
||||||
|
emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account has reached its limit for playtime. This "
|
||||||
|
"Microsoft account has been blocked from logging in."));
|
||||||
|
return true;
|
||||||
|
case 2148916227: {
|
||||||
|
emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account was banned by Xbox for violating one or more "
|
||||||
|
"Community Standards for Xbox and is unable to be used."));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 2148916229: {
|
||||||
|
emit finished(AccountTaskState::STATE_FAILED_SOFT,
|
||||||
|
tr("This Microsoft account is currently restricted and your guardian has not given you permission to play "
|
||||||
|
"online. Login to %1 and have your guardian change your permissions.")
|
||||||
|
.arg("<a href=\"https://account.microsoft.com/family/\">account.microsoft.com</a>"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 2148916234: {
|
||||||
|
emit finished(AccountTaskState::STATE_FAILED_SOFT,
|
||||||
|
tr("This Microsoft account has not accepted Xbox's Terms of Service. Please login and accept them."));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
@ -79,6 +79,7 @@ void ExtractNatives::executeTask()
|
|||||||
auto settings = minecraftInstance->settings();
|
auto settings = minecraftInstance->settings();
|
||||||
|
|
||||||
auto outputPath = minecraftInstance->getNativePath();
|
auto outputPath = minecraftInstance->getNativePath();
|
||||||
|
FS::ensureFolderPathExists(outputPath);
|
||||||
auto javaVersion = minecraftInstance->getJavaVersion();
|
auto javaVersion = minecraftInstance->getJavaVersion();
|
||||||
bool jniHackEnabled = javaVersion.major() >= 8;
|
bool jniHackEnabled = javaVersion.major() >= 8;
|
||||||
for (const auto& source : toExtract) {
|
for (const auto& source : toExtract) {
|
||||||
|
@ -16,8 +16,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <launch/LaunchStep.h>
|
#include <launch/LaunchStep.h>
|
||||||
#include <memory>
|
|
||||||
#include "minecraft/auth/AuthSession.h"
|
|
||||||
|
|
||||||
// FIXME: temporary wrapper for existing task.
|
// FIXME: temporary wrapper for existing task.
|
||||||
class ExtractNatives : public LaunchStep {
|
class ExtractNatives : public LaunchStep {
|
||||||
|
@ -66,32 +66,6 @@ LauncherPartLaunch::LauncherPartLaunch(LaunchTask* parent) : LaunchStep(parent)
|
|||||||
connect(&m_process, &LoggedProcess::stateChanged, this, &LauncherPartLaunch::on_state);
|
connect(&m_process, &LoggedProcess::stateChanged, this, &LauncherPartLaunch::on_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
// returns 8.3 file format from long path
|
|
||||||
#include <windows.h>
|
|
||||||
QString shortPathName(const QString& file)
|
|
||||||
{
|
|
||||||
auto input = file.toStdWString();
|
|
||||||
std::wstring output;
|
|
||||||
long length = GetShortPathNameW(input.c_str(), NULL, 0);
|
|
||||||
// NOTE: this resizing might seem weird...
|
|
||||||
// when GetShortPathNameW fails, it returns length including null character
|
|
||||||
// when it succeeds, it returns length excluding null character
|
|
||||||
// See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989(v=vs.85).aspx
|
|
||||||
output.resize(length);
|
|
||||||
GetShortPathNameW(input.c_str(), (LPWSTR)output.c_str(), length);
|
|
||||||
output.resize(length - 1);
|
|
||||||
QString ret = QString::fromStdWString(output);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// if the string survives roundtrip through local 8bit encoding...
|
|
||||||
bool fitsInLocal8bit(const QString& string)
|
|
||||||
{
|
|
||||||
return string == QString::fromLocal8Bit(string.toLocal8Bit());
|
|
||||||
}
|
|
||||||
|
|
||||||
void LauncherPartLaunch::executeTask()
|
void LauncherPartLaunch::executeTask()
|
||||||
{
|
{
|
||||||
QString jarPath = APPLICATION->getJarPath("NewLaunch.jar");
|
QString jarPath = APPLICATION->getJarPath("NewLaunch.jar");
|
||||||
@ -136,24 +110,15 @@ void LauncherPartLaunch::executeTask()
|
|||||||
|
|
||||||
auto natPath = minecraftInstance->getNativePath();
|
auto natPath = minecraftInstance->getNativePath();
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
if (!fitsInLocal8bit(natPath)) {
|
natPath = FS::getPathNameInLocal8bit(natPath);
|
||||||
args << "-Djava.library.path=" + shortPathName(natPath);
|
|
||||||
} else {
|
|
||||||
args << "-Djava.library.path=" + natPath;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
args << "-Djava.library.path=" + natPath;
|
|
||||||
#endif
|
#endif
|
||||||
|
args << "-Djava.library.path=" + natPath;
|
||||||
|
|
||||||
args << "-cp";
|
args << "-cp";
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
QStringList processed;
|
QStringList processed;
|
||||||
for (auto& item : classPath) {
|
for (auto& item : classPath) {
|
||||||
if (!fitsInLocal8bit(item)) {
|
processed << FS::getPathNameInLocal8bit(item);
|
||||||
processed << shortPathName(item);
|
|
||||||
} else {
|
|
||||||
processed << item;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
args << processed.join(';');
|
args << processed.join(';');
|
||||||
#else
|
#else
|
||||||
|
@ -274,7 +274,7 @@ QPixmap Mod::icon(QSize size, Qt::AspectRatioMode mode) const
|
|||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (m_pack_image_cache_key.was_ever_used) {
|
if (m_pack_image_cache_key.was_ever_used) {
|
||||||
qDebug() << "Mod" << name() << "Had it's icon evicted form the cache. reloading...";
|
qDebug() << "Mod" << name() << "Had it's icon evicted from the cache. reloading...";
|
||||||
PixmapCache::markCacheMissByEviciton();
|
PixmapCache::markCacheMissByEviciton();
|
||||||
}
|
}
|
||||||
// Image got evicted from the cache or an attempt to load it has not been made. load it and retry.
|
// Image got evicted from the cache or an attempt to load it has not been made. load it and retry.
|
||||||
|
@ -306,7 +306,6 @@ void ResourceFolderModel::applyUpdates(QSet<QString>& current_set, QSet<QString>
|
|||||||
auto removed_it = m_resources.begin() + removed_index;
|
auto removed_it = m_resources.begin() + removed_index;
|
||||||
|
|
||||||
Q_ASSERT(removed_it != m_resources.end());
|
Q_ASSERT(removed_it != m_resources.end());
|
||||||
Q_ASSERT(removed_set.contains(removed_it->get()->internal_id()));
|
|
||||||
|
|
||||||
if ((*removed_it)->isResolving()) {
|
if ((*removed_it)->isResolving()) {
|
||||||
auto ticket = (*removed_it)->resolutionTicket();
|
auto ticket = (*removed_it)->resolutionTicket();
|
||||||
|
@ -57,9 +57,11 @@ GetModDependenciesTask::GetModDependenciesTask(QObject* parent,
|
|||||||
, m_version(mcVersion(instance))
|
, m_version(mcVersion(instance))
|
||||||
, m_loaderType(mcLoaders(instance))
|
, m_loaderType(mcLoaders(instance))
|
||||||
{
|
{
|
||||||
for (auto mod : folder->allMods())
|
for (auto mod : folder->allMods()) {
|
||||||
|
m_mods_file_names << mod->fileinfo().fileName();
|
||||||
if (auto meta = mod->metadata(); meta)
|
if (auto meta = mod->metadata(); meta)
|
||||||
m_mods.append(meta);
|
m_mods.append(meta);
|
||||||
|
}
|
||||||
prepare();
|
prepare();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +184,9 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
|
|||||||
|
|
||||||
ResourceAPI::DependencySearchArgs args = { dep, m_version, m_loaderType };
|
ResourceAPI::DependencySearchArgs args = { dep, m_version, m_loaderType };
|
||||||
ResourceAPI::DependencySearchCallbacks callbacks;
|
ResourceAPI::DependencySearchCallbacks callbacks;
|
||||||
|
callbacks.on_fail = [](QString reason, int) {
|
||||||
|
qCritical() << tr("A network error occurred. Could not load project dependencies:%1").arg(reason);
|
||||||
|
};
|
||||||
callbacks.on_succeed = [dep, provider, pDep, level, this](auto& doc, [[maybe_unused]] auto& pack) {
|
callbacks.on_succeed = [dep, provider, pDep, level, this](auto& doc, [[maybe_unused]] auto& pack) {
|
||||||
try {
|
try {
|
||||||
QJsonArray arr;
|
QJsonArray arr;
|
||||||
@ -229,8 +233,13 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
|
|||||||
if (dep_.addonId != pDep->version.addonId) {
|
if (dep_.addonId != pDep->version.addonId) {
|
||||||
removePack(pDep->version.addonId);
|
removePack(pDep->version.addonId);
|
||||||
addTask(prepareDependencyTask(dep_, provider.name, level));
|
addTask(prepareDependencyTask(dep_, provider.name, level));
|
||||||
} else
|
} else {
|
||||||
addTask(getProjectInfoTask(pDep));
|
addTask(getProjectInfoTask(pDep));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isLocalyInstalled(pDep)) {
|
||||||
|
removePack(pDep->version.addonId);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
for (auto dep_ : getDependenciesForVersion(pDep->version, provider.name)) {
|
for (auto dep_ : getDependenciesForVersion(pDep->version, provider.name)) {
|
||||||
addTask(prepareDependencyTask(dep_, provider.name, level - 1));
|
addTask(prepareDependencyTask(dep_, provider.name, level - 1));
|
||||||
@ -256,9 +265,9 @@ void GetModDependenciesTask::removePack(const QVariant& addonId)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<QString, QStringList> GetModDependenciesTask::getRequiredBy()
|
auto GetModDependenciesTask::getExtraInfo() -> QHash<QString, PackDependencyExtraInfo>
|
||||||
{
|
{
|
||||||
QHash<QString, QStringList> rby;
|
QHash<QString, PackDependencyExtraInfo> rby;
|
||||||
auto fullList = m_selected + m_pack_dependencies;
|
auto fullList = m_selected + m_pack_dependencies;
|
||||||
for (auto& mod : fullList) {
|
for (auto& mod : fullList) {
|
||||||
auto addonId = mod->pack->addonId;
|
auto addonId = mod->pack->addonId;
|
||||||
@ -280,7 +289,61 @@ QHash<QString, QStringList> GetModDependenciesTask::getRequiredBy()
|
|||||||
req.append(smod->pack->name);
|
req.append(smod->pack->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rby[addonId.toString()] = req;
|
rby[addonId.toString()] = { maybeInstalled(mod), req };
|
||||||
}
|
}
|
||||||
return rby;
|
return rby;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// super lax compare (but not fuzzy)
|
||||||
|
// convert to lowercase
|
||||||
|
// convert all speratores to whitespace
|
||||||
|
// simplify sequence of internal whitespace to a single space
|
||||||
|
// efectivly compare two strings ignoring all separators and case
|
||||||
|
auto laxCompare = [](QString fsfilename, QString metadataFilename, bool excludeDigits = false) {
|
||||||
|
// allowed character seperators
|
||||||
|
QList<QChar> allowedSeperators = { '-', '+', '.', '_' };
|
||||||
|
if (excludeDigits)
|
||||||
|
allowedSeperators.append({ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' });
|
||||||
|
|
||||||
|
// copy in lowercase
|
||||||
|
auto fsName = fsfilename.toLower();
|
||||||
|
auto metaName = metadataFilename.toLower();
|
||||||
|
|
||||||
|
// replace all potential allowed seperatores with whitespace
|
||||||
|
for (auto sep : allowedSeperators) {
|
||||||
|
fsName = fsName.replace(sep, ' ');
|
||||||
|
metaName = metaName.replace(sep, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove extraneous whitespace
|
||||||
|
fsName = fsName.simplified();
|
||||||
|
metaName = metaName.simplified();
|
||||||
|
|
||||||
|
return fsName.compare(metaName) == 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool GetModDependenciesTask::isLocalyInstalled(std::shared_ptr<PackDependency> pDep)
|
||||||
|
{
|
||||||
|
return pDep->version.fileName.isEmpty() ||
|
||||||
|
|
||||||
|
std::find_if(m_selected.begin(), m_selected.end(),
|
||||||
|
[pDep](std::shared_ptr<PackDependency> i) {
|
||||||
|
return !i->version.fileName.isEmpty() && laxCompare(i->version.fileName, pDep->version.fileName);
|
||||||
|
}) != m_selected.end() || // check the selected versions
|
||||||
|
|
||||||
|
std::find_if(m_mods_file_names.begin(), m_mods_file_names.end(),
|
||||||
|
[pDep](QString i) { return !i.isEmpty() && laxCompare(i, pDep->version.fileName); }) !=
|
||||||
|
m_mods_file_names.end() || // check the existing mods
|
||||||
|
|
||||||
|
std::find_if(m_pack_dependencies.begin(), m_pack_dependencies.end(), [pDep](std::shared_ptr<PackDependency> i) {
|
||||||
|
return pDep->pack->addonId != i->pack->addonId && !i->version.fileName.isEmpty() &&
|
||||||
|
laxCompare(pDep->version.fileName, i->version.fileName);
|
||||||
|
}) != m_pack_dependencies.end(); // check loaded dependencies
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetModDependenciesTask::maybeInstalled(std::shared_ptr<PackDependency> pDep)
|
||||||
|
{
|
||||||
|
return std::find_if(m_mods_file_names.begin(), m_mods_file_names.end(), [pDep](QString i) {
|
||||||
|
return !i.isEmpty() && laxCompare(i, pDep->version.fileName, true);
|
||||||
|
}) != m_mods_file_names.end(); // check the existing mods
|
||||||
|
}
|
||||||
|
@ -50,6 +50,11 @@ class GetModDependenciesTask : public SequentialTask {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PackDependencyExtraInfo {
|
||||||
|
bool maybe_installed;
|
||||||
|
QStringList required_by;
|
||||||
|
};
|
||||||
|
|
||||||
struct Provider {
|
struct Provider {
|
||||||
ModPlatform::ResourceProvider name;
|
ModPlatform::ResourceProvider name;
|
||||||
std::shared_ptr<ResourceDownload::ModModel> mod;
|
std::shared_ptr<ResourceDownload::ModModel> mod;
|
||||||
@ -62,7 +67,7 @@ class GetModDependenciesTask : public SequentialTask {
|
|||||||
QList<std::shared_ptr<PackDependency>> selected);
|
QList<std::shared_ptr<PackDependency>> selected);
|
||||||
|
|
||||||
auto getDependecies() const -> QList<std::shared_ptr<PackDependency>> { return m_pack_dependencies; }
|
auto getDependecies() const -> QList<std::shared_ptr<PackDependency>> { return m_pack_dependencies; }
|
||||||
QHash<QString, QStringList> getRequiredBy();
|
QHash<QString, PackDependencyExtraInfo> getExtraInfo();
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
Task::Ptr prepareDependencyTask(const ModPlatform::Dependency&, ModPlatform::ResourceProvider, int);
|
Task::Ptr prepareDependencyTask(const ModPlatform::Dependency&, ModPlatform::ResourceProvider, int);
|
||||||
@ -73,10 +78,14 @@ class GetModDependenciesTask : public SequentialTask {
|
|||||||
ModPlatform::Dependency getOverride(const ModPlatform::Dependency&, ModPlatform::ResourceProvider providerName);
|
ModPlatform::Dependency getOverride(const ModPlatform::Dependency&, ModPlatform::ResourceProvider providerName);
|
||||||
void removePack(const QVariant& addonId);
|
void removePack(const QVariant& addonId);
|
||||||
|
|
||||||
|
bool isLocalyInstalled(std::shared_ptr<PackDependency> pDep);
|
||||||
|
bool maybeInstalled(std::shared_ptr<PackDependency> pDep);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<std::shared_ptr<PackDependency>> m_pack_dependencies;
|
QList<std::shared_ptr<PackDependency>> m_pack_dependencies;
|
||||||
QList<std::shared_ptr<Metadata::ModStruct>> m_mods;
|
QList<std::shared_ptr<Metadata::ModStruct>> m_mods;
|
||||||
QList<std::shared_ptr<PackDependency>> m_selected;
|
QList<std::shared_ptr<PackDependency>> m_selected;
|
||||||
|
QStringList m_mods_file_names;
|
||||||
Provider m_flame_provider;
|
Provider m_flame_provider;
|
||||||
Provider m_modrinth_provider;
|
Provider m_modrinth_provider;
|
||||||
|
|
||||||
|
@ -469,7 +469,7 @@ bool processZIP(Mod& mod, [[maybe_unused]] ProcessingLevel level)
|
|||||||
|
|
||||||
QuaZipFile file(&zip);
|
QuaZipFile file(&zip);
|
||||||
|
|
||||||
if (zip.setCurrentFile("META-INF/mods.toml")) {
|
if (zip.setCurrentFile("META-INF/mods.toml") || zip.setCurrentFile("META-INF/neoforge.mods.toml")) {
|
||||||
if (!file.open(QIODevice::ReadOnly)) {
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
zip.close();
|
zip.close();
|
||||||
return false;
|
return false;
|
||||||
|
@ -28,6 +28,7 @@ class CheckUpdateTask : public Task {
|
|||||||
QString changelog;
|
QString changelog;
|
||||||
ModPlatform::ResourceProvider provider;
|
ModPlatform::ResourceProvider provider;
|
||||||
shared_qobject_ptr<ResourceDownloadTask> download;
|
shared_qobject_ptr<ResourceDownloadTask> download;
|
||||||
|
bool enabled = true;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UpdatableMod(QString name,
|
UpdatableMod(QString name,
|
||||||
@ -37,7 +38,8 @@ class CheckUpdateTask : public Task {
|
|||||||
std::optional<ModPlatform::IndexedVersionType> new_v_type,
|
std::optional<ModPlatform::IndexedVersionType> new_v_type,
|
||||||
QString changelog,
|
QString changelog,
|
||||||
ModPlatform::ResourceProvider p,
|
ModPlatform::ResourceProvider p,
|
||||||
shared_qobject_ptr<ResourceDownloadTask> t)
|
shared_qobject_ptr<ResourceDownloadTask> t,
|
||||||
|
bool enabled = true)
|
||||||
: name(name)
|
: name(name)
|
||||||
, old_hash(old_h)
|
, old_hash(old_h)
|
||||||
, old_version(old_v)
|
, old_version(old_v)
|
||||||
@ -46,6 +48,7 @@ class CheckUpdateTask : public Task {
|
|||||||
, changelog(changelog)
|
, changelog(changelog)
|
||||||
, provider(p)
|
, provider(p)
|
||||||
, download(t)
|
, download(t)
|
||||||
|
, enabled(enabled)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -149,6 +149,7 @@ void EnsureMetadataTask::executeTask()
|
|||||||
if (m_current_task)
|
if (m_current_task)
|
||||||
m_current_task.reset();
|
m_current_task.reset();
|
||||||
});
|
});
|
||||||
|
connect(project_task.get(), &Task::failed, this, &EnsureMetadataTask::emitFailed);
|
||||||
|
|
||||||
m_current_task = project_task;
|
m_current_task = project_task;
|
||||||
project_task->start();
|
project_task->start();
|
||||||
|
@ -122,6 +122,8 @@ struct ExtraPackData {
|
|||||||
QString wikiUrl;
|
QString wikiUrl;
|
||||||
QString discordUrl;
|
QString discordUrl;
|
||||||
|
|
||||||
|
QString status;
|
||||||
|
|
||||||
QString body;
|
QString body;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -96,6 +96,7 @@ class ResourceAPI {
|
|||||||
};
|
};
|
||||||
struct VersionSearchCallbacks {
|
struct VersionSearchCallbacks {
|
||||||
std::function<void(QJsonDocument&, ModPlatform::IndexedPack)> on_succeed;
|
std::function<void(QJsonDocument&, ModPlatform::IndexedPack)> on_succeed;
|
||||||
|
std::function<void(QString const& reason, int network_error_code)> on_fail;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ProjectInfoArgs {
|
struct ProjectInfoArgs {
|
||||||
@ -118,6 +119,7 @@ class ResourceAPI {
|
|||||||
|
|
||||||
struct DependencySearchCallbacks {
|
struct DependencySearchCallbacks {
|
||||||
std::function<void(QJsonDocument&, const ModPlatform::Dependency&)> on_succeed;
|
std::function<void(QJsonDocument&, const ModPlatform::Dependency&)> on_succeed;
|
||||||
|
std::function<void(QString const& reason, int network_error_code)> on_fail;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -1031,6 +1031,12 @@ void PackInstallTask::install()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
components->setComponentVersion("net.minecraftforge", version);
|
components->setComponentVersion("net.minecraftforge", version);
|
||||||
|
} else if (m_version.loader.type == QString("neoforge")) {
|
||||||
|
auto version = getVersionForLoader("net.neoforged");
|
||||||
|
if (version == Q_NULLPTR)
|
||||||
|
return;
|
||||||
|
|
||||||
|
components->setComponentVersion("net.neoforged", version);
|
||||||
} else if (m_version.loader.type == QString("fabric")) {
|
} else if (m_version.loader.type == QString("fabric")) {
|
||||||
auto version = getVersionForLoader("net.fabricmc.fabric-loader");
|
auto version = getVersionForLoader("net.fabricmc.fabric-loader");
|
||||||
if (version == Q_NULLPTR)
|
if (version == Q_NULLPTR)
|
||||||
|
@ -119,7 +119,6 @@ void Flame::FileResolvingTask::netJobFinished()
|
|||||||
connect(m_checkJob.get(), &NetJob::failed, this, [this, step_progress](QString reason) {
|
connect(m_checkJob.get(), &NetJob::failed, this, [this, step_progress](QString reason) {
|
||||||
step_progress->state = TaskStepState::Failed;
|
step_progress->state = TaskStepState::Failed;
|
||||||
stepProgress(*step_progress);
|
stepProgress(*step_progress);
|
||||||
emitFailed(reason);
|
|
||||||
});
|
});
|
||||||
connect(m_checkJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress);
|
connect(m_checkJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress);
|
||||||
connect(m_checkJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) {
|
connect(m_checkJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) {
|
||||||
|
@ -24,7 +24,7 @@ bool FlameCheckUpdate::abort()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info)
|
ModPlatform::IndexedPack FlameCheckUpdate::getProjectInfo(ModPlatform::IndexedVersion& ver_info)
|
||||||
{
|
{
|
||||||
ModPlatform::IndexedPack pack;
|
ModPlatform::IndexedPack pack;
|
||||||
|
|
||||||
@ -57,6 +57,7 @@ ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(get_project_job, &NetJob::failed, this, &FlameCheckUpdate::emitFailed);
|
||||||
QObject::connect(get_project_job, &NetJob::finished, [&loop, get_project_job] {
|
QObject::connect(get_project_job, &NetJob::finished, [&loop, get_project_job] {
|
||||||
get_project_job->deleteLater();
|
get_project_job->deleteLater();
|
||||||
loop.quit();
|
loop.quit();
|
||||||
@ -68,7 +69,7 @@ ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info)
|
|||||||
return pack;
|
return pack;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId)
|
ModPlatform::IndexedVersion FlameCheckUpdate::getFileInfo(int addonId, int fileId)
|
||||||
{
|
{
|
||||||
ModPlatform::IndexedVersion ver;
|
ModPlatform::IndexedVersion ver;
|
||||||
|
|
||||||
@ -100,7 +101,7 @@ ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId)
|
|||||||
qDebug() << doc;
|
qDebug() << doc;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
connect(get_file_info_job, &NetJob::failed, this, &FlameCheckUpdate::emitFailed);
|
||||||
QObject::connect(get_file_info_job, &NetJob::finished, [&loop, get_file_info_job] {
|
QObject::connect(get_file_info_job, &NetJob::finished, [&loop, get_file_info_job] {
|
||||||
get_file_info_job->deleteLater();
|
get_file_info_job->deleteLater();
|
||||||
loop.quit();
|
loop.quit();
|
||||||
|
@ -22,6 +22,9 @@ class FlameCheckUpdate : public CheckUpdateTask {
|
|||||||
void executeTask() override;
|
void executeTask() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info);
|
||||||
|
ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId);
|
||||||
|
|
||||||
NetJob* m_net_job = nullptr;
|
NetJob* m_net_job = nullptr;
|
||||||
|
|
||||||
bool m_was_aborted = false;
|
bool m_was_aborted = false;
|
||||||
|
@ -227,6 +227,7 @@ bool FlameCreationTask::updateInstance()
|
|||||||
m_files_to_remove.append(old_minecraft_dir.absoluteFilePath(relative_path));
|
m_files_to_remove.append(old_minecraft_dir.absoluteFilePath(relative_path));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
connect(job.get(), &Task::failed, this, [](QString reason) { qCritical() << "Failed to get files: " << reason; });
|
||||||
connect(job.get(), &Task::finished, &loop, &QEventLoop::quit);
|
connect(job.get(), &Task::finished, &loop, &QEventLoop::quit);
|
||||||
|
|
||||||
m_process_update_file_info_job = job;
|
m_process_update_file_info_job = job;
|
||||||
@ -353,6 +354,8 @@ bool FlameCreationTask::createInstance()
|
|||||||
auto id = loader.id;
|
auto id = loader.id;
|
||||||
if (id.startsWith("neoforge-")) {
|
if (id.startsWith("neoforge-")) {
|
||||||
id.remove("neoforge-");
|
id.remove("neoforge-");
|
||||||
|
if (id.startsWith("1.20.1-"))
|
||||||
|
id.remove("1.20.1-"); // this is a mess for curseforge
|
||||||
loaderType = "neoforge";
|
loaderType = "neoforge";
|
||||||
loaderUid = "net.neoforged";
|
loaderUid = "net.neoforged";
|
||||||
} else if (id.startsWith("forge-")) {
|
} else if (id.startsWith("forge-")) {
|
||||||
@ -427,6 +430,9 @@ bool FlameCreationTask::createInstance()
|
|||||||
// Don't add managed info to packs without an ID (most likely imported from ZIP)
|
// Don't add managed info to packs without an ID (most likely imported from ZIP)
|
||||||
if (!m_managed_id.isEmpty())
|
if (!m_managed_id.isEmpty())
|
||||||
instance.setManagedPack("flame", m_managed_id, m_pack.name, m_managed_version_id, m_pack.version);
|
instance.setManagedPack("flame", m_managed_id, m_pack.name, m_managed_version_id, m_pack.version);
|
||||||
|
else
|
||||||
|
instance.setManagedPack("flame", "", name(), "", "");
|
||||||
|
|
||||||
instance.setName(name());
|
instance.setName(name());
|
||||||
|
|
||||||
m_mod_id_resolver.reset(new Flame::FileResolvingTask(APPLICATION->network(), m_pack));
|
m_mod_id_resolver.reset(new Flame::FileResolvingTask(APPLICATION->network(), m_pack));
|
||||||
@ -531,7 +537,12 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
|||||||
selectedOptionalMods = optionalModDialog.getResult();
|
selectedOptionalMods = optionalModDialog.getResult();
|
||||||
}
|
}
|
||||||
for (const auto& result : results) {
|
for (const auto& result : results) {
|
||||||
auto relpath = FS::PathCombine(result.targetFolder, result.fileName);
|
auto fileName = result.fileName;
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
fileName = FS::RemoveInvalidPathChars(fileName);
|
||||||
|
#endif
|
||||||
|
auto relpath = FS::PathCombine(result.targetFolder, fileName);
|
||||||
|
|
||||||
if (!result.required && !selectedOptionalMods.contains(relpath)) {
|
if (!result.required && !selectedOptionalMods.contains(relpath)) {
|
||||||
relpath += ".disabled";
|
relpath += ".disabled";
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "FlameModIndex.h"
|
#include "FlameModIndex.h"
|
||||||
|
|
||||||
|
#include "FileSystem.h"
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include "minecraft/MinecraftInstance.h"
|
||||||
#include "minecraft/PackProfile.h"
|
#include "minecraft/PackProfile.h"
|
||||||
@ -138,6 +139,9 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
|
|||||||
file.version = Json::requireString(obj, "displayName");
|
file.version = Json::requireString(obj, "displayName");
|
||||||
file.downloadUrl = Json::ensureString(obj, "downloadUrl");
|
file.downloadUrl = Json::ensureString(obj, "downloadUrl");
|
||||||
file.fileName = Json::requireString(obj, "fileName");
|
file.fileName = Json::requireString(obj, "fileName");
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
file.fileName = FS::RemoveInvalidPathChars(file.fileName);
|
||||||
|
#endif
|
||||||
|
|
||||||
ModPlatform::IndexedVersionType::VersionType ver_type;
|
ModPlatform::IndexedVersionType::VersionType ver_type;
|
||||||
switch (Json::requireInteger(obj, "releaseType")) {
|
switch (Json::requireInteger(obj, "releaseType")) {
|
||||||
|
@ -201,7 +201,7 @@ void FlamePackExportTask::makeApiRequest()
|
|||||||
<< " reason: " << parseError.errorString();
|
<< " reason: " << parseError.errorString();
|
||||||
qWarning() << *response;
|
qWarning() << *response;
|
||||||
|
|
||||||
failed(parseError.errorString());
|
emitFailed(parseError.errorString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,6 +213,7 @@ void FlamePackExportTask::makeApiRequest()
|
|||||||
if (dataArr.isEmpty()) {
|
if (dataArr.isEmpty()) {
|
||||||
qWarning() << "No matches found for fingerprint search!";
|
qWarning() << "No matches found for fingerprint search!";
|
||||||
|
|
||||||
|
getProjectsInfo();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (auto match : dataArr) {
|
for (auto match : dataArr) {
|
||||||
@ -243,9 +244,9 @@ void FlamePackExportTask::makeApiRequest()
|
|||||||
qDebug() << doc;
|
qDebug() << doc;
|
||||||
}
|
}
|
||||||
pendingHashes.clear();
|
pendingHashes.clear();
|
||||||
|
getProjectsInfo();
|
||||||
});
|
});
|
||||||
connect(task.get(), &Task::finished, this, &FlamePackExportTask::getProjectsInfo);
|
connect(task.get(), &NetJob::failed, this, &FlamePackExportTask::getProjectsInfo);
|
||||||
connect(task.get(), &NetJob::failed, this, &FlamePackExportTask::emitFailed);
|
|
||||||
task->start();
|
task->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,7 +280,7 @@ void FlamePackExportTask::getProjectsInfo()
|
|||||||
qWarning() << "Error while parsing JSON response from CurseForge projects task at " << parseError.offset
|
qWarning() << "Error while parsing JSON response from CurseForge projects task at " << parseError.offset
|
||||||
<< " reason: " << parseError.errorString();
|
<< " reason: " << parseError.errorString();
|
||||||
qWarning() << *response;
|
qWarning() << *response;
|
||||||
failed(parseError.errorString());
|
emitFailed(parseError.errorString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,6 +324,7 @@ void FlamePackExportTask::getProjectsInfo()
|
|||||||
}
|
}
|
||||||
buildZip();
|
buildZip();
|
||||||
});
|
});
|
||||||
|
connect(projTask.get(), &Task::failed, this, &FlamePackExportTask::emitFailed);
|
||||||
task.reset(projTask);
|
task.reset(projTask);
|
||||||
task->start();
|
task->start();
|
||||||
}
|
}
|
||||||
@ -332,7 +334,7 @@ void FlamePackExportTask::buildZip()
|
|||||||
setStatus(tr("Adding files..."));
|
setStatus(tr("Adding files..."));
|
||||||
setProgress(4, 5);
|
setProgress(4, 5);
|
||||||
|
|
||||||
auto zipTask = makeShared<MMCZip::ExportToZipTask>(output, gameRoot, files, "overrides/", true);
|
auto zipTask = makeShared<MMCZip::ExportToZipTask>(output, gameRoot, files, "overrides/", true, false);
|
||||||
zipTask->addExtraFile("manifest.json", generateIndex());
|
zipTask->addExtraFile("manifest.json", generateIndex());
|
||||||
zipTask->addExtraFile("modlist.html", generateHTML());
|
zipTask->addExtraFile("modlist.html", generateHTML());
|
||||||
|
|
||||||
@ -392,13 +394,17 @@ QByteArray FlamePackExportTask::generateIndex()
|
|||||||
version["version"] = minecraft->m_version;
|
version["version"] = minecraft->m_version;
|
||||||
QString id;
|
QString id;
|
||||||
if (quilt != nullptr)
|
if (quilt != nullptr)
|
||||||
id = "quilt-" + quilt->getVersion();
|
id = "quilt-" + quilt->m_version;
|
||||||
else if (fabric != nullptr)
|
else if (fabric != nullptr)
|
||||||
id = "fabric-" + fabric->getVersion();
|
id = "fabric-" + fabric->m_version;
|
||||||
else if (forge != nullptr)
|
else if (forge != nullptr)
|
||||||
id = "forge-" + forge->getVersion();
|
id = "forge-" + forge->m_version;
|
||||||
else if (neoforge != nullptr)
|
else if (neoforge != nullptr) {
|
||||||
id = "neoforge-" + neoforge->getVersion();
|
id = "neoforge-";
|
||||||
|
if (minecraft->m_version == "1.20.1")
|
||||||
|
id += "1.20.1-";
|
||||||
|
id += neoforge->m_version;
|
||||||
|
}
|
||||||
version["modLoaders"] = QJsonArray();
|
version["modLoaders"] = QJsonArray();
|
||||||
if (!id.isEmpty()) {
|
if (!id.isEmpty()) {
|
||||||
QJsonObject loader;
|
QJsonObject loader;
|
||||||
|
@ -43,7 +43,7 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&
|
|||||||
callbacks.on_succeed(doc);
|
callbacks.on_succeed(doc);
|
||||||
});
|
});
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::failed, [&netJob, callbacks](QString reason) {
|
QObject::connect(netJob.get(), &NetJob::failed, [netJob, callbacks](const QString& reason) {
|
||||||
int network_error_code = -1;
|
int network_error_code = -1;
|
||||||
if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply)
|
if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply)
|
||||||
network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
@ -102,6 +102,13 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi
|
|||||||
|
|
||||||
callbacks.on_succeed(doc, args.pack);
|
callbacks.on_succeed(doc, args.pack);
|
||||||
});
|
});
|
||||||
|
QObject::connect(netJob.get(), &NetJob::failed, [netJob, callbacks](const QString& reason) {
|
||||||
|
int network_error_code = -1;
|
||||||
|
if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply)
|
||||||
|
network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
|
||||||
|
callbacks.on_fail(reason, network_error_code);
|
||||||
|
});
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
@ -146,6 +153,12 @@ Task::Ptr NetworkResourceAPI::getDependencyVersion(DependencySearchArgs&& args,
|
|||||||
|
|
||||||
callbacks.on_succeed(doc, args.dependency);
|
callbacks.on_succeed(doc, args.dependency);
|
||||||
});
|
});
|
||||||
|
QObject::connect(netJob.get(), &NetJob::failed, [netJob, callbacks](const QString& reason) {
|
||||||
|
int network_error_code = -1;
|
||||||
|
if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply)
|
||||||
|
network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
|
||||||
|
callbacks.on_fail(reason, network_error_code);
|
||||||
|
});
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ Modpack parseDirectory(QString path)
|
|||||||
modpack.version = Json::requireString(root, "version", "version");
|
modpack.version = Json::requireString(root, "version", "version");
|
||||||
modpack.mcVersion = Json::requireString(root, "mcVersion", "mcVersion");
|
modpack.mcVersion = Json::requireString(root, "mcVersion", "mcVersion");
|
||||||
modpack.jvmArgs = Json::ensureVariant(root, "jvmArgs", {}, "jvmArgs");
|
modpack.jvmArgs = Json::ensureVariant(root, "jvmArgs", {}, "jvmArgs");
|
||||||
|
modpack.totalPlayTime = Json::requireInteger(root, "totalPlayTime", "totalPlayTime");
|
||||||
} catch (const Exception& e) {
|
} catch (const Exception& e) {
|
||||||
qDebug() << "Couldn't load ftb instance json: " << e.cause();
|
qDebug() << "Couldn't load ftb instance json: " << e.cause();
|
||||||
return {};
|
return {};
|
||||||
|
@ -36,6 +36,7 @@ struct Modpack {
|
|||||||
QString name;
|
QString name;
|
||||||
QString version;
|
QString version;
|
||||||
QString mcVersion;
|
QString mcVersion;
|
||||||
|
int totalPlayTime;
|
||||||
// not needed for instance creation
|
// not needed for instance creation
|
||||||
QVariant jvmArgs;
|
QVariant jvmArgs;
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ void PackInstallTask::executeTask()
|
|||||||
progress(1, 2);
|
progress(1, 2);
|
||||||
|
|
||||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] {
|
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] {
|
||||||
FS::copy folderCopy(m_pack.path, FS::PathCombine(m_stagingPath, ".minecraft"));
|
FS::copy folderCopy(m_pack.path, FS::PathCombine(m_stagingPath, "minecraft"));
|
||||||
folderCopy.followSymlinks(true);
|
folderCopy.followSymlinks(true);
|
||||||
return folderCopy();
|
return folderCopy();
|
||||||
});
|
});
|
||||||
@ -55,6 +55,7 @@ void PackInstallTask::copySettings()
|
|||||||
instanceSettings->suspendSave();
|
instanceSettings->suspendSave();
|
||||||
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
||||||
instance.settings()->set("InstanceType", "OneSix");
|
instance.settings()->set("InstanceType", "OneSix");
|
||||||
|
instance.settings()->set("totalTimePlayed", m_pack.totalPlayTime / 1000);
|
||||||
|
|
||||||
if (m_pack.jvmArgs.isValid() && !m_pack.jvmArgs.toString().isEmpty()) {
|
if (m_pack.jvmArgs.isValid() && !m_pack.jvmArgs.toString().isEmpty()) {
|
||||||
instance.settings()->set("OverrideJavaArgs", true);
|
instance.settings()->set("OverrideJavaArgs", true);
|
||||||
|
@ -137,7 +137,7 @@ void PackInstallTask::install()
|
|||||||
QDir unzipMcDir(m_stagingPath + "/unzip/minecraft");
|
QDir unzipMcDir(m_stagingPath + "/unzip/minecraft");
|
||||||
if (unzipMcDir.exists()) {
|
if (unzipMcDir.exists()) {
|
||||||
// ok, found minecraft dir, move contents to instance dir
|
// ok, found minecraft dir, move contents to instance dir
|
||||||
if (!QDir().rename(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/.minecraft")) {
|
if (!QDir().rename(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/minecraft")) {
|
||||||
emitFailed(tr("Failed to move unzipped Minecraft!"));
|
emitFailed(tr("Failed to move unzipped Minecraft!"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -155,7 +155,7 @@ void PackInstallTask::install()
|
|||||||
bool fallback = true;
|
bool fallback = true;
|
||||||
|
|
||||||
// handle different versions
|
// handle different versions
|
||||||
QFile packJson(m_stagingPath + "/.minecraft/pack.json");
|
QFile packJson(m_stagingPath + "/minecraft/pack.json");
|
||||||
QDir jarmodDir = QDir(m_stagingPath + "/unzip/instMods");
|
QDir jarmodDir = QDir(m_stagingPath + "/unzip/instMods");
|
||||||
if (packJson.exists()) {
|
if (packJson.exists()) {
|
||||||
packJson.open(QIODevice::ReadOnly | QIODevice::Text);
|
packJson.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||||
|
@ -72,9 +72,7 @@ void ModrinthCheckUpdate::executeTask()
|
|||||||
auto response = std::make_shared<QByteArray>();
|
auto response = std::make_shared<QByteArray>();
|
||||||
auto job = api.latestVersions(hashes, best_hash_type, m_game_versions, m_loaders, response);
|
auto job = api.latestVersions(hashes, best_hash_type, m_game_versions, m_loaders, response);
|
||||||
|
|
||||||
QEventLoop lock;
|
connect(job.get(), &Task::succeeded, this, [this, response, mappings, best_hash_type, job] {
|
||||||
|
|
||||||
connect(job.get(), &Task::succeeded, this, [this, response, &mappings, best_hash_type, job] {
|
|
||||||
QJsonParseError parse_error{};
|
QJsonParseError parse_error{};
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||||
if (parse_error.error != QJsonParseError::NoError) {
|
if (parse_error.error != QJsonParseError::NoError) {
|
||||||
@ -82,7 +80,7 @@ void ModrinthCheckUpdate::executeTask()
|
|||||||
<< " reason: " << parse_error.errorString();
|
<< " reason: " << parse_error.errorString();
|
||||||
qWarning() << *response;
|
qWarning() << *response;
|
||||||
|
|
||||||
failed(parse_error.errorString());
|
emitFailed(parse_error.errorString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,19 +165,17 @@ void ModrinthCheckUpdate::executeTask()
|
|||||||
m_deps.append(std::make_shared<GetModDependenciesTask::PackDependency>(pack, project_ver));
|
m_deps.append(std::make_shared<GetModDependenciesTask::PackDependency>(pack, project_ver));
|
||||||
}
|
}
|
||||||
} catch (Json::JsonException& e) {
|
} catch (Json::JsonException& e) {
|
||||||
failed(e.cause() + " : " + e.what());
|
emitFailed(e.cause() + " : " + e.what());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
emitSucceeded();
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(job.get(), &Task::finished, &lock, &QEventLoop::quit);
|
connect(job.get(), &Task::failed, this, &ModrinthCheckUpdate::emitFailed);
|
||||||
|
|
||||||
setStatus(tr("Waiting for the API response from Modrinth..."));
|
setStatus(tr("Waiting for the API response from Modrinth..."));
|
||||||
setProgress(1, 3);
|
setProgress(1, 3);
|
||||||
|
|
||||||
m_net_job = qSharedPointerObjectCast<NetJob, Task>(job);
|
m_net_job = qSharedPointerObjectCast<NetJob, Task>(job);
|
||||||
job->start();
|
job->start();
|
||||||
|
|
||||||
lock.exec();
|
|
||||||
|
|
||||||
emitSucceeded();
|
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "ui/pages/modplatform/OptionalModDialog.h"
|
#include "ui/pages/modplatform/OptionalModDialog.h"
|
||||||
|
|
||||||
#include <QAbstractButton>
|
#include <QAbstractButton>
|
||||||
|
#include <QFileInfo>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
bool ModrinthCreationTask::abort()
|
bool ModrinthCreationTask::abort()
|
||||||
@ -58,6 +59,7 @@ bool ModrinthCreationTask::updateInstance()
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto version_name = inst->getManagedPackVersionName();
|
auto version_name = inst->getManagedPackVersionName();
|
||||||
|
m_root_path = QFileInfo(inst->gameRoot()).fileName();
|
||||||
auto version_str = !version_name.isEmpty() ? tr(" (version %1)").arg(version_name) : "";
|
auto version_str = !version_name.isEmpty() ? tr(" (version %1)").arg(version_name) : "";
|
||||||
|
|
||||||
if (shouldConfirmUpdate()) {
|
if (shouldConfirmUpdate()) {
|
||||||
@ -173,7 +175,7 @@ bool ModrinthCreationTask::createInstance()
|
|||||||
FS::ensureFilePathExists(new_index_place);
|
FS::ensureFilePathExists(new_index_place);
|
||||||
QFile::rename(index_path, new_index_place);
|
QFile::rename(index_path, new_index_place);
|
||||||
|
|
||||||
auto mcPath = FS::PathCombine(m_stagingPath, ".minecraft");
|
auto mcPath = FS::PathCombine(m_stagingPath, m_root_path);
|
||||||
|
|
||||||
auto override_path = FS::PathCombine(m_stagingPath, "overrides");
|
auto override_path = FS::PathCombine(m_stagingPath, "overrides");
|
||||||
if (QFile::exists(override_path)) {
|
if (QFile::exists(override_path)) {
|
||||||
@ -226,20 +228,27 @@ bool ModrinthCreationTask::createInstance()
|
|||||||
// Don't add managed info to packs without an ID (most likely imported from ZIP)
|
// Don't add managed info to packs without an ID (most likely imported from ZIP)
|
||||||
if (!m_managed_id.isEmpty())
|
if (!m_managed_id.isEmpty())
|
||||||
instance.setManagedPack("modrinth", m_managed_id, m_managed_name, m_managed_version_id, version());
|
instance.setManagedPack("modrinth", m_managed_id, m_managed_name, m_managed_version_id, version());
|
||||||
|
else
|
||||||
|
instance.setManagedPack("modrinth", "", name(), "", "");
|
||||||
|
|
||||||
instance.setName(name());
|
instance.setName(name());
|
||||||
instance.saveNow();
|
instance.saveNow();
|
||||||
|
|
||||||
m_files_job.reset(new NetJob(tr("Mod Download Modrinth"), APPLICATION->network()));
|
m_files_job.reset(new NetJob(tr("Mod Download Modrinth"), APPLICATION->network()));
|
||||||
|
|
||||||
auto root_modpack_path = FS::PathCombine(m_stagingPath, ".minecraft");
|
auto root_modpack_path = FS::PathCombine(m_stagingPath, m_root_path);
|
||||||
auto root_modpack_url = QUrl::fromLocalFile(root_modpack_path);
|
auto root_modpack_url = QUrl::fromLocalFile(root_modpack_path);
|
||||||
|
|
||||||
for (auto file : m_files) {
|
for (auto file : m_files) {
|
||||||
auto file_path = FS::PathCombine(root_modpack_path, file.path);
|
auto fileName = file.path;
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
fileName = FS::RemoveInvalidPathChars(fileName);
|
||||||
|
#endif
|
||||||
|
auto file_path = FS::PathCombine(root_modpack_path, fileName);
|
||||||
if (!root_modpack_url.isParentOf(QUrl::fromLocalFile(file_path))) {
|
if (!root_modpack_url.isParentOf(QUrl::fromLocalFile(file_path))) {
|
||||||
// This means we somehow got out of the root folder, so abort here to prevent exploits
|
// This means we somehow got out of the root folder, so abort here to prevent exploits
|
||||||
setError(tr("One of the files has a path that leads to an arbitrary location (%1). This is a security risk and isn't allowed.")
|
setError(tr("One of the files has a path that leads to an arbitrary location (%1). This is a security risk and isn't allowed.")
|
||||||
.arg(file.path));
|
.arg(fileName));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +298,7 @@ bool ModrinthCreationTask::createInstance()
|
|||||||
// Only change the name if it didn't use a custom name, so that the previous custom name
|
// Only change the name if it didn't use a custom name, so that the previous custom name
|
||||||
// is preserved, but if we're using the original one, we update the version string.
|
// is preserved, but if we're using the original one, we update the version string.
|
||||||
// NOTE: This needs to come before the copyManagedPack call!
|
// NOTE: This needs to come before the copyManagedPack call!
|
||||||
if (inst->name().contains(inst->getManagedPackVersionName())) {
|
if (inst->name().contains(inst->getManagedPackVersionName()) && inst->name() != instance.name()) {
|
||||||
if (askForChangingInstanceName(m_parent, inst->name(), instance.name()) == InstanceNameChange::ShouldChange)
|
if (askForChangingInstanceName(m_parent, inst->name(), instance.name()) == InstanceNameChange::ShouldChange)
|
||||||
inst->setName(instance.name());
|
inst->setName(instance.name());
|
||||||
}
|
}
|
||||||
|
@ -46,4 +46,6 @@ class ModrinthCreationTask final : public InstanceCreationTask {
|
|||||||
NetJob::Ptr m_files_job;
|
NetJob::Ptr m_files_job;
|
||||||
|
|
||||||
std::optional<InstancePtr> m_instance;
|
std::optional<InstancePtr> m_instance;
|
||||||
|
|
||||||
|
QString m_root_path = "minecraft";
|
||||||
};
|
};
|
||||||
|
@ -200,7 +200,7 @@ void ModrinthPackExportTask::buildZip()
|
|||||||
{
|
{
|
||||||
setStatus(tr("Adding files..."));
|
setStatus(tr("Adding files..."));
|
||||||
|
|
||||||
auto zipTask = makeShared<MMCZip::ExportToZipTask>(output, gameRoot, files, "overrides/", true);
|
auto zipTask = makeShared<MMCZip::ExportToZipTask>(output, gameRoot, files, "overrides/", true, true);
|
||||||
zipTask->addExtraFile("modrinth.index.json", generateIndex());
|
zipTask->addExtraFile("modrinth.index.json", generateIndex());
|
||||||
|
|
||||||
zipTask->setExcludeFiles(resolvedFiles.keys());
|
zipTask->setExcludeFiles(resolvedFiles.keys());
|
||||||
@ -287,16 +287,12 @@ QByteArray ModrinthPackExportTask::generateIndex()
|
|||||||
env["client"] = "required";
|
env["client"] = "required";
|
||||||
env["server"] = "required";
|
env["server"] = "required";
|
||||||
}
|
}
|
||||||
switch (iterator->side) {
|
|
||||||
case Metadata::ModSide::ClientSide:
|
// a server side mod does not imply that the mod does not work on the client
|
||||||
env["server"] = "unsupported";
|
// however, if a mrpack mod is marked as server-only it will not install on the client
|
||||||
break;
|
if (iterator->side == Metadata::ModSide::ClientSide)
|
||||||
case Metadata::ModSide::ServerSide:
|
env["server"] = "unsupported";
|
||||||
env["client"] = "unsupported";
|
|
||||||
break;
|
|
||||||
case Metadata::ModSide::UniversalSide:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
fileOut["env"] = env;
|
fileOut["env"] = env;
|
||||||
|
|
||||||
fileOut["path"] = path;
|
fileOut["path"] = path;
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ModrinthPackIndex.h"
|
#include "ModrinthPackIndex.h"
|
||||||
|
#include "FileSystem.h"
|
||||||
#include "ModrinthAPI.h"
|
#include "ModrinthAPI.h"
|
||||||
|
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
@ -104,6 +105,8 @@ void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& ob
|
|||||||
pack.extraData.donate.append(donate);
|
pack.extraData.donate.append(donate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pack.extraData.status = Json::ensureString(obj, "status");
|
||||||
|
|
||||||
pack.extraData.body = Json::ensureString(obj, "body").remove("<br>");
|
pack.extraData.body = Json::ensureString(obj, "body").remove("<br>");
|
||||||
|
|
||||||
pack.extraDataLoaded = true;
|
pack.extraDataLoaded = true;
|
||||||
@ -224,6 +227,9 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t
|
|||||||
if (parent.contains("url")) {
|
if (parent.contains("url")) {
|
||||||
file.downloadUrl = Json::requireString(parent, "url");
|
file.downloadUrl = Json::requireString(parent, "url");
|
||||||
file.fileName = Json::requireString(parent, "filename");
|
file.fileName = Json::requireString(parent, "filename");
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
file.fileName = FS::RemoveInvalidPathChars(file.fileName);
|
||||||
|
#endif
|
||||||
file.is_preferred = Json::requireBoolean(parent, "primary") || (files.count() == 1);
|
file.is_preferred = Json::requireBoolean(parent, "primary") || (files.count() == 1);
|
||||||
auto hash_list = Json::requireObject(parent, "hashes");
|
auto hash_list = Json::requireObject(parent, "hashes");
|
||||||
|
|
||||||
|
@ -95,6 +95,8 @@ void loadIndexedInfo(Modpack& pack, QJsonObject& obj)
|
|||||||
pack.extra.donate.append(donate);
|
pack.extra.donate.append(donate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pack.extra.status = Json::ensureString(obj, "status");
|
||||||
|
|
||||||
pack.extraInfoLoaded = true;
|
pack.extraInfoLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +77,8 @@ struct ModpackExtra {
|
|||||||
QString discordUrl;
|
QString discordUrl;
|
||||||
|
|
||||||
QList<DonationData> donate;
|
QList<DonationData> donate;
|
||||||
|
|
||||||
|
QString status;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ModpackVersion {
|
struct ModpackVersion {
|
||||||
|
@ -62,7 +62,7 @@ void Technic::SingleZipPackInstallTask::downloadSucceeded()
|
|||||||
m_abortable = false;
|
m_abortable = false;
|
||||||
|
|
||||||
setStatus(tr("Extracting modpack"));
|
setStatus(tr("Extracting modpack"));
|
||||||
QDir extractDir(FS::PathCombine(m_stagingPath, ".minecraft"));
|
QDir extractDir(FS::PathCombine(m_stagingPath, "minecraft"));
|
||||||
qDebug() << "Attempting to create instance from" << m_archivePath;
|
qDebug() << "Attempting to create instance from" << m_archivePath;
|
||||||
|
|
||||||
// open the zip and find relevant files in it
|
// open the zip and find relevant files in it
|
||||||
|
@ -140,7 +140,7 @@ void Technic::SolderPackInstallTask::downloadSucceeded()
|
|||||||
m_filesNetJob.reset();
|
m_filesNetJob.reset();
|
||||||
m_extractFuture = QtConcurrent::run([this]() {
|
m_extractFuture = QtConcurrent::run([this]() {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
QString extractDir = FS::PathCombine(m_stagingPath, ".minecraft");
|
QString extractDir = FS::PathCombine(m_stagingPath, "minecraft");
|
||||||
FS::ensureFolderPathExists(extractDir);
|
FS::ensureFolderPathExists(extractDir);
|
||||||
|
|
||||||
while (m_modCount > i) {
|
while (m_modCount > i) {
|
||||||
|
@ -33,7 +33,7 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings,
|
|||||||
const QString& minecraftVersion,
|
const QString& minecraftVersion,
|
||||||
[[maybe_unused]] const bool isSolder)
|
[[maybe_unused]] const bool isSolder)
|
||||||
{
|
{
|
||||||
QString minecraftPath = FS::PathCombine(stagingPath, ".minecraft");
|
QString minecraftPath = FS::PathCombine(stagingPath, "minecraft");
|
||||||
QString configPath = FS::PathCombine(stagingPath, "instance.cfg");
|
QString configPath = FS::PathCombine(stagingPath, "instance.cfg");
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
||||||
MinecraftInstance instance(globalSettings, instanceSettings, stagingPath);
|
MinecraftInstance instance(globalSettings, instanceSettings, stagingPath);
|
||||||
@ -155,8 +155,26 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings,
|
|||||||
auto libraryObject = Json::ensureObject(library, {}, "");
|
auto libraryObject = Json::ensureObject(library, {}, "");
|
||||||
auto libraryName = Json::ensureString(libraryObject, "name", "", "");
|
auto libraryName = Json::ensureString(libraryObject, "name", "", "");
|
||||||
|
|
||||||
if ((libraryName.startsWith("net.minecraftforge:forge:") || libraryName.startsWith("net.minecraftforge:fmlloader:")) &&
|
if (libraryName.startsWith("net.neoforged.fancymodloader:")) { // it is neoforge
|
||||||
libraryName.contains('-')) {
|
// no easy way to get the version from the libs so use the arguments
|
||||||
|
auto arguments = Json::ensureObject(root, "arguments", {});
|
||||||
|
bool isVersionArg = false;
|
||||||
|
QString neoforgeVersion;
|
||||||
|
for (auto arg : Json::ensureArray(arguments, "game", {})) {
|
||||||
|
auto argument = Json::ensureString(arg, "");
|
||||||
|
if (isVersionArg) {
|
||||||
|
neoforgeVersion = argument;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
isVersionArg = "--fml.neoForgeVersion" == argument || "--fml.forgeVersion" == argument;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!neoforgeVersion.isEmpty()) {
|
||||||
|
components->setComponentVersion("net.neoforged", neoforgeVersion);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else if ((libraryName.startsWith("net.minecraftforge:forge:") || libraryName.startsWith("net.minecraftforge:fmlloader:")) &&
|
||||||
|
libraryName.contains('-')) {
|
||||||
QString libraryVersion = libraryName.section(':', 2);
|
QString libraryVersion = libraryName.section(':', 2);
|
||||||
if (!libraryVersion.startsWith("1.7.10-")) {
|
if (!libraryVersion.startsWith("1.7.10-")) {
|
||||||
components->setComponentVersion("net.minecraftforge", libraryName.section('-', 1));
|
components->setComponentVersion("net.minecraftforge", libraryName.section('-', 1));
|
||||||
@ -164,6 +182,7 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings,
|
|||||||
// 1.7.10 versions sometimes look like 1.7.10-10.13.4.1614-1.7.10, this filters out the 10.13.4.1614 part
|
// 1.7.10 versions sometimes look like 1.7.10-10.13.4.1614-1.7.10, this filters out the 10.13.4.1614 part
|
||||||
components->setComponentVersion("net.minecraftforge", libraryName.section('-', 1, 1));
|
components->setComponentVersion("net.minecraftforge", libraryName.section('-', 1, 1));
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
} else {
|
} else {
|
||||||
// <Technic library name prefix> -> <our component name>
|
// <Technic library name prefix> -> <our component name>
|
||||||
static QMap<QString, QString> loaderMap{ { "net.minecraftforge:minecraftforge:", "net.minecraftforge" },
|
static QMap<QString, QString> loaderMap{ { "net.minecraftforge:minecraftforge:", "net.minecraftforge" },
|
||||||
|
@ -84,6 +84,9 @@ auto HttpMetaCache::getEntry(QString base, QString resource_path) -> MetaEntryPt
|
|||||||
|
|
||||||
auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString expected_etag) -> MetaEntryPtr
|
auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString expected_etag) -> MetaEntryPtr
|
||||||
{
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
resource_path = FS::RemoveInvalidPathChars(resource_path);
|
||||||
|
#endif
|
||||||
auto entry = getEntry(base, resource_path);
|
auto entry = getEntry(base, resource_path);
|
||||||
// it's not present? generate a default stale entry
|
// it's not present? generate a default stale entry
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "NetJob.h"
|
#include "NetJob.h"
|
||||||
|
#include "tasks/ConcurrentTask.h"
|
||||||
#if defined(LAUNCHER_APPLICATION)
|
#if defined(LAUNCHER_APPLICATION)
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#endif
|
#endif
|
||||||
@ -60,18 +61,15 @@ auto NetJob::addNetAction(NetAction::Ptr action) -> bool
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetJob::startNext()
|
void NetJob::executeNextSubTask()
|
||||||
{
|
{
|
||||||
if (m_queue.isEmpty() && m_doing.isEmpty()) {
|
// We're finished, check for failures and retry if we can (up to 3 times)
|
||||||
// We're finished, check for failures and retry if we can (up to 3 times)
|
if (isRunning() && m_queue.isEmpty() && m_doing.isEmpty() && !m_failed.isEmpty() && m_try < 3) {
|
||||||
if (!m_failed.isEmpty() && m_try < 3) {
|
m_try += 1;
|
||||||
m_try += 1;
|
while (!m_failed.isEmpty())
|
||||||
while (!m_failed.isEmpty())
|
m_queue.enqueue(m_failed.take(*m_failed.keyBegin()));
|
||||||
m_queue.enqueue(m_failed.take(*m_failed.keyBegin()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
ConcurrentTask::executeNextSubTask();
|
||||||
ConcurrentTask::startNext();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto NetJob::size() const -> int
|
auto NetJob::size() const -> int
|
||||||
|
@ -55,8 +55,6 @@ class NetJob : public ConcurrentTask {
|
|||||||
explicit NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network, int max_concurrent = -1);
|
explicit NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network, int max_concurrent = -1);
|
||||||
~NetJob() override = default;
|
~NetJob() override = default;
|
||||||
|
|
||||||
void startNext() override;
|
|
||||||
|
|
||||||
auto size() const -> int;
|
auto size() const -> int;
|
||||||
|
|
||||||
auto canAbort() const -> bool override;
|
auto canAbort() const -> bool override;
|
||||||
@ -69,6 +67,9 @@ class NetJob : public ConcurrentTask {
|
|||||||
// Qt can't handle auto at the start for some reason?
|
// Qt can't handle auto at the start for some reason?
|
||||||
bool abort() override;
|
bool abort() override;
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void executeNextSubTask() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void updateState() override;
|
void updateState() override;
|
||||||
|
|
||||||
|
@ -69,7 +69,8 @@ void NetRequest::executeTask()
|
|||||||
|
|
||||||
if (getState() == Task::State::AbortedByUser) {
|
if (getState() == Task::State::AbortedByUser) {
|
||||||
qCWarning(logCat) << getUid().toString() << "Attempt to start an aborted Request:" << m_url.toString();
|
qCWarning(logCat) << getUid().toString() << "Attempt to start an aborted Request:" << m_url.toString();
|
||||||
emitAborted();
|
emit aborted();
|
||||||
|
emit finished();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,10 +87,12 @@ void NetRequest::executeTask()
|
|||||||
break;
|
break;
|
||||||
case State::Inactive:
|
case State::Inactive:
|
||||||
case State::Failed:
|
case State::Failed:
|
||||||
emitFailed();
|
emit failed("Failed to initilize sink");
|
||||||
|
emit finished();
|
||||||
return;
|
return;
|
||||||
case State::AbortedByUser:
|
case State::AbortedByUser:
|
||||||
emitAborted();
|
emit aborted();
|
||||||
|
emit finished();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
qt.*.debug=false
|
qt.*.debug=false
|
||||||
# don't log credentials by default
|
# don't log credentials by default
|
||||||
launcher.auth.credentials.debug=false
|
launcher.auth.credentials.debug=false
|
||||||
|
katabasis.*.debug=false
|
||||||
# remove the debug lines, other log levels still get through
|
# remove the debug lines, other log levels still get through
|
||||||
launcher.task.net.download.debug=false
|
launcher.task.net.download.debug=false
|
||||||
# enable or disable whole catageries
|
# enable or disable whole catageries
|
||||||
|
@ -58,14 +58,14 @@ void ImgurUpload::init()
|
|||||||
|
|
||||||
QNetworkReply* ImgurUpload::getReply(QNetworkRequest& request)
|
QNetworkReply* ImgurUpload::getReply(QNetworkRequest& request)
|
||||||
{
|
{
|
||||||
auto file = new QFile(m_fileInfo.absoluteFilePath());
|
auto file = new QFile(m_fileInfo.absoluteFilePath(), this);
|
||||||
|
|
||||||
if (!file->open(QFile::ReadOnly)) {
|
if (!file->open(QFile::ReadOnly)) {
|
||||||
emitFailed();
|
emitFailed();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType, this);
|
||||||
file->setParent(multipart);
|
file->setParent(multipart);
|
||||||
QHttpPart filePart;
|
QHttpPart filePart;
|
||||||
filePart.setBodyDevice(file);
|
filePart.setBodyDevice(file);
|
||||||
|
@ -54,6 +54,7 @@ bool INIFile::saveFile(QString fileName)
|
|||||||
insert("ConfigVersion", "1.2");
|
insert("ConfigVersion", "1.2");
|
||||||
QSettings _settings_obj{ fileName, QSettings::Format::IniFormat };
|
QSettings _settings_obj{ fileName, QSettings::Format::IniFormat };
|
||||||
_settings_obj.setFallbacksEnabled(false);
|
_settings_obj.setFallbacksEnabled(false);
|
||||||
|
_settings_obj.clear();
|
||||||
|
|
||||||
for (Iterator iter = begin(); iter != end(); iter++)
|
for (Iterator iter = begin(); iter != end(); iter++)
|
||||||
_settings_obj.setValue(iter.key(), iter.value());
|
_settings_obj.setValue(iter.key(), iter.value());
|
||||||
|
@ -35,7 +35,6 @@
|
|||||||
*/
|
*/
|
||||||
#include "ConcurrentTask.h"
|
#include "ConcurrentTask.h"
|
||||||
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include "tasks/Task.h"
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
@ -47,9 +46,9 @@ ConcurrentTask::ConcurrentTask(QObject* parent, QString task_name, int max_concu
|
|||||||
|
|
||||||
ConcurrentTask::~ConcurrentTask()
|
ConcurrentTask::~ConcurrentTask()
|
||||||
{
|
{
|
||||||
for (auto task : m_queue) {
|
for (auto task : m_doing) {
|
||||||
if (task)
|
if (task)
|
||||||
task->deleteLater();
|
task->disconnect(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,15 +64,13 @@ void ConcurrentTask::addTask(Task::Ptr task)
|
|||||||
|
|
||||||
void ConcurrentTask::executeTask()
|
void ConcurrentTask::executeTask()
|
||||||
{
|
{
|
||||||
// Start one task, startNext handles starting the up to the m_total_max_size
|
for (auto i = 0; i < m_total_max_size; i++)
|
||||||
// while tracking the number currently being done
|
QMetaObject::invokeMethod(this, &ConcurrentTask::executeNextSubTask, Qt::QueuedConnection);
|
||||||
QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConcurrentTask::abort()
|
bool ConcurrentTask::abort()
|
||||||
{
|
{
|
||||||
m_queue.clear();
|
m_queue.clear();
|
||||||
m_aborted = true;
|
|
||||||
|
|
||||||
if (m_doing.isEmpty()) {
|
if (m_doing.isEmpty()) {
|
||||||
// Don't call emitAborted() here, we want to bypass the 'is the task running' check
|
// Don't call emitAborted() here, we want to bypass the 'is the task running' check
|
||||||
@ -108,29 +105,36 @@ void ConcurrentTask::clear()
|
|||||||
m_failed.clear();
|
m_failed.clear();
|
||||||
m_queue.clear();
|
m_queue.clear();
|
||||||
|
|
||||||
m_aborted = false;
|
|
||||||
|
|
||||||
m_progress = 0;
|
m_progress = 0;
|
||||||
m_stepProgress = 0;
|
m_stepProgress = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConcurrentTask::startNext()
|
void ConcurrentTask::executeNextSubTask()
|
||||||
{
|
{
|
||||||
if (m_aborted || m_doing.count() > m_total_max_size)
|
if (!isRunning()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
if (m_queue.isEmpty() && m_doing.isEmpty() && !wasSuccessful()) {
|
if (m_doing.count() >= m_total_max_size) {
|
||||||
emitSucceeded();
|
return;
|
||||||
|
}
|
||||||
|
if (m_queue.isEmpty()) {
|
||||||
|
if (m_doing.isEmpty()) {
|
||||||
|
if (m_failed.isEmpty())
|
||||||
|
emitSucceeded();
|
||||||
|
else
|
||||||
|
emitFailed(tr("One or more subtasks failed"));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_queue.isEmpty())
|
startSubTask(m_queue.dequeue());
|
||||||
return;
|
}
|
||||||
|
|
||||||
Task::Ptr next = m_queue.dequeue();
|
|
||||||
|
|
||||||
|
void ConcurrentTask::startSubTask(Task::Ptr next)
|
||||||
|
{
|
||||||
connect(next.get(), &Task::succeeded, this, [this, next]() { subTaskSucceeded(next); });
|
connect(next.get(), &Task::succeeded, this, [this, next]() { subTaskSucceeded(next); });
|
||||||
connect(next.get(), &Task::failed, this, [this, next](QString msg) { subTaskFailed(next, msg); });
|
connect(next.get(), &Task::failed, this, [this, next](QString msg) { subTaskFailed(next, msg); });
|
||||||
|
// this should never happen but if it does, it's better to fail the task than get stuck
|
||||||
connect(next.get(), &Task::aborted, this, [this, next] { subTaskFailed(next, "Aborted"); });
|
connect(next.get(), &Task::aborted, this, [this, next] { subTaskFailed(next, "Aborted"); });
|
||||||
|
|
||||||
connect(next.get(), &Task::status, this, [this, next](QString msg) { subTaskStatus(next, msg); });
|
connect(next.get(), &Task::status, this, [this, next](QString msg) { subTaskStatus(next, msg); });
|
||||||
@ -140,55 +144,42 @@ void ConcurrentTask::startNext()
|
|||||||
connect(next.get(), &Task::progress, this, [this, next](qint64 current, qint64 total) { subTaskProgress(next, current, total); });
|
connect(next.get(), &Task::progress, this, [this, next](qint64 current, qint64 total) { subTaskProgress(next, current, total); });
|
||||||
|
|
||||||
m_doing.insert(next.get(), next);
|
m_doing.insert(next.get(), next);
|
||||||
qsizetype num_starts = qMin(m_queue.size(), m_total_max_size - m_doing.size());
|
|
||||||
auto task_progress = std::make_shared<TaskStepProgress>(next->getUid());
|
auto task_progress = std::make_shared<TaskStepProgress>(next->getUid());
|
||||||
m_task_progress.insert(next->getUid(), task_progress);
|
m_task_progress.insert(next->getUid(), task_progress);
|
||||||
|
|
||||||
updateState();
|
updateState();
|
||||||
updateStepProgress(*task_progress.get(), Operation::ADDED);
|
updateStepProgress(*task_progress.get(), Operation::ADDED);
|
||||||
|
|
||||||
QCoreApplication::processEvents();
|
|
||||||
|
|
||||||
QMetaObject::invokeMethod(next.get(), &Task::start, Qt::QueuedConnection);
|
QMetaObject::invokeMethod(next.get(), &Task::start, Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
// Allow going up the number of concurrent tasks in case of tasks being added in the middle of a running task.
|
void ConcurrentTask::subTaskFinished(Task::Ptr task, TaskStepState state)
|
||||||
for (int i = 0; i < num_starts; i++)
|
{
|
||||||
QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection);
|
m_done.insert(task.get(), task);
|
||||||
|
(state == TaskStepState::Succeeded ? m_succeeded : m_failed).insert(task.get(), task);
|
||||||
|
|
||||||
|
m_doing.remove(task.get());
|
||||||
|
|
||||||
|
auto task_progress = m_task_progress.value(task->getUid());
|
||||||
|
task_progress->state = state;
|
||||||
|
|
||||||
|
disconnect(task.get(), 0, this, 0);
|
||||||
|
|
||||||
|
emit stepProgress(*task_progress);
|
||||||
|
updateState();
|
||||||
|
updateStepProgress(*task_progress, Operation::REMOVED);
|
||||||
|
QMetaObject::invokeMethod(this, &ConcurrentTask::executeNextSubTask, Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConcurrentTask::subTaskSucceeded(Task::Ptr task)
|
void ConcurrentTask::subTaskSucceeded(Task::Ptr task)
|
||||||
{
|
{
|
||||||
m_done.insert(task.get(), task);
|
subTaskFinished(task, TaskStepState::Succeeded);
|
||||||
m_succeeded.insert(task.get(), task);
|
|
||||||
|
|
||||||
m_doing.remove(task.get());
|
|
||||||
auto task_progress = m_task_progress.value(task->getUid());
|
|
||||||
task_progress->state = TaskStepState::Succeeded;
|
|
||||||
|
|
||||||
disconnect(task.get(), 0, this, 0);
|
|
||||||
|
|
||||||
emit stepProgress(*task_progress);
|
|
||||||
updateState();
|
|
||||||
updateStepProgress(*task_progress, Operation::REMOVED);
|
|
||||||
startNext();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConcurrentTask::subTaskFailed(Task::Ptr task, [[maybe_unused]] const QString& msg)
|
void ConcurrentTask::subTaskFailed(Task::Ptr task, [[maybe_unused]] const QString& msg)
|
||||||
{
|
{
|
||||||
m_done.insert(task.get(), task);
|
subTaskFinished(task, TaskStepState::Failed);
|
||||||
m_failed.insert(task.get(), task);
|
|
||||||
|
|
||||||
m_doing.remove(task.get());
|
|
||||||
|
|
||||||
auto task_progress = m_task_progress.value(task->getUid());
|
|
||||||
task_progress->state = TaskStepState::Failed;
|
|
||||||
|
|
||||||
disconnect(task.get(), 0, this, 0);
|
|
||||||
|
|
||||||
emit stepProgress(*task_progress);
|
|
||||||
updateState();
|
|
||||||
updateStepProgress(*task_progress, Operation::REMOVED);
|
|
||||||
startNext();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConcurrentTask::subTaskStatus(Task::Ptr task, const QString& msg)
|
void ConcurrentTask::subTaskStatus(Task::Ptr task, const QString& msg)
|
||||||
|
@ -72,10 +72,11 @@ class ConcurrentTask : public Task {
|
|||||||
protected slots:
|
protected slots:
|
||||||
void executeTask() override;
|
void executeTask() override;
|
||||||
|
|
||||||
virtual void startNext();
|
virtual void executeNextSubTask();
|
||||||
|
|
||||||
void subTaskSucceeded(Task::Ptr);
|
void subTaskSucceeded(Task::Ptr);
|
||||||
void subTaskFailed(Task::Ptr, const QString& msg);
|
virtual void subTaskFailed(Task::Ptr, const QString& msg);
|
||||||
|
void subTaskFinished(Task::Ptr, TaskStepState);
|
||||||
void subTaskStatus(Task::Ptr task, const QString& msg);
|
void subTaskStatus(Task::Ptr task, const QString& msg);
|
||||||
void subTaskDetails(Task::Ptr task, const QString& msg);
|
void subTaskDetails(Task::Ptr task, const QString& msg);
|
||||||
void subTaskProgress(Task::Ptr task, qint64 current, qint64 total);
|
void subTaskProgress(Task::Ptr task, qint64 current, qint64 total);
|
||||||
@ -90,6 +91,8 @@ class ConcurrentTask : public Task {
|
|||||||
|
|
||||||
virtual void updateState();
|
virtual void updateState();
|
||||||
|
|
||||||
|
void startSubTask(Task::Ptr task);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString m_name;
|
QString m_name;
|
||||||
QString m_step_status;
|
QString m_step_status;
|
||||||
@ -107,6 +110,4 @@ class ConcurrentTask : public Task {
|
|||||||
|
|
||||||
qint64 m_stepProgress = 0;
|
qint64 m_stepProgress = 0;
|
||||||
qint64 m_stepTotalProgress = 100;
|
qint64 m_stepTotalProgress = 100;
|
||||||
|
|
||||||
bool m_aborted = false;
|
|
||||||
};
|
};
|
||||||
|
@ -36,9 +36,9 @@
|
|||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
MultipleOptionsTask::MultipleOptionsTask(QObject* parent, const QString& task_name) : SequentialTask(parent, task_name) {}
|
MultipleOptionsTask::MultipleOptionsTask(QObject* parent, const QString& task_name) : ConcurrentTask(parent, task_name, 1) {}
|
||||||
|
|
||||||
void MultipleOptionsTask::startNext()
|
void MultipleOptionsTask::executeNextSubTask()
|
||||||
{
|
{
|
||||||
if (m_done.size() != m_failed.size()) {
|
if (m_done.size() != m_failed.size()) {
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
@ -51,7 +51,7 @@ void MultipleOptionsTask::startNext()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConcurrentTask::startNext();
|
ConcurrentTask::executeNextSubTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultipleOptionsTask::updateState()
|
void MultipleOptionsTask::updateState()
|
||||||
|
@ -34,18 +34,18 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "SequentialTask.h"
|
#include "ConcurrentTask.h"
|
||||||
|
|
||||||
/* This task type will attempt to do run each of it's subtasks in sequence,
|
/* This task type will attempt to do run each of it's subtasks in sequence,
|
||||||
* until one of them succeeds. When that happens, the remaining tasks will not run.
|
* until one of them succeeds. When that happens, the remaining tasks will not run.
|
||||||
* */
|
* */
|
||||||
class MultipleOptionsTask : public SequentialTask {
|
class MultipleOptionsTask : public ConcurrentTask {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit MultipleOptionsTask(QObject* parent = nullptr, const QString& task_name = "");
|
explicit MultipleOptionsTask(QObject* parent = nullptr, const QString& task_name = "");
|
||||||
~MultipleOptionsTask() override = default;
|
~MultipleOptionsTask() override = default;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void startNext() override;
|
void executeNextSubTask() override;
|
||||||
void updateState() override;
|
void updateState() override;
|
||||||
};
|
};
|
||||||
|
@ -36,18 +36,15 @@
|
|||||||
#include "SequentialTask.h"
|
#include "SequentialTask.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include "tasks/ConcurrentTask.h"
|
||||||
|
|
||||||
SequentialTask::SequentialTask(QObject* parent, QString task_name) : ConcurrentTask(parent, task_name, 1) {}
|
SequentialTask::SequentialTask(QObject* parent, QString task_name) : ConcurrentTask(parent, task_name, 1) {}
|
||||||
|
|
||||||
void SequentialTask::startNext()
|
void SequentialTask::subTaskFailed(Task::Ptr task, const QString& msg)
|
||||||
{
|
{
|
||||||
if (m_failed.size() > 0) {
|
emitFailed(msg);
|
||||||
emitFailed(tr("One of the tasks failed!"));
|
qWarning() << msg;
|
||||||
qWarning() << m_failed.constBegin()->get()->failReason();
|
ConcurrentTask::subTaskFailed(task, msg);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConcurrentTask::startNext();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SequentialTask::updateState()
|
void SequentialTask::updateState()
|
||||||
|
@ -50,7 +50,9 @@ class SequentialTask : public ConcurrentTask {
|
|||||||
explicit SequentialTask(QObject* parent = nullptr, QString task_name = "");
|
explicit SequentialTask(QObject* parent = nullptr, QString task_name = "");
|
||||||
~SequentialTask() override = default;
|
~SequentialTask() override = default;
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
virtual void subTaskFailed(Task::Ptr, const QString& msg) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void startNext() override;
|
|
||||||
void updateState() override;
|
void updateState() override;
|
||||||
};
|
};
|
||||||
|
@ -185,6 +185,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||||||
|
|
||||||
ui->instanceToolBar->addContextMenuAction(ui->newsToolBar->toggleViewAction());
|
ui->instanceToolBar->addContextMenuAction(ui->newsToolBar->toggleViewAction());
|
||||||
ui->instanceToolBar->addContextMenuAction(ui->instanceToolBar->toggleViewAction());
|
ui->instanceToolBar->addContextMenuAction(ui->instanceToolBar->toggleViewAction());
|
||||||
|
ui->instanceToolBar->addContextMenuAction(ui->actionToggleStatusBar);
|
||||||
ui->instanceToolBar->addContextMenuAction(ui->actionLockToolbars);
|
ui->instanceToolBar->addContextMenuAction(ui->actionLockToolbars);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +230,8 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||||||
setInstanceActionsEnabled(false);
|
setInstanceActionsEnabled(false);
|
||||||
|
|
||||||
// add a close button at the end of the main toolbar when running on gamescope / steam deck
|
// add a close button at the end of the main toolbar when running on gamescope / steam deck
|
||||||
// FIXME: detect if we don't have server side decorations instead
|
// this is only needed on gamescope because it defaults to an X11/XWayland session and
|
||||||
|
// does not implement decorations
|
||||||
if (qgetenv("XDG_CURRENT_DESKTOP") == "gamescope") {
|
if (qgetenv("XDG_CURRENT_DESKTOP") == "gamescope") {
|
||||||
ui->mainToolBar->addAction(ui->actionCloseWindow);
|
ui->mainToolBar->addAction(ui->actionCloseWindow);
|
||||||
}
|
}
|
||||||
@ -318,6 +320,14 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||||||
setCatBackground(cat_enable);
|
setCatBackground(cat_enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Togglable status bar
|
||||||
|
{
|
||||||
|
bool statusBarVisible = APPLICATION->settings()->get("StatusBarVisible").toBool();
|
||||||
|
ui->actionToggleStatusBar->setChecked(statusBarVisible);
|
||||||
|
connect(ui->actionToggleStatusBar, &QAction::toggled, this, &MainWindow::setStatusBarVisibility);
|
||||||
|
setStatusBarVisibility(statusBarVisible);
|
||||||
|
}
|
||||||
|
|
||||||
// Lock toolbars
|
// Lock toolbars
|
||||||
{
|
{
|
||||||
bool toolbarsLocked = APPLICATION->settings()->get("ToolbarsLocked").toBool();
|
bool toolbarsLocked = APPLICATION->settings()->get("ToolbarsLocked").toBool();
|
||||||
@ -450,10 +460,16 @@ QMenu* MainWindow::createPopupMenu()
|
|||||||
QMenu* filteredMenu = QMainWindow::createPopupMenu();
|
QMenu* filteredMenu = QMainWindow::createPopupMenu();
|
||||||
filteredMenu->removeAction(ui->mainToolBar->toggleViewAction());
|
filteredMenu->removeAction(ui->mainToolBar->toggleViewAction());
|
||||||
|
|
||||||
|
filteredMenu->addAction(ui->actionToggleStatusBar);
|
||||||
filteredMenu->addAction(ui->actionLockToolbars);
|
filteredMenu->addAction(ui->actionLockToolbars);
|
||||||
|
|
||||||
return filteredMenu;
|
return filteredMenu;
|
||||||
}
|
}
|
||||||
|
void MainWindow::setStatusBarVisibility(bool state)
|
||||||
|
{
|
||||||
|
statusBar()->setVisible(state);
|
||||||
|
APPLICATION->settings()->set("StatusBarVisible", state);
|
||||||
|
}
|
||||||
void MainWindow::lockToolbars(bool state)
|
void MainWindow::lockToolbars(bool state)
|
||||||
{
|
{
|
||||||
ui->mainToolBar->setMovable(!state);
|
ui->mainToolBar->setMovable(!state);
|
||||||
@ -1181,18 +1197,18 @@ void MainWindow::undoTrashInstance()
|
|||||||
|
|
||||||
void MainWindow::on_actionViewLauncherRootFolder_triggered()
|
void MainWindow::on_actionViewLauncherRootFolder_triggered()
|
||||||
{
|
{
|
||||||
DesktopServices::openDirectory(".");
|
DesktopServices::openPath(".");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionViewInstanceFolder_triggered()
|
void MainWindow::on_actionViewInstanceFolder_triggered()
|
||||||
{
|
{
|
||||||
QString str = APPLICATION->settings()->get("InstanceDir").toString();
|
QString str = APPLICATION->settings()->get("InstanceDir").toString();
|
||||||
DesktopServices::openDirectory(str);
|
DesktopServices::openPath(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionViewCentralModsFolder_triggered()
|
void MainWindow::on_actionViewCentralModsFolder_triggered()
|
||||||
{
|
{
|
||||||
DesktopServices::openDirectory(APPLICATION->settings()->get("CentralModsDir").toString(), true);
|
DesktopServices::openPath(APPLICATION->settings()->get("CentralModsDir").toString(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionViewSkinsFolder_triggered()
|
void MainWindow::on_actionViewSkinsFolder_triggered()
|
||||||
@ -1202,27 +1218,27 @@ void MainWindow::on_actionViewSkinsFolder_triggered()
|
|||||||
|
|
||||||
void MainWindow::on_actionViewIconThemeFolder_triggered()
|
void MainWindow::on_actionViewIconThemeFolder_triggered()
|
||||||
{
|
{
|
||||||
DesktopServices::openDirectory(APPLICATION->themeManager()->getIconThemesFolder().path(), true);
|
DesktopServices::openPath(APPLICATION->themeManager()->getIconThemesFolder().path(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionViewWidgetThemeFolder_triggered()
|
void MainWindow::on_actionViewWidgetThemeFolder_triggered()
|
||||||
{
|
{
|
||||||
DesktopServices::openDirectory(APPLICATION->themeManager()->getApplicationThemesFolder().path(), true);
|
DesktopServices::openPath(APPLICATION->themeManager()->getApplicationThemesFolder().path(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionViewCatPackFolder_triggered()
|
void MainWindow::on_actionViewCatPackFolder_triggered()
|
||||||
{
|
{
|
||||||
DesktopServices::openDirectory(APPLICATION->themeManager()->getCatPacksFolder().path(), true);
|
DesktopServices::openPath(APPLICATION->themeManager()->getCatPacksFolder().path(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionViewIconsFolder_triggered()
|
void MainWindow::on_actionViewIconsFolder_triggered()
|
||||||
{
|
{
|
||||||
DesktopServices::openDirectory(APPLICATION->icons()->getDirectory(), true);
|
DesktopServices::openPath(APPLICATION->icons()->getDirectory(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionViewLogsFolder_triggered()
|
void MainWindow::on_actionViewLogsFolder_triggered()
|
||||||
{
|
{
|
||||||
DesktopServices::openDirectory("logs", true);
|
DesktopServices::openPath("logs", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::refreshInstances()
|
void MainWindow::refreshInstances()
|
||||||
@ -1441,7 +1457,7 @@ void MainWindow::on_actionViewSelectedInstFolder_triggered()
|
|||||||
{
|
{
|
||||||
if (m_selectedInstance) {
|
if (m_selectedInstance) {
|
||||||
QString str = m_selectedInstance->instanceRoot();
|
QString str = m_selectedInstance->instanceRoot();
|
||||||
DesktopServices::openDirectory(QDir(str).absolutePath());
|
DesktopServices::openPath(QFileInfo(str));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,6 +207,8 @@ class MainWindow : public QMainWindow {
|
|||||||
|
|
||||||
void globalSettingsClosed();
|
void globalSettingsClosed();
|
||||||
|
|
||||||
|
void setStatusBarVisibility(bool);
|
||||||
|
|
||||||
void lockToolbars(bool);
|
void lockToolbars(bool);
|
||||||
|
|
||||||
#ifndef Q_OS_MAC
|
#ifndef Q_OS_MAC
|
||||||
|
@ -176,6 +176,7 @@
|
|||||||
<addaction name="actionChangeTheme"/>
|
<addaction name="actionChangeTheme"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionCAT"/>
|
<addaction name="actionCAT"/>
|
||||||
|
<addaction name="actionToggleStatusBar"/>
|
||||||
<addaction name="actionLockToolbars"/>
|
<addaction name="actionLockToolbars"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
</widget>
|
</widget>
|
||||||
@ -258,6 +259,14 @@
|
|||||||
<string>It's a fluffy kitty :3</string>
|
<string>It's a fluffy kitty :3</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionToggleStatusBar">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Status Bar</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="actionLockToolbars">
|
<action name="actionLockToolbars">
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
@ -174,8 +174,7 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDia
|
|||||||
QString urlText("<html><head/><body><p><a href=\"%1\">%1</a></p></body></html>");
|
QString urlText("<html><head/><body><p><a href=\"%1\">%1</a></p></body></html>");
|
||||||
ui->urlLabel->setText(urlText.arg(BuildConfig.LAUNCHER_GIT));
|
ui->urlLabel->setText(urlText.arg(BuildConfig.LAUNCHER_GIT));
|
||||||
|
|
||||||
QString copyText("© 2022-2023 %1");
|
ui->copyLabel->setText(BuildConfig.LAUNCHER_COPYRIGHT);
|
||||||
ui->copyLabel->setText(copyText.arg(BuildConfig.LAUNCHER_COPYRIGHT));
|
|
||||||
|
|
||||||
connect(ui->closeButton, SIGNAL(clicked()), SLOT(close()));
|
connect(ui->closeButton, SIGNAL(clicked()), SLOT(close()));
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <net/NetJob.h>
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
@ -31,7 +30,4 @@ class AboutDialog : public QDialog {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::AboutDialog* ui;
|
Ui::AboutDialog* ui;
|
||||||
|
|
||||||
NetJob::Ptr netJob;
|
|
||||||
QByteArray dataSink;
|
|
||||||
};
|
};
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, const QString& text, QList<BlockedMod>& mods, QString hash_type)
|
BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, const QString& text, QList<BlockedMod>& mods, QString hash_type)
|
||||||
: QDialog(parent), ui(new Ui::BlockedModsDialog), m_mods(mods), m_hash_type(hash_type)
|
: QDialog(parent), ui(new Ui::BlockedModsDialog), m_mods(mods), m_hash_type(hash_type)
|
||||||
@ -60,8 +61,13 @@ BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, cons
|
|||||||
|
|
||||||
qDebug() << "[Blocked Mods Dialog] Mods List: " << mods;
|
qDebug() << "[Blocked Mods Dialog] Mods List: " << mods;
|
||||||
|
|
||||||
setupWatch();
|
// defer setup of file system watchers until after the dialog is shown
|
||||||
scanPaths();
|
// this allows OS (namely macOS) permission prompts to show after the relevant dialog appears
|
||||||
|
QTimer::singleShot(0, this, [this] {
|
||||||
|
setupWatch();
|
||||||
|
scanPaths();
|
||||||
|
update();
|
||||||
|
});
|
||||||
|
|
||||||
this->setWindowTitle(title);
|
this->setWindowTitle(title);
|
||||||
ui->labelDescription->setText(text);
|
ui->labelDescription->setText(text);
|
||||||
@ -158,7 +164,8 @@ void BlockedModsDialog::update()
|
|||||||
|
|
||||||
QString watching;
|
QString watching;
|
||||||
for (auto& dir : m_watcher.directories()) {
|
for (auto& dir : m_watcher.directories()) {
|
||||||
watching += QString("<a href=\"%1\">%1</a><br/>").arg(dir);
|
QUrl fileURL = QUrl::fromLocalFile(dir);
|
||||||
|
watching += QString("<a href=\"%1\">%2</a><br/>").arg(fileURL.toString(), dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
ui->textBrowserWatched->setText(watching);
|
ui->textBrowserWatched->setText(watching);
|
||||||
@ -194,6 +201,10 @@ void BlockedModsDialog::setupWatch()
|
|||||||
void BlockedModsDialog::watchPath(QString path, bool watch_recursive)
|
void BlockedModsDialog::watchPath(QString path, bool watch_recursive)
|
||||||
{
|
{
|
||||||
auto to_watch = QFileInfo(path);
|
auto to_watch = QFileInfo(path);
|
||||||
|
if (!to_watch.isReadable()) {
|
||||||
|
qWarning() << "[Blocked Mods Dialog] Failed to add Watch Path (unable to read):" << path;
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto to_watch_path = to_watch.canonicalFilePath();
|
auto to_watch_path = to_watch.canonicalFilePath();
|
||||||
if (m_watcher.directories().contains(to_watch_path))
|
if (m_watcher.directories().contains(to_watch_path))
|
||||||
return; // don't watch the same path twice (no loops!)
|
return; // don't watch the same path twice (no loops!)
|
||||||
|
@ -146,7 +146,7 @@ void ExportInstanceDialog::doExport()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto task = makeShared<MMCZip::ExportToZipTask>(output, m_instance->instanceRoot(), files, "", true);
|
auto task = makeShared<MMCZip::ExportToZipTask>(output, m_instance->instanceRoot(), files, "", true, true);
|
||||||
|
|
||||||
connect(task.get(), &Task::failed, this,
|
connect(task.get(), &Task::failed, this,
|
||||||
[this, output](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });
|
[this, output](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });
|
||||||
|
@ -47,11 +47,18 @@ ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPla
|
|||||||
|
|
||||||
if (m_provider == ModPlatform::ResourceProvider::MODRINTH) {
|
if (m_provider == ModPlatform::ResourceProvider::MODRINTH) {
|
||||||
setWindowTitle(tr("Export Modrinth Pack"));
|
setWindowTitle(tr("Export Modrinth Pack"));
|
||||||
ui->summary->setText(instance->settings()->get("ExportSummary").toString());
|
|
||||||
|
ui->authorLabel->hide();
|
||||||
|
ui->author->hide();
|
||||||
|
|
||||||
|
ui->summary->setPlainText(instance->settings()->get("ExportSummary").toString());
|
||||||
} else {
|
} else {
|
||||||
setWindowTitle(tr("Export CurseForge Pack"));
|
setWindowTitle(tr("Export CurseForge Pack"));
|
||||||
ui->summaryLabel->setText(tr("&Author"));
|
|
||||||
ui->summary->setText(instance->settings()->get("ExportAuthor").toString());
|
ui->summaryLabel->hide();
|
||||||
|
ui->summary->hide();
|
||||||
|
|
||||||
|
ui->author->setText(instance->settings()->get("ExportAuthor").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure a valid pack is generated
|
// ensure a valid pack is generated
|
||||||
@ -108,9 +115,13 @@ void ExportPackDialog::done(int result)
|
|||||||
auto settings = instance->settings();
|
auto settings = instance->settings();
|
||||||
settings->set("ExportName", ui->name->text());
|
settings->set("ExportName", ui->name->text());
|
||||||
settings->set("ExportVersion", ui->version->text());
|
settings->set("ExportVersion", ui->version->text());
|
||||||
settings->set(m_provider == ModPlatform::ResourceProvider::FLAME ? "ExportAuthor" : "ExportSummary", ui->summary->text());
|
|
||||||
settings->set("ExportOptionalFiles", ui->optionalFiles->isChecked());
|
settings->set("ExportOptionalFiles", ui->optionalFiles->isChecked());
|
||||||
|
|
||||||
|
if (m_provider == ModPlatform::ResourceProvider::MODRINTH)
|
||||||
|
settings->set("ExportSummary", ui->summary->toPlainText());
|
||||||
|
else
|
||||||
|
settings->set("ExportAuthor", ui->author->text());
|
||||||
|
|
||||||
if (result == Accepted) {
|
if (result == Accepted) {
|
||||||
const QString name = ui->name->text().isEmpty() ? instance->name() : ui->name->text();
|
const QString name = ui->name->text().isEmpty() ? instance->name() : ui->name->text();
|
||||||
const QString filename = FS::RemoveInvalidFilenameChars(name);
|
const QString filename = FS::RemoveInvalidFilenameChars(name);
|
||||||
@ -134,10 +145,10 @@ void ExportPackDialog::done(int result)
|
|||||||
|
|
||||||
Task* task;
|
Task* task;
|
||||||
if (m_provider == ModPlatform::ResourceProvider::MODRINTH) {
|
if (m_provider == ModPlatform::ResourceProvider::MODRINTH) {
|
||||||
task = new ModrinthPackExportTask(name, ui->version->text(), ui->summary->text(), ui->optionalFiles->isChecked(), instance,
|
task = new ModrinthPackExportTask(name, ui->version->text(), ui->summary->toPlainText(), ui->optionalFiles->isChecked(),
|
||||||
output, std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1));
|
instance, output, std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1));
|
||||||
} else {
|
} else {
|
||||||
task = new FlamePackExportTask(name, ui->version->text(), ui->summary->text(), ui->optionalFiles->isChecked(), instance, output,
|
task = new FlamePackExportTask(name, ui->version->text(), ui->author->text(), ui->optionalFiles->isChecked(), instance, output,
|
||||||
std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1));
|
std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>650</width>
|
<width>650</width>
|
||||||
<height>510</height>
|
<height>532</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeGripEnabled">
|
<property name="sizeGripEnabled">
|
||||||
@ -19,21 +19,8 @@
|
|||||||
<property name="title">
|
<property name="title">
|
||||||
<string>&Description</string>
|
<string>&Description</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
<item row="3" column="0">
|
<item>
|
||||||
<widget class="QLabel" name="summaryLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Summary</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>summary</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QLineEdit" name="summary"/>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="nameLabel">
|
<widget class="QLabel" name="nameLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Name</string>
|
<string>&Name</string>
|
||||||
@ -43,7 +30,10 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item>
|
||||||
|
<widget class="QLineEdit" name="name"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
<widget class="QLabel" name="versionLabel">
|
<widget class="QLabel" name="versionLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Version</string>
|
<string>&Version</string>
|
||||||
@ -53,16 +43,43 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item>
|
||||||
<widget class="QLineEdit" name="name"/>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QLineEdit" name="version">
|
<widget class="QLineEdit" name="version">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>1.0.0</string>
|
<string>1.0.0</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="summaryLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Summary</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>summary</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPlainTextEdit" name="summary">
|
||||||
|
<property name="tabChangesFocus">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="authorLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Author</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>author</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="author"/>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -124,6 +141,7 @@
|
|||||||
<tabstop>name</tabstop>
|
<tabstop>name</tabstop>
|
||||||
<tabstop>version</tabstop>
|
<tabstop>version</tabstop>
|
||||||
<tabstop>summary</tabstop>
|
<tabstop>summary</tabstop>
|
||||||
|
<tabstop>author</tabstop>
|
||||||
<tabstop>files</tabstop>
|
<tabstop>files</tabstop>
|
||||||
<tabstop>optionalFiles</tabstop>
|
<tabstop>optionalFiles</tabstop>
|
||||||
</tabstops>
|
</tabstops>
|
||||||
|
@ -159,5 +159,5 @@ IconPickerDialog::~IconPickerDialog()
|
|||||||
|
|
||||||
void IconPickerDialog::openFolder()
|
void IconPickerDialog::openFolder()
|
||||||
{
|
{
|
||||||
DesktopServices::openDirectory(APPLICATION->icons()->getDirectory(), true);
|
DesktopServices::openPath(APPLICATION->icons()->getDirectory(), true);
|
||||||
}
|
}
|
||||||
|
@ -214,19 +214,25 @@ void ModUpdateDialog::checkCandidates()
|
|||||||
}
|
}
|
||||||
static FlameAPI api;
|
static FlameAPI api;
|
||||||
|
|
||||||
auto getRequiredBy = depTask->getRequiredBy();
|
auto dependencyExtraInfo = depTask->getExtraInfo();
|
||||||
|
|
||||||
for (auto dep : depTask->getDependecies()) {
|
for (auto dep : depTask->getDependecies()) {
|
||||||
auto changelog = dep->version.changelog;
|
auto changelog = dep->version.changelog;
|
||||||
if (dep->pack->provider == ModPlatform::ResourceProvider::FLAME)
|
if (dep->pack->provider == ModPlatform::ResourceProvider::FLAME)
|
||||||
changelog = api.getModFileChangelog(dep->version.addonId.toInt(), dep->version.fileId.toInt());
|
changelog = api.getModFileChangelog(dep->version.addonId.toInt(), dep->version.fileId.toInt());
|
||||||
auto download_task = makeShared<ResourceDownloadTask>(dep->pack, dep->version, m_mod_model);
|
auto download_task = makeShared<ResourceDownloadTask>(dep->pack, dep->version, m_mod_model);
|
||||||
CheckUpdateTask::UpdatableMod updatable = {
|
auto extraInfo = dependencyExtraInfo.value(dep->version.addonId.toString());
|
||||||
dep->pack->name, dep->version.hash, "", dep->version.version, dep->version.version_type,
|
CheckUpdateTask::UpdatableMod updatable = { dep->pack->name,
|
||||||
changelog, dep->pack->provider, download_task
|
dep->version.hash,
|
||||||
};
|
"",
|
||||||
|
dep->version.version,
|
||||||
|
dep->version.version_type,
|
||||||
|
changelog,
|
||||||
|
dep->pack->provider,
|
||||||
|
download_task,
|
||||||
|
!extraInfo.maybe_installed };
|
||||||
|
|
||||||
appendMod(updatable, getRequiredBy.value(dep->version.addonId.toString()));
|
appendMod(updatable, extraInfo.required_by);
|
||||||
m_tasks.insert(updatable.name, updatable.download);
|
m_tasks.insert(updatable.name, updatable.download);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -328,6 +334,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool
|
|||||||
connect(modrinth_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) {
|
connect(modrinth_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) {
|
||||||
onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::MODRINTH);
|
onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::MODRINTH);
|
||||||
});
|
});
|
||||||
|
connect(modrinth_task.get(), &EnsureMetadataTask::failed,
|
||||||
|
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
||||||
|
|
||||||
if (modrinth_task->getHashingTask())
|
if (modrinth_task->getHashingTask())
|
||||||
seq.addTask(modrinth_task->getHashingTask());
|
seq.addTask(modrinth_task->getHashingTask());
|
||||||
@ -341,6 +349,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool
|
|||||||
connect(flame_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) {
|
connect(flame_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) {
|
||||||
onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::FLAME);
|
onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::FLAME);
|
||||||
});
|
});
|
||||||
|
connect(flame_task.get(), &EnsureMetadataTask::failed,
|
||||||
|
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
||||||
|
|
||||||
if (flame_task->getHashingTask())
|
if (flame_task->getHashingTask())
|
||||||
seq.addTask(flame_task->getHashingTask());
|
seq.addTask(flame_task->getHashingTask());
|
||||||
@ -394,6 +404,8 @@ void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::R
|
|||||||
auto task = makeShared<EnsureMetadataTask>(mod, index_dir, next(first_choice));
|
auto task = makeShared<EnsureMetadataTask>(mod, index_dir, next(first_choice));
|
||||||
connect(task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); });
|
connect(task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); });
|
||||||
connect(task.get(), &EnsureMetadataTask::metadataFailed, [this](Mod* candidate) { onMetadataFailed(candidate, false); });
|
connect(task.get(), &EnsureMetadataTask::metadataFailed, [this](Mod* candidate) { onMetadataFailed(candidate, false); });
|
||||||
|
connect(task.get(), &EnsureMetadataTask::failed,
|
||||||
|
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
||||||
|
|
||||||
m_second_try_metadata->addTask(task);
|
m_second_try_metadata->addTask(task);
|
||||||
} else {
|
} else {
|
||||||
@ -406,7 +418,10 @@ void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::R
|
|||||||
void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStringList requiredBy)
|
void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStringList requiredBy)
|
||||||
{
|
{
|
||||||
auto item_top = new QTreeWidgetItem(ui->modTreeWidget);
|
auto item_top = new QTreeWidgetItem(ui->modTreeWidget);
|
||||||
item_top->setCheckState(0, Qt::CheckState::Checked);
|
item_top->setCheckState(0, info.enabled ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
|
||||||
|
if (!info.enabled) {
|
||||||
|
item_top->setToolTip(0, tr("Mod was disabled as it may be already instaled."));
|
||||||
|
}
|
||||||
item_top->setText(0, info.name);
|
item_top->setText(0, info.name);
|
||||||
item_top->setExpanded(true);
|
item_top->setExpanded(true);
|
||||||
|
|
||||||
@ -437,6 +452,9 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStri
|
|||||||
reqItem->insertChildren(i++, { reqItem });
|
reqItem->insertChildren(i++, { reqItem });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui->toggleDepsButton->show();
|
||||||
|
m_deps << item_top;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto changelog_item = new QTreeWidgetItem(item_top);
|
auto changelog_item = new QTreeWidgetItem(item_top);
|
||||||
|
@ -97,6 +97,9 @@ NewInstanceDialog::NewInstanceDialog(const QString& initialGroup,
|
|||||||
ui->verticalLayout->insertWidget(2, m_container);
|
ui->verticalLayout->insertWidget(2, m_container);
|
||||||
|
|
||||||
m_container->addButtons(m_buttons);
|
m_container->addButtons(m_buttons);
|
||||||
|
connect(m_container, &PageContainer::selectedPageChanged, this, [this](BasePage* previous, BasePage* selected) {
|
||||||
|
m_buttons->button(QDialogButtonBox::Ok)->setEnabled(creationTask && !instName().isEmpty());
|
||||||
|
});
|
||||||
|
|
||||||
// Bonk Qt over its stupid head and make sure it understands which button is the default one...
|
// Bonk Qt over its stupid head and make sure it understands which button is the default one...
|
||||||
// See: https://stackoverflow.com/questions/24556831/qbuttonbox-set-default-button
|
// See: https://stackoverflow.com/questions/24556831/qbuttonbox-set-default-button
|
||||||
|
@ -132,7 +132,7 @@ void ResourceDownloadDialog::confirm()
|
|||||||
auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourcesString()));
|
auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourcesString()));
|
||||||
confirm_dialog->retranslateUi(resourcesString());
|
confirm_dialog->retranslateUi(resourcesString());
|
||||||
|
|
||||||
QHash<QString, QStringList> getRequiredBy;
|
QHash<QString, GetModDependenciesTask::PackDependencyExtraInfo> dependencyExtraInfo;
|
||||||
if (auto task = getModDependenciesTask(); task) {
|
if (auto task = getModDependenciesTask(); task) {
|
||||||
connect(task.get(), &Task::failed, this,
|
connect(task.get(), &Task::failed, this,
|
||||||
[&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
[&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
||||||
@ -157,7 +157,7 @@ void ResourceDownloadDialog::confirm()
|
|||||||
} else {
|
} else {
|
||||||
for (auto dep : task->getDependecies())
|
for (auto dep : task->getDependecies())
|
||||||
addResource(dep->pack, dep->version);
|
addResource(dep->pack, dep->version);
|
||||||
getRequiredBy = task->getRequiredBy();
|
dependencyExtraInfo = task->getExtraInfo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,9 +166,10 @@ void ResourceDownloadDialog::confirm()
|
|||||||
return QString::compare(a->getName(), b->getName(), Qt::CaseInsensitive) < 0;
|
return QString::compare(a->getName(), b->getName(), Qt::CaseInsensitive) < 0;
|
||||||
});
|
});
|
||||||
for (auto& task : selected) {
|
for (auto& task : selected) {
|
||||||
|
auto extraInfo = dependencyExtraInfo.value(task->getPack()->addonId.toString());
|
||||||
confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath(),
|
confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath(),
|
||||||
ProviderCaps.name(task->getProvider()), getRequiredBy.value(task->getPack()->addonId.toString()),
|
ProviderCaps.name(task->getProvider()), extraInfo.required_by,
|
||||||
task->getVersion().version_type.toString() });
|
task->getVersion().version_type.toString(), !extraInfo.maybe_installed });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (confirm_dialog->exec()) {
|
if (confirm_dialog->exec()) {
|
||||||
|
@ -13,6 +13,7 @@ ReviewMessageBox::ReviewMessageBox(QWidget* parent, [[maybe_unused]] QString con
|
|||||||
auto back_button = ui->buttonBox->button(QDialogButtonBox::Cancel);
|
auto back_button = ui->buttonBox->button(QDialogButtonBox::Cancel);
|
||||||
back_button->setText(tr("Back"));
|
back_button->setText(tr("Back"));
|
||||||
|
|
||||||
|
ui->toggleDepsButton->hide();
|
||||||
ui->modTreeWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
ui->modTreeWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||||
ui->modTreeWidget->header()->setStretchLastSection(false);
|
ui->modTreeWidget->header()->setStretchLastSection(false);
|
||||||
ui->modTreeWidget->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
|
ui->modTreeWidget->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
|
||||||
@ -34,8 +35,11 @@ auto ReviewMessageBox::create(QWidget* parent, QString&& title, QString&& icon)
|
|||||||
void ReviewMessageBox::appendResource(ResourceInformation&& info)
|
void ReviewMessageBox::appendResource(ResourceInformation&& info)
|
||||||
{
|
{
|
||||||
auto itemTop = new QTreeWidgetItem(ui->modTreeWidget);
|
auto itemTop = new QTreeWidgetItem(ui->modTreeWidget);
|
||||||
itemTop->setCheckState(0, Qt::CheckState::Checked);
|
itemTop->setCheckState(0, info.enabled ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
|
||||||
itemTop->setText(0, info.name);
|
itemTop->setText(0, info.name);
|
||||||
|
if (!info.enabled) {
|
||||||
|
itemTop->setToolTip(0, tr("Mod was disabled as it may be already instaled."));
|
||||||
|
}
|
||||||
|
|
||||||
auto filenameItem = new QTreeWidgetItem(itemTop);
|
auto filenameItem = new QTreeWidgetItem(itemTop);
|
||||||
filenameItem->setText(0, tr("Filename: %1").arg(info.filename));
|
filenameItem->setText(0, tr("Filename: %1").arg(info.filename));
|
||||||
@ -75,6 +79,8 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
itemTop->insertChildren(childIndx++, { requiredByItem });
|
itemTop->insertChildren(childIndx++, { requiredByItem });
|
||||||
|
ui->toggleDepsButton->show();
|
||||||
|
m_deps << itemTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto versionTypeItem = new QTreeWidgetItem(itemTop);
|
auto versionTypeItem = new QTreeWidgetItem(itemTop);
|
||||||
@ -108,3 +114,10 @@ void ReviewMessageBox::retranslateUi(QString resources_name)
|
|||||||
ui->explainLabel->setText(tr("You're about to download the following %1:").arg(resources_name));
|
ui->explainLabel->setText(tr("You're about to download the following %1:").arg(resources_name));
|
||||||
ui->onlyCheckedLabel->setText(tr("Only %1 with a check will be downloaded!").arg(resources_name));
|
ui->onlyCheckedLabel->setText(tr("Only %1 with a check will be downloaded!").arg(resources_name));
|
||||||
}
|
}
|
||||||
|
void ReviewMessageBox::on_toggleDepsButton_clicked()
|
||||||
|
{
|
||||||
|
m_deps_checked = !m_deps_checked;
|
||||||
|
auto state = m_deps_checked ? Qt::Checked : Qt::Unchecked;
|
||||||
|
for (auto dep : m_deps)
|
||||||
|
dep->setCheckState(0, state);
|
||||||
|
};
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
#include <QTreeWidgetItem>
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class ReviewMessageBox;
|
class ReviewMessageBox;
|
||||||
@ -19,6 +20,7 @@ class ReviewMessageBox : public QDialog {
|
|||||||
QString provider;
|
QString provider;
|
||||||
QStringList required_by;
|
QStringList required_by;
|
||||||
QString version_type;
|
QString version_type;
|
||||||
|
bool enabled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
void appendResource(ResourceInformation&& info);
|
void appendResource(ResourceInformation&& info);
|
||||||
@ -28,8 +30,14 @@ class ReviewMessageBox : public QDialog {
|
|||||||
|
|
||||||
~ReviewMessageBox() override;
|
~ReviewMessageBox() override;
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void on_toggleDepsButton_clicked();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ReviewMessageBox(QWidget* parent, const QString& title, const QString& icon);
|
ReviewMessageBox(QWidget* parent, const QString& title, const QString& icon);
|
||||||
|
|
||||||
Ui::ReviewMessageBox* ui;
|
Ui::ReviewMessageBox* ui;
|
||||||
|
|
||||||
|
QList<QTreeWidgetItem*> m_deps;
|
||||||
|
bool m_deps_checked = true;
|
||||||
};
|
};
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user