mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2025-06-08 11:25:47 +02:00
Merge remote-tracking branch 'upstream/develop' into data-packs
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
This commit is contained in:
commit
6ab4fef0c5
20
.clang-tidy
20
.clang-tidy
@ -1,5 +1,23 @@
|
||||
Checks:
|
||||
- modernize-use-using
|
||||
- readability-avoid-const-params-in-decls
|
||||
- misc-unused-parameters,
|
||||
- readability-identifier-naming
|
||||
|
||||
SystemHeaders: false
|
||||
# ^ Without unused-parameters the readability-identifier-naming check doesn't cause any warnings.
|
||||
|
||||
CheckOptions:
|
||||
- { key: readability-identifier-naming.ClassCase, value: PascalCase }
|
||||
- { key: readability-identifier-naming.EnumCase, value: PascalCase }
|
||||
- { key: readability-identifier-naming.FunctionCase, value: camelCase }
|
||||
- { key: readability-identifier-naming.GlobalVariableCase, value: camelCase }
|
||||
- { key: readability-identifier-naming.GlobalFunctionCase, value: camelCase }
|
||||
- { key: readability-identifier-naming.GlobalConstantCase, value: SCREAMING_SNAKE_CASE }
|
||||
- { key: readability-identifier-naming.MacroDefinitionCase, value: SCREAMING_SNAKE_CASE }
|
||||
- { key: readability-identifier-naming.ClassMemberCase, value: camelCase }
|
||||
- { key: readability-identifier-naming.PrivateMemberPrefix, value: m_ }
|
||||
- { key: readability-identifier-naming.ProtectedMemberPrefix, value: m_ }
|
||||
- { key: readability-identifier-naming.PrivateStaticMemberPrefix, value: s_ }
|
||||
- { key: readability-identifier-naming.ProtectedStaticMemberPrefix, value: s_ }
|
||||
- { key: readability-identifier-naming.PublicStaticConstantCase, value: SCREAMING_SNAKE_CASE }
|
||||
- { key: readability-identifier-naming.EnumConstantCase, value: SCREAMING_SNAKE_CASE }
|
@ -2,3 +2,6 @@
|
||||
|
||||
# tabs -> spaces
|
||||
bbb3b3e6f6e3c0f95873f22e6d0a4aaf350f49d9
|
||||
|
||||
# (nix) alejandra -> nixfmt
|
||||
4c81d8c53d09196426568c4a31a4e752ed05397a
|
||||
|
2
.github/workflows/backport.yml
vendored
2
.github/workflows/backport.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Create backport PRs
|
||||
uses: korthout/backport-action@v3.0.2
|
||||
uses: korthout/backport-action@v3.2.0
|
||||
with:
|
||||
# Config README: https://github.com/korthout/backport-action#backport-action
|
||||
pull_description: |-
|
||||
|
105
.github/workflows/build.yml
vendored
105
.github/workflows/build.yml
vendored
@ -56,14 +56,14 @@ jobs:
|
||||
qt_ver: 5
|
||||
qt_host: linux
|
||||
qt_arch: ""
|
||||
qt_version: "5.12.8"
|
||||
qt_version: "5.15.2"
|
||||
qt_modules: "qtnetworkauth"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
- os: ubuntu-22.04
|
||||
qt_ver: 6
|
||||
qt_host: linux
|
||||
qt_arch: ""
|
||||
qt_version: "6.2.4"
|
||||
qt_version: "6.5.3"
|
||||
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||
|
||||
- os: windows-2022
|
||||
@ -77,10 +77,12 @@ jobs:
|
||||
architecture: "x64"
|
||||
vcvars_arch: "amd64"
|
||||
qt_ver: 6
|
||||
qt_host: windows
|
||||
qt_arch: ""
|
||||
qt_version: "6.7.2"
|
||||
qt_host: "windows"
|
||||
qt_arch: "win64_msvc2022_64"
|
||||
qt_version: "6.8.1"
|
||||
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||
nscurl_tag: "v24.9.26.122"
|
||||
nscurl_sha256: "AEE6C4BE3CB6455858E9C1EE4B3AFE0DB9960FA03FE99CCDEDC28390D57CCBB0"
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-MSVC-arm64"
|
||||
@ -88,21 +90,23 @@ jobs:
|
||||
architecture: "arm64"
|
||||
vcvars_arch: "amd64_arm64"
|
||||
qt_ver: 6
|
||||
qt_host: windows
|
||||
qt_arch: "win64_msvc2019_arm64"
|
||||
qt_version: "6.7.2"
|
||||
qt_host: "windows"
|
||||
qt_arch: "win64_msvc2022_arm64_cross_compiled"
|
||||
qt_version: "6.8.1"
|
||||
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||
nscurl_tag: "v24.9.26.122"
|
||||
nscurl_sha256: "AEE6C4BE3CB6455858E9C1EE4B3AFE0DB9960FA03FE99CCDEDC28390D57CCBB0"
|
||||
|
||||
- os: macos-12
|
||||
- os: macos-14
|
||||
name: macOS
|
||||
macosx_deployment_target: 11.0
|
||||
qt_ver: 6
|
||||
qt_host: mac
|
||||
qt_arch: ""
|
||||
qt_version: "6.7.2"
|
||||
qt_version: "6.8.1"
|
||||
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||
|
||||
- os: macos-12
|
||||
- os: macos-14
|
||||
name: macOS-Legacy
|
||||
macosx_deployment_target: 10.13
|
||||
qt_ver: 5
|
||||
@ -160,13 +164,13 @@ jobs:
|
||||
|
||||
- name: Setup ccache
|
||||
if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug'
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
uses: hendrikmuhs/ccache-action@v1.2.17
|
||||
with:
|
||||
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }}
|
||||
|
||||
- name: Retrieve ccache cache (Windows MinGW-w64)
|
||||
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
||||
uses: actions/cache@v4.0.2
|
||||
uses: actions/cache@v4.2.3
|
||||
with:
|
||||
path: '${{ github.workspace }}\.ccache'
|
||||
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
|
||||
@ -199,7 +203,7 @@ jobs:
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install ninja-build extra-cmake-modules scdoc appstream
|
||||
sudo apt-get -y install ninja-build extra-cmake-modules scdoc appstream libxcb-cursor-dev
|
||||
|
||||
- name: Install Dependencies (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
@ -209,14 +213,14 @@ jobs:
|
||||
|
||||
- name: Install host Qt (Windows MSVC arm64)
|
||||
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||
uses: jurplel/install-qt-action@v3
|
||||
uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
aqtversion: "==3.1.*"
|
||||
py7zrversion: ">=0.20.2"
|
||||
version: ${{ matrix.qt_version }}
|
||||
host: "windows"
|
||||
target: "desktop"
|
||||
arch: ""
|
||||
arch: ${{ matrix.qt_arch }}
|
||||
modules: ${{ matrix.qt_modules }}
|
||||
cache: ${{ inputs.is_qt_cached }}
|
||||
cache-key-prefix: host-qt-arm64-windows
|
||||
@ -225,7 +229,7 @@ jobs:
|
||||
|
||||
- name: Install Qt (macOS, Linux & Windows MSVC)
|
||||
if: matrix.msystem == ''
|
||||
uses: jurplel/install-qt-action@v3
|
||||
uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
aqtversion: "==3.1.*"
|
||||
py7zrversion: ">=0.20.2"
|
||||
@ -252,13 +256,19 @@ jobs:
|
||||
|
||||
wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage"
|
||||
|
||||
sudo apt install libopengl0
|
||||
sudo apt install libopengl0 libfuse2
|
||||
|
||||
- name: Add QT_HOST_PATH var (Windows MSVC arm64)
|
||||
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||
run: |
|
||||
echo "QT_HOST_PATH=${{ github.workspace }}\HostQt\Qt\${{ matrix.qt_version }}\msvc2019_64" >> $env:GITHUB_ENV
|
||||
echo "QT_HOST_PATH=${{ github.workspace }}\HostQt\Qt\${{ matrix.qt_version }}\msvc2022_64" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Setup java (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: "17"
|
||||
##
|
||||
# CONFIGURE
|
||||
##
|
||||
@ -266,23 +276,23 @@ jobs:
|
||||
- name: Configure CMake (macOS)
|
||||
if: runner.os == 'macOS' && matrix.qt_ver == 6
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_ENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja
|
||||
|
||||
- name: Configure CMake (macOS-Legacy)
|
||||
if: runner.os == 'macOS' && matrix.qt_ver == 5
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_ENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -DCMAKE_OSX_ARCHITECTURES="x86_64" -G Ninja
|
||||
|
||||
- name: Configure CMake (Windows MinGW-w64)
|
||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} -G Ninja
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_ENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} -G Ninja
|
||||
|
||||
- name: Configure CMake (Windows MSVC)
|
||||
if: runner.os == 'Windows' && matrix.msystem == ''
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }}
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_ENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }}
|
||||
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
|
||||
if ("${{ env.CCACHE_VAR }}")
|
||||
{
|
||||
@ -297,7 +307,7 @@ jobs:
|
||||
- name: Configure CMake (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -G Ninja
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_ENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -G Ninja
|
||||
|
||||
##
|
||||
# BUILD
|
||||
@ -367,11 +377,13 @@ jobs:
|
||||
|
||||
if [ -n '${{ secrets.APPLE_CODESIGN_ID }}' ]; then
|
||||
APPLE_CODESIGN_ID='${{ secrets.APPLE_CODESIGN_ID }}'
|
||||
ENTITLEMENTS_FILE='../program_info/App.entitlements'
|
||||
else
|
||||
APPLE_CODESIGN_ID='-'
|
||||
ENTITLEMENTS_FILE='../program_info/AdhocSignedApp.entitlements'
|
||||
fi
|
||||
|
||||
sudo codesign --sign "$APPLE_CODESIGN_ID" --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
||||
sudo codesign --sign "$APPLE_CODESIGN_ID" --deep --force --entitlements "$ENTITLEMENTS_FILE" --options runtime "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
||||
mv "PrismLauncher.app" "Prism Launcher.app"
|
||||
|
||||
- name: Notarize (macOS)
|
||||
@ -397,9 +409,8 @@ jobs:
|
||||
if: matrix.name == 'macOS'
|
||||
run: |
|
||||
if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then
|
||||
brew install openssl@3
|
||||
echo '${{ secrets.SPARKLE_ED25519_KEY }}' > ed25519-priv.pem
|
||||
signature=$(/usr/local/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.zip -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
|
||||
signature=$(/opt/homebrew/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.zip -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
|
||||
rm ed25519-priv.pem
|
||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||
### Artifact Information :information_source:
|
||||
@ -465,6 +476,16 @@ jobs:
|
||||
- name: Package (Windows, installer)
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
if ('${{ matrix.nscurl_tag }}') {
|
||||
New-Item -Name NSISPlugins -ItemType Directory
|
||||
Invoke-Webrequest https://github.com/negrutiu/nsis-nscurl/releases/download/${{ matrix.nscurl_tag }}/NScurl.zip -OutFile NSISPlugins\NScurl.zip
|
||||
$nscurl_hash = Get-FileHash NSISPlugins\NScurl.zip -Algorithm Sha256 | Select-Object -ExpandProperty Hash
|
||||
if ( $nscurl_hash -ne "${{ matrix.nscurl_sha256 }}") {
|
||||
echo "::error:: NSCurl.zip sha256 mismatch"
|
||||
exit 1
|
||||
}
|
||||
Expand-Archive -Path NSISPlugins\NScurl.zip -DestinationPath NSISPlugins\NScurl
|
||||
}
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
|
||||
|
||||
@ -497,8 +518,8 @@ jobs:
|
||||
|
||||
cp -r ${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
||||
|
||||
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||
cp /usr/lib/x86_64-linux-gnu/libssl.so.* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||
cp /usr/lib/x86_64-linux-gnu/libOpenGL.so.0* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||
|
||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
|
||||
@ -533,9 +554,9 @@ jobs:
|
||||
mkdir ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
cp /lib/x86_64-linux-gnu/libbz2.so.1.0 ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
cp /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0 ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
cp /usr/lib/x86_64-linux-gnu/libffi.so.7 ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.* ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
cp /usr/lib/x86_64-linux-gnu/libssl.so.* ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
cp /usr/lib/x86_64-linux-gnu/libffi.so.*.* ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
mv ${{ env.INSTALL_PORTABLE_DIR }}/bin/*.so* ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
|
||||
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
|
||||
@ -607,21 +628,3 @@ jobs:
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
ccache -s
|
||||
|
||||
flatpak:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: bilelmoussaoui/flatpak-github-actions:kde-6.7
|
||||
options: --privileged
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
if: inputs.build_type == 'Debug'
|
||||
with:
|
||||
submodules: "true"
|
||||
- name: Build Flatpak (Linux)
|
||||
if: inputs.build_type == 'Debug'
|
||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
||||
with:
|
||||
bundle: "Prism Launcher.flatpak"
|
||||
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
|
||||
|
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
||||
run:
|
||||
sudo apt-get -y update
|
||||
|
||||
sudo apt-get -y install ninja-build extra-cmake-modules scdoc qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 libqt5networkauth5 libqt5networkauth5-dev
|
||||
sudo apt-get -y install ninja-build extra-cmake-modules scdoc qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 libqt5networkauth5 libqt5networkauth5-dev libqt5opengl5 libqt5opengl5-dev
|
||||
|
||||
- name: Configure and Build
|
||||
run: |
|
||||
|
62
.github/workflows/flatpak.yml
vendored
Normal file
62
.github/workflows/flatpak.yml
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
name: Flatpak
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- "**/LICENSE"
|
||||
- ".github/ISSUE_TEMPLATE/**"
|
||||
- ".markdownlint**"
|
||||
- "nix/**"
|
||||
# We don't do anything with these artifacts on releases. They go to Flathub
|
||||
tags-ignore:
|
||||
- "*"
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- "**/LICENSE"
|
||||
- ".github/ISSUE_TEMPLATE/**"
|
||||
- ".markdownlint**"
|
||||
- "nix/**"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build (${{ matrix.arch }})
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
arch: x86_64
|
||||
|
||||
- os: ubuntu-22.04-arm
|
||||
arch: aarch64
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
container:
|
||||
image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.8
|
||||
options: --privileged
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set short version
|
||||
shell: bash
|
||||
run: |
|
||||
echo "VERSION=${GITHUB_SHA::7}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Build Flatpak
|
||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
||||
with:
|
||||
bundle: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-Flatpak.flatpak
|
||||
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
|
||||
arch: ${{ matrix.arch }}
|
90
.github/workflows/nix.yml
vendored
Normal file
90
.github/workflows/nix.yml
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
name: Nix
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- "**/LICENSE"
|
||||
- ".github/ISSUE_TEMPLATE/**"
|
||||
- ".markdownlint**"
|
||||
- "flatpak/**"
|
||||
pull_request_target:
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- "**/LICENSE"
|
||||
- ".github/ISSUE_TEMPLATE/**"
|
||||
- ".markdownlint**"
|
||||
- "flatpak/**"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
DEBUG: ${{ github.ref_type != 'tag' }}
|
||||
USE_DETERMINATE: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build (${{ matrix.system }})
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
system: x86_64-linux
|
||||
|
||||
- os: ubuntu-22.04-arm
|
||||
system: aarch64-linux
|
||||
|
||||
- os: macos-13
|
||||
system: x86_64-darwin
|
||||
|
||||
- os: macos-14
|
||||
system: aarch64-darwin
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@v16
|
||||
with:
|
||||
determinate: ${{ env.USE_DETERMINATE }}
|
||||
|
||||
# For PRs
|
||||
- name: Setup Nix Magic Cache
|
||||
if: ${{ env.USE_DETERMINATE }}
|
||||
uses: DeterminateSystems/flakehub-cache-action@v1
|
||||
|
||||
# For in-tree builds
|
||||
- name: Setup Cachix
|
||||
if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}
|
||||
uses: cachix/cachix-action@v16
|
||||
with:
|
||||
name: prismlauncher
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
|
||||
- name: Run Flake checks
|
||||
run: |
|
||||
nix flake check --print-build-logs --show-trace
|
||||
|
||||
- name: Build debug package
|
||||
if: ${{ env.DEBUG }}
|
||||
run: |
|
||||
nix build \
|
||||
--no-link --print-build-logs --print-out-paths \
|
||||
.#prismlauncher-debug >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Build release package
|
||||
if: ${{ !env.DEBUG }}
|
||||
run: |
|
||||
nix build \
|
||||
--no-link --print-build-logs --print-out-paths \
|
||||
.#prismlauncher >> "$GITHUB_STEP_SUMMARY"
|
45
.github/workflows/publish.yml
vendored
Normal file
45
.github/workflows/publish.yml
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
name: Publish
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [ released ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
flakehub:
|
||||
name: FlakeHub
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v31
|
||||
|
||||
- name: Publish on FlakeHub
|
||||
uses: determinatesystems/flakehub-push@v5
|
||||
with:
|
||||
visibility: "public"
|
||||
|
||||
winget:
|
||||
name: Winget
|
||||
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: Publish on Winget
|
||||
uses: vedantmgoyal2009/winget-releaser@v2
|
||||
with:
|
||||
identifier: PrismLauncher.PrismLauncher
|
||||
version: ${{ github.event.release.tag_name }}
|
||||
installers-regex: 'PrismLauncher-Windows-MSVC(:?-arm64|-Legacy)?-Setup-.+\.exe$'
|
||||
token: ${{ secrets.WINGET_TOKEN }}
|
29
.github/workflows/stale.yml
vendored
Normal file
29
.github/workflows/stale.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: Stale
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# run weekly on sunday
|
||||
- cron: "0 0 * * 0"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
label:
|
||||
name: Label issues and PRs
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
days-before-stale: 60
|
||||
days-before-close: -1 # Don't close anything
|
||||
exempt-issue-labels: rfc,nostale,help wanted
|
||||
exempt-all-milestones: true
|
||||
exempt-all-assignees: true
|
||||
operations-per-run: 1000
|
||||
stale-issue-label: inactive
|
||||
stale-pr-label: inactive
|
4
.github/workflows/update-flake.yml
vendored
4
.github/workflows/update-flake.yml
vendored
@ -17,9 +17,9 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@ba0dd844c9180cbf77aa72a116d6fbc515d0e87b # v27
|
||||
- uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
|
||||
|
||||
- uses: DeterminateSystems/update-flake-lock@v23
|
||||
- uses: DeterminateSystems/update-flake-lock@v24
|
||||
with:
|
||||
commit-msg: "chore(nix): update lockfile"
|
||||
pr-title: "chore(nix): update lockfile"
|
||||
|
15
.github/workflows/winget.yml
vendored
15
.github/workflows/winget.yml
vendored
@ -1,15 +0,0 @@
|
||||
name: Publish to WinGet
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: vedantmgoyal2009/winget-releaser@v2
|
||||
with:
|
||||
identifier: PrismLauncher.PrismLauncher
|
||||
version: ${{ github.event.release.tag_name }}
|
||||
installers-regex: 'PrismLauncher-Windows-MSVC(:?-arm64|-Legacy)?-Setup-.+\.exe$'
|
||||
token: ${{ secrets.WINGET_TOKEN }}
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -21,6 +21,7 @@ CMakeCache.txt
|
||||
/.vs
|
||||
cmake-build-*/
|
||||
Debug
|
||||
compile_commands.json
|
||||
|
||||
# Build dirs
|
||||
build
|
||||
|
@ -78,6 +78,13 @@ else()
|
||||
# ATL's pack list needs more than the default 1 Mib stack on windows
|
||||
if(WIN32)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,8388608 ${CMAKE_EXE_LINKER_FLAGS}")
|
||||
|
||||
# -ffunction-sections and -fdata-sections help reduce binary size
|
||||
# -mguard=cf enables Control Flow Guard
|
||||
# TODO: Look into -gc-sections to further reduce binary size
|
||||
foreach(lang C CXX)
|
||||
set("CMAKE_${lang}_FLAGS_RELEASE" "-ffunction-sections -fdata-sections -mguard=cf")
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@ -92,6 +99,12 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0")
|
||||
# set CXXFLAGS for build targets
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
|
||||
# Export compile commands for debug builds if we can (useful in LSPs like clangd)
|
||||
# https://cmake.org/cmake/help/v3.31/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html
|
||||
if(CMAKE_GENERATOR STREQUAL "Unix Makefiles" OR CMAKE_GENERATOR STREQUAL "Ninja" AND CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
endif()
|
||||
|
||||
option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF)
|
||||
|
||||
# If this is a Debug build turn on address sanitiser
|
||||
@ -106,14 +119,14 @@ if ((CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebI
|
||||
else()
|
||||
# AppleClang and Clang
|
||||
message(STATUS "Address Sanitizer available on Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||
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")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||
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")
|
||||
@ -176,9 +189,11 @@ endif()
|
||||
set(Launcher_NEWS_RSS_URL "https://prismlauncher.org/feed/feed.xml" CACHE STRING "URL to fetch Prism Launcher's news RSS feed from.")
|
||||
set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'")
|
||||
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
||||
set(Launcher_LOGIN_CALLBACK_URL "https://prismlauncher.org/successful-login" CACHE STRING "URL that gets opened when the user successfully logins.")
|
||||
set(Launcher_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" CACHE STRING "URL for FML Libraries.")
|
||||
|
||||
######## Set version numbers ########
|
||||
set(Launcher_VERSION_MAJOR 9)
|
||||
set(Launcher_VERSION_MAJOR 10)
|
||||
set(Launcher_VERSION_MINOR 0)
|
||||
|
||||
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
||||
@ -205,6 +220,7 @@ set(Launcher_BUG_TRACKER_URL "https://github.com/PrismLauncher/PrismLauncher/iss
|
||||
|
||||
# Translations Platform URL
|
||||
set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/prismlauncher/launcher/" CACHE STRING "URL for the translations platform.")
|
||||
set(Launcher_TRANSLATION_FILES_URL "https://i18n.prismlauncher.org/" CACHE STRING "URL for the translations files.")
|
||||
|
||||
# Matrix Space
|
||||
set(Launcher_MATRIX_URL "https://prismlauncher.org/matrix" CACHE STRING "URL to the Matrix Space")
|
||||
@ -219,6 +235,19 @@ set(Launcher_SUBREDDIT_URL "https://prismlauncher.org/reddit" CACHE STRING "URL
|
||||
set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
|
||||
set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against")
|
||||
|
||||
# Java downloader
|
||||
set(Launcher_ENABLE_JAVA_DOWNLOADER_DEFAULT ON)
|
||||
|
||||
# Although we recommend enabling this, we cannot guarantee binary compatibility on
|
||||
# differing Linux/BSD/etc distributions. Downstream packagers should be explicitly opt-ing into this
|
||||
# feature if they know it will work with their distribution.
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(Launcher_ENABLE_JAVA_DOWNLOADER_DEFAULT OFF)
|
||||
endif()
|
||||
|
||||
# Java downloader
|
||||
option(Launcher_ENABLE_JAVA_DOWNLOADER "Build the java downloader feature" ${Launcher_ENABLE_JAVA_DOWNLOADER_DEFAULT})
|
||||
|
||||
# Native libraries
|
||||
if(UNIX AND APPLE)
|
||||
set(Launcher_GLFW_LIBRARY_NAME "libglfw.dylib" CACHE STRING "Name of native glfw library")
|
||||
@ -282,7 +311,9 @@ endif()
|
||||
include(QtVersionlessBackport)
|
||||
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
||||
set(QT_VERSION_MAJOR 5)
|
||||
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml NetworkAuth)
|
||||
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml NetworkAuth OpenGL)
|
||||
find_package(Qt5 COMPONENTS DBus)
|
||||
list(APPEND Launcher_QT_DBUS Qt5::DBus)
|
||||
|
||||
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
||||
find_package(QuaZip-Qt5 1.3 QUIET)
|
||||
@ -296,7 +327,9 @@ if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUNICODE -D_UNICODE")
|
||||
elseif(Launcher_QT_VERSION_MAJOR EQUAL 6)
|
||||
set(QT_VERSION_MAJOR 6)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml Core5Compat NetworkAuth)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml Core5Compat NetworkAuth OpenGL)
|
||||
find_package(Qt6 COMPONENTS DBus)
|
||||
list(APPEND Launcher_QT_DBUS Qt6::DBus)
|
||||
list(APPEND Launcher_QT_LIBS Qt6::Core5Compat)
|
||||
|
||||
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
||||
@ -381,8 +414,8 @@ if(UNIX AND APPLE)
|
||||
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=" CACHE STRING "Public key for Sparkle update feed")
|
||||
set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml" CACHE STRING "URL for Sparkle update feed")
|
||||
|
||||
set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.5.2/Sparkle-2.5.2.tar.xz" CACHE STRING "URL to Sparkle release archive")
|
||||
set(MACOSX_SPARKLE_SHA256 "572dd67ae398a466f19f343a449e1890bac1ef74885b4739f68f979a8a89884b" CACHE STRING "SHA256 checksum for Sparkle release archive")
|
||||
set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.6.4/Sparkle-2.6.4.tar.xz" CACHE STRING "URL to Sparkle release archive")
|
||||
set(MACOSX_SPARKLE_SHA256 "50612a06038abc931f16011d7903b8326a362c1074dabccb718404ce8e585f0b" CACHE STRING "SHA256 checksum for Sparkle release archive")
|
||||
set(MACOSX_SPARKLE_DIR "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
|
||||
|
||||
# directories to look for dependencies
|
||||
@ -516,10 +549,12 @@ else()
|
||||
endif()
|
||||
if(NOT cmark_FOUND)
|
||||
message(STATUS "Using bundled cmark")
|
||||
set(ORIGINAL_BUILD_TESTING ${BUILD_TESTING})
|
||||
set(BUILD_TESTING 0)
|
||||
set(BUILD_SHARED_LIBS 0)
|
||||
add_subdirectory(libraries/cmark EXCLUDE_FROM_ALL) # Markdown parser
|
||||
add_library(cmark::cmark ALIAS cmark)
|
||||
set(BUILD_TESTING ${ORIGINAL_BUILD_TESTING})
|
||||
else()
|
||||
message(STATUS "Using system cmark")
|
||||
endif()
|
||||
|
@ -2,16 +2,59 @@
|
||||
|
||||
## Code formatting
|
||||
|
||||
Try to follow the existing formatting.
|
||||
If there is no existing formatting, you may use `clang-format` with our included `.clang-format` configuration.
|
||||
All files are formatted with `clang-format` using the configuration in `.clang-format`. Ensure it is run on changed files before committing!
|
||||
|
||||
In general, in order of importance:
|
||||
Please also follow the project's conventions for C++:
|
||||
|
||||
- Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
|
||||
- Prefer readability over dogma.
|
||||
- Keep to the existing formatting.
|
||||
- Indent with 4 space unless it's in a submodule.
|
||||
- Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
|
||||
- Class and type names should be formatted as `PascalCase`: `MyClass`.
|
||||
- Private or protected class data members should be formatted as `camelCase` prefixed with `m_`: `m_myCounter`.
|
||||
- Private or protected `static` class data members should be formatted as `camelCase` prefixed with `s_`: `s_instance`.
|
||||
- Public class data members should be formatted as `camelCase` without the prefix: `dateOfBirth`.
|
||||
- Public, private or protected `static const` class data members should be formatted as `SCREAMING_SNAKE_CASE`: `MAX_VALUE`.
|
||||
- Class function members should be formatted as `camelCase` without a prefix: `incrementCounter`.
|
||||
- Global functions and non-`const` global variables should be formatted as `camelCase` without a prefix: `globalData`.
|
||||
- `const` global variables, macros, and enum constants should be formatted as `SCREAMING_SNAKE_CASE`: `LIGHT_GRAY`.
|
||||
- Avoid inventing acronyms or abbreviations especially for a name of multiple words - like `tp` for `texturePack`.
|
||||
|
||||
Most of these rules are included in the `.clang-tidy` file, so you can run `clang-tidy` to check for any violations.
|
||||
|
||||
Here is what these conventions with the formatting configuration look like:
|
||||
|
||||
```c++
|
||||
#define AWESOMENESS 10
|
||||
|
||||
constexpr double PI = 3.14159;
|
||||
|
||||
enum class PizzaToppings { HAM_AND_PINEAPPLE, OREO_AND_KETCHUP };
|
||||
|
||||
struct Person {
|
||||
QString name;
|
||||
QDateTime dateOfBirth;
|
||||
|
||||
long daysOld() const { return dateOfBirth.daysTo(QDateTime::currentDateTime()); }
|
||||
};
|
||||
|
||||
class ImportantClass {
|
||||
public:
|
||||
void incrementCounter()
|
||||
{
|
||||
if (m_counter + 1 > MAX_COUNTER_VALUE)
|
||||
throw std::runtime_error("Counter has reached limit!");
|
||||
|
||||
++m_counter;
|
||||
}
|
||||
|
||||
int counter() const { return m_counter; }
|
||||
|
||||
private:
|
||||
static constexpr int MAX_COUNTER_VALUE = 100;
|
||||
int m_counter;
|
||||
};
|
||||
|
||||
ImportantClass importantClassInstance;
|
||||
```
|
||||
|
||||
If you see any names which do not follow these conventions, it is preferred that you leave them be - renames increase the number of changes therefore make reviewing harder and make your PR more prone to conflicts. However, if you're refactoring a whole class anyway, it's fine.
|
||||
|
||||
## Signing your work
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
## Prism Launcher
|
||||
|
||||
Prism Launcher - Minecraft Launcher
|
||||
Copyright (C) 2022-2024 Prism Launcher Contributors
|
||||
Copyright (C) 2022-2025 Prism Launcher Contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -49,7 +49,7 @@ Config::Config()
|
||||
LAUNCHER_DOMAIN = "@Launcher_Domain@";
|
||||
LAUNCHER_CONFIGFILE = "@Launcher_ConfigFile@";
|
||||
LAUNCHER_GIT = "@Launcher_Git@";
|
||||
LAUNCHER_DESKTOPFILENAME = "@Launcher_DesktopFileName@";
|
||||
LAUNCHER_APPID = "@Launcher_AppID@";
|
||||
LAUNCHER_SVGFILENAME = "@Launcher_SVGFileName@";
|
||||
|
||||
USER_AGENT = "@Launcher_UserAgent@";
|
||||
@ -81,6 +81,9 @@ Config::Config()
|
||||
UPDATER_ENABLED = true;
|
||||
}
|
||||
|
||||
#cmakedefine01 Launcher_ENABLE_JAVA_DOWNLOADER
|
||||
JAVA_DOWNLOADER_ENABLED = Launcher_ENABLE_JAVA_DOWNLOADER;
|
||||
|
||||
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
||||
GIT_TAG = "@Launcher_GIT_TAG@";
|
||||
GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";
|
||||
@ -113,16 +116,19 @@ Config::Config()
|
||||
NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
|
||||
NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@";
|
||||
HELP_URL = "@Launcher_HELP_URL@";
|
||||
LOGIN_CALLBACK_URL = "@Launcher_LOGIN_CALLBACK_URL@";
|
||||
IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
|
||||
MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@";
|
||||
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
|
||||
META_URL = "@Launcher_META_URL@";
|
||||
FMLLIBS_BASE_URL = "@Launcher_FMLLIBS_BASE_URL@";
|
||||
|
||||
GLFW_LIBRARY_NAME = "@Launcher_GLFW_LIBRARY_NAME@";
|
||||
OPENAL_LIBRARY_NAME = "@Launcher_OPENAL_LIBRARY_NAME@";
|
||||
|
||||
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
|
||||
TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@";
|
||||
TRANSLATION_FILES_URL = "@Launcher_TRANSLATION_FILES_URL@";
|
||||
MATRIX_URL = "@Launcher_MATRIX_URL@";
|
||||
DISCORD_URL = "@Launcher_DISCORD_URL@";
|
||||
SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@";
|
||||
|
@ -52,7 +52,7 @@ class Config {
|
||||
QString LAUNCHER_DOMAIN;
|
||||
QString LAUNCHER_CONFIGFILE;
|
||||
QString LAUNCHER_GIT;
|
||||
QString LAUNCHER_DESKTOPFILENAME;
|
||||
QString LAUNCHER_APPID;
|
||||
QString LAUNCHER_SVGFILENAME;
|
||||
|
||||
/// The major version number.
|
||||
@ -67,6 +67,7 @@ class Config {
|
||||
QString VERSION_CHANNEL;
|
||||
|
||||
bool UPDATER_ENABLED = false;
|
||||
bool JAVA_DOWNLOADER_ENABLED = false;
|
||||
|
||||
/// A short string identifying this build's platform or distribution.
|
||||
QString BUILD_PLATFORM;
|
||||
@ -132,6 +133,11 @@ class Config {
|
||||
*/
|
||||
QString HELP_URL;
|
||||
|
||||
/**
|
||||
* URL that gets opened when the user succesfully logins.
|
||||
*/
|
||||
QString LOGIN_CALLBACK_URL;
|
||||
|
||||
/**
|
||||
* Client ID you can get from Imgur when you register an application
|
||||
*/
|
||||
@ -164,8 +170,8 @@ class Config {
|
||||
QString RESOURCE_BASE = "https://resources.download.minecraft.net/";
|
||||
QString LIBRARY_BASE = "https://libraries.minecraft.net/";
|
||||
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
|
||||
QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists
|
||||
QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists
|
||||
QString FMLLIBS_BASE_URL;
|
||||
QString TRANSLATION_FILES_URL;
|
||||
|
||||
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
|
||||
|
||||
|
@ -8,6 +8,8 @@
|
||||
<string>A Minecraft mod wants to access your microphone.</string>
|
||||
<key>NSDownloadsFolderUsageDescription</key>
|
||||
<string>Prism uses access to your Downloads folder to help you more quickly add mods that can't be automatically downloaded to your instance. You can change where Prism scans for downloaded mods in Settings or the prompt that appears.</string>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>Minecraft uses the local network to find and connect to LAN servers.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
|
@ -1,6 +1,4 @@
|
||||
(
|
||||
import
|
||||
(
|
||||
(import (
|
||||
let
|
||||
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
|
||||
in
|
||||
@ -8,7 +6,4 @@
|
||||
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
|
||||
sha256 = lock.nodes.flake-compat.locked.narHash;
|
||||
}
|
||||
)
|
||||
{src = ./.;}
|
||||
)
|
||||
.defaultNix
|
||||
) { src = ./.; }).defaultNix
|
||||
|
100
flake.lock
generated
100
flake.lock
generated
@ -3,11 +3,11 @@
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"lastModified": 1733328505,
|
||||
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -16,47 +16,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1722555600,
|
||||
"narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "8471fe90ad337a8074e957b69ca4d0089218391d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"pre-commit-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709087332,
|
||||
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"libnbtplusplus": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
@ -73,13 +32,28 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-filter": {
|
||||
"locked": {
|
||||
"lastModified": 1731533336,
|
||||
"narHash": "sha256-oRam5PS1vcrr5UPgALW0eo1m/5/pls27Z/pabHNy2Ms=",
|
||||
"owner": "numtide",
|
||||
"repo": "nix-filter",
|
||||
"rev": "f7653272fd234696ae94229839a99b73c9ab7de0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "nix-filter",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1723637854,
|
||||
"narHash": "sha256-med8+5DSWa2UnOqtdICndjDAEjxr5D7zaIiK4pn0Q7c=",
|
||||
"lastModified": 1742422364,
|
||||
"narHash": "sha256-mNqIplmEohk5jRkqYqG19GA8MbQ/D4gQSK0Mu4LvfRQ=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c3aa7b8938b17aebd2deecf7be0636000d62a2b9",
|
||||
"rev": "a84ebe20c6bc2ecbcfb000a50776219f48d134cc",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -89,40 +63,12 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"flake-compat"
|
||||
],
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-stable": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1723803910,
|
||||
"narHash": "sha256-yezvUuFiEnCFbGuwj/bQcqg7RykIEqudOy/RBrId0pc=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "bfef0ada09e2c8ac55bbcd0831bd0c9d42e651ba",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-parts": "flake-parts",
|
||||
"libnbtplusplus": "libnbtplusplus",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pre-commit-hooks": "pre-commit-hooks"
|
||||
"nix-filter": "nix-filter",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
159
flake.nix
159
flake.nix
@ -2,52 +2,147 @@
|
||||
description = "A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once (Fork of MultiMC)";
|
||||
|
||||
nixConfig = {
|
||||
extra-substituters = ["https://cache.garnix.io"];
|
||||
extra-trusted-public-keys = ["cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g="];
|
||||
extra-substituters = [ "https://prismlauncher.cachix.org" ];
|
||||
extra-trusted-public-keys = [
|
||||
"prismlauncher.cachix.org-1:9/n/FGyABA2jLUVfY+DEp4hKds/rwO+SCOtbOkDzd+c="
|
||||
];
|
||||
};
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
flake-parts = {
|
||||
url = "github:hercules-ci/flake-parts";
|
||||
inputs.nixpkgs-lib.follows = "nixpkgs";
|
||||
};
|
||||
pre-commit-hooks = {
|
||||
url = "github:cachix/pre-commit-hooks.nix";
|
||||
inputs = {
|
||||
nixpkgs.follows = "nixpkgs";
|
||||
nixpkgs-stable.follows = "nixpkgs";
|
||||
flake-compat.follows = "flake-compat";
|
||||
};
|
||||
};
|
||||
flake-compat = {
|
||||
url = "github:edolstra/flake-compat";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
libnbtplusplus = {
|
||||
url = "github:PrismLauncher/libnbtplusplus";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
nix-filter.url = "github:numtide/nix-filter";
|
||||
|
||||
/*
|
||||
Inputs below this are optional and can be removed
|
||||
|
||||
```
|
||||
{
|
||||
inputs.prismlauncher = {
|
||||
url = "github:PrismLauncher/PrismLauncher";
|
||||
inputs = {
|
||||
flake-compat.follows = "";
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
*/
|
||||
|
||||
flake-compat = {
|
||||
url = "github:edolstra/flake-compat";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
|
||||
outputs = {
|
||||
flake-parts,
|
||||
pre-commit-hooks,
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
libnbtplusplus,
|
||||
nix-filter,
|
||||
...
|
||||
} @ inputs:
|
||||
flake-parts.lib.mkFlake {inherit inputs;} {
|
||||
imports = [
|
||||
pre-commit-hooks.flakeModule
|
||||
}:
|
||||
let
|
||||
inherit (nixpkgs) lib;
|
||||
|
||||
./nix/dev.nix
|
||||
./nix/distribution.nix
|
||||
# While we only officially support aarch and x86_64 on Linux and MacOS,
|
||||
# we expose a reasonable amount of other systems for users who want to
|
||||
# build for most exotic platforms
|
||||
systems = lib.systems.flakeExposed;
|
||||
|
||||
forAllSystems = lib.genAttrs systems;
|
||||
nixpkgsFor = forAllSystems (system: nixpkgs.legacyPackages.${system});
|
||||
in
|
||||
{
|
||||
checks = forAllSystems (
|
||||
system:
|
||||
let
|
||||
checks' = nixpkgsFor.${system}.callPackage ./nix/checks.nix { inherit self; };
|
||||
in
|
||||
lib.filterAttrs (_: lib.isDerivation) checks'
|
||||
);
|
||||
|
||||
devShells = forAllSystems (
|
||||
system:
|
||||
let
|
||||
pkgs = nixpkgsFor.${system};
|
||||
in
|
||||
{
|
||||
default = pkgs.mkShell {
|
||||
inputsFrom = [ self.packages.${system}.prismlauncher-unwrapped ];
|
||||
buildInputs = with pkgs; [
|
||||
ccache
|
||||
ninja
|
||||
llvmPackages_19.clang-tools
|
||||
];
|
||||
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
cmakeFlags = self.packages.${system}.prismlauncher-unwrapped.cmakeFlags ++ [
|
||||
"-GNinja"
|
||||
"-Bbuild"
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
cmake $cmakeFlags -D CMAKE_BUILD_TYPE=Debug
|
||||
ln -s {build/,}compile_commands.json
|
||||
'';
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
formatter = forAllSystems (system: nixpkgsFor.${system}.nixfmt-rfc-style);
|
||||
|
||||
overlays.default = final: prev: {
|
||||
prismlauncher-unwrapped = prev.callPackage ./nix/unwrapped.nix {
|
||||
inherit
|
||||
libnbtplusplus
|
||||
nix-filter
|
||||
self
|
||||
;
|
||||
};
|
||||
|
||||
prismlauncher = final.callPackage ./nix/wrapper.nix { };
|
||||
};
|
||||
|
||||
packages = forAllSystems (
|
||||
system:
|
||||
let
|
||||
pkgs = nixpkgsFor.${system};
|
||||
|
||||
# Build a scope from our overlay
|
||||
prismPackages = lib.makeScope pkgs.newScope (final: self.overlays.default final pkgs);
|
||||
|
||||
# Grab our packages from it and set the default
|
||||
packages = {
|
||||
inherit (prismPackages) prismlauncher-unwrapped prismlauncher;
|
||||
default = prismPackages.prismlauncher;
|
||||
};
|
||||
in
|
||||
# Only output them if they're available on the current system
|
||||
lib.filterAttrs (_: lib.meta.availableOn pkgs.stdenv.hostPlatform) packages
|
||||
);
|
||||
|
||||
# We put these under legacyPackages as they are meant for CI, not end user consumption
|
||||
legacyPackages = forAllSystems (
|
||||
system:
|
||||
let
|
||||
prismPackages = self.packages.${system};
|
||||
legacyPackages = self.legacyPackages.${system};
|
||||
in
|
||||
{
|
||||
prismlauncher-debug = prismPackages.prismlauncher.override {
|
||||
prismlauncher-unwrapped = legacyPackages.prismlauncher-unwrapped-debug;
|
||||
};
|
||||
|
||||
prismlauncher-unwrapped-debug = prismPackages.prismlauncher-unwrapped.overrideAttrs {
|
||||
cmakeBuildType = "Debug";
|
||||
dontStrip = true;
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
20
flatpak/flite.json
Normal file
20
flatpak/flite.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "flite",
|
||||
"config-opts": [
|
||||
"--enable-shared",
|
||||
"--with-audio=pulseaudio"
|
||||
],
|
||||
"no-parallel-make": true,
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/festvox/flite.git",
|
||||
"tag": "v2.2",
|
||||
"commit": "e9e2e37c329dbe98bfeb27a1828ef9a71fa84f88",
|
||||
"x-checker-data": {
|
||||
"type": "git",
|
||||
"tag-pattern": "^v([\\d.]+)$"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -8,11 +8,7 @@
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://gitlab.freedesktop.org/libdecor/libdecor.git",
|
||||
"commit": "73260393a97291c887e1074ab7f318e031be0ac6"
|
||||
},
|
||||
{
|
||||
"type": "patch",
|
||||
"path": "patches/weird_libdecor.patch"
|
||||
"commit": "c2bd8ad6fa42c0cb17553ce77ad8a87d1f543b1f"
|
||||
}
|
||||
],
|
||||
"cleanup": [
|
||||
|
@ -1,11 +1,9 @@
|
||||
id: org.prismlauncher.PrismLauncher
|
||||
runtime: org.kde.Platform
|
||||
runtime-version: 6.7
|
||||
runtime-version: '6.8'
|
||||
sdk: org.kde.Sdk
|
||||
sdk-extensions:
|
||||
- org.freedesktop.Sdk.Extension.openjdk21
|
||||
- org.freedesktop.Sdk.Extension.openjdk17
|
||||
- org.freedesktop.Sdk.Extension.openjdk8
|
||||
|
||||
command: prismlauncher
|
||||
finish-args:
|
||||
@ -21,9 +19,12 @@ finish-args:
|
||||
- --filesystem=xdg-download:ro
|
||||
# FTBApp import
|
||||
- --filesystem=~/.ftba:ro
|
||||
|
||||
cleanup:
|
||||
- /lib/libGLU*
|
||||
# Userspace visibility for manual hugepages configuration
|
||||
# Required for -XX:+UseLargePages
|
||||
- --filesystem=/sys/kernel/mm/hugepages:ro
|
||||
# Userspace visibility for transparent hugepages configuration
|
||||
# Required for -XX:+UseTransparentHugePages
|
||||
- --filesystem=/sys/kernel/mm/transparent_hugepage:ro
|
||||
|
||||
modules:
|
||||
# Might be needed by some Controller mods (see https://github.com/isXander/Controlify/issues/31)
|
||||
@ -32,50 +33,39 @@ modules:
|
||||
# Needed for proper Wayland support
|
||||
- libdecor.json
|
||||
|
||||
# Text to Speech in the game
|
||||
- flite.json
|
||||
|
||||
- name: prismlauncher
|
||||
buildsystem: cmake-ninja
|
||||
builddir: true
|
||||
config-opts:
|
||||
- -DLauncher_BUILD_PLATFORM=flatpak
|
||||
# This allows us to manage and update Java independently of this Flatpak
|
||||
- -DLauncher_ENABLE_JAVA_DOWNLOADER=ON
|
||||
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
build-options:
|
||||
env:
|
||||
JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17
|
||||
JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac
|
||||
run-tests: true
|
||||
sources:
|
||||
- type: dir
|
||||
path: ../
|
||||
|
||||
- name: openjdk
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- mkdir -p /app/jdk/
|
||||
- /usr/lib/sdk/openjdk21/install.sh
|
||||
- mv /app/jre /app/jdk/21
|
||||
- /usr/lib/sdk/openjdk17/install.sh
|
||||
- mv /app/jre /app/jdk/17
|
||||
- /usr/lib/sdk/openjdk8/install.sh
|
||||
- mv /app/jre /app/jdk/8
|
||||
cleanup:
|
||||
- /jre
|
||||
|
||||
- name: glfw
|
||||
buildsystem: cmake-ninja
|
||||
config-opts:
|
||||
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
- -DBUILD_SHARED_LIBS:BOOL=ON
|
||||
- -DGLFW_USE_WAYLAND:BOOL=ON
|
||||
- -DGLFW_BUILD_WAYLAND:BOOL=ON
|
||||
- -DGLFW_BUILD_DOCS:BOOL=OFF
|
||||
sources:
|
||||
- type: git
|
||||
url: https://github.com/glfw/glfw.git
|
||||
commit: 3fa2360720eeba1964df3c0ecf4b5df8648a8e52
|
||||
commit: 7b6aead9fb88b3623e3b3725ebb42670cbe4c579 # 3.4
|
||||
- type: patch
|
||||
path: patches/0003-Don-t-crash-on-calls-to-focus-or-icon.patch
|
||||
- type: patch
|
||||
path: patches/0005-Add-warning-about-being-an-unofficial-patch.patch
|
||||
- type: patch
|
||||
path: patches/0007-Platform-Prefer-Wayland-over-X11.patch
|
||||
path: patches/0009-Defer-setting-cursor-position-until-the-cursor-is-lo.patch
|
||||
cleanup:
|
||||
- /include
|
||||
- /lib/cmake
|
||||
@ -85,8 +75,8 @@ modules:
|
||||
buildsystem: autotools
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.2.tar.xz
|
||||
sha256: c8bee4790d9058bacc4b6246456c58021db58a87ddda1a9d0139bf5f18f1f240
|
||||
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.3.tar.xz
|
||||
sha256: f8dd7566adb74147fab9964680b6bbadee87cf406a7fcff51718a5e6949b841c
|
||||
x-checker-data:
|
||||
type: anitya
|
||||
project-id: 14957
|
||||
@ -108,8 +98,8 @@ modules:
|
||||
sources:
|
||||
- type: archive
|
||||
dest-filename: gamemode.tar.gz
|
||||
url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.8.1
|
||||
sha256: 969cf85b5ca3944f3e315cd73a0ee9bea4f9c968cd7d485e9f4745bc1e679c4e
|
||||
url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.8.2
|
||||
sha256: 2886d4ce543c78bd2a364316d5e7fd59ef06b71de63f896b37c6d3dc97658f60
|
||||
x-checker-data:
|
||||
type: json
|
||||
url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest
|
||||
|
@ -1,24 +0,0 @@
|
||||
diff --git a/src/wl_window.c b/src/wl_window.c
|
||||
index 52d3b9eb..4ac4eb5d 100644
|
||||
--- a/src/wl_window.c
|
||||
+++ b/src/wl_window.c
|
||||
@@ -2117,8 +2117,7 @@ void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title)
|
||||
void _glfwSetWindowIconWayland(_GLFWwindow* window,
|
||||
int count, const GLFWimage* images)
|
||||
{
|
||||
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||
- "Wayland: The platform does not support setting the window icon");
|
||||
+ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the window icon\n");
|
||||
}
|
||||
|
||||
void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos)
|
||||
@@ -2361,8 +2360,7 @@ void _glfwRequestWindowAttentionWayland(_GLFWwindow* window)
|
||||
|
||||
void _glfwFocusWindowWayland(_GLFWwindow* window)
|
||||
{
|
||||
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||
- "Wayland: The platform does not support setting the input focus");
|
||||
+ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the input focus\n");
|
||||
}
|
||||
|
||||
void _glfwSetWindowMonitorWayland(_GLFWwindow* window,
|
@ -1,17 +0,0 @@
|
||||
diff --git a/src/init.c b/src/init.c
|
||||
index 06dbb3f2..a7c6da86 100644
|
||||
--- a/src/init.c
|
||||
+++ b/src/init.c
|
||||
@@ -449,6 +449,12 @@ GLFWAPI int glfwInit(void)
|
||||
_glfw.initialized = GLFW_TRUE;
|
||||
|
||||
glfwDefaultWindowHints();
|
||||
+
|
||||
+ fprintf(stderr, "!!! Patched GLFW from https://github.com/Admicos/minecraft-wayland\n"
|
||||
+ "!!! If any issues with the window, or some issues with rendering, occur, "
|
||||
+ "first try with the built-in GLFW, and if that solves the issue, report there first.\n"
|
||||
+ "!!! Use outside Minecraft is untested, and things might break.\n");
|
||||
+
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
@ -1,20 +0,0 @@
|
||||
diff --git a/src/platform.c b/src/platform.c
|
||||
index c5966ae7..3e7442f9 100644
|
||||
--- a/src/platform.c
|
||||
+++ b/src/platform.c
|
||||
@@ -49,12 +49,12 @@ static const struct
|
||||
#if defined(_GLFW_COCOA)
|
||||
{ GLFW_PLATFORM_COCOA, _glfwConnectCocoa },
|
||||
#endif
|
||||
-#if defined(_GLFW_X11)
|
||||
- { GLFW_PLATFORM_X11, _glfwConnectX11 },
|
||||
-#endif
|
||||
#if defined(_GLFW_WAYLAND)
|
||||
{ GLFW_PLATFORM_WAYLAND, _glfwConnectWayland },
|
||||
#endif
|
||||
+#if defined(_GLFW_X11)
|
||||
+ { GLFW_PLATFORM_X11, _glfwConnectX11 },
|
||||
+#endif
|
||||
};
|
||||
|
||||
GLFWbool _glfwSelectPlatform(int desiredID, _GLFWplatform* platform)
|
@ -0,0 +1,59 @@
|
||||
From 9997ae55a47de469ea26f8437c30b51483abda5f Mon Sep 17 00:00:00 2001
|
||||
From: Dan Klishch <danilklishch@gmail.com>
|
||||
Date: Sat, 30 Sep 2023 23:38:05 -0400
|
||||
Subject: Defer setting cursor position until the cursor is locked
|
||||
|
||||
---
|
||||
src/wl_platform.h | 3 +++
|
||||
src/wl_window.c | 14 ++++++++++++--
|
||||
2 files changed, 15 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/wl_platform.h b/src/wl_platform.h
|
||||
index ca34f66e..cd1f227f 100644
|
||||
--- a/src/wl_platform.h
|
||||
+++ b/src/wl_platform.h
|
||||
@@ -403,6 +403,9 @@ typedef struct _GLFWwindowWayland
|
||||
int scaleSize;
|
||||
int compositorPreferredScale;
|
||||
|
||||
+ double askedCursorPosX, askedCursorPosY;
|
||||
+ GLFWbool didAskForSetCursorPos;
|
||||
+
|
||||
struct zwp_relative_pointer_v1* relativePointer;
|
||||
struct zwp_locked_pointer_v1* lockedPointer;
|
||||
struct zwp_confined_pointer_v1* confinedPointer;
|
||||
diff --git a/src/wl_window.c b/src/wl_window.c
|
||||
index 1de26558..0df16747 100644
|
||||
--- a/src/wl_window.c
|
||||
+++ b/src/wl_window.c
|
||||
@@ -2586,8 +2586,9 @@ void _glfwGetCursorPosWayland(_GLFWwindow* window, double* xpos, double* ypos)
|
||||
|
||||
void _glfwSetCursorPosWayland(_GLFWwindow* window, double x, double y)
|
||||
{
|
||||
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||
- "Wayland: The platform does not support setting the cursor position");
|
||||
+ window->wl.didAskForSetCursorPos = true;
|
||||
+ window->wl.askedCursorPosX = x;
|
||||
+ window->wl.askedCursorPosY = y;
|
||||
}
|
||||
|
||||
void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode)
|
||||
@@ -2819,6 +2820,15 @@ static const struct zwp_relative_pointer_v1_listener relativePointerListener =
|
||||
static void lockedPointerHandleLocked(void* userData,
|
||||
struct zwp_locked_pointer_v1* lockedPointer)
|
||||
{
|
||||
+ _GLFWwindow* window = userData;
|
||||
+
|
||||
+ if (window->wl.didAskForSetCursorPos)
|
||||
+ {
|
||||
+ window->wl.didAskForSetCursorPos = false;
|
||||
+ zwp_locked_pointer_v1_set_cursor_position_hint(window->wl.lockedPointer,
|
||||
+ wl_fixed_from_double(window->wl.askedCursorPosX),
|
||||
+ wl_fixed_from_double(window->wl.askedCursorPosY));
|
||||
+ }
|
||||
}
|
||||
|
||||
static void lockedPointerHandleUnlocked(void* userData,
|
||||
--
|
||||
2.42.0
|
||||
|
@ -1,40 +0,0 @@
|
||||
diff --git a/src/libdecor.c b/src/libdecor.c
|
||||
index a9c1106..1aa38b3 100644
|
||||
--- a/src/libdecor.c
|
||||
+++ b/src/libdecor.c
|
||||
@@ -1391,22 +1391,32 @@ calculate_priority(const struct libdecor_plugin_description *plugin_description)
|
||||
static bool
|
||||
check_symbol_conflicts(const struct libdecor_plugin_description *plugin_description)
|
||||
{
|
||||
+ bool ret = true;
|
||||
char * const *symbol;
|
||||
+ void* main_prog = dlopen(NULL, RTLD_LAZY);
|
||||
+ if (!main_prog) {
|
||||
+ fprintf(stderr, "Plugin \"%s\" couldn't check conflicting symbols: \"%s\".\n",
|
||||
+ plugin_description->description, dlerror());
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
|
||||
symbol = plugin_description->conflicting_symbols;
|
||||
while (*symbol) {
|
||||
dlerror();
|
||||
- dlsym (RTLD_DEFAULT, *symbol);
|
||||
+ dlsym (main_prog, *symbol);
|
||||
if (!dlerror()) {
|
||||
fprintf(stderr, "Plugin \"%s\" uses conflicting symbol \"%s\".\n",
|
||||
plugin_description->description, *symbol);
|
||||
- return false;
|
||||
+ ret = false;
|
||||
+ break;
|
||||
}
|
||||
|
||||
symbol++;
|
||||
}
|
||||
|
||||
- return true;
|
||||
+ dlclose(main_prog);
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static struct plugin_loader *
|
@ -1 +1 @@
|
||||
Subproject commit f2b0c16a2a217a1822ce5a6538ba8f755ed1dd32
|
||||
Subproject commit f5d368a31d6ef046eb2955c74ec6f54f32ed5c4e
|
@ -1,7 +0,0 @@
|
||||
builds:
|
||||
exclude:
|
||||
- "*.x86_64-darwin.*"
|
||||
include:
|
||||
- "checks.x86_64-linux.*"
|
||||
- "devShells.*.*"
|
||||
- "packages.*.*"
|
@ -44,9 +44,11 @@
|
||||
#include "BuildConfig.h"
|
||||
|
||||
#include "DataMigrationTask.h"
|
||||
#include "java/JavaInstallList.h"
|
||||
#include "net/PasteUpload.h"
|
||||
#include "pathmatcher/MultiMatcher.h"
|
||||
#include "pathmatcher/SimplePrefixMatcher.h"
|
||||
#include "tasks/Task.h"
|
||||
#include "tools/GenericProfiler.h"
|
||||
#include "ui/InstanceWindow.h"
|
||||
#include "ui/MainWindow.h"
|
||||
@ -57,8 +59,6 @@
|
||||
#include "ui/pages/BasePageProvider.h"
|
||||
#include "ui/pages/global/APIPage.h"
|
||||
#include "ui/pages/global/AccountListPage.h"
|
||||
#include "ui/pages/global/CustomCommandsPage.h"
|
||||
#include "ui/pages/global/EnvironmentVariablesPage.h"
|
||||
#include "ui/pages/global/ExternalToolsPage.h"
|
||||
#include "ui/pages/global/JavaPage.h"
|
||||
#include "ui/pages/global/LanguagePage.h"
|
||||
@ -66,8 +66,10 @@
|
||||
#include "ui/pages/global/MinecraftPage.h"
|
||||
#include "ui/pages/global/ProxyPage.h"
|
||||
|
||||
#include "ui/setupwizard/AutoJavaWizardPage.h"
|
||||
#include "ui/setupwizard/JavaWizardPage.h"
|
||||
#include "ui/setupwizard/LanguageWizardPage.h"
|
||||
#include "ui/setupwizard/LoginWizardPage.h"
|
||||
#include "ui/setupwizard/PasteWizardPage.h"
|
||||
#include "ui/setupwizard/SetupWizard.h"
|
||||
#include "ui/setupwizard/ThemeWizardPage.h"
|
||||
@ -125,6 +127,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys.h>
|
||||
#include "SysInfo.h"
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#include <dlfcn.h>
|
||||
@ -151,6 +154,7 @@
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
#include <windows.h>
|
||||
#include <QStyleHints>
|
||||
#include "WindowsConsole.h"
|
||||
#endif
|
||||
|
||||
@ -221,8 +225,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
setApplicationName(BuildConfig.LAUNCHER_NAME);
|
||||
setApplicationDisplayName(QString("%1 %2").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString()));
|
||||
setApplicationVersion(BuildConfig.printableVersionString() + "\n" + BuildConfig.GIT_COMMIT);
|
||||
setDesktopFileName(BuildConfig.LAUNCHER_DESKTOPFILENAME);
|
||||
startTime = QDateTime::currentDateTime();
|
||||
setDesktopFileName(BuildConfig.LAUNCHER_APPID);
|
||||
m_startTime = QDateTime::currentDateTime();
|
||||
|
||||
// Don't quit on hiding the last window
|
||||
this->setQuitOnLastWindowClosed(false);
|
||||
@ -238,6 +242,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
|
||||
{ { "w", "world" }, "Join the specified world on launch (only valid in combination with --launch)", "world" },
|
||||
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" },
|
||||
{ { "o", "offline" }, "Launch offline, with given player name (only valid in combination with --launch)", "offline" },
|
||||
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
|
||||
{ { "I", "import" }, "Import instance or resource from specified local path or URL", "url" },
|
||||
{ "show", "Opens the window for the specified instance (by instance ID)", "show" } });
|
||||
@ -253,6 +258,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
m_serverToJoin = parser.value("server");
|
||||
m_worldToJoin = parser.value("world");
|
||||
m_profileToUse = parser.value("profile");
|
||||
if (parser.isSet("offline")) {
|
||||
m_offline = true;
|
||||
m_offlineName = parser.value("offline");
|
||||
}
|
||||
m_liveCheck = parser.isSet("alive");
|
||||
|
||||
m_instanceIdToShowWindowOf = parser.value("show");
|
||||
@ -267,8 +276,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
}
|
||||
|
||||
// error if --launch is missing with --server or --profile
|
||||
if (((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty()) || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) {
|
||||
std::cerr << "--server and --profile can only be used in combination with --launch!" << std::endl;
|
||||
if ((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty() || !m_profileToUse.isEmpty() || m_offline) &&
|
||||
m_instanceIdToLaunch.isEmpty()) {
|
||||
std::cerr << "--server, --profile and --offline can only be used in combination with --launch!" << std::endl;
|
||||
m_status = Application::Failed;
|
||||
return;
|
||||
}
|
||||
@ -393,6 +403,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
if (!m_profileToUse.isEmpty()) {
|
||||
launch.args["profile"] = m_profileToUse;
|
||||
}
|
||||
if (m_offline) {
|
||||
launch.args["offline_enabled"] = "true";
|
||||
launch.args["offline_name"] = m_offlineName;
|
||||
}
|
||||
m_peerInstance->sendMessage(launch.serialize(), timeout);
|
||||
}
|
||||
m_status = Application::Succeeded;
|
||||
@ -601,7 +615,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
m_settings->registerSetting("IconsDir", "icons");
|
||||
m_settings->registerSetting("DownloadsDir", QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
|
||||
m_settings->registerSetting("DownloadsDirWatchRecursive", false);
|
||||
m_settings->registerSetting("MoveModsFromDownloadsDir", false);
|
||||
m_settings->registerSetting("SkinsDir", "skins");
|
||||
m_settings->registerSetting("JavaDir", "java");
|
||||
|
||||
// Editors
|
||||
m_settings->registerSetting("JsonEditor", QString());
|
||||
@ -630,7 +646,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
|
||||
// Memory
|
||||
m_settings->registerSetting({ "MinMemAlloc", "MinMemoryAlloc" }, 512);
|
||||
m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, suitableMaxMem());
|
||||
m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, SysInfo::suitableMaxMem());
|
||||
m_settings->registerSetting("PermGen", 128);
|
||||
|
||||
// Java Settings
|
||||
@ -644,6 +660,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
m_settings->registerSetting("JvmArgs", "");
|
||||
m_settings->registerSetting("IgnoreJavaCompatibility", false);
|
||||
m_settings->registerSetting("IgnoreJavaWizard", false);
|
||||
auto defaultEnableAutoJava = m_settings->get("JavaPath").toString().isEmpty();
|
||||
m_settings->registerSetting("AutomaticJavaSwitch", defaultEnableAutoJava);
|
||||
m_settings->registerSetting("AutomaticJavaDownload", defaultEnableAutoJava);
|
||||
m_settings->registerSetting("UserAskedAboutAutomaticJavaDownload", false);
|
||||
|
||||
// Legacy settings
|
||||
m_settings->registerSetting("OnlineFixes", false);
|
||||
@ -776,6 +796,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
// FTBApp instances
|
||||
m_settings->registerSetting("FTBAppInstancesPath", "");
|
||||
|
||||
// Custom Technic Client ID
|
||||
m_settings->registerSetting("TechnicClientID", "");
|
||||
|
||||
// Init page provider
|
||||
{
|
||||
m_globalSettingsProvider = std::make_shared<GenericPageProvider>(tr("Settings"));
|
||||
@ -783,8 +806,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
m_globalSettingsProvider->addPage<MinecraftPage>();
|
||||
m_globalSettingsProvider->addPage<JavaPage>();
|
||||
m_globalSettingsProvider->addPage<LanguagePage>();
|
||||
m_globalSettingsProvider->addPage<CustomCommandsPage>();
|
||||
m_globalSettingsProvider->addPage<EnvironmentVariablesPage>();
|
||||
m_globalSettingsProvider->addPage<ProxyPage>();
|
||||
m_globalSettingsProvider->addPage<ExternalToolsPage>();
|
||||
m_globalSettingsProvider->addPage<AccountListPage>();
|
||||
@ -828,7 +849,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
":/icons/multimc/128x128/instances/", ":/icons/multimc/scalable/instances/" };
|
||||
m_icons.reset(new IconList(instFolders, setting->get().toString()));
|
||||
connect(setting.get(), &Setting::SettingChanged,
|
||||
[&](const Setting&, QVariant value) { m_icons->directoryChanged(value.toString()); });
|
||||
[this](const Setting&, QVariant value) { m_icons->directoryChanged(value.toString()); });
|
||||
qDebug() << "<> Instance icons initialized.";
|
||||
}
|
||||
|
||||
@ -878,6 +899,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
m_metacache->addBase("ModrinthModpacks", QDir("cache/ModrinthModpacks").absolutePath());
|
||||
m_metacache->addBase("translations", QDir("translations").absolutePath());
|
||||
m_metacache->addBase("meta", QDir("meta").absolutePath());
|
||||
m_metacache->addBase("java", QDir("cache/java").absolutePath());
|
||||
m_metacache->Load();
|
||||
qDebug() << "<> Cache initialized.";
|
||||
}
|
||||
@ -1017,7 +1039,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
}
|
||||
|
||||
// notify user if /tmp is mounted with `noexec` (#1693)
|
||||
{
|
||||
QString jvmArgs = m_settings->get("JvmArgs").toString();
|
||||
if (jvmArgs.indexOf("java.io.tmpdir") == -1) { /* java.io.tmpdir is a valid workaround, so don't annoy */
|
||||
bool is_tmp_noexec = false;
|
||||
|
||||
#if defined(Q_OS_LINUX)
|
||||
@ -1037,7 +1060,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
if (is_tmp_noexec) {
|
||||
auto infoMsg =
|
||||
tr("Your /tmp directory is currently mounted with the 'noexec' flag enabled.\n"
|
||||
"Some versions of Minecraft may not launch.\n");
|
||||
"Some versions of Minecraft may not launch.\n"
|
||||
"\n"
|
||||
"You may solve this issue by remounting /tmp as 'exec' or setting "
|
||||
"the java.io.tmpdir JVM argument to a writeable directory in a "
|
||||
"filesystem where the 'exec' flag is set (e.g., /home/user/.local/tmp)\n");
|
||||
auto msgBox = new QMessageBox(QMessageBox::Information, tr("Incompatible system configuration"), infoMsg, QMessageBox::Ok);
|
||||
msgBox->setDefaultButton(QMessageBox::Ok);
|
||||
msgBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||
@ -1057,8 +1084,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
|
||||
bool Application::createSetupWizard()
|
||||
{
|
||||
bool javaRequired = [&]() {
|
||||
bool ignoreJavaWizard = m_settings->get("IgnoreJavaWizard").toBool();
|
||||
bool javaRequired = [this]() {
|
||||
if (BuildConfig.JAVA_DOWNLOADER_ENABLED && settings()->get("AutomaticJavaDownload").toBool()) {
|
||||
return false;
|
||||
}
|
||||
bool ignoreJavaWizard = settings()->get("IgnoreJavaWizard").toBool();
|
||||
if (ignoreJavaWizard) {
|
||||
return false;
|
||||
}
|
||||
@ -1070,24 +1100,31 @@ bool Application::createSetupWizard()
|
||||
}
|
||||
QString currentJavaPath = settings()->get("JavaPath").toString();
|
||||
QString actualPath = FS::ResolveExecutable(currentJavaPath);
|
||||
if (actualPath.isNull()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return actualPath.isNull();
|
||||
}();
|
||||
bool askjava = BuildConfig.JAVA_DOWNLOADER_ENABLED && !javaRequired && !settings()->get("AutomaticJavaDownload").toBool() &&
|
||||
!settings()->get("AutomaticJavaSwitch").toBool() && !settings()->get("UserAskedAboutAutomaticJavaDownload").toBool();
|
||||
bool languageRequired = settings()->get("Language").toString().isEmpty();
|
||||
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
|
||||
bool validWidgets = m_themeManager->isValidApplicationTheme(settings()->get("ApplicationTheme").toString());
|
||||
bool validIcons = m_themeManager->isValidIconTheme(settings()->get("IconTheme").toString());
|
||||
bool login = !m_accounts->anyAccountIsValid() && capabilities() & Application::SupportsMSA;
|
||||
bool themeInterventionRequired = !validWidgets || !validIcons;
|
||||
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
|
||||
|
||||
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired || askjava || login;
|
||||
if (wizardRequired) {
|
||||
// set default theme after going into theme wizard
|
||||
if (!validIcons)
|
||||
settings()->set("IconTheme", QString("pe_colored"));
|
||||
if (!validWidgets)
|
||||
settings()->set("ApplicationTheme", QString("system"));
|
||||
if (!validWidgets) {
|
||||
#if defined(Q_OS_WIN32) && QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
|
||||
const QString style =
|
||||
QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark ? QStringLiteral("dark") : QStringLiteral("bright");
|
||||
#else
|
||||
const QString style = QStringLiteral("system");
|
||||
#endif
|
||||
|
||||
settings()->set("ApplicationTheme", style);
|
||||
}
|
||||
|
||||
m_themeManager->applyCurrentlySelectedTheme(true);
|
||||
|
||||
@ -1098,6 +1135,8 @@ bool Application::createSetupWizard()
|
||||
|
||||
if (javaRequired) {
|
||||
m_setupWizard->addPage(new JavaWizardPage(m_setupWizard));
|
||||
} else if (askjava) {
|
||||
m_setupWizard->addPage(new AutoJavaWizardPage(m_setupWizard));
|
||||
}
|
||||
|
||||
if (pasteInterventionRequired) {
|
||||
@ -1108,11 +1147,14 @@ bool Application::createSetupWizard()
|
||||
m_setupWizard->addPage(new ThemeWizardPage(m_setupWizard));
|
||||
}
|
||||
|
||||
if (login) {
|
||||
m_setupWizard->addPage(new LoginWizardPage(m_setupWizard));
|
||||
}
|
||||
connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished);
|
||||
m_setupWizard->show();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return wizardRequired || login;
|
||||
}
|
||||
|
||||
bool Application::updaterEnabled()
|
||||
@ -1149,6 +1191,9 @@ bool Application::event(QEvent* event)
|
||||
#endif
|
||||
|
||||
if (event->type() == QEvent::FileOpen) {
|
||||
if (!m_mainWindow) {
|
||||
showMainWindow(false);
|
||||
}
|
||||
auto ev = static_cast<QFileOpenEvent*>(event);
|
||||
m_mainWindow->processURLs({ ev->url() });
|
||||
}
|
||||
@ -1189,7 +1234,7 @@ void Application::performMainStartupAction()
|
||||
qDebug() << " Launching with account" << m_profileToUse;
|
||||
}
|
||||
|
||||
launch(inst, true, false, targetToJoin, accountToUse);
|
||||
launch(inst, !m_offline, false, targetToJoin, accountToUse, m_offlineName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1257,16 +1302,23 @@ Application::~Application()
|
||||
|
||||
void Application::messageReceived(const QByteArray& message)
|
||||
{
|
||||
if (status() != Initialized) {
|
||||
qDebug() << "Received message" << message << "while still initializing. It will be ignored.";
|
||||
return;
|
||||
}
|
||||
|
||||
ApplicationMessage received;
|
||||
received.parse(message);
|
||||
|
||||
auto& command = received.command;
|
||||
|
||||
if (status() != Initialized) {
|
||||
bool isLoginAtempt = false;
|
||||
if (command == "import") {
|
||||
QString url = received.args["url"];
|
||||
isLoginAtempt = !url.isEmpty() && normalizeImportUrl(url).scheme() == BuildConfig.LAUNCHER_APP_BINARY_NAME;
|
||||
}
|
||||
if (!isLoginAtempt) {
|
||||
qDebug() << "Received message" << message << "while still initializing. It will be ignored.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (command == "activate") {
|
||||
showMainWindow();
|
||||
} else if (command == "import") {
|
||||
@ -1275,12 +1327,17 @@ void Application::messageReceived(const QByteArray& message)
|
||||
qWarning() << "Received" << command << "message without a zip path/URL.";
|
||||
return;
|
||||
}
|
||||
if (!m_mainWindow) {
|
||||
showMainWindow(false);
|
||||
}
|
||||
m_mainWindow->processURLs({ normalizeImportUrl(url) });
|
||||
} else if (command == "launch") {
|
||||
QString id = received.args["id"];
|
||||
QString server = received.args["server"];
|
||||
QString world = received.args["world"];
|
||||
QString profile = received.args["profile"];
|
||||
bool offline = received.args["offline_enabled"] == "true";
|
||||
QString offlineName = received.args["offline_name"];
|
||||
|
||||
InstancePtr instance;
|
||||
if (!id.isEmpty()) {
|
||||
@ -1310,7 +1367,7 @@ void Application::messageReceived(const QByteArray& message)
|
||||
}
|
||||
}
|
||||
|
||||
launch(instance, true, false, serverObject, accountObject);
|
||||
launch(instance, !offline, false, serverObject, accountObject, offlineName);
|
||||
} else {
|
||||
qWarning() << "Received invalid message" << message;
|
||||
}
|
||||
@ -1348,11 +1405,17 @@ bool Application::openJsonEditor(const QString& filename)
|
||||
}
|
||||
}
|
||||
|
||||
bool Application::launch(InstancePtr instance, bool online, bool demo, MinecraftTarget::Ptr targetToJoin, MinecraftAccountPtr accountToUse)
|
||||
bool Application::launch(InstancePtr instance,
|
||||
bool online,
|
||||
bool demo,
|
||||
MinecraftTarget::Ptr targetToJoin,
|
||||
MinecraftAccountPtr accountToUse,
|
||||
const QString& offlineName)
|
||||
{
|
||||
if (m_updateRunning) {
|
||||
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
|
||||
} else if (instance->canLaunch()) {
|
||||
QMutexLocker locker(&m_instanceExtrasMutex);
|
||||
auto& extras = m_instanceExtras[instance->id()];
|
||||
auto window = extras.window;
|
||||
if (window) {
|
||||
@ -1368,6 +1431,7 @@ bool Application::launch(InstancePtr instance, bool online, bool demo, Minecraft
|
||||
controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get());
|
||||
controller->setTargetToJoin(targetToJoin);
|
||||
controller->setAccountToUse(accountToUse);
|
||||
controller->setOfflineName(offlineName);
|
||||
if (window) {
|
||||
controller->setParentWidget(window);
|
||||
} else if (m_mainWindow) {
|
||||
@ -1377,7 +1441,7 @@ bool Application::launch(InstancePtr instance, bool online, bool demo, Minecraft
|
||||
connect(controller.get(), &LaunchController::failed, this, &Application::controllerFailed);
|
||||
connect(controller.get(), &LaunchController::aborted, this, [this] { controllerFailed(tr("Aborted")); });
|
||||
addRunningInstance();
|
||||
controller->start();
|
||||
QMetaObject::invokeMethod(controller.get(), &Task::start, Qt::QueuedConnection);
|
||||
return true;
|
||||
} else if (instance->isRunning()) {
|
||||
showInstanceWindow(instance, "console");
|
||||
@ -1395,9 +1459,11 @@ bool Application::kill(InstancePtr instance)
|
||||
qWarning() << "Attempted to kill instance" << instance->id() << ", which isn't running.";
|
||||
return false;
|
||||
}
|
||||
QMutexLocker locker(&m_instanceExtrasMutex);
|
||||
auto& extras = m_instanceExtras[instance->id()];
|
||||
// NOTE: copy of the shared pointer keeps it alive
|
||||
auto controller = extras.controller;
|
||||
locker.unlock();
|
||||
if (controller) {
|
||||
return controller->abort();
|
||||
}
|
||||
@ -1451,12 +1517,14 @@ void Application::controllerSucceeded()
|
||||
if (!controller)
|
||||
return;
|
||||
auto id = controller->id();
|
||||
|
||||
QMutexLocker locker(&m_instanceExtrasMutex);
|
||||
auto& extras = m_instanceExtras[id];
|
||||
|
||||
// on success, do...
|
||||
if (controller->instance()->settings()->get("AutoCloseConsole").toBool()) {
|
||||
if (extras.window) {
|
||||
extras.window->close();
|
||||
QMetaObject::invokeMethod(extras.window, &QWidget::close, Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
extras.controller.reset();
|
||||
@ -1476,6 +1544,7 @@ void Application::controllerFailed(const QString& error)
|
||||
if (!controller)
|
||||
return;
|
||||
auto id = controller->id();
|
||||
QMutexLocker locker(&m_instanceExtrasMutex);
|
||||
auto& extras = m_instanceExtras[id];
|
||||
|
||||
// on failure, do... nothing
|
||||
@ -1533,6 +1602,7 @@ InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString pa
|
||||
if (!instance)
|
||||
return nullptr;
|
||||
auto id = instance->id();
|
||||
QMutexLocker locker(&m_instanceExtrasMutex);
|
||||
auto& extras = m_instanceExtras[id];
|
||||
auto& window = extras.window;
|
||||
|
||||
@ -1570,6 +1640,7 @@ void Application::on_windowClose()
|
||||
m_openWindows--;
|
||||
auto instWindow = qobject_cast<InstanceWindow*>(QObject::sender());
|
||||
if (instWindow) {
|
||||
QMutexLocker locker(&m_instanceExtrasMutex);
|
||||
auto& extras = m_instanceExtras[instWindow->instanceId()];
|
||||
extras.window = nullptr;
|
||||
if (extras.controller) {
|
||||
@ -1745,20 +1816,6 @@ QString Application::getUserAgentUncached()
|
||||
return BuildConfig.USER_AGENT_UNCACHED;
|
||||
}
|
||||
|
||||
int Application::suitableMaxMem()
|
||||
{
|
||||
float totalRAM = (float)Sys::getSystemRam() / (float)Sys::mebibyte;
|
||||
int maxMemoryAlloc;
|
||||
|
||||
// If totalRAM < 6GB, use (totalRAM / 1.5), else 4GB
|
||||
if (totalRAM < (4096 * 1.5))
|
||||
maxMemoryAlloc = (int)(totalRAM / 1.5);
|
||||
else
|
||||
maxMemoryAlloc = 4096;
|
||||
|
||||
return maxMemoryAlloc;
|
||||
}
|
||||
|
||||
bool Application::handleDataMigration(const QString& currentData,
|
||||
const QString& oldData,
|
||||
const QString& name,
|
||||
@ -1831,7 +1888,7 @@ bool Application::handleDataMigration(const QString& currentData,
|
||||
matcher->add(std::make_shared<SimplePrefixMatcher>("themes/"));
|
||||
|
||||
ProgressDialog diag;
|
||||
DataMigrationTask task(nullptr, oldData, currentData, matcher);
|
||||
DataMigrationTask task(oldData, currentData, matcher);
|
||||
if (diag.execWithTask(&task)) {
|
||||
qDebug() << "<> Migration succeeded";
|
||||
setDoNotMigrate();
|
||||
@ -1865,3 +1922,36 @@ QUrl Application::normalizeImportUrl(QString const& url)
|
||||
return QUrl::fromUserInput(url);
|
||||
}
|
||||
}
|
||||
|
||||
const QString Application::javaPath()
|
||||
{
|
||||
return m_settings->get("JavaDir").toString();
|
||||
}
|
||||
|
||||
void Application::addQSavePath(QString path)
|
||||
{
|
||||
QMutexLocker locker(&m_qsaveResourcesMutex);
|
||||
m_qsaveResources[path] = m_qsaveResources.value(path, 0) + 1;
|
||||
}
|
||||
|
||||
void Application::removeQSavePath(QString path)
|
||||
{
|
||||
QMutexLocker locker(&m_qsaveResourcesMutex);
|
||||
auto count = m_qsaveResources.value(path, 0) - 1;
|
||||
if (count <= 0) {
|
||||
m_qsaveResources.remove(path);
|
||||
} else {
|
||||
m_qsaveResources[path] = count;
|
||||
}
|
||||
}
|
||||
|
||||
bool Application::checkQSavePath(QString path)
|
||||
{
|
||||
QMutexLocker locker(&m_qsaveResourcesMutex);
|
||||
for (auto partialPath : m_qsaveResources.keys()) {
|
||||
if (path.startsWith(partialPath) && m_qsaveResources.value(partialPath, 0) > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <QDebug>
|
||||
#include <QFlag>
|
||||
#include <QIcon>
|
||||
#include <QMutex>
|
||||
#include <QUrl>
|
||||
#include <memory>
|
||||
|
||||
@ -81,6 +82,12 @@ class Index;
|
||||
#endif
|
||||
#define APPLICATION (static_cast<Application*>(QCoreApplication::instance()))
|
||||
|
||||
// Used for checking if is a test
|
||||
#if defined(APPLICATION_DYN)
|
||||
#undef APPLICATION_DYN
|
||||
#endif
|
||||
#define APPLICATION_DYN (dynamic_cast<Application*>(QCoreApplication::instance()))
|
||||
|
||||
class Application : public QApplication {
|
||||
// friends for the purpose of limiting access to deprecated stuff
|
||||
Q_OBJECT
|
||||
@ -105,7 +112,7 @@ class Application : public QApplication {
|
||||
|
||||
std::shared_ptr<SettingsObject> settings() const { return m_settings; }
|
||||
|
||||
qint64 timeSinceStart() const { return startTime.msecsTo(QDateTime::currentDateTime()); }
|
||||
qint64 timeSinceStart() const { return m_startTime.msecsTo(QDateTime::currentDateTime()); }
|
||||
|
||||
QIcon getThemedIcon(const QString& name);
|
||||
|
||||
@ -161,6 +168,9 @@ class Application : public QApplication {
|
||||
/// the data path the application is using
|
||||
const QString& dataRoot() { return m_dataPath; }
|
||||
|
||||
/// the java installed path the application is using
|
||||
const QString javaPath();
|
||||
|
||||
bool isPortable() { return m_portable; }
|
||||
|
||||
const Capabilities capabilities() { return m_capabilities; }
|
||||
@ -179,8 +189,6 @@ class Application : public QApplication {
|
||||
|
||||
void ShowGlobalSettings(class QWidget* parent, QString open_page = QString());
|
||||
|
||||
int suitableMaxMem();
|
||||
|
||||
bool updaterEnabled();
|
||||
QString updaterBinaryName();
|
||||
|
||||
@ -203,7 +211,8 @@ class Application : public QApplication {
|
||||
bool online = true,
|
||||
bool demo = false,
|
||||
MinecraftTarget::Ptr targetToJoin = nullptr,
|
||||
MinecraftAccountPtr accountToUse = nullptr);
|
||||
MinecraftAccountPtr accountToUse = nullptr,
|
||||
const QString& offlineName = QString());
|
||||
bool kill(InstancePtr instance);
|
||||
void closeCurrentWindow();
|
||||
|
||||
@ -228,7 +237,7 @@ class Application : public QApplication {
|
||||
bool shouldExitNow() const;
|
||||
|
||||
private:
|
||||
QDateTime startTime;
|
||||
QDateTime m_startTime;
|
||||
|
||||
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
||||
|
||||
@ -271,6 +280,7 @@ class Application : public QApplication {
|
||||
shared_qobject_ptr<LaunchController> controller;
|
||||
};
|
||||
std::map<QString, InstanceXtras> m_instanceExtras;
|
||||
mutable QMutex m_instanceExtrasMutex;
|
||||
|
||||
// main state variables
|
||||
size_t m_openWindows = 0;
|
||||
@ -292,8 +302,19 @@ class Application : public QApplication {
|
||||
QString m_serverToJoin;
|
||||
QString m_worldToJoin;
|
||||
QString m_profileToUse;
|
||||
bool m_offline = false;
|
||||
QString m_offlineName;
|
||||
bool m_liveCheck = false;
|
||||
QList<QUrl> m_urlsToImport;
|
||||
QString m_instanceIdToShowWindowOf;
|
||||
std::unique_ptr<QFile> logFile;
|
||||
|
||||
public:
|
||||
void addQSavePath(QString);
|
||||
void removeQSavePath(QString);
|
||||
bool checkQSavePath(QString);
|
||||
|
||||
private:
|
||||
QHash<QString, int> m_qsaveResources;
|
||||
mutable QMutex m_qsaveResourcesMutex;
|
||||
};
|
||||
|
@ -411,3 +411,8 @@ void BaseInstance::updateRuntimeContext()
|
||||
{
|
||||
// NOOP
|
||||
}
|
||||
|
||||
bool BaseInstance::isLegacy()
|
||||
{
|
||||
return traits().contains("legacyLaunch") || traits().contains("alphaLaunch");
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||
virtual void loadSpecificSettings() = 0;
|
||||
|
||||
/// returns a valid update task
|
||||
virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0;
|
||||
virtual QList<Task::Ptr> createUpdateTask() = 0;
|
||||
|
||||
/// returns a valid launcher (task container)
|
||||
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) = 0;
|
||||
@ -215,7 +215,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||
|
||||
virtual QString typeName() const = 0;
|
||||
|
||||
void updateRuntimeContext();
|
||||
virtual void updateRuntimeContext();
|
||||
RuntimeContext runtimeContext() const { return m_runtimeContext; }
|
||||
|
||||
bool hasVersionBroken() const { return m_hasBrokenVersion; }
|
||||
@ -269,6 +269,8 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||
bool removeLinkedInstanceId(const QString& id);
|
||||
bool isLinkedToInstanceId(const QString& id) const;
|
||||
|
||||
bool isLegacy();
|
||||
|
||||
protected:
|
||||
void changeStatus(Status newStatus);
|
||||
|
||||
|
@ -78,6 +78,14 @@ QVariant BaseVersionList::data(const QModelIndex& index, int role) const
|
||||
case TypeRole:
|
||||
return version->typeString();
|
||||
|
||||
case JavaMajorRole: {
|
||||
auto major = version->name();
|
||||
if (major.startsWith("java")) {
|
||||
major = "Java " + major.mid(4);
|
||||
}
|
||||
return major;
|
||||
}
|
||||
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
@ -110,6 +118,8 @@ QHash<int, QByteArray> BaseVersionList::roleNames() const
|
||||
roles.insert(TypeRole, "type");
|
||||
roles.insert(BranchRole, "branch");
|
||||
roles.insert(PathRole, "path");
|
||||
roles.insert(ArchitectureRole, "architecture");
|
||||
roles.insert(JavaNameRole, "javaName");
|
||||
roles.insert(CPUArchitectureRole, "architecture");
|
||||
roles.insert(JavaMajorRole, "javaMajor");
|
||||
return roles;
|
||||
}
|
||||
|
@ -48,7 +48,9 @@ class BaseVersionList : public QAbstractListModel {
|
||||
TypeRole,
|
||||
BranchRole,
|
||||
PathRole,
|
||||
ArchitectureRole,
|
||||
JavaNameRole,
|
||||
JavaMajorRole,
|
||||
CPUArchitectureRole,
|
||||
SortRole
|
||||
};
|
||||
using RoleList = QList<int>;
|
||||
|
@ -24,10 +24,13 @@ set(CORE_SOURCES
|
||||
NullInstance.h
|
||||
MMCZip.h
|
||||
MMCZip.cpp
|
||||
Untar.h
|
||||
Untar.cpp
|
||||
StringUtils.h
|
||||
StringUtils.cpp
|
||||
QVariantUtils.h
|
||||
RuntimeContext.h
|
||||
PSaveFile.h
|
||||
|
||||
# Basic instance manipulation tasks (derived from InstanceTask)
|
||||
InstanceCreationTask.h
|
||||
@ -158,8 +161,6 @@ set(LAUNCH_SOURCES
|
||||
launch/steps/PreLaunchCommand.h
|
||||
launch/steps/TextPrint.cpp
|
||||
launch/steps/TextPrint.h
|
||||
launch/steps/Update.cpp
|
||||
launch/steps/Update.h
|
||||
launch/steps/QuitAfterGameStop.cpp
|
||||
launch/steps/QuitAfterGameStop.h
|
||||
launch/steps/PrintServers.cpp
|
||||
@ -170,6 +171,8 @@ set(LAUNCH_SOURCES
|
||||
launch/LaunchTask.h
|
||||
launch/LogModel.cpp
|
||||
launch/LogModel.h
|
||||
launch/TaskStepWrapper.cpp
|
||||
launch/TaskStepWrapper.h
|
||||
)
|
||||
|
||||
# Old update system
|
||||
@ -205,6 +208,11 @@ set(ICONS_SOURCES
|
||||
|
||||
# Support for Minecraft instances and launch
|
||||
set(MINECRAFT_SOURCES
|
||||
|
||||
# Logging
|
||||
minecraft/Logging.h
|
||||
minecraft/Logging.cpp
|
||||
|
||||
# Minecraft support
|
||||
minecraft/auth/AccountData.cpp
|
||||
minecraft/auth/AccountData.h
|
||||
@ -272,6 +280,8 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/launch/ScanModFolders.h
|
||||
minecraft/launch/VerifyJavaInstall.cpp
|
||||
minecraft/launch/VerifyJavaInstall.h
|
||||
minecraft/launch/AutoInstallJava.cpp
|
||||
minecraft/launch/AutoInstallJava.h
|
||||
|
||||
minecraft/GradleSpecifier.h
|
||||
minecraft/MinecraftInstance.cpp
|
||||
@ -286,8 +296,6 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/ComponentUpdateTask.h
|
||||
minecraft/MinecraftLoadAndCheck.h
|
||||
minecraft/MinecraftLoadAndCheck.cpp
|
||||
minecraft/MinecraftUpdate.h
|
||||
minecraft/MinecraftUpdate.cpp
|
||||
minecraft/MojangVersionFormat.cpp
|
||||
minecraft/MojangVersionFormat.h
|
||||
minecraft/Rule.cpp
|
||||
@ -339,17 +347,14 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/mod/TexturePackFolderModel.h
|
||||
minecraft/mod/TexturePackFolderModel.cpp
|
||||
minecraft/mod/ShaderPackFolderModel.h
|
||||
minecraft/mod/tasks/BasicFolderLoadTask.h
|
||||
minecraft/mod/tasks/ModFolderLoadTask.h
|
||||
minecraft/mod/tasks/ModFolderLoadTask.cpp
|
||||
minecraft/mod/tasks/ResourceFolderLoadTask.h
|
||||
minecraft/mod/tasks/ResourceFolderLoadTask.cpp
|
||||
minecraft/mod/tasks/LocalModParseTask.h
|
||||
minecraft/mod/tasks/LocalModParseTask.cpp
|
||||
minecraft/mod/tasks/LocalModUpdateTask.h
|
||||
minecraft/mod/tasks/LocalModUpdateTask.cpp
|
||||
minecraft/mod/tasks/LocalResourceUpdateTask.h
|
||||
minecraft/mod/tasks/LocalResourceUpdateTask.cpp
|
||||
minecraft/mod/tasks/LocalDataPackParseTask.h
|
||||
minecraft/mod/tasks/LocalDataPackParseTask.cpp
|
||||
minecraft/mod/tasks/LocalResourcePackParseTask.h
|
||||
minecraft/mod/tasks/LocalResourcePackParseTask.cpp
|
||||
minecraft/mod/tasks/LocalTexturePackParseTask.h
|
||||
minecraft/mod/tasks/LocalTexturePackParseTask.cpp
|
||||
minecraft/mod/tasks/LocalShaderPackParseTask.h
|
||||
@ -419,8 +424,6 @@ set(SETTINGS_SOURCES
|
||||
set(JAVA_SOURCES
|
||||
java/JavaChecker.h
|
||||
java/JavaChecker.cpp
|
||||
java/JavaCheckerJob.h
|
||||
java/JavaCheckerJob.cpp
|
||||
java/JavaInstall.h
|
||||
java/JavaInstall.cpp
|
||||
java/JavaInstallList.h
|
||||
@ -429,6 +432,20 @@ set(JAVA_SOURCES
|
||||
java/JavaUtils.cpp
|
||||
java/JavaVersion.h
|
||||
java/JavaVersion.cpp
|
||||
|
||||
java/JavaMetadata.h
|
||||
java/JavaMetadata.cpp
|
||||
java/download/ArchiveDownloadTask.cpp
|
||||
java/download/ArchiveDownloadTask.h
|
||||
java/download/ManifestDownloadTask.cpp
|
||||
java/download/ManifestDownloadTask.h
|
||||
java/download/SymlinkTask.cpp
|
||||
java/download/SymlinkTask.h
|
||||
|
||||
ui/java/InstallJavaDialog.h
|
||||
ui/java/InstallJavaDialog.cpp
|
||||
ui/java/VersionList.h
|
||||
ui/java/VersionList.cpp
|
||||
)
|
||||
|
||||
set(TRANSLATIONS_SOURCES
|
||||
@ -651,6 +668,22 @@ ecm_qt_declare_logging_category(CORE_SOURCES
|
||||
EXPORT "${Launcher_Name}"
|
||||
)
|
||||
|
||||
ecm_qt_export_logging_category(
|
||||
IDENTIFIER instanceProfileC
|
||||
CATEGORY_NAME "launcher.instance.profile"
|
||||
DEFAULT_SEVERITY Debug
|
||||
DESCRIPTION "Profile actions"
|
||||
EXPORT "${Launcher_Name}"
|
||||
)
|
||||
|
||||
ecm_qt_export_logging_category(
|
||||
IDENTIFIER instanceProfileResolveC
|
||||
CATEGORY_NAME "launcher.instance.profile.resolve"
|
||||
DEFAULT_SEVERITY Debug
|
||||
DESCRIPTION "Profile component resolusion actions"
|
||||
EXPORT "${Launcher_Name}"
|
||||
)
|
||||
|
||||
ecm_qt_export_logging_category(
|
||||
IDENTIFIER taskLogC
|
||||
CATEGORY_NAME "launcher.task"
|
||||
@ -663,7 +696,7 @@ ecm_qt_export_logging_category(
|
||||
IDENTIFIER taskNetLogC
|
||||
CATEGORY_NAME "launcher.task.net"
|
||||
DEFAULT_SEVERITY Debug
|
||||
DESCRIPTION "task network action"
|
||||
DESCRIPTION "Task network action"
|
||||
EXPORT "${Launcher_Name}"
|
||||
)
|
||||
|
||||
@ -671,14 +704,14 @@ ecm_qt_export_logging_category(
|
||||
IDENTIFIER taskDownloadLogC
|
||||
CATEGORY_NAME "launcher.task.net.download"
|
||||
DEFAULT_SEVERITY Debug
|
||||
DESCRIPTION "task network download actions"
|
||||
DESCRIPTION "Task network download actions"
|
||||
EXPORT "${Launcher_Name}"
|
||||
)
|
||||
ecm_qt_export_logging_category(
|
||||
IDENTIFIER taskUploadLogC
|
||||
CATEGORY_NAME "launcher.task.net.upload"
|
||||
DEFAULT_SEVERITY Debug
|
||||
DESCRIPTION "task network upload actions"
|
||||
DESCRIPTION "Task network upload actions"
|
||||
EXPORT "${Launcher_Name}"
|
||||
)
|
||||
|
||||
@ -748,6 +781,8 @@ SET(LAUNCHER_SOURCES
|
||||
DataMigrationTask.cpp
|
||||
ApplicationMessage.h
|
||||
ApplicationMessage.cpp
|
||||
SysInfo.h
|
||||
SysInfo.cpp
|
||||
|
||||
# GUI - general utilities
|
||||
DesktopServices.h
|
||||
@ -775,7 +810,8 @@ SET(LAUNCHER_SOURCES
|
||||
resources/flat/flat.qrc
|
||||
resources/flat_white/flat_white.qrc
|
||||
resources/documents/documents.qrc
|
||||
../${Launcher_Branding_LogoQRC}
|
||||
resources/shaders/shaders.qrc
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_LogoQRC}"
|
||||
|
||||
# Icons
|
||||
icons/MMCIcon.h
|
||||
@ -786,8 +822,6 @@ SET(LAUNCHER_SOURCES
|
||||
# GUI - windows
|
||||
ui/GuiUtil.h
|
||||
ui/GuiUtil.cpp
|
||||
ui/ColorCache.h
|
||||
ui/ColorCache.cpp
|
||||
ui/MainWindow.h
|
||||
ui/MainWindow.cpp
|
||||
ui/InstanceWindow.h
|
||||
@ -811,6 +845,10 @@ SET(LAUNCHER_SOURCES
|
||||
ui/setupwizard/PasteWizardPage.h
|
||||
ui/setupwizard/ThemeWizardPage.cpp
|
||||
ui/setupwizard/ThemeWizardPage.h
|
||||
ui/setupwizard/AutoJavaWizardPage.cpp
|
||||
ui/setupwizard/AutoJavaWizardPage.h
|
||||
ui/setupwizard/LoginWizardPage.cpp
|
||||
ui/setupwizard/LoginWizardPage.h
|
||||
|
||||
# GUI - themes
|
||||
ui/themes/FusionTheme.cpp
|
||||
@ -873,7 +911,6 @@ SET(LAUNCHER_SOURCES
|
||||
ui/pages/instance/NotesPage.h
|
||||
ui/pages/instance/LogPage.cpp
|
||||
ui/pages/instance/LogPage.h
|
||||
ui/pages/instance/InstanceSettingsPage.cpp
|
||||
ui/pages/instance/InstanceSettingsPage.h
|
||||
ui/pages/instance/ScreenshotsPage.cpp
|
||||
ui/pages/instance/ScreenshotsPage.h
|
||||
@ -883,21 +920,22 @@ SET(LAUNCHER_SOURCES
|
||||
ui/pages/instance/ServersPage.h
|
||||
ui/pages/instance/WorldListPage.cpp
|
||||
ui/pages/instance/WorldListPage.h
|
||||
ui/pages/instance/McClient.cpp
|
||||
ui/pages/instance/McClient.h
|
||||
ui/pages/instance/McResolver.cpp
|
||||
ui/pages/instance/McResolver.h
|
||||
ui/pages/instance/ServerPingTask.cpp
|
||||
ui/pages/instance/ServerPingTask.h
|
||||
|
||||
# GUI - global settings pages
|
||||
ui/pages/global/AccountListPage.cpp
|
||||
ui/pages/global/AccountListPage.h
|
||||
ui/pages/global/CustomCommandsPage.cpp
|
||||
ui/pages/global/CustomCommandsPage.h
|
||||
ui/pages/global/EnvironmentVariablesPage.cpp
|
||||
ui/pages/global/EnvironmentVariablesPage.h
|
||||
ui/pages/global/ExternalToolsPage.cpp
|
||||
ui/pages/global/ExternalToolsPage.h
|
||||
ui/pages/global/JavaPage.cpp
|
||||
ui/pages/global/JavaPage.h
|
||||
ui/pages/global/LanguagePage.cpp
|
||||
ui/pages/global/LanguagePage.h
|
||||
ui/pages/global/MinecraftPage.cpp
|
||||
ui/pages/global/MinecraftPage.h
|
||||
ui/pages/global/LauncherPage.cpp
|
||||
ui/pages/global/LauncherPage.h
|
||||
@ -933,6 +971,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/pages/modplatform/DataPackPage.cpp
|
||||
ui/pages/modplatform/DataPackModel.cpp
|
||||
|
||||
ui/pages/modplatform/ModpackProviderBasePage.h
|
||||
|
||||
ui/pages/modplatform/atlauncher/AtlFilterModel.cpp
|
||||
ui/pages/modplatform/atlauncher/AtlFilterModel.h
|
||||
ui/pages/modplatform/atlauncher/AtlListModel.cpp
|
||||
@ -995,8 +1035,6 @@ SET(LAUNCHER_SOURCES
|
||||
ui/dialogs/CopyInstanceDialog.h
|
||||
ui/dialogs/CustomMessageBox.cpp
|
||||
ui/dialogs/CustomMessageBox.h
|
||||
ui/dialogs/EditAccountDialog.cpp
|
||||
ui/dialogs/EditAccountDialog.h
|
||||
ui/dialogs/ExportInstanceDialog.cpp
|
||||
ui/dialogs/ExportInstanceDialog.h
|
||||
ui/dialogs/ExportPackDialog.cpp
|
||||
@ -1033,14 +1071,21 @@ SET(LAUNCHER_SOURCES
|
||||
ui/dialogs/BlockedModsDialog.h
|
||||
ui/dialogs/ChooseProviderDialog.h
|
||||
ui/dialogs/ChooseProviderDialog.cpp
|
||||
ui/dialogs/ModUpdateDialog.cpp
|
||||
ui/dialogs/ModUpdateDialog.h
|
||||
ui/dialogs/ResourceUpdateDialog.cpp
|
||||
ui/dialogs/ResourceUpdateDialog.h
|
||||
ui/dialogs/InstallLoaderDialog.cpp
|
||||
ui/dialogs/InstallLoaderDialog.h
|
||||
|
||||
ui/dialogs/skins/SkinManageDialog.cpp
|
||||
ui/dialogs/skins/SkinManageDialog.h
|
||||
|
||||
ui/dialogs/skins/draw/SkinOpenGLWindow.h
|
||||
ui/dialogs/skins/draw/SkinOpenGLWindow.cpp
|
||||
ui/dialogs/skins/draw/Scene.h
|
||||
ui/dialogs/skins/draw/Scene.cpp
|
||||
ui/dialogs/skins/draw/BoxGeometry.h
|
||||
ui/dialogs/skins/draw/BoxGeometry.cpp
|
||||
|
||||
# GUI - widgets
|
||||
ui/widgets/CheckComboBox.cpp
|
||||
ui/widgets/CheckComboBox.h
|
||||
@ -1050,14 +1095,12 @@ SET(LAUNCHER_SOURCES
|
||||
ui/widgets/CustomCommands.h
|
||||
ui/widgets/EnvironmentVariables.cpp
|
||||
ui/widgets/EnvironmentVariables.h
|
||||
ui/widgets/DropLabel.cpp
|
||||
ui/widgets/DropLabel.h
|
||||
ui/widgets/FocusLineEdit.cpp
|
||||
ui/widgets/FocusLineEdit.h
|
||||
ui/widgets/IconLabel.cpp
|
||||
ui/widgets/IconLabel.h
|
||||
ui/widgets/JavaSettingsWidget.cpp
|
||||
ui/widgets/JavaSettingsWidget.h
|
||||
ui/widgets/JavaWizardWidget.cpp
|
||||
ui/widgets/JavaWizardWidget.h
|
||||
ui/widgets/LabeledToolButton.cpp
|
||||
ui/widgets/LabeledToolButton.h
|
||||
ui/widgets/LanguageSelectionWidget.cpp
|
||||
@ -1093,6 +1136,10 @@ SET(LAUNCHER_SOURCES
|
||||
ui/widgets/WideBar.cpp
|
||||
ui/widgets/ThemeCustomizationWidget.h
|
||||
ui/widgets/ThemeCustomizationWidget.cpp
|
||||
ui/widgets/MinecraftSettingsWidget.h
|
||||
ui/widgets/MinecraftSettingsWidget.cpp
|
||||
ui/widgets/JavaSettingsWidget.h
|
||||
ui/widgets/JavaSettingsWidget.cpp
|
||||
|
||||
# GUI - instance group view
|
||||
ui/instanceview/InstanceProxyModel.cpp
|
||||
@ -1128,13 +1175,14 @@ endif()
|
||||
qt_wrap_ui(LAUNCHER_UI
|
||||
ui/MainWindow.ui
|
||||
ui/setupwizard/PasteWizardPage.ui
|
||||
ui/setupwizard/AutoJavaWizardPage.ui
|
||||
ui/setupwizard/LoginWizardPage.ui
|
||||
ui/setupwizard/ThemeWizardPage.ui
|
||||
ui/pages/global/AccountListPage.ui
|
||||
ui/pages/global/JavaPage.ui
|
||||
ui/pages/global/LauncherPage.ui
|
||||
ui/pages/global/APIPage.ui
|
||||
ui/pages/global/ProxyPage.ui
|
||||
ui/pages/global/MinecraftPage.ui
|
||||
ui/pages/global/ExternalToolsPage.ui
|
||||
ui/pages/instance/ExternalResourcesPage.ui
|
||||
ui/pages/instance/NotesPage.ui
|
||||
@ -1142,7 +1190,6 @@ qt_wrap_ui(LAUNCHER_UI
|
||||
ui/pages/instance/ServersPage.ui
|
||||
ui/pages/instance/GameOptionsPage.ui
|
||||
ui/pages/instance/OtherLogsPage.ui
|
||||
ui/pages/instance/InstanceSettingsPage.ui
|
||||
ui/pages/instance/VersionPage.ui
|
||||
ui/pages/instance/ManagedPackPage.ui
|
||||
ui/pages/instance/WorldListPage.ui
|
||||
@ -1165,6 +1212,8 @@ qt_wrap_ui(LAUNCHER_UI
|
||||
ui/widgets/ModFilterWidget.ui
|
||||
ui/widgets/SubTaskProgressBar.ui
|
||||
ui/widgets/ThemeCustomizationWidget.ui
|
||||
ui/widgets/MinecraftSettingsWidget.ui
|
||||
ui/widgets/JavaSettingsWidget.ui
|
||||
ui/dialogs/CopyInstanceDialog.ui
|
||||
ui/dialogs/ProfileSetupDialog.ui
|
||||
ui/dialogs/ProgressDialog.ui
|
||||
@ -1180,12 +1229,10 @@ qt_wrap_ui(LAUNCHER_UI
|
||||
ui/dialogs/MSALoginDialog.ui
|
||||
ui/dialogs/OfflineLoginDialog.ui
|
||||
ui/dialogs/AboutDialog.ui
|
||||
ui/dialogs/EditAccountDialog.ui
|
||||
ui/dialogs/ReviewMessageBox.ui
|
||||
ui/dialogs/ScrollMessageBox.ui
|
||||
ui/dialogs/BlockedModsDialog.ui
|
||||
ui/dialogs/ChooseProviderDialog.ui
|
||||
|
||||
ui/dialogs/skins/SkinManageDialog.ui
|
||||
)
|
||||
|
||||
@ -1210,7 +1257,8 @@ qt_add_resources(LAUNCHER_RESOURCES
|
||||
resources/iOS/iOS.qrc
|
||||
resources/flat/flat.qrc
|
||||
resources/documents/documents.qrc
|
||||
../${Launcher_Branding_LogoQRC}
|
||||
resources/shaders/shaders.qrc
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_LogoQRC}"
|
||||
)
|
||||
|
||||
qt_wrap_ui(PRISMUPDATER_UI
|
||||
@ -1228,14 +1276,10 @@ include(CompilerWarnings)
|
||||
|
||||
# Add executable
|
||||
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES})
|
||||
if(BUILD_TESTING)
|
||||
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_TEST)
|
||||
endif()
|
||||
set_project_warnings(Launcher_logic
|
||||
"${Launcher_MSVC_WARNINGS}"
|
||||
"${Launcher_CLANG_WARNINGS}"
|
||||
"${Launcher_GCC_WARNINGS}")
|
||||
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
|
||||
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
|
||||
target_link_libraries(Launcher_logic
|
||||
@ -1264,6 +1308,8 @@ target_link_libraries(Launcher_logic
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
Qt${QT_VERSION_MAJOR}::NetworkAuth
|
||||
Qt${QT_VERSION_MAJOR}::OpenGL
|
||||
${Launcher_QT_DBUS}
|
||||
${Launcher_QT_LIBS}
|
||||
)
|
||||
target_link_libraries(Launcher_logic
|
||||
@ -1272,6 +1318,10 @@ target_link_libraries(Launcher_logic
|
||||
LocalPeer
|
||||
Launcher_rainbow
|
||||
)
|
||||
if (TARGET ${Launcher_QT_DBUS})
|
||||
add_compile_definitions(WITH_QTDBUS)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(CMAKE_MACOSX_RPATH 1)
|
||||
set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/")
|
||||
|
@ -12,11 +12,8 @@
|
||||
|
||||
#include <QtConcurrent>
|
||||
|
||||
DataMigrationTask::DataMigrationTask(QObject* parent,
|
||||
const QString& sourcePath,
|
||||
const QString& targetPath,
|
||||
const IPathMatcher::Ptr pathMatcher)
|
||||
: Task(parent), m_sourcePath(sourcePath), m_targetPath(targetPath), m_pathMatcher(pathMatcher), m_copy(sourcePath, targetPath)
|
||||
DataMigrationTask::DataMigrationTask(const QString& sourcePath, const QString& targetPath, const IPathMatcher::Ptr pathMatcher)
|
||||
: Task(), m_sourcePath(sourcePath), m_targetPath(targetPath), m_pathMatcher(pathMatcher), m_copy(sourcePath, targetPath)
|
||||
{
|
||||
m_copy.matcher(m_pathMatcher.get()).whitelist(true);
|
||||
}
|
||||
@ -27,7 +24,7 @@ void DataMigrationTask::executeTask()
|
||||
|
||||
// 1. Scan
|
||||
// Check how many files we gotta copy
|
||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] {
|
||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] {
|
||||
return m_copy(true); // dry run to collect amount of files
|
||||
});
|
||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::dryRunFinished);
|
||||
@ -60,7 +57,7 @@ void DataMigrationTask::dryRunFinished()
|
||||
setProgress(m_copy.totalCopied(), m_toCopy);
|
||||
setStatus(tr("Copying %1…").arg(shortenedName));
|
||||
});
|
||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] {
|
||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] {
|
||||
return m_copy(false); // actually copy now
|
||||
});
|
||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::copyFinished);
|
||||
|
@ -18,7 +18,7 @@
|
||||
class DataMigrationTask : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DataMigrationTask(QObject* parent, const QString& sourcePath, const QString& targetPath, IPathMatcher::Ptr pathmatcher);
|
||||
explicit DataMigrationTask(const QString& sourcePath, const QString& targetPath, IPathMatcher::Ptr pathmatcher);
|
||||
~DataMigrationTask() override = default;
|
||||
|
||||
protected:
|
||||
|
@ -40,12 +40,11 @@
|
||||
#include <QFileSystemModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QStack>
|
||||
#include <algorithm>
|
||||
#include "FileSystem.h"
|
||||
#include "SeparatorPrefixTree.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
FileIgnoreProxy::FileIgnoreProxy(QString root, QObject* parent) : QSortFilterProxyModel(parent), root(root) {}
|
||||
FileIgnoreProxy::FileIgnoreProxy(QString root, QObject* parent) : QSortFilterProxyModel(parent), m_root(root) {}
|
||||
// NOTE: Sadly, we have to do sorting ourselves.
|
||||
bool FileIgnoreProxy::lessThan(const QModelIndex& left, const QModelIndex& right) const
|
||||
{
|
||||
@ -104,10 +103,10 @@ QVariant FileIgnoreProxy::data(const QModelIndex& index, int role) const
|
||||
if (index.column() == 0 && role == Qt::CheckStateRole) {
|
||||
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
|
||||
auto blockedPath = relPath(fsm->filePath(sourceIndex));
|
||||
auto cover = blocked.cover(blockedPath);
|
||||
auto cover = m_blocked.cover(blockedPath);
|
||||
if (!cover.isNull()) {
|
||||
return QVariant(Qt::Unchecked);
|
||||
} else if (blocked.exists(blockedPath)) {
|
||||
} else if (m_blocked.exists(blockedPath)) {
|
||||
return QVariant(Qt::PartiallyChecked);
|
||||
} else {
|
||||
return QVariant(Qt::Checked);
|
||||
@ -130,7 +129,7 @@ bool FileIgnoreProxy::setData(const QModelIndex& index, const QVariant& value, i
|
||||
|
||||
QString FileIgnoreProxy::relPath(const QString& path) const
|
||||
{
|
||||
return QDir(root).relativeFilePath(path);
|
||||
return QDir(m_root).relativeFilePath(path);
|
||||
}
|
||||
|
||||
bool FileIgnoreProxy::setFilterState(QModelIndex index, Qt::CheckState state)
|
||||
@ -146,18 +145,18 @@ bool FileIgnoreProxy::setFilterState(QModelIndex index, Qt::CheckState state)
|
||||
bool changed = false;
|
||||
if (state == Qt::Unchecked) {
|
||||
// blocking a path
|
||||
auto& node = blocked.insert(blockedPath);
|
||||
auto& node = m_blocked.insert(blockedPath);
|
||||
// get rid of all blocked nodes below
|
||||
node.clear();
|
||||
changed = true;
|
||||
} else if (state == Qt::Checked || state == Qt::PartiallyChecked) {
|
||||
if (!blocked.remove(blockedPath)) {
|
||||
auto cover = blocked.cover(blockedPath);
|
||||
if (!m_blocked.remove(blockedPath)) {
|
||||
auto cover = m_blocked.cover(blockedPath);
|
||||
qDebug() << "Blocked by cover" << cover;
|
||||
// uncover
|
||||
blocked.remove(cover);
|
||||
m_blocked.remove(cover);
|
||||
// block all contents, except for any cover
|
||||
QModelIndex rootIndex = fsm->index(FS::PathCombine(root, cover));
|
||||
QModelIndex rootIndex = fsm->index(FS::PathCombine(m_root, cover));
|
||||
QModelIndex doing = rootIndex;
|
||||
int row = 0;
|
||||
QStack<QModelIndex> todo;
|
||||
@ -179,7 +178,7 @@ bool FileIgnoreProxy::setFilterState(QModelIndex index, Qt::CheckState state)
|
||||
todo.push(node);
|
||||
} else {
|
||||
// or just block this one.
|
||||
blocked.insert(relpath);
|
||||
m_blocked.insert(relpath);
|
||||
}
|
||||
row++;
|
||||
}
|
||||
@ -229,7 +228,7 @@ bool FileIgnoreProxy::shouldExpand(QModelIndex index)
|
||||
return false;
|
||||
}
|
||||
auto blockedPath = relPath(fsm->filePath(sourceIndex));
|
||||
auto found = blocked.find(blockedPath);
|
||||
auto found = m_blocked.find(blockedPath);
|
||||
if (found) {
|
||||
return !found->leaf();
|
||||
}
|
||||
@ -239,8 +238,8 @@ bool FileIgnoreProxy::shouldExpand(QModelIndex index)
|
||||
void FileIgnoreProxy::setBlockedPaths(QStringList paths)
|
||||
{
|
||||
beginResetModel();
|
||||
blocked.clear();
|
||||
blocked.insert(paths);
|
||||
m_blocked.clear();
|
||||
m_blocked.insert(paths);
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
@ -272,5 +271,30 @@ bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const
|
||||
|
||||
bool FileIgnoreProxy::filterFile(const QString& fileName) const
|
||||
{
|
||||
return blocked.covers(fileName) || ignoreFile(QFileInfo(QDir(root), fileName));
|
||||
return m_blocked.covers(fileName) || ignoreFile(QFileInfo(QDir(m_root), fileName));
|
||||
}
|
||||
|
||||
void FileIgnoreProxy::loadBlockedPathsFromFile(const QString& fileName)
|
||||
{
|
||||
QFile ignoreFile(fileName);
|
||||
if (!ignoreFile.open(QIODevice::ReadOnly)) {
|
||||
return;
|
||||
}
|
||||
auto ignoreData = ignoreFile.readAll();
|
||||
auto string = QString::fromUtf8(ignoreData);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
setBlockedPaths(string.split('\n', Qt::SkipEmptyParts));
|
||||
#else
|
||||
setBlockedPaths(string.split('\n', QString::SkipEmptyParts));
|
||||
#endif
|
||||
}
|
||||
|
||||
void FileIgnoreProxy::saveBlockedPathsToFile(const QString& fileName)
|
||||
{
|
||||
auto ignoreData = blockedPaths().toStringList().join('\n').toUtf8();
|
||||
try {
|
||||
FS::write(fileName, ignoreData);
|
||||
} catch (const Exception& e) {
|
||||
qWarning() << e.cause();
|
||||
}
|
||||
}
|
||||
|
@ -61,8 +61,8 @@ class FileIgnoreProxy : public QSortFilterProxyModel {
|
||||
|
||||
void setBlockedPaths(QStringList paths);
|
||||
|
||||
inline const SeparatorPrefixTree<'/'>& blockedPaths() const { return blocked; }
|
||||
inline SeparatorPrefixTree<'/'>& blockedPaths() { return blocked; }
|
||||
inline const SeparatorPrefixTree<'/'>& blockedPaths() const { return m_blocked; }
|
||||
inline SeparatorPrefixTree<'/'>& blockedPaths() { return m_blocked; }
|
||||
|
||||
// list of file names that need to be removed completely from model
|
||||
inline QStringList& ignoreFilesWithName() { return m_ignoreFiles; }
|
||||
@ -71,6 +71,10 @@ class FileIgnoreProxy : public QSortFilterProxyModel {
|
||||
|
||||
bool filterFile(const QString& fileName) const;
|
||||
|
||||
void loadBlockedPathsFromFile(const QString& fileName);
|
||||
|
||||
void saveBlockedPathsToFile(const QString& fileName);
|
||||
|
||||
protected:
|
||||
bool filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const;
|
||||
bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
|
||||
@ -78,8 +82,8 @@ class FileIgnoreProxy : public QSortFilterProxyModel {
|
||||
bool ignoreFile(QFileInfo file) const;
|
||||
|
||||
private:
|
||||
const QString root;
|
||||
SeparatorPrefixTree<'/'> blocked;
|
||||
const QString m_root;
|
||||
SeparatorPrefixTree<'/'> m_blocked;
|
||||
QStringList m_ignoreFiles;
|
||||
SeparatorPrefixTree<'/'> m_ignoreFilePaths;
|
||||
};
|
||||
|
@ -45,7 +45,6 @@
|
||||
#include <QDirIterator>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QSaveFile>
|
||||
#include <QStandardPaths>
|
||||
#include <QStorageInfo>
|
||||
#include <QTextStream>
|
||||
@ -54,6 +53,7 @@
|
||||
#include <system_error>
|
||||
|
||||
#include "DesktopServices.h"
|
||||
#include "PSaveFile.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
@ -191,8 +191,8 @@ void ensureExists(const QDir& dir)
|
||||
void write(const QString& filename, const QByteArray& data)
|
||||
{
|
||||
ensureExists(QFileInfo(filename).dir());
|
||||
QSaveFile file(filename);
|
||||
if (!file.open(QSaveFile::WriteOnly)) {
|
||||
PSaveFile file(filename);
|
||||
if (!file.open(PSaveFile::WriteOnly)) {
|
||||
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
|
||||
}
|
||||
if (data.size() != file.write(data)) {
|
||||
@ -213,8 +213,8 @@ void appendSafe(const QString& filename, const QByteArray& data)
|
||||
buffer = QByteArray();
|
||||
}
|
||||
buffer.append(data);
|
||||
QSaveFile file(filename);
|
||||
if (!file.open(QSaveFile::WriteOnly)) {
|
||||
PSaveFile file(filename);
|
||||
if (!file.open(PSaveFile::WriteOnly)) {
|
||||
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
|
||||
}
|
||||
if (buffer.size() != file.write(buffer)) {
|
||||
@ -276,6 +276,9 @@ bool ensureFolderPathExists(const QFileInfo folderPath)
|
||||
{
|
||||
QDir dir;
|
||||
QString ensuredPath = folderPath.filePath();
|
||||
if (folderPath.exists())
|
||||
return true;
|
||||
|
||||
bool success = dir.mkpath(ensuredPath);
|
||||
return success;
|
||||
}
|
||||
@ -338,7 +341,7 @@ bool copy::operator()(const QString& offset, bool dryRun)
|
||||
opt |= copy_opts::overwrite_existing;
|
||||
|
||||
// Function that'll do the actual copying
|
||||
auto copy_file = [&](QString src_path, QString relative_dst_path) {
|
||||
auto copy_file = [this, dryRun, src, dst, opt, &err](QString src_path, QString relative_dst_path) {
|
||||
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
|
||||
return;
|
||||
|
||||
@ -425,7 +428,7 @@ void create_link::make_link_list(const QString& offset)
|
||||
m_recursive = true;
|
||||
|
||||
// Function that'll do the actual linking
|
||||
auto link_file = [&](QString src_path, QString relative_dst_path) {
|
||||
auto link_file = [this, dst](QString src_path, QString relative_dst_path) {
|
||||
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist)) {
|
||||
qDebug() << "path" << relative_dst_path << "in black list or not in whitelist";
|
||||
return;
|
||||
@ -520,7 +523,7 @@ void create_link::runPrivileged(const QString& offset)
|
||||
|
||||
QString serverName = BuildConfig.LAUNCHER_APP_BINARY_NAME + "_filelink_server" + StringUtils::getRandomAlphaNumeric();
|
||||
|
||||
connect(&m_linkServer, &QLocalServer::newConnection, this, [&]() {
|
||||
connect(&m_linkServer, &QLocalServer::newConnection, this, [this, &gotResults]() {
|
||||
qDebug() << "Client connected, sending out pairs";
|
||||
// construct block of data to send
|
||||
QByteArray block;
|
||||
@ -602,7 +605,7 @@ void create_link::runPrivileged(const QString& offset)
|
||||
}
|
||||
|
||||
ExternalLinkFileProcess* linkFileProcess = new ExternalLinkFileProcess(serverName, m_useHardLinks, this);
|
||||
connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [&]() { emit finishedPrivileged(gotResults); });
|
||||
connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [this, gotResults]() { emit finishedPrivileged(gotResults); });
|
||||
connect(linkFileProcess, &ExternalLinkFileProcess::finished, linkFileProcess, &QObject::deleteLater);
|
||||
|
||||
linkFileProcess->start();
|
||||
@ -918,6 +921,10 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
|
||||
if (destination.isEmpty()) {
|
||||
destination = PathCombine(getDesktopDir(), RemoveInvalidFilenameChars(name));
|
||||
}
|
||||
if (!ensureFilePathExists(destination)) {
|
||||
qWarning() << "Destination path can't be created!";
|
||||
return false;
|
||||
}
|
||||
#if defined(Q_OS_MACOS)
|
||||
// Create the Application
|
||||
QDir applicationDirectory =
|
||||
@ -964,8 +971,7 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
|
||||
if (!args.empty())
|
||||
argstring = " \"" + args.join("\" \"") + "\"";
|
||||
|
||||
stream << "#!/bin/bash"
|
||||
<< "\n";
|
||||
stream << "#!/bin/bash" << "\n";
|
||||
stream << "\"" << target << "\" " << argstring << "\n";
|
||||
|
||||
stream.flush();
|
||||
@ -1009,12 +1015,9 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
|
||||
if (!args.empty())
|
||||
argstring = " '" + args.join("' '") + "'";
|
||||
|
||||
stream << "[Desktop Entry]"
|
||||
<< "\n";
|
||||
stream << "Type=Application"
|
||||
<< "\n";
|
||||
stream << "Categories=Game;ActionGame;AdventureGame;Simulation"
|
||||
<< "\n";
|
||||
stream << "[Desktop Entry]" << "\n";
|
||||
stream << "Type=Application" << "\n";
|
||||
stream << "Categories=Game;ActionGame;AdventureGame;Simulation" << "\n";
|
||||
stream << "Exec=\"" << target.toLocal8Bit() << "\"" << argstring.toLocal8Bit() << "\n";
|
||||
stream << "Name=" << name.toLocal8Bit() << "\n";
|
||||
if (!icon.isEmpty()) {
|
||||
@ -1292,7 +1295,7 @@ bool clone::operator()(const QString& offset, bool dryRun)
|
||||
std::error_code err;
|
||||
|
||||
// Function that'll do the actual cloneing
|
||||
auto cloneFile = [&](QString src_path, QString relative_dst_path) {
|
||||
auto cloneFile = [this, dryRun, dst, &err](QString src_path, QString relative_dst_path) {
|
||||
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
|
||||
return;
|
||||
|
||||
|
@ -1,16 +1,12 @@
|
||||
#include "Filter.h"
|
||||
|
||||
Filter::~Filter() {}
|
||||
|
||||
ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern) {}
|
||||
ContainsFilter::~ContainsFilter() {}
|
||||
bool ContainsFilter::accepts(const QString& value)
|
||||
{
|
||||
return value.contains(pattern);
|
||||
}
|
||||
|
||||
ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern) {}
|
||||
ExactFilter::~ExactFilter() {}
|
||||
bool ExactFilter::accepts(const QString& value)
|
||||
{
|
||||
return value == pattern;
|
||||
@ -27,10 +23,15 @@ RegexpFilter::RegexpFilter(const QString& regexp, bool invert) : invert(invert)
|
||||
pattern.setPattern(regexp);
|
||||
pattern.optimize();
|
||||
}
|
||||
RegexpFilter::~RegexpFilter() {}
|
||||
bool RegexpFilter::accepts(const QString& value)
|
||||
{
|
||||
auto match = pattern.match(value);
|
||||
bool matched = match.hasMatch();
|
||||
return invert ? (!matched) : (matched);
|
||||
}
|
||||
|
||||
ExactListFilter::ExactListFilter(const QStringList& pattern) : m_pattern(pattern) {}
|
||||
bool ExactListFilter::accepts(const QString& value)
|
||||
{
|
||||
return m_pattern.isEmpty() || m_pattern.contains(value);
|
||||
}
|
@ -5,14 +5,14 @@
|
||||
|
||||
class Filter {
|
||||
public:
|
||||
virtual ~Filter();
|
||||
virtual ~Filter() = default;
|
||||
virtual bool accepts(const QString& value) = 0;
|
||||
};
|
||||
|
||||
class ContainsFilter : public Filter {
|
||||
public:
|
||||
ContainsFilter(const QString& pattern);
|
||||
virtual ~ContainsFilter();
|
||||
virtual ~ContainsFilter() = default;
|
||||
bool accepts(const QString& value) override;
|
||||
|
||||
private:
|
||||
@ -22,7 +22,7 @@ class ContainsFilter : public Filter {
|
||||
class ExactFilter : public Filter {
|
||||
public:
|
||||
ExactFilter(const QString& pattern);
|
||||
virtual ~ExactFilter();
|
||||
virtual ~ExactFilter() = default;
|
||||
bool accepts(const QString& value) override;
|
||||
|
||||
private:
|
||||
@ -32,7 +32,7 @@ class ExactFilter : public Filter {
|
||||
class ExactIfPresentFilter : public Filter {
|
||||
public:
|
||||
ExactIfPresentFilter(const QString& pattern);
|
||||
~ExactIfPresentFilter() override = default;
|
||||
virtual ~ExactIfPresentFilter() override = default;
|
||||
bool accepts(const QString& value) override;
|
||||
|
||||
private:
|
||||
@ -42,10 +42,20 @@ class ExactIfPresentFilter : public Filter {
|
||||
class RegexpFilter : public Filter {
|
||||
public:
|
||||
RegexpFilter(const QString& regexp, bool invert);
|
||||
virtual ~RegexpFilter();
|
||||
virtual ~RegexpFilter() = default;
|
||||
bool accepts(const QString& value) override;
|
||||
|
||||
private:
|
||||
QRegularExpression pattern;
|
||||
bool invert = false;
|
||||
};
|
||||
|
||||
class ExactListFilter : public Filter {
|
||||
public:
|
||||
ExactListFilter(const QStringList& pattern = {});
|
||||
virtual ~ExactListFilter() = default;
|
||||
bool accepts(const QString& value) override;
|
||||
|
||||
private:
|
||||
QStringList m_pattern;
|
||||
};
|
||||
|
@ -91,7 +91,7 @@ void InstanceCopyTask::executeTask()
|
||||
QEventLoop loop;
|
||||
bool got_priv_results = false;
|
||||
|
||||
connect(&folderLink, &FS::create_link::finishedPrivileged, this, [&](bool gotResults) {
|
||||
connect(&folderLink, &FS::create_link::finishedPrivileged, this, [&got_priv_results, &loop](bool gotResults) {
|
||||
if (!gotResults) {
|
||||
qDebug() << "Privileged run exited without results!";
|
||||
}
|
||||
@ -173,7 +173,11 @@ void InstanceCopyTask::copyFinished()
|
||||
allowed_symlinks_file
|
||||
.filePath()); // we dont want to modify the original. also make sure the resulting file is not itself a link.
|
||||
|
||||
try {
|
||||
FS::write(allowed_symlinks_file.filePath(), allowed_symlinks);
|
||||
} catch (const FS::FileSystemException& e) {
|
||||
qCritical() << "Failed to write symlink :" << e.cause();
|
||||
}
|
||||
}
|
||||
|
||||
emitSucceeded();
|
||||
|
@ -38,22 +38,29 @@ void InstanceCreationTask::executeTask()
|
||||
// files scheduled to, and we'd better not let the user abort in the middle of it, since it'd
|
||||
// put the instance in an invalid state.
|
||||
if (shouldOverride()) {
|
||||
bool deleteFailed = false;
|
||||
|
||||
setAbortable(false);
|
||||
setStatus(tr("Removing old conflicting files..."));
|
||||
qDebug() << "Removing old files";
|
||||
|
||||
for (auto path : m_files_to_remove) {
|
||||
for (const QString& path : m_files_to_remove) {
|
||||
if (!QFile::exists(path))
|
||||
continue;
|
||||
|
||||
qDebug() << "Removing" << path;
|
||||
if (!FS::deletePath(path)) {
|
||||
qCritical() << "Couldn't remove the old conflicting files.";
|
||||
|
||||
if (!QFile::remove(path)) {
|
||||
qCritical() << "Could not remove" << path;
|
||||
deleteFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteFailed) {
|
||||
emitFailed(tr("Failed to remove old conflicting files."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_abort)
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
|
@ -69,9 +69,11 @@ bool InstanceImportTask::abort()
|
||||
if (!canAbort())
|
||||
return false;
|
||||
|
||||
if (task)
|
||||
task->abort();
|
||||
return Task::abort();
|
||||
bool wasAborted = false;
|
||||
if (m_task)
|
||||
wasAborted = m_task->abort();
|
||||
Task::abort();
|
||||
return wasAborted;
|
||||
}
|
||||
|
||||
void InstanceImportTask::executeTask()
|
||||
@ -104,7 +106,7 @@ void InstanceImportTask::downloadFromUrl()
|
||||
connect(filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||
connect(filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::emitFailed);
|
||||
connect(filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::emitAborted);
|
||||
task.reset(filesNetJob);
|
||||
m_task.reset(filesNetJob);
|
||||
filesNetJob->start();
|
||||
}
|
||||
|
||||
@ -193,7 +195,7 @@ void InstanceImportTask::processZipPack()
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
|
||||
connect(zipTask.get(), &Task::succeeded, this, &InstanceImportTask::extractFinished);
|
||||
connect(zipTask.get(), &Task::succeeded, this, &InstanceImportTask::extractFinished, Qt::QueuedConnection);
|
||||
connect(zipTask.get(), &Task::aborted, this, &InstanceImportTask::emitAborted);
|
||||
connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) {
|
||||
progressStep->state = TaskStepState::Failed;
|
||||
@ -210,12 +212,13 @@ void InstanceImportTask::processZipPack()
|
||||
progressStep->status = status;
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
task.reset(zipTask);
|
||||
m_task.reset(zipTask);
|
||||
zipTask->start();
|
||||
}
|
||||
|
||||
void InstanceImportTask::extractFinished()
|
||||
{
|
||||
setAbortable(false);
|
||||
QDir extractDir(m_stagingPath);
|
||||
|
||||
qDebug() << "Fixing permissions for extracted pack files...";
|
||||
@ -289,8 +292,11 @@ void InstanceImportTask::processFlame()
|
||||
inst_creation_task->setGroup(m_instGroup);
|
||||
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
|
||||
|
||||
connect(inst_creation_task.get(), &Task::succeeded, this, [this, inst_creation_task] {
|
||||
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
|
||||
auto weak = inst_creation_task.toWeakRef();
|
||||
connect(inst_creation_task.get(), &Task::succeeded, this, [this, weak] {
|
||||
if (auto sp = weak.lock()) {
|
||||
setOverride(sp->shouldOverride(), sp->originalInstanceID());
|
||||
}
|
||||
emitSucceeded();
|
||||
});
|
||||
connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||
@ -299,11 +305,12 @@ void InstanceImportTask::processFlame()
|
||||
connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus);
|
||||
connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails);
|
||||
|
||||
connect(this, &Task::aborted, inst_creation_task.get(), &InstanceCreationTask::abort);
|
||||
connect(inst_creation_task.get(), &Task::aborted, this, &Task::abort);
|
||||
connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
|
||||
|
||||
inst_creation_task->start();
|
||||
m_task.reset(inst_creation_task);
|
||||
setAbortable(true);
|
||||
m_task->start();
|
||||
}
|
||||
|
||||
void InstanceImportTask::processTechnic()
|
||||
@ -350,7 +357,7 @@ void InstanceImportTask::processMultiMC()
|
||||
|
||||
void InstanceImportTask::processModrinth()
|
||||
{
|
||||
ModrinthCreationTask* inst_creation_task = nullptr;
|
||||
shared_qobject_ptr<ModrinthCreationTask> inst_creation_task = nullptr;
|
||||
if (!m_extra_info.isEmpty()) {
|
||||
auto pack_id_it = m_extra_info.constFind("pack_id");
|
||||
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
|
||||
@ -367,7 +374,7 @@ void InstanceImportTask::processModrinth()
|
||||
original_instance_id = original_instance_id_it.value();
|
||||
|
||||
inst_creation_task =
|
||||
new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
||||
makeShared<ModrinthCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
||||
} else {
|
||||
QString pack_id;
|
||||
if (!m_sourceUrl.isEmpty()) {
|
||||
@ -376,7 +383,7 @@ void InstanceImportTask::processModrinth()
|
||||
}
|
||||
|
||||
// FIXME: Find a way to get the ID in directly imported ZIPs
|
||||
inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id);
|
||||
inst_creation_task = makeShared<ModrinthCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id);
|
||||
}
|
||||
|
||||
inst_creation_task->setName(*this);
|
||||
@ -384,20 +391,23 @@ void InstanceImportTask::processModrinth()
|
||||
inst_creation_task->setGroup(m_instGroup);
|
||||
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
|
||||
|
||||
connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] {
|
||||
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
|
||||
auto weak = inst_creation_task.toWeakRef();
|
||||
connect(inst_creation_task.get(), &Task::succeeded, this, [this, weak] {
|
||||
if (auto sp = weak.lock()) {
|
||||
setOverride(sp->shouldOverride(), sp->originalInstanceID());
|
||||
}
|
||||
emitSucceeded();
|
||||
});
|
||||
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress);
|
||||
connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus);
|
||||
connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails);
|
||||
connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater);
|
||||
connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||
connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress);
|
||||
connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||
connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus);
|
||||
connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails);
|
||||
|
||||
connect(this, &Task::aborted, inst_creation_task, &InstanceCreationTask::abort);
|
||||
connect(inst_creation_task, &Task::aborted, this, &Task::abort);
|
||||
connect(inst_creation_task, &Task::abortStatusChanged, this, &Task::setAbortable);
|
||||
connect(inst_creation_task.get(), &Task::aborted, this, &Task::abort);
|
||||
connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
|
||||
|
||||
inst_creation_task->start();
|
||||
m_task.reset(inst_creation_task);
|
||||
setAbortable(true);
|
||||
m_task->start();
|
||||
}
|
||||
|
@ -40,16 +40,13 @@
|
||||
#include <QUrl>
|
||||
#include "InstanceTask.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
class QuaZip;
|
||||
|
||||
class InstanceImportTask : public InstanceTask {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit InstanceImportTask(const QUrl& sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {});
|
||||
|
||||
virtual ~InstanceImportTask() = default;
|
||||
bool abort() override;
|
||||
|
||||
protected:
|
||||
@ -70,7 +67,7 @@ class InstanceImportTask : public InstanceTask {
|
||||
private: /* data */
|
||||
QUrl m_sourceUrl;
|
||||
QString m_archivePath;
|
||||
Task::Ptr task;
|
||||
Task::Ptr m_task;
|
||||
enum class ModpackType {
|
||||
Unknown,
|
||||
MultiMC,
|
||||
|
@ -487,7 +487,7 @@ InstanceList::InstListError InstanceList::loadList()
|
||||
int front_bookmark = -1;
|
||||
int back_bookmark = -1;
|
||||
int currentItem = -1;
|
||||
auto removeNow = [&]() {
|
||||
auto removeNow = [this, &front_bookmark, &back_bookmark, ¤tItem]() {
|
||||
beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark);
|
||||
m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1);
|
||||
endRemoveRows();
|
||||
|
@ -43,7 +43,7 @@ class InstancePageProvider : protected QObject, public BasePageProvider {
|
||||
values.append(new ServersPage(onesix));
|
||||
// values.append(new GameOptionsPage(onesix.get()));
|
||||
values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots")));
|
||||
values.append(new InstanceSettingsPage(onesix.get()));
|
||||
values.append(new InstanceSettingsPage(onesix));
|
||||
auto logMatcher = inst->getLogFileMatcher();
|
||||
if (logMatcher) {
|
||||
values.append(new OtherLogsPage(inst->getLogFileRoot(), logMatcher));
|
||||
|
@ -63,7 +63,7 @@ bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget* parent)
|
||||
return true;
|
||||
}
|
||||
|
||||
void JavaCommon::javaWasOk(QWidget* parent, const JavaCheckResult& result)
|
||||
void JavaCommon::javaWasOk(QWidget* parent, const JavaChecker::Result& result)
|
||||
{
|
||||
QString text;
|
||||
text += QObject::tr(
|
||||
@ -79,7 +79,7 @@ void JavaCommon::javaWasOk(QWidget* parent, const JavaCheckResult& result)
|
||||
CustomMessageBox::selectable(parent, QObject::tr("Java test success"), text, QMessageBox::Information)->show();
|
||||
}
|
||||
|
||||
void JavaCommon::javaArgsWereBad(QWidget* parent, const JavaCheckResult& result)
|
||||
void JavaCommon::javaArgsWereBad(QWidget* parent, const JavaChecker::Result& result)
|
||||
{
|
||||
auto htmlError = result.errorLog;
|
||||
QString text;
|
||||
@ -89,7 +89,7 @@ void JavaCommon::javaArgsWereBad(QWidget* parent, const JavaCheckResult& result)
|
||||
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
|
||||
}
|
||||
|
||||
void JavaCommon::javaBinaryWasBad(QWidget* parent, const JavaCheckResult& result)
|
||||
void JavaCommon::javaBinaryWasBad(QWidget* parent, const JavaChecker::Result& result)
|
||||
{
|
||||
QString text;
|
||||
text += QObject::tr(
|
||||
@ -116,34 +116,26 @@ void JavaCommon::TestCheck::run()
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
checker.reset(new JavaChecker());
|
||||
checker.reset(new JavaChecker(m_path, "", 0, 0, 0, 0));
|
||||
connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinished);
|
||||
checker->m_path = m_path;
|
||||
checker->performCheck();
|
||||
checker->start();
|
||||
}
|
||||
|
||||
void JavaCommon::TestCheck::checkFinished(JavaCheckResult result)
|
||||
void JavaCommon::TestCheck::checkFinished(const JavaChecker::Result& result)
|
||||
{
|
||||
if (result.validity != JavaCheckResult::Validity::Valid) {
|
||||
if (result.validity != JavaChecker::Result::Validity::Valid) {
|
||||
javaBinaryWasBad(m_parent, result);
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
checker.reset(new JavaChecker());
|
||||
checker.reset(new JavaChecker(m_path, m_args, m_maxMem, m_maxMem, result.javaVersion.requiresPermGen() ? m_permGen : 0, 0));
|
||||
connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinishedWithArgs);
|
||||
checker->m_path = m_path;
|
||||
checker->m_args = m_args;
|
||||
checker->m_minMem = m_minMem;
|
||||
checker->m_maxMem = m_maxMem;
|
||||
if (result.javaVersion.requiresPermGen()) {
|
||||
checker->m_permGen = m_permGen;
|
||||
}
|
||||
checker->performCheck();
|
||||
checker->start();
|
||||
}
|
||||
|
||||
void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result)
|
||||
void JavaCommon::TestCheck::checkFinishedWithArgs(const JavaChecker::Result& result)
|
||||
{
|
||||
if (result.validity == JavaCheckResult::Validity::Valid) {
|
||||
if (result.validity == JavaChecker::Result::Validity::Valid) {
|
||||
javaWasOk(m_parent, result);
|
||||
emit finished();
|
||||
return;
|
||||
|
@ -10,11 +10,11 @@ namespace JavaCommon {
|
||||
bool checkJVMArgs(QString args, QWidget* parent);
|
||||
|
||||
// Show a dialog saying that the Java binary was usable
|
||||
void javaWasOk(QWidget* parent, const JavaCheckResult& result);
|
||||
void javaWasOk(QWidget* parent, const JavaChecker::Result& result);
|
||||
// Show a dialog saying that the Java binary was not usable because of bad options
|
||||
void javaArgsWereBad(QWidget* parent, const JavaCheckResult& result);
|
||||
void javaArgsWereBad(QWidget* parent, const JavaChecker::Result& result);
|
||||
// Show a dialog saying that the Java binary was not usable
|
||||
void javaBinaryWasBad(QWidget* parent, const JavaCheckResult& result);
|
||||
void javaBinaryWasBad(QWidget* parent, const JavaChecker::Result& result);
|
||||
// Show a dialog if we couldn't find Java Checker
|
||||
void javaCheckNotFound(QWidget* parent);
|
||||
|
||||
@ -32,11 +32,11 @@ class TestCheck : public QObject {
|
||||
void finished();
|
||||
|
||||
private slots:
|
||||
void checkFinished(JavaCheckResult result);
|
||||
void checkFinishedWithArgs(JavaCheckResult result);
|
||||
void checkFinished(const JavaChecker::Result& result);
|
||||
void checkFinishedWithArgs(const JavaChecker::Result& result);
|
||||
|
||||
private:
|
||||
std::shared_ptr<JavaChecker> checker;
|
||||
JavaChecker::Ptr checker;
|
||||
QWidget* m_parent = nullptr;
|
||||
QString m_path;
|
||||
QString m_args;
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "ui/InstanceWindow.h"
|
||||
#include "ui/MainWindow.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui/dialogs/MSALoginDialog.h"
|
||||
#include "ui/dialogs/ProfileSelectDialog.h"
|
||||
#include "ui/dialogs/ProfileSetupDialog.h"
|
||||
#include "ui/dialogs/ProgressDialog.h"
|
||||
@ -53,6 +54,7 @@
|
||||
#include <QLineEdit>
|
||||
#include <QList>
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
#include <QStringList>
|
||||
|
||||
#include "BuildConfig.h"
|
||||
@ -60,7 +62,7 @@
|
||||
#include "launch/steps/TextPrint.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
LaunchController::LaunchController(QObject* parent) : Task(parent) {}
|
||||
LaunchController::LaunchController() : Task() {}
|
||||
|
||||
void LaunchController::executeTask()
|
||||
{
|
||||
@ -198,8 +200,7 @@ void LaunchController::login()
|
||||
m_accountToUse->shouldRefresh()) {
|
||||
// Force account refresh on the account used to launch the instance updating the AccountState
|
||||
// only on first try and if it is not meant to be offline
|
||||
auto accounts = APPLICATION->accounts();
|
||||
accounts->requestRefresh(m_accountToUse->internalId());
|
||||
m_accountToUse->refresh();
|
||||
}
|
||||
while (tryagain) {
|
||||
if (tries > 0 && tries % 3 == 0) {
|
||||
@ -218,13 +219,34 @@ void LaunchController::login()
|
||||
m_session->demo = m_demo;
|
||||
m_accountToUse->fillSession(m_session);
|
||||
|
||||
// Launch immediately in true offline mode
|
||||
if (m_accountToUse->accountType() == AccountType::Offline) {
|
||||
MinecraftAccountPtr accountToCheck;
|
||||
|
||||
if (m_accountToUse->ownsMinecraft())
|
||||
accountToCheck = m_accountToUse;
|
||||
else if (const MinecraftAccountPtr defaultAccount = APPLICATION->accounts()->defaultAccount();
|
||||
defaultAccount != nullptr && defaultAccount->ownsMinecraft()) {
|
||||
accountToCheck = defaultAccount;
|
||||
} else {
|
||||
for (int i = 0; i < APPLICATION->accounts()->count(); i++) {
|
||||
MinecraftAccountPtr account = APPLICATION->accounts()->at(i);
|
||||
if (account->ownsMinecraft())
|
||||
accountToCheck = account;
|
||||
}
|
||||
}
|
||||
|
||||
if (accountToCheck == nullptr) {
|
||||
if (!m_session->demo)
|
||||
m_session->demo = askPlayDemo();
|
||||
|
||||
if (m_session->demo)
|
||||
launchInstance();
|
||||
else
|
||||
emitFailed(tr("Launch cancelled - account does not own Minecraft."));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (m_accountToUse->accountState()) {
|
||||
switch (accountToCheck->accountState()) {
|
||||
case AccountState::Offline: {
|
||||
m_session->wants_online = false;
|
||||
}
|
||||
@ -233,16 +255,19 @@ void LaunchController::login()
|
||||
if (!m_session->wants_online) {
|
||||
// we ask the user for a player name
|
||||
bool ok = false;
|
||||
auto name = askOfflineName(m_session->player_name, m_session->demo, ok);
|
||||
QString name;
|
||||
if (m_offlineName.isEmpty()) {
|
||||
name = askOfflineName(m_session->player_name, m_session->demo, ok);
|
||||
if (!ok) {
|
||||
tryagain = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
name = m_offlineName;
|
||||
}
|
||||
m_session->MakeOffline(name);
|
||||
// offline flavored game from here :3
|
||||
}
|
||||
if (m_accountToUse->ownsMinecraft()) {
|
||||
if (!m_accountToUse->hasProfile()) {
|
||||
} else if (m_accountToUse == accountToCheck && !m_accountToUse->hasProfile()) {
|
||||
// Now handle setting up a profile name here...
|
||||
ProfileSetupDialog dialog(m_accountToUse, m_parentWidget);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
@ -253,26 +278,18 @@ void LaunchController::login()
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_accountToUse->accountType() == AccountType::Offline)
|
||||
m_session->wants_online = false;
|
||||
|
||||
// we own Minecraft, there is a profile, it's all ready to go!
|
||||
launchInstance();
|
||||
return;
|
||||
} else {
|
||||
// play demo ?
|
||||
if (!m_session->demo) {
|
||||
m_session->demo = askPlayDemo();
|
||||
}
|
||||
if (m_session->demo) { // play demo here
|
||||
launchInstance();
|
||||
} else {
|
||||
emitFailed(tr("Launch cancelled - account does not own Minecraft."));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
case AccountState::Errored:
|
||||
// This means some sort of soft error that we can fix with a refresh ... so let's refresh.
|
||||
case AccountState::Unchecked: {
|
||||
m_accountToUse->refresh();
|
||||
accountToCheck->refresh();
|
||||
}
|
||||
/* fallthrough */
|
||||
case AccountState::Working: {
|
||||
@ -281,19 +298,19 @@ void LaunchController::login()
|
||||
if (m_online) {
|
||||
progDialog.setSkipButton(true, tr("Play Offline"));
|
||||
}
|
||||
auto task = m_accountToUse->currentTask();
|
||||
auto task = accountToCheck->currentTask();
|
||||
progDialog.execWithTask(task.get());
|
||||
continue;
|
||||
}
|
||||
case AccountState::Expired: {
|
||||
auto errorString = tr("The account has expired and needs to be logged into manually again.");
|
||||
QMessageBox::warning(m_parentWidget, tr("Account refresh failed"), errorString, QMessageBox::StandardButton::Ok,
|
||||
QMessageBox::StandardButton::Ok);
|
||||
emitFailed(errorString);
|
||||
if (reauthenticateAccount(accountToCheck))
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
case AccountState::Disabled: {
|
||||
auto errorString = tr("The launcher's client identification has changed. Please remove this account and add it again.");
|
||||
auto errorString = tr("The launcher's client identification has changed. Please remove '%1' and try again.")
|
||||
.arg(accountToCheck->profileName());
|
||||
|
||||
QMessageBox::warning(m_parentWidget, tr("Client identification changed"), errorString, QMessageBox::StandardButton::Ok,
|
||||
QMessageBox::StandardButton::Ok);
|
||||
emitFailed(errorString);
|
||||
@ -301,8 +318,9 @@ void LaunchController::login()
|
||||
}
|
||||
case AccountState::Gone: {
|
||||
auto errorString =
|
||||
tr("The account no longer exists on the servers. It may have been migrated, in which case please add the new account "
|
||||
"you migrated this one to.");
|
||||
tr("'%1' no longer exists on the servers. It may have been migrated, in which case please add the new account "
|
||||
"you migrated this one to.")
|
||||
.arg(accountToCheck->profileName());
|
||||
QMessageBox::warning(m_parentWidget, tr("Account gone"), errorString, QMessageBox::StandardButton::Ok,
|
||||
QMessageBox::StandardButton::Ok);
|
||||
emitFailed(errorString);
|
||||
@ -313,6 +331,38 @@ void LaunchController::login()
|
||||
emitFailed(tr("Failed to launch."));
|
||||
}
|
||||
|
||||
bool LaunchController::reauthenticateAccount(MinecraftAccountPtr account)
|
||||
{
|
||||
auto button = QMessageBox::warning(
|
||||
m_parentWidget, tr("Account refresh failed"),
|
||||
tr("'%1' has expired and needs to be reauthenticated. Do you want to reauthenticate this account?").arg(account->profileName()),
|
||||
QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, QMessageBox::StandardButton::Yes);
|
||||
if (button == QMessageBox::StandardButton::Yes) {
|
||||
auto accounts = APPLICATION->accounts();
|
||||
bool isDefault = accounts->defaultAccount() == account;
|
||||
accounts->removeAccount(accounts->index(accounts->findAccountByProfileId(account->profileId())));
|
||||
if (account->accountType() == AccountType::MSA) {
|
||||
auto newAccount = MSALoginDialog::newAccount(m_parentWidget);
|
||||
|
||||
if (newAccount != nullptr) {
|
||||
accounts->addAccount(newAccount);
|
||||
|
||||
if (isDefault)
|
||||
accounts->setDefaultAccount(newAccount);
|
||||
|
||||
if (m_accountToUse == account) {
|
||||
m_accountToUse = nullptr;
|
||||
decideAccount();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emitFailed(tr("The account has expired and needs to be reauthenticated"));
|
||||
return false;
|
||||
}
|
||||
|
||||
void LaunchController::launchInstance()
|
||||
{
|
||||
Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL");
|
||||
|
@ -47,7 +47,7 @@ class LaunchController : public Task {
|
||||
public:
|
||||
void executeTask() override;
|
||||
|
||||
LaunchController(QObject* parent = nullptr);
|
||||
LaunchController();
|
||||
virtual ~LaunchController() = default;
|
||||
|
||||
void setInstance(InstancePtr instance) { m_instance = instance; }
|
||||
@ -56,6 +56,8 @@ class LaunchController : public Task {
|
||||
|
||||
void setOnline(bool online) { m_online = online; }
|
||||
|
||||
void setOfflineName(const QString& offlineName) { m_offlineName = offlineName; }
|
||||
|
||||
void setDemo(bool demo) { m_demo = demo; }
|
||||
|
||||
void setProfiler(BaseProfilerFactory* profiler) { m_profiler = profiler; }
|
||||
@ -76,6 +78,7 @@ class LaunchController : public Task {
|
||||
void decideAccount();
|
||||
bool askPlayDemo();
|
||||
QString askOfflineName(QString playerName, bool demo, bool& ok);
|
||||
bool reauthenticateAccount(MinecraftAccountPtr account);
|
||||
|
||||
private slots:
|
||||
void readyForLaunch();
|
||||
@ -87,6 +90,7 @@ class LaunchController : public Task {
|
||||
private:
|
||||
BaseProfilerFactory* m_profiler = nullptr;
|
||||
bool m_online = true;
|
||||
QString m_offlineName;
|
||||
bool m_demo = false;
|
||||
InstancePtr m_instance;
|
||||
QWidget* m_parentWidget = nullptr;
|
||||
|
@ -39,8 +39,16 @@ if [ "x$DEPS_LIST" = "x" ]; then
|
||||
# Just to be sure...
|
||||
chmod +x "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}"
|
||||
|
||||
ARGS=("${LAUNCHER_DIR}/${LAUNCHER_NAME}" "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}")
|
||||
|
||||
if [ -f portable.txt ]; then
|
||||
ARGS+=("-d" "${LAUNCHER_DIR}")
|
||||
fi
|
||||
|
||||
ARGS+=("$@")
|
||||
|
||||
# Run the launcher
|
||||
exec -a "${LAUNCHER_DIR}/${LAUNCHER_NAME}" "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}" -d "${LAUNCHER_DIR}" "$@"
|
||||
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}" "$@"
|
||||
|
@ -39,7 +39,8 @@
|
||||
#include <QTextDecoder>
|
||||
#include "MessageLevel.h"
|
||||
|
||||
LoggedProcess::LoggedProcess(QObject* parent) : QProcess(parent)
|
||||
LoggedProcess::LoggedProcess(const QTextCodec* output_codec, QObject* parent)
|
||||
: QProcess(parent), m_err_decoder(output_codec), m_out_decoder(output_codec)
|
||||
{
|
||||
// QProcess has a strange interface... let's map a lot of those into a few.
|
||||
connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut);
|
||||
|
@ -49,7 +49,7 @@ class LoggedProcess : public QProcess {
|
||||
enum State { NotRunning, Starting, FailedToStart, Running, Finished, Crashed, Aborted };
|
||||
|
||||
public:
|
||||
explicit LoggedProcess(QObject* parent = 0);
|
||||
explicit LoggedProcess(const QTextCodec* output_codec = QTextCodec::codecForLocale(), QObject* parent = 0);
|
||||
virtual ~LoggedProcess();
|
||||
|
||||
State state() const;
|
||||
@ -80,8 +80,8 @@ class LoggedProcess : public QProcess {
|
||||
QStringList reprocess(const QByteArray& data, QTextDecoder& decoder);
|
||||
|
||||
private:
|
||||
QTextDecoder m_err_decoder = QTextDecoder(QTextCodec::codecForLocale());
|
||||
QTextDecoder m_out_decoder = QTextDecoder(QTextCodec::codecForLocale());
|
||||
QTextDecoder m_err_decoder;
|
||||
QTextDecoder m_out_decoder;
|
||||
QString m_leftover_line;
|
||||
bool m_killed = false;
|
||||
State m_state = NotRunning;
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -378,7 +378,7 @@ std::optional<QStringList> extractDir(QString fileCompressed, QString dir)
|
||||
if (fileInfo.size() == 22) {
|
||||
return QStringList();
|
||||
}
|
||||
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();
|
||||
qWarning() << "Could not open archive for unpacking:" << fileCompressed << "Error:" << zip.getZipError();
|
||||
;
|
||||
return std::nullopt;
|
||||
}
|
||||
@ -395,7 +395,7 @@ std::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QS
|
||||
if (fileInfo.size() == 22) {
|
||||
return QStringList();
|
||||
}
|
||||
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();
|
||||
qWarning() << "Could not open archive for unpacking:" << fileCompressed << "Error:" << zip.getZipError();
|
||||
;
|
||||
return std::nullopt;
|
||||
}
|
||||
@ -412,7 +412,7 @@ bool extractFile(QString fileCompressed, QString file, QString target)
|
||||
if (fileInfo.size() == 22) {
|
||||
return true;
|
||||
}
|
||||
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();
|
||||
qWarning() << "Could not open archive for unpacking:" << fileCompressed << "Error:" << zip.getZipError();
|
||||
return false;
|
||||
}
|
||||
return extractRelFile(&zip, file, target);
|
||||
@ -536,6 +536,10 @@ bool ExportToZipTask::abort()
|
||||
|
||||
void ExtractZipTask::executeTask()
|
||||
{
|
||||
if (!m_input->isOpen() && !m_input->open(QuaZip::mdUnzip)) {
|
||||
emitFailed(tr("Unable to open supplied zip file."));
|
||||
return;
|
||||
}
|
||||
m_zip_future = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return extractZip(); });
|
||||
connect(&m_zip_watcher, &QFutureWatcher<ZipResult>::finished, this, &ExtractZipTask::finish);
|
||||
m_zip_watcher.setFuture(m_zip_future);
|
||||
@ -573,7 +577,7 @@ auto ExtractZipTask::extractZip() -> ZipResult
|
||||
|
||||
auto relative_file_name = QDir::fromNativeSeparators(file_name.mid(m_subdirectory.size()));
|
||||
auto original_name = relative_file_name;
|
||||
setStatus("Unziping: " + relative_file_name);
|
||||
setStatus("Unpacking: " + relative_file_name);
|
||||
|
||||
// Fix subdirs/files ending with a / getting transformed into absolute paths
|
||||
if (relative_file_name.startsWith('/'))
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -153,6 +153,7 @@ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, Q
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
class ExportToZipTask : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ExportToZipTask(QString outputPath,
|
||||
QDir dir,
|
||||
@ -207,7 +208,11 @@ class ExportToZipTask : public Task {
|
||||
};
|
||||
|
||||
class ExtractZipTask : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ExtractZipTask(QString input, QDir outputDir, QString subdirectory = "")
|
||||
: ExtractZipTask(std::make_shared<QuaZip>(input), outputDir, subdirectory)
|
||||
{}
|
||||
ExtractZipTask(std::shared_ptr<QuaZip> input, QDir outputDir, QString subdirectory = "")
|
||||
: m_input(input), m_output_dir(outputDir), m_subdirectory(subdirectory)
|
||||
{}
|
||||
|
@ -101,7 +101,7 @@ class PixmapCache final : public QObject {
|
||||
*/
|
||||
bool _markCacheMissByEviciton()
|
||||
{
|
||||
static constexpr uint maxInt = static_cast<uint>(std::numeric_limits<int>::max());
|
||||
static constexpr uint maxCache = static_cast<uint>(std::numeric_limits<int>::max()) / 4;
|
||||
static constexpr uint step = 10240;
|
||||
static constexpr int oneSecond = 1000;
|
||||
|
||||
@ -118,8 +118,8 @@ class PixmapCache final : public QObject {
|
||||
if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) {
|
||||
// increase the cache size
|
||||
uint newSize = _cacheLimit() + step;
|
||||
if (newSize >= maxInt) { // increase it until you overflow :D
|
||||
newSize = maxInt;
|
||||
if (newSize >= maxCache) { // increase it until you overflow :D
|
||||
newSize = maxCache;
|
||||
qDebug() << m_consecutive_fast_evicitons
|
||||
<< tr("pixmap cache misses by eviction happened too fast, doing nothing as the cache size reached it's limit");
|
||||
} else {
|
||||
|
@ -108,24 +108,31 @@ QString getLibraryString()
|
||||
if (filePath.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
auto conf = Json::requireDocument(filePath, vkLayer);
|
||||
auto confObject = Json::requireObject(conf, vkLayer);
|
||||
auto layer = Json::ensureObject(confObject, "layer");
|
||||
QString libraryName = Json::ensureString(layer, "library_path");
|
||||
|
||||
if (libraryName.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (QFileInfo(libraryName).isAbsolute()) {
|
||||
return libraryName;
|
||||
}
|
||||
|
||||
#ifdef __GLIBC__
|
||||
// Check whether mangohud is usable on a glibc based system
|
||||
if (!libraryName.isEmpty()) {
|
||||
QString libraryPath = findLibrary(libraryName);
|
||||
if (!libraryPath.isEmpty()) {
|
||||
return libraryPath;
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Without glibc return recorded shared library as-is.
|
||||
return libraryName;
|
||||
#endif
|
||||
} catch (const Exception& e) {
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
|
@ -53,7 +53,7 @@ class NullInstance : public BaseInstance {
|
||||
QSet<QString> traits() const override { return {}; };
|
||||
QString instanceConfigFolder() const override { return instanceRoot(); };
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftTarget::Ptr) override { return nullptr; }
|
||||
shared_qobject_ptr<Task> createUpdateTask([[maybe_unused]] Net::Mode mode) override { return nullptr; }
|
||||
QList<Task::Ptr> createUpdateTask() override { return {}; }
|
||||
QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); }
|
||||
QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); }
|
||||
QMap<QString, QString> getVariables() override { return QMap<QString, QString>(); }
|
||||
|
71
launcher/PSaveFile.h
Normal file
71
launcher/PSaveFile.h
Normal file
@ -0,0 +1,71 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QSaveFile>
|
||||
#include "Application.h"
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
|
||||
/* PSaveFile
|
||||
* A class that mimics QSaveFile for Windows.
|
||||
*
|
||||
* When reading resources, we need to avoid accessing temporary files
|
||||
* generated by QSaveFile. If we start reading such a file, we may
|
||||
* inadvertently keep it open while QSaveFile is trying to remove it,
|
||||
* or we might detect the file just before it is removed, leading to
|
||||
* race conditions and errors.
|
||||
*
|
||||
* Unfortunately, QSaveFile doesn't provide a way to retrieve the
|
||||
* temporary file name or to set a specific template for the temporary
|
||||
* file name it uses. By default, QSaveFile appends a `.XXXXXX` suffix
|
||||
* to the original file name, where the `XXXXXX` part is dynamically
|
||||
* generated to ensure uniqueness.
|
||||
*
|
||||
* This class acts like a lock by adding and removing the target file
|
||||
* name into/from a global string set, helping to manage access to
|
||||
* files during critical operations.
|
||||
*
|
||||
* Note: Please do not use the `setFileName` function directly, as it
|
||||
* is not virtual and cannot be overridden.
|
||||
*/
|
||||
class PSaveFile : public QSaveFile {
|
||||
public:
|
||||
PSaveFile(const QString& name) : QSaveFile(name) { addPath(name); }
|
||||
PSaveFile(const QString& name, QObject* parent) : QSaveFile(name, parent) { addPath(name); }
|
||||
virtual ~PSaveFile()
|
||||
{
|
||||
if (auto app = APPLICATION_DYN) {
|
||||
app->removeQSavePath(m_absoluteFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void addPath(const QString& path)
|
||||
{
|
||||
m_absoluteFilePath = QFileInfo(path).absoluteFilePath() + "."; // add dot for tmp files only
|
||||
if (auto app = APPLICATION_DYN) {
|
||||
app->addQSavePath(m_absoluteFilePath);
|
||||
}
|
||||
}
|
||||
QString m_absoluteFilePath;
|
||||
};
|
||||
#else
|
||||
#define PSaveFile QSaveFile
|
||||
#endif
|
@ -33,7 +33,7 @@ class shared_qobject_ptr : public QSharedPointer<T> {
|
||||
{}
|
||||
|
||||
void reset() { QSharedPointer<T>::reset(); }
|
||||
void reset(T*&& other)
|
||||
void reset(T* other)
|
||||
{
|
||||
shared_qobject_ptr<T> t(other);
|
||||
this->swap(t);
|
||||
|
@ -24,7 +24,9 @@
|
||||
#include "minecraft/mod/ModFolderModel.h"
|
||||
#include "minecraft/mod/ResourceFolderModel.h"
|
||||
|
||||
#include "modplatform/helpers/HashUtils.h"
|
||||
#include "net/ApiDownload.h"
|
||||
#include "net/ChecksumValidator.h"
|
||||
|
||||
ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
||||
ModPlatform::IndexedVersion version,
|
||||
@ -33,9 +35,9 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
||||
QString custom_target_folder)
|
||||
: m_pack(std::move(pack)), m_pack_version(std::move(version)), m_pack_model(packs), m_custom_target_folder(custom_target_folder)
|
||||
{
|
||||
if (auto model = dynamic_cast<ModFolderModel*>(m_pack_model.get()); model && is_indexed) {
|
||||
m_update_task.reset(new LocalModUpdateTask(model->indexDir(), *m_pack, m_pack_version));
|
||||
connect(m_update_task.get(), &LocalModUpdateTask::hasOldMod, this, &ResourceDownloadTask::hasOldResource);
|
||||
if (is_indexed) {
|
||||
m_update_task.reset(new LocalResourceUpdateTask(m_pack_model->indexDir(), *m_pack, m_pack_version));
|
||||
connect(m_update_task.get(), &LocalResourceUpdateTask::hasOldResource, this, &ResourceDownloadTask::hasOldResource);
|
||||
|
||||
addTask(m_update_task);
|
||||
}
|
||||
@ -53,7 +55,29 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
||||
}
|
||||
}
|
||||
|
||||
m_filesNetJob->addNetAction(Net::ApiDownload::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename())));
|
||||
auto action = Net::ApiDownload::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename()));
|
||||
if (!m_pack_version.hash_type.isEmpty() && !m_pack_version.hash.isEmpty()) {
|
||||
switch (Hashing::algorithmFromString(m_pack_version.hash_type)) {
|
||||
case Hashing::Algorithm::Md4:
|
||||
action->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Md4, m_pack_version.hash));
|
||||
break;
|
||||
case Hashing::Algorithm::Md5:
|
||||
action->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Md5, m_pack_version.hash));
|
||||
break;
|
||||
case Hashing::Algorithm::Sha1:
|
||||
action->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Sha1, m_pack_version.hash));
|
||||
break;
|
||||
case Hashing::Algorithm::Sha256:
|
||||
action->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Sha256, m_pack_version.hash));
|
||||
break;
|
||||
case Hashing::Algorithm::Sha512:
|
||||
action->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Sha512, m_pack_version.hash));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_filesNetJob->addNetAction(action);
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded);
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged);
|
||||
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propagateStepProgress);
|
||||
@ -67,12 +91,8 @@ void ResourceDownloadTask::downloadSucceeded()
|
||||
m_filesNetJob.reset();
|
||||
auto name = std::get<0>(to_delete);
|
||||
auto filename = std::get<1>(to_delete);
|
||||
if (!name.isEmpty() && filename != m_pack_version.fileName) {
|
||||
if (auto model = dynamic_cast<ModFolderModel*>(m_pack_model.get()); model)
|
||||
model->uninstallMod(filename, true);
|
||||
else
|
||||
m_pack_model->uninstallResource(filename);
|
||||
}
|
||||
if (!name.isEmpty() && filename != m_pack_version.fileName)
|
||||
m_pack_model->uninstallResource(filename, true);
|
||||
}
|
||||
|
||||
void ResourceDownloadTask::downloadFailed(QString reason)
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "net/NetJob.h"
|
||||
#include "tasks/SequentialTask.h"
|
||||
|
||||
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
|
||||
#include "minecraft/mod/tasks/LocalResourceUpdateTask.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
class ResourceFolderModel;
|
||||
@ -50,7 +50,7 @@ class ResourceDownloadTask : public SequentialTask {
|
||||
QString m_custom_target_folder;
|
||||
|
||||
NetJob::Ptr m_filesNetJob;
|
||||
LocalModUpdateTask::Ptr m_update_task;
|
||||
LocalResourceUpdateTask::Ptr m_update_task;
|
||||
|
||||
void downloadProgressChanged(qint64 current, qint64 total);
|
||||
void downloadFailed(QString reason);
|
||||
|
@ -20,13 +20,13 @@
|
||||
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include "SysInfo.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
|
||||
struct RuntimeContext {
|
||||
QString javaArchitecture;
|
||||
QString javaRealArchitecture;
|
||||
QString javaPath;
|
||||
QString system;
|
||||
QString system = SysInfo::currentSystem();
|
||||
|
||||
QString mappedJavaRealArchitecture() const
|
||||
{
|
||||
@ -45,8 +45,6 @@ struct RuntimeContext {
|
||||
{
|
||||
javaArchitecture = instanceSettings->get("JavaArchitecture").toString();
|
||||
javaRealArchitecture = instanceSettings->get("JavaRealArchitecture").toString();
|
||||
javaPath = instanceSettings->get("JavaPath").toString();
|
||||
system = currentSystem();
|
||||
}
|
||||
|
||||
QString getClassifier() const { return system + "-" + mappedJavaRealArchitecture(); }
|
||||
@ -68,21 +66,4 @@ struct RuntimeContext {
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
static QString currentSystem()
|
||||
{
|
||||
#if defined(Q_OS_LINUX)
|
||||
return "linux";
|
||||
#elif defined(Q_OS_MACOS)
|
||||
return "osx";
|
||||
#elif defined(Q_OS_WINDOWS)
|
||||
return "windows";
|
||||
#elif defined(Q_OS_FREEBSD)
|
||||
return "freebsd";
|
||||
#elif defined(Q_OS_OPENBSD)
|
||||
return "openbsd";
|
||||
#else
|
||||
return "unknown";
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
99
launcher/SysInfo.cpp
Normal file
99
launcher/SysInfo.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
#include <QDebug>
|
||||
#include <QString>
|
||||
#include "sys.h"
|
||||
#ifdef Q_OS_MACOS
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
#include <QFile>
|
||||
#include <QMap>
|
||||
#include <QProcess>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
bool rosettaDetect()
|
||||
{
|
||||
int ret = 0;
|
||||
size_t size = sizeof(ret);
|
||||
if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
|
||||
return false;
|
||||
}
|
||||
return ret == 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace SysInfo {
|
||||
QString currentSystem()
|
||||
{
|
||||
#if defined(Q_OS_LINUX)
|
||||
return "linux";
|
||||
#elif defined(Q_OS_MACOS)
|
||||
return "osx";
|
||||
#elif defined(Q_OS_WINDOWS)
|
||||
return "windows";
|
||||
#elif defined(Q_OS_FREEBSD)
|
||||
return "freebsd";
|
||||
#elif defined(Q_OS_OPENBSD)
|
||||
return "openbsd";
|
||||
#else
|
||||
return "unknown";
|
||||
#endif
|
||||
}
|
||||
|
||||
QString useQTForArch()
|
||||
{
|
||||
#if defined(Q_OS_MACOS) && !defined(Q_PROCESSOR_ARM)
|
||||
if (rosettaDetect()) {
|
||||
return "arm64";
|
||||
} else {
|
||||
return "x86_64";
|
||||
}
|
||||
#endif
|
||||
return QSysInfo::currentCpuArchitecture();
|
||||
}
|
||||
|
||||
int suitableMaxMem()
|
||||
{
|
||||
float totalRAM = (float)Sys::getSystemRam() / (float)Sys::mebibyte;
|
||||
int maxMemoryAlloc;
|
||||
|
||||
// If totalRAM < 6GB, use (totalRAM / 1.5), else 4GB
|
||||
if (totalRAM < (4096 * 1.5))
|
||||
maxMemoryAlloc = (int)(totalRAM / 1.5);
|
||||
else
|
||||
maxMemoryAlloc = 4096;
|
||||
|
||||
return maxMemoryAlloc;
|
||||
}
|
||||
|
||||
QString getSupportedJavaArchitecture()
|
||||
{
|
||||
auto sys = currentSystem();
|
||||
auto arch = useQTForArch();
|
||||
if (sys == "windows") {
|
||||
if (arch == "x86_64")
|
||||
return "windows-x64";
|
||||
if (arch == "i386")
|
||||
return "windows-x86";
|
||||
// Unknown, maybe arm, appending arch
|
||||
return "windows-" + arch;
|
||||
}
|
||||
if (sys == "osx") {
|
||||
if (arch == "arm64")
|
||||
return "mac-os-arm64";
|
||||
if (arch.contains("64"))
|
||||
return "mac-os-x64";
|
||||
if (arch.contains("86"))
|
||||
return "mac-os-x86";
|
||||
// Unknown, maybe something new, appending arch
|
||||
return "mac-os-" + arch;
|
||||
} else if (sys == "linux") {
|
||||
if (arch == "x86_64")
|
||||
return "linux-x64";
|
||||
if (arch == "i386")
|
||||
return "linux-x86";
|
||||
// will work for arm32 arm(64)
|
||||
return "linux-" + arch;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
} // namespace SysInfo
|
8
launcher/SysInfo.h
Normal file
8
launcher/SysInfo.h
Normal file
@ -0,0 +1,8 @@
|
||||
#include <QString>
|
||||
|
||||
namespace SysInfo {
|
||||
QString currentSystem();
|
||||
QString useQTForArch();
|
||||
QString getSupportedJavaArchitecture();
|
||||
int suitableMaxMem();
|
||||
} // namespace SysInfo
|
260
launcher/Untar.cpp
Normal file
260
launcher/Untar.cpp
Normal file
@ -0,0 +1,260 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "Untar.h"
|
||||
#include <quagzipfile.h>
|
||||
#include <QByteArray>
|
||||
#include <QFileInfo>
|
||||
#include <QIODevice>
|
||||
#include <QString>
|
||||
#include "FileSystem.h"
|
||||
|
||||
// adaptation of the:
|
||||
// - https://github.com/madler/zlib/blob/develop/contrib/untgz/untgz.c
|
||||
// - https://en.wikipedia.org/wiki/Tar_(computing)
|
||||
// - https://github.com/euroelessar/cutereader/blob/master/karchive/src/ktar.cpp
|
||||
|
||||
#define BLOCKSIZE 512
|
||||
#define SHORTNAMESIZE 100
|
||||
|
||||
enum class TypeFlag : char {
|
||||
Regular = '0', // regular file
|
||||
ARegular = 0, // regular file
|
||||
Link = '1', // link
|
||||
Symlink = '2', // reserved
|
||||
Character = '3', // character special
|
||||
Block = '4', // block special
|
||||
Directory = '5', // directory
|
||||
FIFO = '6', // FIFO special
|
||||
Contiguous = '7', // reserved
|
||||
// Posix stuff
|
||||
GlobalPosixHeader = 'g',
|
||||
ExtendedPosixHeader = 'x',
|
||||
// 'A'– 'Z' Vendor specific extensions(POSIX .1 - 1988)
|
||||
// GNU
|
||||
GNULongLink = 'K', /* long link name */
|
||||
GNULongName = 'L', /* long file name */
|
||||
};
|
||||
|
||||
// struct Header { /* byte offset */
|
||||
// char name[100]; /* 0 */
|
||||
// char mode[8]; /* 100 */
|
||||
// char uid[8]; /* 108 */
|
||||
// char gid[8]; /* 116 */
|
||||
// char size[12]; /* 124 */
|
||||
// char mtime[12]; /* 136 */
|
||||
// char chksum[8]; /* 148 */
|
||||
// TypeFlag typeflag; /* 156 */
|
||||
// char linkname[100]; /* 157 */
|
||||
// char magic[6]; /* 257 */
|
||||
// char version[2]; /* 263 */
|
||||
// char uname[32]; /* 265 */
|
||||
// char gname[32]; /* 297 */
|
||||
// char devmajor[8]; /* 329 */
|
||||
// char devminor[8]; /* 337 */
|
||||
// char prefix[155]; /* 345 */
|
||||
// /* 500 */
|
||||
// };
|
||||
|
||||
bool readLonglink(QIODevice* in, qint64 size, QByteArray& longlink)
|
||||
{
|
||||
qint64 n = 0;
|
||||
size--; // ignore trailing null
|
||||
if (size < 0) {
|
||||
qCritical() << "The filename size is negative";
|
||||
return false;
|
||||
}
|
||||
longlink.resize(size + (BLOCKSIZE - size % BLOCKSIZE)); // make the size divisible by BLOCKSIZE
|
||||
for (qint64 offset = 0; offset < longlink.size(); offset += BLOCKSIZE) {
|
||||
n = in->read(longlink.data() + offset, BLOCKSIZE);
|
||||
if (n != BLOCKSIZE) {
|
||||
qCritical() << "The expected blocksize was not respected for the name";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
longlink.truncate(qstrlen(longlink.constData()));
|
||||
return true;
|
||||
}
|
||||
|
||||
int getOctal(char* buffer, int maxlenght, bool* ok)
|
||||
{
|
||||
return QByteArray(buffer, qstrnlen(buffer, maxlenght)).toInt(ok, 8);
|
||||
}
|
||||
|
||||
QString decodeName(char* name)
|
||||
{
|
||||
return QFile::decodeName(QByteArray(name, qstrnlen(name, 100)));
|
||||
}
|
||||
bool Tar::extract(QIODevice* in, QString dst)
|
||||
{
|
||||
char buffer[BLOCKSIZE];
|
||||
QString name, symlink, firstFolderName;
|
||||
bool doNotReset = false, ok;
|
||||
while (true) {
|
||||
auto n = in->read(buffer, BLOCKSIZE);
|
||||
if (n != BLOCKSIZE) { // allways expect complete blocks
|
||||
qCritical() << "The expected blocksize was not respected";
|
||||
return false;
|
||||
}
|
||||
if (buffer[0] == 0) { // end of archive
|
||||
return true;
|
||||
}
|
||||
int mode = getOctal(buffer + 100, 8, &ok) | QFile::ReadUser | QFile::WriteUser; // hack to ensure write and read permisions
|
||||
if (!ok) {
|
||||
qCritical() << "The file mode can't be read";
|
||||
return false;
|
||||
}
|
||||
// there are names that are exactly 100 bytes long
|
||||
// and neither longlink nor \0 terminated (bug:101472)
|
||||
|
||||
if (name.isEmpty()) {
|
||||
name = decodeName(buffer);
|
||||
if (!firstFolderName.isEmpty() && name.startsWith(firstFolderName)) {
|
||||
name = name.mid(firstFolderName.size());
|
||||
}
|
||||
}
|
||||
if (symlink.isEmpty())
|
||||
symlink = decodeName(buffer);
|
||||
qint64 size = getOctal(buffer + 124, 12, &ok);
|
||||
if (!ok) {
|
||||
qCritical() << "The file size can't be read";
|
||||
return false;
|
||||
}
|
||||
switch (TypeFlag(buffer[156])) {
|
||||
case TypeFlag::Regular:
|
||||
/* fallthrough */
|
||||
case TypeFlag::ARegular: {
|
||||
auto fileName = FS::PathCombine(dst, name);
|
||||
if (!FS::ensureFilePathExists(fileName)) {
|
||||
qCritical() << "Can't ensure the file path to exist: " << fileName;
|
||||
return false;
|
||||
}
|
||||
QFile out(fileName);
|
||||
if (!out.open(QFile::WriteOnly)) {
|
||||
qCritical() << "Can't open file:" << fileName;
|
||||
return false;
|
||||
}
|
||||
out.setPermissions(QFile::Permissions(mode));
|
||||
while (size > 0) {
|
||||
QByteArray tmp(BLOCKSIZE, 0);
|
||||
n = in->read(tmp.data(), BLOCKSIZE);
|
||||
if (n != BLOCKSIZE) {
|
||||
qCritical() << "The expected blocksize was not respected when reading file";
|
||||
return false;
|
||||
}
|
||||
tmp.truncate(qMin(qint64(BLOCKSIZE), size));
|
||||
out.write(tmp);
|
||||
size -= BLOCKSIZE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TypeFlag::Directory: {
|
||||
if (firstFolderName.isEmpty()) {
|
||||
firstFolderName = name;
|
||||
break;
|
||||
}
|
||||
auto folderPath = FS::PathCombine(dst, name);
|
||||
if (!FS::ensureFolderPathExists(folderPath)) {
|
||||
qCritical() << "Can't ensure that folder exists: " << folderPath;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TypeFlag::GNULongLink: {
|
||||
doNotReset = true;
|
||||
QByteArray longlink;
|
||||
if (readLonglink(in, size, longlink)) {
|
||||
symlink = QFile::decodeName(longlink.constData());
|
||||
} else {
|
||||
qCritical() << "Failed to read long link";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TypeFlag::GNULongName: {
|
||||
doNotReset = true;
|
||||
QByteArray longlink;
|
||||
if (readLonglink(in, size, longlink)) {
|
||||
name = QFile::decodeName(longlink.constData());
|
||||
} else {
|
||||
qCritical() << "Failed to read long name";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TypeFlag::Link:
|
||||
/* fallthrough */
|
||||
case TypeFlag::Symlink: {
|
||||
auto fileName = FS::PathCombine(dst, name);
|
||||
if (!FS::create_link(FS::PathCombine(QFileInfo(fileName).path(), symlink), fileName)()) { // do not use symlinks
|
||||
qCritical() << "Can't create link for:" << fileName << " to:" << FS::PathCombine(QFileInfo(fileName).path(), symlink);
|
||||
return false;
|
||||
}
|
||||
FS::ensureFilePathExists(fileName);
|
||||
QFile::setPermissions(fileName, QFile::Permissions(mode));
|
||||
break;
|
||||
}
|
||||
case TypeFlag::Character:
|
||||
/* fallthrough */
|
||||
case TypeFlag::Block:
|
||||
/* fallthrough */
|
||||
case TypeFlag::FIFO:
|
||||
/* fallthrough */
|
||||
case TypeFlag::Contiguous:
|
||||
/* fallthrough */
|
||||
case TypeFlag::GlobalPosixHeader:
|
||||
/* fallthrough */
|
||||
case TypeFlag::ExtendedPosixHeader:
|
||||
/* fallthrough */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!doNotReset) {
|
||||
name.truncate(0);
|
||||
symlink.truncate(0);
|
||||
}
|
||||
doNotReset = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GZTar::extract(QString src, QString dst)
|
||||
{
|
||||
QuaGzipFile a(src);
|
||||
if (!a.open(QIODevice::ReadOnly)) {
|
||||
qCritical() << "Can't open tar file:" << src;
|
||||
return false;
|
||||
}
|
||||
return Tar::extract(&a, dst);
|
||||
}
|
46
launcher/Untar.h
Normal file
46
launcher/Untar.h
Normal file
@ -0,0 +1,46 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#pragma once
|
||||
#include <QIODevice>
|
||||
|
||||
// this is a hack used for the java downloader (feel free to remove it in favor of a library)
|
||||
// both extract functions will extract the first folder inside dest(disregarding the prefix)
|
||||
namespace Tar {
|
||||
bool extract(QIODevice* in, QString dst);
|
||||
}
|
||||
|
||||
namespace GZTar {
|
||||
bool extract(QString src, QString dst);
|
||||
}
|
@ -79,7 +79,7 @@ void Version::parse()
|
||||
if (m_string.isEmpty())
|
||||
return;
|
||||
|
||||
auto classChange = [&](QChar lastChar, QChar currentChar) {
|
||||
auto classChange = [¤tSection](QChar lastChar, QChar currentChar) {
|
||||
if (lastChar.isNull())
|
||||
return false;
|
||||
if (lastChar.isDigit() != currentChar.isDigit())
|
||||
@ -123,8 +123,7 @@ QDebug operator<<(QDebug debug, const Version& v)
|
||||
first = false;
|
||||
}
|
||||
|
||||
debug.nospace() << " ]"
|
||||
<< " }";
|
||||
debug.nospace() << " ]" << " }";
|
||||
|
||||
return debug;
|
||||
}
|
||||
|
@ -114,10 +114,14 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation,
|
||||
return tr("Branch");
|
||||
case Type:
|
||||
return tr("Type");
|
||||
case Architecture:
|
||||
case CPUArchitecture:
|
||||
return tr("Architecture");
|
||||
case Path:
|
||||
return tr("Path");
|
||||
case JavaName:
|
||||
return tr("Java Name");
|
||||
case JavaMajor:
|
||||
return tr("Major Version");
|
||||
case Time:
|
||||
return tr("Released");
|
||||
}
|
||||
@ -131,10 +135,14 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation,
|
||||
return tr("The version's branch");
|
||||
case Type:
|
||||
return tr("The version's type");
|
||||
case Architecture:
|
||||
case CPUArchitecture:
|
||||
return tr("CPU Architecture");
|
||||
case Path:
|
||||
return tr("Filesystem path to this version");
|
||||
case JavaName:
|
||||
return tr("The alternative name of the Java version");
|
||||
case JavaMajor:
|
||||
return tr("The Java major version");
|
||||
case Time:
|
||||
return tr("Release date of this version");
|
||||
}
|
||||
@ -165,10 +173,14 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::BranchRole);
|
||||
case Type:
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::TypeRole);
|
||||
case Architecture:
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::ArchitectureRole);
|
||||
case CPUArchitecture:
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::CPUArchitectureRole);
|
||||
case Path:
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::PathRole);
|
||||
case JavaName:
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::JavaNameRole);
|
||||
case JavaMajor:
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::JavaMajorRole);
|
||||
case Time:
|
||||
return sourceModel()->data(parentIndex, Meta::VersionList::TimeRole).toDate();
|
||||
default:
|
||||
@ -295,6 +307,7 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw)
|
||||
if (!replacing) {
|
||||
roles.clear();
|
||||
filterModel->setSourceModel(replacing);
|
||||
endResetModel();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -308,12 +321,18 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw)
|
||||
m_columns.push_back(ParentVersion);
|
||||
}
|
||||
*/
|
||||
if (roles.contains(BaseVersionList::ArchitectureRole)) {
|
||||
m_columns.push_back(Architecture);
|
||||
if (roles.contains(BaseVersionList::CPUArchitectureRole)) {
|
||||
m_columns.push_back(CPUArchitecture);
|
||||
}
|
||||
if (roles.contains(BaseVersionList::PathRole)) {
|
||||
m_columns.push_back(Path);
|
||||
}
|
||||
if (roles.contains(BaseVersionList::JavaNameRole)) {
|
||||
m_columns.push_back(JavaName);
|
||||
}
|
||||
if (roles.contains(BaseVersionList::JavaMajorRole)) {
|
||||
m_columns.push_back(JavaMajor);
|
||||
}
|
||||
if (roles.contains(Meta::VersionList::TimeRole)) {
|
||||
m_columns.push_back(Time);
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ class VersionFilterModel;
|
||||
class VersionProxyModel : public QAbstractProxyModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Column { Name, ParentVersion, Branch, Type, Architecture, Path, Time };
|
||||
enum Column { Name, ParentVersion, Branch, Type, CPUArchitecture, Path, Time, JavaName, JavaMajor };
|
||||
using FilterMap = QHash<BaseVersionList::ModelRoles, std::shared_ptr<Filter>>;
|
||||
|
||||
public:
|
||||
|
@ -104,11 +104,11 @@ void FileLinkApp::joinServer(QString server)
|
||||
|
||||
in.setDevice(&socket);
|
||||
|
||||
connect(&socket, &QLocalSocket::connected, this, [&]() { qDebug() << "connected to server"; });
|
||||
connect(&socket, &QLocalSocket::connected, this, []() { qDebug() << "connected to server"; });
|
||||
|
||||
connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs);
|
||||
|
||||
connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError) {
|
||||
connect(&socket, &QLocalSocket::errorOccurred, this, [this](QLocalSocket::LocalSocketError socketError) {
|
||||
m_status = Failed;
|
||||
switch (socketError) {
|
||||
case QLocalSocket::ServerNotFoundError:
|
||||
@ -132,7 +132,7 @@ void FileLinkApp::joinServer(QString server)
|
||||
}
|
||||
});
|
||||
|
||||
connect(&socket, &QLocalSocket::disconnected, this, [&]() {
|
||||
connect(&socket, &QLocalSocket::disconnected, this, [this]() {
|
||||
qDebug() << "disconnected from server, should exit";
|
||||
m_status = Succeeded;
|
||||
exit();
|
||||
|
@ -37,7 +37,6 @@
|
||||
#include "IconList.h"
|
||||
#include <FileSystem.h>
|
||||
#include <QDebug>
|
||||
#include <QEventLoop>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QMap>
|
||||
#include <QMimeData>
|
||||
@ -47,24 +46,24 @@
|
||||
|
||||
#define MAX_SIZE 1024
|
||||
|
||||
IconList::IconList(const QStringList& builtinPaths, QString path, QObject* parent) : QAbstractListModel(parent)
|
||||
IconList::IconList(const QStringList& builtinPaths, const QString& path, QObject* parent) : QAbstractListModel(parent)
|
||||
{
|
||||
QSet<QString> builtinNames;
|
||||
|
||||
// add builtin icons
|
||||
for (auto& builtinPath : builtinPaths) {
|
||||
QDir instance_icons(builtinPath);
|
||||
auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name);
|
||||
for (auto file_info : file_info_list) {
|
||||
builtinNames.insert(file_info.completeBaseName());
|
||||
for (const auto& builtinPath : builtinPaths) {
|
||||
QDir instanceIcons(builtinPath);
|
||||
auto fileInfoList = instanceIcons.entryInfoList(QDir::Files, QDir::Name);
|
||||
for (const auto& fileInfo : fileInfoList) {
|
||||
builtinNames.insert(fileInfo.baseName());
|
||||
}
|
||||
}
|
||||
for (auto& builtinName : builtinNames) {
|
||||
for (const auto& builtinName : builtinNames) {
|
||||
addThemeIcon(builtinName);
|
||||
}
|
||||
|
||||
m_watcher.reset(new QFileSystemWatcher());
|
||||
is_watching = false;
|
||||
m_isWatching = false;
|
||||
connect(m_watcher.get(), &QFileSystemWatcher::directoryChanged, this, &IconList::directoryChanged);
|
||||
connect(m_watcher.get(), &QFileSystemWatcher::fileChanged, this, &IconList::fileChanged);
|
||||
|
||||
@ -77,91 +76,131 @@ IconList::IconList(const QStringList& builtinPaths, QString path, QObject* paren
|
||||
void IconList::sortIconList()
|
||||
{
|
||||
qDebug() << "Sorting icon list...";
|
||||
std::sort(icons.begin(), icons.end(), [](const MMCIcon& a, const MMCIcon& b) { return a.m_key.localeAwareCompare(b.m_key) < 0; });
|
||||
std::sort(m_icons.begin(), m_icons.end(), [](const MMCIcon& a, const MMCIcon& b) {
|
||||
bool aIsSubdir = a.m_key.contains(QDir::separator());
|
||||
bool bIsSubdir = b.m_key.contains(QDir::separator());
|
||||
if (aIsSubdir != bIsSubdir) {
|
||||
return !aIsSubdir; // root-level icons come first
|
||||
}
|
||||
return a.m_key.localeAwareCompare(b.m_key) < 0;
|
||||
});
|
||||
reindex();
|
||||
}
|
||||
|
||||
// Helper function to add directories recursively
|
||||
bool IconList::addPathRecursively(const QString& path)
|
||||
{
|
||||
QDir dir(path);
|
||||
if (!dir.exists())
|
||||
return false;
|
||||
|
||||
// Add the directory itself
|
||||
bool watching = m_watcher->addPath(path);
|
||||
|
||||
// Add all subdirectories
|
||||
QFileInfoList entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
for (const QFileInfo& entry : entries) {
|
||||
if (addPathRecursively(entry.absoluteFilePath())) {
|
||||
watching = true;
|
||||
}
|
||||
}
|
||||
return watching;
|
||||
}
|
||||
|
||||
QStringList IconList::getIconFilePaths() const
|
||||
{
|
||||
QStringList iconFiles{};
|
||||
QStringList directories{ m_dir.absolutePath() };
|
||||
while (!directories.isEmpty()) {
|
||||
QString first = directories.takeFirst();
|
||||
QDir dir(first);
|
||||
for (QFileInfo& fileInfo : dir.entryInfoList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot, QDir::Name)) {
|
||||
if (fileInfo.isDir())
|
||||
directories.push_back(fileInfo.absoluteFilePath());
|
||||
else
|
||||
iconFiles.push_back(fileInfo.absoluteFilePath());
|
||||
}
|
||||
}
|
||||
return iconFiles;
|
||||
}
|
||||
|
||||
QString formatName(const QDir& iconsDir, const QFileInfo& iconFile)
|
||||
{
|
||||
if (iconFile.dir() == iconsDir)
|
||||
return iconFile.baseName();
|
||||
|
||||
constexpr auto delimiter = " » ";
|
||||
QString relativePathWithoutExtension = iconsDir.relativeFilePath(iconFile.dir().path()) + QDir::separator() + iconFile.baseName();
|
||||
return relativePathWithoutExtension.replace(QDir::separator(), delimiter);
|
||||
}
|
||||
|
||||
/// Split into a separate function because the preprocessing impedes readability
|
||||
QSet<QString> toStringSet(const QList<QString>& list)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
QSet<QString> set(list.begin(), list.end());
|
||||
#else
|
||||
QSet<QString> set = list.toSet();
|
||||
#endif
|
||||
return set;
|
||||
}
|
||||
|
||||
void IconList::directoryChanged(const QString& path)
|
||||
{
|
||||
QDir new_dir(path);
|
||||
if (m_dir.absolutePath() != new_dir.absolutePath()) {
|
||||
QDir newDir(path);
|
||||
if (m_dir.absolutePath() != newDir.absolutePath()) {
|
||||
if (!path.startsWith(m_dir.absolutePath()))
|
||||
m_dir.setPath(path);
|
||||
m_dir.refresh();
|
||||
if (is_watching)
|
||||
if (m_isWatching)
|
||||
stopWatching();
|
||||
startWatching();
|
||||
}
|
||||
if (!m_dir.exists())
|
||||
if (!FS::ensureFolderPathExists(m_dir.absolutePath()))
|
||||
if (!m_dir.exists() && !FS::ensureFolderPathExists(m_dir.absolutePath()))
|
||||
return;
|
||||
m_dir.refresh();
|
||||
auto new_list = m_dir.entryList(QDir::Files, QDir::Name);
|
||||
for (auto it = new_list.begin(); it != new_list.end(); it++) {
|
||||
QString& foo = (*it);
|
||||
foo = m_dir.filePath(foo);
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
QSet<QString> new_set(new_list.begin(), new_list.end());
|
||||
#else
|
||||
auto new_set = new_list.toSet();
|
||||
#endif
|
||||
QList<QString> current_list;
|
||||
for (auto& it : icons) {
|
||||
const QStringList newFileNamesList = getIconFilePaths();
|
||||
const QSet<QString> newSet = toStringSet(newFileNamesList);
|
||||
QSet<QString> currentSet;
|
||||
for (const MMCIcon& it : m_icons) {
|
||||
if (!it.has(IconType::FileBased))
|
||||
continue;
|
||||
current_list.push_back(it.m_images[IconType::FileBased].filename);
|
||||
currentSet.insert(it.m_images[IconType::FileBased].filename);
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
QSet<QString> current_set(current_list.begin(), current_list.end());
|
||||
#else
|
||||
QSet<QString> current_set = current_list.toSet();
|
||||
#endif
|
||||
QSet<QString> toRemove = currentSet - newSet;
|
||||
QSet<QString> toAdd = newSet - currentSet;
|
||||
|
||||
QSet<QString> to_remove = current_set;
|
||||
to_remove -= new_set;
|
||||
|
||||
QSet<QString> to_add = new_set;
|
||||
to_add -= current_set;
|
||||
|
||||
for (auto remove : to_remove) {
|
||||
qDebug() << "Removing " << remove;
|
||||
QFileInfo rmfile(remove);
|
||||
QString key = rmfile.completeBaseName();
|
||||
|
||||
QString suffix = rmfile.suffix();
|
||||
// The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well
|
||||
if (!IconUtils::isIconSuffix(suffix))
|
||||
key = rmfile.fileName();
|
||||
for (const QString& removedPath : toRemove) {
|
||||
qDebug() << "Removing icon " << removedPath;
|
||||
QFileInfo removedFile(removedPath);
|
||||
QString key = m_dir.relativeFilePath(removedFile.absoluteFilePath());
|
||||
|
||||
int idx = getIconIndex(key);
|
||||
if (idx == -1)
|
||||
continue;
|
||||
icons[idx].remove(IconType::FileBased);
|
||||
if (icons[idx].type() == IconType::ToBeDeleted) {
|
||||
m_icons[idx].remove(FileBased);
|
||||
if (m_icons[idx].type() == ToBeDeleted) {
|
||||
beginRemoveRows(QModelIndex(), idx, idx);
|
||||
icons.remove(idx);
|
||||
m_icons.remove(idx);
|
||||
reindex();
|
||||
endRemoveRows();
|
||||
} else {
|
||||
dataChanged(index(idx), index(idx));
|
||||
}
|
||||
m_watcher->removePath(remove);
|
||||
m_watcher->removePath(removedPath);
|
||||
emit iconUpdated(key);
|
||||
}
|
||||
|
||||
for (auto add : to_add) {
|
||||
qDebug() << "Adding " << add;
|
||||
for (const QString& addedPath : toAdd) {
|
||||
qDebug() << "Adding icon " << addedPath;
|
||||
|
||||
QFileInfo addfile(add);
|
||||
QString key = addfile.completeBaseName();
|
||||
QFileInfo addfile(addedPath);
|
||||
QString relativePath = m_dir.relativeFilePath(addfile.absoluteFilePath());
|
||||
QString key = QFileInfo(relativePath).completeBaseName();
|
||||
QString name = formatName(m_dir, addfile);
|
||||
|
||||
QString suffix = addfile.suffix();
|
||||
// The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well
|
||||
if (!IconUtils::isIconSuffix(suffix))
|
||||
key = addfile.fileName();
|
||||
|
||||
if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased)) {
|
||||
m_watcher->addPath(add);
|
||||
if (addIcon(key, name, addfile.filePath(), IconType::FileBased)) {
|
||||
m_watcher->addPath(addedPath);
|
||||
emit iconUpdated(key);
|
||||
}
|
||||
}
|
||||
@ -171,24 +210,24 @@ void IconList::directoryChanged(const QString& path)
|
||||
|
||||
void IconList::fileChanged(const QString& path)
|
||||
{
|
||||
qDebug() << "Checking " << path;
|
||||
qDebug() << "Checking icon " << path;
|
||||
QFileInfo checkfile(path);
|
||||
if (!checkfile.exists())
|
||||
return;
|
||||
QString key = checkfile.completeBaseName();
|
||||
QString key = m_dir.relativeFilePath(checkfile.absoluteFilePath());
|
||||
int idx = getIconIndex(key);
|
||||
if (idx == -1)
|
||||
return;
|
||||
QIcon icon(path);
|
||||
if (!icon.availableSizes().size())
|
||||
if (icon.availableSizes().empty())
|
||||
return;
|
||||
|
||||
icons[idx].m_images[IconType::FileBased].icon = icon;
|
||||
m_icons[idx].m_images[IconType::FileBased].icon = icon;
|
||||
dataChanged(index(idx), index(idx));
|
||||
emit iconUpdated(key);
|
||||
}
|
||||
|
||||
void IconList::SettingChanged(const Setting& setting, QVariant value)
|
||||
void IconList::SettingChanged(const Setting& setting, const QVariant& value)
|
||||
{
|
||||
if (setting.id() != "IconsDir")
|
||||
return;
|
||||
@ -200,8 +239,8 @@ void IconList::startWatching()
|
||||
{
|
||||
auto abs_path = m_dir.absolutePath();
|
||||
FS::ensureFolderPathExists(abs_path);
|
||||
is_watching = m_watcher->addPath(abs_path);
|
||||
if (is_watching) {
|
||||
m_isWatching = addPathRecursively(abs_path);
|
||||
if (m_isWatching) {
|
||||
qDebug() << "Started watching " << abs_path;
|
||||
} else {
|
||||
qDebug() << "Failed to start watching " << abs_path;
|
||||
@ -212,7 +251,7 @@ void IconList::stopWatching()
|
||||
{
|
||||
m_watcher->removePaths(m_watcher->files());
|
||||
m_watcher->removePaths(m_watcher->directories());
|
||||
is_watching = false;
|
||||
m_isWatching = false;
|
||||
}
|
||||
|
||||
QStringList IconList::mimeTypes() const
|
||||
@ -242,7 +281,7 @@ bool IconList::dropMimeData(const QMimeData* data,
|
||||
if (data->hasUrls()) {
|
||||
auto urls = data->urls();
|
||||
QStringList iconFiles;
|
||||
for (auto url : urls) {
|
||||
for (const auto& url : urls) {
|
||||
// only local files may be dropped...
|
||||
if (!url.isLocalFile())
|
||||
continue;
|
||||
@ -263,33 +302,33 @@ Qt::ItemFlags IconList::flags(const QModelIndex& index) const
|
||||
QVariant IconList::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
return {};
|
||||
|
||||
int row = index.row();
|
||||
|
||||
if (row < 0 || row >= icons.size())
|
||||
return QVariant();
|
||||
if (row < 0 || row >= m_icons.size())
|
||||
return {};
|
||||
|
||||
switch (role) {
|
||||
case Qt::DecorationRole:
|
||||
return icons[row].icon();
|
||||
return m_icons[row].icon();
|
||||
case Qt::DisplayRole:
|
||||
return icons[row].name();
|
||||
return m_icons[row].name();
|
||||
case Qt::UserRole:
|
||||
return icons[row].m_key;
|
||||
return m_icons[row].m_key;
|
||||
default:
|
||||
return QVariant();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
int IconList::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
return parent.isValid() ? 0 : icons.size();
|
||||
return parent.isValid() ? 0 : m_icons.size();
|
||||
}
|
||||
|
||||
void IconList::installIcons(const QStringList& iconFiles)
|
||||
{
|
||||
for (QString file : iconFiles)
|
||||
for (const QString& file : iconFiles)
|
||||
installIcon(file, {});
|
||||
}
|
||||
|
||||
@ -312,12 +351,13 @@ bool IconList::iconFileExists(const QString& key) const
|
||||
return iconEntry && iconEntry->has(IconType::FileBased);
|
||||
}
|
||||
|
||||
/// Returns the icon with the given key or nullptr if it doesn't exist.
|
||||
const MMCIcon* IconList::icon(const QString& key) const
|
||||
{
|
||||
int iconIdx = getIconIndex(key);
|
||||
if (iconIdx == -1)
|
||||
return nullptr;
|
||||
return &icons[iconIdx];
|
||||
return &m_icons[iconIdx];
|
||||
}
|
||||
|
||||
bool IconList::deleteIcon(const QString& key)
|
||||
@ -332,22 +372,22 @@ bool IconList::trashIcon(const QString& key)
|
||||
|
||||
bool IconList::addThemeIcon(const QString& key)
|
||||
{
|
||||
auto iter = name_index.find(key);
|
||||
if (iter != name_index.end()) {
|
||||
auto& oldOne = icons[*iter];
|
||||
auto iter = m_nameIndex.find(key);
|
||||
if (iter != m_nameIndex.end()) {
|
||||
auto& oldOne = m_icons[*iter];
|
||||
oldOne.replace(Builtin, key);
|
||||
dataChanged(index(*iter), index(*iter));
|
||||
return true;
|
||||
}
|
||||
// add a new icon
|
||||
beginInsertRows(QModelIndex(), icons.size(), icons.size());
|
||||
beginInsertRows(QModelIndex(), m_icons.size(), m_icons.size());
|
||||
{
|
||||
MMCIcon mmc_icon;
|
||||
mmc_icon.m_name = key;
|
||||
mmc_icon.m_key = key;
|
||||
mmc_icon.replace(Builtin, key);
|
||||
icons.push_back(mmc_icon);
|
||||
name_index[key] = icons.size() - 1;
|
||||
m_icons.push_back(mmc_icon);
|
||||
m_nameIndex[key] = m_icons.size() - 1;
|
||||
}
|
||||
endInsertRows();
|
||||
return true;
|
||||
@ -359,22 +399,22 @@ bool IconList::addIcon(const QString& key, const QString& name, const QString& p
|
||||
QIcon icon(path);
|
||||
if (icon.isNull())
|
||||
return false;
|
||||
auto iter = name_index.find(key);
|
||||
if (iter != name_index.end()) {
|
||||
auto& oldOne = icons[*iter];
|
||||
auto iter = m_nameIndex.find(key);
|
||||
if (iter != m_nameIndex.end()) {
|
||||
auto& oldOne = m_icons[*iter];
|
||||
oldOne.replace(type, icon, path);
|
||||
dataChanged(index(*iter), index(*iter));
|
||||
return true;
|
||||
}
|
||||
// add a new icon
|
||||
beginInsertRows(QModelIndex(), icons.size(), icons.size());
|
||||
beginInsertRows(QModelIndex(), m_icons.size(), m_icons.size());
|
||||
{
|
||||
MMCIcon mmc_icon;
|
||||
mmc_icon.m_name = name;
|
||||
mmc_icon.m_key = key;
|
||||
mmc_icon.replace(type, icon, path);
|
||||
icons.push_back(mmc_icon);
|
||||
name_index[key] = icons.size() - 1;
|
||||
m_icons.push_back(mmc_icon);
|
||||
m_nameIndex[key] = m_icons.size() - 1;
|
||||
}
|
||||
endInsertRows();
|
||||
return true;
|
||||
@ -389,33 +429,32 @@ void IconList::saveIcon(const QString& key, const QString& path, const char* for
|
||||
|
||||
void IconList::reindex()
|
||||
{
|
||||
name_index.clear();
|
||||
int i = 0;
|
||||
for (auto& iter : icons) {
|
||||
name_index[iter.m_key] = i;
|
||||
i++;
|
||||
m_nameIndex.clear();
|
||||
for (int i = 0; i < m_icons.size(); i++) {
|
||||
m_nameIndex[m_icons[i].m_key] = i;
|
||||
emit iconUpdated(m_icons[i].m_key); // prevents incorrect indices with proxy model
|
||||
}
|
||||
}
|
||||
|
||||
QIcon IconList::getIcon(const QString& key) const
|
||||
{
|
||||
int icon_index = getIconIndex(key);
|
||||
int iconIndex = getIconIndex(key);
|
||||
|
||||
if (icon_index != -1)
|
||||
return icons[icon_index].icon();
|
||||
if (iconIndex != -1)
|
||||
return m_icons[iconIndex].icon();
|
||||
|
||||
// Fallback for icons that don't exist.
|
||||
icon_index = getIconIndex("grass");
|
||||
// Fallback for icons that don't exist.b
|
||||
iconIndex = getIconIndex("grass");
|
||||
|
||||
if (icon_index != -1)
|
||||
return icons[icon_index].icon();
|
||||
return QIcon();
|
||||
if (iconIndex != -1)
|
||||
return m_icons[iconIndex].icon();
|
||||
return {};
|
||||
}
|
||||
|
||||
int IconList::getIconIndex(const QString& key) const
|
||||
{
|
||||
auto iter = name_index.find(key == "default" ? "grass" : key);
|
||||
if (iter != name_index.end())
|
||||
auto iter = m_nameIndex.find(key == "default" ? "grass" : key);
|
||||
if (iter != m_nameIndex.end())
|
||||
return *iter;
|
||||
|
||||
return -1;
|
||||
@ -425,3 +464,15 @@ QString IconList::getDirectory() const
|
||||
{
|
||||
return m_dir.absolutePath();
|
||||
}
|
||||
|
||||
/// Returns the directory of the icon with the given key or the default directory if it's a builtin icon.
|
||||
QString IconList::iconDirectory(const QString& key) const
|
||||
{
|
||||
for (const auto& mmcIcon : m_icons) {
|
||||
if (mmcIcon.m_key == key && mmcIcon.has(IconType::FileBased)) {
|
||||
QFileInfo iconFile(mmcIcon.getFilePath());
|
||||
return iconFile.dir().path();
|
||||
}
|
||||
}
|
||||
return getDirectory();
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ class QFileSystemWatcher;
|
||||
class IconList : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit IconList(const QStringList& builtinPaths, QString path, QObject* parent = 0);
|
||||
explicit IconList(const QStringList& builtinPaths, const QString& path, QObject* parent = 0);
|
||||
virtual ~IconList() {};
|
||||
|
||||
QIcon getIcon(const QString& key) const;
|
||||
@ -72,6 +72,7 @@ class IconList : public QAbstractListModel {
|
||||
bool deleteIcon(const QString& key);
|
||||
bool trashIcon(const QString& key);
|
||||
bool iconFileExists(const QString& key) const;
|
||||
QString iconDirectory(const QString& key) const;
|
||||
|
||||
void installIcons(const QStringList& iconFiles);
|
||||
void installIcon(const QString& file, const QString& name);
|
||||
@ -91,18 +92,20 @@ class IconList : public QAbstractListModel {
|
||||
IconList& operator=(const IconList&) = delete;
|
||||
void reindex();
|
||||
void sortIconList();
|
||||
bool addPathRecursively(const QString& path);
|
||||
QStringList getIconFilePaths() const;
|
||||
|
||||
public slots:
|
||||
void directoryChanged(const QString& path);
|
||||
|
||||
protected slots:
|
||||
void fileChanged(const QString& path);
|
||||
void SettingChanged(const Setting& setting, QVariant value);
|
||||
void SettingChanged(const Setting& setting, const QVariant& value);
|
||||
|
||||
private:
|
||||
shared_qobject_ptr<QFileSystemWatcher> m_watcher;
|
||||
bool is_watching;
|
||||
QMap<QString, int> name_index;
|
||||
QVector<MMCIcon> icons;
|
||||
bool m_isWatching;
|
||||
QMap<QString, int> m_nameIndex;
|
||||
QVector<MMCIcon> m_icons;
|
||||
QDir m_dir;
|
||||
};
|
||||
|
@ -39,7 +39,7 @@
|
||||
#include "FileSystem.h"
|
||||
|
||||
namespace {
|
||||
static const QStringList validIconExtensions = { { "svg", "png", "ico", "gif", "jpg", "jpeg" } };
|
||||
static const QStringList validIconExtensions = { { "svg", "png", "ico", "gif", "jpg", "jpeg", "webp" } };
|
||||
}
|
||||
|
||||
namespace IconUtils {
|
||||
|
@ -40,14 +40,15 @@
|
||||
#include <QMap>
|
||||
#include <QProcess>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Commandline.h"
|
||||
#include "FileSystem.h"
|
||||
#include "JavaUtils.h"
|
||||
#include "java/JavaUtils.h"
|
||||
|
||||
JavaChecker::JavaChecker(QObject* parent) : QObject(parent) {}
|
||||
JavaChecker::JavaChecker(QString path, QString args, int minMem, int maxMem, int permGen, int id)
|
||||
: Task(), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen), m_id(id)
|
||||
{}
|
||||
|
||||
void JavaChecker::performCheck()
|
||||
void JavaChecker::executeTask()
|
||||
{
|
||||
QString checkerJar = JavaUtils::getJavaCheckPath();
|
||||
|
||||
@ -72,7 +73,7 @@ void JavaChecker::performCheck()
|
||||
if (m_maxMem != 0) {
|
||||
args << QString("-Xmx%1m").arg(m_maxMem);
|
||||
}
|
||||
if (m_permGen != 64) {
|
||||
if (m_permGen != 64 && m_permGen != 0) {
|
||||
args << QString("-XX:PermSize=%1m").arg(m_permGen);
|
||||
}
|
||||
|
||||
@ -115,11 +116,10 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||
QProcessPtr _process = process;
|
||||
process.reset();
|
||||
|
||||
JavaCheckResult result;
|
||||
{
|
||||
result.path = m_path;
|
||||
result.id = m_id;
|
||||
}
|
||||
Result result = {
|
||||
m_path,
|
||||
m_id,
|
||||
};
|
||||
result.errorLog = m_stderr;
|
||||
result.outLog = m_stdout;
|
||||
qDebug() << "STDOUT" << m_stdout;
|
||||
@ -127,8 +127,9 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||
qDebug() << "Java checker finished with status" << status << "exit code" << exitcode;
|
||||
|
||||
if (status == QProcess::CrashExit || exitcode == 1) {
|
||||
result.validity = JavaCheckResult::Validity::Errored;
|
||||
result.validity = Result::Validity::Errored;
|
||||
emit checkFinished(result);
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -161,17 +162,18 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||
}
|
||||
|
||||
if (!results.contains("os.arch") || !results.contains("java.version") || !results.contains("java.vendor") || !success) {
|
||||
result.validity = JavaCheckResult::Validity::ReturnedInvalidData;
|
||||
result.validity = Result::Validity::ReturnedInvalidData;
|
||||
emit checkFinished(result);
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
|
||||
auto os_arch = results["os.arch"];
|
||||
auto java_version = results["java.version"];
|
||||
auto java_vendor = results["java.vendor"];
|
||||
bool is_64 = os_arch == "x86_64" || os_arch == "amd64" || os_arch == "aarch64" || os_arch == "arm64";
|
||||
bool is_64 = os_arch == "x86_64" || os_arch == "amd64" || os_arch == "aarch64" || os_arch == "arm64" || os_arch == "riscv64";
|
||||
|
||||
result.validity = JavaCheckResult::Validity::Valid;
|
||||
result.validity = Result::Validity::Valid;
|
||||
result.is_64bit = is_64;
|
||||
result.mojangPlatform = is_64 ? "64" : "32";
|
||||
result.realPlatform = os_arch;
|
||||
@ -179,6 +181,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||
result.javaVendor = java_vendor;
|
||||
qDebug() << "Java checker succeeded.";
|
||||
emit checkFinished(result);
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
void JavaChecker::error(QProcess::ProcessError err)
|
||||
@ -190,15 +193,9 @@ void JavaChecker::error(QProcess::ProcessError err)
|
||||
qDebug() << "Native environment:";
|
||||
qDebug() << QProcessEnvironment::systemEnvironment().toStringList();
|
||||
killTimer.stop();
|
||||
JavaCheckResult result;
|
||||
{
|
||||
result.path = m_path;
|
||||
result.id = m_id;
|
||||
}
|
||||
|
||||
emit checkFinished(result);
|
||||
return;
|
||||
emit checkFinished({ m_path, m_id });
|
||||
}
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
void JavaChecker::timeout()
|
||||
|
@ -1,16 +1,20 @@
|
||||
#pragma once
|
||||
#include <QProcess>
|
||||
#include <QTimer>
|
||||
#include <memory>
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
|
||||
#include "JavaVersion.h"
|
||||
#include "QObjectPtr.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
class JavaChecker;
|
||||
class JavaChecker : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
using QProcessPtr = shared_qobject_ptr<QProcess>;
|
||||
using Ptr = shared_qobject_ptr<JavaChecker>;
|
||||
|
||||
struct JavaCheckResult {
|
||||
struct Result {
|
||||
QString path;
|
||||
int id;
|
||||
QString mojangPlatform;
|
||||
QString realPlatform;
|
||||
JavaVersion javaVersion;
|
||||
@ -18,34 +22,31 @@ struct JavaCheckResult {
|
||||
QString outLog;
|
||||
QString errorLog;
|
||||
bool is_64bit = false;
|
||||
int id;
|
||||
enum class Validity { Errored, ReturnedInvalidData, Valid } validity = Validity::Errored;
|
||||
};
|
||||
};
|
||||
|
||||
using QProcessPtr = shared_qobject_ptr<QProcess>;
|
||||
using JavaCheckerPtr = shared_qobject_ptr<JavaChecker>;
|
||||
class JavaChecker : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit JavaChecker(QObject* parent = 0);
|
||||
void performCheck();
|
||||
|
||||
QString m_path;
|
||||
QString m_args;
|
||||
int m_id = 0;
|
||||
int m_minMem = 0;
|
||||
int m_maxMem = 0;
|
||||
int m_permGen = 64;
|
||||
explicit JavaChecker(QString path, QString args, int minMem = 0, int maxMem = 0, int permGen = 0, int id = 0);
|
||||
|
||||
signals:
|
||||
void checkFinished(JavaCheckResult result);
|
||||
void checkFinished(const Result& result);
|
||||
|
||||
protected:
|
||||
virtual void executeTask() override;
|
||||
|
||||
private:
|
||||
QProcessPtr process;
|
||||
QTimer killTimer;
|
||||
QString m_stdout;
|
||||
QString m_stderr;
|
||||
public slots:
|
||||
|
||||
QString m_path;
|
||||
QString m_args;
|
||||
int m_minMem = 0;
|
||||
int m_maxMem = 0;
|
||||
int m_permGen = 64;
|
||||
int m_id = 0;
|
||||
|
||||
private slots:
|
||||
void timeout();
|
||||
void finished(int exitcode, QProcess::ExitStatus);
|
||||
void error(QProcess::ProcessError);
|
||||
|
@ -1,41 +0,0 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "JavaCheckerJob.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
void JavaCheckerJob::partFinished(JavaCheckResult result)
|
||||
{
|
||||
num_finished++;
|
||||
qDebug() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/" << javacheckers.size();
|
||||
setProgress(num_finished, javacheckers.size());
|
||||
|
||||
javaresults.replace(result.id, result);
|
||||
|
||||
if (num_finished == javacheckers.size()) {
|
||||
emitSucceeded();
|
||||
}
|
||||
}
|
||||
|
||||
void JavaCheckerJob::executeTask()
|
||||
{
|
||||
qDebug() << m_job_name.toLocal8Bit() << " started.";
|
||||
for (auto iter : javacheckers) {
|
||||
javaresults.append(JavaCheckResult());
|
||||
connect(iter.get(), &JavaChecker::checkFinished, this, &JavaCheckerJob::partFinished);
|
||||
iter->performCheck();
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtNetwork>
|
||||
#include "JavaChecker.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
class JavaCheckerJob;
|
||||
using JavaCheckerJobPtr = shared_qobject_ptr<JavaCheckerJob>;
|
||||
|
||||
// FIXME: this just seems horribly redundant
|
||||
class JavaCheckerJob : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name) {};
|
||||
virtual ~JavaCheckerJob() {};
|
||||
|
||||
bool addJavaCheckerAction(JavaCheckerPtr base)
|
||||
{
|
||||
javacheckers.append(base);
|
||||
// if this is already running, the action needs to be started right away!
|
||||
if (isRunning()) {
|
||||
setProgress(num_finished, javacheckers.size());
|
||||
connect(base.get(), &JavaChecker::checkFinished, this, &JavaCheckerJob::partFinished);
|
||||
base->performCheck();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
QList<JavaCheckResult> getResults() { return javaresults; }
|
||||
|
||||
private slots:
|
||||
void partFinished(JavaCheckResult result);
|
||||
|
||||
protected:
|
||||
virtual void executeTask() override;
|
||||
|
||||
private:
|
||||
QString m_job_name;
|
||||
QList<JavaCheckerPtr> javacheckers;
|
||||
QList<JavaCheckResult> javaresults;
|
||||
int num_finished = 0;
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -40,6 +40,7 @@ struct JavaInstall : public BaseVersion {
|
||||
QString arch;
|
||||
QString path;
|
||||
bool recommended = false;
|
||||
bool is_64bit = false;
|
||||
};
|
||||
|
||||
using JavaInstallPtr = std::shared_ptr<JavaInstall>;
|
||||
|
@ -38,13 +38,17 @@
|
||||
#include <QtXml>
|
||||
|
||||
#include <QDebug>
|
||||
#include <algorithm>
|
||||
|
||||
#include "java/JavaCheckerJob.h"
|
||||
#include "Application.h"
|
||||
#include "java/JavaChecker.h"
|
||||
#include "java/JavaInstallList.h"
|
||||
#include "java/JavaUtils.h"
|
||||
#include "minecraft/VersionFilterData.h"
|
||||
#include "tasks/ConcurrentTask.h"
|
||||
|
||||
JavaInstallList::JavaInstallList(QObject* parent) : BaseVersionList(parent) {}
|
||||
JavaInstallList::JavaInstallList(QObject* parent, bool onlyManagedVersions)
|
||||
: BaseVersionList(parent), m_only_managed_versions(onlyManagedVersions)
|
||||
{}
|
||||
|
||||
Task::Ptr JavaInstallList::getLoadTask()
|
||||
{
|
||||
@ -55,7 +59,7 @@ Task::Ptr JavaInstallList::getLoadTask()
|
||||
Task::Ptr JavaInstallList::getCurrentTask()
|
||||
{
|
||||
if (m_status == Status::InProgress) {
|
||||
return m_loadTask;
|
||||
return m_load_task;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@ -64,8 +68,8 @@ void JavaInstallList::load()
|
||||
{
|
||||
if (m_status != Status::InProgress) {
|
||||
m_status = Status::InProgress;
|
||||
m_loadTask.reset(new JavaListLoadTask(this));
|
||||
m_loadTask->start();
|
||||
m_load_task.reset(new JavaListLoadTask(this, m_only_managed_versions));
|
||||
m_load_task->start();
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +110,7 @@ QVariant JavaInstallList::data(const QModelIndex& index, int role) const
|
||||
return version->recommended;
|
||||
case PathRole:
|
||||
return version->path;
|
||||
case ArchitectureRole:
|
||||
case CPUArchitectureRole:
|
||||
return version->arch;
|
||||
default:
|
||||
return QVariant();
|
||||
@ -115,7 +119,7 @@ QVariant JavaInstallList::data(const QModelIndex& index, int role) const
|
||||
|
||||
BaseVersionList::RoleList JavaInstallList::providesRoles() const
|
||||
{
|
||||
return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, ArchitectureRole };
|
||||
return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, CPUArchitectureRole };
|
||||
}
|
||||
|
||||
void JavaInstallList::updateListData(QList<BaseVersion::Ptr> versions)
|
||||
@ -129,7 +133,7 @@ void JavaInstallList::updateListData(QList<BaseVersion::Ptr> versions)
|
||||
}
|
||||
endResetModel();
|
||||
m_status = Status::Done;
|
||||
m_loadTask.reset();
|
||||
m_load_task.reset();
|
||||
}
|
||||
|
||||
bool sortJavas(BaseVersion::Ptr left, BaseVersion::Ptr right)
|
||||
@ -146,35 +150,30 @@ void JavaInstallList::sortVersions()
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
JavaListLoadTask::JavaListLoadTask(JavaInstallList* vlist) : Task()
|
||||
JavaListLoadTask::JavaListLoadTask(JavaInstallList* vlist, bool onlyManagedVersions) : Task(), m_only_managed_versions(onlyManagedVersions)
|
||||
{
|
||||
m_list = vlist;
|
||||
m_currentRecommended = NULL;
|
||||
m_current_recommended = NULL;
|
||||
}
|
||||
|
||||
JavaListLoadTask::~JavaListLoadTask() {}
|
||||
|
||||
void JavaListLoadTask::executeTask()
|
||||
{
|
||||
setStatus(tr("Detecting Java installations..."));
|
||||
|
||||
JavaUtils ju;
|
||||
QList<QString> candidate_paths = ju.FindJavaPaths();
|
||||
QList<QString> candidate_paths = m_only_managed_versions ? getPrismJavaBundle() : ju.FindJavaPaths();
|
||||
|
||||
m_job.reset(new JavaCheckerJob("Java detection"));
|
||||
ConcurrentTask::Ptr job(new ConcurrentTask("Java detection", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()));
|
||||
m_job.reset(job);
|
||||
connect(m_job.get(), &Task::finished, this, &JavaListLoadTask::javaCheckerFinished);
|
||||
connect(m_job.get(), &Task::progress, this, &Task::setProgress);
|
||||
|
||||
qDebug() << "Probing the following Java paths: ";
|
||||
int id = 0;
|
||||
for (QString candidate : candidate_paths) {
|
||||
qDebug() << " " << candidate;
|
||||
|
||||
auto candidate_checker = new JavaChecker();
|
||||
candidate_checker->m_path = candidate;
|
||||
candidate_checker->m_id = id;
|
||||
m_job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker));
|
||||
|
||||
auto checker = new JavaChecker(candidate, "", 0, 0, 0, id);
|
||||
connect(checker, &JavaChecker::checkFinished, [this](const JavaChecker::Result& result) { m_results << result; });
|
||||
job->addTask(Task::Ptr(checker));
|
||||
id++;
|
||||
}
|
||||
|
||||
@ -184,16 +183,17 @@ void JavaListLoadTask::executeTask()
|
||||
void JavaListLoadTask::javaCheckerFinished()
|
||||
{
|
||||
QList<JavaInstallPtr> candidates;
|
||||
auto results = m_job->getResults();
|
||||
std::sort(m_results.begin(), m_results.end(), [](const JavaChecker::Result& a, const JavaChecker::Result& b) { return a.id < b.id; });
|
||||
|
||||
qDebug() << "Found the following valid Java installations:";
|
||||
for (JavaCheckResult result : results) {
|
||||
if (result.validity == JavaCheckResult::Validity::Valid) {
|
||||
for (auto result : m_results) {
|
||||
if (result.validity == JavaChecker::Result::Validity::Valid) {
|
||||
JavaInstallPtr javaVersion(new JavaInstall());
|
||||
|
||||
javaVersion->id = result.javaVersion;
|
||||
javaVersion->arch = result.realPlatform;
|
||||
javaVersion->path = result.path;
|
||||
javaVersion->is_64bit = result.is_64bit;
|
||||
candidates.append(javaVersion);
|
||||
|
||||
qDebug() << " " << javaVersion->id.toString() << javaVersion->arch << javaVersion->path;
|
||||
|
@ -19,9 +19,9 @@
|
||||
#include <QObject>
|
||||
|
||||
#include "BaseVersionList.h"
|
||||
#include "java/JavaChecker.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
#include "JavaCheckerJob.h"
|
||||
#include "JavaInstall.h"
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
@ -33,7 +33,7 @@ class JavaInstallList : public BaseVersionList {
|
||||
enum class Status { NotDone, InProgress, Done };
|
||||
|
||||
public:
|
||||
explicit JavaInstallList(QObject* parent = 0);
|
||||
explicit JavaInstallList(QObject* parent = 0, bool onlyManagedVersions = false);
|
||||
|
||||
[[nodiscard]] Task::Ptr getLoadTask() override;
|
||||
bool isLoaded() override;
|
||||
@ -53,23 +53,27 @@ class JavaInstallList : public BaseVersionList {
|
||||
|
||||
protected:
|
||||
Status m_status = Status::NotDone;
|
||||
shared_qobject_ptr<JavaListLoadTask> m_loadTask;
|
||||
shared_qobject_ptr<JavaListLoadTask> m_load_task;
|
||||
QList<BaseVersion::Ptr> m_vlist;
|
||||
bool m_only_managed_versions;
|
||||
};
|
||||
|
||||
class JavaListLoadTask : public Task {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit JavaListLoadTask(JavaInstallList* vlist);
|
||||
virtual ~JavaListLoadTask();
|
||||
explicit JavaListLoadTask(JavaInstallList* vlist, bool onlyManagedVersions = false);
|
||||
virtual ~JavaListLoadTask() = default;
|
||||
|
||||
protected:
|
||||
void executeTask() override;
|
||||
public slots:
|
||||
void javaCheckerFinished();
|
||||
|
||||
protected:
|
||||
shared_qobject_ptr<JavaCheckerJob> m_job;
|
||||
Task::Ptr m_job;
|
||||
JavaInstallList* m_list;
|
||||
JavaInstall* m_currentRecommended;
|
||||
JavaInstall* m_current_recommended;
|
||||
QList<JavaChecker::Result> m_results;
|
||||
bool m_only_managed_versions;
|
||||
};
|
||||
|
128
launcher/java/JavaMetadata.cpp
Normal file
128
launcher/java/JavaMetadata.cpp
Normal file
@ -0,0 +1,128 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "java/JavaMetadata.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "Json.h"
|
||||
#include "StringUtils.h"
|
||||
#include "java/JavaVersion.h"
|
||||
#include "minecraft/ParseUtils.h"
|
||||
|
||||
namespace Java {
|
||||
|
||||
DownloadType parseDownloadType(QString javaDownload)
|
||||
{
|
||||
if (javaDownload == "manifest")
|
||||
return DownloadType::Manifest;
|
||||
else if (javaDownload == "archive")
|
||||
return DownloadType::Archive;
|
||||
else
|
||||
return DownloadType::Unknown;
|
||||
}
|
||||
QString downloadTypeToString(DownloadType javaDownload)
|
||||
{
|
||||
switch (javaDownload) {
|
||||
case DownloadType::Manifest:
|
||||
return "manifest";
|
||||
case DownloadType::Archive:
|
||||
return "archive";
|
||||
case DownloadType::Unknown:
|
||||
break;
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
MetadataPtr parseJavaMeta(const QJsonObject& in)
|
||||
{
|
||||
auto meta = std::make_shared<Metadata>();
|
||||
|
||||
meta->m_name = Json::ensureString(in, "name", "");
|
||||
meta->vendor = Json::ensureString(in, "vendor", "");
|
||||
meta->url = Json::ensureString(in, "url", "");
|
||||
meta->releaseTime = timeFromS3Time(Json::ensureString(in, "releaseTime", ""));
|
||||
meta->downloadType = parseDownloadType(Json::ensureString(in, "downloadType", ""));
|
||||
meta->packageType = Json::ensureString(in, "packageType", "");
|
||||
meta->runtimeOS = Json::ensureString(in, "runtimeOS", "unknown");
|
||||
|
||||
if (in.contains("checksum")) {
|
||||
auto obj = Json::requireObject(in, "checksum");
|
||||
meta->checksumHash = Json::ensureString(obj, "hash", "");
|
||||
meta->checksumType = Json::ensureString(obj, "type", "");
|
||||
}
|
||||
|
||||
if (in.contains("version")) {
|
||||
auto obj = Json::requireObject(in, "version");
|
||||
auto name = Json::ensureString(obj, "name", "");
|
||||
auto major = Json::ensureInteger(obj, "major", 0);
|
||||
auto minor = Json::ensureInteger(obj, "minor", 0);
|
||||
auto security = Json::ensureInteger(obj, "security", 0);
|
||||
auto build = Json::ensureInteger(obj, "build", 0);
|
||||
meta->version = JavaVersion(major, minor, security, build, name);
|
||||
}
|
||||
return meta;
|
||||
}
|
||||
|
||||
bool Metadata::operator<(const Metadata& rhs)
|
||||
{
|
||||
auto id = version;
|
||||
if (id < rhs.version) {
|
||||
return true;
|
||||
}
|
||||
if (id > rhs.version) {
|
||||
return false;
|
||||
}
|
||||
auto date = releaseTime;
|
||||
if (date < rhs.releaseTime) {
|
||||
return true;
|
||||
}
|
||||
if (date > rhs.releaseTime) {
|
||||
return false;
|
||||
}
|
||||
return StringUtils::naturalCompare(m_name, rhs.m_name, Qt::CaseInsensitive) < 0;
|
||||
}
|
||||
|
||||
bool Metadata::operator==(const Metadata& rhs)
|
||||
{
|
||||
return version == rhs.version && m_name == rhs.m_name;
|
||||
}
|
||||
|
||||
bool Metadata::operator>(const Metadata& rhs)
|
||||
{
|
||||
return (!operator<(rhs)) && (!operator==(rhs));
|
||||
}
|
||||
|
||||
bool Metadata::operator<(BaseVersion& a)
|
||||
{
|
||||
try {
|
||||
return operator<(dynamic_cast<Metadata&>(a));
|
||||
} catch (const std::bad_cast& e) {
|
||||
return BaseVersion::operator<(a);
|
||||
}
|
||||
}
|
||||
|
||||
bool Metadata::operator>(BaseVersion& a)
|
||||
{
|
||||
try {
|
||||
return operator>(dynamic_cast<Metadata&>(a));
|
||||
} catch (const std::bad_cast& e) {
|
||||
return BaseVersion::operator>(a);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Java
|
64
launcher/java/JavaMetadata.h
Normal file
64
launcher/java/JavaMetadata.h
Normal file
@ -0,0 +1,64 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "BaseVersion.h"
|
||||
#include "java/JavaVersion.h"
|
||||
|
||||
namespace Java {
|
||||
|
||||
enum class DownloadType { Manifest, Archive, Unknown };
|
||||
|
||||
class Metadata : public BaseVersion {
|
||||
public:
|
||||
virtual QString descriptor() override { return version.toString(); }
|
||||
|
||||
virtual QString name() override { return m_name; }
|
||||
|
||||
virtual QString typeString() const override { return vendor; }
|
||||
|
||||
virtual bool operator<(BaseVersion& a) override;
|
||||
virtual bool operator>(BaseVersion& a) override;
|
||||
bool operator<(const Metadata& rhs);
|
||||
bool operator==(const Metadata& rhs);
|
||||
bool operator>(const Metadata& rhs);
|
||||
|
||||
QString m_name;
|
||||
QString vendor;
|
||||
QString url;
|
||||
QDateTime releaseTime;
|
||||
QString checksumType;
|
||||
QString checksumHash;
|
||||
DownloadType downloadType;
|
||||
QString packageType;
|
||||
JavaVersion version;
|
||||
QString runtimeOS;
|
||||
};
|
||||
using MetadataPtr = std::shared_ptr<Metadata>;
|
||||
|
||||
DownloadType parseDownloadType(QString javaDownload);
|
||||
QString downloadTypeToString(DownloadType javaDownload);
|
||||
MetadataPtr parseJavaMeta(const QJsonObject& libObj);
|
||||
|
||||
} // namespace Java
|
@ -102,6 +102,8 @@ QProcessEnvironment CleanEnviroment()
|
||||
QString newValue = stripVariableEntries(key, value, rawenv.value("LAUNCHER_" + key));
|
||||
|
||||
qDebug() << "Env: stripped" << key << value << "to" << newValue;
|
||||
|
||||
value = newValue;
|
||||
}
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||
// Strip IBus
|
||||
@ -347,6 +349,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
}
|
||||
|
||||
candidates.append(getMinecraftJavaBundle());
|
||||
candidates.append(getPrismJavaBundle());
|
||||
candidates = addJavasFromEnv(candidates);
|
||||
candidates.removeDuplicates();
|
||||
return candidates;
|
||||
@ -391,6 +394,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
}
|
||||
|
||||
javas.append(getMinecraftJavaBundle());
|
||||
javas.append(getPrismJavaBundle());
|
||||
javas = addJavasFromEnv(javas);
|
||||
javas.removeDuplicates();
|
||||
return javas;
|
||||
@ -401,12 +405,17 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
{
|
||||
QList<QString> javas;
|
||||
javas.append(this->GetDefaultJava()->path);
|
||||
auto scanJavaDir = [&](const QString& dirPath) {
|
||||
auto scanJavaDir = [&javas](
|
||||
const QString& dirPath,
|
||||
const std::function<bool(const QFileInfo&)>& filter = [](const QFileInfo&) { return true; }) {
|
||||
QDir dir(dirPath);
|
||||
if (!dir.exists())
|
||||
return;
|
||||
auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
for (auto& entry : entries) {
|
||||
if (!filter(entry))
|
||||
continue;
|
||||
|
||||
QString prefix;
|
||||
prefix = entry.canonicalFilePath();
|
||||
javas.append(FS::PathCombine(prefix, "jre/bin/java"));
|
||||
@ -415,7 +424,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
};
|
||||
// java installed in a snap is installed in the standard directory, but underneath $SNAP
|
||||
auto snap = qEnvironmentVariable("SNAP");
|
||||
auto scanJavaDirs = [&](const QString& dirPath) {
|
||||
auto scanJavaDirs = [scanJavaDir, snap](const QString& dirPath) {
|
||||
scanJavaDir(dirPath);
|
||||
if (!snap.isNull()) {
|
||||
scanJavaDir(snap + dirPath);
|
||||
@ -429,9 +438,19 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
scanJavaDirs("/usr/lib64/jvm");
|
||||
scanJavaDirs("/usr/lib32/jvm");
|
||||
// Gentoo's locations for openjdk and openjdk-bin respectively
|
||||
scanJavaDir("/usr/lib64");
|
||||
scanJavaDir("/usr/lib");
|
||||
scanJavaDir("/opt");
|
||||
auto gentooFilter = [](const QFileInfo& info) {
|
||||
QString fileName = info.fileName();
|
||||
return fileName.startsWith("openjdk-") || fileName.startsWith("openj9-");
|
||||
};
|
||||
// AOSC OS's locations for openjdk
|
||||
auto aoscFilter = [](const QFileInfo& info) {
|
||||
QString fileName = info.fileName();
|
||||
return fileName == "java" || fileName.startsWith("java-");
|
||||
};
|
||||
scanJavaDir("/usr/lib64", gentooFilter);
|
||||
scanJavaDir("/usr/lib", gentooFilter);
|
||||
scanJavaDir("/opt", gentooFilter);
|
||||
scanJavaDir("/usr/lib", aoscFilter);
|
||||
// javas stored in Prism Launcher's folder
|
||||
scanJavaDirs("java");
|
||||
// manually installed JDKs in /opt
|
||||
@ -454,6 +473,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
scanJavaDirs(FS::PathCombine(home, ".gradle/jdks"));
|
||||
|
||||
javas.append(getMinecraftJavaBundle());
|
||||
javas.append(getPrismJavaBundle());
|
||||
javas = addJavasFromEnv(javas);
|
||||
javas.removeDuplicates();
|
||||
return javas;
|
||||
@ -467,6 +487,8 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
javas.append(this->GetDefaultJava()->path);
|
||||
|
||||
javas.append(getMinecraftJavaBundle());
|
||||
javas.append(getPrismJavaBundle());
|
||||
javas.removeDuplicates();
|
||||
return addJavasFromEnv(javas);
|
||||
}
|
||||
#endif
|
||||
@ -478,12 +500,10 @@ QString JavaUtils::getJavaCheckPath()
|
||||
|
||||
QStringList getMinecraftJavaBundle()
|
||||
{
|
||||
QString executable = "java";
|
||||
QStringList processpaths;
|
||||
#if defined(Q_OS_OSX)
|
||||
processpaths << FS::PathCombine(QDir::homePath(), FS::PathCombine("Library", "Application Support", "minecraft", "runtime"));
|
||||
#elif defined(Q_OS_WIN32)
|
||||
executable += "w.exe";
|
||||
|
||||
auto appDataPath = QProcessEnvironment::systemEnvironment().value("APPDATA", "");
|
||||
processpaths << FS::PathCombine(QFileInfo(appDataPath).absoluteFilePath(), ".minecraft", "runtime");
|
||||
@ -508,7 +528,7 @@ QStringList getMinecraftJavaBundle()
|
||||
auto binFound = false;
|
||||
for (auto& entry : entries) {
|
||||
if (entry.baseName() == "bin") {
|
||||
javas.append(FS::PathCombine(entry.canonicalFilePath(), executable));
|
||||
javas.append(FS::PathCombine(entry.canonicalFilePath(), JavaUtils::javaExecutable));
|
||||
binFound = true;
|
||||
break;
|
||||
}
|
||||
@ -521,3 +541,33 @@ QStringList getMinecraftJavaBundle()
|
||||
}
|
||||
return javas;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_WIN32)
|
||||
const QString JavaUtils::javaExecutable = "javaw.exe";
|
||||
#else
|
||||
const QString JavaUtils::javaExecutable = "java";
|
||||
#endif
|
||||
|
||||
QStringList getPrismJavaBundle()
|
||||
{
|
||||
QList<QString> javas;
|
||||
|
||||
auto scanDir = [&javas](QString prefix) {
|
||||
javas.append(FS::PathCombine(prefix, "jre", "bin", JavaUtils::javaExecutable));
|
||||
javas.append(FS::PathCombine(prefix, "bin", JavaUtils::javaExecutable));
|
||||
javas.append(FS::PathCombine(prefix, JavaUtils::javaExecutable));
|
||||
};
|
||||
auto scanJavaDir = [scanDir](const QString& dirPath) {
|
||||
QDir dir(dirPath);
|
||||
if (!dir.exists())
|
||||
return;
|
||||
auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
for (auto& entry : entries) {
|
||||
scanDir(entry.canonicalFilePath());
|
||||
}
|
||||
};
|
||||
|
||||
scanJavaDir(APPLICATION->javaPath());
|
||||
|
||||
return javas;
|
||||
}
|
||||
|
@ -15,10 +15,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QProcess>
|
||||
#include <QStringList>
|
||||
|
||||
#include "JavaChecker.h"
|
||||
#include "JavaInstallList.h"
|
||||
#include "java/JavaInstall.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
@ -27,6 +26,7 @@
|
||||
QString stripVariableEntries(QString name, QString target, QString remove);
|
||||
QProcessEnvironment CleanEnviroment();
|
||||
QStringList getMinecraftJavaBundle();
|
||||
QStringList getPrismJavaBundle();
|
||||
|
||||
class JavaUtils : public QObject {
|
||||
Q_OBJECT
|
||||
@ -42,4 +42,5 @@ class JavaUtils : public QObject {
|
||||
#endif
|
||||
|
||||
static QString getJavaCheckPath();
|
||||
static const QString javaExecutable;
|
||||
};
|
||||
|
@ -43,12 +43,18 @@ QString JavaVersion::toString() const
|
||||
return m_string;
|
||||
}
|
||||
|
||||
bool JavaVersion::requiresPermGen()
|
||||
bool JavaVersion::requiresPermGen() const
|
||||
{
|
||||
return !m_parseable || m_major < 8;
|
||||
}
|
||||
|
||||
bool JavaVersion::isModular()
|
||||
bool JavaVersion::defaultsToUtf8() const
|
||||
{
|
||||
// starting from Java 18, UTF-8 is the default charset: https://openjdk.org/jeps/400
|
||||
return m_parseable && m_major >= 18;
|
||||
}
|
||||
|
||||
bool JavaVersion::isModular() const
|
||||
{
|
||||
return m_parseable && m_major >= 9;
|
||||
}
|
||||
@ -59,12 +65,6 @@ bool JavaVersion::operator<(const JavaVersion& rhs)
|
||||
auto major = m_major;
|
||||
auto rmajor = rhs.m_major;
|
||||
|
||||
// HACK: discourage using java 9
|
||||
if (major > 8)
|
||||
major = -major;
|
||||
if (rmajor > 8)
|
||||
rmajor = -rmajor;
|
||||
|
||||
if (major < rmajor)
|
||||
return true;
|
||||
if (major > rmajor)
|
||||
@ -109,3 +109,24 @@ bool JavaVersion::operator>(const JavaVersion& rhs)
|
||||
{
|
||||
return (!operator<(rhs)) && (!operator==(rhs));
|
||||
}
|
||||
|
||||
JavaVersion::JavaVersion(int major, int minor, int security, int build, QString name)
|
||||
: m_major(major), m_minor(minor), m_security(security), m_name(name), m_parseable(true)
|
||||
{
|
||||
QStringList versions;
|
||||
if (build != 0) {
|
||||
m_prerelease = QString::number(build);
|
||||
versions.push_front(m_prerelease);
|
||||
}
|
||||
if (m_security != 0)
|
||||
versions.push_front(QString::number(m_security));
|
||||
else if (!versions.isEmpty())
|
||||
versions.push_front("0");
|
||||
|
||||
if (m_minor != 0)
|
||||
versions.push_front(QString::number(m_minor));
|
||||
else if (!versions.isEmpty())
|
||||
versions.push_front("0");
|
||||
versions.push_front(QString::number(m_major));
|
||||
m_string = versions.join(".");
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ class JavaVersion {
|
||||
public:
|
||||
JavaVersion() {}
|
||||
JavaVersion(const QString& rhs);
|
||||
JavaVersion(int major, int minor, int security, int build = 0, QString name = "");
|
||||
|
||||
JavaVersion& operator=(const QString& rhs);
|
||||
|
||||
@ -23,21 +24,24 @@ class JavaVersion {
|
||||
bool operator==(const JavaVersion& rhs);
|
||||
bool operator>(const JavaVersion& rhs);
|
||||
|
||||
bool requiresPermGen();
|
||||
|
||||
bool isModular();
|
||||
bool requiresPermGen() const;
|
||||
bool defaultsToUtf8() const;
|
||||
bool isModular() const;
|
||||
|
||||
QString toString() const;
|
||||
|
||||
int major() { return m_major; }
|
||||
int minor() { return m_minor; }
|
||||
int security() { return m_security; }
|
||||
int major() const { return m_major; }
|
||||
int minor() const { return m_minor; }
|
||||
int security() const { return m_security; }
|
||||
QString build() const { return m_prerelease; }
|
||||
QString name() const { return m_name; }
|
||||
|
||||
private:
|
||||
QString m_string;
|
||||
int m_major = 0;
|
||||
int m_minor = 0;
|
||||
int m_security = 0;
|
||||
QString m_name = "";
|
||||
bool m_parseable = false;
|
||||
QString m_prerelease;
|
||||
};
|
||||
|
141
launcher/java/download/ArchiveDownloadTask.cpp
Normal file
141
launcher/java/download/ArchiveDownloadTask.cpp
Normal file
@ -0,0 +1,141 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "java/download/ArchiveDownloadTask.h"
|
||||
#include <quazip.h>
|
||||
#include <memory>
|
||||
#include "MMCZip.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "Untar.h"
|
||||
#include "net/ChecksumValidator.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
namespace Java {
|
||||
ArchiveDownloadTask::ArchiveDownloadTask(QUrl url, QString final_path, QString checksumType, QString checksumHash)
|
||||
: m_url(url), m_final_path(final_path), m_checksum_type(checksumType), m_checksum_hash(checksumHash)
|
||||
{}
|
||||
|
||||
void ArchiveDownloadTask::executeTask()
|
||||
{
|
||||
// JRE found ! download the zip
|
||||
setStatus(tr("Downloading Java"));
|
||||
|
||||
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("java", m_url.fileName());
|
||||
|
||||
auto download = makeShared<NetJob>(QString("JRE::DownloadJava"), APPLICATION->network());
|
||||
auto action = Net::Download::makeCached(m_url, entry);
|
||||
if (!m_checksum_hash.isEmpty() && !m_checksum_type.isEmpty()) {
|
||||
auto hashType = QCryptographicHash::Algorithm::Sha1;
|
||||
if (m_checksum_type == "sha256") {
|
||||
hashType = QCryptographicHash::Algorithm::Sha256;
|
||||
}
|
||||
action->addValidator(new Net::ChecksumValidator(hashType, QByteArray::fromHex(m_checksum_hash.toUtf8())));
|
||||
}
|
||||
download->addNetAction(action);
|
||||
auto fullPath = entry->getFullPath();
|
||||
|
||||
connect(download.get(), &Task::failed, this, &ArchiveDownloadTask::emitFailed);
|
||||
connect(download.get(), &Task::progress, this, &ArchiveDownloadTask::setProgress);
|
||||
connect(download.get(), &Task::stepProgress, this, &ArchiveDownloadTask::propagateStepProgress);
|
||||
connect(download.get(), &Task::status, this, &ArchiveDownloadTask::setStatus);
|
||||
connect(download.get(), &Task::details, this, &ArchiveDownloadTask::setDetails);
|
||||
connect(download.get(), &Task::succeeded, [this, fullPath] {
|
||||
// This should do all of the extracting and creating folders
|
||||
extractJava(fullPath);
|
||||
});
|
||||
m_task = download;
|
||||
m_task->start();
|
||||
}
|
||||
|
||||
void ArchiveDownloadTask::extractJava(QString input)
|
||||
{
|
||||
setStatus(tr("Extracting Java"));
|
||||
if (input.endsWith("tar")) {
|
||||
setStatus(tr("Extracting Java (Progress is not reported for tar archives)"));
|
||||
QFile in(input);
|
||||
if (!in.open(QFile::ReadOnly)) {
|
||||
emitFailed(tr("Unable to open supplied tar file."));
|
||||
return;
|
||||
}
|
||||
if (!Tar::extract(&in, QDir(m_final_path).absolutePath())) {
|
||||
emitFailed(tr("Unable to extract supplied tar file."));
|
||||
return;
|
||||
}
|
||||
emitSucceeded();
|
||||
return;
|
||||
} else if (input.endsWith("tar.gz") || input.endsWith("taz") || input.endsWith("tgz")) {
|
||||
setStatus(tr("Extracting Java (Progress is not reported for tar archives)"));
|
||||
if (!GZTar::extract(input, QDir(m_final_path).absolutePath())) {
|
||||
emitFailed(tr("Unable to extract supplied tar file."));
|
||||
return;
|
||||
}
|
||||
emitSucceeded();
|
||||
return;
|
||||
} else if (input.endsWith("zip")) {
|
||||
auto zip = std::make_shared<QuaZip>(input);
|
||||
if (!zip->open(QuaZip::mdUnzip)) {
|
||||
emitFailed(tr("Unable to open supplied zip file."));
|
||||
return;
|
||||
}
|
||||
auto files = zip->getFileNameList();
|
||||
if (files.isEmpty()) {
|
||||
emitFailed(tr("No files were found in the supplied zip file."));
|
||||
return;
|
||||
}
|
||||
m_task = makeShared<MMCZip::ExtractZipTask>(zip, m_final_path, files[0]);
|
||||
|
||||
auto progressStep = std::make_shared<TaskStepProgress>();
|
||||
connect(m_task.get(), &Task::finished, this, [this, progressStep] {
|
||||
progressStep->state = TaskStepState::Succeeded;
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
|
||||
connect(m_task.get(), &Task::succeeded, this, &ArchiveDownloadTask::emitSucceeded);
|
||||
connect(m_task.get(), &Task::aborted, this, &ArchiveDownloadTask::emitAborted);
|
||||
connect(m_task.get(), &Task::failed, this, [this, progressStep](QString reason) {
|
||||
progressStep->state = TaskStepState::Failed;
|
||||
stepProgress(*progressStep);
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(m_task.get(), &Task::stepProgress, this, &ArchiveDownloadTask::propagateStepProgress);
|
||||
|
||||
connect(m_task.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) {
|
||||
progressStep->update(current, total);
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
connect(m_task.get(), &Task::status, this, [this, progressStep](QString status) {
|
||||
progressStep->status = status;
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
m_task->start();
|
||||
return;
|
||||
}
|
||||
|
||||
emitFailed(tr("Could not determine archive type!"));
|
||||
}
|
||||
|
||||
bool ArchiveDownloadTask::abort()
|
||||
{
|
||||
auto aborted = canAbort();
|
||||
if (m_task)
|
||||
aborted = m_task->abort();
|
||||
emitAborted();
|
||||
return aborted;
|
||||
};
|
||||
} // namespace Java
|
45
launcher/java/download/ArchiveDownloadTask.h
Normal file
45
launcher/java/download/ArchiveDownloadTask.h
Normal file
@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QUrl>
|
||||
#include "tasks/Task.h"
|
||||
|
||||
namespace Java {
|
||||
class ArchiveDownloadTask : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ArchiveDownloadTask(QUrl url, QString final_path, QString checksumType = "", QString checksumHash = "");
|
||||
virtual ~ArchiveDownloadTask() = default;
|
||||
|
||||
[[nodiscard]] bool canAbort() const override { return true; }
|
||||
void executeTask() override;
|
||||
virtual bool abort() override;
|
||||
|
||||
private slots:
|
||||
void extractJava(QString input);
|
||||
|
||||
protected:
|
||||
QUrl m_url;
|
||||
QString m_final_path;
|
||||
QString m_checksum_type;
|
||||
QString m_checksum_hash;
|
||||
Task::Ptr m_task;
|
||||
};
|
||||
} // namespace Java
|
137
launcher/java/download/ManifestDownloadTask.cpp
Normal file
137
launcher/java/download/ManifestDownloadTask.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "java/download/ManifestDownloadTask.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "FileSystem.h"
|
||||
#include "Json.h"
|
||||
#include "net/ChecksumValidator.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
struct File {
|
||||
QString path;
|
||||
QString url;
|
||||
QByteArray hash;
|
||||
bool isExec;
|
||||
};
|
||||
|
||||
namespace Java {
|
||||
ManifestDownloadTask::ManifestDownloadTask(QUrl url, QString final_path, QString checksumType, QString checksumHash)
|
||||
: m_url(url), m_final_path(final_path), m_checksum_type(checksumType), m_checksum_hash(checksumHash)
|
||||
{}
|
||||
|
||||
void ManifestDownloadTask::executeTask()
|
||||
{
|
||||
setStatus(tr("Downloading Java"));
|
||||
auto download = makeShared<NetJob>(QString("JRE::DownloadJava"), APPLICATION->network());
|
||||
auto files = std::make_shared<QByteArray>();
|
||||
|
||||
auto action = Net::Download::makeByteArray(m_url, files);
|
||||
if (!m_checksum_hash.isEmpty() && !m_checksum_type.isEmpty()) {
|
||||
auto hashType = QCryptographicHash::Algorithm::Sha1;
|
||||
if (m_checksum_type == "sha256") {
|
||||
hashType = QCryptographicHash::Algorithm::Sha256;
|
||||
}
|
||||
action->addValidator(new Net::ChecksumValidator(hashType, QByteArray::fromHex(m_checksum_hash.toUtf8())));
|
||||
}
|
||||
download->addNetAction(action);
|
||||
|
||||
connect(download.get(), &Task::failed, this, &ManifestDownloadTask::emitFailed);
|
||||
connect(download.get(), &Task::progress, this, &ManifestDownloadTask::setProgress);
|
||||
connect(download.get(), &Task::stepProgress, this, &ManifestDownloadTask::propagateStepProgress);
|
||||
connect(download.get(), &Task::status, this, &ManifestDownloadTask::setStatus);
|
||||
connect(download.get(), &Task::details, this, &ManifestDownloadTask::setDetails);
|
||||
|
||||
connect(download.get(), &Task::succeeded, [files, this] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*files, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response at " << parse_error.offset << ". Reason: " << parse_error.errorString();
|
||||
qWarning() << *files;
|
||||
emitFailed(parse_error.errorString());
|
||||
return;
|
||||
}
|
||||
downloadJava(doc);
|
||||
});
|
||||
m_task = download;
|
||||
m_task->start();
|
||||
}
|
||||
|
||||
void ManifestDownloadTask::downloadJava(const QJsonDocument& doc)
|
||||
{
|
||||
// valid json doc, begin making jre spot
|
||||
FS::ensureFolderPathExists(m_final_path);
|
||||
std::vector<File> toDownload;
|
||||
auto list = Json::ensureObject(Json::ensureObject(doc.object()), "files");
|
||||
for (const auto& paths : list.keys()) {
|
||||
auto file = FS::PathCombine(m_final_path, paths);
|
||||
|
||||
const QJsonObject& meta = Json::ensureObject(list, paths);
|
||||
auto type = Json::ensureString(meta, "type");
|
||||
if (type == "directory") {
|
||||
FS::ensureFolderPathExists(file);
|
||||
} else if (type == "link") {
|
||||
// this is *nix only !
|
||||
auto path = Json::ensureString(meta, "target");
|
||||
if (!path.isEmpty()) {
|
||||
QFile::link(path, file);
|
||||
}
|
||||
} else if (type == "file") {
|
||||
// TODO download compressed version if it exists ?
|
||||
auto raw = Json::ensureObject(Json::ensureObject(meta, "downloads"), "raw");
|
||||
auto isExec = Json::ensureBoolean(meta, "executable", false);
|
||||
auto url = Json::ensureString(raw, "url");
|
||||
if (!url.isEmpty() && QUrl(url).isValid()) {
|
||||
auto f = File{ file, url, QByteArray::fromHex(Json::ensureString(raw, "sha1").toLatin1()), isExec };
|
||||
toDownload.push_back(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
auto elementDownload = makeShared<NetJob>("JRE::FileDownload", APPLICATION->network());
|
||||
for (const auto& file : toDownload) {
|
||||
auto dl = Net::Download::makeFile(file.url, file.path);
|
||||
if (!file.hash.isEmpty()) {
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, file.hash));
|
||||
}
|
||||
if (file.isExec) {
|
||||
connect(dl.get(), &Net::Download::succeeded,
|
||||
[file] { QFile(file.path).setPermissions(QFile(file.path).permissions() | QFileDevice::Permissions(0x1111)); });
|
||||
}
|
||||
elementDownload->addNetAction(dl);
|
||||
}
|
||||
|
||||
connect(elementDownload.get(), &Task::failed, this, &ManifestDownloadTask::emitFailed);
|
||||
connect(elementDownload.get(), &Task::progress, this, &ManifestDownloadTask::setProgress);
|
||||
connect(elementDownload.get(), &Task::stepProgress, this, &ManifestDownloadTask::propagateStepProgress);
|
||||
connect(elementDownload.get(), &Task::status, this, &ManifestDownloadTask::setStatus);
|
||||
connect(elementDownload.get(), &Task::details, this, &ManifestDownloadTask::setDetails);
|
||||
|
||||
connect(elementDownload.get(), &Task::succeeded, this, &ManifestDownloadTask::emitSucceeded);
|
||||
m_task = elementDownload;
|
||||
m_task->start();
|
||||
}
|
||||
|
||||
bool ManifestDownloadTask::abort()
|
||||
{
|
||||
auto aborted = canAbort();
|
||||
if (m_task)
|
||||
aborted = m_task->abort();
|
||||
emitAborted();
|
||||
return aborted;
|
||||
};
|
||||
} // namespace Java
|
46
launcher/java/download/ManifestDownloadTask.h
Normal file
46
launcher/java/download/ManifestDownloadTask.h
Normal file
@ -0,0 +1,46 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QUrl>
|
||||
#include "tasks/Task.h"
|
||||
|
||||
namespace Java {
|
||||
|
||||
class ManifestDownloadTask : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ManifestDownloadTask(QUrl url, QString final_path, QString checksumType = "", QString checksumHash = "");
|
||||
virtual ~ManifestDownloadTask() = default;
|
||||
|
||||
[[nodiscard]] bool canAbort() const override { return true; }
|
||||
void executeTask() override;
|
||||
virtual bool abort() override;
|
||||
|
||||
private slots:
|
||||
void downloadJava(const QJsonDocument& doc);
|
||||
|
||||
protected:
|
||||
QUrl m_url;
|
||||
QString m_final_path;
|
||||
QString m_checksum_type;
|
||||
QString m_checksum_hash;
|
||||
Task::Ptr m_task;
|
||||
};
|
||||
} // namespace Java
|
81
launcher/java/download/SymlinkTask.cpp
Normal file
81
launcher/java/download/SymlinkTask.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "java/download/SymlinkTask.h"
|
||||
#include <QFileInfo>
|
||||
|
||||
#include "FileSystem.h"
|
||||
|
||||
namespace Java {
|
||||
SymlinkTask::SymlinkTask(QString final_path) : m_path(final_path) {}
|
||||
|
||||
QString findBinPath(QString root, QString pattern)
|
||||
{
|
||||
auto path = FS::PathCombine(root, pattern);
|
||||
if (QFileInfo::exists(path)) {
|
||||
return path;
|
||||
}
|
||||
|
||||
auto entries = QDir(root).entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
for (auto& entry : entries) {
|
||||
path = FS::PathCombine(entry.absoluteFilePath(), pattern);
|
||||
if (QFileInfo::exists(path)) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void SymlinkTask::executeTask()
|
||||
{
|
||||
setStatus(tr("Checking for Java binary path"));
|
||||
const auto binPath = FS::PathCombine("bin", "java");
|
||||
const auto wantedPath = FS::PathCombine(m_path, binPath);
|
||||
if (QFileInfo::exists(wantedPath)) {
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
|
||||
setStatus(tr("Searching for Java binary path"));
|
||||
const auto contentsPartialPath = FS::PathCombine("Contents", "Home", binPath);
|
||||
const auto relativePathToBin = findBinPath(m_path, contentsPartialPath);
|
||||
if (relativePathToBin.isEmpty()) {
|
||||
emitFailed(tr("Failed to find Java binary path"));
|
||||
return;
|
||||
}
|
||||
const auto folderToLink = relativePathToBin.chopped(binPath.length());
|
||||
|
||||
setStatus(tr("Collecting folders to symlink"));
|
||||
auto entries = QDir(folderToLink).entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries);
|
||||
QList<FS::LinkPair> files;
|
||||
setProgress(0, entries.length());
|
||||
for (auto& entry : entries) {
|
||||
files.append({ entry.absoluteFilePath(), FS::PathCombine(m_path, entry.fileName()) });
|
||||
}
|
||||
|
||||
setStatus(tr("Symlinking Java binary path"));
|
||||
FS::create_link folderLink(files);
|
||||
connect(&folderLink, &FS::create_link::fileLinked, [this](QString src, QString dst) { setProgress(m_progress + 1, m_progressTotal); });
|
||||
if (!folderLink()) {
|
||||
emitFailed(folderLink.getOSError().message().c_str());
|
||||
} else {
|
||||
emitSucceeded();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Java
|
36
launcher/java/download/SymlinkTask.h
Normal file
36
launcher/java/download/SymlinkTask.h
Normal file
@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tasks/Task.h"
|
||||
namespace Java {
|
||||
|
||||
class SymlinkTask : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SymlinkTask(QString final_path);
|
||||
virtual ~SymlinkTask() = default;
|
||||
|
||||
void executeTask() override;
|
||||
|
||||
protected:
|
||||
QString m_path;
|
||||
Task::Ptr m_task;
|
||||
};
|
||||
} // namespace Java
|
@ -16,9 +16,8 @@
|
||||
#include "LaunchStep.h"
|
||||
#include "LaunchTask.h"
|
||||
|
||||
void LaunchStep::bind(LaunchTask* parent)
|
||||
LaunchStep::LaunchStep(LaunchTask* parent) : Task(), m_parent(parent)
|
||||
{
|
||||
m_parent = parent;
|
||||
connect(this, &LaunchStep::readyForLaunch, parent, &LaunchTask::onReadyForLaunch);
|
||||
connect(this, &LaunchStep::logLine, parent, &LaunchTask::onLogLine);
|
||||
connect(this, &LaunchStep::logLines, parent, &LaunchTask::onLogLines);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user