mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2025-04-30 22:54:38 +02:00
Compare commits
230 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a58f7bf240 | ||
![]() |
81ea7cef5a | ||
![]() |
0df7a8dd3c | ||
![]() |
c254a8961c | ||
![]() |
2cd69664ab | ||
![]() |
0ea9190d25 | ||
![]() |
580e38a985 | ||
![]() |
8d7f916f80 | ||
![]() |
658244574a | ||
![]() |
119295c87f | ||
![]() |
263b167aae | ||
![]() |
56b64edc44 | ||
![]() |
1a51cb76ad | ||
![]() |
e475f4ed36 | ||
![]() |
0a27d3859c | ||
![]() |
cd6f061335 | ||
![]() |
ab6c5c122e | ||
![]() |
317b127330 | ||
![]() |
354f4ef3d8 | ||
![]() |
8984cd5f66 | ||
![]() |
5934849d87 | ||
![]() |
786a452f28 | ||
![]() |
62a2217291 | ||
![]() |
1af7126582 | ||
![]() |
4d0e4f434f | ||
![]() |
af936353b1 | ||
![]() |
c728f43533 | ||
![]() |
bdf3497028 | ||
![]() |
9c7d1e80e3 | ||
![]() |
616556ea7b | ||
![]() |
d23fa73f52 | ||
![]() |
b2e5ea46d7 | ||
![]() |
dda7e3073d | ||
![]() |
a800b86689 | ||
![]() |
cfeb72450e | ||
![]() |
67e0b68cd5 | ||
![]() |
fbf819c034 | ||
![]() |
be2ecff97c | ||
![]() |
a053a4040c | ||
![]() |
975b0b5761 | ||
![]() |
446740ba4f | ||
![]() |
9808ab9602 | ||
![]() |
865fd91038 | ||
![]() |
0cca84a335 | ||
![]() |
fa2c2a7c85 | ||
![]() |
f43873a8ba | ||
![]() |
6a49f513d8 | ||
![]() |
5d25e21719 | ||
![]() |
da2a67227a | ||
![]() |
875c6bbada | ||
![]() |
401de52f61 | ||
![]() |
0c84d58915 | ||
![]() |
096028f1a6 | ||
![]() |
fa0492f606 | ||
![]() |
e201cad3f8 | ||
![]() |
1d8ee37504 | ||
![]() |
672c8c8b4c | ||
![]() |
87ca2ce303 | ||
![]() |
905d9d2826 | ||
![]() |
ca359330c2 | ||
![]() |
828222eb27 | ||
![]() |
f7cad1110e | ||
![]() |
bffb2a3609 | ||
![]() |
0d3596c864 | ||
![]() |
8eeab0805a | ||
![]() |
8898eb0a19 | ||
![]() |
43286eea91 | ||
![]() |
37af82a3c0 | ||
![]() |
e1555ff74e | ||
![]() |
47e5855527 | ||
![]() |
3328ec970a | ||
![]() |
7caed93d2c | ||
![]() |
c0706b30ee | ||
![]() |
a57a21af3b | ||
![]() |
b679a57939 | ||
![]() |
d3a16715b8 | ||
![]() |
28378c77aa | ||
![]() |
cf87aed7ff | ||
![]() |
d43653605f | ||
![]() |
257297d966 | ||
![]() |
ea0c74b302 | ||
![]() |
c02257024e | ||
![]() |
098f1f1281 | ||
![]() |
119ee7a5b4 | ||
![]() |
87354d9a7d | ||
![]() |
46a9880e2e | ||
![]() |
1daf4ad85f | ||
![]() |
c74099ec7b | ||
![]() |
4cd6ac5fe5 | ||
![]() |
f06bd54d6f | ||
![]() |
0b535e44e1 | ||
![]() |
877de6da40 | ||
![]() |
c330452e84 | ||
![]() |
3f3af4134e | ||
![]() |
565c7d81b3 | ||
![]() |
a532c99aa5 | ||
![]() |
47bd02637a | ||
![]() |
64797b8bff | ||
![]() |
7960282e07 | ||
![]() |
cb0fe08bed | ||
![]() |
160c8cb70d | ||
![]() |
13afbd5699 | ||
![]() |
7b1c5a7e1a | ||
![]() |
1a44258c9d | ||
![]() |
95cfcedc96 | ||
![]() |
5f7a5bfa2d | ||
![]() |
f221b08f11 | ||
![]() |
0af9bde06f | ||
![]() |
d9b133a28d | ||
![]() |
3477b8c2c2 | ||
![]() |
e43341ce92 | ||
![]() |
acfd05a70e | ||
![]() |
b2cca30cd9 | ||
![]() |
3e5e131a94 | ||
![]() |
1e5a29c423 | ||
![]() |
5b168dd7b1 | ||
![]() |
17d5d78a5b | ||
![]() |
58d32ea1af | ||
![]() |
0a3b85dbc5 | ||
![]() |
99203cbd9b | ||
![]() |
d262a081c5 | ||
![]() |
f9df20aae3 | ||
![]() |
fc4b5205dd | ||
![]() |
7c2a9df98e | ||
![]() |
b854c56c8c | ||
![]() |
2c864a4aad | ||
![]() |
4642c13ca4 | ||
![]() |
b255c8b17f | ||
![]() |
c0f14c569b | ||
![]() |
ca3516d26d | ||
![]() |
067fcaf0d9 | ||
![]() |
1a379280b8 | ||
![]() |
284633ff91 | ||
![]() |
a7bfd08ce3 | ||
![]() |
8c5cc43fc5 | ||
![]() |
a8553e7897 | ||
![]() |
ca1b68b7ca | ||
![]() |
1fe710777a | ||
![]() |
e349e24548 | ||
![]() |
8ad7c37d5e | ||
![]() |
b7f079b006 | ||
![]() |
c39ca8852d | ||
![]() |
69a10e4b3d | ||
![]() |
3b02b6b7d7 | ||
![]() |
82584e5d7e | ||
![]() |
6c8ee3dfa9 | ||
![]() |
f9ba4a746e | ||
![]() |
e37cf125c3 | ||
![]() |
b7dc0b83b2 | ||
![]() |
0113456dea | ||
![]() |
4add24696b | ||
![]() |
c6441d231b | ||
![]() |
438ccc41ef | ||
![]() |
80f863f6b6 | ||
![]() |
d8e34e6c39 | ||
![]() |
c4cb36b9d5 | ||
![]() |
72c4a52119 | ||
![]() |
8eba326f62 | ||
![]() |
69d675dc76 | ||
![]() |
e9991e99c0 | ||
![]() |
6da36140eb | ||
![]() |
816c900edc | ||
![]() |
4ebce79353 | ||
![]() |
256ed2a038 | ||
![]() |
3f30e4c691 | ||
![]() |
ad9dbb4a68 | ||
![]() |
fb04b6fa85 | ||
![]() |
6badd694c5 | ||
![]() |
6f5cddeec8 | ||
![]() |
25ba275230 | ||
![]() |
cf6ffc3954 | ||
![]() |
5e3b7d88ec | ||
![]() |
1f5552dd81 | ||
![]() |
a2aede0d94 | ||
![]() |
2fd5635b2d | ||
![]() |
bc63e1906f | ||
![]() |
a8b168dc1f | ||
![]() |
fce2163350 | ||
![]() |
5d0c83dec2 | ||
![]() |
557fce577a | ||
![]() |
bcb5b9c9c2 | ||
![]() |
428260b91f | ||
![]() |
fec1a00dc8 | ||
![]() |
8dd898f895 | ||
![]() |
f8585602d9 | ||
![]() |
0ec3b9ce0e | ||
![]() |
6f5d074b4b | ||
![]() |
7d9b95b4ce | ||
![]() |
c08890391c | ||
![]() |
422135b2ab | ||
![]() |
b83b25cbe3 | ||
![]() |
4a17799449 | ||
![]() |
3fe18e0810 | ||
![]() |
5f4e0dfc14 | ||
![]() |
afc73ffa16 | ||
![]() |
7ae3c05f1d | ||
![]() |
744b81fc30 | ||
![]() |
f4617d7c90 | ||
![]() |
a00966986c | ||
![]() |
42ea2ecbc8 | ||
![]() |
568734eb8a | ||
![]() |
3b578ecfe0 | ||
![]() |
bb843b86ab | ||
![]() |
b12dffe420 | ||
![]() |
36d8ffd3c1 | ||
![]() |
f31bf5a1f1 | ||
![]() |
270accf6d7 | ||
![]() |
6816364354 | ||
![]() |
2f3715beed | ||
![]() |
6912782f4b | ||
![]() |
d8809197f8 | ||
![]() |
199312dd5a | ||
![]() |
a108a01be7 | ||
![]() |
7568e90655 | ||
![]() |
c216713844 | ||
![]() |
4dfe925cf9 | ||
![]() |
534b4e4aa3 | ||
![]() |
9aa8e1d7ad | ||
![]() |
ff146d5855 | ||
![]() |
d0cb62cb62 | ||
![]() |
743d48744a | ||
![]() |
2837ae8bff | ||
![]() |
3e54d4ddbb | ||
![]() |
426deb4454 | ||
![]() |
52ccf3d93b | ||
![]() |
b6a366ed97 | ||
![]() |
1ba6ed9fe9 | ||
![]() |
db4a7ce239 | ||
![]() |
983c943eef | ||
![]() |
aaab95ba55 |
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
|
|
91
.github/workflows/build.yml
vendored
91
.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
|
||||||
@ -61,7 +76,7 @@ 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.6.3'
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_tools: ''
|
qt_tools: ''
|
||||||
|
|
||||||
@ -73,7 +88,7 @@ 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.6.3'
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_tools: ''
|
qt_tools: ''
|
||||||
|
|
||||||
@ -83,7 +98,7 @@ 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.6.3'
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_tools: ''
|
qt_tools: ''
|
||||||
|
|
||||||
@ -244,7 +259,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 +350,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 +371,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 +406,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:
|
||||||
@ -442,7 +495,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }}
|
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
|
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 }}
|
cd ${{ env.INSTALL_DIR }}
|
||||||
tar --owner root --group root -czf ../PrismLauncher.tar.gz *
|
tar --owner root --group root -czf ../PrismLauncher.tar.gz *
|
||||||
|
|
||||||
@ -451,9 +503,12 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
|
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||||
|
|
||||||
|
# workaround to make portable installs to work on fedora
|
||||||
|
mkdir ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||||
|
cp /lib/x86_64-linux-gnu/libbz2.so.1.0 ${{ 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
|
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 }}
|
cd ${{ env.INSTALL_PORTABLE_DIR }}
|
||||||
tar -czf ../PrismLauncher-portable.tar.gz *
|
tar -czf ../PrismLauncher-portable.tar.gz *
|
||||||
|
|
||||||
@ -472,13 +527,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 +537,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
|
||||||
@ -520,7 +567,7 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
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'
|
||||||
|
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 }}
|
||||||
|
15
.github/workflows/trigger_release.yml
vendored
15
.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 }}
|
||||||
|
|
||||||
@ -46,8 +51,8 @@ jobs:
|
|||||||
mv PrismLauncher-Linux-Qt5*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt5-${{ 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 }}
|
||||||
|
|
||||||
@ -102,6 +107,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
|
||||||
|
@ -179,7 +179,7 @@ set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRIN
|
|||||||
|
|
||||||
######## Set version numbers ########
|
######## Set version numbers ########
|
||||||
set(Launcher_VERSION_MAJOR 8)
|
set(Launcher_VERSION_MAJOR 8)
|
||||||
set(Launcher_VERSION_MINOR 0)
|
set(Launcher_VERSION_MINOR 4)
|
||||||
|
|
||||||
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
||||||
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
|
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
|
||||||
@ -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
|
||||||
@ -504,11 +504,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
|
||||||
|
@ -163,7 +163,6 @@ class Config {
|
|||||||
|
|
||||||
QString RESOURCE_BASE = "https://resources.download.minecraft.net/";
|
QString RESOURCE_BASE = "https://resources.download.minecraft.net/";
|
||||||
QString LIBRARY_BASE = "https://libraries.minecraft.net/";
|
QString LIBRARY_BASE = "https://libraries.minecraft.net/";
|
||||||
QString AUTH_BASE = "https://authserver.mojang.com/";
|
|
||||||
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
|
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
|
||||||
QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists
|
QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists
|
||||||
QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists
|
QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists
|
||||||
|
@ -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>
|
||||||
|
22
flake.lock
generated
22
flake.lock
generated
@ -18,7 +18,9 @@
|
|||||||
},
|
},
|
||||||
"flake-parts": {
|
"flake-parts": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs-lib": "nixpkgs-lib"
|
"nixpkgs-lib": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1698882062,
|
"lastModified": 1698882062,
|
||||||
@ -120,24 +122,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": [
|
||||||
|
18
flake.nix
18
flake.nix
@ -1,15 +1,25 @@
|
|||||||
{
|
{
|
||||||
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 = {
|
||||||
|
url = "github:hercules-ci/flake-parts";
|
||||||
|
inputs.nixpkgs-lib.follows = "nixpkgs";
|
||||||
|
};
|
||||||
nix-filter.url = "github:numtide/nix-filter";
|
nix-filter.url = "github:numtide/nix-filter";
|
||||||
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,6 +1,6 @@
|
|||||||
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.openjdk17
|
- org.freedesktop.Sdk.Extension.openjdk17
|
||||||
@ -62,7 +62,8 @@ modules:
|
|||||||
config-opts:
|
config-opts:
|
||||||
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||||
- -DBUILD_SHARED_LIBS:BOOL=ON
|
- -DBUILD_SHARED_LIBS:BOOL=ON
|
||||||
- -DGLFW_USE_WAYLAND=ON
|
- -DGLFW_USE_WAYLAND:BOOL=ON
|
||||||
|
- -DGLFW_BUILD_DOCS:BOOL=OFF
|
||||||
sources:
|
sources:
|
||||||
- type: git
|
- type: git
|
||||||
url: https://github.com/glfw/glfw.git
|
url: https://github.com/glfw/glfw.git
|
||||||
@ -104,18 +105,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
|
@ -225,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;
|
||||||
@ -494,8 +495,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;
|
||||||
@ -749,6 +749,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"));
|
||||||
@ -1511,6 +1514,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 {
|
||||||
@ -1518,6 +1532,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", "[]");
|
||||||
|
@ -827,6 +827,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
|
||||||
|
@ -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,16 +801,68 @@ QString NormalizePath(QString path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString badFilenameChars = "\"\\/?<>:;*|!+\r\n";
|
static const QString BAD_WIN_CHARS = "<>:\"|?*\r\n";
|
||||||
|
static const QString BAD_NTFS_CHARS = "<>:\"|?*";
|
||||||
|
static const QString BAD_HFS_CHARS = ":";
|
||||||
|
|
||||||
|
static const QString BAD_FILENAME_CHARS = BAD_WIN_CHARS + "\\/";
|
||||||
|
|
||||||
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
|
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 path, QChar replaceWith)
|
||||||
|
{
|
||||||
|
QString invalidChars;
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
invalidChars = BAD_WIN_CHARS;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// the null character is ignored in this check as it was not a problem until now
|
||||||
|
switch (statFS(path).fsType) {
|
||||||
|
case FilesystemType::FAT: // similar to NTFS
|
||||||
|
/* fallthrough */
|
||||||
|
case FilesystemType::NTFS:
|
||||||
|
/* fallthrough */
|
||||||
|
case FilesystemType::REFS: // similar to NTFS(should be available only on windows)
|
||||||
|
invalidChars += BAD_NTFS_CHARS;
|
||||||
|
break;
|
||||||
|
// case FilesystemType::EXT:
|
||||||
|
// case FilesystemType::EXT_2_OLD:
|
||||||
|
// case FilesystemType::EXT_2_3_4:
|
||||||
|
// case FilesystemType::XFS:
|
||||||
|
// case FilesystemType::BTRFS:
|
||||||
|
// case FilesystemType::NFS:
|
||||||
|
// case FilesystemType::ZFS:
|
||||||
|
case FilesystemType::APFS:
|
||||||
|
/* fallthrough */
|
||||||
|
case FilesystemType::HFS:
|
||||||
|
/* fallthrough */
|
||||||
|
case FilesystemType::HFSPLUS:
|
||||||
|
/* fallthrough */
|
||||||
|
case FilesystemType::HFSX:
|
||||||
|
invalidChars += BAD_HFS_CHARS;
|
||||||
|
break;
|
||||||
|
// case FilesystemType::FUSEBLK:
|
||||||
|
// case FilesystemType::F2FS:
|
||||||
|
// case FilesystemType::UNKNOWN:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invalidChars.size() != 0) {
|
||||||
|
for (int i = 0; i < path.length(); i++) {
|
||||||
|
if (path.at(i) < ' ' || invalidChars.contains(path.at(i))) {
|
||||||
|
path[i] = replaceWith;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return string;
|
|
||||||
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DirNameFromString(QString string, QString inDir)
|
QString DirNameFromString(QString string, QString inDir)
|
||||||
@ -1581,4 +1637,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 "!"
|
||||||
@ -370,6 +378,7 @@ enum class FilesystemType {
|
|||||||
HFSX,
|
HFSX,
|
||||||
FUSEBLK,
|
FUSEBLK,
|
||||||
F2FS,
|
F2FS,
|
||||||
|
BCACHEFS,
|
||||||
UNKNOWN
|
UNKNOWN
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -398,6 +407,7 @@ static const QMap<FilesystemType, QStringList> s_filesystem_type_names = { { Fil
|
|||||||
{ FilesystemType::HFSX, { "HFSX" } },
|
{ FilesystemType::HFSX, { "HFSX" } },
|
||||||
{ FilesystemType::FUSEBLK, { "FUSEBLK" } },
|
{ FilesystemType::FUSEBLK, { "FUSEBLK" } },
|
||||||
{ FilesystemType::F2FS, { "F2FS" } },
|
{ FilesystemType::F2FS, { "F2FS" } },
|
||||||
|
{ FilesystemType::BCACHEFS, { "BCACHEFS" } },
|
||||||
{ FilesystemType::UNKNOWN, { "UNKNOWN" } } };
|
{ FilesystemType::UNKNOWN, { "UNKNOWN" } } };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -450,7 +460,7 @@ QString nearestExistentAncestor(const QString& path);
|
|||||||
FilesystemInfo statFS(const QString& path);
|
FilesystemInfo statFS(const QString& path);
|
||||||
|
|
||||||
static const QList<FilesystemType> s_clone_filesystems = { FilesystemType::BTRFS, FilesystemType::APFS, FilesystemType::ZFS,
|
static const QList<FilesystemType> s_clone_filesystems = { FilesystemType::BTRFS, FilesystemType::APFS, FilesystemType::ZFS,
|
||||||
FilesystemType::XFS, FilesystemType::REFS };
|
FilesystemType::XFS, FilesystemType::REFS, FilesystemType::BCACHEFS };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief if the Filesystem is reflink/clone capable
|
* @brief if the Filesystem is reflink/clone capable
|
||||||
@ -545,4 +555,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
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
@ -317,7 +316,7 @@ void LaunchController::launchInstance()
|
|||||||
online_mode = "online";
|
online_mode = "online";
|
||||||
|
|
||||||
// Prepend Server Status
|
// Prepend Server Status
|
||||||
QStringList servers = { "authserver.mojang.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" };
|
QStringList servers = { "login.microsoftonline.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" };
|
||||||
QString resolved_servers = "";
|
QString resolved_servers = "";
|
||||||
QHostInfo host_info;
|
QHostInfo host_info;
|
||||||
|
|
||||||
|
@ -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,11 @@ std::optional<QStringList> extractSubDir(QuaZip* zip, const QString& subdir, con
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
QString file_name = zip->getCurrentFileName();
|
QString file_name = zip->getCurrentFileName();
|
||||||
|
file_name = FS::RemoveInvalidPathChars(file_name);
|
||||||
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
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -212,3 +212,25 @@ QPair<QString, QString> StringUtils::splitFirst(const QString& s, const QRegular
|
|||||||
right = s.mid(end);
|
right = s.mid(end);
|
||||||
return qMakePair(left, right);
|
return qMakePair(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const QRegularExpression ulMatcher("<\\s*/\\s*ul\\s*>");
|
||||||
|
|
||||||
|
QString StringUtils::htmlListPatch(QString htmlStr)
|
||||||
|
{
|
||||||
|
int pos = htmlStr.indexOf(ulMatcher);
|
||||||
|
int imgPos;
|
||||||
|
while (pos != -1) {
|
||||||
|
pos = htmlStr.indexOf(">", pos) + 1; // Get the size of the </ul> tag. Add one for zeroeth index
|
||||||
|
imgPos = htmlStr.indexOf("<img ", pos);
|
||||||
|
if (imgPos == -1)
|
||||||
|
break; // no image after the tag
|
||||||
|
|
||||||
|
auto textBetween = htmlStr.mid(pos, imgPos - pos).trimmed(); // trim all white spaces
|
||||||
|
|
||||||
|
if (textBetween.isEmpty())
|
||||||
|
htmlStr.insert(pos, "<br>");
|
||||||
|
|
||||||
|
pos = htmlStr.indexOf(ulMatcher, pos);
|
||||||
|
}
|
||||||
|
return htmlStr;
|
||||||
|
}
|
@ -85,4 +85,6 @@ QPair<QString, QString> splitFirst(const QString& s, const QString& sep, Qt::Cas
|
|||||||
QPair<QString, QString> splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs = Qt::CaseSensitive);
|
QPair<QString, QString> splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs = Qt::CaseSensitive);
|
||||||
QPair<QString, QString> splitFirst(const QString& s, const QRegularExpression& re);
|
QPair<QString, QString> splitFirst(const QString& s, const QRegularExpression& re);
|
||||||
|
|
||||||
|
QString htmlListPatch(QString htmlStr);
|
||||||
|
|
||||||
} // namespace StringUtils
|
} // namespace StringUtils
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -207,7 +207,7 @@ QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString
|
|||||||
QString newKeyName = keyName + "\\" + newSubkeyName + subkeySuffix;
|
QString newKeyName = keyName + "\\" + newSubkeyName + subkeySuffix;
|
||||||
|
|
||||||
HKEY newKey;
|
HKEY newKey;
|
||||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, newKeyName.toStdWString().c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &newKey) ==
|
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, newKeyName.toStdWString().c_str(), 0, KEY_READ | keyType, &newKey) ==
|
||||||
ERROR_SUCCESS) {
|
ERROR_SUCCESS) {
|
||||||
// Read the JavaHome value to find where Java is installed.
|
// Read the JavaHome value to find where Java is installed.
|
||||||
DWORD valueSz = 0;
|
DWORD valueSz = 0;
|
||||||
@ -283,6 +283,12 @@ QList<QString> JavaUtils::FindJavaPaths()
|
|||||||
QList<JavaInstallPtr> ADOPTIUMJDK64s =
|
QList<JavaInstallPtr> ADOPTIUMJDK64s =
|
||||||
this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI");
|
this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI");
|
||||||
|
|
||||||
|
// IBM Semeru
|
||||||
|
QList<JavaInstallPtr> SEMERUJRE32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Semeru\\JRE", "Path", "\\openj9\\MSI");
|
||||||
|
QList<JavaInstallPtr> SEMERUJRE64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Semeru\\JRE", "Path", "\\openj9\\MSI");
|
||||||
|
QList<JavaInstallPtr> SEMERUJDK32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Semeru\\JDK", "Path", "\\openj9\\MSI");
|
||||||
|
QList<JavaInstallPtr> SEMERUJDK64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Semeru\\JDK", "Path", "\\openj9\\MSI");
|
||||||
|
|
||||||
// Microsoft
|
// Microsoft
|
||||||
QList<JavaInstallPtr> MICROSOFTJDK64s =
|
QList<JavaInstallPtr> MICROSOFTJDK64s =
|
||||||
this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Microsoft\\JDK", "Path", "\\hotspot\\MSI");
|
this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Microsoft\\JDK", "Path", "\\hotspot\\MSI");
|
||||||
@ -300,6 +306,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
|||||||
java_candidates.append(NEWJRE64s);
|
java_candidates.append(NEWJRE64s);
|
||||||
java_candidates.append(ADOPTOPENJRE64s);
|
java_candidates.append(ADOPTOPENJRE64s);
|
||||||
java_candidates.append(ADOPTIUMJRE64s);
|
java_candidates.append(ADOPTIUMJRE64s);
|
||||||
|
java_candidates.append(SEMERUJRE64s);
|
||||||
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre8/bin/javaw.exe"));
|
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre8/bin/javaw.exe"));
|
||||||
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe"));
|
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe"));
|
||||||
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe"));
|
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe"));
|
||||||
@ -308,6 +315,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
|||||||
java_candidates.append(ADOPTOPENJDK64s);
|
java_candidates.append(ADOPTOPENJDK64s);
|
||||||
java_candidates.append(FOUNDATIONJDK64s);
|
java_candidates.append(FOUNDATIONJDK64s);
|
||||||
java_candidates.append(ADOPTIUMJDK64s);
|
java_candidates.append(ADOPTIUMJDK64s);
|
||||||
|
java_candidates.append(SEMERUJDK64s);
|
||||||
java_candidates.append(MICROSOFTJDK64s);
|
java_candidates.append(MICROSOFTJDK64s);
|
||||||
java_candidates.append(ZULU64s);
|
java_candidates.append(ZULU64s);
|
||||||
java_candidates.append(LIBERICA64s);
|
java_candidates.append(LIBERICA64s);
|
||||||
@ -316,6 +324,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
|||||||
java_candidates.append(NEWJRE32s);
|
java_candidates.append(NEWJRE32s);
|
||||||
java_candidates.append(ADOPTOPENJRE32s);
|
java_candidates.append(ADOPTOPENJRE32s);
|
||||||
java_candidates.append(ADOPTIUMJRE32s);
|
java_candidates.append(ADOPTIUMJRE32s);
|
||||||
|
java_candidates.append(SEMERUJRE32s);
|
||||||
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre8/bin/javaw.exe"));
|
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre8/bin/javaw.exe"));
|
||||||
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe"));
|
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe"));
|
||||||
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe"));
|
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe"));
|
||||||
@ -324,6 +333,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
|||||||
java_candidates.append(ADOPTOPENJDK32s);
|
java_candidates.append(ADOPTOPENJDK32s);
|
||||||
java_candidates.append(FOUNDATIONJDK32s);
|
java_candidates.append(FOUNDATIONJDK32s);
|
||||||
java_candidates.append(ADOPTIUMJDK32s);
|
java_candidates.append(ADOPTIUMJDK32s);
|
||||||
|
java_candidates.append(SEMERUJDK32s);
|
||||||
java_candidates.append(ZULU32s);
|
java_candidates.append(ZULU32s);
|
||||||
java_candidates.append(LIBERICA32s);
|
java_candidates.append(LIBERICA32s);
|
||||||
|
|
||||||
@ -362,6 +372,12 @@ QList<QString> JavaUtils::FindJavaPaths()
|
|||||||
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
|
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
|
||||||
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java");
|
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto home = qEnvironmentVariable("HOME");
|
||||||
|
|
||||||
|
// javas downloaded by sdkman
|
||||||
|
javas.append(FS::PathCombine(home, ".sdkman/candidates/java"));
|
||||||
|
|
||||||
javas.append(getMinecraftJavaBundle());
|
javas.append(getMinecraftJavaBundle());
|
||||||
javas = addJavasFromEnv(javas);
|
javas = addJavasFromEnv(javas);
|
||||||
javas.removeDuplicates();
|
javas.removeDuplicates();
|
||||||
@ -404,6 +420,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
|||||||
// manually installed JDKs in /opt
|
// manually installed JDKs in /opt
|
||||||
scanJavaDirs("/opt/jdk");
|
scanJavaDirs("/opt/jdk");
|
||||||
scanJavaDirs("/opt/jdks");
|
scanJavaDirs("/opt/jdks");
|
||||||
|
scanJavaDirs("/opt/ibm"); // IBM Semeru Certified Edition
|
||||||
// flatpak
|
// flatpak
|
||||||
scanJavaDirs("/app/jdk");
|
scanJavaDirs("/app/jdk");
|
||||||
|
|
||||||
@ -413,6 +430,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 +458,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()) {
|
||||||
|
@ -50,6 +50,7 @@ void Library::getApplicableFiles(const RuntimeContext& runtimeContext,
|
|||||||
{
|
{
|
||||||
bool local = isLocal();
|
bool local = isLocal();
|
||||||
auto actualPath = [&](QString relPath) {
|
auto actualPath = [&](QString relPath) {
|
||||||
|
relPath = FS::RemoveInvalidPathChars(relPath);
|
||||||
QFileInfo out(FS::PathCombine(storagePrefix(), relPath));
|
QFileInfo out(FS::PathCombine(storagePrefix(), relPath));
|
||||||
if (local && !overridePath.isEmpty()) {
|
if (local && !overridePath.isEmpty()) {
|
||||||
QString fileName = out.fileName();
|
QString fileName = out.fileName();
|
||||||
|
@ -594,9 +594,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 +601,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");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -662,8 +657,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;
|
||||||
@ -993,7 +992,7 @@ QString MinecraftInstance::getStatusbarDescription()
|
|||||||
QString description;
|
QString description;
|
||||||
description.append(tr("Minecraft %1").arg(mcVersion));
|
description.append(tr("Minecraft %1").arg(mcVersion));
|
||||||
if (m_settings->get("ShowGameTime").toBool()) {
|
if (m_settings->get("ShowGameTime").toBool()) {
|
||||||
if (lastTimePlayed() > 0) {
|
if (lastTimePlayed() > 0 && lastLaunch() > 0) {
|
||||||
QDateTime lastLaunchTime = QDateTime::fromMSecsSinceEpoch(lastLaunch());
|
QDateTime lastLaunchTime = QDateTime::fromMSecsSinceEpoch(lastLaunch());
|
||||||
description.append(
|
description.append(
|
||||||
tr(", last played on %1 for %2")
|
tr(", last played on %1 for %2")
|
||||||
|
@ -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
|
||||||
|
@ -28,15 +28,52 @@
|
|||||||
#include "Version.h"
|
#include "Version.h"
|
||||||
|
|
||||||
// Values taken from:
|
// Values taken from:
|
||||||
// https://minecraft.wiki/w/Tutorials/Creating_a_data_pack#%22pack_format%22
|
// https://minecraft.wiki/w/Pack_format#List_of_data_pack_formats
|
||||||
static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = {
|
static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = { { 4, { Version("1.13"), Version("1.14.4") } },
|
||||||
{ 4, { Version("1.13"), Version("1.14.4") } }, { 5, { Version("1.15"), Version("1.16.1") } },
|
{ 5, { Version("1.15"), Version("1.16.1") } },
|
||||||
{ 6, { Version("1.16.2"), Version("1.16.5") } }, { 7, { Version("1.17"), Version("1.17.1") } },
|
{ 6, { Version("1.16.2"), Version("1.16.5") } },
|
||||||
{ 8, { Version("1.18"), Version("1.18.1") } }, { 9, { Version("1.18.2"), Version("1.18.2") } },
|
{ 7, { Version("1.17"), Version("1.17.1") } },
|
||||||
{ 10, { Version("1.19"), Version("1.19.3") } }, { 11, { Version("23w03a"), Version("23w05a") } },
|
{ 8, { Version("1.18"), Version("1.18.1") } },
|
||||||
{ 12, { Version("1.19.4"), Version("1.19.4") } }, { 13, { Version("23w12a"), Version("23w14a") } },
|
{ 9, { Version("1.18.2"), Version("1.18.2") } },
|
||||||
{ 14, { Version("23w16a"), Version("23w17a") } }, { 15, { Version("1.20"), Version("1.20") } },
|
{ 10, { Version("1.19"), Version("1.19.3") } },
|
||||||
};
|
{ 11, { Version("23w03a"), Version("23w05a") } },
|
||||||
|
{ 12, { Version("1.19.4"), Version("1.19.4") } },
|
||||||
|
{ 13, { Version("23w12a"), Version("23w14a") } },
|
||||||
|
{ 14, { Version("23w16a"), Version("23w17a") } },
|
||||||
|
{ 15, { Version("1.20"), Version("1.20.1") } },
|
||||||
|
{ 16, { Version("23w31a"), Version("23w31a") } },
|
||||||
|
{ 17, { Version("23w32a"), Version("23w35a") } },
|
||||||
|
{ 18, { Version("1.20.2"), Version("1.20.2") } },
|
||||||
|
{ 19, { Version("23w40a"), Version("23w40a") } },
|
||||||
|
{ 20, { Version("23w41a"), Version("23w41a") } },
|
||||||
|
{ 21, { Version("23w42a"), Version("23w42a") } },
|
||||||
|
{ 22, { Version("23w43a"), Version("23w43b") } },
|
||||||
|
{ 23, { Version("23w44a"), Version("23w44a") } },
|
||||||
|
{ 24, { Version("23w45a"), Version("23w45a") } },
|
||||||
|
{ 25, { Version("23w46a"), Version("23w46a") } },
|
||||||
|
{ 26, { Version("1.20.3"), Version("1.20.4") } },
|
||||||
|
{ 27, { Version("23w51a"), Version("23w51b") } },
|
||||||
|
{ 28, { Version("24w05a"), Version("24w05b") } },
|
||||||
|
{ 29, { Version("24w04a"), Version("24w04a") } },
|
||||||
|
{ 30, { Version("24w05a"), Version("24w05b") } },
|
||||||
|
{ 31, { Version("24w06a"), Version("24w06a") } },
|
||||||
|
{ 32, { Version("24w07a"), Version("24w07a") } },
|
||||||
|
{ 33, { Version("24w09a"), Version("24w09a") } },
|
||||||
|
{ 34, { Version("24w10a"), Version("24w10a") } },
|
||||||
|
{ 35, { Version("24w11a"), Version("24w11a") } },
|
||||||
|
{ 36, { Version("24w12a"), Version("24w12a") } },
|
||||||
|
{ 37, { Version("24w13a"), Version("24w13a") } },
|
||||||
|
{ 38, { Version("24w14a"), Version("24w14a") } },
|
||||||
|
{ 39, { Version("1.20.5-pre1"), Version("1.20.5-pre1") } },
|
||||||
|
{ 40, { Version("1.20.5-pre2"), Version("1.20.5-pre2") } },
|
||||||
|
{ 41, { Version("1.20.5"), Version("1.20.6") } },
|
||||||
|
{ 42, { Version("24w18a"), Version("24w18a") } },
|
||||||
|
{ 43, { Version("24w19a"), Version("24w19b") } },
|
||||||
|
{ 44, { Version("24w20a"), Version("24w20a") } },
|
||||||
|
{ 45, { Version("21w21a"), Version("21w21b") } },
|
||||||
|
{ 46, { Version("1.21-pre1"), Version("1.21-pre1") } },
|
||||||
|
{ 47, { Version("1.21-pre2"), Version("1.21-pre2") } },
|
||||||
|
{ 48, { Version("1.21"), Version("1.21") } } };
|
||||||
|
|
||||||
void DataPack::setPackFormat(int new_format_id)
|
void DataPack::setPackFormat(int new_format_id)
|
||||||
{
|
{
|
||||||
|
@ -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();
|
||||||
|
@ -11,15 +11,24 @@
|
|||||||
#include "minecraft/mod/tasks/LocalResourcePackParseTask.h"
|
#include "minecraft/mod/tasks/LocalResourcePackParseTask.h"
|
||||||
|
|
||||||
// Values taken from:
|
// Values taken from:
|
||||||
// https://minecraft.wiki/w/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
|
// https://minecraft.wiki/w/Pack_format#List_of_resource_pack_formats
|
||||||
static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = {
|
static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = {
|
||||||
{ 1, { Version("1.6.1"), Version("1.8.9") } }, { 2, { Version("1.9"), Version("1.10.2") } },
|
{ 1, { Version("1.6.1"), Version("1.8.9") } }, { 2, { Version("1.9"), Version("1.10.2") } },
|
||||||
{ 3, { Version("1.11"), Version("1.12.2") } }, { 4, { Version("1.13"), Version("1.14.4") } },
|
{ 3, { Version("1.11"), Version("1.12.2") } }, { 4, { Version("1.13"), Version("1.14.4") } },
|
||||||
{ 5, { Version("1.15"), Version("1.16.1") } }, { 6, { Version("1.16.2"), Version("1.16.5") } },
|
{ 5, { Version("1.15"), Version("1.16.1") } }, { 6, { Version("1.16.2"), Version("1.16.5") } },
|
||||||
{ 7, { Version("1.17"), Version("1.17.1") } }, { 8, { Version("1.18"), Version("1.18.2") } },
|
{ 7, { Version("1.17"), Version("1.17.1") } }, { 8, { Version("1.18"), Version("1.18.2") } },
|
||||||
{ 9, { Version("1.19"), Version("1.19.2") } }, { 11, { Version("22w42a"), Version("22w44a") } },
|
{ 9, { Version("1.19"), Version("1.19.2") } }, { 11, { Version("22w42a"), Version("22w44a") } },
|
||||||
{ 12, { Version("1.19.3"), Version("1.19.3") } }, { 13, { Version("1.19.4"), Version("1.19.4") } },
|
{ 12, { Version("1.19.3"), Version("1.19.3") } }, { 13, { Version("1.19.4"), Version("1.19.4") } },
|
||||||
{ 14, { Version("1.20"), Version("1.20") } }
|
{ 14, { Version("23w14a"), Version("23w16a") } }, { 15, { Version("1.20"), Version("1.20.1") } },
|
||||||
|
{ 16, { Version("23w31a"), Version("23w31a") } }, { 17, { Version("23w32a"), Version("23w35a") } },
|
||||||
|
{ 18, { Version("1.20.2"), Version("23w16a") } }, { 19, { Version("23w42a"), Version("23w42a") } },
|
||||||
|
{ 20, { Version("23w43a"), Version("23w44a") } }, { 21, { Version("23w45a"), Version("23w46a") } },
|
||||||
|
{ 22, { Version("1.20.3-pre1"), Version("23w51b") } }, { 24, { Version("24w03a"), Version("24w04a") } },
|
||||||
|
{ 25, { Version("24w05a"), Version("24w05b") } }, { 26, { Version("24w06a"), Version("24w07a") } },
|
||||||
|
{ 28, { Version("24w09a"), Version("24w10a") } }, { 29, { Version("24w11a"), Version("24w11a") } },
|
||||||
|
{ 30, { Version("24w12a"), Version("23w12a") } }, { 31, { Version("24w13a"), Version("1.20.5-pre3") } },
|
||||||
|
{ 32, { Version("1.20.5-pre4"), Version("1.20.6") } }, { 33, { Version("24w18a"), Version("24w20a") } },
|
||||||
|
{ 34, { Version("24w21a"), Version("1.21") } }
|
||||||
};
|
};
|
||||||
|
|
||||||
void ResourcePack::setPackFormat(int new_format_id)
|
void ResourcePack::setPackFormat(int new_format_id)
|
||||||
|
@ -182,7 +182,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;
|
||||||
|
@ -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;
|
||||||
|
@ -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,10 @@ 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;
|
||||||
|
fileName = FS::RemoveInvalidPathChars(fileName);
|
||||||
|
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,7 @@ 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");
|
||||||
|
file.fileName = FS::RemoveInvalidPathChars(file.fileName);
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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();
|
|
||||||
}
|
}
|
||||||
|
@ -226,6 +226,9 @@ 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();
|
||||||
|
|
||||||
@ -235,11 +238,13 @@ bool ModrinthCreationTask::createInstance()
|
|||||||
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;
|
||||||
|
fileName = FS::RemoveInvalidPathChars(fileName);
|
||||||
|
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 +294,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());
|
||||||
}
|
}
|
||||||
|
@ -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,7 @@ 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");
|
||||||
|
file.fileName = FS::RemoveInvalidPathChars(file.fileName);
|
||||||
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 {
|
||||||
|
@ -83,8 +83,10 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings,
|
|||||||
data = file.readAll();
|
data = file.readAll();
|
||||||
file.close();
|
file.close();
|
||||||
} else {
|
} else {
|
||||||
if (minecraftVersion.isEmpty())
|
if (minecraftVersion.isEmpty()) {
|
||||||
emit failed(tr("Could not find \"version.json\" inside \"bin/modpack.jar\", but Minecraft version is unknown"));
|
emit failed(tr("Could not find \"version.json\" inside \"bin/modpack.jar\", but Minecraft version is unknown"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
components->setComponentVersion("net.minecraft", minecraftVersion, true);
|
components->setComponentVersion("net.minecraft", minecraftVersion, true);
|
||||||
components->installJarMods({ modpackJar });
|
components->installJarMods({ modpackJar });
|
||||||
|
|
||||||
@ -131,7 +133,9 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings,
|
|||||||
file.close();
|
file.close();
|
||||||
} else {
|
} else {
|
||||||
// This is the "Vanilla" modpack, excluded by the search code
|
// This is the "Vanilla" modpack, excluded by the search code
|
||||||
emit failed(tr("Unable to find a \"version.json\"!"));
|
components->setComponentVersion("net.minecraft", minecraftVersion, true);
|
||||||
|
components->saveNow();
|
||||||
|
emit succeeded();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,8 +159,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 +186,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,7 @@ 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
|
||||||
{
|
{
|
||||||
|
resource_path = FS::RemoveInvalidPathChars(resource_path);
|
||||||
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
|
||||||
@ -56,18 +57,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);
|
explicit NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network);
|
||||||
~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;
|
||||||
|
|
||||||
|
@ -68,7 +68,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,10 +86,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
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
Net::NetRequest::Ptr ImgurAlbumCreation::make(std::shared_ptr<ImgurAlbumCreation::Result> output, QList<ScreenShot::Ptr> screenshots)
|
Net::NetRequest::Ptr ImgurAlbumCreation::make(std::shared_ptr<ImgurAlbumCreation::Result> output, QList<ScreenShot::Ptr> screenshots)
|
||||||
{
|
{
|
||||||
auto up = makeShared<ImgurAlbumCreation>();
|
auto up = makeShared<ImgurAlbumCreation>();
|
||||||
up->m_url = BuildConfig.IMGUR_BASE_URL + "album.json";
|
up->m_url = BuildConfig.IMGUR_BASE_URL + "album";
|
||||||
up->m_sink.reset(new Sink(output));
|
up->m_sink.reset(new Sink(output));
|
||||||
up->m_screenshots = screenshots;
|
up->m_screenshots = screenshots;
|
||||||
return up;
|
return up;
|
||||||
@ -72,7 +72,7 @@ void ImgurAlbumCreation::init()
|
|||||||
qDebug() << "Setting up imgur upload";
|
qDebug() << "Setting up imgur upload";
|
||||||
auto api_headers = new Net::StaticHeaderProxy(
|
auto api_headers = new Net::StaticHeaderProxy(
|
||||||
QList<Net::HeaderPair>{ { "Content-Type", "application/x-www-form-urlencoded" },
|
QList<Net::HeaderPair>{ { "Content-Type", "application/x-www-form-urlencoded" },
|
||||||
{ "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str() },
|
{ "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toUtf8() },
|
||||||
{ "Accept", "application/json" } });
|
{ "Accept", "application/json" } });
|
||||||
addHeaderProxy(api_headers);
|
addHeaderProxy(api_headers);
|
||||||
}
|
}
|
||||||
|
@ -50,34 +50,33 @@
|
|||||||
void ImgurUpload::init()
|
void ImgurUpload::init()
|
||||||
{
|
{
|
||||||
qDebug() << "Setting up imgur upload";
|
qDebug() << "Setting up imgur upload";
|
||||||
auto api_headers = new Net::StaticHeaderProxy(
|
auto api_headers = new Net::StaticHeaderProxy(QList<Net::HeaderPair>{
|
||||||
QList<Net::HeaderPair>{ { "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str() },
|
{ "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toUtf8() }, { "Accept", "application/json" } });
|
||||||
{ "Accept", "application/json" } });
|
|
||||||
addHeaderProxy(api_headers);
|
addHeaderProxy(api_headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
filePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/png");
|
filePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/png");
|
||||||
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"image\"");
|
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"image\"; filename=\"" + file->fileName() + "\"");
|
||||||
multipart->append(filePart);
|
multipart->append(filePart);
|
||||||
QHttpPart typePart;
|
QHttpPart typePart;
|
||||||
typePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"type\"");
|
typePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"type\"");
|
||||||
typePart.setBody("file");
|
typePart.setBody("file");
|
||||||
multipart->append(typePart);
|
multipart->append(typePart);
|
||||||
QHttpPart namePart;
|
QHttpPart namePart;
|
||||||
namePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"name\"");
|
namePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"title\"");
|
||||||
namePart.setBody(m_fileInfo.baseName().toUtf8());
|
namePart.setBody(m_fileInfo.baseName().toUtf8());
|
||||||
multipart->append(namePart);
|
multipart->append(namePart);
|
||||||
|
|
||||||
@ -124,7 +123,7 @@ auto ImgurUpload::Sink::finalize(QNetworkReply&) -> Task::State
|
|||||||
Net::NetRequest::Ptr ImgurUpload::make(ScreenShot::Ptr m_shot)
|
Net::NetRequest::Ptr ImgurUpload::make(ScreenShot::Ptr m_shot)
|
||||||
{
|
{
|
||||||
auto up = makeShared<ImgurUpload>(m_shot->m_file);
|
auto up = makeShared<ImgurUpload>(m_shot->m_file);
|
||||||
up->m_url = std::move(BuildConfig.IMGUR_BASE_URL + "upload.json");
|
up->m_url = std::move(BuildConfig.IMGUR_BASE_URL + "image");
|
||||||
up->m_sink.reset(new Sink(m_shot));
|
up->m_sink.reset(new Sink(m_shot));
|
||||||
return up;
|
return up;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -1182,43 +1182,43 @@ 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_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()
|
||||||
@ -1437,7 +1437,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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
#include "Markdown.h"
|
#include "Markdown.h"
|
||||||
|
#include "StringUtils.h"
|
||||||
#include "ui_AboutDialog.h"
|
#include "ui_AboutDialog.h"
|
||||||
|
|
||||||
#include <net/NetJob.h>
|
#include <net/NetJob.h>
|
||||||
@ -139,10 +140,10 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDia
|
|||||||
setWindowTitle(tr("About %1").arg(launcherName));
|
setWindowTitle(tr("About %1").arg(launcherName));
|
||||||
|
|
||||||
QString chtml = getCreditsHtml();
|
QString chtml = getCreditsHtml();
|
||||||
ui->creditsText->setHtml(chtml);
|
ui->creditsText->setHtml(StringUtils::htmlListPatch(chtml));
|
||||||
|
|
||||||
QString lhtml = getLicenseHtml();
|
QString lhtml = getLicenseHtml();
|
||||||
ui->licenseText->setHtml(lhtml);
|
ui->licenseText->setHtml(StringUtils::htmlListPatch(lhtml));
|
||||||
|
|
||||||
ui->urlLabel->setOpenExternalLinks(true);
|
ui->urlLabel->setOpenExternalLinks(true);
|
||||||
|
|
||||||
@ -174,8 +175,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>
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "Markdown.h"
|
#include "Markdown.h"
|
||||||
|
#include "StringUtils.h"
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include "minecraft/MinecraftInstance.h"
|
||||||
#include "minecraft/mod/ModFolderModel.h"
|
#include "minecraft/mod/ModFolderModel.h"
|
||||||
#include "modplatform/helpers/ExportToModList.h"
|
#include "modplatform/helpers/ExportToModList.h"
|
||||||
@ -143,10 +144,10 @@ void ExportToModListDialog::triggerImp()
|
|||||||
case ExportToModList::CUSTOM:
|
case ExportToModList::CUSTOM:
|
||||||
return;
|
return;
|
||||||
case ExportToModList::HTML:
|
case ExportToModList::HTML:
|
||||||
ui->resultText->setHtml(txt);
|
ui->resultText->setHtml(StringUtils::htmlListPatch(txt));
|
||||||
break;
|
break;
|
||||||
case ExportToModList::MARKDOWN:
|
case ExportToModList::MARKDOWN:
|
||||||
ui->resultText->setHtml(markdownToHTML(txt));
|
ui->resultText->setHtml(StringUtils::htmlListPatch(markdownToHTML(txt)));
|
||||||
break;
|
break;
|
||||||
case ExportToModList::PLAINTXT:
|
case ExportToModList::PLAINTXT:
|
||||||
break;
|
break;
|
||||||
|
@ -159,5 +159,5 @@ IconPickerDialog::~IconPickerDialog()
|
|||||||
|
|
||||||
void IconPickerDialog::openFolder()
|
void IconPickerDialog::openFolder()
|
||||||
{
|
{
|
||||||
DesktopServices::openDirectory(APPLICATION->icons()->getDirectory(), true);
|
DesktopServices::openPath(APPLICATION->icons()->getDirectory(), true);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "CustomMessageBox.h"
|
#include "CustomMessageBox.h"
|
||||||
#include "ProgressDialog.h"
|
#include "ProgressDialog.h"
|
||||||
#include "ScrollMessageBox.h"
|
#include "ScrollMessageBox.h"
|
||||||
|
#include "StringUtils.h"
|
||||||
#include "minecraft/mod/tasks/GetModDependenciesTask.h"
|
#include "minecraft/mod/tasks/GetModDependenciesTask.h"
|
||||||
#include "modplatform/ModIndex.h"
|
#include "modplatform/ModIndex.h"
|
||||||
#include "modplatform/flame/FlameAPI.h"
|
#include "modplatform/flame/FlameAPI.h"
|
||||||
@ -328,6 +329,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 +344,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 +399,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 {
|
||||||
@ -437,6 +444,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);
|
||||||
@ -455,7 +465,7 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStri
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
changelog_area->setHtml(text);
|
changelog_area->setHtml(StringUtils::htmlListPatch(text));
|
||||||
changelog_area->setOpenExternalLinks(true);
|
changelog_area->setOpenExternalLinks(true);
|
||||||
changelog_area->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth);
|
changelog_area->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth);
|
||||||
changelog_area->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded);
|
changelog_area->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded);
|
||||||
|
@ -52,6 +52,7 @@
|
|||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QLayout>
|
#include <QLayout>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
#include <QScreen>
|
||||||
#include <QValidator>
|
#include <QValidator>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@ -63,6 +64,7 @@
|
|||||||
#include "ui/pages/modplatform/modrinth/ModrinthPage.h"
|
#include "ui/pages/modplatform/modrinth/ModrinthPage.h"
|
||||||
#include "ui/pages/modplatform/technic/TechnicPage.h"
|
#include "ui/pages/modplatform/technic/TechnicPage.h"
|
||||||
#include "ui/widgets/PageContainer.h"
|
#include "ui/widgets/PageContainer.h"
|
||||||
|
|
||||||
NewInstanceDialog::NewInstanceDialog(const QString& initialGroup,
|
NewInstanceDialog::NewInstanceDialog(const QString& initialGroup,
|
||||||
const QString& url,
|
const QString& url,
|
||||||
const QMap<QString, QString>& extra_info,
|
const QMap<QString, QString>& extra_info,
|
||||||
@ -124,7 +126,17 @@ NewInstanceDialog::NewInstanceDialog(const QString& initialGroup,
|
|||||||
|
|
||||||
updateDialogState();
|
updateDialogState();
|
||||||
|
|
||||||
restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("NewInstanceGeometry").toByteArray()));
|
if (APPLICATION->settings()->get("NewInstanceGeometry").isValid()) {
|
||||||
|
restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("NewInstanceGeometry").toByteArray()));
|
||||||
|
} else {
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||||
|
auto screen = parent->screen();
|
||||||
|
#else
|
||||||
|
auto screen = QGuiApplication::primaryScreen();
|
||||||
|
#endif
|
||||||
|
auto geometry = screen->availableSize();
|
||||||
|
resize(width(), qMin(geometry.height() - 50, 710));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NewInstanceDialog::reject()
|
void NewInstanceDialog::reject()
|
||||||
|
@ -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);
|
||||||
@ -75,6 +76,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 +111,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;
|
||||||
@ -28,8 +29,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;
|
||||||
};
|
};
|
||||||
|
@ -44,15 +44,20 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="explainLabel">
|
<widget class="QLabel" name="explainLabel"/>
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0" rowspan="2">
|
<item row="5" column="0" rowspan="2">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="onlyCheckedLabel">
|
<widget class="QPushButton" name="toggleDepsButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Toggle Dependencies</string>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="onlyCheckedLabel"/>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
<property name="standardButtons">
|
<property name="standardButtons">
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
#include "Markdown.h"
|
#include "Markdown.h"
|
||||||
|
#include "StringUtils.h"
|
||||||
#include "ui_UpdateAvailableDialog.h"
|
#include "ui_UpdateAvailableDialog.h"
|
||||||
|
|
||||||
UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion,
|
UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion,
|
||||||
@ -43,7 +44,7 @@ UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion,
|
|||||||
ui->icon->setPixmap(APPLICATION->getThemedIcon("checkupdate").pixmap(64));
|
ui->icon->setPixmap(APPLICATION->getThemedIcon("checkupdate").pixmap(64));
|
||||||
|
|
||||||
auto releaseNotesHtml = markdownToHTML(releaseNotes);
|
auto releaseNotesHtml = markdownToHTML(releaseNotes);
|
||||||
ui->releaseNotes->setHtml(releaseNotesHtml);
|
ui->releaseNotes->setHtml(StringUtils::htmlListPatch(releaseNotesHtml));
|
||||||
ui->releaseNotes->setOpenExternalLinks(true);
|
ui->releaseNotes->setOpenExternalLinks(true);
|
||||||
|
|
||||||
connect(ui->skipButton, &QPushButton::clicked, this, [this]() {
|
connect(ui->skipButton, &QPushButton::clicked, this, [this]() {
|
||||||
|
@ -480,32 +480,42 @@ void InstanceView::paintEvent([[maybe_unused]] QPaintEvent* event)
|
|||||||
|
|
||||||
if (model()->rowCount() == 0) {
|
if (model()->rowCount() == 0) {
|
||||||
painter.save();
|
painter.save();
|
||||||
const QString line1 = tr("Welcome!");
|
QString emptyString = tr("Welcome!") + "\n" + tr("Click \"Add Instance\" to get started.");
|
||||||
const QString line2 = tr("Click \"Add Instance\" to get started.");
|
|
||||||
auto rect = this->viewport()->rect();
|
|
||||||
auto font = option.font;
|
|
||||||
font.setPointSize(37);
|
|
||||||
painter.setFont(font);
|
|
||||||
auto fm = painter.fontMetrics();
|
|
||||||
|
|
||||||
if (rect.height() <= (fm.height() * 5) || rect.width() <= fm.horizontalAdvance(line2)) {
|
// calculate the rect for the overlay
|
||||||
auto s = rect.height() / (5. * fm.height());
|
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||||
auto sx = rect.width() * 1. / fm.horizontalAdvance(line2);
|
QFont font("sans", 20);
|
||||||
if (s >= sx)
|
font.setBold(true);
|
||||||
s = sx;
|
|
||||||
auto ps = font.pointSize() * s;
|
QRect bounds = viewport()->geometry();
|
||||||
if (ps <= 0)
|
bounds.moveTop(0);
|
||||||
ps = 1;
|
auto innerBounds = bounds;
|
||||||
font.setPointSize(ps);
|
innerBounds.adjust(10, 10, -10, -10);
|
||||||
painter.setFont(font);
|
|
||||||
fm = painter.fontMetrics();
|
QColor background = QApplication::palette().color(QPalette::WindowText);
|
||||||
|
QColor foreground = QApplication::palette().color(QPalette::Base);
|
||||||
|
foreground.setAlpha(190);
|
||||||
|
painter.setFont(font);
|
||||||
|
auto fontMetrics = painter.fontMetrics();
|
||||||
|
auto textRect = fontMetrics.boundingRect(innerBounds, Qt::AlignHCenter | Qt::TextWordWrap, emptyString);
|
||||||
|
textRect.moveCenter(bounds.center());
|
||||||
|
|
||||||
|
auto wrapRect = textRect;
|
||||||
|
wrapRect.adjust(-10, -10, 10, 10);
|
||||||
|
|
||||||
|
// check if we are allowed to draw in our area
|
||||||
|
if (!event->rect().intersects(wrapRect)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// text
|
painter.setBrush(QBrush(background));
|
||||||
rect.setTop(rect.top() + fm.height() * 1.5);
|
painter.setPen(foreground);
|
||||||
painter.drawText(rect, Qt::AlignHCenter, line1);
|
painter.drawRoundedRect(wrapRect, 5.0, 5.0);
|
||||||
rect.setTop(rect.top() + fm.height());
|
|
||||||
painter.drawText(rect, Qt::AlignHCenter, line2);
|
painter.setPen(foreground);
|
||||||
|
painter.setFont(font);
|
||||||
|
painter.drawText(textRect, Qt::AlignHCenter | Qt::TextWordWrap, emptyString);
|
||||||
|
|
||||||
painter.restore();
|
painter.restore();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,9 @@ void VisualGroup::update()
|
|||||||
rows[currentRow].height = maxRowHeight;
|
rows[currentRow].height = maxRowHeight;
|
||||||
rows[currentRow].top = offsetFromTop;
|
rows[currentRow].top = offsetFromTop;
|
||||||
currentRow++;
|
currentRow++;
|
||||||
|
if (currentRow >= rows.size()) {
|
||||||
|
currentRow = rows.size() - 1;
|
||||||
|
}
|
||||||
offsetFromTop += maxRowHeight + 5;
|
offsetFromTop += maxRowHeight + 5;
|
||||||
positionInRow = 0;
|
positionInRow = 0;
|
||||||
maxRowHeight = 0;
|
maxRowHeight = 0;
|
||||||
@ -158,13 +161,14 @@ void VisualGroup::drawHeader(QPainter* painter, const QStyleOptionViewItem& opti
|
|||||||
painter->setRenderHint(QPainter::Antialiasing);
|
painter->setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
// sizes and offsets, to keep things consistent below
|
// sizes and offsets, to keep things consistent below
|
||||||
int arrowOffsetLeft = fontMetrics.height() / 2 + 7;
|
const int arrowOffsetLeft = fontMetrics.height() / 2 + 7;
|
||||||
int textOffsetLeft = arrowOffsetLeft * 2;
|
const int textOffsetLeft = arrowOffsetLeft * 2;
|
||||||
int arrowSize = 6;
|
const int centerHeight = optRect.top() + fontMetrics.height() / 2;
|
||||||
int centerHeight = optRect.top() + fontMetrics.height() / 2;
|
const QString& textToDraw = text.isEmpty() ? QObject::tr("Ungrouped") : text;
|
||||||
|
|
||||||
// BEGIN: arrow
|
// BEGIN: arrow
|
||||||
{
|
{
|
||||||
|
constexpr int arrowSize = 6;
|
||||||
QPolygon arrowPolygon;
|
QPolygon arrowPolygon;
|
||||||
if (collapsed) {
|
if (collapsed) {
|
||||||
arrowPolygon << QPoint(arrowOffsetLeft - arrowSize / 2, centerHeight - arrowSize)
|
arrowPolygon << QPoint(arrowOffsetLeft - arrowSize / 2, centerHeight - arrowSize)
|
||||||
@ -188,9 +192,26 @@ void VisualGroup::drawHeader(QPainter* painter, const QStyleOptionViewItem& opti
|
|||||||
textRect.setHeight(fontMetrics.height());
|
textRect.setHeight(fontMetrics.height());
|
||||||
textRect.setRight(textRect.right() - 7);
|
textRect.setRight(textRect.right() - 7);
|
||||||
|
|
||||||
painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, !text.isEmpty() ? text : QObject::tr("Ungrouped"));
|
painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, textToDraw);
|
||||||
}
|
}
|
||||||
// END: text
|
// END: text
|
||||||
|
|
||||||
|
// BEGIN: horizontal line
|
||||||
|
{
|
||||||
|
penColor.setAlphaF(0.05);
|
||||||
|
pen.setColor(penColor);
|
||||||
|
painter->setPen(pen);
|
||||||
|
// startPoint is left + arrow + text + space
|
||||||
|
const int startPoint =
|
||||||
|
optRect.left() + fontMetrics.height() + fontMetrics.size(Qt::AlignLeft | Qt::AlignVCenter, textToDraw).width() + 20;
|
||||||
|
painter->setRenderHint(QPainter::Antialiasing, false);
|
||||||
|
QPolygon polygon;
|
||||||
|
// for some reason the height (yPos) doesn't look centered, so we are adding 1 to the center height
|
||||||
|
const int lineHeight = centerHeight + 1;
|
||||||
|
polygon << QPoint(startPoint, lineHeight) << QPoint(optRect.right() - 3, lineHeight);
|
||||||
|
painter->drawPolyline(polygon);
|
||||||
|
}
|
||||||
|
// END: horizontal line
|
||||||
}
|
}
|
||||||
|
|
||||||
int VisualGroup::totalHeight() const
|
int VisualGroup::totalHeight() const
|
||||||
|
@ -206,7 +206,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="onlineFixes">
|
<widget class="QCheckBox" name="onlineFixes">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>Emulates usages of old online services which are no longer operating.</p><p>This currently allows modern skins to be used.</p></body></html></string>
|
<string><html><head/><body><p>Emulates usages of old online services which are no longer operating.</p><p>Current fixes include: skin and online mode support.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable online fixes (experimental)</string>
|
<string>Enable online fixes (experimental)</string>
|
||||||
|
@ -290,12 +290,12 @@ void ExternalResourcesPage::disableItem()
|
|||||||
|
|
||||||
void ExternalResourcesPage::viewConfigs()
|
void ExternalResourcesPage::viewConfigs()
|
||||||
{
|
{
|
||||||
DesktopServices::openDirectory(m_instance->instanceConfigFolder(), true);
|
DesktopServices::openPath(m_instance->instanceConfigFolder(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExternalResourcesPage::viewFolder()
|
void ExternalResourcesPage::viewFolder()
|
||||||
{
|
{
|
||||||
DesktopServices::openDirectory(m_model->dir().absolutePath(), true);
|
DesktopServices::openPath(m_model->dir().absolutePath(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExternalResourcesPage::current(const QModelIndex& current, const QModelIndex& previous)
|
bool ExternalResourcesPage::current(const QModelIndex& current, const QModelIndex& previous)
|
||||||
|
@ -346,7 +346,7 @@ void InstanceSettingsPage::loadSettings()
|
|||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
ui->lineEditOpenALPath->setPlaceholderText(APPLICATION->m_detectedOpenALPath);
|
ui->lineEditOpenALPath->setPlaceholderText(APPLICATION->m_detectedOpenALPath);
|
||||||
#else
|
#else
|
||||||
ui->lineEditGLFWPath->setPlaceholderText(tr("Path to %1 library file").arg(BuildConfig.OPENAL_LIBRARY_NAME));
|
ui->lineEditOpenALPath->setPlaceholderText(tr("Path to %1 library file").arg(BuildConfig.OPENAL_LIBRARY_NAME));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Performance
|
// Performance
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user