Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into disablemods
This commit is contained in:
commit
80383d237a
5
.clang-tidy
Normal file
5
.clang-tidy
Normal file
@ -0,0 +1,5 @@
|
||||
Checks:
|
||||
- modernize-use-using
|
||||
- readability-avoid-const-params-in-decls
|
||||
|
||||
SystemHeaders: false
|
2
.github/workflows/backport.yml
vendored
2
.github/workflows/backport.yml
vendored
@ -24,7 +24,7 @@ jobs:
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Create backport PRs
|
||||
uses: korthout/backport-action@v1.4.0
|
||||
uses: korthout/backport-action@v2.1.1
|
||||
with:
|
||||
# Config README: https://github.com/korthout/backport-action#backport-action
|
||||
pull_description: |-
|
||||
|
124
.github/workflows/build.yml
vendored
124
.github/workflows/build.yml
vendored
@ -37,44 +37,43 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
|
||||
- os: ubuntu-20.04
|
||||
qt_ver: 5
|
||||
|
||||
- os: ubuntu-20.04
|
||||
qt_ver: 6
|
||||
qt_host: linux
|
||||
qt_arch: ''
|
||||
qt_version: '6.2.4'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
qt_tools: ''
|
||||
qt_arch: ""
|
||||
qt_version: "6.2.4"
|
||||
qt_modules: "qt5compat qtimageformats"
|
||||
qt_tools: ""
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-MinGW-w64"
|
||||
msystem: clang64
|
||||
vcvars_arch: 'amd64_x86'
|
||||
vcvars_arch: "amd64_x86"
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-MSVC"
|
||||
msystem: ''
|
||||
architecture: 'x64'
|
||||
vcvars_arch: 'amd64'
|
||||
msystem: ""
|
||||
architecture: "x64"
|
||||
vcvars_arch: "amd64"
|
||||
qt_ver: 6
|
||||
qt_host: windows
|
||||
qt_arch: ''
|
||||
qt_version: '6.5.2'
|
||||
qt_version: '6.6.0'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
qt_tools: ''
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-MSVC-arm64"
|
||||
msystem: ''
|
||||
architecture: 'arm64'
|
||||
vcvars_arch: 'amd64_arm64'
|
||||
msystem: ""
|
||||
architecture: "arm64"
|
||||
vcvars_arch: "amd64_arm64"
|
||||
qt_ver: 6
|
||||
qt_host: windows
|
||||
qt_arch: 'win64_msvc2019_arm64'
|
||||
qt_version: '6.5.2'
|
||||
qt_version: '6.6.0'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
qt_tools: ''
|
||||
|
||||
@ -84,7 +83,7 @@ jobs:
|
||||
qt_ver: 6
|
||||
qt_host: mac
|
||||
qt_arch: ''
|
||||
qt_version: '6.5.2'
|
||||
qt_version: '6.6.0'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
qt_tools: ''
|
||||
|
||||
@ -93,9 +92,9 @@ jobs:
|
||||
macosx_deployment_target: 10.13
|
||||
qt_ver: 5
|
||||
qt_host: mac
|
||||
qt_version: '5.15.2'
|
||||
qt_modules: ''
|
||||
qt_tools: ''
|
||||
qt_version: "5.15.2"
|
||||
qt_modules: ""
|
||||
qt_tools: ""
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
@ -115,9 +114,9 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
submodules: "true"
|
||||
|
||||
- name: 'Setup MSYS2'
|
||||
- name: "Setup MSYS2"
|
||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
@ -157,7 +156,7 @@ jobs:
|
||||
path: '${{ github.workspace }}\.ccache'
|
||||
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
${{ matrix.os }}-mingw-w64-ccache
|
||||
${{ matrix.os }}-mingw-w64-ccache
|
||||
|
||||
- name: Setup ccache (Windows MinGW-w64)
|
||||
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
||||
@ -202,35 +201,35 @@ jobs:
|
||||
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
aqtversion: '==3.1.*'
|
||||
py7zrversion: '>=0.20.2'
|
||||
version: ${{ matrix.qt_version }}
|
||||
host: 'windows'
|
||||
target: 'desktop'
|
||||
arch: ''
|
||||
modules: ${{ matrix.qt_modules }}
|
||||
tools: ${{ matrix.qt_tools }}
|
||||
cache: ${{ inputs.is_qt_cached }}
|
||||
cache-key-prefix: host-qt-arm64-windows
|
||||
dir: ${{ github.workspace }}\HostQt
|
||||
set-env: false
|
||||
aqtversion: "==3.1.*"
|
||||
py7zrversion: ">=0.20.2"
|
||||
version: ${{ matrix.qt_version }}
|
||||
host: "windows"
|
||||
target: "desktop"
|
||||
arch: ""
|
||||
modules: ${{ matrix.qt_modules }}
|
||||
tools: ${{ matrix.qt_tools }}
|
||||
cache: ${{ inputs.is_qt_cached }}
|
||||
cache-key-prefix: host-qt-arm64-windows
|
||||
dir: ${{ github.workspace }}\HostQt
|
||||
set-env: false
|
||||
|
||||
- name: Install Qt (macOS, Linux, Qt 6 & Windows MSVC)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS' || (runner.os == 'Windows' && matrix.msystem == '')
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
aqtversion: '==3.1.*'
|
||||
py7zrversion: '>=0.20.2'
|
||||
version: ${{ matrix.qt_version }}
|
||||
host: ${{ matrix.qt_host }}
|
||||
target: 'desktop'
|
||||
arch: ${{ matrix.qt_arch }}
|
||||
modules: ${{ matrix.qt_modules }}
|
||||
tools: ${{ matrix.qt_tools }}
|
||||
cache: ${{ inputs.is_qt_cached }}
|
||||
aqtversion: "==3.1.*"
|
||||
py7zrversion: ">=0.20.2"
|
||||
version: ${{ matrix.qt_version }}
|
||||
host: ${{ matrix.qt_host }}
|
||||
target: "desktop"
|
||||
arch: ${{ matrix.qt_arch }}
|
||||
modules: ${{ matrix.qt_modules }}
|
||||
tools: ${{ matrix.qt_tools }}
|
||||
cache: ${{ inputs.is_qt_cached }}
|
||||
|
||||
- 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
|
||||
with:
|
||||
vsversion: 2022
|
||||
@ -271,12 +270,12 @@ jobs:
|
||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -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)
|
||||
if: runner.os == 'Windows' && matrix.msystem == ''
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON
|
||||
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)
|
||||
if ("${{ env.CCACHE_VAR }}")
|
||||
{
|
||||
@ -291,7 +290,7 @@ jobs:
|
||||
- name: Configure CMake (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -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
|
||||
@ -331,7 +330,7 @@ jobs:
|
||||
- name: Test (Windows MSVC)
|
||||
if: runner.os == 'Windows' && matrix.msystem == '' && matrix.architecture != 'arm64'
|
||||
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
|
||||
@ -373,7 +372,7 @@ jobs:
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }}
|
||||
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)
|
||||
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
|
||||
|
||||
|
||||
- name: Fetch codesign certificate (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: |
|
||||
echo '${{ secrets.WINDOWS_CODESIGN_CERT }}' | base64 --decode > codesign.pfx
|
||||
|
||||
@ -403,7 +401,7 @@ jobs:
|
||||
if (Get-Content ./codesign.pfx){
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
# We ship the exact same executable for portable and non-portable editions, so signing just once is fine
|
||||
SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com 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 {
|
||||
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||
}
|
||||
@ -495,15 +493,7 @@ jobs:
|
||||
export LD_LIBRARY_PATH
|
||||
|
||||
chmod +x AppImageUpdate-x86_64.AppImage
|
||||
./AppImageUpdate-x86_64.AppImage --appimage-extract
|
||||
|
||||
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
|
||||
cp AppImageUpdate-x86_64.AppImage ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin
|
||||
|
||||
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_KEY=${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||
mkdir -p ~/.gnupg/
|
||||
printf "$GPG_PRIVATE_KEY" | base64 --decode > ~/.gnupg/private.key
|
||||
echo "$GPG_PRIVATE_KEY" > ~/.gnupg/private.key
|
||||
gpg --import ~/.gnupg/private.key
|
||||
else
|
||||
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
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
name: PrismLauncher-${{ runner.os }}-Qt5-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PrismLauncher.tar.gz
|
||||
|
||||
- name: Upload binary tarball (Linux, portable, Qt 5)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||
uses: actions/upload-artifact@v3
|
||||
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
|
||||
|
||||
- name: Upload binary tarball (Linux, Qt 6)
|
||||
@ -587,7 +577,7 @@ jobs:
|
||||
with:
|
||||
name: 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)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||
uses: actions/upload-artifact@v3
|
||||
@ -604,17 +594,17 @@ jobs:
|
||||
flatpak:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: bilelmoussaoui/flatpak-github-actions:kde-5.15-22.08
|
||||
image: bilelmoussaoui/flatpak-github-actions:kde-5.15-23.08
|
||||
options: --privileged
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
if: inputs.build_type == 'Debug'
|
||||
with:
|
||||
submodules: 'true'
|
||||
submodules: "true"
|
||||
- name: Build Flatpak (Linux)
|
||||
if: inputs.build_type == 'Debug'
|
||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
||||
with:
|
||||
bundle: "Prism Launcher.flatpak"
|
||||
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
|
||||
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
|
||||
|
29
.github/workflows/trigger_builds.yml
vendored
29
.github/workflows/trigger_builds.yml
vendored
@ -3,26 +3,25 @@ name: Build Application
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'renovate/**'
|
||||
- "renovate/**"
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**/LICENSE'
|
||||
- 'flake.lock'
|
||||
- 'packages/**'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
- '.markdownlint**'
|
||||
- "**.md"
|
||||
- "**/LICENSE"
|
||||
- "flake.lock"
|
||||
- "packages/**"
|
||||
- ".github/ISSUE_TEMPLATE/**"
|
||||
- ".markdownlint**"
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**/LICENSE'
|
||||
- 'flake.lock'
|
||||
- 'packages/**'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
- '.markdownlint**'
|
||||
- "**.md"
|
||||
- "**/LICENSE"
|
||||
- "flake.lock"
|
||||
- "packages/**"
|
||||
- ".github/ISSUE_TEMPLATE/**"
|
||||
- ".markdownlint**"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
build_debug:
|
||||
name: Build Debug
|
||||
uses: ./.github/workflows/build.yml
|
||||
@ -34,3 +33,5 @@ jobs:
|
||||
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
||||
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||
|
12
.github/workflows/trigger_release.yml
vendored
12
.github/workflows/trigger_release.yml
vendored
@ -17,6 +17,8 @@ jobs:
|
||||
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
||||
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||
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:
|
||||
needs: build_release
|
||||
@ -39,9 +41,9 @@ jobs:
|
||||
run: |
|
||||
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
|
||||
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-Linux-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*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-Linux-Qt5-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ 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.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||
mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
||||
@ -85,8 +87,8 @@ jobs:
|
||||
draft: true
|
||||
prerelease: false
|
||||
files: |
|
||||
PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-x86_64.AppImage
|
||||
PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||
PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||
|
@ -188,8 +188,11 @@ set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_M
|
||||
# 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.")
|
||||
|
||||
# Channel list URL
|
||||
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
|
||||
# Github repo URL with releases for 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
|
||||
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
|
||||
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
|
||||
include(GetGitRevisionDescription)
|
||||
@ -339,6 +347,11 @@ add_subdirectory(program_info)
|
||||
####################################### Install layout #######################################
|
||||
|
||||
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))
|
||||
# Install "portable.txt" if selected component is "portable"
|
||||
|
13
README.md
13
README.md
@ -18,11 +18,18 @@
|
||||
</a>
|
||||
|
||||
- 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
|
||||
|
||||
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**.
|
||||
|
||||
@ -30,7 +37,7 @@ For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**,
|
||||
|
||||
[](https://aur.archlinux.org/packages/prismlauncher-git) [](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [](https://mpr.makedeb.org/packages/prismlauncher-git)<br />[](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [](https://build.opensuse.org/project/show/home:getchoo) [](https://packages.gentoo.org/packages/games-action/prismlauncher)
|
||||
|
||||
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
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <qstringliteral.h>
|
||||
#include "BuildConfig.h"
|
||||
#include <QObject>
|
||||
|
||||
@ -59,8 +60,16 @@ Config::Config()
|
||||
VERSION_MINOR = @Launcher_VERSION_MINOR@;
|
||||
|
||||
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
|
||||
BUILD_ARTIFACT = "@Launcher_BUILD_ARTIFACT@";
|
||||
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_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
|
||||
@ -68,6 +77,8 @@ Config::Config()
|
||||
if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
|
||||
{
|
||||
UPDATER_ENABLED = true;
|
||||
} else if(!UPDATER_GITHUB_REPO.isEmpty() && !BUILD_ARTIFACT.isEmpty()) {
|
||||
UPDATER_ENABLED = true;
|
||||
}
|
||||
|
||||
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
||||
@ -88,10 +99,7 @@ Config::Config()
|
||||
if (GIT_REFSPEC.startsWith("refs/heads/"))
|
||||
{
|
||||
VERSION_CHANNEL = GIT_REFSPEC;
|
||||
VERSION_CHANNEL.remove("refs/heads/");
|
||||
if(!UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty()) {
|
||||
UPDATER_ENABLED = true;
|
||||
}
|
||||
VERSION_CHANNEL.remove("refs/heads/");
|
||||
}
|
||||
else if (!GIT_COMMIT.isEmpty())
|
||||
{
|
||||
@ -136,3 +144,16 @@ QString Config::printableVersionString() const
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -71,11 +71,29 @@ class Config {
|
||||
/// A short string identifying this build's platform or distribution.
|
||||
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
|
||||
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
|
||||
QString UPDATER_BASE;
|
||||
QString UPDATER_GITHUB_REPO;
|
||||
|
||||
/// The public key used to sign releases for the Sparkle updater appcast
|
||||
QString MAC_SPARKLE_PUB_KEY;
|
||||
@ -175,6 +193,18 @@ class Config {
|
||||
* \return The version number in string format (major.minor.revision.build).
|
||||
*/
|
||||
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;
|
||||
|
@ -68,6 +68,8 @@ function(
|
||||
/w14906 # string literal cast to 'LPWSTR'
|
||||
/w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied
|
||||
/permissive- # standards conformance mode for MSVC compiler.
|
||||
|
||||
/we4062 # forbid omitting a possible value of an enum in a switch statement
|
||||
)
|
||||
endif()
|
||||
|
||||
@ -93,6 +95,8 @@ function(
|
||||
# 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
|
||||
-Wno-gnu-zero-variadic-macro-arguments
|
||||
|
||||
-Werror=switch # forbid omitting a possible value of an enum in a switch statement
|
||||
)
|
||||
endif()
|
||||
|
||||
@ -104,6 +108,8 @@ function(
|
||||
-Wduplicated-branches # warn if if / else branches have duplicated code
|
||||
-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
|
||||
|
||||
-Werror=switch # forbid omitting a possible value of an enum in a switch statement
|
||||
)
|
||||
endif()
|
||||
|
||||
@ -128,6 +134,8 @@ function(
|
||||
-Woverloaded-virtual
|
||||
-Wuseless-cast
|
||||
-Wextra-semi
|
||||
|
||||
-Werror=switch # forbid omitting a possible value of an enum in a switch statement
|
||||
)
|
||||
|
||||
target_compile_options(
|
||||
|
30
flake.lock
generated
30
flake.lock
generated
@ -21,11 +21,11 @@
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1696343447,
|
||||
"narHash": "sha256-B2xAZKLkkeRFG5XcHHSXXcP7To9Xzr59KXeZiRf4vdQ=",
|
||||
"lastModified": 1698882062,
|
||||
"narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "c9afaba3dfa4085dbd2ccb38dfade5141e33d9d4",
|
||||
"rev": "8c9fa2545007b49a5db5f650ae91f227672c3877",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -76,11 +76,11 @@
|
||||
"libnbtplusplus": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1690036783,
|
||||
"narHash": "sha256-A5kTgICnx+Qdq3Fir/bKTfdTt/T1NQP2SC+nhN1ENug=",
|
||||
"lastModified": 1699286814,
|
||||
"narHash": "sha256-yy0q+bky80LtK1GWzz7qpM+aAGrOqLuewbid8WT1ilk=",
|
||||
"owner": "PrismLauncher",
|
||||
"repo": "libnbtplusplus",
|
||||
"rev": "a5e8fd52b8bf4ab5d5bcc042b2a247867589985f",
|
||||
"rev": "23b955121b8217c1c348a9ed2483167a6f3ff4ad",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -106,11 +106,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1697009197,
|
||||
"narHash": "sha256-viVRhBTFT8fPJTb1N3brQIpFZnttmwo3JVKNuWRVc3s=",
|
||||
"lastModified": 1700108881,
|
||||
"narHash": "sha256-+Lqybl8kj0+nD/IlAWPPG/RDTa47gff9nbei0u7BntE=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "01441e14af5e29c9d27ace398e6dd0b293e25a54",
|
||||
"rev": "7414e9ee0b3e9903c24d3379f577a417f0aae5f1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -123,11 +123,11 @@
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"dir": "lib",
|
||||
"lastModified": 1696019113,
|
||||
"narHash": "sha256-X3+DKYWJm93DRSdC5M6K5hLqzSya9BjibtBsuARoPco=",
|
||||
"lastModified": 1698611440,
|
||||
"narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "f5892ddac112a1e9b3612c39af1b72987ee5783a",
|
||||
"rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -153,11 +153,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1696846637,
|
||||
"narHash": "sha256-0hv4kbXxci2+pxhuXlVgftj/Jq79VSmtAyvfabCCtYk=",
|
||||
"lastModified": 1700064067,
|
||||
"narHash": "sha256-1ZWNDzhu8UlVCK7+DUN9dVQfiHX1bv6OQP9VxstY/gs=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "42e1b6095ef80a51f79595d9951eb38e91c4e6ca",
|
||||
"rev": "e558068cba67b23b4fbc5537173dbb43748a17e8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
id: org.prismlauncher.PrismLauncher
|
||||
runtime: org.kde.Platform
|
||||
runtime-version: "5.15-22.08"
|
||||
runtime-version: "5.15-23.08"
|
||||
sdk: org.kde.Sdk
|
||||
sdk-extensions:
|
||||
- org.freedesktop.Sdk.Extension.openjdk17
|
||||
@ -113,6 +113,9 @@ modules:
|
||||
version-query: .tag_name
|
||||
url-query: .tarball_url
|
||||
timestamp-query: .published_at
|
||||
# from https://github.com/flathub/net.gaijin.WarThunder/blob/7ea6f7a9f84b9c77150c003a7059dc03f8dcbc7f/gamemode.patch
|
||||
- type: patch
|
||||
path: patches/gamemode.patch
|
||||
cleanup:
|
||||
- /include
|
||||
- /lib/pkgconfig
|
||||
|
12
flatpak/patches/gamemode.patch
Normal file
12
flatpak/patches/gamemode.patch
Normal 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 */
|
@ -1,5 +1,6 @@
|
||||
builds:
|
||||
exclude: []
|
||||
exclude:
|
||||
- "*.x86_64-darwin.*"
|
||||
include:
|
||||
- "checks.x86_64-linux.*"
|
||||
- "devShells.*.*"
|
||||
|
@ -58,6 +58,7 @@
|
||||
#include "ui/pages/global/APIPage.h"
|
||||
#include "ui/pages/global/AccountListPage.h"
|
||||
#include "ui/pages/global/CustomCommandsPage.h"
|
||||
#include "ui/pages/global/EnvironmentVariablesPage.h"
|
||||
#include "ui/pages/global/ExternalToolsPage.h"
|
||||
#include "ui/pages/global/JavaPage.h"
|
||||
#include "ui/pages/global/LanguagePage.h"
|
||||
@ -122,6 +123,7 @@
|
||||
#include <FileSystem.h>
|
||||
#include <LocalPeer.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys.h>
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
@ -130,9 +132,13 @@
|
||||
#include "gamemode_client.h"
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_MAC) && defined(SPARKLE_ENABLED)
|
||||
#if defined(Q_OS_MAC)
|
||||
#if defined(SPARKLE_ENABLED)
|
||||
#include "updater/MacSparkleUpdater.h"
|
||||
#endif
|
||||
#else
|
||||
#include "updater/PrismExternalUpdater.h"
|
||||
#endif
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
#include "WindowsConsole.h"
|
||||
@ -164,6 +170,34 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt
|
||||
|
||||
} // 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)
|
||||
{
|
||||
#if defined Q_OS_WIN32
|
||||
@ -296,6 +330,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
.arg(dataPath));
|
||||
return;
|
||||
}
|
||||
m_dataPath = dataPath;
|
||||
|
||||
/*
|
||||
* 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() << "Platform : " << BuildConfig.BUILD_PLATFORM;
|
||||
qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
|
||||
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()) {
|
||||
qDebug() << "Work dir before adjustment : " << origcwdPath;
|
||||
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("IgnoreJavaWizard", false);
|
||||
|
||||
// Legacy settings
|
||||
m_settings->registerSetting("OnlineFixes", false);
|
||||
|
||||
// Native library workarounds
|
||||
m_settings->registerSetting("UseNativeOpenAL", false);
|
||||
m_settings->registerSetting("CustomOpenALPath", "");
|
||||
@ -601,6 +644,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
|
||||
// Minecraft mods
|
||||
m_settings->registerSetting("ModMetadataDisabled", false);
|
||||
m_settings->registerSetting("ModDependenciesDisabled", false);
|
||||
|
||||
// Minecraft offline player name
|
||||
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("QuitAfterGameStop", false);
|
||||
|
||||
m_settings->registerSetting("Env", QVariant(QMap<QString, QVariant>()));
|
||||
|
||||
// Custom Microsoft Authentication Client ID
|
||||
m_settings->registerSetting("MSAClientIDOverride", "");
|
||||
|
||||
@ -702,6 +748,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
m_globalSettingsProvider->addPage<JavaPage>();
|
||||
m_globalSettingsProvider->addPage<LanguagePage>();
|
||||
m_globalSettingsProvider->addPage<CustomCommandsPage>();
|
||||
m_globalSettingsProvider->addPage<EnvironmentVariablesPage>();
|
||||
m_globalSettingsProvider->addPage<ProxyPage>();
|
||||
m_globalSettingsProvider->addPage<ExternalToolsPage>();
|
||||
m_globalSettingsProvider->addPage<AccountListPage>();
|
||||
@ -738,15 +785,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
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
|
||||
{
|
||||
auto setting = APPLICATION->settings()->getSetting("IconsDir");
|
||||
@ -849,6 +887,107 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
|
||||
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()) {
|
||||
return;
|
||||
}
|
||||
@ -917,6 +1056,26 @@ bool Application::createSetupWizard()
|
||||
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)
|
||||
{
|
||||
#ifdef Q_OS_MACOS
|
||||
@ -985,6 +1144,20 @@ void Application::performMainStartupAction()
|
||||
showMainWindow(false);
|
||||
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()) {
|
||||
qDebug() << "<> Importing from url:" << m_urlsToImport;
|
||||
m_mainWindow->processURLs(m_urlsToImport);
|
||||
|
@ -159,6 +159,9 @@ class Application : public QApplication {
|
||||
/// this is the root of the 'installation'. Used for automatic updates
|
||||
const QString& root() { return m_rootPath; }
|
||||
|
||||
/// the data path the application is using
|
||||
const QString& dataRoot() { return m_dataPath; }
|
||||
|
||||
bool isPortable() { return m_portable; }
|
||||
|
||||
const Capabilities capabilities() { return m_capabilities; }
|
||||
@ -179,6 +182,9 @@ class Application : public QApplication {
|
||||
|
||||
int suitableMaxMem();
|
||||
|
||||
bool updaterEnabled();
|
||||
QString updaterBinaryName();
|
||||
|
||||
QUrl normalizeImportUrl(QString const& url);
|
||||
|
||||
signals:
|
||||
@ -244,6 +250,7 @@ class Application : public QApplication {
|
||||
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
|
||||
|
||||
QString m_rootPath;
|
||||
QString m_dataPath;
|
||||
Status m_status = Application::StartingUp;
|
||||
Capabilities m_capabilities;
|
||||
bool m_portable = false;
|
||||
|
@ -388,7 +388,7 @@ QString BaseInstance::name() 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!!!
|
||||
|
@ -64,7 +64,7 @@ class LaunchTask;
|
||||
class BaseInstance;
|
||||
|
||||
// pointer for lazy people
|
||||
typedef std::shared_ptr<BaseInstance> InstancePtr;
|
||||
using InstancePtr = std::shared_ptr<BaseInstance>;
|
||||
|
||||
/*!
|
||||
* \brief Base class for instances.
|
||||
|
@ -51,7 +51,7 @@ class BaseVersionList : public QAbstractListModel {
|
||||
ArchitectureRole,
|
||||
SortRole
|
||||
};
|
||||
typedef QList<int> RoleList;
|
||||
using RoleList = QList<int>;
|
||||
|
||||
explicit BaseVersionList(QObject* parent = 0);
|
||||
|
||||
|
@ -139,6 +139,7 @@ set(NET_SOURCES
|
||||
net/HeaderProxy.h
|
||||
net/RawHeaderProxy.h
|
||||
net/ApiHeaderProxy.h
|
||||
net/StaticHeaderProxy.h
|
||||
net/ApiDownload.h
|
||||
net/ApiDownload.cpp
|
||||
net/ApiUpload.cpp
|
||||
@ -181,6 +182,11 @@ set(MAC_UPDATE_SOURCES
|
||||
updater/MacSparkleUpdater.mm
|
||||
)
|
||||
|
||||
set(PRISM_UPDATE_SOURCES
|
||||
updater/PrismExternalUpdater.h
|
||||
updater/PrismExternalUpdater.cpp
|
||||
)
|
||||
|
||||
# Backend for the news bar... there's usually no news.
|
||||
set(NEWS_SOURCES
|
||||
# News System
|
||||
@ -579,6 +585,63 @@ set(LINKEXE_SOURCES
|
||||
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 ########
|
||||
|
||||
ecm_qt_declare_logging_category(CORE_SOURCES
|
||||
@ -675,6 +738,8 @@ set(LOGIC_SOURCES
|
||||
|
||||
if(APPLE AND Launcher_ENABLE_UPDATER)
|
||||
set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES})
|
||||
else()
|
||||
set (LOGIC_SOURCES ${LOGIC_SOURCES} ${PRISM_UPDATE_SOURCES})
|
||||
endif()
|
||||
|
||||
SET(LAUNCHER_SOURCES
|
||||
@ -824,6 +889,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/pages/global/AccountListPage.h
|
||||
ui/pages/global/CustomCommandsPage.cpp
|
||||
ui/pages/global/CustomCommandsPage.h
|
||||
ui/pages/global/EnvironmentVariablesPage.cpp
|
||||
ui/pages/global/EnvironmentVariablesPage.h
|
||||
ui/pages/global/ExternalToolsPage.cpp
|
||||
ui/pages/global/ExternalToolsPage.h
|
||||
ui/pages/global/JavaPage.cpp
|
||||
@ -975,6 +1042,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/widgets/Common.h
|
||||
ui/widgets/CustomCommands.cpp
|
||||
ui/widgets/CustomCommands.h
|
||||
ui/widgets/EnvironmentVariables.cpp
|
||||
ui/widgets/EnvironmentVariables.h
|
||||
ui/widgets/DropLabel.cpp
|
||||
ui/widgets/DropLabel.h
|
||||
ui/widgets/FocusLineEdit.cpp
|
||||
@ -1033,6 +1102,15 @@ SET(LAUNCHER_SOURCES
|
||||
ui/instanceview/VisualGroup.h
|
||||
)
|
||||
|
||||
if (NOT Apple)
|
||||
set(LAUNCHER_SOURCES
|
||||
${LAUNCHER_SOURCES}
|
||||
|
||||
ui/dialogs/UpdateAvailableDialog.h
|
||||
ui/dialogs/UpdateAvailableDialog.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(LAUNCHER_SOURCES
|
||||
WindowsConsole.cpp
|
||||
@ -1076,6 +1154,7 @@ qt_wrap_ui(LAUNCHER_UI
|
||||
ui/pages/modplatform/technic/TechnicPage.ui
|
||||
ui/widgets/InstanceCardWidget.ui
|
||||
ui/widgets/CustomCommands.ui
|
||||
ui/widgets/EnvironmentVariables.ui
|
||||
ui/widgets/InfoFrame.ui
|
||||
ui/widgets/ModFilterWidget.ui
|
||||
ui/widgets/SubTaskProgressBar.ui
|
||||
@ -1103,6 +1182,14 @@ qt_wrap_ui(LAUNCHER_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
|
||||
resources/backgrounds/backgrounds.qrc
|
||||
resources/multimc/multimc.qrc
|
||||
@ -1119,6 +1206,12 @@ qt_add_resources(LAUNCHER_RESOURCES
|
||||
../${Launcher_Branding_LogoQRC}
|
||||
)
|
||||
|
||||
qt_wrap_ui(PRISMUPDATER_UI
|
||||
updater/prismupdater/SelectReleaseDialog.ui
|
||||
ui/widgets/SubTaskProgressBar.ui
|
||||
ui/dialogs/ProgressDialog.ui
|
||||
)
|
||||
|
||||
######## Windows resource files ########
|
||||
if(WIN32)
|
||||
set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC})
|
||||
@ -1137,6 +1230,7 @@ set_project_warnings(Launcher_logic
|
||||
"${Launcher_GCC_WARNINGS}")
|
||||
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
|
||||
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
|
||||
target_link_libraries(Launcher_logic
|
||||
systeminfo
|
||||
Launcher_murmur2
|
||||
@ -1218,7 +1312,45 @@ install(TARGETS ${Launcher_Name}
|
||||
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})
|
||||
set_project_warnings(filelink_logic
|
||||
"${Launcher_MSVC_WARNINGS}"
|
||||
@ -1237,7 +1369,7 @@ if(WIN32)
|
||||
${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)
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
class DataMigrationTask : public Task {
|
||||
Q_OBJECT
|
||||
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;
|
||||
|
||||
protected:
|
||||
|
@ -108,12 +108,9 @@ bool openDirectory(const QString& path, [[maybe_unused]] bool ensureExists)
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
if (!isSandbox()) {
|
||||
return IndirectOpen(f);
|
||||
} else {
|
||||
return f();
|
||||
}
|
||||
#else
|
||||
return f();
|
||||
#endif
|
||||
return f();
|
||||
}
|
||||
|
||||
bool openFile(const QString& path)
|
||||
|
@ -123,26 +123,35 @@ namespace fs = ghc::filesystem;
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
|
||||
typedef struct _DUPLICATE_EXTENTS_DATA {
|
||||
struct _DUPLICATE_EXTENTS_DATA {
|
||||
HANDLE FileHandle;
|
||||
LARGE_INTEGER SourceFileOffset;
|
||||
LARGE_INTEGER TargetFileOffset;
|
||||
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 Reserved; // Must be 0
|
||||
DWORD Flags; // FSCTL_INTEGRITY_FLAG_xxx
|
||||
DWORD ChecksumChunkSizeInBytes;
|
||||
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 Reserved; // Must be 0
|
||||
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
|
||||
|
||||
@ -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)
|
||||
{
|
||||
QFile file(filename);
|
||||
@ -287,6 +330,9 @@ bool copy::operator()(const QString& offset, bool dryRun)
|
||||
if (!m_followSymlinks)
|
||||
opt |= copy_opts::copy_symlinks;
|
||||
|
||||
if (m_overwrite)
|
||||
opt |= copy_opts::overwrite_existing;
|
||||
|
||||
// Function that'll do the actual copying
|
||||
auto copy_file = [&](QString src_path, QString relative_dst_path) {
|
||||
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";
|
||||
stream << "Type=Application"
|
||||
<< "\n";
|
||||
stream << "Categories=Game;ActionGame;AdventureGame;Simulation"
|
||||
<< "\n";
|
||||
stream << "Exec=\"" << target.toLocal8Bit() << "\"" << argstring.toLocal8Bit() << "\n";
|
||||
stream << "Name=" << name.toLocal8Bit() << "\n";
|
||||
if (!icon.isEmpty()) {
|
||||
|
@ -61,6 +61,16 @@ class FileSystemException : public ::Exception {
|
||||
*/
|
||||
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\
|
||||
*/
|
||||
@ -109,6 +119,11 @@ class copy : public QObject {
|
||||
m_whitelist = whitelist;
|
||||
return *this;
|
||||
}
|
||||
copy& overwrite(const bool overwrite)
|
||||
{
|
||||
m_overwrite = overwrite;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||
|
||||
@ -128,6 +143,7 @@ class copy : public QObject {
|
||||
bool m_followSymlinks = true;
|
||||
const IPathMatcher* m_matcher = nullptr;
|
||||
bool m_whitelist = false;
|
||||
bool m_overwrite = false;
|
||||
QDir m_src;
|
||||
QDir m_dst;
|
||||
qsizetype m_copied;
|
||||
|
@ -59,7 +59,7 @@
|
||||
|
||||
#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)
|
||||
{}
|
||||
|
||||
|
@ -54,7 +54,7 @@ class FileResolvingTask;
|
||||
class InstanceImportTask : public InstanceTask {
|
||||
Q_OBJECT
|
||||
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;
|
||||
const QVector<Flame::File>& getBlockedFiles() const { return m_blockedMods; }
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* 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
|
||||
* 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();
|
||||
}
|
||||
|
||||
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);
|
||||
if (!inst) {
|
||||
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());
|
||||
if (iter != m_instanceGroupIndex.end()) {
|
||||
if (*iter != name) {
|
||||
decreaseGroupCount(*iter);
|
||||
*iter = name;
|
||||
changed = true;
|
||||
}
|
||||
@ -258,7 +263,7 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
m_groupNameCache.insert(name);
|
||||
increaseGroupCount(name);
|
||||
auto idx = getInstIndex(inst.get());
|
||||
emit dataChanged(index(idx), index(idx), { GroupRole });
|
||||
saveGroupList();
|
||||
@ -267,29 +272,55 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
|
||||
|
||||
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;
|
||||
qDebug() << "Delete group" << name;
|
||||
for (auto& instance : m_instances) {
|
||||
const auto& instID = instance->id();
|
||||
auto instGroupName = getInstanceGroup(instID);
|
||||
const QString& instID = instance->id();
|
||||
const QString instGroupName = getInstanceGroup(instID);
|
||||
if (instGroupName == name) {
|
||||
m_instanceGroupIndex.remove(instID);
|
||||
qDebug() << "Remove" << instID << "from group" << name;
|
||||
removed = true;
|
||||
auto idx = getInstIndex(instance.get());
|
||||
if (idx > 0) {
|
||||
if (idx >= 0)
|
||||
emit dataChanged(index(idx), index(idx), { GroupRole });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removed) {
|
||||
if (removed)
|
||||
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)
|
||||
@ -305,12 +336,13 @@ bool InstanceList::trashInstance(const InstanceId& id)
|
||||
return false;
|
||||
}
|
||||
|
||||
auto cachedGroupId = m_instanceGroupIndex[id];
|
||||
QString cachedGroupId = m_instanceGroupIndex[id];
|
||||
|
||||
qDebug() << "Will trash instance" << id;
|
||||
QString trashedLoc;
|
||||
|
||||
if (m_instanceGroupIndex.remove(id)) {
|
||||
decreaseGroupCount(cachedGroupId);
|
||||
saveGroupList();
|
||||
}
|
||||
|
||||
@ -348,7 +380,7 @@ void InstanceList::undoTrashInstance()
|
||||
QFile(top.trashPath).rename(top.polyPath);
|
||||
|
||||
m_instanceGroupIndex[top.id] = top.groupName;
|
||||
m_groupNameCache.insert(top.groupName);
|
||||
increaseGroupCount(top.groupName);
|
||||
|
||||
saveGroupList();
|
||||
emit instancesChanged();
|
||||
@ -362,7 +394,10 @@ void InstanceList::deleteInstance(const InstanceId& id)
|
||||
return;
|
||||
}
|
||||
|
||||
QString cachedGroupId = m_instanceGroupIndex[id];
|
||||
|
||||
if (m_instanceGroupIndex.remove(id)) {
|
||||
decreaseGroupCount(cachedGroupId);
|
||||
saveGroupList();
|
||||
}
|
||||
|
||||
@ -610,6 +645,25 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id)
|
||||
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()
|
||||
{
|
||||
qDebug() << "Will save group list now.";
|
||||
@ -621,7 +675,7 @@ void InstanceList::saveGroupList()
|
||||
QString groupFileName = m_instDir + "/instgroups.json";
|
||||
QMap<QString, QSet<QString>> reverseGroupMap;
|
||||
for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++) {
|
||||
QString id = iter.key();
|
||||
const QString& id = iter.key();
|
||||
QString group = iter.value();
|
||||
if (group.isEmpty())
|
||||
continue;
|
||||
@ -711,17 +765,22 @@ void InstanceList::loadGroupList()
|
||||
return;
|
||||
}
|
||||
|
||||
QSet<QString> groupSet;
|
||||
m_instanceGroupIndex.clear();
|
||||
m_groupNameCache.clear();
|
||||
|
||||
// Iterate through all the groups.
|
||||
QJsonObject groupMapping = rootObj.value("groups").toObject();
|
||||
for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++) {
|
||||
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 (!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;
|
||||
}
|
||||
|
||||
@ -733,23 +792,19 @@ void InstanceList::loadGroupList()
|
||||
continue;
|
||||
}
|
||||
|
||||
// keep a list/set of groups for choosing
|
||||
groupSet.insert(groupName);
|
||||
|
||||
auto hidden = groupObj.value("hidden").toBool(false);
|
||||
if (hidden) {
|
||||
if (hidden)
|
||||
m_collapsedGroups.insert(groupName);
|
||||
}
|
||||
|
||||
// Iterate through the list of instances in the group.
|
||||
QJsonArray instancesArray = groupObj.value("instances").toArray();
|
||||
|
||||
for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); iter2++) {
|
||||
m_instanceGroupIndex[(*iter2).toString()] = groupName;
|
||||
for (auto value : instancesArray) {
|
||||
m_instanceGroupIndex[value.toString()] = groupName;
|
||||
increaseGroupCount(groupName);
|
||||
}
|
||||
}
|
||||
m_groupsLoaded = true;
|
||||
m_groupNameCache.unite(groupSet);
|
||||
qDebug() << "Group list loaded.";
|
||||
}
|
||||
|
||||
@ -768,6 +823,9 @@ void InstanceList::on_InstFolderChanged([[maybe_unused]] const Setting& setting,
|
||||
}
|
||||
m_instDir = newInstDir;
|
||||
m_groupsLoaded = false;
|
||||
beginRemoveRows(QModelIndex(), 0, count());
|
||||
m_instances.erase(m_instances.begin(), m_instances.end());
|
||||
endRemoveRows();
|
||||
emit instancesChanged();
|
||||
}
|
||||
}
|
||||
@ -892,9 +950,12 @@ QString InstanceList::getStagedInstancePath()
|
||||
|
||||
bool InstanceList::commitStagedInstance(const QString& path,
|
||||
InstanceName const& instanceName,
|
||||
const QString& groupName,
|
||||
QString groupName,
|
||||
InstanceTask const& commiting)
|
||||
{
|
||||
if (groupName.isEmpty() && !groupName.isNull())
|
||||
groupName = QString();
|
||||
|
||||
QDir dir;
|
||||
QString instID;
|
||||
InstancePtr inst;
|
||||
@ -925,7 +986,7 @@ bool InstanceList::commitStagedInstance(const QString& path,
|
||||
}
|
||||
|
||||
m_instanceGroupIndex[instID] = groupName;
|
||||
m_groupNameCache.insert(groupName);
|
||||
increaseGroupCount(groupName);
|
||||
}
|
||||
|
||||
instanceSet.insert(instID);
|
||||
|
@ -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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@ -86,9 +106,10 @@ class InstanceList : public QAbstractListModel {
|
||||
bool isGroupCollapsed(const QString& groupName);
|
||||
|
||||
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 renameGroup(const GroupId& src, const GroupId& dst);
|
||||
bool trashInstance(const InstanceId& id);
|
||||
bool trashedSomething();
|
||||
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
|
||||
* - 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.
|
||||
@ -158,12 +179,16 @@ class InstanceList : public QAbstractListModel {
|
||||
QList<InstanceId> discoverInstances();
|
||||
InstancePtr loadInstance(const InstanceId& id);
|
||||
|
||||
void increaseGroupCount(const QString& group);
|
||||
void decreaseGroupCount(const QString& group);
|
||||
|
||||
private:
|
||||
int m_watchLevel = 0;
|
||||
int totalPlayTime = 0;
|
||||
bool m_dirty = false;
|
||||
QList<InstancePtr> m_instances;
|
||||
QSet<QString> m_groupNameCache;
|
||||
// id -> refs
|
||||
QMap<QString, int> m_groupNameCache;
|
||||
|
||||
SettingsObjectPtr m_globalSettings;
|
||||
QString m_instDir;
|
||||
|
@ -36,6 +36,7 @@
|
||||
|
||||
#include "LaunchController.h"
|
||||
#include "Application.h"
|
||||
#include "minecraft/auth/AccountData.h"
|
||||
#include "minecraft/auth/AccountList.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.
|
||||
auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"),
|
||||
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?"),
|
||||
QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)
|
||||
->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
|
||||
auto instanceAccountId = m_instance->settings()->get("InstanceAccountId").toString();
|
||||
auto instanceAccountIndex = accounts->findAccountByProfileId(instanceAccountId);
|
||||
if (instanceAccountIndex == -1) {
|
||||
if (instanceAccountIndex == -1 || instanceAccountId.isEmpty()) {
|
||||
m_accountToUse = accounts->defaultAccount();
|
||||
} else {
|
||||
m_accountToUse = accounts->at(instanceAccountIndex);
|
||||
@ -161,7 +162,7 @@ void LaunchController::login()
|
||||
m_accountToUse->fillSession(m_session);
|
||||
|
||||
// Launch immediately in true offline mode
|
||||
if (m_accountToUse->isOffline()) {
|
||||
if (m_accountToUse->accountType() == AccountType::Offline) {
|
||||
launchInstance();
|
||||
return;
|
||||
}
|
||||
|
@ -42,11 +42,15 @@
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
#include <QtConcurrentRun>
|
||||
#endif
|
||||
|
||||
namespace MMCZip {
|
||||
// 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());
|
||||
modZip.open(QuaZip::mdUnzip);
|
||||
@ -132,6 +136,7 @@ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files,
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
// ours
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ours
|
||||
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;
|
||||
}
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
void ExportToZipTask::executeTask()
|
||||
{
|
||||
setStatus("Adding files...");
|
||||
@ -500,5 +507,6 @@ bool ExportToZipTask::abort()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace MMCZip
|
||||
} // namespace MMCZip
|
||||
|
@ -48,7 +48,10 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
#include "minecraft/mod/Mod.h"
|
||||
#endif
|
||||
#include "tasks/Task.h"
|
||||
|
||||
namespace MMCZip {
|
||||
@ -57,7 +60,7 @@ using FilterFunction = std::function<bool(const QString&)>;
|
||||
/**
|
||||
* 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
|
||||
@ -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);
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
/**
|
||||
* take a source jar, add mods to it, resulting in target jar
|
||||
*/
|
||||
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods);
|
||||
|
||||
#endif
|
||||
/**
|
||||
* 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);
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
class ExportToZipTask : public Task {
|
||||
public:
|
||||
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 addExtraFile(QString fileName, QByteArray data) { m_extra_files.insert(fileName, data); }
|
||||
|
||||
typedef std::optional<QString> ZipResult;
|
||||
using ZipResult = std::optional<QString>;
|
||||
|
||||
protected:
|
||||
virtual void executeTask() override;
|
||||
@ -189,4 +194,5 @@ class ExportToZipTask : public Task {
|
||||
QFuture<ZipResult> m_build_zip_future;
|
||||
QFutureWatcher<ZipResult> m_build_zip_watcher;
|
||||
};
|
||||
#endif
|
||||
} // namespace MMCZip
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <QPixmapCache>
|
||||
#include <QThread>
|
||||
#include <QTime>
|
||||
#include <limits>
|
||||
|
||||
#define GET_TYPE() \
|
||||
Qt::ConnectionType type; \
|
||||
@ -100,10 +101,14 @@ class PixmapCache final : public QObject {
|
||||
*/
|
||||
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();
|
||||
if (!m_last_cache_miss_by_eviciton.isNull()) {
|
||||
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;
|
||||
} else {
|
||||
m_consecutive_fast_evicitons = 0;
|
||||
@ -111,11 +116,17 @@ class PixmapCache final : public QObject {
|
||||
}
|
||||
m_last_cache_miss_by_eviciton = now;
|
||||
if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) {
|
||||
// double the cache size
|
||||
auto newSize = _cacheLimit() * 2;
|
||||
qDebug() << m_consecutive_fast_evicitons << "pixmap cache misses by eviction happened too fast, doubling cache size to"
|
||||
<< newSize;
|
||||
_setCacheLimit(newSize);
|
||||
// increase the cache size
|
||||
uint newSize = _cacheLimit() + step;
|
||||
if (newSize >= maxInt) { // increase it until you overflow :D
|
||||
newSize = maxInt;
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ class RecursiveFileSystemWatcher : public QObject {
|
||||
QDir rootDir() const { return m_root; }
|
||||
|
||||
// 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; }
|
||||
|
||||
void setMatcher(IPathMatcher::Ptr matcher) { m_matcher = matcher; }
|
||||
|
@ -32,7 +32,7 @@ class ResourceDownloadTask : public SequentialTask {
|
||||
public:
|
||||
explicit ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
||||
ModPlatform::IndexedVersion version,
|
||||
const std::shared_ptr<ResourceFolderModel> packs,
|
||||
std::shared_ptr<ResourceFolderModel> packs,
|
||||
bool is_indexed = true,
|
||||
QString custom_target_folder = {});
|
||||
const QString& getFilename() const { return m_pack_version.fileName; }
|
||||
|
@ -35,6 +35,7 @@
|
||||
*/
|
||||
|
||||
#include "StringUtils.h"
|
||||
#include <qpair.h>
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QUuid>
|
||||
@ -149,7 +150,7 @@ QString StringUtils::truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_
|
||||
}
|
||||
|
||||
if ((url_compact.length() >= max_len) && hard_limit) {
|
||||
// still too long, truncate normaly
|
||||
// still too long, truncate normally
|
||||
url_compact = QString(str_url);
|
||||
auto to_remove = url_compact.length() - max_len + 3;
|
||||
url_compact.remove(url_compact.length() - to_remove - 1, to_remove);
|
||||
@ -182,3 +183,32 @@ QString StringUtils::getRandomAlphaNumeric()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
@ -36,8 +36,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QPair>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <utility>
|
||||
|
||||
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
|
||||
* @param url Url to truncate
|
||||
* @param max_len max lenght of url in charaters
|
||||
* @param hard_limit if truncating the path can't get the url short enough, truncate it normaly.
|
||||
* @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 normally.
|
||||
*/
|
||||
QString truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_limit = false);
|
||||
|
||||
QString humanReadableFileSize(double bytes, bool use_si = false, int decimal_points = 1);
|
||||
|
||||
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
|
||||
|
@ -56,6 +56,7 @@ class Version {
|
||||
bool operator!=(const Version& other) const;
|
||||
|
||||
QString toString() const { return m_string; }
|
||||
bool isEmpty() const { return m_string.isEmpty(); }
|
||||
|
||||
friend QDebug operator<<(QDebug debug, const Version& v);
|
||||
|
||||
|
@ -10,7 +10,7 @@ class VersionProxyModel : public QAbstractProxyModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
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:
|
||||
VersionProxyModel(QObject* parent = 0);
|
||||
@ -28,7 +28,7 @@ class VersionProxyModel : public QAbstractProxyModel {
|
||||
|
||||
const FilterMap& filters() 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 clearFilters();
|
||||
QModelIndex getRecommended() const;
|
||||
|
@ -93,6 +93,7 @@ FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv),
|
||||
joinServer(serverToJoin);
|
||||
} else {
|
||||
qDebug() << "no server to join";
|
||||
m_status = Failed;
|
||||
exit();
|
||||
}
|
||||
}
|
||||
@ -108,6 +109,7 @@ void FileLinkApp::joinServer(QString server)
|
||||
connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs);
|
||||
|
||||
connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError) {
|
||||
m_status = Failed;
|
||||
switch (socketError) {
|
||||
case QLocalSocket::ServerNotFoundError:
|
||||
qDebug()
|
||||
@ -132,6 +134,7 @@ void FileLinkApp::joinServer(QString server)
|
||||
|
||||
connect(&socket, &QLocalSocket::disconnected, this, [&]() {
|
||||
qDebug() << "disconnected from server, should exit";
|
||||
m_status = Succeeded;
|
||||
exit();
|
||||
});
|
||||
|
||||
|
@ -41,8 +41,10 @@ class FileLinkApp : public QCoreApplication {
|
||||
// friends for the purpose of limiting access to deprecated stuff
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Status { Starting, Failed, Succeeded, Initialized };
|
||||
FileLinkApp(int& argc, char** argv);
|
||||
virtual ~FileLinkApp();
|
||||
Status status() const { return m_status; }
|
||||
|
||||
private:
|
||||
void joinServer(QString server);
|
||||
@ -50,6 +52,8 @@ class FileLinkApp : public QCoreApplication {
|
||||
void runLink();
|
||||
void sendResults();
|
||||
|
||||
Status m_status = Status::Starting;
|
||||
|
||||
bool m_useHardLinks = false;
|
||||
|
||||
QDateTime m_startTime;
|
||||
|
@ -26,5 +26,16 @@ int main(int argc, char* 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;
|
||||
}
|
||||
}
|
@ -67,7 +67,7 @@ class IconList : public QAbstractListModel {
|
||||
virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
|
||||
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;
|
||||
bool deleteIcon(const QString& key);
|
||||
bool trashIcon(const QString& key);
|
||||
|
@ -22,8 +22,8 @@ struct JavaCheckResult {
|
||||
enum class Validity { Errored, ReturnedInvalidData, Valid } validity = Validity::Errored;
|
||||
};
|
||||
|
||||
typedef shared_qobject_ptr<QProcess> QProcessPtr;
|
||||
typedef shared_qobject_ptr<JavaChecker> JavaCheckerPtr;
|
||||
using QProcessPtr = shared_qobject_ptr<QProcess>;
|
||||
using JavaCheckerPtr = shared_qobject_ptr<JavaChecker>;
|
||||
class JavaChecker : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include "tasks/Task.h"
|
||||
|
||||
class JavaCheckerJob;
|
||||
typedef shared_qobject_ptr<JavaCheckerJob> JavaCheckerJobPtr;
|
||||
using JavaCheckerJobPtr = shared_qobject_ptr<JavaCheckerJob>;
|
||||
|
||||
// FIXME: this just seems horribly redundant
|
||||
class JavaCheckerJob : public Task {
|
||||
|
@ -42,4 +42,4 @@ struct JavaInstall : public BaseVersion {
|
||||
bool recommended = false;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<JavaInstall> JavaInstallPtr;
|
||||
using JavaInstallPtr = std::shared_ptr<JavaInstall>;
|
||||
|
@ -34,6 +34,7 @@
|
||||
*/
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
@ -335,6 +336,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
}
|
||||
}
|
||||
|
||||
candidates.append(getMinecraftJavaBundle());
|
||||
candidates = addJavasFromEnv(candidates);
|
||||
candidates.removeDuplicates();
|
||||
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/Commands/java");
|
||||
}
|
||||
javas.append(getMinecraftJavaBundle());
|
||||
javas = addJavasFromEnv(javas);
|
||||
javas.removeDuplicates();
|
||||
return javas;
|
||||
@ -411,6 +414,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
// javas downloaded by sdkman
|
||||
scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java"));
|
||||
|
||||
javas.append(getMinecraftJavaBundle());
|
||||
javas = addJavasFromEnv(javas);
|
||||
javas.removeDuplicates();
|
||||
return javas;
|
||||
@ -423,6 +427,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
QList<QString> javas;
|
||||
javas.append(this->GetDefaultJava()->path);
|
||||
|
||||
javas.append(getMinecraftJavaBundle());
|
||||
return addJavasFromEnv(javas);
|
||||
}
|
||||
#endif
|
||||
@ -431,3 +436,50 @@ QString JavaUtils::getJavaCheckPath()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
QString stripVariableEntries(QString name, QString target, QString remove);
|
||||
QProcessEnvironment CleanEnviroment();
|
||||
QStringList getMinecraftJavaBundle();
|
||||
|
||||
class JavaUtils : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -45,10 +45,12 @@ QString JavaVersion::toString() const
|
||||
|
||||
bool JavaVersion::requiresPermGen()
|
||||
{
|
||||
if (m_parseable) {
|
||||
return m_major < 8;
|
||||
}
|
||||
return true;
|
||||
return !m_parseable || m_major < 8;
|
||||
}
|
||||
|
||||
bool JavaVersion::isModular()
|
||||
{
|
||||
return m_parseable && m_major >= 9;
|
||||
}
|
||||
|
||||
bool JavaVersion::operator<(const JavaVersion& rhs)
|
||||
|
@ -25,6 +25,8 @@ class JavaVersion {
|
||||
|
||||
bool requiresPermGen();
|
||||
|
||||
bool isModular();
|
||||
|
||||
QString toString() const;
|
||||
|
||||
int major() { return m_major; }
|
||||
|
@ -55,6 +55,6 @@ class Index : public QAbstractListModel, public BaseEntity {
|
||||
QVector<VersionList::Ptr> m_lists;
|
||||
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
|
||||
|
@ -64,7 +64,7 @@ class Version : public QObject, public BaseVersion, public BaseEntity {
|
||||
|
||||
public: // for usage by format parsers only
|
||||
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 setVolatile(bool volatile_);
|
||||
void setRecommended(bool recommended);
|
||||
|
@ -79,7 +79,7 @@ class VersionList : public BaseVersionList, public BaseEntity {
|
||||
|
||||
Version::Ptr m_recommended;
|
||||
|
||||
void setupAddedVersion(const int row, const Version::Ptr& version);
|
||||
void setupAddedVersion(int row, const Version::Ptr& version);
|
||||
};
|
||||
} // namespace Meta
|
||||
Q_DECLARE_METATYPE(Meta::VersionList::Ptr)
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
class Agent;
|
||||
|
||||
typedef std::shared_ptr<Agent> AgentPtr;
|
||||
using AgentPtr = std::shared_ptr<Agent>;
|
||||
|
||||
class Agent {
|
||||
public:
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "FileSystem.h"
|
||||
#include "net/ApiDownload.h"
|
||||
#include "net/ChecksumValidator.h"
|
||||
#include "net/Download.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
|
@ -105,4 +105,4 @@ class Component : public QObject, public ProblemProvider {
|
||||
bool m_loaded = false;
|
||||
};
|
||||
|
||||
typedef shared_qobject_ptr<Component> ComponentPtr;
|
||||
using ComponentPtr = shared_qobject_ptr<Component>;
|
||||
|
@ -52,7 +52,7 @@
|
||||
class Library;
|
||||
class MinecraftInstance;
|
||||
|
||||
typedef std::shared_ptr<Library> LibraryPtr;
|
||||
using LibraryPtr = std::shared_ptr<Library>;
|
||||
|
||||
class Library {
|
||||
friend class OneSixVersionFormat;
|
||||
|
@ -184,6 +184,13 @@ void MinecraftInstance::loadSpecificSettings()
|
||||
m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), 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");
|
||||
}
|
||||
|
||||
@ -513,20 +520,28 @@ QStringList MinecraftInstance::javaArguments()
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
QString MinecraftInstance::getLauncher()
|
||||
{
|
||||
auto profile = m_components->getProfile();
|
||||
|
||||
// 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 "standard";
|
||||
}
|
||||
|
||||
bool MinecraftInstance::shouldApplyOnlineFixes()
|
||||
{
|
||||
return traits().contains("legacyServices") && settings()->get("OnlineFixes").toBool();
|
||||
}
|
||||
|
||||
QMap<QString, QString> MinecraftInstance::getVariables()
|
||||
{
|
||||
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_JAVA", settings()->get("JavaPath").toString());
|
||||
out.insert("INST_JAVA_ARGS", javaArguments().join(' '));
|
||||
out.insert("NO_COLOR", "1");
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -549,6 +565,22 @@ QProcessEnvironment MinecraftInstance::createEnvironment()
|
||||
for (auto it = variables.begin(); it != variables.end(); ++it) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -559,16 +591,27 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
if (settings()->get("EnableMangoHud").toBool() && APPLICATION->capabilities() & Application::SupportsMangoHud) {
|
||||
auto preloadList = env.value("LD_PRELOAD").split(QLatin1String(":"));
|
||||
auto libPaths = env.value("LD_LIBRARY_PATH").split(QLatin1String(":"));
|
||||
QStringList preloadList;
|
||||
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();
|
||||
if (!mangoHudLibString.isEmpty()) {
|
||||
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
|
||||
preloadList << "libMangoHud_dlsym.so" << mangoHudLib.fileName();
|
||||
libPaths << mangoHudLib.absolutePath();
|
||||
appendLib("libMangoHud_dlsym.so");
|
||||
appendLib("libMangoHud_opengl.so");
|
||||
appendLib(mangoHudLib.fileName());
|
||||
libPaths << libPath;
|
||||
}
|
||||
|
||||
env.insert("LD_PRELOAD", preloadList.join(QLatin1String(":")));
|
||||
@ -585,7 +628,6 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
|
||||
env.insert("__GLX_VENDOR_LIBRARY_NAME", "nvidia");
|
||||
}
|
||||
#endif
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
@ -698,7 +740,7 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS
|
||||
{
|
||||
QString windowParams;
|
||||
if (settings()->get("LaunchMaximized").toBool())
|
||||
windowParams = "max";
|
||||
windowParams = "maximized";
|
||||
else
|
||||
windowParams =
|
||||
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";
|
||||
}
|
||||
|
||||
// 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
|
||||
if (session) {
|
||||
launchScript += "userName " + session->player_name + "\n";
|
||||
@ -716,6 +771,9 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS
|
||||
launchScript += "traits " + trait + "\n";
|
||||
}
|
||||
|
||||
if (shouldApplyOnlineFixes())
|
||||
launchScript += "onlineFixes true\n";
|
||||
|
||||
launchScript += "launcher " + getLauncher() + "\n";
|
||||
|
||||
// qDebug() << "Generated launch script:" << launchScript;
|
||||
|
@ -129,6 +129,7 @@ class MinecraftInstance : public BaseInstance {
|
||||
/// get arguments passed to java
|
||||
QStringList javaArguments();
|
||||
QString getLauncher();
|
||||
bool shouldApplyOnlineFixes();
|
||||
|
||||
/// get variables for launch command variable substitution/environment
|
||||
QMap<QString, QString> getVariables() override;
|
||||
@ -173,4 +174,4 @@ class MinecraftInstance : public BaseInstance {
|
||||
mutable std::shared_ptr<GameOptions> m_game_options;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<MinecraftInstance> MinecraftInstancePtr;
|
||||
using MinecraftInstancePtr = std::shared_ptr<MinecraftInstance>;
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
struct MojangDownloadInfo {
|
||||
// types
|
||||
typedef std::shared_ptr<MojangDownloadInfo> Ptr;
|
||||
using Ptr = std::shared_ptr<MojangDownloadInfo>;
|
||||
|
||||
// data
|
||||
/// Local filesystem path. WARNING: not used, only here so we can pass through mojang files unmolested!
|
||||
@ -23,7 +23,7 @@ struct MojangLibraryDownloadInfo {
|
||||
MojangLibraryDownloadInfo() {}
|
||||
|
||||
// types
|
||||
typedef std::shared_ptr<MojangLibraryDownloadInfo> Ptr;
|
||||
using Ptr = std::shared_ptr<MojangLibraryDownloadInfo>;
|
||||
|
||||
// methods
|
||||
MojangDownloadInfo* getDownloadInfo(QString classifier)
|
||||
@ -42,7 +42,7 @@ struct MojangLibraryDownloadInfo {
|
||||
|
||||
struct MojangAssetIndexInfo : public MojangDownloadInfo {
|
||||
// types
|
||||
typedef std::shared_ptr<MojangAssetIndexInfo> Ptr;
|
||||
using Ptr = std::shared_ptr<MojangAssetIndexInfo>;
|
||||
|
||||
// methods
|
||||
MojangAssetIndexInfo() {}
|
||||
|
@ -9,7 +9,7 @@
|
||||
class OneSixVersionFormat {
|
||||
public:
|
||||
// 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);
|
||||
|
||||
// libraries
|
||||
|
@ -430,7 +430,7 @@ bool PackProfile::remove(const int index)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PackProfile::remove(const QString id)
|
||||
bool PackProfile::remove(const QString& id)
|
||||
{
|
||||
int i = 0;
|
||||
for (auto patch : d->components) {
|
||||
|
@ -89,13 +89,13 @@ class PackProfile : public QAbstractListModel {
|
||||
|
||||
enum MoveDirection { MoveUp, MoveDown };
|
||||
/// 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
|
||||
bool remove(const int index);
|
||||
bool remove(int index);
|
||||
|
||||
/// remove component file by id - including files/records
|
||||
bool remove(const QString id);
|
||||
bool remove(const QString& id);
|
||||
|
||||
bool customize(int index);
|
||||
|
||||
|
@ -140,7 +140,7 @@ VersionFilePtr parseJsonFile(const QFileInfo& fileInfo, const bool 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();
|
||||
QSaveFile jsonFile(filename);
|
||||
|
@ -38,7 +38,7 @@
|
||||
#include "VersionFile.h"
|
||||
|
||||
namespace ProfileUtils {
|
||||
typedef QStringList PatchOrder;
|
||||
using PatchOrder = QStringList;
|
||||
|
||||
/// Read and parse a OneSix format order file
|
||||
bool readOverrideOrders(QString path, PatchOrder& order);
|
||||
@ -47,10 +47,10 @@ bool readOverrideOrders(QString path, PatchOrder& order);
|
||||
bool writeOverrideOrders(QString path, const PatchOrder& order);
|
||||
|
||||
/// 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)
|
||||
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.
|
||||
void removeLwjglFromPatch(VersionFilePtr patch);
|
||||
|
@ -283,9 +283,15 @@ QVariant AccountList::data(const QModelIndex& index, int role) const
|
||||
return account->accountDisplayString();
|
||||
|
||||
case TypeColumn: {
|
||||
auto typeStr = account->typeString();
|
||||
typeStr[0] = typeStr[0].toUpper();
|
||||
return typeStr;
|
||||
switch (account->accountType()) {
|
||||
case AccountType::MSA: {
|
||||
return tr("MSA", "Account type");
|
||||
}
|
||||
case AccountType::Offline: {
|
||||
return tr("Offline", "Account type");
|
||||
}
|
||||
}
|
||||
return tr("Unknown", "Account type");
|
||||
}
|
||||
|
||||
case StatusColumn: {
|
||||
|
@ -75,7 +75,7 @@ class AccountList : public QAbstractListModel {
|
||||
virtual Qt::ItemFlags flags(const QModelIndex& index) const 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);
|
||||
int findAccountByProfileId(const QString& profileId) const;
|
||||
MinecraftAccountPtr getAccountByProfileName(const QString& profileName) const;
|
||||
|
@ -43,4 +43,4 @@ struct AuthSession {
|
||||
bool demo = false;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<AuthSession> AuthSessionPtr;
|
||||
using AuthSessionPtr = std::shared_ptr<AuthSession>;
|
||||
|
@ -52,6 +52,7 @@
|
||||
|
||||
#include "flows/MSA.h"
|
||||
#include "flows/Offline.h"
|
||||
#include "minecraft/auth/AccountData.h"
|
||||
|
||||
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.
|
||||
} break;
|
||||
case AccountTaskState::STATE_FAILED_HARD: {
|
||||
if (isMSA()) {
|
||||
if (accountType() == AccountType::MSA) {
|
||||
data.msaToken.token = QString();
|
||||
data.msaToken.refresh_token = QString();
|
||||
data.msaToken.validity = Katabasis::Validity::None;
|
||||
|
@ -54,7 +54,7 @@ class Task;
|
||||
class AccountTask;
|
||||
class MinecraftAccount;
|
||||
|
||||
typedef shared_qobject_ptr<MinecraftAccount> MinecraftAccountPtr;
|
||||
using MinecraftAccountPtr = shared_qobject_ptr<MinecraftAccount>;
|
||||
Q_DECLARE_METATYPE(MinecraftAccountPtr)
|
||||
|
||||
/**
|
||||
@ -118,9 +118,7 @@ class MinecraftAccount : public QObject, public Usable {
|
||||
|
||||
bool isActive() const;
|
||||
|
||||
bool isMSA() const { return data.type == AccountType::MSA; }
|
||||
|
||||
bool isOffline() const { return data.type == AccountType::Offline; }
|
||||
[[nodiscard]] AccountType accountType() const noexcept { return data.type; }
|
||||
|
||||
bool ownsMinecraft() const { return data.minecraftEntitlement.ownsMinecraft; }
|
||||
|
||||
|
@ -105,6 +105,17 @@ void LauncherPartLaunch::executeTask()
|
||||
auto instance = m_parent->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);
|
||||
QStringList args = minecraftInstance->javaArguments();
|
||||
QString allArgs = args.join(", ");
|
||||
@ -120,6 +131,9 @@ void LauncherPartLaunch::executeTask()
|
||||
auto classPath = minecraftInstance->getClassPath();
|
||||
classPath.prepend(jarPath);
|
||||
|
||||
if (!legacyJarPath.isEmpty())
|
||||
classPath.prepend(legacyJarPath);
|
||||
|
||||
auto natPath = minecraftInstance->getNativePath();
|
||||
#ifdef Q_OS_WIN
|
||||
if (!fitsInLocal8bit(natPath)) {
|
||||
|
@ -26,4 +26,4 @@ struct MinecraftServerTarget {
|
||||
static MinecraftServerTarget parse(const QString& fullAddress);
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<MinecraftServerTarget> MinecraftServerTargetPtr;
|
||||
using MinecraftServerTargetPtr = std::shared_ptr<MinecraftServerTarget>;
|
||||
|
@ -37,9 +37,9 @@
|
||||
#include "ModFolderModel.h"
|
||||
|
||||
#include <FileSystem.h>
|
||||
#include <qheaderview.h>
|
||||
#include <QDebug>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QHeaderView>
|
||||
#include <QIcon>
|
||||
#include <QMimeData>
|
||||
#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_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_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch,
|
||||
QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents };
|
||||
m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch,
|
||||
QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive };
|
||||
m_columnsHideable = { false, true, false, true, true, true };
|
||||
}
|
||||
|
||||
@ -131,6 +131,11 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const
|
||||
}
|
||||
return {};
|
||||
}
|
||||
case Qt::SizeHintRole:
|
||||
if (column == ImageColumn) {
|
||||
return QSize(32, 32);
|
||||
}
|
||||
return {};
|
||||
case Qt::CheckStateRole:
|
||||
switch (column) {
|
||||
case ActiveColumn:
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
#include <QHeaderView>
|
||||
#include <QIcon>
|
||||
#include <QMenu>
|
||||
#include <QMimeData>
|
||||
@ -45,7 +46,7 @@ ResourceFolderModel::~ResourceFolderModel()
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
|
||||
bool ResourceFolderModel::startWatching(const QStringList paths)
|
||||
bool ResourceFolderModel::startWatching(const QStringList& paths)
|
||||
{
|
||||
if (m_is_watching)
|
||||
return false;
|
||||
@ -64,7 +65,7 @@ bool ResourceFolderModel::startWatching(const QStringList paths)
|
||||
return m_is_watching;
|
||||
}
|
||||
|
||||
bool ResourceFolderModel::stopWatching(const QStringList paths)
|
||||
bool ResourceFolderModel::stopWatching(const QStringList& paths)
|
||||
{
|
||||
if (!m_is_watching)
|
||||
return false;
|
||||
@ -460,9 +461,9 @@ bool ResourceFolderModel::setData(const QModelIndex& index, [[maybe_unused]] con
|
||||
if (role == Qt::CheckStateRole) {
|
||||
if (m_instance != nullptr && m_instance->isRunning()) {
|
||||
auto response =
|
||||
CustomMessageBox::selectable(nullptr, "Confirm toggle",
|
||||
"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?",
|
||||
CustomMessageBox::selectable(nullptr, tr("Confirm toggle"),
|
||||
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?"),
|
||||
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
|
||||
->exec();
|
||||
|
||||
@ -516,36 +517,22 @@ void ResourceFolderModel::setupHeaderAction(QAction* act, int 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)
|
||||
: m_instance->settings()->registerSetting(setting_name);
|
||||
|
||||
auto hiddenColumns = setting->get().toStringList();
|
||||
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);
|
||||
setting->set(tree->header()->saveState());
|
||||
}
|
||||
|
||||
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)
|
||||
: m_instance->settings()->registerSetting(setting_name);
|
||||
|
||||
auto hiddenColumns = setting->get().toStringList();
|
||||
auto col_names = columnNames(false);
|
||||
for (auto col_name : hiddenColumns) {
|
||||
auto index = col_names.indexOf(col_name);
|
||||
if (index >= 0)
|
||||
tree->setColumnHidden(index, true);
|
||||
}
|
||||
tree->header()->restoreState(setting->get().toByteArray());
|
||||
}
|
||||
|
||||
QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree)
|
||||
@ -570,7 +557,7 @@ QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree)
|
||||
if (m_column_resize_modes.at(c) == QHeaderView::ResizeToContents)
|
||||
tree->resizeColumnToContents(c);
|
||||
}
|
||||
saveHiddenColumn(col, !toggled);
|
||||
saveColumns(tree);
|
||||
});
|
||||
|
||||
menu->addAction(act);
|
||||
|
@ -39,14 +39,14 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||
* Returns whether starting to watch all the paths was successful.
|
||||
* If one or more fails, it returns false.
|
||||
*/
|
||||
bool startWatching(const QStringList paths);
|
||||
bool startWatching(const QStringList& paths);
|
||||
|
||||
/** Stops watching the paths for changes.
|
||||
*
|
||||
* Returns whether stopping to watch all the paths was successful.
|
||||
* 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. */
|
||||
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;
|
||||
|
||||
void setupHeaderAction(QAction* act, int column);
|
||||
void saveHiddenColumn(int column, bool hidden);
|
||||
void loadHiddenColumns(QTreeView* tree);
|
||||
void saveColumns(QTreeView* tree);
|
||||
void loadColumns(QTreeView* tree);
|
||||
QMenu* createHeaderContextMenu(QTreeView* tree);
|
||||
|
||||
/** 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 };
|
||||
QStringList m_column_names = { "Enable", "Name", "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,
|
||||
QHeaderView::ResizeToContents };
|
||||
QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive };
|
||||
QList<bool> m_columnsHideable = { false, false, true };
|
||||
|
||||
QDir m_dir;
|
||||
|
@ -52,8 +52,8 @@ ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstanc
|
||||
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_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,
|
||||
QHeaderView::ResizeToContents };
|
||||
m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive,
|
||||
QHeaderView::Interactive };
|
||||
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();
|
||||
}
|
||||
case Qt::SizeHintRole:
|
||||
if (column == ImageColumn) {
|
||||
return QSize(32, 32);
|
||||
}
|
||||
return {};
|
||||
case Qt::CheckStateRole:
|
||||
switch (column) {
|
||||
case ActiveColumn:
|
||||
|
@ -47,8 +47,7 @@ TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance*
|
||||
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_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, 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 };
|
||||
m_columnsHideable = { false, true, false, true };
|
||||
}
|
||||
|
||||
@ -104,6 +103,11 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const
|
||||
}
|
||||
return {};
|
||||
}
|
||||
case Qt::SizeHintRole:
|
||||
if (column == ImageColumn) {
|
||||
return QSize(32, 32);
|
||||
}
|
||||
return {};
|
||||
case Qt::CheckStateRole:
|
||||
if (column == ActiveColumn) {
|
||||
return m_resources[row]->enabled() ? Qt::Checked : Qt::Unchecked;
|
||||
|
@ -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 responseInfo = std::make_shared<QByteArray>();
|
||||
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{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*responseInfo, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
removePack(pDep->pack->addonId);
|
||||
qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset
|
||||
<< " reason: " << parse_error.errorString();
|
||||
qDebug() << *responseInfo;
|
||||
@ -151,6 +152,7 @@ Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptr<PackDepende
|
||||
: Json::requireObject(doc);
|
||||
provider.mod->loadIndexedPack(*pDep->pack, obj);
|
||||
} catch (const JSONValidationError& e) {
|
||||
removePack(pDep->pack->addonId);
|
||||
qDebug() << doc;
|
||||
qWarning() << "Error while reading mod info: " << e.cause();
|
||||
}
|
||||
@ -211,11 +213,13 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
|
||||
pDep->pack->versionsLoaded = true;
|
||||
|
||||
} catch (const JSONValidationError& e) {
|
||||
removePack(dep.addonId);
|
||||
qDebug() << doc;
|
||||
qWarning() << "Error while reading mod version: " << e.cause();
|
||||
return;
|
||||
}
|
||||
if (level == 0) {
|
||||
removePack(dep.addonId);
|
||||
qWarning() << "Dependency cycle exceeded";
|
||||
return;
|
||||
}
|
||||
@ -238,7 +242,7 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
|
||||
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; };
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
|
||||
|
@ -65,13 +65,13 @@ class GetModDependenciesTask : public SequentialTask {
|
||||
QHash<QString, QStringList> getRequiredBy();
|
||||
|
||||
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&,
|
||||
const ModPlatform::ResourceProvider providerName);
|
||||
ModPlatform::ResourceProvider providerName);
|
||||
void prepare();
|
||||
Task::Ptr getProjectInfoTask(std::shared_ptr<PackDependency> pDep);
|
||||
ModPlatform::Dependency getOverride(const ModPlatform::Dependency&, const ModPlatform::ResourceProvider providerName);
|
||||
void removePack(const QVariant addonId);
|
||||
ModPlatform::Dependency getOverride(const ModPlatform::Dependency&, ModPlatform::ResourceProvider providerName);
|
||||
void removePack(const QVariant& addonId);
|
||||
|
||||
private:
|
||||
QList<std::shared_ptr<PackDependency>> m_pack_dependencies;
|
||||
|
@ -688,6 +688,7 @@ bool loadIconFile(const Mod& mod)
|
||||
return png_invalid(); // icon invalid
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case ResourceType::ZIPFILE: {
|
||||
QuaZip zip(mod.fileinfo().filePath());
|
||||
@ -714,6 +715,7 @@ bool loadIconFile(const Mod& mod)
|
||||
} else {
|
||||
return png_invalid(); // could not set icon as current file.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case ResourceType::LITEMOD: {
|
||||
return false; // can lightmods even have icons?
|
||||
|
@ -232,10 +232,9 @@ bool processPackPNG(const ResourcePack& pack)
|
||||
} else {
|
||||
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: {
|
||||
Q_ASSERT(pack.type() == ResourceType::ZIPFILE);
|
||||
|
||||
QuaZip zip(pack.fileinfo().filePath());
|
||||
if (!zip.open(QuaZip::mdUnzip))
|
||||
return false; // can't open zip file
|
||||
@ -259,6 +258,7 @@ bool processPackPNG(const ResourcePack& pack)
|
||||
} else {
|
||||
return png_invalid(); // could not set pack.mcmeta as current file.
|
||||
}
|
||||
return false; // not processed correctly; https://github.com/PrismLauncher/PrismLauncher/issues/1740
|
||||
}
|
||||
default:
|
||||
qWarning() << "Invalid type for resource pack parse task!";
|
||||
|
@ -186,10 +186,9 @@ bool processPackPNG(const TexturePack& pack)
|
||||
} else {
|
||||
return png_invalid(); // pack.png does not exists or is not a valid file.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case ResourceType::ZIPFILE: {
|
||||
Q_ASSERT(pack.type() == ResourceType::ZIPFILE);
|
||||
|
||||
QuaZip zip(pack.fileinfo().filePath());
|
||||
if (!zip.open(QuaZip::mdUnzip))
|
||||
return false; // can't open zip file
|
||||
@ -215,6 +214,7 @@ bool processPackPNG(const TexturePack& pack)
|
||||
zip.close();
|
||||
return png_invalid(); // could not set pack.mcmeta as current file.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
qWarning() << "Invalid type for resource pack parse task!";
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <QtNetwork/QtNetwork>
|
||||
#include "tasks/Task.h"
|
||||
|
||||
typedef shared_qobject_ptr<class SkinDelete> SkinDeletePtr;
|
||||
using SkinDeletePtr = shared_qobject_ptr<class SkinDelete>;
|
||||
|
||||
class SkinDelete : public Task {
|
||||
Q_OBJECT
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <memory>
|
||||
#include "tasks/Task.h"
|
||||
|
||||
typedef shared_qobject_ptr<class SkinUpload> SkinUploadPtr;
|
||||
using SkinUploadPtr = shared_qobject_ptr<class SkinUpload>;
|
||||
|
||||
class SkinUpload : public Task {
|
||||
Q_OBJECT
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "ATLPackInstallTask.h"
|
||||
|
||||
#include <QtConcurrent>
|
||||
#include <algorithm>
|
||||
|
||||
#include <quazip/quazip.h>
|
||||
|
||||
@ -50,6 +51,7 @@
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/OneSixVersionFormat.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "modplatform/atlauncher/ATLPackManifest.h"
|
||||
#include "net/ChecksumValidator.h"
|
||||
#include "settings/INISettingsObject.h"
|
||||
|
||||
@ -57,6 +59,7 @@
|
||||
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "ui/dialogs/BlockedModsDialog.h"
|
||||
|
||||
namespace ATLauncher {
|
||||
|
||||
@ -717,6 +720,8 @@ void PackInstallTask::downloadMods()
|
||||
|
||||
jarmods.clear();
|
||||
jobPtr.reset(new NetJob(tr("Mod download"), APPLICATION->network()));
|
||||
|
||||
QList<VersionMod> blocked_mods;
|
||||
for (const auto& mod : m_version.mods) {
|
||||
// skip non-client mods
|
||||
if (!mod.client)
|
||||
@ -731,9 +736,10 @@ void PackInstallTask::downloadMods()
|
||||
case DownloadType::Server:
|
||||
url = BuildConfig.ATL_DOWNLOAD_SERVER_URL + mod.url;
|
||||
break;
|
||||
case DownloadType::Browser:
|
||||
emitFailed(tr("Unsupported download type: %1").arg(mod.download_raw));
|
||||
return;
|
||||
case DownloadType::Browser: {
|
||||
blocked_mods.append(mod);
|
||||
continue;
|
||||
}
|
||||
case DownloadType::Direct:
|
||||
url = mod.url;
|
||||
break;
|
||||
@ -805,24 +811,86 @@ void PackInstallTask::downloadMods()
|
||||
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::failed, [&](QString reason) {
|
||||
abortable = false;
|
||||
jobPtr.reset();
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
|
||||
connect(jobPtr.get(), &NetJob::progress, [this](qint64 current, qint64 total) {
|
||||
setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
|
||||
abortable = true;
|
||||
setProgress(current, total);
|
||||
});
|
||||
connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress);
|
||||
connect(jobPtr.get(), &NetJob::aborted, [&] {
|
||||
abortable = false;
|
||||
jobPtr.reset();
|
||||
emitAborted();
|
||||
});
|
||||
connect(jobPtr.get(), &NetJob::aborted, &PackInstallTask::emitAborted);
|
||||
connect(jobPtr.get(), &NetJob::failed, &PackInstallTask::emitFailed);
|
||||
|
||||
jobPtr->start();
|
||||
}
|
||||
@ -843,7 +911,7 @@ void PackInstallTask::onModsDownloaded()
|
||||
QtConcurrent::run(QThreadPool::globalInstance(), this, &PackInstallTask::extractMods, modsToExtract, modsToDecomp, modsToCopy);
|
||||
#endif
|
||||
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);
|
||||
} else {
|
||||
install();
|
||||
|
@ -208,17 +208,15 @@ Task::Ptr FlameAPI::getFile(const QString& addonId, const QString& fileId, std::
|
||||
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
|
||||
{
|
||||
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") } };
|
||||
}
|
||||
|
@ -568,7 +568,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
||||
m_mod_id_resolver.reset();
|
||||
connect(m_files_job.get(), &NetJob::succeeded, this, [&]() {
|
||||
m_files_job.reset();
|
||||
validateZIPResouces();
|
||||
validateZIPResources();
|
||||
});
|
||||
connect(m_files_job.get(), &NetJob::failed, [&](QString reason) {
|
||||
m_files_job.reset();
|
||||
@ -617,7 +617,7 @@ void FlameCreationTask::copyBlockedMods(QList<BlockedMod> const& blocked_mods)
|
||||
setAbortable(true);
|
||||
}
|
||||
|
||||
void FlameCreationTask::validateZIPResouces()
|
||||
void FlameCreationTask::validateZIPResources()
|
||||
{
|
||||
qDebug() << "Validating whether resources stored as .zip are in the right place";
|
||||
for (auto [fileName, targetFolder] : m_ZIP_resources) {
|
||||
@ -670,8 +670,8 @@ void FlameCreationTask::validateZIPResouces()
|
||||
validatePath(fileName, targetFolder, "datapacks");
|
||||
break;
|
||||
case PackedResourceType::ShaderPack:
|
||||
// in theroy flame API can't do this but who knows, that *may* change ?
|
||||
// better to handle it if it *does* occure in the future
|
||||
// in theory flame API can't do this but who knows, that *may* change ?
|
||||
// better to handle it if it *does* occur in the future
|
||||
validatePath(fileName, targetFolder, "shaderpacks");
|
||||
break;
|
||||
case PackedResourceType::WorldSave:
|
||||
|
@ -74,7 +74,7 @@ class FlameCreationTask final : public InstanceCreationTask {
|
||||
void idResolverSucceeded(QEventLoop&);
|
||||
void setupDownloadJob(QEventLoop&);
|
||||
void copyBlockedMods(QList<BlockedMod> const& blocked_mods);
|
||||
void validateZIPResouces();
|
||||
void validateZIPResources();
|
||||
QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion);
|
||||
|
||||
private:
|
||||
|
@ -45,7 +45,7 @@ struct Modpack {
|
||||
QIcon icon;
|
||||
};
|
||||
|
||||
typedef QList<Modpack> ModpackList;
|
||||
using ModpackList = QList<Modpack>;
|
||||
|
||||
Modpack parseDirectory(QString path);
|
||||
|
||||
|
@ -31,7 +31,7 @@ struct Modpack {
|
||||
QString packCode;
|
||||
};
|
||||
|
||||
typedef QList<Modpack> ModpackList;
|
||||
using ModpackList = QList<Modpack>;
|
||||
|
||||
} // namespace LegacyFTB
|
||||
|
||||
|
@ -111,14 +111,12 @@ Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, std::shared_ptr<QByteAr
|
||||
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
|
||||
{
|
||||
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") } };
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ class ModrinthPackExportTask : public Task {
|
||||
void collectFiles();
|
||||
void collectHashes();
|
||||
void makeApiRequest();
|
||||
void parseApiResponse(const std::shared_ptr<QByteArray> response);
|
||||
void parseApiResponse(std::shared_ptr<QByteArray> response);
|
||||
void buildZip();
|
||||
|
||||
QByteArray generateIndex();
|
||||
|
@ -33,6 +33,6 @@ class TechnicPackProcessor : public QObject {
|
||||
const QString& instIcon,
|
||||
const QString& stagingPath,
|
||||
const QString& minecraftVersion = QString(),
|
||||
const bool isSolder = false);
|
||||
bool isSolder = false);
|
||||
};
|
||||
} // namespace Technic
|
||||
|
@ -89,4 +89,4 @@ QNetworkReply* Download::getReply(QNetworkRequest& request)
|
||||
{
|
||||
return m_network->get(request);
|
||||
}
|
||||
} // namespace Net
|
||||
} // namespace Net
|
||||
|
@ -36,11 +36,16 @@
|
||||
*/
|
||||
|
||||
#include "NetJob.h"
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
#include "Application.h"
|
||||
#endif
|
||||
|
||||
NetJob::NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network)
|
||||
: ConcurrentTask(nullptr, job_name, APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()), m_network(network)
|
||||
{}
|
||||
NetJob::NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network) : ConcurrentTask(nullptr, job_name), m_network(network)
|
||||
{
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
setMaxConcurrent(APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
|
||||
#endif
|
||||
}
|
||||
|
||||
auto NetJob::addNetAction(NetAction::Ptr action) -> bool
|
||||
{
|
||||
|
@ -46,6 +46,7 @@
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
#include "Application.h"
|
||||
#endif
|
||||
#include "BuildConfig.h"
|
||||
|
||||
#include "net/NetAction.h"
|
||||
|
||||
@ -111,6 +112,8 @@ void NetRequest::executeTask()
|
||||
m_last_progress_bytes = 0;
|
||||
|
||||
QNetworkReply* rep = getReply(request);
|
||||
if (rep == nullptr) // it failed
|
||||
return;
|
||||
m_reply.reset(rep);
|
||||
connect(rep, &QNetworkReply::downloadProgress, this, &NetRequest::downloadProgress);
|
||||
connect(rep, &QNetworkReply::finished, this, &NetRequest::downloadFinished);
|
||||
|
@ -87,7 +87,7 @@ class NetRequest : public NetAction {
|
||||
std::unique_ptr<Sink> m_sink;
|
||||
Options m_options;
|
||||
|
||||
typedef const QLoggingCategory& (*logCatFunc)();
|
||||
using logCatFunc = const QLoggingCategory& (*)();
|
||||
logCatFunc logCat = taskUploadLogC;
|
||||
|
||||
std::chrono::steady_clock m_clock;
|
||||
|
39
launcher/net/StaticHeaderProxy.h
Normal file
39
launcher/net/StaticHeaderProxy.h
Normal 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
|
@ -51,4 +51,4 @@ class NewsEntry : public QObject {
|
||||
QString link;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<NewsEntry> NewsEntryPtr;
|
||||
using NewsEntryPtr = std::shared_ptr<NewsEntry>;
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
class IPathMatcher {
|
||||
public:
|
||||
typedef std::shared_ptr<IPathMatcher> Ptr;
|
||||
using Ptr = std::shared_ptr<IPathMatcher>;
|
||||
|
||||
public:
|
||||
virtual ~IPathMatcher() {}
|
||||
|
@ -9,6 +9,7 @@
|
||||
<file>scalable/copy.svg</file>
|
||||
<file>scalable/coremods.svg</file>
|
||||
<file>scalable/custom-commands.svg</file>
|
||||
<file>scalable/environment-variables.svg</file>
|
||||
<file>scalable/discord.svg</file>
|
||||
<file>scalable/externaltools.svg</file>
|
||||
<file>scalable/help.svg</file>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user