Merge remote-tracking branch 'upstream/develop' into sysprops
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
This commit is contained in:
commit
cf33927f21
4
.github/workflows/backport.yml
vendored
4
.github/workflows/backport.yml
vendored
@ -20,11 +20,11 @@ jobs:
|
|||||||
if: github.repository_owner == 'PrismLauncher' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
|
if: github.repository_owner == 'PrismLauncher' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
- name: Create backport PRs
|
- name: Create backport PRs
|
||||||
uses: korthout/backport-action@v1.3.1
|
uses: korthout/backport-action@v1.4.0
|
||||||
with:
|
with:
|
||||||
# Config README: https://github.com/korthout/backport-action#backport-action
|
# Config README: https://github.com/korthout/backport-action#backport-action
|
||||||
pull_description: |-
|
pull_description: |-
|
||||||
|
134
.github/workflows/build.yml
vendored
134
.github/workflows/build.yml
vendored
@ -24,6 +24,12 @@ on:
|
|||||||
CACHIX_AUTH_TOKEN:
|
CACHIX_AUTH_TOKEN:
|
||||||
description: Private token for authenticating against Cachix cache
|
description: Private token for authenticating against Cachix cache
|
||||||
required: false
|
required: false
|
||||||
|
GPG_PRIVATE_KEY:
|
||||||
|
description: Private key for AppImage signing
|
||||||
|
required: false
|
||||||
|
GPG_PRIVATE_KEY_ID:
|
||||||
|
description: ID for the GPG_PRIVATE_KEY, to select the signing key
|
||||||
|
required: false
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@ -31,56 +37,43 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
|
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
qt_ver: 5
|
qt_ver: 5
|
||||||
|
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: linux
|
qt_host: linux
|
||||||
qt_arch: ''
|
qt_arch: ""
|
||||||
qt_version: '6.2.4'
|
qt_version: "6.2.4"
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: "qt5compat qtimageformats"
|
||||||
qt_tools: ''
|
qt_tools: ""
|
||||||
|
|
||||||
- os: windows-2022
|
- os: windows-2022
|
||||||
name: "Windows-MinGW-w64"
|
name: "Windows-MinGW-w64"
|
||||||
msystem: clang64
|
msystem: clang64
|
||||||
vcvars_arch: 'amd64_x86'
|
vcvars_arch: "amd64_x86"
|
||||||
|
|
||||||
- os: windows-2022
|
|
||||||
name: "Windows-MSVC-Legacy"
|
|
||||||
msystem: ''
|
|
||||||
architecture: 'win32'
|
|
||||||
vcvars_arch: 'amd64_x86'
|
|
||||||
qt_ver: 5
|
|
||||||
qt_host: windows
|
|
||||||
qt_arch: 'win32_msvc2019'
|
|
||||||
qt_version: '5.15.2'
|
|
||||||
qt_modules: ''
|
|
||||||
qt_tools: 'tools_openssl_x86'
|
|
||||||
|
|
||||||
- os: windows-2022
|
- os: windows-2022
|
||||||
name: "Windows-MSVC"
|
name: "Windows-MSVC"
|
||||||
msystem: ''
|
msystem: ""
|
||||||
architecture: 'x64'
|
architecture: "x64"
|
||||||
vcvars_arch: 'amd64'
|
vcvars_arch: "amd64"
|
||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: windows
|
qt_host: windows
|
||||||
qt_arch: ''
|
qt_arch: ''
|
||||||
qt_version: '6.5.2'
|
qt_version: '6.6.0'
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_tools: ''
|
qt_tools: ''
|
||||||
|
|
||||||
- os: windows-2022
|
- os: windows-2022
|
||||||
name: "Windows-MSVC-arm64"
|
name: "Windows-MSVC-arm64"
|
||||||
msystem: ''
|
msystem: ""
|
||||||
architecture: 'arm64'
|
architecture: "arm64"
|
||||||
vcvars_arch: 'amd64_arm64'
|
vcvars_arch: "amd64_arm64"
|
||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: windows
|
qt_host: windows
|
||||||
qt_arch: 'win64_msvc2019_arm64'
|
qt_arch: 'win64_msvc2019_arm64'
|
||||||
qt_version: '6.5.2'
|
qt_version: '6.6.0'
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_tools: ''
|
qt_tools: ''
|
||||||
|
|
||||||
@ -90,7 +83,7 @@ jobs:
|
|||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: mac
|
qt_host: mac
|
||||||
qt_arch: ''
|
qt_arch: ''
|
||||||
qt_version: '6.5.2'
|
qt_version: '6.6.0'
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_tools: ''
|
qt_tools: ''
|
||||||
|
|
||||||
@ -99,9 +92,9 @@ jobs:
|
|||||||
macosx_deployment_target: 10.13
|
macosx_deployment_target: 10.13
|
||||||
qt_ver: 5
|
qt_ver: 5
|
||||||
qt_host: mac
|
qt_host: mac
|
||||||
qt_version: '5.15.2'
|
qt_version: "5.15.2"
|
||||||
qt_modules: ''
|
qt_modules: ""
|
||||||
qt_tools: ''
|
qt_tools: ""
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
@ -119,11 +112,11 @@ jobs:
|
|||||||
# PREPARE
|
# PREPARE
|
||||||
##
|
##
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
submodules: "true"
|
||||||
|
|
||||||
- name: 'Setup MSYS2'
|
- name: "Setup MSYS2"
|
||||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||||
uses: msys2/setup-msys2@v2
|
uses: msys2/setup-msys2@v2
|
||||||
with:
|
with:
|
||||||
@ -152,13 +145,13 @@ jobs:
|
|||||||
|
|
||||||
- name: Setup ccache
|
- name: Setup ccache
|
||||||
if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug'
|
if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug'
|
||||||
uses: hendrikmuhs/ccache-action@v1.2.9
|
uses: hendrikmuhs/ccache-action@v1.2.10
|
||||||
with:
|
with:
|
||||||
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }}
|
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }}
|
||||||
|
|
||||||
- name: Retrieve ccache cache (Windows MinGW-w64)
|
- name: Retrieve ccache cache (Windows MinGW-w64)
|
||||||
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
||||||
uses: actions/cache@v3.3.1
|
uses: actions/cache@v3.3.2
|
||||||
with:
|
with:
|
||||||
path: '${{ github.workspace }}\.ccache'
|
path: '${{ github.workspace }}\.ccache'
|
||||||
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
|
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
|
||||||
@ -208,12 +201,12 @@ jobs:
|
|||||||
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v3
|
||||||
with:
|
with:
|
||||||
aqtversion: '==3.1.*'
|
aqtversion: "==3.1.*"
|
||||||
py7zrversion: '>=0.20.2'
|
py7zrversion: ">=0.20.2"
|
||||||
version: ${{ matrix.qt_version }}
|
version: ${{ matrix.qt_version }}
|
||||||
host: 'windows'
|
host: "windows"
|
||||||
target: 'desktop'
|
target: "desktop"
|
||||||
arch: ''
|
arch: ""
|
||||||
modules: ${{ matrix.qt_modules }}
|
modules: ${{ matrix.qt_modules }}
|
||||||
tools: ${{ matrix.qt_tools }}
|
tools: ${{ matrix.qt_tools }}
|
||||||
cache: ${{ inputs.is_qt_cached }}
|
cache: ${{ inputs.is_qt_cached }}
|
||||||
@ -225,11 +218,11 @@ jobs:
|
|||||||
if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS' || (runner.os == 'Windows' && matrix.msystem == '')
|
if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS' || (runner.os == 'Windows' && matrix.msystem == '')
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v3
|
||||||
with:
|
with:
|
||||||
aqtversion: '==3.1.*'
|
aqtversion: "==3.1.*"
|
||||||
py7zrversion: '>=0.20.2'
|
py7zrversion: ">=0.20.2"
|
||||||
version: ${{ matrix.qt_version }}
|
version: ${{ matrix.qt_version }}
|
||||||
host: ${{ matrix.qt_host }}
|
host: ${{ matrix.qt_host }}
|
||||||
target: 'desktop'
|
target: "desktop"
|
||||||
arch: ${{ matrix.qt_arch }}
|
arch: ${{ matrix.qt_arch }}
|
||||||
modules: ${{ matrix.qt_modules }}
|
modules: ${{ matrix.qt_modules }}
|
||||||
tools: ${{ matrix.qt_tools }}
|
tools: ${{ matrix.qt_tools }}
|
||||||
@ -249,6 +242,8 @@ jobs:
|
|||||||
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage"
|
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage"
|
||||||
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage"
|
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage"
|
||||||
|
|
||||||
|
wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage"
|
||||||
|
|
||||||
${{ github.workspace }}/.github/scripts/prepare_JREs.sh
|
${{ github.workspace }}/.github/scripts/prepare_JREs.sh
|
||||||
sudo apt install libopengl0
|
sudo apt install libopengl0
|
||||||
|
|
||||||
@ -275,12 +270,12 @@ jobs:
|
|||||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} -G Ninja
|
||||||
|
|
||||||
- name: Configure CMake (Windows MSVC)
|
- name: Configure CMake (Windows MSVC)
|
||||||
if: runner.os == 'Windows' && matrix.msystem == ''
|
if: runner.os == 'Windows' && matrix.msystem == ''
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }}
|
||||||
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
|
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
|
||||||
if ("${{ env.CCACHE_VAR }}")
|
if ("${{ env.CCACHE_VAR }}")
|
||||||
{
|
{
|
||||||
@ -295,7 +290,7 @@ jobs:
|
|||||||
- name: Configure CMake (Linux)
|
- name: Configure CMake (Linux)
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -G Ninja
|
||||||
|
|
||||||
##
|
##
|
||||||
# BUILD
|
# BUILD
|
||||||
@ -387,14 +382,13 @@ jobs:
|
|||||||
cd ${{ env.INSTALL_DIR }}
|
cd ${{ env.INSTALL_DIR }}
|
||||||
if ("${{ matrix.qt_ver }}" -eq "5")
|
if ("${{ matrix.qt_ver }}" -eq "5")
|
||||||
{
|
{
|
||||||
Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll
|
Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll
|
||||||
Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll
|
Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll
|
||||||
}
|
}
|
||||||
cd ${{ github.workspace }}
|
cd ${{ github.workspace }}
|
||||||
|
|
||||||
Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
|
Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
|
||||||
|
|
||||||
|
|
||||||
- name: Fetch codesign certificate (Windows)
|
- name: Fetch codesign certificate (Windows)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
shell: bash # yes, we are not using MSYS2 or PowerShell here
|
shell: bash # yes, we are not using MSYS2 or PowerShell here
|
||||||
@ -407,7 +401,7 @@ jobs:
|
|||||||
if (Get-Content ./codesign.pfx){
|
if (Get-Content ./codesign.pfx){
|
||||||
cd ${{ env.INSTALL_DIR }}
|
cd ${{ env.INSTALL_DIR }}
|
||||||
# We ship the exact same executable for portable and non-portable editions, so signing just once is fine
|
# We ship the exact same executable for portable and non-portable editions, so signing just once is fine
|
||||||
SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_filelink.exe
|
SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_updater.exe prismlauncher_filelink.exe
|
||||||
} else {
|
} else {
|
||||||
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||||
}
|
}
|
||||||
@ -466,11 +460,15 @@ jobs:
|
|||||||
- name: Package AppImage (Linux)
|
- name: Package AppImage (Linux)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
shell: bash
|
shell: bash
|
||||||
|
env:
|
||||||
|
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
run: |
|
run: |
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
|
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
|
||||||
|
|
||||||
mv ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.metainfo.xml ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.appdata.xml
|
mv ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.metainfo.xml ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.appdata.xml
|
||||||
export "NO_APPSTREAM=1" # we have to skip appstream checking because appstream on ubuntu 20.04 is outdated
|
export "NO_APPSTREAM=1" # we have to skip appstream checking because appstream on ubuntu 20.04 is outdated
|
||||||
export OUTPUT="PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
|
|
||||||
|
export OUTPUT="PrismLauncher-Linux-x86_64.AppImage"
|
||||||
|
|
||||||
chmod +x linuxdeploy-*.AppImage
|
chmod +x linuxdeploy-*.AppImage
|
||||||
|
|
||||||
@ -481,7 +479,7 @@ jobs:
|
|||||||
|
|
||||||
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk
|
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk
|
||||||
|
|
||||||
cp -r /home/runner/work/PrismLauncher/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
cp -r ${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
||||||
|
|
||||||
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||||
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||||
@ -494,8 +492,25 @@ jobs:
|
|||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
|
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
|
||||||
export LD_LIBRARY_PATH
|
export LD_LIBRARY_PATH
|
||||||
|
|
||||||
|
chmod +x AppImageUpdate-x86_64.AppImage
|
||||||
|
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"
|
||||||
|
|
||||||
|
if [ '${{ secrets.GPG_PRIVATE_KEY_ID }}' != '' ]; then
|
||||||
|
export SIGN=1
|
||||||
|
export SIGN_KEY=${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||||
|
mkdir -p ~/.gnupg/
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
|
||||||
./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.prismlauncher.PrismLauncher.svg
|
./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.prismlauncher.PrismLauncher.svg
|
||||||
|
|
||||||
|
mv "PrismLauncher-Linux-x86_64.AppImage" "PrismLauncher-Linux-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
|
||||||
|
|
||||||
##
|
##
|
||||||
# UPLOAD BUILDS
|
# UPLOAD BUILDS
|
||||||
##
|
##
|
||||||
@ -532,14 +547,14 @@ jobs:
|
|||||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ runner.os }}-Qt5-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PrismLauncher.tar.gz
|
path: PrismLauncher.tar.gz
|
||||||
|
|
||||||
- name: Upload binary tarball (Linux, portable, Qt 5)
|
- name: Upload binary tarball (Linux, portable, Qt 5)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ runner.os }}-Qt5-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PrismLauncher-portable.tar.gz
|
path: PrismLauncher-portable.tar.gz
|
||||||
|
|
||||||
- name: Upload binary tarball (Linux, Qt 6)
|
- name: Upload binary tarball (Linux, Qt 6)
|
||||||
@ -563,6 +578,13 @@ jobs:
|
|||||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||||
path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||||
|
|
||||||
|
- name: Upload AppImage Zsync (Linux)
|
||||||
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync
|
||||||
|
path: PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||||
|
|
||||||
- name: ccache stats (Windows MinGW-w64)
|
- name: ccache stats (Windows MinGW-w64)
|
||||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
@ -576,10 +598,10 @@ jobs:
|
|||||||
options: --privileged
|
options: --privileged
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
if: inputs.build_type == 'Debug'
|
if: inputs.build_type == 'Debug'
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
submodules: "true"
|
||||||
- name: Build Flatpak (Linux)
|
- name: Build Flatpak (Linux)
|
||||||
if: inputs.build_type == 'Debug'
|
if: inputs.build_type == 'Debug'
|
||||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
||||||
|
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
|
|
||||||
|
29
.github/workflows/trigger_builds.yml
vendored
29
.github/workflows/trigger_builds.yml
vendored
@ -3,26 +3,25 @@ name: Build Application
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'renovate/**'
|
- "renovate/**"
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '**.md'
|
- "**.md"
|
||||||
- '**/LICENSE'
|
- "**/LICENSE"
|
||||||
- 'flake.lock'
|
- "flake.lock"
|
||||||
- 'packages/**'
|
- "packages/**"
|
||||||
- '.github/ISSUE_TEMPLATE/**'
|
- ".github/ISSUE_TEMPLATE/**"
|
||||||
- '.markdownlint**'
|
- ".markdownlint**"
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '**.md'
|
- "**.md"
|
||||||
- '**/LICENSE'
|
- "**/LICENSE"
|
||||||
- 'flake.lock'
|
- "flake.lock"
|
||||||
- 'packages/**'
|
- "packages/**"
|
||||||
- '.github/ISSUE_TEMPLATE/**'
|
- ".github/ISSUE_TEMPLATE/**"
|
||||||
- '.markdownlint**'
|
- ".markdownlint**"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
build_debug:
|
build_debug:
|
||||||
name: Build Debug
|
name: Build Debug
|
||||||
uses: ./.github/workflows/build.yml
|
uses: ./.github/workflows/build.yml
|
||||||
@ -34,3 +33,5 @@ jobs:
|
|||||||
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
||||||
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||||
|
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
|
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||||
|
31
.github/workflows/trigger_release.yml
vendored
31
.github/workflows/trigger_release.yml
vendored
@ -3,10 +3,9 @@ name: Build Application and Make Release
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- '*'
|
- "*"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
build_release:
|
build_release:
|
||||||
name: Build Release
|
name: Build Release
|
||||||
uses: ./.github/workflows/build.yml
|
uses: ./.github/workflows/build.yml
|
||||||
@ -18,6 +17,8 @@ jobs:
|
|||||||
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
||||||
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||||
|
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
|
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||||
|
|
||||||
create_release:
|
create_release:
|
||||||
needs: build_release
|
needs: build_release
|
||||||
@ -26,10 +27,10 @@ jobs:
|
|||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
submodules: "true"
|
||||||
path: 'PrismLauncher-source'
|
path: "PrismLauncher-source"
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v3
|
||||||
- name: Grab and store version
|
- name: Grab and store version
|
||||||
@ -41,9 +42,10 @@ jobs:
|
|||||||
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
|
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
|
||||||
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||||
mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||||
mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
|
mv PrismLauncher-Linux-Qt5-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
|
||||||
mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
|
mv PrismLauncher-Linux-Qt5*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz
|
||||||
mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage
|
mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage
|
||||||
|
mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||||
mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
||||||
mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
|
mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
|
||||||
|
|
||||||
@ -78,25 +80,22 @@ jobs:
|
|||||||
- name: Create release
|
- name: Create release
|
||||||
id: create_release
|
id: create_release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
tag_name: ${{ github.ref }}
|
tag_name: ${{ github.ref }}
|
||||||
name: Prism Launcher ${{ env.VERSION }}
|
name: Prism Launcher ${{ env.VERSION }}
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: false
|
prerelease: false
|
||||||
files: |
|
files: |
|
||||||
PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
|
PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz
|
||||||
PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
|
PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
|
||||||
PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage
|
PrismLauncher-Linux-x86_64.AppImage
|
||||||
|
PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||||
PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||||
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||||
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MinGW-w64-Setup-${{ env.VERSION }}.exe
|
PrismLauncher-Windows-MinGW-w64-Setup-${{ env.VERSION }}.exe
|
||||||
PrismLauncher-Windows-MSVC-Legacy-${{ env.VERSION }}.zip
|
|
||||||
PrismLauncher-Windows-MSVC-Legacy-Portable-${{ env.VERSION }}.zip
|
|
||||||
PrismLauncher-Windows-MSVC-Legacy-Setup-${{ env.VERSION }}.exe
|
|
||||||
PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MSVC-arm64-Portable-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MSVC-arm64-Portable-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MSVC-arm64-Setup-${{ env.VERSION }}.exe
|
PrismLauncher-Windows-MSVC-arm64-Setup-${{ env.VERSION }}.exe
|
||||||
|
8
.github/workflows/update-flake.yml
vendored
8
.github/workflows/update-flake.yml
vendored
@ -16,13 +16,15 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: cachix/install-nix-action@v22
|
- uses: cachix/install-nix-action@6a9a9e84a173d90b3ffb42c5ddaf9ea033fad011 # v23
|
||||||
|
|
||||||
- uses: DeterminateSystems/update-flake-lock@v19
|
- uses: DeterminateSystems/update-flake-lock@v20
|
||||||
with:
|
with:
|
||||||
commit-msg: "chore(nix): update lockfile"
|
commit-msg: "chore(nix): update lockfile"
|
||||||
pr-title: "chore(nix): update lockfile"
|
pr-title: "chore(nix): update lockfile"
|
||||||
pr-labels: |
|
pr-labels: |
|
||||||
Linux
|
Linux
|
||||||
|
packaging
|
||||||
simple change
|
simple change
|
||||||
|
changelog:omit
|
||||||
|
@ -33,6 +33,13 @@ if(MSVC)
|
|||||||
# Use /W4 as /Wall includes unnesserey warnings such as added padding to structs
|
# Use /W4 as /Wall includes unnesserey warnings such as added padding to structs
|
||||||
set(CMAKE_CXX_FLAGS "/GS /permissive- /W4 ${CMAKE_CXX_FLAGS}")
|
set(CMAKE_CXX_FLAGS "/GS /permissive- /W4 ${CMAKE_CXX_FLAGS}")
|
||||||
|
|
||||||
|
# /EHs Enables stack unwind semantics for standard C++ exceptions to ensure stackframes are unwound
|
||||||
|
# and object deconstructors are called when an exception is caught.
|
||||||
|
# without it memory leaks and a warning is printed
|
||||||
|
# /EHc tells the compiler to assume that functions declared as extern "C" never throw a C++ exception
|
||||||
|
# This appears to not always be a defualt compiler option in CMAKE
|
||||||
|
set(CMAKE_CXX_FLAGS "/EHsc ${CMAKE_CXX_FLAGS}")
|
||||||
|
|
||||||
# LINK accepts /SUBSYSTEM whics sets if we are a WINDOWS (gui) or a CONSOLE programs
|
# LINK accepts /SUBSYSTEM whics sets if we are a WINDOWS (gui) or a CONSOLE programs
|
||||||
# This implicitly selects an entrypoint specific to the subsystem selected
|
# This implicitly selects an entrypoint specific to the subsystem selected
|
||||||
# qtmain/QtEntryPointLib provides the correct entrypoint (wWinMain) for gui programs
|
# qtmain/QtEntryPointLib provides the correct entrypoint (wWinMain) for gui programs
|
||||||
@ -85,38 +92,39 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0")
|
|||||||
# set CXXFLAGS for build targets
|
# set CXXFLAGS for build targets
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
|
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||||
|
|
||||||
option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" on)
|
option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF)
|
||||||
|
|
||||||
# If this is a Debug build turn on address sanitiser
|
# If this is a Debug build turn on address sanitiser
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND DEBUG_ADDRESS_SANITIZER)
|
if ((CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") AND DEBUG_ADDRESS_SANITIZER)
|
||||||
message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off")
|
message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off")
|
||||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||||
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||||
# using clang with clang-cl front end
|
# using clang with clang-cl front end
|
||||||
message(STATUS "Address Sanitizer available on Clang MSVC frontend")
|
message(STATUS "Address Sanitizer available on Clang MSVC frontend")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
|
||||||
else()
|
else()
|
||||||
# AppleClang and Clang
|
# AppleClang and Clang
|
||||||
message(STATUS "Address Sanitizer available on Clang")
|
message(STATUS "Address Sanitizer available on Clang")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||||
endif()
|
endif()
|
||||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||||
# GCC
|
# GCC
|
||||||
message(STATUS "Address Sanitizer available on GCC")
|
message(STATUS "Address Sanitizer available on GCC")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||||
link_libraries("asan")
|
link_libraries("asan")
|
||||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||||
message(STATUS "Address Sanitizer available on MSVC")
|
message(STATUS "Address Sanitizer available on MSVC")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
|
||||||
else()
|
else()
|
||||||
message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}")
|
message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
option(ENABLE_LTO "Enable Link Time Optimization" off)
|
option(ENABLE_LTO "Enable Link Time Optimization" off)
|
||||||
|
|
||||||
if(ENABLE_LTO)
|
if(ENABLE_LTO)
|
||||||
@ -180,8 +188,11 @@ set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_M
|
|||||||
# Build platform.
|
# Build platform.
|
||||||
set(Launcher_BUILD_PLATFORM "unknown" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.")
|
set(Launcher_BUILD_PLATFORM "unknown" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.")
|
||||||
|
|
||||||
# Channel list URL
|
# Github repo URL with releases for updater
|
||||||
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
|
set(Launcher_UPDATER_GITHUB_REPO "https://github.com/PrismLauncher/PrismLauncher" CACHE STRING "Base github URL for the updater.")
|
||||||
|
|
||||||
|
# Name to help updater identify valid artifacts
|
||||||
|
set(Launcher_BUILD_ARTIFACT "" CACHE STRING "Artifact name to help the updater identify valid artifacts.")
|
||||||
|
|
||||||
# The metadata server
|
# The metadata server
|
||||||
set(Launcher_META_URL "https://meta.prismlauncher.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
|
set(Launcher_META_URL "https://meta.prismlauncher.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
|
||||||
@ -208,6 +219,18 @@ set(Launcher_SUBREDDIT_URL "https://prismlauncher.org/reddit" CACHE STRING "URL
|
|||||||
set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
|
set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
|
||||||
set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against")
|
set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against")
|
||||||
|
|
||||||
|
# Native libraries
|
||||||
|
if(UNIX AND APPLE)
|
||||||
|
set(Launcher_GLFW_LIBRARY_NAME "libglfw.dylib" CACHE STRING "Name of native glfw library")
|
||||||
|
set(Launcher_OPENAL_LIBRARY_NAME "libopenal.dylib" CACHE STRING "Name of native openal library")
|
||||||
|
elseif(UNIX)
|
||||||
|
set(Launcher_GLFW_LIBRARY_NAME "libglfw.so" CACHE STRING "Name of native glfw library")
|
||||||
|
set(Launcher_OPENAL_LIBRARY_NAME "libopenal.so" CACHE STRING "Name of native openal library")
|
||||||
|
elseif(WIN32)
|
||||||
|
set(Launcher_GLFW_LIBRARY_NAME "glfw.dll" CACHE STRING "Name of native glfw library")
|
||||||
|
set(Launcher_OPENAL_LIBRARY_NAME "OpenAL.dll" CACHE STRING "Name of native openal library")
|
||||||
|
endif()
|
||||||
|
|
||||||
# API Keys
|
# API Keys
|
||||||
# NOTE: These API keys are here for convenience. If you rebrand this software or intend to break the terms of service
|
# NOTE: These API keys are here for convenience. If you rebrand this software or intend to break the terms of service
|
||||||
# of these platforms, please change these API keys beforehand.
|
# of these platforms, please change these API keys beforehand.
|
||||||
@ -225,6 +248,11 @@ set(Launcher_MSA_CLIENT_ID "c36a9fb6-4f2a-41ff-90bd-ae7cc92031eb" CACHE STRING "
|
|||||||
# This key was issued specifically for Prism Launcher
|
# This key was issued specifically for Prism Launcher
|
||||||
set(Launcher_CURSEFORGE_API_KEY "$2a$10$wuAJuNZuted3NORVmpgUC.m8sI.pv1tOPKZyBgLFGjxFp/br0lZCC" CACHE STRING "API key for the CurseForge platform")
|
set(Launcher_CURSEFORGE_API_KEY "$2a$10$wuAJuNZuted3NORVmpgUC.m8sI.pv1tOPKZyBgLFGjxFp/br0lZCC" CACHE STRING "API key for the CurseForge platform")
|
||||||
|
|
||||||
|
set(Launcher_COMPILER_NAME ${CMAKE_CXX_COMPILER_ID})
|
||||||
|
set(Launcher_COMPILER_VERSION ${CMAKE_CXX_COMPILER_VERSION})
|
||||||
|
set(Launcher_COMPILER_TARGET_SYSTEM ${CMAKE_SYSTEM_NAME})
|
||||||
|
set(Launcher_COMPILER_TARGET_SYSTEM_VERSION ${CMAKE_SYSTEM_VERSION})
|
||||||
|
set(Launcher_COMPILER_TARGET_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR})
|
||||||
|
|
||||||
#### Check the current Git commit and branch
|
#### Check the current Git commit and branch
|
||||||
include(GetGitRevisionDescription)
|
include(GetGitRevisionDescription)
|
||||||
@ -319,6 +347,11 @@ add_subdirectory(program_info)
|
|||||||
####################################### Install layout #######################################
|
####################################### Install layout #######################################
|
||||||
|
|
||||||
set(Launcher_ENABLE_UPDATER NO)
|
set(Launcher_ENABLE_UPDATER NO)
|
||||||
|
set(Launcher_BUILD_UPDATER NO)
|
||||||
|
|
||||||
|
if (NOT APPLE AND (NOT Launcher_UPDATER_GITHUB_REPO STREQUAL "" AND NOT Launcher_BUILD_ARTIFACT STREQUAL ""))
|
||||||
|
set(Launcher_BUILD_UPDATER YES)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT (UNIX AND APPLE))
|
if(NOT (UNIX AND APPLE))
|
||||||
# Install "portable.txt" if selected component is "portable"
|
# Install "portable.txt" if selected component is "portable"
|
||||||
|
@ -50,7 +50,7 @@ Feel free to create a GitHub issue if you find a bug or want to suggest a new fe
|
|||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|
||||||
The translation effort for Prism Launcher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at <https://github.com/PrismLauncher/Translations>
|
The translation effort for Prism Launcher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at <https://github.com/PrismLauncher/Translations>.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <qstringliteral.h>
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
@ -59,8 +60,16 @@ Config::Config()
|
|||||||
VERSION_MINOR = @Launcher_VERSION_MINOR@;
|
VERSION_MINOR = @Launcher_VERSION_MINOR@;
|
||||||
|
|
||||||
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
|
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
|
||||||
|
BUILD_ARTIFACT = "@Launcher_BUILD_ARTIFACT@";
|
||||||
BUILD_DATE = "@Launcher_BUILD_TIMESTAMP@";
|
BUILD_DATE = "@Launcher_BUILD_TIMESTAMP@";
|
||||||
UPDATER_BASE = "@Launcher_UPDATER_BASE@";
|
UPDATER_GITHUB_REPO = "@Launcher_UPDATER_GITHUB_REPO@";
|
||||||
|
|
||||||
|
COMPILER_NAME = "@Launcher_COMPILER_NAME@";
|
||||||
|
COMPILER_VERSION = "@Launcher_COMPILER_VERSION@";
|
||||||
|
|
||||||
|
COMPILER_TARGET_SYSTEM = "@Launcher_COMPILER_TARGET_SYSTEM@";
|
||||||
|
COMPILER_TARGET_SYSTEM_VERSION = "@Launcher_COMPILER_TARGET_SYSTEM_VERSION@";
|
||||||
|
COMPILER_TARGET_SYSTEM_PROCESSOR = "@Launcher_COMPILER_TARGET_PROCESSOR@";
|
||||||
|
|
||||||
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
|
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
|
||||||
MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
|
MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
|
||||||
@ -68,6 +77,8 @@ Config::Config()
|
|||||||
if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
|
if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
|
||||||
{
|
{
|
||||||
UPDATER_ENABLED = true;
|
UPDATER_ENABLED = true;
|
||||||
|
} else if(!UPDATER_GITHUB_REPO.isEmpty() && !BUILD_ARTIFACT.isEmpty()) {
|
||||||
|
UPDATER_ENABLED = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
||||||
@ -89,9 +100,6 @@ Config::Config()
|
|||||||
{
|
{
|
||||||
VERSION_CHANNEL = GIT_REFSPEC;
|
VERSION_CHANNEL = GIT_REFSPEC;
|
||||||
VERSION_CHANNEL.remove("refs/heads/");
|
VERSION_CHANNEL.remove("refs/heads/");
|
||||||
if(!UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty()) {
|
|
||||||
UPDATER_ENABLED = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (!GIT_COMMIT.isEmpty())
|
else if (!GIT_COMMIT.isEmpty())
|
||||||
{
|
{
|
||||||
@ -110,6 +118,9 @@ Config::Config()
|
|||||||
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
|
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
|
||||||
META_URL = "@Launcher_META_URL@";
|
META_URL = "@Launcher_META_URL@";
|
||||||
|
|
||||||
|
GLFW_LIBRARY_NAME = "@Launcher_GLFW_LIBRARY_NAME@";
|
||||||
|
OPENAL_LIBRARY_NAME = "@Launcher_OPENAL_LIBRARY_NAME@";
|
||||||
|
|
||||||
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
|
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
|
||||||
TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@";
|
TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@";
|
||||||
MATRIX_URL = "@Launcher_MATRIX_URL@";
|
MATRIX_URL = "@Launcher_MATRIX_URL@";
|
||||||
@ -133,3 +144,16 @@ QString Config::printableVersionString() const
|
|||||||
}
|
}
|
||||||
return vstr;
|
return vstr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Config::compilerID() const
|
||||||
|
{
|
||||||
|
if (COMPILER_VERSION.isEmpty())
|
||||||
|
return COMPILER_NAME;
|
||||||
|
return QStringLiteral("%1 - %2").arg(COMPILER_NAME).arg(COMPILER_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Config::systemID() const
|
||||||
|
{
|
||||||
|
return QStringLiteral("%1 %2 %3").arg(COMPILER_TARGET_SYSTEM, COMPILER_TARGET_SYSTEM_VERSION, COMPILER_TARGET_SYSTEM_PROCESSOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -71,11 +71,29 @@ class Config {
|
|||||||
/// A short string identifying this build's platform or distribution.
|
/// A short string identifying this build's platform or distribution.
|
||||||
QString BUILD_PLATFORM;
|
QString BUILD_PLATFORM;
|
||||||
|
|
||||||
|
/// A short string identifying this build's valid artifacts int he updater. For example, "lin64" or "win32".
|
||||||
|
QString BUILD_ARTIFACT;
|
||||||
|
|
||||||
/// A string containing the build timestamp
|
/// A string containing the build timestamp
|
||||||
QString BUILD_DATE;
|
QString BUILD_DATE;
|
||||||
|
|
||||||
|
/// A string identifying the compiler use to build
|
||||||
|
QString COMPILER_NAME;
|
||||||
|
|
||||||
|
/// A string identifying the compiler version used to build
|
||||||
|
QString COMPILER_VERSION;
|
||||||
|
|
||||||
|
/// A string identifying the compiler target system os
|
||||||
|
QString COMPILER_TARGET_SYSTEM;
|
||||||
|
|
||||||
|
/// A String identifying the compiler target system version
|
||||||
|
QString COMPILER_TARGET_SYSTEM_VERSION;
|
||||||
|
|
||||||
|
/// A String identifying the compiler target processor
|
||||||
|
QString COMPILER_TARGET_SYSTEM_PROCESSOR;
|
||||||
|
|
||||||
/// URL for the updater's channel
|
/// URL for the updater's channel
|
||||||
QString UPDATER_BASE;
|
QString UPDATER_GITHUB_REPO;
|
||||||
|
|
||||||
/// The public key used to sign releases for the Sparkle updater appcast
|
/// The public key used to sign releases for the Sparkle updater appcast
|
||||||
QString MAC_SPARKLE_PUB_KEY;
|
QString MAC_SPARKLE_PUB_KEY;
|
||||||
@ -134,6 +152,9 @@ class Config {
|
|||||||
*/
|
*/
|
||||||
QString META_URL;
|
QString META_URL;
|
||||||
|
|
||||||
|
QString GLFW_LIBRARY_NAME;
|
||||||
|
QString OPENAL_LIBRARY_NAME;
|
||||||
|
|
||||||
QString BUG_TRACKER_URL;
|
QString BUG_TRACKER_URL;
|
||||||
QString TRANSLATIONS_URL;
|
QString TRANSLATIONS_URL;
|
||||||
QString MATRIX_URL;
|
QString MATRIX_URL;
|
||||||
@ -172,6 +193,18 @@ class Config {
|
|||||||
* \return The version number in string format (major.minor.revision.build).
|
* \return The version number in string format (major.minor.revision.build).
|
||||||
*/
|
*/
|
||||||
QString printableVersionString() const;
|
QString printableVersionString() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Compiler ID String
|
||||||
|
* \return a string of the form "Name - Version" of just "Name" if the version is empty
|
||||||
|
*/
|
||||||
|
QString compilerID() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief System ID String
|
||||||
|
* \return a string of the form "OS Verison Processor"
|
||||||
|
*/
|
||||||
|
QString systemID() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const Config BuildConfig;
|
extern const Config BuildConfig;
|
||||||
|
155
cmake/CompilerWarnings.cmake
Normal file
155
cmake/CompilerWarnings.cmake
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
#
|
||||||
|
# Function to set compiler warnings with reasonable defaults at the project level.
|
||||||
|
# Taken from https://github.com/aminya/project_options/blob/main/src/CompilerWarnings.cmake
|
||||||
|
# under the folowing license:
|
||||||
|
#
|
||||||
|
# MIT License
|
||||||
|
#
|
||||||
|
# Copyright (c) 2022-2100 Amin Yahyaabadi
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
include_guard()
|
||||||
|
|
||||||
|
function(_set_project_warnings_add_target_link_option TARGET OPTIONS)
|
||||||
|
target_link_options(${_project_name} INTERFACE ${OPTIONS})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Set the compiler warnings
|
||||||
|
#
|
||||||
|
# https://clang.llvm.org/docs/DiagnosticsReference.html
|
||||||
|
# https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md
|
||||||
|
function(
|
||||||
|
set_project_warnings
|
||||||
|
_project_name
|
||||||
|
MSVC_WARNINGS
|
||||||
|
CLANG_WARNINGS
|
||||||
|
GCC_WARNINGS
|
||||||
|
)
|
||||||
|
if("${MSVC_WARNINGS}" STREQUAL "")
|
||||||
|
set(MSVC_WARNINGS
|
||||||
|
/W4 # Baseline reasonable warnings
|
||||||
|
/w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data
|
||||||
|
/w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||||
|
/w14263 # 'function': member function does not override any base class virtual member function
|
||||||
|
/w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not
|
||||||
|
# be destructed correctly
|
||||||
|
/w14287 # 'operator': unsigned/negative constant mismatch
|
||||||
|
/we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside
|
||||||
|
# the for-loop scope
|
||||||
|
/w14296 # 'operator': expression is always 'boolean_value'
|
||||||
|
/w14311 # 'variable': pointer truncation from 'type1' to 'type2'
|
||||||
|
/w14545 # expression before comma evaluates to a function which is missing an argument list
|
||||||
|
/w14546 # function call before comma missing argument list
|
||||||
|
/w14547 # 'operator': operator before comma has no effect; expected operator with side-effect
|
||||||
|
/w14549 # 'operator': operator before comma has no effect; did you intend 'operator'?
|
||||||
|
/w14555 # expression has no effect; expected expression with side- effect
|
||||||
|
/w14619 # pragma warning: there is no warning number 'number'
|
||||||
|
/w14640 # Enable warning on thread un-safe static member initialization
|
||||||
|
/w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior.
|
||||||
|
/w14905 # wide string literal cast to 'LPSTR'
|
||||||
|
/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.
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if("${CLANG_WARNINGS}" STREQUAL "")
|
||||||
|
set(CLANG_WARNINGS
|
||||||
|
-Wall
|
||||||
|
-Wextra # reasonable and standard
|
||||||
|
-Wshadow # warn the user if a variable declaration shadows one from a parent context
|
||||||
|
-Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps
|
||||||
|
# catch hard to track down memory errors
|
||||||
|
-Wold-style-cast # warn for c-style casts
|
||||||
|
-Wcast-align # warn for potential performance problem casts
|
||||||
|
-Wunused # warn on anything being unused
|
||||||
|
-Woverloaded-virtual # warn if you overload (not override) a virtual function
|
||||||
|
-Wpedantic # warn if non-standard C++ is used
|
||||||
|
-Wconversion # warn on type conversions that may lose data
|
||||||
|
-Wsign-conversion # warn on sign conversions
|
||||||
|
-Wnull-dereference # warn if a null dereference is detected
|
||||||
|
-Wdouble-promotion # warn if float is implicit promoted to double
|
||||||
|
-Wformat=2 # warn on security issues around functions that format output (ie printf)
|
||||||
|
-Wimplicit-fallthrough # warn on statements that fallthrough without an explicit annotation
|
||||||
|
# -Wgnu-zero-variadic-macro-arguments (part of -pedantic) is triggered by every qCDebug() call and therefore results
|
||||||
|
# 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
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if("${GCC_WARNINGS}" STREQUAL "")
|
||||||
|
set(GCC_WARNINGS
|
||||||
|
${CLANG_WARNINGS}
|
||||||
|
-Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist
|
||||||
|
-Wduplicated-cond # warn if if / else chain has duplicated conditions
|
||||||
|
-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
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
set(PROJECT_WARNINGS_CXX ${MSVC_WARNINGS})
|
||||||
|
elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
|
||||||
|
set(PROJECT_WARNINGS_CXX ${CLANG_WARNINGS})
|
||||||
|
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
|
set(PROJECT_WARNINGS_CXX ${GCC_WARNINGS})
|
||||||
|
else()
|
||||||
|
message(AUTHOR_WARNING "No compiler warnings set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'")
|
||||||
|
# TODO support Intel compiler
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add C warnings
|
||||||
|
set(PROJECT_WARNINGS_C "${PROJECT_WARNINGS_CXX}")
|
||||||
|
list(
|
||||||
|
REMOVE_ITEM
|
||||||
|
PROJECT_WARNINGS_C
|
||||||
|
-Wnon-virtual-dtor
|
||||||
|
-Wold-style-cast
|
||||||
|
-Woverloaded-virtual
|
||||||
|
-Wuseless-cast
|
||||||
|
-Wextra-semi
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_options(
|
||||||
|
${_project_name}
|
||||||
|
INTERFACE # C++ warnings
|
||||||
|
$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_WARNINGS_CXX}>
|
||||||
|
# C warnings
|
||||||
|
$<$<COMPILE_LANGUAGE:C>:${PROJECT_WARNINGS_C}>
|
||||||
|
)
|
||||||
|
|
||||||
|
# If we are using the compiler as a linker driver pass the warnings to it
|
||||||
|
# (most useful when using LTO or warnings as errors)
|
||||||
|
if(CMAKE_CXX_LINK_EXECUTABLE MATCHES "^<CMAKE_CXX_COMPILER>")
|
||||||
|
_set_project_warnings_add_target_link_option(
|
||||||
|
${_project_name} "$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_WARNINGS_CXX}>"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_C_LINK_EXECUTABLE MATCHES "^<CMAKE_C_COMPILER>")
|
||||||
|
_set_project_warnings_add_target_link_option(
|
||||||
|
${_project_name} "$<$<COMPILE_LANGUAGE:C>:${PROJECT_WARNINGS_C}>"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
endfunction()
|
@ -67,5 +67,16 @@
|
|||||||
<string>Alternate</string>
|
<string>Alternate</string>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>Curseforge</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>curseforge</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
46
flake.lock
generated
46
flake.lock
generated
@ -3,11 +3,11 @@
|
|||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1673956053,
|
"lastModified": 1696426674,
|
||||||
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||||
"owner": "edolstra",
|
"owner": "edolstra",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -21,11 +21,11 @@
|
|||||||
"nixpkgs-lib": "nixpkgs-lib"
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1688466019,
|
"lastModified": 1696343447,
|
||||||
"narHash": "sha256-VeM2akYrBYMsb4W/MmBo1zmaMfgbL4cH3Pu8PGyIwJ0=",
|
"narHash": "sha256-B2xAZKLkkeRFG5XcHHSXXcP7To9Xzr59KXeZiRf4vdQ=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "8e8d955c22df93dbe24f19ea04f47a74adbdc5ec",
|
"rev": "c9afaba3dfa4085dbd2ccb38dfade5141e33d9d4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -89,13 +89,28 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nix-filter": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1694857738,
|
||||||
|
"narHash": "sha256-bxxNyLHjhu0N8T3REINXQ2ZkJco0ABFPn6PIe2QUfqo=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "nix-filter",
|
||||||
|
"rev": "41fd48e00c22b4ced525af521ead8792402de0ea",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "nix-filter",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1690630721,
|
"lastModified": 1697009197,
|
||||||
"narHash": "sha256-Y04onHyBQT4Erfr2fc82dbJTfXGYrf4V0ysLUYnPOP8=",
|
"narHash": "sha256-viVRhBTFT8fPJTb1N3brQIpFZnttmwo3JVKNuWRVc3s=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "d2b52322f35597c62abf56de91b0236746b2a03d",
|
"rev": "01441e14af5e29c9d27ace398e6dd0b293e25a54",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -108,11 +123,11 @@
|
|||||||
"nixpkgs-lib": {
|
"nixpkgs-lib": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "lib",
|
"dir": "lib",
|
||||||
"lastModified": 1688049487,
|
"lastModified": 1696019113,
|
||||||
"narHash": "sha256-100g4iaKC9MalDjUW9iN6Jl/OocTDtXdeAj7pEGIRh4=",
|
"narHash": "sha256-X3+DKYWJm93DRSdC5M6K5hLqzSya9BjibtBsuARoPco=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "4bc72cae107788bf3f24f30db2e2f685c9298dc9",
|
"rev": "f5892ddac112a1e9b3612c39af1b72987ee5783a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -138,11 +153,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1690628027,
|
"lastModified": 1696846637,
|
||||||
"narHash": "sha256-OTSbA2hM6VmxyZ/4siYPANffMBzIsKu04GLjXcv8ST0=",
|
"narHash": "sha256-0hv4kbXxci2+pxhuXlVgftj/Jq79VSmtAyvfabCCtYk=",
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "pre-commit-hooks.nix",
|
"repo": "pre-commit-hooks.nix",
|
||||||
"rev": "1e2443dd3f669eb65433b2fc26a3065e05a7dc9c",
|
"rev": "42e1b6095ef80a51f79595d9951eb38e91c4e6ca",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -156,6 +171,7 @@
|
|||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
"flake-parts": "flake-parts",
|
"flake-parts": "flake-parts",
|
||||||
"libnbtplusplus": "libnbtplusplus",
|
"libnbtplusplus": "libnbtplusplus",
|
||||||
|
"nix-filter": "nix-filter",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"pre-commit-hooks": "pre-commit-hooks"
|
"pre-commit-hooks": "pre-commit-hooks"
|
||||||
}
|
}
|
||||||
|
25
flake.nix
25
flake.nix
@ -4,6 +4,7 @@
|
|||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||||
|
nix-filter.url = "github:numtide/nix-filter";
|
||||||
pre-commit-hooks = {
|
pre-commit-hooks = {
|
||||||
url = "github:cachix/pre-commit-hooks.nix";
|
url = "github:cachix/pre-commit-hooks.nix";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
@ -20,8 +21,24 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = inputs:
|
outputs = {
|
||||||
inputs.flake-parts.lib.mkFlake
|
flake-parts,
|
||||||
{inherit inputs;}
|
pre-commit-hooks,
|
||||||
{imports = [./nix];};
|
...
|
||||||
|
} @ inputs:
|
||||||
|
flake-parts.lib.mkFlake {inherit inputs;} {
|
||||||
|
imports = [
|
||||||
|
pre-commit-hooks.flakeModule
|
||||||
|
|
||||||
|
./nix/dev.nix
|
||||||
|
./nix/distribution.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
systems = [
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
"x86_64-darwin"
|
||||||
|
"aarch64-darwin"
|
||||||
|
];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
* Copyright (C) 2022 Tayou <git@tayou.org>
|
* Copyright (C) 2022 Tayou <git@tayou.org>
|
||||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||||
* Copyright (C) 2023 seth <getchoo at tuta dot io>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -123,6 +122,7 @@
|
|||||||
#include <FileSystem.h>
|
#include <FileSystem.h>
|
||||||
#include <LocalPeer.h>
|
#include <LocalPeer.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
#include <sys.h>
|
#include <sys.h>
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
@ -131,16 +131,16 @@
|
|||||||
#include "gamemode_client.h"
|
#include "gamemode_client.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_MAC) && defined(SPARKLE_ENABLED)
|
#if defined(Q_OS_MAC)
|
||||||
|
#if defined(SPARKLE_ENABLED)
|
||||||
#include "updater/MacSparkleUpdater.h"
|
#include "updater/MacSparkleUpdater.h"
|
||||||
#endif
|
#endif
|
||||||
|
#else
|
||||||
|
#include "updater/PrismExternalUpdater.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
#include "WindowsConsole.h"
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define STRINGIFY(x) #x
|
#define STRINGIFY(x) #x
|
||||||
@ -169,25 +169,39 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
std::tuple<QDateTime, QString, QString, QString, QString> read_lock_File(const QString& path)
|
||||||
|
{
|
||||||
|
auto contents = QString(FS::read(path));
|
||||||
|
auto lines = contents.split('\n');
|
||||||
|
|
||||||
|
QDateTime timestamp;
|
||||||
|
QString from, to, target, data_path;
|
||||||
|
for (auto line : lines) {
|
||||||
|
auto index = line.indexOf("=");
|
||||||
|
if (index < 0)
|
||||||
|
continue;
|
||||||
|
auto left = line.left(index);
|
||||||
|
auto right = line.mid(index + 1);
|
||||||
|
if (left.toLower() == "timestamp") {
|
||||||
|
timestamp = QDateTime::fromString(right, Qt::ISODate);
|
||||||
|
} else if (left.toLower() == "from") {
|
||||||
|
from = right;
|
||||||
|
} else if (left.toLower() == "to") {
|
||||||
|
to = right;
|
||||||
|
} else if (left.toLower() == "target") {
|
||||||
|
target = right;
|
||||||
|
} else if (left.toLower() == "data_path") {
|
||||||
|
data_path = right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::make_tuple(timestamp, from, to, target, data_path);
|
||||||
|
}
|
||||||
|
|
||||||
Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
{
|
{
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
// attach the parent console
|
// attach the parent console if stdout not already captured
|
||||||
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
if (AttachWindowsConsole()) {
|
||||||
// if attach succeeds, reopen and sync all the i/o
|
|
||||||
if (freopen("CON", "w", stdout)) {
|
|
||||||
std::cout.sync_with_stdio();
|
|
||||||
}
|
|
||||||
if (freopen("CON", "w", stderr)) {
|
|
||||||
std::cerr.sync_with_stdio();
|
|
||||||
}
|
|
||||||
if (freopen("CON", "r", stdin)) {
|
|
||||||
std::cin.sync_with_stdio();
|
|
||||||
}
|
|
||||||
auto out = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
DWORD written;
|
|
||||||
const char* endline = "\n";
|
|
||||||
WriteConsole(out, endline, strlen(endline), &written, NULL);
|
|
||||||
consoleAttached = true;
|
consoleAttached = true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -212,8 +226,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
|
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
|
||||||
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" },
|
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" },
|
||||||
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
|
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
|
||||||
{ { "I", "import" }, "Import instance from specified zip (local path or URL)", "file" },
|
{ { "I", "import" }, "Import instance or resource from specified local path or URL", "url" },
|
||||||
{ "show", "Opens the window for the specified instance (by instance ID)", "show" } });
|
{ "show", "Opens the window for the specified instance (by instance ID)", "show" } });
|
||||||
|
// Has to be positional for some OS to handle that properly
|
||||||
|
parser.addPositionalArgument("URL", "Import the resource(s) at the given URL(s) (same as -I / --import)", "[URL...]");
|
||||||
|
|
||||||
parser.addHelpOption();
|
parser.addHelpOption();
|
||||||
parser.addVersionOption();
|
parser.addVersionOption();
|
||||||
|
|
||||||
@ -226,13 +243,13 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
m_instanceIdToShowWindowOf = parser.value("show");
|
m_instanceIdToShowWindowOf = parser.value("show");
|
||||||
|
|
||||||
for (auto zip_path : parser.values("import")) {
|
for (auto url : parser.values("import")) {
|
||||||
m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath()));
|
m_urlsToImport.append(normalizeImportUrl(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
// treat unspecified positional arguments as import urls
|
// treat unspecified positional arguments as import urls
|
||||||
for (auto zip_path : parser.positionalArguments()) {
|
for (auto url : parser.positionalArguments()) {
|
||||||
m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath()));
|
m_urlsToImport.append(normalizeImportUrl(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
// error if --launch is missing with --server or --profile
|
// error if --launch is missing with --server or --profile
|
||||||
@ -312,6 +329,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
.arg(dataPath));
|
.arg(dataPath));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
m_dataPath = dataPath;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Establish the mechanism for communication with an already running PrismLauncher that uses the same data path.
|
* Establish the mechanism for communication with an already running PrismLauncher that uses the same data path.
|
||||||
@ -331,11 +349,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
activate.command = "activate";
|
activate.command = "activate";
|
||||||
m_peerInstance->sendMessage(activate.serialize(), timeout);
|
m_peerInstance->sendMessage(activate.serialize(), timeout);
|
||||||
|
|
||||||
if (!m_zipsToImport.isEmpty()) {
|
if (!m_urlsToImport.isEmpty()) {
|
||||||
for (auto zip_url : m_zipsToImport) {
|
for (auto url : m_urlsToImport) {
|
||||||
ApplicationMessage import;
|
ApplicationMessage import;
|
||||||
import.command = "import";
|
import.command = "import";
|
||||||
import.args.insert("path", zip_url.toString());
|
import.args.insert("url", url.toString());
|
||||||
m_peerInstance->sendMessage(import.serialize(), timeout);
|
m_peerInstance->sendMessage(import.serialize(), timeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -466,11 +484,16 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT;
|
qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << ", (c) 2022-2023 "
|
||||||
|
<< qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", "));
|
||||||
qDebug() << "Version : " << BuildConfig.printableVersionString();
|
qDebug() << "Version : " << BuildConfig.printableVersionString();
|
||||||
qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM;
|
qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM;
|
||||||
qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
|
qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
|
||||||
qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC;
|
qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC;
|
||||||
|
qDebug() << "Compiled for : " << BuildConfig.systemID();
|
||||||
|
qDebug() << "Compiled by : " << BuildConfig.compilerID();
|
||||||
|
qDebug() << "Build Artifact : " << BuildConfig.BUILD_ARTIFACT;
|
||||||
|
qDebug() << "Updates Enabled : " << (updaterEnabled() ? "Yes" : "No");
|
||||||
if (adjustedBy.size()) {
|
if (adjustedBy.size()) {
|
||||||
qDebug() << "Work dir before adjustment : " << origcwdPath;
|
qDebug() << "Work dir before adjustment : " << origcwdPath;
|
||||||
qDebug() << "Work dir after adjustment : " << QDir::currentPath();
|
qDebug() << "Work dir after adjustment : " << QDir::currentPath();
|
||||||
@ -510,7 +533,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
m_settings.reset(new INISettingsObject({ BuildConfig.LAUNCHER_CONFIGFILE, "polymc.cfg", "multimc.cfg" }, this));
|
m_settings.reset(new INISettingsObject({ BuildConfig.LAUNCHER_CONFIGFILE, "polymc.cfg", "multimc.cfg" }, this));
|
||||||
|
|
||||||
// Theming
|
// Theming
|
||||||
m_settings->registerSetting("IconTheme", QString("pe_colored"));
|
m_settings->registerSetting("IconTheme", QString());
|
||||||
m_settings->registerSetting("ApplicationTheme", QString());
|
m_settings->registerSetting("ApplicationTheme", QString());
|
||||||
m_settings->registerSetting("BackgroundCat", QString("kitteh"));
|
m_settings->registerSetting("BackgroundCat", QString("kitteh"));
|
||||||
|
|
||||||
@ -519,6 +542,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
m_settings->registerSetting("MenuBarInsteadOfToolBar", false);
|
m_settings->registerSetting("MenuBarInsteadOfToolBar", false);
|
||||||
|
|
||||||
|
m_settings->registerSetting("NumberOfConcurrentTasks", 10);
|
||||||
|
m_settings->registerSetting("NumberOfConcurrentDownloads", 6);
|
||||||
|
|
||||||
QString defaultMonospace;
|
QString defaultMonospace;
|
||||||
int defaultSize = 11;
|
int defaultSize = 11;
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
@ -595,12 +621,14 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
m_settings->registerSetting("IgnoreJavaCompatibility", false);
|
m_settings->registerSetting("IgnoreJavaCompatibility", false);
|
||||||
m_settings->registerSetting("IgnoreJavaWizard", false);
|
m_settings->registerSetting("IgnoreJavaWizard", false);
|
||||||
|
|
||||||
// Mod loader settings
|
// Legacy settings
|
||||||
m_settings->registerSetting("DisableQuiltBeacon", false);
|
m_settings->registerSetting("OnlineFixes", false);
|
||||||
|
|
||||||
// Native library workarounds
|
// Native library workarounds
|
||||||
m_settings->registerSetting("UseNativeOpenAL", false);
|
m_settings->registerSetting("UseNativeOpenAL", false);
|
||||||
|
m_settings->registerSetting("CustomOpenALPath", "");
|
||||||
m_settings->registerSetting("UseNativeGLFW", false);
|
m_settings->registerSetting("UseNativeGLFW", false);
|
||||||
|
m_settings->registerSetting("CustomGLFWPath", "");
|
||||||
|
|
||||||
// Peformance related options
|
// Peformance related options
|
||||||
m_settings->registerSetting("EnableFeralGamemode", false);
|
m_settings->registerSetting("EnableFeralGamemode", false);
|
||||||
@ -611,6 +639,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
m_settings->registerSetting("ShowGameTime", true);
|
m_settings->registerSetting("ShowGameTime", true);
|
||||||
m_settings->registerSetting("ShowGlobalGameTime", true);
|
m_settings->registerSetting("ShowGlobalGameTime", true);
|
||||||
m_settings->registerSetting("RecordGameTime", true);
|
m_settings->registerSetting("RecordGameTime", true);
|
||||||
|
m_settings->registerSetting("ShowGameTimeWithoutDays", false);
|
||||||
|
|
||||||
// Minecraft mods
|
// Minecraft mods
|
||||||
m_settings->registerSetting("ModMetadataDisabled", false);
|
m_settings->registerSetting("ModMetadataDisabled", false);
|
||||||
@ -751,15 +780,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
qDebug() << "<> Translations loaded.";
|
qDebug() << "<> Translations loaded.";
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize the updater
|
|
||||||
if (BuildConfig.UPDATER_ENABLED) {
|
|
||||||
qDebug() << "Initializing updater";
|
|
||||||
#if defined(Q_OS_MAC) && defined(SPARKLE_ENABLED)
|
|
||||||
m_updater.reset(new MacSparkleUpdater());
|
|
||||||
#endif
|
|
||||||
qDebug() << "<> Updater started.";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instance icons
|
// Instance icons
|
||||||
{
|
{
|
||||||
auto setting = APPLICATION->settings()->getSetting("IconsDir");
|
auto setting = APPLICATION->settings()->getSetting("IconsDir");
|
||||||
@ -772,7 +792,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Themes
|
// Themes
|
||||||
m_themeManager = std::make_unique<ThemeManager>(m_mainWindow);
|
m_themeManager = std::make_unique<ThemeManager>();
|
||||||
|
|
||||||
// initialize and load all instances
|
// initialize and load all instances
|
||||||
{
|
{
|
||||||
@ -858,14 +878,116 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
applyCurrentlySelectedTheme(true);
|
|
||||||
|
|
||||||
updateCapabilities();
|
updateCapabilities();
|
||||||
|
|
||||||
|
detectLibraries();
|
||||||
|
|
||||||
|
// check update locks
|
||||||
|
{
|
||||||
|
auto update_log_path = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log");
|
||||||
|
|
||||||
|
auto update_lock = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.lock"));
|
||||||
|
if (update_lock.exists()) {
|
||||||
|
auto [timestamp, from, to, target, data_path] = read_lock_File(update_lock.absoluteFilePath());
|
||||||
|
auto infoMsg = tr("This installation has a update lock file present at: %1\n"
|
||||||
|
"\n"
|
||||||
|
"Timestamp: %2\n"
|
||||||
|
"Updating from version %3 to %4\n"
|
||||||
|
"Target install path: %5\n"
|
||||||
|
"Data Path: %6"
|
||||||
|
"\n"
|
||||||
|
"This likely means that a update attempt failed. Please ensure your installation is in working order before "
|
||||||
|
"proceeding.\n"
|
||||||
|
"Check the Prism Launcher updater log at: \n"
|
||||||
|
"%7\n"
|
||||||
|
"for details on the last update attempt.\n"
|
||||||
|
"\n"
|
||||||
|
"To delete this lock and proceed select \"Ignore\" below.")
|
||||||
|
.arg(update_lock.absoluteFilePath())
|
||||||
|
.arg(timestamp.toString(Qt::ISODate), from, to, target, data_path)
|
||||||
|
.arg(update_log_path);
|
||||||
|
auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update In Progress"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort);
|
||||||
|
msgBox.setDefaultButton(QMessageBox::Abort);
|
||||||
|
msgBox.setModal(true);
|
||||||
|
msgBox.setDetailedText(FS::read(update_log_path));
|
||||||
|
msgBox.setMinimumWidth(460);
|
||||||
|
msgBox.adjustSize();
|
||||||
|
auto res = msgBox.exec();
|
||||||
|
switch (res) {
|
||||||
|
case QMessageBox::Ignore: {
|
||||||
|
FS::deletePath(update_lock.absoluteFilePath());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QMessageBox::Abort:
|
||||||
|
[[fallthrough]];
|
||||||
|
default: {
|
||||||
|
qDebug() << "Exiting because update lockfile is present";
|
||||||
|
QMetaObject::invokeMethod(
|
||||||
|
this, []() { exit(1); }, Qt::QueuedConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto update_fail_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.fail"));
|
||||||
|
if (update_fail_marker.exists()) {
|
||||||
|
auto infoMsg = tr("An update attempt failed\n"
|
||||||
|
"\n"
|
||||||
|
"Please ensure your installation is in working order before "
|
||||||
|
"proceeding.\n"
|
||||||
|
"Check the Prism Launcher updater log at: \n"
|
||||||
|
"%1\n"
|
||||||
|
"for details on the last update attempt.")
|
||||||
|
.arg(update_log_path);
|
||||||
|
auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Failed"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort);
|
||||||
|
msgBox.setDefaultButton(QMessageBox::Abort);
|
||||||
|
msgBox.setModal(true);
|
||||||
|
msgBox.setDetailedText(FS::read(update_log_path));
|
||||||
|
msgBox.setMinimumWidth(460);
|
||||||
|
msgBox.adjustSize();
|
||||||
|
auto res = msgBox.exec();
|
||||||
|
switch (res) {
|
||||||
|
case QMessageBox::Ignore: {
|
||||||
|
FS::deletePath(update_fail_marker.absoluteFilePath());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QMessageBox::Abort:
|
||||||
|
[[fallthrough]];
|
||||||
|
default: {
|
||||||
|
qDebug() << "Exiting because update lockfile is present";
|
||||||
|
QMetaObject::invokeMethod(
|
||||||
|
this, []() { exit(1); }, Qt::QueuedConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto update_success_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.success"));
|
||||||
|
if (update_success_marker.exists()) {
|
||||||
|
auto infoMsg = tr("Update succeeded\n"
|
||||||
|
"\n"
|
||||||
|
"You are now running %1 .\n"
|
||||||
|
"Check the Prism Launcher updater log at: \n"
|
||||||
|
"%1\n"
|
||||||
|
"for details.")
|
||||||
|
.arg(BuildConfig.printableVersionString())
|
||||||
|
.arg(update_log_path);
|
||||||
|
auto msgBox = new QMessageBox(QMessageBox::Information, tr("Update Succeeded"), infoMsg, QMessageBox::Ok);
|
||||||
|
msgBox->setDefaultButton(QMessageBox::Ok);
|
||||||
|
msgBox->setDetailedText(FS::read(update_log_path));
|
||||||
|
msgBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
msgBox->setMinimumWidth(460);
|
||||||
|
msgBox->adjustSize();
|
||||||
|
msgBox->open();
|
||||||
|
FS::deletePath(update_success_marker.absoluteFilePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (createSetupWizard()) {
|
if (createSetupWizard()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_themeManager->applyCurrentlySelectedTheme(true);
|
||||||
performMainStartupAction();
|
performMainStartupAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -891,10 +1013,20 @@ bool Application::createSetupWizard()
|
|||||||
}();
|
}();
|
||||||
bool languageRequired = settings()->get("Language").toString().isEmpty();
|
bool languageRequired = settings()->get("Language").toString().isEmpty();
|
||||||
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
|
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
|
||||||
bool themeInterventionRequired = settings()->get("ApplicationTheme") == "";
|
bool validWidgets = m_themeManager->isValidApplicationTheme(settings()->get("ApplicationTheme").toString());
|
||||||
|
bool validIcons = m_themeManager->isValidIconTheme(settings()->get("IconTheme").toString());
|
||||||
|
bool themeInterventionRequired = !validWidgets || !validIcons;
|
||||||
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
|
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
|
||||||
|
|
||||||
if (wizardRequired) {
|
if (wizardRequired) {
|
||||||
|
// set default theme after going into theme wizard
|
||||||
|
if (!validIcons)
|
||||||
|
settings()->set("IconTheme", QString("pe_colored"));
|
||||||
|
if (!validWidgets)
|
||||||
|
settings()->set("ApplicationTheme", QString("system"));
|
||||||
|
|
||||||
|
m_themeManager->applyCurrentlySelectedTheme(true);
|
||||||
|
|
||||||
m_setupWizard = new SetupWizard(nullptr);
|
m_setupWizard = new SetupWizard(nullptr);
|
||||||
if (languageRequired) {
|
if (languageRequired) {
|
||||||
m_setupWizard->addPage(new LanguageWizardPage(m_setupWizard));
|
m_setupWizard->addPage(new LanguageWizardPage(m_setupWizard));
|
||||||
@ -909,9 +1041,9 @@ bool Application::createSetupWizard()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (themeInterventionRequired) {
|
if (themeInterventionRequired) {
|
||||||
settings()->set("ApplicationTheme", QString("system")); // set default theme after going into theme wizard
|
|
||||||
m_setupWizard->addPage(new ThemeWizardPage(m_setupWizard));
|
m_setupWizard->addPage(new ThemeWizardPage(m_setupWizard));
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished);
|
connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished);
|
||||||
m_setupWizard->show();
|
m_setupWizard->show();
|
||||||
return true;
|
return true;
|
||||||
@ -919,6 +1051,26 @@ bool Application::createSetupWizard()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Application::updaterEnabled()
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_MAC)
|
||||||
|
return BuildConfig.UPDATER_ENABLED;
|
||||||
|
#else
|
||||||
|
return BuildConfig.UPDATER_ENABLED && QFileInfo(FS::PathCombine(m_rootPath, updaterBinaryName())).isFile();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Application::updaterBinaryName()
|
||||||
|
{
|
||||||
|
auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
|
||||||
|
#if defined Q_OS_WIN32
|
||||||
|
exe_name.append(".exe");
|
||||||
|
#else
|
||||||
|
exe_name.prepend("bin/");
|
||||||
|
#endif
|
||||||
|
return exe_name;
|
||||||
|
}
|
||||||
|
|
||||||
bool Application::event(QEvent* event)
|
bool Application::event(QEvent* event)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
@ -970,7 +1122,7 @@ void Application::performMainStartupAction()
|
|||||||
qDebug() << " Launching with account" << m_profileToUse;
|
qDebug() << " Launching with account" << m_profileToUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
launch(inst, true, false, nullptr, serverToJoin, accountToUse);
|
launch(inst, true, false, serverToJoin, accountToUse);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -987,9 +1139,23 @@ void Application::performMainStartupAction()
|
|||||||
showMainWindow(false);
|
showMainWindow(false);
|
||||||
qDebug() << "<> Main window shown.";
|
qDebug() << "<> Main window shown.";
|
||||||
}
|
}
|
||||||
if (!m_zipsToImport.isEmpty()) {
|
|
||||||
qDebug() << "<> Importing from zip:" << m_zipsToImport;
|
// initialize the updater
|
||||||
m_mainWindow->processURLs(m_zipsToImport);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1031,12 +1197,12 @@ void Application::messageReceived(const QByteArray& message)
|
|||||||
if (command == "activate") {
|
if (command == "activate") {
|
||||||
showMainWindow();
|
showMainWindow();
|
||||||
} else if (command == "import") {
|
} else if (command == "import") {
|
||||||
QString path = received.args["path"];
|
QString url = received.args["url"];
|
||||||
if (path.isEmpty()) {
|
if (url.isEmpty()) {
|
||||||
qWarning() << "Received" << command << "message without a zip path/URL.";
|
qWarning() << "Received" << command << "message without a zip path/URL.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_mainWindow->processURLs({ QUrl::fromLocalFile(QFileInfo(path).absoluteFilePath()) });
|
m_mainWindow->processURLs({ normalizeImportUrl(url) });
|
||||||
} else if (command == "launch") {
|
} else if (command == "launch") {
|
||||||
QString id = received.args["id"];
|
QString id = received.args["id"];
|
||||||
QString server = received.args["server"];
|
QString server = received.args["server"];
|
||||||
@ -1069,7 +1235,7 @@ void Application::messageReceived(const QByteArray& message)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
launch(instance, true, false, nullptr, serverObject, accountObject);
|
launch(instance, true, false, serverObject, accountObject);
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Received invalid message" << message;
|
qWarning() << "Received invalid message" << message;
|
||||||
}
|
}
|
||||||
@ -1088,26 +1254,6 @@ std::shared_ptr<JavaInstallList> Application::javalist()
|
|||||||
return m_javalist;
|
return m_javalist;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<ITheme*> Application::getValidApplicationThemes()
|
|
||||||
{
|
|
||||||
return m_themeManager->getValidApplicationThemes();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::applyCurrentlySelectedTheme(bool initial)
|
|
||||||
{
|
|
||||||
m_themeManager->applyCurrentlySelectedTheme(initial);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::setApplicationTheme(const QString& name)
|
|
||||||
{
|
|
||||||
m_themeManager->setApplicationTheme(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::setIconTheme(const QString& name)
|
|
||||||
{
|
|
||||||
m_themeManager->setIconTheme(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon Application::getThemedIcon(const QString& name)
|
QIcon Application::getThemedIcon(const QString& name)
|
||||||
{
|
{
|
||||||
if (name == "logo") {
|
if (name == "logo") {
|
||||||
@ -1116,16 +1262,6 @@ QIcon Application::getThemedIcon(const QString& name)
|
|||||||
return QIcon::fromTheme(name);
|
return QIcon::fromTheme(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<CatPack*> Application::getValidCatPacks()
|
|
||||||
{
|
|
||||||
return m_themeManager->getValidCatPacks();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Application::getCatPack(QString catName)
|
|
||||||
{
|
|
||||||
return m_themeManager->getCatPack(catName);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Application::openJsonEditor(const QString& filename)
|
bool Application::openJsonEditor(const QString& filename)
|
||||||
{
|
{
|
||||||
const QString file = QDir::current().absoluteFilePath(filename);
|
const QString file = QDir::current().absoluteFilePath(filename);
|
||||||
@ -1140,7 +1276,6 @@ bool Application::openJsonEditor(const QString& filename)
|
|||||||
bool Application::launch(InstancePtr instance,
|
bool Application::launch(InstancePtr instance,
|
||||||
bool online,
|
bool online,
|
||||||
bool demo,
|
bool demo,
|
||||||
BaseProfilerFactory* profiler,
|
|
||||||
MinecraftServerTargetPtr serverToJoin,
|
MinecraftServerTargetPtr serverToJoin,
|
||||||
MinecraftAccountPtr accountToUse)
|
MinecraftAccountPtr accountToUse)
|
||||||
{
|
{
|
||||||
@ -1148,7 +1283,7 @@ bool Application::launch(InstancePtr instance,
|
|||||||
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
|
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
|
||||||
} else if (instance->canLaunch()) {
|
} else if (instance->canLaunch()) {
|
||||||
auto& extras = m_instanceExtras[instance->id()];
|
auto& extras = m_instanceExtras[instance->id()];
|
||||||
auto& window = extras.window;
|
auto window = extras.window;
|
||||||
if (window) {
|
if (window) {
|
||||||
if (!window->saveAll()) {
|
if (!window->saveAll()) {
|
||||||
return false;
|
return false;
|
||||||
@ -1159,7 +1294,7 @@ bool Application::launch(InstancePtr instance,
|
|||||||
controller->setInstance(instance);
|
controller->setInstance(instance);
|
||||||
controller->setOnline(online);
|
controller->setOnline(online);
|
||||||
controller->setDemo(demo);
|
controller->setDemo(demo);
|
||||||
controller->setProfiler(profiler);
|
controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get());
|
||||||
controller->setServerToJoin(serverToJoin);
|
controller->setServerToJoin(serverToJoin);
|
||||||
controller->setAccountToUse(accountToUse);
|
controller->setAccountToUse(accountToUse);
|
||||||
if (window) {
|
if (window) {
|
||||||
@ -1451,6 +1586,15 @@ void Application::updateCapabilities()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::detectLibraries()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
m_detectedGLFWPath = MangoHud::findLibrary(BuildConfig.GLFW_LIBRARY_NAME);
|
||||||
|
m_detectedOpenALPath = MangoHud::findLibrary(BuildConfig.OPENAL_LIBRARY_NAME);
|
||||||
|
qDebug() << "Detected native libraries:" << m_detectedGLFWPath << m_detectedOpenALPath;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
QString Application::getJarPath(QString jarFile)
|
QString Application::getJarPath(QString jarFile)
|
||||||
{
|
{
|
||||||
QStringList potentialPaths = {
|
QStringList potentialPaths = {
|
||||||
@ -1629,3 +1773,13 @@ void Application::triggerUpdateCheck()
|
|||||||
qDebug() << "Updater not available.";
|
qDebug() << "Updater not available.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QUrl Application::normalizeImportUrl(QString const& url)
|
||||||
|
{
|
||||||
|
auto local_file = QFileInfo(url);
|
||||||
|
if (local_file.exists()) {
|
||||||
|
return QUrl::fromLocalFile(local_file.absoluteFilePath());
|
||||||
|
} else {
|
||||||
|
return QUrl::fromUserInput(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -71,6 +71,7 @@ class TranslationsModel;
|
|||||||
class ITheme;
|
class ITheme;
|
||||||
class MCEditTool;
|
class MCEditTool;
|
||||||
class ThemeManager;
|
class ThemeManager;
|
||||||
|
class IconTheme;
|
||||||
|
|
||||||
namespace Meta {
|
namespace Meta {
|
||||||
class Index;
|
class Index;
|
||||||
@ -109,17 +110,7 @@ class Application : public QApplication {
|
|||||||
|
|
||||||
QIcon getThemedIcon(const QString& name);
|
QIcon getThemedIcon(const QString& name);
|
||||||
|
|
||||||
void setIconTheme(const QString& name);
|
ThemeManager* themeManager() { return m_themeManager.get(); }
|
||||||
|
|
||||||
void applyCurrentlySelectedTheme(bool initial = false);
|
|
||||||
|
|
||||||
QList<ITheme*> getValidApplicationThemes();
|
|
||||||
|
|
||||||
void setApplicationTheme(const QString& name);
|
|
||||||
|
|
||||||
QList<CatPack*> getValidCatPacks();
|
|
||||||
|
|
||||||
QString getCatPack(QString catName = "");
|
|
||||||
|
|
||||||
shared_qobject_ptr<ExternalUpdater> updater() { return m_updater; }
|
shared_qobject_ptr<ExternalUpdater> updater() { return m_updater; }
|
||||||
|
|
||||||
@ -151,6 +142,8 @@ class Application : public QApplication {
|
|||||||
|
|
||||||
void updateCapabilities();
|
void updateCapabilities();
|
||||||
|
|
||||||
|
void detectLibraries();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Finds and returns the full path to a jar file.
|
* Finds and returns the full path to a jar file.
|
||||||
* Returns a null-string if it could not be found.
|
* Returns a null-string if it could not be found.
|
||||||
@ -166,6 +159,9 @@ class Application : public QApplication {
|
|||||||
/// this is the root of the 'installation'. Used for automatic updates
|
/// this is the root of the 'installation'. Used for automatic updates
|
||||||
const QString& root() { return m_rootPath; }
|
const QString& root() { return m_rootPath; }
|
||||||
|
|
||||||
|
/// the data path the application is using
|
||||||
|
const QString& dataRoot() { return m_dataPath; }
|
||||||
|
|
||||||
bool isPortable() { return m_portable; }
|
bool isPortable() { return m_portable; }
|
||||||
|
|
||||||
const Capabilities capabilities() { return m_capabilities; }
|
const Capabilities capabilities() { return m_capabilities; }
|
||||||
@ -186,6 +182,11 @@ class Application : public QApplication {
|
|||||||
|
|
||||||
int suitableMaxMem();
|
int suitableMaxMem();
|
||||||
|
|
||||||
|
bool updaterEnabled();
|
||||||
|
QString updaterBinaryName();
|
||||||
|
|
||||||
|
QUrl normalizeImportUrl(QString const& url);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void updateAllowedChanged(bool status);
|
void updateAllowedChanged(bool status);
|
||||||
void globalSettingsAboutToOpen();
|
void globalSettingsAboutToOpen();
|
||||||
@ -200,7 +201,6 @@ class Application : public QApplication {
|
|||||||
bool launch(InstancePtr instance,
|
bool launch(InstancePtr instance,
|
||||||
bool online = true,
|
bool online = true,
|
||||||
bool demo = false,
|
bool demo = false,
|
||||||
BaseProfilerFactory* profiler = nullptr,
|
|
||||||
MinecraftServerTargetPtr serverToJoin = nullptr,
|
MinecraftServerTargetPtr serverToJoin = nullptr,
|
||||||
MinecraftAccountPtr accountToUse = nullptr);
|
MinecraftAccountPtr accountToUse = nullptr);
|
||||||
bool kill(InstancePtr instance);
|
bool kill(InstancePtr instance);
|
||||||
@ -250,6 +250,7 @@ class Application : public QApplication {
|
|||||||
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
|
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
|
||||||
|
|
||||||
QString m_rootPath;
|
QString m_rootPath;
|
||||||
|
QString m_dataPath;
|
||||||
Status m_status = Application::StartingUp;
|
Status m_status = Application::StartingUp;
|
||||||
Capabilities m_capabilities;
|
Capabilities m_capabilities;
|
||||||
bool m_portable = false;
|
bool m_portable = false;
|
||||||
@ -284,11 +285,13 @@ class Application : public QApplication {
|
|||||||
SetupWizard* m_setupWizard = nullptr;
|
SetupWizard* m_setupWizard = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
QString m_detectedGLFWPath;
|
||||||
|
QString m_detectedOpenALPath;
|
||||||
QString m_instanceIdToLaunch;
|
QString m_instanceIdToLaunch;
|
||||||
QString m_serverToJoin;
|
QString m_serverToJoin;
|
||||||
QString m_profileToUse;
|
QString m_profileToUse;
|
||||||
bool m_liveCheck = false;
|
bool m_liveCheck = false;
|
||||||
QList<QUrl> m_zipsToImport;
|
QList<QUrl> m_urlsToImport;
|
||||||
QString m_instanceIdToShowWindowOf;
|
QString m_instanceIdToShowWindowOf;
|
||||||
std::unique_ptr<QFile> logFile;
|
std::unique_ptr<QFile> logFile;
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -100,6 +101,8 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
|
|||||||
m_settings->registerSetting("ManagedPackName", "");
|
m_settings->registerSetting("ManagedPackName", "");
|
||||||
m_settings->registerSetting("ManagedPackVersionID", "");
|
m_settings->registerSetting("ManagedPackVersionID", "");
|
||||||
m_settings->registerSetting("ManagedPackVersionName", "");
|
m_settings->registerSetting("ManagedPackVersionName", "");
|
||||||
|
|
||||||
|
m_settings->registerSetting("Profiler", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BaseInstance::getPreLaunchCommand()
|
QString BaseInstance::getPreLaunchCommand()
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -38,6 +39,7 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QMenu>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
@ -86,7 +88,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
/// virtual destructor to make sure the destruction is COMPLETE
|
/// virtual destructor to make sure the destruction is COMPLETE
|
||||||
virtual ~BaseInstance(){};
|
virtual ~BaseInstance() {}
|
||||||
|
|
||||||
virtual void saveNow() = 0;
|
virtual void saveNow() = 0;
|
||||||
|
|
||||||
@ -146,7 +148,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
|||||||
void copyManagedPack(BaseInstance& other);
|
void copyManagedPack(BaseInstance& other);
|
||||||
|
|
||||||
/// guess log level from a line of game log
|
/// guess log level from a line of game log
|
||||||
virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString& line, MessageLevel::Enum level) { return level; };
|
virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString& line, MessageLevel::Enum level) { return level; }
|
||||||
|
|
||||||
virtual QStringList extraArguments();
|
virtual QStringList extraArguments();
|
||||||
|
|
||||||
@ -246,6 +248,8 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
|||||||
virtual bool canEdit() const = 0;
|
virtual bool canEdit() const = 0;
|
||||||
virtual bool canExport() const = 0;
|
virtual bool canExport() const = 0;
|
||||||
|
|
||||||
|
virtual void populateLaunchMenu(QMenu* menu) = 0;
|
||||||
|
|
||||||
bool reloadSettings();
|
bool reloadSettings();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -267,7 +271,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
|||||||
protected:
|
protected:
|
||||||
void changeStatus(Status newStatus);
|
void changeStatus(Status newStatus);
|
||||||
|
|
||||||
SettingsObjectPtr globalSettings() const { return m_global_settings.lock(); };
|
SettingsObjectPtr globalSettings() const { return m_global_settings.lock(); }
|
||||||
|
|
||||||
bool isSpecificSettingsLoaded() const { return m_specific_settings_loaded; }
|
bool isSpecificSettingsLoaded() const { return m_specific_settings_loaded; }
|
||||||
void setSpecificSettingsLoaded(bool loaded) { m_specific_settings_loaded = loaded; }
|
void setSpecificSettingsLoaded(bool loaded) { m_specific_settings_loaded = loaded; }
|
||||||
@ -282,6 +286,8 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
|||||||
|
|
||||||
void runningStatusChanged(bool running);
|
void runningStatusChanged(bool running);
|
||||||
|
|
||||||
|
void profilerChanged();
|
||||||
|
|
||||||
void statusChanged(Status from, Status to);
|
void statusChanged(Status from, Status to);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
|
@ -43,9 +43,8 @@ class BaseVersion {
|
|||||||
* the kind of version this is (Stable, Beta, Snapshot, whatever)
|
* the kind of version this is (Stable, Beta, Snapshot, whatever)
|
||||||
*/
|
*/
|
||||||
virtual QString typeString() const = 0;
|
virtual QString typeString() const = 0;
|
||||||
|
virtual bool operator<(BaseVersion& a) { return name() < a.name(); }
|
||||||
virtual bool operator<(BaseVersion& a) { return name() < a.name(); };
|
virtual bool operator>(BaseVersion& a) { return name() > a.name(); }
|
||||||
virtual bool operator>(BaseVersion& a) { return name() > a.name(); };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(BaseVersion::Ptr)
|
Q_DECLARE_METATYPE(BaseVersion::Ptr)
|
||||||
|
@ -136,6 +136,15 @@ set(NET_SOURCES
|
|||||||
net/Validator.h
|
net/Validator.h
|
||||||
net/Upload.cpp
|
net/Upload.cpp
|
||||||
net/Upload.h
|
net/Upload.h
|
||||||
|
net/HeaderProxy.h
|
||||||
|
net/RawHeaderProxy.h
|
||||||
|
net/ApiHeaderProxy.h
|
||||||
|
net/ApiDownload.h
|
||||||
|
net/ApiDownload.cpp
|
||||||
|
net/ApiUpload.cpp
|
||||||
|
net/ApiUpload.h
|
||||||
|
net/NetRequest.cpp
|
||||||
|
net/NetRequest.h
|
||||||
)
|
)
|
||||||
|
|
||||||
# Game launch logic
|
# Game launch logic
|
||||||
@ -172,6 +181,11 @@ set(MAC_UPDATE_SOURCES
|
|||||||
updater/MacSparkleUpdater.mm
|
updater/MacSparkleUpdater.mm
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(PRISM_UPDATE_SOURCES
|
||||||
|
updater/PrismExternalUpdater.h
|
||||||
|
updater/PrismExternalUpdater.cpp
|
||||||
|
)
|
||||||
|
|
||||||
# Backend for the news bar... there's usually no news.
|
# Backend for the news bar... there's usually no news.
|
||||||
set(NEWS_SOURCES
|
set(NEWS_SOURCES
|
||||||
# News System
|
# News System
|
||||||
@ -207,13 +221,9 @@ set(MINECRAFT_SOURCES
|
|||||||
minecraft/auth/MinecraftAccount.h
|
minecraft/auth/MinecraftAccount.h
|
||||||
minecraft/auth/Parsers.cpp
|
minecraft/auth/Parsers.cpp
|
||||||
minecraft/auth/Parsers.h
|
minecraft/auth/Parsers.h
|
||||||
minecraft/auth/Yggdrasil.cpp
|
|
||||||
minecraft/auth/Yggdrasil.h
|
|
||||||
|
|
||||||
minecraft/auth/flows/AuthFlow.cpp
|
minecraft/auth/flows/AuthFlow.cpp
|
||||||
minecraft/auth/flows/AuthFlow.h
|
minecraft/auth/flows/AuthFlow.h
|
||||||
minecraft/auth/flows/Mojang.cpp
|
|
||||||
minecraft/auth/flows/Mojang.h
|
|
||||||
minecraft/auth/flows/MSA.cpp
|
minecraft/auth/flows/MSA.cpp
|
||||||
minecraft/auth/flows/MSA.h
|
minecraft/auth/flows/MSA.h
|
||||||
minecraft/auth/flows/Offline.cpp
|
minecraft/auth/flows/Offline.cpp
|
||||||
@ -227,12 +237,8 @@ set(MINECRAFT_SOURCES
|
|||||||
minecraft/auth/steps/GetSkinStep.h
|
minecraft/auth/steps/GetSkinStep.h
|
||||||
minecraft/auth/steps/LauncherLoginStep.cpp
|
minecraft/auth/steps/LauncherLoginStep.cpp
|
||||||
minecraft/auth/steps/LauncherLoginStep.h
|
minecraft/auth/steps/LauncherLoginStep.h
|
||||||
minecraft/auth/steps/MigrationEligibilityStep.cpp
|
|
||||||
minecraft/auth/steps/MigrationEligibilityStep.h
|
|
||||||
minecraft/auth/steps/MinecraftProfileStep.cpp
|
minecraft/auth/steps/MinecraftProfileStep.cpp
|
||||||
minecraft/auth/steps/MinecraftProfileStep.h
|
minecraft/auth/steps/MinecraftProfileStep.h
|
||||||
minecraft/auth/steps/MinecraftProfileStepMojang.cpp
|
|
||||||
minecraft/auth/steps/MinecraftProfileStepMojang.h
|
|
||||||
minecraft/auth/steps/MSAStep.cpp
|
minecraft/auth/steps/MSAStep.cpp
|
||||||
minecraft/auth/steps/MSAStep.h
|
minecraft/auth/steps/MSAStep.h
|
||||||
minecraft/auth/steps/XboxAuthorizationStep.cpp
|
minecraft/auth/steps/XboxAuthorizationStep.cpp
|
||||||
@ -241,8 +247,6 @@ set(MINECRAFT_SOURCES
|
|||||||
minecraft/auth/steps/XboxProfileStep.h
|
minecraft/auth/steps/XboxProfileStep.h
|
||||||
minecraft/auth/steps/XboxUserStep.cpp
|
minecraft/auth/steps/XboxUserStep.cpp
|
||||||
minecraft/auth/steps/XboxUserStep.h
|
minecraft/auth/steps/XboxUserStep.h
|
||||||
minecraft/auth/steps/YggdrasilStep.cpp
|
|
||||||
minecraft/auth/steps/YggdrasilStep.h
|
|
||||||
|
|
||||||
minecraft/gameoptions/GameOptions.h
|
minecraft/gameoptions/GameOptions.h
|
||||||
minecraft/gameoptions/GameOptions.cpp
|
minecraft/gameoptions/GameOptions.cpp
|
||||||
@ -566,6 +570,9 @@ set(ATLAUNCHER_SOURCES
|
|||||||
)
|
)
|
||||||
|
|
||||||
set(LINKEXE_SOURCES
|
set(LINKEXE_SOURCES
|
||||||
|
WindowsConsole.cpp
|
||||||
|
WindowsConsole.h
|
||||||
|
|
||||||
filelink/FileLink.h
|
filelink/FileLink.h
|
||||||
filelink/FileLink.cpp
|
filelink/FileLink.cpp
|
||||||
FileSystem.h
|
FileSystem.h
|
||||||
@ -577,6 +584,63 @@ set(LINKEXE_SOURCES
|
|||||||
DesktopServices.cpp
|
DesktopServices.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(PRISMUPDATER_SOURCES
|
||||||
|
updater/prismupdater/PrismUpdater.h
|
||||||
|
updater/prismupdater/PrismUpdater.cpp
|
||||||
|
updater/prismupdater/UpdaterDialogs.h
|
||||||
|
updater/prismupdater/UpdaterDialogs.cpp
|
||||||
|
updater/prismupdater/GitHubRelease.h
|
||||||
|
updater/prismupdater/GitHubRelease.cpp
|
||||||
|
|
||||||
|
Json.h
|
||||||
|
Json.cpp
|
||||||
|
FileSystem.h
|
||||||
|
FileSystem.cpp
|
||||||
|
StringUtils.h
|
||||||
|
StringUtils.cpp
|
||||||
|
DesktopServices.h
|
||||||
|
DesktopServices.cpp
|
||||||
|
Version.h
|
||||||
|
Version.cpp
|
||||||
|
Markdown.h
|
||||||
|
Markdown.cpp
|
||||||
|
|
||||||
|
# Zip
|
||||||
|
MMCZip.h
|
||||||
|
MMCZip.cpp
|
||||||
|
|
||||||
|
# Time
|
||||||
|
MMCTime.h
|
||||||
|
MMCTime.cpp
|
||||||
|
|
||||||
|
net/ByteArraySink.h
|
||||||
|
net/ChecksumValidator.h
|
||||||
|
net/Download.cpp
|
||||||
|
net/Download.h
|
||||||
|
net/FileSink.cpp
|
||||||
|
net/FileSink.h
|
||||||
|
net/HttpMetaCache.cpp
|
||||||
|
net/HttpMetaCache.h
|
||||||
|
net/Logging.h
|
||||||
|
net/Logging.cpp
|
||||||
|
net/NetAction.h
|
||||||
|
net/NetRequest.cpp
|
||||||
|
net/NetRequest.h
|
||||||
|
net/NetJob.cpp
|
||||||
|
net/NetJob.h
|
||||||
|
net/NetUtils.h
|
||||||
|
net/Sink.h
|
||||||
|
net/Validator.h
|
||||||
|
net/HeaderProxy.h
|
||||||
|
net/RawHeaderProxy.h
|
||||||
|
|
||||||
|
ui/dialogs/ProgressDialog.cpp
|
||||||
|
ui/dialogs/ProgressDialog.h
|
||||||
|
ui/widgets/SubTaskProgressBar.h
|
||||||
|
ui/widgets/SubTaskProgressBar.cpp
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
######## Logging categories ########
|
######## Logging categories ########
|
||||||
|
|
||||||
ecm_qt_declare_logging_category(CORE_SOURCES
|
ecm_qt_declare_logging_category(CORE_SOURCES
|
||||||
@ -673,6 +737,8 @@ set(LOGIC_SOURCES
|
|||||||
|
|
||||||
if(APPLE AND Launcher_ENABLE_UPDATER)
|
if(APPLE AND Launcher_ENABLE_UPDATER)
|
||||||
set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES})
|
set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES})
|
||||||
|
else()
|
||||||
|
set (LOGIC_SOURCES ${LOGIC_SOURCES} ${PRISM_UPDATE_SOURCES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
SET(LAUNCHER_SOURCES
|
SET(LAUNCHER_SOURCES
|
||||||
@ -762,6 +828,8 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/themes/ITheme.h
|
ui/themes/ITheme.h
|
||||||
ui/themes/SystemTheme.cpp
|
ui/themes/SystemTheme.cpp
|
||||||
ui/themes/SystemTheme.h
|
ui/themes/SystemTheme.h
|
||||||
|
ui/themes/IconTheme.cpp
|
||||||
|
ui/themes/IconTheme.h
|
||||||
ui/themes/ThemeManager.cpp
|
ui/themes/ThemeManager.cpp
|
||||||
ui/themes/ThemeManager.h
|
ui/themes/ThemeManager.h
|
||||||
ui/themes/CatPack.cpp
|
ui/themes/CatPack.cpp
|
||||||
@ -902,6 +970,9 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/pages/modplatform/ImportPage.cpp
|
ui/pages/modplatform/ImportPage.cpp
|
||||||
ui/pages/modplatform/ImportPage.h
|
ui/pages/modplatform/ImportPage.h
|
||||||
|
|
||||||
|
ui/pages/modplatform/OptionalModDialog.cpp
|
||||||
|
ui/pages/modplatform/OptionalModDialog.h
|
||||||
|
|
||||||
ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp
|
ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp
|
||||||
ui/pages/modplatform/modrinth/ModrinthResourceModels.h
|
ui/pages/modplatform/modrinth/ModrinthResourceModels.h
|
||||||
ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp
|
ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp
|
||||||
@ -930,8 +1001,6 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/dialogs/IconPickerDialog.h
|
ui/dialogs/IconPickerDialog.h
|
||||||
ui/dialogs/ImportResourceDialog.cpp
|
ui/dialogs/ImportResourceDialog.cpp
|
||||||
ui/dialogs/ImportResourceDialog.h
|
ui/dialogs/ImportResourceDialog.h
|
||||||
ui/dialogs/LoginDialog.cpp
|
|
||||||
ui/dialogs/LoginDialog.h
|
|
||||||
ui/dialogs/MSALoginDialog.cpp
|
ui/dialogs/MSALoginDialog.cpp
|
||||||
ui/dialogs/MSALoginDialog.h
|
ui/dialogs/MSALoginDialog.h
|
||||||
ui/dialogs/OfflineLoginDialog.cpp
|
ui/dialogs/OfflineLoginDialog.cpp
|
||||||
@ -962,6 +1031,8 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/dialogs/ChooseProviderDialog.cpp
|
ui/dialogs/ChooseProviderDialog.cpp
|
||||||
ui/dialogs/ModUpdateDialog.cpp
|
ui/dialogs/ModUpdateDialog.cpp
|
||||||
ui/dialogs/ModUpdateDialog.h
|
ui/dialogs/ModUpdateDialog.h
|
||||||
|
ui/dialogs/InstallLoaderDialog.cpp
|
||||||
|
ui/dialogs/InstallLoaderDialog.h
|
||||||
|
|
||||||
# GUI - widgets
|
# GUI - widgets
|
||||||
ui/widgets/Common.cpp
|
ui/widgets/Common.cpp
|
||||||
@ -1026,6 +1097,23 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/instanceview/VisualGroup.h
|
ui/instanceview/VisualGroup.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (NOT Apple)
|
||||||
|
set(LAUNCHER_SOURCES
|
||||||
|
${LAUNCHER_SOURCES}
|
||||||
|
|
||||||
|
ui/dialogs/UpdateAvailableDialog.h
|
||||||
|
ui/dialogs/UpdateAvailableDialog.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
set(LAUNCHER_SOURCES
|
||||||
|
WindowsConsole.cpp
|
||||||
|
WindowsConsole.h
|
||||||
|
${LAUNCHER_SOURCES}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
qt_wrap_ui(LAUNCHER_UI
|
qt_wrap_ui(LAUNCHER_UI
|
||||||
ui/MainWindow.ui
|
ui/MainWindow.ui
|
||||||
ui/setupwizard/PasteWizardPage.ui
|
ui/setupwizard/PasteWizardPage.ui
|
||||||
@ -1056,6 +1144,7 @@ qt_wrap_ui(LAUNCHER_UI
|
|||||||
ui/pages/modplatform/legacy_ftb/Page.ui
|
ui/pages/modplatform/legacy_ftb/Page.ui
|
||||||
ui/pages/modplatform/import_ftb/ImportFTBPage.ui
|
ui/pages/modplatform/import_ftb/ImportFTBPage.ui
|
||||||
ui/pages/modplatform/ImportPage.ui
|
ui/pages/modplatform/ImportPage.ui
|
||||||
|
ui/pages/modplatform/OptionalModDialog.ui
|
||||||
ui/pages/modplatform/modrinth/ModrinthPage.ui
|
ui/pages/modplatform/modrinth/ModrinthPage.ui
|
||||||
ui/pages/modplatform/technic/TechnicPage.ui
|
ui/pages/modplatform/technic/TechnicPage.ui
|
||||||
ui/widgets/InstanceCardWidget.ui
|
ui/widgets/InstanceCardWidget.ui
|
||||||
@ -1080,7 +1169,6 @@ qt_wrap_ui(LAUNCHER_UI
|
|||||||
ui/dialogs/MSALoginDialog.ui
|
ui/dialogs/MSALoginDialog.ui
|
||||||
ui/dialogs/OfflineLoginDialog.ui
|
ui/dialogs/OfflineLoginDialog.ui
|
||||||
ui/dialogs/AboutDialog.ui
|
ui/dialogs/AboutDialog.ui
|
||||||
ui/dialogs/LoginDialog.ui
|
|
||||||
ui/dialogs/EditAccountDialog.ui
|
ui/dialogs/EditAccountDialog.ui
|
||||||
ui/dialogs/ReviewMessageBox.ui
|
ui/dialogs/ReviewMessageBox.ui
|
||||||
ui/dialogs/ScrollMessageBox.ui
|
ui/dialogs/ScrollMessageBox.ui
|
||||||
@ -1088,6 +1176,14 @@ qt_wrap_ui(LAUNCHER_UI
|
|||||||
ui/dialogs/ChooseProviderDialog.ui
|
ui/dialogs/ChooseProviderDialog.ui
|
||||||
)
|
)
|
||||||
|
|
||||||
|
qt_wrap_ui(PRISM_UPDATE_UI
|
||||||
|
ui/dialogs/UpdateAvailableDialog.ui
|
||||||
|
)
|
||||||
|
|
||||||
|
if (NOT Apple)
|
||||||
|
set (LAUNCHER_UI ${LAUNCHER_UI} ${PRISM_UPDATE_UI})
|
||||||
|
endif()
|
||||||
|
|
||||||
qt_add_resources(LAUNCHER_RESOURCES
|
qt_add_resources(LAUNCHER_RESOURCES
|
||||||
resources/backgrounds/backgrounds.qrc
|
resources/backgrounds/backgrounds.qrc
|
||||||
resources/multimc/multimc.qrc
|
resources/multimc/multimc.qrc
|
||||||
@ -1104,14 +1200,31 @@ qt_add_resources(LAUNCHER_RESOURCES
|
|||||||
../${Launcher_Branding_LogoQRC}
|
../${Launcher_Branding_LogoQRC}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
qt_wrap_ui(PRISMUPDATER_UI
|
||||||
|
updater/prismupdater/SelectReleaseDialog.ui
|
||||||
|
ui/widgets/SubTaskProgressBar.ui
|
||||||
|
ui/dialogs/ProgressDialog.ui
|
||||||
|
)
|
||||||
|
|
||||||
######## Windows resource files ########
|
######## Windows resource files ########
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC})
|
set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
include(CompilerWarnings)
|
||||||
|
|
||||||
# Add executable
|
# Add executable
|
||||||
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES})
|
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES})
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_TEST)
|
||||||
|
endif()
|
||||||
|
set_project_warnings(Launcher_logic
|
||||||
|
"${Launcher_MSVC_WARNINGS}"
|
||||||
|
"${Launcher_CLANG_WARNINGS}"
|
||||||
|
"${Launcher_GCC_WARNINGS}")
|
||||||
|
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
|
||||||
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
|
||||||
target_link_libraries(Launcher_logic
|
target_link_libraries(Launcher_logic
|
||||||
systeminfo
|
systeminfo
|
||||||
Launcher_murmur2
|
Launcher_murmur2
|
||||||
@ -1193,8 +1306,51 @@ install(TARGETS ${Launcher_Name}
|
|||||||
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
|
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
|
||||||
)
|
)
|
||||||
|
|
||||||
if(WIN32)
|
if(Launcher_BUILD_UPDATER)
|
||||||
|
# Updater
|
||||||
|
add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI})
|
||||||
|
target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
target_link_libraries(prism_updater_logic
|
||||||
|
QuaZip::QuaZip
|
||||||
|
${ZLIB_LIBRARIES}
|
||||||
|
systeminfo
|
||||||
|
BuildConfig
|
||||||
|
ghcFilesystem::ghc_filesystem
|
||||||
|
Qt${QT_VERSION_MAJOR}::Widgets
|
||||||
|
Qt${QT_VERSION_MAJOR}::Core
|
||||||
|
Qt${QT_VERSION_MAJOR}::Network
|
||||||
|
${Launcher_QT_LIBS}
|
||||||
|
cmark::cmark
|
||||||
|
Katabasis
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp)
|
||||||
|
target_sources("${Launcher_Name}_updater" PRIVATE updater/prismupdater/updater.exe.manifest)
|
||||||
|
target_link_libraries("${Launcher_Name}_updater" prism_updater_logic)
|
||||||
|
|
||||||
|
if(DEFINED Launcher_APP_BINARY_NAME)
|
||||||
|
set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater")
|
||||||
|
endif()
|
||||||
|
if(DEFINED Launcher_BINARY_RPATH)
|
||||||
|
SET_TARGET_PROPERTIES("${Launcher_Name}_updater" PROPERTIES INSTALL_RPATH "${Launcher_BINARY_RPATH}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install(TARGETS "${Launcher_Name}_updater"
|
||||||
|
BUNDLE DESTINATION "." COMPONENT Runtime
|
||||||
|
LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime
|
||||||
|
RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime
|
||||||
|
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
|
||||||
|
# File link
|
||||||
add_library(filelink_logic STATIC ${LINKEXE_SOURCES})
|
add_library(filelink_logic STATIC ${LINKEXE_SOURCES})
|
||||||
|
set_project_warnings(filelink_logic
|
||||||
|
"${Launcher_MSVC_WARNINGS}"
|
||||||
|
"${Launcher_CLANG_WARNINGS}"
|
||||||
|
"${Launcher_GCC_WARNINGS}")
|
||||||
|
|
||||||
target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
target_link_libraries(filelink_logic
|
target_link_libraries(filelink_logic
|
||||||
systeminfo
|
systeminfo
|
||||||
@ -1207,7 +1363,7 @@ if(WIN32)
|
|||||||
${Launcher_QT_LIBS}
|
${Launcher_QT_LIBS}
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable("${Launcher_Name}_filelink" WIN32 filelink/main.cpp)
|
add_executable("${Launcher_Name}_filelink" WIN32 filelink/filelink_main.cpp)
|
||||||
|
|
||||||
target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest)
|
target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest)
|
||||||
|
|
||||||
|
@ -96,12 +96,12 @@ bool IndirectOpen(T callable, qint64* pid_forked = nullptr)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace DesktopServices {
|
namespace DesktopServices {
|
||||||
bool openDirectory(const QString& path, bool ensureExists)
|
bool openDirectory(const QString& path, [[maybe_unused]] bool ensureExists)
|
||||||
{
|
{
|
||||||
qDebug() << "Opening directory" << path;
|
qDebug() << "Opening directory" << path;
|
||||||
QDir parentPath;
|
QDir parentPath;
|
||||||
QDir dir(path);
|
QDir dir(path);
|
||||||
if (!dir.exists()) {
|
if (ensureExists && !dir.exists()) {
|
||||||
parentPath.mkpath(dir.absolutePath());
|
parentPath.mkpath(dir.absolutePath());
|
||||||
}
|
}
|
||||||
auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); };
|
auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); };
|
||||||
|
@ -267,10 +267,7 @@ bool FileIgnoreProxy::filterAcceptsRow(int sourceRow, const QModelIndex& sourceP
|
|||||||
|
|
||||||
bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const
|
bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const
|
||||||
{
|
{
|
||||||
auto fileName = fileInfo.fileName();
|
return m_ignoreFiles.contains(fileInfo.fileName()) || m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath()));
|
||||||
auto path = relPath(fileInfo.absoluteFilePath());
|
|
||||||
return std::any_of(m_ignoreFiles.cbegin(), m_ignoreFiles.cend(), [fileName](auto iFileName) { return fileName == iFileName; }) ||
|
|
||||||
m_ignoreFilePaths.covers(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileIgnoreProxy::filterFile(const QString& fileName) const
|
bool FileIgnoreProxy::filterFile(const QString& fileName) const
|
||||||
|
@ -194,6 +194,40 @@ void write(const QString& filename, const QByteArray& data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void appendSafe(const QString& filename, const QByteArray& data)
|
||||||
|
{
|
||||||
|
ensureExists(QFileInfo(filename).dir());
|
||||||
|
QByteArray buffer;
|
||||||
|
try {
|
||||||
|
buffer = read(filename);
|
||||||
|
} catch (FileSystemException&) {
|
||||||
|
buffer = QByteArray();
|
||||||
|
}
|
||||||
|
buffer.append(data);
|
||||||
|
QSaveFile file(filename);
|
||||||
|
if (!file.open(QSaveFile::WriteOnly)) {
|
||||||
|
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
|
||||||
|
}
|
||||||
|
if (buffer.size() != file.write(buffer)) {
|
||||||
|
throw FileSystemException("Error writing data to " + filename + ": " + file.errorString());
|
||||||
|
}
|
||||||
|
if (!file.commit()) {
|
||||||
|
throw FileSystemException("Error while committing data to " + filename + ": " + file.errorString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(const QString& filename, const QByteArray& data)
|
||||||
|
{
|
||||||
|
ensureExists(QFileInfo(filename).dir());
|
||||||
|
QFile file(filename);
|
||||||
|
if (!file.open(QFile::Append)) {
|
||||||
|
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
|
||||||
|
}
|
||||||
|
if (data.size() != file.write(data)) {
|
||||||
|
throw FileSystemException("Error writing data to " + filename + ": " + file.errorString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray read(const QString& filename)
|
QByteArray read(const QString& filename)
|
||||||
{
|
{
|
||||||
QFile file(filename);
|
QFile file(filename);
|
||||||
@ -238,6 +272,28 @@ bool ensureFolderPathExists(QString foldernamepath)
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool copyFileAttributes(QString src, QString dst)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
auto attrs = GetFileAttributesW(src.toStdWString().c_str());
|
||||||
|
if (attrs == INVALID_FILE_ATTRIBUTES)
|
||||||
|
return false;
|
||||||
|
return SetFileAttributesW(dst.toStdWString().c_str(), attrs);
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// needs folders to exists
|
||||||
|
void copyFolderAttributes(QString src, QString dst, QString relative)
|
||||||
|
{
|
||||||
|
auto path = PathCombine(src, relative);
|
||||||
|
QDir dsrc(src);
|
||||||
|
while ((path = QFileInfo(path).path()).length() >= src.length()) {
|
||||||
|
auto dst_path = PathCombine(dst, dsrc.relativeFilePath(path));
|
||||||
|
copyFileAttributes(path, dst_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copies a directory and it's contents from src to dest
|
* @brief Copies a directory and it's contents from src to dest
|
||||||
* @param offset subdirectory form src to copy to dest
|
* @param offset subdirectory form src to copy to dest
|
||||||
@ -265,6 +321,9 @@ bool copy::operator()(const QString& offset, bool dryRun)
|
|||||||
if (!m_followSymlinks)
|
if (!m_followSymlinks)
|
||||||
opt |= copy_opts::copy_symlinks;
|
opt |= copy_opts::copy_symlinks;
|
||||||
|
|
||||||
|
if (m_overwrite)
|
||||||
|
opt |= copy_opts::overwrite_existing;
|
||||||
|
|
||||||
// Function that'll do the actual copying
|
// Function that'll do the actual copying
|
||||||
auto copy_file = [&](QString src_path, QString relative_dst_path) {
|
auto copy_file = [&](QString src_path, QString relative_dst_path) {
|
||||||
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
|
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
|
||||||
@ -273,6 +332,9 @@ bool copy::operator()(const QString& offset, bool dryRun)
|
|||||||
auto dst_path = PathCombine(dst, relative_dst_path);
|
auto dst_path = PathCombine(dst, relative_dst_path);
|
||||||
if (!dryRun) {
|
if (!dryRun) {
|
||||||
ensureFilePathExists(dst_path);
|
ensureFilePathExists(dst_path);
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
copyFolderAttributes(src, dst, relative_dst_path);
|
||||||
|
#endif
|
||||||
fs::copy(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), opt, err);
|
fs::copy(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), opt, err);
|
||||||
}
|
}
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -61,6 +61,16 @@ class FileSystemException : public ::Exception {
|
|||||||
*/
|
*/
|
||||||
void write(const QString& filename, const QByteArray& data);
|
void write(const QString& filename, const QByteArray& data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* append data to a file safely
|
||||||
|
*/
|
||||||
|
void appendSafe(const QString& filename, const QByteArray& data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* append data to a file
|
||||||
|
*/
|
||||||
|
void append(const QString& filename, const QByteArray& data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* read data from a file safely\
|
* read data from a file safely\
|
||||||
*/
|
*/
|
||||||
@ -109,11 +119,16 @@ class copy : public QObject {
|
|||||||
m_whitelist = whitelist;
|
m_whitelist = whitelist;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
copy& overwrite(const bool overwrite)
|
||||||
|
{
|
||||||
|
m_overwrite = overwrite;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||||
|
|
||||||
int totalCopied() { return m_copied; }
|
qsizetype totalCopied() { return m_copied; }
|
||||||
int totalFailed() { return m_failedPaths.length(); }
|
qsizetype totalFailed() { return m_failedPaths.length(); }
|
||||||
QStringList failed() { return m_failedPaths; }
|
QStringList failed() { return m_failedPaths; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@ -128,9 +143,10 @@ class copy : public QObject {
|
|||||||
bool m_followSymlinks = true;
|
bool m_followSymlinks = true;
|
||||||
const IPathMatcher* m_matcher = nullptr;
|
const IPathMatcher* m_matcher = nullptr;
|
||||||
bool m_whitelist = false;
|
bool m_whitelist = false;
|
||||||
|
bool m_overwrite = false;
|
||||||
QDir m_src;
|
QDir m_src;
|
||||||
QDir m_dst;
|
QDir m_dst;
|
||||||
int m_copied;
|
qsizetype m_copied;
|
||||||
QStringList m_failedPaths;
|
QStringList m_failedPaths;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -474,8 +490,8 @@ class clone : public QObject {
|
|||||||
|
|
||||||
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||||
|
|
||||||
int totalCloned() { return m_cloned; }
|
qsizetype totalCloned() { return m_cloned; }
|
||||||
int totalFailed() { return m_failedClones.length(); }
|
qsizetype totalFailed() { return m_failedClones.length(); }
|
||||||
|
|
||||||
QList<QPair<QString, QString>> failed() { return m_failedClones; }
|
QList<QPair<QString, QString>> failed() { return m_failedClones; }
|
||||||
|
|
||||||
@ -491,7 +507,7 @@ class clone : public QObject {
|
|||||||
bool m_whitelist = false;
|
bool m_whitelist = false;
|
||||||
QDir m_src;
|
QDir m_src;
|
||||||
QDir m_dst;
|
QDir m_dst;
|
||||||
int m_cloned;
|
qsizetype m_cloned;
|
||||||
QList<QPair<QString, QString>> m_failedClones;
|
QList<QPair<QString, QString>> m_failedClones;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,6 +16,12 @@ bool ExactFilter::accepts(const QString& value)
|
|||||||
return value == pattern;
|
return value == pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExactIfPresentFilter::ExactIfPresentFilter(const QString& pattern) : pattern(pattern) {}
|
||||||
|
bool ExactIfPresentFilter::accepts(const QString& value)
|
||||||
|
{
|
||||||
|
return value.isEmpty() || value == pattern;
|
||||||
|
}
|
||||||
|
|
||||||
RegexpFilter::RegexpFilter(const QString& regexp, bool invert) : invert(invert)
|
RegexpFilter::RegexpFilter(const QString& regexp, bool invert) : invert(invert)
|
||||||
{
|
{
|
||||||
pattern.setPattern(regexp);
|
pattern.setPattern(regexp);
|
||||||
|
@ -29,6 +29,16 @@ class ExactFilter : public Filter {
|
|||||||
QString pattern;
|
QString pattern;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ExactIfPresentFilter : public Filter {
|
||||||
|
public:
|
||||||
|
ExactIfPresentFilter(const QString& pattern);
|
||||||
|
~ExactIfPresentFilter() override = default;
|
||||||
|
bool accepts(const QString& value) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString pattern;
|
||||||
|
};
|
||||||
|
|
||||||
class RegexpFilter : public Filter {
|
class RegexpFilter : public Filter {
|
||||||
public:
|
public:
|
||||||
RegexpFilter(const QString& regexp, bool invert);
|
RegexpFilter(const QString& regexp, bool invert);
|
||||||
|
@ -50,6 +50,9 @@
|
|||||||
#include "modplatform/technic/TechnicPackProcessor.h"
|
#include "modplatform/technic/TechnicPackProcessor.h"
|
||||||
|
|
||||||
#include "settings/INISettingsObject.h"
|
#include "settings/INISettingsObject.h"
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
|
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -88,24 +91,26 @@ void InstanceImportTask::executeTask()
|
|||||||
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
||||||
m_downloadRequired = true;
|
m_downloadRequired = true;
|
||||||
|
|
||||||
const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path());
|
downloadFromUrl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceImportTask::downloadFromUrl()
|
||||||
|
{
|
||||||
|
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
|
||||||
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
||||||
entry->setStale(true);
|
entry->setStale(true);
|
||||||
m_archivePath = entry->getFullPath();
|
|
||||||
|
|
||||||
m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network()));
|
m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network()));
|
||||||
m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
|
m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry));
|
||||||
|
m_archivePath = entry->getFullPath();
|
||||||
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
||||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
||||||
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||||
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
||||||
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
|
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
|
||||||
|
|
||||||
m_filesNetJob->start();
|
m_filesNetJob->start();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceImportTask::downloadSucceeded()
|
void InstanceImportTask::downloadSucceeded()
|
||||||
{
|
{
|
||||||
|
@ -101,4 +101,5 @@ class InstanceImportTask : public InstanceTask {
|
|||||||
|
|
||||||
// FIXME: nuke
|
// FIXME: nuke
|
||||||
QWidget* m_parent;
|
QWidget* m_parent;
|
||||||
|
void downloadFromUrl();
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -96,7 +97,11 @@ Qt::DropActions InstanceList::supportedDropActions() const
|
|||||||
return Qt::MoveAction;
|
return Qt::MoveAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const
|
bool InstanceList::canDropMimeData(const QMimeData* data,
|
||||||
|
[[maybe_unused]] Qt::DropAction action,
|
||||||
|
[[maybe_unused]] int row,
|
||||||
|
[[maybe_unused]] int column,
|
||||||
|
[[maybe_unused]] const QModelIndex& parent) const
|
||||||
{
|
{
|
||||||
if (data && data->hasFormat("application/x-instanceid")) {
|
if (data && data->hasFormat("application/x-instanceid")) {
|
||||||
return true;
|
return true;
|
||||||
@ -104,7 +109,11 @@ bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InstanceList::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)
|
bool InstanceList::dropMimeData(const QMimeData* data,
|
||||||
|
[[maybe_unused]] Qt::DropAction action,
|
||||||
|
[[maybe_unused]] int row,
|
||||||
|
[[maybe_unused]] int column,
|
||||||
|
[[maybe_unused]] const QModelIndex& parent)
|
||||||
{
|
{
|
||||||
if (data && data->hasFormat("application/x-instanceid")) {
|
if (data && data->hasFormat("application/x-instanceid")) {
|
||||||
return true;
|
return true;
|
||||||
@ -229,8 +238,11 @@ GroupId InstanceList::getInstanceGroup(const InstanceId& id) const
|
|||||||
return GroupId();
|
return GroupId();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
|
void InstanceList::setInstanceGroup(const InstanceId& id, GroupId name)
|
||||||
{
|
{
|
||||||
|
if (name.isEmpty() && !name.isNull())
|
||||||
|
name = QString();
|
||||||
|
|
||||||
auto inst = getInstanceById(id);
|
auto inst = getInstanceById(id);
|
||||||
if (!inst) {
|
if (!inst) {
|
||||||
qDebug() << "Attempt to set a null instance's group";
|
qDebug() << "Attempt to set a null instance's group";
|
||||||
@ -241,6 +253,7 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
|
|||||||
auto iter = m_instanceGroupIndex.find(inst->id());
|
auto iter = m_instanceGroupIndex.find(inst->id());
|
||||||
if (iter != m_instanceGroupIndex.end()) {
|
if (iter != m_instanceGroupIndex.end()) {
|
||||||
if (*iter != name) {
|
if (*iter != name) {
|
||||||
|
decreaseGroupCount(*iter);
|
||||||
*iter = name;
|
*iter = name;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
@ -250,7 +263,7 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
m_groupNameCache.insert(name);
|
increaseGroupCount(name);
|
||||||
auto idx = getInstIndex(inst.get());
|
auto idx = getInstIndex(inst.get());
|
||||||
emit dataChanged(index(idx), index(idx), { GroupRole });
|
emit dataChanged(index(idx), index(idx), { GroupRole });
|
||||||
saveGroupList();
|
saveGroupList();
|
||||||
@ -259,29 +272,55 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
|
|||||||
|
|
||||||
QStringList InstanceList::getGroups()
|
QStringList InstanceList::getGroups()
|
||||||
{
|
{
|
||||||
return m_groupNameCache.values();
|
return m_groupNameCache.keys();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceList::deleteGroup(const QString& name)
|
void InstanceList::deleteGroup(const GroupId& name)
|
||||||
{
|
{
|
||||||
|
m_groupNameCache.remove(name);
|
||||||
|
m_collapsedGroups.remove(name);
|
||||||
|
|
||||||
bool removed = false;
|
bool removed = false;
|
||||||
qDebug() << "Delete group" << name;
|
qDebug() << "Delete group" << name;
|
||||||
for (auto& instance : m_instances) {
|
for (auto& instance : m_instances) {
|
||||||
const auto& instID = instance->id();
|
const QString& instID = instance->id();
|
||||||
auto instGroupName = getInstanceGroup(instID);
|
const QString instGroupName = getInstanceGroup(instID);
|
||||||
if (instGroupName == name) {
|
if (instGroupName == name) {
|
||||||
m_instanceGroupIndex.remove(instID);
|
m_instanceGroupIndex.remove(instID);
|
||||||
qDebug() << "Remove" << instID << "from group" << name;
|
qDebug() << "Remove" << instID << "from group" << name;
|
||||||
removed = true;
|
removed = true;
|
||||||
auto idx = getInstIndex(instance.get());
|
auto idx = getInstIndex(instance.get());
|
||||||
if (idx > 0) {
|
if (idx > 0)
|
||||||
emit dataChanged(index(idx), index(idx), { GroupRole });
|
emit dataChanged(index(idx), index(idx), { GroupRole });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (removed)
|
||||||
if (removed) {
|
|
||||||
saveGroupList();
|
saveGroupList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InstanceList::renameGroup(const QString& src, const QString& dst)
|
||||||
|
{
|
||||||
|
m_groupNameCache.remove(src);
|
||||||
|
if (m_collapsedGroups.remove(src))
|
||||||
|
m_collapsedGroups.insert(dst);
|
||||||
|
|
||||||
|
bool modified = false;
|
||||||
|
qDebug() << "Rename group" << src << "to" << dst;
|
||||||
|
for (auto& instance : m_instances) {
|
||||||
|
const QString& instID = instance->id();
|
||||||
|
const QString instGroupName = getInstanceGroup(instID);
|
||||||
|
if (instGroupName == src) {
|
||||||
|
m_instanceGroupIndex[instID] = dst;
|
||||||
|
increaseGroupCount(dst);
|
||||||
|
qDebug() << "Set" << instID << "group to" << dst;
|
||||||
|
modified = true;
|
||||||
|
auto idx = getInstIndex(instance.get());
|
||||||
|
if (idx > 0)
|
||||||
|
emit dataChanged(index(idx), index(idx), { GroupRole });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (modified)
|
||||||
|
saveGroupList();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InstanceList::isGroupCollapsed(const QString& group)
|
bool InstanceList::isGroupCollapsed(const QString& group)
|
||||||
@ -297,12 +336,13 @@ bool InstanceList::trashInstance(const InstanceId& id)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto cachedGroupId = m_instanceGroupIndex[id];
|
QString cachedGroupId = m_instanceGroupIndex[id];
|
||||||
|
|
||||||
qDebug() << "Will trash instance" << id;
|
qDebug() << "Will trash instance" << id;
|
||||||
QString trashedLoc;
|
QString trashedLoc;
|
||||||
|
|
||||||
if (m_instanceGroupIndex.remove(id)) {
|
if (m_instanceGroupIndex.remove(id)) {
|
||||||
|
decreaseGroupCount(cachedGroupId);
|
||||||
saveGroupList();
|
saveGroupList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,7 +380,7 @@ void InstanceList::undoTrashInstance()
|
|||||||
QFile(top.trashPath).rename(top.polyPath);
|
QFile(top.trashPath).rename(top.polyPath);
|
||||||
|
|
||||||
m_instanceGroupIndex[top.id] = top.groupName;
|
m_instanceGroupIndex[top.id] = top.groupName;
|
||||||
m_groupNameCache.insert(top.groupName);
|
increaseGroupCount(top.groupName);
|
||||||
|
|
||||||
saveGroupList();
|
saveGroupList();
|
||||||
emit instancesChanged();
|
emit instancesChanged();
|
||||||
@ -354,7 +394,10 @@ void InstanceList::deleteInstance(const InstanceId& id)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString cachedGroupId = m_instanceGroupIndex[id];
|
||||||
|
|
||||||
if (m_instanceGroupIndex.remove(id)) {
|
if (m_instanceGroupIndex.remove(id)) {
|
||||||
|
decreaseGroupCount(cachedGroupId);
|
||||||
saveGroupList();
|
saveGroupList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -602,6 +645,25 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id)
|
|||||||
return inst;
|
return inst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InstanceList::increaseGroupCount(const QString& group)
|
||||||
|
{
|
||||||
|
if (group.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
++m_groupNameCache[group];
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceList::decreaseGroupCount(const QString& group)
|
||||||
|
{
|
||||||
|
if (group.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (--m_groupNameCache[group] < 1) {
|
||||||
|
m_groupNameCache.remove(group);
|
||||||
|
m_collapsedGroups.remove(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void InstanceList::saveGroupList()
|
void InstanceList::saveGroupList()
|
||||||
{
|
{
|
||||||
qDebug() << "Will save group list now.";
|
qDebug() << "Will save group list now.";
|
||||||
@ -613,7 +675,7 @@ void InstanceList::saveGroupList()
|
|||||||
QString groupFileName = m_instDir + "/instgroups.json";
|
QString groupFileName = m_instDir + "/instgroups.json";
|
||||||
QMap<QString, QSet<QString>> reverseGroupMap;
|
QMap<QString, QSet<QString>> reverseGroupMap;
|
||||||
for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++) {
|
for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++) {
|
||||||
QString id = iter.key();
|
const QString& id = iter.key();
|
||||||
QString group = iter.value();
|
QString group = iter.value();
|
||||||
if (group.isEmpty())
|
if (group.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
@ -703,17 +765,22 @@ void InstanceList::loadGroupList()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSet<QString> groupSet;
|
|
||||||
m_instanceGroupIndex.clear();
|
m_instanceGroupIndex.clear();
|
||||||
|
m_groupNameCache.clear();
|
||||||
|
|
||||||
// Iterate through all the groups.
|
// Iterate through all the groups.
|
||||||
QJsonObject groupMapping = rootObj.value("groups").toObject();
|
QJsonObject groupMapping = rootObj.value("groups").toObject();
|
||||||
for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++) {
|
for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++) {
|
||||||
QString groupName = iter.key();
|
QString groupName = iter.key();
|
||||||
|
|
||||||
|
if (iter.key().isEmpty()) {
|
||||||
|
qWarning() << "Redundant empty group found";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// If not an object, complain and skip to the next one.
|
// If not an object, complain and skip to the next one.
|
||||||
if (!iter.value().isObject()) {
|
if (!iter.value().isObject()) {
|
||||||
qWarning() << QString("Group '%1' in the group list should be an object.").arg(groupName).toUtf8();
|
qWarning() << QString("Group '%1' in the group list should be an object").arg(groupName).toUtf8();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -725,23 +792,19 @@ void InstanceList::loadGroupList()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// keep a list/set of groups for choosing
|
|
||||||
groupSet.insert(groupName);
|
|
||||||
|
|
||||||
auto hidden = groupObj.value("hidden").toBool(false);
|
auto hidden = groupObj.value("hidden").toBool(false);
|
||||||
if (hidden) {
|
if (hidden)
|
||||||
m_collapsedGroups.insert(groupName);
|
m_collapsedGroups.insert(groupName);
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate through the list of instances in the group.
|
// Iterate through the list of instances in the group.
|
||||||
QJsonArray instancesArray = groupObj.value("instances").toArray();
|
QJsonArray instancesArray = groupObj.value("instances").toArray();
|
||||||
|
|
||||||
for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); iter2++) {
|
for (auto value : instancesArray) {
|
||||||
m_instanceGroupIndex[(*iter2).toString()] = groupName;
|
m_instanceGroupIndex[value.toString()] = groupName;
|
||||||
|
increaseGroupCount(groupName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_groupsLoaded = true;
|
m_groupsLoaded = true;
|
||||||
m_groupNameCache.unite(groupSet);
|
|
||||||
qDebug() << "Group list loaded.";
|
qDebug() << "Group list loaded.";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -751,7 +814,7 @@ void InstanceList::instanceDirContentsChanged(const QString& path)
|
|||||||
emit instancesChanged();
|
emit instancesChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceList::on_InstFolderChanged(const Setting& setting, QVariant value)
|
void InstanceList::on_InstFolderChanged([[maybe_unused]] const Setting& setting, QVariant value)
|
||||||
{
|
{
|
||||||
QString newInstDir = QDir(value.toString()).canonicalPath();
|
QString newInstDir = QDir(value.toString()).canonicalPath();
|
||||||
if (newInstDir != m_instDir) {
|
if (newInstDir != m_instDir) {
|
||||||
@ -789,7 +852,7 @@ class InstanceStaging : public Task {
|
|||||||
, m_groupName(std::move(groupName))
|
, m_groupName(std::move(groupName))
|
||||||
{
|
{
|
||||||
m_child.reset(child);
|
m_child.reset(child);
|
||||||
connect(child, &Task::succeeded, this, &InstanceStaging::childSucceded);
|
connect(child, &Task::succeeded, this, &InstanceStaging::childSucceeded);
|
||||||
connect(child, &Task::failed, this, &InstanceStaging::childFailed);
|
connect(child, &Task::failed, this, &InstanceStaging::childFailed);
|
||||||
connect(child, &Task::aborted, this, &InstanceStaging::childAborted);
|
connect(child, &Task::aborted, this, &InstanceStaging::childAborted);
|
||||||
connect(child, &Task::abortStatusChanged, this, &InstanceStaging::setAbortable);
|
connect(child, &Task::abortStatusChanged, this, &InstanceStaging::setAbortable);
|
||||||
@ -797,7 +860,7 @@ class InstanceStaging : public Task {
|
|||||||
connect(child, &Task::details, this, &InstanceStaging::setDetails);
|
connect(child, &Task::details, this, &InstanceStaging::setDetails);
|
||||||
connect(child, &Task::progress, this, &InstanceStaging::setProgress);
|
connect(child, &Task::progress, this, &InstanceStaging::setProgress);
|
||||||
connect(child, &Task::stepProgress, this, &InstanceStaging::propagateStepProgress);
|
connect(child, &Task::stepProgress, this, &InstanceStaging::propagateStepProgress);
|
||||||
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded);
|
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceeded);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~InstanceStaging(){};
|
virtual ~InstanceStaging(){};
|
||||||
@ -819,7 +882,7 @@ class InstanceStaging : public Task {
|
|||||||
QStringList warnings() const override { return m_child->warnings(); }
|
QStringList warnings() const override { return m_child->warnings(); }
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void childSucceded()
|
void childSucceeded()
|
||||||
{
|
{
|
||||||
unsigned sleepTime = backoff();
|
unsigned sleepTime = backoff();
|
||||||
if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get())) {
|
if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get())) {
|
||||||
@ -917,7 +980,7 @@ bool InstanceList::commitStagedInstance(const QString& path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_instanceGroupIndex[instID] = groupName;
|
m_instanceGroupIndex[instID] = groupName;
|
||||||
m_groupNameCache.insert(groupName);
|
increaseGroupCount(groupName);
|
||||||
}
|
}
|
||||||
|
|
||||||
instanceSet.insert(instID);
|
instanceSet.insert(instID);
|
||||||
|
@ -1,4 +1,24 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* 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
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -86,9 +106,10 @@ class InstanceList : public QAbstractListModel {
|
|||||||
bool isGroupCollapsed(const QString& groupName);
|
bool isGroupCollapsed(const QString& groupName);
|
||||||
|
|
||||||
GroupId getInstanceGroup(const InstanceId& id) const;
|
GroupId getInstanceGroup(const InstanceId& id) const;
|
||||||
void setInstanceGroup(const InstanceId& id, const GroupId& name);
|
void setInstanceGroup(const InstanceId& id, GroupId name);
|
||||||
|
|
||||||
void deleteGroup(const GroupId& name);
|
void deleteGroup(const GroupId& name);
|
||||||
|
void renameGroup(const GroupId& src, const GroupId& dst);
|
||||||
bool trashInstance(const InstanceId& id);
|
bool trashInstance(const InstanceId& id);
|
||||||
bool trashedSomething();
|
bool trashedSomething();
|
||||||
void undoTrashInstance();
|
void undoTrashInstance();
|
||||||
@ -158,12 +179,16 @@ class InstanceList : public QAbstractListModel {
|
|||||||
QList<InstanceId> discoverInstances();
|
QList<InstanceId> discoverInstances();
|
||||||
InstancePtr loadInstance(const InstanceId& id);
|
InstancePtr loadInstance(const InstanceId& id);
|
||||||
|
|
||||||
|
void increaseGroupCount(const QString& group);
|
||||||
|
void decreaseGroupCount(const QString& group);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_watchLevel = 0;
|
int m_watchLevel = 0;
|
||||||
int totalPlayTime = 0;
|
int totalPlayTime = 0;
|
||||||
bool m_dirty = false;
|
bool m_dirty = false;
|
||||||
QList<InstancePtr> m_instances;
|
QList<InstancePtr> m_instances;
|
||||||
QSet<QString> m_groupNameCache;
|
// id -> refs
|
||||||
|
QMap<QString, int> m_groupNameCache;
|
||||||
|
|
||||||
SettingsObjectPtr m_globalSettings;
|
SettingsObjectPtr m_globalSettings;
|
||||||
QString m_instDir;
|
QString m_instDir;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -87,8 +88,8 @@ void LaunchController::decideAccount()
|
|||||||
if (accounts->count() <= 0) {
|
if (accounts->count() <= 0) {
|
||||||
// Tell the user they need to log in at least one account in order to play.
|
// Tell the user they need to log in at least one account in order to play.
|
||||||
auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"),
|
auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"),
|
||||||
tr("In order to play Minecraft, you must have at least one Microsoft or Mojang "
|
tr("In order to play Minecraft, you must have at least one Microsoft "
|
||||||
"account logged in. Mojang accounts can only be used offline. "
|
"account which owns Minecraft logged in."
|
||||||
"Would you like to open the account manager to add an account now?"),
|
"Would you like to open the account manager to add an account now?"),
|
||||||
QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)
|
QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)
|
||||||
->exec();
|
->exec();
|
||||||
@ -361,22 +362,21 @@ void LaunchController::readyForLaunch()
|
|||||||
QString error;
|
QString error;
|
||||||
if (!m_profiler->check(&error)) {
|
if (!m_profiler->check(&error)) {
|
||||||
m_launcher->abort();
|
m_launcher->abort();
|
||||||
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't start profiler: %1").arg(error));
|
|
||||||
emitFailed("Profiler startup failed!");
|
emitFailed("Profiler startup failed!");
|
||||||
|
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Profiler check for %1 failed: %2").arg(m_profiler->name(), error));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
BaseProfiler* profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this);
|
BaseProfiler* profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this);
|
||||||
|
|
||||||
connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString& message) {
|
connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString& message) {
|
||||||
QMessageBox msg;
|
QMessageBox msg(m_parentWidget);
|
||||||
msg.setText(tr("The game launch is delayed until you press the "
|
msg.setText(tr("The game launch is delayed until you press the "
|
||||||
"button. This is the right time to setup the profiler, as the "
|
"button. This is the right time to setup the profiler, as the "
|
||||||
"profiler server is running now.\n\n%1")
|
"profiler server is running now.\n\n%1")
|
||||||
.arg(message));
|
.arg(message));
|
||||||
msg.setWindowTitle(tr("Waiting."));
|
msg.setWindowTitle(tr("Waiting."));
|
||||||
msg.setIcon(QMessageBox::Information);
|
msg.setIcon(QMessageBox::Information);
|
||||||
msg.addButton(tr("Launch"), QMessageBox::AcceptRole);
|
msg.addButton(tr("&Launch"), QMessageBox::AcceptRole);
|
||||||
msg.setModal(true);
|
|
||||||
msg.exec();
|
msg.exec();
|
||||||
m_launcher->proceed();
|
m_launcher->proceed();
|
||||||
});
|
});
|
||||||
|
@ -65,13 +65,8 @@ QStringList LoggedProcess::reprocess(const QByteArray& data, QTextDecoder& decod
|
|||||||
m_leftover_line = "";
|
m_leftover_line = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed);
|
||||||
auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed, QString::SkipEmptyParts);
|
|
||||||
#else
|
|
||||||
auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed, Qt::SkipEmptyParts);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!str.endsWith(QChar::LineFeed))
|
|
||||||
m_leftover_line = lines.takeLast();
|
m_leftover_line = lines.takeLast();
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
@ -16,19 +16,20 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <MMCTime.h>
|
#include <MMCTime.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
|
|
||||||
QString Time::prettifyDuration(int64_t duration)
|
QString Time::prettifyDuration(int64_t duration, bool noDays)
|
||||||
{
|
{
|
||||||
int seconds = (int)(duration % 60);
|
int seconds = (int)(duration % 60);
|
||||||
duration /= 60;
|
duration /= 60;
|
||||||
int minutes = (int)(duration % 60);
|
int minutes = (int)(duration % 60);
|
||||||
duration /= 60;
|
duration /= 60;
|
||||||
int hours = (int)(duration % 24);
|
int hours = (int)(noDays ? duration : (duration % 24));
|
||||||
int days = (int)(duration / 24);
|
int days = (int)(noDays ? 0 : (duration / 24));
|
||||||
if ((hours == 0) && (days == 0)) {
|
if ((hours == 0) && (days == 0)) {
|
||||||
return QObject::tr("%1min %2s").arg(minutes).arg(seconds);
|
return QObject::tr("%1min %2s").arg(minutes).arg(seconds);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
namespace Time {
|
namespace Time {
|
||||||
|
|
||||||
QString prettifyDuration(int64_t duration);
|
QString prettifyDuration(int64_t duration, bool noDays = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns a string with short form time duration ie. `2days 1h3m4s56.0ms`.
|
* @brief Returns a string with short form time duration ie. `2days 1h3m4s56.0ms`.
|
||||||
|
@ -42,7 +42,11 @@
|
|||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
#if defined(LAUNCHER_APPLICATION)
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace MMCZip {
|
namespace MMCZip {
|
||||||
// ours
|
// ours
|
||||||
@ -132,6 +136,7 @@ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(LAUNCHER_APPLICATION)
|
||||||
// ours
|
// ours
|
||||||
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods)
|
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods)
|
||||||
{
|
{
|
||||||
@ -217,6 +222,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// ours
|
// ours
|
||||||
QString findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root)
|
QString findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root)
|
||||||
@ -422,6 +428,7 @@ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, Q
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(LAUNCHER_APPLICATION)
|
||||||
void ExportToZipTask::executeTask()
|
void ExportToZipTask::executeTask()
|
||||||
{
|
{
|
||||||
setStatus("Adding files...");
|
setStatus("Adding files...");
|
||||||
@ -500,5 +507,6 @@ bool ExportToZipTask::abort()
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace MMCZip
|
} // namespace MMCZip
|
@ -48,7 +48,10 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
#if defined(LAUNCHER_APPLICATION)
|
||||||
#include "minecraft/mod/Mod.h"
|
#include "minecraft/mod/Mod.h"
|
||||||
|
#endif
|
||||||
#include "tasks/Task.h"
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
namespace MMCZip {
|
namespace MMCZip {
|
||||||
@ -79,11 +82,12 @@ bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool follow
|
|||||||
*/
|
*/
|
||||||
bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false);
|
bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false);
|
||||||
|
|
||||||
|
#if defined(LAUNCHER_APPLICATION)
|
||||||
/**
|
/**
|
||||||
* take a source jar, add mods to it, resulting in target jar
|
* take a source jar, add mods to it, resulting in target jar
|
||||||
*/
|
*/
|
||||||
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods);
|
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods);
|
||||||
|
#endif
|
||||||
/**
|
/**
|
||||||
* Find a single file in archive by file name (not path)
|
* Find a single file in archive by file name (not path)
|
||||||
*
|
*
|
||||||
@ -147,6 +151,7 @@ bool extractFile(QString fileCompressed, QString file, QString dir);
|
|||||||
*/
|
*/
|
||||||
bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter);
|
bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter);
|
||||||
|
|
||||||
|
#if defined(LAUNCHER_APPLICATION)
|
||||||
class ExportToZipTask : public Task {
|
class ExportToZipTask : public Task {
|
||||||
public:
|
public:
|
||||||
ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false)
|
ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false)
|
||||||
@ -189,4 +194,5 @@ class ExportToZipTask : public Task {
|
|||||||
QFuture<ZipResult> m_build_zip_future;
|
QFuture<ZipResult> m_build_zip_future;
|
||||||
QFutureWatcher<ZipResult> m_build_zip_watcher;
|
QFutureWatcher<ZipResult> m_build_zip_watcher;
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
} // namespace MMCZip
|
} // namespace MMCZip
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
@ -26,6 +27,15 @@
|
|||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
#include "MangoHud.h"
|
#include "MangoHud.h"
|
||||||
|
|
||||||
|
#ifdef __GLIBC__
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#define UNDEF_GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace MangoHud {
|
namespace MangoHud {
|
||||||
|
|
||||||
QString getLibraryString()
|
QString getLibraryString()
|
||||||
@ -106,4 +116,37 @@ QString getLibraryString()
|
|||||||
|
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString findLibrary(QString libName)
|
||||||
|
{
|
||||||
|
#ifdef __GLIBC__
|
||||||
|
const char* library = libName.toLocal8Bit().constData();
|
||||||
|
|
||||||
|
void* handle = dlopen(library, RTLD_NOW);
|
||||||
|
if (!handle) {
|
||||||
|
qCritical() << "dlopen() failed:" << dlerror();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
char path[PATH_MAX];
|
||||||
|
if (dlinfo(handle, RTLD_DI_ORIGIN, path) == -1) {
|
||||||
|
qCritical() << "dlinfo() failed:" << dlerror();
|
||||||
|
dlclose(handle);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fullPath = FS::PathCombine(QString(path), libName);
|
||||||
|
|
||||||
|
dlclose(handle);
|
||||||
|
return fullPath;
|
||||||
|
#else
|
||||||
|
qWarning() << "MangoHud::findLibrary is not implemented on this platform";
|
||||||
|
return {};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
} // namespace MangoHud
|
} // namespace MangoHud
|
||||||
|
|
||||||
|
#ifdef UNDEF_GNU_SOURCE
|
||||||
|
#undef _GNU_SOURCE
|
||||||
|
#undef UNDEF_GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
@ -24,4 +24,6 @@
|
|||||||
namespace MangoHud {
|
namespace MangoHud {
|
||||||
|
|
||||||
QString getLibraryString();
|
QString getLibraryString();
|
||||||
}
|
|
||||||
|
QString findLibrary(QString libName);
|
||||||
|
} // namespace MangoHud
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -52,7 +53,7 @@ class NullInstance : public BaseInstance {
|
|||||||
QSet<QString> traits() const override { return {}; };
|
QSet<QString> traits() const override { return {}; };
|
||||||
QString instanceConfigFolder() const override { return instanceRoot(); };
|
QString instanceConfigFolder() const override { return instanceRoot(); };
|
||||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override { return nullptr; }
|
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override { return nullptr; }
|
||||||
shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override { return nullptr; }
|
shared_qobject_ptr<Task> createUpdateTask([[maybe_unused]] Net::Mode mode) override { return nullptr; }
|
||||||
QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); }
|
QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); }
|
||||||
QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); }
|
QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); }
|
||||||
QMap<QString, QString> getVariables() override { return QMap<QString, QString>(); }
|
QMap<QString, QString> getVariables() override { return QMap<QString, QString>(); }
|
||||||
@ -62,6 +63,7 @@ class NullInstance : public BaseInstance {
|
|||||||
bool canExport() const override { return false; }
|
bool canExport() const override { return false; }
|
||||||
bool canEdit() const override { return false; }
|
bool canEdit() const override { return false; }
|
||||||
bool canLaunch() const override { return false; }
|
bool canLaunch() const override { return false; }
|
||||||
|
void populateLaunchMenu(QMenu* menu) override {}
|
||||||
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override
|
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override
|
||||||
{
|
{
|
||||||
QStringList out;
|
QStringList out;
|
||||||
|
@ -12,7 +12,7 @@ struct PatchProblem {
|
|||||||
|
|
||||||
class ProblemProvider {
|
class ProblemProvider {
|
||||||
public:
|
public:
|
||||||
virtual ~ProblemProvider(){};
|
virtual ~ProblemProvider() {}
|
||||||
virtual const QList<PatchProblem> getProblems() const = 0;
|
virtual const QList<PatchProblem> getProblems() const = 0;
|
||||||
virtual ProblemSeverity getProblemSeverity() const = 0;
|
virtual ProblemSeverity getProblemSeverity() const = 0;
|
||||||
};
|
};
|
||||||
|
@ -90,7 +90,7 @@ void RecursiveFileSystemWatcher::fileChange(const QString& path)
|
|||||||
{
|
{
|
||||||
emit fileChanged(path);
|
emit fileChanged(path);
|
||||||
}
|
}
|
||||||
void RecursiveFileSystemWatcher::directoryChange(const QString& path)
|
void RecursiveFileSystemWatcher::directoryChange([[maybe_unused]] const QString& path)
|
||||||
{
|
{
|
||||||
setFiles(scanRecursive(m_root));
|
setFiles(scanRecursive(m_root));
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
#include "minecraft/mod/ModFolderModel.h"
|
#include "minecraft/mod/ModFolderModel.h"
|
||||||
#include "minecraft/mod/ResourceFolderModel.h"
|
#include "minecraft/mod/ResourceFolderModel.h"
|
||||||
|
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
|
|
||||||
ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
||||||
ModPlatform::IndexedVersion version,
|
ModPlatform::IndexedVersion version,
|
||||||
const std::shared_ptr<ResourceFolderModel> packs,
|
const std::shared_ptr<ResourceFolderModel> packs,
|
||||||
@ -51,7 +53,7 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_filesNetJob->addNetAction(Net::Download::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename())));
|
m_filesNetJob->addNetAction(Net::ApiDownload::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename())));
|
||||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded);
|
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded);
|
||||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged);
|
connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged);
|
||||||
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propagateStepProgress);
|
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propagateStepProgress);
|
||||||
|
@ -35,6 +35,11 @@ QPixmap getFaceFromCache(QString username, int height, int width)
|
|||||||
QPixmap skinTexture(fskin.fileName());
|
QPixmap skinTexture(fskin.fileName());
|
||||||
if (!skinTexture.isNull()) {
|
if (!skinTexture.isNull()) {
|
||||||
QPixmap skin = QPixmap(8, 8);
|
QPixmap skin = QPixmap(8, 8);
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||||
|
skin.fill(QColorConstants::Transparent);
|
||||||
|
#else
|
||||||
|
skin.fill(QColor(0, 0, 0, 0));
|
||||||
|
#endif
|
||||||
QPainter painter(&skin);
|
QPainter painter(&skin);
|
||||||
painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8));
|
painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8));
|
||||||
painter.drawPixmap(0, 0, skinTexture.copy(40, 8, 8, 8));
|
painter.drawPixmap(0, 0, skinTexture.copy(40, 8, 8, 8));
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
|
#include <qpair.h>
|
||||||
|
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
@ -149,7 +150,7 @@ QString StringUtils::truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((url_compact.length() >= max_len) && hard_limit) {
|
if ((url_compact.length() >= max_len) && hard_limit) {
|
||||||
// still too long, truncate normaly
|
// still too long, truncate normally
|
||||||
url_compact = QString(str_url);
|
url_compact = QString(str_url);
|
||||||
auto to_remove = url_compact.length() - max_len + 3;
|
auto to_remove = url_compact.length() - max_len + 3;
|
||||||
url_compact.remove(url_compact.length() - to_remove - 1, to_remove);
|
url_compact.remove(url_compact.length() - to_remove - 1, to_remove);
|
||||||
@ -182,3 +183,32 @@ QString StringUtils::getRandomAlphaNumeric()
|
|||||||
{
|
{
|
||||||
return QUuid::createUuid().toString(QUuid::Id128);
|
return QUuid::createUuid().toString(QUuid::Id128);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPair<QString, QString> StringUtils::splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs)
|
||||||
|
{
|
||||||
|
QString left, right;
|
||||||
|
auto index = s.indexOf(sep, 0, cs);
|
||||||
|
left = s.mid(0, index);
|
||||||
|
right = s.mid(index + sep.length());
|
||||||
|
return qMakePair(left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPair<QString, QString> StringUtils::splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs)
|
||||||
|
{
|
||||||
|
QString left, right;
|
||||||
|
auto index = s.indexOf(sep, 0, cs);
|
||||||
|
left = s.mid(0, index);
|
||||||
|
right = s.mid(left.length() + 1);
|
||||||
|
return qMakePair(left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPair<QString, QString> StringUtils::splitFirst(const QString& s, const QRegularExpression& re)
|
||||||
|
{
|
||||||
|
QString left, right;
|
||||||
|
QRegularExpressionMatch match;
|
||||||
|
auto index = s.indexOf(re, 0, &match);
|
||||||
|
left = s.mid(0, index);
|
||||||
|
auto end = match.hasMatch() ? left.length() + match.capturedLength() : left.length() + 1;
|
||||||
|
right = s.mid(end);
|
||||||
|
return qMakePair(left, right);
|
||||||
|
}
|
||||||
|
@ -36,8 +36,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QPair>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace StringUtils {
|
namespace StringUtils {
|
||||||
|
|
||||||
@ -70,12 +72,17 @@ int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs)
|
|||||||
/**
|
/**
|
||||||
* @brief Truncate a url while keeping its readability py placing the `...` in the middle of the path
|
* @brief Truncate a url while keeping its readability py placing the `...` in the middle of the path
|
||||||
* @param url Url to truncate
|
* @param url Url to truncate
|
||||||
* @param max_len max lenght of url in charaters
|
* @param max_len max length of url in characters
|
||||||
* @param hard_limit if truncating the path can't get the url short enough, truncate it normaly.
|
* @param hard_limit if truncating the path can't get the url short enough, truncate it normally.
|
||||||
*/
|
*/
|
||||||
QString truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_limit = false);
|
QString truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_limit = false);
|
||||||
|
|
||||||
QString humanReadableFileSize(double bytes, bool use_si = false, int decimal_points = 1);
|
QString humanReadableFileSize(double bytes, bool use_si = false, int decimal_points = 1);
|
||||||
|
|
||||||
QString getRandomAlphaNumeric();
|
QString getRandomAlphaNumeric();
|
||||||
|
|
||||||
|
QPair<QString, QString> splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs = Qt::CaseSensitive);
|
||||||
|
QPair<QString, QString> splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs = Qt::CaseSensitive);
|
||||||
|
QPair<QString, QString> splitFirst(const QString& s, const QRegularExpression& re);
|
||||||
|
|
||||||
} // namespace StringUtils
|
} // namespace StringUtils
|
||||||
|
@ -16,6 +16,8 @@ class Usable {
|
|||||||
friend class UseLock;
|
friend class UseLock;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
virtual ~Usable() {}
|
||||||
|
|
||||||
std::size_t useCount() const { return m_useCount; }
|
std::size_t useCount() const { return m_useCount; }
|
||||||
bool isInUse() const { return m_useCount > 0; }
|
bool isInUse() const { return m_useCount > 0; }
|
||||||
|
|
||||||
|
@ -56,6 +56,7 @@ class Version {
|
|||||||
bool operator!=(const Version& other) const;
|
bool operator!=(const Version& other) const;
|
||||||
|
|
||||||
QString toString() const { return m_string; }
|
QString toString() const { return m_string; }
|
||||||
|
bool isEmpty() const { return m_string.isEmpty(); }
|
||||||
|
|
||||||
friend QDebug operator<<(QDebug debug, const Version& v);
|
friend QDebug operator<<(QDebug debug, const Version& v);
|
||||||
|
|
||||||
@ -63,7 +64,7 @@ class Version {
|
|||||||
struct Section {
|
struct Section {
|
||||||
explicit Section(QString fullString) : m_fullString(std::move(fullString))
|
explicit Section(QString fullString) : m_fullString(std::move(fullString))
|
||||||
{
|
{
|
||||||
int cutoff = m_fullString.size();
|
qsizetype cutoff = m_fullString.size();
|
||||||
for (int i = 0; i < m_fullString.size(); i++) {
|
for (int i = 0; i < m_fullString.size(); i++) {
|
||||||
if (!m_fullString[i].isDigit()) {
|
if (!m_fullString[i].isDigit()) {
|
||||||
cutoff = i;
|
cutoff = i;
|
||||||
@ -103,14 +104,8 @@ class Version {
|
|||||||
|
|
||||||
QString m_fullString;
|
QString m_fullString;
|
||||||
|
|
||||||
[[nodiscard]] inline bool isAppendix() const
|
[[nodiscard]] inline bool isAppendix() const { return m_stringPart.startsWith('+'); }
|
||||||
{
|
[[nodiscard]] inline bool isPreRelease() const { return m_stringPart.startsWith('-') && m_stringPart.length() > 1; }
|
||||||
return m_stringPart.startsWith('+');
|
|
||||||
}
|
|
||||||
[[nodiscard]] inline bool isPreRelease() const
|
|
||||||
{
|
|
||||||
return m_stringPart.startsWith('-') && m_stringPart.length() > 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool operator==(const Section& other) const
|
inline bool operator==(const Section& other) const
|
||||||
{
|
{
|
||||||
@ -156,14 +151,8 @@ class Version {
|
|||||||
return m_fullString < other.m_fullString;
|
return m_fullString < other.m_fullString;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator!=(const Section& other) const
|
inline bool operator!=(const Section& other) const { return !(*this == other); }
|
||||||
{
|
inline bool operator>(const Section& other) const { return !(*this < other || *this == other); }
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
inline bool operator>(const Section& other) const
|
|
||||||
{
|
|
||||||
return !(*this < other || *this == other);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -194,12 +194,12 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
|
|||||||
switch (column) {
|
switch (column) {
|
||||||
case Name: {
|
case Name: {
|
||||||
if (hasRecommended) {
|
if (hasRecommended) {
|
||||||
auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
|
auto recommenced = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
|
||||||
if (value.toBool()) {
|
if (recommenced.toBool()) {
|
||||||
return APPLICATION->getThemedIcon("star");
|
return APPLICATION->getThemedIcon("star");
|
||||||
} else if (hasLatest) {
|
} else if (hasLatest) {
|
||||||
auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
|
auto latest = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
|
||||||
if (value.toBool()) {
|
if (latest.toBool()) {
|
||||||
return APPLICATION->getThemedIcon("bug");
|
return APPLICATION->getThemedIcon("bug");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -228,7 +228,7 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex VersionProxyModel::parent(const QModelIndex& child) const
|
QModelIndex VersionProxyModel::parent([[maybe_unused]] const QModelIndex& child) const
|
||||||
{
|
{
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
}
|
}
|
||||||
@ -408,7 +408,9 @@ void VersionProxyModel::sourceRowsAboutToBeInserted(const QModelIndex& parent, i
|
|||||||
beginInsertRows(parent, first, last);
|
beginInsertRows(parent, first, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VersionProxyModel::sourceRowsInserted(const QModelIndex& parent, int first, int last)
|
void VersionProxyModel::sourceRowsInserted([[maybe_unused]] const QModelIndex& parent,
|
||||||
|
[[maybe_unused]] int first,
|
||||||
|
[[maybe_unused]] int last)
|
||||||
{
|
{
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
}
|
}
|
||||||
@ -418,7 +420,7 @@ void VersionProxyModel::sourceRowsAboutToBeRemoved(const QModelIndex& parent, in
|
|||||||
beginRemoveRows(parent, first, last);
|
beginRemoveRows(parent, first, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VersionProxyModel::sourceRowsRemoved(const QModelIndex& parent, int first, int last)
|
void VersionProxyModel::sourceRowsRemoved([[maybe_unused]] const QModelIndex& parent, [[maybe_unused]] int first, [[maybe_unused]] int last)
|
||||||
{
|
{
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
}
|
}
|
||||||
|
128
launcher/WindowsConsole.cpp
Normal file
128
launcher/WindowsConsole.cpp
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 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/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <io.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
void RedirectHandle(DWORD handle, FILE* stream, const char* mode)
|
||||||
|
{
|
||||||
|
HANDLE stdHandle = GetStdHandle(handle);
|
||||||
|
if (stdHandle != INVALID_HANDLE_VALUE) {
|
||||||
|
int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT);
|
||||||
|
if (fileDescriptor != -1) {
|
||||||
|
FILE* file = _fdopen(fileDescriptor, mode);
|
||||||
|
if (file != NULL) {
|
||||||
|
int dup2Result = _dup2(_fileno(file), _fileno(stream));
|
||||||
|
if (dup2Result == 0) {
|
||||||
|
setvbuf(stream, NULL, _IONBF, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// taken from https://stackoverflow.com/a/25927081
|
||||||
|
// getting a proper output to console with redirection support on windows is apparently hell
|
||||||
|
void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr)
|
||||||
|
{
|
||||||
|
// Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been
|
||||||
|
// observed that the file number of our standard handle file objects can be assigned internally to a value of -2
|
||||||
|
// when not bound to a valid target, which represents some kind of unknown internal invalid state. In this state our
|
||||||
|
// call to "_dup2" fails, as it specifically tests to ensure that the target file number isn't equal to this value
|
||||||
|
// before allowing the operation to continue. We can resolve this issue by first "re-opening" the target files to
|
||||||
|
// use the "nul" device, which will place them into a valid state, after which we can redirect them to our target
|
||||||
|
// using the "_dup2" function.
|
||||||
|
if (bindStdIn) {
|
||||||
|
FILE* dummyFile;
|
||||||
|
freopen_s(&dummyFile, "nul", "r", stdin);
|
||||||
|
}
|
||||||
|
if (bindStdOut) {
|
||||||
|
FILE* dummyFile;
|
||||||
|
freopen_s(&dummyFile, "nul", "w", stdout);
|
||||||
|
}
|
||||||
|
if (bindStdErr) {
|
||||||
|
FILE* dummyFile;
|
||||||
|
freopen_s(&dummyFile, "nul", "w", stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect unbuffered stdin from the current standard input handle
|
||||||
|
if (bindStdIn) {
|
||||||
|
RedirectHandle(STD_INPUT_HANDLE, stdin, "r");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect unbuffered stdout to the current standard output handle
|
||||||
|
if (bindStdOut) {
|
||||||
|
RedirectHandle(STD_OUTPUT_HANDLE, stdout, "w");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect unbuffered stderr to the current standard error handle
|
||||||
|
if (bindStdErr) {
|
||||||
|
RedirectHandle(STD_ERROR_HANDLE, stderr, "w");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the error state for each of the C++ standard stream objects. We need to do this, as attempts to access the
|
||||||
|
// standard streams before they refer to a valid target will cause the iostream objects to enter an error state. In
|
||||||
|
// versions of Visual Studio after 2005, this seems to always occur during startup regardless of whether anything
|
||||||
|
// has been read from or written to the targets or not.
|
||||||
|
if (bindStdIn) {
|
||||||
|
std::wcin.clear();
|
||||||
|
std::cin.clear();
|
||||||
|
}
|
||||||
|
if (bindStdOut) {
|
||||||
|
std::wcout.clear();
|
||||||
|
std::cout.clear();
|
||||||
|
}
|
||||||
|
if (bindStdErr) {
|
||||||
|
std::wcerr.clear();
|
||||||
|
std::cerr.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AttachWindowsConsole()
|
||||||
|
{
|
||||||
|
auto stdinType = GetFileType(GetStdHandle(STD_INPUT_HANDLE));
|
||||||
|
auto stdoutType = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE));
|
||||||
|
auto stderrType = GetFileType(GetStdHandle(STD_ERROR_HANDLE));
|
||||||
|
|
||||||
|
bool bindStdIn = false;
|
||||||
|
bool bindStdOut = false;
|
||||||
|
bool bindStdErr = false;
|
||||||
|
|
||||||
|
if (stdinType == FILE_TYPE_CHAR || stdinType == FILE_TYPE_UNKNOWN) {
|
||||||
|
bindStdIn = true;
|
||||||
|
}
|
||||||
|
if (stdoutType == FILE_TYPE_CHAR || stdoutType == FILE_TYPE_UNKNOWN) {
|
||||||
|
bindStdOut = true;
|
||||||
|
}
|
||||||
|
if (stderrType == FILE_TYPE_CHAR || stderrType == FILE_TYPE_UNKNOWN) {
|
||||||
|
bindStdErr = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||||
|
BindCrtHandlesToStdHandles(bindStdIn, bindStdOut, bindStdErr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
@ -1,4 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
@ -20,11 +19,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "FileLink.h"
|
#pragma once
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr);
|
||||||
{
|
bool AttachWindowsConsole();
|
||||||
FileLinkApp ldh(argc, argv);
|
|
||||||
|
|
||||||
return ldh.exec();
|
|
||||||
}
|
|
@ -37,11 +37,7 @@
|
|||||||
#include <sys.h>
|
#include <sys.h>
|
||||||
|
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
#include "WindowsConsole.h"
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header
|
// Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header
|
||||||
@ -67,21 +63,7 @@ FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv),
|
|||||||
{
|
{
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
// attach the parent console
|
// attach the parent console
|
||||||
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
if (AttachWindowsConsole()) {
|
||||||
// if attach succeeds, reopen and sync all the i/o
|
|
||||||
if (freopen("CON", "w", stdout)) {
|
|
||||||
std::cout.sync_with_stdio();
|
|
||||||
}
|
|
||||||
if (freopen("CON", "w", stderr)) {
|
|
||||||
std::cerr.sync_with_stdio();
|
|
||||||
}
|
|
||||||
if (freopen("CON", "r", stdin)) {
|
|
||||||
std::cin.sync_with_stdio();
|
|
||||||
}
|
|
||||||
auto out = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
DWORD written;
|
|
||||||
const char* endline = "\n";
|
|
||||||
WriteConsole(out, endline, strlen(endline), &written, NULL);
|
|
||||||
consoleAttached = true;
|
consoleAttached = true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -111,6 +93,7 @@ FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv),
|
|||||||
joinServer(serverToJoin);
|
joinServer(serverToJoin);
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "no server to join";
|
qDebug() << "no server to join";
|
||||||
|
m_status = Failed;
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,6 +109,7 @@ void FileLinkApp::joinServer(QString server)
|
|||||||
connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs);
|
connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs);
|
||||||
|
|
||||||
connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError) {
|
connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError) {
|
||||||
|
m_status = Failed;
|
||||||
switch (socketError) {
|
switch (socketError) {
|
||||||
case QLocalSocket::ServerNotFoundError:
|
case QLocalSocket::ServerNotFoundError:
|
||||||
qDebug()
|
qDebug()
|
||||||
@ -150,6 +134,7 @@ void FileLinkApp::joinServer(QString server)
|
|||||||
|
|
||||||
connect(&socket, &QLocalSocket::disconnected, this, [&]() {
|
connect(&socket, &QLocalSocket::disconnected, this, [&]() {
|
||||||
qDebug() << "disconnected from server, should exit";
|
qDebug() << "disconnected from server, should exit";
|
||||||
|
m_status = Succeeded;
|
||||||
exit();
|
exit();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -188,7 +173,7 @@ void FileLinkApp::runLink()
|
|||||||
FS::LinkResult result = { src_path, dst_path, QString::fromStdString(os_err.message()), os_err.value() };
|
FS::LinkResult result = { src_path, dst_path, QString::fromStdString(os_err.message()), os_err.value() };
|
||||||
m_path_results.append(result);
|
m_path_results.append(result);
|
||||||
} else {
|
} else {
|
||||||
FS::LinkResult result = { src_path, dst_path };
|
FS::LinkResult result = { src_path, dst_path, "", 0 };
|
||||||
m_path_results.append(result);
|
m_path_results.append(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -248,7 +233,7 @@ void FileLinkApp::readPathPairs()
|
|||||||
in >> numLinks;
|
in >> numLinks;
|
||||||
qDebug() << "numLinks" << numLinks;
|
qDebug() << "numLinks" << numLinks;
|
||||||
|
|
||||||
for (int i = 0; i < numLinks; i++) {
|
for (quint32 i = 0; i < numLinks; i++) {
|
||||||
FS::LinkPair pair;
|
FS::LinkPair pair;
|
||||||
in >> pair.src;
|
in >> pair.src;
|
||||||
in >> pair.dst;
|
in >> pair.dst;
|
||||||
@ -271,7 +256,6 @@ FileLinkApp::~FileLinkApp()
|
|||||||
fclose(stdout);
|
fclose(stdout);
|
||||||
fclose(stdin);
|
fclose(stdin);
|
||||||
fclose(stderr);
|
fclose(stderr);
|
||||||
FreeConsole();
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -41,8 +41,10 @@ class FileLinkApp : public QCoreApplication {
|
|||||||
// friends for the purpose of limiting access to deprecated stuff
|
// friends for the purpose of limiting access to deprecated stuff
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
enum Status { Starting, Failed, Succeeded, Initialized };
|
||||||
FileLinkApp(int& argc, char** argv);
|
FileLinkApp(int& argc, char** argv);
|
||||||
virtual ~FileLinkApp();
|
virtual ~FileLinkApp();
|
||||||
|
Status status() const { return m_status; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void joinServer(QString server);
|
void joinServer(QString server);
|
||||||
@ -50,6 +52,8 @@ class FileLinkApp : public QCoreApplication {
|
|||||||
void runLink();
|
void runLink();
|
||||||
void sendResults();
|
void sendResults();
|
||||||
|
|
||||||
|
Status m_status = Status::Starting;
|
||||||
|
|
||||||
bool m_useHardLinks = false;
|
bool m_useHardLinks = false;
|
||||||
|
|
||||||
QDateTime m_startTime;
|
QDateTime m_startTime;
|
||||||
|
41
launcher/filelink/filelink_main.cpp
Normal file
41
launcher/filelink/filelink_main.cpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 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/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "FileLink.h"
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
FileLinkApp ldh(argc, argv);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -42,6 +43,7 @@
|
|||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include "icons/IconUtils.h"
|
||||||
|
|
||||||
#define MAX_SIZE 1024
|
#define MAX_SIZE 1024
|
||||||
|
|
||||||
@ -128,7 +130,7 @@ void IconList::directoryChanged(const QString& path)
|
|||||||
|
|
||||||
QString suffix = rmfile.suffix();
|
QString suffix = rmfile.suffix();
|
||||||
// The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well
|
// The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well
|
||||||
if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif")
|
if (!IconUtils::isIconSuffix(suffix))
|
||||||
key = rmfile.fileName();
|
key = rmfile.fileName();
|
||||||
|
|
||||||
int idx = getIconIndex(key);
|
int idx = getIconIndex(key);
|
||||||
@ -155,7 +157,7 @@ void IconList::directoryChanged(const QString& path)
|
|||||||
|
|
||||||
QString suffix = addfile.suffix();
|
QString suffix = addfile.suffix();
|
||||||
// The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well
|
// The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well
|
||||||
if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif")
|
if (!IconUtils::isIconSuffix(suffix))
|
||||||
key = addfile.fileName();
|
key = addfile.fileName();
|
||||||
|
|
||||||
if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased)) {
|
if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased)) {
|
||||||
@ -255,9 +257,6 @@ bool IconList::dropMimeData(const QMimeData* data,
|
|||||||
Qt::ItemFlags IconList::flags(const QModelIndex& index) const
|
Qt::ItemFlags IconList::flags(const QModelIndex& index) const
|
||||||
{
|
{
|
||||||
Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
|
Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
|
||||||
if (index.isValid())
|
|
||||||
return Qt::ItemIsDropEnabled | defaultFlags;
|
|
||||||
else
|
|
||||||
return Qt::ItemIsDropEnabled | defaultFlags;
|
return Qt::ItemIsDropEnabled | defaultFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,19 +289,8 @@ int IconList::rowCount(const QModelIndex& parent) const
|
|||||||
|
|
||||||
void IconList::installIcons(const QStringList& iconFiles)
|
void IconList::installIcons(const QStringList& iconFiles)
|
||||||
{
|
{
|
||||||
for (QString file : iconFiles) {
|
for (QString file : iconFiles)
|
||||||
QFileInfo fileinfo(file);
|
installIcon(file, {});
|
||||||
if (!fileinfo.isReadable() || !fileinfo.isFile())
|
|
||||||
continue;
|
|
||||||
QString target = FS::PathCombine(getDirectory(), fileinfo.fileName());
|
|
||||||
|
|
||||||
QString suffix = fileinfo.suffix();
|
|
||||||
if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!QFile::copy(file, target))
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IconList::installIcon(const QString& file, const QString& name)
|
void IconList::installIcon(const QString& file, const QString& name)
|
||||||
@ -311,18 +299,17 @@ void IconList::installIcon(const QString& file, const QString& name)
|
|||||||
if (!fileinfo.isReadable() || !fileinfo.isFile())
|
if (!fileinfo.isReadable() || !fileinfo.isFile())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QString target = FS::PathCombine(getDirectory(), name);
|
if (!IconUtils::isIconSuffix(fileinfo.suffix()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString target = FS::PathCombine(getDirectory(), name.isEmpty() ? fileinfo.fileName() : name);
|
||||||
QFile::copy(file, target);
|
QFile::copy(file, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IconList::iconFileExists(const QString& key) const
|
bool IconList::iconFileExists(const QString& key) const
|
||||||
{
|
{
|
||||||
auto iconEntry = icon(key);
|
auto iconEntry = icon(key);
|
||||||
if (!iconEntry) {
|
return iconEntry && iconEntry->has(IconType::FileBased);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return iconEntry->has(IconType::FileBased);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const MMCIcon* IconList::icon(const QString& key) const
|
const MMCIcon* IconList::icon(const QString& key) const
|
||||||
@ -335,18 +322,12 @@ const MMCIcon* IconList::icon(const QString& key) const
|
|||||||
|
|
||||||
bool IconList::deleteIcon(const QString& key)
|
bool IconList::deleteIcon(const QString& key)
|
||||||
{
|
{
|
||||||
if (!iconFileExists(key))
|
return iconFileExists(key) && QFile::remove(icon(key)->getFilePath());
|
||||||
return false;
|
|
||||||
|
|
||||||
return QFile::remove(icon(key)->getFilePath());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IconList::trashIcon(const QString& key)
|
bool IconList::trashIcon(const QString& key)
|
||||||
{
|
{
|
||||||
if (!iconFileExists(key))
|
return iconFileExists(key) && FS::trash(icon(key)->getFilePath(), nullptr);
|
||||||
return false;
|
|
||||||
|
|
||||||
return FS::trash(icon(key)->getFilePath(), nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IconList::addThemeIcon(const QString& key)
|
bool IconList::addThemeIcon(const QString& key)
|
||||||
@ -357,7 +338,7 @@ bool IconList::addThemeIcon(const QString& key)
|
|||||||
oldOne.replace(Builtin, key);
|
oldOne.replace(Builtin, key);
|
||||||
dataChanged(index(*iter), index(*iter));
|
dataChanged(index(*iter), index(*iter));
|
||||||
return true;
|
return true;
|
||||||
} else {
|
}
|
||||||
// add a new icon
|
// add a new icon
|
||||||
beginInsertRows(QModelIndex(), icons.size(), icons.size());
|
beginInsertRows(QModelIndex(), icons.size(), icons.size());
|
||||||
{
|
{
|
||||||
@ -371,7 +352,6 @@ bool IconList::addThemeIcon(const QString& key)
|
|||||||
endInsertRows();
|
endInsertRows();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool IconList::addIcon(const QString& key, const QString& name, const QString& path, const IconType type)
|
bool IconList::addIcon(const QString& key, const QString& name, const QString& path, const IconType type)
|
||||||
{
|
{
|
||||||
@ -385,7 +365,7 @@ bool IconList::addIcon(const QString& key, const QString& name, const QString& p
|
|||||||
oldOne.replace(type, icon, path);
|
oldOne.replace(type, icon, path);
|
||||||
dataChanged(index(*iter), index(*iter));
|
dataChanged(index(*iter), index(*iter));
|
||||||
return true;
|
return true;
|
||||||
} else {
|
}
|
||||||
// add a new icon
|
// add a new icon
|
||||||
beginInsertRows(QModelIndex(), icons.size(), icons.size());
|
beginInsertRows(QModelIndex(), icons.size(), icons.size());
|
||||||
{
|
{
|
||||||
@ -399,7 +379,6 @@ bool IconList::addIcon(const QString& key, const QString& name, const QString& p
|
|||||||
endInsertRows();
|
endInsertRows();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void IconList::saveIcon(const QString& key, const QString& path, const char* format) const
|
void IconList::saveIcon(const QString& key, const QString& path, const char* format) const
|
||||||
{
|
{
|
||||||
@ -446,5 +425,3 @@ QString IconList::getDirectory() const
|
|||||||
{
|
{
|
||||||
return m_dir.absolutePath();
|
return m_dir.absolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
//#include "IconList.moc"
|
|
||||||
|
@ -1,4 +1,24 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -12,7 +32,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
|
@ -1,19 +1,51 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "IconUtils.h"
|
#include "IconUtils.h"
|
||||||
|
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::array<const char*, 6> validIconExtensions = { { "svg", "png", "ico", "gif", "jpg", "jpeg" } };
|
static const QStringList validIconExtensions = { { "svg", "png", "ico", "gif", "jpg", "jpeg" } };
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace IconUtils {
|
namespace IconUtils {
|
||||||
|
|
||||||
QString findBestIconIn(const QString& folder, const QString& iconKey)
|
QString findBestIconIn(const QString& folder, const QString& iconKey)
|
||||||
{
|
{
|
||||||
int best_found = validIconExtensions.size();
|
|
||||||
QString best_filename;
|
QString best_filename;
|
||||||
|
|
||||||
QDirIterator it(folder, QDir::NoDotAndDotDot | QDir::Files, QDirIterator::NoIteratorFlags);
|
QDirIterator it(folder, QDir::NoDotAndDotDot | QDir::Files, QDirIterator::NoIteratorFlags);
|
||||||
@ -21,36 +53,20 @@ QString findBestIconIn(const QString& folder, const QString& iconKey)
|
|||||||
it.next();
|
it.next();
|
||||||
auto fileInfo = it.fileInfo();
|
auto fileInfo = it.fileInfo();
|
||||||
|
|
||||||
if (fileInfo.completeBaseName() != iconKey)
|
if (fileInfo.completeBaseName() == iconKey && isIconSuffix(fileInfo.suffix()))
|
||||||
continue;
|
return fileInfo.absoluteFilePath();
|
||||||
|
|
||||||
auto extension = fileInfo.suffix();
|
|
||||||
|
|
||||||
for (int i = 0; i < best_found; i++) {
|
|
||||||
if (extension == validIconExtensions[i]) {
|
|
||||||
best_found = i;
|
|
||||||
qDebug() << i << " : " << fileInfo.fileName();
|
|
||||||
best_filename = fileInfo.fileName();
|
|
||||||
}
|
}
|
||||||
}
|
return {};
|
||||||
}
|
|
||||||
return FS::PathCombine(folder, best_filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString getIconFilter()
|
QString getIconFilter()
|
||||||
{
|
{
|
||||||
QString out;
|
return "(*." + validIconExtensions.join(" *.") + ")";
|
||||||
QTextStream stream(&out);
|
|
||||||
stream << '(';
|
|
||||||
for (size_t i = 0; i < validIconExtensions.size() - 1; i++) {
|
|
||||||
if (i > 0) {
|
|
||||||
stream << " ";
|
|
||||||
}
|
}
|
||||||
stream << "*." << validIconExtensions[i];
|
|
||||||
}
|
bool isIconSuffix(QString suffix)
|
||||||
stream << " *." << validIconExtensions[validIconExtensions.size() - 1];
|
{
|
||||||
stream << ')';
|
return validIconExtensions.contains(suffix);
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace IconUtils
|
} // namespace IconUtils
|
||||||
|
@ -1,3 +1,38 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
@ -10,4 +45,5 @@ QString findBestIconIn(const QString& folder, const QString& iconKey);
|
|||||||
// Get icon file type filter for file browser dialogs
|
// Get icon file type filter for file browser dialogs
|
||||||
QString getIconFilter();
|
QString getIconFilter();
|
||||||
|
|
||||||
|
bool isIconSuffix(QString suffix);
|
||||||
} // namespace IconUtils
|
} // namespace IconUtils
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -50,8 +51,8 @@ IconType operator--(IconType& t, int)
|
|||||||
case IconType::FileBased:
|
case IconType::FileBased:
|
||||||
t = IconType::Transient;
|
t = IconType::Transient;
|
||||||
break;
|
break;
|
||||||
default: {
|
default:
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,24 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -12,7 +32,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
@ -24,11 +24,11 @@
|
|||||||
struct JavaInstall : public BaseVersion {
|
struct JavaInstall : public BaseVersion {
|
||||||
JavaInstall() {}
|
JavaInstall() {}
|
||||||
JavaInstall(QString id, QString arch, QString path) : id(id), arch(arch), path(path) {}
|
JavaInstall(QString id, QString arch, QString path) : id(id), arch(arch), path(path) {}
|
||||||
virtual QString descriptor() { return id.toString(); }
|
virtual QString descriptor() override { return id.toString(); }
|
||||||
|
|
||||||
virtual QString name() { return id.toString(); }
|
virtual QString name() override { return id.toString(); }
|
||||||
|
|
||||||
virtual QString typeString() const { return arch; }
|
virtual QString typeString() const override { return arch; }
|
||||||
|
|
||||||
virtual bool operator<(BaseVersion& a) override;
|
virtual bool operator<(BaseVersion& a) override;
|
||||||
virtual bool operator>(BaseVersion& a) override;
|
virtual bool operator>(BaseVersion& a) override;
|
||||||
|
@ -403,6 +403,14 @@ QList<QString> JavaUtils::FindJavaPaths()
|
|||||||
scanJavaDirs("/opt/jdks");
|
scanJavaDirs("/opt/jdks");
|
||||||
// flatpak
|
// flatpak
|
||||||
scanJavaDirs("/app/jdk");
|
scanJavaDirs("/app/jdk");
|
||||||
|
|
||||||
|
auto home = qEnvironmentVariable("HOME");
|
||||||
|
|
||||||
|
// javas downloaded by IntelliJ
|
||||||
|
scanJavaDirs(FS::PathCombine(home, ".jdks"));
|
||||||
|
// javas downloaded by sdkman
|
||||||
|
scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java"));
|
||||||
|
|
||||||
javas = addJavasFromEnv(javas);
|
javas = addJavasFromEnv(javas);
|
||||||
javas.removeDuplicates();
|
javas.removeDuplicates();
|
||||||
return javas;
|
return javas;
|
||||||
|
@ -45,10 +45,12 @@ QString JavaVersion::toString() const
|
|||||||
|
|
||||||
bool JavaVersion::requiresPermGen()
|
bool JavaVersion::requiresPermGen()
|
||||||
{
|
{
|
||||||
if (m_parseable) {
|
return !m_parseable || m_major < 8;
|
||||||
return m_major < 8;
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
|
bool JavaVersion::isModular()
|
||||||
|
{
|
||||||
|
return m_parseable && m_major >= 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JavaVersion::operator<(const JavaVersion& rhs)
|
bool JavaVersion::operator<(const JavaVersion& rhs)
|
||||||
|
@ -14,7 +14,7 @@ class JavaVersion {
|
|||||||
friend class JavaVersionTest;
|
friend class JavaVersionTest;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
JavaVersion(){};
|
JavaVersion() {}
|
||||||
JavaVersion(const QString& rhs);
|
JavaVersion(const QString& rhs);
|
||||||
|
|
||||||
JavaVersion& operator=(const QString& rhs);
|
JavaVersion& operator=(const QString& rhs);
|
||||||
@ -25,6 +25,8 @@ class JavaVersion {
|
|||||||
|
|
||||||
bool requiresPermGen();
|
bool requiresPermGen();
|
||||||
|
|
||||||
|
bool isModular();
|
||||||
|
|
||||||
QString toString() const;
|
QString toString() const;
|
||||||
|
|
||||||
int major() { return m_major; }
|
int major() { return m_major; }
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
#include "BaseEntity.h"
|
#include "BaseEntity.h"
|
||||||
|
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
#include "net/Download.h"
|
#include "net/ApiDownload.h"
|
||||||
#include "net/HttpMetaCache.h"
|
#include "net/HttpMetaCache.h"
|
||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ class ParsingValidator : public Net::Validator {
|
|||||||
bool init(QNetworkRequest&) override { return true; }
|
bool init(QNetworkRequest&) override { return true; }
|
||||||
bool write(QByteArray& data) override
|
bool write(QByteArray& data) override
|
||||||
{
|
{
|
||||||
this->data.append(data);
|
this->m_data.append(data);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool abort() override { return true; }
|
bool abort() override { return true; }
|
||||||
@ -40,7 +40,7 @@ class ParsingValidator : public Net::Validator {
|
|||||||
{
|
{
|
||||||
auto fname = m_entity->localFilename();
|
auto fname = m_entity->localFilename();
|
||||||
try {
|
try {
|
||||||
auto doc = Json::requireDocument(data, fname);
|
auto doc = Json::requireDocument(m_data, fname);
|
||||||
auto obj = Json::requireObject(doc, fname);
|
auto obj = Json::requireObject(doc, fname);
|
||||||
m_entity->parse(obj);
|
m_entity->parse(obj);
|
||||||
return true;
|
return true;
|
||||||
@ -51,7 +51,7 @@ class ParsingValidator : public Net::Validator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
QByteArray data;
|
QByteArray m_data;
|
||||||
Meta::BaseEntity* m_entity;
|
Meta::BaseEntity* m_entity;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ void Meta::BaseEntity::load(Net::Mode loadType)
|
|||||||
auto url = this->url();
|
auto url = this->url();
|
||||||
auto entry = APPLICATION->metacache()->resolveEntry("meta", localFilename());
|
auto entry = APPLICATION->metacache()->resolveEntry("meta", localFilename());
|
||||||
entry->setStale(true);
|
entry->setStale(true);
|
||||||
auto dl = Net::Download::makeCached(url, entry);
|
auto dl = Net::ApiDownload::makeCached(url, entry);
|
||||||
/*
|
/*
|
||||||
* The validator parses the file and loads it into the object.
|
* The validator parses the file and loads it into the object.
|
||||||
* If that fails, the file is not written to storage.
|
* If that fails, the file is not written to storage.
|
||||||
|
@ -46,8 +46,8 @@
|
|||||||
#include "AssetsUtils.h"
|
#include "AssetsUtils.h"
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
#include "net/ChecksumValidator.h"
|
#include "net/ChecksumValidator.h"
|
||||||
#include "net/Download.h"
|
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
@ -279,7 +279,7 @@ NetAction::Ptr AssetObject::getDownloadAction()
|
|||||||
{
|
{
|
||||||
QFileInfo objectFile(getLocalPath());
|
QFileInfo objectFile(getLocalPath());
|
||||||
if ((!objectFile.isFile()) || (objectFile.size() != size)) {
|
if ((!objectFile.isFile()) || (objectFile.size() != size)) {
|
||||||
auto objectDL = Net::Download::makeFile(getUrl(), objectFile.filePath());
|
auto objectDL = Net::ApiDownload::makeFile(getUrl(), objectFile.filePath());
|
||||||
if (hash.size()) {
|
if (hash.size()) {
|
||||||
auto rawHash = QByteArray::fromHex(hash.toLatin1());
|
auto rawHash = QByteArray::fromHex(hash.toLatin1());
|
||||||
objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash));
|
objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash));
|
||||||
|
@ -25,7 +25,8 @@ class Component : public QObject, public ProblemProvider {
|
|||||||
Component(PackProfile* parent, std::shared_ptr<Meta::Version> version);
|
Component(PackProfile* parent, std::shared_ptr<Meta::Version> version);
|
||||||
Component(PackProfile* parent, const QString& uid, std::shared_ptr<VersionFile> file);
|
Component(PackProfile* parent, const QString& uid, std::shared_ptr<VersionFile> file);
|
||||||
|
|
||||||
virtual ~Component(){};
|
virtual ~Component() {}
|
||||||
|
|
||||||
void applyTo(LaunchProfile* profile);
|
void applyTo(LaunchProfile* profile);
|
||||||
|
|
||||||
bool isEnabled();
|
bool isEnabled();
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
#include "Component.h"
|
#include "Component.h"
|
||||||
#include "ComponentUpdateTask_p.h"
|
#include "ComponentUpdateTask_p.h"
|
||||||
#include "OneSixVersionFormat.h"
|
|
||||||
#include "PackProfile.h"
|
#include "PackProfile.h"
|
||||||
#include "PackProfile_p.h"
|
#include "PackProfile_p.h"
|
||||||
#include "Version.h"
|
#include "Version.h"
|
||||||
#include "cassert"
|
#include "cassert"
|
||||||
#include "meta/Index.h"
|
#include "meta/Index.h"
|
||||||
#include "meta/Version.h"
|
#include "meta/Version.h"
|
||||||
#include "meta/VersionList.h"
|
#include "minecraft/OneSixVersionFormat.h"
|
||||||
|
#include "minecraft/ProfileUtils.h"
|
||||||
#include "net/Mode.h"
|
#include "net/Mode.h"
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
|
|
||||||
class LaunchProfile : public ProblemProvider {
|
class LaunchProfile : public ProblemProvider {
|
||||||
public:
|
public:
|
||||||
virtual ~LaunchProfile(){};
|
virtual ~LaunchProfile() {}
|
||||||
|
|
||||||
public: /* application of profile variables from patches */
|
public: /* application of profile variables from patches */
|
||||||
void applyMinecraftVersion(const QString& id);
|
void applyMinecraftVersion(const QString& id);
|
||||||
|
@ -38,8 +38,8 @@
|
|||||||
|
|
||||||
#include <BuildConfig.h>
|
#include <BuildConfig.h>
|
||||||
#include <FileSystem.h>
|
#include <FileSystem.h>
|
||||||
|
#include <net/ApiDownload.h>
|
||||||
#include <net/ChecksumValidator.h>
|
#include <net/ChecksumValidator.h>
|
||||||
#include <net/Download.h>
|
|
||||||
|
|
||||||
void Library::getApplicableFiles(const RuntimeContext& runtimeContext,
|
void Library::getApplicableFiles(const RuntimeContext& runtimeContext,
|
||||||
QStringList& jar,
|
QStringList& jar,
|
||||||
@ -115,12 +115,12 @@ QList<NetAction::Ptr> Library::getDownloads(const RuntimeContext& runtimeContext
|
|||||||
|
|
||||||
if (sha1.size()) {
|
if (sha1.size()) {
|
||||||
auto rawSha1 = QByteArray::fromHex(sha1.toLatin1());
|
auto rawSha1 = QByteArray::fromHex(sha1.toLatin1());
|
||||||
auto dl = Net::Download::makeCached(url, entry, options);
|
auto dl = Net::ApiDownload::makeCached(url, entry, options);
|
||||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
|
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
|
||||||
qDebug() << "Checksummed Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url;
|
qDebug() << "Checksummed Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url;
|
||||||
out.append(dl);
|
out.append(dl);
|
||||||
} else {
|
} else {
|
||||||
out.append(Net::Download::makeCached(url, entry, options));
|
out.append(Net::ApiDownload::makeCached(url, entry, options));
|
||||||
qDebug() << "Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url;
|
qDebug() << "Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -3,8 +3,7 @@
|
|||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (C) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
* Copyright (C) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
* Copyright (c) 2023 seth <getchoo at tuta dot io>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -88,6 +87,10 @@
|
|||||||
#include "minecraft/gameoptions/GameOptions.h"
|
#include "minecraft/gameoptions/GameOptions.h"
|
||||||
#include "minecraft/update/FoldersTask.h"
|
#include "minecraft/update/FoldersTask.h"
|
||||||
|
|
||||||
|
#include "tools/BaseProfiler.h"
|
||||||
|
|
||||||
|
#include <QActionGroup>
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
#include "MangoHud.h"
|
#include "MangoHud.h"
|
||||||
#endif
|
#endif
|
||||||
@ -166,7 +169,9 @@ void MinecraftInstance::loadSpecificSettings()
|
|||||||
// Native library workarounds
|
// Native library workarounds
|
||||||
auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false);
|
auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false);
|
||||||
m_settings->registerOverride(global_settings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride);
|
m_settings->registerOverride(global_settings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride);
|
||||||
|
m_settings->registerOverride(global_settings->getSetting("CustomOpenALPath"), nativeLibraryWorkaroundsOverride);
|
||||||
m_settings->registerOverride(global_settings->getSetting("UseNativeGLFW"), nativeLibraryWorkaroundsOverride);
|
m_settings->registerOverride(global_settings->getSetting("UseNativeGLFW"), nativeLibraryWorkaroundsOverride);
|
||||||
|
m_settings->registerOverride(global_settings->getSetting("CustomGLFWPath"), nativeLibraryWorkaroundsOverride);
|
||||||
|
|
||||||
// Peformance related options
|
// Peformance related options
|
||||||
auto performanceOverride = m_settings->registerSetting("OverridePerformance", false);
|
auto performanceOverride = m_settings->registerSetting("OverridePerformance", false);
|
||||||
@ -179,9 +184,9 @@ void MinecraftInstance::loadSpecificSettings()
|
|||||||
m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride);
|
m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride);
|
||||||
m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride);
|
m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride);
|
||||||
|
|
||||||
// Mod loader specific options
|
// Legacy-related options
|
||||||
auto modLoaderSettings = m_settings->registerSetting("OverrideModLoaderSettings", false);
|
auto legacySettings = m_settings->registerSetting("OverrideLegacySettings", false);
|
||||||
m_settings->registerOverride(global_settings->getSetting("DisableQuiltBeacon"), modLoaderSettings);
|
m_settings->registerOverride(global_settings->getSetting("OnlineFixes"), legacySettings);
|
||||||
|
|
||||||
m_settings->set("InstanceType", "OneSix");
|
m_settings->set("InstanceType", "OneSix");
|
||||||
}
|
}
|
||||||
@ -194,6 +199,12 @@ void MinecraftInstance::loadSpecificSettings()
|
|||||||
m_settings->registerSetting("UseAccountForInstance", false);
|
m_settings->registerSetting("UseAccountForInstance", false);
|
||||||
m_settings->registerSetting("InstanceAccountId", "");
|
m_settings->registerSetting("InstanceAccountId", "");
|
||||||
|
|
||||||
|
m_settings->registerSetting("ExportName", "");
|
||||||
|
m_settings->registerSetting("ExportVersion", "1.0.0");
|
||||||
|
m_settings->registerSetting("ExportSummary", "");
|
||||||
|
m_settings->registerSetting("ExportAuthor", "");
|
||||||
|
m_settings->registerSetting("ExportOptionalFiles", true);
|
||||||
|
|
||||||
qDebug() << "Instance-type specific settings were loaded!";
|
qDebug() << "Instance-type specific settings were loaded!";
|
||||||
|
|
||||||
setSpecificSettingsLoaded(true);
|
setSpecificSettingsLoaded(true);
|
||||||
@ -229,6 +240,50 @@ QSet<QString> MinecraftInstance::traits() const
|
|||||||
return profile->getTraits();
|
return profile->getTraits();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: move UI code out of MinecraftInstance
|
||||||
|
void MinecraftInstance::populateLaunchMenu(QMenu* menu)
|
||||||
|
{
|
||||||
|
QAction* normalLaunch = menu->addAction(tr("&Launch"));
|
||||||
|
normalLaunch->setShortcut(QKeySequence::Open);
|
||||||
|
QAction* normalLaunchOffline = menu->addAction(tr("Launch &Offline"));
|
||||||
|
normalLaunchOffline->setShortcut(QKeySequence(tr("Ctrl+Shift+O")));
|
||||||
|
QAction* normalLaunchDemo = menu->addAction(tr("Launch &Demo"));
|
||||||
|
normalLaunchDemo->setShortcut(QKeySequence(tr("Ctrl+Alt+O")));
|
||||||
|
|
||||||
|
normalLaunchDemo->setEnabled(supportsDemo());
|
||||||
|
|
||||||
|
connect(normalLaunch, &QAction::triggered, [this] { APPLICATION->launch(shared_from_this()); });
|
||||||
|
connect(normalLaunchOffline, &QAction::triggered, [this] { APPLICATION->launch(shared_from_this(), false, false); });
|
||||||
|
connect(normalLaunchDemo, &QAction::triggered, [this] { APPLICATION->launch(shared_from_this(), false, true); });
|
||||||
|
|
||||||
|
QString profilersTitle = tr("Profilers");
|
||||||
|
menu->addSeparator()->setText(profilersTitle);
|
||||||
|
|
||||||
|
auto profilers = new QActionGroup(menu);
|
||||||
|
profilers->setExclusive(true);
|
||||||
|
connect(profilers, &QActionGroup::triggered, [this](QAction* action) {
|
||||||
|
settings()->set("Profiler", action->data());
|
||||||
|
emit profilerChanged();
|
||||||
|
});
|
||||||
|
|
||||||
|
QAction* noProfilerAction = menu->addAction(tr("&No Profiler"));
|
||||||
|
noProfilerAction->setData("");
|
||||||
|
noProfilerAction->setCheckable(true);
|
||||||
|
noProfilerAction->setChecked(true);
|
||||||
|
profilers->addAction(noProfilerAction);
|
||||||
|
|
||||||
|
for (auto profiler = APPLICATION->profilers().begin(); profiler != APPLICATION->profilers().end(); profiler++) {
|
||||||
|
QAction* profilerAction = menu->addAction(profiler.value()->name());
|
||||||
|
profilers->addAction(profilerAction);
|
||||||
|
profilerAction->setData(profiler.key());
|
||||||
|
profilerAction->setCheckable(true);
|
||||||
|
profilerAction->setChecked(settings()->get("Profiler").toString() == profiler.key());
|
||||||
|
|
||||||
|
QString error;
|
||||||
|
profilerAction->setEnabled(profiler.value()->check(&error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QString MinecraftInstance::gameRoot() const
|
QString MinecraftInstance::gameRoot() const
|
||||||
{
|
{
|
||||||
QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
|
QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
|
||||||
@ -260,7 +315,7 @@ QString MinecraftInstance::getLocalLibraryPath() const
|
|||||||
bool MinecraftInstance::supportsDemo() const
|
bool MinecraftInstance::supportsDemo() const
|
||||||
{
|
{
|
||||||
Version instance_ver{ getPackProfile()->getComponentVersion("net.minecraft") };
|
Version instance_ver{ getPackProfile()->getComponentVersion("net.minecraft") };
|
||||||
// Demo mode was introduced in 1.3.1: https://minecraft.fandom.com/wiki/Demo_mode#History
|
// Demo mode was introduced in 1.3.1: https://minecraft.wiki/w/Demo_mode#History
|
||||||
// FIXME: Due to Version constraints atm, this can't handle well non-release versions
|
// FIXME: Due to Version constraints atm, this can't handle well non-release versions
|
||||||
return instance_ver >= Version("1.3.1");
|
return instance_ver >= Version("1.3.1");
|
||||||
}
|
}
|
||||||
@ -385,10 +440,31 @@ QStringList MinecraftInstance::extraArguments()
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const auto loaders = version->getModLoaders();
|
QString openALPath;
|
||||||
if (loaders.has_value() && loaders.value() & ResourceAPI::Quilt && settings()->get("DisableQuiltBeacon").toBool())
|
QString glfwPath;
|
||||||
list.append("-Dloader.disable_beacon=true");
|
|
||||||
|
if (settings()->get("UseNativeOpenAL").toBool()) {
|
||||||
|
openALPath = APPLICATION->m_detectedOpenALPath;
|
||||||
|
auto customPath = settings()->get("CustomOpenALPath").toString();
|
||||||
|
if (!customPath.isEmpty())
|
||||||
|
openALPath = customPath;
|
||||||
}
|
}
|
||||||
|
if (settings()->get("UseNativeGLFW").toBool()) {
|
||||||
|
glfwPath = APPLICATION->m_detectedGLFWPath;
|
||||||
|
auto customPath = settings()->get("CustomGLFWPath").toString();
|
||||||
|
if (!customPath.isEmpty())
|
||||||
|
glfwPath = customPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo openALInfo(openALPath);
|
||||||
|
QFileInfo glfwInfo(glfwPath);
|
||||||
|
|
||||||
|
if (!openALPath.isEmpty() && openALInfo.exists())
|
||||||
|
list.append("-Dorg.lwjgl.openal.libname=" + openALInfo.absoluteFilePath());
|
||||||
|
if (!glfwPath.isEmpty() && glfwInfo.exists())
|
||||||
|
list.append("-Dorg.lwjgl.glfw.libname=" + glfwInfo.absoluteFilePath());
|
||||||
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,20 +517,28 @@ QStringList MinecraftInstance::javaArguments()
|
|||||||
|
|
||||||
args << "-Duser.language=en";
|
args << "-Duser.language=en";
|
||||||
|
|
||||||
|
if (javaVersion.isModular() && shouldApplyOnlineFixes())
|
||||||
|
// allow reflective access to java.net - required by the skin fix
|
||||||
|
args << "--add-opens"
|
||||||
|
<< "java.base/java.net=ALL-UNNAMED";
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString MinecraftInstance::getLauncher()
|
QString MinecraftInstance::getLauncher()
|
||||||
{
|
{
|
||||||
auto profile = m_components->getProfile();
|
|
||||||
|
|
||||||
// use legacy launcher if the traits are set
|
// use legacy launcher if the traits are set
|
||||||
if (profile->getTraits().contains("legacyLaunch") || profile->getTraits().contains("alphaLaunch"))
|
if (traits().contains("legacyLaunch") || traits().contains("alphaLaunch"))
|
||||||
return "legacy";
|
return "legacy";
|
||||||
|
|
||||||
return "standard";
|
return "standard";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MinecraftInstance::shouldApplyOnlineFixes()
|
||||||
|
{
|
||||||
|
return traits().contains("legacyServices") && settings()->get("OnlineFixes").toBool();
|
||||||
|
}
|
||||||
|
|
||||||
QMap<QString, QString> MinecraftInstance::getVariables()
|
QMap<QString, QString> MinecraftInstance::getVariables()
|
||||||
{
|
{
|
||||||
QMap<QString, QString> out;
|
QMap<QString, QString> out;
|
||||||
@ -651,6 +735,9 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS
|
|||||||
launchScript += "traits " + trait + "\n";
|
launchScript += "traits " + trait + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldApplyOnlineFixes())
|
||||||
|
launchScript += "onlineFixes true\n";
|
||||||
|
|
||||||
launchScript += "launcher " + getLauncher() + "\n";
|
launchScript += "launcher " + getLauncher() + "\n";
|
||||||
|
|
||||||
// qDebug() << "Generated launch script:" << launchScript;
|
// qDebug() << "Generated launch script:" << launchScript;
|
||||||
@ -791,9 +878,6 @@ QMap<QString, QString> MinecraftInstance::createCensorFilterFromSession(AuthSess
|
|||||||
if (sessionRef.access_token != "0") {
|
if (sessionRef.access_token != "0") {
|
||||||
addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>"));
|
addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>"));
|
||||||
}
|
}
|
||||||
if (sessionRef.client_token.size()) {
|
|
||||||
addToFilter(sessionRef.client_token, tr("<CLIENT TOKEN>"));
|
|
||||||
}
|
|
||||||
addToFilter(sessionRef.uuid, tr("<PROFILE ID>"));
|
addToFilter(sessionRef.uuid, tr("<PROFILE ID>"));
|
||||||
|
|
||||||
return filter;
|
return filter;
|
||||||
@ -875,13 +959,16 @@ QString MinecraftInstance::getStatusbarDescription()
|
|||||||
if (m_settings->get("ShowGameTime").toBool()) {
|
if (m_settings->get("ShowGameTime").toBool()) {
|
||||||
if (lastTimePlayed() > 0) {
|
if (lastTimePlayed() > 0) {
|
||||||
QDateTime lastLaunchTime = QDateTime::fromMSecsSinceEpoch(lastLaunch());
|
QDateTime lastLaunchTime = QDateTime::fromMSecsSinceEpoch(lastLaunch());
|
||||||
description.append(tr(", last played on %1 for %2")
|
description.append(
|
||||||
|
tr(", last played on %1 for %2")
|
||||||
.arg(QLocale().toString(lastLaunchTime, QLocale::ShortFormat))
|
.arg(QLocale().toString(lastLaunchTime, QLocale::ShortFormat))
|
||||||
.arg(Time::prettifyDuration(lastTimePlayed())));
|
.arg(Time::prettifyDuration(lastTimePlayed(), APPLICATION->settings()->get("ShowGameTimeWithoutDays").toBool())));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (totalTimePlayed() > 0) {
|
if (totalTimePlayed() > 0) {
|
||||||
description.append(tr(", total played for %1").arg(Time::prettifyDuration(totalTimePlayed())));
|
description.append(
|
||||||
|
tr(", total played for %1")
|
||||||
|
.arg(Time::prettifyDuration(totalTimePlayed(), APPLICATION->settings()->get("ShowGameTimeWithoutDays").toBool())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hasCrashed()) {
|
if (hasCrashed()) {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -70,6 +70,8 @@ class MinecraftInstance : public BaseInstance {
|
|||||||
|
|
||||||
bool canExport() const override { return true; }
|
bool canExport() const override { return true; }
|
||||||
|
|
||||||
|
void populateLaunchMenu(QMenu* menu) override;
|
||||||
|
|
||||||
////// Directories and files //////
|
////// Directories and files //////
|
||||||
QString jarModsDir() const;
|
QString jarModsDir() const;
|
||||||
QString resourcePacksDir() const;
|
QString resourcePacksDir() const;
|
||||||
@ -127,6 +129,7 @@ class MinecraftInstance : public BaseInstance {
|
|||||||
/// get arguments passed to java
|
/// get arguments passed to java
|
||||||
QStringList javaArguments();
|
QStringList javaArguments();
|
||||||
QString getLauncher();
|
QString getLauncher();
|
||||||
|
bool shouldApplyOnlineFixes();
|
||||||
|
|
||||||
/// get variables for launch command variable substitution/environment
|
/// get variables for launch command variable substitution/environment
|
||||||
QMap<QString, QString> getVariables() override;
|
QMap<QString, QString> getVariables() override;
|
||||||
|
@ -19,8 +19,8 @@ struct MojangDownloadInfo {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct MojangLibraryDownloadInfo {
|
struct MojangLibraryDownloadInfo {
|
||||||
MojangLibraryDownloadInfo(MojangDownloadInfo::Ptr artifact) : artifact(artifact){};
|
MojangLibraryDownloadInfo(MojangDownloadInfo::Ptr artifact_) : artifact(artifact_) {}
|
||||||
MojangLibraryDownloadInfo(){};
|
MojangLibraryDownloadInfo() {}
|
||||||
|
|
||||||
// types
|
// types
|
||||||
typedef std::shared_ptr<MojangLibraryDownloadInfo> Ptr;
|
typedef std::shared_ptr<MojangLibraryDownloadInfo> Ptr;
|
||||||
@ -47,18 +47,18 @@ struct MojangAssetIndexInfo : public MojangDownloadInfo {
|
|||||||
// methods
|
// methods
|
||||||
MojangAssetIndexInfo() {}
|
MojangAssetIndexInfo() {}
|
||||||
|
|
||||||
MojangAssetIndexInfo(QString id)
|
MojangAssetIndexInfo(QString id_)
|
||||||
{
|
{
|
||||||
this->id = id;
|
this->id = id_;
|
||||||
// HACK: ignore assets from other version files than Minecraft
|
// HACK: ignore assets from other version files than Minecraft
|
||||||
// workaround for stupid assets issue caused by amazon:
|
// workaround for stupid assets issue caused by amazon:
|
||||||
// https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/
|
// https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/
|
||||||
if (id == "legacy") {
|
if (id_ == "legacy") {
|
||||||
url = "https://piston-meta.mojang.com/mc/assets/legacy/c0fd82e8ce9fbc93119e40d96d5a4e62cfa3f729/legacy.json";
|
url = "https://piston-meta.mojang.com/mc/assets/legacy/c0fd82e8ce9fbc93119e40d96d5a4e62cfa3f729/legacy.json";
|
||||||
}
|
}
|
||||||
// HACK
|
// HACK
|
||||||
else {
|
else {
|
||||||
url = "https://s3.amazonaws.com/Minecraft.Download/indexes/" + id + ".json";
|
url = "https://s3.amazonaws.com/Minecraft.Download/indexes/" + id_ + ".json";
|
||||||
}
|
}
|
||||||
known = false;
|
known = false;
|
||||||
}
|
}
|
||||||
|
@ -350,7 +350,7 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr& patch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LibraryPtr OneSixVersionFormat::plusJarModFromJson(ProblemContainer& problems,
|
LibraryPtr OneSixVersionFormat::plusJarModFromJson([[maybe_unused]] ProblemContainer& problems,
|
||||||
const QJsonObject& libObj,
|
const QJsonObject& libObj,
|
||||||
const QString& filename,
|
const QString& filename,
|
||||||
const QString& originalName)
|
const QString& originalName)
|
||||||
|
@ -58,14 +58,14 @@
|
|||||||
#include "ComponentUpdateTask.h"
|
#include "ComponentUpdateTask.h"
|
||||||
#include "PackProfile.h"
|
#include "PackProfile.h"
|
||||||
#include "PackProfile_p.h"
|
#include "PackProfile_p.h"
|
||||||
|
#include "minecraft/mod/Mod.h"
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
|
|
||||||
#include "Application.h"
|
static const QMap<QString, ModPlatform::ModLoaderType> modloaderMapping{ { "net.neoforged", ModPlatform::NeoForge },
|
||||||
#include "modplatform/ResourceAPI.h"
|
{ "net.minecraftforge", ModPlatform::Forge },
|
||||||
|
{ "net.fabricmc.fabric-loader", ModPlatform::Fabric },
|
||||||
static const QMap<QString, ResourceAPI::ModLoaderType> modloaderMapping{ { "net.minecraftforge", ResourceAPI::Forge },
|
{ "org.quiltmc.quilt-loader", ModPlatform::Quilt },
|
||||||
{ "net.fabricmc.fabric-loader", ResourceAPI::Fabric },
|
{ "com.mumfrey.liteloader", ModPlatform::LiteLoader } };
|
||||||
{ "org.quiltmc.quilt-loader", ResourceAPI::Quilt },
|
|
||||||
{ "com.mumfrey.liteloader", ResourceAPI::LiteLoader } };
|
|
||||||
|
|
||||||
PackProfile::PackProfile(MinecraftInstance* instance) : QAbstractListModel()
|
PackProfile::PackProfile(MinecraftInstance* instance) : QAbstractListModel()
|
||||||
{
|
{
|
||||||
@ -204,10 +204,10 @@ static bool loadPackProfile(PackProfile* parent,
|
|||||||
}
|
}
|
||||||
auto orderArray = Json::requireArray(obj.value("components"));
|
auto orderArray = Json::requireArray(obj.value("components"));
|
||||||
for (auto item : orderArray) {
|
for (auto item : orderArray) {
|
||||||
auto obj = Json::requireObject(item, "Component must be an object.");
|
auto comp_obj = Json::requireObject(item, "Component must be an object.");
|
||||||
container.append(componentFromJsonV1(parent, componentJsonPattern, obj));
|
container.append(componentFromJsonV1(parent, componentJsonPattern, comp_obj));
|
||||||
}
|
}
|
||||||
} catch (const JSONValidationError& err) {
|
} catch ([[maybe_unused]] const JSONValidationError& err) {
|
||||||
qCritical() << "Couldn't parse" << componentsFile.fileName() << ": bad file format";
|
qCritical() << "Couldn't parse" << componentsFile.fileName() << ": bad file format";
|
||||||
container.clear();
|
container.clear();
|
||||||
return false;
|
return false;
|
||||||
@ -377,7 +377,7 @@ void PackProfile::insertComponent(size_t index, ComponentPtr component)
|
|||||||
qWarning() << "Attempt to add a component that is already present!";
|
qWarning() << "Attempt to add a component that is already present!";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
beginInsertRows(QModelIndex(), index, index);
|
beginInsertRows(QModelIndex(), static_cast<int>(index), static_cast<int>(index));
|
||||||
d->components.insert(index, component);
|
d->components.insert(index, component);
|
||||||
d->componentIndex[id] = component;
|
d->componentIndex[id] = component;
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
@ -389,7 +389,7 @@ void PackProfile::componentDataChanged()
|
|||||||
{
|
{
|
||||||
auto objPtr = qobject_cast<Component*>(sender());
|
auto objPtr = qobject_cast<Component*>(sender());
|
||||||
if (!objPtr) {
|
if (!objPtr) {
|
||||||
qWarning() << "PackProfile got dataChenged signal from a non-Component!";
|
qWarning() << "PackProfile got dataChanged signal from a non-Component!";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (objPtr->getID() == "net.minecraft") {
|
if (objPtr->getID() == "net.minecraft") {
|
||||||
@ -405,7 +405,7 @@ void PackProfile::componentDataChanged()
|
|||||||
}
|
}
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
qWarning() << "PackProfile got dataChenged signal from a Component which does not belong to it!";
|
qWarning() << "PackProfile got dataChanged signal from a Component which does not belong to it!";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PackProfile::remove(const int index)
|
bool PackProfile::remove(const int index)
|
||||||
@ -483,9 +483,9 @@ ComponentPtr PackProfile::getComponent(const QString& id)
|
|||||||
return (*iter);
|
return (*iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
ComponentPtr PackProfile::getComponent(int index)
|
ComponentPtr PackProfile::getComponent(size_t index)
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= d->components.size()) {
|
if (index >= static_cast<size_t>(d->components.size())) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return d->components[index];
|
return d->components[index];
|
||||||
@ -547,7 +547,7 @@ QVariant PackProfile::data(const QModelIndex& index, int role) const
|
|||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PackProfile::setData(const QModelIndex& index, const QVariant& value, int role)
|
bool PackProfile::setData(const QModelIndex& index, [[maybe_unused]] const QVariant& value, int role)
|
||||||
{
|
{
|
||||||
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(index.parent())) {
|
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(index.parent())) {
|
||||||
return false;
|
return false;
|
||||||
@ -989,12 +989,12 @@ void PackProfile::disableInteraction(bool disable)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<ResourceAPI::ModLoaderTypes> PackProfile::getModLoaders()
|
std::optional<ModPlatform::ModLoaderTypes> PackProfile::getModLoaders()
|
||||||
{
|
{
|
||||||
ResourceAPI::ModLoaderTypes result;
|
ModPlatform::ModLoaderTypes result;
|
||||||
bool has_any_loader = false;
|
bool has_any_loader = false;
|
||||||
|
|
||||||
QMapIterator<QString, ResourceAPI::ModLoaderType> i(modloaderMapping);
|
QMapIterator<QString, ModPlatform::ModLoaderType> i(modloaderMapping);
|
||||||
|
|
||||||
while (i.hasNext()) {
|
while (i.hasNext()) {
|
||||||
i.next();
|
i.next();
|
||||||
@ -1008,3 +1008,17 @@ std::optional<ResourceAPI::ModLoaderTypes> PackProfile::getModLoaders()
|
|||||||
return {};
|
return {};
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<ModPlatform::ModLoaderTypes> PackProfile::getSupportedModLoaders()
|
||||||
|
{
|
||||||
|
auto loadersOpt = getModLoaders();
|
||||||
|
if (!loadersOpt.has_value())
|
||||||
|
return loadersOpt;
|
||||||
|
auto loaders = loadersOpt.value();
|
||||||
|
// TODO: remove this or add version condition once Quilt drops official Fabric support
|
||||||
|
if (loaders & ModPlatform::Quilt)
|
||||||
|
loaders |= ModPlatform::Fabric;
|
||||||
|
if (getComponentVersion("net.minecraft") == "1.20.1" && (loaders & ModPlatform::NeoForge))
|
||||||
|
loaders |= ModPlatform::Forge;
|
||||||
|
return loaders;
|
||||||
|
}
|
||||||
|
@ -44,14 +44,11 @@
|
|||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "BaseVersion.h"
|
|
||||||
#include "Component.h"
|
#include "Component.h"
|
||||||
#include "LaunchProfile.h"
|
#include "LaunchProfile.h"
|
||||||
#include "Library.h"
|
#include "modplatform/ModIndex.h"
|
||||||
#include "MojangDownloadInfo.h"
|
|
||||||
#include "ProfileUtils.h"
|
|
||||||
#include "modplatform/ResourceAPI.h"
|
|
||||||
#include "net/Mode.h"
|
#include "net/Mode.h"
|
||||||
|
|
||||||
class MinecraftInstance;
|
class MinecraftInstance;
|
||||||
@ -140,13 +137,15 @@ class PackProfile : public QAbstractListModel {
|
|||||||
ComponentPtr getComponent(const QString& id);
|
ComponentPtr getComponent(const QString& id);
|
||||||
|
|
||||||
/// get the profile component by index
|
/// get the profile component by index
|
||||||
ComponentPtr getComponent(int index);
|
ComponentPtr getComponent(size_t index);
|
||||||
|
|
||||||
/// Add the component to the internal list of patches
|
/// Add the component to the internal list of patches
|
||||||
// todo(merged): is this the best approach
|
// todo(merged): is this the best approach
|
||||||
void appendComponent(ComponentPtr component);
|
void appendComponent(ComponentPtr component);
|
||||||
|
|
||||||
std::optional<ResourceAPI::ModLoaderTypes> getModLoaders();
|
std::optional<ModPlatform::ModLoaderTypes> getModLoaders();
|
||||||
|
// this returns aditional loaders(Quilt supports fabric and NeoForge supports Forge)
|
||||||
|
std::optional<ModPlatform::ModLoaderTypes> getSupportedModLoaders();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void scheduleSave();
|
void scheduleSave();
|
||||||
|
@ -82,7 +82,7 @@ bool readOverrideOrders(QString path, PatchOrder& order)
|
|||||||
for (auto item : orderArray) {
|
for (auto item : orderArray) {
|
||||||
order.append(Json::requireString(item));
|
order.append(Json::requireString(item));
|
||||||
}
|
}
|
||||||
} catch (const JSONValidationError& err) {
|
} catch ([[maybe_unused]] const JSONValidationError& err) {
|
||||||
qCritical() << "Couldn't parse" << orderFile.fileName() << ": bad file format";
|
qCritical() << "Couldn't parse" << orderFile.fileName() << ": bad file format";
|
||||||
qWarning() << "Ignoring overriden order";
|
qWarning() << "Ignoring overriden order";
|
||||||
order.clear();
|
order.clear();
|
||||||
|
@ -55,7 +55,7 @@ class Rule {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
Rule(RuleAction result) : m_result(result) {}
|
Rule(RuleAction result) : m_result(result) {}
|
||||||
virtual ~Rule(){};
|
virtual ~Rule() {}
|
||||||
virtual QJsonObject toJson() = 0;
|
virtual QJsonObject toJson() = 0;
|
||||||
RuleAction apply(const Library* parent, const RuntimeContext& runtimeContext)
|
RuleAction apply(const Library* parent, const RuntimeContext& runtimeContext)
|
||||||
{
|
{
|
||||||
|
@ -368,11 +368,11 @@ optional<QString> read_string(nbt::value& parent, const char* name)
|
|||||||
}
|
}
|
||||||
auto& tag_str = namedValue.as<nbt::tag_string>();
|
auto& tag_str = namedValue.as<nbt::tag_string>();
|
||||||
return QString::fromStdString(tag_str.get());
|
return QString::fromStdString(tag_str.get());
|
||||||
} catch (const std::out_of_range& e) {
|
} catch ([[maybe_unused]] const std::out_of_range& e) {
|
||||||
// fallback for old world formats
|
// fallback for old world formats
|
||||||
qWarning() << "String NBT tag" << name << "could not be found.";
|
qWarning() << "String NBT tag" << name << "could not be found.";
|
||||||
return nullopt;
|
return nullopt;
|
||||||
} catch (const std::bad_cast& e) {
|
} catch ([[maybe_unused]] const std::bad_cast& e) {
|
||||||
// type mismatch
|
// type mismatch
|
||||||
qWarning() << "NBT tag" << name << "could not be converted to string.";
|
qWarning() << "NBT tag" << name << "could not be converted to string.";
|
||||||
return nullopt;
|
return nullopt;
|
||||||
@ -388,11 +388,11 @@ optional<int64_t> read_long(nbt::value& parent, const char* name)
|
|||||||
}
|
}
|
||||||
auto& tag_str = namedValue.as<nbt::tag_long>();
|
auto& tag_str = namedValue.as<nbt::tag_long>();
|
||||||
return tag_str.get();
|
return tag_str.get();
|
||||||
} catch (const std::out_of_range& e) {
|
} catch ([[maybe_unused]] const std::out_of_range& e) {
|
||||||
// fallback for old world formats
|
// fallback for old world formats
|
||||||
qWarning() << "Long NBT tag" << name << "could not be found.";
|
qWarning() << "Long NBT tag" << name << "could not be found.";
|
||||||
return nullopt;
|
return nullopt;
|
||||||
} catch (const std::bad_cast& e) {
|
} catch ([[maybe_unused]] const std::bad_cast& e) {
|
||||||
// type mismatch
|
// type mismatch
|
||||||
qWarning() << "NBT tag" << name << "could not be converted to long.";
|
qWarning() << "NBT tag" << name << "could not be converted to long.";
|
||||||
return nullopt;
|
return nullopt;
|
||||||
@ -408,11 +408,11 @@ optional<int> read_int(nbt::value& parent, const char* name)
|
|||||||
}
|
}
|
||||||
auto& tag_str = namedValue.as<nbt::tag_int>();
|
auto& tag_str = namedValue.as<nbt::tag_int>();
|
||||||
return tag_str.get();
|
return tag_str.get();
|
||||||
} catch (const std::out_of_range& e) {
|
} catch ([[maybe_unused]] const std::out_of_range& e) {
|
||||||
// fallback for old world formats
|
// fallback for old world formats
|
||||||
qWarning() << "Int NBT tag" << name << "could not be found.";
|
qWarning() << "Int NBT tag" << name << "could not be found.";
|
||||||
return nullopt;
|
return nullopt;
|
||||||
} catch (const std::bad_cast& e) {
|
} catch ([[maybe_unused]] const std::bad_cast& e) {
|
||||||
// type mismatch
|
// type mismatch
|
||||||
qWarning() << "NBT tag" << name << "could not be converted to int.";
|
qWarning() << "NBT tag" << name << "could not be converted to int.";
|
||||||
return nullopt;
|
return nullopt;
|
||||||
|
@ -255,7 +255,7 @@ QVariant WorldList::data(const QModelIndex& index, int role) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant WorldList::headerData(int section, Qt::Orientation orientation, int role) const
|
QVariant WorldList::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const
|
||||||
{
|
{
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
@ -294,7 +294,6 @@ QVariant WorldList::headerData(int section, Qt::Orientation orientation, int rol
|
|||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
return QVariant();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList WorldList::mimeTypes() const
|
QStringList WorldList::mimeTypes() const
|
||||||
@ -339,19 +338,19 @@ QMimeData* WorldList::mimeData(const QModelIndexList& indexes) const
|
|||||||
if (indexes.size() == 0)
|
if (indexes.size() == 0)
|
||||||
return new QMimeData();
|
return new QMimeData();
|
||||||
|
|
||||||
QList<World> worlds;
|
QList<World> worlds_;
|
||||||
for (auto idx : indexes) {
|
for (auto idx : indexes) {
|
||||||
if (idx.column() != 0)
|
if (idx.column() != 0)
|
||||||
continue;
|
continue;
|
||||||
int row = idx.row();
|
int row = idx.row();
|
||||||
if (row < 0 || row >= this->worlds.size())
|
if (row < 0 || row >= this->worlds.size())
|
||||||
continue;
|
continue;
|
||||||
worlds.append(this->worlds[row]);
|
worlds_.append(this->worlds[row]);
|
||||||
}
|
}
|
||||||
if (!worlds.size()) {
|
if (!worlds_.size()) {
|
||||||
return new QMimeData();
|
return new QMimeData();
|
||||||
}
|
}
|
||||||
return new WorldMimeData(worlds);
|
return new WorldMimeData(worlds_);
|
||||||
}
|
}
|
||||||
|
|
||||||
Qt::ItemFlags WorldList::flags(const QModelIndex& index) const
|
Qt::ItemFlags WorldList::flags(const QModelIndex& index) const
|
||||||
|
@ -278,67 +278,6 @@ bool entitlementFromJSONV3(const QJsonObject& parent, MinecraftEntitlement& out)
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool AccountData::resumeStateFromV2(QJsonObject data)
|
|
||||||
{
|
|
||||||
// The JSON object must at least have a username for it to be valid.
|
|
||||||
if (!data.value("username").isString()) {
|
|
||||||
qCritical() << "Can't load Mojang account info from JSON object. Username field is missing or of the wrong type.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString userName = data.value("username").toString("");
|
|
||||||
QString clientToken = data.value("clientToken").toString("");
|
|
||||||
QString accessToken = data.value("accessToken").toString("");
|
|
||||||
|
|
||||||
QJsonArray profileArray = data.value("profiles").toArray();
|
|
||||||
if (profileArray.size() < 1) {
|
|
||||||
qCritical() << "Can't load Mojang account with username \"" << userName << "\". No profiles found.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AccountProfile {
|
|
||||||
QString id;
|
|
||||||
QString name;
|
|
||||||
bool legacy;
|
|
||||||
};
|
|
||||||
|
|
||||||
QList<AccountProfile> profiles;
|
|
||||||
int currentProfileIndex = 0;
|
|
||||||
int index = -1;
|
|
||||||
QString currentProfile = data.value("activeProfile").toString("");
|
|
||||||
for (QJsonValue profileVal : profileArray) {
|
|
||||||
index++;
|
|
||||||
QJsonObject profileObject = profileVal.toObject();
|
|
||||||
QString id = profileObject.value("id").toString("");
|
|
||||||
QString name = profileObject.value("name").toString("");
|
|
||||||
bool legacy = profileObject.value("legacy").toBool(false);
|
|
||||||
if (id.isEmpty() || name.isEmpty()) {
|
|
||||||
qWarning() << "Unable to load a profile" << name << "because it was missing an ID or a name.";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (id == currentProfile) {
|
|
||||||
currentProfileIndex = index;
|
|
||||||
}
|
|
||||||
profiles.append({ id, name, legacy });
|
|
||||||
}
|
|
||||||
auto& profile = profiles[currentProfileIndex];
|
|
||||||
|
|
||||||
type = AccountType::Mojang;
|
|
||||||
legacy = profile.legacy;
|
|
||||||
|
|
||||||
minecraftProfile.id = profile.id;
|
|
||||||
minecraftProfile.name = profile.name;
|
|
||||||
minecraftProfile.validity = Katabasis::Validity::Assumed;
|
|
||||||
|
|
||||||
yggdrasilToken.token = accessToken;
|
|
||||||
yggdrasilToken.extra["clientToken"] = clientToken;
|
|
||||||
yggdrasilToken.extra["userName"] = userName;
|
|
||||||
yggdrasilToken.validity = Katabasis::Validity::Assumed;
|
|
||||||
|
|
||||||
validity_ = minecraftProfile.validity;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AccountData::resumeStateFromV3(QJsonObject data)
|
bool AccountData::resumeStateFromV3(QJsonObject data)
|
||||||
{
|
{
|
||||||
auto typeV = data.value("type");
|
auto typeV = data.value("type");
|
||||||
@ -349,8 +288,6 @@ bool AccountData::resumeStateFromV3(QJsonObject data)
|
|||||||
auto typeS = typeV.toString();
|
auto typeS = typeV.toString();
|
||||||
if (typeS == "MSA") {
|
if (typeS == "MSA") {
|
||||||
type = AccountType::MSA;
|
type = AccountType::MSA;
|
||||||
} else if (typeS == "Mojang") {
|
|
||||||
type = AccountType::Mojang;
|
|
||||||
} else if (typeS == "Offline") {
|
} else if (typeS == "Offline") {
|
||||||
type = AccountType::Offline;
|
type = AccountType::Offline;
|
||||||
} else {
|
} else {
|
||||||
@ -358,11 +295,6 @@ bool AccountData::resumeStateFromV3(QJsonObject data)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == AccountType::Mojang) {
|
|
||||||
legacy = data.value("legacy").toBool(false);
|
|
||||||
canMigrateToMSA = data.value("canMigrateToMSA").toBool(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == AccountType::MSA) {
|
if (type == AccountType::MSA) {
|
||||||
auto clientIDV = data.value("msa-client-id");
|
auto clientIDV = data.value("msa-client-id");
|
||||||
if (clientIDV.isString()) {
|
if (clientIDV.isString()) {
|
||||||
@ -395,15 +327,7 @@ bool AccountData::resumeStateFromV3(QJsonObject data)
|
|||||||
QJsonObject AccountData::saveState() const
|
QJsonObject AccountData::saveState() const
|
||||||
{
|
{
|
||||||
QJsonObject output;
|
QJsonObject output;
|
||||||
if (type == AccountType::Mojang) {
|
if (type == AccountType::MSA) {
|
||||||
output["type"] = "Mojang";
|
|
||||||
if (legacy) {
|
|
||||||
output["legacy"] = true;
|
|
||||||
}
|
|
||||||
if (canMigrateToMSA) {
|
|
||||||
output["canMigrateToMSA"] = true;
|
|
||||||
}
|
|
||||||
} else if (type == AccountType::MSA) {
|
|
||||||
output["type"] = "MSA";
|
output["type"] = "MSA";
|
||||||
output["msa-client-id"] = msaClientID;
|
output["msa-client-id"] = msaClientID;
|
||||||
tokenToJSONV3(output, msaToken, "msa");
|
tokenToJSONV3(output, msaToken, "msa");
|
||||||
@ -420,51 +344,11 @@ QJsonObject AccountData::saveState() const
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AccountData::userName() const
|
|
||||||
{
|
|
||||||
if (type == AccountType::MSA) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
return yggdrasilToken.extra["userName"].toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString AccountData::accessToken() const
|
QString AccountData::accessToken() const
|
||||||
{
|
{
|
||||||
return yggdrasilToken.token;
|
return yggdrasilToken.token;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AccountData::clientToken() const
|
|
||||||
{
|
|
||||||
if (type != AccountType::Mojang) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
return yggdrasilToken.extra["clientToken"].toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AccountData::setClientToken(QString clientToken)
|
|
||||||
{
|
|
||||||
if (type != AccountType::Mojang) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
yggdrasilToken.extra["clientToken"] = clientToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AccountData::generateClientTokenIfMissing()
|
|
||||||
{
|
|
||||||
if (yggdrasilToken.extra.contains("clientToken")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
invalidateClientToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AccountData::invalidateClientToken()
|
|
||||||
{
|
|
||||||
if (type != AccountType::Mojang) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{-}]"));
|
|
||||||
}
|
|
||||||
|
|
||||||
QString AccountData::profileId() const
|
QString AccountData::profileId() const
|
||||||
{
|
{
|
||||||
return minecraftProfile.id;
|
return minecraftProfile.id;
|
||||||
@ -482,9 +366,6 @@ QString AccountData::profileName() const
|
|||||||
QString AccountData::accountDisplayString() const
|
QString AccountData::accountDisplayString() const
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AccountType::Mojang: {
|
|
||||||
return userName();
|
|
||||||
}
|
|
||||||
case AccountType::Offline: {
|
case AccountType::Offline: {
|
||||||
return QObject::tr("<Offline>");
|
return QObject::tr("<Offline>");
|
||||||
}
|
}
|
||||||
|
@ -71,27 +71,17 @@ struct MinecraftProfile {
|
|||||||
Katabasis::Validity validity = Katabasis::Validity::None;
|
Katabasis::Validity validity = Katabasis::Validity::None;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class AccountType { MSA, Mojang, Offline };
|
enum class AccountType { MSA, Offline };
|
||||||
|
|
||||||
enum class AccountState { Unchecked, Offline, Working, Online, Disabled, Errored, Expired, Gone };
|
enum class AccountState { Unchecked, Offline, Working, Online, Disabled, Errored, Expired, Gone };
|
||||||
|
|
||||||
struct AccountData {
|
struct AccountData {
|
||||||
QJsonObject saveState() const;
|
QJsonObject saveState() const;
|
||||||
bool resumeStateFromV2(QJsonObject data);
|
|
||||||
bool resumeStateFromV3(QJsonObject data);
|
bool resumeStateFromV3(QJsonObject data);
|
||||||
|
|
||||||
//! userName for Mojang accounts, gamertag for MSA
|
//! userName for Mojang accounts, gamertag for MSA
|
||||||
QString accountDisplayString() const;
|
QString accountDisplayString() const;
|
||||||
|
|
||||||
//! Only valid for Mojang accounts. MSA does not preserve this information
|
|
||||||
QString userName() const;
|
|
||||||
|
|
||||||
//! Only valid for Mojang accounts.
|
|
||||||
QString clientToken() const;
|
|
||||||
void setClientToken(QString clientToken);
|
|
||||||
void invalidateClientToken();
|
|
||||||
void generateClientTokenIfMissing();
|
|
||||||
|
|
||||||
//! Yggdrasil access token, as passed to the game.
|
//! Yggdrasil access token, as passed to the game.
|
||||||
QString accessToken() const;
|
QString accessToken() const;
|
||||||
|
|
||||||
@ -101,8 +91,6 @@ struct AccountData {
|
|||||||
QString lastError() const;
|
QString lastError() const;
|
||||||
|
|
||||||
AccountType type = AccountType::MSA;
|
AccountType type = AccountType::MSA;
|
||||||
bool legacy = false;
|
|
||||||
bool canMigrateToMSA = false;
|
|
||||||
|
|
||||||
QString msaClientID;
|
QString msaClientID;
|
||||||
Katabasis::Token msaToken;
|
Katabasis::Token msaToken;
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
enum AccountListVersion { MojangOnly = 2, MojangMSA = 3 };
|
enum AccountListVersion { MojangMSA = 3 };
|
||||||
|
|
||||||
AccountList::AccountList(QObject* parent) : QAbstractListModel(parent)
|
AccountList::AccountList(QObject* parent) : QAbstractListModel(parent)
|
||||||
{
|
{
|
||||||
@ -320,17 +320,6 @@ QVariant AccountList::data(const QModelIndex& index, int role) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case MigrationColumn: {
|
|
||||||
if (account->isMSA() || account->isOffline()) {
|
|
||||||
return tr("N/A", "Can Migrate");
|
|
||||||
}
|
|
||||||
if (account->canMigrate()) {
|
|
||||||
return tr("Yes", "Can Migrate");
|
|
||||||
} else {
|
|
||||||
return tr("No", "Can Migrate");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
@ -353,7 +342,7 @@ QVariant AccountList::data(const QModelIndex& index, int role) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant AccountList::headerData(int section, Qt::Orientation orientation, int role) const
|
QVariant AccountList::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const
|
||||||
{
|
{
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
@ -366,8 +355,6 @@ QVariant AccountList::headerData(int section, Qt::Orientation orientation, int r
|
|||||||
return tr("Type");
|
return tr("Type");
|
||||||
case StatusColumn:
|
case StatusColumn:
|
||||||
return tr("Status");
|
return tr("Status");
|
||||||
case MigrationColumn:
|
|
||||||
return tr("Can Migrate?");
|
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
@ -379,11 +366,9 @@ QVariant AccountList::headerData(int section, Qt::Orientation orientation, int r
|
|||||||
case NameColumn:
|
case NameColumn:
|
||||||
return tr("User name of the account.");
|
return tr("User name of the account.");
|
||||||
case TypeColumn:
|
case TypeColumn:
|
||||||
return tr("Type of the account - Mojang or MSA.");
|
return tr("Type of the account (MSA or Offline)");
|
||||||
case StatusColumn:
|
case StatusColumn:
|
||||||
return tr("Current status of the account.");
|
return tr("Current status of the account.");
|
||||||
case MigrationColumn:
|
|
||||||
return tr("Can this account migrate to a Microsoft account?");
|
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
@ -415,7 +400,7 @@ Qt::ItemFlags AccountList::flags(const QModelIndex& index) const
|
|||||||
|
|
||||||
bool AccountList::setData(const QModelIndex& idx, const QVariant& value, int role)
|
bool AccountList::setData(const QModelIndex& idx, const QVariant& value, int role)
|
||||||
{
|
{
|
||||||
if (idx.row() < 0 || idx.row() >= rowCount(idx) || !idx.isValid()) {
|
if (idx.row() < 0 || idx.row() >= rowCount(idx.parent()) || !idx.isValid()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,7 +408,8 @@ bool AccountList::setData(const QModelIndex& idx, const QVariant& value, int rol
|
|||||||
if (value == Qt::Checked) {
|
if (value == Qt::Checked) {
|
||||||
MinecraftAccountPtr account = at(idx.row());
|
MinecraftAccountPtr account = at(idx.row());
|
||||||
setDefaultAccount(account);
|
setDefaultAccount(account);
|
||||||
}
|
} else if (m_defaultAccount == at(idx.row()))
|
||||||
|
setDefaultAccount(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit dataChanged(idx, index(idx.row(), columnCount(QModelIndex()) - 1));
|
emit dataChanged(idx, index(idx.row(), columnCount(QModelIndex()) - 1));
|
||||||
@ -472,9 +458,6 @@ bool AccountList::loadList()
|
|||||||
// Make sure the format version matches.
|
// Make sure the format version matches.
|
||||||
auto listVersion = root.value("formatVersion").toVariant().toInt();
|
auto listVersion = root.value("formatVersion").toVariant().toInt();
|
||||||
switch (listVersion) {
|
switch (listVersion) {
|
||||||
case AccountListVersion::MojangOnly: {
|
|
||||||
return loadV2(root);
|
|
||||||
} break;
|
|
||||||
case AccountListVersion::MojangMSA: {
|
case AccountListVersion::MojangMSA: {
|
||||||
return loadV3(root);
|
return loadV3(root);
|
||||||
} break;
|
} break;
|
||||||
@ -488,36 +471,6 @@ bool AccountList::loadList()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AccountList::loadV2(QJsonObject& root)
|
|
||||||
{
|
|
||||||
beginResetModel();
|
|
||||||
auto defaultUserName = root.value("activeAccount").toString("");
|
|
||||||
QJsonArray accounts = root.value("accounts").toArray();
|
|
||||||
for (QJsonValue accountVal : accounts) {
|
|
||||||
QJsonObject accountObj = accountVal.toObject();
|
|
||||||
MinecraftAccountPtr account = MinecraftAccount::loadFromJsonV2(accountObj);
|
|
||||||
if (account.get() != nullptr) {
|
|
||||||
auto profileId = account->profileId();
|
|
||||||
if (!profileId.size()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (findAccountByProfileId(profileId) != -1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
connect(account.get(), &MinecraftAccount::changed, this, &AccountList::accountChanged);
|
|
||||||
connect(account.get(), &MinecraftAccount::activityChanged, this, &AccountList::accountActivityChanged);
|
|
||||||
m_accounts.append(account);
|
|
||||||
if (defaultUserName.size() && account->mojangUserName() == defaultUserName) {
|
|
||||||
m_defaultAccount = account;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qWarning() << "Failed to load an account.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
endResetModel();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AccountList::loadV3(QJsonObject& root)
|
bool AccountList::loadV3(QJsonObject& root)
|
||||||
{
|
{
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
|
@ -55,7 +55,6 @@ class AccountList : public QAbstractListModel {
|
|||||||
// TODO: Add icon column.
|
// TODO: Add icon column.
|
||||||
ProfileNameColumn = 0,
|
ProfileNameColumn = 0,
|
||||||
NameColumn,
|
NameColumn,
|
||||||
MigrationColumn,
|
|
||||||
TypeColumn,
|
TypeColumn,
|
||||||
StatusColumn,
|
StatusColumn,
|
||||||
|
|
||||||
@ -97,7 +96,6 @@ class AccountList : public QAbstractListModel {
|
|||||||
void setListFilePath(QString path, bool autosave = false);
|
void setListFilePath(QString path, bool autosave = false);
|
||||||
|
|
||||||
bool loadList();
|
bool loadList();
|
||||||
bool loadV2(QJsonObject& root);
|
|
||||||
bool loadV3(QJsonObject& root);
|
bool loadV3(QJsonObject& root);
|
||||||
bool saveList();
|
bool saveList();
|
||||||
|
|
||||||
|
@ -24,10 +24,6 @@ struct AuthSession {
|
|||||||
GoneOrMigrated
|
GoneOrMigrated
|
||||||
} status = Undetermined;
|
} status = Undetermined;
|
||||||
|
|
||||||
// client token
|
|
||||||
QString client_token;
|
|
||||||
// account user name
|
|
||||||
QString username;
|
|
||||||
// combined session ID
|
// combined session ID
|
||||||
QString session;
|
QString session;
|
||||||
// volatile auth token
|
// volatile auth token
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
#include "MinecraftAccount.h"
|
#include "MinecraftAccount.h"
|
||||||
|
|
||||||
|
#include <QColor>
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
@ -50,7 +51,6 @@
|
|||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
|
||||||
#include "flows/MSA.h"
|
#include "flows/MSA.h"
|
||||||
#include "flows/Mojang.h"
|
|
||||||
#include "flows/Offline.h"
|
#include "flows/Offline.h"
|
||||||
|
|
||||||
MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent)
|
MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent)
|
||||||
@ -58,15 +58,6 @@ MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent)
|
|||||||
data.internalId = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
|
data.internalId = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
MinecraftAccountPtr MinecraftAccount::loadFromJsonV2(const QJsonObject& json)
|
|
||||||
{
|
|
||||||
MinecraftAccountPtr account(new MinecraftAccount());
|
|
||||||
if (account->data.resumeStateFromV2(json)) {
|
|
||||||
return account;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
MinecraftAccountPtr MinecraftAccount::loadFromJsonV3(const QJsonObject& json)
|
MinecraftAccountPtr MinecraftAccount::loadFromJsonV3(const QJsonObject& json)
|
||||||
{
|
{
|
||||||
MinecraftAccountPtr account(new MinecraftAccount());
|
MinecraftAccountPtr account(new MinecraftAccount());
|
||||||
@ -76,15 +67,6 @@ MinecraftAccountPtr MinecraftAccount::loadFromJsonV3(const QJsonObject& json)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
MinecraftAccountPtr MinecraftAccount::createFromUsername(const QString& username)
|
|
||||||
{
|
|
||||||
auto account = makeShared<MinecraftAccount>();
|
|
||||||
account->data.type = AccountType::Mojang;
|
|
||||||
account->data.yggdrasilToken.extra["userName"] = username;
|
|
||||||
account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
|
|
||||||
return account;
|
|
||||||
}
|
|
||||||
|
|
||||||
MinecraftAccountPtr MinecraftAccount::createBlankMSA()
|
MinecraftAccountPtr MinecraftAccount::createBlankMSA()
|
||||||
{
|
{
|
||||||
MinecraftAccountPtr account(new MinecraftAccount());
|
MinecraftAccountPtr account(new MinecraftAccount());
|
||||||
@ -126,24 +108,17 @@ QPixmap MinecraftAccount::getFace() const
|
|||||||
return QPixmap();
|
return QPixmap();
|
||||||
}
|
}
|
||||||
QPixmap skin = QPixmap(8, 8);
|
QPixmap skin = QPixmap(8, 8);
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||||
|
skin.fill(QColorConstants::Transparent);
|
||||||
|
#else
|
||||||
|
skin.fill(QColor(0, 0, 0, 0));
|
||||||
|
#endif
|
||||||
QPainter painter(&skin);
|
QPainter painter(&skin);
|
||||||
painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8));
|
painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8));
|
||||||
painter.drawPixmap(0, 0, skinTexture.copy(40, 8, 8, 8));
|
painter.drawPixmap(0, 0, skinTexture.copy(40, 8, 8, 8));
|
||||||
return skin.scaled(64, 64, Qt::KeepAspectRatio);
|
return skin.scaled(64, 64, Qt::KeepAspectRatio);
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_qobject_ptr<AccountTask> MinecraftAccount::login(QString password)
|
|
||||||
{
|
|
||||||
Q_ASSERT(m_currentTask.get() == nullptr);
|
|
||||||
|
|
||||||
m_currentTask.reset(new MojangLogin(&data, password));
|
|
||||||
connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded);
|
|
||||||
connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed);
|
|
||||||
connect(m_currentTask.get(), &Task::aborted, this, [this] { authFailed(tr("Aborted")); });
|
|
||||||
emit activityChanged(true);
|
|
||||||
return m_currentTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_qobject_ptr<AccountTask> MinecraftAccount::loginMSA()
|
shared_qobject_ptr<AccountTask> MinecraftAccount::loginMSA()
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_currentTask.get() == nullptr);
|
Q_ASSERT(m_currentTask.get() == nullptr);
|
||||||
@ -176,10 +151,8 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::refresh()
|
|||||||
|
|
||||||
if (data.type == AccountType::MSA) {
|
if (data.type == AccountType::MSA) {
|
||||||
m_currentTask.reset(new MSASilent(&data));
|
m_currentTask.reset(new MSASilent(&data));
|
||||||
} else if (data.type == AccountType::Offline) {
|
|
||||||
m_currentTask.reset(new OfflineRefresh(&data));
|
|
||||||
} else {
|
} else {
|
||||||
m_currentTask.reset(new MojangRefresh(&data));
|
m_currentTask.reset(new OfflineRefresh(&data));
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded);
|
connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded);
|
||||||
@ -290,13 +263,8 @@ void MinecraftAccount::fillSession(AuthSessionPtr session)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the user name. you have to have an user name
|
|
||||||
// FIXME: not with MSA
|
|
||||||
session->username = data.userName();
|
|
||||||
// volatile auth token
|
// volatile auth token
|
||||||
session->access_token = data.accessToken();
|
session->access_token = data.accessToken();
|
||||||
// the semi-permanent client token
|
|
||||||
session->client_token = data.clientToken();
|
|
||||||
// profile name
|
// profile name
|
||||||
session->player_name = data.profileName();
|
session->player_name = data.profileName();
|
||||||
// profile ID
|
// profile ID
|
||||||
|
@ -85,13 +85,10 @@ class MinecraftAccount : public QObject, public Usable {
|
|||||||
//! Default constructor
|
//! Default constructor
|
||||||
explicit MinecraftAccount(QObject* parent = 0);
|
explicit MinecraftAccount(QObject* parent = 0);
|
||||||
|
|
||||||
static MinecraftAccountPtr createFromUsername(const QString& username);
|
|
||||||
|
|
||||||
static MinecraftAccountPtr createBlankMSA();
|
static MinecraftAccountPtr createBlankMSA();
|
||||||
|
|
||||||
static MinecraftAccountPtr createOffline(const QString& username);
|
static MinecraftAccountPtr createOffline(const QString& username);
|
||||||
|
|
||||||
static MinecraftAccountPtr loadFromJsonV2(const QJsonObject& json);
|
|
||||||
static MinecraftAccountPtr loadFromJsonV3(const QJsonObject& json);
|
static MinecraftAccountPtr loadFromJsonV3(const QJsonObject& json);
|
||||||
|
|
||||||
static QUuid uuidFromUsername(QString username);
|
static QUuid uuidFromUsername(QString username);
|
||||||
@ -100,12 +97,6 @@ class MinecraftAccount : public QObject, public Usable {
|
|||||||
QJsonObject saveToJson() const;
|
QJsonObject saveToJson() const;
|
||||||
|
|
||||||
public: /* manipulation */
|
public: /* manipulation */
|
||||||
/**
|
|
||||||
* Attempt to login. Empty password means we use the token.
|
|
||||||
* If the attempt fails because we already are performing some task, it returns false.
|
|
||||||
*/
|
|
||||||
shared_qobject_ptr<AccountTask> login(QString password);
|
|
||||||
|
|
||||||
shared_qobject_ptr<AccountTask> loginMSA();
|
shared_qobject_ptr<AccountTask> loginMSA();
|
||||||
|
|
||||||
shared_qobject_ptr<AccountTask> loginOffline();
|
shared_qobject_ptr<AccountTask> loginOffline();
|
||||||
@ -119,8 +110,6 @@ class MinecraftAccount : public QObject, public Usable {
|
|||||||
|
|
||||||
QString accountDisplayString() const { return data.accountDisplayString(); }
|
QString accountDisplayString() const { return data.accountDisplayString(); }
|
||||||
|
|
||||||
QString mojangUserName() const { return data.userName(); }
|
|
||||||
|
|
||||||
QString accessToken() const { return data.accessToken(); }
|
QString accessToken() const { return data.accessToken(); }
|
||||||
|
|
||||||
QString profileId() const { return data.profileId(); }
|
QString profileId() const { return data.profileId(); }
|
||||||
@ -129,8 +118,6 @@ class MinecraftAccount : public QObject, public Usable {
|
|||||||
|
|
||||||
bool isActive() const;
|
bool isActive() const;
|
||||||
|
|
||||||
bool canMigrate() const { return data.canMigrateToMSA; }
|
|
||||||
|
|
||||||
bool isMSA() const { return data.type == AccountType::MSA; }
|
bool isMSA() const { return data.type == AccountType::MSA; }
|
||||||
|
|
||||||
bool isOffline() const { return data.type == AccountType::Offline; }
|
bool isOffline() const { return data.type == AccountType::Offline; }
|
||||||
@ -142,12 +129,6 @@ class MinecraftAccount : public QObject, public Usable {
|
|||||||
QString typeString() const
|
QString typeString() const
|
||||||
{
|
{
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case AccountType::Mojang: {
|
|
||||||
if (data.legacy) {
|
|
||||||
return "legacy";
|
|
||||||
}
|
|
||||||
return "mojang";
|
|
||||||
} break;
|
|
||||||
case AccountType::MSA: {
|
case AccountType::MSA: {
|
||||||
return "msa";
|
return "msa";
|
||||||
} break;
|
} break;
|
||||||
|
@ -113,16 +113,16 @@ bool parseXTokenResponse(QByteArray& data, Katabasis::Token& output, QString nam
|
|||||||
if (!item.isObject()) {
|
if (!item.isObject()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto obj = item.toObject();
|
auto obj_ = item.toObject();
|
||||||
if (obj.contains("uhs")) {
|
if (obj_.contains("uhs")) {
|
||||||
foundUHS = true;
|
foundUHS = true;
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// consume all 'display claims' ... whatever that means
|
// consume all 'display claims' ... whatever that means
|
||||||
for (auto iter = obj.begin(); iter != obj.end(); iter++) {
|
for (auto iter = obj_.begin(); iter != obj_.end(); iter++) {
|
||||||
QString claim;
|
QString claim;
|
||||||
if (!getString(obj.value(iter.key()), claim)) {
|
if (!getString(obj_.value(iter.key()), claim)) {
|
||||||
qWarning() << "display claim " << iter.key() << " is not a string...";
|
qWarning() << "display claim " << iter.key() << " is not a string...";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1,342 +0,0 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Yggdrasil.h"
|
|
||||||
#include "AccountData.h"
|
|
||||||
|
|
||||||
#include <QByteArray>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QNetworkReply>
|
|
||||||
#include <QObject>
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include "Application.h"
|
|
||||||
|
|
||||||
Yggdrasil::Yggdrasil(AccountData* data, QObject* parent) : AccountTask(data, parent)
|
|
||||||
{
|
|
||||||
changeState(AccountTaskState::STATE_CREATED);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Yggdrasil::sendRequest(QUrl endpoint, QByteArray content)
|
|
||||||
{
|
|
||||||
changeState(AccountTaskState::STATE_WORKING);
|
|
||||||
|
|
||||||
QNetworkRequest netRequest(endpoint);
|
|
||||||
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
|
||||||
m_netReply = APPLICATION->network()->post(netRequest, content);
|
|
||||||
connect(m_netReply, &QNetworkReply::finished, this, &Yggdrasil::processReply);
|
|
||||||
connect(m_netReply, &QNetworkReply::uploadProgress, this, &Yggdrasil::refreshTimers);
|
|
||||||
connect(m_netReply, &QNetworkReply::downloadProgress, this, &Yggdrasil::refreshTimers);
|
|
||||||
connect(m_netReply, &QNetworkReply::sslErrors, this, &Yggdrasil::sslErrors);
|
|
||||||
timeout_keeper.setSingleShot(true);
|
|
||||||
timeout_keeper.start(timeout_max);
|
|
||||||
counter.setSingleShot(false);
|
|
||||||
counter.start(time_step);
|
|
||||||
progress(0, timeout_max);
|
|
||||||
connect(&timeout_keeper, &QTimer::timeout, this, &Yggdrasil::abortByTimeout);
|
|
||||||
connect(&counter, &QTimer::timeout, this, &Yggdrasil::heartbeat);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Yggdrasil::executeTask() {}
|
|
||||||
|
|
||||||
void Yggdrasil::refresh()
|
|
||||||
{
|
|
||||||
start();
|
|
||||||
/*
|
|
||||||
* {
|
|
||||||
* "clientToken": "client identifier"
|
|
||||||
* "accessToken": "current access token to be refreshed"
|
|
||||||
* "selectedProfile": // specifying this causes errors
|
|
||||||
* {
|
|
||||||
* "id": "profile ID"
|
|
||||||
* "name": "profile name"
|
|
||||||
* }
|
|
||||||
* "requestUser": true/false // request the user structure
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
QJsonObject req;
|
|
||||||
req.insert("clientToken", m_data->clientToken());
|
|
||||||
req.insert("accessToken", m_data->accessToken());
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
auto currentProfile = m_account->currentProfile();
|
|
||||||
QJsonObject profile;
|
|
||||||
profile.insert("id", currentProfile->id());
|
|
||||||
profile.insert("name", currentProfile->name());
|
|
||||||
req.insert("selectedProfile", profile);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
req.insert("requestUser", false);
|
|
||||||
QJsonDocument doc(req);
|
|
||||||
|
|
||||||
QUrl reqUrl("https://authserver.mojang.com/refresh");
|
|
||||||
QByteArray requestData = doc.toJson();
|
|
||||||
|
|
||||||
sendRequest(reqUrl, requestData);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Yggdrasil::login(QString password)
|
|
||||||
{
|
|
||||||
start();
|
|
||||||
/*
|
|
||||||
* {
|
|
||||||
* "agent": { // optional
|
|
||||||
* "name": "Minecraft", // So far this is the only encountered value
|
|
||||||
* "version": 1 // This number might be increased
|
|
||||||
* // by the vanilla client in the future
|
|
||||||
* },
|
|
||||||
* "username": "mojang account name", // Can be an email address or player name for
|
|
||||||
* // unmigrated accounts
|
|
||||||
* "password": "mojang account password",
|
|
||||||
* "clientToken": "client identifier", // optional
|
|
||||||
* "requestUser": true/false // request the user structure
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
QJsonObject req;
|
|
||||||
|
|
||||||
{
|
|
||||||
QJsonObject agent;
|
|
||||||
// C++ makes string literals void* for some stupid reason, so we have to tell it
|
|
||||||
// QString... Thanks Obama.
|
|
||||||
agent.insert("name", QString("Minecraft"));
|
|
||||||
agent.insert("version", 1);
|
|
||||||
req.insert("agent", agent);
|
|
||||||
}
|
|
||||||
|
|
||||||
req.insert("username", m_data->userName());
|
|
||||||
req.insert("password", password);
|
|
||||||
req.insert("requestUser", false);
|
|
||||||
|
|
||||||
// If we already have a client token, give it to the server.
|
|
||||||
// Otherwise, let the server give us one.
|
|
||||||
|
|
||||||
m_data->generateClientTokenIfMissing();
|
|
||||||
req.insert("clientToken", m_data->clientToken());
|
|
||||||
|
|
||||||
QJsonDocument doc(req);
|
|
||||||
|
|
||||||
QUrl reqUrl("https://authserver.mojang.com/authenticate");
|
|
||||||
QNetworkRequest netRequest(reqUrl);
|
|
||||||
QByteArray requestData = doc.toJson();
|
|
||||||
|
|
||||||
sendRequest(reqUrl, requestData);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Yggdrasil::refreshTimers(qint64, qint64)
|
|
||||||
{
|
|
||||||
timeout_keeper.stop();
|
|
||||||
timeout_keeper.start(timeout_max);
|
|
||||||
progress(count = 0, timeout_max);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Yggdrasil::heartbeat()
|
|
||||||
{
|
|
||||||
count += time_step;
|
|
||||||
progress(count, timeout_max);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Yggdrasil::abort()
|
|
||||||
{
|
|
||||||
progress(timeout_max, timeout_max);
|
|
||||||
// TODO: actually use this in a meaningful way
|
|
||||||
m_aborted = Yggdrasil::BY_USER;
|
|
||||||
m_netReply->abort();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Yggdrasil::abortByTimeout()
|
|
||||||
{
|
|
||||||
progress(timeout_max, timeout_max);
|
|
||||||
// TODO: actually use this in a meaningful way
|
|
||||||
m_aborted = Yggdrasil::BY_TIMEOUT;
|
|
||||||
m_netReply->abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Yggdrasil::sslErrors(QList<QSslError> errors)
|
|
||||||
{
|
|
||||||
int i = 1;
|
|
||||||
for (auto error : errors) {
|
|
||||||
qCritical() << "LOGIN SSL Error #" << i << " : " << error.errorString();
|
|
||||||
auto cert = error.certificate();
|
|
||||||
qCritical() << "Certificate in question:\n" << cert.toText();
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Yggdrasil::processResponse(QJsonObject responseData)
|
|
||||||
{
|
|
||||||
// Read the response data. We need to get the client token, access token, and the selected
|
|
||||||
// profile.
|
|
||||||
qDebug() << "Processing authentication response.";
|
|
||||||
|
|
||||||
// qDebug() << responseData;
|
|
||||||
// If we already have a client token, make sure the one the server gave us matches our
|
|
||||||
// existing one.
|
|
||||||
QString clientToken = responseData.value("clientToken").toString("");
|
|
||||||
if (clientToken.isEmpty()) {
|
|
||||||
// Fail if the server gave us an empty client token
|
|
||||||
changeState(AccountTaskState::STATE_FAILED_HARD, tr("Authentication server didn't send a client token."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (m_data->clientToken().isEmpty()) {
|
|
||||||
m_data->setClientToken(clientToken);
|
|
||||||
} else if (clientToken != m_data->clientToken()) {
|
|
||||||
changeState(AccountTaskState::STATE_FAILED_HARD,
|
|
||||||
tr("Authentication server attempted to change the client token. This isn't supported."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now, we set the access token.
|
|
||||||
qDebug() << "Getting access token.";
|
|
||||||
QString accessToken = responseData.value("accessToken").toString("");
|
|
||||||
if (accessToken.isEmpty()) {
|
|
||||||
// Fail if the server didn't give us an access token.
|
|
||||||
changeState(AccountTaskState::STATE_FAILED_HARD, tr("Authentication server didn't send an access token."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Set the access token.
|
|
||||||
m_data->yggdrasilToken.token = accessToken;
|
|
||||||
m_data->yggdrasilToken.validity = Katabasis::Validity::Certain;
|
|
||||||
m_data->yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc();
|
|
||||||
|
|
||||||
// Get UUID here since we need it for later
|
|
||||||
auto profile = responseData.value("selectedProfile");
|
|
||||||
if (!profile.isObject()) {
|
|
||||||
changeState(AccountTaskState::STATE_FAILED_HARD, tr("Authentication server didn't send a selected profile."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto profileObj = profile.toObject();
|
|
||||||
for (auto i = profileObj.constBegin(); i != profileObj.constEnd(); ++i) {
|
|
||||||
if (i.key() == "name" && i.value().isString()) {
|
|
||||||
m_data->minecraftProfile.name = i->toString();
|
|
||||||
} else if (i.key() == "id" && i.value().isString()) {
|
|
||||||
m_data->minecraftProfile.id = i->toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_data->minecraftProfile.id.isEmpty()) {
|
|
||||||
changeState(AccountTaskState::STATE_FAILED_HARD, tr("Authentication server didn't send a UUID in selected profile."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We've made it through the minefield of possible errors. Return true to indicate that
|
|
||||||
// we've succeeded.
|
|
||||||
qDebug() << "Finished reading authentication response.";
|
|
||||||
changeState(AccountTaskState::STATE_SUCCEEDED);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Yggdrasil::processReply()
|
|
||||||
{
|
|
||||||
changeState(AccountTaskState::STATE_WORKING);
|
|
||||||
|
|
||||||
switch (m_netReply->error()) {
|
|
||||||
case QNetworkReply::NoError:
|
|
||||||
break;
|
|
||||||
case QNetworkReply::TimeoutError:
|
|
||||||
changeState(AccountTaskState::STATE_FAILED_SOFT, tr("Authentication operation timed out."));
|
|
||||||
return;
|
|
||||||
case QNetworkReply::OperationCanceledError:
|
|
||||||
changeState(AccountTaskState::STATE_FAILED_SOFT, tr("Authentication operation cancelled."));
|
|
||||||
return;
|
|
||||||
case QNetworkReply::SslHandshakeFailedError:
|
|
||||||
changeState(AccountTaskState::STATE_FAILED_SOFT,
|
|
||||||
tr("<b>SSL Handshake failed.</b><br/>There might be a few causes for it:<br/>"
|
|
||||||
"<ul>"
|
|
||||||
"<li>You use Windows and need to update your root certificates, please install any outstanding updates.</li>"
|
|
||||||
"<li>Some device on your network is interfering with SSL traffic. In that case, "
|
|
||||||
"you have bigger worries than Minecraft not starting.</li>"
|
|
||||||
"<li>Possibly something else. Check the log file for details</li>"
|
|
||||||
"</ul>"));
|
|
||||||
return;
|
|
||||||
// used for invalid credentials and similar errors. Fall through.
|
|
||||||
case QNetworkReply::ContentAccessDenied:
|
|
||||||
case QNetworkReply::ContentOperationNotPermittedError:
|
|
||||||
break;
|
|
||||||
case QNetworkReply::ContentGoneError: {
|
|
||||||
changeState(AccountTaskState::STATE_FAILED_GONE,
|
|
||||||
tr("The Mojang account no longer exists. It may have been migrated to a Microsoft account."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
changeState(AccountTaskState::STATE_FAILED_SOFT, tr("Authentication operation failed due to a network error: %1 (%2)")
|
|
||||||
.arg(m_netReply->errorString())
|
|
||||||
.arg(m_netReply->error()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to parse the response regardless of the response code.
|
|
||||||
// Sometimes the auth server will give more information and an error code.
|
|
||||||
QJsonParseError jsonError;
|
|
||||||
QByteArray replyData = m_netReply->readAll();
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError);
|
|
||||||
// Check the response code.
|
|
||||||
int responseCode = m_netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
||||||
|
|
||||||
if (responseCode == 200) {
|
|
||||||
// If the response code was 200, then there shouldn't be an error. Make sure
|
|
||||||
// anyways.
|
|
||||||
// Also, sometimes an empty reply indicates success. If there was no data received,
|
|
||||||
// pass an empty json object to the processResponse function.
|
|
||||||
if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0) {
|
|
||||||
processResponse(replyData.size() > 0 ? doc.object() : QJsonObject());
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
changeState(AccountTaskState::STATE_FAILED_SOFT,
|
|
||||||
tr("Failed to parse authentication server response JSON response: %1 at offset %2.")
|
|
||||||
.arg(jsonError.errorString())
|
|
||||||
.arg(jsonError.offset));
|
|
||||||
qCritical() << replyData;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the response code was not 200, then Yggdrasil may have given us information
|
|
||||||
// about the error.
|
|
||||||
// If we can parse the response, then get information from it. Otherwise just say
|
|
||||||
// there was an unknown error.
|
|
||||||
if (jsonError.error == QJsonParseError::NoError) {
|
|
||||||
// We were able to parse the server's response. Woo!
|
|
||||||
// Call processError. If a subclass has overridden it then they'll handle their
|
|
||||||
// stuff there.
|
|
||||||
qDebug() << "The request failed, but the server gave us an error message. Processing error.";
|
|
||||||
processError(doc.object());
|
|
||||||
} else {
|
|
||||||
// The server didn't say anything regarding the error. Give the user an unknown
|
|
||||||
// error.
|
|
||||||
qDebug() << "The request failed and the server gave no error message. Unknown error.";
|
|
||||||
changeState(
|
|
||||||
AccountTaskState::STATE_FAILED_SOFT,
|
|
||||||
tr("An unknown error occurred when trying to communicate with the authentication server: %1").arg(m_netReply->errorString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Yggdrasil::processError(QJsonObject responseData)
|
|
||||||
{
|
|
||||||
QJsonValue errorVal = responseData.value("error");
|
|
||||||
QJsonValue errorMessageValue = responseData.value("errorMessage");
|
|
||||||
QJsonValue causeVal = responseData.value("cause");
|
|
||||||
|
|
||||||
if (errorVal.isString() && errorMessageValue.isString()) {
|
|
||||||
m_error = std::shared_ptr<Error>(new Error{ errorVal.toString(""), errorMessageValue.toString(""), causeVal.toString("") });
|
|
||||||
changeState(AccountTaskState::STATE_FAILED_HARD, m_error->m_errorMessageVerbose);
|
|
||||||
} else {
|
|
||||||
// Error is not in standard format. Don't set m_error and return unknown error.
|
|
||||||
changeState(AccountTaskState::STATE_FAILED_HARD, tr("An unknown Yggdrasil error occurred."));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,92 +0,0 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "AccountTask.h"
|
|
||||||
|
|
||||||
#include <qsslerror.h>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QString>
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
#include "MinecraftAccount.h"
|
|
||||||
|
|
||||||
class QNetworkAccessManager;
|
|
||||||
class QNetworkReply;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Yggdrasil task is a task that performs an operation on a given mojang account.
|
|
||||||
*/
|
|
||||||
class Yggdrasil : public AccountTask {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit Yggdrasil(AccountData* data, QObject* parent = 0);
|
|
||||||
virtual ~Yggdrasil() = default;
|
|
||||||
|
|
||||||
void refresh();
|
|
||||||
void login(QString password);
|
|
||||||
|
|
||||||
struct Error {
|
|
||||||
QString m_errorMessageShort;
|
|
||||||
QString m_errorMessageVerbose;
|
|
||||||
QString m_cause;
|
|
||||||
};
|
|
||||||
std::shared_ptr<Error> m_error;
|
|
||||||
|
|
||||||
enum AbortedBy { BY_NOTHING, BY_USER, BY_TIMEOUT } m_aborted = BY_NOTHING;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void executeTask() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Processes the response received from the server.
|
|
||||||
* If an error occurred, this should emit a failed signal.
|
|
||||||
* If Yggdrasil gave an error response, it should call setError() first, and then return false.
|
|
||||||
* Otherwise, it should return true.
|
|
||||||
* Note: If the response from the server was blank, and the HTTP code was 200, this function is called with
|
|
||||||
* an empty QJsonObject.
|
|
||||||
*/
|
|
||||||
void processResponse(QJsonObject responseData);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Processes an error response received from the server.
|
|
||||||
* The default implementation will read data from Yggdrasil's standard error response format and set it as this task's Error.
|
|
||||||
* \returns a QString error message that will be passed to emitFailed.
|
|
||||||
*/
|
|
||||||
virtual void processError(QJsonObject responseData);
|
|
||||||
|
|
||||||
protected slots:
|
|
||||||
void processReply();
|
|
||||||
void refreshTimers(qint64, qint64);
|
|
||||||
void heartbeat();
|
|
||||||
void sslErrors(QList<QSslError>);
|
|
||||||
void abortByTimeout();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
virtual bool abort() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void sendRequest(QUrl endpoint, QByteArray content);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
QNetworkReply* m_netReply = nullptr;
|
|
||||||
QTimer timeout_keeper;
|
|
||||||
QTimer counter;
|
|
||||||
int count = 0; // num msec since time reset
|
|
||||||
|
|
||||||
const int timeout_max = 30000;
|
|
||||||
const int time_step = 50;
|
|
||||||
};
|
|
@ -12,7 +12,6 @@
|
|||||||
#include "minecraft/auth/AccountData.h"
|
#include "minecraft/auth/AccountData.h"
|
||||||
#include "minecraft/auth/AccountTask.h"
|
#include "minecraft/auth/AccountTask.h"
|
||||||
#include "minecraft/auth/AuthStep.h"
|
#include "minecraft/auth/AuthStep.h"
|
||||||
#include "minecraft/auth/Yggdrasil.h"
|
|
||||||
|
|
||||||
class AuthFlow : public AccountTask {
|
class AuthFlow : public AccountTask {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
#include "Mojang.h"
|
|
||||||
|
|
||||||
#include "minecraft/auth/steps/GetSkinStep.h"
|
|
||||||
#include "minecraft/auth/steps/MigrationEligibilityStep.h"
|
|
||||||
#include "minecraft/auth/steps/MinecraftProfileStepMojang.h"
|
|
||||||
#include "minecraft/auth/steps/YggdrasilStep.h"
|
|
||||||
|
|
||||||
MojangRefresh::MojangRefresh(AccountData* data, QObject* parent) : AuthFlow(data, parent)
|
|
||||||
{
|
|
||||||
m_steps.append(makeShared<YggdrasilStep>(m_data, QString()));
|
|
||||||
m_steps.append(makeShared<MinecraftProfileStepMojang>(m_data));
|
|
||||||
m_steps.append(makeShared<MigrationEligibilityStep>(m_data));
|
|
||||||
m_steps.append(makeShared<GetSkinStep>(m_data));
|
|
||||||
}
|
|
||||||
|
|
||||||
MojangLogin::MojangLogin(AccountData* data, QString password, QObject* parent) : AuthFlow(data, parent), m_password(password)
|
|
||||||
{
|
|
||||||
m_steps.append(makeShared<YggdrasilStep>(m_data, m_password));
|
|
||||||
m_steps.append(makeShared<MinecraftProfileStepMojang>(m_data));
|
|
||||||
m_steps.append(makeShared<MigrationEligibilityStep>(m_data));
|
|
||||||
m_steps.append(makeShared<GetSkinStep>(m_data));
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "AuthFlow.h"
|
|
||||||
|
|
||||||
class MojangRefresh : public AuthFlow {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit MojangRefresh(AccountData* data, QObject* parent = 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
class MojangLogin : public AuthFlow {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit MojangLogin(AccountData* data, QString password, QObject* parent = 0);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString m_password;
|
|
||||||
};
|
|
@ -36,7 +36,9 @@ void EntitlementsStep::rehydrate()
|
|||||||
// NOOP, for now. We only save bools and there's nothing to check.
|
// NOOP, for now. We only save bools and there's nothing to check.
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntitlementsStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList<QNetworkReply::RawHeaderPair> headers)
|
void EntitlementsStep::onRequestDone([[maybe_unused]] QNetworkReply::NetworkError error,
|
||||||
|
QByteArray data,
|
||||||
|
[[maybe_unused]] QList<QNetworkReply::RawHeaderPair> headers)
|
||||||
{
|
{
|
||||||
auto requestor = qobject_cast<AuthRequest*>(QObject::sender());
|
auto requestor = qobject_cast<AuthRequest*>(QObject::sender());
|
||||||
requestor->deleteLater();
|
requestor->deleteLater();
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
#include "MigrationEligibilityStep.h"
|
|
||||||
|
|
||||||
#include <QNetworkRequest>
|
|
||||||
|
|
||||||
#include "minecraft/auth/AuthRequest.h"
|
|
||||||
#include "minecraft/auth/Parsers.h"
|
|
||||||
|
|
||||||
MigrationEligibilityStep::MigrationEligibilityStep(AccountData* data) : AuthStep(data) {}
|
|
||||||
|
|
||||||
MigrationEligibilityStep::~MigrationEligibilityStep() noexcept = default;
|
|
||||||
|
|
||||||
QString MigrationEligibilityStep::describe()
|
|
||||||
{
|
|
||||||
return tr("Checking for migration eligibility.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void MigrationEligibilityStep::perform()
|
|
||||||
{
|
|
||||||
auto url = QUrl("https://api.minecraftservices.com/rollout/v1/msamigration");
|
|
||||||
QNetworkRequest request = QNetworkRequest(url);
|
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
|
||||||
request.setRawHeader("Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8());
|
|
||||||
|
|
||||||
AuthRequest* requestor = new AuthRequest(this);
|
|
||||||
connect(requestor, &AuthRequest::finished, this, &MigrationEligibilityStep::onRequestDone);
|
|
||||||
requestor->get(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MigrationEligibilityStep::rehydrate()
|
|
||||||
{
|
|
||||||
// NOOP, for now. We only save bools and there's nothing to check.
|
|
||||||
}
|
|
||||||
|
|
||||||
void MigrationEligibilityStep::onRequestDone(QNetworkReply::NetworkError error,
|
|
||||||
QByteArray data,
|
|
||||||
QList<QNetworkReply::RawHeaderPair> headers)
|
|
||||||
{
|
|
||||||
auto requestor = qobject_cast<AuthRequest*>(QObject::sender());
|
|
||||||
requestor->deleteLater();
|
|
||||||
|
|
||||||
if (error == QNetworkReply::NoError) {
|
|
||||||
Parsers::parseRolloutResponse(data, m_data->canMigrateToMSA);
|
|
||||||
}
|
|
||||||
emit finished(AccountTaskState::STATE_WORKING, tr("Got migration flags"));
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include "QObjectPtr.h"
|
|
||||||
#include "minecraft/auth/AuthStep.h"
|
|
||||||
|
|
||||||
class MigrationEligibilityStep : public AuthStep {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit MigrationEligibilityStep(AccountData* data);
|
|
||||||
virtual ~MigrationEligibilityStep() noexcept;
|
|
||||||
|
|
||||||
void perform() override;
|
|
||||||
void rehydrate() override;
|
|
||||||
|
|
||||||
QString describe() override;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
|
|
||||||
};
|
|
@ -41,10 +41,6 @@ void MinecraftProfileStep::onRequestDone(QNetworkReply::NetworkError error, QByt
|
|||||||
qCDebug(authCredentials()) << data;
|
qCDebug(authCredentials()) << data;
|
||||||
if (error == QNetworkReply::ContentNotFoundError) {
|
if (error == QNetworkReply::ContentNotFoundError) {
|
||||||
// NOTE: Succeed even if we do not have a profile. This is a valid account state.
|
// NOTE: Succeed even if we do not have a profile. This is a valid account state.
|
||||||
if (m_data->type == AccountType::Mojang) {
|
|
||||||
m_data->minecraftEntitlement.canPlayMinecraft = false;
|
|
||||||
m_data->minecraftEntitlement.ownsMinecraft = false;
|
|
||||||
}
|
|
||||||
m_data->minecraftProfile = MinecraftProfile();
|
m_data->minecraftProfile = MinecraftProfile();
|
||||||
emit finished(AccountTaskState::STATE_SUCCEEDED, tr("Account has no Minecraft profile."));
|
emit finished(AccountTaskState::STATE_SUCCEEDED, tr("Account has no Minecraft profile."));
|
||||||
return;
|
return;
|
||||||
@ -73,10 +69,5 @@ void MinecraftProfileStep::onRequestDone(QNetworkReply::NetworkError error, QByt
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_data->type == AccountType::Mojang) {
|
|
||||||
auto validProfile = m_data->minecraftProfile.validity == Katabasis::Validity::Certain;
|
|
||||||
m_data->minecraftEntitlement.canPlayMinecraft = validProfile;
|
|
||||||
m_data->minecraftEntitlement.ownsMinecraft = validProfile;
|
|
||||||
}
|
|
||||||
emit finished(AccountTaskState::STATE_WORKING, tr("Minecraft Java profile acquisition succeeded."));
|
emit finished(AccountTaskState::STATE_WORKING, tr("Minecraft Java profile acquisition succeeded."));
|
||||||
}
|
}
|
||||||
|
@ -1,87 +0,0 @@
|
|||||||
#include "MinecraftProfileStepMojang.h"
|
|
||||||
|
|
||||||
#include <QNetworkRequest>
|
|
||||||
|
|
||||||
#include "Logging.h"
|
|
||||||
#include "minecraft/auth/AuthRequest.h"
|
|
||||||
#include "minecraft/auth/Parsers.h"
|
|
||||||
#include "net/NetUtils.h"
|
|
||||||
|
|
||||||
MinecraftProfileStepMojang::MinecraftProfileStepMojang(AccountData* data) : AuthStep(data) {}
|
|
||||||
|
|
||||||
MinecraftProfileStepMojang::~MinecraftProfileStepMojang() noexcept = default;
|
|
||||||
|
|
||||||
QString MinecraftProfileStepMojang::describe()
|
|
||||||
{
|
|
||||||
return tr("Fetching the Minecraft profile.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void MinecraftProfileStepMojang::perform()
|
|
||||||
{
|
|
||||||
if (m_data->minecraftProfile.id.isEmpty()) {
|
|
||||||
emit finished(AccountTaskState::STATE_FAILED_HARD, tr("A UUID is required to get the profile."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// use session server instead of profile due to profile endpoint being locked for locked Mojang accounts
|
|
||||||
QUrl url = QUrl("https://sessionserver.mojang.com/session/minecraft/profile/" + m_data->minecraftProfile.id);
|
|
||||||
QNetworkRequest req = QNetworkRequest(url);
|
|
||||||
AuthRequest* request = new AuthRequest(this);
|
|
||||||
connect(request, &AuthRequest::finished, this, &MinecraftProfileStepMojang::onRequestDone);
|
|
||||||
request->get(req);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MinecraftProfileStepMojang::rehydrate()
|
|
||||||
{
|
|
||||||
// NOOP, for now. We only save bools and there's nothing to check.
|
|
||||||
}
|
|
||||||
|
|
||||||
void MinecraftProfileStepMojang::onRequestDone(QNetworkReply::NetworkError error,
|
|
||||||
QByteArray data,
|
|
||||||
QList<QNetworkReply::RawHeaderPair> headers)
|
|
||||||
{
|
|
||||||
auto requestor = qobject_cast<AuthRequest*>(QObject::sender());
|
|
||||||
requestor->deleteLater();
|
|
||||||
|
|
||||||
qCDebug(authCredentials()) << data;
|
|
||||||
if (error == QNetworkReply::ContentNotFoundError) {
|
|
||||||
// NOTE: Succeed even if we do not have a profile. This is a valid account state.
|
|
||||||
if (m_data->type == AccountType::Mojang) {
|
|
||||||
m_data->minecraftEntitlement.canPlayMinecraft = false;
|
|
||||||
m_data->minecraftEntitlement.ownsMinecraft = false;
|
|
||||||
}
|
|
||||||
m_data->minecraftProfile = MinecraftProfile();
|
|
||||||
emit finished(AccountTaskState::STATE_SUCCEEDED, tr("Account has no Minecraft profile."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (error != QNetworkReply::NoError) {
|
|
||||||
qWarning() << "Error getting profile:";
|
|
||||||
qWarning() << " HTTP Status: " << requestor->httpStatus_;
|
|
||||||
qWarning() << " Internal error no.: " << error;
|
|
||||||
qWarning() << " Error string: " << requestor->errorString_;
|
|
||||||
|
|
||||||
qWarning() << " Response:";
|
|
||||||
qWarning() << QString::fromUtf8(data);
|
|
||||||
|
|
||||||
if (Net::isApplicationError(error)) {
|
|
||||||
emit finished(AccountTaskState::STATE_FAILED_SOFT,
|
|
||||||
tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_));
|
|
||||||
} else {
|
|
||||||
emit finished(AccountTaskState::STATE_OFFLINE,
|
|
||||||
tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!Parsers::parseMinecraftProfileMojang(data, m_data->minecraftProfile)) {
|
|
||||||
m_data->minecraftProfile = MinecraftProfile();
|
|
||||||
emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Minecraft Java profile response could not be parsed"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_data->type == AccountType::Mojang) {
|
|
||||||
auto validProfile = m_data->minecraftProfile.validity == Katabasis::Validity::Certain;
|
|
||||||
m_data->minecraftEntitlement.canPlayMinecraft = validProfile;
|
|
||||||
m_data->minecraftEntitlement.ownsMinecraft = validProfile;
|
|
||||||
}
|
|
||||||
emit finished(AccountTaskState::STATE_WORKING, tr("Minecraft Java profile acquisition succeeded."));
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include "QObjectPtr.h"
|
|
||||||
#include "minecraft/auth/AuthStep.h"
|
|
||||||
|
|
||||||
class MinecraftProfileStepMojang : public AuthStep {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit MinecraftProfileStepMojang(AccountData* data);
|
|
||||||
virtual ~MinecraftProfileStepMojang() noexcept;
|
|
||||||
|
|
||||||
void perform() override;
|
|
||||||
void rehydrate() override;
|
|
||||||
|
|
||||||
QString describe() override;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
|
|
||||||
};
|
|
@ -38,7 +38,7 @@ void XboxUserStep::perform()
|
|||||||
QNetworkRequest request = QNetworkRequest(QUrl("https://user.auth.xboxlive.com/user/authenticate"));
|
QNetworkRequest request = QNetworkRequest(QUrl("https://user.auth.xboxlive.com/user/authenticate"));
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
request.setRawHeader("Accept", "application/json");
|
request.setRawHeader("Accept", "application/json");
|
||||||
// set contract-verison header (prevent err 400 bad-request?)
|
// set contract-version header (prevent err 400 bad-request?)
|
||||||
// https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/reference/live/rest/additional/httpstandardheaders
|
// https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/reference/live/rest/additional/httpstandardheaders
|
||||||
request.setRawHeader("x-xbl-contract-version", "1");
|
request.setRawHeader("x-xbl-contract-version", "1");
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user