mirror of
https://github.com/hero-persson/FjordLauncherUnlocked.git
synced 2025-04-11 22:58:48 +02:00
Merge tag '9.0' into develop
This is an intermediate/halfway commit, more refactoring is needed to get this to build.
This commit is contained in:
commit
7dbaa896a6
532 changed files with 15502 additions and 7137 deletions
|
@ -2,3 +2,6 @@
|
|||
|
||||
# tabs -> spaces
|
||||
bbb3b3e6f6e3c0f95873f22e6d0a4aaf350f49d9
|
||||
|
||||
# (nix) alejandra -> nixfmt
|
||||
4c81d8c53d09196426568c4a31a4e752ed05397a
|
||||
|
|
3
.github/workflows/backport.yml
vendored
3
.github/workflows/backport.yml
vendored
|
@ -16,6 +16,7 @@ jobs:
|
|||
permissions:
|
||||
contents: write # for korthout/backport-action to create branch
|
||||
pull-requests: write # for korthout/backport-action to create PR to backport
|
||||
actions: write # for korthout/backport-action to create PR with workflow changes
|
||||
name: Backport Pull Request
|
||||
if: github.repository_owner == 'PrismLauncher' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -24,7 +25,7 @@ jobs:
|
|||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Create backport PRs
|
||||
uses: korthout/backport-action@v2.1.1
|
||||
uses: korthout/backport-action@v3.1.0
|
||||
with:
|
||||
# Config README: https://github.com/korthout/backport-action#backport-action
|
||||
pull_description: |-
|
||||
|
|
230
.github/workflows/build.yml
vendored
230
.github/workflows/build.yml
vendored
|
@ -39,6 +39,9 @@ on:
|
|||
APPLE_NOTARIZE_PASSWORD:
|
||||
description: Password used for notarizing macOS builds
|
||||
required: false
|
||||
CACHIX_AUTH_TOKEN:
|
||||
description: Private token for authenticating against Cachix cache
|
||||
required: false
|
||||
GPG_PRIVATE_KEY:
|
||||
description: Private key for AppImage signing
|
||||
required: false
|
||||
|
@ -54,14 +57,17 @@ jobs:
|
|||
include:
|
||||
- os: ubuntu-20.04
|
||||
qt_ver: 5
|
||||
qt_host: linux
|
||||
qt_arch: ""
|
||||
qt_version: "5.12.8"
|
||||
qt_modules: "qtnetworkauth"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
qt_ver: 6
|
||||
qt_host: linux
|
||||
qt_arch: ""
|
||||
qt_version: "6.2.4"
|
||||
qt_modules: "qt5compat qtimageformats"
|
||||
qt_tools: ""
|
||||
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-MinGW-w64"
|
||||
|
@ -75,10 +81,11 @@ jobs:
|
|||
vcvars_arch: "amd64"
|
||||
qt_ver: 6
|
||||
qt_host: windows
|
||||
qt_arch: ''
|
||||
qt_version: '6.6.3'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
qt_tools: ''
|
||||
qt_arch: ""
|
||||
qt_version: "6.7.3"
|
||||
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||
nscurl_tag: "v24.9.26.122"
|
||||
nscurl_sha256: "AEE6C4BE3CB6455858E9C1EE4B3AFE0DB9960FA03FE99CCDEDC28390D57CCBB0"
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-MSVC-arm64"
|
||||
|
@ -87,29 +94,28 @@ jobs:
|
|||
vcvars_arch: "amd64_arm64"
|
||||
qt_ver: 6
|
||||
qt_host: windows
|
||||
qt_arch: 'win64_msvc2019_arm64'
|
||||
qt_version: '6.6.3'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
qt_tools: ''
|
||||
qt_arch: "win64_msvc2019_arm64"
|
||||
qt_version: "6.7.3"
|
||||
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.6.3'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
qt_tools: ''
|
||||
qt_arch: ""
|
||||
qt_version: "6.7.3"
|
||||
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||
|
||||
- os: macos-12
|
||||
- os: macos-14
|
||||
name: macOS-Legacy
|
||||
macosx_deployment_target: 10.13
|
||||
qt_ver: 5
|
||||
qt_host: mac
|
||||
qt_version: "5.15.2"
|
||||
qt_modules: ""
|
||||
qt_tools: ""
|
||||
qt_modules: "qtnetworkauth"
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
|
@ -151,6 +157,7 @@ jobs:
|
|||
quazip-qt6:p
|
||||
ccache:p
|
||||
qt6-5compat:p
|
||||
qt6-networkauth:p
|
||||
cmark:p
|
||||
|
||||
- name: Force newer ccache
|
||||
|
@ -160,13 +167,13 @@ jobs:
|
|||
|
||||
- name: Setup ccache
|
||||
if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug'
|
||||
uses: hendrikmuhs/ccache-action@v1.2.10
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
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@v3.3.2
|
||||
uses: actions/cache@v4.1.1
|
||||
with:
|
||||
path: '${{ github.workspace }}\.ccache'
|
||||
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
|
||||
|
@ -207,11 +214,6 @@ jobs:
|
|||
brew update
|
||||
brew install ninja extra-cmake-modules
|
||||
|
||||
- name: Install Qt (Linux)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||
run: |
|
||||
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
|
||||
|
||||
- name: Install host Qt (Windows MSVC arm64)
|
||||
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||
uses: jurplel/install-qt-action@v3
|
||||
|
@ -223,20 +225,18 @@ jobs:
|
|||
target: "desktop"
|
||||
arch: ""
|
||||
modules: ${{ matrix.qt_modules }}
|
||||
tools: ${{ matrix.qt_tools }}
|
||||
cache: ${{ inputs.is_qt_cached }}
|
||||
cache-key-prefix: host-qt-arm64-windows
|
||||
dir: ${{ github.workspace }}\HostQt
|
||||
set-env: false
|
||||
|
||||
- name: Install Qt (macOS, Linux, Qt 6 & Windows MSVC)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS' || (runner.os == 'Windows' && matrix.msystem == '')
|
||||
- name: Install Qt (macOS, Linux & Windows MSVC)
|
||||
if: matrix.msystem == ''
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
aqtversion: "==3.1.*"
|
||||
py7zrversion: ">=0.20.2"
|
||||
version: ${{ matrix.qt_version }}
|
||||
host: ${{ matrix.qt_host }}
|
||||
target: "desktop"
|
||||
arch: ${{ matrix.qt_arch }}
|
||||
modules: ${{ matrix.qt_modules }}
|
||||
|
@ -266,6 +266,12 @@ jobs:
|
|||
run: |
|
||||
echo "QT_HOST_PATH=${{ github.workspace }}\HostQt\Qt\${{ matrix.qt_version }}\msvc2019_64" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Setup java (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: "17"
|
||||
##
|
||||
# CONFIGURE
|
||||
##
|
||||
|
@ -273,23 +279,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 }}")
|
||||
{
|
||||
|
@ -304,7 +310,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
|
||||
|
@ -404,9 +410,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 }}/FjordLauncher.zip -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
|
||||
signature=$(/opt/homebrew/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/FjordLauncher.zip -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
|
||||
rm ed25519-priv.pem
|
||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||
### Artifact Information :information_source:
|
||||
|
@ -432,12 +437,6 @@ jobs:
|
|||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }}
|
||||
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
if ("${{ matrix.qt_ver }}" -eq "5")
|
||||
{
|
||||
Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll
|
||||
Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll
|
||||
}
|
||||
cd ${{ github.workspace }}
|
||||
|
||||
Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
|
||||
|
@ -454,7 +453,7 @@ jobs:
|
|||
if (Get-Content ./codesign.pfx){
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
# We ship the exact same executable for portable and non-portable editions, so signing just once is fine
|
||||
SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com fjordlauncher.exe fjordlauncher_filelink.exe
|
||||
SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com fjordlauncher.exe fjordlauncher_updater.exe fjordlauncher_filelink.exe
|
||||
} else {
|
||||
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||
}
|
||||
|
@ -478,6 +477,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"
|
||||
|
||||
|
@ -490,28 +499,6 @@ jobs:
|
|||
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||
}
|
||||
|
||||
- name: Package (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }}
|
||||
for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_DIR }}/manifest.txt
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
tar --owner root --group root -czf ../FjordLauncher.tar.gz *
|
||||
|
||||
- name: Package (Linux, portable)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||
|
||||
# workaround to make portable installs to work on fedora
|
||||
mkdir ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
cp /lib/x86_64-linux-gnu/libbz2.so.1.0 ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
|
||||
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
|
||||
cd ${{ env.INSTALL_PORTABLE_DIR }}
|
||||
tar -czf ../FjordLauncher-portable.tar.gz *
|
||||
|
||||
- name: Package AppImage (Linux)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||
shell: bash
|
||||
|
@ -520,7 +507,7 @@ jobs:
|
|||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
|
||||
|
||||
mv ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.unmojang.FjordLauncher.metainfo.xml ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.unmojang.FjordLauncher.appdata.xml
|
||||
mv ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.unmojang.FjordLauncher.metainfo.xml ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.fjordlauncher.FjordLauncher.appdata.xml
|
||||
export "NO_APPSTREAM=1" # we have to skip appstream checking because appstream on ubuntu 20.04 is outdated
|
||||
|
||||
export OUTPUT="FjordLauncher-Linux-x86_64.AppImage"
|
||||
|
@ -558,76 +545,81 @@ jobs:
|
|||
|
||||
mv "FjordLauncher-Linux-x86_64.AppImage" "FjordLauncher-Linux-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
|
||||
|
||||
- name: Package (Linux, portable)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_PORTABLE_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -DINSTALL_BUNDLE=full -G Ninja
|
||||
cmake --install ${{ env.BUILD_DIR }}
|
||||
cmake --install ${{ env.BUILD_DIR }} --component portable
|
||||
|
||||
mkdir ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
cp /lib/x86_64-linux-gnu/libbz2.so.1.0 ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
cp /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0 ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
cp /usr/lib/x86_64-linux-gnu/libffi.so.7 ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
mv ${{ env.INSTALL_PORTABLE_DIR }}/bin/*.so* ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||
|
||||
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
|
||||
cd ${{ env.INSTALL_PORTABLE_DIR }}
|
||||
tar -czf ../FjordLauncher-portable.tar.gz *
|
||||
|
||||
##
|
||||
# UPLOAD BUILDS
|
||||
##
|
||||
|
||||
- name: Upload binary tarball (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: FjordLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: FjordLauncher.zip
|
||||
|
||||
- name: Upload binary zip (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: FjordLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: ${{ env.INSTALL_DIR }}/**
|
||||
|
||||
- name: Upload binary zip (Windows, portable)
|
||||
if: runner.os == 'Windows'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: FjordLauncher-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: ${{ env.INSTALL_PORTABLE_DIR }}/**
|
||||
|
||||
- name: Upload installer (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: FjordLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: FjordLauncher-Setup.exe
|
||||
|
||||
- name: Upload binary tarball (Linux, Qt 5)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: FjordLauncher-${{ runner.os }}-Qt5-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: FjordLauncher.tar.gz
|
||||
|
||||
- name: Upload binary tarball (Linux, portable, Qt 5)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: FjordLauncher-${{ runner.os }}-Qt5-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: FjordLauncher-portable.tar.gz
|
||||
|
||||
- name: Upload binary tarball (Linux, Qt 6)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver !=5
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: FjordLauncher-${{ runner.os }}-Qt6-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: FjordLauncher.tar.gz
|
||||
|
||||
- name: Upload binary tarball (Linux, portable, Qt 6)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: FjordLauncher-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: FjordLauncher-portable.tar.gz
|
||||
|
||||
- name: Upload AppImage (Linux)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: FjordLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||
path: FjordLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||
|
||||
- name: Upload AppImage Zsync (Linux)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: FjordLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync
|
||||
path: FjordLauncher-Linux-x86_64.AppImage.zsync
|
||||
|
@ -641,23 +633,67 @@ jobs:
|
|||
flatpak:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: bilelmoussaoui/flatpak-github-actions:kde-5.15-23.08
|
||||
image: bilelmoussaoui/flatpak-github-actions:kde-6.7
|
||||
options: --privileged
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
if: inputs.build_type == 'Release'
|
||||
if: inputs.build_type == 'Debug'
|
||||
with:
|
||||
submodules: "true"
|
||||
- name: Build Flatpak (Linux)
|
||||
if: inputs.build_type == 'Release'
|
||||
if: inputs.build_type == 'Debug'
|
||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
||||
with:
|
||||
bundle: "FjordLauncher.flatpak"
|
||||
bundle: "Fjord Launcher.flatpak"
|
||||
manifest-path: flatpak/org.unmojang.FjordLauncher.yml
|
||||
- name: Upload Flatpak (Linux)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
nix:
|
||||
name: Nix (${{ matrix.system }})
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
system: x86_64-linux
|
||||
|
||||
- os: macos-13
|
||||
system: x86_64-darwin
|
||||
|
||||
- os: macos-14
|
||||
system: aarch64-darwin
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v30
|
||||
|
||||
# For PRs
|
||||
- name: Setup Nix Magic Cache
|
||||
uses: DeterminateSystems/magic-nix-cache-action@v8
|
||||
|
||||
# For in-tree builds
|
||||
- name: Setup Cachix
|
||||
uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: FjordLauncher-${{ runner.os }}-x86_64.flatpak
|
||||
path: FjordLauncher.flatpak
|
||||
name: fjordlauncher
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
|
||||
- name: Run flake checks
|
||||
run: |
|
||||
nix flake check --print-build-logs --show-trace
|
||||
|
||||
- name: Build debug package
|
||||
if: ${{ inputs.build_type == 'Debug' }}
|
||||
run: |
|
||||
nix build --print-build-logs .#fjordlauncher-debug
|
||||
|
||||
- name: Build release package
|
||||
if: ${{ inputs.build_type != 'Debug' }}
|
||||
run: |
|
||||
nix build --print-build-logs .#fjordlauncher
|
||||
|
|
6
.github/workflows/codeql.yml
vendored
6
.github/workflows/codeql.yml
vendored
|
@ -13,7 +13,7 @@ jobs:
|
|||
submodules: 'true'
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
queries: security-and-quality
|
||||
|
@ -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
|
||||
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
|
||||
|
||||
- name: Configure and Build
|
||||
run: |
|
||||
|
@ -32,4 +32,4 @@ jobs:
|
|||
cmake --build build
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
|
1
.github/workflows/trigger_builds.yml
vendored
1
.github/workflows/trigger_builds.yml
vendored
|
@ -38,5 +38,6 @@ jobs:
|
|||
APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
|
||||
APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
|
||||
APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
|
||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||
|
|
9
.github/workflows/trigger_release.yml
vendored
9
.github/workflows/trigger_release.yml
vendored
|
@ -22,6 +22,7 @@ jobs:
|
|||
APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
|
||||
APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
|
||||
APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
|
||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||
|
||||
|
@ -37,7 +38,7 @@ jobs:
|
|||
submodules: "true"
|
||||
path: "FjordLauncher-source"
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
- name: Grab and store version
|
||||
run: |
|
||||
tag_name=$(echo ${{ github.ref }} | grep -oE "[^/]+$")
|
||||
|
@ -46,9 +47,7 @@ jobs:
|
|||
run: |
|
||||
mv ${{ github.workspace }}/FjordLauncher-source FjordLauncher-${{ env.VERSION }}
|
||||
mv FjordLauncher-Linux-Qt6-Portable*/FjordLauncher-portable.tar.gz FjordLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||
mv FjordLauncher-Linux-Qt6*/FjordLauncher.tar.gz FjordLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||
mv FjordLauncher-Linux-Qt5-Portable*/FjordLauncher-portable.tar.gz FjordLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
|
||||
mv FjordLauncher-Linux-Qt5*/FjordLauncher.tar.gz FjordLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz
|
||||
mv FjordLauncher-*.AppImage/FjordLauncher-*.AppImage FjordLauncher-Linux-x86_64.AppImage
|
||||
mv FjordLauncher-*.AppImage.zsync/FjordLauncher-*.AppImage.zsync FjordLauncher-Linux-x86_64.AppImage.zsync
|
||||
mv FjordLauncher-macOS-Legacy*/FjordLauncher.zip FjordLauncher-macOS-Legacy-${{ env.VERSION }}.zip
|
||||
|
@ -84,7 +83,7 @@ jobs:
|
|||
|
||||
- name: Create release
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag_name: ${{ github.ref }}
|
||||
|
@ -92,11 +91,9 @@ jobs:
|
|||
draft: true
|
||||
prerelease: false
|
||||
files: |
|
||||
FjordLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz
|
||||
FjordLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
|
||||
FjordLauncher-Linux-x86_64.AppImage
|
||||
FjordLauncher-Linux-x86_64.AppImage.zsync
|
||||
FjordLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||
FjordLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||
FjordLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip
|
||||
FjordLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip
|
||||
|
|
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@7ac1ec25491415c381d9b62f0657c7a028df52a7 # v24
|
||||
- uses: cachix/install-nix-action@08dcb3a5e62fa31e2da3d490afc4176ef55ecd72 # v30
|
||||
|
||||
- uses: DeterminateSystems/update-flake-lock@v20
|
||||
- uses: DeterminateSystems/update-flake-lock@v24
|
||||
with:
|
||||
commit-msg: "chore(nix): update lockfile"
|
||||
pr-title: "chore(nix): update lockfile"
|
||||
|
|
3
BUILD.md
3
BUILD.md
|
@ -1,3 +0,0 @@
|
|||
# Build Instructions
|
||||
|
||||
Full build instructions are available on [the website](https://prismlauncher.org/wiki/development/build-instructions/).
|
|
@ -99,7 +99,7 @@ if ((CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebI
|
|||
message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off")
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||
# using clang with clang-cl front end
|
||||
# using clang with clang-cl front end
|
||||
message(STATUS "Address Sanitizer available on Clang MSVC frontend")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
|
||||
|
@ -176,11 +176,13 @@ endif()
|
|||
set(Launcher_NEWS_RSS_URL "" 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 8)
|
||||
set(Launcher_VERSION_MINOR 4)
|
||||
set(Launcher_VERSION_PATCH 3)
|
||||
set(Launcher_VERSION_MAJOR 9)
|
||||
set(Launcher_VERSION_MINOR 0)
|
||||
set(Launcher_VERSION_PATCH 0)
|
||||
|
||||
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}")
|
||||
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}.0")
|
||||
|
@ -206,6 +208,7 @@ set(Launcher_BUG_TRACKER_URL "https://github.com/unmojang/FjordLauncher/issues"
|
|||
|
||||
# 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 "" CACHE STRING "URL to the Matrix Space")
|
||||
|
@ -220,6 +223,19 @@ set(Launcher_SUBREDDIT_URL "" CACHE STRING "URL for the subreddit.")
|
|||
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")
|
||||
|
@ -275,7 +291,7 @@ 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)
|
||||
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml NetworkAuth)
|
||||
|
||||
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
||||
find_package(QuaZip-Qt5 1.3 QUIET)
|
||||
|
@ -289,7 +305,7 @@ 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)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml Core5Compat NetworkAuth)
|
||||
list(APPEND Launcher_QT_LIBS Qt6::Core5Compat)
|
||||
|
||||
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
||||
|
@ -410,7 +426,19 @@ elseif(UNIX)
|
|||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
|
||||
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "share/${Launcher_Name}")
|
||||
|
||||
|
||||
if (INSTALL_BUNDLE STREQUAL full)
|
||||
set(PLUGIN_DEST_DIR "plugins")
|
||||
set(BUNDLE_DEST_DIR ".")
|
||||
set(RESOURCES_DEST_DIR ".")
|
||||
|
||||
# Apps to bundle
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${Launcher_APP_BINARY_NAME}")
|
||||
|
||||
# directories to look for dependencies
|
||||
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
||||
endif()
|
||||
|
||||
if(Launcher_ManPage)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")
|
||||
endif()
|
||||
|
@ -460,7 +488,7 @@ if(FORCE_BUNDLED_ZLIB)
|
|||
set(SKIP_INSTALL_ALL ON)
|
||||
add_subdirectory(libraries/zlib EXCLUDE_FROM_ALL)
|
||||
|
||||
# On OS where unistd.h exists, zlib's generated header defines `Z_HAVE_UNISTD_H`, while the included header does not.
|
||||
# On OS where unistd.h exists, zlib's generated header defines `Z_HAVE_UNISTD_H`, while the included header does not.
|
||||
# We cannot safely undo the rename on those systems, and they generally have packages for zlib anyway.
|
||||
check_include_file(unistd.h NEED_GENERATED_ZCONF)
|
||||
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h.included" AND NOT NEED_GENERATED_ZCONF)
|
||||
|
@ -497,14 +525,15 @@ 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)
|
||||
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()
|
||||
add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much
|
||||
add_subdirectory(libraries/gamemode)
|
||||
add_subdirectory(libraries/murmur2) # Hash for usage with the CurseForge API
|
||||
if (NOT ghc_filesystem_FOUND)
|
||||
|
|
26
COPYING.md
26
COPYING.md
|
@ -401,32 +401,6 @@
|
|||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
## O2 (Katabasis fork)
|
||||
|
||||
Copyright (c) 2012, Akos Polster
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
## Gamemode
|
||||
|
||||
Copyright (c) 2017-2022, Feral Interactive
|
||||
|
|
|
@ -102,7 +102,12 @@ AppImages are available in the [releases section](https://github.com/unmojang/Fj
|
|||
|
||||
## Building
|
||||
|
||||
To build the launcher yourself, follow [the instructions on the Prism Launcher website](https://prismlauncher.org/wiki/development/build-instructions) but clone this repo instead.
|
||||
To build the launcher yourself, follow the instructions on the Prism Launcher website, but clone this repo instead:
|
||||
|
||||
- [Windows](https://prismlauncher.org/wiki/development/build-instructions/windows/)
|
||||
- [Linux](https://prismlauncher.org/wiki/development/build-instructions/linux/)
|
||||
- [MacOS](https://prismlauncher.org/wiki/development/build-instructions/macos/)
|
||||
- [OpenBSD](https://prismlauncher.org/wiki/development/build-instructions/openbsd/)
|
||||
|
||||
## Notes
|
||||
|
||||
|
|
|
@ -83,6 +83,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@";
|
||||
|
@ -115,16 +118,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@";
|
||||
|
|
|
@ -70,6 +70,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;
|
||||
|
@ -135,6 +136,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
|
||||
*/
|
||||
|
@ -173,8 +179,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/";
|
||||
|
||||
|
|
|
@ -79,6 +79,14 @@
|
|||
<string>curseforge</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>Prismlauncher</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>prismlauncher</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
23
default.nix
23
default.nix
|
@ -1,14 +1,9 @@
|
|||
(
|
||||
import
|
||||
(
|
||||
let
|
||||
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
|
||||
in
|
||||
fetchTarball {
|
||||
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
|
||||
(import (
|
||||
let
|
||||
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
|
||||
in
|
||||
fetchTarball {
|
||||
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
|
||||
|
|
84
flake.lock
generated
84
flake.lock
generated
|
@ -16,47 +16,6 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1719877454,
|
||||
"narHash": "sha256-g5N1yyOSsPNiOlFfkuI/wcUjmtah+nxdImJqrSATjOU=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "4e3583423212f9303aa1a6337f8dffb415920e4f",
|
||||
"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": {
|
||||
|
@ -90,55 +49,26 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1719826879,
|
||||
"narHash": "sha256-xs7PlULe8O1SAcs/9e/HOjeUjBrU5FNtkAF/bSEcFto=",
|
||||
"owner": "nixos",
|
||||
"lastModified": 1729256560,
|
||||
"narHash": "sha256-/uilDXvCIEs3C9l73JTACm4quuHUsIHcns1c+cHUJwA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b9014df496d5b68bf7c0145d0e9b0f529ce4f2a8",
|
||||
"rev": "4c2fcb090b1f3e5b47eaa7bd33913b574a11e0a0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"flake-compat"
|
||||
],
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-stable": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1719259945,
|
||||
"narHash": "sha256-F1h+XIsGKT9TkGO3omxDLEb/9jOOsI6NnzsXFsZhry4=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "0ff4381bbb8f7a52ca4a851660fc7a437a4c6e07",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-parts": "flake-parts",
|
||||
"libnbtplusplus": "libnbtplusplus",
|
||||
"nix-filter": "nix-filter",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pre-commit-hooks": "pre-commit-hooks"
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
147
flake.nix
147
flake.nix
|
@ -2,53 +2,130 @@
|
|||
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/nixpkgs-unstable";
|
||||
flake-parts = {
|
||||
url = "github:hercules-ci/flake-parts";
|
||||
inputs.nixpkgs-lib.follows = "nixpkgs";
|
||||
};
|
||||
nix-filter.url = "github:numtide/nix-filter";
|
||||
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;
|
||||
};
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
|
||||
libnbtplusplus = {
|
||||
url = "github:PrismLauncher/libnbtplusplus";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
nix-filter.url = "github:numtide/nix-filter";
|
||||
|
||||
/*
|
||||
Inputs below this are optional and can be removed
|
||||
|
||||
```
|
||||
{
|
||||
inputs.fjordlauncher = {
|
||||
url = "github:unmojang/FjordLauncher";
|
||||
inputs = {
|
||||
flake-compat.follows = "";
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
*/
|
||||
|
||||
flake-compat = {
|
||||
url = "github:edolstra/flake-compat";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
|
||||
outputs = {
|
||||
flake-parts,
|
||||
pre-commit-hooks,
|
||||
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;
|
||||
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
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}.fjordlauncher-unwrapped];
|
||||
buildInputs = with pkgs; [
|
||||
ccache
|
||||
ninja
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
formatter = forAllSystems (system: nixpkgsFor.${system}.nixfmt-rfc-style);
|
||||
|
||||
overlays.default = final: prev: let
|
||||
version = builtins.substring 0 8 self.lastModifiedDate or "dirty";
|
||||
in {
|
||||
fjordlauncher-unwrapped = prev.callPackage ./nix/unwrapped.nix {
|
||||
inherit
|
||||
libnbtplusplus
|
||||
nix-filter
|
||||
self
|
||||
version
|
||||
;
|
||||
};
|
||||
|
||||
fjordlauncher = final.callPackage ./nix/wrapper.nix {};
|
||||
};
|
||||
|
||||
packages = forAllSystems (
|
||||
system: let
|
||||
pkgs = nixpkgsFor.${system};
|
||||
|
||||
# Build a scope from our overlay
|
||||
fjordPackages = lib.makeScope pkgs.newScope (final: self.overlays.default final pkgs);
|
||||
|
||||
# Grab our packages from it and set the default
|
||||
packages = {
|
||||
inherit (fjordPackages) fjordlauncher-unwrapped fjordlauncher;
|
||||
default = fjordPackages.fjordlauncher;
|
||||
};
|
||||
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
|
||||
fjordPackages = self.packages.${system};
|
||||
legacyPackages = self.legacyPackages.${system};
|
||||
in {
|
||||
fjordlauncher-debug = fjordPackages.fjordlauncher.override {
|
||||
fjordlauncher-unwrapped = legacyPackages.fjordlauncher-unwrapped-debug;
|
||||
};
|
||||
|
||||
fjordlauncher-unwrapped-debug = fjordPackages.fjordlauncher-unwrapped.overrideAttrs {
|
||||
cmakeBuildType = "Debug";
|
||||
dontStrip = true;
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
id: org.unmojang.FjordLauncher
|
||||
runtime: org.kde.Platform
|
||||
runtime-version: 5.15-23.08
|
||||
runtime-version: 6.7
|
||||
sdk: org.kde.Sdk
|
||||
sdk-extensions:
|
||||
- org.freedesktop.Sdk.Extension.openjdk17
|
||||
- org.freedesktop.Sdk.Extension.openjdk8
|
||||
add-extensions:
|
||||
com.valvesoftware.Steam.Utility.gamescope:
|
||||
version: stable
|
||||
add-ld-path: lib
|
||||
no-autodownload: true
|
||||
autodelete: false
|
||||
directory: utils/gamescope
|
||||
|
||||
command: fjordlauncher
|
||||
finish-args:
|
||||
|
@ -25,9 +17,8 @@ finish-args:
|
|||
- --filesystem=xdg-run/app/com.discordapp.Discord:create
|
||||
# Mod drag&drop
|
||||
- --filesystem=xdg-download:ro
|
||||
|
||||
cleanup:
|
||||
- /lib/libGLU*
|
||||
# FTBApp import
|
||||
- --filesystem=~/.ftba:ro
|
||||
|
||||
modules:
|
||||
# Might be needed by some Controller mods (see https://github.com/isXander/Controlify/issues/31)
|
||||
|
@ -41,31 +32,20 @@ modules:
|
|||
builddir: true
|
||||
config-opts:
|
||||
- -DLauncher_BUILD_PLATFORM=flatpak
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DLauncher_QT_VERSION_MAJOR=5
|
||||
- -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/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=Release
|
||||
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
- -DBUILD_SHARED_LIBS:BOOL=ON
|
||||
- -DGLFW_USE_WAYLAND:BOOL=ON
|
||||
- -DGLFW_BUILD_DOCS:BOOL=OFF
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
builds:
|
||||
exclude:
|
||||
- "*.x86_64-darwin.*"
|
||||
include:
|
||||
- "checks.x86_64-linux.*"
|
||||
- "devShells.*.*"
|
||||
- "packages.*.*"
|
|
@ -44,10 +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 "settings/INIFile.h"
|
||||
#include "tools/GenericProfiler.h"
|
||||
#include "ui/InstanceWindow.h"
|
||||
#include "ui/MainWindow.h"
|
||||
|
||||
|
@ -66,8 +67,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"
|
||||
|
@ -106,9 +109,7 @@
|
|||
#include "icons/IconList.h"
|
||||
#include "net/HttpMetaCache.h"
|
||||
|
||||
#include "ui/GuiUtil.h"
|
||||
|
||||
#include "java/JavaUtils.h"
|
||||
#include "java/JavaInstallList.h"
|
||||
|
||||
#include "updater/ExternalUpdater.h"
|
||||
|
||||
|
@ -129,6 +130,7 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <sys.h>
|
||||
#include "SysInfo.h"
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#include <dlfcn.h>
|
||||
|
@ -154,6 +156,7 @@
|
|||
#endif
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
#include <windows.h>
|
||||
#include "WindowsConsole.h"
|
||||
#endif
|
||||
|
||||
|
@ -239,6 +242,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
{ { { "d", "dir" }, "Use a custom path as application root (use '.' for current directory)", "directory" },
|
||||
{ { "l", "launch" }, "Launch the specified instance (by instance ID)", "instance" },
|
||||
{ { "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" },
|
||||
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
|
||||
{ { "I", "import" }, "Import instance or resource from specified local path or URL", "url" },
|
||||
|
@ -253,6 +257,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
|
||||
m_instanceIdToLaunch = parser.value("launch");
|
||||
m_serverToJoin = parser.value("server");
|
||||
m_worldToJoin = parser.value("world");
|
||||
m_profileToUse = parser.value("profile");
|
||||
m_liveCheck = parser.isSet("alive");
|
||||
|
||||
|
@ -268,7 +273,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
}
|
||||
|
||||
// error if --launch is missing with --server or --profile
|
||||
if ((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) {
|
||||
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;
|
||||
m_status = Application::Failed;
|
||||
return;
|
||||
|
@ -295,12 +300,17 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
QString adjustedBy;
|
||||
QString dataPath;
|
||||
// change folder
|
||||
QString dataDirEnv;
|
||||
QString dirParam = parser.value("dir");
|
||||
if (!dirParam.isEmpty()) {
|
||||
// the dir param. it makes multimc data path point to whatever the user specified
|
||||
// on command line
|
||||
adjustedBy = "Command line";
|
||||
dataPath = dirParam;
|
||||
} else if (dataDirEnv = QProcessEnvironment::systemEnvironment().value(QString("%1_DATA_DIR").arg(BuildConfig.LAUNCHER_NAME.toUpper()));
|
||||
!dataDirEnv.isEmpty()) {
|
||||
adjustedBy = "System environment";
|
||||
dataPath = dataDirEnv;
|
||||
} else {
|
||||
QDir foo;
|
||||
if (DesktopServices::isSnap()) {
|
||||
|
@ -313,7 +323,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
adjustedBy = "Persistent data path";
|
||||
|
||||
#ifndef Q_OS_MACOS
|
||||
if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) {
|
||||
if (auto portableUserData = FS::PathCombine(m_rootPath, "UserData"); QDir(portableUserData).exists()) {
|
||||
dataPath = portableUserData;
|
||||
adjustedBy = "Portable user data path";
|
||||
m_portable = true;
|
||||
} else if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) {
|
||||
dataPath = m_rootPath;
|
||||
adjustedBy = "Portable data path";
|
||||
m_portable = true;
|
||||
|
@ -379,6 +393,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
|
||||
if (!m_serverToJoin.isEmpty()) {
|
||||
launch.args["server"] = m_serverToJoin;
|
||||
} else if (!m_worldToJoin.isEmpty()) {
|
||||
launch.args["world"] = m_worldToJoin;
|
||||
}
|
||||
if (!m_profileToUse.isEmpty()) {
|
||||
launch.args["profile"] = m_profileToUse;
|
||||
|
@ -394,20 +410,15 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
{
|
||||
static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "-%0.log";
|
||||
static const QString logBase = FS::PathCombine("logs", baseLogFile);
|
||||
auto moveFile = [](const QString& oldName, const QString& newName) {
|
||||
QFile::remove(newName);
|
||||
QFile::copy(oldName, newName);
|
||||
QFile::remove(oldName);
|
||||
};
|
||||
if (FS::ensureFolderPathExists("logs")) { // if this did not fail
|
||||
for (auto i = 0; i <= 4; i++)
|
||||
if (auto oldName = baseLogFile.arg(i);
|
||||
QFile::exists(oldName)) // do not pointlessly delete new files if the old ones are not there
|
||||
moveFile(oldName, logBase.arg(i));
|
||||
FS::move(oldName, logBase.arg(i));
|
||||
}
|
||||
|
||||
for (auto i = 4; i > 0; i--)
|
||||
moveFile(logBase.arg(i - 1), logBase.arg(i));
|
||||
FS::move(logBase.arg(i - 1), logBase.arg(i));
|
||||
|
||||
logFile = std::unique_ptr<QFile>(new QFile(logBase.arg(0)));
|
||||
if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
|
||||
|
@ -447,7 +458,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
|
||||
// search the dataPath()
|
||||
// seach app data standard path
|
||||
if (!foundLoggingRules && !isPortable() && dirParam.isEmpty()) {
|
||||
if (!foundLoggingRules && !isPortable() && dirParam.isEmpty() && dataDirEnv.isEmpty()) {
|
||||
logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile));
|
||||
if (!logRulesPath.isEmpty()) {
|
||||
qDebug() << "Found" << logRulesPath << "...";
|
||||
|
@ -530,6 +541,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
}
|
||||
if (!m_serverToJoin.isEmpty()) {
|
||||
qDebug() << "Address of server to join :" << m_serverToJoin;
|
||||
} else if (!m_worldToJoin.isEmpty()) {
|
||||
qDebug() << "Name of the world to join :" << m_worldToJoin;
|
||||
}
|
||||
qDebug() << "<> Paths set.";
|
||||
}
|
||||
|
@ -571,6 +584,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
|
||||
m_settings->registerSetting("NumberOfConcurrentTasks", 10);
|
||||
m_settings->registerSetting("NumberOfConcurrentDownloads", 6);
|
||||
m_settings->registerSetting("NumberOfManualRetries", 1);
|
||||
m_settings->registerSetting("RequestTimeout", 60);
|
||||
|
||||
QString defaultMonospace;
|
||||
int defaultSize = 11;
|
||||
|
@ -605,6 +620,8 @@ 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("SkinsDir", "skins");
|
||||
m_settings->registerSetting("JavaDir", "java");
|
||||
|
||||
// Editors
|
||||
m_settings->registerSetting("JsonEditor", QString());
|
||||
|
@ -633,7 +650,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
|
||||
|
@ -647,6 +664,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", true);
|
||||
|
@ -657,10 +678,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
m_settings->registerSetting("UseNativeGLFW", false);
|
||||
m_settings->registerSetting("CustomGLFWPath", "");
|
||||
|
||||
// Peformance related options
|
||||
// Performance related options
|
||||
m_settings->registerSetting("EnableFeralGamemode", false);
|
||||
m_settings->registerSetting("EnableMangoHud", false);
|
||||
m_settings->registerSetting("UseDiscreteGpu", false);
|
||||
m_settings->registerSetting("UseZink", false);
|
||||
|
||||
// Game time
|
||||
m_settings->registerSetting("ShowGameTime", true);
|
||||
|
@ -671,6 +693,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
// Minecraft mods
|
||||
m_settings->registerSetting("ModMetadataDisabled", false);
|
||||
m_settings->registerSetting("ModDependenciesDisabled", false);
|
||||
m_settings->registerSetting("SkipModpackUpdatePrompt", false);
|
||||
|
||||
// Missing authlib-injector behavior
|
||||
m_settings->registerSetting("MissingAuthlibInjectorBehavior", MissingAuthlibInjectorBehavior::Ask);
|
||||
|
@ -687,6 +710,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
|
||||
// The cat
|
||||
m_settings->registerSetting("TheCat", false);
|
||||
m_settings->registerSetting("CatOpacity", 100);
|
||||
|
||||
m_settings->registerSetting("StatusBarVisible", true);
|
||||
|
||||
m_settings->registerSetting("ToolbarsLocked", false);
|
||||
|
||||
|
@ -773,6 +799,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"));
|
||||
|
@ -826,7 +855,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
m_icons.reset(new IconList(instFolders, setting->get().toString()));
|
||||
connect(setting.get(), &Setting::SettingChanged,
|
||||
[&](const Setting&, QVariant value) { m_icons->directoryChanged(value.toString()); });
|
||||
qDebug() << "<> Instance icons intialized.";
|
||||
qDebug() << "<> Instance icons initialized.";
|
||||
}
|
||||
|
||||
// Themes
|
||||
|
@ -863,25 +892,19 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
{
|
||||
m_metacache.reset(new HttpMetaCache("metacache"));
|
||||
m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath());
|
||||
m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath());
|
||||
m_metacache->addBase("versions", QDir("versions").absolutePath());
|
||||
m_metacache->addBase("libraries", QDir("libraries").absolutePath());
|
||||
m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
|
||||
m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath());
|
||||
m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
|
||||
m_metacache->addBase("general", QDir("cache").absolutePath());
|
||||
m_metacache->addBase("ATLauncherPacks", QDir("cache/ATLauncherPacks").absolutePath());
|
||||
m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath());
|
||||
m_metacache->addBase("ModpacksCHPacks", QDir("cache/ModpacksCHPacks").absolutePath());
|
||||
m_metacache->addBase("TechnicPacks", QDir("cache/TechnicPacks").absolutePath());
|
||||
m_metacache->addBase("FlamePacks", QDir("cache/FlamePacks").absolutePath());
|
||||
m_metacache->addBase("FlameMods", QDir("cache/FlameMods").absolutePath());
|
||||
m_metacache->addBase("ModrinthPacks", QDir("cache/ModrinthPacks").absolutePath());
|
||||
m_metacache->addBase("ModrinthModpacks", QDir("cache/ModrinthModpacks").absolutePath());
|
||||
m_metacache->addBase("root", QDir::currentPath());
|
||||
m_metacache->addBase("translations", QDir("translations").absolutePath());
|
||||
m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
|
||||
m_metacache->addBase("meta", QDir("meta").absolutePath());
|
||||
m_metacache->addBase("java", QDir("cache/java").absolutePath());
|
||||
m_metacache->Load();
|
||||
qDebug() << "<> Cache initialized.";
|
||||
}
|
||||
|
@ -892,6 +915,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
// FIXME: what to do with these?
|
||||
m_profilers.insert("jprofiler", std::shared_ptr<BaseProfilerFactory>(new JProfilerFactory()));
|
||||
m_profilers.insert("jvisualvm", std::shared_ptr<BaseProfilerFactory>(new JVisualVMFactory()));
|
||||
m_profilers.insert("generic", std::shared_ptr<BaseProfilerFactory>(new GenericProfilerFactory()));
|
||||
for (auto profiler : m_profilers.values()) {
|
||||
profiler->registerSettings(m_settings);
|
||||
}
|
||||
|
@ -1004,7 +1028,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
"\n"
|
||||
"You are now running %1 .\n"
|
||||
"Check the Fjord Launcher updater log at: \n"
|
||||
"%1\n"
|
||||
"%2\n"
|
||||
"for details.")
|
||||
.arg(BuildConfig.printableVersionString())
|
||||
.arg(update_log_path);
|
||||
|
@ -1020,7 +1044,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)
|
||||
|
@ -1040,7 +1065,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);
|
||||
|
@ -1078,13 +1107,15 @@ bool Application::createSetupWizard()
|
|||
}
|
||||
return false;
|
||||
}();
|
||||
bool askjava = BuildConfig.JAVA_DOWNLOADER_ENABLED && !javaRequired && !m_settings->get("AutomaticJavaDownload").toBool() &&
|
||||
!m_settings->get("AutomaticJavaSwitch").toBool() && !m_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)
|
||||
|
@ -1101,6 +1132,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) {
|
||||
|
@ -1111,11 +1144,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()
|
||||
|
@ -1171,14 +1207,17 @@ void Application::performMainStartupAction()
|
|||
if (!m_instanceIdToLaunch.isEmpty()) {
|
||||
auto inst = instances()->getInstanceById(m_instanceIdToLaunch);
|
||||
if (inst) {
|
||||
MinecraftServerTargetPtr serverToJoin = nullptr;
|
||||
MinecraftTarget::Ptr targetToJoin = nullptr;
|
||||
MinecraftAccountPtr accountToUse = nullptr;
|
||||
|
||||
qDebug() << "<> Instance" << m_instanceIdToLaunch << "launching";
|
||||
if (!m_serverToJoin.isEmpty()) {
|
||||
// FIXME: validate the server string
|
||||
serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(m_serverToJoin)));
|
||||
targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(m_serverToJoin, false)));
|
||||
qDebug() << " Launching with server" << m_serverToJoin;
|
||||
} else if (!m_worldToJoin.isEmpty()) {
|
||||
targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(m_worldToJoin, true)));
|
||||
qDebug() << " Launching with world" << m_worldToJoin;
|
||||
}
|
||||
|
||||
if (!m_profileToUse.isEmpty()) {
|
||||
|
@ -1189,7 +1228,7 @@ void Application::performMainStartupAction()
|
|||
qDebug() << " Launching with account" << m_profileToUse;
|
||||
}
|
||||
|
||||
launch(inst, true, false, serverToJoin, accountToUse);
|
||||
launch(inst, true, false, targetToJoin, accountToUse);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1245,6 +1284,12 @@ void Application::performMainStartupAction()
|
|||
qDebug() << "<> Updater started.";
|
||||
}
|
||||
|
||||
{ // delete instances tmp dirctory
|
||||
auto instDir = m_settings->get("InstanceDir").toString();
|
||||
const QString tempRoot = FS::PathCombine(instDir, ".tmp");
|
||||
FS::deletePath(tempRoot);
|
||||
}
|
||||
|
||||
if (!m_urlsToImport.isEmpty()) {
|
||||
qDebug() << "<> Importing from url:" << m_urlsToImport;
|
||||
m_mainWindow->processURLs(m_urlsToImport);
|
||||
|
@ -1276,16 +1321,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") {
|
||||
|
@ -1298,6 +1350,7 @@ void Application::messageReceived(const QByteArray& message)
|
|||
} else if (command == "launch") {
|
||||
QString id = received.args["id"];
|
||||
QString server = received.args["server"];
|
||||
QString world = received.args["world"];
|
||||
QString profile = received.args["profile"];
|
||||
|
||||
InstancePtr instance;
|
||||
|
@ -1312,11 +1365,12 @@ void Application::messageReceived(const QByteArray& message)
|
|||
return;
|
||||
}
|
||||
|
||||
MinecraftServerTargetPtr serverObject = nullptr;
|
||||
MinecraftTarget::Ptr serverObject = nullptr;
|
||||
if (!server.isEmpty()) {
|
||||
serverObject = std::make_shared<MinecraftServerTarget>(MinecraftServerTarget::parse(server));
|
||||
serverObject = std::make_shared<MinecraftTarget>(MinecraftTarget::parse(server, false));
|
||||
} else if (!world.isEmpty()) {
|
||||
serverObject = std::make_shared<MinecraftTarget>(MinecraftTarget::parse(world, true));
|
||||
}
|
||||
|
||||
MinecraftAccountPtr accountObject;
|
||||
if (!profile.isEmpty()) {
|
||||
accountObject = accounts()->getAccountByProfileName(profile);
|
||||
|
@ -1365,11 +1419,7 @@ bool Application::openJsonEditor(const QString& filename)
|
|||
}
|
||||
}
|
||||
|
||||
bool Application::launch(InstancePtr instance,
|
||||
bool online,
|
||||
bool demo,
|
||||
MinecraftServerTargetPtr serverToJoin,
|
||||
MinecraftAccountPtr accountToUse)
|
||||
bool Application::launch(InstancePtr instance, bool online, bool demo, MinecraftTarget::Ptr targetToJoin, MinecraftAccountPtr accountToUse)
|
||||
{
|
||||
if (m_updateRunning) {
|
||||
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
|
||||
|
@ -1387,7 +1437,7 @@ bool Application::launch(InstancePtr instance,
|
|||
controller->setOnline(online);
|
||||
controller->setDemo(demo);
|
||||
controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get());
|
||||
controller->setServerToJoin(serverToJoin);
|
||||
controller->setTargetToJoin(targetToJoin);
|
||||
controller->setAccountToUse(accountToUse);
|
||||
if (window) {
|
||||
controller->setParentWidget(window);
|
||||
|
@ -1766,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,
|
||||
|
@ -1886,3 +1922,8 @@ QUrl Application::normalizeImportUrl(QString const& url)
|
|||
return QUrl::fromUserInput(url);
|
||||
}
|
||||
}
|
||||
|
||||
const QString Application::javaPath()
|
||||
{
|
||||
return m_settings->get("JavaDir").toString();
|
||||
}
|
||||
|
|
|
@ -47,8 +47,7 @@
|
|||
|
||||
#include <BaseInstance.h>
|
||||
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
#include "ui/themes/CatPack.h"
|
||||
#include "minecraft/launch/MinecraftTarget.h"
|
||||
|
||||
class LaunchController;
|
||||
class LocalPeer;
|
||||
|
@ -82,6 +81,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
|
||||
|
@ -162,6 +167,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; }
|
||||
|
@ -180,8 +188,6 @@ class Application : public QApplication {
|
|||
|
||||
void ShowGlobalSettings(class QWidget* parent, QString open_page = QString());
|
||||
|
||||
int suitableMaxMem();
|
||||
|
||||
bool updaterEnabled();
|
||||
QString updaterBinaryName();
|
||||
|
||||
|
@ -193,6 +199,8 @@ class Application : public QApplication {
|
|||
void globalSettingsClosed();
|
||||
int currentCatChanged(int index);
|
||||
|
||||
void oauthReplyRecieved(QVariantMap);
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
void clickedOnDock();
|
||||
#endif
|
||||
|
@ -201,7 +209,7 @@ class Application : public QApplication {
|
|||
bool launch(InstancePtr instance,
|
||||
bool online = true,
|
||||
bool demo = false,
|
||||
MinecraftServerTargetPtr serverToJoin = nullptr,
|
||||
MinecraftTarget::Ptr targetToJoin = nullptr,
|
||||
MinecraftAccountPtr accountToUse = nullptr);
|
||||
bool kill(InstancePtr instance);
|
||||
void closeCurrentWindow();
|
||||
|
@ -289,6 +297,7 @@ class Application : public QApplication {
|
|||
QString m_detectedOpenALPath;
|
||||
QString m_instanceIdToLaunch;
|
||||
QString m_serverToJoin;
|
||||
QString m_worldToJoin;
|
||||
QString m_profileToUse;
|
||||
bool m_liveCheck = false;
|
||||
QList<QUrl> m_urlsToImport;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <QFile>
|
||||
|
||||
#include "BaseInstaller.h"
|
||||
#include "FileSystem.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
|
||||
BaseInstaller::BaseInstaller() {}
|
||||
|
@ -42,7 +43,7 @@ bool BaseInstaller::add(MinecraftInstance* to)
|
|||
|
||||
bool BaseInstaller::remove(MinecraftInstance* from)
|
||||
{
|
||||
return QFile::remove(filename(from->instanceRoot()));
|
||||
return FS::deletePath(filename(from->instanceRoot()));
|
||||
}
|
||||
|
||||
QString BaseInstaller::filename(const QString& root) const
|
||||
|
|
|
@ -269,13 +269,18 @@ void BaseInstance::setRunning(bool running)
|
|||
|
||||
m_isRunning = running;
|
||||
|
||||
if (!m_settings->get("RecordGameTime").toBool()) {
|
||||
emit runningStatusChanged(running);
|
||||
emit runningStatusChanged(running);
|
||||
}
|
||||
|
||||
void BaseInstance::setMinecraftRunning(bool running)
|
||||
{
|
||||
if (!settings()->get("RecordGameTime").toBool()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (running) {
|
||||
m_timeStarted = QDateTime::currentDateTime();
|
||||
setLastLaunch(m_timeStarted.toMSecsSinceEpoch());
|
||||
} else {
|
||||
QDateTime timeEnded = QDateTime::currentDateTime();
|
||||
|
||||
|
@ -285,8 +290,6 @@ void BaseInstance::setRunning(bool running)
|
|||
|
||||
emit propertiesChanged(this);
|
||||
}
|
||||
|
||||
emit runningStatusChanged(running);
|
||||
}
|
||||
|
||||
int64_t BaseInstance::totalTimePlayed() const
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
#include "net/Mode.h"
|
||||
|
||||
#include "RuntimeContext.h"
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
#include "minecraft/launch/MinecraftTarget.h"
|
||||
|
||||
class QDir;
|
||||
class Task;
|
||||
|
@ -104,6 +104,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
|||
/// be unique.
|
||||
virtual QString id() const;
|
||||
|
||||
void setMinecraftRunning(bool running);
|
||||
void setRunning(bool running);
|
||||
bool isRunning() const;
|
||||
int64_t totalTimePlayed() const;
|
||||
|
@ -180,10 +181,10 @@ 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, MinecraftServerTargetPtr serverToJoin) = 0;
|
||||
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) = 0;
|
||||
|
||||
/// returns the current launch task (if any)
|
||||
shared_qobject_ptr<LaunchTask> getLaunchTask();
|
||||
|
@ -214,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; }
|
||||
|
@ -255,7 +256,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
|||
/**
|
||||
* 'print' a verbose description of the instance into a QStringList
|
||||
*/
|
||||
virtual QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) = 0;
|
||||
virtual QStringList verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) = 0;
|
||||
|
||||
Status currentStatus() const;
|
||||
|
||||
|
|
|
@ -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,6 +24,8 @@ set(CORE_SOURCES
|
|||
NullInstance.h
|
||||
MMCZip.h
|
||||
MMCZip.cpp
|
||||
Untar.h
|
||||
Untar.cpp
|
||||
StringUtils.h
|
||||
StringUtils.cpp
|
||||
QVariantUtils.h
|
||||
|
@ -135,7 +137,6 @@ set(NET_SOURCES
|
|||
net/MetaCacheSink.h
|
||||
net/Logging.h
|
||||
net/Logging.cpp
|
||||
net/NetAction.h
|
||||
net/NetJob.cpp
|
||||
net/NetJob.h
|
||||
net/NetUtils.h
|
||||
|
@ -148,7 +149,6 @@ set(NET_SOURCES
|
|||
net/HeaderProxy.h
|
||||
net/RawHeaderProxy.h
|
||||
net/ApiHeaderProxy.h
|
||||
net/StaticHeaderProxy.h
|
||||
net/ApiDownload.h
|
||||
net/ApiDownload.cpp
|
||||
net/ApiUpload.cpp
|
||||
|
@ -169,16 +169,18 @@ 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
|
||||
launch/steps/PrintServers.h
|
||||
launch/LaunchStep.cpp
|
||||
launch/LaunchStep.h
|
||||
launch/LaunchTask.cpp
|
||||
launch/LaunchTask.h
|
||||
launch/LogModel.cpp
|
||||
launch/LogModel.h
|
||||
launch/TaskStepWrapper.cpp
|
||||
launch/TaskStepWrapper.h
|
||||
)
|
||||
|
||||
# Old update system
|
||||
|
@ -214,18 +216,18 @@ 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
|
||||
minecraft/auth/AccountList.cpp
|
||||
minecraft/auth/AccountList.h
|
||||
minecraft/auth/AccountTask.cpp
|
||||
minecraft/auth/AccountTask.h
|
||||
minecraft/auth/AuthRequest.cpp
|
||||
minecraft/auth/AuthRequest.h
|
||||
minecraft/auth/AuthSession.cpp
|
||||
minecraft/auth/AuthSession.h
|
||||
minecraft/auth/AuthStep.cpp
|
||||
minecraft/auth/AuthStep.h
|
||||
minecraft/auth/MinecraftAccount.cpp
|
||||
minecraft/auth/MinecraftAccount.h
|
||||
|
@ -234,35 +236,21 @@ set(MINECRAFT_SOURCES
|
|||
minecraft/auth/Yggdrasil.cpp
|
||||
minecraft/auth/Yggdrasil.h
|
||||
|
||||
minecraft/auth/flows/AuthFlow.cpp
|
||||
minecraft/auth/flows/AuthFlow.h
|
||||
minecraft/auth/flows/AuthlibInjector.cpp
|
||||
minecraft/auth/flows/AuthlibInjector.h
|
||||
minecraft/auth/flows/Mojang.cpp
|
||||
minecraft/auth/flows/Mojang.h
|
||||
minecraft/auth/flows/MSA.cpp
|
||||
minecraft/auth/flows/MSA.h
|
||||
minecraft/auth/flows/Offline.cpp
|
||||
minecraft/auth/flows/Offline.h
|
||||
minecraft/auth/AuthFlow.cpp
|
||||
minecraft/auth/AuthFlow.h
|
||||
|
||||
minecraft/auth/steps/AuthlibInjectorMetadataStep.cpp
|
||||
minecraft/auth/steps/AuthlibInjectorMetadataStep.h
|
||||
minecraft/auth/steps/EntitlementsStep.cpp
|
||||
minecraft/auth/steps/EntitlementsStep.h
|
||||
minecraft/auth/steps/GetSkinStep.cpp
|
||||
minecraft/auth/steps/GetSkinStep.h
|
||||
minecraft/auth/steps/LauncherLoginStep.cpp
|
||||
minecraft/auth/steps/LauncherLoginStep.h
|
||||
minecraft/auth/steps/MigrationEligibilityStep.cpp
|
||||
minecraft/auth/steps/MigrationEligibilityStep.h
|
||||
minecraft/auth/steps/MinecraftProfileStep.cpp
|
||||
minecraft/auth/steps/MinecraftProfileStep.h
|
||||
minecraft/auth/steps/MinecraftProfileStepMojang.cpp
|
||||
minecraft/auth/steps/MinecraftProfileStepMojang.h
|
||||
minecraft/auth/steps/MSADeviceCodeStep.cpp
|
||||
minecraft/auth/steps/MSADeviceCodeStep.h
|
||||
minecraft/auth/steps/MSAStep.cpp
|
||||
minecraft/auth/steps/MSAStep.h
|
||||
minecraft/auth/steps/OfflineStep.cpp
|
||||
minecraft/auth/steps/OfflineStep.h
|
||||
minecraft/auth/steps/XboxAuthorizationStep.cpp
|
||||
minecraft/auth/steps/XboxAuthorizationStep.h
|
||||
minecraft/auth/steps/XboxProfileStep.cpp
|
||||
|
@ -294,8 +282,8 @@ set(MINECRAFT_SOURCES
|
|||
minecraft/launch/ExtractNatives.h
|
||||
minecraft/launch/LauncherPartLaunch.cpp
|
||||
minecraft/launch/LauncherPartLaunch.h
|
||||
minecraft/launch/MinecraftServerTarget.cpp
|
||||
minecraft/launch/MinecraftServerTarget.h
|
||||
minecraft/launch/MinecraftTarget.cpp
|
||||
minecraft/launch/MinecraftTarget.h
|
||||
minecraft/launch/PrintInstanceInfo.cpp
|
||||
minecraft/launch/PrintInstanceInfo.h
|
||||
minecraft/launch/ReconstructAssets.cpp
|
||||
|
@ -304,6 +292,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
|
||||
|
@ -318,8 +308,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
|
||||
|
@ -395,13 +383,17 @@ set(MINECRAFT_SOURCES
|
|||
minecraft/AssetsUtils.h
|
||||
minecraft/AssetsUtils.cpp
|
||||
|
||||
# Minecraft services
|
||||
minecraft/services/CapeChange.cpp
|
||||
minecraft/services/CapeChange.h
|
||||
minecraft/services/SkinUpload.cpp
|
||||
minecraft/services/SkinUpload.h
|
||||
minecraft/services/SkinDelete.cpp
|
||||
minecraft/services/SkinDelete.h
|
||||
# Minecraft skins
|
||||
minecraft/skins/CapeChange.cpp
|
||||
minecraft/skins/CapeChange.h
|
||||
minecraft/skins/SkinUpload.cpp
|
||||
minecraft/skins/SkinUpload.h
|
||||
minecraft/skins/SkinDelete.cpp
|
||||
minecraft/skins/SkinDelete.h
|
||||
minecraft/skins/SkinModel.cpp
|
||||
minecraft/skins/SkinModel.h
|
||||
minecraft/skins/SkinList.cpp
|
||||
minecraft/skins/SkinList.h
|
||||
|
||||
minecraft/Agent.h)
|
||||
|
||||
|
@ -446,8 +438,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
|
||||
|
@ -456,6 +446,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
|
||||
|
@ -477,6 +481,8 @@ set(TOOLS_SOURCES
|
|||
tools/JVisualVM.h
|
||||
tools/MCEditTool.cpp
|
||||
tools/MCEditTool.h
|
||||
tools/GenericProfiler.cpp
|
||||
tools/GenericProfiler.h
|
||||
)
|
||||
|
||||
set(META_SOURCES
|
||||
|
@ -623,7 +629,7 @@ set(PRISMUPDATER_SOURCES
|
|||
updater/prismupdater/UpdaterDialogs.cpp
|
||||
updater/prismupdater/GitHubRelease.h
|
||||
updater/prismupdater/GitHubRelease.cpp
|
||||
|
||||
|
||||
Json.h
|
||||
Json.cpp
|
||||
FileSystem.h
|
||||
|
@ -640,7 +646,7 @@ set(PRISMUPDATER_SOURCES
|
|||
# Zip
|
||||
MMCZip.h
|
||||
MMCZip.cpp
|
||||
|
||||
|
||||
# Time
|
||||
MMCTime.h
|
||||
MMCTime.cpp
|
||||
|
@ -655,7 +661,6 @@ set(PRISMUPDATER_SOURCES
|
|||
net/HttpMetaCache.h
|
||||
net/Logging.h
|
||||
net/Logging.cpp
|
||||
net/NetAction.h
|
||||
net/NetRequest.cpp
|
||||
net/NetRequest.h
|
||||
net/NetJob.cpp
|
||||
|
@ -684,6 +689,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"
|
||||
|
@ -696,7 +717,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}"
|
||||
)
|
||||
|
||||
|
@ -704,14 +725,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}"
|
||||
)
|
||||
|
||||
|
@ -782,6 +803,8 @@ SET(LAUNCHER_SOURCES
|
|||
DataMigrationTask.cpp
|
||||
ApplicationMessage.h
|
||||
ApplicationMessage.cpp
|
||||
SysInfo.h
|
||||
SysInfo.cpp
|
||||
|
||||
# GUI - general utilities
|
||||
DesktopServices.h
|
||||
|
@ -820,16 +843,12 @@ 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
|
||||
ui/InstanceWindow.cpp
|
||||
|
||||
# FIXME: maybe find a better home for this.
|
||||
SkinUtils.cpp
|
||||
SkinUtils.h
|
||||
FileIgnoreProxy.cpp
|
||||
FileIgnoreProxy.h
|
||||
FastFileIconProvider.cpp
|
||||
|
@ -847,6 +866,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
|
||||
|
@ -964,6 +987,9 @@ SET(LAUNCHER_SOURCES
|
|||
ui/pages/modplatform/ShaderPackPage.cpp
|
||||
ui/pages/modplatform/ShaderPackModel.cpp
|
||||
|
||||
|
||||
ui/pages/modplatform/ModpackProviderBasePage.h
|
||||
|
||||
ui/pages/modplatform/atlauncher/AtlFilterModel.cpp
|
||||
ui/pages/modplatform/atlauncher/AtlFilterModel.h
|
||||
ui/pages/modplatform/atlauncher/AtlListModel.cpp
|
||||
|
@ -1067,8 +1093,6 @@ SET(LAUNCHER_SOURCES
|
|||
ui/dialogs/ReviewMessageBox.h
|
||||
ui/dialogs/VersionSelectDialog.cpp
|
||||
ui/dialogs/VersionSelectDialog.h
|
||||
ui/dialogs/SkinUploadDialog.cpp
|
||||
ui/dialogs/SkinUploadDialog.h
|
||||
ui/dialogs/ResourceDownloadDialog.cpp
|
||||
ui/dialogs/ResourceDownloadDialog.h
|
||||
ui/dialogs/ScrollMessageBox.cpp
|
||||
|
@ -1082,7 +1106,12 @@ SET(LAUNCHER_SOURCES
|
|||
ui/dialogs/InstallLoaderDialog.cpp
|
||||
ui/dialogs/InstallLoaderDialog.h
|
||||
|
||||
ui/dialogs/skins/SkinManageDialog.cpp
|
||||
ui/dialogs/skins/SkinManageDialog.h
|
||||
|
||||
# GUI - widgets
|
||||
ui/widgets/CheckComboBox.cpp
|
||||
ui/widgets/CheckComboBox.h
|
||||
ui/widgets/Common.cpp
|
||||
ui/widgets/Common.h
|
||||
ui/widgets/CustomCommands.cpp
|
||||
|
@ -1167,6 +1196,8 @@ 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
|
||||
|
@ -1212,7 +1243,6 @@ qt_wrap_ui(LAUNCHER_UI
|
|||
ui/dialogs/NewComponentDialog.ui
|
||||
ui/dialogs/NewsDialog.ui
|
||||
ui/dialogs/ProfileSelectDialog.ui
|
||||
ui/dialogs/SkinUploadDialog.ui
|
||||
ui/dialogs/ExportInstanceDialog.ui
|
||||
ui/dialogs/ExportPackDialog.ui
|
||||
ui/dialogs/ExportToModListDialog.ui
|
||||
|
@ -1228,6 +1258,8 @@ qt_wrap_ui(LAUNCHER_UI
|
|||
ui/dialogs/ScrollMessageBox.ui
|
||||
ui/dialogs/BlockedModsDialog.ui
|
||||
ui/dialogs/ChooseProviderDialog.ui
|
||||
|
||||
ui/dialogs/skins/SkinManageDialog.ui
|
||||
)
|
||||
|
||||
qt_wrap_ui(PRISM_UPDATE_UI
|
||||
|
@ -1269,14 +1301,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
|
||||
|
@ -1287,7 +1315,6 @@ target_link_libraries(Launcher_logic
|
|||
tomlplusplus::tomlplusplus
|
||||
qdcss
|
||||
BuildConfig
|
||||
Katabasis
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
ghcFilesystem::ghc_filesystem
|
||||
)
|
||||
|
@ -1305,6 +1332,7 @@ target_link_libraries(Launcher_logic
|
|||
Qt${QT_VERSION_MAJOR}::Concurrent
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
Qt${QT_VERSION_MAJOR}::NetworkAuth
|
||||
${Launcher_QT_LIBS}
|
||||
)
|
||||
target_link_libraries(Launcher_logic
|
||||
|
@ -1375,13 +1403,12 @@ if(Launcher_BUILD_UPDATER)
|
|||
Qt${QT_VERSION_MAJOR}::Network
|
||||
${Launcher_QT_LIBS}
|
||||
cmark::cmark
|
||||
Katabasis
|
||||
)
|
||||
|
||||
add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp)
|
||||
target_sources("${Launcher_Name}_updater" PRIVATE updater/prismupdater/updater.exe.manifest)
|
||||
target_link_libraries("${Launcher_Name}_updater" prism_updater_logic)
|
||||
|
||||
|
||||
if(DEFINED Launcher_APP_BINARY_NAME)
|
||||
set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater")
|
||||
endif()
|
||||
|
@ -1540,7 +1567,6 @@ if(INSTALL_BUNDLE STREQUAL "full")
|
|||
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
PATTERN "*qopensslbackend*" EXCLUDE
|
||||
PATTERN "*qcertonlybackend*" EXCLUDE
|
||||
)
|
||||
install(
|
||||
|
@ -1551,10 +1577,78 @@ if(INSTALL_BUNDLE STREQUAL "full")
|
|||
REGEX "dd\\." EXCLUDE
|
||||
REGEX "_debug\\." EXCLUDE
|
||||
REGEX "\\.dSYM" EXCLUDE
|
||||
PATTERN "*qopensslbackend*" EXCLUDE
|
||||
PATTERN "*qcertonlybackend*" EXCLUDE
|
||||
)
|
||||
endif()
|
||||
# Wayland support
|
||||
if(EXISTS "${QT_PLUGINS_DIR}/wayland-graphics-integration-client")
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-client"
|
||||
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
)
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-client"
|
||||
CONFIGURATIONS Release MinSizeRel
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "dd\\." EXCLUDE
|
||||
REGEX "_debug\\." EXCLUDE
|
||||
REGEX "\\.dSYM" EXCLUDE
|
||||
)
|
||||
endif()
|
||||
if(EXISTS "${QT_PLUGINS_DIR}/wayland-graphics-integration-server")
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-server"
|
||||
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
)
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-server"
|
||||
CONFIGURATIONS Release MinSizeRel
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "dd\\." EXCLUDE
|
||||
REGEX "_debug\\." EXCLUDE
|
||||
REGEX "\\.dSYM" EXCLUDE
|
||||
)
|
||||
endif()
|
||||
if(EXISTS "${QT_PLUGINS_DIR}/wayland-decoration-client")
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/wayland-decoration-client"
|
||||
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
)
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/wayland-decoration-client"
|
||||
CONFIGURATIONS Release MinSizeRel
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "dd\\." EXCLUDE
|
||||
REGEX "_debug\\." EXCLUDE
|
||||
REGEX "\\.dSYM" EXCLUDE
|
||||
)
|
||||
endif()
|
||||
if(EXISTS "${QT_PLUGINS_DIR}/wayland-shell-integration")
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/wayland-shell-integration"
|
||||
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
)
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/wayland-shell-integration"
|
||||
CONFIGURATIONS Release MinSizeRel
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "dd\\." EXCLUDE
|
||||
REGEX "_debug\\." EXCLUDE
|
||||
REGEX "\\.dSYM" EXCLUDE
|
||||
)
|
||||
endif()
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake"
|
||||
|
|
|
@ -29,29 +29,18 @@
|
|||
#include "BuildConfig.h"
|
||||
|
||||
CreateAuthlibInjectorAccount::CreateAuthlibInjectorAccount(QUrl url, MinecraftAccountPtr account, QString username)
|
||||
: NetAction(), m_url(url), m_account(account), m_username(username)
|
||||
: NetRequest(), m_url(url), m_account(account), m_username(username)
|
||||
{}
|
||||
|
||||
void CreateAuthlibInjectorAccount::executeTask()
|
||||
QNetworkReply* CreateAuthlibInjectorAccount::getReply(QNetworkRequest& request)
|
||||
{
|
||||
m_state = State::Running;
|
||||
QNetworkRequest request(m_url);
|
||||
QNetworkReply* rep = APPLICATION->network()->get(request);
|
||||
|
||||
m_reply.reset(rep);
|
||||
connect(rep, &QNetworkReply::finished, this, &CreateAuthlibInjectorAccount::downloadFinished);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15
|
||||
connect(rep, &QNetworkReply::errorOccurred, this, &CreateAuthlibInjectorAccount::downloadError);
|
||||
#else
|
||||
connect(rep, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &CreateAuthlibInjectorAccount::downloadError);
|
||||
#endif
|
||||
connect(rep, &QNetworkReply::sslErrors, this, &CreateAuthlibInjectorAccount::sslErrors);
|
||||
setStatus(tr("Getting authlib-injector server details"));
|
||||
return m_network->get(request);
|
||||
}
|
||||
|
||||
void CreateAuthlibInjectorAccount::downloadError(QNetworkReply::NetworkError error)
|
||||
CreateAuthlibInjectorAccount::Ptr CreateAuthlibInjectorAccount::make(QUrl url, MinecraftAccountPtr account, QString username)
|
||||
{
|
||||
qDebug() << m_reply->errorString();
|
||||
m_state = State::Failed;
|
||||
return CreateAuthlibInjectorAccount::Ptr(new CreateAuthlibInjectorAccount(url, account, username));
|
||||
}
|
||||
|
||||
void CreateAuthlibInjectorAccount::downloadFinished()
|
||||
|
|
|
@ -19,28 +19,22 @@
|
|||
#pragma once
|
||||
|
||||
#include "minecraft/auth/MinecraftAccount.h"
|
||||
#include "net/NetAction.h"
|
||||
#include "net/NetRequest.h"
|
||||
|
||||
typedef shared_qobject_ptr<class CreateAuthlibInjectorAccount> CreateAuthlibInjectorAccountPtr;
|
||||
class CreateAuthlibInjectorAccount : public NetAction {
|
||||
class CreateAuthlibInjectorAccount : public Net::NetRequest {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CreateAuthlibInjectorAccount(QUrl url, MinecraftAccountPtr account, QString username);
|
||||
static CreateAuthlibInjectorAccountPtr make(QUrl url, MinecraftAccountPtr account, QString username)
|
||||
{
|
||||
return CreateAuthlibInjectorAccountPtr(new CreateAuthlibInjectorAccount(url, account, username));
|
||||
}
|
||||
using Ptr = shared_qobject_ptr<CreateAuthlibInjectorAccount>;
|
||||
CreateAuthlibInjectorAccount(QUrl url, MinecraftAccountPtr account, QString username);
|
||||
virtual ~CreateAuthlibInjectorAccount() = default;
|
||||
|
||||
static CreateAuthlibInjectorAccount::Ptr make(QUrl url, MinecraftAccountPtr account, QString username);
|
||||
|
||||
MinecraftAccountPtr getAccount();
|
||||
|
||||
void init() override {};
|
||||
|
||||
protected slots:
|
||||
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override {}
|
||||
void downloadError(QNetworkReply::NetworkError error) override;
|
||||
void downloadFinished() override;
|
||||
void downloadReadyRead() override {}
|
||||
|
||||
public slots:
|
||||
void executeTask() override;
|
||||
virtual QNetworkReply* getReply(QNetworkRequest&) override;
|
||||
void downloadFinished();
|
||||
|
||||
private:
|
||||
QUrl m_url;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -647,6 +650,19 @@ void ExternalLinkFileProcess::runLinkFile()
|
|||
qDebug() << "Process exited";
|
||||
}
|
||||
|
||||
bool moveByCopy(const QString& source, const QString& dest)
|
||||
{
|
||||
if (!copy(source, dest)()) { // copy
|
||||
qDebug() << "Copy of" << source << "to" << dest << "failed!";
|
||||
return false;
|
||||
}
|
||||
if (!deletePath(source)) { // remove original
|
||||
qDebug() << "Deletion of" << source << "failed!";
|
||||
return false;
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool move(const QString& source, const QString& dest)
|
||||
{
|
||||
std::error_code err;
|
||||
|
@ -654,13 +670,14 @@ bool move(const QString& source, const QString& dest)
|
|||
ensureFilePathExists(dest);
|
||||
fs::rename(StringUtils::toStdString(source), StringUtils::toStdString(dest), err);
|
||||
|
||||
if (err) {
|
||||
qWarning() << "Failed to move file:" << QString::fromStdString(err.message());
|
||||
qDebug() << "Source file:" << source;
|
||||
qDebug() << "Destination file:" << dest;
|
||||
if (err.value() != 0) {
|
||||
if (moveByCopy(source, dest))
|
||||
return true;
|
||||
qDebug() << "Move of" << source << "to" << dest << "failed!";
|
||||
qWarning() << "Failed to move file:" << QString::fromStdString(err.message()) << QString::number(err.value());
|
||||
return false;
|
||||
}
|
||||
|
||||
return err.value() == 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool deletePath(QString path)
|
||||
|
@ -904,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 =
|
||||
|
@ -1677,4 +1698,30 @@ QString getPathNameInLocal8bit(const QString& file)
|
|||
}
|
||||
#endif
|
||||
|
||||
QString getUniqueResourceName(const QString& filePath)
|
||||
{
|
||||
auto newFileName = filePath;
|
||||
if (!newFileName.endsWith(".disabled")) {
|
||||
return newFileName; // prioritize enabled mods
|
||||
}
|
||||
newFileName.chop(9);
|
||||
if (!QFile::exists(newFileName)) {
|
||||
return filePath;
|
||||
}
|
||||
QFileInfo fileInfo(filePath);
|
||||
auto baseName = fileInfo.completeBaseName();
|
||||
auto path = fileInfo.absolutePath();
|
||||
|
||||
int counter = 1;
|
||||
do {
|
||||
if (counter == 1) {
|
||||
newFileName = FS::PathCombine(path, baseName + ".duplicate");
|
||||
} else {
|
||||
newFileName = FS::PathCombine(path, baseName + ".duplicate" + QString::number(counter));
|
||||
}
|
||||
counter++;
|
||||
} while (QFile::exists(newFileName));
|
||||
|
||||
return newFileName;
|
||||
}
|
||||
} // namespace FS
|
||||
|
|
|
@ -72,7 +72,7 @@ void appendSafe(const QString& filename, const QByteArray& data);
|
|||
void append(const QString& filename, const QByteArray& data);
|
||||
|
||||
/**
|
||||
* read data from a file safely\
|
||||
* read data from a file safely
|
||||
*/
|
||||
QByteArray read(const QString& filename);
|
||||
|
||||
|
@ -240,6 +240,7 @@ class create_link : public QObject {
|
|||
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||
|
||||
int totalLinked() { return m_linked; }
|
||||
int totalToLink() { return static_cast<int>(m_links_to_make.size()); }
|
||||
|
||||
void runPrivileged() { runPrivileged(QString()); }
|
||||
void runPrivileged(const QString& offset);
|
||||
|
@ -559,4 +560,6 @@ uintmax_t hardLinkCount(const QString& path);
|
|||
QString getPathNameInLocal8bit(const QString& file);
|
||||
#endif
|
||||
|
||||
QString getUniqueResourceName(const QString& filePath);
|
||||
|
||||
} // namespace FS
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
#include "InstanceCopyTask.h"
|
||||
#include <QDebug>
|
||||
#include <QtConcurrentRun>
|
||||
#include <memory>
|
||||
#include "FileSystem.h"
|
||||
#include "NullInstance.h"
|
||||
#include "pathmatcher/RegexpMatcher.h"
|
||||
#include "settings/INISettingsObject.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs)
|
||||
{
|
||||
|
@ -38,38 +40,50 @@ void InstanceCopyTask::executeTask()
|
|||
{
|
||||
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
|
||||
|
||||
auto copySaves = [&]() {
|
||||
QFileInfo mcDir(FS::PathCombine(m_stagingPath, "minecraft"));
|
||||
QFileInfo dotMCDir(FS::PathCombine(m_stagingPath, ".minecraft"));
|
||||
|
||||
QString staging_mc_dir;
|
||||
if (mcDir.exists() && !dotMCDir.exists())
|
||||
staging_mc_dir = mcDir.filePath();
|
||||
else
|
||||
staging_mc_dir = dotMCDir.filePath();
|
||||
|
||||
FS::copy savesCopy(FS::PathCombine(m_origInstance->gameRoot(), "saves"), FS::PathCombine(staging_mc_dir, "saves"));
|
||||
savesCopy.followSymlinks(true);
|
||||
|
||||
return savesCopy();
|
||||
};
|
||||
|
||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this, copySaves] {
|
||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] {
|
||||
if (m_useClone) {
|
||||
FS::clone folderClone(m_origInstance->instanceRoot(), m_stagingPath);
|
||||
folderClone.matcher(m_matcher.get());
|
||||
|
||||
folderClone(true);
|
||||
setProgress(0, folderClone.totalCloned());
|
||||
connect(&folderClone, &FS::clone::fileCloned,
|
||||
[this](QString src, QString dst) { setProgress(m_progress + 1, m_progressTotal); });
|
||||
return folderClone();
|
||||
} else if (m_useLinks || m_useHardLinks) {
|
||||
}
|
||||
if (m_useLinks || m_useHardLinks) {
|
||||
std::unique_ptr<FS::copy> savesCopy;
|
||||
if (m_copySaves) {
|
||||
QFileInfo mcDir(FS::PathCombine(m_stagingPath, "minecraft"));
|
||||
QFileInfo dotMCDir(FS::PathCombine(m_stagingPath, ".minecraft"));
|
||||
|
||||
QString staging_mc_dir;
|
||||
if (dotMCDir.exists() && !mcDir.exists())
|
||||
staging_mc_dir = dotMCDir.filePath();
|
||||
else
|
||||
staging_mc_dir = mcDir.filePath();
|
||||
|
||||
savesCopy = std::make_unique<FS::copy>(FS::PathCombine(m_origInstance->gameRoot(), "saves"),
|
||||
FS::PathCombine(staging_mc_dir, "saves"));
|
||||
savesCopy->followSymlinks(true);
|
||||
(*savesCopy)(true);
|
||||
setProgress(0, savesCopy->totalCopied());
|
||||
connect(savesCopy.get(), &FS::copy::fileCopied, [this](QString src) { setProgress(m_progress + 1, m_progressTotal); });
|
||||
}
|
||||
FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath);
|
||||
int depth = m_linkRecursively ? -1 : 0; // we need to at least link the top level instead of the instance folder
|
||||
folderLink.linkRecursively(true).setMaxDepth(depth).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
|
||||
|
||||
folderLink(true);
|
||||
setProgress(0, m_progressTotal + folderLink.totalToLink());
|
||||
connect(&folderLink, &FS::create_link::fileLinked,
|
||||
[this](QString src, QString dst) { setProgress(m_progress + 1, m_progressTotal); });
|
||||
bool there_were_errors = false;
|
||||
|
||||
if (!folderLink()) {
|
||||
#if defined Q_OS_WIN32
|
||||
if (!m_useHardLinks) {
|
||||
setProgress(0, m_progressTotal);
|
||||
qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
|
||||
|
||||
qDebug() << "attempting to run with privelage";
|
||||
|
@ -94,13 +108,11 @@ void InstanceCopyTask::executeTask()
|
|||
}
|
||||
}
|
||||
|
||||
if (m_copySaves) {
|
||||
there_were_errors |= !copySaves();
|
||||
if (savesCopy) {
|
||||
there_were_errors |= !(*savesCopy)();
|
||||
}
|
||||
|
||||
return got_priv_results && !there_were_errors;
|
||||
} else {
|
||||
qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
|
||||
}
|
||||
#else
|
||||
qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
|
||||
|
@ -108,17 +120,19 @@ void InstanceCopyTask::executeTask()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (m_copySaves) {
|
||||
there_were_errors |= !copySaves();
|
||||
if (savesCopy) {
|
||||
there_were_errors |= !(*savesCopy)();
|
||||
}
|
||||
|
||||
return !there_were_errors;
|
||||
} else {
|
||||
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
|
||||
folderCopy.followSymlinks(false).matcher(m_matcher.get());
|
||||
|
||||
return folderCopy();
|
||||
}
|
||||
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
|
||||
folderCopy.followSymlinks(false).matcher(m_matcher.get());
|
||||
|
||||
folderCopy(true);
|
||||
setProgress(0, folderCopy.totalCopied());
|
||||
connect(&folderCopy, &FS::copy::fileCopied, [this](QString src) { setProgress(m_progress + 1, m_progressTotal); });
|
||||
return folderCopy();
|
||||
});
|
||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished);
|
||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &InstanceCopyTask::copyAborted);
|
||||
|
@ -159,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.
|
||||
|
||||
FS::write(allowed_symlinks_file.filePath(), allowed_symlinks);
|
||||
try {
|
||||
FS::write(allowed_symlinks_file.filePath(), allowed_symlinks);
|
||||
} catch (const FS::FileSystemException& e) {
|
||||
qCritical() << "Failed to write symlink :" << e.cause();
|
||||
}
|
||||
}
|
||||
|
||||
emitSucceeded();
|
||||
|
@ -170,3 +188,14 @@ void InstanceCopyTask::copyAborted()
|
|||
emitFailed(tr("Instance folder copy has been aborted."));
|
||||
return;
|
||||
}
|
||||
|
||||
bool InstanceCopyTask::abort()
|
||||
{
|
||||
if (m_copyFutureWatcher.isRunning()) {
|
||||
m_copyFutureWatcher.cancel();
|
||||
// NOTE: Here we don't do `emitAborted()` because it will be done when `m_copyFutureWatcher` actually cancels, which may not occur
|
||||
// immediately.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
|
@ -19,6 +19,7 @@ class InstanceCopyTask : public InstanceTask {
|
|||
protected:
|
||||
//! Entry point for tasks.
|
||||
virtual void executeTask() override;
|
||||
bool abort() override;
|
||||
void copyFinished();
|
||||
void copyAborted();
|
||||
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
||||
InstanceCreationTask::InstanceCreationTask() = default;
|
||||
#include "FileSystem.h"
|
||||
|
||||
void InstanceCreationTask::executeTask()
|
||||
{
|
||||
|
@ -39,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 (!QFile::remove(path)) {
|
||||
qCritical() << "Couldn't remove the old conflicting files.";
|
||||
emitFailed(tr("Failed to remove old conflicting files."));
|
||||
return;
|
||||
qCritical() << "Could not remove" << path;
|
||||
deleteFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteFailed) {
|
||||
emitFailed(tr("Failed to remove old conflicting files."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
class InstanceCreationTask : public InstanceTask {
|
||||
Q_OBJECT
|
||||
public:
|
||||
InstanceCreationTask();
|
||||
InstanceCreationTask() = default;
|
||||
virtual ~InstanceCreationTask() = default;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
|
||||
#include <QtConcurrentRun>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include <quazip/quazipdir.h>
|
||||
|
||||
|
@ -68,15 +69,8 @@ bool InstanceImportTask::abort()
|
|||
if (!canAbort())
|
||||
return false;
|
||||
|
||||
if (m_filesNetJob)
|
||||
m_filesNetJob->abort();
|
||||
if (m_extractFuture.isRunning()) {
|
||||
// NOTE: The tasks created by QtConcurrent::run() can't actually get cancelled,
|
||||
// but we can use this call to check the state when the extraction finishes.
|
||||
m_extractFuture.cancel();
|
||||
m_extractFuture.waitForFinished();
|
||||
}
|
||||
|
||||
if (task)
|
||||
task->abort();
|
||||
return Task::abort();
|
||||
}
|
||||
|
||||
|
@ -89,7 +83,6 @@ void InstanceImportTask::executeTask()
|
|||
processZipPack();
|
||||
} else {
|
||||
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
||||
m_downloadRequired = true;
|
||||
|
||||
downloadFromUrl();
|
||||
}
|
||||
|
@ -97,115 +90,132 @@ void InstanceImportTask::executeTask()
|
|||
|
||||
void InstanceImportTask::downloadFromUrl()
|
||||
{
|
||||
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
|
||||
const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path());
|
||||
|
||||
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
||||
entry->setStale(true);
|
||||
m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network()));
|
||||
m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry));
|
||||
m_archivePath = entry->getFullPath();
|
||||
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
||||
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
||||
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
|
||||
m_filesNetJob->start();
|
||||
auto filesNetJob = makeShared<NetJob>(tr("Modpack download"), APPLICATION->network());
|
||||
filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry));
|
||||
|
||||
connect(filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::processZipPack);
|
||||
connect(filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::setProgress);
|
||||
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);
|
||||
filesNetJob->start();
|
||||
}
|
||||
|
||||
void InstanceImportTask::downloadSucceeded()
|
||||
QString InstanceImportTask::getRootFromZip(QuaZip* zip, const QString& root)
|
||||
{
|
||||
processZipPack();
|
||||
m_filesNetJob.reset();
|
||||
}
|
||||
if (!isRunning()) {
|
||||
return {};
|
||||
}
|
||||
QuaZipDir rootDir(zip, root);
|
||||
for (auto&& fileName : rootDir.entryList(QDir::Files)) {
|
||||
setDetails(fileName);
|
||||
if (fileName == "instance.cfg") {
|
||||
qDebug() << "MultiMC:" << true;
|
||||
m_modpackType = ModpackType::MultiMC;
|
||||
return root;
|
||||
}
|
||||
if (fileName == "manifest.json") {
|
||||
qDebug() << "Flame:" << true;
|
||||
m_modpackType = ModpackType::Flame;
|
||||
return root;
|
||||
}
|
||||
|
||||
void InstanceImportTask::downloadFailed(QString reason)
|
||||
{
|
||||
emitFailed(reason);
|
||||
m_filesNetJob.reset();
|
||||
}
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
|
||||
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
|
||||
{
|
||||
setProgress(current, total);
|
||||
}
|
||||
// Recurse the search to non-ignored subfolders
|
||||
for (auto&& fileName : rootDir.entryList(QDir::Dirs)) {
|
||||
if ("overrides/" == fileName)
|
||||
continue;
|
||||
|
||||
void InstanceImportTask::downloadAborted()
|
||||
{
|
||||
emitAborted();
|
||||
m_filesNetJob.reset();
|
||||
QString result = getRootFromZip(zip, root + fileName);
|
||||
if (!result.isEmpty())
|
||||
return result;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void InstanceImportTask::processZipPack()
|
||||
{
|
||||
setStatus(tr("Extracting modpack"));
|
||||
setStatus(tr("Attempting to determine instance type"));
|
||||
QDir extractDir(m_stagingPath);
|
||||
qDebug() << "Attempting to create instance from" << m_archivePath;
|
||||
|
||||
// open the zip and find relevant files in it
|
||||
m_packZip.reset(new QuaZip(m_archivePath));
|
||||
if (!m_packZip->open(QuaZip::mdUnzip)) {
|
||||
auto packZip = std::make_shared<QuaZip>(m_archivePath);
|
||||
if (!packZip->open(QuaZip::mdUnzip)) {
|
||||
emitFailed(tr("Unable to open supplied modpack zip file."));
|
||||
return;
|
||||
}
|
||||
|
||||
QuaZipDir packZipDir(m_packZip.get());
|
||||
QuaZipDir packZipDir(packZip.get());
|
||||
qDebug() << "Attempting to determine instance type";
|
||||
|
||||
// https://docs.modrinth.com/docs/modpacks/format_definition/#storage
|
||||
bool modrinthFound = packZipDir.exists("/modrinth.index.json");
|
||||
bool technicFound = packZipDir.exists("/bin/modpack.jar") || packZipDir.exists("/bin/version.json");
|
||||
QString root;
|
||||
|
||||
// NOTE: Prioritize modpack platforms that aren't searched for recursively.
|
||||
// Especially Flame has a very common filename for its manifest, which may appear inside overrides for example
|
||||
if (modrinthFound) {
|
||||
// https://docs.modrinth.com/docs/modpacks/format_definition/#storage
|
||||
if (packZipDir.exists("/modrinth.index.json")) {
|
||||
// process as Modrinth pack
|
||||
qDebug() << "Modrinth:" << modrinthFound;
|
||||
qDebug() << "Modrinth:" << true;
|
||||
m_modpackType = ModpackType::Modrinth;
|
||||
} else if (technicFound) {
|
||||
} else if (packZipDir.exists("/bin/modpack.jar") || packZipDir.exists("/bin/version.json")) {
|
||||
// process as Technic pack
|
||||
qDebug() << "Technic:" << technicFound;
|
||||
extractDir.mkpath(".minecraft");
|
||||
extractDir.cd(".minecraft");
|
||||
qDebug() << "Technic:" << true;
|
||||
extractDir.mkpath("minecraft");
|
||||
extractDir.cd("minecraft");
|
||||
m_modpackType = ModpackType::Technic;
|
||||
} else {
|
||||
QStringList paths_to_ignore{ "overrides/" };
|
||||
|
||||
if (QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg", paths_to_ignore); !mmcRoot.isNull()) {
|
||||
// process as MultiMC instance/pack
|
||||
qDebug() << "MultiMC:" << mmcRoot;
|
||||
root = mmcRoot;
|
||||
m_modpackType = ModpackType::MultiMC;
|
||||
} else if (QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json", paths_to_ignore);
|
||||
!flameRoot.isNull()) {
|
||||
// process as Flame pack
|
||||
qDebug() << "Flame:" << flameRoot;
|
||||
root = flameRoot;
|
||||
m_modpackType = ModpackType::Flame;
|
||||
}
|
||||
root = getRootFromZip(packZip.get());
|
||||
setDetails("");
|
||||
}
|
||||
if (m_modpackType == ModpackType::Unknown) {
|
||||
emitFailed(tr("Archive does not contain a recognized modpack type."));
|
||||
return;
|
||||
}
|
||||
setStatus(tr("Extracting modpack"));
|
||||
|
||||
// make sure we extract just the pack
|
||||
m_extractFuture =
|
||||
QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
|
||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
|
||||
m_extractFutureWatcher.setFuture(m_extractFuture);
|
||||
auto zipTask = makeShared<MMCZip::ExtractZipTask>(packZip, extractDir, root);
|
||||
|
||||
auto progressStep = std::make_shared<TaskStepProgress>();
|
||||
connect(zipTask.get(), &Task::finished, this, [this, progressStep] {
|
||||
progressStep->state = TaskStepState::Succeeded;
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
|
||||
connect(zipTask.get(), &Task::succeeded, this, &InstanceImportTask::extractFinished);
|
||||
connect(zipTask.get(), &Task::aborted, this, &InstanceImportTask::emitAborted);
|
||||
connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) {
|
||||
progressStep->state = TaskStepState::Failed;
|
||||
stepProgress(*progressStep);
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(zipTask.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||
|
||||
connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) {
|
||||
progressStep->update(current, total);
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) {
|
||||
progressStep->status = status;
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
task.reset(zipTask);
|
||||
zipTask->start();
|
||||
}
|
||||
|
||||
void InstanceImportTask::extractFinished()
|
||||
{
|
||||
m_packZip.reset();
|
||||
|
||||
if (m_extractFuture.isCanceled())
|
||||
return;
|
||||
if (!m_extractFuture.result().has_value()) {
|
||||
emitFailed(tr("Failed to extract modpack"));
|
||||
return;
|
||||
}
|
||||
|
||||
QDir extractDir(m_stagingPath);
|
||||
|
||||
qDebug() << "Fixing permissions for extracted pack files...";
|
||||
|
@ -324,13 +334,15 @@ void InstanceImportTask::processMultiMC()
|
|||
m_instIcon = instance.iconKey();
|
||||
|
||||
auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon);
|
||||
if (importIconPath.isNull() || !QFile::exists(importIconPath))
|
||||
importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), "icon.png");
|
||||
if (!importIconPath.isNull() && QFile::exists(importIconPath)) {
|
||||
// import icon
|
||||
auto iconList = APPLICATION->icons();
|
||||
if (iconList->iconFileExists(m_instIcon)) {
|
||||
iconList->deleteIcon(m_instIcon);
|
||||
}
|
||||
iconList->installIcons({ importIconPath });
|
||||
iconList->installIcon(importIconPath, m_instIcon);
|
||||
}
|
||||
}
|
||||
emitSucceeded();
|
||||
|
|
|
@ -39,11 +39,8 @@
|
|||
#include <QFutureWatcher>
|
||||
#include <QUrl>
|
||||
#include "InstanceTask.h"
|
||||
#include "QObjectPtr.h"
|
||||
#include "modplatform/flame/PackManifest.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
class QuaZip;
|
||||
|
@ -54,35 +51,26 @@ class InstanceImportTask : public InstanceTask {
|
|||
explicit InstanceImportTask(const QUrl& sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {});
|
||||
|
||||
bool abort() override;
|
||||
const QVector<Flame::File>& getBlockedFiles() const { return m_blockedMods; }
|
||||
|
||||
protected:
|
||||
//! Entry point for tasks.
|
||||
virtual void executeTask() override;
|
||||
|
||||
private:
|
||||
void processZipPack();
|
||||
void processMultiMC();
|
||||
void processTechnic();
|
||||
void processFlame();
|
||||
void processModrinth();
|
||||
QString getRootFromZip(QuaZip* zip, const QString& root = "");
|
||||
|
||||
private slots:
|
||||
void downloadSucceeded();
|
||||
void downloadFailed(QString reason);
|
||||
void downloadProgressChanged(qint64 current, qint64 total);
|
||||
void downloadAborted();
|
||||
void processZipPack();
|
||||
void extractFinished();
|
||||
|
||||
private: /* data */
|
||||
NetJob::Ptr m_filesNetJob;
|
||||
QUrl m_sourceUrl;
|
||||
QString m_archivePath;
|
||||
bool m_downloadRequired = false;
|
||||
std::unique_ptr<QuaZip> m_packZip;
|
||||
QFuture<std::optional<QStringList>> m_extractFuture;
|
||||
QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher;
|
||||
QVector<Flame::File> m_blockedMods;
|
||||
Task::Ptr task;
|
||||
enum class ModpackType {
|
||||
Unknown,
|
||||
MultiMC,
|
||||
|
|
|
@ -372,13 +372,13 @@ void InstanceList::undoTrashInstance()
|
|||
|
||||
auto top = m_trashHistory.pop();
|
||||
|
||||
while (QDir(top.polyPath).exists()) {
|
||||
while (QDir(top.path).exists()) {
|
||||
top.id += "1";
|
||||
top.polyPath += "1";
|
||||
top.path += "1";
|
||||
}
|
||||
|
||||
qDebug() << "Moving" << top.trashPath << "back to" << top.polyPath;
|
||||
QFile(top.trashPath).rename(top.polyPath);
|
||||
qDebug() << "Moving" << top.trashPath << "back to" << top.path;
|
||||
QFile(top.trashPath).rename(top.path);
|
||||
|
||||
m_instanceGroupIndex[top.id] = top.groupName;
|
||||
increaseGroupCount(top.groupName);
|
||||
|
@ -635,8 +635,8 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id)
|
|||
|
||||
QString inst_type = instanceSettings->get("InstanceType").toString();
|
||||
|
||||
// NOTE: Some PolyMC versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a OneSix
|
||||
// instance
|
||||
// NOTE: Some launcher versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a
|
||||
// OneSix instance
|
||||
if (inst_type == "OneSix" || inst_type.isEmpty()) {
|
||||
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
|
||||
} else {
|
||||
|
@ -710,6 +710,12 @@ void InstanceList::saveGroupList()
|
|||
groupsArr.insert(name, groupObj);
|
||||
}
|
||||
toplevel.insert("groups", groupsArr);
|
||||
// empty string represents ungrouped "group"
|
||||
if (m_collapsedGroups.contains("")) {
|
||||
QJsonObject ungrouped;
|
||||
ungrouped.insert("hidden", QJsonValue(true));
|
||||
toplevel.insert("ungrouped", ungrouped);
|
||||
}
|
||||
QJsonDocument doc(toplevel);
|
||||
try {
|
||||
FS::write(groupFileName, doc.toJson());
|
||||
|
@ -805,6 +811,16 @@ void InstanceList::loadGroupList()
|
|||
increaseGroupCount(groupName);
|
||||
}
|
||||
}
|
||||
|
||||
bool ungroupedHidden = false;
|
||||
if (rootObj.value("ungrouped").isObject()) {
|
||||
QJsonObject ungrouped = rootObj.value("ungrouped").toObject();
|
||||
ungroupedHidden = ungrouped.value("hidden").toBool(false);
|
||||
}
|
||||
if (ungroupedHidden) {
|
||||
// empty string represents ungrouped "group"
|
||||
m_collapsedGroups.insert("");
|
||||
}
|
||||
m_groupsLoaded = true;
|
||||
qDebug() << "Group list loaded.";
|
||||
}
|
||||
|
@ -972,7 +988,6 @@ bool InstanceList::commitStagedInstance(const QString& path,
|
|||
if (groupName.isEmpty() && !groupName.isNull())
|
||||
groupName = QString();
|
||||
|
||||
QDir dir;
|
||||
QString instID;
|
||||
InstancePtr inst;
|
||||
|
||||
|
@ -996,7 +1011,7 @@ bool InstanceList::commitStagedInstance(const QString& path,
|
|||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!dir.rename(path, destination)) {
|
||||
if (!FS::move(path, destination)) {
|
||||
qWarning() << "Failed to move" << path << "to" << destination;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ enum class GroupsState { NotLoaded, Steady, Dirty };
|
|||
|
||||
struct TrashHistoryItem {
|
||||
QString id;
|
||||
QString polyPath;
|
||||
QString path;
|
||||
QString trashPath;
|
||||
QString groupName;
|
||||
};
|
||||
|
|
|
@ -22,7 +22,7 @@ class InstancePageProvider : protected QObject, public BasePageProvider {
|
|||
public:
|
||||
explicit InstancePageProvider(InstancePtr parent) { inst = parent; }
|
||||
|
||||
virtual ~InstancePageProvider() {};
|
||||
virtual ~InstancePageProvider() = default;
|
||||
virtual QList<BasePage*> getPages() override
|
||||
{
|
||||
QList<BasePage*> values;
|
||||
|
@ -39,7 +39,7 @@ class InstancePageProvider : protected QObject, public BasePageProvider {
|
|||
values.append(new TexturePackPage(onesix.get(), onesix->texturePackList()));
|
||||
values.append(new ShaderPackPage(onesix.get(), onesix->shaderPackList()));
|
||||
values.append(new NotesPage(onesix.get()));
|
||||
values.append(new WorldListPage(onesix.get(), onesix->worldList()));
|
||||
values.append(new WorldListPage(onesix, onesix->worldList()));
|
||||
values.append(new ServersPage(onesix));
|
||||
// values.append(new GameOptionsPage(onesix.get()));
|
||||
values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots")));
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "InstanceTask.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
|
||||
#include <QPushButton>
|
||||
|
@ -22,6 +24,9 @@ InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& ol
|
|||
|
||||
ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name)
|
||||
{
|
||||
if (APPLICATION->settings()->get("SkipModpackUpdatePrompt").toBool())
|
||||
return ShouldUpdate::SkipUpdating;
|
||||
|
||||
auto info = CustomMessageBox::selectable(
|
||||
parent, QObject::tr("Similar modpack was found!"),
|
||||
QObject::tr(
|
||||
|
|
|
@ -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, this));
|
||||
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, this));
|
||||
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;
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
#include "LaunchController.h"
|
||||
#include "Application.h"
|
||||
#include "launch/steps/PrintServers.h"
|
||||
#include "minecraft/auth/AccountData.h"
|
||||
#include "minecraft/auth/AccountList.h"
|
||||
#include "settings/MissingAuthlibInjectorBehavior.h"
|
||||
|
@ -55,14 +56,12 @@
|
|||
#include <QLineEdit>
|
||||
#include <QList>
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
#include <QStringList>
|
||||
|
||||
#include "BuildConfig.h"
|
||||
#include "JavaCommon.h"
|
||||
#include "launch/steps/TextPrint.h"
|
||||
#include "meta/Index.h"
|
||||
#include "meta/VersionList.h"
|
||||
#include "minecraft/auth/AccountTask.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
LaunchController::LaunchController(QObject* parent) : Task(parent) {}
|
||||
|
@ -90,7 +89,7 @@ void LaunchController::decideAccount()
|
|||
|
||||
// Find an account to use.
|
||||
auto accounts = APPLICATION->accounts();
|
||||
if (accounts->count() <= 0) {
|
||||
if (accounts->count() <= 0 || !accounts->anyAccountIsValid()) {
|
||||
// Tell the user they need to log in at least one account in order to play.
|
||||
auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"),
|
||||
tr("In order to play Minecraft, you must pick your username or log in to an account "
|
||||
|
@ -133,12 +132,63 @@ void LaunchController::decideAccount()
|
|||
}
|
||||
}
|
||||
|
||||
bool LaunchController::askPlayDemo()
|
||||
{
|
||||
QMessageBox box(m_parentWidget);
|
||||
box.setWindowTitle(tr("Play demo?"));
|
||||
box.setText(
|
||||
tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play "
|
||||
"the demo?"));
|
||||
box.setIcon(QMessageBox::Warning);
|
||||
auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole);
|
||||
auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole);
|
||||
box.setDefaultButton(cancelButton);
|
||||
|
||||
box.exec();
|
||||
return box.clickedButton() == demoButton;
|
||||
}
|
||||
|
||||
QString LaunchController::askOfflineName(QString playerName, bool demo, bool& ok)
|
||||
{
|
||||
// we ask the user for a player name
|
||||
QString message = tr("Choose your offline mode player name.");
|
||||
if (demo) {
|
||||
message = tr("Choose your demo mode player name.");
|
||||
}
|
||||
|
||||
QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString();
|
||||
QString usedname = lastOfflinePlayerName.isEmpty() ? playerName : lastOfflinePlayerName;
|
||||
QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok);
|
||||
if (!ok)
|
||||
return {};
|
||||
if (name.length()) {
|
||||
usedname = name;
|
||||
APPLICATION->settings()->set("LastOfflinePlayerName", usedname);
|
||||
}
|
||||
return usedname;
|
||||
}
|
||||
|
||||
void LaunchController::login()
|
||||
{
|
||||
decideAccount();
|
||||
|
||||
// if no account is selected, we bail
|
||||
if (!m_accountToUse) {
|
||||
// if no account is selected, ask about demo
|
||||
if (!m_demo) {
|
||||
m_demo = askPlayDemo();
|
||||
}
|
||||
if (m_demo) {
|
||||
// we ask the user for a player name
|
||||
bool ok = false;
|
||||
auto name = askOfflineName("Player", m_demo, ok);
|
||||
if (ok) {
|
||||
m_session = std::make_shared<AuthSession>();
|
||||
m_session->MakeDemo(name, MinecraftAccount::uuidFromUsername(name).toString().remove(QRegularExpression("[{}-]")));
|
||||
launchInstance();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// if no account is selected, we bail
|
||||
emitFailed(tr("No account selected for launch."));
|
||||
return;
|
||||
}
|
||||
|
@ -237,7 +287,8 @@ void LaunchController::login()
|
|||
bool tryagain = true;
|
||||
unsigned int tries = 0;
|
||||
|
||||
if (m_accountToUse->accountType() != AccountType::Offline && m_accountToUse->accountState() == AccountState::Offline) {
|
||||
if ((m_accountToUse->accountType() != AccountType::Offline && m_accountToUse->accountState() == AccountState::Offline) ||
|
||||
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();
|
||||
|
@ -275,24 +326,12 @@ void LaunchController::login()
|
|||
if (!m_session->wants_online) {
|
||||
// we ask the user for a player name
|
||||
bool ok = false;
|
||||
|
||||
QString message = tr("Choose your offline mode player name.");
|
||||
if (m_session->demo) {
|
||||
message = tr("Choose your demo mode player name.");
|
||||
}
|
||||
|
||||
QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString();
|
||||
QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName;
|
||||
QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok);
|
||||
auto name = askOfflineName(m_session->player_name, m_session->demo, ok);
|
||||
if (!ok) {
|
||||
tryagain = false;
|
||||
break;
|
||||
}
|
||||
if (name.length()) {
|
||||
usedname = name;
|
||||
APPLICATION->settings()->set("LastOfflinePlayerName", usedname);
|
||||
}
|
||||
m_session->MakeOffline(usedname);
|
||||
m_session->MakeOffline(name);
|
||||
// offline flavored game from here :3
|
||||
}
|
||||
if (m_accountToUse->ownsMinecraft()) {
|
||||
|
@ -312,20 +351,10 @@ void LaunchController::login()
|
|||
return;
|
||||
} else {
|
||||
// play demo ?
|
||||
QMessageBox box(m_parentWidget);
|
||||
box.setWindowTitle(tr("Play demo?"));
|
||||
box.setText(
|
||||
tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play "
|
||||
"the demo?"));
|
||||
box.setIcon(QMessageBox::Warning);
|
||||
auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole);
|
||||
auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole);
|
||||
box.setDefaultButton(cancelButton);
|
||||
|
||||
box.exec();
|
||||
if (box.clickedButton() == demoButton) {
|
||||
// play demo here
|
||||
m_session->MakeDemo();
|
||||
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."));
|
||||
|
@ -388,7 +417,7 @@ void LaunchController::launchInstance()
|
|||
return;
|
||||
}
|
||||
|
||||
m_launcher = m_instance->createLaunchTask(m_session, m_serverToJoin);
|
||||
m_launcher = m_instance->createLaunchTask(m_session, m_targetToJoin);
|
||||
if (!m_launcher) {
|
||||
emitFailed(tr("Couldn't instantiate a launcher."));
|
||||
return;
|
||||
|
@ -411,25 +440,8 @@ void LaunchController::launchInstance()
|
|||
|
||||
// Prepend Server Status
|
||||
QStringList servers = { "login.live.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" };
|
||||
QString resolved_servers = "";
|
||||
QHostInfo host_info;
|
||||
|
||||
for (QString server : servers) {
|
||||
host_info = QHostInfo::fromName(server);
|
||||
resolved_servers = resolved_servers + server + " resolves to:\n [";
|
||||
if (!host_info.addresses().isEmpty()) {
|
||||
for (QHostAddress address : host_info.addresses()) {
|
||||
resolved_servers = resolved_servers + address.toString();
|
||||
if (!host_info.addresses().endsWith(address)) {
|
||||
resolved_servers = resolved_servers + ", ";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resolved_servers = resolved_servers + "N/A";
|
||||
}
|
||||
resolved_servers = resolved_servers + "]\n\n";
|
||||
}
|
||||
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), resolved_servers, MessageLevel::Launcher));
|
||||
m_launcher->prependStep(makeShared<PrintServers>(m_launcher.get(), servers));
|
||||
} else {
|
||||
online_mode = m_demo ? "demo" : "offline";
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "minecraft/auth/MinecraftAccount.h"
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
#include "minecraft/launch/MinecraftTarget.h"
|
||||
|
||||
class InstanceWindow;
|
||||
class LaunchController : public Task {
|
||||
|
@ -50,7 +50,7 @@ class LaunchController : public Task {
|
|||
void executeTask() override;
|
||||
|
||||
LaunchController(QObject* parent = nullptr);
|
||||
virtual ~LaunchController() {};
|
||||
virtual ~LaunchController() = default;
|
||||
|
||||
void setInstance(InstancePtr instance) { m_instance = instance; }
|
||||
|
||||
|
@ -64,7 +64,7 @@ class LaunchController : public Task {
|
|||
|
||||
void setParentWidget(QWidget* widget) { m_parentWidget = widget; }
|
||||
|
||||
void setServerToJoin(MinecraftServerTargetPtr serverToJoin) { m_serverToJoin = std::move(serverToJoin); }
|
||||
void setTargetToJoin(MinecraftTarget::Ptr targetToJoin) { m_targetToJoin = std::move(targetToJoin); }
|
||||
|
||||
void setAccountToUse(MinecraftAccountPtr accountToUse) { m_accountToUse = std::move(accountToUse); }
|
||||
|
||||
|
@ -76,6 +76,8 @@ class LaunchController : public Task {
|
|||
void login();
|
||||
void launchInstance();
|
||||
void decideAccount();
|
||||
bool askPlayDemo();
|
||||
QString askOfflineName(QString playerName, bool demo, bool& ok);
|
||||
|
||||
private slots:
|
||||
void readyForLaunch();
|
||||
|
@ -94,5 +96,5 @@ class LaunchController : public Task {
|
|||
MinecraftAccountPtr m_accountToUse = nullptr;
|
||||
AuthSessionPtr m_session;
|
||||
shared_qobject_ptr<LaunchTask> m_launcher;
|
||||
MinecraftServerTargetPtr m_serverToJoin;
|
||||
MinecraftTarget::Ptr m_targetToJoin;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
@ -42,6 +42,7 @@
|
|||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
#include <QUrl>
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
|
@ -122,7 +123,7 @@ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files,
|
|||
zip.setUtf8Enabled(true);
|
||||
QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
|
||||
if (!zip.open(QuaZip::mdCreate)) {
|
||||
QFile::remove(fileCompressed);
|
||||
FS::deletePath(fileCompressed);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -130,7 +131,7 @@ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files,
|
|||
|
||||
zip.close();
|
||||
if (zip.getZipError() != 0) {
|
||||
QFile::remove(fileCompressed);
|
||||
FS::deletePath(fileCompressed);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -144,7 +145,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
|||
QuaZip zipOut(targetJarPath);
|
||||
zipOut.setUtf8Enabled(true);
|
||||
if (!zipOut.open(QuaZip::mdCreate)) {
|
||||
QFile::remove(targetJarPath);
|
||||
FS::deletePath(targetJarPath);
|
||||
qCritical() << "Failed to open the minecraft.jar for modding";
|
||||
return false;
|
||||
}
|
||||
|
@ -162,7 +163,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
|||
if (mod->type() == ResourceType::ZIPFILE) {
|
||||
if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles)) {
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
FS::deletePath(targetJarPath);
|
||||
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
|
||||
return false;
|
||||
}
|
||||
|
@ -171,7 +172,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
|||
auto filename = mod->fileinfo();
|
||||
if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) {
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
FS::deletePath(targetJarPath);
|
||||
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
|
||||
return false;
|
||||
}
|
||||
|
@ -194,7 +195,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
|||
|
||||
if (!compressDirFiles(&zipOut, parent_dir, files)) {
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
FS::deletePath(targetJarPath);
|
||||
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
|
||||
return false;
|
||||
}
|
||||
|
@ -202,7 +203,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
|||
} else {
|
||||
// Make sure we do not continue launching when something is missing or undefined...
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
FS::deletePath(targetJarPath);
|
||||
qCritical() << "Failed to add unknown mod type" << mod->fileinfo().fileName() << "to the jar.";
|
||||
return false;
|
||||
}
|
||||
|
@ -210,7 +211,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
|||
|
||||
if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key) { return !key.contains("META-INF"); })) {
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
FS::deletePath(targetJarPath);
|
||||
qCritical() << "Failed to insert minecraft.jar contents.";
|
||||
return false;
|
||||
}
|
||||
|
@ -218,7 +219,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
|||
// Recompress the jar
|
||||
zipOut.close();
|
||||
if (zipOut.getZipError() != 0) {
|
||||
QFile::remove(targetJarPath);
|
||||
FS::deletePath(targetJarPath);
|
||||
qCritical() << "Failed to finalize minecraft.jar!";
|
||||
return false;
|
||||
}
|
||||
|
@ -330,9 +331,31 @@ std::optional<QStringList> extractSubDir(QuaZip* zip, const QString& subdir, con
|
|||
}
|
||||
|
||||
extracted.append(target_file_path);
|
||||
QFile::setPermissions(target_file_path,
|
||||
QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser);
|
||||
auto fileInfo = QFileInfo(target_file_path);
|
||||
if (fileInfo.isFile()) {
|
||||
auto permissions = fileInfo.permissions();
|
||||
auto maxPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser |
|
||||
QFileDevice::Permission::ReadGroup | QFileDevice::Permission::ReadOther;
|
||||
auto minPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
|
||||
|
||||
auto newPermisions = (permissions & maxPermisions) | minPermisions;
|
||||
if (newPermisions != permissions) {
|
||||
if (!QFile::setPermissions(target_file_path, newPermisions)) {
|
||||
qWarning() << (QObject::tr("Could not fix permissions for %1").arg(target_file_path));
|
||||
}
|
||||
}
|
||||
} else if (fileInfo.isDir()) {
|
||||
// Ensure the folder has the minimal required permissions
|
||||
QFile::Permissions minimalPermissions = QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | QFile::ReadGroup |
|
||||
QFile::ExeGroup | QFile::ReadOther | QFile::ExeOther;
|
||||
|
||||
QFile::Permissions currentPermissions = fileInfo.permissions();
|
||||
if ((currentPermissions & minimalPermissions) != minimalPermissions) {
|
||||
if (!QFile::setPermissions(target_file_path, minimalPermissions)) {
|
||||
qWarning() << (QObject::tr("Could not fix permissions for %1").arg(target_file_path));
|
||||
}
|
||||
}
|
||||
}
|
||||
qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path;
|
||||
} while (zip->goToNextFile());
|
||||
|
||||
|
@ -466,7 +489,7 @@ auto ExportToZipTask::exportZip() -> ZipResult
|
|||
|
||||
auto absolute = file.absoluteFilePath();
|
||||
auto relative = m_dir.relativeFilePath(absolute);
|
||||
setStatus("Compresing: " + relative);
|
||||
setStatus("Compressing: " + relative);
|
||||
setProgress(m_progress + 1, m_progressTotal);
|
||||
if (m_follow_symlinks) {
|
||||
if (file.isSymLink())
|
||||
|
@ -490,10 +513,10 @@ auto ExportToZipTask::exportZip() -> ZipResult
|
|||
void ExportToZipTask::finish()
|
||||
{
|
||||
if (m_build_zip_future.isCanceled()) {
|
||||
QFile::remove(m_output_path);
|
||||
FS::deletePath(m_output_path);
|
||||
emitAborted();
|
||||
} else if (auto result = m_build_zip_future.result(); result.has_value()) {
|
||||
QFile::remove(m_output_path);
|
||||
FS::deletePath(m_output_path);
|
||||
emitFailed(result.value());
|
||||
} else {
|
||||
emitSucceeded();
|
||||
|
@ -510,6 +533,138 @@ bool ExportToZipTask::abort()
|
|||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
auto ExtractZipTask::extractZip() -> ZipResult
|
||||
{
|
||||
auto target = m_output_dir.absolutePath();
|
||||
auto target_top_dir = QUrl::fromLocalFile(target);
|
||||
|
||||
QStringList extracted;
|
||||
|
||||
qDebug() << "Extracting subdir" << m_subdirectory << "from" << m_input->getZipName() << "to" << target;
|
||||
auto numEntries = m_input->getEntriesCount();
|
||||
if (numEntries < 0) {
|
||||
return ZipResult(tr("Failed to enumerate files in archive"));
|
||||
}
|
||||
if (numEntries == 0) {
|
||||
logWarning(tr("Extracting empty archives seems odd..."));
|
||||
return ZipResult();
|
||||
}
|
||||
if (!m_input->goToFirstFile()) {
|
||||
return ZipResult(tr("Failed to seek to first file in zip"));
|
||||
}
|
||||
|
||||
setStatus("Extracting files...");
|
||||
setProgress(0, numEntries);
|
||||
do {
|
||||
if (m_zip_future.isCanceled())
|
||||
return ZipResult();
|
||||
setProgress(m_progress + 1, m_progressTotal);
|
||||
QString file_name = m_input->getCurrentFileName();
|
||||
if (!file_name.startsWith(m_subdirectory))
|
||||
continue;
|
||||
|
||||
auto relative_file_name = QDir::fromNativeSeparators(file_name.mid(m_subdirectory.size()));
|
||||
auto original_name = relative_file_name;
|
||||
setStatus("Unziping: " + relative_file_name);
|
||||
|
||||
// Fix subdirs/files ending with a / getting transformed into absolute paths
|
||||
if (relative_file_name.startsWith('/'))
|
||||
relative_file_name = relative_file_name.mid(1);
|
||||
|
||||
// Fix weird "folders with a single file get squashed" thing
|
||||
QString sub_path;
|
||||
if (relative_file_name.contains('/') && !relative_file_name.endsWith('/')) {
|
||||
sub_path = relative_file_name.section('/', 0, -2) + '/';
|
||||
FS::ensureFolderPathExists(FS::PathCombine(target, sub_path));
|
||||
|
||||
relative_file_name = relative_file_name.split('/').last();
|
||||
}
|
||||
|
||||
QString target_file_path;
|
||||
if (relative_file_name.isEmpty()) {
|
||||
target_file_path = target + '/';
|
||||
} else {
|
||||
target_file_path = FS::PathCombine(target_top_dir.toLocalFile(), sub_path, relative_file_name);
|
||||
if (relative_file_name.endsWith('/') && !target_file_path.endsWith('/'))
|
||||
target_file_path += '/';
|
||||
}
|
||||
|
||||
if (!target_top_dir.isParentOf(QUrl::fromLocalFile(target_file_path))) {
|
||||
return ZipResult(tr("Extracting %1 was cancelled, because it was effectively outside of the target path %2")
|
||||
.arg(relative_file_name, target));
|
||||
}
|
||||
|
||||
if (!JlCompress::extractFile(m_input.get(), "", target_file_path)) {
|
||||
JlCompress::removeFile(extracted);
|
||||
return ZipResult(tr("Failed to extract file %1 to %2").arg(original_name, target_file_path));
|
||||
}
|
||||
|
||||
extracted.append(target_file_path);
|
||||
auto fileInfo = QFileInfo(target_file_path);
|
||||
if (fileInfo.isFile()) {
|
||||
auto permissions = fileInfo.permissions();
|
||||
auto maxPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser |
|
||||
QFileDevice::Permission::ReadGroup | QFileDevice::Permission::ReadOther;
|
||||
auto minPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
|
||||
|
||||
auto newPermisions = (permissions & maxPermisions) | minPermisions;
|
||||
if (newPermisions != permissions) {
|
||||
if (!QFile::setPermissions(target_file_path, newPermisions)) {
|
||||
logWarning(tr("Could not fix permissions for %1").arg(target_file_path));
|
||||
}
|
||||
}
|
||||
} else if (fileInfo.isDir()) {
|
||||
// Ensure the folder has the minimal required permissions
|
||||
QFile::Permissions minimalPermissions = QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | QFile::ReadGroup |
|
||||
QFile::ExeGroup | QFile::ReadOther | QFile::ExeOther;
|
||||
|
||||
QFile::Permissions currentPermissions = fileInfo.permissions();
|
||||
if ((currentPermissions & minimalPermissions) != minimalPermissions) {
|
||||
if (!QFile::setPermissions(target_file_path, minimalPermissions)) {
|
||||
logWarning(tr("Could not fix permissions for %1").arg(target_file_path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path;
|
||||
} while (m_input->goToNextFile());
|
||||
|
||||
return ZipResult();
|
||||
}
|
||||
|
||||
void ExtractZipTask::finish()
|
||||
{
|
||||
if (m_zip_future.isCanceled()) {
|
||||
emitAborted();
|
||||
} else if (auto result = m_zip_future.result(); result.has_value()) {
|
||||
emitFailed(result.value());
|
||||
} else {
|
||||
emitSucceeded();
|
||||
}
|
||||
}
|
||||
|
||||
bool ExtractZipTask::abort()
|
||||
{
|
||||
if (m_zip_future.isRunning()) {
|
||||
m_zip_future.cancel();
|
||||
// NOTE: Here we don't do `emitAborted()` because it will be done when `m_build_zip_future` actually cancels, which may not occur
|
||||
// immediately.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace MMCZip
|
||||
|
|
|
@ -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,
|
||||
|
@ -205,5 +206,34 @@ class ExportToZipTask : public Task {
|
|||
QFuture<ZipResult> m_build_zip_future;
|
||||
QFutureWatcher<ZipResult> m_build_zip_watcher;
|
||||
};
|
||||
|
||||
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)
|
||||
{}
|
||||
virtual ~ExtractZipTask() = default;
|
||||
|
||||
using ZipResult = std::optional<QString>;
|
||||
|
||||
protected:
|
||||
virtual void executeTask() override;
|
||||
bool abort() override;
|
||||
|
||||
ZipResult extractZip();
|
||||
void finish();
|
||||
|
||||
private:
|
||||
std::shared_ptr<QuaZip> m_input;
|
||||
QDir m_output_dir;
|
||||
QString m_subdirectory;
|
||||
|
||||
QFuture<ZipResult> m_zip_future;
|
||||
QFutureWatcher<ZipResult> m_zip_watcher;
|
||||
};
|
||||
#endif
|
||||
} // namespace MMCZip
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -40,8 +40,8 @@ namespace MangoHud {
|
|||
|
||||
QString getLibraryString()
|
||||
{
|
||||
/*
|
||||
* Check for vulkan layers in this order:
|
||||
/**
|
||||
* Guess MangoHud install location by searching for vulkan layers in this order:
|
||||
*
|
||||
* $VK_LAYER_PATH
|
||||
* $XDG_DATA_DIRS (/usr/local/share/:/usr/share/)
|
||||
|
@ -49,8 +49,9 @@ QString getLibraryString()
|
|||
* /etc
|
||||
* $XDG_CONFIG_DIRS (/etc/xdg)
|
||||
* $XDG_CONFIG_HOME (~/.config)
|
||||
*
|
||||
* @returns Absolute path of libMangoHud.so if found and empty QString otherwise.
|
||||
*/
|
||||
|
||||
QStringList vkLayerList;
|
||||
{
|
||||
QString home = QDir::homePath();
|
||||
|
@ -85,7 +86,7 @@ QString getLibraryString()
|
|||
vkLayerList << FS::PathCombine(xdgConfigHome, "vulkan", "implicit_layer.d");
|
||||
}
|
||||
|
||||
for (QString vkLayer : vkLayerList) {
|
||||
for (const QString& vkLayer : vkLayerList) {
|
||||
// prefer to use architecture specific vulkan layers
|
||||
QString currentArch = QSysInfo::currentCpuArchitecture();
|
||||
|
||||
|
@ -95,8 +96,8 @@ QString getLibraryString()
|
|||
|
||||
QStringList manifestNames = { QString("MangoHud.%1.json").arg(currentArch), "MangoHud.json" };
|
||||
|
||||
QString filePath = "";
|
||||
for (QString manifestName : manifestNames) {
|
||||
QString filePath{};
|
||||
for (const QString& manifestName : manifestNames) {
|
||||
QString tryPath = FS::PathCombine(vkLayer, manifestName);
|
||||
if (QFile::exists(tryPath)) {
|
||||
filePath = tryPath;
|
||||
|
@ -107,14 +108,34 @@ 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");
|
||||
|
||||
auto conf = Json::requireDocument(filePath, vkLayer);
|
||||
auto confObject = Json::requireObject(conf, vkLayer);
|
||||
auto layer = Json::ensureObject(confObject, "layer");
|
||||
return 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
|
||||
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 QString();
|
||||
return {};
|
||||
}
|
||||
|
||||
QString findLibrary(QString libName)
|
||||
|
|
|
@ -46,14 +46,14 @@ class NullInstance : public BaseInstance {
|
|||
{
|
||||
setVersionBroken(true);
|
||||
}
|
||||
virtual ~NullInstance() {};
|
||||
virtual ~NullInstance() = default;
|
||||
void saveNow() override {}
|
||||
void loadSpecificSettings() override { setSpecificSettingsLoaded(true); }
|
||||
QString getStatusbarDescription() override { return tr("Unknown instance type"); };
|
||||
QSet<QString> traits() const override { return {}; };
|
||||
QString instanceConfigFolder() const override { return instanceRoot(); };
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override { return nullptr; }
|
||||
shared_qobject_ptr<Task> createUpdateTask([[maybe_unused]] Net::Mode mode) override { return nullptr; }
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftTarget::Ptr) 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>(); }
|
||||
|
@ -64,7 +64,7 @@ class NullInstance : public BaseInstance {
|
|||
bool canEdit() const override { return false; }
|
||||
bool canLaunch() const override { return false; }
|
||||
void populateLaunchMenu(QMenu* menu) override {}
|
||||
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override
|
||||
QStringList verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) override
|
||||
{
|
||||
QStringList out;
|
||||
out << "Null instance - placeholder.";
|
||||
|
|
|
@ -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,
|
||||
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,52 +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 "SkinUtils.h"
|
||||
#include "Application.h"
|
||||
#include "net/HttpMetaCache.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QPainter>
|
||||
|
||||
namespace SkinUtils {
|
||||
/*
|
||||
* Given a username, return a pixmap of the cached skin (if it exists), QPixmap() otherwise
|
||||
*/
|
||||
QPixmap getFaceFromCache(QString username, int height, int width)
|
||||
{
|
||||
QFile fskin(APPLICATION->metacache()->resolveEntry("skins", username + ".png")->getFullPath());
|
||||
|
||||
if (fskin.exists()) {
|
||||
QPixmap skinTexture(fskin.fileName());
|
||||
if (!skinTexture.isNull()) {
|
||||
QPixmap skin = QPixmap(8, 8);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
skin.fill(QColorConstants::Transparent);
|
||||
#else
|
||||
skin.fill(QColor(0, 0, 0, 0));
|
||||
#endif
|
||||
QPainter painter(&skin);
|
||||
painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8));
|
||||
painter.drawPixmap(0, 0, skinTexture.copy(40, 8, 8, 8));
|
||||
return skin.scaled(height, width, Qt::KeepAspectRatio);
|
||||
}
|
||||
}
|
||||
|
||||
return QPixmap();
|
||||
}
|
||||
} // namespace SkinUtils
|
|
@ -1,22 +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 <QPixmap>
|
||||
|
||||
namespace SkinUtils {
|
||||
QPixmap getFaceFromCache(QString id, int height = 64, int width = 64);
|
||||
}
|
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-64";
|
||||
if (arch.contains("86"))
|
||||
return "mac-os-86";
|
||||
// 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);
|
||||
}
|
|
@ -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:
|
||||
|
@ -308,12 +320,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:
|
||||
|
|
|
@ -322,7 +322,7 @@ const MMCIcon* IconList::icon(const QString& key) const
|
|||
|
||||
bool IconList::deleteIcon(const QString& key)
|
||||
{
|
||||
return iconFileExists(key) && QFile::remove(icon(key)->getFilePath());
|
||||
return iconFileExists(key) && FS::deletePath(icon(key)->getFilePath());
|
||||
}
|
||||
|
||||
bool IconList::trashIcon(const QString& key)
|
||||
|
|
|
@ -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 {
|
||||
|
@ -52,8 +52,7 @@ QString findBestIconIn(const QString& folder, const QString& iconKey)
|
|||
while (it.hasNext()) {
|
||||
it.next();
|
||||
auto fileInfo = it.fileInfo();
|
||||
|
||||
if (fileInfo.completeBaseName() == iconKey && isIconSuffix(fileInfo.suffix()))
|
||||
if ((fileInfo.completeBaseName() == iconKey || fileInfo.fileName() == iconKey) && isIconSuffix(fileInfo.suffix()))
|
||||
return fileInfo.absoluteFilePath();
|
||||
}
|
||||
return {};
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
set(CMAKE_MODULE_PATH "@CMAKE_MODULE_PATH@")
|
||||
|
||||
file(GLOB_RECURSE QTPLUGINS "${CMAKE_INSTALL_PREFIX}/@PLUGIN_DEST_DIR@/*@CMAKE_SHARED_LIBRARY_SUFFIX@")
|
||||
function(gp_resolved_file_type_override resolved_file type_var)
|
||||
if(resolved_file MATCHES "^/(usr/)?lib/libQt")
|
||||
|
|
|
@ -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, QObject* parent)
|
||||
: Task(parent), 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,8 +162,9 @@ 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;
|
||||
}
|
||||
|
||||
|
@ -171,7 +173,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
|||
auto java_vendor = results["java.vendor"];
|
||||
bool is_64 = os_arch == "x86_64" || os_arch == "amd64" || os_arch == "aarch64" || os_arch == "arm64";
|
||||
|
||||
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()
|
||||
|
|
|
@ -3,49 +3,51 @@
|
|||
#include <QTimer>
|
||||
#include <memory>
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
|
||||
#include "JavaVersion.h"
|
||||
#include "QObjectPtr.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
class JavaChecker;
|
||||
|
||||
struct JavaCheckResult {
|
||||
QString path;
|
||||
QString mojangPlatform;
|
||||
QString realPlatform;
|
||||
JavaVersion javaVersion;
|
||||
QString javaVendor;
|
||||
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 {
|
||||
class JavaChecker : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit JavaChecker(QObject* parent = 0);
|
||||
void performCheck();
|
||||
using QProcessPtr = shared_qobject_ptr<QProcess>;
|
||||
using Ptr = shared_qobject_ptr<JavaChecker>;
|
||||
|
||||
QString m_path;
|
||||
QString m_args;
|
||||
int m_id = 0;
|
||||
int m_minMem = 0;
|
||||
int m_maxMem = 0;
|
||||
int m_permGen = 64;
|
||||
struct Result {
|
||||
QString path;
|
||||
int id;
|
||||
QString mojangPlatform;
|
||||
QString realPlatform;
|
||||
JavaVersion javaVersion;
|
||||
QString javaVendor;
|
||||
QString outLog;
|
||||
QString errorLog;
|
||||
bool is_64bit = false;
|
||||
enum class Validity { Errored, ReturnedInvalidData, Valid } validity = Validity::Errored;
|
||||
};
|
||||
|
||||
explicit JavaChecker(QString path, QString args, int minMem = 0, int maxMem = 0, int permGen = 0, int id = 0, QObject* parent = 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,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(this, "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, this);
|
||||
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,9 +33,9 @@ class JavaInstallList : public BaseVersionList {
|
|||
enum class Status { NotDone, InProgress, Done };
|
||||
|
||||
public:
|
||||
explicit JavaInstallList(QObject* parent = 0);
|
||||
explicit JavaInstallList(QObject* parent = 0, bool onlyManagedVersions = false);
|
||||
|
||||
Task::Ptr getLoadTask() override;
|
||||
[[nodiscard]] Task::Ptr getLoadTask() override;
|
||||
bool isLoaded() override;
|
||||
const BaseVersion::Ptr at(int i) const override;
|
||||
int count() const 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
|
|
@ -182,56 +182,58 @@ QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString
|
|||
else if (keyType == KEY_WOW64_32KEY)
|
||||
archType = "32";
|
||||
|
||||
HKEY jreKey;
|
||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyName.toStdWString().c_str(), 0, KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) ==
|
||||
ERROR_SUCCESS) {
|
||||
// Read the current type version from the registry.
|
||||
// This will be used to find any key that contains the JavaHome value.
|
||||
for (HKEY baseRegistry : { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE }) {
|
||||
HKEY jreKey;
|
||||
if (RegOpenKeyExW(baseRegistry, keyName.toStdWString().c_str(), 0, KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) ==
|
||||
ERROR_SUCCESS) {
|
||||
// Read the current type version from the registry.
|
||||
// This will be used to find any key that contains the JavaHome value.
|
||||
|
||||
WCHAR subKeyName[255];
|
||||
DWORD subKeyNameSize, numSubKeys, retCode;
|
||||
WCHAR subKeyName[255];
|
||||
DWORD subKeyNameSize, numSubKeys, retCode;
|
||||
|
||||
// Get the number of subkeys
|
||||
RegQueryInfoKeyW(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
// Get the number of subkeys
|
||||
RegQueryInfoKeyW(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
|
||||
// Iterate until RegEnumKeyEx fails
|
||||
if (numSubKeys > 0) {
|
||||
for (DWORD i = 0; i < numSubKeys; i++) {
|
||||
subKeyNameSize = 255;
|
||||
retCode = RegEnumKeyExW(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL, NULL);
|
||||
QString newSubkeyName = QString::fromWCharArray(subKeyName);
|
||||
if (retCode == ERROR_SUCCESS) {
|
||||
// Now open the registry key for the version that we just got.
|
||||
QString newKeyName = keyName + "\\" + newSubkeyName + subkeySuffix;
|
||||
// Iterate until RegEnumKeyEx fails
|
||||
if (numSubKeys > 0) {
|
||||
for (DWORD i = 0; i < numSubKeys; i++) {
|
||||
subKeyNameSize = 255;
|
||||
retCode = RegEnumKeyExW(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL, NULL);
|
||||
QString newSubkeyName = QString::fromWCharArray(subKeyName);
|
||||
if (retCode == ERROR_SUCCESS) {
|
||||
// Now open the registry key for the version that we just got.
|
||||
QString newKeyName = keyName + "\\" + newSubkeyName + subkeySuffix;
|
||||
|
||||
HKEY newKey;
|
||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, newKeyName.toStdWString().c_str(), 0, KEY_READ | keyType, &newKey) ==
|
||||
ERROR_SUCCESS) {
|
||||
// Read the JavaHome value to find where Java is installed.
|
||||
DWORD valueSz = 0;
|
||||
if (RegQueryValueExW(newKey, keyJavaDir.toStdWString().c_str(), NULL, NULL, NULL, &valueSz) == ERROR_SUCCESS) {
|
||||
WCHAR* value = new WCHAR[valueSz];
|
||||
RegQueryValueExW(newKey, keyJavaDir.toStdWString().c_str(), NULL, NULL, (BYTE*)value, &valueSz);
|
||||
HKEY newKey;
|
||||
if (RegOpenKeyExW(baseRegistry, newKeyName.toStdWString().c_str(), 0, KEY_READ | keyType, &newKey) ==
|
||||
ERROR_SUCCESS) {
|
||||
// Read the JavaHome value to find where Java is installed.
|
||||
DWORD valueSz = 0;
|
||||
if (RegQueryValueExW(newKey, keyJavaDir.toStdWString().c_str(), NULL, NULL, NULL, &valueSz) == ERROR_SUCCESS) {
|
||||
WCHAR* value = new WCHAR[valueSz];
|
||||
RegQueryValueExW(newKey, keyJavaDir.toStdWString().c_str(), NULL, NULL, (BYTE*)value, &valueSz);
|
||||
|
||||
QString newValue = QString::fromWCharArray(value);
|
||||
delete[] value;
|
||||
QString newValue = QString::fromWCharArray(value);
|
||||
delete[] value;
|
||||
|
||||
// Now, we construct the version object and add it to the list.
|
||||
JavaInstallPtr javaVersion(new JavaInstall());
|
||||
// Now, we construct the version object and add it to the list.
|
||||
JavaInstallPtr javaVersion(new JavaInstall());
|
||||
|
||||
javaVersion->id = newSubkeyName;
|
||||
javaVersion->arch = archType;
|
||||
javaVersion->path = QDir(FS::PathCombine(newValue, "bin")).absoluteFilePath("javaw.exe");
|
||||
javas.append(javaVersion);
|
||||
javaVersion->id = newSubkeyName;
|
||||
javaVersion->arch = archType;
|
||||
javaVersion->path = QDir(FS::PathCombine(newValue, "bin")).absoluteFilePath("javaw.exe");
|
||||
javas.append(javaVersion);
|
||||
}
|
||||
|
||||
RegCloseKey(newKey);
|
||||
}
|
||||
|
||||
RegCloseKey(newKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RegCloseKey(jreKey);
|
||||
RegCloseKey(jreKey);
|
||||
}
|
||||
}
|
||||
|
||||
return javas;
|
||||
|
@ -345,6 +347,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
|||
}
|
||||
|
||||
candidates.append(getMinecraftJavaBundle());
|
||||
candidates.append(getPrismJavaBundle());
|
||||
candidates = addJavasFromEnv(candidates);
|
||||
candidates.removeDuplicates();
|
||||
return candidates;
|
||||
|
@ -374,25 +377,43 @@ QList<QString> JavaUtils::FindJavaPaths()
|
|||
auto home = qEnvironmentVariable("HOME");
|
||||
|
||||
// javas downloaded by sdkman
|
||||
javas.append(FS::PathCombine(home, ".sdkman/candidates/java"));
|
||||
QDir sdkmanDir(FS::PathCombine(home, ".sdkman/candidates/java"));
|
||||
QStringList sdkmanJavas = sdkmanDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
foreach (const QString& java, sdkmanJavas) {
|
||||
javas.append(sdkmanDir.absolutePath() + "/" + java + "/bin/java");
|
||||
}
|
||||
|
||||
// java in user library folder (like from intellij downloads)
|
||||
QDir userLibraryJVMDir(FS::PathCombine(home, "Library/Java/JavaVirtualMachines/"));
|
||||
QStringList userLibraryJVMJavas = userLibraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
foreach (const QString& java, userLibraryJVMJavas) {
|
||||
javas.append(userLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
|
||||
javas.append(userLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java");
|
||||
}
|
||||
|
||||
javas.append(getMinecraftJavaBundle());
|
||||
javas.append(getPrismJavaBundle());
|
||||
javas = addJavasFromEnv(javas);
|
||||
javas.removeDuplicates();
|
||||
return javas;
|
||||
}
|
||||
|
||||
#elif defined(Q_OS_LINUX)
|
||||
#elif defined(Q_OS_LINUX) || defined(Q_OS_OPENBSD) || defined(Q_OS_FREEBSD)
|
||||
QList<QString> JavaUtils::FindJavaPaths()
|
||||
{
|
||||
QList<QString> javas;
|
||||
javas.append(this->GetDefaultJava()->path);
|
||||
auto scanJavaDir = [&](const QString& dirPath) {
|
||||
auto scanJavaDir = [&](
|
||||
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"));
|
||||
|
@ -407,12 +428,21 @@ QList<QString> JavaUtils::FindJavaPaths()
|
|||
scanJavaDir(snap + dirPath);
|
||||
}
|
||||
};
|
||||
#if defined(Q_OS_LINUX)
|
||||
// oracle RPMs
|
||||
scanJavaDirs("/usr/java");
|
||||
// general locations used by distro packaging
|
||||
scanJavaDirs("/usr/lib/jvm");
|
||||
scanJavaDirs("/usr/lib64/jvm");
|
||||
scanJavaDirs("/usr/lib32/jvm");
|
||||
// Gentoo's locations for openjdk and openjdk-bin respectively
|
||||
auto gentooFilter = [](const QFileInfo& info) {
|
||||
QString fileName = info.fileName();
|
||||
return fileName.startsWith("openjdk-") || fileName.startsWith("openj9-");
|
||||
};
|
||||
scanJavaDir("/usr/lib64", gentooFilter);
|
||||
scanJavaDir("/usr/lib", gentooFilter);
|
||||
scanJavaDir("/opt", gentooFilter);
|
||||
// javas stored in Prism Launcher's folder
|
||||
scanJavaDirs("java");
|
||||
// manually installed JDKs in /opt
|
||||
|
@ -421,7 +451,10 @@ QList<QString> JavaUtils::FindJavaPaths()
|
|||
scanJavaDirs("/opt/ibm"); // IBM Semeru Certified Edition
|
||||
// flatpak
|
||||
scanJavaDirs("/app/jdk");
|
||||
|
||||
#elif defined(Q_OS_OPENBSD) || defined(Q_OS_FREEBSD)
|
||||
// ports install to /usr/local on OpenBSD & FreeBSD
|
||||
scanJavaDirs("/usr/local");
|
||||
#endif
|
||||
auto home = qEnvironmentVariable("HOME");
|
||||
|
||||
// javas downloaded by IntelliJ
|
||||
|
@ -432,6 +465,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
|||
scanJavaDirs(FS::PathCombine(home, ".gradle/jdks"));
|
||||
|
||||
javas.append(getMinecraftJavaBundle());
|
||||
javas.append(getPrismJavaBundle());
|
||||
javas = addJavasFromEnv(javas);
|
||||
javas.removeDuplicates();
|
||||
return javas;
|
||||
|
@ -445,6 +479,8 @@ QList<QString> JavaUtils::FindJavaPaths()
|
|||
javas.append(this->GetDefaultJava()->path);
|
||||
|
||||
javas.append(getMinecraftJavaBundle());
|
||||
javas.append(getPrismJavaBundle());
|
||||
javas.removeDuplicates();
|
||||
return addJavasFromEnv(javas);
|
||||
}
|
||||
#endif
|
||||
|
@ -456,12 +492,13 @@ 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");
|
||||
|
||||
auto appDataPath = QProcessEnvironment::systemEnvironment().value("APPDATA", "");
|
||||
processpaths << FS::PathCombine(QFileInfo(appDataPath).absoluteFilePath(), ".minecraft", "runtime");
|
||||
|
@ -486,7 +523,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;
|
||||
}
|
||||
|
@ -499,3 +536,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 = [&](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 = [&](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
|
138
launcher/java/download/ManifestDownloadTask.cpp
Normal file
138
launcher/java/download/ManifestDownloadTask.cpp
Normal file
|
@ -0,0 +1,138 @@
|
|||
// 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 linux only !
|
||||
auto path = Json::ensureString(meta, "target");
|
||||
if (!path.isEmpty()) {
|
||||
auto target = FS::PathCombine(file, "../" + path);
|
||||
QFile(target).link(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(parent), 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);
|
||||
|
|
|
@ -24,11 +24,8 @@ class LaunchTask;
|
|||
class LaunchStep : public Task {
|
||||
Q_OBJECT
|
||||
public: /* methods */
|
||||
explicit LaunchStep(LaunchTask* parent) : Task(nullptr), m_parent(parent) { bind(parent); };
|
||||
virtual ~LaunchStep() {};
|
||||
|
||||
private: /* methods */
|
||||
void bind(LaunchTask* parent);
|
||||
explicit LaunchStep(LaunchTask* parent);
|
||||
virtual ~LaunchStep() = default;
|
||||
|
||||
signals:
|
||||
void logLines(QStringList lines, MessageLevel::Enum level);
|
||||
|
|
|
@ -44,7 +44,6 @@
|
|||
#include <QRegularExpression>
|
||||
#include <QStandardPaths>
|
||||
#include "MessageLevel.h"
|
||||
#include "java/JavaChecker.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
void LaunchTask::init()
|
||||
|
@ -52,14 +51,14 @@ void LaunchTask::init()
|
|||
m_instance->setRunning(true);
|
||||
}
|
||||
|
||||
shared_qobject_ptr<LaunchTask> LaunchTask::create(InstancePtr inst)
|
||||
shared_qobject_ptr<LaunchTask> LaunchTask::create(MinecraftInstancePtr inst)
|
||||
{
|
||||
shared_qobject_ptr<LaunchTask> proc(new LaunchTask(inst));
|
||||
proc->init();
|
||||
return proc;
|
||||
}
|
||||
|
||||
LaunchTask::LaunchTask(InstancePtr instance) : m_instance(instance) {}
|
||||
LaunchTask::LaunchTask(MinecraftInstancePtr instance) : m_instance(instance) {}
|
||||
|
||||
void LaunchTask::appendStep(shared_qobject_ptr<LaunchStep> step)
|
||||
{
|
||||
|
|
|
@ -37,31 +37,31 @@
|
|||
|
||||
#pragma once
|
||||
#include <QObjectPtr.h>
|
||||
#include <minecraft/MinecraftInstance.h>
|
||||
#include <QProcess>
|
||||
#include "BaseInstance.h"
|
||||
#include "LaunchStep.h"
|
||||
#include "LogModel.h"
|
||||
#include "LoggedProcess.h"
|
||||
#include "MessageLevel.h"
|
||||
|
||||
class LaunchTask : public Task {
|
||||
Q_OBJECT
|
||||
protected:
|
||||
explicit LaunchTask(InstancePtr instance);
|
||||
explicit LaunchTask(MinecraftInstancePtr instance);
|
||||
void init();
|
||||
|
||||
public:
|
||||
enum State { NotStarted, Running, Waiting, Failed, Aborted, Finished };
|
||||
|
||||
public: /* methods */
|
||||
static shared_qobject_ptr<LaunchTask> create(InstancePtr inst);
|
||||
virtual ~LaunchTask() {};
|
||||
static shared_qobject_ptr<LaunchTask> create(MinecraftInstancePtr inst);
|
||||
virtual ~LaunchTask() = default;
|
||||
|
||||
void appendStep(shared_qobject_ptr<LaunchStep> step);
|
||||
void prependStep(shared_qobject_ptr<LaunchStep> step);
|
||||
void setCensorFilter(QMap<QString, QString> filter);
|
||||
|
||||
InstancePtr instance() { return m_instance; }
|
||||
MinecraftInstancePtr instance() { return m_instance; }
|
||||
|
||||
void setPid(qint64 pid) { m_pid = pid; }
|
||||
|
||||
|
@ -116,7 +116,7 @@ class LaunchTask : public Task {
|
|||
void finalizeSteps(bool successful, const QString& error);
|
||||
|
||||
protected: /* data */
|
||||
InstancePtr m_instance;
|
||||
MinecraftInstancePtr m_instance;
|
||||
shared_qobject_ptr<LogModel> m_logModel;
|
||||
QList<shared_qobject_ptr<LaunchStep>> m_steps;
|
||||
QMap<QString, QString> m_censorFilter;
|
||||
|
|
67
launcher/launch/TaskStepWrapper.cpp
Normal file
67
launcher/launch/TaskStepWrapper.cpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
/* 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 "TaskStepWrapper.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
void TaskStepWrapper::executeTask()
|
||||
{
|
||||
if (m_state == Task::State::AbortedByUser) {
|
||||
emitFailed(tr("Task aborted."));
|
||||
return;
|
||||
}
|
||||
connect(m_task.get(), &Task::finished, this, &TaskStepWrapper::updateFinished);
|
||||
connect(m_task.get(), &Task::progress, this, &TaskStepWrapper::setProgress);
|
||||
connect(m_task.get(), &Task::stepProgress, this, &TaskStepWrapper::propagateStepProgress);
|
||||
connect(m_task.get(), &Task::status, this, &TaskStepWrapper::setStatus);
|
||||
connect(m_task.get(), &Task::details, this, &TaskStepWrapper::setDetails);
|
||||
emit progressReportingRequest();
|
||||
}
|
||||
|
||||
void TaskStepWrapper::proceed()
|
||||
{
|
||||
m_task->start();
|
||||
}
|
||||
|
||||
void TaskStepWrapper::updateFinished()
|
||||
{
|
||||
if (m_task->wasSuccessful()) {
|
||||
m_task.reset();
|
||||
emitSucceeded();
|
||||
} else {
|
||||
QString reason = tr("Instance update failed because: %1\n\n").arg(m_task->failReason());
|
||||
m_task.reset();
|
||||
emit logLine(reason, MessageLevel::Fatal);
|
||||
emitFailed(reason);
|
||||
}
|
||||
}
|
||||
|
||||
bool TaskStepWrapper::canAbort() const
|
||||
{
|
||||
if (m_task) {
|
||||
return m_task->canAbort();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TaskStepWrapper::abort()
|
||||
{
|
||||
if (m_task && m_task->canAbort()) {
|
||||
auto status = m_task->abort();
|
||||
emitFailed("Aborted.");
|
||||
return status;
|
||||
}
|
||||
return Task::abort();
|
||||
}
|
|
@ -21,12 +21,11 @@
|
|||
#include <launch/LaunchStep.h>
|
||||
#include <net/Mode.h>
|
||||
|
||||
// FIXME: stupid. should be defined by the instance type? or even completely abstracted away...
|
||||
class Update : public LaunchStep {
|
||||
class TaskStepWrapper : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Update(LaunchTask* parent, Net::Mode mode) : LaunchStep(parent), m_mode(mode) {};
|
||||
virtual ~Update() {};
|
||||
explicit TaskStepWrapper(LaunchTask* parent, Task::Ptr task) : LaunchStep(parent), m_task(task) {};
|
||||
virtual ~TaskStepWrapper() = default;
|
||||
|
||||
void executeTask() override;
|
||||
bool canAbort() const override;
|
||||
|
@ -38,7 +37,5 @@ class Update : public LaunchStep {
|
|||
void updateFinished();
|
||||
|
||||
private:
|
||||
Task::Ptr m_updateTask;
|
||||
bool m_aborted = false;
|
||||
Net::Mode m_mode = Net::Mode::Offline;
|
||||
Task::Ptr m_task;
|
||||
};
|
|
@ -37,6 +37,7 @@
|
|||
#include <FileSystem.h>
|
||||
#include <launch/LaunchTask.h>
|
||||
#include <sys.h>
|
||||
#include <QCryptographicHash>
|
||||
#include <QFileInfo>
|
||||
#include <QStandardPaths>
|
||||
#include "java/JavaUtils.h"
|
||||
|
@ -45,20 +46,23 @@ void CheckJava::executeTask()
|
|||
{
|
||||
auto instance = m_parent->instance();
|
||||
auto settings = instance->settings();
|
||||
m_javaPath = FS::ResolveExecutable(settings->get("JavaPath").toString());
|
||||
|
||||
QString javaPathSetting = settings->get("JavaPath").toString();
|
||||
m_javaPath = FS::ResolveExecutable(javaPathSetting);
|
||||
|
||||
bool perInstance = settings->get("OverrideJava").toBool() || settings->get("OverrideJavaLocation").toBool();
|
||||
|
||||
auto realJavaPath = QStandardPaths::findExecutable(m_javaPath);
|
||||
if (realJavaPath.isEmpty()) {
|
||||
if (perInstance) {
|
||||
emit logLine(QString("The java binary \"%1\" couldn't be found. Please fix the java path "
|
||||
emit logLine(QString("The Java binary \"%1\" couldn't be found. Please fix the Java path "
|
||||
"override in the instance's settings or disable it.")
|
||||
.arg(m_javaPath),
|
||||
.arg(javaPathSetting),
|
||||
MessageLevel::Warning);
|
||||
} else {
|
||||
emit logLine(QString("The java binary \"%1\" couldn't be found. Please set up java in "
|
||||
emit logLine(QString("The Java binary \"%1\" couldn't be found. Please set up Java in "
|
||||
"the settings.")
|
||||
.arg(m_javaPath),
|
||||
.arg(javaPathSetting),
|
||||
MessageLevel::Warning);
|
||||
}
|
||||
emitFailed(QString("Java path is not valid."));
|
||||
|
@ -90,11 +94,10 @@ void CheckJava::executeTask()
|
|||
// if timestamps are not the same, or something is missing, check!
|
||||
if (m_javaSignature != storedSignature || storedVersion.size() == 0 || storedArchitecture.size() == 0 ||
|
||||
storedRealArchitecture.size() == 0 || storedVendor.size() == 0) {
|
||||
m_JavaChecker.reset(new JavaChecker);
|
||||
m_JavaChecker.reset(new JavaChecker(realJavaPath, "", 0, 0, 0, 0, this));
|
||||
emit logLine(QString("Checking Java version..."), MessageLevel::Launcher);
|
||||
connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished);
|
||||
m_JavaChecker->m_path = realJavaPath;
|
||||
m_JavaChecker->performCheck();
|
||||
m_JavaChecker->start();
|
||||
return;
|
||||
} else {
|
||||
auto verString = instance->settings()->get("JavaVersion").toString();
|
||||
|
@ -103,13 +106,14 @@ void CheckJava::executeTask()
|
|||
auto vendorString = instance->settings()->get("JavaVendor").toString();
|
||||
printJavaInfo(verString, archString, realArchString, vendorString);
|
||||
}
|
||||
m_parent->instance()->updateRuntimeContext();
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
void CheckJava::checkJavaFinished(JavaCheckResult result)
|
||||
void CheckJava::checkJavaFinished(const JavaChecker::Result& result)
|
||||
{
|
||||
switch (result.validity) {
|
||||
case JavaCheckResult::Validity::Errored: {
|
||||
case JavaChecker::Result::Validity::Errored: {
|
||||
// Error message displayed if java can't start
|
||||
emit logLine(QString("Could not start java:"), MessageLevel::Error);
|
||||
emit logLines(result.errorLog.split('\n'), MessageLevel::Error);
|
||||
|
@ -117,14 +121,15 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
|
|||
emitFailed(QString("Could not start java!"));
|
||||
return;
|
||||
}
|
||||
case JavaCheckResult::Validity::ReturnedInvalidData: {
|
||||
case JavaChecker::Result::Validity::ReturnedInvalidData: {
|
||||
emit logLine(QString("Java checker returned some invalid data we don't understand:"), MessageLevel::Error);
|
||||
emit logLines(result.outLog.split('\n'), MessageLevel::Warning);
|
||||
emit logLine("\nMinecraft might not start properly.", MessageLevel::Launcher);
|
||||
m_parent->instance()->updateRuntimeContext();
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
case JavaCheckResult::Validity::Valid: {
|
||||
case JavaChecker::Result::Validity::Valid: {
|
||||
auto instance = m_parent->instance();
|
||||
printJavaInfo(result.javaVersion.toString(), result.mojangPlatform, result.realPlatform, result.javaVendor);
|
||||
instance->settings()->set("JavaVersion", result.javaVersion.toString());
|
||||
|
@ -132,6 +137,7 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
|
|||
instance->settings()->set("JavaRealArchitecture", result.realPlatform);
|
||||
instance->settings()->set("JavaVendor", result.javaVendor);
|
||||
instance->settings()->set("JavaSignature", m_javaSignature);
|
||||
m_parent->instance()->updateRuntimeContext();
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -23,12 +23,12 @@ class CheckJava : public LaunchStep {
|
|||
Q_OBJECT
|
||||
public:
|
||||
explicit CheckJava(LaunchTask* parent) : LaunchStep(parent) {};
|
||||
virtual ~CheckJava() {};
|
||||
virtual ~CheckJava() = default;
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool canAbort() const { return false; }
|
||||
private slots:
|
||||
void checkJavaFinished(JavaCheckResult result);
|
||||
void checkJavaFinished(const JavaChecker::Result& result);
|
||||
|
||||
private:
|
||||
void printJavaInfo(const QString& version, const QString& architecture, const QString& realArchitecture, const QString& vendor);
|
||||
|
@ -37,5 +37,5 @@ class CheckJava : public LaunchStep {
|
|||
private:
|
||||
QString m_javaPath;
|
||||
QString m_javaSignature;
|
||||
JavaCheckerPtr m_JavaChecker;
|
||||
JavaChecker::Ptr m_JavaChecker;
|
||||
};
|
||||
|
|
|
@ -30,7 +30,7 @@ void LookupServerAddress::setLookupAddress(const QString& lookupAddress)
|
|||
m_dnsLookup->setName(QString("_minecraft._tcp.%1").arg(lookupAddress));
|
||||
}
|
||||
|
||||
void LookupServerAddress::setOutputAddressPtr(MinecraftServerTargetPtr output)
|
||||
void LookupServerAddress::setOutputAddressPtr(MinecraftTarget::Ptr output)
|
||||
{
|
||||
m_output = std::move(output);
|
||||
}
|
||||
|
|
|
@ -19,20 +19,20 @@
|
|||
#include <launch/LaunchStep.h>
|
||||
#include <QDnsLookup>
|
||||
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
#include "minecraft/launch/MinecraftTarget.h"
|
||||
|
||||
class LookupServerAddress : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LookupServerAddress(LaunchTask* parent);
|
||||
virtual ~LookupServerAddress() {};
|
||||
virtual ~LookupServerAddress() = default;
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool abort();
|
||||
virtual bool canAbort() const { return true; }
|
||||
|
||||
void setLookupAddress(const QString& lookupAddress);
|
||||
void setOutputAddressPtr(MinecraftServerTargetPtr output);
|
||||
void setOutputAddressPtr(MinecraftTarget::Ptr output);
|
||||
|
||||
private slots:
|
||||
void on_dnsLookupFinished();
|
||||
|
@ -42,5 +42,5 @@ class LookupServerAddress : public LaunchStep {
|
|||
|
||||
QDnsLookup* m_dnsLookup;
|
||||
QString m_lookupAddress;
|
||||
MinecraftServerTargetPtr m_output;
|
||||
MinecraftTarget::Ptr m_output;
|
||||
};
|
||||
|
|
65
launcher/launch/steps/PrintServers.cpp
Normal file
65
launcher/launch/steps/PrintServers.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2024 Leia uwu <leia@tutamail.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 "PrintServers.h"
|
||||
#include "QHostInfo"
|
||||
|
||||
PrintServers::PrintServers(LaunchTask* parent, const QStringList& servers) : LaunchStep(parent)
|
||||
{
|
||||
m_servers = servers;
|
||||
}
|
||||
|
||||
void PrintServers::executeTask()
|
||||
{
|
||||
for (QString server : m_servers) {
|
||||
QHostInfo::lookupHost(server, this, &PrintServers::resolveServer);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintServers::resolveServer(const QHostInfo& host_info)
|
||||
{
|
||||
QString server = host_info.hostName();
|
||||
QString addresses = server + " resolves to:\n [";
|
||||
|
||||
if (!host_info.addresses().isEmpty()) {
|
||||
for (QHostAddress address : host_info.addresses()) {
|
||||
addresses += address.toString();
|
||||
if (!host_info.addresses().endsWith(address)) {
|
||||
addresses += ", ";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addresses += "N/A";
|
||||
}
|
||||
addresses += "]\n\n";
|
||||
|
||||
m_server_to_address.insert(server, addresses);
|
||||
|
||||
// print server info in order once all servers are resolved
|
||||
if (m_server_to_address.size() >= m_servers.size()) {
|
||||
for (QString serv : m_servers) {
|
||||
emit logLine(m_server_to_address.value(serv), MessageLevel::Launcher);
|
||||
}
|
||||
emitSucceeded();
|
||||
}
|
||||
}
|
||||
|
||||
bool PrintServers::canAbort() const
|
||||
{
|
||||
return true;
|
||||
}
|
37
launcher/launch/steps/PrintServers.h
Normal file
37
launcher/launch/steps/PrintServers.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2024 Leia uwu <leia@tutamail.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 <LoggedProcess.h>
|
||||
#include <java/JavaChecker.h>
|
||||
#include <launch/LaunchStep.h>
|
||||
#include <QHostInfo>
|
||||
|
||||
class PrintServers : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
PrintServers(LaunchTask* parent, const QStringList& servers);
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool canAbort() const;
|
||||
|
||||
private:
|
||||
void resolveServer(const QHostInfo& host_info);
|
||||
QMap<QString, QString> m_server_to_address;
|
||||
QStringList m_servers;
|
||||
};
|
|
@ -24,7 +24,7 @@ class QuitAfterGameStop : public LaunchStep {
|
|||
Q_OBJECT
|
||||
public:
|
||||
explicit QuitAfterGameStop(LaunchTask* parent) : LaunchStep(parent) {};
|
||||
virtual ~QuitAfterGameStop() {};
|
||||
virtual ~QuitAfterGameStop() = default;
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool canAbort() const { return false; }
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue