mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2026-06-29 18:09:59 +03:00
Compare commits
111 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96ad89845c | ||
|
|
a9c065174c | ||
|
|
4dec044883 | ||
|
|
c3a1fd7e49 | ||
|
|
03d6818570 | ||
|
|
f1a30bf251 | ||
|
|
7e4ea5a47e | ||
|
|
e3b3ef65ee | ||
|
|
6ef89db08e | ||
|
|
993243b6a3 | ||
|
|
1f22a88935 | ||
|
|
29a4ae516a | ||
|
|
8315113f26 | ||
|
|
5e4163d91c | ||
|
|
406b56060c | ||
|
|
33b515d0ee | ||
|
|
3a836e8f6d | ||
|
|
e2d633e2c4 | ||
|
|
e40dd81c38 | ||
|
|
7981c6c64e | ||
|
|
d32568fd5e | ||
|
|
5368452c9f | ||
|
|
52ddb88d34 | ||
|
|
0bd8f05f82 | ||
|
|
6ee81b527d | ||
|
|
f0725e9edf | ||
|
|
dcb65e8a64 | ||
|
|
9ceee0a6e7 | ||
|
|
d33874f24e | ||
|
|
66f7fb909d | ||
|
|
928adcdb4e | ||
|
|
a4db3bfb88 | ||
|
|
73a68659f2 | ||
|
|
35a99bb5e6 | ||
|
|
5e628d9258 | ||
|
|
29f68ba1cf | ||
|
|
6762a1f448 | ||
|
|
c18128dd9f | ||
|
|
52a42d63ba | ||
|
|
a4e86f213f | ||
|
|
13427d77db | ||
|
|
a26954dafa | ||
|
|
94d18c44f3 | ||
|
|
66452b16f8 | ||
|
|
9c80e019cb | ||
|
|
317b8eea9b | ||
|
|
c968bafb0a | ||
|
|
9b7c83ef23 | ||
|
|
5ee0286635 | ||
|
|
10bee70c42 | ||
|
|
1e07305803 | ||
|
|
8d8e9d0390 | ||
|
|
2128e87d92 | ||
|
|
dbfae0599f | ||
|
|
b2d7211254 | ||
|
|
0631359ec4 | ||
|
|
e81ec9c39c | ||
|
|
b30677ef10 | ||
|
|
75215b0d31 | ||
|
|
bf0aa5f980 | ||
|
|
5520dc6aaf | ||
|
|
6d59334777 | ||
|
|
e90965adc1 | ||
|
|
a524d93ada | ||
|
|
7101f15a2c | ||
|
|
131dc0acf3 | ||
|
|
fd4436880a | ||
|
|
2582a90b90 | ||
|
|
31cf378171 | ||
|
|
4173faba7a | ||
|
|
0b3fb6c4ce | ||
|
|
302e10a7d9 | ||
|
|
947a8faa0d | ||
|
|
ad83592834 | ||
|
|
3f9b6ae452 | ||
|
|
f1e382b035 | ||
|
|
a0797d00e3 | ||
|
|
3d805dff29 | ||
|
|
8f314c982a | ||
|
|
9389b9d582 | ||
|
|
df172c0923 | ||
|
|
47be7ae502 | ||
|
|
e1941a5794 | ||
|
|
f85d399928 | ||
|
|
3fbbebe93b | ||
|
|
1274eb7e48 | ||
|
|
68010c6c49 | ||
|
|
49e9671c96 | ||
|
|
819b4e49c8 | ||
|
|
a74b1dd79d | ||
|
|
add5966c52 | ||
|
|
c89150a26e | ||
|
|
c8c6304a15 | ||
|
|
8bfb9b90c1 | ||
|
|
da62b63f52 | ||
|
|
881bb22d45 | ||
|
|
0776291e55 | ||
|
|
c2d324aff3 | ||
|
|
af83cd92c0 | ||
|
|
a639091a39 | ||
|
|
ab71c44ed6 | ||
|
|
9fef9c7bd8 | ||
|
|
811e3de29b | ||
|
|
1ebe081e03 | ||
|
|
79be92ca74 | ||
|
|
8fcfebb321 | ||
|
|
01bb8c81cf | ||
|
|
708222bb80 | ||
|
|
474be07724 | ||
|
|
6b952403c9 | ||
|
|
7711a9aa81 |
84 changed files with 1202 additions and 479 deletions
26
.clang-tidy
26
.clang-tidy
|
|
@ -7,17 +7,17 @@ Checks:
|
||||||
# ^ Without unused-parameters the readability-identifier-naming check doesn't cause any warnings.
|
# ^ Without unused-parameters the readability-identifier-naming check doesn't cause any warnings.
|
||||||
|
|
||||||
CheckOptions:
|
CheckOptions:
|
||||||
- { key: readability-identifier-naming.ClassCase, value: PascalCase }
|
- { key: readability-identifier-naming.ClassCase, value: PascalCase }
|
||||||
- { key: readability-identifier-naming.EnumCase, value: PascalCase }
|
- { key: readability-identifier-naming.EnumCase, value: PascalCase }
|
||||||
- { key: readability-identifier-naming.FunctionCase, value: camelCase }
|
- { key: readability-identifier-naming.FunctionCase, value: camelCase }
|
||||||
- { key: readability-identifier-naming.GlobalVariableCase, value: camelCase }
|
- { key: readability-identifier-naming.GlobalVariableCase, value: camelCase }
|
||||||
- { key: readability-identifier-naming.GlobalFunctionCase, value: camelCase }
|
- { key: readability-identifier-naming.GlobalFunctionCase, value: camelCase }
|
||||||
- { key: readability-identifier-naming.GlobalConstantCase, value: SCREAMING_SNAKE_CASE }
|
- { key: readability-identifier-naming.GlobalConstantCase, value: SCREAMING_SNAKE_CASE }
|
||||||
- { key: readability-identifier-naming.MacroDefinitionCase, value: SCREAMING_SNAKE_CASE }
|
- { key: readability-identifier-naming.MacroDefinitionCase, value: SCREAMING_SNAKE_CASE }
|
||||||
- { key: readability-identifier-naming.ClassMemberCase, value: camelCase }
|
- { key: readability-identifier-naming.ClassMemberCase, value: camelCase }
|
||||||
- { key: readability-identifier-naming.PrivateMemberPrefix, value: m_ }
|
- { key: readability-identifier-naming.PrivateMemberPrefix, value: m_ }
|
||||||
- { key: readability-identifier-naming.ProtectedMemberPrefix, value: m_ }
|
- { key: readability-identifier-naming.ProtectedMemberPrefix, value: m_ }
|
||||||
- { key: readability-identifier-naming.PrivateStaticMemberPrefix, value: s_ }
|
- { key: readability-identifier-naming.PrivateStaticMemberPrefix, value: s_ }
|
||||||
- { key: readability-identifier-naming.ProtectedStaticMemberPrefix, value: s_ }
|
- { key: readability-identifier-naming.ProtectedStaticMemberPrefix, value: s_ }
|
||||||
- { key: readability-identifier-naming.PublicStaticConstantCase, value: SCREAMING_SNAKE_CASE }
|
- { key: readability-identifier-naming.PublicStaticConstantCase, value: SCREAMING_SNAKE_CASE }
|
||||||
- { key: readability-identifier-naming.EnumConstantCase, value: SCREAMING_SNAKE_CASE }
|
- { key: readability-identifier-naming.EnumConstantCase, value: PascalCase }
|
||||||
|
|
|
||||||
51
.github/actions/package/linux/action.yml
vendored
51
.github/actions/package/linux/action.yml
vendored
|
|
@ -52,15 +52,13 @@ runs:
|
||||||
- name: Package AppImage
|
- name: Package AppImage
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
VERSION: ${{ inputs.version }}
|
VERSION: ${{ github.ref_type == 'tag' && github.ref_name || inputs.version }}
|
||||||
BUILD_DIR: build
|
BUILD_DIR: build
|
||||||
INSTALL_APPIMAGE_DIR: install-appdir
|
INSTALL_APPIMAGE_DIR: install-appdir
|
||||||
|
|
||||||
GPG_PRIVATE_KEY: ${{ inputs.gpg-private-key }}
|
GPG_PRIVATE_KEY: ${{ inputs.gpg-private-key }}
|
||||||
run: |
|
run: |
|
||||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
|
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}
|
||||||
|
|
||||||
cp ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.{metainfo,appdata}.xml
|
|
||||||
|
|
||||||
if [ '${{ inputs.gpg-private-key-id }}' != '' ]; then
|
if [ '${{ inputs.gpg-private-key-id }}' != '' ]; then
|
||||||
echo "$GPG_PRIVATE_KEY" > privkey.asc
|
echo "$GPG_PRIVATE_KEY" > privkey.asc
|
||||||
|
|
@ -70,14 +68,32 @@ runs:
|
||||||
echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY
|
echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
appimagetool -s deploy "$INSTALL_APPIMAGE_DIR"/usr/share/applications/*.desktop
|
sharun lib4bin \
|
||||||
cp ~/bin/AppImageUpdate.AppImage "$INSTALL_APPIMAGE_DIR"/usr/bin/
|
--hard-links \
|
||||||
# FIXME(@getchoo): Validate AppStream information when https://github.com/probonopd/go-appimage/pull/379 is merged
|
--with-hooks \
|
||||||
|
--dst-dir "$INSTALL_APPIMAGE_DIR" \
|
||||||
|
"$INSTALL_APPIMAGE_DIR"/bin/* "$QT_PLUGIN_PATH"/*/*.so
|
||||||
|
|
||||||
|
cp ~/bin/AppImageUpdate.AppImage "$INSTALL_APPIMAGE_DIR"/bin/
|
||||||
|
# FIXME(@getchoo): gamemode doesn't seem to be very portable with DBus. Find a way to make it work!
|
||||||
|
find "$INSTALL_APPIMAGE_DIR" -name '*gamemode*' -exec rm {} +
|
||||||
|
|
||||||
|
ln -s org.prismlauncher.PrismLauncher.metainfo.xml "$INSTALL_APPIMAGE_DIR"/share/metainfo/org.prismlauncher.PrismLauncher.appdata.xml
|
||||||
|
ln -s share/applications/org.prismlauncher.PrismLauncher.desktop "$INSTALL_APPIMAGE_DIR"
|
||||||
|
ln -s share/icons/hicolor/256x256/apps/org.prismlauncher.PrismLauncher.png "$INSTALL_APPIMAGE_DIR"
|
||||||
|
mv "$INSTALL_APPIMAGE_DIR"/{sharun,AppRun}
|
||||||
|
ls -la "$INSTALL_APPIMAGE_DIR"
|
||||||
|
|
||||||
|
if [[ "${{ github.ref_type }}" == "tag" ]]; then
|
||||||
|
APPIMAGE_DEST="PrismLauncher-Linux-$APPIMAGE_ARCH.AppImage"
|
||||||
|
else
|
||||||
|
APPIMAGE_DEST="PrismLauncher-Linux-$VERSION-${{ inputs.build-type }}-$APPIMAGE_ARCH.AppImage"
|
||||||
|
fi
|
||||||
|
|
||||||
mkappimage \
|
mkappimage \
|
||||||
--no-appstream \
|
|
||||||
--updateinformation "gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-$APPIMAGE_ARCH.AppImage.zsync" \
|
--updateinformation "gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-$APPIMAGE_ARCH.AppImage.zsync" \
|
||||||
"$INSTALL_APPIMAGE_DIR" \
|
"$INSTALL_APPIMAGE_DIR" \
|
||||||
"PrismLauncher-Linux-$VERSION-${{ inputs.build-type }}-$APPIMAGE_ARCH.AppImage"
|
"$APPIMAGE_DEST"
|
||||||
|
|
||||||
- name: Package portable tarball
|
- name: Package portable tarball
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
@ -89,11 +105,16 @@ runs:
|
||||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
|
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
|
||||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||||
|
|
||||||
#the linked cmark .so is of the version that ubuntu uses, so without this it breaks on most updated distros
|
sharun lib4bin \
|
||||||
mkdir ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
--with-hooks \
|
||||||
cp /lib/$APPIMAGE_ARCH-linux-gnu/libcmark.so.0.* ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
--hard-links \
|
||||||
|
--dst-dir "$INSTALL_PORTABLE_DIR" \
|
||||||
|
"$INSTALL_PORTABLE_DIR"/bin/* "$QT_PLUGIN_PATH"/*/*.so
|
||||||
|
|
||||||
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
|
# FIXME(@getchoo): gamemode doesn't seem to be very portable with DBus. Find a way to make it work!
|
||||||
|
find "$INSTALL_PORTABLE_DIR" -name '*gamemode*' -exec rm {} +
|
||||||
|
|
||||||
|
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f -o -type l); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
|
||||||
cd ${{ env.INSTALL_PORTABLE_DIR }}
|
cd ${{ env.INSTALL_PORTABLE_DIR }}
|
||||||
tar -czf ../PrismLauncher-portable.tar.gz *
|
tar -czf ../PrismLauncher-portable.tar.gz *
|
||||||
|
|
||||||
|
|
@ -107,10 +128,10 @@ runs:
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-${{ env.APPIMAGE_ARCH }}.AppImage
|
name: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-${{ env.APPIMAGE_ARCH }}.AppImage
|
||||||
path: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-${{ env.APPIMAGE_ARCH }}.AppImage
|
path: PrismLauncher-${{ runner.os }}-*${{ env.APPIMAGE_ARCH }}.AppImage
|
||||||
|
|
||||||
- name: Upload AppImage Zsync
|
- name: Upload AppImage Zsync
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-${{ env.APPIMAGE_ARCH }}.AppImage.zsync
|
name: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-${{ env.APPIMAGE_ARCH }}.AppImage.zsync
|
||||||
path: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-${{ env.APPIMAGE_ARCH }}.AppImage.zsync
|
path: PrismLauncher-${{ runner.os }}-*${{ env.APPIMAGE_ARCH }}.AppImage.zsync
|
||||||
|
|
|
||||||
8
.github/actions/package/windows/action.yml
vendored
8
.github/actions/package/windows/action.yml
vendored
|
|
@ -54,13 +54,13 @@ runs:
|
||||||
Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
|
Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
|
||||||
|
|
||||||
- name: Emit warning for unsigned builds
|
- name: Emit warning for unsigned builds
|
||||||
if: ${{ github.ref_name != 'develop' || inputs.azure-client-id == '' }}
|
if: ${{ env.CI_HAS_ACCESS_TO_AZURE == '' || inputs.azure-client-id == '' }}
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
- name: Login to Azure
|
- name: Login to Azure
|
||||||
if: ${{ github.ref_name == 'develop' && inputs.azure-client-id != '' }}
|
if: ${{ env.CI_HAS_ACCESS_TO_AZURE != '' && inputs.azure-client-id != '' }}
|
||||||
uses: azure/login@v2
|
uses: azure/login@v2
|
||||||
with:
|
with:
|
||||||
client-id: ${{ inputs.azure-client-id }}
|
client-id: ${{ inputs.azure-client-id }}
|
||||||
|
|
@ -68,7 +68,7 @@ runs:
|
||||||
subscription-id: ${{ inputs.azure-subscription-id }}
|
subscription-id: ${{ inputs.azure-subscription-id }}
|
||||||
|
|
||||||
- name: Sign executables
|
- name: Sign executables
|
||||||
if: ${{ github.ref_name == 'develop' && inputs.azure-client-id != '' }}
|
if: ${{ env.CI_HAS_ACCESS_TO_AZURE != '' && inputs.azure-client-id != '' }}
|
||||||
uses: azure/trusted-signing-action@v0
|
uses: azure/trusted-signing-action@v0
|
||||||
with:
|
with:
|
||||||
endpoint: https://eus.codesigning.azure.net/
|
endpoint: https://eus.codesigning.azure.net/
|
||||||
|
|
@ -140,7 +140,7 @@ runs:
|
||||||
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
|
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
|
||||||
|
|
||||||
- name: Sign installer
|
- name: Sign installer
|
||||||
if: ${{ github.ref_name == 'develop' && inputs.azure-client-id != '' }}
|
if: ${{ env.CI_HAS_ACCESS_TO_AZURE != '' && inputs.azure-client-id != '' }}
|
||||||
uses: azure/trusted-signing-action@v0
|
uses: azure/trusted-signing-action@v0
|
||||||
with:
|
with:
|
||||||
endpoint: https://eus.codesigning.azure.net/
|
endpoint: https://eus.codesigning.azure.net/
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ inputs:
|
||||||
qt-version:
|
qt-version:
|
||||||
description: Version of Qt to use
|
description: Version of Qt to use
|
||||||
required: true
|
required: true
|
||||||
default: 6.9.3
|
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
build-type:
|
build-type:
|
||||||
|
|
@ -78,6 +77,5 @@ runs:
|
||||||
with:
|
with:
|
||||||
aqtversion: "==3.1.*"
|
aqtversion: "==3.1.*"
|
||||||
version: ${{ inputs.qt-version }}
|
version: ${{ inputs.qt-version }}
|
||||||
arch: ${{ inputs.qt-architecture }}
|
|
||||||
modules: qtimageformats qtnetworkauth
|
modules: qtimageformats qtnetworkauth
|
||||||
cache: ${{ inputs.build-type == 'Debug' }}
|
cache: ${{ inputs.build-type == 'Debug' }}
|
||||||
|
|
|
||||||
|
|
@ -12,29 +12,7 @@ runs:
|
||||||
dpkg-dev \
|
dpkg-dev \
|
||||||
ninja-build extra-cmake-modules pkg-config scdoc \
|
ninja-build extra-cmake-modules pkg-config scdoc \
|
||||||
cmark gamemode-dev libarchive-dev libcmark-dev libqrencode-dev zlib1g-dev \
|
cmark gamemode-dev libarchive-dev libcmark-dev libqrencode-dev zlib1g-dev \
|
||||||
libxcb-cursor-dev
|
libxcb-cursor-dev libtomlplusplus-dev
|
||||||
|
|
||||||
# TODO(@getchoo): Install with the above when all targets use Ubuntu 24.04
|
|
||||||
- name: Install tomlplusplus
|
|
||||||
if: ${{ runner.arch == 'ARM64' }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
sudo apt-get -y install libtomlplusplus-dev
|
|
||||||
|
|
||||||
# FIXME(@getchoo): THIS IS HORRIBLE TO DO!
|
|
||||||
# Install tomlplusplus from Ubuntu 24.04, since it never got backported to 22.04
|
|
||||||
# I've done too much to continue keeping this as a submodule....
|
|
||||||
- name: Install tomlplusplus from 24.04
|
|
||||||
if: ${{ runner.arch != 'ARM64' }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
deb_arch="$(dpkg-architecture -q DEB_HOST_ARCH)"
|
|
||||||
curl -Lo libtomlplusplus-dev.deb http://mirrors.kernel.org/ubuntu/pool/universe/t/tomlplusplus/libtomlplusplus-dev_3.4.0+ds-0.2build1_"$deb_arch".deb
|
|
||||||
curl -Lo libtomlplusplus3t64.deb http://mirrors.kernel.org/ubuntu/pool/universe/t/tomlplusplus/libtomlplusplus3t64_3.4.0+ds-0.2build1_"$deb_arch".deb
|
|
||||||
sudo dpkg -i libtomlplusplus3t64.deb
|
|
||||||
sudo dpkg -i libtomlplusplus-dev.deb
|
|
||||||
rm *.deb
|
|
||||||
sudo apt-get install -f
|
|
||||||
|
|
||||||
- name: Setup AppImage tooling
|
- name: Setup AppImage tooling
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
@ -56,19 +34,20 @@ runs:
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
gh release download \
|
||||||
|
--repo VHSgunzo/sharun \
|
||||||
|
--pattern "sharun-$APPIMAGE_ARCH-aio" \
|
||||||
|
--output ~/bin/sharun
|
||||||
|
|
||||||
|
# FIXME!: revert this to probonopd/go-appimage once https://github.com/probonopd/go-appimage/pull/377 is merged!
|
||||||
gh release download continuous \
|
gh release download continuous \
|
||||||
--repo probonopd/go-appimage \
|
--repo DioEgizio/go-appimage \
|
||||||
--pattern "appimagetool-*-$APPIMAGE_ARCH.AppImage" \
|
|
||||||
--output ~/bin/appimagetool
|
|
||||||
gh release download continuous \
|
|
||||||
--repo probonopd/go-appimage \
|
|
||||||
--pattern "mkappimage-*-$APPIMAGE_ARCH.AppImage" \
|
--pattern "mkappimage-*-$APPIMAGE_ARCH.AppImage" \
|
||||||
--output ~/bin/mkappimage
|
--output ~/bin/mkappimage
|
||||||
chmod +x ~/bin/appimagetool ~/bin/mkappimage
|
|
||||||
echo "$HOME/bin" >> "$GITHUB_PATH"
|
|
||||||
|
|
||||||
gh release download \
|
gh release download \
|
||||||
--repo AppImageCommunity/AppImageUpdate \
|
--repo AppImageCommunity/AppImageUpdate \
|
||||||
--pattern "AppImageUpdate-$APPIMAGE_ARCH.AppImage" \
|
--pattern "AppImageUpdate-$APPIMAGE_ARCH.AppImage" \
|
||||||
--output ~/bin/AppImageUpdate.AppImage
|
--output ~/bin/AppImageUpdate.AppImage
|
||||||
chmod +x ~/bin/AppImageUpdate.AppImage
|
chmod +x ~/bin/*
|
||||||
|
echo "$HOME/bin" >> "$GITHUB_PATH"
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ runs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
brew update
|
brew update
|
||||||
brew install ninja extra-cmake-modules temurin@17
|
brew install ninja extra-cmake-modules temurin@17 mono
|
||||||
|
|
||||||
- name: Set JAVA_HOME
|
- name: Set JAVA_HOME
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
@ -44,4 +44,4 @@ runs:
|
||||||
- name: Setup vcpkg environment
|
- name: Setup vcpkg environment
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
echo "CMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" >> "$GITHUB_ENV"
|
echo "VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> "$GITHUB_ENV"
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ runs:
|
||||||
if: ${{ inputs.msystem == '' }}
|
if: ${{ inputs.msystem == '' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
echo "CMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" >> "$GITHUB_ENV"
|
echo "VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
- name: Setup MSYS2 (MinGW)
|
- name: Setup MSYS2 (MinGW)
|
||||||
if: ${{ inputs.msystem != '' }}
|
if: ${{ inputs.msystem != '' }}
|
||||||
|
|
|
||||||
22
.github/workflows/build.yml
vendored
22
.github/workflows/build.yml
vendored
|
|
@ -62,6 +62,9 @@ on:
|
||||||
description: Type of build (Debug or Release)
|
description: Type of build (Debug or Release)
|
||||||
type: string
|
type: string
|
||||||
default: Debug
|
default: Debug
|
||||||
|
environment:
|
||||||
|
description: Deployment environment to run under
|
||||||
|
type: string
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
build-type:
|
build-type:
|
||||||
|
|
@ -73,6 +76,8 @@ jobs:
|
||||||
build:
|
build:
|
||||||
name: Build (${{ matrix.artifact-name }})
|
name: Build (${{ matrix.artifact-name }})
|
||||||
|
|
||||||
|
environment: ${{ inputs.environment || '' }}
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
# Required for Azure Trusted Signing
|
# Required for Azure Trusted Signing
|
||||||
id-token: write
|
id-token: write
|
||||||
|
|
@ -83,17 +88,15 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-22.04
|
- os: ubuntu-24.04
|
||||||
artifact-name: Linux
|
artifact-name: Linux
|
||||||
cmake-preset: linux
|
cmake-preset: linux
|
||||||
|
qt-version: 6.10.1
|
||||||
|
|
||||||
# NOTE(@getchoo): Yes, we're intentionally using 24.04 here!!!
|
|
||||||
#
|
|
||||||
# It's not really documented anywhere AFAICT, but upstream Qt binaries
|
|
||||||
# *for the same version* are compiled against 24.04 on ARM, and *not* 22.04 like x64
|
|
||||||
- os: ubuntu-24.04-arm
|
- os: ubuntu-24.04-arm
|
||||||
artifact-name: Linux-aarch64
|
artifact-name: Linux-aarch64
|
||||||
cmake-preset: linux
|
cmake-preset: linux
|
||||||
|
qt-version: 6.10.1
|
||||||
|
|
||||||
- os: windows-2022
|
- os: windows-2022
|
||||||
artifact-name: Windows-MinGW-w64
|
artifact-name: Windows-MinGW-w64
|
||||||
|
|
@ -112,16 +115,19 @@ jobs:
|
||||||
cmake-preset: windows_msvc
|
cmake-preset: windows_msvc
|
||||||
# TODO(@getchoo): This is the default in setup-dependencies/windows. Why isn't it working?!?!
|
# TODO(@getchoo): This is the default in setup-dependencies/windows. Why isn't it working?!?!
|
||||||
vcvars-arch: amd64
|
vcvars-arch: amd64
|
||||||
|
qt-version: 6.10.1
|
||||||
|
|
||||||
- os: windows-11-arm
|
- os: windows-11-arm
|
||||||
artifact-name: Windows-MSVC-arm64
|
artifact-name: Windows-MSVC-arm64
|
||||||
cmake-preset: windows_msvc
|
cmake-preset: windows_msvc
|
||||||
vcvars-arch: arm64
|
vcvars-arch: arm64
|
||||||
|
qt-version: 6.10.1
|
||||||
|
|
||||||
- os: macos-14
|
- os: macos-26
|
||||||
artifact-name: macOS
|
artifact-name: macOS
|
||||||
cmake-preset: macos_universal
|
cmake-preset: macos_universal
|
||||||
macosx-deployment-target: 12.0
|
macosx-deployment-target: 12.0
|
||||||
|
qt-version: 6.9.3
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
|
@ -155,7 +161,7 @@ jobs:
|
||||||
artifact-name: ${{ matrix.artifact-name }}
|
artifact-name: ${{ matrix.artifact-name }}
|
||||||
msystem: ${{ matrix.msystem }}
|
msystem: ${{ matrix.msystem }}
|
||||||
vcvars-arch: ${{ matrix.vcvars-arch }}
|
vcvars-arch: ${{ matrix.vcvars-arch }}
|
||||||
qt-architecture: ${{ matrix.qt-architecture }}
|
qt-version: ${{ matrix.qt-version }}
|
||||||
|
|
||||||
##
|
##
|
||||||
# BUILD
|
# BUILD
|
||||||
|
|
@ -214,6 +220,8 @@ jobs:
|
||||||
- name: Package (Windows)
|
- name: Package (Windows)
|
||||||
if: ${{ runner.os == 'Windows' }}
|
if: ${{ runner.os == 'Windows' }}
|
||||||
uses: ./.github/actions/package/windows
|
uses: ./.github/actions/package/windows
|
||||||
|
env:
|
||||||
|
CI_HAS_ACCESS_TO_AZURE: ${{ vars.CI_HAS_ACCESS_TO_AZURE || '' }}
|
||||||
with:
|
with:
|
||||||
version: ${{ steps.short-version.outputs.version }}
|
version: ${{ steps.short-version.outputs.version }}
|
||||||
build-type: ${{ steps.setup-dependencies.outputs.build-type }}
|
build-type: ${{ steps.setup-dependencies.outputs.build-type }}
|
||||||
|
|
|
||||||
1
.github/workflows/codeql.yml
vendored
1
.github/workflows/codeql.yml
vendored
|
|
@ -79,6 +79,7 @@ jobs:
|
||||||
uses: ./.github/actions/setup-dependencies
|
uses: ./.github/actions/setup-dependencies
|
||||||
with:
|
with:
|
||||||
build-type: Debug
|
build-type: Debug
|
||||||
|
qt-version: 6.10.1
|
||||||
|
|
||||||
- name: Configure and Build
|
- name: Configure and Build
|
||||||
run: |
|
run: |
|
||||||
|
|
|
||||||
3
.github/workflows/flatpak.yml
vendored
3
.github/workflows/flatpak.yml
vendored
|
|
@ -77,9 +77,6 @@ jobs:
|
||||||
- os: ubuntu-22.04
|
- os: ubuntu-22.04
|
||||||
arch: x86_64
|
arch: x86_64
|
||||||
|
|
||||||
- os: ubuntu-22.04-arm
|
|
||||||
arch: aarch64
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
container:
|
container:
|
||||||
|
|
|
||||||
3
.github/workflows/nix.yml
vendored
3
.github/workflows/nix.yml
vendored
|
|
@ -86,9 +86,6 @@ jobs:
|
||||||
- os: ubuntu-22.04-arm
|
- os: ubuntu-22.04-arm
|
||||||
system: aarch64-linux
|
system: aarch64-linux
|
||||||
|
|
||||||
- os: macos-15-intel
|
|
||||||
system: x86_64-darwin
|
|
||||||
|
|
||||||
- os: macos-14
|
- os: macos-14
|
||||||
system: aarch64-darwin
|
system: aarch64-darwin
|
||||||
|
|
||||||
|
|
|
||||||
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
|
|
@ -11,6 +11,7 @@ jobs:
|
||||||
uses: ./.github/workflows/build.yml
|
uses: ./.github/workflows/build.yml
|
||||||
with:
|
with:
|
||||||
build-type: Release
|
build-type: Release
|
||||||
|
environment: Release
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
create_release:
|
create_release:
|
||||||
|
|
@ -34,6 +35,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
|
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
|
||||||
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||||
|
mv PrismLauncher-Linux-aarch64-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-aarch64-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||||
mv PrismLauncher-*.AppImage/PrismLauncher-*-x86_64.AppImage PrismLauncher-Linux-x86_64.AppImage
|
mv PrismLauncher-*.AppImage/PrismLauncher-*-x86_64.AppImage PrismLauncher-Linux-x86_64.AppImage
|
||||||
mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*-x86_64.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
|
mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*-x86_64.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||||
mv PrismLauncher-*.AppImage/PrismLauncher-*-aarch64.AppImage PrismLauncher-Linux-aarch64.AppImage
|
mv PrismLauncher-*.AppImage/PrismLauncher-*-aarch64.AppImage PrismLauncher-Linux-aarch64.AppImage
|
||||||
|
|
@ -88,6 +90,7 @@ jobs:
|
||||||
name: Prism Launcher ${{ env.VERSION }}
|
name: Prism Launcher ${{ env.VERSION }}
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: false
|
prerelease: false
|
||||||
|
fail_on_unmatched_files: true
|
||||||
files: |
|
files: |
|
||||||
PrismLauncher-Linux-x86_64.AppImage
|
PrismLauncher-Linux-x86_64.AppImage
|
||||||
PrismLauncher-Linux-x86_64.AppImage.zsync
|
PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||||
|
|
|
||||||
110
CMakeLists.txt
110
CMakeLists.txt
|
|
@ -1,6 +1,9 @@
|
||||||
cmake_minimum_required(VERSION 3.22) # minimum version required by Qt
|
cmake_minimum_required(VERSION 3.22) # minimum version required by Qt
|
||||||
|
|
||||||
project(Launcher)
|
project(Launcher LANGUAGES C CXX)
|
||||||
|
if(APPLE)
|
||||||
|
enable_language(OBJC OBJCXX)
|
||||||
|
endif()
|
||||||
|
|
||||||
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
|
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
|
||||||
if(IS_IN_SOURCE_BUILD)
|
if(IS_IN_SOURCE_BUILD)
|
||||||
|
|
@ -79,19 +82,6 @@ else()
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,8388608 ${CMAKE_EXE_LINKER_FLAGS}")
|
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,8388608 ${CMAKE_EXE_LINKER_FLAGS}")
|
||||||
|
|
||||||
# Emit PDBs for WinDbg, etc.
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--pdb= ${CMAKE_EXE_LINKER_FLAGS}")
|
|
||||||
|
|
||||||
foreach(lang C CXX)
|
|
||||||
set("CMAKE_${lang}_FLAGS" "-gcodeview ${CMAKE_${lang}_FLAGS}")
|
|
||||||
|
|
||||||
# Force-enabling this to use generator expressions like TARGET_PDB_FILE
|
|
||||||
# (and because we can actually emit PDBs)
|
|
||||||
set("CMAKE_${lang}_LINKER_SUPPORTS_PDB" ON)
|
|
||||||
endforeach()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# -ffunction-sections and -fdata-sections help reduce binary size
|
# -ffunction-sections and -fdata-sections help reduce binary size
|
||||||
# -mguard=cf enables Control Flow Guard
|
# -mguard=cf enables Control Flow Guard
|
||||||
# TODO: Look into -gc-sections to further reduce binary size
|
# TODO: Look into -gc-sections to further reduce binary size
|
||||||
|
|
@ -116,30 +106,24 @@ endif()
|
||||||
option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF)
|
option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF)
|
||||||
|
|
||||||
# If this is a Debug build turn on address sanitiser
|
# If this is a Debug build turn on address sanitiser
|
||||||
if ((CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") AND DEBUG_ADDRESS_SANITIZER)
|
if (DEBUG_ADDRESS_SANITIZER)
|
||||||
message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off")
|
message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off")
|
||||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
|
||||||
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
set(USE_ASAN_COMPILE_OPTIONS $<AND:$<CONFIG:Debug,RelWithDebInfo>,$<COMPILE_LANGUAGE:C,CXX>>)
|
||||||
# using clang with clang-cl front end
|
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||||
message(STATUS "Address Sanitizer available on Clang MSVC frontend")
|
message(STATUS "Using Address Sanitizer compile options for MSVC frontend")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
|
add_compile_options(
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
|
$<${USE_ASAN_COMPILE_OPTIONS}:/fsanitize=address>
|
||||||
else()
|
$<${USE_ASAN_COMPILE_OPTIONS}:/Oy->
|
||||||
# AppleClang and Clang
|
)
|
||||||
message(STATUS "Address Sanitizer available on Clang")
|
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=null")
|
message(STATUS "Using Address Sanitizer compile options for GCC/Clang")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=null")
|
add_compile_options(
|
||||||
endif()
|
$<${USE_ASAN_COMPILE_OPTIONS}:-fsanitize=address,undefined>
|
||||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
$<${USE_ASAN_COMPILE_OPTIONS}:-fno-omit-frame-pointer>
|
||||||
# GCC
|
$<${USE_ASAN_COMPILE_OPTIONS}:-fno-sanitize-recover=null>
|
||||||
message(STATUS "Address Sanitizer available on GCC")
|
)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover")
|
link_libraries("asan" "ubsan")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover")
|
|
||||||
link_libraries("asan")
|
|
||||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|
||||||
message(STATUS "Address Sanitizer available on MSVC")
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
|
|
||||||
else()
|
else()
|
||||||
message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}")
|
message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}")
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -192,7 +176,7 @@ set(Launcher_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" CACHE S
|
||||||
######## Set version numbers ########
|
######## Set version numbers ########
|
||||||
set(Launcher_VERSION_MAJOR 10)
|
set(Launcher_VERSION_MAJOR 10)
|
||||||
set(Launcher_VERSION_MINOR 0)
|
set(Launcher_VERSION_MINOR 0)
|
||||||
set(Launcher_VERSION_PATCH 0)
|
set(Launcher_VERSION_PATCH 1)
|
||||||
|
|
||||||
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}")
|
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}")
|
||||||
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}.0")
|
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}.0")
|
||||||
|
|
@ -337,7 +321,7 @@ if(NOT LibArchive_FOUND)
|
||||||
pkg_check_modules(libarchive REQUIRED IMPORTED_TARGET libarchive)
|
pkg_check_modules(libarchive REQUIRED IMPORTED_TARGET libarchive)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(tomlplusplus 3.2.0 REQUIRED)
|
find_package(tomlplusplus 3.2.0)
|
||||||
# fallback to pkgconfig, important especially as many distros package toml++ built with meson
|
# fallback to pkgconfig, important especially as many distros package toml++ built with meson
|
||||||
if(NOT tomlplusplus_FOUND)
|
if(NOT tomlplusplus_FOUND)
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
@ -379,7 +363,7 @@ if(UNIX AND APPLE)
|
||||||
# Mac bundle settings
|
# Mac bundle settings
|
||||||
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_DisplayName}")
|
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_DisplayName}")
|
||||||
set(MACOSX_BUNDLE_INFO_STRING "${Launcher_DisplayName}: A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.")
|
set(MACOSX_BUNDLE_INFO_STRING "${Launcher_DisplayName}: A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.")
|
||||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.prismlauncher.${Launcher_Name}")
|
set(MACOSX_BUNDLE_GUI_IDENTIFIER "${Launcher_AppID}")
|
||||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_NAME}")
|
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_NAME}")
|
||||||
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}")
|
||||||
|
|
@ -399,6 +383,52 @@ if(UNIX AND APPLE)
|
||||||
# Add the icon
|
# Add the icon
|
||||||
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
|
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
|
||||||
|
|
||||||
|
find_program(ACTOOL_EXE actool DOC "Path to the apple asset catalog compiler")
|
||||||
|
if(ACTOOL_EXE)
|
||||||
|
execute_process(
|
||||||
|
COMMAND xcodebuild -version
|
||||||
|
OUTPUT_VARIABLE XCODE_VERSION_OUTPUT
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
|
||||||
|
string(REGEX MATCH "Xcode ([0-9]+\.[0-9]+)" XCODE_VERSION_MATCH "${XCODE_VERSION_OUTPUT}")
|
||||||
|
if(XCODE_VERSION_MATCH)
|
||||||
|
set(XCODE_VERSION ${CMAKE_MATCH_1})
|
||||||
|
else()
|
||||||
|
set(XCODE_VERSION 0.0)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(XCODE_VERSION VERSION_GREATER_EQUAL 26.0)
|
||||||
|
set(ASSETS_OUT_DIR "${CMAKE_BINARY_DIR}/program_info")
|
||||||
|
set(GENERATED_ASSETS_CAR "${ASSETS_OUT_DIR}/Assets.car")
|
||||||
|
set(ICON_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_Branding_MAC_ICON}")
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${GENERATED_ASSETS_CAR}"
|
||||||
|
COMMAND ${ACTOOL_EXE} "${ICON_SOURCE}"
|
||||||
|
--compile "${ASSETS_OUT_DIR}"
|
||||||
|
--output-partial-info-plist /dev/null
|
||||||
|
--app-icon PrismLauncher
|
||||||
|
--enable-on-demand-resources NO
|
||||||
|
--target-device mac
|
||||||
|
--minimum-deployment-target ${CMAKE_OSX_DEPLOYMENT_TARGET}
|
||||||
|
--platform macosx
|
||||||
|
DEPENDS "${ICON_SOURCE}"
|
||||||
|
COMMENT "Compiling asset catalog (${ICON_SOURCE})"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
add_custom_target(compile_assets ALL DEPENDS "${GENERATED_ASSETS_CAR}")
|
||||||
|
install(FILES "${GENERATED_ASSETS_CAR}" DESTINATION "${RESOURCES_DEST_DIR}")
|
||||||
|
else()
|
||||||
|
message(WARNING "Xcode ${XCODE_VERSION} is too old. Minimum required version is 26.0. Not compiling liquid glass icons.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
else()
|
||||||
|
message(WARNING "actool not found. Cannot compile macOS app icons.\n"
|
||||||
|
"Install Xcode command line tools: 'xcode-select --install'")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
elseif(UNIX)
|
elseif(UNIX)
|
||||||
include(KDEInstallDirs)
|
include(KDEInstallDirs)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,9 @@
|
||||||
"type": "equals",
|
"type": "equals",
|
||||||
"lhs": "${hostSystemName}",
|
"lhs": "${hostSystemName}",
|
||||||
"rhs": "Darwin"
|
"rhs": "Darwin"
|
||||||
|
},
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_TOOLCHAIN_FILE": "$penv{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -50,6 +53,7 @@
|
||||||
"macos"
|
"macos"
|
||||||
],
|
],
|
||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
|
"CMAKE_TOOLCHAIN_FILE": "$penv{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
|
||||||
"CMAKE_OSX_ARCHITECTURES": "x86_64;arm64",
|
"CMAKE_OSX_ARCHITECTURES": "x86_64;arm64",
|
||||||
"VCPKG_TARGET_TRIPLET": "universal-osx"
|
"VCPKG_TARGET_TRIPLET": "universal-osx"
|
||||||
}
|
}
|
||||||
|
|
@ -76,6 +80,9 @@
|
||||||
"type": "equals",
|
"type": "equals",
|
||||||
"lhs": "${hostSystemName}",
|
"lhs": "${hostSystemName}",
|
||||||
"rhs": "Windows"
|
"rhs": "Windows"
|
||||||
|
},
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_TOOLCHAIN_FILE": "$penv{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ Please also follow the project's conventions for C++:
|
||||||
- Public, private or protected `static const` class data members should be formatted as `SCREAMING_SNAKE_CASE`: `MAX_VALUE`.
|
- Public, private or protected `static const` class data members should be formatted as `SCREAMING_SNAKE_CASE`: `MAX_VALUE`.
|
||||||
- Class function members should be formatted as `camelCase` without a prefix: `incrementCounter`.
|
- Class function members should be formatted as `camelCase` without a prefix: `incrementCounter`.
|
||||||
- Global functions and non-`const` global variables should be formatted as `camelCase` without a prefix: `globalData`.
|
- Global functions and non-`const` global variables should be formatted as `camelCase` without a prefix: `globalData`.
|
||||||
- `const` global variables, macros, and enum constants should be formatted as `SCREAMING_SNAKE_CASE`: `LIGHT_GRAY`.
|
- `const` global variables and macros should be formatted as `SCREAMING_SNAKE_CASE`: `LIGHT_GRAY`.
|
||||||
|
- enum constants should be formatted as `PascalCase`: `CamelusBactrianus`
|
||||||
- Avoid inventing acronyms or abbreviations especially for a name of multiple words - like `tp` for `texturePack`.
|
- Avoid inventing acronyms or abbreviations especially for a name of multiple words - like `tp` for `texturePack`.
|
||||||
- Avoid using `[[nodiscard]]` unless ignoring the return value is likely to cause a bug in cases such as:
|
- Avoid using `[[nodiscard]]` unless ignoring the return value is likely to cause a bug in cases such as:
|
||||||
- A function allocates memory or another resource and the caller needs to clean it up.
|
- A function allocates memory or another resource and the caller needs to clean it up.
|
||||||
|
|
@ -30,7 +31,7 @@ Here is what these conventions with the formatting configuration look like:
|
||||||
|
|
||||||
constexpr double PI = 3.14159;
|
constexpr double PI = 3.14159;
|
||||||
|
|
||||||
enum class PizzaToppings { HAM_AND_PINEAPPLE, OREO_AND_KETCHUP };
|
enum class PizzaToppings { HamAndPineapple, OreoAndKetchup };
|
||||||
|
|
||||||
struct Person {
|
struct Person {
|
||||||
QString name;
|
QString name;
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <qstringliteral.h>
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,9 @@
|
||||||
<key>CFBundleGetInfoString</key>
|
<key>CFBundleGetInfoString</key>
|
||||||
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
|
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
|
<string>${Launcher_Name}</string>
|
||||||
|
<key>CFBundleIconName</key>
|
||||||
|
<string>${Launcher_Name}</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
|
@ -42,6 +44,8 @@
|
||||||
<true/>
|
<true/>
|
||||||
<key>LSRequiresCarbon</key>
|
<key>LSRequiresCarbon</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>LSApplicationCategoryType</key>
|
||||||
|
<string>public.app-category.games</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||||
<key>SUPublicEDKey</key>
|
<key>SUPublicEDKey</key>
|
||||||
|
|
|
||||||
17
flake.lock
generated
17
flake.lock
generated
|
|
@ -18,18 +18,15 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1765472234,
|
"lastModified": 1766473571,
|
||||||
"narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=",
|
"narHash": "sha256-QvjEJNgMVuOootbR+DEfbiW+zSK57U32CE0jmVdcNjQ=",
|
||||||
"owner": "NixOS",
|
"rev": "76701a179d3a98b07653e2b0409847499b2a07d3",
|
||||||
"repo": "nixpkgs",
|
"type": "tarball",
|
||||||
"rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b",
|
"url": "https://releases.nixos.org/nixos/25.11/nixos-25.11.2403.76701a179d3a/nixexprs.tar.xz"
|
||||||
"type": "github"
|
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"type": "tarball",
|
||||||
"ref": "nixos-unstable",
|
"url": "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz"
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
nixpkgs.url = "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz";
|
||||||
|
|
||||||
libnbtplusplus = {
|
libnbtplusplus = {
|
||||||
url = "github:PrismLauncher/libnbtplusplus";
|
url = "github:PrismLauncher/libnbtplusplus";
|
||||||
|
|
|
||||||
|
|
@ -371,25 +371,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
QString origcwdPath = QDir::currentPath();
|
QString origcwdPath = QDir::currentPath();
|
||||||
#if defined(Q_OS_LINUX)
|
|
||||||
const QString binFilePath = applicationFilePath();
|
|
||||||
const bool isAppImage = binFilePath.startsWith("/tmp/.mount_");
|
|
||||||
// Yes, this can technically trigger the logic below if someone makes an AppImage with an actual launcher exe named "ld-linux"
|
|
||||||
// Please don't :)
|
|
||||||
const bool executedFromLinker = QFileInfo(binFilePath).fileName().startsWith("ld-linux");
|
|
||||||
|
|
||||||
// NOTE(@getchoo): In order for `go-appimage` to generate self-contained AppImages, it executes apps from a bundled linker at
|
|
||||||
// <root>/lib64
|
|
||||||
// This is not the path to our actual binary, which we want
|
|
||||||
QString binPath;
|
|
||||||
if (isAppImage && executedFromLinker) {
|
|
||||||
binPath = FS::PathCombine(applicationDirPath(), "../usr/bin");
|
|
||||||
} else {
|
|
||||||
binPath = applicationDirPath();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
QString binPath = applicationDirPath();
|
QString binPath = applicationDirPath();
|
||||||
#endif
|
|
||||||
|
|
||||||
{
|
{
|
||||||
// Root path is used for updates and portable data
|
// Root path is used for updates and portable data
|
||||||
|
|
|
||||||
|
|
@ -26,13 +26,13 @@ set(CORE_SOURCES
|
||||||
NullInstance.h
|
NullInstance.h
|
||||||
MMCZip.h
|
MMCZip.h
|
||||||
MMCZip.cpp
|
MMCZip.cpp
|
||||||
archive/ArchiveReader.cpp
|
archive/ArchiveReader.cpp
|
||||||
archive/ArchiveReader.h
|
archive/ArchiveReader.h
|
||||||
archive/ArchiveWriter.cpp
|
archive/ArchiveWriter.cpp
|
||||||
archive/ArchiveWriter.h
|
archive/ArchiveWriter.h
|
||||||
archive/ExportToZipTask.cpp
|
archive/ExportToZipTask.cpp
|
||||||
archive/ExportToZipTask.h
|
archive/ExportToZipTask.h
|
||||||
archive/ExtractZipTask.cpp
|
archive/ExtractZipTask.cpp
|
||||||
archive/ExtractZipTask.h
|
archive/ExtractZipTask.h
|
||||||
StringUtils.h
|
StringUtils.h
|
||||||
StringUtils.cpp
|
StringUtils.cpp
|
||||||
|
|
@ -346,6 +346,7 @@ set(MINECRAFT_SOURCES
|
||||||
minecraft/mod/TexturePackFolderModel.h
|
minecraft/mod/TexturePackFolderModel.h
|
||||||
minecraft/mod/TexturePackFolderModel.cpp
|
minecraft/mod/TexturePackFolderModel.cpp
|
||||||
minecraft/mod/ShaderPackFolderModel.h
|
minecraft/mod/ShaderPackFolderModel.h
|
||||||
|
minecraft/mod/ShaderPackFolderModel.cpp
|
||||||
minecraft/mod/tasks/ResourceFolderLoadTask.h
|
minecraft/mod/tasks/ResourceFolderLoadTask.h
|
||||||
minecraft/mod/tasks/ResourceFolderLoadTask.cpp
|
minecraft/mod/tasks/ResourceFolderLoadTask.cpp
|
||||||
minecraft/mod/tasks/LocalModParseTask.h
|
minecraft/mod/tasks/LocalModParseTask.h
|
||||||
|
|
@ -621,10 +622,10 @@ set(PRISMUPDATER_SOURCES
|
||||||
# Zip
|
# Zip
|
||||||
MMCZip.h
|
MMCZip.h
|
||||||
MMCZip.cpp
|
MMCZip.cpp
|
||||||
archive/ArchiveReader.cpp
|
archive/ArchiveReader.cpp
|
||||||
archive/ArchiveReader.h
|
archive/ArchiveReader.h
|
||||||
archive/ArchiveWriter.cpp
|
archive/ArchiveWriter.cpp
|
||||||
archive/ArchiveWriter.h
|
archive/ArchiveWriter.h
|
||||||
|
|
||||||
# Time
|
# Time
|
||||||
MMCTime.h
|
MMCTime.h
|
||||||
|
|
@ -1300,6 +1301,16 @@ endif()
|
||||||
|
|
||||||
include(CompilerWarnings)
|
include(CompilerWarnings)
|
||||||
|
|
||||||
|
######## Precompiled Headers ###########
|
||||||
|
|
||||||
|
set(PRECOMPILED_HEADERS
|
||||||
|
include/base.pch.hpp
|
||||||
|
include/qtcore.pch.hpp
|
||||||
|
include/qtgui.pch.hpp
|
||||||
|
)
|
||||||
|
|
||||||
|
####### Targets ########
|
||||||
|
|
||||||
# Add executable
|
# Add executable
|
||||||
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES})
|
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES})
|
||||||
set_project_warnings(Launcher_logic
|
set_project_warnings(Launcher_logic
|
||||||
|
|
@ -1308,6 +1319,7 @@ set_project_warnings(Launcher_logic
|
||||||
"${Launcher_GCC_WARNINGS}")
|
"${Launcher_GCC_WARNINGS}")
|
||||||
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
|
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
|
||||||
|
target_precompile_headers(Launcher_logic PRIVATE ${PRECOMPILED_HEADERS})
|
||||||
target_link_libraries(Launcher_logic
|
target_link_libraries(Launcher_logic
|
||||||
systeminfo
|
systeminfo
|
||||||
Launcher_murmur2
|
Launcher_murmur2
|
||||||
|
|
@ -1389,6 +1401,7 @@ endif()
|
||||||
target_link_libraries(Launcher_logic)
|
target_link_libraries(Launcher_logic)
|
||||||
|
|
||||||
add_executable(${Launcher_Name} MACOSX_BUNDLE WIN32 main.cpp ${LAUNCHER_RCS})
|
add_executable(${Launcher_Name} MACOSX_BUNDLE WIN32 main.cpp ${LAUNCHER_RCS})
|
||||||
|
target_precompile_headers(${Launcher_Name} REUSE_FROM Launcher_logic)
|
||||||
target_link_libraries(${Launcher_Name} Launcher_logic)
|
target_link_libraries(${Launcher_Name} Launcher_logic)
|
||||||
|
|
||||||
if(DEFINED Launcher_APP_BINARY_NAME)
|
if(DEFINED Launcher_APP_BINARY_NAME)
|
||||||
|
|
@ -1412,14 +1425,15 @@ install(TARGETS ${Launcher_Name}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Deploy PDBs
|
# Deploy PDBs
|
||||||
if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
|
if(CMAKE_CXX_LINKER_SUPPORTS_PDB)
|
||||||
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}> DESTINATION ${BINARY_DEST_DIR})
|
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}> DESTINATION ${BINARY_DEST_DIR} OPTIONAL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(Launcher_BUILD_UPDATER)
|
if(Launcher_BUILD_UPDATER)
|
||||||
# Updater
|
# Updater
|
||||||
add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI})
|
add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI})
|
||||||
target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
target_precompile_headers(prism_updater_logic PRIVATE ${PRECOMPILED_HEADERS})
|
||||||
target_link_libraries(prism_updater_logic
|
target_link_libraries(prism_updater_logic
|
||||||
${ZLIB_LIBRARIES}
|
${ZLIB_LIBRARIES}
|
||||||
systeminfo
|
systeminfo
|
||||||
|
|
@ -1439,6 +1453,7 @@ if(Launcher_BUILD_UPDATER)
|
||||||
add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp)
|
add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp)
|
||||||
target_sources("${Launcher_Name}_updater" PRIVATE updater/prismupdater/updater.exe.manifest)
|
target_sources("${Launcher_Name}_updater" PRIVATE updater/prismupdater/updater.exe.manifest)
|
||||||
target_link_libraries("${Launcher_Name}_updater" prism_updater_logic)
|
target_link_libraries("${Launcher_Name}_updater" prism_updater_logic)
|
||||||
|
target_precompile_headers("${Launcher_Name}_updater" REUSE_FROM prism_updater_logic)
|
||||||
|
|
||||||
if(DEFINED Launcher_APP_BINARY_NAME)
|
if(DEFINED Launcher_APP_BINARY_NAME)
|
||||||
set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater")
|
set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater")
|
||||||
|
|
@ -1455,8 +1470,8 @@ if(Launcher_BUILD_UPDATER)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Deploy PDBs
|
# Deploy PDBs
|
||||||
if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
|
if(CMAKE_CXX_LINKER_SUPPORTS_PDB)
|
||||||
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_updater> DESTINATION ${BINARY_DEST_DIR})
|
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_updater> DESTINATION ${BINARY_DEST_DIR} OPTIONAL)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
@ -1469,6 +1484,8 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
|
||||||
"${Launcher_GCC_WARNINGS}")
|
"${Launcher_GCC_WARNINGS}")
|
||||||
|
|
||||||
target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
target_precompile_headers(filelink_logic PRIVATE ${PRECOMPILED_HEADERS})
|
||||||
|
|
||||||
target_link_libraries(filelink_logic
|
target_link_libraries(filelink_logic
|
||||||
systeminfo
|
systeminfo
|
||||||
BuildConfig
|
BuildConfig
|
||||||
|
|
@ -1482,6 +1499,8 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
|
||||||
add_executable("${Launcher_Name}_filelink" WIN32 filelink/filelink_main.cpp)
|
add_executable("${Launcher_Name}_filelink" WIN32 filelink/filelink_main.cpp)
|
||||||
|
|
||||||
target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest)
|
target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest)
|
||||||
|
target_precompile_headers("${Launcher_Name}_filelink" REUSE_FROM filelink_logic)
|
||||||
|
|
||||||
# HACK: Fix manifest issues with Ninja in release mode (and only release mode) and MSVC
|
# HACK: Fix manifest issues with Ninja in release mode (and only release mode) and MSVC
|
||||||
# I have no idea why this works or why it's needed. UPDATE THIS IF YOU EDIT THE MANIFEST!!! -@getchoo
|
# I have no idea why this works or why it's needed. UPDATE THIS IF YOU EDIT THE MANIFEST!!! -@getchoo
|
||||||
# Thank you 2018 CMake mailing list thread https://cmake.cmake.narkive.com/LnotZXus/conflicting-msvc-manifests
|
# Thank you 2018 CMake mailing list thread https://cmake.cmake.narkive.com/LnotZXus/conflicting-msvc-manifests
|
||||||
|
|
@ -1506,8 +1525,8 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
|
||||||
)
|
)
|
||||||
|
|
||||||
# Deploy PDBs
|
# Deploy PDBs
|
||||||
if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
|
if(CMAKE_CXX_LINKER_SUPPORTS_PDB)
|
||||||
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_filelink> DESTINATION ${BINARY_DEST_DIR})
|
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_filelink> DESTINATION ${BINARY_DEST_DIR} OPTIONAL)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 dada513 <dada513@protonmail.com>
|
* Copyright (C) 2022 dada513 <dada513@protonmail.com>
|
||||||
* Copyright (C) 2025 Seth Flynn <getchoo@tuta.io>
|
|
||||||
*
|
*
|
||||||
* 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
|
||||||
|
|
@ -76,15 +75,6 @@ bool isFlatpak()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSelfContained()
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_LINUX
|
|
||||||
return QFileInfo(QCoreApplication::applicationFilePath()).fileName().startsWith("ld-linux");
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isSnap()
|
bool isSnap()
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,6 @@ bool openUrl(const QUrl& url);
|
||||||
*/
|
*/
|
||||||
bool isFlatpak();
|
bool isFlatpak();
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the launcher is running in a self-contained Linux bundle
|
|
||||||
*/
|
|
||||||
bool isSelfContained();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether the launcher is running in a Snap environment
|
* Determine whether the launcher is running in a Snap environment
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -266,7 +266,21 @@ bool FileIgnoreProxy::filterAcceptsRow(int sourceRow, const QModelIndex& sourceP
|
||||||
|
|
||||||
bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const
|
bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const
|
||||||
{
|
{
|
||||||
return m_ignoreFiles.contains(fileInfo.fileName()) || m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath()));
|
if (m_ignoreFiles.contains(fileInfo.fileName())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& suffix : m_ignoreFilesSuffixes) {
|
||||||
|
if (fileInfo.fileName().endsWith(suffix)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath()))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileIgnoreProxy::filterFile(const QFileInfo& file) const
|
bool FileIgnoreProxy::filterFile(const QFileInfo& file) const
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ class FileIgnoreProxy : public QSortFilterProxyModel {
|
||||||
|
|
||||||
// list of file names that need to be removed completely from model
|
// list of file names that need to be removed completely from model
|
||||||
inline QStringList& ignoreFilesWithName() { return m_ignoreFiles; }
|
inline QStringList& ignoreFilesWithName() { return m_ignoreFiles; }
|
||||||
|
inline QStringList& ignoreFilesWithSuffix() { return m_ignoreFilesSuffixes; }
|
||||||
// list of relative paths that need to be removed completely from model
|
// list of relative paths that need to be removed completely from model
|
||||||
inline SeparatorPrefixTree<'/'>& ignoreFilesWithPath() { return m_ignoreFilePaths; }
|
inline SeparatorPrefixTree<'/'>& ignoreFilesWithPath() { return m_ignoreFilePaths; }
|
||||||
|
|
||||||
|
|
@ -85,5 +86,6 @@ class FileIgnoreProxy : public QSortFilterProxyModel {
|
||||||
const QString m_root;
|
const QString m_root;
|
||||||
SeparatorPrefixTree<'/'> m_blocked;
|
SeparatorPrefixTree<'/'> m_blocked;
|
||||||
QStringList m_ignoreFiles;
|
QStringList m_ignoreFiles;
|
||||||
|
QStringList m_ignoreFilesSuffixes;
|
||||||
SeparatorPrefixTree<'/'> m_ignoreFilePaths;
|
SeparatorPrefixTree<'/'> m_ignoreFilePaths;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
* Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
* Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||||
* Copyright (C) 2025 Seth Flynn <getchoo@tuta.io>
|
|
||||||
*
|
*
|
||||||
* 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
|
||||||
|
|
@ -773,34 +772,6 @@ QString ResolveExecutable(QString path)
|
||||||
return pathInfo.absoluteFilePath();
|
return pathInfo.absoluteFilePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<QProcess> createProcess(const QString& program, const QStringList& arguments)
|
|
||||||
{
|
|
||||||
qDebug() << "Creating process for" << program;
|
|
||||||
auto proc = std::unique_ptr<QProcess>(new QProcess());
|
|
||||||
|
|
||||||
#if defined(Q_OS_LINUX)
|
|
||||||
if (DesktopServices::isSelfContained()) {
|
|
||||||
const auto linkerPath = QCoreApplication::applicationFilePath();
|
|
||||||
qDebug() << "Wrapping" << program << "with self-contained linker at" << linkerPath;
|
|
||||||
|
|
||||||
QStringList wrappedArguments;
|
|
||||||
wrappedArguments << "--inhibit-cache" << program;
|
|
||||||
wrappedArguments += arguments;
|
|
||||||
|
|
||||||
proc->setProgram(linkerPath);
|
|
||||||
proc->setArguments(wrappedArguments);
|
|
||||||
} else {
|
|
||||||
proc->setProgram(program);
|
|
||||||
proc->setArguments(arguments);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
proc->setProgram(program);
|
|
||||||
proc->setArguments(arguments);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return proc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize path
|
* Normalize path
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
* Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
* Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||||
* Copyright (C) 2025 Seth Flynn <getchoo@tuta.io>
|
|
||||||
*
|
*
|
||||||
* 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
|
||||||
|
|
@ -43,13 +42,11 @@
|
||||||
|
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFlags>
|
#include <QFlags>
|
||||||
#include <QLocalServer>
|
#include <QLocalServer>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QPair>
|
#include <QPair>
|
||||||
#include <QProcess>
|
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
namespace FS {
|
namespace FS {
|
||||||
|
|
@ -336,14 +333,6 @@ QString pathTruncate(const QString& path, int depth);
|
||||||
*/
|
*/
|
||||||
QString ResolveExecutable(QString path);
|
QString ResolveExecutable(QString path);
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a QProcess instance
|
|
||||||
*
|
|
||||||
* This wrapper is currently only required for wrapping binaries called in
|
|
||||||
* self-contained AppImages (like those created by `go-appimage`)
|
|
||||||
*/
|
|
||||||
std::unique_ptr<QProcess> createProcess(const QString& program, const QStringList& arguments);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize path
|
* Normalize path
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -271,7 +271,7 @@ bool installIcon(QString root, QString instIconKey)
|
||||||
if (iconList->iconFileExists(instIconKey)) {
|
if (iconList->iconFileExists(instIconKey)) {
|
||||||
iconList->deleteIcon(instIconKey);
|
iconList->deleteIcon(instIconKey);
|
||||||
}
|
}
|
||||||
iconList->installIcon(importIconPath, instIconKey + ".png");
|
iconList->installIcon(importIconPath, instIconKey + "." + QFileInfo(importIconPath).suffix());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,21 @@ QJsonArray requireArray(const QJsonDocument& doc, const QString& what)
|
||||||
return doc.array();
|
return doc.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QJsonDocument parseUntilGarbage(const QByteArray& json, QJsonParseError* error, QString* garbage)
|
||||||
|
{
|
||||||
|
auto doc = QJsonDocument::fromJson(json, error);
|
||||||
|
if (error->error == QJsonParseError::GarbageAtEnd) {
|
||||||
|
qsizetype offset = error->offset;
|
||||||
|
QByteArray validJson = json.left(offset);
|
||||||
|
doc = QJsonDocument::fromJson(validJson, error);
|
||||||
|
|
||||||
|
if (garbage)
|
||||||
|
*garbage = json.right(json.size() - offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
void writeString(QJsonObject& to, const QString& key, const QString& value)
|
void writeString(QJsonObject& to, const QString& key, const QString& value)
|
||||||
{
|
{
|
||||||
if (!value.isEmpty()) {
|
if (!value.isEmpty()) {
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,9 @@ QJsonArray toJsonArray(const QList<T>& container)
|
||||||
|
|
||||||
////////////////// READING ////////////////////
|
////////////////// READING ////////////////////
|
||||||
|
|
||||||
|
// Attempt to parse JSON up until garbage is encountered
|
||||||
|
QJsonDocument parseUntilGarbage(const QByteArray& json, QJsonParseError* error = nullptr, QString* garbage = nullptr);
|
||||||
|
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T requireIsType(const QJsonValue& value, const QString& what = "Value");
|
T requireIsType(const QJsonValue& value, const QString& what = "Value");
|
||||||
|
|
|
||||||
|
|
@ -18,89 +18,19 @@ LAUNCHER_NAME=@Launcher_APP_BINARY_NAME@
|
||||||
LAUNCHER_DIR="$(dirname "$(readlink -f "$0")")"
|
LAUNCHER_DIR="$(dirname "$(readlink -f "$0")")"
|
||||||
echo "Launcher Dir: ${LAUNCHER_DIR}"
|
echo "Launcher Dir: ${LAUNCHER_DIR}"
|
||||||
|
|
||||||
# Set up env.
|
# Makes the launcher use portals for file picking
|
||||||
# Pass our custom variables separately so that the launcher can remove them for child processes
|
export QT_QPA_PLATFORMTHEME=xdgdesktopportal
|
||||||
export LAUNCHER_LD_LIBRARY_PATH="${LAUNCHER_DIR}/lib@LIB_SUFFIX@"
|
|
||||||
export LAUNCHER_LD_PRELOAD=""
|
|
||||||
export LAUNCHER_QT_PLUGIN_PATH="${LAUNCHER_DIR}/plugins"
|
|
||||||
export LAUNCHER_QT_FONTPATH="${LAUNCHER_DIR}/fonts"
|
|
||||||
|
|
||||||
export LD_LIBRARY_PATH="$LAUNCHER_LD_LIBRARY_PATH:$LD_LIBRARY_PATH"
|
# Just to be sure...
|
||||||
export LD_PRELOAD="$LAUNCHER_LD_PRELOAD:$LD_PRELOAD"
|
chmod +x "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}"
|
||||||
export QT_PLUGIN_PATH="$LAUNCHER_QT_PLUGIN_PATH:$QT_PLUGIN_PATH"
|
|
||||||
export QT_FONTPATH="$LAUNCHER_QT_FONTPATH:$QT_FONTPATH"
|
|
||||||
|
|
||||||
# Detect missing dependencies...
|
ARGS=("${LAUNCHER_DIR}/${LAUNCHER_NAME}" "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}")
|
||||||
DEPS_LIST=`ldd "${LAUNCHER_DIR}"/plugins/*/*.so 2>/dev/null | grep "not found" | sort -u | awk -vORS=", " '{ print $1 }'`
|
|
||||||
if [ "x$DEPS_LIST" = "x" ]; then
|
|
||||||
# We have all our dependencies. Run the launcher.
|
|
||||||
echo "No missing dependencies found."
|
|
||||||
|
|
||||||
# Just to be sure...
|
if [ -f portable.txt ]; then
|
||||||
chmod +x "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}"
|
ARGS+=("-d" "${LAUNCHER_DIR}")
|
||||||
|
|
||||||
ARGS=("${LAUNCHER_DIR}/${LAUNCHER_NAME}" "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}")
|
|
||||||
|
|
||||||
if [ -f portable.txt ]; then
|
|
||||||
ARGS+=("-d" "${LAUNCHER_DIR}")
|
|
||||||
fi
|
|
||||||
|
|
||||||
ARGS+=("$@")
|
|
||||||
|
|
||||||
# Run the launcher
|
|
||||||
exec -a "${ARGS[@]}"
|
|
||||||
|
|
||||||
# Run the launcher in valgrind
|
|
||||||
# valgrind --log-file="valgrind.log" --leak-check=full --track-origins=yes "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}" -d "${LAUNCHER_DIR}" "$@"
|
|
||||||
|
|
||||||
# Run the launcher with callgrind, delay instrumentation
|
|
||||||
# valgrind --log-file="valgrind.log" --tool=callgrind --instr-atstart=no "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}" -d "${LAUNCHER_DIR}" "$@"
|
|
||||||
# use callgrind_control -i on/off to profile actions
|
|
||||||
|
|
||||||
# Exit with launcher's exit code.
|
|
||||||
# exit $?
|
|
||||||
else
|
|
||||||
# apt
|
|
||||||
if which apt-file &>/dev/null; then
|
|
||||||
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
|
|
||||||
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do apt-file -l search $LIBRARY; done`
|
|
||||||
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
|
|
||||||
INSTALL_CMD="sudo apt-get install $COMMAND_LIBS"
|
|
||||||
# pacman
|
|
||||||
elif which pkgfile &>/dev/null; then
|
|
||||||
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
|
|
||||||
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do pkgfile $LIBRARY; done`
|
|
||||||
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
|
|
||||||
INSTALL_CMD="sudo pacman -S $COMMAND_LIBS"
|
|
||||||
# dnf
|
|
||||||
elif which dnf &>/dev/null; then
|
|
||||||
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
|
|
||||||
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do dnf whatprovides -q $LIBRARY; done`
|
|
||||||
COMMAND_LIBS=`echo "$COMMAND_LIBS" | grep -v 'Repo' | sort -u | awk -vORS=" " '{ print $1 }'`
|
|
||||||
INSTALL_CMD="sudo dnf install $COMMAND_LIBS"
|
|
||||||
# yum
|
|
||||||
elif which yum &>/dev/null; then
|
|
||||||
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
|
|
||||||
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do yum whatprovides $LIBRARY; done`
|
|
||||||
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
|
|
||||||
INSTALL_CMD="sudo yum install $COMMAND_LIBS"
|
|
||||||
# zypper
|
|
||||||
elif which zypper &>/dev/null; then
|
|
||||||
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
|
|
||||||
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do zypper wp $LIBRARY; done`
|
|
||||||
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
|
|
||||||
INSTALL_CMD="sudo zypper install $COMMAND_LIBS"
|
|
||||||
# emerge
|
|
||||||
elif which pfl &>/dev/null; then
|
|
||||||
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
|
|
||||||
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do pfl $LIBRARY; done`
|
|
||||||
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
|
|
||||||
INSTALL_CMD="sudo emerge $COMMAND_LIBS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
MESSAGE="Error: The launcher is missing the following libraries that it needs to work correctly:\n\t${DEPS_LIST}\nPlease install them from your distribution's package manager."
|
|
||||||
MESSAGE="$MESSAGE\n\nHint (please apply common sense): $INSTALL_CMD\n"
|
|
||||||
|
|
||||||
printerror "$MESSAGE"
|
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
ARGS+=("$@")
|
||||||
|
|
||||||
|
# Run the launcher
|
||||||
|
exec -a "${ARGS[@]}"
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <MMCTime.h>
|
#include <MMCTime.h>
|
||||||
#include <qobject.h>
|
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <qlogging.h>
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <compare>
|
#include <compare>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
#include <qpair.h>
|
|
||||||
|
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
|
|
||||||
17
launcher/include/base.pch.hpp
Normal file
17
launcher/include/base.pch.hpp
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
#ifndef PRISM_PRECOMPILED_BASE_HEADERS_H
|
||||||
|
#define PRISM_PRECOMPILED_BASE_HEADERS_H
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <BuildConfig.h>
|
||||||
|
#include <FileSystem.h>
|
||||||
|
#include <Json.h>
|
||||||
|
#include <Version.h>
|
||||||
|
#include <sys.h>
|
||||||
|
|
||||||
|
#endif // PRISM_PRECOMPILED_BASE_HEADERS_H
|
||||||
59
launcher/include/qtcore.pch.hpp
Normal file
59
launcher/include/qtcore.pch.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
#pragma once
|
||||||
|
#ifndef PRISM_PRECOMPILED_QTCORE_HEADERS_H
|
||||||
|
#define PRISM_PRECOMPILED_QTCORE_HEADERS_H
|
||||||
|
|
||||||
|
#include <QEvent>
|
||||||
|
#include <QMetaType>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
// collections
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QList>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QPair>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QFutureWatcher>
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDirIterator>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
|
#include <QMimeData>
|
||||||
|
#include <QSaveFile>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QProcess>
|
||||||
|
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QUuid>
|
||||||
|
#include <QtMath>
|
||||||
|
|
||||||
|
#endif // PRISM_PRECOMPILED_QTCORE_HEADERS_H
|
||||||
47
launcher/include/qtgui.pch.hpp
Normal file
47
launcher/include/qtgui.pch.hpp
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
#pragma once
|
||||||
|
#ifndef PRISM_PRECOMPILED_QTGUI_HEADERS_H
|
||||||
|
#define PRISM_PRECOMPILED_QTGUI_HEADERS_H
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
|
||||||
|
#include <QClipboard>
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QPushButton>
|
||||||
|
|
||||||
|
#include <QStyle>
|
||||||
|
#include <QStyledItemDelegate>
|
||||||
|
|
||||||
|
#include <QScrollBar>
|
||||||
|
|
||||||
|
#include <QTabBar>
|
||||||
|
#include <QToolButton>
|
||||||
|
|
||||||
|
#include <QHeaderView>
|
||||||
|
#include <QListView>
|
||||||
|
#include <QTreeView>
|
||||||
|
|
||||||
|
#include <QLayout>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QPixmapCache>
|
||||||
|
|
||||||
|
#endif // PRISM_PRECOMPILED_GUI_HEADERS_H
|
||||||
|
|
@ -19,7 +19,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "minecraft/Logging.h"
|
#include "minecraft/Logging.h"
|
||||||
#include <qloggingcategory.h>
|
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(instanceProfileC, "launcher.instance.profile")
|
Q_LOGGING_CATEGORY(instanceProfileC, "launcher.instance.profile")
|
||||||
Q_LOGGING_CATEGORY(instanceProfileResolveC, "launcher.instance.profile.resolve")
|
Q_LOGGING_CATEGORY(instanceProfileResolveC, "launcher.instance.profile.resolve")
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@
|
||||||
#include <QActionGroup>
|
#include <QActionGroup>
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QScreen>
|
#include <QScreen>
|
||||||
|
#include <QStandardPaths>
|
||||||
#include <QWindow>
|
#include <QWindow>
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
|
|
@ -588,6 +589,16 @@ QStringList MinecraftInstance::javaArguments()
|
||||||
"minecraft.exe.heapdump");
|
"minecraft.exe.heapdump");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// LWJGL2 reads `LWJGL_DISABLE_XRANDR` to force disable xrandr usage and fall back to xf86videomode.
|
||||||
|
// It *SHOULD* check for the executable to exist before trying to use it for queries but it doesnt,
|
||||||
|
// so WE can and force disable xrandr if it is not available.
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
// LWJGL2 is "org.lwjgl" LWJGL3 is "org.lwjgl3"
|
||||||
|
if (m_components->getComponent("org.lwjgl") != nullptr && QStandardPaths::findExecutable("xrandr").isEmpty()) {
|
||||||
|
args << QString("-DLWJGL_DISABLE_XRANDR=true");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int min = settings()->get("MinMemAlloc").toInt();
|
int min = settings()->get("MinMemAlloc").toInt();
|
||||||
int max = settings()->get("MaxMemAlloc").toInt();
|
int max = settings()->get("MaxMemAlloc").toInt();
|
||||||
if (min < max) {
|
if (min < max) {
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Version.h>
|
#include <Version.h>
|
||||||
#include <qlogging.h>
|
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@
|
||||||
#include "WorldList.h"
|
#include "WorldList.h"
|
||||||
|
|
||||||
#include <FileSystem.h>
|
#include <FileSystem.h>
|
||||||
#include <qmimedata.h>
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
#include <QFileSystemWatcher>
|
#include <QFileSystemWatcher>
|
||||||
|
|
|
||||||
|
|
@ -669,7 +669,7 @@ void AccountList::beginActivity()
|
||||||
void AccountList::endActivity()
|
void AccountList::endActivity()
|
||||||
{
|
{
|
||||||
if (m_activityCount == 0) {
|
if (m_activityCount == 0) {
|
||||||
qWarning() << m_name << " - Activity count would become below zero";
|
qWarning() << "Activity count would become below zero";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bool deactivating = m_activityCount == 1;
|
bool deactivating = m_activityCount == 1;
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,6 @@ class AccountList : public QAbstractListModel {
|
||||||
void endActivity();
|
void endActivity();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const char* m_name;
|
|
||||||
uint32_t m_activityCount = 0;
|
uint32_t m_activityCount = 0;
|
||||||
signals:
|
signals:
|
||||||
void listChanged();
|
void listChanged();
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "DataPackFolderModel.h"
|
#include "DataPackFolderModel.h"
|
||||||
#include <qnamespace.h>
|
|
||||||
#include <qsize.h>
|
|
||||||
|
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QStyle>
|
#include <QStyle>
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Mod.h"
|
#include "Mod.h"
|
||||||
#include <qpixmap.h>
|
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#include "modplatform/flame/FlameAPI.h"
|
#include "modplatform/flame/FlameAPI.h"
|
||||||
#include "modplatform/flame/FlameModIndex.h"
|
#include "modplatform/flame/FlameModIndex.h"
|
||||||
#include "settings/Setting.h"
|
#include "settings/Setting.h"
|
||||||
|
#include "tasks/SequentialTask.h"
|
||||||
#include "tasks/Task.h"
|
#include "tasks/Task.h"
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
|
|
||||||
|
|
@ -207,7 +208,13 @@ void ResourceFolderModel::installResourceWithFlameMetadata(QString path, ModPlat
|
||||||
bool ResourceFolderModel::uninstallResource(const QString& file_name, bool preserve_metadata)
|
bool ResourceFolderModel::uninstallResource(const QString& file_name, bool preserve_metadata)
|
||||||
{
|
{
|
||||||
for (auto& resource : m_resources) {
|
for (auto& resource : m_resources) {
|
||||||
if (resource->fileinfo().fileName() == file_name) {
|
auto resourceFileInfo = resource->fileinfo();
|
||||||
|
auto resourceFileName = resource->fileinfo().fileName();
|
||||||
|
if (!resource->enabled() && resourceFileName.endsWith(".disabled")) {
|
||||||
|
resourceFileName.chop(9);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourceFileName == file_name) {
|
||||||
auto res = resource->destroy(indexDir(), preserve_metadata, false);
|
auto res = resource->destroy(indexDir(), preserve_metadata, false);
|
||||||
|
|
||||||
update();
|
update();
|
||||||
|
|
@ -328,7 +335,20 @@ bool ResourceFolderModel::update()
|
||||||
},
|
},
|
||||||
Qt::ConnectionType::QueuedConnection);
|
Qt::ConnectionType::QueuedConnection);
|
||||||
|
|
||||||
QThreadPool::globalInstance()->start(m_current_update_task.get());
|
Task::Ptr preUpdate{createPreUpdateTask()};
|
||||||
|
|
||||||
|
if (preUpdate != nullptr) {
|
||||||
|
auto task = new SequentialTask("ResourceFolderModel::update");
|
||||||
|
|
||||||
|
task->addTask(preUpdate);
|
||||||
|
task->addTask(m_current_update_task);
|
||||||
|
|
||||||
|
connect(task, &Task::finished, [task] { task->deleteLater(); });
|
||||||
|
|
||||||
|
QThreadPool::globalInstance()->start(task);
|
||||||
|
} else {
|
||||||
|
QThreadPool::globalInstance()->start(m_current_update_task.get());
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||||
virtual bool startWatching() { return startWatching({ indexDir().absolutePath(), m_dir.absolutePath() }); }
|
virtual bool startWatching() { return startWatching({ indexDir().absolutePath(), m_dir.absolutePath() }); }
|
||||||
virtual bool stopWatching() { return stopWatching({ indexDir().absolutePath(), m_dir.absolutePath() }); }
|
virtual bool stopWatching() { return stopWatching({ indexDir().absolutePath(), m_dir.absolutePath() }); }
|
||||||
|
|
||||||
QDir indexDir() { return { QString("%1/.index").arg(dir().absolutePath()) }; }
|
virtual QDir indexDir() const { return { QString("%1/.index").arg(dir().absolutePath()) }; }
|
||||||
|
|
||||||
/** Given a path in the system, install that resource, moving it to its place in the
|
/** Given a path in the system, install that resource, moving it to its place in the
|
||||||
* instance file hierarchy.
|
* instance file hierarchy.
|
||||||
|
|
@ -188,6 +188,7 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||||
void parseFinished();
|
void parseFinished();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
[[nodiscard]] virtual Task* createPreUpdateTask() { return nullptr; }
|
||||||
/** This creates a new update task to be executed by update().
|
/** This creates a new update task to be executed by update().
|
||||||
*
|
*
|
||||||
* The task should load and parse all resources necessary, and provide a way of accessing such results.
|
* The task should load and parse all resources necessary, and provide a way of accessing such results.
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ResourcePackFolderModel.h"
|
#include "ResourcePackFolderModel.h"
|
||||||
#include <qnamespace.h>
|
|
||||||
#include <qsize.h>
|
|
||||||
|
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QStyle>
|
#include <QStyle>
|
||||||
|
|
|
||||||
56
launcher/minecraft/mod/ShaderPackFolderModel.cpp
Normal file
56
launcher/minecraft/mod/ShaderPackFolderModel.cpp
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
#include "FileSystem.h"
|
||||||
|
#include "ShaderPackFolderModel.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
class ShaderPackIndexMigrateTask : public Task {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ShaderPackIndexMigrateTask(QDir resourceDir, QDir indexDir) : m_resourceDir(std::move(resourceDir)), m_indexDir(std::move(indexDir)) {}
|
||||||
|
|
||||||
|
void executeTask() override
|
||||||
|
{
|
||||||
|
if (!m_indexDir.exists()) {
|
||||||
|
qDebug() << m_indexDir.absolutePath() << "does not exist; nothing to migrate";
|
||||||
|
emitSucceeded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList pwFiles = m_indexDir.entryList({ "*.pw.toml" }, QDir::Files);
|
||||||
|
bool movedAll = true;
|
||||||
|
|
||||||
|
for (const auto& file : pwFiles) {
|
||||||
|
QString src = m_indexDir.filePath(file);
|
||||||
|
QString dest = m_resourceDir.filePath(file);
|
||||||
|
|
||||||
|
if (FS::move(src, dest)) {
|
||||||
|
qDebug() << "Moved" << src << "to" << dest;
|
||||||
|
} else {
|
||||||
|
movedAll = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!movedAll) {
|
||||||
|
// FIXME: not shown in the UI
|
||||||
|
emitFailed(tr("Failed to migrate shaderpack metadata from .index"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FS::deletePath(m_indexDir.absolutePath())) {
|
||||||
|
emitFailed(tr("Failed to remove old .index dir"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emitSucceeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QDir m_resourceDir, m_indexDir;
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Task* ShaderPackFolderModel::createPreUpdateTask()
|
||||||
|
{
|
||||||
|
return new ShaderPackIndexMigrateTask(m_dir, ResourceFolderModel::indexDir());
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "ShaderPackFolderModel.moc"
|
||||||
|
|
@ -21,5 +21,16 @@ class ShaderPackFolderModel : public ResourceFolderModel {
|
||||||
return new LocalShaderPackParseTask(m_next_resolution_ticket, static_cast<ShaderPack&>(resource));
|
return new LocalShaderPackParseTask(m_next_resolution_ticket, static_cast<ShaderPack&>(resource));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QDir indexDir() const override { return m_dir; }
|
||||||
|
|
||||||
|
Task* createPreUpdateTask() override;
|
||||||
|
|
||||||
|
// avoid watching twice
|
||||||
|
virtual bool startWatching() override { return ResourceFolderModel::startWatching({ m_dir.absolutePath() }); }
|
||||||
|
virtual bool stopWatching() override { return ResourceFolderModel::stopWatching({ m_dir.absolutePath() }); }
|
||||||
|
|
||||||
RESOURCE_HELPERS(ShaderPack);
|
RESOURCE_HELPERS(ShaderPack);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMutex m_migrateLock;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -168,10 +168,15 @@ bool processZIP(DataPack* pack, ProcessingLevel level)
|
||||||
// https://minecraft.wiki/w/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
|
// https://minecraft.wiki/w/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
|
||||||
bool processMCMeta(DataPack* pack, QByteArray&& raw_data)
|
bool processMCMeta(DataPack* pack, QByteArray&& raw_data)
|
||||||
{
|
{
|
||||||
try {
|
QJsonParseError parse_error;
|
||||||
auto json_doc = QJsonDocument::fromJson(raw_data);
|
auto json_doc = Json::parseUntilGarbage(raw_data, &parse_error);
|
||||||
auto pack_obj = Json::requireObject(json_doc.object(), "pack", {});
|
if (parse_error.error != QJsonParseError::NoError) {
|
||||||
|
qWarning() << "Failed to parse JSON:" << parse_error.errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto pack_obj = Json::requireObject(json_doc.object(), "pack", {});
|
||||||
pack->setPackFormat(pack_obj["pack_format"].toInt());
|
pack->setPackFormat(pack_obj["pack_format"].toInt());
|
||||||
pack->setDescription(DataPackUtils::processComponent(pack_obj.value("description")));
|
pack->setDescription(DataPackUtils::processComponent(pack_obj.value("description")));
|
||||||
} catch (Json::JsonException& e) {
|
} catch (Json::JsonException& e) {
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,10 @@ void ResourceFolderLoadTask::getFromMetadata()
|
||||||
{
|
{
|
||||||
m_index_dir.refresh();
|
m_index_dir.refresh();
|
||||||
for (auto entry : m_index_dir.entryList(QDir::Files)) {
|
for (auto entry : m_index_dir.entryList(QDir::Files)) {
|
||||||
|
if (!entry.endsWith(".pw.toml")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
auto metadata = Metadata::get(m_index_dir, entry);
|
auto metadata = Metadata::get(m_index_dir, entry);
|
||||||
|
|
||||||
if (!metadata.isValid())
|
if (!metadata.isValid())
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
* Copyright (c) 2023-2025 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
* Copyright (c) 2025 Rinth, Inc.
|
||||||
*
|
*
|
||||||
* 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
|
||||||
|
|
@ -22,29 +23,89 @@
|
||||||
|
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
|
||||||
|
static void setAlpha(QImage& image, const QRect& region, const int alpha)
|
||||||
|
{
|
||||||
|
for (int y = region.top(); y < region.bottom(); ++y) {
|
||||||
|
QRgb* line = reinterpret_cast<QRgb*>(image.scanLine(y));
|
||||||
|
for (int x = region.left(); x < region.right(); ++x) {
|
||||||
|
QRgb pixel = line[x];
|
||||||
|
line[x] = qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void doNotchTransparencyHack(QImage& image)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < 32; y++) {
|
||||||
|
QRgb* line = reinterpret_cast<QRgb*>(image.scanLine(y));
|
||||||
|
for (int x = 32; x < 64; x++) {
|
||||||
|
if (qAlpha(line[x]) < 128) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setAlpha(image, { 32, 0, 32, 32 }, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static QImage improveSkin(QImage skin)
|
static QImage improveSkin(QImage skin)
|
||||||
{
|
{
|
||||||
|
int height = skin.height();
|
||||||
|
int width = skin.width();
|
||||||
|
if (width != 64 || (height != 32 && height != 64)) { // this is no minecraft skin
|
||||||
|
return skin;
|
||||||
|
}
|
||||||
// It seems some older skins may use this format, which can't be drawn onto
|
// It seems some older skins may use this format, which can't be drawn onto
|
||||||
// https://github.com/PrismLauncher/PrismLauncher/issues/4032
|
// https://github.com/PrismLauncher/PrismLauncher/issues/4032
|
||||||
// https://doc.qt.io/qt-6/qpainter.html#begin
|
// https://doc.qt.io/qt-6/qpainter.html#begin
|
||||||
if (skin.format() == QImage::Format_Indexed8) {
|
if (skin.format() == QImage::Format_Indexed8) {
|
||||||
skin = skin.convertToFormat(QImage::Format_RGB32);
|
skin = skin.convertToFormat(QImage::Format_ARGB32);
|
||||||
}
|
}
|
||||||
if (skin.size() == QSize(64, 32)) { // old format
|
|
||||||
|
auto isLegacy = height == 32; // old format
|
||||||
|
if (isLegacy) {
|
||||||
auto newSkin = QImage(QSize(64, 64), skin.format());
|
auto newSkin = QImage(QSize(64, 64), skin.format());
|
||||||
newSkin.fill(Qt::transparent);
|
newSkin.fill(Qt::transparent);
|
||||||
QPainter p(&newSkin);
|
QPainter p(&newSkin);
|
||||||
p.drawImage(QPoint(0, 0), skin.copy(QRect(0, 0, 64, 32))); // copy head
|
p.drawImage(0, 0, skin);
|
||||||
|
|
||||||
auto leg = skin.copy(QRect(0, 16, 16, 16));
|
auto copyRect = [&p, &newSkin](int startX, int startY, int offsetX, int offsetY, int sizeX, int sizeY) {
|
||||||
p.drawImage(QPoint(16, 48), leg); // copy leg
|
QImage region = newSkin.copy(startX, startY, sizeX, sizeY);
|
||||||
|
region = region.mirrored(true, false);
|
||||||
|
|
||||||
auto arm = skin.copy(QRect(40, 16, 16, 16));
|
p.drawImage(startX + offsetX, startY + offsetY, region);
|
||||||
p.drawImage(QPoint(32, 48), arm); // copy arm
|
};
|
||||||
return newSkin;
|
static const struct {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int offsetX;
|
||||||
|
int offsetY;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
} faces[] = {
|
||||||
|
{ 4, 16, 16, 32, 4, 4 }, { 8, 16, 16, 32, 4, 4 }, { 0, 20, 24, 32, 4, 12 }, { 4, 20, 16, 32, 4, 12 },
|
||||||
|
{ 8, 20, 8, 32, 4, 12 }, { 12, 20, 16, 32, 4, 12 }, { 44, 16, -8, 32, 4, 4 }, { 48, 16, -8, 32, 4, 4 },
|
||||||
|
{ 40, 20, 0, 32, 4, 12 }, { 44, 20, -8, 32, 4, 12 }, { 48, 20, -16, 32, 4, 12 }, { 52, 20, -8, 32, 4, 12 },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& face : faces) {
|
||||||
|
copyRect(face.x, face.y, face.offsetX, face.offsetY, face.width, face.height);
|
||||||
|
}
|
||||||
|
doNotchTransparencyHack(newSkin);
|
||||||
|
skin = newSkin;
|
||||||
|
}
|
||||||
|
static const QRect opaqueParts[] = {
|
||||||
|
{ 0, 0, 32, 16 },
|
||||||
|
{ 0, 16, 64, 16 },
|
||||||
|
{ 16, 48, 32, 16 },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& p : opaqueParts) {
|
||||||
|
setAlpha(skin, p, 255);
|
||||||
}
|
}
|
||||||
return skin;
|
return skin;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QImage getSkin(const QString path)
|
static QImage getSkin(const QString path)
|
||||||
{
|
{
|
||||||
return improveSkin(QImage(path));
|
return improveSkin(QImage(path));
|
||||||
|
|
@ -66,8 +127,8 @@ static QImage generatePreviews(QImage texture, bool slim)
|
||||||
paint.drawImage(4, 22, texture.copy(4, 20, 4, 12));
|
paint.drawImage(4, 22, texture.copy(4, 20, 4, 12));
|
||||||
paint.drawImage(4, 22, texture.copy(4, 36, 4, 12));
|
paint.drawImage(4, 22, texture.copy(4, 36, 4, 12));
|
||||||
// left leg
|
// left leg
|
||||||
paint.drawImage(8, 22, texture.copy(4, 52, 4, 12));
|
|
||||||
paint.drawImage(8, 22, texture.copy(20, 52, 4, 12));
|
paint.drawImage(8, 22, texture.copy(20, 52, 4, 12));
|
||||||
|
paint.drawImage(8, 22, texture.copy(4, 52, 4, 12));
|
||||||
|
|
||||||
auto armWidth = slim ? 3 : 4;
|
auto armWidth = slim ? 3 : 4;
|
||||||
auto armPosX = slim ? 1 : 0;
|
auto armPosX = slim ? 1 : 0;
|
||||||
|
|
@ -89,8 +150,8 @@ static QImage generatePreviews(QImage texture, bool slim)
|
||||||
paint.drawImage(24, 22, texture.copy(12, 20, 4, 12));
|
paint.drawImage(24, 22, texture.copy(12, 20, 4, 12));
|
||||||
paint.drawImage(24, 22, texture.copy(12, 36, 4, 12));
|
paint.drawImage(24, 22, texture.copy(12, 36, 4, 12));
|
||||||
// left leg
|
// left leg
|
||||||
paint.drawImage(28, 22, texture.copy(12, 52, 4, 12));
|
|
||||||
paint.drawImage(28, 22, texture.copy(28, 52, 4, 12));
|
paint.drawImage(28, 22, texture.copy(28, 52, 4, 12));
|
||||||
|
paint.drawImage(28, 22, texture.copy(12, 52, 4, 12));
|
||||||
|
|
||||||
// right arm
|
// right arm
|
||||||
paint.drawImage(armPosX + 20, 10, texture.copy(48 + armWidth, 20, armWidth, 12));
|
paint.drawImage(armPosX + 20, 10, texture.copy(48 + armWidth, 20, armWidth, 12));
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,8 @@ ModPlatform::IndexedVersion Modrinth::loadIndexedPackVersion(QJsonObject& obj, Q
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
for (auto mcVer : versionArray) {
|
for (auto mcVer : versionArray) {
|
||||||
file.mcVersion.append(ModrinthAPI::mapMCVersionFromModrinth(mcVer.toString()));
|
file.mcVersion.append({ ModrinthAPI::mapMCVersionFromModrinth(mcVer.toString()),
|
||||||
|
mcVer.toString() }); // double this so we can check both strings when filtering
|
||||||
}
|
}
|
||||||
auto loaders = Json::requireArray(obj, "loaders");
|
auto loaders = Json::requireArray(obj, "loaders");
|
||||||
for (auto loader : loaders) {
|
for (auto loader : loaders) {
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <qloggingcategory.h>
|
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "PasteUpload.h"
|
#include "PasteUpload.h"
|
||||||
#include <qobject.h>
|
|
||||||
|
|
||||||
#include <QHttpPart>
|
#include <QHttpPart>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@
|
||||||
#include "InstanceWindow.h"
|
#include "InstanceWindow.h"
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
#include <qlayoutitem.h>
|
|
||||||
#include <QCloseEvent>
|
#include <QCloseEvent>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@
|
||||||
#include "ui_AboutDialog.h"
|
#include "ui_AboutDialog.h"
|
||||||
|
|
||||||
#include <net/NetJob.h>
|
#include <net/NetJob.h>
|
||||||
#include <qobject.h>
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
QString getCreditsHtml()
|
QString getCreditsHtml()
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ ExportPackDialog::ExportPackDialog(MinecraftInstancePtr instance, QWidget* paren
|
||||||
m_proxy->ignoreFilesWithPath().insert(FS::PathCombine(prefix, path));
|
m_proxy->ignoreFilesWithPath().insert(FS::PathCombine(prefix, path));
|
||||||
}
|
}
|
||||||
m_proxy->ignoreFilesWithName().append({ ".DS_Store", "thumbs.db", "Thumbs.db" });
|
m_proxy->ignoreFilesWithName().append({ ".DS_Store", "thumbs.db", "Thumbs.db" });
|
||||||
|
m_proxy->ignoreFilesWithSuffix().append(".pw.toml");
|
||||||
m_proxy->setSourceModel(model);
|
m_proxy->setSourceModel(model);
|
||||||
m_proxy->loadBlockedPathsFromFile(ignoreFileName());
|
m_proxy->loadBlockedPathsFromFile(ignoreFileName());
|
||||||
|
|
||||||
|
|
@ -103,8 +104,19 @@ ExportPackDialog::ExportPackDialog(MinecraftInstancePtr instance, QWidget* paren
|
||||||
MinecraftInstance* mcInstance = dynamic_cast<MinecraftInstance*>(instance.get());
|
MinecraftInstance* mcInstance = dynamic_cast<MinecraftInstance*>(instance.get());
|
||||||
if (mcInstance) {
|
if (mcInstance) {
|
||||||
for (auto resourceModel : mcInstance->resourceLists()) {
|
for (auto resourceModel : mcInstance->resourceLists()) {
|
||||||
if (resourceModel && resourceModel->indexDir().exists())
|
if (resourceModel == nullptr) {
|
||||||
m_proxy->ignoreFilesWithPath().insert(instanceRoot.relativeFilePath(resourceModel->indexDir().absolutePath()));
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resourceModel->indexDir().exists()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourceModel->dir() == resourceModel->indexDir()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_proxy->ignoreFilesWithPath().insert(instanceRoot.relativeFilePath(resourceModel->indexDir().absolutePath()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,12 @@ SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct)
|
||||||
{
|
{
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
m_skinPreview = new SkinOpenGLWindow(this, palette().color(QPalette::Normal, QPalette::Base));
|
if (SkinOpenGLWindow::hasOpenGL()) {
|
||||||
|
m_skinPreview = new SkinOpenGLWindow(this, palette().color(QPalette::Normal, QPalette::Base));
|
||||||
|
} else {
|
||||||
|
m_skinPreviewLabel = new QLabel(this);
|
||||||
|
m_skinPreviewLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
}
|
||||||
|
|
||||||
setWindowModality(Qt::WindowModal);
|
setWindowModality(Qt::WindowModal);
|
||||||
|
|
||||||
|
|
@ -92,7 +97,9 @@ SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct)
|
||||||
connect(contentsWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &SkinManageDialog::selectionChanged);
|
connect(contentsWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &SkinManageDialog::selectionChanged);
|
||||||
connect(m_ui->listView, &QListView::customContextMenuRequested, this, &SkinManageDialog::show_context_menu);
|
connect(m_ui->listView, &QListView::customContextMenuRequested, this, &SkinManageDialog::show_context_menu);
|
||||||
connect(m_ui->elytraCB, &QCheckBox::stateChanged, this, [this]() {
|
connect(m_ui->elytraCB, &QCheckBox::stateChanged, this, [this]() {
|
||||||
m_skinPreview->setElytraVisible(m_ui->elytraCB->isChecked());
|
if (m_skinPreview) {
|
||||||
|
m_skinPreview->setElytraVisible(m_ui->elytraCB->isChecked());
|
||||||
|
}
|
||||||
on_capeCombo_currentIndexChanged(0);
|
on_capeCombo_currentIndexChanged(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -103,13 +110,19 @@ SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct)
|
||||||
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
|
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
|
||||||
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
|
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
|
||||||
|
|
||||||
m_ui->skinLayout->insertWidget(0, QWidget::createWindowContainer(m_skinPreview, this));
|
if (m_skinPreview) {
|
||||||
|
m_ui->skinLayout->insertWidget(0, QWidget::createWindowContainer(m_skinPreview, this));
|
||||||
|
} else {
|
||||||
|
m_ui->skinLayout->insertWidget(0, m_skinPreviewLabel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SkinManageDialog::~SkinManageDialog()
|
SkinManageDialog::~SkinManageDialog()
|
||||||
{
|
{
|
||||||
delete m_ui;
|
delete m_ui;
|
||||||
delete m_skinPreview;
|
if (m_skinPreview) {
|
||||||
|
delete m_skinPreview;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkinManageDialog::activated(QModelIndex index)
|
void SkinManageDialog::activated(QModelIndex index)
|
||||||
|
|
@ -131,7 +144,12 @@ void SkinManageDialog::selectionChanged(QItemSelection selected, [[maybe_unused]
|
||||||
if (!skin)
|
if (!skin)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_skinPreview->updateScene(skin);
|
if (m_skinPreview) {
|
||||||
|
m_skinPreview->updateScene(skin);
|
||||||
|
} else {
|
||||||
|
m_skinPreviewLabel->setPixmap(
|
||||||
|
QPixmap::fromImage(skin->getPreview()).scaled(m_skinPreviewLabel->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
|
||||||
|
}
|
||||||
m_ui->capeCombo->setCurrentIndex(m_capesIdx.value(skin->getCapeId()));
|
m_ui->capeCombo->setCurrentIndex(m_capesIdx.value(skin->getCapeId()));
|
||||||
m_ui->steveBtn->setChecked(skin->getModel() == SkinModel::CLASSIC);
|
m_ui->steveBtn->setChecked(skin->getModel() == SkinModel::CLASSIC);
|
||||||
m_ui->alexBtn->setChecked(skin->getModel() == SkinModel::SLIM);
|
m_ui->alexBtn->setChecked(skin->getModel() == SkinModel::SLIM);
|
||||||
|
|
@ -165,17 +183,17 @@ void SkinManageDialog::on_fileBtn_clicked()
|
||||||
QPixmap previewCape(QImage capeImage, bool elytra = false)
|
QPixmap previewCape(QImage capeImage, bool elytra = false)
|
||||||
{
|
{
|
||||||
if (elytra) {
|
if (elytra) {
|
||||||
auto wing = capeImage.copy(34, 0, 12, 22);
|
auto wing = capeImage.copy(34, 2, 12, 20);
|
||||||
QImage mirrored = wing.mirrored(true, false);
|
QImage mirrored = wing.mirrored(true, false);
|
||||||
|
|
||||||
QImage combined(wing.width() * 2 - 2, wing.height(), capeImage.format());
|
QImage combined(wing.width() * 2 + 1, wing.height() + 14, capeImage.format());
|
||||||
combined.fill(Qt::transparent);
|
combined.fill(Qt::transparent);
|
||||||
|
|
||||||
QPainter painter(&combined);
|
QPainter painter(&combined);
|
||||||
painter.drawImage(0, 0, wing);
|
painter.drawImage(0, 7, wing);
|
||||||
painter.drawImage(wing.width() - 2, 0, mirrored);
|
painter.drawImage(wing.width() + 1, 7, mirrored);
|
||||||
painter.end();
|
painter.end();
|
||||||
return QPixmap::fromImage(combined.scaled(96, 176, Qt::IgnoreAspectRatio, Qt::FastTransformation));
|
return QPixmap::fromImage(combined.scaled(84, 128, Qt::KeepAspectRatio, Qt::FastTransformation));
|
||||||
}
|
}
|
||||||
return QPixmap::fromImage(capeImage.copy(1, 1, 10, 16).scaled(80, 128, Qt::IgnoreAspectRatio, Qt::FastTransformation));
|
return QPixmap::fromImage(capeImage.copy(1, 1, 10, 16).scaled(80, 128, Qt::IgnoreAspectRatio, Qt::FastTransformation));
|
||||||
}
|
}
|
||||||
|
|
@ -244,10 +262,17 @@ void SkinManageDialog::on_capeCombo_currentIndexChanged(int index)
|
||||||
} else {
|
} else {
|
||||||
m_ui->capeImage->clear();
|
m_ui->capeImage->clear();
|
||||||
}
|
}
|
||||||
m_skinPreview->updateCape(cape);
|
if (m_skinPreview) {
|
||||||
|
m_skinPreview->updateCape(cape);
|
||||||
|
}
|
||||||
if (auto skin = getSelectedSkin(); skin) {
|
if (auto skin = getSelectedSkin(); skin) {
|
||||||
skin->setCapeId(id.toString());
|
skin->setCapeId(id.toString());
|
||||||
m_skinPreview->updateScene(skin);
|
if (m_skinPreview) {
|
||||||
|
m_skinPreview->updateScene(skin);
|
||||||
|
} else {
|
||||||
|
m_skinPreviewLabel->setPixmap(
|
||||||
|
QPixmap::fromImage(skin->getPreview()).scaled(m_skinPreviewLabel->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -255,7 +280,12 @@ void SkinManageDialog::on_steveBtn_toggled(bool checked)
|
||||||
{
|
{
|
||||||
if (auto skin = getSelectedSkin(); skin) {
|
if (auto skin = getSelectedSkin(); skin) {
|
||||||
skin->setModel(checked ? SkinModel::CLASSIC : SkinModel::SLIM);
|
skin->setModel(checked ? SkinModel::CLASSIC : SkinModel::SLIM);
|
||||||
m_skinPreview->updateScene(skin);
|
if (m_skinPreview) {
|
||||||
|
m_skinPreview->updateScene(skin);
|
||||||
|
} else {
|
||||||
|
m_skinPreviewLabel->setPixmap(
|
||||||
|
QPixmap::fromImage(skin->getPreview()).scaled(m_skinPreviewLabel->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -545,6 +575,10 @@ void SkinManageDialog::resizeEvent(QResizeEvent* event)
|
||||||
} else {
|
} else {
|
||||||
m_ui->capeImage->clear();
|
m_ui->capeImage->clear();
|
||||||
}
|
}
|
||||||
|
if (auto skin = getSelectedSkin(); skin && !m_skinPreview) {
|
||||||
|
m_skinPreviewLabel->setPixmap(
|
||||||
|
QPixmap::fromImage(skin->getPreview()).scaled(m_skinPreviewLabel->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SkinModel* SkinManageDialog::getSelectedSkin()
|
SkinModel* SkinManageDialog::getSelectedSkin()
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QItemSelection>
|
#include <QItemSelection>
|
||||||
|
#include <QLabel>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
|
|
||||||
#include "minecraft/auth/MinecraftAccount.h"
|
#include "minecraft/auth/MinecraftAccount.h"
|
||||||
|
|
@ -68,4 +69,5 @@ class SkinManageDialog : public QDialog, public SkinProvider {
|
||||||
QHash<QString, QImage> m_capes;
|
QHash<QString, QImage> m_capes;
|
||||||
QHash<QString, int> m_capesIdx;
|
QHash<QString, int> m_capesIdx;
|
||||||
SkinOpenGLWindow* m_skinPreview = nullptr;
|
SkinOpenGLWindow* m_skinPreview = nullptr;
|
||||||
|
QLabel* m_skinPreviewLabel = nullptr;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -31,32 +31,50 @@ Scene::Scene(const QImage& skin, bool slim, const QImage& cape) : QOpenGLFunctio
|
||||||
m_staticComponents = {
|
m_staticComponents = {
|
||||||
// head
|
// head
|
||||||
new opengl::BoxGeometry(QVector3D(8, 8, 8), QVector3D(0, 4, 0), QPoint(0, 0), QVector3D(8, 8, 8)),
|
new opengl::BoxGeometry(QVector3D(8, 8, 8), QVector3D(0, 4, 0), QPoint(0, 0), QVector3D(8, 8, 8)),
|
||||||
new opengl::BoxGeometry(QVector3D(9, 9, 9), QVector3D(0, 4, 0), QPoint(32, 0), QVector3D(8, 8, 8)),
|
|
||||||
// body
|
// body
|
||||||
new opengl::BoxGeometry(QVector3D(8, 12, 4), QVector3D(0, -6, 0), QPoint(16, 16), QVector3D(8, 12, 4)),
|
new opengl::BoxGeometry(QVector3D(8, 12, 4), QVector3D(0, -6, 0), QPoint(16, 16), QVector3D(8, 12, 4)),
|
||||||
new opengl::BoxGeometry(QVector3D(8.5, 12.5, 4.5), QVector3D(0, -6, 0), QPoint(16, 32), QVector3D(8, 12, 4)),
|
|
||||||
// right leg
|
// right leg
|
||||||
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(-1.9, -18, -0.1), QPoint(0, 16), QVector3D(4, 12, 4)),
|
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(-1.9, -18, -0.1), QPoint(0, 16), QVector3D(4, 12, 4)),
|
||||||
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(-1.9, -18, -0.1), QPoint(0, 32), QVector3D(4, 12, 4)),
|
|
||||||
// left leg
|
// left leg
|
||||||
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(1.9, -18, -0.1), QPoint(16, 48), QVector3D(4, 12, 4)),
|
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(1.9, -18, -0.1), QPoint(16, 48), QVector3D(4, 12, 4)),
|
||||||
|
};
|
||||||
|
|
||||||
|
m_staticComponentsOverlay = {
|
||||||
|
// head
|
||||||
|
new opengl::BoxGeometry(QVector3D(9, 9, 9), QVector3D(0, 4, 0), QPoint(32, 0), QVector3D(8, 8, 8)),
|
||||||
|
// body
|
||||||
|
new opengl::BoxGeometry(QVector3D(8.5, 12.5, 4.5), QVector3D(0, -6, 0), QPoint(16, 32), QVector3D(8, 12, 4)),
|
||||||
|
// right leg
|
||||||
|
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(-1.9, -18, -0.1), QPoint(0, 32), QVector3D(4, 12, 4)),
|
||||||
|
// left leg
|
||||||
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(1.9, -18, -0.1), QPoint(0, 48), QVector3D(4, 12, 4)),
|
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(1.9, -18, -0.1), QPoint(0, 48), QVector3D(4, 12, 4)),
|
||||||
};
|
};
|
||||||
|
|
||||||
m_normalArms = {
|
m_normalArms = {
|
||||||
// Right Arm
|
// Right Arm
|
||||||
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(-6, -6, 0), QPoint(40, 16), QVector3D(4, 12, 4)),
|
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(-6, -6, 0), QPoint(40, 16), QVector3D(4, 12, 4)),
|
||||||
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(-6, -6, 0), QPoint(40, 32), QVector3D(4, 12, 4)),
|
|
||||||
// Left Arm
|
// Left Arm
|
||||||
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(6, -6, 0), QPoint(32, 48), QVector3D(4, 12, 4)),
|
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(6, -6, 0), QPoint(32, 48), QVector3D(4, 12, 4)),
|
||||||
|
};
|
||||||
|
|
||||||
|
m_normalArmsOverlay = {
|
||||||
|
// Right Arm
|
||||||
|
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(-6, -6, 0), QPoint(40, 32), QVector3D(4, 12, 4)),
|
||||||
|
// Left Arm
|
||||||
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(6, -6, 0), QPoint(48, 48), QVector3D(4, 12, 4)),
|
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(6, -6, 0), QPoint(48, 48), QVector3D(4, 12, 4)),
|
||||||
};
|
};
|
||||||
|
|
||||||
m_slimArms = {
|
m_slimArms = {
|
||||||
// Right Arm
|
// Right Arm
|
||||||
new opengl::BoxGeometry(QVector3D(3, 12, 4), QVector3D(-5.5, -6, 0), QPoint(40, 16), QVector3D(3, 12, 4)),
|
new opengl::BoxGeometry(QVector3D(3, 12, 4), QVector3D(-5.5, -6, 0), QPoint(40, 16), QVector3D(3, 12, 4)),
|
||||||
new opengl::BoxGeometry(QVector3D(3.5, 12.5, 4.5), QVector3D(-5.5, -6, 0), QPoint(40, 32), QVector3D(3, 12, 4)),
|
|
||||||
// Left Arm
|
// Left Arm
|
||||||
new opengl::BoxGeometry(QVector3D(3, 12, 4), QVector3D(5.5, -6, 0), QPoint(32, 48), QVector3D(3, 12, 4)),
|
new opengl::BoxGeometry(QVector3D(3, 12, 4), QVector3D(5.5, -6, 0), QPoint(32, 48), QVector3D(3, 12, 4)),
|
||||||
|
};
|
||||||
|
|
||||||
|
m_slimArmsOverlay = {
|
||||||
|
// Right Arm
|
||||||
|
new opengl::BoxGeometry(QVector3D(3.5, 12.5, 4.5), QVector3D(-5.5, -6, 0), QPoint(40, 32), QVector3D(3, 12, 4)),
|
||||||
|
// Left Arm
|
||||||
new opengl::BoxGeometry(QVector3D(3.5, 12.5, 4.5), QVector3D(5.5, -6, 0), QPoint(48, 48), QVector3D(3, 12, 4)),
|
new opengl::BoxGeometry(QVector3D(3.5, 12.5, 4.5), QVector3D(5.5, -6, 0), QPoint(48, 48), QVector3D(3, 12, 4)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -88,7 +106,8 @@ Scene::Scene(const QImage& skin, bool slim, const QImage& cape) : QOpenGLFunctio
|
||||||
}
|
}
|
||||||
Scene::~Scene()
|
Scene::~Scene()
|
||||||
{
|
{
|
||||||
for (auto array : { m_staticComponents, m_normalArms, m_slimArms, m_elytra }) {
|
for (auto array :
|
||||||
|
{ m_staticComponents, m_normalArms, m_slimArms, m_elytra, m_staticComponentsOverlay, m_normalArmsOverlay, m_slimArmsOverlay }) {
|
||||||
for (auto g : array) {
|
for (auto g : array) {
|
||||||
delete g;
|
delete g;
|
||||||
}
|
}
|
||||||
|
|
@ -106,7 +125,8 @@ void Scene::draw(QOpenGLShaderProgram* program)
|
||||||
{
|
{
|
||||||
m_skinTexture->bind();
|
m_skinTexture->bind();
|
||||||
program->setUniformValue("texture", 0);
|
program->setUniformValue("texture", 0);
|
||||||
for (auto toDraw : { m_staticComponents, m_slim ? m_slimArms : m_normalArms }) {
|
for (auto toDraw : { m_staticComponents, m_slim ? m_slimArms : m_normalArms, m_staticComponentsOverlay,
|
||||||
|
m_slim ? m_slimArmsOverlay : m_normalArmsOverlay }) {
|
||||||
for (auto g : toDraw) {
|
for (auto g : toDraw) {
|
||||||
g->draw(program);
|
g->draw(program);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,9 @@ class Scene : protected QOpenGLFunctions {
|
||||||
QList<BoxGeometry*> m_staticComponents;
|
QList<BoxGeometry*> m_staticComponents;
|
||||||
QList<BoxGeometry*> m_normalArms;
|
QList<BoxGeometry*> m_normalArms;
|
||||||
QList<BoxGeometry*> m_slimArms;
|
QList<BoxGeometry*> m_slimArms;
|
||||||
|
QList<BoxGeometry*> m_staticComponentsOverlay;
|
||||||
|
QList<BoxGeometry*> m_normalArmsOverlay;
|
||||||
|
QList<BoxGeometry*> m_slimArmsOverlay;
|
||||||
BoxGeometry* m_cape = nullptr;
|
BoxGeometry* m_cape = nullptr;
|
||||||
QList<BoxGeometry*> m_elytra;
|
QList<BoxGeometry*> m_elytra;
|
||||||
QOpenGLTexture* m_skinTexture = nullptr;
|
QOpenGLTexture* m_skinTexture = nullptr;
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#include <QVector2D>
|
#include <QVector2D>
|
||||||
#include <QVector3D>
|
#include <QVector3D>
|
||||||
#include <QtMath>
|
#include <QtMath>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#include "minecraft/skins/SkinModel.h"
|
#include "minecraft/skins/SkinModel.h"
|
||||||
#include "rainbow.h"
|
#include "rainbow.h"
|
||||||
|
|
@ -270,11 +271,12 @@ void SkinOpenGLWindow::updateCape(const QImage& cape)
|
||||||
|
|
||||||
QColor calculateContrastingColor(const QColor& color)
|
QColor calculateContrastingColor(const QColor& color)
|
||||||
{
|
{
|
||||||
constexpr float contrast = 0.2;
|
|
||||||
auto luma = Rainbow::luma(color);
|
auto luma = Rainbow::luma(color);
|
||||||
if (luma < 0.5) {
|
if (luma < 0.5) {
|
||||||
|
constexpr float contrast = 0.05;
|
||||||
return Rainbow::lighten(color, contrast);
|
return Rainbow::lighten(color, contrast);
|
||||||
} else {
|
} else {
|
||||||
|
constexpr float contrast = 0.2;
|
||||||
return Rainbow::darken(color, contrast);
|
return Rainbow::darken(color, contrast);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -282,11 +284,14 @@ QColor calculateContrastingColor(const QColor& color)
|
||||||
QImage generateChessboardImage(int width, int height, int tileSize, QColor baseColor)
|
QImage generateChessboardImage(int width, int height, int tileSize, QColor baseColor)
|
||||||
{
|
{
|
||||||
QImage image(width, height, QImage::Format_RGB888);
|
QImage image(width, height, QImage::Format_RGB888);
|
||||||
auto white = baseColor;
|
bool isDarkBase = Rainbow::luma(baseColor) < 0.5;
|
||||||
auto black = calculateContrastingColor(baseColor);
|
float contrast = isDarkBase ? 0.05 : 0.45;
|
||||||
|
auto contrastFunc = std::bind(isDarkBase ? Rainbow::lighten : Rainbow::darken, std::placeholders::_1, contrast, 1.0);
|
||||||
|
auto white = contrastFunc(baseColor);
|
||||||
|
auto black = contrastFunc(calculateContrastingColor(baseColor));
|
||||||
for (int y = 0; y < height; ++y) {
|
for (int y = 0; y < height; ++y) {
|
||||||
for (int x = 0; x < width; ++x) {
|
for (int x = 0; x < width; ++x) {
|
||||||
bool isWhite = ((x / tileSize) % 2) == ((y / tileSize) % 2);
|
bool isWhite = ((x / tileSize) + (y / tileSize)) % 2 == 0;
|
||||||
image.setPixelColor(x, y, isWhite ? white : black);
|
image.setPixelColor(x, y, isWhite ? white : black);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -325,3 +330,9 @@ void SkinOpenGLWindow::setElytraVisible(bool visible)
|
||||||
if (m_scene)
|
if (m_scene)
|
||||||
m_scene->setElytraVisible(visible);
|
m_scene->setElytraVisible(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SkinOpenGLWindow::hasOpenGL()
|
||||||
|
{
|
||||||
|
QOpenGLContext ctx;
|
||||||
|
return ctx.create();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ class SkinOpenGLWindow : public QOpenGLWindow, protected QOpenGLFunctions {
|
||||||
void updateCape(const QImage& cape);
|
void updateCape(const QImage& cape);
|
||||||
void setElytraVisible(bool visible);
|
void setElytraVisible(bool visible);
|
||||||
|
|
||||||
|
static bool hasOpenGL();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void mousePressEvent(QMouseEvent* e) override;
|
void mousePressEvent(QMouseEvent* e) override;
|
||||||
void mouseReleaseEvent(QMouseEvent* e) override;
|
void mouseReleaseEvent(QMouseEvent* e) override;
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,6 @@
|
||||||
#include "AccessibleInstanceView_p.h"
|
#include "AccessibleInstanceView_p.h"
|
||||||
#include "InstanceView.h"
|
#include "InstanceView.h"
|
||||||
|
|
||||||
#include <qaccessible.h>
|
|
||||||
#include <qheaderview.h>
|
|
||||||
#include <qvariant.h>
|
|
||||||
|
|
||||||
#ifndef QT_NO_ACCESSIBILITY
|
#ifndef QT_NO_ACCESSIBILITY
|
||||||
|
|
||||||
QAccessibleInterface* groupViewAccessibleFactory(const QString& classname, QObject* object)
|
QAccessibleInterface* groupViewAccessibleFactory(const QString& classname, QObject* object)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
<height>620</height>
|
<height>620</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,7 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared
|
||||||
m_model->loadColumns(ui->treeView);
|
m_model->loadColumns(ui->treeView);
|
||||||
connect(ui->treeView->header(), &QHeaderView::sectionResized, this, [this] { m_model->saveColumns(ui->treeView); });
|
connect(ui->treeView->header(), &QHeaderView::sectionResized, this, [this] { m_model->saveColumns(ui->treeView); });
|
||||||
connect(ui->filterEdit, &QLineEdit::textChanged, this, &ExternalResourcesPage::filterTextChanged);
|
connect(ui->filterEdit, &QLineEdit::textChanged, this, &ExternalResourcesPage::filterTextChanged);
|
||||||
|
updateActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
ExternalResourcesPage::~ExternalResourcesPage()
|
ExternalResourcesPage::~ExternalResourcesPage()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
#include <qtconcurrentrun.h>
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
@ -80,7 +79,13 @@ void McClient::parseResponse()
|
||||||
Q_UNUSED(readVarInt(m_resp)); // json length
|
Q_UNUSED(readVarInt(m_resp)); // json length
|
||||||
|
|
||||||
// 'resp' should now be the JSON string
|
// 'resp' should now be the JSON string
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(m_resp);
|
QJsonParseError parseError;
|
||||||
|
QJsonDocument doc = Json::parseUntilGarbage(m_resp, &parseError);
|
||||||
|
if (parseError.error != QJsonParseError::NoError) {
|
||||||
|
qDebug() << "Failed to parse JSON:" << parseError.errorString();
|
||||||
|
emitFail(parseError.errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
emitSucceed(doc.object());
|
emitSucceed(doc.object());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,19 @@
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
|
|
||||||
#include <Json.h>
|
#include <Json.h>
|
||||||
|
#include "Exception.h"
|
||||||
#include "McClient.h"
|
#include "McClient.h"
|
||||||
#include "McResolver.h"
|
#include "McResolver.h"
|
||||||
#include "ServerPingTask.h"
|
#include "ServerPingTask.h"
|
||||||
|
|
||||||
unsigned getOnlinePlayers(QJsonObject data)
|
unsigned getOnlinePlayers(QJsonObject data)
|
||||||
{
|
{
|
||||||
return Json::requireInteger(Json::requireObject(data, "players"), "online");
|
try {
|
||||||
|
return Json::requireInteger(Json::requireObject(data, "players"), "online");
|
||||||
|
} catch (Exception& e) {
|
||||||
|
qWarning() << "server ping failed to parse response" << e.what();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerPingTask::executeTask()
|
void ServerPingTask::executeTask()
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,10 @@
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Note: If your FTB instances are not in the default location, select it using the button next to search.</string>
|
<string>Note: Many recent FTB modpacks are also available from CurseForge! Also, if your FTB instances are not in the default location, select it using the button next to search.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignCenter</set>
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
@ -47,8 +47,7 @@
|
||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="viewfolder">
|
<iconset theme="viewfolder"/>
|
||||||
<normaloff>.</normaloff>.</iconset>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="flat">
|
<property name="flat">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
|
@ -82,7 +81,7 @@
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer">
|
<spacer name="horizontalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
|
||||||
++it;
|
++it;
|
||||||
#endif
|
#endif
|
||||||
for (const auto& version : m_current->versions) {
|
for (const auto& version : m_current->versions) {
|
||||||
m_ui->versionSelectionBox->addItem(version.getVersionDisplayString(), QVariant(version.addonId));
|
m_ui->versionSelectionBox->addItem(version.getVersionDisplayString(), QVariant(version.fileId));
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant current_updated;
|
QVariant current_updated;
|
||||||
|
|
@ -227,9 +227,9 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
|
||||||
for (auto version : m_current->versions) {
|
for (auto version : m_current->versions) {
|
||||||
if (!version.version.contains(version.version))
|
if (!version.version.contains(version.version))
|
||||||
m_ui->versionSelectionBox->addItem(QString("%1 - %2").arg(version.version, version.version_number),
|
m_ui->versionSelectionBox->addItem(QString("%1 - %2").arg(version.version, version.version_number),
|
||||||
QVariant(version.addonId));
|
QVariant(version.fileId));
|
||||||
else
|
else
|
||||||
m_ui->versionSelectionBox->addItem(version.version, QVariant(version.addonId));
|
m_ui->versionSelectionBox->addItem(version.version, QVariant(version.fileId));
|
||||||
}
|
}
|
||||||
|
|
||||||
suggestCurrent();
|
suggestCurrent();
|
||||||
|
|
@ -312,7 +312,7 @@ void ModrinthPage::suggestCurrent()
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& ver : m_current->versions) {
|
for (auto& ver : m_current->versions) {
|
||||||
if (ver.addonId == m_selectedVersion) {
|
if (ver.fileId == m_selectedVersion) {
|
||||||
QMap<QString, QString> extra_info;
|
QMap<QString, QString> extra_info;
|
||||||
extra_info.insert("pack_id", m_current->addonId.toString());
|
extra_info.insert("pack_id", m_current->addonId.toString());
|
||||||
extra_info.insert("pack_version_id", ver.fileId.toString());
|
extra_info.insert("pack_version_id", ver.fileId.toString());
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
#include <BaseVersion.h>
|
#include <BaseVersion.h>
|
||||||
#include <QObjectPtr.h>
|
#include <QObjectPtr.h>
|
||||||
#include <java/JavaChecker.h>
|
#include <java/JavaChecker.h>
|
||||||
#include <qcheckbox.h>
|
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
|
||||||
class QLineEdit;
|
class QLineEdit;
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QProcess>
|
||||||
#include <QProgressDialog>
|
#include <QProgressDialog>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
@ -34,7 +35,6 @@
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
|
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
#include "FileSystem.h"
|
|
||||||
|
|
||||||
#include "ui/dialogs/UpdateAvailableDialog.h"
|
#include "ui/dialogs/UpdateAvailableDialog.h"
|
||||||
|
|
||||||
|
|
@ -97,9 +97,14 @@ void PrismExternalUpdater::checkForUpdates(bool triggeredByUser)
|
||||||
progress.show();
|
progress.show();
|
||||||
QCoreApplication::processEvents();
|
QCoreApplication::processEvents();
|
||||||
|
|
||||||
|
QProcess proc;
|
||||||
auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
|
auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
exe_name.append(".exe");
|
exe_name.append(".exe");
|
||||||
|
|
||||||
|
auto env = QProcessEnvironment::systemEnvironment();
|
||||||
|
env.insert("__COMPAT_LAYER", "RUNASINVOKER");
|
||||||
|
proc.setProcessEnvironment(env);
|
||||||
#else
|
#else
|
||||||
exe_name = QString("bin/%1").arg(exe_name);
|
exe_name = QString("bin/%1").arg(exe_name);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -108,21 +113,15 @@ void PrismExternalUpdater::checkForUpdates(bool triggeredByUser)
|
||||||
if (priv->allowBeta)
|
if (priv->allowBeta)
|
||||||
args.append("--pre-release");
|
args.append("--pre-release");
|
||||||
|
|
||||||
auto proc = FS::createProcess(priv->appDir.absoluteFilePath(exe_name), args);
|
proc.start(priv->appDir.absoluteFilePath(exe_name), args);
|
||||||
#if defined Q_OS_WIN32
|
auto result_start = proc.waitForStarted(5000);
|
||||||
auto env = QProcessEnvironment::systemEnvironment();
|
|
||||||
env.insert("__COMPAT_LAYER", "RUNASINVOKER");
|
|
||||||
proc->setProcessEnvironment(env);
|
|
||||||
#endif
|
|
||||||
proc->start(proc->program(), proc->arguments());
|
|
||||||
auto result_start = proc->waitForStarted(5000);
|
|
||||||
if (!result_start) {
|
if (!result_start) {
|
||||||
auto err = proc->error();
|
auto err = proc.error();
|
||||||
qDebug() << "Failed to start updater after 5 seconds."
|
qDebug() << "Failed to start updater after 5 seconds."
|
||||||
<< "reason:" << err << proc->errorString();
|
<< "reason:" << err << proc.errorString();
|
||||||
auto msgBox =
|
auto msgBox =
|
||||||
QMessageBox(QMessageBox::Information, tr("Update Check Failed"),
|
QMessageBox(QMessageBox::Information, tr("Update Check Failed"),
|
||||||
tr("Failed to start after 5 seconds\nReason: %1.").arg(proc->errorString()), QMessageBox::Ok, priv->parent);
|
tr("Failed to start after 5 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent);
|
||||||
msgBox.setMinimumWidth(460);
|
msgBox.setMinimumWidth(460);
|
||||||
msgBox.adjustSize();
|
msgBox.adjustSize();
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
|
|
@ -134,16 +133,16 @@ void PrismExternalUpdater::checkForUpdates(bool triggeredByUser)
|
||||||
}
|
}
|
||||||
QCoreApplication::processEvents();
|
QCoreApplication::processEvents();
|
||||||
|
|
||||||
auto result_finished = proc->waitForFinished(60000);
|
auto result_finished = proc.waitForFinished(60000);
|
||||||
if (!result_finished) {
|
if (!result_finished) {
|
||||||
proc->kill();
|
proc.kill();
|
||||||
auto err = proc->error();
|
auto err = proc.error();
|
||||||
auto output = proc->readAll();
|
auto output = proc.readAll();
|
||||||
qDebug() << "Updater failed to close after 60 seconds."
|
qDebug() << "Updater failed to close after 60 seconds."
|
||||||
<< "reason:" << err << proc->errorString();
|
<< "reason:" << err << proc.errorString();
|
||||||
auto msgBox =
|
auto msgBox =
|
||||||
QMessageBox(QMessageBox::Information, tr("Update Check Failed"),
|
QMessageBox(QMessageBox::Information, tr("Update Check Failed"),
|
||||||
tr("Updater failed to close 60 seconds\nReason: %1.").arg(proc->errorString()), QMessageBox::Ok, priv->parent);
|
tr("Updater failed to close 60 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent);
|
||||||
msgBox.setDetailedText(output);
|
msgBox.setDetailedText(output);
|
||||||
msgBox.setMinimumWidth(460);
|
msgBox.setMinimumWidth(460);
|
||||||
msgBox.adjustSize();
|
msgBox.adjustSize();
|
||||||
|
|
@ -155,10 +154,10 @@ void PrismExternalUpdater::checkForUpdates(bool triggeredByUser)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto exit_code = proc->exitCode();
|
auto exit_code = proc.exitCode();
|
||||||
|
|
||||||
auto std_output = proc->readAllStandardOutput();
|
auto std_output = proc.readAllStandardOutput();
|
||||||
auto std_error = proc->readAllStandardError();
|
auto std_error = proc.readAllStandardError();
|
||||||
|
|
||||||
progress.hide();
|
progress.hide();
|
||||||
QCoreApplication::processEvents();
|
QCoreApplication::processEvents();
|
||||||
|
|
@ -336,9 +335,14 @@ void PrismExternalUpdater::offerUpdate(const QString& version_name, const QStrin
|
||||||
|
|
||||||
void PrismExternalUpdater::performUpdate(const QString& version_tag)
|
void PrismExternalUpdater::performUpdate(const QString& version_tag)
|
||||||
{
|
{
|
||||||
|
QProcess proc;
|
||||||
auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
|
auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
exe_name.append(".exe");
|
exe_name.append(".exe");
|
||||||
|
|
||||||
|
auto env = QProcessEnvironment::systemEnvironment();
|
||||||
|
env.insert("__COMPAT_LAYER", "RUNASINVOKER");
|
||||||
|
proc.setProcessEnvironment(env);
|
||||||
#else
|
#else
|
||||||
exe_name = QString("bin/%1").arg(exe_name);
|
exe_name = QString("bin/%1").arg(exe_name);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -347,16 +351,9 @@ void PrismExternalUpdater::performUpdate(const QString& version_tag)
|
||||||
if (priv->allowBeta)
|
if (priv->allowBeta)
|
||||||
args.append("--pre-release");
|
args.append("--pre-release");
|
||||||
|
|
||||||
auto proc = FS::createProcess(exe_name, args);
|
auto result = proc.startDetached(priv->appDir.absoluteFilePath(exe_name), args);
|
||||||
#if defined Q_OS_WIN32
|
|
||||||
auto env = QProcessEnvironment::systemEnvironment();
|
|
||||||
env.insert("__COMPAT_LAYER", "RUNASINVOKER");
|
|
||||||
proc->setProcessEnvironment(env);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
auto result = proc->startDetached(priv->appDir.absoluteFilePath(exe_name), args);
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
qDebug() << "Failed to start updater:" << proc->error() << proc->errorString();
|
qDebug() << "Failed to start updater:" << proc.error() << proc.errorString();
|
||||||
}
|
}
|
||||||
QCoreApplication::exit();
|
QCoreApplication::exit();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,19 +124,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
|
||||||
logToConsole = parser.isSet("debug");
|
logToConsole = parser.isSet("debug");
|
||||||
|
|
||||||
QString origCwdPath = QDir::currentPath();
|
QString origCwdPath = QDir::currentPath();
|
||||||
#if defined(Q_OS_LINUX)
|
|
||||||
// NOTE(@getchoo): In order for `go-appimage` to generate self-contained AppImages, it executes apps from a bundled linker at
|
|
||||||
// <root>/lib64
|
|
||||||
// This is not the path to our actual binary, which we want
|
|
||||||
QString binPath;
|
|
||||||
if (DesktopServices::isSelfContained()) {
|
|
||||||
binPath = FS::PathCombine(applicationDirPath(), "../usr/bin");
|
|
||||||
} else {
|
|
||||||
binPath = applicationDirPath();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
QString binPath = applicationDirPath();
|
QString binPath = applicationDirPath();
|
||||||
#endif
|
|
||||||
|
|
||||||
{ // find data director
|
{ // find data director
|
||||||
// Root path is used for updates and portable data
|
// Root path is used for updates and portable data
|
||||||
|
|
@ -819,16 +807,13 @@ QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset)
|
||||||
bool PrismUpdaterApp::callAppImageUpdate()
|
bool PrismUpdaterApp::callAppImageUpdate()
|
||||||
{
|
{
|
||||||
auto appimage_path = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE"));
|
auto appimage_path = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE"));
|
||||||
|
QProcess proc = QProcess();
|
||||||
qDebug() << "Calling: AppImageUpdate" << appimage_path;
|
qDebug() << "Calling: AppImageUpdate" << appimage_path;
|
||||||
const auto program = FS::PathCombine(m_rootPath, "bin", "AppImageUpdate.AppImage");
|
proc.setProgram(FS::PathCombine(m_rootPath, "bin", "AppImageUpdate.AppImage"));
|
||||||
auto proc = FS::createProcess(program, { appimage_path });
|
proc.setArguments({ appimage_path });
|
||||||
if (!proc) {
|
auto result = proc.startDetached();
|
||||||
qCritical() << "Unable to create process:" << program;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto result = proc->startDetached();
|
|
||||||
if (!result)
|
if (!result)
|
||||||
qDebug() << "Failed to start AppImageUpdate reason:" << proc->errorString();
|
qDebug() << "Failed to start AppImageUpdate reason:" << proc.errorString();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1091,42 +1076,13 @@ std::optional<QDir> PrismUpdaterApp::unpackArchive(QFileInfo archive)
|
||||||
FS::ensureFolderPathExists(temp_extract_path);
|
FS::ensureFolderPathExists(temp_extract_path);
|
||||||
auto tmp_extract_dir = QDir(temp_extract_path);
|
auto tmp_extract_dir = QDir(temp_extract_path);
|
||||||
|
|
||||||
if (archive.fileName().endsWith(".zip")) {
|
auto result = MMCZip::extractDir(archive.absoluteFilePath(), tmp_extract_dir.absolutePath());
|
||||||
auto result = MMCZip::extractDir(archive.absoluteFilePath(), tmp_extract_dir.absolutePath());
|
if (result) {
|
||||||
if (result) {
|
logUpdate(tr("Extracted the following to \"%1\":\n %2").arg(tmp_extract_dir.absolutePath()).arg(result->join("\n ")));
|
||||||
logUpdate(tr("Extracted the following to \"%1\":\n %2").arg(tmp_extract_dir.absolutePath()).arg(result->join("\n ")));
|
|
||||||
} else {
|
|
||||||
logUpdate(tr("Failed to extract %1 to %2").arg(archive.absoluteFilePath()).arg(tmp_extract_dir.absolutePath()));
|
|
||||||
showFatalErrorMessage("Failed to extract archive",
|
|
||||||
tr("Failed to extract %1 to %2").arg(archive.absoluteFilePath()).arg(tmp_extract_dir.absolutePath()));
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (archive.fileName().endsWith(".tar.gz")) {
|
|
||||||
QString cmd = "tar";
|
|
||||||
QStringList args = { "-xvf", archive.absoluteFilePath(), "-C", tmp_extract_dir.absolutePath() };
|
|
||||||
logUpdate(tr("Running: `%1 %2`").arg(cmd).arg(args.join(" ")));
|
|
||||||
QProcess proc = QProcess();
|
|
||||||
proc.start(cmd, args);
|
|
||||||
if (!proc.waitForStarted(5000)) { // wait 5 seconds to start
|
|
||||||
auto msg = tr("Failed to launch child process \"%1 %2\".").arg(cmd).arg(args.join(" "));
|
|
||||||
logUpdate(msg);
|
|
||||||
showFatalErrorMessage(tr("Failed extract archive"), msg);
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
auto result = proc.waitForFinished(5000);
|
|
||||||
auto out = proc.readAll();
|
|
||||||
logUpdate(out);
|
|
||||||
if (!result) {
|
|
||||||
auto msg = tr("Child process \"%1 %2\" failed.").arg(cmd).arg(args.join(" "));
|
|
||||||
logUpdate(msg);
|
|
||||||
showFatalErrorMessage(tr("Failed to extract archive"), msg);
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
logUpdate(tr("Unknown archive format for %1").arg(archive.absoluteFilePath()));
|
logUpdate(tr("Failed to extract %1 to %2").arg(archive.absoluteFilePath()).arg(tmp_extract_dir.absolutePath()));
|
||||||
showFatalErrorMessage("Can not extract", QStringLiteral("Unknown archive format %1").arg(archive.absoluteFilePath()));
|
showFatalErrorMessage("Failed to extract archive",
|
||||||
|
tr("Failed to extract %1 to %2").arg(archive.absoluteFilePath()).arg(tmp_extract_dir.absolutePath()));
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ symlinkJoin {
|
||||||
|
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
kdePackages.qtbase
|
kdePackages.qtbase
|
||||||
|
kdePackages.qtimageformats
|
||||||
kdePackages.qtsvg
|
kdePackages.qtsvg
|
||||||
]
|
]
|
||||||
++ lib.optional (
|
++ lib.optional (
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ set(Launcher_MetaInfo "program_info/${Launcher_AppID}.metainfo.xml" PARENT_SCOPE
|
||||||
set(Launcher_PNG_256 "program_info/${Launcher_AppID}_256.png" PARENT_SCOPE)
|
set(Launcher_PNG_256 "program_info/${Launcher_AppID}_256.png" PARENT_SCOPE)
|
||||||
set(Launcher_SVG "program_info/${Launcher_SVGFileName}" PARENT_SCOPE)
|
set(Launcher_SVG "program_info/${Launcher_SVGFileName}" PARENT_SCOPE)
|
||||||
set(Launcher_Branding_ICNS "program_info/prismlauncher.icns" PARENT_SCOPE)
|
set(Launcher_Branding_ICNS "program_info/prismlauncher.icns" PARENT_SCOPE)
|
||||||
|
set(Launcher_Branding_MAC_ICON "program_info/PrismLauncher.icon" PARENT_SCOPE)
|
||||||
set(Launcher_Branding_ICO "program_info/prismlauncher.ico")
|
set(Launcher_Branding_ICO "program_info/prismlauncher.ico")
|
||||||
set(Launcher_Branding_ICO "${Launcher_Branding_ICO}" PARENT_SCOPE)
|
set(Launcher_Branding_ICO "${Launcher_Branding_ICO}" PARENT_SCOPE)
|
||||||
set(Launcher_Branding_WindowsRC "program_info/prismlauncher.rc" PARENT_SCOPE)
|
set(Launcher_Branding_WindowsRC "program_info/prismlauncher.rc" PARENT_SCOPE)
|
||||||
|
|
|
||||||
91
program_info/PrismLauncher.icon/Assets/block.svg
Normal file
91
program_info/PrismLauncher.icon/Assets/block.svg
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="48"
|
||||||
|
height="48"
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 12.7 12.7"
|
||||||
|
id="svg_combined"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<title>Prism Launcher Logo</title>
|
||||||
|
<g
|
||||||
|
transform="matrix(0.88,0,0,0.88,-10.906,-1.2421)"
|
||||||
|
id="g32_top"
|
||||||
|
style="display:inline">
|
||||||
|
<g
|
||||||
|
transform="translate(13.26,2.2776)"
|
||||||
|
id="g28"
|
||||||
|
style="display:inline">
|
||||||
|
<path
|
||||||
|
transform="matrix(0.96975,0,0,0.96975,0.1921,0.1921)"
|
||||||
|
d="m 6.3498,2.9393 c -0.34105,0 -2.7827,1.4099 -2.9532,1.7052 L 6.3498,9.7602 9.3036,4.6445 C 9.13308,4.34915 6.6909,2.9393 6.3498,2.9393 Z"
|
||||||
|
fill="#ffffff"
|
||||||
|
stroke-width="0.26458"
|
||||||
|
id="path26"
|
||||||
|
style="display:inline" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(0.88,0,0,0.88,-10.906,-1.2421)"
|
||||||
|
id="g32"
|
||||||
|
style="display:inline">
|
||||||
|
<path
|
||||||
|
d="m 16.746,6.9737 2.8639,4.9609 c 0.33073,0 2.6991,-1.3672 2.8644,-1.6536 0.16536,-0.28642 0.16536,-3.0209 0,-3.3073 l -2.8644,1.6536 z"
|
||||||
|
fill="#dfdfdf"
|
||||||
|
stroke-width="0.26458"
|
||||||
|
id="path30"
|
||||||
|
style="display:inline" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
d="m 3.8299,4.8948 c -0.14551,0.25205 -0.14553,2.6584 0,2.9104 0.14553,0.25204 2.2292,1.4552 2.5203,1.4552 V 6.35 Z"
|
||||||
|
fill="#d6d2d2"
|
||||||
|
stroke-width="0.26458"
|
||||||
|
id="path34"
|
||||||
|
style="display:inline" />
|
||||||
|
<metadata
|
||||||
|
id="metadata64">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:title>Prism Launcher Logo</dc:title>
|
||||||
|
<dc:date>19/10/2022</dc:date>
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Prism Launcher</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
<dc:contributor>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:contributor>
|
||||||
|
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
|
||||||
|
<dc:publisher>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Prism Launcher</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:publisher>
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3 KiB |
95
program_info/PrismLauncher.icon/Assets/rainbow.svg
Normal file
95
program_info/PrismLauncher.icon/Assets/rainbow.svg
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="48"
|
||||||
|
height="48"
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 12.7 12.7"
|
||||||
|
id="svg_rainbow"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<title>Prism Launcher Logo</title>
|
||||||
|
<g stroke-width="0.26458">
|
||||||
|
<path
|
||||||
|
d="M 6.35,0.52917 3.8292,4.89477 6.35,6.34997 8.8703,4.89477 8.97985,1.79517 C 7.82875,1.13058 6.64105,0.52907 6.35005,0.52907 Z"
|
||||||
|
fill="#df6277"
|
||||||
|
id="path6" />
|
||||||
|
<path
|
||||||
|
d="M 8.9798,1.7952 6.35,6.35 8.8703,7.8052 11.3911,3.4396 C 11.24558,3.18755 10.131,2.45985 8.9799,1.7953 Z"
|
||||||
|
fill="#fb9168"
|
||||||
|
id="path8" />
|
||||||
|
<path
|
||||||
|
d="M 11.391,3.4396 6.35,6.35 8.8703,7.8052 11.6092,6.35 c 0,-1.3292 -0.07255,-2.6584 -0.21808,-2.9104 z"
|
||||||
|
fill="#f3db6c"
|
||||||
|
id="path10" />
|
||||||
|
<path
|
||||||
|
d="m 6.35,6.35 v 2.9104 h 5.041 C 11.53652,9.00835 11.60907,7.6792 11.60908,6.35 Z"
|
||||||
|
fill="#7ab392"
|
||||||
|
id="path12" />
|
||||||
|
<path
|
||||||
|
d="m 6.35,6.35 v 2.9104 l 2.6298,1.6443 c 1.1511,-0.66459 2.2657,-1.3923 2.4112,-1.6443 z"
|
||||||
|
fill="#4b7cbc"
|
||||||
|
id="path14" />
|
||||||
|
<path
|
||||||
|
d="M 6.35,6.35 3.8292,7.8052 6.35,12.1708 c 0.29104,0 1.4787,-0.60148 2.6298,-1.2661 z"
|
||||||
|
fill="#6f488c"
|
||||||
|
id="path16" />
|
||||||
|
<path
|
||||||
|
d="M 3.8292,4.8948 1.3089,9.2604 c 0.29104,0.5041 4.459,2.9104 5.041,2.9104 V 6.35 Z"
|
||||||
|
fill="#4d3f33"
|
||||||
|
id="path18" />
|
||||||
|
<path
|
||||||
|
d="m 1.309,3.4396 c -0.29104,0.5041 -0.29104,5.3167 0,5.8208 L 6.35,6.35 V 3.4396 Z"
|
||||||
|
fill="#7a573b"
|
||||||
|
id="path20" />
|
||||||
|
<path
|
||||||
|
d="m 6.35,0.52917 c -0.58208,-2e-8 -4.75,2.4063 -5.041,2.9104 l 5.041,2.9104 z"
|
||||||
|
fill="#99cd61"
|
||||||
|
id="path22" />
|
||||||
|
</g>
|
||||||
|
<metadata
|
||||||
|
id="metadata64">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:title>Prism Launcher Logo</dc:title>
|
||||||
|
<dc:date>19/10/2022</dc:date>
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Prism Launcher</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
<dc:contributor>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:contributor>
|
||||||
|
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
|
||||||
|
<dc:publisher>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Prism Launcher</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:publisher>
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.3 KiB |
72
program_info/PrismLauncher.icon/icon.json
Normal file
72
program_info/PrismLauncher.icon/icon.json
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
{
|
||||||
|
"color-space-for-untagged-svg-colors" : "display-p3",
|
||||||
|
"fill" : {
|
||||||
|
"solid" : "extended-gray:1.00000,1.00000"
|
||||||
|
},
|
||||||
|
"groups" : [
|
||||||
|
{
|
||||||
|
"layers" : [
|
||||||
|
{
|
||||||
|
"image-name" : "block.svg",
|
||||||
|
"name" : "block",
|
||||||
|
"position" : {
|
||||||
|
"scale" : 19.28,
|
||||||
|
"translation-in-points" : [
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"shadow" : {
|
||||||
|
"kind" : "neutral",
|
||||||
|
"opacity" : 0.5
|
||||||
|
},
|
||||||
|
"translucency" : {
|
||||||
|
"enabled" : true,
|
||||||
|
"value" : 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blur-material-specializations" : [
|
||||||
|
{
|
||||||
|
"value" : 0.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearance" : "dark",
|
||||||
|
"value" : null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"layers" : [
|
||||||
|
{
|
||||||
|
"blend-mode" : "normal",
|
||||||
|
"fill" : "automatic",
|
||||||
|
"glass" : true,
|
||||||
|
"hidden" : false,
|
||||||
|
"image-name" : "rainbow.svg",
|
||||||
|
"name" : "rainbow",
|
||||||
|
"position" : {
|
||||||
|
"scale" : 19.28,
|
||||||
|
"translation-in-points" : [
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"shadow" : {
|
||||||
|
"kind" : "neutral",
|
||||||
|
"opacity" : 0.5
|
||||||
|
},
|
||||||
|
"translucency" : {
|
||||||
|
"enabled" : false,
|
||||||
|
"value" : 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"supported-platforms" : {
|
||||||
|
"squares" : [
|
||||||
|
"macOS"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<ws2:longPathAware>true</ws2:longPathAware>
|
<ws2:longPathAware>true</ws2:longPathAware>
|
||||||
</windowsSettings>
|
</windowsSettings>
|
||||||
</application>
|
</application>
|
||||||
<assemblyIdentity name="PrismLauncher.Application.1" type="win32" version="@Launcher_VERSION_NAME4@" />
|
<assemblyIdentity name="@Launcher_CommonName@.Application.1" type="win32" version="@Launcher_VERSION_NAME4@" />
|
||||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
<security>
|
<security>
|
||||||
<requestedPrivileges>
|
<requestedPrivileges>
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,9 @@ BEGIN
|
||||||
BLOCK "000004b0"
|
BLOCK "000004b0"
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "MultiMC & Prism Launcher Contributors"
|
VALUE "CompanyName", "MultiMC & Prism Launcher Contributors"
|
||||||
VALUE "FileDescription", "Prism Launcher"
|
VALUE "FileDescription", "@Launcher_DisplayName@"
|
||||||
VALUE "FileVersion", "@Launcher_VERSION_NAME4@"
|
VALUE "FileVersion", "@Launcher_VERSION_NAME4@"
|
||||||
VALUE "ProductName", "Prism Launcher"
|
VALUE "ProductName", "@Launcher_DisplayName@"
|
||||||
VALUE "ProductVersion", "@Launcher_VERSION_NAME4@"
|
VALUE "ProductVersion", "@Launcher_VERSION_NAME4@"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
|
|
|
||||||
|
|
@ -393,9 +393,9 @@ Section "@Launcher_DisplayName@"
|
||||||
WriteRegStr HKCU Software\Classes\curseforge "URL Protocol" ""
|
WriteRegStr HKCU Software\Classes\curseforge "URL Protocol" ""
|
||||||
WriteRegStr HKCU Software\Classes\curseforge\shell\open\command "" '"$INSTDIR\@Launcher_APP_BINARY_NAME@.exe" "%1"'
|
WriteRegStr HKCU Software\Classes\curseforge\shell\open\command "" '"$INSTDIR\@Launcher_APP_BINARY_NAME@.exe" "%1"'
|
||||||
|
|
||||||
; Write the URL Handler into registry for prismlauncher
|
; Write the URL Handler into registry for prismlauncher
|
||||||
WriteRegStr HKCU Software\Classes\prismlauncher "URL Protocol" ""
|
WriteRegStr HKCU Software\Classes\@Launcher_APP_BINARY_NAME@ "URL Protocol" ""
|
||||||
WriteRegStr HKCU Software\Classes\prismlauncher\shell\open\command "" '"$INSTDIR\@Launcher_APP_BINARY_NAME@.exe" "%1"'
|
WriteRegStr HKCU Software\Classes\@Launcher_APP_BINARY_NAME@\shell\open\command "" '"$INSTDIR\@Launcher_APP_BINARY_NAME@.exe" "%1"'
|
||||||
|
|
||||||
; Write the uninstall keys for Windows
|
; Write the uninstall keys for Windows
|
||||||
; https://learn.microsoft.com/en-us/windows/win32/msi/uninstall-registry-key
|
; https://learn.microsoft.com/en-us/windows/win32/msi/uninstall-registry-key
|
||||||
|
|
|
||||||
191
tools/ninjatracing.py
Normal file
191
tools/ninjatracing.py
Normal file
|
|
@ -0,0 +1,191 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright 2018 Nico Weber
|
||||||
|
# Patched to work with v7 logs
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Converts one (or several) .ninja_log files into chrome's about:tracing format.
|
||||||
|
|
||||||
|
If clang -ftime-trace .json files are found adjacent to generated files they
|
||||||
|
are embedded.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
ninja -C $BUILDDIR
|
||||||
|
ninjatracing $BUILDDIR/.ninja_log > trace.json
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import optparse
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
class Target:
|
||||||
|
"""Represents a single line read for a .ninja_log file. Start and end times
|
||||||
|
are milliseconds."""
|
||||||
|
def __init__(self, start, end):
|
||||||
|
self.start = int(start)
|
||||||
|
self.end = int(end)
|
||||||
|
self.targets = []
|
||||||
|
|
||||||
|
|
||||||
|
def read_targets(log, show_all):
|
||||||
|
"""Reads all targets from .ninja_log file |log_file|, sorted by start
|
||||||
|
time"""
|
||||||
|
header = log.readline()
|
||||||
|
m = re.search(r'^# ninja log v(\d+)\n$', header)
|
||||||
|
assert m, "unrecognized ninja log version %r" % header
|
||||||
|
version = int(m.group(1))
|
||||||
|
assert 5 <= version <= 7, "unsupported ninja log version %d" % version
|
||||||
|
if version >= 6:
|
||||||
|
# Skip header line
|
||||||
|
next(log)
|
||||||
|
|
||||||
|
targets = {}
|
||||||
|
last_end_seen = 0
|
||||||
|
for line in log:
|
||||||
|
if line.startswith('#'):
|
||||||
|
continue
|
||||||
|
start, end, _, name, cmdhash = line.strip().split('\t') # Ignore restat.
|
||||||
|
if not show_all and int(end) < last_end_seen:
|
||||||
|
# An earlier time stamp means that this step is the first in a new
|
||||||
|
# build, possibly an incremental build. Throw away the previous data
|
||||||
|
# so that this new build will be displayed independently.
|
||||||
|
targets = {}
|
||||||
|
last_end_seen = int(end)
|
||||||
|
targets.setdefault(cmdhash, Target(start, end)).targets.append(name)
|
||||||
|
return sorted(targets.values(), key=lambda job: job.end, reverse=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Threads:
|
||||||
|
"""Tries to reconstruct the parallelism from a .ninja_log"""
|
||||||
|
def __init__(self):
|
||||||
|
self.workers = [] # Maps thread id to time that thread is occupied for.
|
||||||
|
|
||||||
|
def alloc(self, target):
|
||||||
|
"""Places target in an available thread, or adds a new thread."""
|
||||||
|
for worker in range(len(self.workers)):
|
||||||
|
if self.workers[worker] >= target.end:
|
||||||
|
self.workers[worker] = target.start
|
||||||
|
return worker
|
||||||
|
self.workers.append(target.start)
|
||||||
|
return len(self.workers) - 1
|
||||||
|
|
||||||
|
|
||||||
|
def read_events(trace, options):
|
||||||
|
"""Reads all events from time-trace json file |trace|."""
|
||||||
|
trace_data = json.load(trace)
|
||||||
|
|
||||||
|
def include_event(event, options):
|
||||||
|
"""Only include events if they are complete events, are longer than
|
||||||
|
granularity, and are not totals."""
|
||||||
|
return ((event['ph'] == 'X') and
|
||||||
|
(event['dur'] >= options['granularity']) and
|
||||||
|
(not event['name'].startswith('Total')))
|
||||||
|
|
||||||
|
return [x for x in trace_data['traceEvents'] if include_event(x, options)]
|
||||||
|
|
||||||
|
|
||||||
|
def trace_to_dicts(target, trace, options, pid, tid):
|
||||||
|
"""Read a file-like object |trace| containing -ftime-trace data and yields
|
||||||
|
about:tracing dict per eligible event in that log."""
|
||||||
|
ninja_time = (target.end - target.start) * 1000
|
||||||
|
for event in read_events(trace, options):
|
||||||
|
# Check if any event duration is greater than the duration from ninja.
|
||||||
|
if event['dur'] > ninja_time:
|
||||||
|
print("Inconsistent timing found (clang time > ninja time). Please"
|
||||||
|
" ensure that timings are from consistent builds.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Set tid and pid from ninja log.
|
||||||
|
event['pid'] = pid
|
||||||
|
event['tid'] = tid
|
||||||
|
|
||||||
|
# Offset trace time stamp by ninja start time.
|
||||||
|
event['ts'] += (target.start * 1000)
|
||||||
|
|
||||||
|
yield event
|
||||||
|
|
||||||
|
|
||||||
|
def embed_time_trace(ninja_log_dir, target, pid, tid, options):
|
||||||
|
"""Produce time trace output for the specified ninja target. Expects
|
||||||
|
time-trace file to be in .json file named based on .o file."""
|
||||||
|
for t in target.targets:
|
||||||
|
o_path = os.path.join(ninja_log_dir, t)
|
||||||
|
json_trace_path = os.path.splitext(o_path)[0] + '.json'
|
||||||
|
try:
|
||||||
|
with open(json_trace_path, 'r') as trace:
|
||||||
|
for time_trace_event in trace_to_dicts(target, trace, options,
|
||||||
|
pid, tid):
|
||||||
|
yield time_trace_event
|
||||||
|
except IOError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def log_to_dicts(log, pid, options):
|
||||||
|
"""Reads a file-like object |log| containing a .ninja_log, and yields one
|
||||||
|
about:tracing dict per command found in the log."""
|
||||||
|
threads = Threads()
|
||||||
|
for target in read_targets(log, options['showall']):
|
||||||
|
tid = threads.alloc(target)
|
||||||
|
|
||||||
|
yield {
|
||||||
|
'name': '%0s' % ', '.join(target.targets), 'cat': 'targets',
|
||||||
|
'ph': 'X', 'ts': (target.start * 1000),
|
||||||
|
'dur': ((target.end - target.start) * 1000),
|
||||||
|
'pid': pid, 'tid': tid, 'args': {},
|
||||||
|
}
|
||||||
|
if options.get('embed_time_trace', False):
|
||||||
|
# Add time-trace information into the ninja trace.
|
||||||
|
try:
|
||||||
|
ninja_log_dir = os.path.dirname(log.name)
|
||||||
|
except AttributeError:
|
||||||
|
continue
|
||||||
|
for time_trace in embed_time_trace(ninja_log_dir, target, pid,
|
||||||
|
tid, options):
|
||||||
|
yield time_trace
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
usage = __doc__
|
||||||
|
parser = optparse.OptionParser(usage)
|
||||||
|
parser.add_option('-a', '--showall', action='store_true', dest='showall',
|
||||||
|
default=False,
|
||||||
|
help='report on last build step for all outputs. Default '
|
||||||
|
'is to report just on the last (possibly incremental) '
|
||||||
|
'build')
|
||||||
|
parser.add_option('-g', '--granularity', type='int', default=50000,
|
||||||
|
dest='granularity',
|
||||||
|
help='minimum length time-trace event to embed in '
|
||||||
|
'microseconds. Default: %default')
|
||||||
|
parser.add_option('-e', '--embed-time-trace', action='store_true',
|
||||||
|
default=False, dest='embed_time_trace',
|
||||||
|
help='embed clang -ftime-trace json file found adjacent '
|
||||||
|
'to a target file')
|
||||||
|
(options, args) = parser.parse_args()
|
||||||
|
|
||||||
|
if len(args) == 0:
|
||||||
|
print('Must specify at least one .ninja_log file')
|
||||||
|
parser.print_help()
|
||||||
|
return 1
|
||||||
|
|
||||||
|
entries = []
|
||||||
|
for pid, log_file in enumerate(args):
|
||||||
|
with open(log_file, 'r') as log:
|
||||||
|
entries += list(log_to_dicts(log, pid, vars(options)))
|
||||||
|
json.dump(entries, sys.stdout)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main(sys.argv[1:]))
|
||||||
Loading…
Add table
Add a link
Reference in a new issue