mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2026-06-30 02:20:00 +03:00
Compare commits
137 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
702d4eb75c | ||
|
|
7cc148ed60 | ||
|
|
4b91ae019e | ||
|
|
323a50efa2 | ||
|
|
5923504aff | ||
|
|
60f598a0e7 | ||
|
|
810558a6f4 | ||
|
|
6d60fc1cbe | ||
|
|
a80c215113 | ||
|
|
1109ddce2a | ||
|
|
b2e195a651 | ||
|
|
0008542228 | ||
|
|
e6bfaf4717 | ||
|
|
185d5cb4bb | ||
|
|
bbc94272be | ||
|
|
05f3280405 | ||
|
|
0d32167303 | ||
|
|
295ed87c1d | ||
|
|
6aad22b75a | ||
|
|
9652a43fe2 | ||
|
|
f2f9cfb103 | ||
|
|
96634879bb | ||
|
|
8a432e9e5e | ||
|
|
2c00eb53a5 | ||
|
|
fd1d7f3c28 | ||
|
|
2c01416040 | ||
|
|
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 |
111 changed files with 1361 additions and 567 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.
|
||||
|
||||
CheckOptions:
|
||||
- { key: readability-identifier-naming.ClassCase, value: PascalCase }
|
||||
- { key: readability-identifier-naming.EnumCase, value: PascalCase }
|
||||
- { key: readability-identifier-naming.FunctionCase, value: camelCase }
|
||||
- { key: readability-identifier-naming.GlobalVariableCase, value: camelCase }
|
||||
- { key: readability-identifier-naming.GlobalFunctionCase, value: camelCase }
|
||||
- { key: readability-identifier-naming.GlobalConstantCase, value: SCREAMING_SNAKE_CASE }
|
||||
- { key: readability-identifier-naming.MacroDefinitionCase, value: SCREAMING_SNAKE_CASE }
|
||||
- { key: readability-identifier-naming.ClassMemberCase, value: camelCase }
|
||||
- { key: readability-identifier-naming.PrivateMemberPrefix, value: m_ }
|
||||
- { key: readability-identifier-naming.ProtectedMemberPrefix, value: m_ }
|
||||
- { key: readability-identifier-naming.PrivateStaticMemberPrefix, value: s_ }
|
||||
- { key: readability-identifier-naming.ClassCase, value: PascalCase }
|
||||
- { key: readability-identifier-naming.EnumCase, value: PascalCase }
|
||||
- { key: readability-identifier-naming.FunctionCase, value: camelCase }
|
||||
- { key: readability-identifier-naming.GlobalVariableCase, value: camelCase }
|
||||
- { key: readability-identifier-naming.GlobalFunctionCase, value: camelCase }
|
||||
- { key: readability-identifier-naming.GlobalConstantCase, value: SCREAMING_SNAKE_CASE }
|
||||
- { key: readability-identifier-naming.MacroDefinitionCase, value: SCREAMING_SNAKE_CASE }
|
||||
- { key: readability-identifier-naming.ClassMemberCase, value: camelCase }
|
||||
- { key: readability-identifier-naming.PrivateMemberPrefix, value: m_ }
|
||||
- { key: readability-identifier-naming.ProtectedMemberPrefix, value: m_ }
|
||||
- { key: readability-identifier-naming.PrivateStaticMemberPrefix, value: s_ }
|
||||
- { key: readability-identifier-naming.ProtectedStaticMemberPrefix, value: s_ }
|
||||
- { key: readability-identifier-naming.PublicStaticConstantCase, value: SCREAMING_SNAKE_CASE }
|
||||
- { key: readability-identifier-naming.EnumConstantCase, value: SCREAMING_SNAKE_CASE }
|
||||
- { key: readability-identifier-naming.PublicStaticConstantCase, 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
|
||||
shell: bash
|
||||
env:
|
||||
VERSION: ${{ inputs.version }}
|
||||
VERSION: ${{ github.ref_type == 'tag' && github.ref_name || inputs.version }}
|
||||
BUILD_DIR: build
|
||||
INSTALL_APPIMAGE_DIR: install-appdir
|
||||
|
||||
GPG_PRIVATE_KEY: ${{ inputs.gpg-private-key }}
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
|
||||
|
||||
cp ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.{metainfo,appdata}.xml
|
||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}
|
||||
|
||||
if [ '${{ inputs.gpg-private-key-id }}' != '' ]; then
|
||||
echo "$GPG_PRIVATE_KEY" > privkey.asc
|
||||
|
|
@ -70,14 +68,32 @@ runs:
|
|||
echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
appimagetool -s deploy "$INSTALL_APPIMAGE_DIR"/usr/share/applications/*.desktop
|
||||
cp ~/bin/AppImageUpdate.AppImage "$INSTALL_APPIMAGE_DIR"/usr/bin/
|
||||
# FIXME(@getchoo): Validate AppStream information when https://github.com/probonopd/go-appimage/pull/379 is merged
|
||||
sharun lib4bin \
|
||||
--hard-links \
|
||||
--with-hooks \
|
||||
--dst-dir "$INSTALL_APPIMAGE_DIR" \
|
||||
"$INSTALL_APPIMAGE_DIR"/bin/* "$QT_PLUGIN_PATH"/*/*.so
|
||||
|
||||
cp ~/bin/AppImageUpdate.AppImage "$INSTALL_APPIMAGE_DIR"/bin/
|
||||
# FIXME(@getchoo): gamemode doesn't seem to be very portable with DBus. Find a way to make it work!
|
||||
find "$INSTALL_APPIMAGE_DIR" -name '*gamemode*' -exec rm {} +
|
||||
|
||||
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 \
|
||||
--no-appstream \
|
||||
--updateinformation "gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-$APPIMAGE_ARCH.AppImage.zsync" \
|
||||
"$INSTALL_APPIMAGE_DIR" \
|
||||
"PrismLauncher-Linux-$VERSION-${{ inputs.build-type }}-$APPIMAGE_ARCH.AppImage"
|
||||
"$APPIMAGE_DEST"
|
||||
|
||||
- name: Package portable tarball
|
||||
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 }} --component portable
|
||||
|
||||
#the linked cmark .so is of the version that ubuntu uses, so without this it breaks on most updated distros
|
||||
mkdir ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
cp /lib/$APPIMAGE_ARCH-linux-gnu/libcmark.so.0.* ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
sharun lib4bin \
|
||||
--with-hooks \
|
||||
--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 }}
|
||||
tar -czf ../PrismLauncher-portable.tar.gz *
|
||||
|
||||
|
|
@ -107,10 +128,10 @@ runs:
|
|||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
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
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
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
|
||||
|
||||
- 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
|
||||
run: |
|
||||
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||
|
||||
- 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
|
||||
with:
|
||||
client-id: ${{ inputs.azure-client-id }}
|
||||
|
|
@ -68,7 +68,7 @@ runs:
|
|||
subscription-id: ${{ inputs.azure-subscription-id }}
|
||||
|
||||
- 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
|
||||
with:
|
||||
endpoint: https://eus.codesigning.azure.net/
|
||||
|
|
@ -140,7 +140,7 @@ runs:
|
|||
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
|
||||
|
||||
- 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
|
||||
with:
|
||||
endpoint: https://eus.codesigning.azure.net/
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ inputs:
|
|||
qt-version:
|
||||
description: Version of Qt to use
|
||||
required: true
|
||||
default: 6.9.3
|
||||
|
||||
outputs:
|
||||
build-type:
|
||||
|
|
@ -78,6 +77,5 @@ runs:
|
|||
with:
|
||||
aqtversion: "==3.1.*"
|
||||
version: ${{ inputs.qt-version }}
|
||||
arch: ${{ inputs.qt-architecture }}
|
||||
modules: qtimageformats qtnetworkauth
|
||||
cache: ${{ inputs.build-type == 'Debug' }}
|
||||
|
|
|
|||
|
|
@ -12,29 +12,7 @@ runs:
|
|||
dpkg-dev \
|
||||
ninja-build extra-cmake-modules pkg-config scdoc \
|
||||
cmark gamemode-dev libarchive-dev libcmark-dev libqrencode-dev zlib1g-dev \
|
||||
libxcb-cursor-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
|
||||
libxcb-cursor-dev libtomlplusplus-dev
|
||||
|
||||
- name: Setup AppImage tooling
|
||||
shell: bash
|
||||
|
|
@ -56,19 +34,20 @@ runs:
|
|||
;;
|
||||
esac
|
||||
|
||||
gh release download \
|
||||
--repo VHSgunzo/sharun \
|
||||
--pattern "sharun-$APPIMAGE_ARCH-aio" \
|
||||
--output ~/bin/sharun
|
||||
|
||||
# FIXME!: revert this to probonopd/go-appimage once https://github.com/probonopd/go-appimage/pull/377 is merged!
|
||||
gh release download continuous \
|
||||
--repo probonopd/go-appimage \
|
||||
--pattern "appimagetool-*-$APPIMAGE_ARCH.AppImage" \
|
||||
--output ~/bin/appimagetool
|
||||
gh release download continuous \
|
||||
--repo probonopd/go-appimage \
|
||||
--repo DioEgizio/go-appimage \
|
||||
--pattern "mkappimage-*-$APPIMAGE_ARCH.AppImage" \
|
||||
--output ~/bin/mkappimage
|
||||
chmod +x ~/bin/appimagetool ~/bin/mkappimage
|
||||
echo "$HOME/bin" >> "$GITHUB_PATH"
|
||||
|
||||
gh release download \
|
||||
--repo AppImageCommunity/AppImageUpdate \
|
||||
--pattern "AppImageUpdate-$APPIMAGE_ARCH.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
|
||||
run: |
|
||||
brew update
|
||||
brew install ninja extra-cmake-modules temurin@17
|
||||
brew install ninja extra-cmake-modules temurin@17 mono
|
||||
|
||||
- name: Set JAVA_HOME
|
||||
shell: bash
|
||||
|
|
@ -44,4 +44,4 @@ runs:
|
|||
- name: Setup vcpkg environment
|
||||
shell: bash
|
||||
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 == '' }}
|
||||
shell: bash
|
||||
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)
|
||||
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)
|
||||
type: string
|
||||
default: Debug
|
||||
environment:
|
||||
description: Deployment environment to run under
|
||||
type: string
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
build-type:
|
||||
|
|
@ -73,6 +76,8 @@ jobs:
|
|||
build:
|
||||
name: Build (${{ matrix.artifact-name }})
|
||||
|
||||
environment: ${{ inputs.environment || '' }}
|
||||
|
||||
permissions:
|
||||
# Required for Azure Trusted Signing
|
||||
id-token: write
|
||||
|
|
@ -83,17 +88,15 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
- os: ubuntu-24.04
|
||||
artifact-name: 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
|
||||
artifact-name: Linux-aarch64
|
||||
cmake-preset: linux
|
||||
qt-version: 6.10.1
|
||||
|
||||
- os: windows-2022
|
||||
artifact-name: Windows-MinGW-w64
|
||||
|
|
@ -112,16 +115,19 @@ jobs:
|
|||
cmake-preset: windows_msvc
|
||||
# TODO(@getchoo): This is the default in setup-dependencies/windows. Why isn't it working?!?!
|
||||
vcvars-arch: amd64
|
||||
qt-version: 6.10.1
|
||||
|
||||
- os: windows-11-arm
|
||||
artifact-name: Windows-MSVC-arm64
|
||||
cmake-preset: windows_msvc
|
||||
vcvars-arch: arm64
|
||||
qt-version: 6.10.1
|
||||
|
||||
- os: macos-14
|
||||
- os: macos-26
|
||||
artifact-name: macOS
|
||||
cmake-preset: macos_universal
|
||||
macosx-deployment-target: 12.0
|
||||
qt-version: 6.9.3
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
|
|
@ -155,7 +161,7 @@ jobs:
|
|||
artifact-name: ${{ matrix.artifact-name }}
|
||||
msystem: ${{ matrix.msystem }}
|
||||
vcvars-arch: ${{ matrix.vcvars-arch }}
|
||||
qt-architecture: ${{ matrix.qt-architecture }}
|
||||
qt-version: ${{ matrix.qt-version }}
|
||||
|
||||
##
|
||||
# BUILD
|
||||
|
|
@ -214,6 +220,8 @@ jobs:
|
|||
- name: Package (Windows)
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
uses: ./.github/actions/package/windows
|
||||
env:
|
||||
CI_HAS_ACCESS_TO_AZURE: ${{ vars.CI_HAS_ACCESS_TO_AZURE || '' }}
|
||||
with:
|
||||
version: ${{ steps.short-version.outputs.version }}
|
||||
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
|
||||
with:
|
||||
build-type: Debug
|
||||
qt-version: 6.10.1
|
||||
|
||||
- name: Configure and Build
|
||||
run: |
|
||||
|
|
|
|||
3
.github/workflows/flatpak.yml
vendored
3
.github/workflows/flatpak.yml
vendored
|
|
@ -77,9 +77,6 @@ jobs:
|
|||
- os: ubuntu-22.04
|
||||
arch: x86_64
|
||||
|
||||
- os: ubuntu-22.04-arm
|
||||
arch: aarch64
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
container:
|
||||
|
|
|
|||
3
.github/workflows/nix.yml
vendored
3
.github/workflows/nix.yml
vendored
|
|
@ -86,9 +86,6 @@ jobs:
|
|||
- os: ubuntu-22.04-arm
|
||||
system: aarch64-linux
|
||||
|
||||
- os: macos-15-intel
|
||||
system: x86_64-darwin
|
||||
|
||||
- os: macos-14
|
||||
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
|
||||
with:
|
||||
build-type: Release
|
||||
environment: Release
|
||||
secrets: inherit
|
||||
|
||||
create_release:
|
||||
|
|
@ -34,6 +35,7 @@ jobs:
|
|||
run: |
|
||||
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
|
||||
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-Linux-aarch64-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-aarch64-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-*.AppImage/PrismLauncher-*-x86_64.AppImage PrismLauncher-Linux-x86_64.AppImage
|
||||
mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*-x86_64.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||
mv PrismLauncher-*.AppImage/PrismLauncher-*-aarch64.AppImage PrismLauncher-Linux-aarch64.AppImage
|
||||
|
|
@ -88,6 +90,7 @@ jobs:
|
|||
name: Prism Launcher ${{ env.VERSION }}
|
||||
draft: true
|
||||
prerelease: false
|
||||
fail_on_unmatched_files: true
|
||||
files: |
|
||||
PrismLauncher-Linux-x86_64.AppImage
|
||||
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
|
||||
|
||||
project(Launcher)
|
||||
project(Launcher LANGUAGES C CXX)
|
||||
if(APPLE)
|
||||
enable_language(OBJC OBJCXX)
|
||||
endif()
|
||||
|
||||
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
|
||||
if(IS_IN_SOURCE_BUILD)
|
||||
|
|
@ -79,19 +82,6 @@ else()
|
|||
if(WIN32)
|
||||
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
|
||||
# -mguard=cf enables Control Flow Guard
|
||||
# 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)
|
||||
|
||||
# If this is a Debug build turn on address sanitiser
|
||||
if ((CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") AND DEBUG_ADDRESS_SANITIZER)
|
||||
if (DEBUG_ADDRESS_SANITIZER)
|
||||
message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off")
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||
# using clang with clang-cl front end
|
||||
message(STATUS "Address Sanitizer available on Clang MSVC frontend")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
|
||||
else()
|
||||
# AppleClang and Clang
|
||||
message(STATUS "Address Sanitizer available on Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=null")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=null")
|
||||
endif()
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
# GCC
|
||||
message(STATUS "Address Sanitizer available on GCC")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover")
|
||||
link_libraries("asan")
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
message(STATUS "Address Sanitizer available on MSVC")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
|
||||
|
||||
set(USE_ASAN_COMPILE_OPTIONS $<AND:$<CONFIG:Debug,RelWithDebInfo>,$<COMPILE_LANGUAGE:C,CXX>>)
|
||||
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||
message(STATUS "Using Address Sanitizer compile options for MSVC frontend")
|
||||
add_compile_options(
|
||||
$<${USE_ASAN_COMPILE_OPTIONS}:/fsanitize=address>
|
||||
$<${USE_ASAN_COMPILE_OPTIONS}:/Oy->
|
||||
)
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
message(STATUS "Using Address Sanitizer compile options for GCC/Clang")
|
||||
add_compile_options(
|
||||
$<${USE_ASAN_COMPILE_OPTIONS}:-fsanitize=address,undefined>
|
||||
$<${USE_ASAN_COMPILE_OPTIONS}:-fno-omit-frame-pointer>
|
||||
$<${USE_ASAN_COMPILE_OPTIONS}:-fno-sanitize-recover=null>
|
||||
)
|
||||
link_libraries("asan" "ubsan")
|
||||
else()
|
||||
message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}")
|
||||
endif()
|
||||
|
|
@ -192,7 +176,7 @@ set(Launcher_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" CACHE S
|
|||
######## Set version numbers ########
|
||||
set(Launcher_VERSION_MAJOR 10)
|
||||
set(Launcher_VERSION_MINOR 0)
|
||||
set(Launcher_VERSION_PATCH 0)
|
||||
set(Launcher_VERSION_PATCH 2)
|
||||
|
||||
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")
|
||||
|
|
@ -337,7 +321,7 @@ if(NOT LibArchive_FOUND)
|
|||
pkg_check_modules(libarchive REQUIRED IMPORTED_TARGET libarchive)
|
||||
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
|
||||
if(NOT tomlplusplus_FOUND)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
|
@ -379,7 +363,7 @@ if(UNIX AND APPLE)
|
|||
# Mac bundle settings
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_DisplayName}")
|
||||
set(MACOSX_BUNDLE_INFO_STRING "${Launcher_DisplayName}: A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.")
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.prismlauncher.${Launcher_Name}")
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "${Launcher_AppID}")
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_NAME}")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}")
|
||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}")
|
||||
|
|
@ -399,6 +383,52 @@ if(UNIX AND APPLE)
|
|||
# Add the icon
|
||||
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
|
||||
|
||||
find_program(ACTOOL_EXE actool DOC "Path to the apple asset catalog compiler")
|
||||
if(ACTOOL_EXE)
|
||||
execute_process(
|
||||
COMMAND xcodebuild -version
|
||||
OUTPUT_VARIABLE XCODE_VERSION_OUTPUT
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
string(REGEX MATCH "Xcode ([0-9]+\.[0-9]+)" XCODE_VERSION_MATCH "${XCODE_VERSION_OUTPUT}")
|
||||
if(XCODE_VERSION_MATCH)
|
||||
set(XCODE_VERSION ${CMAKE_MATCH_1})
|
||||
else()
|
||||
set(XCODE_VERSION 0.0)
|
||||
endif()
|
||||
|
||||
if(XCODE_VERSION VERSION_GREATER_EQUAL 26.0)
|
||||
set(ASSETS_OUT_DIR "${CMAKE_BINARY_DIR}/program_info")
|
||||
set(GENERATED_ASSETS_CAR "${ASSETS_OUT_DIR}/Assets.car")
|
||||
set(ICON_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_Branding_MAC_ICON}")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${GENERATED_ASSETS_CAR}"
|
||||
COMMAND ${ACTOOL_EXE} "${ICON_SOURCE}"
|
||||
--compile "${ASSETS_OUT_DIR}"
|
||||
--output-partial-info-plist /dev/null
|
||||
--app-icon 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)
|
||||
include(KDEInstallDirs)
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@
|
|||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Darwin"
|
||||
},
|
||||
"cacheVariables": {
|
||||
"CMAKE_TOOLCHAIN_FILE": "$penv{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -50,6 +53,7 @@
|
|||
"macos"
|
||||
],
|
||||
"cacheVariables": {
|
||||
"CMAKE_TOOLCHAIN_FILE": "$penv{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
|
||||
"CMAKE_OSX_ARCHITECTURES": "x86_64;arm64",
|
||||
"VCPKG_TARGET_TRIPLET": "universal-osx"
|
||||
}
|
||||
|
|
@ -76,6 +80,9 @@
|
|||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"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`.
|
||||
- Class function members should be formatted as `camelCase` without a prefix: `incrementCounter`.
|
||||
- Global functions and non-`const` global variables should be formatted as `camelCase` without a prefix: `globalData`.
|
||||
- `const` global variables, macros, and enum constants should be formatted as `SCREAMING_SNAKE_CASE`: `LIGHT_GRAY`.
|
||||
- `const` global variables and macros should be formatted as `SCREAMING_SNAKE_CASE`: `LIGHT_GRAY`.
|
||||
- enum constants should be formatted as `PascalCase`: `CamelusBactrianus`
|
||||
- Avoid inventing acronyms or abbreviations especially for a name of multiple words - like `tp` for `texturePack`.
|
||||
- Avoid using `[[nodiscard]]` unless ignoring the return value is likely to cause a bug in cases such as:
|
||||
- A function allocates memory or another resource and the caller needs to clean it up.
|
||||
|
|
@ -30,7 +31,7 @@ Here is what these conventions with the formatting configuration look like:
|
|||
|
||||
constexpr double PI = 3.14159;
|
||||
|
||||
enum class PizzaToppings { HAM_AND_PINEAPPLE, OREO_AND_KETCHUP };
|
||||
enum class PizzaToppings { HamAndPineapple, OreoAndKetchup };
|
||||
|
||||
struct Person {
|
||||
QString name;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS*
|
|||
|
||||
For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**, respectively, you can use these packages for the latest development versions:
|
||||
|
||||
[](https://aur.archlinux.org/packages/prismlauncher-git) [](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [](https://mpr.makedeb.org/packages/prismlauncher-git)<br />[](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [](https://build.opensuse.org/project/show/home:getchoo) [](https://packages.gentoo.org/packages/games-action/prismlauncher)
|
||||
[](https://aur.archlinux.org/packages/prismlauncher-git) [](https://mpr.makedeb.org/packages/prismlauncher-git)<br />[](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [](https://build.opensuse.org/project/show/home:getchoo) [](https://packages.gentoo.org/packages/games-action/prismlauncher)
|
||||
|
||||
These packages are also available to all the distributions based on the ones mentioned above.
|
||||
|
||||
|
|
@ -61,12 +61,7 @@ The translation effort for Prism Launcher is hosted on [Weblate](https://hosted.
|
|||
|
||||
## Building
|
||||
|
||||
If you want to build Prism Launcher yourself, check the build instructions:
|
||||
|
||||
- [Windows](https://prismlauncher.org/wiki/development/build-instructions/windows/)
|
||||
- [Linux](https://prismlauncher.org/wiki/development/build-instructions/linux/)
|
||||
- [MacOS](https://prismlauncher.org/wiki/development/build-instructions/macos/)
|
||||
- [OpenBSD](https://prismlauncher.org/wiki/development/build-instructions/openbsd/)
|
||||
If you want to build Prism Launcher yourself, check the [build instructions](https://prismlauncher.org/wiki/development/build-instructions).
|
||||
|
||||
## Sponsors & Partners
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <qstringliteral.h>
|
||||
#include <QObject>
|
||||
#include "BuildConfig.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@
|
|||
<key>CFBundleGetInfoString</key>
|
||||
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
|
||||
<string>${Launcher_Name}</string>
|
||||
<key>CFBundleIconName</key>
|
||||
<string>${Launcher_Name}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
|
|
@ -42,6 +44,8 @@
|
|||
<true/>
|
||||
<key>LSRequiresCarbon</key>
|
||||
<true/>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.games</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||
<key>SUPublicEDKey</key>
|
||||
|
|
|
|||
17
flake.lock
generated
17
flake.lock
generated
|
|
@ -18,18 +18,15 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1765472234,
|
||||
"narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b",
|
||||
"type": "github"
|
||||
"lastModified": 1766473571,
|
||||
"narHash": "sha256-QvjEJNgMVuOootbR+DEfbiW+zSK57U32CE0jmVdcNjQ=",
|
||||
"rev": "76701a179d3a98b07653e2b0409847499b2a07d3",
|
||||
"type": "tarball",
|
||||
"url": "https://releases.nixos.org/nixos/25.11/nixos-25.11.2403.76701a179d3a/nixexprs.tar.xz"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
"type": "tarball",
|
||||
"url": "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
};
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
nixpkgs.url = "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz";
|
||||
|
||||
libnbtplusplus = {
|
||||
url = "github:PrismLauncher/libnbtplusplus";
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@
|
|||
#include "tools/GenericProfiler.h"
|
||||
#include "ui/InstanceWindow.h"
|
||||
#include "ui/MainWindow.h"
|
||||
#include "ui/ToolTipFilter.h"
|
||||
#include "ui/ViewLogWindow.h"
|
||||
|
||||
#include "ui/dialogs/ProgressDialog.h"
|
||||
|
|
@ -371,25 +372,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
}
|
||||
|
||||
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();
|
||||
#endif
|
||||
|
||||
{
|
||||
// Root path is used for updates and portable data
|
||||
|
|
@ -1217,6 +1200,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
}
|
||||
}
|
||||
|
||||
if (qgetenv("XDG_CURRENT_DESKTOP") == "gamescope") {
|
||||
installEventFilter(new ToolTipFilter);
|
||||
}
|
||||
|
||||
if (createSetupWizard()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,13 +26,13 @@ set(CORE_SOURCES
|
|||
NullInstance.h
|
||||
MMCZip.h
|
||||
MMCZip.cpp
|
||||
archive/ArchiveReader.cpp
|
||||
archive/ArchiveReader.h
|
||||
archive/ArchiveWriter.cpp
|
||||
archive/ArchiveWriter.h
|
||||
archive/ExportToZipTask.cpp
|
||||
archive/ArchiveReader.cpp
|
||||
archive/ArchiveReader.h
|
||||
archive/ArchiveWriter.cpp
|
||||
archive/ArchiveWriter.h
|
||||
archive/ExportToZipTask.cpp
|
||||
archive/ExportToZipTask.h
|
||||
archive/ExtractZipTask.cpp
|
||||
archive/ExtractZipTask.cpp
|
||||
archive/ExtractZipTask.h
|
||||
StringUtils.h
|
||||
StringUtils.cpp
|
||||
|
|
@ -346,6 +346,7 @@ set(MINECRAFT_SOURCES
|
|||
minecraft/mod/TexturePackFolderModel.h
|
||||
minecraft/mod/TexturePackFolderModel.cpp
|
||||
minecraft/mod/ShaderPackFolderModel.h
|
||||
minecraft/mod/ShaderPackFolderModel.cpp
|
||||
minecraft/mod/tasks/ResourceFolderLoadTask.h
|
||||
minecraft/mod/tasks/ResourceFolderLoadTask.cpp
|
||||
minecraft/mod/tasks/LocalModParseTask.h
|
||||
|
|
@ -621,10 +622,10 @@ set(PRISMUPDATER_SOURCES
|
|||
# Zip
|
||||
MMCZip.h
|
||||
MMCZip.cpp
|
||||
archive/ArchiveReader.cpp
|
||||
archive/ArchiveReader.h
|
||||
archive/ArchiveWriter.cpp
|
||||
archive/ArchiveWriter.h
|
||||
archive/ArchiveReader.cpp
|
||||
archive/ArchiveReader.h
|
||||
archive/ArchiveWriter.cpp
|
||||
archive/ArchiveWriter.h
|
||||
|
||||
# Time
|
||||
MMCTime.h
|
||||
|
|
@ -842,6 +843,8 @@ SET(LAUNCHER_SOURCES
|
|||
ui/InstanceWindow.cpp
|
||||
ui/ViewLogWindow.h
|
||||
ui/ViewLogWindow.cpp
|
||||
ui/ToolTipFilter.h
|
||||
ui/ToolTipFilter.cpp
|
||||
|
||||
# FIXME: maybe find a better home for this.
|
||||
FileIgnoreProxy.cpp
|
||||
|
|
@ -1300,6 +1303,16 @@ endif()
|
|||
|
||||
include(CompilerWarnings)
|
||||
|
||||
######## Precompiled Headers ###########
|
||||
|
||||
set(PRECOMPILED_HEADERS
|
||||
include/base.pch.hpp
|
||||
include/qtcore.pch.hpp
|
||||
include/qtgui.pch.hpp
|
||||
)
|
||||
|
||||
####### Targets ########
|
||||
|
||||
# Add executable
|
||||
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES})
|
||||
set_project_warnings(Launcher_logic
|
||||
|
|
@ -1308,6 +1321,7 @@ set_project_warnings(Launcher_logic
|
|||
"${Launcher_GCC_WARNINGS}")
|
||||
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
|
||||
target_precompile_headers(Launcher_logic PRIVATE ${PRECOMPILED_HEADERS})
|
||||
target_link_libraries(Launcher_logic
|
||||
systeminfo
|
||||
Launcher_murmur2
|
||||
|
|
@ -1389,6 +1403,7 @@ endif()
|
|||
target_link_libraries(Launcher_logic)
|
||||
|
||||
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)
|
||||
|
||||
if(DEFINED Launcher_APP_BINARY_NAME)
|
||||
|
|
@ -1412,14 +1427,15 @@ install(TARGETS ${Launcher_Name}
|
|||
)
|
||||
|
||||
# Deploy PDBs
|
||||
if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
|
||||
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}> DESTINATION ${BINARY_DEST_DIR})
|
||||
if(CMAKE_CXX_LINKER_SUPPORTS_PDB)
|
||||
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}> DESTINATION ${BINARY_DEST_DIR} OPTIONAL)
|
||||
endif()
|
||||
|
||||
if(Launcher_BUILD_UPDATER)
|
||||
# Updater
|
||||
add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI})
|
||||
target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_precompile_headers(prism_updater_logic PRIVATE ${PRECOMPILED_HEADERS})
|
||||
target_link_libraries(prism_updater_logic
|
||||
${ZLIB_LIBRARIES}
|
||||
systeminfo
|
||||
|
|
@ -1439,6 +1455,7 @@ if(Launcher_BUILD_UPDATER)
|
|||
add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp)
|
||||
target_sources("${Launcher_Name}_updater" PRIVATE updater/prismupdater/updater.exe.manifest)
|
||||
target_link_libraries("${Launcher_Name}_updater" prism_updater_logic)
|
||||
target_precompile_headers("${Launcher_Name}_updater" REUSE_FROM prism_updater_logic)
|
||||
|
||||
if(DEFINED Launcher_APP_BINARY_NAME)
|
||||
set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater")
|
||||
|
|
@ -1455,8 +1472,8 @@ if(Launcher_BUILD_UPDATER)
|
|||
)
|
||||
|
||||
# Deploy PDBs
|
||||
if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
|
||||
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_updater> DESTINATION ${BINARY_DEST_DIR})
|
||||
if(CMAKE_CXX_LINKER_SUPPORTS_PDB)
|
||||
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_updater> DESTINATION ${BINARY_DEST_DIR} OPTIONAL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
@ -1469,6 +1486,8 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
|
|||
"${Launcher_GCC_WARNINGS}")
|
||||
|
||||
target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_precompile_headers(filelink_logic PRIVATE ${PRECOMPILED_HEADERS})
|
||||
|
||||
target_link_libraries(filelink_logic
|
||||
systeminfo
|
||||
BuildConfig
|
||||
|
|
@ -1482,6 +1501,8 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
|
|||
add_executable("${Launcher_Name}_filelink" WIN32 filelink/filelink_main.cpp)
|
||||
|
||||
target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest)
|
||||
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
|
||||
# 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
|
||||
|
|
@ -1506,8 +1527,8 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
|
|||
)
|
||||
|
||||
# Deploy PDBs
|
||||
if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
|
||||
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_filelink> DESTINATION ${BINARY_DEST_DIR})
|
||||
if(CMAKE_CXX_LINKER_SUPPORTS_PDB)
|
||||
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_filelink> DESTINATION ${BINARY_DEST_DIR} OPTIONAL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -76,15 +75,6 @@ bool isFlatpak()
|
|||
#endif
|
||||
}
|
||||
|
||||
bool isSelfContained()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
return QFileInfo(QCoreApplication::applicationFilePath()).fileName().startsWith("ld-linux");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isSnap()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
|
|
|
|||
|
|
@ -37,11 +37,6 @@ bool openUrl(const QUrl& url);
|
|||
*/
|
||||
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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -266,7 +266,21 @@ bool FileIgnoreProxy::filterAcceptsRow(int sourceRow, const QModelIndex& sourceP
|
|||
|
||||
bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const
|
||||
{
|
||||
return m_ignoreFiles.contains(fileInfo.fileName()) || m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath()));
|
||||
if (m_ignoreFiles.contains(fileInfo.fileName())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto& suffix : m_ignoreFilesSuffixes) {
|
||||
if (fileInfo.fileName().endsWith(suffix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath()))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileIgnoreProxy::filterFile(const QFileInfo& file) const
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ class FileIgnoreProxy : public QSortFilterProxyModel {
|
|||
|
||||
// list of file names that need to be removed completely from model
|
||||
inline QStringList& ignoreFilesWithName() { return m_ignoreFiles; }
|
||||
inline QStringList& ignoreFilesWithSuffix() { return m_ignoreFilesSuffixes; }
|
||||
// list of relative paths that need to be removed completely from model
|
||||
inline SeparatorPrefixTree<'/'>& ignoreFilesWithPath() { return m_ignoreFilePaths; }
|
||||
|
||||
|
|
@ -85,5 +86,6 @@ class FileIgnoreProxy : public QSortFilterProxyModel {
|
|||
const QString m_root;
|
||||
SeparatorPrefixTree<'/'> m_blocked;
|
||||
QStringList m_ignoreFiles;
|
||||
QStringList m_ignoreFilesSuffixes;
|
||||
SeparatorPrefixTree<'/'> m_ignoreFilePaths;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -773,34 +772,6 @@ QString ResolveExecutable(QString path)
|
|||
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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -43,13 +42,11 @@
|
|||
|
||||
#include <system_error>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFlags>
|
||||
#include <QLocalServer>
|
||||
#include <QObject>
|
||||
#include <QPair>
|
||||
#include <QProcess>
|
||||
#include <QThread>
|
||||
|
||||
namespace FS {
|
||||
|
|
@ -336,14 +333,6 @@ QString pathTruncate(const QString& path, int depth);
|
|||
*/
|
||||
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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -150,22 +150,17 @@ void InstanceImportTask::processZipPack()
|
|||
extractDir.cd("minecraft");
|
||||
m_modpackType = ModpackType::Technic;
|
||||
stop = true;
|
||||
} else {
|
||||
QFileInfo fileInfo(fileName);
|
||||
if (fileInfo.fileName() == "instance.cfg") {
|
||||
qDebug() << "MultiMC:" << true;
|
||||
m_modpackType = ModpackType::MultiMC;
|
||||
root = cleanPath(fileInfo.path());
|
||||
stop = true;
|
||||
return true;
|
||||
}
|
||||
if (fileInfo.fileName() == "manifest.json") {
|
||||
qDebug() << "Flame:" << true;
|
||||
m_modpackType = ModpackType::Flame;
|
||||
root = cleanPath(fileInfo.path());
|
||||
stop = true;
|
||||
return true;
|
||||
}
|
||||
} else if (fileName == "manifest.json") {
|
||||
qDebug() << "Flame:" << true;
|
||||
m_modpackType = ModpackType::Flame;
|
||||
stop = true;
|
||||
return true;
|
||||
} else if (QFileInfo fileInfo(fileName); fileInfo.fileName() == "instance.cfg") {
|
||||
qDebug() << "MultiMC:" << true;
|
||||
m_modpackType = ModpackType::MultiMC;
|
||||
root = cleanPath(fileInfo.path());
|
||||
stop = true;
|
||||
return true;
|
||||
}
|
||||
QCoreApplication::processEvents();
|
||||
return true;
|
||||
|
|
@ -271,7 +266,7 @@ bool installIcon(QString root, QString instIconKey)
|
|||
if (iconList->iconFileExists(instIconKey)) {
|
||||
iconList->deleteIcon(instIconKey);
|
||||
}
|
||||
iconList->installIcon(importIconPath, instIconKey + ".png");
|
||||
iconList->installIcon(importIconPath, instIconKey + "." + QFileInfo(importIconPath).suffix());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -101,6 +101,21 @@ QJsonArray requireArray(const QJsonDocument& doc, const QString& what)
|
|||
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)
|
||||
{
|
||||
if (!value.isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -107,6 +107,9 @@ QJsonArray toJsonArray(const QList<T>& container)
|
|||
|
||||
////////////////// READING ////////////////////
|
||||
|
||||
// Attempt to parse JSON up until garbage is encountered
|
||||
QJsonDocument parseUntilGarbage(const QByteArray& json, QJsonParseError* error = nullptr, QString* garbage = nullptr);
|
||||
|
||||
/// @throw JsonException
|
||||
template <typename T>
|
||||
T requireIsType(const QJsonValue& value, const QString& what = "Value");
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ void LaunchController::login()
|
|||
}
|
||||
/* fallthrough */
|
||||
case AccountState::Online: {
|
||||
if (!m_session->wants_online) {
|
||||
if (!m_session->wants_online && m_accountToUse->accountType() != AccountType::Offline) {
|
||||
// we ask the user for a player name
|
||||
bool ok = false;
|
||||
QString name;
|
||||
|
|
|
|||
|
|
@ -18,89 +18,19 @@ LAUNCHER_NAME=@Launcher_APP_BINARY_NAME@
|
|||
LAUNCHER_DIR="$(dirname "$(readlink -f "$0")")"
|
||||
echo "Launcher Dir: ${LAUNCHER_DIR}"
|
||||
|
||||
# Set up env.
|
||||
# Pass our custom variables separately so that the launcher can remove them for child processes
|
||||
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"
|
||||
# Makes the launcher use portals for file picking
|
||||
export QT_QPA_PLATFORMTHEME=xdgdesktopportal
|
||||
|
||||
export LD_LIBRARY_PATH="$LAUNCHER_LD_LIBRARY_PATH:$LD_LIBRARY_PATH"
|
||||
export LD_PRELOAD="$LAUNCHER_LD_PRELOAD:$LD_PRELOAD"
|
||||
export QT_PLUGIN_PATH="$LAUNCHER_QT_PLUGIN_PATH:$QT_PLUGIN_PATH"
|
||||
export QT_FONTPATH="$LAUNCHER_QT_FONTPATH:$QT_FONTPATH"
|
||||
# Just to be sure...
|
||||
chmod +x "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}"
|
||||
|
||||
# Detect missing dependencies...
|
||||
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."
|
||||
ARGS=("${LAUNCHER_DIR}/${LAUNCHER_NAME}" "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}")
|
||||
|
||||
# Just to be sure...
|
||||
chmod +x "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}"
|
||||
|
||||
ARGS=("${LAUNCHER_DIR}/${LAUNCHER_NAME}" "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}")
|
||||
|
||||
if [ -f portable.txt ]; then
|
||||
ARGS+=("-d" "${LAUNCHER_DIR}")
|
||||
fi
|
||||
|
||||
ARGS+=("$@")
|
||||
|
||||
# Run the launcher
|
||||
exec -a "${ARGS[@]}"
|
||||
|
||||
# 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
|
||||
if [ -f portable.txt ]; then
|
||||
ARGS+=("-d" "${LAUNCHER_DIR}")
|
||||
fi
|
||||
|
||||
ARGS+=("$@")
|
||||
|
||||
# Run the launcher
|
||||
exec -a "${ARGS[@]}"
|
||||
|
|
@ -16,7 +16,6 @@
|
|||
*/
|
||||
|
||||
#include <MMCTime.h>
|
||||
#include <qobject.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <qlogging.h>
|
||||
#include <QString>
|
||||
#include <compare>
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@
|
|||
*/
|
||||
|
||||
#include "StringUtils.h"
|
||||
#include <qpair.h>
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QUuid>
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
#include <QFileSystemWatcher>
|
||||
#include <QMap>
|
||||
#include <QMimeData>
|
||||
#include <QPixmap>
|
||||
#include <QSet>
|
||||
#include <QUrl>
|
||||
#include "icons/IconUtils.h"
|
||||
|
|
@ -217,7 +218,13 @@ void IconList::fileChanged(const QString& path)
|
|||
int idx = getIconIndex(key);
|
||||
if (idx == -1)
|
||||
return;
|
||||
QIcon icon(path);
|
||||
QIcon icon;
|
||||
// special handling for jpg and jpeg to go through pixmap to keep the size constant
|
||||
if (path.endsWith(".jpg") || path.endsWith(".jpeg")) {
|
||||
icon.addPixmap(QPixmap(path));
|
||||
} else {
|
||||
icon.addFile(path);
|
||||
}
|
||||
if (icon.availableSizes().empty())
|
||||
return;
|
||||
|
||||
|
|
@ -395,7 +402,14 @@ bool IconList::addThemeIcon(const QString& key)
|
|||
bool IconList::addIcon(const QString& key, const QString& name, const QString& path, const IconType type)
|
||||
{
|
||||
// replace the icon even? is the input valid?
|
||||
QIcon icon(path);
|
||||
QIcon icon;
|
||||
// special handling for jpg and jpeg to go through pixmap to keep the size constant
|
||||
if (path.endsWith(".jpg") || path.endsWith(".jpeg")) {
|
||||
icon.addPixmap(QPixmap(path));
|
||||
} else {
|
||||
icon.addFile(path);
|
||||
}
|
||||
|
||||
if (icon.isNull())
|
||||
return false;
|
||||
auto iter = m_nameIndex.find(key);
|
||||
|
|
|
|||
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
|
||||
|
|
@ -42,7 +42,8 @@ class SecurityBookmarkFileAccess {
|
|||
bool m_readOnly;
|
||||
|
||||
NSURL* securityScopedBookmarkToNSURL(QByteArray& bookmark, bool& isStale);
|
||||
public:
|
||||
|
||||
public:
|
||||
/// \param readOnly A boolean indicating whether the bookmark should be read-only.
|
||||
SecurityBookmarkFileAccess(bool readOnly = false);
|
||||
~SecurityBookmarkFileAccess();
|
||||
|
|
@ -86,4 +87,4 @@ public:
|
|||
bool isAccessingPath(const QString& path);
|
||||
};
|
||||
|
||||
#endif //FILEACCESS_H
|
||||
#endif // FILEACCESS_H
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@
|
|||
*/
|
||||
|
||||
#include "minecraft/Logging.h"
|
||||
#include <qloggingcategory.h>
|
||||
|
||||
Q_LOGGING_CATEGORY(instanceProfileC, "launcher.instance.profile")
|
||||
Q_LOGGING_CATEGORY(instanceProfileResolveC, "launcher.instance.profile.resolve")
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@
|
|||
#include <QActionGroup>
|
||||
#include <QMainWindow>
|
||||
#include <QScreen>
|
||||
#include <QStandardPaths>
|
||||
#include <QWindow>
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
|
|
@ -483,7 +484,7 @@ QStringList MinecraftInstance::getNativeJars()
|
|||
return nativeJars;
|
||||
}
|
||||
|
||||
static QString replaceTokensIn(const QString &text, const QMap<QString, QString> &with)
|
||||
static QString replaceTokensIn(const QString& text, const QMap<QString, QString>& with)
|
||||
{
|
||||
// TODO: does this still work??
|
||||
QString result;
|
||||
|
|
@ -505,7 +506,6 @@ static QString replaceTokensIn(const QString &text, const QMap<QString, QString>
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
QStringList MinecraftInstance::extraArguments()
|
||||
{
|
||||
auto list = BaseInstance::extraArguments();
|
||||
|
|
@ -520,7 +520,7 @@ QStringList MinecraftInstance::extraArguments()
|
|||
if (!addn.isEmpty()) {
|
||||
QMap<QString, QString> tokenMapping = makeProfileVarMapping(m_components->getProfile());
|
||||
|
||||
for (const QString &item : addn) {
|
||||
for (const QString& item : addn) {
|
||||
list.append(replaceTokensIn(item, tokenMapping));
|
||||
}
|
||||
}
|
||||
|
|
@ -588,6 +588,16 @@ QStringList MinecraftInstance::javaArguments()
|
|||
"minecraft.exe.heapdump");
|
||||
#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 max = settings()->get("MaxMemAlloc").toInt();
|
||||
if (min < max) {
|
||||
|
|
@ -753,7 +763,6 @@ QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, Mine
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
QMap<QString, QString> tokenMapping = makeProfileVarMapping(profile);
|
||||
|
||||
// yggdrasil!
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@
|
|||
*/
|
||||
|
||||
#include <Version.h>
|
||||
#include <qlogging.h>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@
|
|||
#include "WorldList.h"
|
||||
|
||||
#include <FileSystem.h>
|
||||
#include <qmimedata.h>
|
||||
#include <QDebug>
|
||||
#include <QDirIterator>
|
||||
#include <QFileSystemWatcher>
|
||||
|
|
|
|||
|
|
@ -669,7 +669,7 @@ void AccountList::beginActivity()
|
|||
void AccountList::endActivity()
|
||||
{
|
||||
if (m_activityCount == 0) {
|
||||
qWarning() << m_name << " - Activity count would become below zero";
|
||||
qWarning() << "Activity count would become below zero";
|
||||
return;
|
||||
}
|
||||
bool deactivating = m_activityCount == 1;
|
||||
|
|
|
|||
|
|
@ -111,7 +111,6 @@ class AccountList : public QAbstractListModel {
|
|||
void endActivity();
|
||||
|
||||
private:
|
||||
const char* m_name;
|
||||
uint32_t m_activityCount = 0;
|
||||
signals:
|
||||
void listChanged();
|
||||
|
|
|
|||
|
|
@ -55,14 +55,14 @@ LauncherPartLaunch::LauncherPartLaunch(LaunchTask* parent)
|
|||
if (parent->instance()->settings()->get("CloseAfterLaunch").toBool()) {
|
||||
static const QRegularExpression s_settingUser(".*Setting user.+", QRegularExpression::CaseInsensitiveOption);
|
||||
std::shared_ptr<QMetaObject::Connection> connection{ new QMetaObject::Connection };
|
||||
*connection = connect(&m_process, &LoggedProcess::log, this,
|
||||
[connection](const QStringList& lines, [[maybe_unused]] MessageLevel level) {
|
||||
qDebug() << lines;
|
||||
if (lines.filter(s_settingUser).length() != 0) {
|
||||
APPLICATION->closeAllWindows();
|
||||
disconnect(*connection);
|
||||
}
|
||||
});
|
||||
*connection =
|
||||
connect(&m_process, &LoggedProcess::log, this, [connection](const QStringList& lines, [[maybe_unused]] MessageLevel level) {
|
||||
qDebug() << lines;
|
||||
if (lines.filter(s_settingUser).length() != 0) {
|
||||
APPLICATION->closeAllWindows();
|
||||
disconnect(*connection);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
connect(&m_process, &LoggedProcess::log, this, &LauncherPartLaunch::logLines);
|
||||
|
|
|
|||
|
|
@ -36,8 +36,6 @@
|
|||
*/
|
||||
|
||||
#include "DataPackFolderModel.h"
|
||||
#include <qnamespace.h>
|
||||
#include <qsize.h>
|
||||
|
||||
#include <QIcon>
|
||||
#include <QStyle>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@
|
|||
*/
|
||||
|
||||
#include "Mod.h"
|
||||
#include <qpixmap.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QRegularExpression>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "modplatform/flame/FlameModIndex.h"
|
||||
#include "settings/Setting.h"
|
||||
#include "tasks/SequentialTask.h"
|
||||
#include "tasks/Task.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)
|
||||
{
|
||||
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);
|
||||
|
||||
update();
|
||||
|
|
@ -328,7 +335,20 @@ bool ResourceFolderModel::update()
|
|||
},
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ class ResourceFolderModel : public QAbstractListModel {
|
|||
virtual bool startWatching() { return startWatching({ 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
|
||||
* instance file hierarchy.
|
||||
|
|
@ -188,6 +188,7 @@ class ResourceFolderModel : public QAbstractListModel {
|
|||
void parseFinished();
|
||||
|
||||
protected:
|
||||
[[nodiscard]] virtual Task* createPreUpdateTask() { return nullptr; }
|
||||
/** 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.
|
||||
|
|
|
|||
|
|
@ -35,8 +35,6 @@
|
|||
*/
|
||||
|
||||
#include "ResourcePackFolderModel.h"
|
||||
#include <qnamespace.h>
|
||||
#include <qsize.h>
|
||||
|
||||
#include <QIcon>
|
||||
#include <QStyle>
|
||||
|
|
|
|||
56
launcher/minecraft/mod/ShaderPackFolderModel.cpp
Normal file
56
launcher/minecraft/mod/ShaderPackFolderModel.cpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#include "ShaderPackFolderModel.h"
|
||||
#include "FileSystem.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));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
bool processMCMeta(DataPack* pack, QByteArray&& raw_data)
|
||||
{
|
||||
try {
|
||||
auto json_doc = QJsonDocument::fromJson(raw_data);
|
||||
auto pack_obj = Json::requireObject(json_doc.object(), "pack", {});
|
||||
QJsonParseError parse_error;
|
||||
auto json_doc = Json::parseUntilGarbage(raw_data, &parse_error);
|
||||
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->setDescription(DataPackUtils::processComponent(pack_obj.value("description")));
|
||||
} catch (Json::JsonException& e) {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,13 @@ LocalResourceUpdateTask::LocalResourceUpdateTask(QDir index_dir, ModPlatform::In
|
|||
}
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
SetFileAttributesW(index_dir.path().toStdWString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
|
||||
std::wstring wpath = index_dir.path().toStdWString();
|
||||
if (index_dir.dirName().startsWith('.')) {
|
||||
SetFileAttributesW(wpath.c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
|
||||
} else {
|
||||
// fix shaderpacks folder being hidden by Prism Launcher 10.0.1
|
||||
SetFileAttributesW(wpath.c_str(), FILE_ATTRIBUTE_NORMAL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -136,6 +136,10 @@ void ResourceFolderLoadTask::getFromMetadata()
|
|||
{
|
||||
m_index_dir.refresh();
|
||||
for (auto entry : m_index_dir.entryList(QDir::Files)) {
|
||||
if (!entry.endsWith(".pw.toml")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto metadata = Metadata::get(m_index_dir, entry);
|
||||
|
||||
if (!metadata.isValid())
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -22,29 +23,89 @@
|
|||
|
||||
#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)
|
||||
{
|
||||
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
|
||||
// https://github.com/PrismLauncher/PrismLauncher/issues/4032
|
||||
// https://doc.qt.io/qt-6/qpainter.html#begin
|
||||
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());
|
||||
newSkin.fill(Qt::transparent);
|
||||
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));
|
||||
p.drawImage(QPoint(16, 48), leg); // copy leg
|
||||
auto copyRect = [&p, &newSkin](int startX, int startY, int offsetX, int offsetY, int sizeX, int sizeY) {
|
||||
QImage region = newSkin.copy(startX, startY, sizeX, sizeY);
|
||||
region = region.mirrored(true, false);
|
||||
|
||||
auto arm = skin.copy(QRect(40, 16, 16, 16));
|
||||
p.drawImage(QPoint(32, 48), arm); // copy arm
|
||||
return newSkin;
|
||||
p.drawImage(startX + offsetX, startY + offsetY, region);
|
||||
};
|
||||
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;
|
||||
}
|
||||
|
||||
static QImage getSkin(const QString 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, 36, 4, 12));
|
||||
// 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(4, 52, 4, 12));
|
||||
|
||||
auto armWidth = slim ? 3 : 4;
|
||||
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, 36, 4, 12));
|
||||
// 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(12, 52, 4, 12));
|
||||
|
||||
// right arm
|
||||
paint.drawImage(armPosX + 20, 10, texture.copy(48 + armWidth, 20, armWidth, 12));
|
||||
|
|
|
|||
|
|
@ -25,11 +25,9 @@
|
|||
|
||||
namespace ModPlatform {
|
||||
|
||||
static const QMap<QString, IndexedVersionType> s_indexed_version_type_names = {
|
||||
{ "release", IndexedVersionType::Release },
|
||||
{ "beta", IndexedVersionType::Beta },
|
||||
{ "alpha", IndexedVersionType::Alpha }
|
||||
};
|
||||
static const QMap<QString, IndexedVersionType> s_indexed_version_type_names = { { "release", IndexedVersionType::Release },
|
||||
{ "beta", IndexedVersionType::Beta },
|
||||
{ "alpha", IndexedVersionType::Alpha } };
|
||||
|
||||
static const QList<ModLoaderType> loaderList = { NeoForge, Forge, Cauldron, LiteLoader, Quilt, Fabric,
|
||||
Babric, BTA, LegacyFabric, Ornithe, Rift };
|
||||
|
|
@ -45,11 +43,13 @@ QList<ModLoaderType> modLoaderTypesToList(ModLoaderTypes flags)
|
|||
return flagList;
|
||||
}
|
||||
|
||||
QString IndexedVersionType::toString() const {
|
||||
QString IndexedVersionType::toString() const
|
||||
{
|
||||
return s_indexed_version_type_names.key(m_type, "unknown");
|
||||
}
|
||||
|
||||
IndexedVersionType IndexedVersionType::fromString(const QString& type) {
|
||||
IndexedVersionType IndexedVersionType::fromString(const QString& type)
|
||||
{
|
||||
return s_indexed_version_type_names.value(type, IndexedVersionType::Unknown);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,6 @@ struct IndexedVersionType {
|
|||
Enum m_type;
|
||||
};
|
||||
|
||||
|
||||
struct Dependency {
|
||||
QVariant addonId;
|
||||
DependencyType type;
|
||||
|
|
@ -121,8 +120,7 @@ struct IndexedVersion {
|
|||
|
||||
QString getVersionDisplayString() const
|
||||
{
|
||||
auto release_type =
|
||||
version_type.isValid() ? QString(" [%1]").arg(version_type.toString()) : "";
|
||||
auto release_type = version_type.isValid() ? QString(" [%1]").arg(version_type.toString()) : "";
|
||||
auto versionStr = !version.contains(version_number) ? version_number : "";
|
||||
QString gameVersion = "";
|
||||
for (auto v : mcVersion) {
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
|
|||
break;
|
||||
default:
|
||||
ver_type = ModPlatform::IndexedVersionType::Unknown;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
file.version_type = ver_type;
|
||||
|
||||
|
|
|
|||
|
|
@ -91,14 +91,14 @@ Modpack parseDirectory(QString path)
|
|||
modpack.loaderType = ModPlatform::Quilt;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
legacyInstanceParsing(path, &modpack.loaderType, &modpack.loaderVersion);
|
||||
}
|
||||
} catch (const Exception& e) {
|
||||
qDebug() << "Couldn't load ftb instance json: " << e.cause();
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
auto iconFile = QFileInfo(FS::PathCombine(path, "folder.jpg"));
|
||||
if (iconFile.exists() && iconFile.isFile()) {
|
||||
modpack.icon = QIcon(iconFile.absoluteFilePath());
|
||||
|
|
@ -145,12 +145,9 @@ void legacyInstanceParsing(QString path, std::optional<ModPlatform::ModLoaderTyp
|
|||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const Exception& e)
|
||||
{
|
||||
} catch (const Exception& e) {
|
||||
qDebug() << "Couldn't load ftb version json: " << e.cause();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} // namespace FTBImportAPP
|
||||
|
||||
|
|
|
|||
|
|
@ -126,7 +126,8 @@ ModPlatform::IndexedVersion Modrinth::loadIndexedPackVersion(QJsonObject& obj, Q
|
|||
return {};
|
||||
}
|
||||
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");
|
||||
for (auto loader : loaders) {
|
||||
|
|
|
|||
|
|
@ -169,9 +169,11 @@ void NetRequest::downloadError(QNetworkReply::NetworkError error)
|
|||
}
|
||||
}
|
||||
// error happened during download.
|
||||
qCCritical(logCat) << getUid().toString() << "Failed" << m_url.toString() << "with reason" << error;
|
||||
qCCritical(logCat) << getUid().toString() << "Failed" << m_url.toString() << "with error" << error;
|
||||
if (m_reply)
|
||||
qCCritical(logCat) << getUid().toString() << "HTTP Status" << replyStatusCode() << ";error" << errorString();
|
||||
qCCritical(logCat) << getUid().toString() << "HTTP status:" << replyStatusCode() << errorString();
|
||||
if (m_errorResponse.size() > 0)
|
||||
qCCritical(logCat) << getUid().toString() << "Response from server:" << m_errorResponse;
|
||||
m_state = State::Failed;
|
||||
}
|
||||
}
|
||||
|
|
@ -308,6 +310,9 @@ void NetRequest::downloadReadyRead()
|
|||
if (m_state == State::Running) {
|
||||
auto data = m_reply->readAll();
|
||||
m_state = m_sink->write(data);
|
||||
if (replyStatusCode() >= 400) {
|
||||
m_errorResponse.append(data);
|
||||
}
|
||||
if (m_state == State::Failed) {
|
||||
qCCritical(logCat) << getUid().toString() << "Failed to process response chunk:" << m_sink->failReason();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <qloggingcategory.h>
|
||||
#include <QNetworkReply>
|
||||
#include <QUrl>
|
||||
#include <chrono>
|
||||
|
|
@ -105,6 +104,7 @@ class NetRequest : public Task {
|
|||
|
||||
/// the network reply
|
||||
unique_qobject_ptr<QNetworkReply> m_reply;
|
||||
QByteArray m_errorResponse;
|
||||
|
||||
/// source URL
|
||||
QUrl m_url;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@
|
|||
*/
|
||||
|
||||
#include "PasteUpload.h"
|
||||
#include <qobject.h>
|
||||
|
||||
#include <QHttpPart>
|
||||
#include <QJsonArray>
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@
|
|||
#include "settings/OverrideSetting.h"
|
||||
#include "settings/Setting.h"
|
||||
|
||||
#include <QVariant>
|
||||
#include <QDir>
|
||||
#include <QVariant>
|
||||
#include <utility>
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
|
|
@ -127,7 +127,8 @@ QString SettingsObject::getPathFromBookmark(const QString& id)
|
|||
}
|
||||
|
||||
// there is no need to use bookmarks if the default value is used or the directory is within the data directory (already can access)
|
||||
if (setting->get() == setting->defValue() || QDir(setting->get().toString()).absolutePath().startsWith(QDir::current().absolutePath())) {
|
||||
if (setting->get() == setting->defValue() ||
|
||||
QDir(setting->get().toString()).absolutePath().startsWith(QDir::current().absolutePath())) {
|
||||
return setting->get().toString();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -251,8 +251,7 @@ void readIndex(const QString& path, QMap<QString, Language>& languages)
|
|||
Language lang(iter.key());
|
||||
|
||||
auto langObj = Json::requireObject(iter.value());
|
||||
lang.setTranslationStats(langObj["translated"].toInt(), langObj["untranslated"].toInt(),
|
||||
langObj["fuzzy"].toInt());
|
||||
lang.setTranslationStats(langObj["translated"].toInt(), langObj["untranslated"].toInt(), langObj["fuzzy"].toInt());
|
||||
lang.file_name = Json::requireString(langObj, "file");
|
||||
lang.file_sha1 = Json::requireString(langObj, "sha1");
|
||||
lang.file_size = Json::requireInteger(langObj, "size");
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@
|
|||
#include "InstanceWindow.h"
|
||||
#include "Application.h"
|
||||
|
||||
#include <qlayoutitem.h>
|
||||
#include <QCloseEvent>
|
||||
#include <QHBoxLayout>
|
||||
#include <QMessageBox>
|
||||
|
|
|
|||
29
launcher/ui/ToolTipFilter.cpp
Normal file
29
launcher/ui/ToolTipFilter.cpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2026 Mark Deneen <mdeneen@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ToolTipFilter.h"
|
||||
|
||||
bool ToolTipFilter::eventFilter(QObject* obj, QEvent* ev)
|
||||
{
|
||||
if (ev->type() == QEvent::ToolTip) {
|
||||
return true;
|
||||
} else {
|
||||
return QObject::eventFilter(obj, ev);
|
||||
}
|
||||
}
|
||||
28
launcher/ui/ToolTipFilter.h
Normal file
28
launcher/ui/ToolTipFilter.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2026 Mark Deneen <mdeneen@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QEvent>
|
||||
#include <QObject>
|
||||
|
||||
class ToolTipFilter : public QObject {
|
||||
Q_OBJECT
|
||||
protected:
|
||||
bool eventFilter(QObject* obj, QEvent* event);
|
||||
};
|
||||
|
|
@ -12,8 +12,8 @@ ViewLogWindow::ViewLogWindow(QWidget* parent)
|
|||
setWindowTitle(tr("View Launcher Logs"));
|
||||
setCentralWidget(m_page);
|
||||
setMinimumSize(m_page->size());
|
||||
setContentsMargins(6, 6, 0, 6); // the "Other Logs" instance page has 6px padding on the right,
|
||||
// to have equal padding in all directions in the dialog we add it to all other sides.
|
||||
setContentsMargins(6, 6, 0, 6); // the "Other Logs" instance page has 6px padding on the right,
|
||||
// to have equal padding in all directions in the dialog we add it to all other sides.
|
||||
m_page->opened();
|
||||
show();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@
|
|||
#include "ui_AboutDialog.h"
|
||||
|
||||
#include <net/NetJob.h>
|
||||
#include <qobject.h>
|
||||
|
||||
namespace {
|
||||
QString getCreditsHtml()
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@
|
|||
|
||||
#include "ui_ChooseOfflineNameDialog.h"
|
||||
|
||||
ChooseOfflineNameDialog::ChooseOfflineNameDialog(const QString& message, QWidget* parent) : QDialog(parent), ui(new Ui::ChooseOfflineNameDialog)
|
||||
ChooseOfflineNameDialog::ChooseOfflineNameDialog(const QString& message, QWidget* parent)
|
||||
: QDialog(parent), ui(new Ui::ChooseOfflineNameDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->label->setText(message);
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ ExportPackDialog::ExportPackDialog(MinecraftInstancePtr instance, QWidget* paren
|
|||
m_proxy->ignoreFilesWithPath().insert(FS::PathCombine(prefix, path));
|
||||
}
|
||||
m_proxy->ignoreFilesWithName().append({ ".DS_Store", "thumbs.db", "Thumbs.db" });
|
||||
m_proxy->ignoreFilesWithSuffix().append(".pw.toml");
|
||||
m_proxy->setSourceModel(model);
|
||||
m_proxy->loadBlockedPathsFromFile(ignoreFileName());
|
||||
|
||||
|
|
@ -103,8 +104,19 @@ ExportPackDialog::ExportPackDialog(MinecraftInstancePtr instance, QWidget* paren
|
|||
MinecraftInstance* mcInstance = dynamic_cast<MinecraftInstance*>(instance.get());
|
||||
if (mcInstance) {
|
||||
for (auto resourceModel : mcInstance->resourceLists()) {
|
||||
if (resourceModel && resourceModel->indexDir().exists())
|
||||
m_proxy->ignoreFilesWithPath().insert(instanceRoot.relativeFilePath(resourceModel->indexDir().absolutePath()));
|
||||
if (resourceModel == nullptr) {
|
||||
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_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);
|
||||
|
||||
|
|
@ -92,7 +97,9 @@ SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct)
|
|||
connect(contentsWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &SkinManageDialog::selectionChanged);
|
||||
connect(m_ui->listView, &QListView::customContextMenuRequested, this, &SkinManageDialog::show_context_menu);
|
||||
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);
|
||||
});
|
||||
|
||||
|
|
@ -103,13 +110,19 @@ SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct)
|
|||
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
|
||||
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()
|
||||
{
|
||||
delete m_ui;
|
||||
delete m_skinPreview;
|
||||
if (m_skinPreview) {
|
||||
delete m_skinPreview;
|
||||
}
|
||||
}
|
||||
|
||||
void SkinManageDialog::activated(QModelIndex index)
|
||||
|
|
@ -131,7 +144,12 @@ void SkinManageDialog::selectionChanged(QItemSelection selected, [[maybe_unused]
|
|||
if (!skin)
|
||||
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->steveBtn->setChecked(skin->getModel() == SkinModel::CLASSIC);
|
||||
m_ui->alexBtn->setChecked(skin->getModel() == SkinModel::SLIM);
|
||||
|
|
@ -165,17 +183,17 @@ void SkinManageDialog::on_fileBtn_clicked()
|
|||
QPixmap previewCape(QImage capeImage, bool elytra = false)
|
||||
{
|
||||
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 combined(wing.width() * 2 - 2, wing.height(), capeImage.format());
|
||||
QImage combined(wing.width() * 2 + 1, wing.height() + 14, capeImage.format());
|
||||
combined.fill(Qt::transparent);
|
||||
|
||||
QPainter painter(&combined);
|
||||
painter.drawImage(0, 0, wing);
|
||||
painter.drawImage(wing.width() - 2, 0, mirrored);
|
||||
painter.drawImage(0, 7, wing);
|
||||
painter.drawImage(wing.width() + 1, 7, mirrored);
|
||||
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));
|
||||
}
|
||||
|
|
@ -244,10 +262,17 @@ void SkinManageDialog::on_capeCombo_currentIndexChanged(int index)
|
|||
} else {
|
||||
m_ui->capeImage->clear();
|
||||
}
|
||||
m_skinPreview->updateCape(cape);
|
||||
if (m_skinPreview) {
|
||||
m_skinPreview->updateCape(cape);
|
||||
}
|
||||
if (auto skin = getSelectedSkin(); skin) {
|
||||
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) {
|
||||
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 {
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include <QDialog>
|
||||
#include <QItemSelection>
|
||||
#include <QLabel>
|
||||
#include <QPixmap>
|
||||
|
||||
#include "minecraft/auth/MinecraftAccount.h"
|
||||
|
|
@ -68,4 +69,5 @@ class SkinManageDialog : public QDialog, public SkinProvider {
|
|||
QHash<QString, QImage> m_capes;
|
||||
QHash<QString, int> m_capesIdx;
|
||||
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 = {
|
||||
// head
|
||||
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
|
||||
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
|
||||
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
|
||||
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)),
|
||||
};
|
||||
|
||||
m_normalArms = {
|
||||
// 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.5, 12.5, 4.5), QVector3D(-6, -6, 0), QPoint(40, 32), QVector3D(4, 12, 4)),
|
||||
// Left Arm
|
||||
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)),
|
||||
};
|
||||
|
||||
m_slimArms = {
|
||||
// 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.5, 12.5, 4.5), QVector3D(-5.5, -6, 0), QPoint(40, 32), QVector3D(3, 12, 4)),
|
||||
// Left Arm
|
||||
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)),
|
||||
};
|
||||
|
||||
|
|
@ -88,7 +106,8 @@ Scene::Scene(const QImage& skin, bool slim, const QImage& cape) : QOpenGLFunctio
|
|||
}
|
||||
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) {
|
||||
delete g;
|
||||
}
|
||||
|
|
@ -106,7 +125,8 @@ void Scene::draw(QOpenGLShaderProgram* program)
|
|||
{
|
||||
m_skinTexture->bind();
|
||||
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) {
|
||||
g->draw(program);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ class Scene : protected QOpenGLFunctions {
|
|||
QList<BoxGeometry*> m_staticComponents;
|
||||
QList<BoxGeometry*> m_normalArms;
|
||||
QList<BoxGeometry*> m_slimArms;
|
||||
QList<BoxGeometry*> m_staticComponentsOverlay;
|
||||
QList<BoxGeometry*> m_normalArmsOverlay;
|
||||
QList<BoxGeometry*> m_slimArmsOverlay;
|
||||
BoxGeometry* m_cape = nullptr;
|
||||
QList<BoxGeometry*> m_elytra;
|
||||
QOpenGLTexture* m_skinTexture = nullptr;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include <QVector2D>
|
||||
#include <QVector3D>
|
||||
#include <QtMath>
|
||||
#include <functional>
|
||||
|
||||
#include "minecraft/skins/SkinModel.h"
|
||||
#include "rainbow.h"
|
||||
|
|
@ -270,11 +271,12 @@ void SkinOpenGLWindow::updateCape(const QImage& cape)
|
|||
|
||||
QColor calculateContrastingColor(const QColor& color)
|
||||
{
|
||||
constexpr float contrast = 0.2;
|
||||
auto luma = Rainbow::luma(color);
|
||||
if (luma < 0.5) {
|
||||
constexpr float contrast = 0.05;
|
||||
return Rainbow::lighten(color, contrast);
|
||||
} else {
|
||||
constexpr float contrast = 0.2;
|
||||
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 image(width, height, QImage::Format_RGB888);
|
||||
auto white = baseColor;
|
||||
auto black = calculateContrastingColor(baseColor);
|
||||
bool isDarkBase = Rainbow::luma(baseColor) < 0.5;
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -325,3 +330,9 @@ void SkinOpenGLWindow::setElytraVisible(bool visible)
|
|||
if (m_scene)
|
||||
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 setElytraVisible(bool visible);
|
||||
|
||||
static bool hasOpenGL();
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent* e) override;
|
||||
void mouseReleaseEvent(QMouseEvent* e) override;
|
||||
|
|
|
|||
|
|
@ -2,10 +2,6 @@
|
|||
#include "AccessibleInstanceView_p.h"
|
||||
#include "InstanceView.h"
|
||||
|
||||
#include <qaccessible.h>
|
||||
#include <qheaderview.h>
|
||||
#include <qvariant.h>
|
||||
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
|
||||
QAccessibleInterface* groupViewAccessibleFactory(const QString& classname, QObject* object)
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ APIPage::APIPage(QWidget* parent) : QWidget(parent), ui(new Ui::APIPage)
|
|||
static const QRegularExpression s_validUrlRegExp("https?://.+");
|
||||
static const QRegularExpression s_validMSAClientID(
|
||||
QRegularExpression::anchoredPattern("[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}"));
|
||||
static const QRegularExpression s_validFlameKey(QRegularExpression::anchoredPattern("\\$2[ayb]\\$.{56}"));
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
|
|
@ -79,7 +78,6 @@ APIPage::APIPage(QWidget* parent) : QWidget(parent), ui(new Ui::APIPage)
|
|||
ui->resourceURL->setValidator(new QRegularExpressionValidator(s_validUrlRegExp, ui->resourceURL));
|
||||
ui->baseURLEntry->setValidator(new QRegularExpressionValidator(s_validUrlRegExp, ui->baseURLEntry));
|
||||
ui->msaClientID->setValidator(new QRegularExpressionValidator(s_validMSAClientID, ui->msaClientID));
|
||||
ui->flameKey->setValidator(new QRegularExpressionValidator(s_validFlameKey, ui->flameKey));
|
||||
|
||||
ui->metaURL->setPlaceholderText(BuildConfig.META_URL);
|
||||
ui->userAgentLineEdit->setPlaceholderText(BuildConfig.USER_AGENT);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
<height>620</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@
|
|||
|
||||
#include <QDebug>
|
||||
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui/dialogs/ChooseOfflineNameDialog.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui/dialogs/MSALoginDialog.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared
|
|||
m_model->loadColumns(ui->treeView);
|
||||
connect(ui->treeView->header(), &QHeaderView::sectionResized, this, [this] { m_model->saveColumns(ui->treeView); });
|
||||
connect(ui->filterEdit, &QLineEdit::textChanged, this, &ExternalResourcesPage::filterTextChanged);
|
||||
updateActions();
|
||||
}
|
||||
|
||||
ExternalResourcesPage::~ExternalResourcesPage()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
#include <qtconcurrentrun.h>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
|
|
@ -80,7 +79,13 @@ void McClient::parseResponse()
|
|||
Q_UNUSED(readVarInt(m_resp)); // json length
|
||||
|
||||
// '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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -393,9 +393,10 @@ bool NilModFolderPage::shouldDisplay() const
|
|||
// Helper function so this doesn't need to be duplicated 3 times
|
||||
inline bool ModFolderPage::handleNoModLoader()
|
||||
{
|
||||
int resp = QMessageBox::question(this, this->tr("Missing Mod Loader"),
|
||||
this->tr("You need to install a compatible mod loader before installing mods. Would you like to do so?"),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
int resp =
|
||||
QMessageBox::question(this, this->tr("Missing Mod Loader"),
|
||||
this->tr("You need to install a compatible mod loader before installing mods. Would you like to do so?"),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
switch (resp) {
|
||||
case QMessageBox::Yes: {
|
||||
// Should be safe
|
||||
|
|
@ -403,8 +404,8 @@ inline bool ModFolderPage::handleNoModLoader()
|
|||
InstallLoaderDialog dialog(profile, QString(), this);
|
||||
bool ret = dialog.exec();
|
||||
this->m_container->refreshContainer();
|
||||
|
||||
// returning negation of dialog.exec which'll be true if the install loader dialog got canceled/closed
|
||||
|
||||
// returning negation of dialog.exec which'll be true if the install loader dialog got canceled/closed
|
||||
// and false if the user went through and installed a loader
|
||||
return !ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,19 @@
|
|||
#include <QFutureWatcher>
|
||||
|
||||
#include <Json.h>
|
||||
#include "Exception.h"
|
||||
#include "McClient.h"
|
||||
#include "McResolver.h"
|
||||
#include "ServerPingTask.h"
|
||||
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -106,8 +106,8 @@ QVariant ModModel::getInstalledPackVersion(ModPlatform::IndexedPack::Ptr pack) c
|
|||
|
||||
bool checkSide(ModPlatform::Side filter, ModPlatform::Side value)
|
||||
{
|
||||
return filter == ModPlatform::Side::NoSide || value == ModPlatform::Side::NoSide || filter == ModPlatform::Side::UniversalSide ||
|
||||
value == ModPlatform::Side::UniversalSide || filter == value;
|
||||
return (filter != ModPlatform::Side::ClientSide && filter != ModPlatform::Side::ServerSide) ||
|
||||
(value != ModPlatform::Side::ClientSide && value != ModPlatform::Side::ServerSide) || filter == value;
|
||||
}
|
||||
|
||||
bool ModModel::checkFilters(ModPlatform::IndexedPack::Ptr pack)
|
||||
|
|
|
|||
|
|
@ -193,8 +193,8 @@ void ListModel::performPaginatedSearch()
|
|||
callbacks.on_succeed = [this](auto& doc) { searchRequestFinished(doc); };
|
||||
callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); };
|
||||
callbacks.on_abort = [this] {
|
||||
qCritical() << "Search task aborted by an unknown reason!";
|
||||
searchRequestFailed("Aborted");
|
||||
qCritical() << "Search task aborted by an unknown reason!";
|
||||
searchRequestFailed("Aborted");
|
||||
};
|
||||
|
||||
auto netJob = api.searchProjects({ ModPlatform::ResourceType::Modpack, m_nextSearchOffset, m_currentSearchTerm, sort, m_filter->loaders,
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@
|
|||
</font>
|
||||
</property>
|
||||
<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 name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
@ -47,8 +47,7 @@
|
|||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="viewfolder">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="viewfolder"/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
|
|
@ -82,7 +81,7 @@
|
|||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
|
|
|
|||
|
|
@ -163,8 +163,8 @@ void ModpackListModel::performPaginatedSearch()
|
|||
callbacks.on_succeed = [this](auto& doc) { searchRequestFinished(doc); };
|
||||
callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); };
|
||||
callbacks.on_abort = [this] {
|
||||
qCritical() << "Search task aborted by an unknown reason!";
|
||||
searchRequestFailed("Aborted");
|
||||
qCritical() << "Search task aborted by an unknown reason!";
|
||||
searchRequestFailed("Aborted");
|
||||
};
|
||||
|
||||
auto netJob = api.searchProjects({ ModPlatform::ResourceType::Modpack, m_nextSearchOffset, m_currentSearchTerm, sort, m_filter->loaders,
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
|
|||
++it;
|
||||
#endif
|
||||
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;
|
||||
|
|
@ -227,9 +227,9 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
|
|||
for (auto version : m_current->versions) {
|
||||
if (!version.version.contains(version.version))
|
||||
m_ui->versionSelectionBox->addItem(QString("%1 - %2").arg(version.version, version.version_number),
|
||||
QVariant(version.addonId));
|
||||
QVariant(version.fileId));
|
||||
else
|
||||
m_ui->versionSelectionBox->addItem(version.version, QVariant(version.addonId));
|
||||
m_ui->versionSelectionBox->addItem(version.version, QVariant(version.fileId));
|
||||
}
|
||||
|
||||
suggestCurrent();
|
||||
|
|
@ -312,7 +312,7 @@ void ModrinthPage::suggestCurrent()
|
|||
}
|
||||
|
||||
for (auto& ver : m_current->versions) {
|
||||
if (ver.addonId == m_selectedVersion) {
|
||||
if (ver.fileId == m_selectedVersion) {
|
||||
QMap<QString, QString> extra_info;
|
||||
extra_info.insert("pack_id", m_current->addonId.toString());
|
||||
extra_info.insert("pack_version_id", ver.fileId.toString());
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ bool CheckComboBox::eventFilter(QObject* receiver, QEvent* event)
|
|||
}
|
||||
case QEvent::MouseButtonPress: {
|
||||
auto ev = static_cast<QMouseEvent*>(event);
|
||||
m_containerMousePress = ev && view()->indexAt(ev->pos()).isValid();
|
||||
m_containerMousePress = ev && view()->indexAt(ev->pos()).isValid() && view()->rect().contains(ev->pos());
|
||||
break;
|
||||
}
|
||||
case QEvent::Wheel:
|
||||
|
|
@ -203,4 +203,4 @@ void CheckComboBox::paintEvent(QPaintEvent*)
|
|||
painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
|
||||
}
|
||||
|
||||
#include "CheckComboBox.moc"
|
||||
#include "CheckComboBox.moc"
|
||||
|
|
|
|||
|
|
@ -65,8 +65,6 @@ void CustomCommands::retranslate()
|
|||
|
||||
bool CustomCommands::checked() const
|
||||
{
|
||||
if (!ui->overrideCheckBox->isVisible())
|
||||
return true;
|
||||
return ui->overrideCheckBox->isChecked();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -96,8 +96,6 @@ void EnvironmentVariables::retranslate()
|
|||
|
||||
bool EnvironmentVariables::override() const
|
||||
{
|
||||
if (!ui->overrideCheckBox->isVisible())
|
||||
return false;
|
||||
return ui->overrideCheckBox->isChecked();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
#include <BaseVersion.h>
|
||||
#include <QObjectPtr.h>
|
||||
#include <java/JavaChecker.h>
|
||||
#include <qcheckbox.h>
|
||||
#include <QIcon>
|
||||
|
||||
class QLineEdit;
|
||||
|
|
|
|||
|
|
@ -207,7 +207,8 @@ void PageContainer::addButtons(QLayout* buttons)
|
|||
m_layout->addLayout(buttons, 2, 1, 1, 2);
|
||||
}
|
||||
|
||||
void PageContainer::useSidebarStyle(bool sidebar) {
|
||||
void PageContainer::useSidebarStyle(bool sidebar)
|
||||
{
|
||||
m_pageList->setProperty("_kde_side_panel_view", sidebar);
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue