Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into disablemods

This commit is contained in:
Trial97 2023-11-23 13:38:11 +02:00
commit 80383d237a
No known key found for this signature in database
GPG Key ID: 55EF5DA53DB36318
221 changed files with 12812 additions and 1331 deletions

5
.clang-tidy Normal file
View File

@ -0,0 +1,5 @@
Checks:
- modernize-use-using
- readability-avoid-const-params-in-decls
SystemHeaders: false

View File

@ -24,7 +24,7 @@ jobs:
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
- name: Create backport PRs - name: Create backport PRs
uses: korthout/backport-action@v1.4.0 uses: korthout/backport-action@v2.1.1
with: with:
# Config README: https://github.com/korthout/backport-action#backport-action # Config README: https://github.com/korthout/backport-action#backport-action
pull_description: |- pull_description: |-

View File

@ -37,44 +37,43 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- os: ubuntu-20.04 - os: ubuntu-20.04
qt_ver: 5 qt_ver: 5
- os: ubuntu-20.04 - os: ubuntu-20.04
qt_ver: 6 qt_ver: 6
qt_host: linux qt_host: linux
qt_arch: '' qt_arch: ""
qt_version: '6.2.4' qt_version: "6.2.4"
qt_modules: 'qt5compat qtimageformats' qt_modules: "qt5compat qtimageformats"
qt_tools: '' qt_tools: ""
- os: windows-2022 - os: windows-2022
name: "Windows-MinGW-w64" name: "Windows-MinGW-w64"
msystem: clang64 msystem: clang64
vcvars_arch: 'amd64_x86' vcvars_arch: "amd64_x86"
- os: windows-2022 - os: windows-2022
name: "Windows-MSVC" name: "Windows-MSVC"
msystem: '' msystem: ""
architecture: 'x64' architecture: "x64"
vcvars_arch: 'amd64' vcvars_arch: "amd64"
qt_ver: 6 qt_ver: 6
qt_host: windows qt_host: windows
qt_arch: '' qt_arch: ''
qt_version: '6.5.2' qt_version: '6.6.0'
qt_modules: 'qt5compat qtimageformats' qt_modules: 'qt5compat qtimageformats'
qt_tools: '' qt_tools: ''
- os: windows-2022 - os: windows-2022
name: "Windows-MSVC-arm64" name: "Windows-MSVC-arm64"
msystem: '' msystem: ""
architecture: 'arm64' architecture: "arm64"
vcvars_arch: 'amd64_arm64' vcvars_arch: "amd64_arm64"
qt_ver: 6 qt_ver: 6
qt_host: windows qt_host: windows
qt_arch: 'win64_msvc2019_arm64' qt_arch: 'win64_msvc2019_arm64'
qt_version: '6.5.2' qt_version: '6.6.0'
qt_modules: 'qt5compat qtimageformats' qt_modules: 'qt5compat qtimageformats'
qt_tools: '' qt_tools: ''
@ -84,7 +83,7 @@ jobs:
qt_ver: 6 qt_ver: 6
qt_host: mac qt_host: mac
qt_arch: '' qt_arch: ''
qt_version: '6.5.2' qt_version: '6.6.0'
qt_modules: 'qt5compat qtimageformats' qt_modules: 'qt5compat qtimageformats'
qt_tools: '' qt_tools: ''
@ -93,9 +92,9 @@ jobs:
macosx_deployment_target: 10.13 macosx_deployment_target: 10.13
qt_ver: 5 qt_ver: 5
qt_host: mac qt_host: mac
qt_version: '5.15.2' qt_version: "5.15.2"
qt_modules: '' qt_modules: ""
qt_tools: '' qt_tools: ""
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@ -115,9 +114,9 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
submodules: 'true' submodules: "true"
- name: 'Setup MSYS2' - name: "Setup MSYS2"
if: runner.os == 'Windows' && matrix.msystem != '' if: runner.os == 'Windows' && matrix.msystem != ''
uses: msys2/setup-msys2@v2 uses: msys2/setup-msys2@v2
with: with:
@ -157,7 +156,7 @@ jobs:
path: '${{ github.workspace }}\.ccache' path: '${{ github.workspace }}\.ccache'
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }} key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
restore-keys: | restore-keys: |
${{ matrix.os }}-mingw-w64-ccache ${{ matrix.os }}-mingw-w64-ccache
- name: Setup ccache (Windows MinGW-w64) - name: Setup ccache (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug' if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
@ -202,35 +201,35 @@ jobs:
if: runner.os == 'Windows' && matrix.architecture == 'arm64' if: runner.os == 'Windows' && matrix.architecture == 'arm64'
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v3
with: with:
aqtversion: '==3.1.*' aqtversion: "==3.1.*"
py7zrversion: '>=0.20.2' py7zrversion: ">=0.20.2"
version: ${{ matrix.qt_version }} version: ${{ matrix.qt_version }}
host: 'windows' host: "windows"
target: 'desktop' target: "desktop"
arch: '' arch: ""
modules: ${{ matrix.qt_modules }} modules: ${{ matrix.qt_modules }}
tools: ${{ matrix.qt_tools }} tools: ${{ matrix.qt_tools }}
cache: ${{ inputs.is_qt_cached }} cache: ${{ inputs.is_qt_cached }}
cache-key-prefix: host-qt-arm64-windows cache-key-prefix: host-qt-arm64-windows
dir: ${{ github.workspace }}\HostQt dir: ${{ github.workspace }}\HostQt
set-env: false set-env: false
- name: Install Qt (macOS, Linux, Qt 6 & Windows MSVC) - 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 == '') if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS' || (runner.os == 'Windows' && matrix.msystem == '')
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v3
with: with:
aqtversion: '==3.1.*' aqtversion: "==3.1.*"
py7zrversion: '>=0.20.2' py7zrversion: ">=0.20.2"
version: ${{ matrix.qt_version }} version: ${{ matrix.qt_version }}
host: ${{ matrix.qt_host }} host: ${{ matrix.qt_host }}
target: 'desktop' target: "desktop"
arch: ${{ matrix.qt_arch }} arch: ${{ matrix.qt_arch }}
modules: ${{ matrix.qt_modules }} modules: ${{ matrix.qt_modules }}
tools: ${{ matrix.qt_tools }} tools: ${{ matrix.qt_tools }}
cache: ${{ inputs.is_qt_cached }} cache: ${{ inputs.is_qt_cached }}
- name: Install MSVC (Windows MSVC) - name: Install MSVC (Windows MSVC)
if: runner.os == 'Windows' # We want this for MinGW builds as well, as we need SignTool if: runner.os == 'Windows' # We want this for MinGW builds as well, as we need SignTool
uses: ilammy/msvc-dev-cmd@v1 uses: ilammy/msvc-dev-cmd@v1
with: with:
vsversion: 2022 vsversion: 2022
@ -271,12 +270,12 @@ jobs:
if: runner.os == 'Windows' && matrix.msystem != '' if: runner.os == 'Windows' && matrix.msystem != ''
shell: msys2 {0} shell: msys2 {0}
run: | 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 -G Ninja 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
- name: Configure CMake (Windows MSVC) - name: Configure CMake (Windows MSVC)
if: runner.os == 'Windows' && matrix.msystem == '' if: runner.os == 'Windows' && matrix.msystem == ''
run: | 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 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 }}
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix) # https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
if ("${{ env.CCACHE_VAR }}") if ("${{ env.CCACHE_VAR }}")
{ {
@ -291,7 +290,7 @@ jobs:
- name: Configure CMake (Linux) - name: Configure CMake (Linux)
if: runner.os == 'Linux' if: runner.os == 'Linux'
run: | 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 }} -G Ninja 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
## ##
# BUILD # BUILD
@ -331,7 +330,7 @@ jobs:
- name: Test (Windows MSVC) - name: Test (Windows MSVC)
if: runner.os == 'Windows' && matrix.msystem == '' && matrix.architecture != 'arm64' if: runner.os == 'Windows' && matrix.msystem == '' && matrix.architecture != 'arm64'
run: | run: |
ctest -E "^example64|example$" --test-dir build --output-on-failure -C ${{ inputs.build_type }} ctest -E "^example64|example$" --test-dir build --output-on-failure -C ${{ inputs.build_type }}
## ##
# PACKAGE BUILDS # PACKAGE BUILDS
@ -373,7 +372,7 @@ jobs:
run: | run: |
cmake --install ${{ env.BUILD_DIR }} cmake --install ${{ env.BUILD_DIR }}
touch ${{ env.INSTALL_DIR }}/manifest.txt touch ${{ env.INSTALL_DIR }}/manifest.txt
for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_DIR }}/manifest.txt for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_DIR }}/manifest.txt
- name: Package (Windows MSVC) - name: Package (Windows MSVC)
if: runner.os == 'Windows' && matrix.msystem == '' if: runner.os == 'Windows' && matrix.msystem == ''
@ -390,10 +389,9 @@ jobs:
Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
- name: Fetch codesign certificate (Windows) - name: Fetch codesign certificate (Windows)
if: runner.os == 'Windows' if: runner.os == 'Windows'
shell: bash # yes, we are not using MSYS2 or PowerShell here shell: bash # yes, we are not using MSYS2 or PowerShell here
run: | run: |
echo '${{ secrets.WINDOWS_CODESIGN_CERT }}' | base64 --decode > codesign.pfx echo '${{ secrets.WINDOWS_CODESIGN_CERT }}' | base64 --decode > codesign.pfx
@ -403,7 +401,7 @@ jobs:
if (Get-Content ./codesign.pfx){ if (Get-Content ./codesign.pfx){
cd ${{ env.INSTALL_DIR }} cd ${{ env.INSTALL_DIR }}
# We ship the exact same executable for portable and non-portable editions, so signing just once is fine # 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 prismlauncher.exe prismlauncher_filelink.exe SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_updater.exe prismlauncher_filelink.exe
} else { } else {
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY ":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
} }
@ -495,15 +493,7 @@ jobs:
export LD_LIBRARY_PATH export LD_LIBRARY_PATH
chmod +x AppImageUpdate-x86_64.AppImage chmod +x AppImageUpdate-x86_64.AppImage
./AppImageUpdate-x86_64.AppImage --appimage-extract cp AppImageUpdate-x86_64.AppImage ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/optional
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins
cp -r squashfs-root/usr/bin/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin
cp -r squashfs-root/usr/lib/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib
cp -r squashfs-root/usr/optional/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/optional
cp -r squashfs-root/usr/optional/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins
export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-x86_64.AppImage.zsync" export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-x86_64.AppImage.zsync"
@ -511,7 +501,7 @@ jobs:
export SIGN=1 export SIGN=1
export SIGN_KEY=${{ secrets.GPG_PRIVATE_KEY_ID }} export SIGN_KEY=${{ secrets.GPG_PRIVATE_KEY_ID }}
mkdir -p ~/.gnupg/ mkdir -p ~/.gnupg/
printf "$GPG_PRIVATE_KEY" | base64 --decode > ~/.gnupg/private.key echo "$GPG_PRIVATE_KEY" > ~/.gnupg/private.key
gpg --import ~/.gnupg/private.key gpg --import ~/.gnupg/private.key
else else
echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY
@ -557,14 +547,14 @@ jobs:
if: runner.os == 'Linux' && matrix.qt_ver != 6 if: runner.os == 'Linux' && matrix.qt_ver != 6
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }} name: PrismLauncher-${{ runner.os }}-Qt5-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher.tar.gz path: PrismLauncher.tar.gz
- name: Upload binary tarball (Linux, portable, Qt 5) - name: Upload binary tarball (Linux, portable, Qt 5)
if: runner.os == 'Linux' && matrix.qt_ver != 6 if: runner.os == 'Linux' && matrix.qt_ver != 6
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: PrismLauncher-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }} name: PrismLauncher-${{ runner.os }}-Qt5-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher-portable.tar.gz path: PrismLauncher-portable.tar.gz
- name: Upload binary tarball (Linux, Qt 6) - name: Upload binary tarball (Linux, Qt 6)
@ -587,7 +577,7 @@ jobs:
with: with:
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
- name: Upload AppImage Zsync (Linux) - name: Upload AppImage Zsync (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 5 if: runner.os == 'Linux' && matrix.qt_ver != 5
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
@ -604,17 +594,17 @@ jobs:
flatpak: flatpak:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: bilelmoussaoui/flatpak-github-actions:kde-5.15-22.08 image: bilelmoussaoui/flatpak-github-actions:kde-5.15-23.08
options: --privileged options: --privileged
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
if: inputs.build_type == 'Debug' if: inputs.build_type == 'Debug'
with: with:
submodules: 'true' submodules: "true"
- name: Build Flatpak (Linux) - name: Build Flatpak (Linux)
if: inputs.build_type == 'Debug' if: inputs.build_type == 'Debug'
uses: flatpak/flatpak-github-actions/flatpak-builder@v6 uses: flatpak/flatpak-github-actions/flatpak-builder@v6
with: with:
bundle: "Prism Launcher.flatpak" bundle: "Prism Launcher.flatpak"
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml

View File

@ -3,26 +3,25 @@ name: Build Application
on: on:
push: push:
branches-ignore: branches-ignore:
- 'renovate/**' - "renovate/**"
paths-ignore: paths-ignore:
- '**.md' - "**.md"
- '**/LICENSE' - "**/LICENSE"
- 'flake.lock' - "flake.lock"
- 'packages/**' - "packages/**"
- '.github/ISSUE_TEMPLATE/**' - ".github/ISSUE_TEMPLATE/**"
- '.markdownlint**' - ".markdownlint**"
pull_request: pull_request:
paths-ignore: paths-ignore:
- '**.md' - "**.md"
- '**/LICENSE' - "**/LICENSE"
- 'flake.lock' - "flake.lock"
- 'packages/**' - "packages/**"
- '.github/ISSUE_TEMPLATE/**' - ".github/ISSUE_TEMPLATE/**"
- '.markdownlint**' - ".markdownlint**"
workflow_dispatch: workflow_dispatch:
jobs: jobs:
build_debug: build_debug:
name: Build Debug name: Build Debug
uses: ./.github/workflows/build.yml uses: ./.github/workflows/build.yml
@ -34,3 +33,5 @@ jobs:
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }} WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }} WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}

View File

@ -17,6 +17,8 @@ jobs:
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }} WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }} WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
create_release: create_release:
needs: build_release needs: build_release
@ -39,9 +41,9 @@ jobs:
run: | run: |
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }} mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux-Qt5-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux-Qt5*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz
mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage
mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
@ -85,8 +87,8 @@ jobs:
draft: true draft: true
prerelease: false prerelease: false
files: | files: |
PrismLauncher-Linux-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz
PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
PrismLauncher-Linux-x86_64.AppImage PrismLauncher-Linux-x86_64.AppImage
PrismLauncher-Linux-x86_64.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz

View File

@ -188,8 +188,11 @@ set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_M
# Build platform. # Build platform.
set(Launcher_BUILD_PLATFORM "unknown" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.") set(Launcher_BUILD_PLATFORM "unknown" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.")
# Channel list URL # Github repo URL with releases for updater
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.") set(Launcher_UPDATER_GITHUB_REPO "https://github.com/PrismLauncher/PrismLauncher" CACHE STRING "Base github URL for the updater.")
# Name to help updater identify valid artifacts
set(Launcher_BUILD_ARTIFACT "" CACHE STRING "Artifact name to help the updater identify valid artifacts.")
# The metadata server # The metadata server
set(Launcher_META_URL "https://meta.prismlauncher.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.") set(Launcher_META_URL "https://meta.prismlauncher.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
@ -245,6 +248,11 @@ set(Launcher_MSA_CLIENT_ID "c36a9fb6-4f2a-41ff-90bd-ae7cc92031eb" CACHE STRING "
# This key was issued specifically for Prism Launcher # This key was issued specifically for Prism Launcher
set(Launcher_CURSEFORGE_API_KEY "$2a$10$wuAJuNZuted3NORVmpgUC.m8sI.pv1tOPKZyBgLFGjxFp/br0lZCC" CACHE STRING "API key for the CurseForge platform") set(Launcher_CURSEFORGE_API_KEY "$2a$10$wuAJuNZuted3NORVmpgUC.m8sI.pv1tOPKZyBgLFGjxFp/br0lZCC" CACHE STRING "API key for the CurseForge platform")
set(Launcher_COMPILER_NAME ${CMAKE_CXX_COMPILER_ID})
set(Launcher_COMPILER_VERSION ${CMAKE_CXX_COMPILER_VERSION})
set(Launcher_COMPILER_TARGET_SYSTEM ${CMAKE_SYSTEM_NAME})
set(Launcher_COMPILER_TARGET_SYSTEM_VERSION ${CMAKE_SYSTEM_VERSION})
set(Launcher_COMPILER_TARGET_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR})
#### Check the current Git commit and branch #### Check the current Git commit and branch
include(GetGitRevisionDescription) include(GetGitRevisionDescription)
@ -339,6 +347,11 @@ add_subdirectory(program_info)
####################################### Install layout ####################################### ####################################### Install layout #######################################
set(Launcher_ENABLE_UPDATER NO) set(Launcher_ENABLE_UPDATER NO)
set(Launcher_BUILD_UPDATER NO)
if (NOT APPLE AND (NOT Launcher_UPDATER_GITHUB_REPO STREQUAL "" AND NOT Launcher_BUILD_ARTIFACT STREQUAL ""))
set(Launcher_BUILD_UPDATER YES)
endif()
if(NOT (UNIX AND APPLE)) if(NOT (UNIX AND APPLE))
# Install "portable.txt" if selected component is "portable" # Install "portable.txt" if selected component is "portable"

View File

@ -18,11 +18,18 @@
</a> </a>
- All downloads and instructions for Prism Launcher can be found on our [Website](https://prismlauncher.org/download). - All downloads and instructions for Prism Launcher can be found on our [Website](https://prismlauncher.org/download).
- Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions). - Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) tab (this also includes the pull requests status).
### Development Builds ### Development Builds
There are development builds available [here](https://github.com/PrismLauncher/PrismLauncher/actions). These have debug information in the binaries, so their file sizes are relatively larger. Please understand that these builds are not intended for most users. There may be bugs, and other instabilities. You have been warned.
There are development builds available through:
- [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) (includes builds from pull requests opened by contribuitors)
- [nightly.link](https://nightly.link/PrismLauncher/PrismLauncher/workflows/trigger_builds/develop) (this will always point only to the latest version of develop)
These have debug information in the binaries, so their file sizes are relatively larger.
Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**. Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**.
@ -30,7 +37,7 @@ For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**,
[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?label=MPR&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git)<br />[![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?label=COPR&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?label=Gentoo&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?label=MPR&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git)<br />[![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?label=COPR&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?label=Gentoo&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher)
These packages are also availiable to all the distributions based on the ones mentioned above. These packages are also available to all the distributions based on the ones mentioned above.
## Community & Support ## Community & Support

View File

@ -33,6 +33,7 @@
* limitations under the License. * limitations under the License.
*/ */
#include <qstringliteral.h>
#include "BuildConfig.h" #include "BuildConfig.h"
#include <QObject> #include <QObject>
@ -59,8 +60,16 @@ Config::Config()
VERSION_MINOR = @Launcher_VERSION_MINOR@; VERSION_MINOR = @Launcher_VERSION_MINOR@;
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@"; BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
BUILD_ARTIFACT = "@Launcher_BUILD_ARTIFACT@";
BUILD_DATE = "@Launcher_BUILD_TIMESTAMP@"; BUILD_DATE = "@Launcher_BUILD_TIMESTAMP@";
UPDATER_BASE = "@Launcher_UPDATER_BASE@"; UPDATER_GITHUB_REPO = "@Launcher_UPDATER_GITHUB_REPO@";
COMPILER_NAME = "@Launcher_COMPILER_NAME@";
COMPILER_VERSION = "@Launcher_COMPILER_VERSION@";
COMPILER_TARGET_SYSTEM = "@Launcher_COMPILER_TARGET_SYSTEM@";
COMPILER_TARGET_SYSTEM_VERSION = "@Launcher_COMPILER_TARGET_SYSTEM_VERSION@";
COMPILER_TARGET_SYSTEM_PROCESSOR = "@Launcher_COMPILER_TARGET_PROCESSOR@";
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@"; MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@"; MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
@ -68,6 +77,8 @@ Config::Config()
if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty()) if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
{ {
UPDATER_ENABLED = true; UPDATER_ENABLED = true;
} else if(!UPDATER_GITHUB_REPO.isEmpty() && !BUILD_ARTIFACT.isEmpty()) {
UPDATER_ENABLED = true;
} }
GIT_COMMIT = "@Launcher_GIT_COMMIT@"; GIT_COMMIT = "@Launcher_GIT_COMMIT@";
@ -88,10 +99,7 @@ Config::Config()
if (GIT_REFSPEC.startsWith("refs/heads/")) if (GIT_REFSPEC.startsWith("refs/heads/"))
{ {
VERSION_CHANNEL = GIT_REFSPEC; VERSION_CHANNEL = GIT_REFSPEC;
VERSION_CHANNEL.remove("refs/heads/"); VERSION_CHANNEL.remove("refs/heads/");
if(!UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty()) {
UPDATER_ENABLED = true;
}
} }
else if (!GIT_COMMIT.isEmpty()) else if (!GIT_COMMIT.isEmpty())
{ {
@ -136,3 +144,16 @@ QString Config::printableVersionString() const
} }
return vstr; return vstr;
} }
QString Config::compilerID() const
{
if (COMPILER_VERSION.isEmpty())
return COMPILER_NAME;
return QStringLiteral("%1 - %2").arg(COMPILER_NAME).arg(COMPILER_VERSION);
}
QString Config::systemID() const
{
return QStringLiteral("%1 %2 %3").arg(COMPILER_TARGET_SYSTEM, COMPILER_TARGET_SYSTEM_VERSION, COMPILER_TARGET_SYSTEM_PROCESSOR);
}

View File

@ -71,11 +71,29 @@ class Config {
/// A short string identifying this build's platform or distribution. /// A short string identifying this build's platform or distribution.
QString BUILD_PLATFORM; QString BUILD_PLATFORM;
/// A short string identifying this build's valid artifacts int he updater. For example, "lin64" or "win32".
QString BUILD_ARTIFACT;
/// A string containing the build timestamp /// A string containing the build timestamp
QString BUILD_DATE; QString BUILD_DATE;
/// A string identifying the compiler use to build
QString COMPILER_NAME;
/// A string identifying the compiler version used to build
QString COMPILER_VERSION;
/// A string identifying the compiler target system os
QString COMPILER_TARGET_SYSTEM;
/// A String identifying the compiler target system version
QString COMPILER_TARGET_SYSTEM_VERSION;
/// A String identifying the compiler target processor
QString COMPILER_TARGET_SYSTEM_PROCESSOR;
/// URL for the updater's channel /// URL for the updater's channel
QString UPDATER_BASE; QString UPDATER_GITHUB_REPO;
/// The public key used to sign releases for the Sparkle updater appcast /// The public key used to sign releases for the Sparkle updater appcast
QString MAC_SPARKLE_PUB_KEY; QString MAC_SPARKLE_PUB_KEY;
@ -175,6 +193,18 @@ class Config {
* \return The version number in string format (major.minor.revision.build). * \return The version number in string format (major.minor.revision.build).
*/ */
QString printableVersionString() const; QString printableVersionString() const;
/**
* \brief Compiler ID String
* \return a string of the form "Name - Version" of just "Name" if the version is empty
*/
QString compilerID() const;
/**
* \brief System ID String
* \return a string of the form "OS Verison Processor"
*/
QString systemID() const;
}; };
extern const Config BuildConfig; extern const Config BuildConfig;

View File

@ -68,6 +68,8 @@ function(
/w14906 # string literal cast to 'LPWSTR' /w14906 # string literal cast to 'LPWSTR'
/w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied
/permissive- # standards conformance mode for MSVC compiler. /permissive- # standards conformance mode for MSVC compiler.
/we4062 # forbid omitting a possible value of an enum in a switch statement
) )
endif() endif()
@ -93,6 +95,8 @@ function(
# in a lot of noise. This warning is only notifying us that clang is emulating the GCC behaviour # in a lot of noise. This warning is only notifying us that clang is emulating the GCC behaviour
# instead of the exact standard wording so we can safely ignore it # instead of the exact standard wording so we can safely ignore it
-Wno-gnu-zero-variadic-macro-arguments -Wno-gnu-zero-variadic-macro-arguments
-Werror=switch # forbid omitting a possible value of an enum in a switch statement
) )
endif() endif()
@ -104,6 +108,8 @@ function(
-Wduplicated-branches # warn if if / else branches have duplicated code -Wduplicated-branches # warn if if / else branches have duplicated code
-Wlogical-op # warn about logical operations being used where bitwise were probably wanted -Wlogical-op # warn about logical operations being used where bitwise were probably wanted
-Wuseless-cast # warn if you perform a cast to the same type -Wuseless-cast # warn if you perform a cast to the same type
-Werror=switch # forbid omitting a possible value of an enum in a switch statement
) )
endif() endif()
@ -128,6 +134,8 @@ function(
-Woverloaded-virtual -Woverloaded-virtual
-Wuseless-cast -Wuseless-cast
-Wextra-semi -Wextra-semi
-Werror=switch # forbid omitting a possible value of an enum in a switch statement
) )
target_compile_options( target_compile_options(

30
flake.lock generated
View File

@ -21,11 +21,11 @@
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": "nixpkgs-lib"
}, },
"locked": { "locked": {
"lastModified": 1696343447, "lastModified": 1698882062,
"narHash": "sha256-B2xAZKLkkeRFG5XcHHSXXcP7To9Xzr59KXeZiRf4vdQ=", "narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "c9afaba3dfa4085dbd2ccb38dfade5141e33d9d4", "rev": "8c9fa2545007b49a5db5f650ae91f227672c3877",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -76,11 +76,11 @@
"libnbtplusplus": { "libnbtplusplus": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1690036783, "lastModified": 1699286814,
"narHash": "sha256-A5kTgICnx+Qdq3Fir/bKTfdTt/T1NQP2SC+nhN1ENug=", "narHash": "sha256-yy0q+bky80LtK1GWzz7qpM+aAGrOqLuewbid8WT1ilk=",
"owner": "PrismLauncher", "owner": "PrismLauncher",
"repo": "libnbtplusplus", "repo": "libnbtplusplus",
"rev": "a5e8fd52b8bf4ab5d5bcc042b2a247867589985f", "rev": "23b955121b8217c1c348a9ed2483167a6f3ff4ad",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -106,11 +106,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1697009197, "lastModified": 1700108881,
"narHash": "sha256-viVRhBTFT8fPJTb1N3brQIpFZnttmwo3JVKNuWRVc3s=", "narHash": "sha256-+Lqybl8kj0+nD/IlAWPPG/RDTa47gff9nbei0u7BntE=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "01441e14af5e29c9d27ace398e6dd0b293e25a54", "rev": "7414e9ee0b3e9903c24d3379f577a417f0aae5f1",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -123,11 +123,11 @@
"nixpkgs-lib": { "nixpkgs-lib": {
"locked": { "locked": {
"dir": "lib", "dir": "lib",
"lastModified": 1696019113, "lastModified": 1698611440,
"narHash": "sha256-X3+DKYWJm93DRSdC5M6K5hLqzSya9BjibtBsuARoPco=", "narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "f5892ddac112a1e9b3612c39af1b72987ee5783a", "rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -153,11 +153,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1696846637, "lastModified": 1700064067,
"narHash": "sha256-0hv4kbXxci2+pxhuXlVgftj/Jq79VSmtAyvfabCCtYk=", "narHash": "sha256-1ZWNDzhu8UlVCK7+DUN9dVQfiHX1bv6OQP9VxstY/gs=",
"owner": "cachix", "owner": "cachix",
"repo": "pre-commit-hooks.nix", "repo": "pre-commit-hooks.nix",
"rev": "42e1b6095ef80a51f79595d9951eb38e91c4e6ca", "rev": "e558068cba67b23b4fbc5537173dbb43748a17e8",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -1,6 +1,6 @@
id: org.prismlauncher.PrismLauncher id: org.prismlauncher.PrismLauncher
runtime: org.kde.Platform runtime: org.kde.Platform
runtime-version: "5.15-22.08" runtime-version: "5.15-23.08"
sdk: org.kde.Sdk sdk: org.kde.Sdk
sdk-extensions: sdk-extensions:
- org.freedesktop.Sdk.Extension.openjdk17 - org.freedesktop.Sdk.Extension.openjdk17
@ -113,6 +113,9 @@ modules:
version-query: .tag_name version-query: .tag_name
url-query: .tarball_url url-query: .tarball_url
timestamp-query: .published_at timestamp-query: .published_at
# from https://github.com/flathub/net.gaijin.WarThunder/blob/7ea6f7a9f84b9c77150c003a7059dc03f8dcbc7f/gamemode.patch
- type: patch
path: patches/gamemode.patch
cleanup: cleanup:
- /include - /include
- /lib/pkgconfig - /lib/pkgconfig

View File

@ -0,0 +1,12 @@
diff -ruN a/common/common-pidfds.c b/common/common-pidfds.c
--- a/common/common-pidfds.c 2021-02-18 20:00:12.000000000 +0100
+++ b/common/common-pidfds.c 2023-09-07 08:57:42.954362763 +0200
@@ -58,6 +58,8 @@
{
return (int)syscall(__NR_pidfd_open, pid, flags);
}
+#else
+#include <sys/pidfd.h>
#endif
/* pidfd functions */

View File

@ -1,5 +1,6 @@
builds: builds:
exclude: [] exclude:
- "*.x86_64-darwin.*"
include: include:
- "checks.x86_64-linux.*" - "checks.x86_64-linux.*"
- "devShells.*.*" - "devShells.*.*"

View File

@ -58,6 +58,7 @@
#include "ui/pages/global/APIPage.h" #include "ui/pages/global/APIPage.h"
#include "ui/pages/global/AccountListPage.h" #include "ui/pages/global/AccountListPage.h"
#include "ui/pages/global/CustomCommandsPage.h" #include "ui/pages/global/CustomCommandsPage.h"
#include "ui/pages/global/EnvironmentVariablesPage.h"
#include "ui/pages/global/ExternalToolsPage.h" #include "ui/pages/global/ExternalToolsPage.h"
#include "ui/pages/global/JavaPage.h" #include "ui/pages/global/JavaPage.h"
#include "ui/pages/global/LanguagePage.h" #include "ui/pages/global/LanguagePage.h"
@ -122,6 +123,7 @@
#include <FileSystem.h> #include <FileSystem.h>
#include <LocalPeer.h> #include <LocalPeer.h>
#include <stdlib.h>
#include <sys.h> #include <sys.h>
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
@ -130,9 +132,13 @@
#include "gamemode_client.h" #include "gamemode_client.h"
#endif #endif
#if defined(Q_OS_MAC) && defined(SPARKLE_ENABLED) #if defined(Q_OS_MAC)
#if defined(SPARKLE_ENABLED)
#include "updater/MacSparkleUpdater.h" #include "updater/MacSparkleUpdater.h"
#endif #endif
#else
#include "updater/PrismExternalUpdater.h"
#endif
#if defined Q_OS_WIN32 #if defined Q_OS_WIN32
#include "WindowsConsole.h" #include "WindowsConsole.h"
@ -164,6 +170,34 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt
} // namespace } // namespace
std::tuple<QDateTime, QString, QString, QString, QString> read_lock_File(const QString& path)
{
auto contents = QString(FS::read(path));
auto lines = contents.split('\n');
QDateTime timestamp;
QString from, to, target, data_path;
for (auto line : lines) {
auto index = line.indexOf("=");
if (index < 0)
continue;
auto left = line.left(index);
auto right = line.mid(index + 1);
if (left.toLower() == "timestamp") {
timestamp = QDateTime::fromString(right, Qt::ISODate);
} else if (left.toLower() == "from") {
from = right;
} else if (left.toLower() == "to") {
to = right;
} else if (left.toLower() == "target") {
target = right;
} else if (left.toLower() == "data_path") {
data_path = right;
}
}
return std::make_tuple(timestamp, from, to, target, data_path);
}
Application::Application(int& argc, char** argv) : QApplication(argc, argv) Application::Application(int& argc, char** argv) : QApplication(argc, argv)
{ {
#if defined Q_OS_WIN32 #if defined Q_OS_WIN32
@ -296,6 +330,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
.arg(dataPath)); .arg(dataPath));
return; return;
} }
m_dataPath = dataPath;
/* /*
* Establish the mechanism for communication with an already running PrismLauncher that uses the same data path. * Establish the mechanism for communication with an already running PrismLauncher that uses the same data path.
@ -450,11 +485,16 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
} }
{ {
qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT; qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << ", (c) 2022-2023 "
<< qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", "));
qDebug() << "Version : " << BuildConfig.printableVersionString(); qDebug() << "Version : " << BuildConfig.printableVersionString();
qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM; qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM;
qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC;
qDebug() << "Compiled for : " << BuildConfig.systemID();
qDebug() << "Compiled by : " << BuildConfig.compilerID();
qDebug() << "Build Artifact : " << BuildConfig.BUILD_ARTIFACT;
qDebug() << "Updates Enabled : " << (updaterEnabled() ? "Yes" : "No");
if (adjustedBy.size()) { if (adjustedBy.size()) {
qDebug() << "Work dir before adjustment : " << origcwdPath; qDebug() << "Work dir before adjustment : " << origcwdPath;
qDebug() << "Work dir after adjustment : " << QDir::currentPath(); qDebug() << "Work dir after adjustment : " << QDir::currentPath();
@ -582,6 +622,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("IgnoreJavaCompatibility", false); m_settings->registerSetting("IgnoreJavaCompatibility", false);
m_settings->registerSetting("IgnoreJavaWizard", false); m_settings->registerSetting("IgnoreJavaWizard", false);
// Legacy settings
m_settings->registerSetting("OnlineFixes", false);
// Native library workarounds // Native library workarounds
m_settings->registerSetting("UseNativeOpenAL", false); m_settings->registerSetting("UseNativeOpenAL", false);
m_settings->registerSetting("CustomOpenALPath", ""); m_settings->registerSetting("CustomOpenALPath", "");
@ -601,6 +644,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// Minecraft mods // Minecraft mods
m_settings->registerSetting("ModMetadataDisabled", false); m_settings->registerSetting("ModMetadataDisabled", false);
m_settings->registerSetting("ModDependenciesDisabled", false);
// Minecraft offline player name // Minecraft offline player name
m_settings->registerSetting("LastOfflinePlayerName", ""); m_settings->registerSetting("LastOfflinePlayerName", "");
@ -677,6 +721,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("CloseAfterLaunch", false); m_settings->registerSetting("CloseAfterLaunch", false);
m_settings->registerSetting("QuitAfterGameStop", false); m_settings->registerSetting("QuitAfterGameStop", false);
m_settings->registerSetting("Env", QVariant(QMap<QString, QVariant>()));
// Custom Microsoft Authentication Client ID // Custom Microsoft Authentication Client ID
m_settings->registerSetting("MSAClientIDOverride", ""); m_settings->registerSetting("MSAClientIDOverride", "");
@ -702,6 +748,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_globalSettingsProvider->addPage<JavaPage>(); m_globalSettingsProvider->addPage<JavaPage>();
m_globalSettingsProvider->addPage<LanguagePage>(); m_globalSettingsProvider->addPage<LanguagePage>();
m_globalSettingsProvider->addPage<CustomCommandsPage>(); m_globalSettingsProvider->addPage<CustomCommandsPage>();
m_globalSettingsProvider->addPage<EnvironmentVariablesPage>();
m_globalSettingsProvider->addPage<ProxyPage>(); m_globalSettingsProvider->addPage<ProxyPage>();
m_globalSettingsProvider->addPage<ExternalToolsPage>(); m_globalSettingsProvider->addPage<ExternalToolsPage>();
m_globalSettingsProvider->addPage<AccountListPage>(); m_globalSettingsProvider->addPage<AccountListPage>();
@ -738,15 +785,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
qDebug() << "<> Translations loaded."; qDebug() << "<> Translations loaded.";
} }
// initialize the updater
if (BuildConfig.UPDATER_ENABLED) {
qDebug() << "Initializing updater";
#if defined(Q_OS_MAC) && defined(SPARKLE_ENABLED)
m_updater.reset(new MacSparkleUpdater());
#endif
qDebug() << "<> Updater started.";
}
// Instance icons // Instance icons
{ {
auto setting = APPLICATION->settings()->getSetting("IconsDir"); auto setting = APPLICATION->settings()->getSetting("IconsDir");
@ -849,6 +887,107 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
detectLibraries(); detectLibraries();
// check update locks
{
auto update_log_path = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log");
auto update_lock = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.lock"));
if (update_lock.exists()) {
auto [timestamp, from, to, target, data_path] = read_lock_File(update_lock.absoluteFilePath());
auto infoMsg = tr("This installation has a update lock file present at: %1\n"
"\n"
"Timestamp: %2\n"
"Updating from version %3 to %4\n"
"Target install path: %5\n"
"Data Path: %6"
"\n"
"This likely means that a update attempt failed. Please ensure your installation is in working order before "
"proceeding.\n"
"Check the Prism Launcher updater log at: \n"
"%7\n"
"for details on the last update attempt.\n"
"\n"
"To delete this lock and proceed select \"Ignore\" below.")
.arg(update_lock.absoluteFilePath())
.arg(timestamp.toString(Qt::ISODate), from, to, target, data_path)
.arg(update_log_path);
auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update In Progress"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort);
msgBox.setDefaultButton(QMessageBox::Abort);
msgBox.setModal(true);
msgBox.setDetailedText(FS::read(update_log_path));
msgBox.setMinimumWidth(460);
msgBox.adjustSize();
auto res = msgBox.exec();
switch (res) {
case QMessageBox::Ignore: {
FS::deletePath(update_lock.absoluteFilePath());
break;
}
case QMessageBox::Abort:
[[fallthrough]];
default: {
qDebug() << "Exiting because update lockfile is present";
QMetaObject::invokeMethod(
this, []() { exit(1); }, Qt::QueuedConnection);
return;
}
}
}
auto update_fail_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.fail"));
if (update_fail_marker.exists()) {
auto infoMsg = tr("An update attempt failed\n"
"\n"
"Please ensure your installation is in working order before "
"proceeding.\n"
"Check the Prism Launcher updater log at: \n"
"%1\n"
"for details on the last update attempt.")
.arg(update_log_path);
auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Failed"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort);
msgBox.setDefaultButton(QMessageBox::Abort);
msgBox.setModal(true);
msgBox.setDetailedText(FS::read(update_log_path));
msgBox.setMinimumWidth(460);
msgBox.adjustSize();
auto res = msgBox.exec();
switch (res) {
case QMessageBox::Ignore: {
FS::deletePath(update_fail_marker.absoluteFilePath());
break;
}
case QMessageBox::Abort:
[[fallthrough]];
default: {
qDebug() << "Exiting because update lockfile is present";
QMetaObject::invokeMethod(
this, []() { exit(1); }, Qt::QueuedConnection);
return;
}
}
}
auto update_success_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.success"));
if (update_success_marker.exists()) {
auto infoMsg = tr("Update succeeded\n"
"\n"
"You are now running %1 .\n"
"Check the Prism Launcher updater log at: \n"
"%1\n"
"for details.")
.arg(BuildConfig.printableVersionString())
.arg(update_log_path);
auto msgBox = new QMessageBox(QMessageBox::Information, tr("Update Succeeded"), infoMsg, QMessageBox::Ok);
msgBox->setDefaultButton(QMessageBox::Ok);
msgBox->setDetailedText(FS::read(update_log_path));
msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->setMinimumWidth(460);
msgBox->adjustSize();
msgBox->open();
FS::deletePath(update_success_marker.absoluteFilePath());
}
}
if (createSetupWizard()) { if (createSetupWizard()) {
return; return;
} }
@ -917,6 +1056,26 @@ bool Application::createSetupWizard()
return false; return false;
} }
bool Application::updaterEnabled()
{
#if defined(Q_OS_MAC)
return BuildConfig.UPDATER_ENABLED;
#else
return BuildConfig.UPDATER_ENABLED && QFileInfo(FS::PathCombine(m_rootPath, updaterBinaryName())).isFile();
#endif
}
QString Application::updaterBinaryName()
{
auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
#if defined Q_OS_WIN32
exe_name.append(".exe");
#else
exe_name.prepend("bin/");
#endif
return exe_name;
}
bool Application::event(QEvent* event) bool Application::event(QEvent* event)
{ {
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
@ -985,6 +1144,20 @@ void Application::performMainStartupAction()
showMainWindow(false); showMainWindow(false);
qDebug() << "<> Main window shown."; qDebug() << "<> Main window shown.";
} }
// initialize the updater
if (updaterEnabled()) {
qDebug() << "Initializing updater";
#ifdef Q_OS_MAC
#if defined(SPARKLE_ENABLED)
m_updater.reset(new MacSparkleUpdater());
#endif
#else
m_updater.reset(new PrismExternalUpdater(m_mainWindow, m_rootPath, m_dataPath));
#endif
qDebug() << "<> Updater started.";
}
if (!m_urlsToImport.isEmpty()) { if (!m_urlsToImport.isEmpty()) {
qDebug() << "<> Importing from url:" << m_urlsToImport; qDebug() << "<> Importing from url:" << m_urlsToImport;
m_mainWindow->processURLs(m_urlsToImport); m_mainWindow->processURLs(m_urlsToImport);

View File

@ -159,6 +159,9 @@ class Application : public QApplication {
/// this is the root of the 'installation'. Used for automatic updates /// this is the root of the 'installation'. Used for automatic updates
const QString& root() { return m_rootPath; } const QString& root() { return m_rootPath; }
/// the data path the application is using
const QString& dataRoot() { return m_dataPath; }
bool isPortable() { return m_portable; } bool isPortable() { return m_portable; }
const Capabilities capabilities() { return m_capabilities; } const Capabilities capabilities() { return m_capabilities; }
@ -179,6 +182,9 @@ class Application : public QApplication {
int suitableMaxMem(); int suitableMaxMem();
bool updaterEnabled();
QString updaterBinaryName();
QUrl normalizeImportUrl(QString const& url); QUrl normalizeImportUrl(QString const& url);
signals: signals:
@ -244,6 +250,7 @@ class Application : public QApplication {
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers; QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
QString m_rootPath; QString m_rootPath;
QString m_dataPath;
Status m_status = Application::StartingUp; Status m_status = Application::StartingUp;
Capabilities m_capabilities; Capabilities m_capabilities;
bool m_portable = false; bool m_portable = false;

View File

@ -388,7 +388,7 @@ QString BaseInstance::name() const
QString BaseInstance::windowTitle() const QString BaseInstance::windowTitle() const
{ {
return BuildConfig.LAUNCHER_DISPLAYNAME + ": " + name().replace(QRegularExpression("\\s+"), " "); return BuildConfig.LAUNCHER_DISPLAYNAME + ": " + name();
} }
// FIXME: why is this here? move it to MinecraftInstance!!! // FIXME: why is this here? move it to MinecraftInstance!!!

View File

@ -64,7 +64,7 @@ class LaunchTask;
class BaseInstance; class BaseInstance;
// pointer for lazy people // pointer for lazy people
typedef std::shared_ptr<BaseInstance> InstancePtr; using InstancePtr = std::shared_ptr<BaseInstance>;
/*! /*!
* \brief Base class for instances. * \brief Base class for instances.

View File

@ -51,7 +51,7 @@ class BaseVersionList : public QAbstractListModel {
ArchitectureRole, ArchitectureRole,
SortRole SortRole
}; };
typedef QList<int> RoleList; using RoleList = QList<int>;
explicit BaseVersionList(QObject* parent = 0); explicit BaseVersionList(QObject* parent = 0);

View File

@ -139,6 +139,7 @@ set(NET_SOURCES
net/HeaderProxy.h net/HeaderProxy.h
net/RawHeaderProxy.h net/RawHeaderProxy.h
net/ApiHeaderProxy.h net/ApiHeaderProxy.h
net/StaticHeaderProxy.h
net/ApiDownload.h net/ApiDownload.h
net/ApiDownload.cpp net/ApiDownload.cpp
net/ApiUpload.cpp net/ApiUpload.cpp
@ -181,6 +182,11 @@ set(MAC_UPDATE_SOURCES
updater/MacSparkleUpdater.mm updater/MacSparkleUpdater.mm
) )
set(PRISM_UPDATE_SOURCES
updater/PrismExternalUpdater.h
updater/PrismExternalUpdater.cpp
)
# Backend for the news bar... there's usually no news. # Backend for the news bar... there's usually no news.
set(NEWS_SOURCES set(NEWS_SOURCES
# News System # News System
@ -579,6 +585,63 @@ set(LINKEXE_SOURCES
DesktopServices.cpp DesktopServices.cpp
) )
set(PRISMUPDATER_SOURCES
updater/prismupdater/PrismUpdater.h
updater/prismupdater/PrismUpdater.cpp
updater/prismupdater/UpdaterDialogs.h
updater/prismupdater/UpdaterDialogs.cpp
updater/prismupdater/GitHubRelease.h
updater/prismupdater/GitHubRelease.cpp
Json.h
Json.cpp
FileSystem.h
FileSystem.cpp
StringUtils.h
StringUtils.cpp
DesktopServices.h
DesktopServices.cpp
Version.h
Version.cpp
Markdown.h
Markdown.cpp
# Zip
MMCZip.h
MMCZip.cpp
# Time
MMCTime.h
MMCTime.cpp
net/ByteArraySink.h
net/ChecksumValidator.h
net/Download.cpp
net/Download.h
net/FileSink.cpp
net/FileSink.h
net/HttpMetaCache.cpp
net/HttpMetaCache.h
net/Logging.h
net/Logging.cpp
net/NetAction.h
net/NetRequest.cpp
net/NetRequest.h
net/NetJob.cpp
net/NetJob.h
net/NetUtils.h
net/Sink.h
net/Validator.h
net/HeaderProxy.h
net/RawHeaderProxy.h
ui/dialogs/ProgressDialog.cpp
ui/dialogs/ProgressDialog.h
ui/widgets/SubTaskProgressBar.h
ui/widgets/SubTaskProgressBar.cpp
)
######## Logging categories ######## ######## Logging categories ########
ecm_qt_declare_logging_category(CORE_SOURCES ecm_qt_declare_logging_category(CORE_SOURCES
@ -675,6 +738,8 @@ set(LOGIC_SOURCES
if(APPLE AND Launcher_ENABLE_UPDATER) if(APPLE AND Launcher_ENABLE_UPDATER)
set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES}) set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES})
else()
set (LOGIC_SOURCES ${LOGIC_SOURCES} ${PRISM_UPDATE_SOURCES})
endif() endif()
SET(LAUNCHER_SOURCES SET(LAUNCHER_SOURCES
@ -824,6 +889,8 @@ SET(LAUNCHER_SOURCES
ui/pages/global/AccountListPage.h ui/pages/global/AccountListPage.h
ui/pages/global/CustomCommandsPage.cpp ui/pages/global/CustomCommandsPage.cpp
ui/pages/global/CustomCommandsPage.h ui/pages/global/CustomCommandsPage.h
ui/pages/global/EnvironmentVariablesPage.cpp
ui/pages/global/EnvironmentVariablesPage.h
ui/pages/global/ExternalToolsPage.cpp ui/pages/global/ExternalToolsPage.cpp
ui/pages/global/ExternalToolsPage.h ui/pages/global/ExternalToolsPage.h
ui/pages/global/JavaPage.cpp ui/pages/global/JavaPage.cpp
@ -975,6 +1042,8 @@ SET(LAUNCHER_SOURCES
ui/widgets/Common.h ui/widgets/Common.h
ui/widgets/CustomCommands.cpp ui/widgets/CustomCommands.cpp
ui/widgets/CustomCommands.h ui/widgets/CustomCommands.h
ui/widgets/EnvironmentVariables.cpp
ui/widgets/EnvironmentVariables.h
ui/widgets/DropLabel.cpp ui/widgets/DropLabel.cpp
ui/widgets/DropLabel.h ui/widgets/DropLabel.h
ui/widgets/FocusLineEdit.cpp ui/widgets/FocusLineEdit.cpp
@ -1033,6 +1102,15 @@ SET(LAUNCHER_SOURCES
ui/instanceview/VisualGroup.h ui/instanceview/VisualGroup.h
) )
if (NOT Apple)
set(LAUNCHER_SOURCES
${LAUNCHER_SOURCES}
ui/dialogs/UpdateAvailableDialog.h
ui/dialogs/UpdateAvailableDialog.cpp
)
endif()
if(WIN32) if(WIN32)
set(LAUNCHER_SOURCES set(LAUNCHER_SOURCES
WindowsConsole.cpp WindowsConsole.cpp
@ -1076,6 +1154,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/pages/modplatform/technic/TechnicPage.ui ui/pages/modplatform/technic/TechnicPage.ui
ui/widgets/InstanceCardWidget.ui ui/widgets/InstanceCardWidget.ui
ui/widgets/CustomCommands.ui ui/widgets/CustomCommands.ui
ui/widgets/EnvironmentVariables.ui
ui/widgets/InfoFrame.ui ui/widgets/InfoFrame.ui
ui/widgets/ModFilterWidget.ui ui/widgets/ModFilterWidget.ui
ui/widgets/SubTaskProgressBar.ui ui/widgets/SubTaskProgressBar.ui
@ -1103,6 +1182,14 @@ qt_wrap_ui(LAUNCHER_UI
ui/dialogs/ChooseProviderDialog.ui ui/dialogs/ChooseProviderDialog.ui
) )
qt_wrap_ui(PRISM_UPDATE_UI
ui/dialogs/UpdateAvailableDialog.ui
)
if (NOT Apple)
set (LAUNCHER_UI ${LAUNCHER_UI} ${PRISM_UPDATE_UI})
endif()
qt_add_resources(LAUNCHER_RESOURCES qt_add_resources(LAUNCHER_RESOURCES
resources/backgrounds/backgrounds.qrc resources/backgrounds/backgrounds.qrc
resources/multimc/multimc.qrc resources/multimc/multimc.qrc
@ -1119,6 +1206,12 @@ qt_add_resources(LAUNCHER_RESOURCES
../${Launcher_Branding_LogoQRC} ../${Launcher_Branding_LogoQRC}
) )
qt_wrap_ui(PRISMUPDATER_UI
updater/prismupdater/SelectReleaseDialog.ui
ui/widgets/SubTaskProgressBar.ui
ui/dialogs/ProgressDialog.ui
)
######## Windows resource files ######## ######## Windows resource files ########
if(WIN32) if(WIN32)
set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC}) set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC})
@ -1137,6 +1230,7 @@ set_project_warnings(Launcher_logic
"${Launcher_GCC_WARNINGS}") "${Launcher_GCC_WARNINGS}")
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION) target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
target_link_libraries(Launcher_logic target_link_libraries(Launcher_logic
systeminfo systeminfo
Launcher_murmur2 Launcher_murmur2
@ -1218,7 +1312,45 @@ install(TARGETS ${Launcher_Name}
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
) )
if(WIN32) if(Launcher_BUILD_UPDATER)
# Updater
add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI})
target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(prism_updater_logic
QuaZip::QuaZip
${ZLIB_LIBRARIES}
systeminfo
BuildConfig
ghcFilesystem::ghc_filesystem
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::Core
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()
if(DEFINED Launcher_BINARY_RPATH)
SET_TARGET_PROPERTIES("${Launcher_Name}_updater" PROPERTIES INSTALL_RPATH "${Launcher_BINARY_RPATH}")
endif()
install(TARGETS "${Launcher_Name}_updater"
BUNDLE DESTINATION "." COMPONENT Runtime
LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime
RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
)
endif()
if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
# File link
add_library(filelink_logic STATIC ${LINKEXE_SOURCES}) add_library(filelink_logic STATIC ${LINKEXE_SOURCES})
set_project_warnings(filelink_logic set_project_warnings(filelink_logic
"${Launcher_MSVC_WARNINGS}" "${Launcher_MSVC_WARNINGS}"
@ -1237,7 +1369,7 @@ if(WIN32)
${Launcher_QT_LIBS} ${Launcher_QT_LIBS}
) )
add_executable("${Launcher_Name}_filelink" WIN32 filelink/main.cpp) add_executable("${Launcher_Name}_filelink" WIN32 filelink/filelink_main.cpp)
target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest) target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest)

View File

@ -18,7 +18,7 @@
class DataMigrationTask : public Task { class DataMigrationTask : public Task {
Q_OBJECT Q_OBJECT
public: public:
explicit DataMigrationTask(QObject* parent, const QString& sourcePath, const QString& targetPath, const IPathMatcher::Ptr pathmatcher); explicit DataMigrationTask(QObject* parent, const QString& sourcePath, const QString& targetPath, IPathMatcher::Ptr pathmatcher);
~DataMigrationTask() override = default; ~DataMigrationTask() override = default;
protected: protected:

View File

@ -108,12 +108,9 @@ bool openDirectory(const QString& path, [[maybe_unused]] bool ensureExists)
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
if (!isSandbox()) { if (!isSandbox()) {
return IndirectOpen(f); return IndirectOpen(f);
} else {
return f();
} }
#else
return f();
#endif #endif
return f();
} }
bool openFile(const QString& path) bool openFile(const QString& path)

View File

@ -123,26 +123,35 @@ namespace fs = ghc::filesystem;
#if defined(__MINGW32__) #if defined(__MINGW32__)
typedef struct _DUPLICATE_EXTENTS_DATA { struct _DUPLICATE_EXTENTS_DATA {
HANDLE FileHandle; HANDLE FileHandle;
LARGE_INTEGER SourceFileOffset; LARGE_INTEGER SourceFileOffset;
LARGE_INTEGER TargetFileOffset; LARGE_INTEGER TargetFileOffset;
LARGE_INTEGER ByteCount; LARGE_INTEGER ByteCount;
} DUPLICATE_EXTENTS_DATA, *PDUPLICATE_EXTENTS_DATA; };
typedef struct _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER { using DUPLICATE_EXTENTS_DATA = _DUPLICATE_EXTENTS_DATA;
using PDUPLICATE_EXTENTS_DATA = _DUPLICATE_EXTENTS_DATA*;
struct _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER {
WORD ChecksumAlgorithm; // Checksum algorithm. e.g. CHECKSUM_TYPE_UNCHANGED, CHECKSUM_TYPE_NONE, CHECKSUM_TYPE_CRC32 WORD ChecksumAlgorithm; // Checksum algorithm. e.g. CHECKSUM_TYPE_UNCHANGED, CHECKSUM_TYPE_NONE, CHECKSUM_TYPE_CRC32
WORD Reserved; // Must be 0 WORD Reserved; // Must be 0
DWORD Flags; // FSCTL_INTEGRITY_FLAG_xxx DWORD Flags; // FSCTL_INTEGRITY_FLAG_xxx
DWORD ChecksumChunkSizeInBytes; DWORD ChecksumChunkSizeInBytes;
DWORD ClusterSizeInBytes; DWORD ClusterSizeInBytes;
} FSCTL_GET_INTEGRITY_INFORMATION_BUFFER, *PFSCTL_GET_INTEGRITY_INFORMATION_BUFFER; };
typedef struct _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER { using FSCTL_GET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER;
using PFSCTL_GET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER*;
struct _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER {
WORD ChecksumAlgorithm; // Checksum algorithm. e.g. CHECKSUM_TYPE_UNCHANGED, CHECKSUM_TYPE_NONE, CHECKSUM_TYPE_CRC32 WORD ChecksumAlgorithm; // Checksum algorithm. e.g. CHECKSUM_TYPE_UNCHANGED, CHECKSUM_TYPE_NONE, CHECKSUM_TYPE_CRC32
WORD Reserved; // Must be 0 WORD Reserved; // Must be 0
DWORD Flags; // FSCTL_INTEGRITY_FLAG_xxx DWORD Flags; // FSCTL_INTEGRITY_FLAG_xxx
} FSCTL_SET_INTEGRITY_INFORMATION_BUFFER, *PFSCTL_SET_INTEGRITY_INFORMATION_BUFFER; };
using FSCTL_SET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER;
using PFSCTL_SET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER*;
#endif #endif
@ -194,6 +203,40 @@ void write(const QString& filename, const QByteArray& data)
} }
} }
void appendSafe(const QString& filename, const QByteArray& data)
{
ensureExists(QFileInfo(filename).dir());
QByteArray buffer;
try {
buffer = read(filename);
} catch (FileSystemException&) {
buffer = QByteArray();
}
buffer.append(data);
QSaveFile file(filename);
if (!file.open(QSaveFile::WriteOnly)) {
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
}
if (buffer.size() != file.write(buffer)) {
throw FileSystemException("Error writing data to " + filename + ": " + file.errorString());
}
if (!file.commit()) {
throw FileSystemException("Error while committing data to " + filename + ": " + file.errorString());
}
}
void append(const QString& filename, const QByteArray& data)
{
ensureExists(QFileInfo(filename).dir());
QFile file(filename);
if (!file.open(QFile::Append)) {
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
}
if (data.size() != file.write(data)) {
throw FileSystemException("Error writing data to " + filename + ": " + file.errorString());
}
}
QByteArray read(const QString& filename) QByteArray read(const QString& filename)
{ {
QFile file(filename); QFile file(filename);
@ -287,6 +330,9 @@ bool copy::operator()(const QString& offset, bool dryRun)
if (!m_followSymlinks) if (!m_followSymlinks)
opt |= copy_opts::copy_symlinks; opt |= copy_opts::copy_symlinks;
if (m_overwrite)
opt |= copy_opts::overwrite_existing;
// Function that'll do the actual copying // Function that'll do the actual copying
auto copy_file = [&](QString src_path, QString relative_dst_path) { auto copy_file = [&](QString src_path, QString relative_dst_path) {
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist)) if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
@ -897,6 +943,8 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
<< "\n"; << "\n";
stream << "Type=Application" stream << "Type=Application"
<< "\n"; << "\n";
stream << "Categories=Game;ActionGame;AdventureGame;Simulation"
<< "\n";
stream << "Exec=\"" << target.toLocal8Bit() << "\"" << argstring.toLocal8Bit() << "\n"; stream << "Exec=\"" << target.toLocal8Bit() << "\"" << argstring.toLocal8Bit() << "\n";
stream << "Name=" << name.toLocal8Bit() << "\n"; stream << "Name=" << name.toLocal8Bit() << "\n";
if (!icon.isEmpty()) { if (!icon.isEmpty()) {

View File

@ -61,6 +61,16 @@ class FileSystemException : public ::Exception {
*/ */
void write(const QString& filename, const QByteArray& data); void write(const QString& filename, const QByteArray& data);
/**
* append data to a file safely
*/
void appendSafe(const QString& filename, const QByteArray& data);
/**
* append data to a file
*/
void append(const QString& filename, const QByteArray& data);
/** /**
* read data from a file safely\ * read data from a file safely\
*/ */
@ -109,6 +119,11 @@ class copy : public QObject {
m_whitelist = whitelist; m_whitelist = whitelist;
return *this; return *this;
} }
copy& overwrite(const bool overwrite)
{
m_overwrite = overwrite;
return *this;
}
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); } bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
@ -128,6 +143,7 @@ class copy : public QObject {
bool m_followSymlinks = true; bool m_followSymlinks = true;
const IPathMatcher* m_matcher = nullptr; const IPathMatcher* m_matcher = nullptr;
bool m_whitelist = false; bool m_whitelist = false;
bool m_overwrite = false;
QDir m_src; QDir m_src;
QDir m_dst; QDir m_dst;
qsizetype m_copied; qsizetype m_copied;

View File

@ -59,7 +59,7 @@
#include <quazip/quazipdir.h> #include <quazip/quazipdir.h>
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent, QMap<QString, QString>&& extra_info) InstanceImportTask::InstanceImportTask(const QUrl& sourceUrl, QWidget* parent, QMap<QString, QString>&& extra_info)
: m_sourceUrl(sourceUrl), m_extra_info(extra_info), m_parent(parent) : m_sourceUrl(sourceUrl), m_extra_info(extra_info), m_parent(parent)
{} {}

View File

@ -54,7 +54,7 @@ class FileResolvingTask;
class InstanceImportTask : public InstanceTask { class InstanceImportTask : public InstanceTask {
Q_OBJECT Q_OBJECT
public: public:
explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {}); explicit InstanceImportTask(const QUrl& sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {});
bool abort() override; bool abort() override;
const QVector<Flame::File>& getBlockedFiles() const { return m_blockedMods; } const QVector<Flame::File>& getBlockedFiles() const { return m_blockedMods; }

View File

@ -2,6 +2,7 @@
/* /*
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -237,8 +238,11 @@ GroupId InstanceList::getInstanceGroup(const InstanceId& id) const
return GroupId(); return GroupId();
} }
void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name) void InstanceList::setInstanceGroup(const InstanceId& id, GroupId name)
{ {
if (name.isEmpty() && !name.isNull())
name = QString();
auto inst = getInstanceById(id); auto inst = getInstanceById(id);
if (!inst) { if (!inst) {
qDebug() << "Attempt to set a null instance's group"; qDebug() << "Attempt to set a null instance's group";
@ -249,6 +253,7 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
auto iter = m_instanceGroupIndex.find(inst->id()); auto iter = m_instanceGroupIndex.find(inst->id());
if (iter != m_instanceGroupIndex.end()) { if (iter != m_instanceGroupIndex.end()) {
if (*iter != name) { if (*iter != name) {
decreaseGroupCount(*iter);
*iter = name; *iter = name;
changed = true; changed = true;
} }
@ -258,7 +263,7 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
} }
if (changed) { if (changed) {
m_groupNameCache.insert(name); increaseGroupCount(name);
auto idx = getInstIndex(inst.get()); auto idx = getInstIndex(inst.get());
emit dataChanged(index(idx), index(idx), { GroupRole }); emit dataChanged(index(idx), index(idx), { GroupRole });
saveGroupList(); saveGroupList();
@ -267,29 +272,55 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
QStringList InstanceList::getGroups() QStringList InstanceList::getGroups()
{ {
return m_groupNameCache.values(); return m_groupNameCache.keys();
} }
void InstanceList::deleteGroup(const QString& name) void InstanceList::deleteGroup(const GroupId& name)
{ {
m_groupNameCache.remove(name);
m_collapsedGroups.remove(name);
bool removed = false; bool removed = false;
qDebug() << "Delete group" << name; qDebug() << "Delete group" << name;
for (auto& instance : m_instances) { for (auto& instance : m_instances) {
const auto& instID = instance->id(); const QString& instID = instance->id();
auto instGroupName = getInstanceGroup(instID); const QString instGroupName = getInstanceGroup(instID);
if (instGroupName == name) { if (instGroupName == name) {
m_instanceGroupIndex.remove(instID); m_instanceGroupIndex.remove(instID);
qDebug() << "Remove" << instID << "from group" << name; qDebug() << "Remove" << instID << "from group" << name;
removed = true; removed = true;
auto idx = getInstIndex(instance.get()); auto idx = getInstIndex(instance.get());
if (idx > 0) { if (idx >= 0)
emit dataChanged(index(idx), index(idx), { GroupRole }); emit dataChanged(index(idx), index(idx), { GroupRole });
}
} }
} }
if (removed) { if (removed)
saveGroupList(); saveGroupList();
}
void InstanceList::renameGroup(const QString& src, const QString& dst)
{
m_groupNameCache.remove(src);
if (m_collapsedGroups.remove(src))
m_collapsedGroups.insert(dst);
bool modified = false;
qDebug() << "Rename group" << src << "to" << dst;
for (auto& instance : m_instances) {
const QString& instID = instance->id();
const QString instGroupName = getInstanceGroup(instID);
if (instGroupName == src) {
m_instanceGroupIndex[instID] = dst;
increaseGroupCount(dst);
qDebug() << "Set" << instID << "group to" << dst;
modified = true;
auto idx = getInstIndex(instance.get());
if (idx >= 0)
emit dataChanged(index(idx), index(idx), { GroupRole });
}
} }
if (modified)
saveGroupList();
} }
bool InstanceList::isGroupCollapsed(const QString& group) bool InstanceList::isGroupCollapsed(const QString& group)
@ -305,12 +336,13 @@ bool InstanceList::trashInstance(const InstanceId& id)
return false; return false;
} }
auto cachedGroupId = m_instanceGroupIndex[id]; QString cachedGroupId = m_instanceGroupIndex[id];
qDebug() << "Will trash instance" << id; qDebug() << "Will trash instance" << id;
QString trashedLoc; QString trashedLoc;
if (m_instanceGroupIndex.remove(id)) { if (m_instanceGroupIndex.remove(id)) {
decreaseGroupCount(cachedGroupId);
saveGroupList(); saveGroupList();
} }
@ -348,7 +380,7 @@ void InstanceList::undoTrashInstance()
QFile(top.trashPath).rename(top.polyPath); QFile(top.trashPath).rename(top.polyPath);
m_instanceGroupIndex[top.id] = top.groupName; m_instanceGroupIndex[top.id] = top.groupName;
m_groupNameCache.insert(top.groupName); increaseGroupCount(top.groupName);
saveGroupList(); saveGroupList();
emit instancesChanged(); emit instancesChanged();
@ -362,7 +394,10 @@ void InstanceList::deleteInstance(const InstanceId& id)
return; return;
} }
QString cachedGroupId = m_instanceGroupIndex[id];
if (m_instanceGroupIndex.remove(id)) { if (m_instanceGroupIndex.remove(id)) {
decreaseGroupCount(cachedGroupId);
saveGroupList(); saveGroupList();
} }
@ -610,6 +645,25 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id)
return inst; return inst;
} }
void InstanceList::increaseGroupCount(const QString& group)
{
if (group.isEmpty())
return;
++m_groupNameCache[group];
}
void InstanceList::decreaseGroupCount(const QString& group)
{
if (group.isEmpty())
return;
if (--m_groupNameCache[group] < 1) {
m_groupNameCache.remove(group);
m_collapsedGroups.remove(group);
}
}
void InstanceList::saveGroupList() void InstanceList::saveGroupList()
{ {
qDebug() << "Will save group list now."; qDebug() << "Will save group list now.";
@ -621,7 +675,7 @@ void InstanceList::saveGroupList()
QString groupFileName = m_instDir + "/instgroups.json"; QString groupFileName = m_instDir + "/instgroups.json";
QMap<QString, QSet<QString>> reverseGroupMap; QMap<QString, QSet<QString>> reverseGroupMap;
for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++) { for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++) {
QString id = iter.key(); const QString& id = iter.key();
QString group = iter.value(); QString group = iter.value();
if (group.isEmpty()) if (group.isEmpty())
continue; continue;
@ -711,17 +765,22 @@ void InstanceList::loadGroupList()
return; return;
} }
QSet<QString> groupSet;
m_instanceGroupIndex.clear(); m_instanceGroupIndex.clear();
m_groupNameCache.clear();
// Iterate through all the groups. // Iterate through all the groups.
QJsonObject groupMapping = rootObj.value("groups").toObject(); QJsonObject groupMapping = rootObj.value("groups").toObject();
for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++) { for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++) {
QString groupName = iter.key(); QString groupName = iter.key();
if (iter.key().isEmpty()) {
qWarning() << "Redundant empty group found";
continue;
}
// If not an object, complain and skip to the next one. // If not an object, complain and skip to the next one.
if (!iter.value().isObject()) { if (!iter.value().isObject()) {
qWarning() << QString("Group '%1' in the group list should be an object.").arg(groupName).toUtf8(); qWarning() << QString("Group '%1' in the group list should be an object").arg(groupName).toUtf8();
continue; continue;
} }
@ -733,23 +792,19 @@ void InstanceList::loadGroupList()
continue; continue;
} }
// keep a list/set of groups for choosing
groupSet.insert(groupName);
auto hidden = groupObj.value("hidden").toBool(false); auto hidden = groupObj.value("hidden").toBool(false);
if (hidden) { if (hidden)
m_collapsedGroups.insert(groupName); m_collapsedGroups.insert(groupName);
}
// Iterate through the list of instances in the group. // Iterate through the list of instances in the group.
QJsonArray instancesArray = groupObj.value("instances").toArray(); QJsonArray instancesArray = groupObj.value("instances").toArray();
for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); iter2++) { for (auto value : instancesArray) {
m_instanceGroupIndex[(*iter2).toString()] = groupName; m_instanceGroupIndex[value.toString()] = groupName;
increaseGroupCount(groupName);
} }
} }
m_groupsLoaded = true; m_groupsLoaded = true;
m_groupNameCache.unite(groupSet);
qDebug() << "Group list loaded."; qDebug() << "Group list loaded.";
} }
@ -768,6 +823,9 @@ void InstanceList::on_InstFolderChanged([[maybe_unused]] const Setting& setting,
} }
m_instDir = newInstDir; m_instDir = newInstDir;
m_groupsLoaded = false; m_groupsLoaded = false;
beginRemoveRows(QModelIndex(), 0, count());
m_instances.erase(m_instances.begin(), m_instances.end());
endRemoveRows();
emit instancesChanged(); emit instancesChanged();
} }
} }
@ -892,9 +950,12 @@ QString InstanceList::getStagedInstancePath()
bool InstanceList::commitStagedInstance(const QString& path, bool InstanceList::commitStagedInstance(const QString& path,
InstanceName const& instanceName, InstanceName const& instanceName,
const QString& groupName, QString groupName,
InstanceTask const& commiting) InstanceTask const& commiting)
{ {
if (groupName.isEmpty() && !groupName.isNull())
groupName = QString();
QDir dir; QDir dir;
QString instID; QString instID;
InstancePtr inst; InstancePtr inst;
@ -925,7 +986,7 @@ bool InstanceList::commitStagedInstance(const QString& path,
} }
m_instanceGroupIndex[instID] = groupName; m_instanceGroupIndex[instID] = groupName;
m_groupNameCache.insert(groupName); increaseGroupCount(groupName);
} }
instanceSet.insert(instID); instanceSet.insert(instID);

View File

@ -1,16 +1,36 @@
/* Copyright 2013-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * This program is free software: you can redistribute it and/or modify
* you may not use this file except in compliance with the License. * it under the terms of the GNU General Public License as published by
* You may obtain a copy of the License at * the Free Software Foundation, version 3.
* *
* http://www.apache.org/licenses/LICENSE-2.0 * 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.
* *
* Unless required by applicable law or agreed to in writing, software * You should have received a copy of the GNU General Public License
* distributed under the License is distributed on an "AS IS" BASIS, * along with this program. If not, see <https://www.gnu.org/licenses/>.
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and * This file incorporates work covered by the following copyright and
* limitations under the License. * permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
#pragma once #pragma once
@ -86,9 +106,10 @@ class InstanceList : public QAbstractListModel {
bool isGroupCollapsed(const QString& groupName); bool isGroupCollapsed(const QString& groupName);
GroupId getInstanceGroup(const InstanceId& id) const; GroupId getInstanceGroup(const InstanceId& id) const;
void setInstanceGroup(const InstanceId& id, const GroupId& name); void setInstanceGroup(const InstanceId& id, GroupId name);
void deleteGroup(const GroupId& name); void deleteGroup(const GroupId& name);
void renameGroup(const GroupId& src, const GroupId& dst);
bool trashInstance(const InstanceId& id); bool trashInstance(const InstanceId& id);
bool trashedSomething(); bool trashedSomething();
void undoTrashInstance(); void undoTrashInstance();
@ -109,7 +130,7 @@ class InstanceList : public QAbstractListModel {
* should_override is used when another similar instance already exists, and we want to override it * should_override is used when another similar instance already exists, and we want to override it
* - for instance, when updating it. * - for instance, when updating it.
*/ */
bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, const QString& groupName, const InstanceTask&); bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, QString groupName, const InstanceTask&);
/** /**
* Destroy a previously created staging area given by @keyPath - used when creation fails. * Destroy a previously created staging area given by @keyPath - used when creation fails.
@ -158,12 +179,16 @@ class InstanceList : public QAbstractListModel {
QList<InstanceId> discoverInstances(); QList<InstanceId> discoverInstances();
InstancePtr loadInstance(const InstanceId& id); InstancePtr loadInstance(const InstanceId& id);
void increaseGroupCount(const QString& group);
void decreaseGroupCount(const QString& group);
private: private:
int m_watchLevel = 0; int m_watchLevel = 0;
int totalPlayTime = 0; int totalPlayTime = 0;
bool m_dirty = false; bool m_dirty = false;
QList<InstancePtr> m_instances; QList<InstancePtr> m_instances;
QSet<QString> m_groupNameCache; // id -> refs
QMap<QString, int> m_groupNameCache;
SettingsObjectPtr m_globalSettings; SettingsObjectPtr m_globalSettings;
QString m_instDir; QString m_instDir;

View File

@ -36,6 +36,7 @@
#include "LaunchController.h" #include "LaunchController.h"
#include "Application.h" #include "Application.h"
#include "minecraft/auth/AccountData.h"
#include "minecraft/auth/AccountList.h" #include "minecraft/auth/AccountList.h"
#include "ui/InstanceWindow.h" #include "ui/InstanceWindow.h"
@ -89,7 +90,7 @@ void LaunchController::decideAccount()
// Tell the user they need to log in at least one account in order to play. // 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"), auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"),
tr("In order to play Minecraft, you must have at least one Microsoft " tr("In order to play Minecraft, you must have at least one Microsoft "
"account which owns Minecraft logged in." "account which owns Minecraft logged in. "
"Would you like to open the account manager to add an account now?"), "Would you like to open the account manager to add an account now?"),
QMessageBox::Information, QMessageBox::Yes | QMessageBox::No) QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)
->exec(); ->exec();
@ -106,7 +107,7 @@ void LaunchController::decideAccount()
// Select the account to use. If the instance has a specific account set, that will be used. Otherwise, the default account will be used // Select the account to use. If the instance has a specific account set, that will be used. Otherwise, the default account will be used
auto instanceAccountId = m_instance->settings()->get("InstanceAccountId").toString(); auto instanceAccountId = m_instance->settings()->get("InstanceAccountId").toString();
auto instanceAccountIndex = accounts->findAccountByProfileId(instanceAccountId); auto instanceAccountIndex = accounts->findAccountByProfileId(instanceAccountId);
if (instanceAccountIndex == -1) { if (instanceAccountIndex == -1 || instanceAccountId.isEmpty()) {
m_accountToUse = accounts->defaultAccount(); m_accountToUse = accounts->defaultAccount();
} else { } else {
m_accountToUse = accounts->at(instanceAccountIndex); m_accountToUse = accounts->at(instanceAccountIndex);
@ -161,7 +162,7 @@ void LaunchController::login()
m_accountToUse->fillSession(m_session); m_accountToUse->fillSession(m_session);
// Launch immediately in true offline mode // Launch immediately in true offline mode
if (m_accountToUse->isOffline()) { if (m_accountToUse->accountType() == AccountType::Offline) {
launchInstance(); launchInstance();
return; return;
} }

View File

@ -42,11 +42,15 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug> #include <QDebug>
#include <QUrl>
#if defined(LAUNCHER_APPLICATION)
#include <QtConcurrentRun> #include <QtConcurrentRun>
#endif
namespace MMCZip { namespace MMCZip {
// ours // ours
bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet<QString>& contained, const FilterFunction filter) bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet<QString>& contained, const FilterFunction& filter)
{ {
QuaZip modZip(from.filePath()); QuaZip modZip(from.filePath());
modZip.open(QuaZip::mdUnzip); modZip.open(QuaZip::mdUnzip);
@ -132,6 +136,7 @@ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files,
return result; return result;
} }
#if defined(LAUNCHER_APPLICATION)
// ours // ours
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods) bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods)
{ {
@ -217,6 +222,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
} }
return true; return true;
} }
#endif
// ours // ours
QString findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root) QString findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root)
@ -422,6 +428,7 @@ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, Q
return true; return true;
} }
#if defined(LAUNCHER_APPLICATION)
void ExportToZipTask::executeTask() void ExportToZipTask::executeTask()
{ {
setStatus("Adding files..."); setStatus("Adding files...");
@ -500,5 +507,6 @@ bool ExportToZipTask::abort()
} }
return false; return false;
} }
#endif
} // namespace MMCZip } // namespace MMCZip

View File

@ -48,7 +48,10 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <optional> #include <optional>
#if defined(LAUNCHER_APPLICATION)
#include "minecraft/mod/Mod.h" #include "minecraft/mod/Mod.h"
#endif
#include "tasks/Task.h" #include "tasks/Task.h"
namespace MMCZip { namespace MMCZip {
@ -57,7 +60,7 @@ using FilterFunction = std::function<bool(const QString&)>;
/** /**
* Merge two zip files, using a filter function * Merge two zip files, using a filter function
*/ */
bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet<QString>& contained, const FilterFunction filter = nullptr); bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet<QString>& contained, const FilterFunction& filter = nullptr);
/** /**
* Compress directory, by providing a list of files to compress * Compress directory, by providing a list of files to compress
@ -79,11 +82,12 @@ bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool follow
*/ */
bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false); bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false);
#if defined(LAUNCHER_APPLICATION)
/** /**
* take a source jar, add mods to it, resulting in target jar * take a source jar, add mods to it, resulting in target jar
*/ */
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods); bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods);
#endif
/** /**
* Find a single file in archive by file name (not path) * Find a single file in archive by file name (not path)
* *
@ -147,6 +151,7 @@ bool extractFile(QString fileCompressed, QString file, QString dir);
*/ */
bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter); bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter);
#if defined(LAUNCHER_APPLICATION)
class ExportToZipTask : public Task { class ExportToZipTask : public Task {
public: public:
ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false)
@ -167,7 +172,7 @@ class ExportToZipTask : public Task {
void setExcludeFiles(QStringList excludeFiles) { m_exclude_files = excludeFiles; } void setExcludeFiles(QStringList excludeFiles) { m_exclude_files = excludeFiles; }
void addExtraFile(QString fileName, QByteArray data) { m_extra_files.insert(fileName, data); } void addExtraFile(QString fileName, QByteArray data) { m_extra_files.insert(fileName, data); }
typedef std::optional<QString> ZipResult; using ZipResult = std::optional<QString>;
protected: protected:
virtual void executeTask() override; virtual void executeTask() override;
@ -189,4 +194,5 @@ class ExportToZipTask : public Task {
QFuture<ZipResult> m_build_zip_future; QFuture<ZipResult> m_build_zip_future;
QFutureWatcher<ZipResult> m_build_zip_watcher; QFutureWatcher<ZipResult> m_build_zip_watcher;
}; };
#endif
} // namespace MMCZip } // namespace MMCZip

View File

@ -5,6 +5,7 @@
#include <QPixmapCache> #include <QPixmapCache>
#include <QThread> #include <QThread>
#include <QTime> #include <QTime>
#include <limits>
#define GET_TYPE() \ #define GET_TYPE() \
Qt::ConnectionType type; \ Qt::ConnectionType type; \
@ -100,10 +101,14 @@ class PixmapCache final : public QObject {
*/ */
bool _markCacheMissByEviciton() bool _markCacheMissByEviciton()
{ {
static constexpr uint maxInt = static_cast<uint>(std::numeric_limits<int>::max());
static constexpr uint step = 10240;
static constexpr int oneSecond = 1000;
auto now = QTime::currentTime(); auto now = QTime::currentTime();
if (!m_last_cache_miss_by_eviciton.isNull()) { if (!m_last_cache_miss_by_eviciton.isNull()) {
auto diff = m_last_cache_miss_by_eviciton.msecsTo(now); auto diff = m_last_cache_miss_by_eviciton.msecsTo(now);
if (diff < 1000) { // less than a second ago if (diff < oneSecond) { // less than a second ago
++m_consecutive_fast_evicitons; ++m_consecutive_fast_evicitons;
} else { } else {
m_consecutive_fast_evicitons = 0; m_consecutive_fast_evicitons = 0;
@ -111,11 +116,17 @@ class PixmapCache final : public QObject {
} }
m_last_cache_miss_by_eviciton = now; m_last_cache_miss_by_eviciton = now;
if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) { if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) {
// double the cache size // increase the cache size
auto newSize = _cacheLimit() * 2; uint newSize = _cacheLimit() + step;
qDebug() << m_consecutive_fast_evicitons << "pixmap cache misses by eviction happened too fast, doubling cache size to" if (newSize >= maxInt) { // increase it until you overflow :D
<< newSize; newSize = maxInt;
_setCacheLimit(newSize); 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 {
qDebug() << m_consecutive_fast_evicitons
<< tr("pixmap cache misses by eviction happened too fast, increasing cache size to") << static_cast<int>(newSize);
}
_setCacheLimit(static_cast<int>(newSize));
m_consecutive_fast_evicitons = 0; m_consecutive_fast_evicitons = 0;
return true; return true;
} }

View File

@ -13,7 +13,7 @@ class RecursiveFileSystemWatcher : public QObject {
QDir rootDir() const { return m_root; } QDir rootDir() const { return m_root; }
// WARNING: setting this to true may be bad for performance // WARNING: setting this to true may be bad for performance
void setWatchFiles(const bool watchFiles); void setWatchFiles(bool watchFiles);
bool watchFiles() const { return m_watchFiles; } bool watchFiles() const { return m_watchFiles; }
void setMatcher(IPathMatcher::Ptr matcher) { m_matcher = matcher; } void setMatcher(IPathMatcher::Ptr matcher) { m_matcher = matcher; }

View File

@ -32,7 +32,7 @@ class ResourceDownloadTask : public SequentialTask {
public: public:
explicit ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, explicit ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion version, ModPlatform::IndexedVersion version,
const std::shared_ptr<ResourceFolderModel> packs, std::shared_ptr<ResourceFolderModel> packs,
bool is_indexed = true, bool is_indexed = true,
QString custom_target_folder = {}); QString custom_target_folder = {});
const QString& getFilename() const { return m_pack_version.fileName; } const QString& getFilename() const { return m_pack_version.fileName; }

View File

@ -35,6 +35,7 @@
*/ */
#include "StringUtils.h" #include "StringUtils.h"
#include <qpair.h>
#include <QRegularExpression> #include <QRegularExpression>
#include <QUuid> #include <QUuid>
@ -149,7 +150,7 @@ QString StringUtils::truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_
} }
if ((url_compact.length() >= max_len) && hard_limit) { if ((url_compact.length() >= max_len) && hard_limit) {
// still too long, truncate normaly // still too long, truncate normally
url_compact = QString(str_url); url_compact = QString(str_url);
auto to_remove = url_compact.length() - max_len + 3; auto to_remove = url_compact.length() - max_len + 3;
url_compact.remove(url_compact.length() - to_remove - 1, to_remove); url_compact.remove(url_compact.length() - to_remove - 1, to_remove);
@ -182,3 +183,32 @@ QString StringUtils::getRandomAlphaNumeric()
{ {
return QUuid::createUuid().toString(QUuid::Id128); return QUuid::createUuid().toString(QUuid::Id128);
} }
QPair<QString, QString> StringUtils::splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs)
{
QString left, right;
auto index = s.indexOf(sep, 0, cs);
left = s.mid(0, index);
right = s.mid(index + sep.length());
return qMakePair(left, right);
}
QPair<QString, QString> StringUtils::splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs)
{
QString left, right;
auto index = s.indexOf(sep, 0, cs);
left = s.mid(0, index);
right = s.mid(left.length() + 1);
return qMakePair(left, right);
}
QPair<QString, QString> StringUtils::splitFirst(const QString& s, const QRegularExpression& re)
{
QString left, right;
QRegularExpressionMatch match;
auto index = s.indexOf(re, 0, &match);
left = s.mid(0, index);
auto end = match.hasMatch() ? left.length() + match.capturedLength() : left.length() + 1;
right = s.mid(end);
return qMakePair(left, right);
}

View File

@ -36,8 +36,10 @@
#pragma once #pragma once
#include <QPair>
#include <QString> #include <QString>
#include <QUrl> #include <QUrl>
#include <utility>
namespace StringUtils { namespace StringUtils {
@ -70,12 +72,17 @@ int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs)
/** /**
* @brief Truncate a url while keeping its readability py placing the `...` in the middle of the path * @brief Truncate a url while keeping its readability py placing the `...` in the middle of the path
* @param url Url to truncate * @param url Url to truncate
* @param max_len max lenght of url in charaters * @param max_len max length of url in characters
* @param hard_limit if truncating the path can't get the url short enough, truncate it normaly. * @param hard_limit if truncating the path can't get the url short enough, truncate it normally.
*/ */
QString truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_limit = false); QString truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_limit = false);
QString humanReadableFileSize(double bytes, bool use_si = false, int decimal_points = 1); QString humanReadableFileSize(double bytes, bool use_si = false, int decimal_points = 1);
QString getRandomAlphaNumeric(); QString getRandomAlphaNumeric();
QPair<QString, QString> splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs = Qt::CaseSensitive);
QPair<QString, QString> splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs = Qt::CaseSensitive);
QPair<QString, QString> splitFirst(const QString& s, const QRegularExpression& re);
} // namespace StringUtils } // namespace StringUtils

View File

@ -56,6 +56,7 @@ class Version {
bool operator!=(const Version& other) const; bool operator!=(const Version& other) const;
QString toString() const { return m_string; } QString toString() const { return m_string; }
bool isEmpty() const { return m_string.isEmpty(); }
friend QDebug operator<<(QDebug debug, const Version& v); friend QDebug operator<<(QDebug debug, const Version& v);

View File

@ -10,7 +10,7 @@ class VersionProxyModel : public QAbstractProxyModel {
Q_OBJECT Q_OBJECT
public: public:
enum Column { Name, ParentVersion, Branch, Type, Architecture, Path, Time }; enum Column { Name, ParentVersion, Branch, Type, Architecture, Path, Time };
typedef QHash<BaseVersionList::ModelRoles, std::shared_ptr<Filter>> FilterMap; using FilterMap = QHash<BaseVersionList::ModelRoles, std::shared_ptr<Filter>>;
public: public:
VersionProxyModel(QObject* parent = 0); VersionProxyModel(QObject* parent = 0);
@ -28,7 +28,7 @@ class VersionProxyModel : public QAbstractProxyModel {
const FilterMap& filters() const; const FilterMap& filters() const;
const QString& search() const; const QString& search() const;
void setFilter(const BaseVersionList::ModelRoles column, Filter* filter); void setFilter(BaseVersionList::ModelRoles column, Filter* filter);
void setSearch(const QString& search); void setSearch(const QString& search);
void clearFilters(); void clearFilters();
QModelIndex getRecommended() const; QModelIndex getRecommended() const;

View File

@ -93,6 +93,7 @@ FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv),
joinServer(serverToJoin); joinServer(serverToJoin);
} else { } else {
qDebug() << "no server to join"; qDebug() << "no server to join";
m_status = Failed;
exit(); exit();
} }
} }
@ -108,6 +109,7 @@ void FileLinkApp::joinServer(QString server)
connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs); connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs);
connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError) { connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError) {
m_status = Failed;
switch (socketError) { switch (socketError) {
case QLocalSocket::ServerNotFoundError: case QLocalSocket::ServerNotFoundError:
qDebug() qDebug()
@ -132,6 +134,7 @@ void FileLinkApp::joinServer(QString server)
connect(&socket, &QLocalSocket::disconnected, this, [&]() { connect(&socket, &QLocalSocket::disconnected, this, [&]() {
qDebug() << "disconnected from server, should exit"; qDebug() << "disconnected from server, should exit";
m_status = Succeeded;
exit(); exit();
}); });

View File

@ -41,8 +41,10 @@ class FileLinkApp : public QCoreApplication {
// friends for the purpose of limiting access to deprecated stuff // friends for the purpose of limiting access to deprecated stuff
Q_OBJECT Q_OBJECT
public: public:
enum Status { Starting, Failed, Succeeded, Initialized };
FileLinkApp(int& argc, char** argv); FileLinkApp(int& argc, char** argv);
virtual ~FileLinkApp(); virtual ~FileLinkApp();
Status status() const { return m_status; }
private: private:
void joinServer(QString server); void joinServer(QString server);
@ -50,6 +52,8 @@ class FileLinkApp : public QCoreApplication {
void runLink(); void runLink();
void sendResults(); void sendResults();
Status m_status = Status::Starting;
bool m_useHardLinks = false; bool m_useHardLinks = false;
QDateTime m_startTime; QDateTime m_startTime;

View File

@ -26,5 +26,16 @@ int main(int argc, char* argv[])
{ {
FileLinkApp ldh(argc, argv); FileLinkApp ldh(argc, argv);
return ldh.exec(); switch (ldh.status()) {
case FileLinkApp::Starting:
case FileLinkApp::Initialized: {
return ldh.exec();
}
case FileLinkApp::Failed:
return 1;
case FileLinkApp::Succeeded:
return 0;
default:
return -1;
}
} }

View File

@ -67,7 +67,7 @@ class IconList : public QAbstractListModel {
virtual Qt::ItemFlags flags(const QModelIndex& index) const override; virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
bool addThemeIcon(const QString& key); bool addThemeIcon(const QString& key);
bool addIcon(const QString& key, const QString& name, const QString& path, const IconType type); bool addIcon(const QString& key, const QString& name, const QString& path, IconType type);
void saveIcon(const QString& key, const QString& path, const char* format) const; void saveIcon(const QString& key, const QString& path, const char* format) const;
bool deleteIcon(const QString& key); bool deleteIcon(const QString& key);
bool trashIcon(const QString& key); bool trashIcon(const QString& key);

View File

@ -22,8 +22,8 @@ struct JavaCheckResult {
enum class Validity { Errored, ReturnedInvalidData, Valid } validity = Validity::Errored; enum class Validity { Errored, ReturnedInvalidData, Valid } validity = Validity::Errored;
}; };
typedef shared_qobject_ptr<QProcess> QProcessPtr; using QProcessPtr = shared_qobject_ptr<QProcess>;
typedef shared_qobject_ptr<JavaChecker> JavaCheckerPtr; using JavaCheckerPtr = shared_qobject_ptr<JavaChecker>;
class JavaChecker : public QObject { class JavaChecker : public QObject {
Q_OBJECT Q_OBJECT
public: public:

View File

@ -20,7 +20,7 @@
#include "tasks/Task.h" #include "tasks/Task.h"
class JavaCheckerJob; class JavaCheckerJob;
typedef shared_qobject_ptr<JavaCheckerJob> JavaCheckerJobPtr; using JavaCheckerJobPtr = shared_qobject_ptr<JavaCheckerJob>;
// FIXME: this just seems horribly redundant // FIXME: this just seems horribly redundant
class JavaCheckerJob : public Task { class JavaCheckerJob : public Task {

View File

@ -42,4 +42,4 @@ struct JavaInstall : public BaseVersion {
bool recommended = false; bool recommended = false;
}; };
typedef std::shared_ptr<JavaInstall> JavaInstallPtr; using JavaInstallPtr = std::shared_ptr<JavaInstall>;

View File

@ -34,6 +34,7 @@
*/ */
#include <QDir> #include <QDir>
#include <QFileInfo>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
@ -335,6 +336,7 @@ QList<QString> JavaUtils::FindJavaPaths()
} }
} }
candidates.append(getMinecraftJavaBundle());
candidates = addJavasFromEnv(candidates); candidates = addJavasFromEnv(candidates);
candidates.removeDuplicates(); candidates.removeDuplicates();
return candidates; return candidates;
@ -360,6 +362,7 @@ QList<QString> JavaUtils::FindJavaPaths()
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java"); javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java");
} }
javas.append(getMinecraftJavaBundle());
javas = addJavasFromEnv(javas); javas = addJavasFromEnv(javas);
javas.removeDuplicates(); javas.removeDuplicates();
return javas; return javas;
@ -411,6 +414,7 @@ QList<QString> JavaUtils::FindJavaPaths()
// javas downloaded by sdkman // javas downloaded by sdkman
scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java")); scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java"));
javas.append(getMinecraftJavaBundle());
javas = addJavasFromEnv(javas); javas = addJavasFromEnv(javas);
javas.removeDuplicates(); javas.removeDuplicates();
return javas; return javas;
@ -423,6 +427,7 @@ QList<QString> JavaUtils::FindJavaPaths()
QList<QString> javas; QList<QString> javas;
javas.append(this->GetDefaultJava()->path); javas.append(this->GetDefaultJava()->path);
javas.append(getMinecraftJavaBundle());
return addJavasFromEnv(javas); return addJavasFromEnv(javas);
} }
#endif #endif
@ -431,3 +436,50 @@ QString JavaUtils::getJavaCheckPath()
{ {
return APPLICATION->getJarPath("JavaCheck.jar"); return APPLICATION->getJarPath("JavaCheck.jar");
} }
QStringList getMinecraftJavaBundle()
{
QString partialPath;
QString executable = "java";
QStringList processpaths;
#if defined(Q_OS_OSX)
partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support");
#elif defined(Q_OS_WIN32)
partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", "");
executable += "w.exe";
// add the microsoft store version of the launcher to the search. the current path is:
// C:\Users\USERNAME\AppData\Local\Packages\Microsoft.4297127D64EC6_8wekyb3d8bbwe\LocalCache\Local\runtime
auto minecraftMSStorePath =
FS::PathCombine(QFileInfo(partialPath).absolutePath(), "Local", "Packages", "Microsoft.4297127D64EC6_8wekyb3d8bbwe");
minecraftMSStorePath = FS::PathCombine(minecraftMSStorePath, "LocalCache", "Local", "runtime");
processpaths << minecraftMSStorePath;
#else
partialPath = QDir::homePath();
#endif
auto minecraftDataPath = FS::PathCombine(partialPath, ".minecraft", "runtime");
processpaths << minecraftDataPath;
QStringList javas;
while (!processpaths.isEmpty()) {
auto dirPath = processpaths.takeFirst();
QDir dir(dirPath);
if (!dir.exists())
continue;
auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
auto binFound = false;
for (auto& entry : entries) {
if (entry.baseName() == "bin") {
javas.append(FS::PathCombine(entry.canonicalFilePath(), executable));
binFound = true;
break;
}
}
if (!binFound) {
for (auto& entry : entries) {
processpaths << entry.canonicalFilePath();
}
}
}
return javas;
}

View File

@ -26,6 +26,7 @@
QString stripVariableEntries(QString name, QString target, QString remove); QString stripVariableEntries(QString name, QString target, QString remove);
QProcessEnvironment CleanEnviroment(); QProcessEnvironment CleanEnviroment();
QStringList getMinecraftJavaBundle();
class JavaUtils : public QObject { class JavaUtils : public QObject {
Q_OBJECT Q_OBJECT

View File

@ -45,10 +45,12 @@ QString JavaVersion::toString() const
bool JavaVersion::requiresPermGen() bool JavaVersion::requiresPermGen()
{ {
if (m_parseable) { return !m_parseable || m_major < 8;
return m_major < 8; }
}
return true; bool JavaVersion::isModular()
{
return m_parseable && m_major >= 9;
} }
bool JavaVersion::operator<(const JavaVersion& rhs) bool JavaVersion::operator<(const JavaVersion& rhs)

View File

@ -25,6 +25,8 @@ class JavaVersion {
bool requiresPermGen(); bool requiresPermGen();
bool isModular();
QString toString() const; QString toString() const;
int major() { return m_major; } int major() { return m_major; }

View File

@ -55,6 +55,6 @@ class Index : public QAbstractListModel, public BaseEntity {
QVector<VersionList::Ptr> m_lists; QVector<VersionList::Ptr> m_lists;
QHash<QString, VersionList::Ptr> m_uids; QHash<QString, VersionList::Ptr> m_uids;
void connectVersionList(const int row, const VersionList::Ptr& list); void connectVersionList(int row, const VersionList::Ptr& list);
}; };
} // namespace Meta } // namespace Meta

View File

@ -64,7 +64,7 @@ class Version : public QObject, public BaseVersion, public BaseEntity {
public: // for usage by format parsers only public: // for usage by format parsers only
void setType(const QString& type); void setType(const QString& type);
void setTime(const qint64 time); void setTime(qint64 time);
void setRequires(const Meta::RequireSet& reqs, const Meta::RequireSet& conflicts); void setRequires(const Meta::RequireSet& reqs, const Meta::RequireSet& conflicts);
void setVolatile(bool volatile_); void setVolatile(bool volatile_);
void setRecommended(bool recommended); void setRecommended(bool recommended);

View File

@ -79,7 +79,7 @@ class VersionList : public BaseVersionList, public BaseEntity {
Version::Ptr m_recommended; Version::Ptr m_recommended;
void setupAddedVersion(const int row, const Version::Ptr& version); void setupAddedVersion(int row, const Version::Ptr& version);
}; };
} // namespace Meta } // namespace Meta
Q_DECLARE_METATYPE(Meta::VersionList::Ptr) Q_DECLARE_METATYPE(Meta::VersionList::Ptr)

View File

@ -6,7 +6,7 @@
class Agent; class Agent;
typedef std::shared_ptr<Agent> AgentPtr; using AgentPtr = std::shared_ptr<Agent>;
class Agent { class Agent {
public: public:

View File

@ -48,6 +48,7 @@
#include "FileSystem.h" #include "FileSystem.h"
#include "net/ApiDownload.h" #include "net/ApiDownload.h"
#include "net/ChecksumValidator.h" #include "net/ChecksumValidator.h"
#include "net/Download.h"
#include "Application.h" #include "Application.h"

View File

@ -105,4 +105,4 @@ class Component : public QObject, public ProblemProvider {
bool m_loaded = false; bool m_loaded = false;
}; };
typedef shared_qobject_ptr<Component> ComponentPtr; using ComponentPtr = shared_qobject_ptr<Component>;

View File

@ -52,7 +52,7 @@
class Library; class Library;
class MinecraftInstance; class MinecraftInstance;
typedef std::shared_ptr<Library> LibraryPtr; using LibraryPtr = std::shared_ptr<Library>;
class Library { class Library {
friend class OneSixVersionFormat; friend class OneSixVersionFormat;

View File

@ -184,6 +184,13 @@ void MinecraftInstance::loadSpecificSettings()
m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride); m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride);
m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride); m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride);
// Legacy-related options
auto legacySettings = m_settings->registerSetting("OverrideLegacySettings", false);
m_settings->registerOverride(global_settings->getSetting("OnlineFixes"), legacySettings);
auto envSetting = m_settings->registerSetting("OverrideEnv", false);
m_settings->registerOverride(global_settings->getSetting("Env"), envSetting);
m_settings->set("InstanceType", "OneSix"); m_settings->set("InstanceType", "OneSix");
} }
@ -513,20 +520,28 @@ QStringList MinecraftInstance::javaArguments()
args << "-Duser.language=en"; args << "-Duser.language=en";
if (javaVersion.isModular() && shouldApplyOnlineFixes())
// allow reflective access to java.net - required by the skin fix
args << "--add-opens"
<< "java.base/java.net=ALL-UNNAMED";
return args; return args;
} }
QString MinecraftInstance::getLauncher() QString MinecraftInstance::getLauncher()
{ {
auto profile = m_components->getProfile();
// use legacy launcher if the traits are set // use legacy launcher if the traits are set
if (profile->getTraits().contains("legacyLaunch") || profile->getTraits().contains("alphaLaunch")) if (traits().contains("legacyLaunch") || traits().contains("alphaLaunch"))
return "legacy"; return "legacy";
return "standard"; return "standard";
} }
bool MinecraftInstance::shouldApplyOnlineFixes()
{
return traits().contains("legacyServices") && settings()->get("OnlineFixes").toBool();
}
QMap<QString, QString> MinecraftInstance::getVariables() QMap<QString, QString> MinecraftInstance::getVariables()
{ {
QMap<QString, QString> out; QMap<QString, QString> out;
@ -536,6 +551,7 @@ QMap<QString, QString> MinecraftInstance::getVariables()
out.insert("INST_MC_DIR", QDir::toNativeSeparators(QDir(gameRoot()).absolutePath())); out.insert("INST_MC_DIR", QDir::toNativeSeparators(QDir(gameRoot()).absolutePath()));
out.insert("INST_JAVA", settings()->get("JavaPath").toString()); out.insert("INST_JAVA", settings()->get("JavaPath").toString());
out.insert("INST_JAVA_ARGS", javaArguments().join(' ')); out.insert("INST_JAVA_ARGS", javaArguments().join(' '));
out.insert("NO_COLOR", "1");
return out; return out;
} }
@ -549,6 +565,22 @@ QProcessEnvironment MinecraftInstance::createEnvironment()
for (auto it = variables.begin(); it != variables.end(); ++it) { for (auto it = variables.begin(); it != variables.end(); ++it) {
env.insert(it.key(), it.value()); env.insert(it.key(), it.value());
} }
// custom env
auto insertEnv = [&env](QMap<QString, QVariant> envMap) {
if (envMap.isEmpty())
return;
for (auto iter = envMap.begin(); iter != envMap.end(); iter++)
env.insert(iter.key(), iter.value().toString());
};
bool overrideEnv = settings()->get("OverrideEnv").toBool();
if (!overrideEnv)
insertEnv(APPLICATION->settings()->get("Env").toMap());
else
insertEnv(settings()->get("Env").toMap());
return env; return env;
} }
@ -559,16 +591,27 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
if (settings()->get("EnableMangoHud").toBool() && APPLICATION->capabilities() & Application::SupportsMangoHud) { if (settings()->get("EnableMangoHud").toBool() && APPLICATION->capabilities() & Application::SupportsMangoHud) {
auto preloadList = env.value("LD_PRELOAD").split(QLatin1String(":")); QStringList preloadList;
auto libPaths = env.value("LD_LIBRARY_PATH").split(QLatin1String(":")); if (auto value = env.value("LD_PRELOAD"); !value.isEmpty())
preloadList = value.split(QLatin1String(":"));
QStringList libPaths;
if (auto value = env.value("LD_LIBRARY_PATH"); !value.isEmpty())
libPaths = value.split(QLatin1String(":"));
auto mangoHudLibString = MangoHud::getLibraryString(); auto mangoHudLibString = MangoHud::getLibraryString();
if (!mangoHudLibString.isEmpty()) { if (!mangoHudLibString.isEmpty()) {
QFileInfo mangoHudLib(mangoHudLibString); QFileInfo mangoHudLib(mangoHudLibString);
QString libPath = mangoHudLib.absolutePath();
auto appendLib = [libPath, &preloadList](QString fileName) {
if (QFileInfo(FS::PathCombine(libPath, fileName)).exists())
preloadList << fileName;
};
// dlsym variant is only needed for OpenGL and not included in the vulkan layer // dlsym variant is only needed for OpenGL and not included in the vulkan layer
preloadList << "libMangoHud_dlsym.so" << mangoHudLib.fileName(); appendLib("libMangoHud_dlsym.so");
libPaths << mangoHudLib.absolutePath(); appendLib("libMangoHud_opengl.so");
appendLib(mangoHudLib.fileName());
libPaths << libPath;
} }
env.insert("LD_PRELOAD", preloadList.join(QLatin1String(":"))); env.insert("LD_PRELOAD", preloadList.join(QLatin1String(":")));
@ -585,7 +628,6 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
env.insert("__GLX_VENDOR_LIBRARY_NAME", "nvidia"); env.insert("__GLX_VENDOR_LIBRARY_NAME", "nvidia");
} }
#endif #endif
return env; return env;
} }
@ -698,7 +740,7 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS
{ {
QString windowParams; QString windowParams;
if (settings()->get("LaunchMaximized").toBool()) if (settings()->get("LaunchMaximized").toBool())
windowParams = "max"; windowParams = "maximized";
else else
windowParams = windowParams =
QString("%1x%2").arg(settings()->get("MinecraftWinWidth").toInt()).arg(settings()->get("MinecraftWinHeight").toInt()); QString("%1x%2").arg(settings()->get("MinecraftWinWidth").toInt()).arg(settings()->get("MinecraftWinHeight").toInt());
@ -706,6 +748,19 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS
launchScript += "windowParams " + windowParams + "\n"; launchScript += "windowParams " + windowParams + "\n";
} }
// launcher info
{
launchScript += "launcherBrand " + BuildConfig.LAUNCHER_NAME + "\n";
launchScript += "launcherVersion " + BuildConfig.printableVersionString() + "\n";
}
// instance info
{
launchScript += "instanceName " + name() + "\n";
launchScript += "instanceIconKey " + name() + "\n";
launchScript += "instanceIconPath icon.png\n"; // we already save a copy here
}
// legacy auth // legacy auth
if (session) { if (session) {
launchScript += "userName " + session->player_name + "\n"; launchScript += "userName " + session->player_name + "\n";
@ -716,6 +771,9 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS
launchScript += "traits " + trait + "\n"; launchScript += "traits " + trait + "\n";
} }
if (shouldApplyOnlineFixes())
launchScript += "onlineFixes true\n";
launchScript += "launcher " + getLauncher() + "\n"; launchScript += "launcher " + getLauncher() + "\n";
// qDebug() << "Generated launch script:" << launchScript; // qDebug() << "Generated launch script:" << launchScript;

View File

@ -129,6 +129,7 @@ class MinecraftInstance : public BaseInstance {
/// get arguments passed to java /// get arguments passed to java
QStringList javaArguments(); QStringList javaArguments();
QString getLauncher(); QString getLauncher();
bool shouldApplyOnlineFixes();
/// get variables for launch command variable substitution/environment /// get variables for launch command variable substitution/environment
QMap<QString, QString> getVariables() override; QMap<QString, QString> getVariables() override;
@ -173,4 +174,4 @@ class MinecraftInstance : public BaseInstance {
mutable std::shared_ptr<GameOptions> m_game_options; mutable std::shared_ptr<GameOptions> m_game_options;
}; };
typedef std::shared_ptr<MinecraftInstance> MinecraftInstancePtr; using MinecraftInstancePtr = std::shared_ptr<MinecraftInstance>;

View File

@ -5,7 +5,7 @@
struct MojangDownloadInfo { struct MojangDownloadInfo {
// types // types
typedef std::shared_ptr<MojangDownloadInfo> Ptr; using Ptr = std::shared_ptr<MojangDownloadInfo>;
// data // data
/// Local filesystem path. WARNING: not used, only here so we can pass through mojang files unmolested! /// Local filesystem path. WARNING: not used, only here so we can pass through mojang files unmolested!
@ -23,7 +23,7 @@ struct MojangLibraryDownloadInfo {
MojangLibraryDownloadInfo() {} MojangLibraryDownloadInfo() {}
// types // types
typedef std::shared_ptr<MojangLibraryDownloadInfo> Ptr; using Ptr = std::shared_ptr<MojangLibraryDownloadInfo>;
// methods // methods
MojangDownloadInfo* getDownloadInfo(QString classifier) MojangDownloadInfo* getDownloadInfo(QString classifier)
@ -42,7 +42,7 @@ struct MojangLibraryDownloadInfo {
struct MojangAssetIndexInfo : public MojangDownloadInfo { struct MojangAssetIndexInfo : public MojangDownloadInfo {
// types // types
typedef std::shared_ptr<MojangAssetIndexInfo> Ptr; using Ptr = std::shared_ptr<MojangAssetIndexInfo>;
// methods // methods
MojangAssetIndexInfo() {} MojangAssetIndexInfo() {}

View File

@ -9,7 +9,7 @@
class OneSixVersionFormat { class OneSixVersionFormat {
public: public:
// version files / profile patches // version files / profile patches
static VersionFilePtr versionFileFromJson(const QJsonDocument& doc, const QString& filename, const bool requireOrder); static VersionFilePtr versionFileFromJson(const QJsonDocument& doc, const QString& filename, bool requireOrder);
static QJsonDocument versionFileToJson(const VersionFilePtr& patch); static QJsonDocument versionFileToJson(const VersionFilePtr& patch);
// libraries // libraries

View File

@ -430,7 +430,7 @@ bool PackProfile::remove(const int index)
return true; return true;
} }
bool PackProfile::remove(const QString id) bool PackProfile::remove(const QString& id)
{ {
int i = 0; int i = 0;
for (auto patch : d->components) { for (auto patch : d->components) {

View File

@ -89,13 +89,13 @@ class PackProfile : public QAbstractListModel {
enum MoveDirection { MoveUp, MoveDown }; enum MoveDirection { MoveUp, MoveDown };
/// move component file # up or down the list /// move component file # up or down the list
void move(const int index, const MoveDirection direction); void move(int index, MoveDirection direction);
/// remove component file # - including files/records /// remove component file # - including files/records
bool remove(const int index); bool remove(int index);
/// remove component file by id - including files/records /// remove component file by id - including files/records
bool remove(const QString id); bool remove(const QString& id);
bool customize(int index); bool customize(int index);

View File

@ -140,7 +140,7 @@ VersionFilePtr parseJsonFile(const QFileInfo& fileInfo, const bool requireOrder)
return guardedParseJson(doc, fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), requireOrder); return guardedParseJson(doc, fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), requireOrder);
} }
bool saveJsonFile(const QJsonDocument doc, const QString& filename) bool saveJsonFile(const QJsonDocument& doc, const QString& filename)
{ {
auto data = doc.toJson(); auto data = doc.toJson();
QSaveFile jsonFile(filename); QSaveFile jsonFile(filename);

View File

@ -38,7 +38,7 @@
#include "VersionFile.h" #include "VersionFile.h"
namespace ProfileUtils { namespace ProfileUtils {
typedef QStringList PatchOrder; using PatchOrder = QStringList;
/// Read and parse a OneSix format order file /// Read and parse a OneSix format order file
bool readOverrideOrders(QString path, PatchOrder& order); bool readOverrideOrders(QString path, PatchOrder& order);
@ -47,10 +47,10 @@ bool readOverrideOrders(QString path, PatchOrder& order);
bool writeOverrideOrders(QString path, const PatchOrder& order); bool writeOverrideOrders(QString path, const PatchOrder& order);
/// Parse a version file in JSON format /// Parse a version file in JSON format
VersionFilePtr parseJsonFile(const QFileInfo& fileInfo, const bool requireOrder); VersionFilePtr parseJsonFile(const QFileInfo& fileInfo, bool requireOrder);
/// Save a JSON file (in any format) /// Save a JSON file (in any format)
bool saveJsonFile(const QJsonDocument doc, const QString& filename); bool saveJsonFile(const QJsonDocument& doc, const QString& filename);
/// Remove LWJGL from a patch file. This is applied to all Mojang-like profile files. /// Remove LWJGL from a patch file. This is applied to all Mojang-like profile files.
void removeLwjglFromPatch(VersionFilePtr patch); void removeLwjglFromPatch(VersionFilePtr patch);

View File

@ -283,9 +283,15 @@ QVariant AccountList::data(const QModelIndex& index, int role) const
return account->accountDisplayString(); return account->accountDisplayString();
case TypeColumn: { case TypeColumn: {
auto typeStr = account->typeString(); switch (account->accountType()) {
typeStr[0] = typeStr[0].toUpper(); case AccountType::MSA: {
return typeStr; return tr("MSA", "Account type");
}
case AccountType::Offline: {
return tr("Offline", "Account type");
}
}
return tr("Unknown", "Account type");
} }
case StatusColumn: { case StatusColumn: {

View File

@ -75,7 +75,7 @@ class AccountList : public QAbstractListModel {
virtual Qt::ItemFlags flags(const QModelIndex& index) const override; virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
virtual bool setData(const QModelIndex& index, const QVariant& value, int role) override; virtual bool setData(const QModelIndex& index, const QVariant& value, int role) override;
void addAccount(const MinecraftAccountPtr account); void addAccount(MinecraftAccountPtr account);
void removeAccount(QModelIndex index); void removeAccount(QModelIndex index);
int findAccountByProfileId(const QString& profileId) const; int findAccountByProfileId(const QString& profileId) const;
MinecraftAccountPtr getAccountByProfileName(const QString& profileName) const; MinecraftAccountPtr getAccountByProfileName(const QString& profileName) const;

View File

@ -43,4 +43,4 @@ struct AuthSession {
bool demo = false; bool demo = false;
}; };
typedef std::shared_ptr<AuthSession> AuthSessionPtr; using AuthSessionPtr = std::shared_ptr<AuthSession>;

View File

@ -52,6 +52,7 @@
#include "flows/MSA.h" #include "flows/MSA.h"
#include "flows/Offline.h" #include "flows/Offline.h"
#include "minecraft/auth/AccountData.h"
MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent)
{ {
@ -185,7 +186,7 @@ void MinecraftAccount::authFailed(QString reason)
// NOTE: this doesn't do much. There was an error of some sort. // NOTE: this doesn't do much. There was an error of some sort.
} break; } break;
case AccountTaskState::STATE_FAILED_HARD: { case AccountTaskState::STATE_FAILED_HARD: {
if (isMSA()) { if (accountType() == AccountType::MSA) {
data.msaToken.token = QString(); data.msaToken.token = QString();
data.msaToken.refresh_token = QString(); data.msaToken.refresh_token = QString();
data.msaToken.validity = Katabasis::Validity::None; data.msaToken.validity = Katabasis::Validity::None;

View File

@ -54,7 +54,7 @@ class Task;
class AccountTask; class AccountTask;
class MinecraftAccount; class MinecraftAccount;
typedef shared_qobject_ptr<MinecraftAccount> MinecraftAccountPtr; using MinecraftAccountPtr = shared_qobject_ptr<MinecraftAccount>;
Q_DECLARE_METATYPE(MinecraftAccountPtr) Q_DECLARE_METATYPE(MinecraftAccountPtr)
/** /**
@ -118,9 +118,7 @@ class MinecraftAccount : public QObject, public Usable {
bool isActive() const; bool isActive() const;
bool isMSA() const { return data.type == AccountType::MSA; } [[nodiscard]] AccountType accountType() const noexcept { return data.type; }
bool isOffline() const { return data.type == AccountType::Offline; }
bool ownsMinecraft() const { return data.minecraftEntitlement.ownsMinecraft; } bool ownsMinecraft() const { return data.minecraftEntitlement.ownsMinecraft; }

View File

@ -105,6 +105,17 @@ void LauncherPartLaunch::executeTask()
auto instance = m_parent->instance(); auto instance = m_parent->instance();
std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance); std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
QString legacyJarPath;
if (minecraftInstance->getLauncher() == "legacy" || minecraftInstance->shouldApplyOnlineFixes()) {
legacyJarPath = APPLICATION->getJarPath("NewLaunchLegacy.jar");
if (legacyJarPath.isEmpty()) {
const char* reason = QT_TR_NOOP("Legacy launcher library could not be found. Please check your installation.");
emit logLine(tr(reason), MessageLevel::Fatal);
emitFailed(tr(reason));
return;
}
}
m_launchScript = minecraftInstance->createLaunchScript(m_session, m_serverToJoin); m_launchScript = minecraftInstance->createLaunchScript(m_session, m_serverToJoin);
QStringList args = minecraftInstance->javaArguments(); QStringList args = minecraftInstance->javaArguments();
QString allArgs = args.join(", "); QString allArgs = args.join(", ");
@ -120,6 +131,9 @@ void LauncherPartLaunch::executeTask()
auto classPath = minecraftInstance->getClassPath(); auto classPath = minecraftInstance->getClassPath();
classPath.prepend(jarPath); classPath.prepend(jarPath);
if (!legacyJarPath.isEmpty())
classPath.prepend(legacyJarPath);
auto natPath = minecraftInstance->getNativePath(); auto natPath = minecraftInstance->getNativePath();
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
if (!fitsInLocal8bit(natPath)) { if (!fitsInLocal8bit(natPath)) {

View File

@ -26,4 +26,4 @@ struct MinecraftServerTarget {
static MinecraftServerTarget parse(const QString& fullAddress); static MinecraftServerTarget parse(const QString& fullAddress);
}; };
typedef std::shared_ptr<MinecraftServerTarget> MinecraftServerTargetPtr; using MinecraftServerTargetPtr = std::shared_ptr<MinecraftServerTarget>;

View File

@ -37,9 +37,9 @@
#include "ModFolderModel.h" #include "ModFolderModel.h"
#include <FileSystem.h> #include <FileSystem.h>
#include <qheaderview.h>
#include <QDebug> #include <QDebug>
#include <QFileSystemWatcher> #include <QFileSystemWatcher>
#include <QHeaderView>
#include <QIcon> #include <QIcon>
#include <QMimeData> #include <QMimeData>
#include <QString> #include <QString>
@ -65,8 +65,8 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool
m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider" }); m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider" });
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider") }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider") });
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER }; m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER };
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch,
QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive };
m_columnsHideable = { false, true, false, true, true, true }; m_columnsHideable = { false, true, false, true, true, true };
} }
@ -131,6 +131,11 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const
} }
return {}; return {};
} }
case Qt::SizeHintRole:
if (column == ImageColumn) {
return QSize(32, 32);
}
return {};
case Qt::CheckStateRole: case Qt::CheckStateRole:
switch (column) { switch (column) {
case ActiveColumn: case ActiveColumn:

View File

@ -4,6 +4,7 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug> #include <QDebug>
#include <QFileInfo> #include <QFileInfo>
#include <QHeaderView>
#include <QIcon> #include <QIcon>
#include <QMenu> #include <QMenu>
#include <QMimeData> #include <QMimeData>
@ -45,7 +46,7 @@ ResourceFolderModel::~ResourceFolderModel()
QCoreApplication::processEvents(); QCoreApplication::processEvents();
} }
bool ResourceFolderModel::startWatching(const QStringList paths) bool ResourceFolderModel::startWatching(const QStringList& paths)
{ {
if (m_is_watching) if (m_is_watching)
return false; return false;
@ -64,7 +65,7 @@ bool ResourceFolderModel::startWatching(const QStringList paths)
return m_is_watching; return m_is_watching;
} }
bool ResourceFolderModel::stopWatching(const QStringList paths) bool ResourceFolderModel::stopWatching(const QStringList& paths)
{ {
if (!m_is_watching) if (!m_is_watching)
return false; return false;
@ -460,9 +461,9 @@ bool ResourceFolderModel::setData(const QModelIndex& index, [[maybe_unused]] con
if (role == Qt::CheckStateRole) { if (role == Qt::CheckStateRole) {
if (m_instance != nullptr && m_instance->isRunning()) { if (m_instance != nullptr && m_instance->isRunning()) {
auto response = auto response =
CustomMessageBox::selectable(nullptr, "Confirm toggle", CustomMessageBox::selectable(nullptr, tr("Confirm toggle"),
"If you enable/disable this resource while the game is running it may crash your game.\n" tr("If you enable/disable this resource while the game is running it may crash your game.\n"
"Are you sure you want to do this?", "Are you sure you want to do this?"),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec(); ->exec();
@ -516,36 +517,22 @@ void ResourceFolderModel::setupHeaderAction(QAction* act, int column)
act->setText(columnNames().at(column)); act->setText(columnNames().at(column));
} }
void ResourceFolderModel::saveHiddenColumn(int column, bool hidden) void ResourceFolderModel::saveColumns(QTreeView* tree)
{ {
auto const setting_name = QString("UI/%1_Page/HiddenColumns").arg(id()); auto const setting_name = QString("UI/%1_Page/Columns").arg(id());
auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name) auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name)
: m_instance->settings()->registerSetting(setting_name); : m_instance->settings()->registerSetting(setting_name);
auto hiddenColumns = setting->get().toStringList(); setting->set(tree->header()->saveState());
auto name = columnNames(false).at(column);
auto index = hiddenColumns.indexOf(name);
if (index >= 0 && !hidden) {
hiddenColumns.removeAt(index);
} else if (index < 0 && hidden) {
hiddenColumns.append(name);
}
setting->set(hiddenColumns);
} }
void ResourceFolderModel::loadHiddenColumns(QTreeView* tree) void ResourceFolderModel::loadColumns(QTreeView* tree)
{ {
auto const setting_name = QString("UI/%1_Page/HiddenColumns").arg(id()); auto const setting_name = QString("UI/%1_Page/Columns").arg(id());
auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name) auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name)
: m_instance->settings()->registerSetting(setting_name); : m_instance->settings()->registerSetting(setting_name);
auto hiddenColumns = setting->get().toStringList(); tree->header()->restoreState(setting->get().toByteArray());
auto col_names = columnNames(false);
for (auto col_name : hiddenColumns) {
auto index = col_names.indexOf(col_name);
if (index >= 0)
tree->setColumnHidden(index, true);
}
} }
QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree) QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree)
@ -570,7 +557,7 @@ QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree)
if (m_column_resize_modes.at(c) == QHeaderView::ResizeToContents) if (m_column_resize_modes.at(c) == QHeaderView::ResizeToContents)
tree->resizeColumnToContents(c); tree->resizeColumnToContents(c);
} }
saveHiddenColumn(col, !toggled); saveColumns(tree);
}); });
menu->addAction(act); menu->addAction(act);

View File

@ -39,14 +39,14 @@ class ResourceFolderModel : public QAbstractListModel {
* Returns whether starting to watch all the paths was successful. * Returns whether starting to watch all the paths was successful.
* If one or more fails, it returns false. * If one or more fails, it returns false.
*/ */
bool startWatching(const QStringList paths); bool startWatching(const QStringList& paths);
/** Stops watching the paths for changes. /** Stops watching the paths for changes.
* *
* Returns whether stopping to watch all the paths was successful. * Returns whether stopping to watch all the paths was successful.
* If one or more fails, it returns false. * If one or more fails, it returns false.
*/ */
bool stopWatching(const QStringList paths); bool stopWatching(const QStringList& paths);
/* Helper methods for subclasses, using a predetermined list of paths. */ /* Helper methods for subclasses, using a predetermined list of paths. */
virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); } virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); }
@ -117,8 +117,8 @@ class ResourceFolderModel : public QAbstractListModel {
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
void setupHeaderAction(QAction* act, int column); void setupHeaderAction(QAction* act, int column);
void saveHiddenColumn(int column, bool hidden); void saveColumns(QTreeView* tree);
void loadHiddenColumns(QTreeView* tree); void loadColumns(QTreeView* tree);
QMenu* createHeaderContextMenu(QTreeView* tree); QMenu* createHeaderContextMenu(QTreeView* tree);
/** This creates a proxy model to filter / sort the model for a UI. /** This creates a proxy model to filter / sort the model for a UI.
@ -201,8 +201,7 @@ class ResourceFolderModel : public QAbstractListModel {
QList<SortType> m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE }; QList<SortType> m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE };
QStringList m_column_names = { "Enable", "Name", "Last Modified" }; QStringList m_column_names = { "Enable", "Name", "Last Modified" };
QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified") }; QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified") };
QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Stretch, QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive };
QHeaderView::ResizeToContents };
QList<bool> m_columnsHideable = { false, false, true }; QList<bool> m_columnsHideable = { false, false, true };
QDir m_dir; QDir m_dir;

View File

@ -52,8 +52,8 @@ ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstanc
m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified" }); m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified" });
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified") }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified") });
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE }; m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE };
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive,
QHeaderView::ResizeToContents }; QHeaderView::Interactive };
m_columnsHideable = { false, true, false, true, true }; m_columnsHideable = { false, true, false, true, true };
} }
@ -117,6 +117,11 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
} }
return m_resources[row]->internal_id(); return m_resources[row]->internal_id();
} }
case Qt::SizeHintRole:
if (column == ImageColumn) {
return QSize(32, 32);
}
return {};
case Qt::CheckStateRole: case Qt::CheckStateRole:
switch (column) { switch (column) {
case ActiveColumn: case ActiveColumn:

View File

@ -47,8 +47,7 @@ TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance*
m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified" }); m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified" });
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified") }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified") });
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE }; m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE };
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive };
QHeaderView::ResizeToContents };
m_columnsHideable = { false, true, false, true }; m_columnsHideable = { false, true, false, true };
} }
@ -104,6 +103,11 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const
} }
return {}; return {};
} }
case Qt::SizeHintRole:
if (column == ImageColumn) {
return QSize(32, 32);
}
return {};
case Qt::CheckStateRole: case Qt::CheckStateRole:
if (column == ActiveColumn) { if (column == ActiveColumn) {
return m_resources[row]->enabled() ? Qt::Checked : Qt::Unchecked; return m_resources[row]->enabled() ? Qt::Checked : Qt::Unchecked;

View File

@ -137,10 +137,11 @@ Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptr<PackDepende
auto provider = pDep->pack->provider == m_flame_provider.name ? m_flame_provider : m_modrinth_provider; auto provider = pDep->pack->provider == m_flame_provider.name ? m_flame_provider : m_modrinth_provider;
auto responseInfo = std::make_shared<QByteArray>(); auto responseInfo = std::make_shared<QByteArray>();
auto info = provider.api->getProject(pDep->pack->addonId.toString(), responseInfo); auto info = provider.api->getProject(pDep->pack->addonId.toString(), responseInfo);
QObject::connect(info.get(), &NetJob::succeeded, [responseInfo, provider, pDep] { QObject::connect(info.get(), &NetJob::succeeded, [this, responseInfo, provider, pDep] {
QJsonParseError parse_error{}; QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(*responseInfo, &parse_error); QJsonDocument doc = QJsonDocument::fromJson(*responseInfo, &parse_error);
if (parse_error.error != QJsonParseError::NoError) { if (parse_error.error != QJsonParseError::NoError) {
removePack(pDep->pack->addonId);
qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset
<< " reason: " << parse_error.errorString(); << " reason: " << parse_error.errorString();
qDebug() << *responseInfo; qDebug() << *responseInfo;
@ -151,6 +152,7 @@ Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptr<PackDepende
: Json::requireObject(doc); : Json::requireObject(doc);
provider.mod->loadIndexedPack(*pDep->pack, obj); provider.mod->loadIndexedPack(*pDep->pack, obj);
} catch (const JSONValidationError& e) { } catch (const JSONValidationError& e) {
removePack(pDep->pack->addonId);
qDebug() << doc; qDebug() << doc;
qWarning() << "Error while reading mod info: " << e.cause(); qWarning() << "Error while reading mod info: " << e.cause();
} }
@ -211,11 +213,13 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
pDep->pack->versionsLoaded = true; pDep->pack->versionsLoaded = true;
} catch (const JSONValidationError& e) { } catch (const JSONValidationError& e) {
removePack(dep.addonId);
qDebug() << doc; qDebug() << doc;
qWarning() << "Error while reading mod version: " << e.cause(); qWarning() << "Error while reading mod version: " << e.cause();
return; return;
} }
if (level == 0) { if (level == 0) {
removePack(dep.addonId);
qWarning() << "Dependency cycle exceeded"; qWarning() << "Dependency cycle exceeded";
return; return;
} }
@ -238,7 +242,7 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
return tasks; return tasks;
} }
void GetModDependenciesTask::removePack(const QVariant addonId) void GetModDependenciesTask::removePack(const QVariant& addonId)
{ {
auto pred = [addonId](const std::shared_ptr<PackDependency>& v) { return v->pack->addonId == addonId; }; auto pred = [addonId](const std::shared_ptr<PackDependency>& v) { return v->pack->addonId == addonId; };
#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)

View File

@ -65,13 +65,13 @@ class GetModDependenciesTask : public SequentialTask {
QHash<QString, QStringList> getRequiredBy(); QHash<QString, QStringList> getRequiredBy();
protected slots: protected slots:
Task::Ptr prepareDependencyTask(const ModPlatform::Dependency&, const ModPlatform::ResourceProvider, int); Task::Ptr prepareDependencyTask(const ModPlatform::Dependency&, ModPlatform::ResourceProvider, int);
QList<ModPlatform::Dependency> getDependenciesForVersion(const ModPlatform::IndexedVersion&, QList<ModPlatform::Dependency> getDependenciesForVersion(const ModPlatform::IndexedVersion&,
const ModPlatform::ResourceProvider providerName); ModPlatform::ResourceProvider providerName);
void prepare(); void prepare();
Task::Ptr getProjectInfoTask(std::shared_ptr<PackDependency> pDep); Task::Ptr getProjectInfoTask(std::shared_ptr<PackDependency> pDep);
ModPlatform::Dependency getOverride(const ModPlatform::Dependency&, const ModPlatform::ResourceProvider providerName); ModPlatform::Dependency getOverride(const ModPlatform::Dependency&, ModPlatform::ResourceProvider providerName);
void removePack(const QVariant addonId); void removePack(const QVariant& addonId);
private: private:
QList<std::shared_ptr<PackDependency>> m_pack_dependencies; QList<std::shared_ptr<PackDependency>> m_pack_dependencies;

View File

@ -688,6 +688,7 @@ bool loadIconFile(const Mod& mod)
return png_invalid(); // icon invalid return png_invalid(); // icon invalid
} }
} }
return false;
} }
case ResourceType::ZIPFILE: { case ResourceType::ZIPFILE: {
QuaZip zip(mod.fileinfo().filePath()); QuaZip zip(mod.fileinfo().filePath());
@ -714,6 +715,7 @@ bool loadIconFile(const Mod& mod)
} else { } else {
return png_invalid(); // could not set icon as current file. return png_invalid(); // could not set icon as current file.
} }
return false;
} }
case ResourceType::LITEMOD: { case ResourceType::LITEMOD: {
return false; // can lightmods even have icons? return false; // can lightmods even have icons?

View File

@ -232,10 +232,9 @@ bool processPackPNG(const ResourcePack& pack)
} else { } else {
return png_invalid(); // pack.png does not exists or is not a valid file. return png_invalid(); // pack.png does not exists or is not a valid file.
} }
return false; // not processed correctly; https://github.com/PrismLauncher/PrismLauncher/issues/1740
} }
case ResourceType::ZIPFILE: { case ResourceType::ZIPFILE: {
Q_ASSERT(pack.type() == ResourceType::ZIPFILE);
QuaZip zip(pack.fileinfo().filePath()); QuaZip zip(pack.fileinfo().filePath());
if (!zip.open(QuaZip::mdUnzip)) if (!zip.open(QuaZip::mdUnzip))
return false; // can't open zip file return false; // can't open zip file
@ -259,6 +258,7 @@ bool processPackPNG(const ResourcePack& pack)
} else { } else {
return png_invalid(); // could not set pack.mcmeta as current file. return png_invalid(); // could not set pack.mcmeta as current file.
} }
return false; // not processed correctly; https://github.com/PrismLauncher/PrismLauncher/issues/1740
} }
default: default:
qWarning() << "Invalid type for resource pack parse task!"; qWarning() << "Invalid type for resource pack parse task!";

View File

@ -186,10 +186,9 @@ bool processPackPNG(const TexturePack& pack)
} else { } else {
return png_invalid(); // pack.png does not exists or is not a valid file. return png_invalid(); // pack.png does not exists or is not a valid file.
} }
return false;
} }
case ResourceType::ZIPFILE: { case ResourceType::ZIPFILE: {
Q_ASSERT(pack.type() == ResourceType::ZIPFILE);
QuaZip zip(pack.fileinfo().filePath()); QuaZip zip(pack.fileinfo().filePath());
if (!zip.open(QuaZip::mdUnzip)) if (!zip.open(QuaZip::mdUnzip))
return false; // can't open zip file return false; // can't open zip file
@ -215,6 +214,7 @@ bool processPackPNG(const TexturePack& pack)
zip.close(); zip.close();
return png_invalid(); // could not set pack.mcmeta as current file. return png_invalid(); // could not set pack.mcmeta as current file.
} }
return false;
} }
default: default:
qWarning() << "Invalid type for resource pack parse task!"; qWarning() << "Invalid type for resource pack parse task!";

View File

@ -4,7 +4,7 @@
#include <QtNetwork/QtNetwork> #include <QtNetwork/QtNetwork>
#include "tasks/Task.h" #include "tasks/Task.h"
typedef shared_qobject_ptr<class SkinDelete> SkinDeletePtr; using SkinDeletePtr = shared_qobject_ptr<class SkinDelete>;
class SkinDelete : public Task { class SkinDelete : public Task {
Q_OBJECT Q_OBJECT

View File

@ -5,7 +5,7 @@
#include <memory> #include <memory>
#include "tasks/Task.h" #include "tasks/Task.h"
typedef shared_qobject_ptr<class SkinUpload> SkinUploadPtr; using SkinUploadPtr = shared_qobject_ptr<class SkinUpload>;
class SkinUpload : public Task { class SkinUpload : public Task {
Q_OBJECT Q_OBJECT

View File

@ -37,6 +37,7 @@
#include "ATLPackInstallTask.h" #include "ATLPackInstallTask.h"
#include <QtConcurrent> #include <QtConcurrent>
#include <algorithm>
#include <quazip/quazip.h> #include <quazip/quazip.h>
@ -50,6 +51,7 @@
#include "minecraft/MinecraftInstance.h" #include "minecraft/MinecraftInstance.h"
#include "minecraft/OneSixVersionFormat.h" #include "minecraft/OneSixVersionFormat.h"
#include "minecraft/PackProfile.h" #include "minecraft/PackProfile.h"
#include "modplatform/atlauncher/ATLPackManifest.h"
#include "net/ChecksumValidator.h" #include "net/ChecksumValidator.h"
#include "settings/INISettingsObject.h" #include "settings/INISettingsObject.h"
@ -57,6 +59,7 @@
#include "Application.h" #include "Application.h"
#include "BuildConfig.h" #include "BuildConfig.h"
#include "ui/dialogs/BlockedModsDialog.h"
namespace ATLauncher { namespace ATLauncher {
@ -717,6 +720,8 @@ void PackInstallTask::downloadMods()
jarmods.clear(); jarmods.clear();
jobPtr.reset(new NetJob(tr("Mod download"), APPLICATION->network())); jobPtr.reset(new NetJob(tr("Mod download"), APPLICATION->network()));
QList<VersionMod> blocked_mods;
for (const auto& mod : m_version.mods) { for (const auto& mod : m_version.mods) {
// skip non-client mods // skip non-client mods
if (!mod.client) if (!mod.client)
@ -731,9 +736,10 @@ void PackInstallTask::downloadMods()
case DownloadType::Server: case DownloadType::Server:
url = BuildConfig.ATL_DOWNLOAD_SERVER_URL + mod.url; url = BuildConfig.ATL_DOWNLOAD_SERVER_URL + mod.url;
break; break;
case DownloadType::Browser: case DownloadType::Browser: {
emitFailed(tr("Unsupported download type: %1").arg(mod.download_raw)); blocked_mods.append(mod);
return; continue;
}
case DownloadType::Direct: case DownloadType::Direct:
url = mod.url; url = mod.url;
break; break;
@ -805,24 +811,86 @@ void PackInstallTask::downloadMods()
modsToCopy[entry->getFullPath()] = path; modsToCopy[entry->getFullPath()] = path;
} }
} }
if (!blocked_mods.isEmpty()) {
QList<BlockedMod> mods;
for (auto mod : blocked_mods) {
BlockedMod blocked_mod;
blocked_mod.name = mod.file;
blocked_mod.websiteUrl = mod.url;
blocked_mod.hash = mod.md5;
blocked_mod.matched = false;
blocked_mod.localPath = "";
mods.append(blocked_mod);
}
qWarning() << "Blocked mods found, displaying mod list";
BlockedModsDialog message_dialog(nullptr, tr("Blocked mods found"),
tr("The following files are not available for download in third party launchers.<br/>"
"You will need to manually download them and add them to the instance."),
mods, "md5");
message_dialog.setModal(true);
if (message_dialog.exec()) {
qDebug() << "Post dialog blocked mods list: " << mods;
for (auto blocked : mods) {
if (!blocked.matched) {
qDebug() << blocked.name << "was not matched to a local file, skipping copy";
continue;
}
auto modIter = std::find_if(blocked_mods.begin(), blocked_mods.end(),
[blocked](const VersionMod& mod) { return mod.url == blocked.websiteUrl; });
if (modIter == blocked_mods.end())
continue;
auto mod = *modIter;
if (mod.type == ModType::Extract || mod.type == ModType::TexturePackExtract || mod.type == ModType::ResourcePackExtract) {
modsToExtract.insert(blocked.localPath, mod);
} else if (mod.type == ModType::Decomp) {
modsToDecomp.insert(blocked.localPath, mod);
} else {
auto relpath = getDirForModType(mod.type, mod.type_raw);
if (relpath == Q_NULLPTR)
continue;
auto path = FS::PathCombine(m_stagingPath, "minecraft", relpath, mod.file);
if (mod.type == ModType::Forge) {
auto ver = getComponentVersion("net.minecraftforge", mod.version);
if (ver) {
componentsToInstall.insert("net.minecraftforge", ver);
continue;
}
qDebug() << "Jarmod: " + path;
jarmods.push_back(path);
}
if (mod.type == ModType::Jar) {
qDebug() << "Jarmod: " + path;
jarmods.push_back(path);
}
modsToCopy[blocked.localPath] = path;
}
}
} else {
emitFailed(tr("Unknown download type: %1").arg("browser"));
return;
}
}
connect(jobPtr.get(), &NetJob::succeeded, this, &PackInstallTask::onModsDownloaded); connect(jobPtr.get(), &NetJob::succeeded, this, &PackInstallTask::onModsDownloaded);
connect(jobPtr.get(), &NetJob::failed, [&](QString reason) { connect(jobPtr.get(), &NetJob::progress, [this](qint64 current, qint64 total) {
abortable = false;
jobPtr.reset();
emitFailed(reason);
});
connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
abortable = true; abortable = true;
setProgress(current, total); setProgress(current, total);
}); });
connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress); connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress);
connect(jobPtr.get(), &NetJob::aborted, [&] { connect(jobPtr.get(), &NetJob::aborted, &PackInstallTask::emitAborted);
abortable = false; connect(jobPtr.get(), &NetJob::failed, &PackInstallTask::emitFailed);
jobPtr.reset();
emitAborted();
});
jobPtr->start(); jobPtr->start();
} }
@ -843,7 +911,7 @@ void PackInstallTask::onModsDownloaded()
QtConcurrent::run(QThreadPool::globalInstance(), this, &PackInstallTask::extractMods, modsToExtract, modsToDecomp, modsToCopy); QtConcurrent::run(QThreadPool::globalInstance(), this, &PackInstallTask::extractMods, modsToExtract, modsToDecomp, modsToCopy);
#endif #endif
connect(&m_modExtractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &PackInstallTask::onModsExtracted); connect(&m_modExtractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &PackInstallTask::onModsExtracted);
connect(&m_modExtractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, [&]() { emitAborted(); }); connect(&m_modExtractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &PackInstallTask::emitAborted);
m_modExtractFutureWatcher.setFuture(m_modExtractFuture); m_modExtractFutureWatcher.setFuture(m_modExtractFuture);
} else { } else {
install(); install();

View File

@ -208,17 +208,15 @@ Task::Ptr FlameAPI::getFile(const QString& addonId, const QString& fileId, std::
return netJob; return netJob;
} }
// https://docs.curseforge.com/?python#tocS_ModsSearchSortField
static QList<ResourceAPI::SortingMethod> s_sorts = { { 1, "Featured", QObject::tr("Sort by Featured") },
{ 2, "Popularity", QObject::tr("Sort by Popularity") },
{ 3, "LastUpdated", QObject::tr("Sort by Last Updated") },
{ 4, "Name", QObject::tr("Sort by Name") },
{ 5, "Author", QObject::tr("Sort by Author") },
{ 6, "TotalDownloads", QObject::tr("Sort by Downloads") },
{ 7, "Category", QObject::tr("Sort by Category") },
{ 8, "GameVersion", QObject::tr("Sort by Game Version") } };
QList<ResourceAPI::SortingMethod> FlameAPI::getSortingMethods() const QList<ResourceAPI::SortingMethod> FlameAPI::getSortingMethods() const
{ {
return s_sorts; // https://docs.curseforge.com/?python#tocS_ModsSearchSortField
return { { 1, "Featured", QObject::tr("Sort by Featured") },
{ 2, "Popularity", QObject::tr("Sort by Popularity") },
{ 3, "LastUpdated", QObject::tr("Sort by Last Updated") },
{ 4, "Name", QObject::tr("Sort by Name") },
{ 5, "Author", QObject::tr("Sort by Author") },
{ 6, "TotalDownloads", QObject::tr("Sort by Downloads") },
{ 7, "Category", QObject::tr("Sort by Category") },
{ 8, "GameVersion", QObject::tr("Sort by Game Version") } };
} }

View File

@ -568,7 +568,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
m_mod_id_resolver.reset(); m_mod_id_resolver.reset();
connect(m_files_job.get(), &NetJob::succeeded, this, [&]() { connect(m_files_job.get(), &NetJob::succeeded, this, [&]() {
m_files_job.reset(); m_files_job.reset();
validateZIPResouces(); validateZIPResources();
}); });
connect(m_files_job.get(), &NetJob::failed, [&](QString reason) { connect(m_files_job.get(), &NetJob::failed, [&](QString reason) {
m_files_job.reset(); m_files_job.reset();
@ -617,7 +617,7 @@ void FlameCreationTask::copyBlockedMods(QList<BlockedMod> const& blocked_mods)
setAbortable(true); setAbortable(true);
} }
void FlameCreationTask::validateZIPResouces() void FlameCreationTask::validateZIPResources()
{ {
qDebug() << "Validating whether resources stored as .zip are in the right place"; qDebug() << "Validating whether resources stored as .zip are in the right place";
for (auto [fileName, targetFolder] : m_ZIP_resources) { for (auto [fileName, targetFolder] : m_ZIP_resources) {
@ -670,8 +670,8 @@ void FlameCreationTask::validateZIPResouces()
validatePath(fileName, targetFolder, "datapacks"); validatePath(fileName, targetFolder, "datapacks");
break; break;
case PackedResourceType::ShaderPack: case PackedResourceType::ShaderPack:
// in theroy flame API can't do this but who knows, that *may* change ? // in theory flame API can't do this but who knows, that *may* change ?
// better to handle it if it *does* occure in the future // better to handle it if it *does* occur in the future
validatePath(fileName, targetFolder, "shaderpacks"); validatePath(fileName, targetFolder, "shaderpacks");
break; break;
case PackedResourceType::WorldSave: case PackedResourceType::WorldSave:

View File

@ -74,7 +74,7 @@ class FlameCreationTask final : public InstanceCreationTask {
void idResolverSucceeded(QEventLoop&); void idResolverSucceeded(QEventLoop&);
void setupDownloadJob(QEventLoop&); void setupDownloadJob(QEventLoop&);
void copyBlockedMods(QList<BlockedMod> const& blocked_mods); void copyBlockedMods(QList<BlockedMod> const& blocked_mods);
void validateZIPResouces(); void validateZIPResources();
QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion); QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion);
private: private:

View File

@ -45,7 +45,7 @@ struct Modpack {
QIcon icon; QIcon icon;
}; };
typedef QList<Modpack> ModpackList; using ModpackList = QList<Modpack>;
Modpack parseDirectory(QString path); Modpack parseDirectory(QString path);

View File

@ -31,7 +31,7 @@ struct Modpack {
QString packCode; QString packCode;
}; };
typedef QList<Modpack> ModpackList; using ModpackList = QList<Modpack>;
} // namespace LegacyFTB } // namespace LegacyFTB

View File

@ -111,14 +111,12 @@ Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, std::shared_ptr<QByteAr
return netJob; return netJob;
} }
// https://docs.modrinth.com/api-spec/#tag/projects/operation/searchProjects
static QList<ResourceAPI::SortingMethod> s_sorts = { { 1, "relevance", QObject::tr("Sort by Relevance") },
{ 2, "downloads", QObject::tr("Sort by Downloads") },
{ 3, "follows", QObject::tr("Sort by Follows") },
{ 4, "newest", QObject::tr("Sort by Last Updated") },
{ 5, "updated", QObject::tr("Sort by Newest") } };
QList<ResourceAPI::SortingMethod> ModrinthAPI::getSortingMethods() const QList<ResourceAPI::SortingMethod> ModrinthAPI::getSortingMethods() const
{ {
return s_sorts; // https://docs.modrinth.com/api-spec/#tag/projects/operation/searchProjects
return { { 1, "relevance", QObject::tr("Sort by Relevance") },
{ 2, "downloads", QObject::tr("Sort by Downloads") },
{ 3, "follows", QObject::tr("Sort by Follows") },
{ 4, "newest", QObject::tr("Sort by Newest") },
{ 5, "updated", QObject::tr("Sort by Last Updated") } };
} }

View File

@ -68,7 +68,7 @@ class ModrinthPackExportTask : public Task {
void collectFiles(); void collectFiles();
void collectHashes(); void collectHashes();
void makeApiRequest(); void makeApiRequest();
void parseApiResponse(const std::shared_ptr<QByteArray> response); void parseApiResponse(std::shared_ptr<QByteArray> response);
void buildZip(); void buildZip();
QByteArray generateIndex(); QByteArray generateIndex();

View File

@ -33,6 +33,6 @@ class TechnicPackProcessor : public QObject {
const QString& instIcon, const QString& instIcon,
const QString& stagingPath, const QString& stagingPath,
const QString& minecraftVersion = QString(), const QString& minecraftVersion = QString(),
const bool isSolder = false); bool isSolder = false);
}; };
} // namespace Technic } // namespace Technic

View File

@ -89,4 +89,4 @@ QNetworkReply* Download::getReply(QNetworkRequest& request)
{ {
return m_network->get(request); return m_network->get(request);
} }
} // namespace Net } // namespace Net

View File

@ -36,11 +36,16 @@
*/ */
#include "NetJob.h" #include "NetJob.h"
#if defined(LAUNCHER_APPLICATION)
#include "Application.h" #include "Application.h"
#endif
NetJob::NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network) NetJob::NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network) : ConcurrentTask(nullptr, job_name), m_network(network)
: ConcurrentTask(nullptr, job_name, APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()), m_network(network) {
{} #if defined(LAUNCHER_APPLICATION)
setMaxConcurrent(APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
#endif
}
auto NetJob::addNetAction(NetAction::Ptr action) -> bool auto NetJob::addNetAction(NetAction::Ptr action) -> bool
{ {

View File

@ -46,6 +46,7 @@
#if defined(LAUNCHER_APPLICATION) #if defined(LAUNCHER_APPLICATION)
#include "Application.h" #include "Application.h"
#endif #endif
#include "BuildConfig.h"
#include "net/NetAction.h" #include "net/NetAction.h"
@ -111,6 +112,8 @@ void NetRequest::executeTask()
m_last_progress_bytes = 0; m_last_progress_bytes = 0;
QNetworkReply* rep = getReply(request); QNetworkReply* rep = getReply(request);
if (rep == nullptr) // it failed
return;
m_reply.reset(rep); m_reply.reset(rep);
connect(rep, &QNetworkReply::downloadProgress, this, &NetRequest::downloadProgress); connect(rep, &QNetworkReply::downloadProgress, this, &NetRequest::downloadProgress);
connect(rep, &QNetworkReply::finished, this, &NetRequest::downloadFinished); connect(rep, &QNetworkReply::finished, this, &NetRequest::downloadFinished);

View File

@ -87,7 +87,7 @@ class NetRequest : public NetAction {
std::unique_ptr<Sink> m_sink; std::unique_ptr<Sink> m_sink;
Options m_options; Options m_options;
typedef const QLoggingCategory& (*logCatFunc)(); using logCatFunc = const QLoggingCategory& (*)();
logCatFunc logCat = taskUploadLogC; logCatFunc logCat = taskUploadLogC;
std::chrono::steady_clock m_clock; std::chrono::steady_clock m_clock;

View File

@ -0,0 +1,39 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.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 "net/HeaderProxy.h"
namespace Net {
class StaticHeaderProxy : public HeaderProxy {
public:
StaticHeaderProxy(QList<HeaderPair> hdrs = {}) : HeaderProxy(), m_hdrs(hdrs){};
virtual ~StaticHeaderProxy() = default;
public:
virtual QList<HeaderPair> headers(const QNetworkRequest&) const override { return m_hdrs; };
void setHeaders(QList<HeaderPair> hdrs) { m_hdrs = hdrs; };
private:
QList<HeaderPair> m_hdrs;
};
} // namespace Net

View File

@ -51,4 +51,4 @@ class NewsEntry : public QObject {
QString link; QString link;
}; };
typedef std::shared_ptr<NewsEntry> NewsEntryPtr; using NewsEntryPtr = std::shared_ptr<NewsEntry>;

View File

@ -4,7 +4,7 @@
class IPathMatcher { class IPathMatcher {
public: public:
typedef std::shared_ptr<IPathMatcher> Ptr; using Ptr = std::shared_ptr<IPathMatcher>;
public: public:
virtual ~IPathMatcher() {} virtual ~IPathMatcher() {}

View File

@ -9,6 +9,7 @@
<file>scalable/copy.svg</file> <file>scalable/copy.svg</file>
<file>scalable/coremods.svg</file> <file>scalable/coremods.svg</file>
<file>scalable/custom-commands.svg</file> <file>scalable/custom-commands.svg</file>
<file>scalable/environment-variables.svg</file>
<file>scalable/discord.svg</file> <file>scalable/discord.svg</file>
<file>scalable/externaltools.svg</file> <file>scalable/externaltools.svg</file>
<file>scalable/help.svg</file> <file>scalable/help.svg</file>

Some files were not shown because too many files have changed in this diff Show More