Compare commits
230 Commits
release-7.
...
feature/ms
Author | SHA1 | Date | |
---|---|---|---|
55207a58a6 | |||
163c22ce4c | |||
615600ec13 | |||
17211c2100 | |||
6f113ae9d6 | |||
46ce358bb7 | |||
aa693f86f4 | |||
d6c708da42 | |||
271fb8829a | |||
15f34451b7 | |||
b3d8de2a7a | |||
2c83ef1c46 | |||
4562700653 | |||
eb7739c0ca | |||
a401a9ceb7 | |||
7dda497cc6 | |||
5e5faa7239 | |||
55ac7625ff | |||
c065d06149 | |||
76cc8a33d4 | |||
84498285c0 | |||
faeadeccca | |||
a67fb3e5ca | |||
32b9adec82 | |||
6866e5367e | |||
1c66c4ed82 | |||
0df38008a1 | |||
6daa653ab3 | |||
a37f1dd69d | |||
862acca151 | |||
c3770a9a32 | |||
2cb22ad280 | |||
5ba13297c6 | |||
24b9ed106f | |||
7c8a010378 | |||
dedc9e4edc | |||
14692eed40 | |||
43e6f05ed5 | |||
b51f1f1d41 | |||
3fe518ff2b | |||
c523765c19 | |||
18e628e873 | |||
81207c6502 | |||
87efa700ab | |||
0008b22d8b | |||
8f9bd9617f | |||
faec21d572 | |||
213f03351f | |||
00be211169 | |||
90fc720190 | |||
23b3711f96 | |||
54cb077b40 | |||
92847b9774 | |||
6e5716f097 | |||
904b128afe | |||
dffffc784e | |||
d25452a64e | |||
41d0f74750 | |||
f8f1c3cf23 | |||
ed4dce2fb6 | |||
4d49486cc9 | |||
8bebd7f042 | |||
903fae94be | |||
6d0e255ca1 | |||
1bd778d0ae | |||
4745ab64cd | |||
953a2590e2 | |||
02b628653b | |||
c75ba0f855 | |||
514080653f | |||
603ed22015 | |||
5eb71fc6a9 | |||
8a3aba1634 | |||
bb8e6ef35e | |||
529e2054ea | |||
bcf45c74a1 | |||
8b576fd2bd | |||
7fdd68d768 | |||
df6d46de9e | |||
4322222acc | |||
cce6a54701 | |||
68865353cf | |||
20ba6e5fb5 | |||
d74a23d5b2 | |||
750209b8bb | |||
741c23a72a | |||
28de461067 | |||
69c709b05a | |||
046d510134 | |||
85bbab0e92 | |||
67db141203 | |||
763b3c3236 | |||
8df5ab8aa7 | |||
83efdccfe1 | |||
f1ebec641a | |||
a4521ac0bb | |||
c5f16276d7 | |||
03361e51ef | |||
05a8232a8f | |||
2e82c1d40c | |||
0975dbc3dd | |||
8aa02320e0 | |||
470518eb3a | |||
ca659136e2 | |||
f1a6dc5332 | |||
4eaa7dc8b1 | |||
9ad29e8d85 | |||
07f3d27fb8 | |||
f2692e60f3 | |||
009623823d | |||
a32a3e25ad | |||
3e3be9ae6f | |||
3c648ff9fd | |||
f769b0b4c6 | |||
75bd626f33 | |||
f2471f0f68 | |||
d9b24f7705 | |||
b62e4c0cc7 | |||
f6f0fbbd9f | |||
c13a90540c | |||
6fd729e285 | |||
8ad9692daa | |||
d02858040e | |||
3ee0fec729 | |||
ec063470d7 | |||
b2ed2bf810 | |||
af6bf11793 | |||
d5b5f0503c | |||
555cb40efd | |||
9d22fce53f | |||
aee0999daa | |||
1bdde1f947 | |||
319ce8c19f | |||
4e66f55d84 | |||
6826f1d605 | |||
0161520b33 | |||
12cd8a7bea | |||
2d00a727f6 | |||
b123f4f948 | |||
fd9a8d1551 | |||
6bc1150ad0 | |||
45cce1d19a | |||
147366bc0a | |||
6667ff9330 | |||
5bf091149d | |||
1ff8136f98 | |||
182e19eae3 | |||
e8b0a7c6f0 | |||
d4f2059b78 | |||
b7d82354cb | |||
0b4807dc1f | |||
d33de2e427 | |||
1191c33c2b | |||
52054469cd | |||
bbd9e4de9b | |||
1e702ee40f | |||
a2d0d5a71d | |||
c343036d3b | |||
7c5047b2ac | |||
961285d6ab | |||
9ca74cd009 | |||
5fe9e30f39 | |||
4eb9083ddc | |||
b28f682ad9 | |||
51c39ec681 | |||
10436ed70c | |||
775236a1b3 | |||
b9503ff15f | |||
b4dff181f7 | |||
737fc1a2a4 | |||
bdff8591aa | |||
27c3775f99 | |||
bf0a577fb9 | |||
7d79abb607 | |||
086a7e19f0 | |||
2f37cb31d9 | |||
e5534cd1f3 | |||
4f0ec908ec | |||
94cd831e8d | |||
ffcc58cb3e | |||
8c7fd3327e | |||
ef6cbdfa2a | |||
74e7c13a17 | |||
3cfcc83ea9 | |||
ee94be624e | |||
2fe3dc5960 | |||
fd7338d3f3 | |||
ed185f047f | |||
d384d991fa | |||
9913080a82 | |||
ec157b766e | |||
469ef3e06d | |||
107b470289 | |||
8b14b946e2 | |||
f7b912fc9d | |||
9fbec3793f | |||
e4449a0ba3 | |||
42dc3ed03f | |||
f6ed2036b3 | |||
f8bf71e152 | |||
61a2355618 | |||
248920a221 | |||
b2ecb9ac09 | |||
f738d7566e | |||
1d167f8fda | |||
460e83207f | |||
75116364c6 | |||
f7931c2ee2 | |||
22bbf1bd1f | |||
10aac4fe17 | |||
42bc91463e | |||
b4fa6e120a | |||
2c744da9f7 | |||
ffaa47bf54 | |||
63c4469475 | |||
c1490cd627 | |||
1a390b6043 | |||
fac33498db | |||
31e84780a8 | |||
f3f8f3574a | |||
7bd26ce468 | |||
bcea19b957 | |||
5079ce8d64 | |||
f231a33f6e | |||
4fe497cd68 | |||
5655a33515 | |||
11f8d25d94 | |||
4fbd5abe41 | |||
d524935b67 | |||
6d5c629b43 |
287
.github/workflows/build.yml
vendored
287
.github/workflows/build.yml
vendored
@ -32,33 +32,33 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
|
|
||||||
- os: ubuntu-20.04
|
#- os: ubuntu-20.04
|
||||||
qt_ver: 5
|
# qt_ver: 5
|
||||||
|
|
||||||
- os: ubuntu-20.04
|
#- os: ubuntu-20.04
|
||||||
qt_ver: 6
|
# qt_ver: 6
|
||||||
qt_host: linux
|
# qt_host: linux
|
||||||
qt_arch: ''
|
# qt_arch: ''
|
||||||
qt_version: '6.2.4'
|
# qt_version: '6.2.4'
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
# qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_tools: ''
|
# qt_tools: ''
|
||||||
|
|
||||||
- os: windows-2022
|
#- os: windows-2022
|
||||||
name: "Windows-MinGW-w64"
|
# name: "Windows-MinGW-w64"
|
||||||
msystem: clang64
|
# msystem: clang64
|
||||||
vcvars_arch: 'amd64_x86'
|
# vcvars_arch: 'amd64_x86'
|
||||||
|
|
||||||
- os: windows-2022
|
#- os: windows-2022
|
||||||
name: "Windows-MSVC-Legacy"
|
# name: "Windows-MSVC-Legacy"
|
||||||
msystem: ''
|
# msystem: ''
|
||||||
architecture: 'win32'
|
# architecture: 'win32'
|
||||||
vcvars_arch: 'amd64_x86'
|
# vcvars_arch: 'amd64_x86'
|
||||||
qt_ver: 5
|
# qt_ver: 5
|
||||||
qt_host: windows
|
# qt_host: windows
|
||||||
qt_arch: 'win32_msvc2019'
|
# qt_arch: 'win32_msvc2019'
|
||||||
qt_version: '5.15.2'
|
# qt_version: '5.15.2'
|
||||||
qt_modules: ''
|
# qt_modules: ''
|
||||||
qt_tools: 'tools_openssl_x86'
|
# qt_tools: 'tools_openssl_x86'
|
||||||
|
|
||||||
- os: windows-2022
|
- os: windows-2022
|
||||||
name: "Windows-MSVC"
|
name: "Windows-MSVC"
|
||||||
@ -71,6 +71,7 @@ jobs:
|
|||||||
qt_version: '6.5.1'
|
qt_version: '6.5.1'
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_tools: ''
|
qt_tools: ''
|
||||||
|
prism_version: '8.0'
|
||||||
|
|
||||||
- os: windows-2022
|
- os: windows-2022
|
||||||
name: "Windows-MSVC-arm64"
|
name: "Windows-MSVC-arm64"
|
||||||
@ -83,25 +84,26 @@ jobs:
|
|||||||
qt_version: '6.5.1'
|
qt_version: '6.5.1'
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_tools: ''
|
qt_tools: ''
|
||||||
|
prism_version: '8.0'
|
||||||
|
|
||||||
- os: macos-12
|
#- os: macos-12
|
||||||
name: macOS
|
# name: macOS
|
||||||
macosx_deployment_target: 11.0
|
# macosx_deployment_target: 11.0
|
||||||
qt_ver: 6
|
# qt_ver: 6
|
||||||
qt_host: mac
|
# qt_host: mac
|
||||||
qt_arch: ''
|
# qt_arch: ''
|
||||||
qt_version: '6.5.0'
|
# qt_version: '6.5.0'
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
# qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_tools: ''
|
# qt_tools: ''
|
||||||
|
|
||||||
- os: macos-12
|
#- os: macos-12
|
||||||
name: macOS-Legacy
|
# name: macOS-Legacy
|
||||||
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 }}
|
||||||
|
|
||||||
@ -264,23 +266,23 @@ jobs:
|
|||||||
- name: Configure CMake (macOS)
|
- name: Configure CMake (macOS)
|
||||||
if: runner.os == 'macOS' && matrix.qt_ver == 6
|
if: runner.os == 'macOS' && matrix.qt_ver == 6
|
||||||
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=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja
|
||||||
|
|
||||||
- name: Configure CMake (macOS-Legacy)
|
- name: Configure CMake (macOS-Legacy)
|
||||||
if: runner.os == 'macOS' && matrix.qt_ver == 5
|
if: runner.os == 'macOS' && matrix.qt_ver == 5
|
||||||
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=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja
|
||||||
|
|
||||||
- name: Configure CMake (Windows MinGW-w64)
|
- name: Configure CMake (Windows MinGW-w64)
|
||||||
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=${{ matrix.name }} -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
|
||||||
|
|
||||||
- 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=${{ matrix.name }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON
|
||||||
# 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 +297,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=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja
|
||||||
|
|
||||||
##
|
##
|
||||||
# BUILD
|
# BUILD
|
||||||
@ -428,13 +430,36 @@ jobs:
|
|||||||
|
|
||||||
Get-ChildItem ${{ env.INSTALL_PORTABLE_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_PORTABLE_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
|
Get-ChildItem ${{ env.INSTALL_PORTABLE_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_PORTABLE_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
|
||||||
|
|
||||||
- name: Package (Windows, installer)
|
- name: Package (Windows, setup.exe)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
run: |
|
run: |
|
||||||
cd ${{ env.INSTALL_DIR }}
|
cd ${{ env.INSTALL_DIR }}
|
||||||
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
|
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
|
||||||
|
|
||||||
- name: Sign installer (Windows)
|
- name: Package (Windows MSVC, MSIX)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem == '' && matrix.architecture != 'win32'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
Set-Location ${{ env.INSTALL_DIR }}
|
||||||
|
|
||||||
|
# MSIX only allows numbers in their versions,
|
||||||
|
# so we need to use another clear identifier
|
||||||
|
$runNumber = "${{ github.run_number }}"
|
||||||
|
if ($runNumber.Length -gt 3) {
|
||||||
|
$version = $runNumber.Substring(($runNumber.Length - 3))
|
||||||
|
} else {
|
||||||
|
$version = $runNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
Copy-Item ${{ github.workspace }}\program_info\prismlauncher_*x*.png .
|
||||||
|
(Get-Content ${{ github.workspace }}\program_info\AppxManifest.xml) `
|
||||||
|
-replace "PRISM_VERSION_REPLACEME","${{ matrix.prism_version }}.0.$version" `
|
||||||
|
-replace "PRISM_ARCH_REPLACEME","${{ matrix.architecture }}" `
|
||||||
|
> .\AppxManifest.xml
|
||||||
|
|
||||||
|
makeappx.exe pack /v /h SHA256 /d . /p prismlauncher-${{ matrix.architecture }}.msix
|
||||||
|
|
||||||
|
- name: Sign installer (Windows, setup.exe)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
run: |
|
run: |
|
||||||
if (Get-Content ./codesign.pfx){
|
if (Get-Content ./codesign.pfx){
|
||||||
@ -443,6 +468,15 @@ jobs:
|
|||||||
":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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- name: Sign installer (Windows MSVC, MSIX)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem == '' && matrix.architecture != 'win32'
|
||||||
|
run: |
|
||||||
|
if (Get-Content ./codesign.pfx){
|
||||||
|
SignTool sign /fd sha256 /td sha256 /f codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com ${{ env.INSTALL_DIR }}/prismlauncher-${{ matrix.architecture }}.msix
|
||||||
|
} else {
|
||||||
|
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||||
|
}
|
||||||
|
|
||||||
- name: Package (Linux)
|
- name: Package (Linux)
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
run: |
|
run: |
|
||||||
@ -521,13 +555,20 @@ jobs:
|
|||||||
name: PrismLauncher-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: ${{ env.INSTALL_PORTABLE_DIR }}/**
|
path: ${{ env.INSTALL_PORTABLE_DIR }}/**
|
||||||
|
|
||||||
- name: Upload installer (Windows)
|
- name: Upload setup.exe (Windows)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PrismLauncher-Setup.exe
|
path: PrismLauncher-Setup.exe
|
||||||
|
|
||||||
|
- name: Upload MSIX Package (Windows MSVC)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem == '' && matrix.architecture != 'win32'
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: PrismLauncher-${{ matrix.name }}-MSIX-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
|
path: ${{ env.INSTALL_DIR }}/prismlauncher-${{ matrix.architecture }}.msix
|
||||||
|
|
||||||
- name: Upload binary tarball (Linux, Qt 5)
|
- name: Upload binary tarball (Linux, 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
|
||||||
@ -569,20 +610,144 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
ccache -s
|
ccache -s
|
||||||
|
|
||||||
flatpak:
|
appinstaller:
|
||||||
runs-on: ubuntu-latest
|
runs-on: windows-latest
|
||||||
container:
|
needs: build
|
||||||
image: bilelmoussaoui/flatpak-github-actions:kde-5.15-22.08
|
env:
|
||||||
options: --privileged
|
BUNDLE_STAGING: "bundle_staging"
|
||||||
|
APPINSTALLER_STAGING: "appinstaller_staging"
|
||||||
|
PRISM_VERSION: '8.0'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Install MSVC
|
||||||
|
uses: ilammy/msvc-dev-cmd@v1
|
||||||
|
with:
|
||||||
|
vsversion: 2022
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
if: inputs.build_type == 'Debug'
|
|
||||||
|
- name: Set short version
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
ver_short=`git rev-parse --short HEAD`
|
||||||
|
echo "VERSION=$ver_short" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set Appx version
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$runNumber = "${{ github.run_number }}"
|
||||||
|
if ($runNumber.Length -gt 3) {
|
||||||
|
$version = $runNumber.Substring(($runNumber.Length - 3))
|
||||||
|
} else {
|
||||||
|
$version = $runNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
"APPX_VERSION=${{ env.PRISM_VERSION }}.0.$version" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Create Bundle Staging Directory
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
New-Item -Type Directory -Path ${{ env.BUNDLE_STAGING }}
|
||||||
|
New-Item -Type Directory -Path ${{ env.APPINSTALLER_STAGING }}
|
||||||
|
|
||||||
|
- name: Download MSIX (x64)
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
name: PrismLauncher-Windows-MSVC-MSIX-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
- name: Build Flatpak (Linux)
|
path: ${{ env.BUNDLE_STAGING }}/
|
||||||
if: inputs.build_type == 'Debug'
|
|
||||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
- name: Download MSIX (arm64)
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
with:
|
with:
|
||||||
bundle: "Prism Launcher.flatpak"
|
name: PrismLauncher-Windows-MSVC-arm64-MSIX-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
|
path: ${{ env.BUNDLE_STAGING }}/
|
||||||
|
|
||||||
|
- name: Create MSIXBundle
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
makeappx.exe bundle /bv ${{ env.APPX_VERSION }} /d ${{ env.BUNDLE_STAGING }} /p ${{ env.BUNDLE_STAGING }}\prismlauncher-${{ env.APPX_VERSION }}.msixbundle
|
||||||
|
|
||||||
|
- name: Update AppInstaller File
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
(Get-Content ${{ github.workspace }}\program_info\prismlauncher.AppInstaller) `
|
||||||
|
-replace "PRISM_VERSION_REPLACEME","${{ env.APPX_VERSION }}" `
|
||||||
|
> ${{ env.APPINSTALLER_STAGING }}\prismlauncher-${{ env.APPX_VERSION }}.appInstaller
|
||||||
|
|
||||||
|
- name: Fetch codesign certificate
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo '${{ secrets.WINDOWS_CODESIGN_CERT }}' | base64 --decode > codesign.pfx
|
||||||
|
|
||||||
|
- name: Sign MSIXBundle
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
if (Get-Content ./codesign.pfx) {
|
||||||
|
SignTool sign /fd sha256 /td sha256 /f codesign.pfx `
|
||||||
|
/p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' `
|
||||||
|
/tr http://timestamp.digicert.com `
|
||||||
|
${{ env.BUNDLE_STAGING }}\prismlauncher-${{ env.APPX_VERSION }}.msixbundle
|
||||||
|
} else {
|
||||||
|
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Upload MSIXBundle
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: PrismLauncher-Windows-MSVC-Universal-MSIXBundle-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
|
path: ${{ env.BUNDLE_STAGING }}/prismlauncher-${{ env.APPX_VERSION }}.msixbundle
|
||||||
|
|
||||||
|
- name: Upload AppInstaller
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: PrismLauncher-Windows-MSVC-Universal-AppInstaller-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
|
path: ${{ env.APPINSTALLER_STAGING }}/prismlauncher-${{ env.APPX_VERSION }}.AppInstaller
|
||||||
|
|
||||||
|
#flatpak:
|
||||||
|
# runs-on: ubuntu-latest
|
||||||
|
# container:
|
||||||
|
# image: bilelmoussaoui/flatpak-github-actions:kde-5.15-22.08
|
||||||
|
# options: --privileged
|
||||||
|
# steps:
|
||||||
|
# - name: Checkout
|
||||||
|
# uses: actions/checkout@v3
|
||||||
|
# if: inputs.build_type == 'Debug'
|
||||||
|
# with:
|
||||||
|
# submodules: 'true'
|
||||||
|
# - name: Build Flatpak (Linux)
|
||||||
|
# if: inputs.build_type == 'Debug'
|
||||||
|
# uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
||||||
|
# with:
|
||||||
|
# bundle: "Prism Launcher.flatpak"
|
||||||
|
# manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
|
||||||
|
|
||||||
|
#nix:
|
||||||
|
# runs-on: ubuntu-latest
|
||||||
|
# strategy:
|
||||||
|
# matrix:
|
||||||
|
# package:
|
||||||
|
# - prismlauncher
|
||||||
|
# - prismlauncher-qt5
|
||||||
|
# steps:
|
||||||
|
# - name: Clone repository
|
||||||
|
# if: inputs.build_type == 'Debug'
|
||||||
|
# uses: actions/checkout@v3
|
||||||
|
# with:
|
||||||
|
# submodules: 'true'
|
||||||
|
# - name: Install nix
|
||||||
|
# if: inputs.build_type == 'Debug'
|
||||||
|
# uses: cachix/install-nix-action@v22
|
||||||
|
# with:
|
||||||
|
# install_url: https://nixos.org/nix/install
|
||||||
|
# extra_nix_config: |
|
||||||
|
# auto-optimise-store = true
|
||||||
|
# experimental-features = nix-command flakes
|
||||||
|
# - uses: cachix/cachix-action@v12
|
||||||
|
# if: inputs.build_type == 'Debug'
|
||||||
|
# with:
|
||||||
|
# name: prismlauncher
|
||||||
|
# authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||||
|
# - name: Build
|
||||||
|
# if: inputs.build_type == 'Debug'
|
||||||
|
# run: nix build .#${{ matrix.package }} --print-build-logs
|
||||||
|
13
.github/workflows/trigger_release.yml
vendored
13
.github/workflows/trigger_release.yml
vendored
@ -55,11 +55,17 @@ jobs:
|
|||||||
ARM64="$(echo -n ${d} | grep -o arm64 || true)"
|
ARM64="$(echo -n ${d} | grep -o arm64 || true)"
|
||||||
INST="$(echo -n ${d} | grep -o Setup || true)"
|
INST="$(echo -n ${d} | grep -o Setup || true)"
|
||||||
PORT="$(echo -n ${d} | grep -o Portable || true)"
|
PORT="$(echo -n ${d} | grep -o Portable || true)"
|
||||||
|
MSIX="$(echo -n ${d} | grep -o 'msix$' || true)"
|
||||||
|
MSIXBUNDLE="$(echo -n ${d} | grep -o 'msixbundle$' || true)"
|
||||||
|
APPINSTALLER="$(echo -n ${d} | grep -o AppInstaller || true)"
|
||||||
NAME="PrismLauncher-Windows-MSVC"
|
NAME="PrismLauncher-Windows-MSVC"
|
||||||
test -z "${LEGACY}" || NAME="${NAME}-Legacy"
|
test -z "${LEGACY}" || NAME="${NAME}-Legacy"
|
||||||
test -z "${ARM64}" || NAME="${NAME}-arm64"
|
test -z "${ARM64}" || NAME="${NAME}-arm64"
|
||||||
test -z "${PORT}" || NAME="${NAME}-Portable"
|
test -z "${PORT}" || NAME="${NAME}-Portable"
|
||||||
test -z "${INST}" || mv PrismLauncher-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
|
test -z "${INST}" || mv PrismLauncher-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
|
||||||
|
test -z "${MSIX}" || mv prismlauncher-*.msix ../${NAME}-${{ env.VERSION }}.msix
|
||||||
|
test -z "${MSIXBUNDLE}" || mv prismlauncher-*.msixbundle ../${NAME}-${{ env.VERSION }}.msixbundle
|
||||||
|
test -z "${APPINSTALLER}" || mv prismlauncher-*.AppInstaller ../${NAME}-${{ env.VERSION }}.AppInstaller
|
||||||
test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" *
|
test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" *
|
||||||
cd ..
|
cd ..
|
||||||
done
|
done
|
||||||
@ -99,10 +105,13 @@ jobs:
|
|||||||
PrismLauncher-Windows-MSVC-Legacy-Setup-${{ env.VERSION }}.exe
|
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-${{ env.VERSION }}.msix
|
||||||
|
PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.msixbundle
|
||||||
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MSVC-Portable-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MSVC-Portable-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MSVC-Setup-${{ env.VERSION }}.exe
|
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.msix
|
||||||
|
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.msixbundle
|
||||||
|
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.AppInstaller
|
||||||
PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
|
PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
|
||||||
PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
||||||
PrismLauncher-${{ env.VERSION }}.tar.gz
|
PrismLauncher-${{ env.VERSION }}.tar.gz
|
||||||
|
2
.github/workflows/update-flake.yml
vendored
2
.github/workflows/update-flake.yml
vendored
@ -7,12 +7,10 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update-flake:
|
update-flake:
|
||||||
if: github.repository == 'PrismLauncher/PrismLauncher'
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
2
.github/workflows/winget.yml
vendored
2
.github/workflows/winget.yml
vendored
@ -11,5 +11,5 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
identifier: PrismLauncher.PrismLauncher
|
identifier: PrismLauncher.PrismLauncher
|
||||||
version: ${{ github.event.release.tag_name }}
|
version: ${{ github.event.release.tag_name }}
|
||||||
installers-regex: 'PrismLauncher-Windows-MSVC(:?-arm64|-Legacy)?-Setup-.+\.exe$'
|
installers-regex: 'PrismLauncher-Windows-MSVC(-arm64|-Legacy-Setup)?-\d.+\.(exe|msix)?$'
|
||||||
token: ${{ secrets.WINGET_TOKEN }}
|
token: ${{ secrets.WINGET_TOKEN }}
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -56,3 +56,7 @@ flatbuild
|
|||||||
|
|
||||||
# Snap
|
# Snap
|
||||||
*.snap
|
*.snap
|
||||||
|
|
||||||
|
# msix
|
||||||
|
bundle_staging
|
||||||
|
microsoft.system.package.metadata
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -19,6 +19,3 @@
|
|||||||
[submodule "libraries/cmark"]
|
[submodule "libraries/cmark"]
|
||||||
path = libraries/cmark
|
path = libraries/cmark
|
||||||
url = https://github.com/commonmark/cmark.git
|
url = https://github.com/commonmark/cmark.git
|
||||||
[submodule "flatpak/shared-modules"]
|
|
||||||
path = flatpak/shared-modules
|
|
||||||
url = https://github.com/flathub/shared-modules.git
|
|
||||||
|
@ -85,38 +85,6 @@ 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)
|
|
||||||
|
|
||||||
# If this is a Debug build turn on address sanitiser
|
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND DEBUG_ADDRESS_SANITIZER)
|
|
||||||
message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off")
|
|
||||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
|
||||||
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
|
||||||
# using clang with clang-cl front end
|
|
||||||
message(STATUS "Address Sanitizer available on Clang MSVC frontend")
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-")
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-")
|
|
||||||
else()
|
|
||||||
# AppleClang and Clang
|
|
||||||
message(STATUS "Address Sanitizer available on Clang")
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
|
|
||||||
endif()
|
|
||||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
|
||||||
# GCC
|
|
||||||
message(STATUS "Address Sanitizer available on GCC")
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
|
|
||||||
link_libraries("asan")
|
|
||||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|
||||||
message(STATUS "Address Sanitizer available on MSVC")
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-")
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-")
|
|
||||||
else()
|
|
||||||
message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
option(ENABLE_LTO "Enable Link Time Optimization" off)
|
option(ENABLE_LTO "Enable Link Time Optimization" off)
|
||||||
|
|
||||||
if(ENABLE_LTO)
|
if(ENABLE_LTO)
|
||||||
@ -170,15 +138,15 @@ set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL th
|
|||||||
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
||||||
|
|
||||||
######## Set version numbers ########
|
######## Set version numbers ########
|
||||||
set(Launcher_VERSION_MAJOR 7)
|
set(Launcher_VERSION_MAJOR 8)
|
||||||
set(Launcher_VERSION_MINOR 2)
|
set(Launcher_VERSION_MINOR 0)
|
||||||
|
|
||||||
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
||||||
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
|
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
|
||||||
set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0")
|
set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0")
|
||||||
|
|
||||||
# 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 "" 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
|
# Channel list URL
|
||||||
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
|
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
|
||||||
@ -364,7 +332,7 @@ elseif(UNIX)
|
|||||||
|
|
||||||
set(BINARY_DEST_DIR "bin")
|
set(BINARY_DEST_DIR "bin")
|
||||||
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
|
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
|
||||||
set(JARS_DEST_DIR "share/${Launcher_Name}")
|
set(JARS_DEST_DIR "share/${Launcher_APP_BINARY_NAME}")
|
||||||
|
|
||||||
# install as bundle with no dependencies included
|
# install as bundle with no dependencies included
|
||||||
set(INSTALL_BUNDLE "nodeps")
|
set(INSTALL_BUNDLE "nodeps")
|
||||||
@ -377,7 +345,7 @@ elseif(UNIX)
|
|||||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION "${KDE_INSTALL_ICONDIR}/hicolor/scalable/apps")
|
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION "${KDE_INSTALL_ICONDIR}/hicolor/scalable/apps")
|
||||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
|
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
|
||||||
|
|
||||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "share/${Launcher_Name}")
|
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "${KDE_INSTALL_DATADIR}/${Launcher_Name}")
|
||||||
|
|
||||||
if(Launcher_ManPage)
|
if(Launcher_ManPage)
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")
|
||||||
|
16
README.md
16
README.md
@ -1,3 +1,5 @@
|
|||||||
|
test test
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<picture>
|
<picture>
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="/program_info/org.prismlauncher.PrismLauncher.logo-darkmode.svg">
|
<source media="(prefers-color-scheme: dark)" srcset="/program_info/org.prismlauncher.PrismLauncher.logo-darkmode.svg">
|
||||||
@ -42,7 +44,7 @@ Feel free to create a GitHub issue if you find a bug or want to suggest a new fe
|
|||||||
|
|
||||||
- **Our Matrix space:**
|
- **Our Matrix space:**
|
||||||
|
|
||||||
[](https://prismlauncher.org/matrix)
|
[](https://prismlauncher.org/matrix)
|
||||||
|
|
||||||
- **Our Subreddit:**
|
- **Our Subreddit:**
|
||||||
|
|
||||||
@ -50,7 +52,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 PrismLauncher 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
|
||||||
|
|
||||||
@ -82,16 +84,14 @@ Thanks to the awesome people over at [MacStadium](https://www.macstadium.com/),
|
|||||||
|
|
||||||
## Forking/Redistributing/Custom builds policy
|
## Forking/Redistributing/Custom builds policy
|
||||||
|
|
||||||
You are free to fork, redistribute and provide custom builds as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy:
|
We don't care what you do with your fork/custom build as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy:
|
||||||
|
|
||||||
- Make it clear that your fork is not Prism Launcher and is not endorsed by or affiliated with the Prism Launcher project (<https://prismlauncher.org>).
|
- Make it clear that your fork is not PrismLauncher and is not endorsed by or affiliated with the PrismLauncher project (<https://prismlauncher.org>).
|
||||||
- Go through [CMakeLists.txt](CMakeLists.txt) and change Prism Launcher's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled).
|
- Go through [CMakeLists.txt](CMakeLists.txt) and change PrismLauncher's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled).
|
||||||
|
|
||||||
If you have any questions or want any clarification on the above conditions please make an issue and ask us.
|
If you have any questions or want any clarification on the above conditions please make an issue and ask us.
|
||||||
|
|
||||||
If you are just building Prism Launcher for your distribution, please make sure to set the `Launcher_BUILD_PLATFORM` to a slug representing your distribution. Examples are `archlinux`, `fedora` and `nixpkgs`.
|
Be aware that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions:
|
||||||
|
|
||||||
Note that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions:
|
|
||||||
|
|
||||||
- [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
|
- [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
|
||||||
- [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions)
|
- [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions)
|
||||||
|
@ -65,7 +65,7 @@ Config::Config()
|
|||||||
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@";
|
||||||
|
|
||||||
if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
|
if (BUILD_PLATFORM == "macOS" && !MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
|
||||||
{
|
{
|
||||||
UPDATER_ENABLED = true;
|
UPDATER_ENABLED = true;
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ class Config {
|
|||||||
|
|
||||||
bool UPDATER_ENABLED = false;
|
bool UPDATER_ENABLED = false;
|
||||||
|
|
||||||
/// A short string identifying this build's platform or distribution.
|
/// A short string identifying this build's platform. For example, "lin64" or "win32".
|
||||||
QString BUILD_PLATFORM;
|
QString BUILD_PLATFORM;
|
||||||
|
|
||||||
/// A string containing the build timestamp
|
/// A string containing the build timestamp
|
||||||
|
24
flake.lock
generated
24
flake.lock
generated
@ -21,11 +21,11 @@
|
|||||||
"nixpkgs-lib": "nixpkgs-lib"
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1688466019,
|
"lastModified": 1688254665,
|
||||||
"narHash": "sha256-VeM2akYrBYMsb4W/MmBo1zmaMfgbL4cH3Pu8PGyIwJ0=",
|
"narHash": "sha256-8FHEgBrr7gYNiS/NzCxIO3m4hvtLRW9YY1nYo1ivm3o=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "8e8d955c22df93dbe24f19ea04f47a74adbdc5ec",
|
"rev": "267149c58a14d15f7f81b4d737308421de9d7152",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -76,11 +76,11 @@
|
|||||||
"libnbtplusplus": {
|
"libnbtplusplus": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1690036783,
|
"lastModified": 1650031308,
|
||||||
"narHash": "sha256-A5kTgICnx+Qdq3Fir/bKTfdTt/T1NQP2SC+nhN1ENug=",
|
"narHash": "sha256-TvVOjkUobYJD9itQYueELJX3wmecvEdCbJ0FinW2mL4=",
|
||||||
"owner": "PrismLauncher",
|
"owner": "PrismLauncher",
|
||||||
"repo": "libnbtplusplus",
|
"repo": "libnbtplusplus",
|
||||||
"rev": "a5e8fd52b8bf4ab5d5bcc042b2a247867589985f",
|
"rev": "2203af7eeb48c45398139b583615134efd8d407f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -91,11 +91,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1690026219,
|
"lastModified": 1688221086,
|
||||||
"narHash": "sha256-oOduRk/kzQxOBknZXTLSEYd7tk+GoKvr8wV6Ab+t4AU=",
|
"narHash": "sha256-cdW6qUL71cNWhHCpMPOJjlw0wzSRP0pVlRn2vqX/VVg=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "f465da166263bc0d4b39dfd4ca28b777c92d4b73",
|
"rev": "cd99c2b3c9f160cd004318e0697f90bbd5960825",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -138,11 +138,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1689668210,
|
"lastModified": 1688386108,
|
||||||
"narHash": "sha256-XAATwDkaUxH958yXLs1lcEOmU6pSEIkatY3qjqk8X0E=",
|
"narHash": "sha256-Vffto9QaVonzYAcPlAzd0soqWYpPpKk60dfNLSIXcFA=",
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "pre-commit-hooks.nix",
|
"repo": "pre-commit-hooks.nix",
|
||||||
"rev": "eb433bff05b285258be76513add6f6c57b441775",
|
"rev": "42587d3414d1747999a5f71e92a83cf6547b62da",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "libdecor",
|
|
||||||
"buildsystem": "meson",
|
|
||||||
"config-opts": [
|
|
||||||
"-Ddemo=false"
|
|
||||||
],
|
|
||||||
"sources": [
|
|
||||||
{
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://gitlab.freedesktop.org/libdecor/libdecor.git",
|
|
||||||
"commit": "73260393a97291c887e1074ab7f318e031be0ac6"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "patch",
|
|
||||||
"path": "patches/weird_libdecor.patch"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cleanup": [
|
|
||||||
"/include",
|
|
||||||
"/lib/pkgconfig"
|
|
||||||
]
|
|
||||||
}
|
|
@ -5,6 +5,13 @@ sdk: org.kde.Sdk
|
|||||||
sdk-extensions:
|
sdk-extensions:
|
||||||
- org.freedesktop.Sdk.Extension.openjdk17
|
- org.freedesktop.Sdk.Extension.openjdk17
|
||||||
- org.freedesktop.Sdk.Extension.openjdk8
|
- org.freedesktop.Sdk.Extension.openjdk8
|
||||||
|
add-extensions:
|
||||||
|
com.valvesoftware.Steam.Utility.gamescope:
|
||||||
|
version: stable
|
||||||
|
add-ld-path: lib
|
||||||
|
no-autodownload: true
|
||||||
|
autodelete: false
|
||||||
|
directory: utils/gamescope
|
||||||
|
|
||||||
command: prismlauncher
|
command: prismlauncher
|
||||||
finish-args:
|
finish-args:
|
||||||
@ -19,31 +26,21 @@ finish-args:
|
|||||||
# Mod drag&drop
|
# Mod drag&drop
|
||||||
- --filesystem=xdg-download:ro
|
- --filesystem=xdg-download:ro
|
||||||
|
|
||||||
cleanup:
|
|
||||||
- /lib/libGLU*
|
|
||||||
|
|
||||||
modules:
|
modules:
|
||||||
# Might be needed by some Controller mods (see https://github.com/isXander/Controlify/issues/31)
|
|
||||||
- shared-modules/libusb/libusb.json
|
|
||||||
|
|
||||||
# Needed for proper Wayland support
|
|
||||||
- libdecor.json
|
|
||||||
|
|
||||||
- name: prismlauncher
|
- name: prismlauncher
|
||||||
buildsystem: cmake-ninja
|
buildsystem: cmake-ninja
|
||||||
builddir: true
|
|
||||||
config-opts:
|
config-opts:
|
||||||
- -DLauncher_BUILD_PLATFORM=flatpak
|
- -DLauncher_BUILD_PLATFORM=flatpak
|
||||||
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
- -DCMAKE_BUILD_TYPE=Debug
|
||||||
- -DLauncher_QT_VERSION_MAJOR=5
|
- -DLauncher_QT_VERSION_MAJOR=5
|
||||||
build-options:
|
build-options:
|
||||||
env:
|
env:
|
||||||
JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17
|
JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17
|
||||||
JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac
|
JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac
|
||||||
sources:
|
sources:
|
||||||
- type: dir
|
- type: dir
|
||||||
path: ../
|
path: ../
|
||||||
|
builddir: true
|
||||||
- name: openjdk
|
- name: openjdk
|
||||||
buildsystem: simple
|
buildsystem: simple
|
||||||
build-commands:
|
build-commands:
|
||||||
@ -52,45 +49,14 @@ modules:
|
|||||||
- mv /app/jre /app/jdk/17
|
- mv /app/jre /app/jdk/17
|
||||||
- /usr/lib/sdk/openjdk8/install.sh
|
- /usr/lib/sdk/openjdk8/install.sh
|
||||||
- mv /app/jre /app/jdk/8
|
- mv /app/jre /app/jdk/8
|
||||||
cleanup:
|
cleanup: [/jre]
|
||||||
- /jre
|
|
||||||
|
|
||||||
- name: glfw
|
|
||||||
buildsystem: cmake-ninja
|
|
||||||
config-opts:
|
|
||||||
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
|
||||||
- -DBUILD_SHARED_LIBS:BOOL=ON
|
|
||||||
- -DGLFW_USE_WAYLAND=ON
|
|
||||||
sources:
|
|
||||||
- type: git
|
|
||||||
url: https://github.com/glfw/glfw.git
|
|
||||||
commit: 3fa2360720eeba1964df3c0ecf4b5df8648a8e52
|
|
||||||
- type: patch
|
|
||||||
path: patches/0003-Don-t-crash-on-calls-to-focus-or-icon.patch
|
|
||||||
- type: patch
|
|
||||||
path: patches/0005-Add-warning-about-being-an-unofficial-patch.patch
|
|
||||||
- type: patch
|
|
||||||
path: patches/0007-Platform-Prefer-Wayland-over-X11.patch
|
|
||||||
cleanup:
|
|
||||||
- /include
|
|
||||||
- /lib/cmake
|
|
||||||
- /lib/pkgconfig
|
|
||||||
|
|
||||||
- name: xrandr
|
- name: xrandr
|
||||||
buildsystem: autotools
|
buildsystem: autotools
|
||||||
sources:
|
sources:
|
||||||
- type: archive
|
- type: archive
|
||||||
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.2.tar.xz
|
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.1.tar.xz
|
||||||
sha256: c8bee4790d9058bacc4b6246456c58021db58a87ddda1a9d0139bf5f18f1f240
|
sha256: 7bc76daf9d72f8aff885efad04ce06b90488a1a169d118dea8a2b661832e8762
|
||||||
x-checker-data:
|
cleanup: [/share/man, /bin/xkeystone]
|
||||||
type: anitya
|
|
||||||
project-id: 14957
|
|
||||||
stable-only: true
|
|
||||||
url-template: https://xorg.freedesktop.org/archive/individual/app/xrandr-$version.tar.xz
|
|
||||||
cleanup:
|
|
||||||
- /share/man
|
|
||||||
- /bin/xkeystone
|
|
||||||
|
|
||||||
- name: gamemode
|
- name: gamemode
|
||||||
buildsystem: meson
|
buildsystem: meson
|
||||||
config-opts:
|
config-opts:
|
||||||
@ -101,56 +67,19 @@ modules:
|
|||||||
# post-install is running inside the build dir, we need it from the source though
|
# post-install is running inside the build dir, we need it from the source though
|
||||||
- install -Dm755 ../data/gamemoderun -t /app/bin
|
- install -Dm755 ../data/gamemoderun -t /app/bin
|
||||||
sources:
|
sources:
|
||||||
- type: archive
|
- type: git
|
||||||
archive-type: tar-gzip
|
url: https://github.com/FeralInteractive/gamemode
|
||||||
url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.7
|
tag: "1.7"
|
||||||
sha256: 57ce73ba605d1cf12f8d13725006a895182308d93eba0f69f285648449641803
|
commit: 4dc99dff76218718763a6b07fc1900fa6d1dafd9
|
||||||
x-checker-data:
|
|
||||||
type: json
|
|
||||||
url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest
|
|
||||||
version-query: .tag_name
|
|
||||||
url-query: .tarball_url
|
|
||||||
timestamp-query: .published_at
|
|
||||||
cleanup:
|
|
||||||
- /include
|
|
||||||
- /lib/pkgconfig
|
|
||||||
- /lib/libgamemodeauto.a
|
|
||||||
|
|
||||||
- name: glxinfo
|
|
||||||
buildsystem: meson
|
|
||||||
config-opts:
|
|
||||||
- --bindir=/app/mesa-demos
|
|
||||||
- -Degl=disabled
|
|
||||||
- -Dglut=disabled
|
|
||||||
- -Dosmesa=disabled
|
|
||||||
- -Dvulkan=disabled
|
|
||||||
- -Dwayland=disabled
|
|
||||||
post-install:
|
|
||||||
- mv -v /app/mesa-demos/glxinfo /app/bin
|
|
||||||
sources:
|
|
||||||
- type: archive
|
|
||||||
url: https://archive.mesa3d.org/demos/mesa-demos-9.0.0.tar.xz
|
|
||||||
sha256: 3046a3d26a7b051af7ebdd257a5f23bfeb160cad6ed952329cdff1e9f1ed496b
|
|
||||||
x-checker-data:
|
|
||||||
type: anitya
|
|
||||||
project-id: 16781
|
|
||||||
stable-only: true
|
|
||||||
url-template: https://archive.mesa3d.org/demos/mesa-demos-$version.tar.xz
|
|
||||||
cleanup:
|
|
||||||
- /include
|
|
||||||
- /mesa-demos
|
|
||||||
- /share
|
|
||||||
modules:
|
|
||||||
- shared-modules/glu/glu-9.json
|
|
||||||
|
|
||||||
- name: enhance
|
- name: enhance
|
||||||
buildsystem: simple
|
buildsystem: simple
|
||||||
build-commands:
|
build-commands:
|
||||||
|
- mkdir -p /app/utils/gamescope
|
||||||
- install -Dm755 prime-run /app/bin/prime-run
|
- install -Dm755 prime-run /app/bin/prime-run
|
||||||
- mv /app/bin/prismlauncher /app/bin/prismrun
|
- mv /app/bin/prismlauncher /app/bin/prismrun
|
||||||
- install -Dm755 prismlauncher /app/bin/prismlauncher
|
- install -Dm755 prismlauncher /app/bin/prismlauncher
|
||||||
sources:
|
sources:
|
||||||
- type: file
|
- type: file
|
||||||
path: prime-run
|
path: ../flatpak/prime-run
|
||||||
- type: file
|
- type: file
|
||||||
path: prismlauncher
|
path: ../flatpak/prismlauncher
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
diff --git a/src/wl_window.c b/src/wl_window.c
|
|
||||||
index 52d3b9eb..4ac4eb5d 100644
|
|
||||||
--- a/src/wl_window.c
|
|
||||||
+++ b/src/wl_window.c
|
|
||||||
@@ -2117,8 +2117,7 @@ void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title)
|
|
||||||
void _glfwSetWindowIconWayland(_GLFWwindow* window,
|
|
||||||
int count, const GLFWimage* images)
|
|
||||||
{
|
|
||||||
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
|
||||||
- "Wayland: The platform does not support setting the window icon");
|
|
||||||
+ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the window icon\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos)
|
|
||||||
@@ -2361,8 +2360,7 @@ void _glfwRequestWindowAttentionWayland(_GLFWwindow* window)
|
|
||||||
|
|
||||||
void _glfwFocusWindowWayland(_GLFWwindow* window)
|
|
||||||
{
|
|
||||||
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
|
||||||
- "Wayland: The platform does not support setting the input focus");
|
|
||||||
+ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the input focus\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void _glfwSetWindowMonitorWayland(_GLFWwindow* window,
|
|
@ -1,17 +0,0 @@
|
|||||||
diff --git a/src/init.c b/src/init.c
|
|
||||||
index 06dbb3f2..a7c6da86 100644
|
|
||||||
--- a/src/init.c
|
|
||||||
+++ b/src/init.c
|
|
||||||
@@ -449,6 +449,12 @@ GLFWAPI int glfwInit(void)
|
|
||||||
_glfw.initialized = GLFW_TRUE;
|
|
||||||
|
|
||||||
glfwDefaultWindowHints();
|
|
||||||
+
|
|
||||||
+ fprintf(stderr, "!!! Patched GLFW from https://github.com/Admicos/minecraft-wayland\n"
|
|
||||||
+ "!!! If any issues with the window, or some issues with rendering, occur, "
|
|
||||||
+ "first try with the built-in GLFW, and if that solves the issue, report there first.\n"
|
|
||||||
+ "!!! Use outside Minecraft is untested, and things might break.\n");
|
|
||||||
+
|
|
||||||
return GLFW_TRUE;
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
|||||||
diff --git a/src/platform.c b/src/platform.c
|
|
||||||
index c5966ae7..3e7442f9 100644
|
|
||||||
--- a/src/platform.c
|
|
||||||
+++ b/src/platform.c
|
|
||||||
@@ -49,12 +49,12 @@ static const struct
|
|
||||||
#if defined(_GLFW_COCOA)
|
|
||||||
{ GLFW_PLATFORM_COCOA, _glfwConnectCocoa },
|
|
||||||
#endif
|
|
||||||
-#if defined(_GLFW_X11)
|
|
||||||
- { GLFW_PLATFORM_X11, _glfwConnectX11 },
|
|
||||||
-#endif
|
|
||||||
#if defined(_GLFW_WAYLAND)
|
|
||||||
{ GLFW_PLATFORM_WAYLAND, _glfwConnectWayland },
|
|
||||||
#endif
|
|
||||||
+#if defined(_GLFW_X11)
|
|
||||||
+ { GLFW_PLATFORM_X11, _glfwConnectX11 },
|
|
||||||
+#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
GLFWbool _glfwSelectPlatform(int desiredID, _GLFWplatform* platform)
|
|
@ -1,40 +0,0 @@
|
|||||||
diff --git a/src/libdecor.c b/src/libdecor.c
|
|
||||||
index a9c1106..1aa38b3 100644
|
|
||||||
--- a/src/libdecor.c
|
|
||||||
+++ b/src/libdecor.c
|
|
||||||
@@ -1391,22 +1391,32 @@ calculate_priority(const struct libdecor_plugin_description *plugin_description)
|
|
||||||
static bool
|
|
||||||
check_symbol_conflicts(const struct libdecor_plugin_description *plugin_description)
|
|
||||||
{
|
|
||||||
+ bool ret = true;
|
|
||||||
char * const *symbol;
|
|
||||||
+ void* main_prog = dlopen(NULL, RTLD_LAZY);
|
|
||||||
+ if (!main_prog) {
|
|
||||||
+ fprintf(stderr, "Plugin \"%s\" couldn't check conflicting symbols: \"%s\".\n",
|
|
||||||
+ plugin_description->description, dlerror());
|
|
||||||
+ return false;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
|
|
||||||
symbol = plugin_description->conflicting_symbols;
|
|
||||||
while (*symbol) {
|
|
||||||
dlerror();
|
|
||||||
- dlsym (RTLD_DEFAULT, *symbol);
|
|
||||||
+ dlsym (main_prog, *symbol);
|
|
||||||
if (!dlerror()) {
|
|
||||||
fprintf(stderr, "Plugin \"%s\" uses conflicting symbol \"%s\".\n",
|
|
||||||
plugin_description->description, *symbol);
|
|
||||||
- return false;
|
|
||||||
+ ret = false;
|
|
||||||
+ break;
|
|
||||||
}
|
|
||||||
|
|
||||||
symbol++;
|
|
||||||
}
|
|
||||||
|
|
||||||
- return true;
|
|
||||||
+ dlclose(main_prog);
|
|
||||||
+ return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct plugin_loader *
|
|
@ -5,7 +5,7 @@ for i in {0..9}; do
|
|||||||
test -S "$XDG_RUNTIME_DIR"/discord-ipc-"$i" || ln -sf {app/com.discordapp.Discord,"$XDG_RUNTIME_DIR"}/discord-ipc-"$i";
|
test -S "$XDG_RUNTIME_DIR"/discord-ipc-"$i" || ln -sf {app/com.discordapp.Discord,"$XDG_RUNTIME_DIR"}/discord-ipc-"$i";
|
||||||
done
|
done
|
||||||
|
|
||||||
export PATH="${PATH}${PATH:+:}/usr/lib/extensions/vulkan/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin"
|
export PATH="${PATH}${PATH:+:}/app/utils/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin"
|
||||||
export VK_LAYER_PATH="/usr/lib/extensions/vulkan/share/vulkan/implicit_layer.d/"
|
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}${LD_LIBRARY_PATH:+:}/usr/lib/extensions/vulkan/MangoHud/\$LIB/"
|
||||||
|
|
||||||
exec /app/bin/prismrun "$@"
|
exec /app/bin/prismrun "$@"
|
||||||
|
Submodule flatpak/shared-modules deleted from 45094ca570
@ -1,5 +0,0 @@
|
|||||||
builds:
|
|
||||||
exclude: []
|
|
||||||
include:
|
|
||||||
- "devShells.*-linux.*"
|
|
||||||
- "packages.*-linux.*"
|
|
@ -9,7 +9,6 @@
|
|||||||
* Copyright (C) 2022 Tayou <tayou@gmx.net>
|
* Copyright (C) 2022 Tayou <tayou@gmx.net>
|
||||||
* 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
|
||||||
@ -434,11 +433,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
}
|
}
|
||||||
// seach root path
|
// seach root path
|
||||||
if(!foundLoggingRules) {
|
if(!foundLoggingRules) {
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
|
||||||
logRulesPath = FS::PathCombine(m_rootPath, "share", BuildConfig.LAUNCHER_NAME, logRulesFile);
|
|
||||||
#else
|
|
||||||
logRulesPath = FS::PathCombine(m_rootPath, logRulesFile);
|
logRulesPath = FS::PathCombine(m_rootPath, logRulesFile);
|
||||||
#endif
|
|
||||||
qDebug() << "Testing" << logRulesPath << "...";
|
qDebug() << "Testing" << logRulesPath << "...";
|
||||||
foundLoggingRules = QFile::exists(logRulesPath);
|
foundLoggingRules = QFile::exists(logRulesPath);
|
||||||
}
|
}
|
||||||
@ -476,7 +471,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT;
|
qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT;
|
||||||
qDebug() << "Version : " << BuildConfig.printableVersionString();
|
qDebug() << "Version : " << BuildConfig.printableVersionString();
|
||||||
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;
|
||||||
if (adjustedBy.size())
|
if (adjustedBy.size())
|
||||||
@ -574,7 +568,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
// Language
|
// Language
|
||||||
m_settings->registerSetting("Language", QString());
|
m_settings->registerSetting("Language", QString());
|
||||||
m_settings->registerSetting("UseSystemLocale", false);
|
|
||||||
|
|
||||||
// Console
|
// Console
|
||||||
m_settings->registerSetting("ShowConsole", false);
|
m_settings->registerSetting("ShowConsole", false);
|
||||||
@ -611,9 +604,6 @@ 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
|
|
||||||
m_settings->registerSetting("DisableQuiltBeacon", false);
|
|
||||||
|
|
||||||
// Native library workarounds
|
// Native library workarounds
|
||||||
m_settings->registerSetting("UseNativeOpenAL", false);
|
m_settings->registerSetting("UseNativeOpenAL", false);
|
||||||
m_settings->registerSetting("UseNativeGLFW", false);
|
m_settings->registerSetting("UseNativeGLFW", false);
|
||||||
@ -697,16 +687,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
m_settings->reset("PastebinCustomAPIBase");
|
m_settings->reset("PastebinCustomAPIBase");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
// meta URL
|
||||||
// Meta URL
|
m_settings->registerSetting("MetaURLOverride", "");
|
||||||
m_settings->registerSetting("MetaURLOverride", "");
|
|
||||||
|
|
||||||
QUrl metaUrl(m_settings->get("MetaURLOverride").toString());
|
|
||||||
|
|
||||||
// get rid of invalid meta urls
|
|
||||||
if (!metaUrl.isValid() || metaUrl.scheme() != "http" || metaUrl.scheme() != "https")
|
|
||||||
m_settings->reset("MetaURLOverride");
|
|
||||||
}
|
|
||||||
|
|
||||||
m_settings->registerSetting("CloseAfterLaunch", false);
|
m_settings->registerSetting("CloseAfterLaunch", false);
|
||||||
m_settings->registerSetting("QuitAfterGameStop", false);
|
m_settings->registerSetting("QuitAfterGameStop", false);
|
||||||
@ -928,7 +910,12 @@ bool Application::createSetupWizard()
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}();
|
}();
|
||||||
bool languageRequired = settings()->get("Language").toString().isEmpty();
|
bool languageRequired = [&]()
|
||||||
|
{
|
||||||
|
if (settings()->get("Language").toString().isEmpty())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}();
|
||||||
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
|
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
|
||||||
bool themeInterventionRequired = settings()->get("ApplicationTheme") == "";
|
bool themeInterventionRequired = settings()->get("ApplicationTheme") == "";
|
||||||
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
|
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
|
||||||
@ -1572,7 +1559,7 @@ QString Application::getJarPath(QString jarFile)
|
|||||||
{
|
{
|
||||||
QStringList potentialPaths = {
|
QStringList potentialPaths = {
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||||
FS::PathCombine(m_rootPath, "share", BuildConfig.LAUNCHER_NAME),
|
FS::PathCombine(m_rootPath, "share/" + BuildConfig.LAUNCHER_APP_BINARY_NAME),
|
||||||
#endif
|
#endif
|
||||||
FS::PathCombine(m_rootPath, "jars"),
|
FS::PathCombine(m_rootPath, "jars"),
|
||||||
FS::PathCombine(applicationDirPath(), "jars"),
|
FS::PathCombine(applicationDirPath(), "jars"),
|
||||||
|
@ -15,15 +15,16 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QMetaType>
|
|
||||||
#include <QString>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <QString>
|
||||||
|
#include <QMetaType>
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* An abstract base class for versions.
|
* An abstract base class for versions.
|
||||||
*/
|
*/
|
||||||
class BaseVersion {
|
class BaseVersion
|
||||||
public:
|
{
|
||||||
|
public:
|
||||||
using Ptr = std::shared_ptr<BaseVersion>;
|
using Ptr = std::shared_ptr<BaseVersion>;
|
||||||
virtual ~BaseVersion() {}
|
virtual ~BaseVersion() {}
|
||||||
/*!
|
/*!
|
||||||
@ -44,8 +45,14 @@ class BaseVersion {
|
|||||||
*/
|
*/
|
||||||
virtual QString typeString() const = 0;
|
virtual QString typeString() const = 0;
|
||||||
|
|
||||||
virtual bool operator<(BaseVersion& a) { return name() < a.name(); };
|
virtual bool operator<(BaseVersion &a)
|
||||||
virtual bool operator>(BaseVersion& a) { return name() > a.name(); };
|
{
|
||||||
|
return name() < a.name();
|
||||||
|
};
|
||||||
|
virtual bool operator>(BaseVersion &a)
|
||||||
|
{
|
||||||
|
return name() > a.name();
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(BaseVersion::Ptr)
|
Q_DECLARE_METATYPE(BaseVersion::Ptr)
|
||||||
|
@ -362,6 +362,8 @@ set(MINECRAFT_SOURCES
|
|||||||
minecraft/mod/tasks/LocalWorldSaveParseTask.cpp
|
minecraft/mod/tasks/LocalWorldSaveParseTask.cpp
|
||||||
minecraft/mod/tasks/LocalResourceParse.h
|
minecraft/mod/tasks/LocalResourceParse.h
|
||||||
minecraft/mod/tasks/LocalResourceParse.cpp
|
minecraft/mod/tasks/LocalResourceParse.cpp
|
||||||
|
minecraft/mod/tasks/GetModDependenciesTask.h
|
||||||
|
minecraft/mod/tasks/GetModDependenciesTask.cpp
|
||||||
|
|
||||||
# Assets
|
# Assets
|
||||||
minecraft/AssetsUtils.h
|
minecraft/AssetsUtils.h
|
||||||
|
@ -187,8 +187,8 @@ void LaunchController::login() {
|
|||||||
switch(m_accountToUse->accountState()) {
|
switch(m_accountToUse->accountState()) {
|
||||||
case AccountState::Offline: {
|
case AccountState::Offline: {
|
||||||
m_session->wants_online = false;
|
m_session->wants_online = false;
|
||||||
|
// NOTE: fallthrough is intentional
|
||||||
}
|
}
|
||||||
/* fallthrough */
|
|
||||||
case AccountState::Online: {
|
case AccountState::Online: {
|
||||||
if(!m_session->wants_online) {
|
if(!m_session->wants_online) {
|
||||||
// we ask the user for a player name
|
// we ask the user for a player name
|
||||||
@ -267,8 +267,8 @@ void LaunchController::login() {
|
|||||||
// This means some sort of soft error that we can fix with a refresh ... so let's refresh.
|
// This means some sort of soft error that we can fix with a refresh ... so let's refresh.
|
||||||
case AccountState::Unchecked: {
|
case AccountState::Unchecked: {
|
||||||
m_accountToUse->refresh();
|
m_accountToUse->refresh();
|
||||||
|
// NOTE: fallthrough intentional
|
||||||
}
|
}
|
||||||
/* fallthrough */
|
|
||||||
case AccountState::Working: {
|
case AccountState::Working: {
|
||||||
// refresh is in progress, we need to wait for it to finish to proceed.
|
// refresh is in progress, we need to wait for it to finish to proceed.
|
||||||
ProgressDialog progDialog(m_parentWidget);
|
ProgressDialog progDialog(m_parentWidget);
|
||||||
@ -390,10 +390,7 @@ void LaunchController::launchInstance()
|
|||||||
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher));
|
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher));
|
||||||
|
|
||||||
// Prepend Version
|
// Prepend Version
|
||||||
{
|
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), BuildConfig.LAUNCHER_DISPLAYNAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher));
|
||||||
auto versionString = QString("%1 version: %2 (%3)").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString(), BuildConfig.BUILD_PLATFORM);
|
|
||||||
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), versionString + "\n\n", MessageLevel::Launcher));
|
|
||||||
}
|
|
||||||
m_launcher->start();
|
m_launcher->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QPixmapCache>
|
#include <QPixmapCache>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
#include <QTime>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
#define GET_TYPE() \
|
#define GET_TYPE() \
|
||||||
Qt::ConnectionType type; \
|
Qt::ConnectionType type; \
|
||||||
@ -60,6 +62,8 @@ class PixmapCache final : public QObject {
|
|||||||
DEFINE_FUNC_ONE_PARAM(remove, bool, const QPixmapCache::Key&)
|
DEFINE_FUNC_ONE_PARAM(remove, bool, const QPixmapCache::Key&)
|
||||||
DEFINE_FUNC_TWO_PARAM(replace, bool, const QPixmapCache::Key&, const QPixmap&)
|
DEFINE_FUNC_TWO_PARAM(replace, bool, const QPixmapCache::Key&, const QPixmap&)
|
||||||
DEFINE_FUNC_ONE_PARAM(setCacheLimit, bool, int)
|
DEFINE_FUNC_ONE_PARAM(setCacheLimit, bool, int)
|
||||||
|
DEFINE_FUNC_NO_PARAM(markCacheMissByEviciton, bool)
|
||||||
|
DEFINE_FUNC_ONE_PARAM(setFastEvictionThreshold, bool, int)
|
||||||
|
|
||||||
// NOTE: Every function returns something non-void to simplify the macros.
|
// NOTE: Every function returns something non-void to simplify the macros.
|
||||||
private slots:
|
private slots:
|
||||||
@ -90,6 +94,43 @@ class PixmapCache final : public QObject {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark that a cache miss occurred because of a eviction if too many of these occur too fast the cache size is increased
|
||||||
|
* @return if the cache size was increased
|
||||||
|
*/
|
||||||
|
bool _markCacheMissByEviciton()
|
||||||
|
{
|
||||||
|
auto now = QTime::currentTime();
|
||||||
|
if (!m_last_cache_miss_by_eviciton.isNull()) {
|
||||||
|
auto diff = m_last_cache_miss_by_eviciton.msecsTo(now);
|
||||||
|
if (diff < 1000) { // less than a second ago
|
||||||
|
++m_consecutive_fast_evicitons;
|
||||||
|
} else {
|
||||||
|
m_consecutive_fast_evicitons = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_last_cache_miss_by_eviciton = now;
|
||||||
|
if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) {
|
||||||
|
// double the cache size
|
||||||
|
auto newSize = _cacheLimit() * 2;
|
||||||
|
qDebug() << m_consecutive_fast_evicitons << "pixmap cache misses by eviction happened too fast, doubling cache size to"
|
||||||
|
<< newSize;
|
||||||
|
_setCacheLimit(newSize);
|
||||||
|
m_consecutive_fast_evicitons = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _setFastEvictionThreshold(int threshold)
|
||||||
|
{
|
||||||
|
m_consecutive_fast_evicitons_threshold = threshold;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static PixmapCache* s_instance;
|
static PixmapCache* s_instance;
|
||||||
|
QTime m_last_cache_miss_by_eviciton;
|
||||||
|
int m_consecutive_fast_evicitons = 0;
|
||||||
|
int m_consecutive_fast_evicitons_threshold = 15;
|
||||||
};
|
};
|
||||||
|
@ -38,6 +38,8 @@ class ResourceDownloadTask : public SequentialTask {
|
|||||||
const QString& getFilename() const { return m_pack_version.fileName; }
|
const QString& getFilename() const { return m_pack_version.fileName; }
|
||||||
const QString& getCustomPath() const { return m_custom_target_folder; }
|
const QString& getCustomPath() const { return m_custom_target_folder; }
|
||||||
const QVariant& getVersionID() const { return m_pack_version.fileId; }
|
const QVariant& getVersionID() const { return m_pack_version.fileId; }
|
||||||
|
const ModPlatform::IndexedVersion& getVersion() const { return m_pack_version; }
|
||||||
|
const ModPlatform::ResourceProvider& getProvider() const { return m_pack->provider; }
|
||||||
const QString& getName() const { return m_pack->name; }
|
const QString& getName() const { return m_pack->name; }
|
||||||
ModPlatform::IndexedPack::Ptr getPack() { return m_pack; }
|
ModPlatform::IndexedPack::Ptr getPack() { return m_pack; }
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - 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
|
||||||
@ -54,9 +55,14 @@ public:
|
|||||||
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
||||||
{
|
{
|
||||||
const auto &filters = m_parent->filters();
|
const auto &filters = m_parent->filters();
|
||||||
|
const QString &search = m_parent->search();
|
||||||
|
const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
|
||||||
|
|
||||||
|
if (!search.isEmpty() && !sourceModel()->data(idx, BaseVersionList::VersionRole).toString().contains(search, Qt::CaseInsensitive))
|
||||||
|
return false;
|
||||||
|
|
||||||
for (auto it = filters.begin(); it != filters.end(); ++it)
|
for (auto it = filters.begin(); it != filters.end(); ++it)
|
||||||
{
|
{
|
||||||
auto idx = sourceModel()->index(source_row, 0, source_parent);
|
|
||||||
auto data = sourceModel()->data(idx, it.key());
|
auto data = sourceModel()->data(idx, it.key());
|
||||||
auto match = data.toString();
|
auto match = data.toString();
|
||||||
if(!it.value()->accepts(match))
|
if(!it.value()->accepts(match))
|
||||||
@ -187,24 +193,31 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
|
|||||||
}
|
}
|
||||||
case Qt::ToolTipRole:
|
case Qt::ToolTipRole:
|
||||||
{
|
{
|
||||||
if(column == Name && hasRecommended)
|
switch(column)
|
||||||
{
|
{
|
||||||
auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
|
case Name:
|
||||||
if(value.toBool())
|
|
||||||
{
|
{
|
||||||
return tr("Recommended");
|
if(hasRecommended)
|
||||||
} else if(hasLatest) {
|
|
||||||
auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
|
|
||||||
if(value.toBool())
|
|
||||||
{
|
{
|
||||||
return tr("Latest");
|
auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
|
||||||
|
if(value.toBool())
|
||||||
|
{
|
||||||
|
return tr("Recommended");
|
||||||
|
}
|
||||||
|
else if(hasLatest)
|
||||||
|
{
|
||||||
|
auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
|
||||||
|
if(value.toBool())
|
||||||
|
{
|
||||||
|
return tr("Latest");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if(index.row() == 0)
|
|
||||||
{
|
|
||||||
return tr("Latest");
|
|
||||||
}
|
}
|
||||||
} else {
|
default:
|
||||||
return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole);
|
{
|
||||||
|
return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case Qt::DecorationRole:
|
case Qt::DecorationRole:
|
||||||
@ -228,10 +241,6 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
|
|||||||
return APPLICATION->getThemedIcon("bug");
|
return APPLICATION->getThemedIcon("bug");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(index.row() == 0)
|
|
||||||
{
|
|
||||||
return APPLICATION->getThemedIcon("bug");
|
|
||||||
}
|
|
||||||
QPixmap pixmap;
|
QPixmap pixmap;
|
||||||
QPixmapCache::find("placeholder", &pixmap);
|
QPixmapCache::find("placeholder", &pixmap);
|
||||||
if(!pixmap)
|
if(!pixmap)
|
||||||
@ -420,6 +429,7 @@ QModelIndex VersionProxyModel::getVersion(const QString& version) const
|
|||||||
void VersionProxyModel::clearFilters()
|
void VersionProxyModel::clearFilters()
|
||||||
{
|
{
|
||||||
m_filters.clear();
|
m_filters.clear();
|
||||||
|
m_search.clear();
|
||||||
filterModel->filterChanged();
|
filterModel->filterChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -429,11 +439,21 @@ void VersionProxyModel::setFilter(const BaseVersionList::ModelRoles column, Filt
|
|||||||
filterModel->filterChanged();
|
filterModel->filterChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VersionProxyModel::setSearch(const QString &search) {
|
||||||
|
m_search = search;
|
||||||
|
filterModel->filterChanged();
|
||||||
|
}
|
||||||
|
|
||||||
const VersionProxyModel::FilterMap &VersionProxyModel::filters() const
|
const VersionProxyModel::FilterMap &VersionProxyModel::filters() const
|
||||||
{
|
{
|
||||||
return m_filters;
|
return m_filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QString &VersionProxyModel::search() const
|
||||||
|
{
|
||||||
|
return m_search;
|
||||||
|
}
|
||||||
|
|
||||||
void VersionProxyModel::sourceAboutToBeReset()
|
void VersionProxyModel::sourceAboutToBeReset()
|
||||||
{
|
{
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
|
@ -38,7 +38,9 @@ public:
|
|||||||
virtual void setSourceModel(QAbstractItemModel *sourceModel) override;
|
virtual void setSourceModel(QAbstractItemModel *sourceModel) override;
|
||||||
|
|
||||||
const FilterMap &filters() const;
|
const FilterMap &filters() const;
|
||||||
|
const QString &search() const;
|
||||||
void setFilter(const BaseVersionList::ModelRoles column, Filter * filter);
|
void setFilter(const BaseVersionList::ModelRoles column, Filter * filter);
|
||||||
|
void setSearch(const QString &search);
|
||||||
void clearFilters();
|
void clearFilters();
|
||||||
QModelIndex getRecommended() const;
|
QModelIndex getRecommended() const;
|
||||||
QModelIndex getVersion(const QString & version) const;
|
QModelIndex getVersion(const QString & version) const;
|
||||||
@ -59,6 +61,7 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
QList<Column> m_columns;
|
QList<Column> m_columns;
|
||||||
FilterMap m_filters;
|
FilterMap m_filters;
|
||||||
|
QString m_search;
|
||||||
BaseVersionList::RoleList roles;
|
BaseVersionList::RoleList roles;
|
||||||
VersionFilterModel * filterModel;
|
VersionFilterModel * filterModel;
|
||||||
bool hasRecommended = false;
|
bool hasRecommended = false;
|
||||||
|
@ -1,64 +1,29 @@
|
|||||||
// 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "JavaInstall.h"
|
#include "JavaInstall.h"
|
||||||
|
|
||||||
#include "BaseVersion.h"
|
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
|
|
||||||
bool JavaInstall::operator<(const JavaInstall& rhs)
|
bool JavaInstall::operator<(const JavaInstall &rhs)
|
||||||
{
|
{
|
||||||
auto archCompare = StringUtils::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive);
|
auto archCompare = StringUtils::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive);
|
||||||
if (archCompare != 0)
|
if(archCompare != 0)
|
||||||
return archCompare < 0;
|
return archCompare < 0;
|
||||||
if (id < rhs.id) {
|
if(id < rhs.id)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (id > rhs.id) {
|
if(id > rhs.id)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return StringUtils::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0;
|
return StringUtils::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JavaInstall::operator==(const JavaInstall& rhs)
|
bool JavaInstall::operator==(const JavaInstall &rhs)
|
||||||
{
|
{
|
||||||
return arch == rhs.arch && id == rhs.id && path == rhs.path;
|
return arch == rhs.arch && id == rhs.id && path == rhs.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JavaInstall::operator>(const JavaInstall& rhs)
|
bool JavaInstall::operator>(const JavaInstall &rhs)
|
||||||
{
|
{
|
||||||
return (!operator<(rhs)) && (!operator==(rhs));
|
return (!operator<(rhs)) && (!operator==(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JavaInstall::operator<(BaseVersion& a)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return operator<(dynamic_cast<JavaInstall&>(a));
|
|
||||||
} catch (const std::bad_cast& e) {
|
|
||||||
return BaseVersion::operator<(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool JavaInstall::operator>(BaseVersion& a)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return operator>(dynamic_cast<JavaInstall&>(a));
|
|
||||||
} catch (const std::bad_cast& e) {
|
|
||||||
return BaseVersion::operator>(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,40 +1,33 @@
|
|||||||
// 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "BaseVersion.h"
|
#include "BaseVersion.h"
|
||||||
#include "JavaVersion.h"
|
#include "JavaVersion.h"
|
||||||
|
|
||||||
struct JavaInstall : public BaseVersion {
|
struct JavaInstall : public BaseVersion
|
||||||
JavaInstall() {}
|
{
|
||||||
JavaInstall(QString id, QString arch, QString path) : id(id), arch(arch), path(path) {}
|
JavaInstall(){}
|
||||||
virtual QString descriptor() { return id.toString(); }
|
JavaInstall(QString id, QString arch, QString path)
|
||||||
|
: id(id), arch(arch), path(path)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual QString descriptor()
|
||||||
|
{
|
||||||
|
return id.toString();
|
||||||
|
}
|
||||||
|
|
||||||
virtual QString name() { return id.toString(); }
|
virtual QString name()
|
||||||
|
{
|
||||||
|
return id.toString();
|
||||||
|
}
|
||||||
|
|
||||||
virtual QString typeString() const { return arch; }
|
virtual QString typeString() const
|
||||||
|
{
|
||||||
|
return arch;
|
||||||
|
}
|
||||||
|
|
||||||
virtual bool operator<(BaseVersion& a) override;
|
bool operator<(const JavaInstall & rhs);
|
||||||
virtual bool operator>(BaseVersion& a) override;
|
bool operator==(const JavaInstall & rhs);
|
||||||
bool operator<(const JavaInstall& rhs);
|
bool operator>(const JavaInstall & rhs);
|
||||||
bool operator==(const JavaInstall& rhs);
|
|
||||||
bool operator>(const JavaInstall& rhs);
|
|
||||||
|
|
||||||
JavaVersion id;
|
JavaVersion id;
|
||||||
QString arch;
|
QString arch;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - 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
|
||||||
@ -98,6 +99,8 @@ QVariant JavaInstallList::data(const QModelIndex &index, int role) const
|
|||||||
auto version = std::dynamic_pointer_cast<JavaInstall>(m_vlist[index.row()]);
|
auto version = std::dynamic_pointer_cast<JavaInstall>(m_vlist[index.row()]);
|
||||||
switch (role)
|
switch (role)
|
||||||
{
|
{
|
||||||
|
case SortRole:
|
||||||
|
return -index.row();
|
||||||
case VersionPointerRole:
|
case VersionPointerRole:
|
||||||
return QVariant::fromValue(m_vlist[index.row()]);
|
return QVariant::fromValue(m_vlist[index.row()]);
|
||||||
case VersionIdRole:
|
case VersionIdRole:
|
||||||
|
@ -45,10 +45,10 @@ QVariant Index::data(const QModelIndex &index, int role) const
|
|||||||
switch (role)
|
switch (role)
|
||||||
{
|
{
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
if (index.column() == 0) {
|
switch (index.column())
|
||||||
return list->humanReadable();
|
{
|
||||||
} else {
|
case 0: return list->humanReadable();
|
||||||
break;
|
default: break;
|
||||||
}
|
}
|
||||||
case UidRole: return list->uid();
|
case UidRole: return list->uid();
|
||||||
case NameRole: return list->name();
|
case NameRole: return list->name();
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
* 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) 2022 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
|
||||||
@ -187,10 +186,6 @@ 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
|
|
||||||
auto modLoaderSettings = m_settings->registerSetting("OverrideModLoaderSettings", false);
|
|
||||||
m_settings->registerOverride(global_settings->getSetting("DisableQuiltBeacon"), modLoaderSettings);
|
|
||||||
|
|
||||||
m_settings->set("InstanceType", "OneSix");
|
m_settings->set("InstanceType", "OneSix");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,12 +391,6 @@ QStringList MinecraftInstance::extraArguments()
|
|||||||
agent->library()->getApplicableFiles(runtimeContext(), jar, temp1, temp2, temp3, getLocalLibraryPath());
|
agent->library()->getApplicableFiles(runtimeContext(), jar, temp1, temp2, temp3, getLocalLibraryPath());
|
||||||
list.append("-javaagent:"+jar[0]+(agent->argument().isEmpty() ? "" : "="+agent->argument()));
|
list.append("-javaagent:"+jar[0]+(agent->argument().isEmpty() ? "" : "="+agent->argument()));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
const auto loaders = version->getModLoaders();
|
|
||||||
if (loaders.has_value() && loaders.value() & ResourceAPI::Quilt && settings()->get("DisableQuiltBeacon").toBool())
|
|
||||||
list.append("-Dloader.disable_beacon=true");
|
|
||||||
}
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -843,7 +832,7 @@ QMap<QString, QString> MinecraftInstance::createCensorFilterFromSession(AuthSess
|
|||||||
{
|
{
|
||||||
addToFilter(sessionRef.session, tr("<SESSION ID>"));
|
addToFilter(sessionRef.session, tr("<SESSION ID>"));
|
||||||
}
|
}
|
||||||
if (sessionRef.access_token != "0") {
|
if (sessionRef.access_token != "offline") {
|
||||||
addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>"));
|
addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>"));
|
||||||
}
|
}
|
||||||
if(sessionRef.client_token.size()) {
|
if(sessionRef.client_token.size()) {
|
||||||
|
@ -65,8 +65,7 @@
|
|||||||
static const QMap<QString, ResourceAPI::ModLoaderType> modloaderMapping{
|
static const QMap<QString, ResourceAPI::ModLoaderType> modloaderMapping{
|
||||||
{"net.minecraftforge", ResourceAPI::Forge},
|
{"net.minecraftforge", ResourceAPI::Forge},
|
||||||
{"net.fabricmc.fabric-loader", ResourceAPI::Fabric},
|
{"net.fabricmc.fabric-loader", ResourceAPI::Fabric},
|
||||||
{"org.quiltmc.quilt-loader", ResourceAPI::Quilt},
|
{"org.quiltmc.quilt-loader", ResourceAPI::Quilt}
|
||||||
{"com.mumfrey.liteloader", ResourceAPI::LiteLoader}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
PackProfile::PackProfile(MinecraftInstance * instance)
|
PackProfile::PackProfile(MinecraftInstance * instance)
|
||||||
|
@ -374,10 +374,6 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
yggdrasilToken = tokenFromJSONV3(data, "ygg");
|
yggdrasilToken = tokenFromJSONV3(data, "ygg");
|
||||||
// versions before 7.2 used "offline" as the offline token
|
|
||||||
if (yggdrasilToken.token == "offline")
|
|
||||||
yggdrasilToken.token = "0";
|
|
||||||
|
|
||||||
minecraftProfile = profileFromJSONV3(data, "profile");
|
minecraftProfile = profileFromJSONV3(data, "profile");
|
||||||
if(!entitlementFromJSONV3(data, minecraftEntitlement)) {
|
if(!entitlementFromJSONV3(data, minecraftEntitlement)) {
|
||||||
if(minecraftProfile.validity != Katabasis::Validity::None) {
|
if(minecraftProfile.validity != Katabasis::Validity::None) {
|
||||||
|
@ -328,9 +328,6 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
|
|||||||
case AccountState::Gone: {
|
case AccountState::Gone: {
|
||||||
return tr("Gone", "Account status");
|
return tr("Gone", "Account status");
|
||||||
}
|
}
|
||||||
default: {
|
|
||||||
return tr("Unknown", "Account status");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,13 +354,12 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
|
|||||||
return QVariant::fromValue(account);
|
return QVariant::fromValue(account);
|
||||||
|
|
||||||
case Qt::CheckStateRole:
|
case Qt::CheckStateRole:
|
||||||
if (index.column() == ProfileNameColumn) {
|
switch (index.column())
|
||||||
return account == m_defaultAccount ? Qt::Checked : Qt::Unchecked;
|
{
|
||||||
} else {
|
case ProfileNameColumn:
|
||||||
return QVariant();
|
return account == m_defaultAccount ? Qt::Checked : Qt::Unchecked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ bool AuthSession::MakeOffline(QString offline_playername)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
session = "-";
|
session = "-";
|
||||||
access_token = "0";
|
|
||||||
player_name = offline_playername;
|
player_name = offline_playername;
|
||||||
status = PlayableOffline;
|
status = PlayableOffline;
|
||||||
return true;
|
return true;
|
||||||
|
@ -93,7 +93,7 @@ MinecraftAccountPtr MinecraftAccount::createOffline(const QString &username)
|
|||||||
{
|
{
|
||||||
auto account = makeShared<MinecraftAccount>();
|
auto account = makeShared<MinecraftAccount>();
|
||||||
account->data.type = AccountType::Offline;
|
account->data.type = AccountType::Offline;
|
||||||
account->data.yggdrasilToken.token = "0";
|
account->data.yggdrasilToken.token = "offline";
|
||||||
account->data.yggdrasilToken.validity = Katabasis::Validity::Certain;
|
account->data.yggdrasilToken.validity = Katabasis::Validity::Certain;
|
||||||
account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc();
|
account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc();
|
||||||
account->data.yggdrasilToken.extra["userName"] = username;
|
account->data.yggdrasilToken.extra["userName"] = username;
|
||||||
|
@ -273,7 +273,6 @@ void Yggdrasil::processReply() {
|
|||||||
AccountTaskState::STATE_FAILED_GONE,
|
AccountTaskState::STATE_FAILED_GONE,
|
||||||
tr("The Mojang account no longer exists. It may have been migrated to a Microsoft account.")
|
tr("The Mojang account no longer exists. It may have been migrated to a Microsoft account.")
|
||||||
);
|
);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
changeState(
|
changeState(
|
||||||
|
@ -74,7 +74,6 @@ std::pair<int, bool> DataPack::compare(const Resource& other, SortType type) con
|
|||||||
auto res = Resource::compare(other, type);
|
auto res = Resource::compare(other, type);
|
||||||
if (res.first != 0)
|
if (res.first != 0)
|
||||||
return res;
|
return res;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case SortType::PACK_FORMAT: {
|
case SortType::PACK_FORMAT: {
|
||||||
auto this_ver = packFormat();
|
auto this_ver = packFormat();
|
||||||
@ -84,7 +83,6 @@ std::pair<int, bool> DataPack::compare(const Resource& other, SortType type) con
|
|||||||
return { 1, type == SortType::PACK_FORMAT };
|
return { 1, type == SortType::PACK_FORMAT };
|
||||||
if (this_ver < other_ver)
|
if (this_ver < other_ver)
|
||||||
return { -1, type == SortType::PACK_FORMAT };
|
return { -1, type == SortType::PACK_FORMAT };
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { 0, false };
|
return { 0, false };
|
||||||
|
@ -41,9 +41,11 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
|
#include "MTPixmapCache.h"
|
||||||
#include "MetadataHandler.h"
|
#include "MetadataHandler.h"
|
||||||
#include "Version.h"
|
#include "Version.h"
|
||||||
#include "minecraft/mod/ModDetails.h"
|
#include "minecraft/mod/ModDetails.h"
|
||||||
|
#include "minecraft/mod/tasks/LocalModParseTask.h"
|
||||||
|
|
||||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||||
|
|
||||||
@ -89,7 +91,6 @@ std::pair<int, bool> Mod::compare(const Resource& other, SortType type) const
|
|||||||
auto res = Resource::compare(other, type);
|
auto res = Resource::compare(other, type);
|
||||||
if (res.first != 0)
|
if (res.first != 0)
|
||||||
return res;
|
return res;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case SortType::VERSION: {
|
case SortType::VERSION: {
|
||||||
auto this_ver = Version(version());
|
auto this_ver = Version(version());
|
||||||
@ -98,13 +99,11 @@ std::pair<int, bool> Mod::compare(const Resource& other, SortType type) const
|
|||||||
return { 1, type == SortType::VERSION };
|
return { 1, type == SortType::VERSION };
|
||||||
if (this_ver < other_ver)
|
if (this_ver < other_ver)
|
||||||
return { -1, type == SortType::VERSION };
|
return { -1, type == SortType::VERSION };
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case SortType::PROVIDER: {
|
case SortType::PROVIDER: {
|
||||||
auto compare_result = QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive);
|
auto compare_result = QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive);
|
||||||
if (compare_result != 0)
|
if (compare_result != 0)
|
||||||
return { compare_result, type == SortType::PROVIDER };
|
return { compare_result, type == SortType::PROVIDER };
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { 0, false };
|
return { 0, false };
|
||||||
@ -124,7 +123,7 @@ bool Mod::applyFilter(QRegularExpression filter) const
|
|||||||
return Resource::applyFilter(filter);
|
return Resource::applyFilter(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Mod::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_trash) -> bool
|
auto Mod::destroy(QDir& index_dir, bool preserve_metadata) -> bool
|
||||||
{
|
{
|
||||||
if (!preserve_metadata) {
|
if (!preserve_metadata) {
|
||||||
qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name());
|
qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name());
|
||||||
@ -137,7 +136,7 @@ auto Mod::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_trash) -
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Resource::destroy(attempt_trash);
|
return Resource::destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Mod::details() const -> const ModDetails&
|
auto Mod::details() const -> const ModDetails&
|
||||||
@ -204,7 +203,10 @@ void Mod::finishResolvingWithDetails(ModDetails&& details)
|
|||||||
m_local_details = std::move(details);
|
m_local_details = std::move(details);
|
||||||
if (metadata)
|
if (metadata)
|
||||||
setMetadata(std::move(metadata));
|
setMetadata(std::move(metadata));
|
||||||
};
|
if (!iconPath().isEmpty()) {
|
||||||
|
m_pack_image_cache_key.was_read_attempt = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto Mod::provider() const -> std::optional<QString>
|
auto Mod::provider() const -> std::optional<QString>
|
||||||
{
|
{
|
||||||
@ -213,6 +215,56 @@ auto Mod::provider() const -> std::optional<QString>
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Mod::licenses() const -> const QList<ModLicense>&
|
||||||
|
{
|
||||||
|
return details().licenses;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Mod::issueTracker() const -> QString
|
||||||
|
{
|
||||||
|
return details().issue_tracker;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mod::setIcon(QImage new_image) const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_data_lock);
|
||||||
|
|
||||||
|
Q_ASSERT(!new_image.isNull());
|
||||||
|
|
||||||
|
if (m_pack_image_cache_key.key.isValid())
|
||||||
|
PixmapCache::remove(m_pack_image_cache_key.key);
|
||||||
|
|
||||||
|
// scale the image to avoid flooding the pixmapcache
|
||||||
|
auto pixmap = QPixmap::fromImage(new_image.scaled({64, 64}, Qt::AspectRatioMode::KeepAspectRatioByExpanding));
|
||||||
|
|
||||||
|
m_pack_image_cache_key.key = PixmapCache::insert(pixmap);
|
||||||
|
m_pack_image_cache_key.was_ever_used = true;
|
||||||
|
m_pack_image_cache_key.was_read_attempt = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap Mod::icon(QSize size, Qt::AspectRatioMode mode) const
|
||||||
|
{
|
||||||
|
QPixmap cached_image;
|
||||||
|
if (PixmapCache::find(m_pack_image_cache_key.key, &cached_image)) {
|
||||||
|
if (size.isNull())
|
||||||
|
return cached_image;
|
||||||
|
return cached_image.scaled(size, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No valid image we can get
|
||||||
|
if ((!m_pack_image_cache_key.was_ever_used && m_pack_image_cache_key.was_read_attempt) || iconPath().isEmpty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (m_pack_image_cache_key.was_ever_used) {
|
||||||
|
qDebug() << "Mod" << name() << "Had it's icon evicted form the cache. reloading...";
|
||||||
|
PixmapCache::markCacheMissByEviciton();
|
||||||
|
}
|
||||||
|
// Image got evicted from the cache or an attempt to load it has not been made. load it and retry.
|
||||||
|
m_pack_image_cache_key.was_read_attempt = true;
|
||||||
|
ModUtils::loadIconFile(*this);
|
||||||
|
return icon(size);
|
||||||
|
}
|
||||||
|
|
||||||
bool Mod::valid() const
|
bool Mod::valid() const
|
||||||
{
|
{
|
||||||
return !m_local_details.mod_id.isEmpty();
|
return !m_local_details.mod_id.isEmpty();
|
||||||
|
@ -38,6 +38,10 @@
|
|||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
#include <QImage>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QPixmapCache>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
@ -64,6 +68,15 @@ public:
|
|||||||
auto authors() const -> QStringList;
|
auto authors() const -> QStringList;
|
||||||
auto status() const -> ModStatus;
|
auto status() const -> ModStatus;
|
||||||
auto provider() const -> std::optional<QString>;
|
auto provider() const -> std::optional<QString>;
|
||||||
|
auto licenses() const -> const QList<ModLicense>&;
|
||||||
|
auto issueTracker() const -> QString;
|
||||||
|
|
||||||
|
/** Get the intneral path to the mod's icon file*/
|
||||||
|
QString iconPath() const { return m_local_details.icon_file; };
|
||||||
|
/** Gets the icon of the mod, converted to a QPixmap for drawing, and scaled to size. */
|
||||||
|
[[nodiscard]] QPixmap icon(QSize size, Qt::AspectRatioMode mode = Qt::AspectRatioMode::IgnoreAspectRatio) const;
|
||||||
|
/** Thread-safe. */
|
||||||
|
void setIcon(QImage new_image) const;
|
||||||
|
|
||||||
auto metadata() -> std::shared_ptr<Metadata::ModStruct>;
|
auto metadata() -> std::shared_ptr<Metadata::ModStruct>;
|
||||||
auto metadata() const -> const std::shared_ptr<Metadata::ModStruct>;
|
auto metadata() const -> const std::shared_ptr<Metadata::ModStruct>;
|
||||||
@ -79,10 +92,19 @@ public:
|
|||||||
[[nodiscard]] bool applyFilter(QRegularExpression filter) const override;
|
[[nodiscard]] bool applyFilter(QRegularExpression filter) const override;
|
||||||
|
|
||||||
// Delete all the files of this mod
|
// Delete all the files of this mod
|
||||||
auto destroy(QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool;
|
auto destroy(QDir& index_dir, bool preserve_metadata = false) -> bool;
|
||||||
|
|
||||||
void finishResolvingWithDetails(ModDetails&& details);
|
void finishResolvingWithDetails(ModDetails&& details);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ModDetails m_local_details;
|
ModDetails m_local_details;
|
||||||
|
|
||||||
|
mutable QMutex m_data_lock;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
QPixmapCache::Key key;
|
||||||
|
bool was_ever_used = false;
|
||||||
|
bool was_read_attempt = false;
|
||||||
|
} mutable m_pack_image_cache_key;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
#include "minecraft/mod/MetadataHandler.h"
|
#include "minecraft/mod/MetadataHandler.h"
|
||||||
|
|
||||||
@ -49,6 +50,84 @@ enum class ModStatus {
|
|||||||
Unknown, // Default status
|
Unknown, // Default status
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ModLicense {
|
||||||
|
QString name = {};
|
||||||
|
QString id = {};
|
||||||
|
QString url = {};
|
||||||
|
QString description = {};
|
||||||
|
|
||||||
|
ModLicense() {}
|
||||||
|
|
||||||
|
ModLicense(const QString license) {
|
||||||
|
// FIXME: come up with a better license parseing.
|
||||||
|
// handle SPDX identifiers? https://spdx.org/licenses/
|
||||||
|
auto parts = license.split(' ');
|
||||||
|
QStringList notNameParts = {};
|
||||||
|
for (auto part : parts) {
|
||||||
|
auto url = QUrl(part);
|
||||||
|
if (part.startsWith("(") && part.endsWith(")"))
|
||||||
|
url = QUrl(part.mid(1, part.size() - 2));
|
||||||
|
|
||||||
|
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||||
|
this->url = url.toString();
|
||||||
|
notNameParts.append(part);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto part : notNameParts) {
|
||||||
|
parts.removeOne(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto licensePart = parts.join(' ');
|
||||||
|
this->name = licensePart;
|
||||||
|
this->description = licensePart;
|
||||||
|
|
||||||
|
if (parts.size() == 1) {
|
||||||
|
this->id = parts.first();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ModLicense(const QString name, const QString id, const QString url, const QString description) {
|
||||||
|
this->name = name;
|
||||||
|
this->id = id;
|
||||||
|
this->url = url;
|
||||||
|
this->description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModLicense(const ModLicense& other)
|
||||||
|
: name(other.name)
|
||||||
|
, id(other.id)
|
||||||
|
, url(other.url)
|
||||||
|
, description(other.description)
|
||||||
|
{}
|
||||||
|
|
||||||
|
ModLicense& operator=(const ModLicense& other)
|
||||||
|
{
|
||||||
|
this->name = other.name;
|
||||||
|
this->id = other.id;
|
||||||
|
this->url = other.url;
|
||||||
|
this->description = other.description;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModLicense& operator=(const ModLicense&& other)
|
||||||
|
{
|
||||||
|
this->name = other.name;
|
||||||
|
this->id = other.id;
|
||||||
|
this->url = other.url;
|
||||||
|
this->description = other.description;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEmpty() {
|
||||||
|
return this->name.isEmpty() && this->id.isEmpty() && this->url.isEmpty() && this->description.isEmpty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct ModDetails
|
struct ModDetails
|
||||||
{
|
{
|
||||||
/* Mod ID as defined in the ModLoader-specific metadata */
|
/* Mod ID as defined in the ModLoader-specific metadata */
|
||||||
@ -72,6 +151,15 @@ struct ModDetails
|
|||||||
/* List of the author's names */
|
/* List of the author's names */
|
||||||
QStringList authors = {};
|
QStringList authors = {};
|
||||||
|
|
||||||
|
/* Issue Tracker URL */
|
||||||
|
QString issue_tracker = {};
|
||||||
|
|
||||||
|
/* License */
|
||||||
|
QList<ModLicense> licenses = {};
|
||||||
|
|
||||||
|
/* Path of mod logo */
|
||||||
|
QString icon_file = {};
|
||||||
|
|
||||||
/* Installation status of the mod */
|
/* Installation status of the mod */
|
||||||
ModStatus status = ModStatus::Unknown;
|
ModStatus status = ModStatus::Unknown;
|
||||||
|
|
||||||
@ -89,6 +177,9 @@ struct ModDetails
|
|||||||
, homeurl(other.homeurl)
|
, homeurl(other.homeurl)
|
||||||
, description(other.description)
|
, description(other.description)
|
||||||
, authors(other.authors)
|
, authors(other.authors)
|
||||||
|
, issue_tracker(other.issue_tracker)
|
||||||
|
, licenses(other.licenses)
|
||||||
|
, icon_file(other.icon_file)
|
||||||
, status(other.status)
|
, status(other.status)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@ -101,6 +192,9 @@ struct ModDetails
|
|||||||
this->homeurl = other.homeurl;
|
this->homeurl = other.homeurl;
|
||||||
this->description = other.description;
|
this->description = other.description;
|
||||||
this->authors = other.authors;
|
this->authors = other.authors;
|
||||||
|
this->issue_tracker = other.issue_tracker;
|
||||||
|
this->licenses = other.licenses;
|
||||||
|
this->icon_file = other.icon_file;
|
||||||
this->status = other.status;
|
this->status = other.status;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
@ -115,6 +209,9 @@ struct ModDetails
|
|||||||
this->homeurl = other.homeurl;
|
this->homeurl = other.homeurl;
|
||||||
this->description = other.description;
|
this->description = other.description;
|
||||||
this->authors = other.authors;
|
this->authors = other.authors;
|
||||||
|
this->issue_tracker = other.issue_tracker;
|
||||||
|
this->licenses = other.licenses;
|
||||||
|
this->icon_file = other.icon_file;
|
||||||
this->status = other.status;
|
this->status = other.status;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#include "ModFolderModel.h"
|
#include "ModFolderModel.h"
|
||||||
|
|
||||||
#include <FileSystem.h>
|
#include <FileSystem.h>
|
||||||
|
#include <qheaderview.h>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFileSystemWatcher>
|
#include <QFileSystemWatcher>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
@ -52,12 +53,14 @@
|
|||||||
|
|
||||||
#include "minecraft/mod/tasks/LocalModParseTask.h"
|
#include "minecraft/mod/tasks/LocalModParseTask.h"
|
||||||
#include "minecraft/mod/tasks/ModFolderLoadTask.h"
|
#include "minecraft/mod/tasks/ModFolderLoadTask.h"
|
||||||
#include "modplatform/ModIndex.h"
|
|
||||||
|
|
||||||
ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir)
|
ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir)
|
||||||
: ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed)
|
: ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed)
|
||||||
{
|
{
|
||||||
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER };
|
m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider" });
|
||||||
|
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider") });
|
||||||
|
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME , SortType::VERSION, SortType::DATE, SortType::PROVIDER};
|
||||||
|
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents};
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant ModFolderModel::data(const QModelIndex &index, int role) const
|
QVariant ModFolderModel::data(const QModelIndex &index, int role) const
|
||||||
@ -118,7 +121,9 @@ QVariant ModFolderModel::data(const QModelIndex &index, int role) const
|
|||||||
case Qt::DecorationRole: {
|
case Qt::DecorationRole: {
|
||||||
if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
|
if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
|
||||||
return APPLICATION->getThemedIcon("status-yellow");
|
return APPLICATION->getThemedIcon("status-yellow");
|
||||||
|
if (column == ImageColumn) {
|
||||||
|
return at(row)->icon({32, 32}, Qt::AspectRatioMode::KeepAspectRatioByExpanding);
|
||||||
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
case Qt::CheckStateRole:
|
case Qt::CheckStateRole:
|
||||||
@ -142,15 +147,12 @@ QVariant ModFolderModel::headerData(int section, Qt::Orientation orientation, in
|
|||||||
switch (section)
|
switch (section)
|
||||||
{
|
{
|
||||||
case ActiveColumn:
|
case ActiveColumn:
|
||||||
return QString();
|
|
||||||
case NameColumn:
|
case NameColumn:
|
||||||
return tr("Name");
|
|
||||||
case VersionColumn:
|
case VersionColumn:
|
||||||
return tr("Version");
|
|
||||||
case DateColumn:
|
case DateColumn:
|
||||||
return tr("Last changed");
|
|
||||||
case ProviderColumn:
|
case ProviderColumn:
|
||||||
return tr("Provider");
|
case ImageColumn:
|
||||||
|
return columnNames().at(section);
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
@ -197,10 +199,10 @@ Task* ModFolderModel::createParseTask(Resource& resource)
|
|||||||
|
|
||||||
bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadata)
|
bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadata)
|
||||||
{
|
{
|
||||||
for(auto mod : allMods()) {
|
for(auto mod : allMods()){
|
||||||
if(mod->fileinfo().fileName() == filename) {
|
if(mod->fileinfo().fileName() == filename){
|
||||||
auto index_dir = indexDir();
|
auto index_dir = indexDir();
|
||||||
mod->destroy(index_dir, preserve_metadata, false);
|
mod->destroy(index_dir, preserve_metadata);
|
||||||
|
|
||||||
update();
|
update();
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ public:
|
|||||||
enum Columns
|
enum Columns
|
||||||
{
|
{
|
||||||
ActiveColumn = 0,
|
ActiveColumn = 0,
|
||||||
|
ImageColumn,
|
||||||
NameColumn,
|
NameColumn,
|
||||||
VersionColumn,
|
VersionColumn,
|
||||||
DateColumn,
|
DateColumn,
|
||||||
@ -77,6 +78,8 @@ public:
|
|||||||
};
|
};
|
||||||
ModFolderModel(const QString &dir, BaseInstance* instance, bool is_indexed = false, bool create_dir = true);
|
ModFolderModel(const QString &dir, BaseInstance* instance, bool is_indexed = false, bool create_dir = true);
|
||||||
|
|
||||||
|
virtual QString id() const override { return "mods"; }
|
||||||
|
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||||
|
@ -71,7 +71,6 @@ std::pair<int, bool> Resource::compare(const Resource& other, SortType type) con
|
|||||||
return { 1, type == SortType::ENABLED };
|
return { 1, type == SortType::ENABLED };
|
||||||
if (!enabled() && other.enabled())
|
if (!enabled() && other.enabled())
|
||||||
return { -1, type == SortType::ENABLED };
|
return { -1, type == SortType::ENABLED };
|
||||||
break;
|
|
||||||
case SortType::NAME: {
|
case SortType::NAME: {
|
||||||
QString this_name{ name() };
|
QString this_name{ name() };
|
||||||
QString other_name{ other.name() };
|
QString other_name{ other.name() };
|
||||||
@ -82,14 +81,12 @@ std::pair<int, bool> Resource::compare(const Resource& other, SortType type) con
|
|||||||
auto compare_result = QString::compare(this_name, other_name, Qt::CaseInsensitive);
|
auto compare_result = QString::compare(this_name, other_name, Qt::CaseInsensitive);
|
||||||
if (compare_result != 0)
|
if (compare_result != 0)
|
||||||
return { compare_result, type == SortType::NAME };
|
return { compare_result, type == SortType::NAME };
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case SortType::DATE:
|
case SortType::DATE:
|
||||||
if (dateTimeChanged() > other.dateTimeChanged())
|
if (dateTimeChanged() > other.dateTimeChanged())
|
||||||
return { 1, type == SortType::DATE };
|
return { 1, type == SortType::DATE };
|
||||||
if (dateTimeChanged() < other.dateTimeChanged())
|
if (dateTimeChanged() < other.dateTimeChanged())
|
||||||
return { -1, type == SortType::DATE };
|
return { -1, type == SortType::DATE };
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { 0, false };
|
return { 0, false };
|
||||||
@ -148,10 +145,14 @@ bool Resource::enable(EnableAction action)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resource::destroy(bool attemptTrash)
|
bool Resource::destroy()
|
||||||
{
|
{
|
||||||
m_type = ResourceType::UNKNOWN;
|
m_type = ResourceType::UNKNOWN;
|
||||||
return (attemptTrash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath());
|
|
||||||
|
if (FS::trash(m_file_info.filePath()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return FS::deletePath(m_file_info.filePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resource::isSymLinkUnder(const QString& instPath) const
|
bool Resource::isSymLinkUnder(const QString& instPath) const
|
||||||
|
@ -92,7 +92,7 @@ class Resource : public QObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete all files of this resource.
|
// Delete all files of this resource.
|
||||||
bool destroy(bool attemptTrash = true);
|
bool destroy();
|
||||||
|
|
||||||
[[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); }
|
[[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); }
|
||||||
|
|
||||||
|
@ -8,12 +8,15 @@
|
|||||||
#include <QStyle>
|
#include <QStyle>
|
||||||
#include <QThreadPool>
|
#include <QThreadPool>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <QMenu>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
|
||||||
|
#include "QVariantUtils.h"
|
||||||
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
|
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
|
||||||
|
|
||||||
|
#include "settings/Setting.h"
|
||||||
#include "tasks/Task.h"
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
ResourceFolderModel::ResourceFolderModel(QDir dir, BaseInstance* instance, QObject* parent, bool create_dir)
|
ResourceFolderModel::ResourceFolderModel(QDir dir, BaseInstance* instance, QObject* parent, bool create_dir)
|
||||||
@ -156,7 +159,7 @@ bool ResourceFolderModel::uninstallResource(QString file_name)
|
|||||||
{
|
{
|
||||||
for (auto& resource : m_resources) {
|
for (auto& resource : m_resources) {
|
||||||
if (resource->fileinfo().fileName() == file_name) {
|
if (resource->fileinfo().fileName() == file_name) {
|
||||||
auto res = resource->destroy(false);
|
auto res = resource->destroy();
|
||||||
|
|
||||||
update();
|
update();
|
||||||
|
|
||||||
@ -471,10 +474,10 @@ QVariant ResourceFolderModel::headerData(int section, Qt::Orientation orientatio
|
|||||||
switch (role) {
|
switch (role) {
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
switch (section) {
|
switch (section) {
|
||||||
|
case ACTIVE_COLUMN:
|
||||||
case NAME_COLUMN:
|
case NAME_COLUMN:
|
||||||
return tr("Name");
|
|
||||||
case DATE_COLUMN:
|
case DATE_COLUMN:
|
||||||
return tr("Last modified");
|
return columnNames().at(section);
|
||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -500,6 +503,75 @@ QVariant ResourceFolderModel::headerData(int section, Qt::Orientation orientatio
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResourceFolderModel::setupHeaderAction(QAction* act, int column)
|
||||||
|
{
|
||||||
|
Q_ASSERT(act);
|
||||||
|
|
||||||
|
act->setText(columnNames().at(column));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceFolderModel::saveHiddenColumn(int column, bool hidden)
|
||||||
|
{
|
||||||
|
auto const setting_name = QString("UI/%1_Page/HiddenColumns").arg(id());
|
||||||
|
auto setting = (m_instance->settings()->contains(setting_name)) ?
|
||||||
|
m_instance->settings()->getSetting(setting_name) : m_instance->settings()->registerSetting(setting_name);
|
||||||
|
|
||||||
|
auto hiddenColumns = setting->get().toStringList();
|
||||||
|
auto name = columnNames(false).at(column);
|
||||||
|
auto index = hiddenColumns.indexOf(name);
|
||||||
|
if (index >= 0 && !hidden) {
|
||||||
|
hiddenColumns.removeAt(index);
|
||||||
|
} else if ( index < 0 && hidden) {
|
||||||
|
hiddenColumns.append(name);
|
||||||
|
}
|
||||||
|
setting->set(hiddenColumns);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceFolderModel::loadHiddenColumns(QTreeView *tree)
|
||||||
|
{
|
||||||
|
auto const setting_name = QString("UI/%1_Page/HiddenColumns").arg(id());
|
||||||
|
auto setting = (m_instance->settings()->contains(setting_name)) ?
|
||||||
|
m_instance->settings()->getSetting(setting_name) : m_instance->settings()->registerSetting(setting_name);
|
||||||
|
|
||||||
|
auto hiddenColumns = setting->get().toStringList();
|
||||||
|
auto col_names = columnNames(false);
|
||||||
|
for (auto col_name : hiddenColumns) {
|
||||||
|
auto index = col_names.indexOf(col_name);
|
||||||
|
if (index >= 0)
|
||||||
|
tree->setColumnHidden(index, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree)
|
||||||
|
{
|
||||||
|
auto menu = new QMenu(tree);
|
||||||
|
|
||||||
|
menu->addSeparator()->setText(tr("Show / Hide Columns"));
|
||||||
|
|
||||||
|
for (int col = 0; col < columnCount(); ++col) {
|
||||||
|
auto act = new QAction(menu);
|
||||||
|
setupHeaderAction(act, col);
|
||||||
|
|
||||||
|
act->setCheckable(true);
|
||||||
|
act->setChecked(!tree->isColumnHidden(col));
|
||||||
|
|
||||||
|
connect(act, &QAction::toggled, tree, [this, col, tree](bool toggled){
|
||||||
|
tree->setColumnHidden(col, !toggled);
|
||||||
|
for(int c = 0; c < columnCount(); ++c) {
|
||||||
|
if (m_column_resize_modes.at(c) == QHeaderView::ResizeToContents)
|
||||||
|
tree->resizeColumnToContents(c);
|
||||||
|
}
|
||||||
|
saveHiddenColumn(col, !toggled);
|
||||||
|
});
|
||||||
|
|
||||||
|
menu->addAction(act);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
QSortFilterProxyModel* ResourceFolderModel::createFilterProxyModel(QObject* parent)
|
QSortFilterProxyModel* ResourceFolderModel::createFilterProxyModel(QObject* parent)
|
||||||
{
|
{
|
||||||
return new ProxyModel(parent);
|
return new ProxyModel(parent);
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QHeaderView>
|
||||||
|
#include <QAction>
|
||||||
|
#include <QTreeView>
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileSystemWatcher>
|
#include <QFileSystemWatcher>
|
||||||
@ -29,6 +32,8 @@ class ResourceFolderModel : public QAbstractListModel {
|
|||||||
ResourceFolderModel(QDir, BaseInstance* instance, QObject* parent = nullptr, bool create_dir = true);
|
ResourceFolderModel(QDir, BaseInstance* instance, QObject* parent = nullptr, bool create_dir = true);
|
||||||
~ResourceFolderModel() override;
|
~ResourceFolderModel() override;
|
||||||
|
|
||||||
|
virtual QString id() const { return "resource"; }
|
||||||
|
|
||||||
/** Starts watching the paths for changes.
|
/** Starts watching the paths for changes.
|
||||||
*
|
*
|
||||||
* Returns whether starting to watch all the paths was successful.
|
* Returns whether starting to watch all the paths was successful.
|
||||||
@ -92,6 +97,7 @@ class ResourceFolderModel : public QAbstractListModel {
|
|||||||
|
|
||||||
/* Basic columns */
|
/* Basic columns */
|
||||||
enum Columns { ACTIVE_COLUMN = 0, NAME_COLUMN, DATE_COLUMN, NUM_COLUMNS };
|
enum Columns { ACTIVE_COLUMN = 0, NAME_COLUMN, DATE_COLUMN, NUM_COLUMNS };
|
||||||
|
QStringList columnNames(bool translated = true) const { return translated ? m_column_names_translated : m_column_names; };
|
||||||
|
|
||||||
[[nodiscard]] int rowCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : static_cast<int>(size()); }
|
[[nodiscard]] int rowCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : static_cast<int>(size()); }
|
||||||
[[nodiscard]] int columnCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : NUM_COLUMNS; };
|
[[nodiscard]] int columnCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : NUM_COLUMNS; };
|
||||||
@ -110,6 +116,11 @@ class ResourceFolderModel : public QAbstractListModel {
|
|||||||
|
|
||||||
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
void setupHeaderAction(QAction* act, int column);
|
||||||
|
void saveHiddenColumn(int column, bool hidden);
|
||||||
|
void loadHiddenColumns(QTreeView* tree);
|
||||||
|
QMenu* createHeaderContextMenu(QTreeView* tree);
|
||||||
|
|
||||||
/** This creates a proxy model to filter / sort the model for a UI.
|
/** This creates a proxy model to filter / sort the model for a UI.
|
||||||
*
|
*
|
||||||
* The actual comparisons and filtering are done directly by the Resource, so to modify behavior go there instead!
|
* The actual comparisons and filtering are done directly by the Resource, so to modify behavior go there instead!
|
||||||
@ -117,6 +128,7 @@ class ResourceFolderModel : public QAbstractListModel {
|
|||||||
QSortFilterProxyModel* createFilterProxyModel(QObject* parent = nullptr);
|
QSortFilterProxyModel* createFilterProxyModel(QObject* parent = nullptr);
|
||||||
|
|
||||||
[[nodiscard]] SortType columnToSortKey(size_t column) const;
|
[[nodiscard]] SortType columnToSortKey(size_t column) const;
|
||||||
|
[[nodiscard]] QList<QHeaderView::ResizeMode> columnResizeModes() const { return m_column_resize_modes; }
|
||||||
|
|
||||||
class ProxyModel : public QSortFilterProxyModel {
|
class ProxyModel : public QSortFilterProxyModel {
|
||||||
public:
|
public:
|
||||||
@ -187,6 +199,9 @@ class ResourceFolderModel : public QAbstractListModel {
|
|||||||
// Represents the relationship between a column's index (represented by the list index), and it's sorting key.
|
// Represents the relationship between a column's index (represented by the list index), and it's sorting key.
|
||||||
// As such, the order in with they appear is very important!
|
// As such, the order in with they appear is very important!
|
||||||
QList<SortType> m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE };
|
QList<SortType> m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE };
|
||||||
|
QStringList m_column_names = {"Enable", "Name", "Last Modified"};
|
||||||
|
QStringList m_column_names_translated = {tr("Enable"), tr("Name"), tr("Last Modified")};
|
||||||
|
QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Stretch, QHeaderView::ResizeToContents };
|
||||||
|
|
||||||
bool m_can_interact = true;
|
bool m_can_interact = true;
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ void ResourcePack::setDescription(QString new_description)
|
|||||||
m_description = new_description;
|
m_description = new_description;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourcePack::setImage(QImage new_image)
|
void ResourcePack::setImage(QImage new_image) const
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_data_lock);
|
QMutexLocker locker(&m_data_lock);
|
||||||
|
|
||||||
@ -49,7 +49,10 @@ void ResourcePack::setImage(QImage new_image)
|
|||||||
if (m_pack_image_cache_key.key.isValid())
|
if (m_pack_image_cache_key.key.isValid())
|
||||||
PixmapCache::instance().remove(m_pack_image_cache_key.key);
|
PixmapCache::instance().remove(m_pack_image_cache_key.key);
|
||||||
|
|
||||||
m_pack_image_cache_key.key = PixmapCache::instance().insert(QPixmap::fromImage(new_image));
|
// scale the image to avoid flooding the pixmapcache
|
||||||
|
auto pixmap = QPixmap::fromImage(new_image.scaled({64, 64}, Qt::AspectRatioMode::KeepAspectRatioByExpanding));
|
||||||
|
|
||||||
|
m_pack_image_cache_key.key = PixmapCache::instance().insert(pixmap);
|
||||||
m_pack_image_cache_key.was_ever_used = true;
|
m_pack_image_cache_key.was_ever_used = true;
|
||||||
|
|
||||||
// This can happen if the pixmap is too big to fit in the cache :c
|
// This can happen if the pixmap is too big to fit in the cache :c
|
||||||
@ -59,21 +62,25 @@ void ResourcePack::setImage(QImage new_image)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QPixmap ResourcePack::image(QSize size)
|
QPixmap ResourcePack::image(QSize size, Qt::AspectRatioMode mode) const
|
||||||
{
|
{
|
||||||
QPixmap cached_image;
|
QPixmap cached_image;
|
||||||
if (PixmapCache::instance().find(m_pack_image_cache_key.key, &cached_image)) {
|
if (PixmapCache::instance().find(m_pack_image_cache_key.key, &cached_image)) {
|
||||||
if (size.isNull())
|
if (size.isNull())
|
||||||
return cached_image;
|
return cached_image;
|
||||||
return cached_image.scaled(size);
|
return cached_image.scaled(size, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// No valid image we can get
|
// No valid image we can get
|
||||||
if (!m_pack_image_cache_key.was_ever_used)
|
if (!m_pack_image_cache_key.was_ever_used) {
|
||||||
return {};
|
return {};
|
||||||
|
} else {
|
||||||
|
qDebug() << "Resource Pack" << name() << "Had it's image evicted from the cache. reloading...";
|
||||||
|
PixmapCache::markCacheMissByEviciton();
|
||||||
|
}
|
||||||
|
|
||||||
// Imaged got evicted from the cache. Re-process it and retry.
|
// Imaged got evicted from the cache. Re-process it and retry.
|
||||||
ResourcePackUtils::process(*this);
|
ResourcePackUtils::processPackPNG(*this);
|
||||||
return image(size);
|
return image(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +102,6 @@ std::pair<int, bool> ResourcePack::compare(const Resource& other, SortType type)
|
|||||||
auto res = Resource::compare(other, type);
|
auto res = Resource::compare(other, type);
|
||||||
if (res.first != 0)
|
if (res.first != 0)
|
||||||
return res;
|
return res;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case SortType::PACK_FORMAT: {
|
case SortType::PACK_FORMAT: {
|
||||||
auto this_ver = packFormat();
|
auto this_ver = packFormat();
|
||||||
@ -105,7 +111,6 @@ std::pair<int, bool> ResourcePack::compare(const Resource& other, SortType type)
|
|||||||
return { 1, type == SortType::PACK_FORMAT };
|
return { 1, type == SortType::PACK_FORMAT };
|
||||||
if (this_ver < other_ver)
|
if (this_ver < other_ver)
|
||||||
return { -1, type == SortType::PACK_FORMAT };
|
return { -1, type == SortType::PACK_FORMAT };
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { 0, false };
|
return { 0, false };
|
||||||
|
@ -31,7 +31,7 @@ class ResourcePack : public Resource {
|
|||||||
[[nodiscard]] QString description() const { return m_description; }
|
[[nodiscard]] QString description() const { return m_description; }
|
||||||
|
|
||||||
/** Gets the image of the resource pack, converted to a QPixmap for drawing, and scaled to size. */
|
/** Gets the image of the resource pack, converted to a QPixmap for drawing, and scaled to size. */
|
||||||
[[nodiscard]] QPixmap image(QSize size);
|
[[nodiscard]] QPixmap image(QSize size, Qt::AspectRatioMode mode = Qt::AspectRatioMode::IgnoreAspectRatio) const;
|
||||||
|
|
||||||
/** Thread-safe. */
|
/** Thread-safe. */
|
||||||
void setPackFormat(int new_format_id);
|
void setPackFormat(int new_format_id);
|
||||||
@ -40,7 +40,7 @@ class ResourcePack : public Resource {
|
|||||||
void setDescription(QString new_description);
|
void setDescription(QString new_description);
|
||||||
|
|
||||||
/** Thread-safe. */
|
/** Thread-safe. */
|
||||||
void setImage(QImage new_image);
|
void setImage(QImage new_image) const;
|
||||||
|
|
||||||
bool valid() const override;
|
bool valid() const override;
|
||||||
|
|
||||||
@ -67,5 +67,5 @@ class ResourcePack : public Resource {
|
|||||||
struct {
|
struct {
|
||||||
QPixmapCache::Key key;
|
QPixmapCache::Key key;
|
||||||
bool was_ever_used = false;
|
bool was_ever_used = false;
|
||||||
} m_pack_image_cache_key;
|
} mutable m_pack_image_cache_key;
|
||||||
};
|
};
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ResourcePackFolderModel.h"
|
#include "ResourcePackFolderModel.h"
|
||||||
|
#include <qnamespace.h>
|
||||||
|
#include <qsize.h>
|
||||||
|
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QStyle>
|
#include <QStyle>
|
||||||
@ -48,7 +50,11 @@
|
|||||||
ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstance* instance)
|
ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstance* instance)
|
||||||
: ResourceFolderModel(QDir(dir), instance)
|
: ResourceFolderModel(QDir(dir), instance)
|
||||||
{
|
{
|
||||||
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE };
|
m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified" });
|
||||||
|
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified") });
|
||||||
|
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE};
|
||||||
|
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents };
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
||||||
@ -84,9 +90,11 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
case Qt::DecorationRole: {
|
case Qt::DecorationRole: {
|
||||||
if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
|
if (column == NameColumn && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
|
||||||
return APPLICATION->getThemedIcon("status-yellow");
|
return APPLICATION->getThemedIcon("status-yellow");
|
||||||
|
if (column == ImageColumn) {
|
||||||
|
return at(row)->image({32, 32}, Qt::AspectRatioMode::KeepAspectRatioByExpanding);
|
||||||
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
case Qt::ToolTipRole: {
|
case Qt::ToolTipRole: {
|
||||||
@ -94,7 +102,7 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
|||||||
//: The string being explained by this is in the format: ID (Lower version - Upper version)
|
//: The string being explained by this is in the format: ID (Lower version - Upper version)
|
||||||
return tr("The resource pack format ID, as well as the Minecraft versions it was designed for.");
|
return tr("The resource pack format ID, as well as the Minecraft versions it was designed for.");
|
||||||
}
|
}
|
||||||
if (column == NAME_COLUMN) {
|
if (column == NameColumn) {
|
||||||
if (at(row)->isSymLinkUnder(instDirPath())) {
|
if (at(row)->isSymLinkUnder(instDirPath())) {
|
||||||
return m_resources[row]->internal_id() +
|
return m_resources[row]->internal_id() +
|
||||||
tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
|
tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
|
||||||
@ -126,13 +134,11 @@ QVariant ResourcePackFolderModel::headerData(int section, Qt::Orientation orient
|
|||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
switch (section) {
|
switch (section) {
|
||||||
case ActiveColumn:
|
case ActiveColumn:
|
||||||
return QString();
|
|
||||||
case NameColumn:
|
case NameColumn:
|
||||||
return tr("Name");
|
|
||||||
case PackFormatColumn:
|
case PackFormatColumn:
|
||||||
return tr("Pack Format");
|
|
||||||
case DateColumn:
|
case DateColumn:
|
||||||
return tr("Last changed");
|
case ImageColumn:
|
||||||
|
return columnNames().at(section);
|
||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -151,6 +157,11 @@ QVariant ResourcePackFolderModel::headerData(int section, Qt::Orientation orient
|
|||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
case Qt::SizeHintRole:
|
||||||
|
if (section == ImageColumn) {
|
||||||
|
return QSize(64,0);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ public:
|
|||||||
enum Columns
|
enum Columns
|
||||||
{
|
{
|
||||||
ActiveColumn = 0,
|
ActiveColumn = 0,
|
||||||
|
ImageColumn,
|
||||||
NameColumn,
|
NameColumn,
|
||||||
PackFormatColumn,
|
PackFormatColumn,
|
||||||
DateColumn,
|
DateColumn,
|
||||||
@ -19,6 +20,8 @@ public:
|
|||||||
|
|
||||||
explicit ResourcePackFolderModel(const QString &dir, BaseInstance* instance);
|
explicit ResourcePackFolderModel(const QString &dir, BaseInstance* instance);
|
||||||
|
|
||||||
|
virtual QString id() const override { return "resourcepacks"; }
|
||||||
|
|
||||||
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||||
|
@ -9,4 +9,6 @@ class ShaderPackFolderModel : public ResourceFolderModel {
|
|||||||
explicit ShaderPackFolderModel(const QString& dir, BaseInstance* instance)
|
explicit ShaderPackFolderModel(const QString& dir, BaseInstance* instance)
|
||||||
: ResourceFolderModel(QDir(dir), instance)
|
: ResourceFolderModel(QDir(dir), instance)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
virtual QString id() const override { return "shaderpacks"; }
|
||||||
};
|
};
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
|
#include "MTPixmapCache.h"
|
||||||
|
|
||||||
#include "minecraft/mod/tasks/LocalTexturePackParseTask.h"
|
#include "minecraft/mod/tasks/LocalTexturePackParseTask.h"
|
||||||
|
|
||||||
void TexturePack::setDescription(QString new_description)
|
void TexturePack::setDescription(QString new_description)
|
||||||
@ -32,34 +34,41 @@ void TexturePack::setDescription(QString new_description)
|
|||||||
m_description = new_description;
|
m_description = new_description;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TexturePack::setImage(QImage new_image)
|
void TexturePack::setImage(QImage new_image) const
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_data_lock);
|
QMutexLocker locker(&m_data_lock);
|
||||||
|
|
||||||
Q_ASSERT(!new_image.isNull());
|
Q_ASSERT(!new_image.isNull());
|
||||||
|
|
||||||
if (m_pack_image_cache_key.key.isValid())
|
if (m_pack_image_cache_key.key.isValid())
|
||||||
QPixmapCache::remove(m_pack_image_cache_key.key);
|
PixmapCache::remove(m_pack_image_cache_key.key);
|
||||||
|
|
||||||
m_pack_image_cache_key.key = QPixmapCache::insert(QPixmap::fromImage(new_image));
|
// scale the image to avoid flooding the pixmapcache
|
||||||
|
auto pixmap = QPixmap::fromImage(new_image.scaled({64, 64}, Qt::AspectRatioMode::KeepAspectRatioByExpanding));
|
||||||
|
|
||||||
|
m_pack_image_cache_key.key = PixmapCache::insert(pixmap);
|
||||||
m_pack_image_cache_key.was_ever_used = true;
|
m_pack_image_cache_key.was_ever_used = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPixmap TexturePack::image(QSize size)
|
QPixmap TexturePack::image(QSize size, Qt::AspectRatioMode mode) const
|
||||||
{
|
{
|
||||||
QPixmap cached_image;
|
QPixmap cached_image;
|
||||||
if (QPixmapCache::find(m_pack_image_cache_key.key, &cached_image)) {
|
if (PixmapCache::find(m_pack_image_cache_key.key, &cached_image)) {
|
||||||
if (size.isNull())
|
if (size.isNull())
|
||||||
return cached_image;
|
return cached_image;
|
||||||
return cached_image.scaled(size);
|
return cached_image.scaled(size, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// No valid image we can get
|
// No valid image we can get
|
||||||
if (!m_pack_image_cache_key.was_ever_used)
|
if (!m_pack_image_cache_key.was_ever_used) {
|
||||||
return {};
|
return {};
|
||||||
|
} else {
|
||||||
|
qDebug() << "Texture Pack" << name() << "Had it's image evicted from the cache. reloading...";
|
||||||
|
PixmapCache::markCacheMissByEviciton();
|
||||||
|
}
|
||||||
|
|
||||||
// Imaged got evicted from the cache. Re-process it and retry.
|
// Imaged got evicted from the cache. Re-process it and retry.
|
||||||
TexturePackUtils::process(*this);
|
TexturePackUtils::processPackPNG(*this);
|
||||||
return image(size);
|
return image(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,13 +40,13 @@ class TexturePack : public Resource {
|
|||||||
[[nodiscard]] QString description() const { return m_description; }
|
[[nodiscard]] QString description() const { return m_description; }
|
||||||
|
|
||||||
/** Gets the image of the texture pack, converted to a QPixmap for drawing, and scaled to size. */
|
/** Gets the image of the texture pack, converted to a QPixmap for drawing, and scaled to size. */
|
||||||
[[nodiscard]] QPixmap image(QSize size);
|
[[nodiscard]] QPixmap image(QSize size, Qt::AspectRatioMode mode = Qt::AspectRatioMode::IgnoreAspectRatio) const;
|
||||||
|
|
||||||
/** Thread-safe. */
|
/** Thread-safe. */
|
||||||
void setDescription(QString new_description);
|
void setDescription(QString new_description);
|
||||||
|
|
||||||
/** Thread-safe. */
|
/** Thread-safe. */
|
||||||
void setImage(QImage new_image);
|
void setImage(QImage new_image) const;
|
||||||
|
|
||||||
bool valid() const override;
|
bool valid() const override;
|
||||||
|
|
||||||
@ -65,5 +65,5 @@ class TexturePack : public Resource {
|
|||||||
struct {
|
struct {
|
||||||
QPixmapCache::Key key;
|
QPixmapCache::Key key;
|
||||||
bool was_ever_used = false;
|
bool was_ever_used = false;
|
||||||
} m_pack_image_cache_key;
|
} mutable m_pack_image_cache_key;
|
||||||
};
|
};
|
||||||
|
@ -33,6 +33,9 @@
|
|||||||
* 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.
|
||||||
*/
|
*/
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
#include "Application.h"
|
||||||
|
|
||||||
#include "TexturePackFolderModel.h"
|
#include "TexturePackFolderModel.h"
|
||||||
|
|
||||||
@ -41,7 +44,13 @@
|
|||||||
|
|
||||||
TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* instance)
|
TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* instance)
|
||||||
: ResourceFolderModel(QDir(dir), instance)
|
: ResourceFolderModel(QDir(dir), instance)
|
||||||
{}
|
{
|
||||||
|
m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified" });
|
||||||
|
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified") });
|
||||||
|
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE };
|
||||||
|
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Task* TexturePackFolderModel::createUpdateTask()
|
Task* TexturePackFolderModel::createUpdateTask()
|
||||||
{
|
{
|
||||||
@ -52,3 +61,96 @@ Task* TexturePackFolderModel::createParseTask(Resource& resource)
|
|||||||
{
|
{
|
||||||
return new LocalTexturePackParseTask(m_next_resolution_ticket, static_cast<TexturePack&>(resource));
|
return new LocalTexturePackParseTask(m_next_resolution_ticket, static_cast<TexturePack&>(resource));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const
|
||||||
|
{
|
||||||
|
if (!validateIndex(index))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
int row = index.row();
|
||||||
|
int column = index.column();
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
switch (column) {
|
||||||
|
case NameColumn:
|
||||||
|
return m_resources[row]->name();
|
||||||
|
case DateColumn:
|
||||||
|
return m_resources[row]->dateTimeChanged();
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
case Qt::ToolTipRole:
|
||||||
|
if (column == NameColumn) {
|
||||||
|
if (at(row)->isSymLinkUnder(instDirPath())) {
|
||||||
|
return m_resources[row]->internal_id() +
|
||||||
|
tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
|
||||||
|
"\nCanonical Path: %1")
|
||||||
|
.arg(at(row)->fileinfo().canonicalFilePath());;
|
||||||
|
}
|
||||||
|
if (at(row)->isMoreThanOneHardLink()) {
|
||||||
|
return m_resources[row]->internal_id() +
|
||||||
|
tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_resources[row]->internal_id();
|
||||||
|
case Qt::DecorationRole: {
|
||||||
|
if (column == NameColumn && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
|
||||||
|
return APPLICATION->getThemedIcon("status-yellow");
|
||||||
|
if (column == ImageColumn) {
|
||||||
|
return at(row)->image({32, 32}, Qt::AspectRatioMode::KeepAspectRatioByExpanding);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
case Qt::CheckStateRole:
|
||||||
|
if (column == ActiveColumn) {
|
||||||
|
return m_resources[row]->enabled() ? Qt::Checked : Qt::Unchecked;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant TexturePackFolderModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
switch (role) {
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
switch (section) {
|
||||||
|
case ActiveColumn:
|
||||||
|
case NameColumn:
|
||||||
|
case DateColumn:
|
||||||
|
case ImageColumn:
|
||||||
|
return columnNames().at(section);
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
case Qt::ToolTipRole: {
|
||||||
|
switch (section) {
|
||||||
|
case ActiveColumn:
|
||||||
|
//: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc.
|
||||||
|
return tr("Is the resource enabled?");
|
||||||
|
case NameColumn:
|
||||||
|
//: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc.
|
||||||
|
return tr("The name of the resource.");
|
||||||
|
case DateColumn:
|
||||||
|
//: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc.
|
||||||
|
return tr("The date and time this resource was last changed (or added).");
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int TexturePackFolderModel::columnCount(const QModelIndex& parent) const
|
||||||
|
{
|
||||||
|
return parent.isValid() ? 0 : NUM_COLUMNS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -38,12 +38,35 @@
|
|||||||
|
|
||||||
#include "ResourceFolderModel.h"
|
#include "ResourceFolderModel.h"
|
||||||
|
|
||||||
|
#include "TexturePack.h"
|
||||||
|
|
||||||
class TexturePackFolderModel : public ResourceFolderModel
|
class TexturePackFolderModel : public ResourceFolderModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
enum Columns
|
||||||
|
{
|
||||||
|
ActiveColumn = 0,
|
||||||
|
ImageColumn,
|
||||||
|
NameColumn,
|
||||||
|
DateColumn,
|
||||||
|
NUM_COLUMNS
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit TexturePackFolderModel(const QString &dir, std::shared_ptr<const BaseInstance> instance);
|
||||||
|
|
||||||
|
virtual QString id() const override { return "texturepacks"; }
|
||||||
|
|
||||||
|
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||||
|
[[nodiscard]] int columnCount(const QModelIndex &parent) const override;
|
||||||
|
|
||||||
explicit TexturePackFolderModel(const QString &dir, BaseInstance* instance);
|
explicit TexturePackFolderModel(const QString &dir, BaseInstance* instance);
|
||||||
[[nodiscard]] Task* createUpdateTask() override;
|
[[nodiscard]] Task* createUpdateTask() override;
|
||||||
[[nodiscard]] Task* createParseTask(Resource&) override;
|
[[nodiscard]] Task* createParseTask(Resource&) override;
|
||||||
|
|
||||||
|
RESOURCE_HELPERS(TexturePack)
|
||||||
};
|
};
|
||||||
|
252
launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp
Normal file
252
launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
// 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "GetModDependenciesTask.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
#include "Json.h"
|
||||||
|
#include "QObjectPtr.h"
|
||||||
|
#include "minecraft/mod/MetadataHandler.h"
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
|
#include "modplatform/ResourceAPI.h"
|
||||||
|
#include "modplatform/flame/FlameAPI.h"
|
||||||
|
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||||
|
#include "tasks/ConcurrentTask.h"
|
||||||
|
#include "tasks/SequentialTask.h"
|
||||||
|
#include "ui/pages/modplatform/ModModel.h"
|
||||||
|
#include "ui/pages/modplatform/flame/FlameResourceModels.h"
|
||||||
|
#include "ui/pages/modplatform/modrinth/ModrinthResourceModels.h"
|
||||||
|
|
||||||
|
static Version mcVersion(BaseInstance* inst)
|
||||||
|
{
|
||||||
|
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
static ResourceAPI::ModLoaderTypes mcLoaders(BaseInstance* inst)
|
||||||
|
{
|
||||||
|
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getModLoaders().value();
|
||||||
|
}
|
||||||
|
|
||||||
|
GetModDependenciesTask::GetModDependenciesTask(QObject* parent,
|
||||||
|
BaseInstance* instance,
|
||||||
|
ModFolderModel* folder,
|
||||||
|
QList<std::shared_ptr<PackDependency>> selected)
|
||||||
|
: SequentialTask(parent, tr("Get dependencies"))
|
||||||
|
, m_selected(selected)
|
||||||
|
, m_flame_provider{ ModPlatform::ResourceProvider::FLAME, std::make_shared<ResourceDownload::FlameModModel>(*instance),
|
||||||
|
std::make_shared<FlameAPI>() }
|
||||||
|
, m_modrinth_provider{ ModPlatform::ResourceProvider::MODRINTH, std::make_shared<ResourceDownload::ModrinthModModel>(*instance),
|
||||||
|
std::make_shared<ModrinthAPI>() }
|
||||||
|
, m_version(mcVersion(instance))
|
||||||
|
, m_loaderType(mcLoaders(instance))
|
||||||
|
{
|
||||||
|
for (auto mod : folder->allMods())
|
||||||
|
if (auto meta = mod->metadata(); meta)
|
||||||
|
m_mods.append(meta);
|
||||||
|
prepare();
|
||||||
|
};
|
||||||
|
|
||||||
|
void GetModDependenciesTask::prepare()
|
||||||
|
{
|
||||||
|
for (auto sel : m_selected) {
|
||||||
|
for (auto dep : getDependenciesForVersion(sel->version, sel->pack->provider)) {
|
||||||
|
addTask(prepareDependencyTask(dep, sel->pack->provider, 20));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ModPlatform::Dependency GetModDependenciesTask::getOverride(const ModPlatform::Dependency& dep,
|
||||||
|
const ModPlatform::ResourceProvider providerName)
|
||||||
|
{
|
||||||
|
if (auto isQuilt = m_loaderType & ResourceAPI::Quilt; isQuilt || m_loaderType & ResourceAPI::Fabric) {
|
||||||
|
auto overide = ModPlatform::getOverrideDeps();
|
||||||
|
auto over = std::find_if(overide.cbegin(), overide.cend(), [dep, providerName, isQuilt](auto o) {
|
||||||
|
return o.provider == providerName && dep.addonId == (isQuilt ? o.fabric : o.quilt);
|
||||||
|
});
|
||||||
|
if (over != overide.cend()) {
|
||||||
|
return { isQuilt ? over->quilt : over->fabric, dep.type };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dep;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<ModPlatform::Dependency> GetModDependenciesTask::getDependenciesForVersion(const ModPlatform::IndexedVersion& version,
|
||||||
|
const ModPlatform::ResourceProvider providerName)
|
||||||
|
{
|
||||||
|
QList<ModPlatform::Dependency> c_dependencies;
|
||||||
|
for (auto ver_dep : version.dependencies) {
|
||||||
|
if (ver_dep.type != ModPlatform::DependencyType::REQUIRED)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto isOnlyVersion = providerName == ModPlatform::ResourceProvider::MODRINTH && ver_dep.addonId.toString().isEmpty();
|
||||||
|
if (auto dep = std::find_if(c_dependencies.begin(), c_dependencies.end(),
|
||||||
|
[&ver_dep, isOnlyVersion](const ModPlatform::Dependency& i) {
|
||||||
|
return isOnlyVersion ? i.version == ver_dep.version : i.addonId == ver_dep.addonId;
|
||||||
|
});
|
||||||
|
dep != c_dependencies.end())
|
||||||
|
continue; // check the current dependency list
|
||||||
|
|
||||||
|
if (auto dep = std::find_if(m_selected.begin(), m_selected.end(),
|
||||||
|
[&ver_dep, providerName, isOnlyVersion](std::shared_ptr<PackDependency> i) {
|
||||||
|
return i->pack->provider == providerName && (isOnlyVersion ? i->version.version == ver_dep.version
|
||||||
|
: i->pack->addonId == ver_dep.addonId);
|
||||||
|
});
|
||||||
|
dep != m_selected.end())
|
||||||
|
continue; // check the selected versions
|
||||||
|
|
||||||
|
if (auto dep = std::find_if(m_mods.begin(), m_mods.end(),
|
||||||
|
[&ver_dep, providerName, isOnlyVersion](std::shared_ptr<Metadata::ModStruct> i) {
|
||||||
|
return i->provider == providerName &&
|
||||||
|
(isOnlyVersion ? i->file_id == ver_dep.version : i->project_id == ver_dep.addonId);
|
||||||
|
});
|
||||||
|
dep != m_mods.end())
|
||||||
|
continue; // check the existing mods
|
||||||
|
|
||||||
|
if (auto dep = std::find_if(m_pack_dependencies.begin(), m_pack_dependencies.end(),
|
||||||
|
[&ver_dep, providerName, isOnlyVersion](std::shared_ptr<PackDependency> i) {
|
||||||
|
return i->pack->provider == providerName && (isOnlyVersion ? i->version.version == ver_dep.addonId
|
||||||
|
: i->pack->addonId == ver_dep.addonId);
|
||||||
|
});
|
||||||
|
dep != m_pack_dependencies.end()) // check loaded dependencies
|
||||||
|
continue;
|
||||||
|
|
||||||
|
c_dependencies.append(getOverride(ver_dep, providerName));
|
||||||
|
}
|
||||||
|
return c_dependencies;
|
||||||
|
};
|
||||||
|
|
||||||
|
Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptr<PackDependency> pDep)
|
||||||
|
{
|
||||||
|
auto provider = pDep->pack->provider == m_flame_provider.name ? m_flame_provider : m_modrinth_provider;
|
||||||
|
auto responseInfo = std::make_shared<QByteArray>();
|
||||||
|
auto info = provider.api->getProject(pDep->pack->addonId.toString(), responseInfo);
|
||||||
|
QObject::connect(info.get(), &NetJob::succeeded, [responseInfo, provider, pDep] {
|
||||||
|
QJsonParseError parse_error{};
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(*responseInfo, &parse_error);
|
||||||
|
if (parse_error.error != QJsonParseError::NoError) {
|
||||||
|
qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset
|
||||||
|
<< " reason: " << parse_error.errorString();
|
||||||
|
qDebug() << *responseInfo;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
auto obj = provider.name == ModPlatform::ResourceProvider::FLAME ? Json::requireObject(Json::requireObject(doc), "data")
|
||||||
|
: Json::requireObject(doc);
|
||||||
|
provider.mod->loadIndexedPack(*pDep->pack, obj);
|
||||||
|
} catch (const JSONValidationError& e) {
|
||||||
|
qDebug() << doc;
|
||||||
|
qWarning() << "Error while reading mod info: " << e.cause();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Dependency& dep,
|
||||||
|
const ModPlatform::ResourceProvider providerName,
|
||||||
|
int level)
|
||||||
|
{
|
||||||
|
auto pDep = std::make_shared<PackDependency>();
|
||||||
|
pDep->dependency = dep;
|
||||||
|
pDep->pack = std::make_shared<ModPlatform::IndexedPack>();
|
||||||
|
pDep->pack->addonId = dep.addonId;
|
||||||
|
pDep->pack->provider = providerName;
|
||||||
|
|
||||||
|
m_pack_dependencies.append(pDep);
|
||||||
|
auto provider = providerName == m_flame_provider.name ? m_flame_provider : m_modrinth_provider;
|
||||||
|
|
||||||
|
auto tasks = makeShared<SequentialTask>(
|
||||||
|
this, QString("DependencyInfo: %1").arg(dep.addonId.toString().isEmpty() ? dep.version : dep.addonId.toString()));
|
||||||
|
|
||||||
|
if (!dep.addonId.toString().isEmpty()) {
|
||||||
|
tasks->addTask(getProjectInfoTask(pDep));
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceAPI::DependencySearchArgs args = { dep, m_version, m_loaderType };
|
||||||
|
ResourceAPI::DependencySearchCallbacks callbacks;
|
||||||
|
|
||||||
|
callbacks.on_succeed = [dep, provider, pDep, level, this](auto& doc, auto& pack) {
|
||||||
|
try {
|
||||||
|
QJsonArray arr;
|
||||||
|
if (dep.version.length() != 0 && doc.isObject()) {
|
||||||
|
arr.append(doc.object());
|
||||||
|
} else {
|
||||||
|
arr = doc.isObject() ? Json::ensureArray(doc.object(), "data") : doc.array();
|
||||||
|
}
|
||||||
|
pDep->version = provider.mod->loadDependencyVersions(dep, arr);
|
||||||
|
if (!pDep->version.addonId.isValid()) {
|
||||||
|
if (m_loaderType & ResourceAPI::Quilt) { // falback for quilt
|
||||||
|
auto overide = ModPlatform::getOverrideDeps();
|
||||||
|
auto over = std::find_if(overide.cbegin(), overide.cend(),
|
||||||
|
[dep, provider](auto o) { return o.provider == provider.name && dep.addonId == o.quilt; });
|
||||||
|
if (over != overide.cend()) {
|
||||||
|
removePack(dep.addonId);
|
||||||
|
addTask(prepareDependencyTask({ over->fabric, dep.type }, provider.name, level));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qWarning() << "Error while reading mod version empty ";
|
||||||
|
qDebug() << doc;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pDep->version.is_currently_selected = true;
|
||||||
|
pDep->pack->versions = { pDep->version };
|
||||||
|
pDep->pack->versionsLoaded = true;
|
||||||
|
|
||||||
|
} catch (const JSONValidationError& e) {
|
||||||
|
qDebug() << doc;
|
||||||
|
qWarning() << "Error while reading mod version: " << e.cause();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (level == 0) {
|
||||||
|
qWarning() << "Dependency cycle exeeded";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (dep.addonId.toString().isEmpty() && !pDep->version.addonId.toString().isEmpty()) {
|
||||||
|
pDep->pack->addonId = pDep->version.addonId;
|
||||||
|
auto dep = getOverride({ pDep->version.addonId, pDep->dependency.type }, provider.name);
|
||||||
|
if (dep.addonId != pDep->version.addonId) {
|
||||||
|
removePack(pDep->version.addonId);
|
||||||
|
addTask(prepareDependencyTask(dep, provider.name, level));
|
||||||
|
} else
|
||||||
|
addTask(getProjectInfoTask(pDep));
|
||||||
|
}
|
||||||
|
for (auto dep : getDependenciesForVersion(pDep->version, provider.name)) {
|
||||||
|
addTask(prepareDependencyTask(dep, provider.name, level - 1));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto version = provider.api->getDependencyVersion(std::move(args), std::move(callbacks));
|
||||||
|
tasks->addTask(version);
|
||||||
|
return tasks;
|
||||||
|
};
|
||||||
|
|
||||||
|
void GetModDependenciesTask::removePack(const QVariant addonId)
|
||||||
|
{
|
||||||
|
auto pred = [addonId](const std::shared_ptr<PackDependency>& v) { return v->pack->addonId == addonId; };
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
|
||||||
|
m_pack_dependencies.removeIf(pred);
|
||||||
|
#else
|
||||||
|
for (auto it = m_pack_dependencies.begin(); it != m_pack_dependencies.end();)
|
||||||
|
if (pred(*it))
|
||||||
|
it = m_pack_dependencies.erase(it);
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
#endif
|
||||||
|
}
|
84
launcher/minecraft/mod/tasks/GetModDependenciesTask.h
Normal file
84
launcher/minecraft/mod/tasks/GetModDependenciesTask.h
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QList>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "minecraft/mod/MetadataHandler.h"
|
||||||
|
#include "minecraft/mod/ModFolderModel.h"
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
|
#include "modplatform/ResourceAPI.h"
|
||||||
|
#include "tasks/SequentialTask.h"
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
#include "ui/pages/modplatform/ModModel.h"
|
||||||
|
|
||||||
|
class GetModDependenciesTask : public SequentialTask {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
using Ptr = shared_qobject_ptr<GetModDependenciesTask>;
|
||||||
|
|
||||||
|
struct PackDependency {
|
||||||
|
ModPlatform::Dependency dependency;
|
||||||
|
ModPlatform::IndexedPack::Ptr pack;
|
||||||
|
ModPlatform::IndexedVersion version;
|
||||||
|
PackDependency() = default;
|
||||||
|
PackDependency(const ModPlatform::IndexedPack::Ptr p, const ModPlatform::IndexedVersion& v)
|
||||||
|
{
|
||||||
|
pack = p;
|
||||||
|
version = v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Provider {
|
||||||
|
ModPlatform::ResourceProvider name;
|
||||||
|
std::shared_ptr<ResourceDownload::ModModel> mod;
|
||||||
|
std::shared_ptr<ResourceAPI> api;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit GetModDependenciesTask(QObject* parent,
|
||||||
|
BaseInstance* instance,
|
||||||
|
ModFolderModel* folder,
|
||||||
|
QList<std::shared_ptr<PackDependency>> selected);
|
||||||
|
|
||||||
|
auto getDependecies() const -> QList<std::shared_ptr<PackDependency>> { return m_pack_dependencies; }
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
Task::Ptr prepareDependencyTask(const ModPlatform::Dependency&, const ModPlatform::ResourceProvider, int);
|
||||||
|
QList<ModPlatform::Dependency> getDependenciesForVersion(const ModPlatform::IndexedVersion&,
|
||||||
|
const ModPlatform::ResourceProvider providerName);
|
||||||
|
void prepare();
|
||||||
|
Task::Ptr getProjectInfoTask(std::shared_ptr<PackDependency> pDep);
|
||||||
|
ModPlatform::Dependency getOverride(const ModPlatform::Dependency&, const ModPlatform::ResourceProvider providerName);
|
||||||
|
void removePack(const QVariant addonId);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<std::shared_ptr<PackDependency>> m_pack_dependencies;
|
||||||
|
QList<std::shared_ptr<Metadata::ModStruct>> m_mods;
|
||||||
|
QList<std::shared_ptr<PackDependency>> m_selected;
|
||||||
|
Provider m_flame_provider;
|
||||||
|
Provider m_modrinth_provider;
|
||||||
|
|
||||||
|
Version m_version;
|
||||||
|
ResourceAPI::ModLoaderTypes m_loaderType;
|
||||||
|
};
|
@ -52,6 +52,10 @@ ModDetails ReadMCModInfo(QByteArray contents)
|
|||||||
authors = firstObj.value("authors").toArray();
|
authors = firstObj.value("authors").toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (firstObj.contains("logoFile")) {
|
||||||
|
details.icon_file = firstObj.value("logoFile").toString();
|
||||||
|
}
|
||||||
|
|
||||||
for (auto author : authors) {
|
for (auto author : authors) {
|
||||||
details.authors.append(author.toString());
|
details.authors.append(author.toString());
|
||||||
}
|
}
|
||||||
@ -166,6 +170,31 @@ ModDetails ReadMCModTOML(QByteArray contents)
|
|||||||
}
|
}
|
||||||
details.homeurl = homeurl;
|
details.homeurl = homeurl;
|
||||||
|
|
||||||
|
QString issueTrackerURL = "";
|
||||||
|
if (auto issueTrackerURLDatum = tomlData["issueTrackerURL"].as_string()) {
|
||||||
|
issueTrackerURL = QString::fromStdString(issueTrackerURLDatum->get());
|
||||||
|
} else if (auto issueTrackerURLDatum = (*modsTable)["issueTrackerURL"].as_string()) {
|
||||||
|
issueTrackerURL = QString::fromStdString(issueTrackerURLDatum->get());
|
||||||
|
}
|
||||||
|
details.issue_tracker = issueTrackerURL;
|
||||||
|
|
||||||
|
QString license = "";
|
||||||
|
if (auto licenseDatum = tomlData["license"].as_string()) {
|
||||||
|
license = QString::fromStdString(licenseDatum->get());
|
||||||
|
} else if (auto licenseDatum =(*modsTable)["license"].as_string()) {
|
||||||
|
license = QString::fromStdString(licenseDatum->get());
|
||||||
|
}
|
||||||
|
if (!license.isEmpty())
|
||||||
|
details.licenses.append(ModLicense(license));
|
||||||
|
|
||||||
|
QString logoFile = "";
|
||||||
|
if (auto logoFileDatum = tomlData["logoFile"].as_string()) {
|
||||||
|
logoFile = QString::fromStdString(logoFileDatum->get());
|
||||||
|
} else if (auto logoFileDatum =(*modsTable)["logoFile"].as_string()) {
|
||||||
|
logoFile = QString::fromStdString(logoFileDatum->get());
|
||||||
|
}
|
||||||
|
details.icon_file = logoFile;
|
||||||
|
|
||||||
return details;
|
return details;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,6 +230,57 @@ ModDetails ReadFabricModInfo(QByteArray contents)
|
|||||||
if (contact.contains("homepage")) {
|
if (contact.contains("homepage")) {
|
||||||
details.homeurl = contact.value("homepage").toString();
|
details.homeurl = contact.value("homepage").toString();
|
||||||
}
|
}
|
||||||
|
if (contact.contains("issues")) {
|
||||||
|
details.issue_tracker = contact.value("issues").toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (object.contains("license")) {
|
||||||
|
auto license = object.value("license");
|
||||||
|
if (license.isArray()) {
|
||||||
|
for (auto l : license.toArray()) {
|
||||||
|
if (l.isString()) {
|
||||||
|
details.licenses.append(ModLicense(l.toString()));
|
||||||
|
} else if (l.isObject()) {
|
||||||
|
auto obj = l.toObject();
|
||||||
|
details.licenses.append(ModLicense(obj.value("name").toString(), obj.value("id").toString(),
|
||||||
|
obj.value("url").toString(), obj.value("description").toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (license.isString()) {
|
||||||
|
details.licenses.append(ModLicense(license.toString()));
|
||||||
|
} else if (license.isObject()) {
|
||||||
|
auto obj = license.toObject();
|
||||||
|
details.licenses.append(ModLicense(obj.value("name").toString(), obj.value("id").toString(), obj.value("url").toString(),
|
||||||
|
obj.value("description").toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (object.contains("icon")) {
|
||||||
|
auto icon = object.value("icon");
|
||||||
|
if (icon.isObject()) {
|
||||||
|
auto obj = icon.toObject();
|
||||||
|
// take the largest icon
|
||||||
|
int largest = 0;
|
||||||
|
for (auto key : obj.keys()) {
|
||||||
|
auto size = key.split('x').first().toInt();
|
||||||
|
if (size > largest) {
|
||||||
|
largest = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (largest > 0) {
|
||||||
|
auto key = QString::number(largest) + "x" + QString::number(largest);
|
||||||
|
details.icon_file = obj.value(key).toString();
|
||||||
|
} else { // parsing the sizes failed
|
||||||
|
// take the first
|
||||||
|
for (auto i : obj) {
|
||||||
|
details.icon_file = i.toString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (icon.isString()) {
|
||||||
|
details.icon_file = icon.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return details;
|
return details;
|
||||||
@ -238,6 +318,58 @@ ModDetails ReadQuiltModInfo(QByteArray contents)
|
|||||||
if (modContact.contains("homepage")) {
|
if (modContact.contains("homepage")) {
|
||||||
details.homeurl = Json::requireString(modContact.value("homepage"));
|
details.homeurl = Json::requireString(modContact.value("homepage"));
|
||||||
}
|
}
|
||||||
|
if (modContact.contains("issues")) {
|
||||||
|
details.issue_tracker = Json::requireString(modContact.value("issues"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modMetadata.contains("license")) {
|
||||||
|
auto license = modMetadata.value("license");
|
||||||
|
if (license.isArray()) {
|
||||||
|
for (auto l : license.toArray()) {
|
||||||
|
if (l.isString()) {
|
||||||
|
details.licenses.append(ModLicense(l.toString()));
|
||||||
|
} else if (l.isObject()) {
|
||||||
|
auto obj = l.toObject();
|
||||||
|
details.licenses.append(ModLicense(obj.value("name").toString(), obj.value("id").toString(),
|
||||||
|
obj.value("url").toString(), obj.value("description").toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (license.isString()) {
|
||||||
|
details.licenses.append(ModLicense(license.toString()));
|
||||||
|
} else if (license.isObject()) {
|
||||||
|
auto obj = license.toObject();
|
||||||
|
details.licenses.append(ModLicense(obj.value("name").toString(), obj.value("id").toString(), obj.value("url").toString(),
|
||||||
|
obj.value("description").toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modMetadata.contains("icon")) {
|
||||||
|
auto icon = modMetadata.value("icon");
|
||||||
|
if (icon.isObject()) {
|
||||||
|
auto obj = icon.toObject();
|
||||||
|
// take the largest icon
|
||||||
|
int largest = 0;
|
||||||
|
for (auto key : obj.keys()) {
|
||||||
|
auto size = key.split('x').first().toInt();
|
||||||
|
if (size > largest) {
|
||||||
|
largest = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (largest > 0) {
|
||||||
|
auto key = QString::number(largest) + "x" + QString::number(largest);
|
||||||
|
details.icon_file = obj.value(key).toString();
|
||||||
|
} else { // parsing the sizes failed
|
||||||
|
// take the first
|
||||||
|
for (auto i : obj) {
|
||||||
|
details.icon_file = i.toString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (icon.isString()) {
|
||||||
|
details.icon_file = icon.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return details;
|
return details;
|
||||||
}
|
}
|
||||||
@ -515,6 +647,85 @@ bool validate(QFileInfo file)
|
|||||||
return ModUtils::process(mod, ProcessingLevel::BasicInfoOnly) && mod.valid();
|
return ModUtils::process(mod, ProcessingLevel::BasicInfoOnly) && mod.valid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool processIconPNG(const Mod& mod, QByteArray&& raw_data)
|
||||||
|
{
|
||||||
|
auto img = QImage::fromData(raw_data);
|
||||||
|
if (!img.isNull()) {
|
||||||
|
mod.setIcon(img);
|
||||||
|
} else {
|
||||||
|
qWarning() << "Failed to parse mod logo:" << mod.iconPath() << "from" << mod.name();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool loadIconFile(const Mod& mod) {
|
||||||
|
if (mod.iconPath().isEmpty()) {
|
||||||
|
qWarning() << "No Iconfile set, be sure to parse the mod first";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto png_invalid = [&mod]() {
|
||||||
|
qWarning() << "Mod at" << mod.fileinfo().filePath() << "does not have a valid icon";
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (mod.type()) {
|
||||||
|
case ResourceType::FOLDER:
|
||||||
|
{
|
||||||
|
QFileInfo icon_info(FS::PathCombine(mod.fileinfo().filePath(), mod.iconPath()));
|
||||||
|
if (icon_info.exists() && icon_info.isFile()) {
|
||||||
|
QFile icon(icon_info.filePath());
|
||||||
|
if (!icon.open(QIODevice::ReadOnly))
|
||||||
|
return false;
|
||||||
|
auto data = icon.readAll();
|
||||||
|
|
||||||
|
bool icon_result = ModUtils::processIconPNG(mod, std::move(data));
|
||||||
|
|
||||||
|
icon.close();
|
||||||
|
|
||||||
|
if (!icon_result) {
|
||||||
|
return png_invalid(); // icon invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ResourceType::ZIPFILE:
|
||||||
|
{
|
||||||
|
QuaZip zip(mod.fileinfo().filePath());
|
||||||
|
if (!zip.open(QuaZip::mdUnzip))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
QuaZipFile file(&zip);
|
||||||
|
|
||||||
|
if (zip.setCurrentFile(mod.iconPath())) {
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
qCritical() << "Failed to open file in zip.";
|
||||||
|
zip.close();
|
||||||
|
return png_invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = file.readAll();
|
||||||
|
|
||||||
|
bool icon_result = ModUtils::processIconPNG(mod, std::move(data));
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
if (!icon_result) {
|
||||||
|
return png_invalid(); // icon png invalid
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return png_invalid(); // could not set icon as current file.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ResourceType::LITEMOD:
|
||||||
|
{
|
||||||
|
return false; // can lightmods even have icons?
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
qWarning() << "Invalid type for mod, can not load icon.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ModUtils
|
} // namespace ModUtils
|
||||||
|
|
||||||
LocalModParseTask::LocalModParseTask(int token, ResourceType type, const QFileInfo& modFile)
|
LocalModParseTask::LocalModParseTask(int token, ResourceType type, const QFileInfo& modFile)
|
||||||
|
@ -25,6 +25,9 @@ bool processLitemod(Mod& mod, ProcessingLevel level = ProcessingLevel::Full);
|
|||||||
|
|
||||||
/** Checks whether a file is valid as a mod or not. */
|
/** Checks whether a file is valid as a mod or not. */
|
||||||
bool validate(QFileInfo file);
|
bool validate(QFileInfo file);
|
||||||
|
|
||||||
|
bool processIconPNG(const Mod& mod, QByteArray&& raw_data);
|
||||||
|
bool loadIconFile(const Mod& mod);
|
||||||
} // namespace ModUtils
|
} // namespace ModUtils
|
||||||
|
|
||||||
class LocalModParseTask : public Task {
|
class LocalModParseTask : public Task {
|
||||||
|
@ -1,25 +1,24 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
*
|
*
|
||||||
* 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
|
||||||
* the Free Software Foundation, version 3.
|
* the Free Software Foundation, version 3.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "LocalModUpdateTask.h"
|
#include "LocalModUpdateTask.h"
|
||||||
|
|
||||||
#include "Application.h"
|
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "minecraft/mod/MetadataHandler.h"
|
#include "minecraft/mod/MetadataHandler.h"
|
||||||
|
|
||||||
|
@ -165,15 +165,16 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level)
|
|||||||
bool pack_png_result = ResourcePackUtils::processPackPNG(pack, std::move(data));
|
bool pack_png_result = ResourcePackUtils::processPackPNG(pack, std::move(data));
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
|
zip.close();
|
||||||
if (!pack_png_result) {
|
if (!pack_png_result) {
|
||||||
return png_invalid(); // pack.png invalid
|
return png_invalid(); // pack.png invalid
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
zip.close();
|
||||||
return png_invalid(); // could not set pack.mcmeta as current file.
|
return png_invalid(); // could not set pack.mcmeta as current file.
|
||||||
}
|
}
|
||||||
|
|
||||||
zip.close();
|
zip.close();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +194,7 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool processPackPNG(ResourcePack& pack, QByteArray&& raw_data)
|
bool processPackPNG(const ResourcePack& pack, QByteArray&& raw_data)
|
||||||
{
|
{
|
||||||
auto img = QImage::fromData(raw_data);
|
auto img = QImage::fromData(raw_data);
|
||||||
if (!img.isNull()) {
|
if (!img.isNull()) {
|
||||||
@ -205,6 +206,68 @@ bool processPackPNG(ResourcePack& pack, QByteArray&& raw_data)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool processPackPNG(const ResourcePack& pack)
|
||||||
|
{
|
||||||
|
auto png_invalid = [&pack]() {
|
||||||
|
qWarning() << "Resource pack at" << pack.fileinfo().filePath() << "does not have a valid pack.png";
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (pack.type()) {
|
||||||
|
case ResourceType::FOLDER:
|
||||||
|
{
|
||||||
|
QFileInfo image_file_info(FS::PathCombine(pack.fileinfo().filePath(), "pack.png"));
|
||||||
|
if (image_file_info.exists() && image_file_info.isFile()) {
|
||||||
|
QFile pack_png_file(image_file_info.filePath());
|
||||||
|
if (!pack_png_file.open(QIODevice::ReadOnly))
|
||||||
|
return png_invalid(); // can't open pack.png file
|
||||||
|
|
||||||
|
auto data = pack_png_file.readAll();
|
||||||
|
|
||||||
|
bool pack_png_result = ResourcePackUtils::processPackPNG(pack, std::move(data));
|
||||||
|
|
||||||
|
pack_png_file.close();
|
||||||
|
if (!pack_png_result) {
|
||||||
|
return png_invalid(); // pack.png invalid
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return png_invalid(); // pack.png does not exists or is not a valid file.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ResourceType::ZIPFILE:
|
||||||
|
{
|
||||||
|
Q_ASSERT(pack.type() == ResourceType::ZIPFILE);
|
||||||
|
|
||||||
|
QuaZip zip(pack.fileinfo().filePath());
|
||||||
|
if (!zip.open(QuaZip::mdUnzip))
|
||||||
|
return false; // can't open zip file
|
||||||
|
|
||||||
|
QuaZipFile file(&zip);
|
||||||
|
if (zip.setCurrentFile("pack.png")) {
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
qCritical() << "Failed to open file in zip.";
|
||||||
|
zip.close();
|
||||||
|
return png_invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = file.readAll();
|
||||||
|
|
||||||
|
bool pack_png_result = ResourcePackUtils::processPackPNG(pack, std::move(data));
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
if (!pack_png_result) {
|
||||||
|
return png_invalid(); // pack.png invalid
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return png_invalid(); // could not set pack.mcmeta as current file.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
qWarning() << "Invalid type for resource pack parse task!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool validate(QFileInfo file)
|
bool validate(QFileInfo file)
|
||||||
{
|
{
|
||||||
ResourcePack rp{ file };
|
ResourcePack rp{ file };
|
||||||
|
@ -35,7 +35,10 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Ful
|
|||||||
bool processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full);
|
bool processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full);
|
||||||
|
|
||||||
bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data);
|
bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data);
|
||||||
bool processPackPNG(ResourcePack& pack, QByteArray&& raw_data);
|
bool processPackPNG(const ResourcePack& pack, QByteArray&& raw_data);
|
||||||
|
|
||||||
|
/// processes ONLY the pack.png (rest of the pack may be invalid)
|
||||||
|
bool processPackPNG(const ResourcePack& pack);
|
||||||
|
|
||||||
/** Checks whether a file is valid as a resource pack or not. */
|
/** Checks whether a file is valid as a resource pack or not. */
|
||||||
bool validate(QFileInfo file);
|
bool validate(QFileInfo file);
|
||||||
|
@ -44,11 +44,7 @@ static const QMap<PackedResourceType, QString> s_packed_type_names = {
|
|||||||
namespace ResourceUtils {
|
namespace ResourceUtils {
|
||||||
PackedResourceType identify(QFileInfo file){
|
PackedResourceType identify(QFileInfo file){
|
||||||
if (file.exists() && file.isFile()) {
|
if (file.exists() && file.isFile()) {
|
||||||
if (ModUtils::validate(file)) {
|
if (ResourcePackUtils::validate(file)) {
|
||||||
// mods can contain resource and data packs so they must be tested first
|
|
||||||
qDebug() << file.fileName() << "is a mod";
|
|
||||||
return PackedResourceType::Mod;
|
|
||||||
} else if (ResourcePackUtils::validate(file)) {
|
|
||||||
qDebug() << file.fileName() << "is a resource pack";
|
qDebug() << file.fileName() << "is a resource pack";
|
||||||
return PackedResourceType::ResourcePack;
|
return PackedResourceType::ResourcePack;
|
||||||
} else if (TexturePackUtils::validate(file)) {
|
} else if (TexturePackUtils::validate(file)) {
|
||||||
@ -57,6 +53,9 @@ PackedResourceType identify(QFileInfo file){
|
|||||||
} else if (DataPackUtils::validate(file)) {
|
} else if (DataPackUtils::validate(file)) {
|
||||||
qDebug() << file.fileName() << "is a data pack";
|
qDebug() << file.fileName() << "is a data pack";
|
||||||
return PackedResourceType::DataPack;
|
return PackedResourceType::DataPack;
|
||||||
|
} else if (ModUtils::validate(file)) {
|
||||||
|
qDebug() << file.fileName() << "is a mod";
|
||||||
|
return PackedResourceType::Mod;
|
||||||
} else if (WorldSaveUtils::validate(file)) {
|
} else if (WorldSaveUtils::validate(file)) {
|
||||||
qDebug() << file.fileName() << "is a world save";
|
qDebug() << file.fileName() << "is a world save";
|
||||||
return PackedResourceType::WorldSave;
|
return PackedResourceType::WorldSave;
|
||||||
|
@ -131,6 +131,7 @@ bool processZIP(TexturePack& pack, ProcessingLevel level)
|
|||||||
bool packPNG_result = TexturePackUtils::processPackPNG(pack, std::move(data));
|
bool packPNG_result = TexturePackUtils::processPackPNG(pack, std::move(data));
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
|
zip.close();
|
||||||
if (!packPNG_result) {
|
if (!packPNG_result) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -147,7 +148,7 @@ bool processPackTXT(TexturePack& pack, QByteArray&& raw_data)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool processPackPNG(TexturePack& pack, QByteArray&& raw_data)
|
bool processPackPNG(const TexturePack& pack, QByteArray&& raw_data)
|
||||||
{
|
{
|
||||||
auto img = QImage::fromData(raw_data);
|
auto img = QImage::fromData(raw_data);
|
||||||
if (!img.isNull()) {
|
if (!img.isNull()) {
|
||||||
@ -159,6 +160,70 @@ bool processPackPNG(TexturePack& pack, QByteArray&& raw_data)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool processPackPNG(const TexturePack& pack)
|
||||||
|
{
|
||||||
|
auto png_invalid = [&pack]() {
|
||||||
|
qWarning() << "Texture pack at" << pack.fileinfo().filePath() << "does not have a valid pack.png";
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (pack.type()) {
|
||||||
|
case ResourceType::FOLDER:
|
||||||
|
{
|
||||||
|
QFileInfo image_file_info(FS::PathCombine(pack.fileinfo().filePath(), "pack.png"));
|
||||||
|
if (image_file_info.exists() && image_file_info.isFile()) {
|
||||||
|
QFile pack_png_file(image_file_info.filePath());
|
||||||
|
if (!pack_png_file.open(QIODevice::ReadOnly))
|
||||||
|
return png_invalid(); // can't open pack.png file
|
||||||
|
|
||||||
|
auto data = pack_png_file.readAll();
|
||||||
|
|
||||||
|
bool pack_png_result = TexturePackUtils::processPackPNG(pack, std::move(data));
|
||||||
|
|
||||||
|
pack_png_file.close();
|
||||||
|
if (!pack_png_result) {
|
||||||
|
return png_invalid(); // pack.png invalid
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return png_invalid(); // pack.png does not exists or is not a valid file.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ResourceType::ZIPFILE:
|
||||||
|
{
|
||||||
|
Q_ASSERT(pack.type() == ResourceType::ZIPFILE);
|
||||||
|
|
||||||
|
QuaZip zip(pack.fileinfo().filePath());
|
||||||
|
if (!zip.open(QuaZip::mdUnzip))
|
||||||
|
return false; // can't open zip file
|
||||||
|
|
||||||
|
QuaZipFile file(&zip);
|
||||||
|
if (zip.setCurrentFile("pack.png")) {
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
qCritical() << "Failed to open file in zip.";
|
||||||
|
zip.close();
|
||||||
|
return png_invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = file.readAll();
|
||||||
|
|
||||||
|
bool pack_png_result = TexturePackUtils::processPackPNG(pack, std::move(data));
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
if (!pack_png_result) {
|
||||||
|
zip.close();
|
||||||
|
return png_invalid(); // pack.png invalid
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
zip.close();
|
||||||
|
return png_invalid(); // could not set pack.mcmeta as current file.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
qWarning() << "Invalid type for resource pack parse task!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool validate(QFileInfo file)
|
bool validate(QFileInfo file)
|
||||||
{
|
{
|
||||||
TexturePack rp{ file };
|
TexturePack rp{ file };
|
||||||
|
@ -36,7 +36,10 @@ bool processZIP(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full
|
|||||||
bool processFolder(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full);
|
bool processFolder(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full);
|
||||||
|
|
||||||
bool processPackTXT(TexturePack& pack, QByteArray&& raw_data);
|
bool processPackTXT(TexturePack& pack, QByteArray&& raw_data);
|
||||||
bool processPackPNG(TexturePack& pack, QByteArray&& raw_data);
|
bool processPackPNG(const TexturePack& pack, QByteArray&& raw_data);
|
||||||
|
|
||||||
|
/// processes ONLY the pack.png (rest of the pack may be invalid)
|
||||||
|
bool processPackPNG(const TexturePack& pack);
|
||||||
|
|
||||||
/** Checks whether a file is valid as a texture pack or not. */
|
/** Checks whether a file is valid as a texture pack or not. */
|
||||||
bool validate(QFileInfo file);
|
bool validate(QFileInfo file);
|
||||||
|
@ -103,7 +103,7 @@ void ModFolderLoadTask::executeTask()
|
|||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
auto mod = iter.next().value();
|
auto mod = iter.next().value();
|
||||||
if (mod->status() == ModStatus::NotInstalled) {
|
if (mod->status() == ModStatus::NotInstalled) {
|
||||||
mod->destroy(m_index_dir, false, false);
|
mod->destroy(m_index_dir, false);
|
||||||
iter.remove();
|
iter.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,8 +145,7 @@ void EnsureMetadataTask::executeTask()
|
|||||||
connect(project_task.get(), &Task::finished, this, [=] {
|
connect(project_task.get(), &Task::finished, this, [=] {
|
||||||
invalidade_leftover();
|
invalidade_leftover();
|
||||||
project_task->deleteLater();
|
project_task->deleteLater();
|
||||||
if (m_current_task)
|
m_current_task = nullptr;
|
||||||
m_current_task.reset();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
m_current_task = project_task;
|
m_current_task = project_task;
|
||||||
@ -155,8 +154,7 @@ void EnsureMetadataTask::executeTask()
|
|||||||
|
|
||||||
connect(version_task.get(), &Task::finished, [=] {
|
connect(version_task.get(), &Task::finished, [=] {
|
||||||
version_task->deleteLater();
|
version_task->deleteLater();
|
||||||
if (m_current_task)
|
m_current_task = nullptr;
|
||||||
m_current_task.reset();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (m_mods.size() > 1)
|
if (m_mods.size() > 1)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||||
|
* 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
|
||||||
@ -33,6 +34,8 @@ enum class ResourceProvider { MODRINTH, FLAME };
|
|||||||
|
|
||||||
enum class ResourceType { MOD, RESOURCE_PACK, SHADER_PACK };
|
enum class ResourceType { MOD, RESOURCE_PACK, SHADER_PACK };
|
||||||
|
|
||||||
|
enum class DependencyType { REQUIRED, OPTIONAL, INCOMPATIBLE, EMBEDDED, TOOL, INCLUDE, UNKNOWN };
|
||||||
|
|
||||||
class ProviderCapabilities {
|
class ProviderCapabilities {
|
||||||
public:
|
public:
|
||||||
auto name(ResourceProvider) -> const char*;
|
auto name(ResourceProvider) -> const char*;
|
||||||
@ -52,6 +55,12 @@ struct DonationData {
|
|||||||
QString url;
|
QString url;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Dependency {
|
||||||
|
QVariant addonId;
|
||||||
|
DependencyType type;
|
||||||
|
QString version;
|
||||||
|
};
|
||||||
|
|
||||||
struct IndexedVersion {
|
struct IndexedVersion {
|
||||||
QVariant addonId;
|
QVariant addonId;
|
||||||
QVariant fileId;
|
QVariant fileId;
|
||||||
@ -66,6 +75,7 @@ struct IndexedVersion {
|
|||||||
QString hash;
|
QString hash;
|
||||||
bool is_preferred = true;
|
bool is_preferred = true;
|
||||||
QString changelog;
|
QString changelog;
|
||||||
|
QList<Dependency> dependencies;
|
||||||
|
|
||||||
// For internal use, not provided by APIs
|
// For internal use, not provided by APIs
|
||||||
bool is_currently_selected = false;
|
bool is_currently_selected = false;
|
||||||
@ -119,6 +129,22 @@ struct IndexedPack {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct OverrideDep {
|
||||||
|
QString quilt;
|
||||||
|
QString fabric;
|
||||||
|
QString slug;
|
||||||
|
ModPlatform::ResourceProvider provider;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline auto getOverrideDeps() -> QList<OverrideDep>
|
||||||
|
{
|
||||||
|
return { { "634179", "306612", "API", ModPlatform::ResourceProvider::FLAME },
|
||||||
|
{ "720410", "308769", "KotlinLibraries", ModPlatform::ResourceProvider::FLAME },
|
||||||
|
|
||||||
|
{ "qvIfYCYJ", "P7dR8mSH", "API", ModPlatform::ResourceProvider::MODRINTH },
|
||||||
|
{ "lwVhp9o5", "Ha28R6CL", "KotlinLibraries", ModPlatform::ResourceProvider::MODRINTH } };
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace ModPlatform
|
} // namespace ModPlatform
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(ModPlatform::IndexedPack)
|
Q_DECLARE_METATYPE(ModPlatform::IndexedPack)
|
||||||
|
@ -111,6 +111,16 @@ class ResourceAPI {
|
|||||||
std::function<void(QJsonDocument&, ModPlatform::IndexedPack)> on_succeed;
|
std::function<void(QJsonDocument&, ModPlatform::IndexedPack)> on_succeed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DependencySearchArgs {
|
||||||
|
ModPlatform::Dependency dependency;
|
||||||
|
Version mcVersion;
|
||||||
|
ModLoaderTypes loader;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DependencySearchCallbacks {
|
||||||
|
std::function<void(QJsonDocument&, const ModPlatform::Dependency&)> on_succeed;
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Gets a list of available sorting methods for this API. */
|
/** Gets a list of available sorting methods for this API. */
|
||||||
[[nodiscard]] virtual auto getSortingMethods() const -> QList<SortingMethod> = 0;
|
[[nodiscard]] virtual auto getSortingMethods() const -> QList<SortingMethod> = 0;
|
||||||
@ -143,6 +153,12 @@ class ResourceAPI {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] virtual Task::Ptr getDependencyVersion(DependencySearchArgs&&, DependencySearchCallbacks&&) const
|
||||||
|
{
|
||||||
|
qWarning() << "TODO";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
static auto getModLoaderString(ModLoaderType type) -> const QString
|
static auto getModLoaderString(ModLoaderType type) -> const QString
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -21,10 +21,6 @@ bool Flame::FileResolvingTask::abort()
|
|||||||
|
|
||||||
void Flame::FileResolvingTask::executeTask()
|
void Flame::FileResolvingTask::executeTask()
|
||||||
{
|
{
|
||||||
if (m_toProcess.files.isEmpty()) { // no file to resolve so leave it empty and emit success immediately
|
|
||||||
emitSucceeded();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setStatus(tr("Resolving mod IDs..."));
|
setStatus(tr("Resolving mod IDs..."));
|
||||||
setProgress(0, 3);
|
setProgress(0, 3);
|
||||||
m_dljob.reset(new NetJob("Mod id resolver", m_network));
|
m_dljob.reset(new NetJob("Mod id resolver", m_network));
|
||||||
@ -132,13 +128,12 @@ void Flame::FileResolvingTask::netJobFinished()
|
|||||||
m_checkJob->start();
|
m_checkJob->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Flame::FileResolvingTask::modrinthCheckFinished()
|
void Flame::FileResolvingTask::modrinthCheckFinished() {
|
||||||
{
|
|
||||||
setProgress(2, 3);
|
setProgress(2, 3);
|
||||||
qDebug() << "Finished with blocked mods : " << blockedProjects.size();
|
qDebug() << "Finished with blocked mods : " << blockedProjects.size();
|
||||||
|
|
||||||
for (auto it = blockedProjects.keyBegin(); it != blockedProjects.keyEnd(); it++) {
|
for (auto it = blockedProjects.keyBegin(); it != blockedProjects.keyEnd(); it++) {
|
||||||
auto& out = *it;
|
auto &out = *it;
|
||||||
auto bytes = blockedProjects[out];
|
auto bytes = blockedProjects[out];
|
||||||
if (!out->resolved) {
|
if (!out->resolved) {
|
||||||
continue;
|
continue;
|
||||||
@ -158,13 +153,15 @@ void Flame::FileResolvingTask::modrinthCheckFinished()
|
|||||||
out->resolved = false;
|
out->resolved = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// copy to an output list and filter out projects found on modrinth
|
//copy to an output list and filter out projects found on modrinth
|
||||||
auto block = std::make_shared<QList<File*>>();
|
auto block = std::make_shared<QList<File*>>();
|
||||||
auto it = blockedProjects.keys();
|
auto it = blockedProjects.keys();
|
||||||
std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File* f) { return !f->resolved; });
|
std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File *f) {
|
||||||
// Display not found mods early
|
return !f->resolved;
|
||||||
|
});
|
||||||
|
//Display not found mods early
|
||||||
if (!block->empty()) {
|
if (!block->empty()) {
|
||||||
// blocked mods found, we need the slug for displaying.... we need another job :D !
|
//blocked mods found, we need the slug for displaying.... we need another job :D !
|
||||||
m_slugJob.reset(new NetJob("Slug Job", m_network));
|
m_slugJob.reset(new NetJob("Slug Job", m_network));
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (auto mod : *block) {
|
for (auto mod : *block) {
|
||||||
@ -176,8 +173,8 @@ void Flame::FileResolvingTask::modrinthCheckFinished()
|
|||||||
QObject::connect(dl.get(), &Net::Download::succeeded, [block, index, output]() {
|
QObject::connect(dl.get(), &Net::Download::succeeded, [block, index, output]() {
|
||||||
auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done
|
auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done
|
||||||
auto json = QJsonDocument::fromJson(*output);
|
auto json = QJsonDocument::fromJson(*output);
|
||||||
auto base =
|
auto base = Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json),"data"),"links"),
|
||||||
Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json), "data"), "links"), "websiteUrl");
|
"websiteUrl");
|
||||||
auto link = QString("%1/download/%2").arg(base, QString::number(mod->fileId));
|
auto link = QString("%1/download/%2").arg(base, QString::number(mod->fileId));
|
||||||
mod->websiteUrl = link;
|
mod->websiteUrl = link;
|
||||||
});
|
});
|
||||||
|
@ -4,8 +4,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "modplatform/ModIndex.h"
|
#include "modplatform/ModIndex.h"
|
||||||
|
#include "modplatform/ResourceAPI.h"
|
||||||
#include "modplatform/helpers/NetworkResourceAPI.h"
|
#include "modplatform/helpers/NetworkResourceAPI.h"
|
||||||
|
|
||||||
class FlameAPI : public NetworkResourceAPI {
|
class FlameAPI : public NetworkResourceAPI {
|
||||||
@ -21,8 +23,6 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
|
|
||||||
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
||||||
|
|
||||||
static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt); }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int getClassId(ModPlatform::ResourceType type)
|
static int getClassId(ModPlatform::ResourceType type)
|
||||||
{
|
{
|
||||||
@ -77,14 +77,48 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
|
|
||||||
[[nodiscard]] std::optional<QString> getVersionsURL(VersionSearchArgs const& args) const override
|
[[nodiscard]] std::optional<QString> getVersionsURL(VersionSearchArgs const& args) const override
|
||||||
{
|
{
|
||||||
QString url{ QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&").arg(args.pack.addonId.toString()) };
|
auto addonId = args.pack.addonId.toString();
|
||||||
|
QString url{ QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&").arg(addonId) };
|
||||||
|
|
||||||
QStringList get_parameters;
|
QStringList get_parameters;
|
||||||
if (args.mcVersions.has_value())
|
if (args.mcVersions.has_value())
|
||||||
get_parameters.append(QString("gameVersion=%1").arg(args.mcVersions.value().front().toString()));
|
get_parameters.append(QString("gameVersion=%1").arg(args.mcVersions.value().front().toString()));
|
||||||
if (args.loaders.has_value())
|
|
||||||
get_parameters.append(QString("modLoaderType=%1").arg(getMappedModLoader(args.loaders.value())));
|
if (args.loaders.has_value()) {
|
||||||
|
int mappedModLoader = getMappedModLoader(args.loaders.value());
|
||||||
|
|
||||||
|
if (args.loaders.value() & Quilt) {
|
||||||
|
auto overide = ModPlatform::getOverrideDeps();
|
||||||
|
auto over = std::find_if(overide.cbegin(), overide.cend(), [addonId](auto dep) {
|
||||||
|
return dep.provider == ModPlatform::ResourceProvider::FLAME && addonId == dep.quilt;
|
||||||
|
});
|
||||||
|
if (over != overide.cend()) {
|
||||||
|
mappedModLoader = 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get_parameters.append(QString("modLoaderType=%1").arg(mappedModLoader));
|
||||||
|
}
|
||||||
|
|
||||||
return url + get_parameters.join('&');
|
return url + get_parameters.join('&');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override
|
||||||
|
{
|
||||||
|
auto mappedModLoader = getMappedModLoader(args.loader);
|
||||||
|
auto addonId = args.dependency.addonId.toString();
|
||||||
|
if (args.loader & Quilt) {
|
||||||
|
auto overide = ModPlatform::getOverrideDeps();
|
||||||
|
auto over = std::find_if(overide.cbegin(), overide.cend(), [addonId](auto dep) {
|
||||||
|
return dep.provider == ModPlatform::ResourceProvider::FLAME && addonId == dep.quilt;
|
||||||
|
});
|
||||||
|
if (over != overide.cend()) {
|
||||||
|
mappedModLoader = 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&gameVersion=%2&modLoaderType=%3")
|
||||||
|
.arg(addonId)
|
||||||
|
.arg(args.mcVersion.toString())
|
||||||
|
.arg(mappedModLoader);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
@ -470,9 +470,8 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
|||||||
switch (result.type) {
|
switch (result.type) {
|
||||||
case Flame::File::Type::Folder: {
|
case Flame::File::Type::Folder: {
|
||||||
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
|
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
|
||||||
// fallthrough intentional, we treat these as plain old mods and dump them wherever.
|
// fall-through intentional, we treat these as plain old mods and dump them wherever.
|
||||||
}
|
}
|
||||||
/* fallthrough */
|
|
||||||
case Flame::File::Type::SingleFile:
|
case Flame::File::Type::SingleFile:
|
||||||
case Flame::File::Type::Mod: {
|
case Flame::File::Type::Mod: {
|
||||||
if (!result.url.isEmpty()) {
|
if (!result.url.isEmpty()) {
|
||||||
@ -563,8 +562,6 @@ void FlameCreationTask::validateZIPResouces()
|
|||||||
if (FS::move(localPath, destPath)) {
|
if (FS::move(localPath, destPath)) {
|
||||||
return destPath;
|
return destPath;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
qDebug() << "Target folder of" << fileName << "is correct at" << targetFolder;
|
|
||||||
}
|
}
|
||||||
return localPath;
|
return localPath;
|
||||||
};
|
};
|
||||||
@ -586,9 +583,6 @@ void FlameCreationTask::validateZIPResouces()
|
|||||||
QString worldPath;
|
QString worldPath;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case PackedResourceType::Mod :
|
|
||||||
validatePath(fileName, targetFolder, "mods");
|
|
||||||
break;
|
|
||||||
case PackedResourceType::ResourcePack :
|
case PackedResourceType::ResourcePack :
|
||||||
validatePath(fileName, targetFolder, "resourcepacks");
|
validatePath(fileName, targetFolder, "resourcepacks");
|
||||||
break;
|
break;
|
||||||
@ -598,6 +592,9 @@ void FlameCreationTask::validateZIPResouces()
|
|||||||
case PackedResourceType::DataPack :
|
case PackedResourceType::DataPack :
|
||||||
validatePath(fileName, targetFolder, "datapacks");
|
validatePath(fileName, targetFolder, "datapacks");
|
||||||
break;
|
break;
|
||||||
|
case PackedResourceType::Mod :
|
||||||
|
validatePath(fileName, targetFolder, "mods");
|
||||||
|
break;
|
||||||
case PackedResourceType::ShaderPack :
|
case PackedResourceType::ShaderPack :
|
||||||
// in theroy flame API can't do this but who knows, that *may* change ?
|
// in theroy flame API can't do this but who knows, that *may* change ?
|
||||||
// better to handle it if it *does* occure in the future
|
// better to handle it if it *does* occure in the future
|
||||||
|
@ -39,15 +39,15 @@ void FlameMod::loadURLs(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
|||||||
auto links_obj = Json::ensureObject(obj, "links");
|
auto links_obj = Json::ensureObject(obj, "links");
|
||||||
|
|
||||||
pack.extraData.issuesUrl = Json::ensureString(links_obj, "issuesUrl");
|
pack.extraData.issuesUrl = Json::ensureString(links_obj, "issuesUrl");
|
||||||
if(pack.extraData.issuesUrl.endsWith('/'))
|
if (pack.extraData.issuesUrl.endsWith('/'))
|
||||||
pack.extraData.issuesUrl.chop(1);
|
pack.extraData.issuesUrl.chop(1);
|
||||||
|
|
||||||
pack.extraData.sourceUrl = Json::ensureString(links_obj, "sourceUrl");
|
pack.extraData.sourceUrl = Json::ensureString(links_obj, "sourceUrl");
|
||||||
if(pack.extraData.sourceUrl.endsWith('/'))
|
if (pack.extraData.sourceUrl.endsWith('/'))
|
||||||
pack.extraData.sourceUrl.chop(1);
|
pack.extraData.sourceUrl.chop(1);
|
||||||
|
|
||||||
pack.extraData.wikiUrl = Json::ensureString(links_obj, "wikiUrl");
|
pack.extraData.wikiUrl = Json::ensureString(links_obj, "wikiUrl");
|
||||||
if(pack.extraData.wikiUrl.endsWith('/'))
|
if (pack.extraData.wikiUrl.endsWith('/'))
|
||||||
pack.extraData.wikiUrl.chop(1);
|
pack.extraData.wikiUrl.chop(1);
|
||||||
|
|
||||||
if (!pack.extraData.body.isEmpty())
|
if (!pack.extraData.body.isEmpty())
|
||||||
@ -56,7 +56,7 @@ void FlameMod::loadURLs(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
|||||||
|
|
||||||
void FlameMod::loadBody(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
void FlameMod::loadBody(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||||
{
|
{
|
||||||
pack.extraData.body = api.getModDescription(pack.addonId.toInt());
|
pack.extraData.body = api.getModDescription(pack.addonId.toInt());
|
||||||
|
|
||||||
if (!pack.extraData.issuesUrl.isEmpty() || !pack.extraData.sourceUrl.isEmpty() || !pack.extraData.wikiUrl.isEmpty())
|
if (!pack.extraData.issuesUrl.isEmpty() || !pack.extraData.sourceUrl.isEmpty() || !pack.extraData.wikiUrl.isEmpty())
|
||||||
pack.extraDataLoaded = true;
|
pack.extraDataLoaded = true;
|
||||||
@ -64,12 +64,12 @@ void FlameMod::loadBody(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
|||||||
|
|
||||||
static QString enumToString(int hash_algorithm)
|
static QString enumToString(int hash_algorithm)
|
||||||
{
|
{
|
||||||
switch(hash_algorithm){
|
switch (hash_algorithm) {
|
||||||
default:
|
default:
|
||||||
case 1:
|
case 1:
|
||||||
return "sha1";
|
return "sha1";
|
||||||
case 2:
|
case 2:
|
||||||
return "md5";
|
return "md5";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,10 +86,10 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
|||||||
auto obj = versionIter.toObject();
|
auto obj = versionIter.toObject();
|
||||||
|
|
||||||
auto file = loadIndexedPackVersion(obj);
|
auto file = loadIndexedPackVersion(obj);
|
||||||
if(!file.addonId.isValid())
|
if (!file.addonId.isValid())
|
||||||
file.addonId = pack.addonId;
|
file.addonId = pack.addonId;
|
||||||
|
|
||||||
if(file.fileId.isValid()) // Heuristic to check if the returned value is valid
|
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid
|
||||||
unsortedVersions.append(file);
|
unsortedVersions.append(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,8 +136,61 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(load_changelog)
|
auto dependencies = Json::ensureArray(obj, "dependencies");
|
||||||
|
for (auto d : dependencies) {
|
||||||
|
auto dep = Json::ensureObject(d);
|
||||||
|
ModPlatform::Dependency dependency;
|
||||||
|
dependency.addonId = Json::requireInteger(dep, "modId");
|
||||||
|
switch (Json::requireInteger(dep, "relationType")) {
|
||||||
|
case 1: // EmbeddedLibrary
|
||||||
|
dependency.type = ModPlatform::DependencyType::EMBEDDED;
|
||||||
|
break;
|
||||||
|
case 2: // OptionalDependency
|
||||||
|
dependency.type = ModPlatform::DependencyType::OPTIONAL;
|
||||||
|
break;
|
||||||
|
case 3: // RequiredDependency
|
||||||
|
dependency.type = ModPlatform::DependencyType::REQUIRED;
|
||||||
|
break;
|
||||||
|
case 4: // Tool
|
||||||
|
dependency.type = ModPlatform::DependencyType::TOOL;
|
||||||
|
break;
|
||||||
|
case 5: // Incompatible
|
||||||
|
dependency.type = ModPlatform::DependencyType::INCOMPATIBLE;
|
||||||
|
break;
|
||||||
|
case 6: // Include
|
||||||
|
dependency.type = ModPlatform::DependencyType::INCLUDE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dependency.type = ModPlatform::DependencyType::UNKNOWN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
file.dependencies.append(dependency);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (load_changelog)
|
||||||
file.changelog = api.getModFileChangelog(file.addonId.toInt(), file.fileId.toInt());
|
file.changelog = api.getModFileChangelog(file.addonId.toInt(), file.fileId.toInt());
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr)
|
||||||
|
{
|
||||||
|
QVector<ModPlatform::IndexedVersion> versions;
|
||||||
|
for (auto versionIter : arr) {
|
||||||
|
auto obj = versionIter.toObject();
|
||||||
|
|
||||||
|
auto file = loadIndexedPackVersion(obj);
|
||||||
|
if (!file.addonId.isValid())
|
||||||
|
file.addonId = m.addonId;
|
||||||
|
|
||||||
|
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid
|
||||||
|
versions.append(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
||||||
|
// dates are in RFC 3339 format
|
||||||
|
return a.date > b.date;
|
||||||
|
};
|
||||||
|
std::sort(versions.begin(), versions.end(), orderSortPredicate);
|
||||||
|
return versions.front();
|
||||||
|
};
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
#include "modplatform/ModIndex.h"
|
#include "modplatform/ModIndex.h"
|
||||||
|
|
||||||
#include "BaseInstance.h"
|
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
|
#include "BaseInstance.h"
|
||||||
|
|
||||||
namespace FlameMod {
|
namespace FlameMod {
|
||||||
|
|
||||||
@ -19,5 +19,5 @@ void loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
|||||||
const shared_qobject_ptr<QNetworkAccessManager>& network,
|
const shared_qobject_ptr<QNetworkAccessManager>& network,
|
||||||
const BaseInstance* inst);
|
const BaseInstance* inst);
|
||||||
auto loadIndexedPackVersion(QJsonObject& obj, bool load_changelog = false) -> ModPlatform::IndexedVersion;
|
auto loadIndexedPackVersion(QJsonObject& obj, bool load_changelog = false) -> ModPlatform::IndexedVersion;
|
||||||
|
auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion;
|
||||||
} // namespace FlameMod
|
} // namespace FlameMod
|
@ -4,6 +4,7 @@
|
|||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
|
|
||||||
namespace Flame {
|
namespace Flame {
|
||||||
|
|
||||||
@ -27,8 +28,7 @@ struct ModpackExtra {
|
|||||||
QString sourceUrl;
|
QString sourceUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IndexedPack
|
struct IndexedPack {
|
||||||
{
|
|
||||||
int addonId;
|
int addonId;
|
||||||
QString name;
|
QString name;
|
||||||
QString description;
|
QString description;
|
||||||
@ -43,9 +43,9 @@ struct IndexedPack
|
|||||||
ModpackExtra extra;
|
ModpackExtra extra;
|
||||||
};
|
};
|
||||||
|
|
||||||
void loadIndexedPack(IndexedPack & m, QJsonObject & obj);
|
void loadIndexedPack(IndexedPack& m, QJsonObject& obj);
|
||||||
void loadIndexedInfo(IndexedPack&, QJsonObject&);
|
void loadIndexedInfo(IndexedPack&, QJsonObject&);
|
||||||
void loadIndexedPackVersions(IndexedPack & m, QJsonArray & arr);
|
void loadIndexedPackVersions(IndexedPack& m, QJsonArray& arr);
|
||||||
}
|
} // namespace Flame
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(Flame::IndexedPack)
|
Q_DECLARE_METATYPE(Flame::IndexedPack)
|
||||||
|
@ -76,8 +76,13 @@ bool Flame::File::parseFromObject(const QJsonObject& obj, bool throw_on_blocked
|
|||||||
// It is also optional
|
// It is also optional
|
||||||
type = File::Type::SingleFile;
|
type = File::Type::SingleFile;
|
||||||
|
|
||||||
targetFolder = "mods";
|
if (fileName.endsWith(".zip")) {
|
||||||
|
// this is probably a resource pack
|
||||||
|
targetFolder = "resourcepacks";
|
||||||
|
} else {
|
||||||
|
// this is probably a mod, dunno what else could modpacks download
|
||||||
|
targetFolder = "mods";
|
||||||
|
}
|
||||||
// get the hash
|
// get the hash
|
||||||
hash = QString();
|
hash = QString();
|
||||||
auto hashes = Json::ensureArray(obj, "hashes");
|
auto hashes = Json::ensureArray(obj, "hashes");
|
||||||
|
@ -117,3 +117,32 @@ Task::Ptr NetworkResourceAPI::getProject(QString addonId, std::shared_ptr<QByteA
|
|||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Task::Ptr NetworkResourceAPI::getDependencyVersion(DependencySearchArgs&& args, DependencySearchCallbacks&& callbacks) const
|
||||||
|
{
|
||||||
|
auto versions_url_optional = getDependencyURL(args);
|
||||||
|
if (!versions_url_optional.has_value())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto versions_url = versions_url_optional.value();
|
||||||
|
|
||||||
|
auto netJob = makeShared<NetJob>(QString("%1::Dependency").arg(args.dependency.addonId.toString()), APPLICATION->network());
|
||||||
|
auto response = std::make_shared<QByteArray>();
|
||||||
|
|
||||||
|
netJob->addNetAction(Net::Download::makeByteArray(versions_url, response));
|
||||||
|
|
||||||
|
QObject::connect(netJob.get(), &NetJob::succeeded, [=] {
|
||||||
|
QJsonParseError parse_error{};
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||||
|
if (parse_error.error != QJsonParseError::NoError) {
|
||||||
|
qWarning() << "Error while parsing JSON response for getting versions at " << parse_error.offset
|
||||||
|
<< " reason: " << parse_error.errorString();
|
||||||
|
qWarning() << *response;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks.on_succeed(doc, args.dependency);
|
||||||
|
});
|
||||||
|
|
||||||
|
return netJob;
|
||||||
|
};
|
||||||
|
@ -15,9 +15,11 @@ class NetworkResourceAPI : public ResourceAPI {
|
|||||||
|
|
||||||
Task::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const override;
|
Task::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const override;
|
||||||
Task::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const override;
|
Task::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const override;
|
||||||
|
Task::Ptr getDependencyVersion(DependencySearchArgs&&, DependencySearchCallbacks&&) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
[[nodiscard]] virtual auto getSearchURL(SearchArgs const& args) const -> std::optional<QString> = 0;
|
[[nodiscard]] virtual auto getSearchURL(SearchArgs const& args) const -> std::optional<QString> = 0;
|
||||||
[[nodiscard]] virtual auto getInfoURL(QString const& id) const -> std::optional<QString> = 0;
|
[[nodiscard]] virtual auto getInfoURL(QString const& id) const -> std::optional<QString> = 0;
|
||||||
[[nodiscard]] virtual auto getVersionsURL(VersionSearchArgs const& args) const -> std::optional<QString> = 0;
|
[[nodiscard]] virtual auto getVersionsURL(VersionSearchArgs const& args) const -> std::optional<QString> = 0;
|
||||||
|
[[nodiscard]] virtual auto getDependencyURL(DependencySearchArgs const& args) const -> std::optional<QString> = 0;
|
||||||
};
|
};
|
||||||
|
@ -37,16 +37,16 @@
|
|||||||
|
|
||||||
#include <QtConcurrent>
|
#include <QtConcurrent>
|
||||||
|
|
||||||
|
#include "MMCZip.h"
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "MMCZip.h"
|
#include "settings/INISettingsObject.h"
|
||||||
#include "minecraft/GradleSpecifier.h"
|
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include "minecraft/MinecraftInstance.h"
|
||||||
#include "minecraft/PackProfile.h"
|
#include "minecraft/PackProfile.h"
|
||||||
#include "settings/INISettingsObject.h"
|
#include "minecraft/GradleSpecifier.h"
|
||||||
|
|
||||||
#include "Application.h"
|
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
|
#include "Application.h"
|
||||||
|
|
||||||
namespace LegacyFTB {
|
namespace LegacyFTB {
|
||||||
|
|
||||||
@ -65,7 +65,6 @@ void PackInstallTask::executeTask()
|
|||||||
void PackInstallTask::downloadPack()
|
void PackInstallTask::downloadPack()
|
||||||
{
|
{
|
||||||
setStatus(tr("Downloading zip for %1").arg(m_pack.name));
|
setStatus(tr("Downloading zip for %1").arg(m_pack.name));
|
||||||
setProgress(1, 4);
|
|
||||||
setAbortable(false);
|
setAbortable(false);
|
||||||
|
|
||||||
archivePath = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file);
|
archivePath = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file);
|
||||||
@ -79,10 +78,11 @@ void PackInstallTask::downloadPack()
|
|||||||
}
|
}
|
||||||
netJobContainer->addNetAction(Net::Download::makeFile(url, archivePath));
|
netJobContainer->addNetAction(Net::Download::makeFile(url, archivePath));
|
||||||
|
|
||||||
connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip);
|
connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded);
|
||||||
connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed);
|
connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed);
|
||||||
|
connect(netJobContainer.get(), &NetJob::progress, this, &PackInstallTask::onDownloadProgress);
|
||||||
connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress);
|
connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress);
|
||||||
connect(netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::emitAborted);
|
connect(netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::onDownloadAborted);
|
||||||
|
|
||||||
netJobContainer->start();
|
netJobContainer->start();
|
||||||
|
|
||||||
@ -90,6 +90,27 @@ void PackInstallTask::downloadPack()
|
|||||||
progress(1, 4);
|
progress(1, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PackInstallTask::onDownloadSucceeded()
|
||||||
|
{
|
||||||
|
unzip();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PackInstallTask::onDownloadFailed(QString reason)
|
||||||
|
{
|
||||||
|
emitFailed(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PackInstallTask::onDownloadProgress(qint64 current, qint64 total)
|
||||||
|
{
|
||||||
|
progress(current, total * 4);
|
||||||
|
setStatus(tr("Downloading zip for %1 (%2%)").arg(m_pack.name).arg(current / 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PackInstallTask::onDownloadAborted()
|
||||||
|
{
|
||||||
|
emitAborted();
|
||||||
|
}
|
||||||
|
|
||||||
void PackInstallTask::unzip()
|
void PackInstallTask::unzip()
|
||||||
{
|
{
|
||||||
setStatus(tr("Extracting modpack"));
|
setStatus(tr("Extracting modpack"));
|
||||||
@ -99,17 +120,16 @@ void PackInstallTask::unzip()
|
|||||||
QDir extractDir(m_stagingPath);
|
QDir extractDir(m_stagingPath);
|
||||||
|
|
||||||
m_packZip.reset(new QuaZip(archivePath));
|
m_packZip.reset(new QuaZip(archivePath));
|
||||||
if (!m_packZip->open(QuaZip::mdUnzip)) {
|
if(!m_packZip->open(QuaZip::mdUnzip))
|
||||||
|
{
|
||||||
emitFailed(tr("Failed to open modpack file %1!").arg(archivePath));
|
emitFailed(tr("Failed to open modpack file %1!").arg(archivePath));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), QOverload<QString, QString>::of(MMCZip::extractDir), archivePath,
|
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), QOverload<QString, QString>::of(MMCZip::extractDir), archivePath, extractDir.absolutePath() + "/unzip");
|
||||||
extractDir.absolutePath() + "/unzip");
|
|
||||||
#else
|
#else
|
||||||
m_extractFuture =
|
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, archivePath, extractDir.absolutePath() + "/unzip");
|
||||||
QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, archivePath, extractDir.absolutePath() + "/unzip");
|
|
||||||
#endif
|
#endif
|
||||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &PackInstallTask::onUnzipFinished);
|
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &PackInstallTask::onUnzipFinished);
|
||||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &PackInstallTask::onUnzipCanceled);
|
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &PackInstallTask::onUnzipCanceled);
|
||||||
@ -131,9 +151,11 @@ void PackInstallTask::install()
|
|||||||
setStatus(tr("Installing modpack"));
|
setStatus(tr("Installing modpack"));
|
||||||
progress(3, 4);
|
progress(3, 4);
|
||||||
QDir unzipMcDir(m_stagingPath + "/unzip/minecraft");
|
QDir unzipMcDir(m_stagingPath + "/unzip/minecraft");
|
||||||
if (unzipMcDir.exists()) {
|
if(unzipMcDir.exists())
|
||||||
// ok, found minecraft dir, move contents to instance dir
|
{
|
||||||
if (!QDir().rename(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/.minecraft")) {
|
//ok, found minecraft dir, move contents to instance dir
|
||||||
|
if(!QDir().rename(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/.minecraft"))
|
||||||
|
{
|
||||||
emitFailed(tr("Failed to move unzipped Minecraft!"));
|
emitFailed(tr("Failed to move unzipped Minecraft!"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -150,20 +172,23 @@ void PackInstallTask::install()
|
|||||||
|
|
||||||
bool fallback = true;
|
bool fallback = true;
|
||||||
|
|
||||||
// handle different versions
|
//handle different versions
|
||||||
QFile packJson(m_stagingPath + "/.minecraft/pack.json");
|
QFile packJson(m_stagingPath + "/.minecraft/pack.json");
|
||||||
QDir jarmodDir = QDir(m_stagingPath + "/unzip/instMods");
|
QDir jarmodDir = QDir(m_stagingPath + "/unzip/instMods");
|
||||||
if (packJson.exists()) {
|
if(packJson.exists())
|
||||||
|
{
|
||||||
packJson.open(QIODevice::ReadOnly | QIODevice::Text);
|
packJson.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(packJson.readAll());
|
QJsonDocument doc = QJsonDocument::fromJson(packJson.readAll());
|
||||||
packJson.close();
|
packJson.close();
|
||||||
|
|
||||||
// we only care about the libs
|
//we only care about the libs
|
||||||
QJsonArray libs = doc.object().value("libraries").toArray();
|
QJsonArray libs = doc.object().value("libraries").toArray();
|
||||||
|
|
||||||
foreach (const QJsonValue& value, libs) {
|
foreach (const QJsonValue &value, libs)
|
||||||
|
{
|
||||||
QString nameValue = value.toObject().value("name").toString();
|
QString nameValue = value.toObject().value("name").toString();
|
||||||
if (!nameValue.startsWith("net.minecraftforge")) {
|
if(!nameValue.startsWith("net.minecraftforge"))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,13 +199,16 @@ void PackInstallTask::install()
|
|||||||
fallback = false;
|
fallback = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jarmodDir.exists()) {
|
if(jarmodDir.exists())
|
||||||
|
{
|
||||||
qDebug() << "Found jarmods, installing...";
|
qDebug() << "Found jarmods, installing...";
|
||||||
|
|
||||||
QStringList jarmods;
|
QStringList jarmods;
|
||||||
for (auto info : jarmodDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) {
|
for (auto info: jarmodDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
|
||||||
|
{
|
||||||
qDebug() << "Jarmod:" << info.fileName();
|
qDebug() << "Jarmod:" << info.fileName();
|
||||||
jarmods.push_back(info.absoluteFilePath());
|
jarmods.push_back(info.absoluteFilePath());
|
||||||
}
|
}
|
||||||
@ -189,11 +217,12 @@ void PackInstallTask::install()
|
|||||||
fallback = false;
|
fallback = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// just nuke unzip directory, it s not needed anymore
|
//just nuke unzip directory, it s not needed anymore
|
||||||
FS::deletePath(m_stagingPath + "/unzip");
|
FS::deletePath(m_stagingPath + "/unzip");
|
||||||
|
|
||||||
if (fallback) {
|
if(fallback)
|
||||||
// TODO: Some fallback mechanism... or just keep failing!
|
{
|
||||||
|
//TODO: Some fallback mechanism... or just keep failing!
|
||||||
emitFailed(tr("No installation method found!"));
|
emitFailed(tr("No installation method found!"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -203,7 +232,8 @@ void PackInstallTask::install()
|
|||||||
progress(4, 4);
|
progress(4, 4);
|
||||||
|
|
||||||
instance.setName(name());
|
instance.setName(name());
|
||||||
if (m_instIcon == "default") {
|
if(m_instIcon == "default")
|
||||||
|
{
|
||||||
m_instIcon = "ftb_logo";
|
m_instIcon = "ftb_logo";
|
||||||
}
|
}
|
||||||
instance.setIconKey(m_instIcon);
|
instance.setIconKey(m_instIcon);
|
||||||
@ -222,4 +252,4 @@ bool PackInstallTask::abort()
|
|||||||
return InstanceTask::abort();
|
return InstanceTask::abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace LegacyFTB
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "InstanceTask.h"
|
||||||
|
#include "net/NetJob.h"
|
||||||
#include <quazip/quazip.h>
|
#include <quazip/quazip.h>
|
||||||
#include <quazip/quazipdir.h>
|
#include <quazip/quazipdir.h>
|
||||||
#include "InstanceTask.h"
|
|
||||||
#include "PackHelpers.h"
|
|
||||||
#include "meta/Index.h"
|
#include "meta/Index.h"
|
||||||
#include "meta/Version.h"
|
#include "meta/Version.h"
|
||||||
#include "meta/VersionList.h"
|
#include "meta/VersionList.h"
|
||||||
#include "net/NetJob.h"
|
#include "PackHelpers.h"
|
||||||
|
|
||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
|
|
||||||
@ -14,31 +14,36 @@
|
|||||||
|
|
||||||
namespace LegacyFTB {
|
namespace LegacyFTB {
|
||||||
|
|
||||||
class PackInstallTask : public InstanceTask {
|
class PackInstallTask : public InstanceTask
|
||||||
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit PackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, Modpack pack, QString version);
|
explicit PackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, Modpack pack, QString version);
|
||||||
virtual ~PackInstallTask() {}
|
virtual ~PackInstallTask(){}
|
||||||
|
|
||||||
bool canAbort() const override { return true; }
|
bool canAbort() const override { return true; }
|
||||||
bool abort() override;
|
bool abort() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//! Entry point for tasks.
|
//! Entry point for tasks.
|
||||||
virtual void executeTask() override;
|
virtual void executeTask() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void downloadPack();
|
void downloadPack();
|
||||||
void unzip();
|
void unzip();
|
||||||
void install();
|
void install();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void onDownloadSucceeded();
|
||||||
|
void onDownloadFailed(QString reason);
|
||||||
|
void onDownloadProgress(qint64 current, qint64 total);
|
||||||
|
void onDownloadAborted();
|
||||||
|
|
||||||
void onUnzipFinished();
|
void onUnzipFinished();
|
||||||
void onUnzipCanceled();
|
void onUnzipCanceled();
|
||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
||||||
bool abortable = false;
|
bool abortable = false;
|
||||||
std::unique_ptr<QuaZip> m_packZip;
|
std::unique_ptr<QuaZip> m_packZip;
|
||||||
@ -51,4 +56,4 @@ class PackInstallTask : public InstanceTask {
|
|||||||
QString m_version;
|
QString m_version;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace LegacyFTB
|
}
|
||||||
|
@ -38,7 +38,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList
|
static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList
|
||||||
{
|
{
|
||||||
QStringList l;
|
QStringList l;
|
||||||
for (auto loader : { Forge, Fabric, Quilt, LiteLoader }) {
|
for (auto loader : { Forge, Fabric, Quilt }) {
|
||||||
if (types & loader) {
|
if (types & loader) {
|
||||||
l << getModLoaderString(loader);
|
l << getModLoaderString(loader);
|
||||||
}
|
}
|
||||||
@ -51,8 +51,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
static auto getModLoaderFilters(ModLoaderTypes types) -> const QString
|
static auto getModLoaderFilters(ModLoaderTypes types) -> const QString
|
||||||
{
|
{
|
||||||
QStringList l;
|
QStringList l;
|
||||||
for (auto loader : getModLoaderStrings(types))
|
for (auto loader : getModLoaderStrings(types)) {
|
||||||
{
|
|
||||||
l << QString("\"categories:%1\"").arg(loader);
|
l << QString("\"categories:%1\"").arg(loader);
|
||||||
}
|
}
|
||||||
return l.join(',');
|
return l.join(',');
|
||||||
@ -93,7 +92,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
{
|
{
|
||||||
if (args.loaders.has_value()) {
|
if (args.loaders.has_value()) {
|
||||||
if (!validateModLoaders(args.loaders.value())) {
|
if (!validateModLoaders(args.loaders.value())) {
|
||||||
qWarning() << "Modrinth - or our interface - does not support any the provided mod loaders!";
|
qWarning() << "Modrinth only have Forge and Fabric-compatible mods!";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,13 +134,22 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
auto getGameVersionsArray(std::list<Version> mcVersions) const -> QString
|
auto getGameVersionsArray(std::list<Version> mcVersions) const -> QString
|
||||||
{
|
{
|
||||||
QString s;
|
QString s;
|
||||||
for(auto& ver : mcVersions){
|
for (auto& ver : mcVersions) {
|
||||||
s += QString("\"versions:%1\",").arg(ver.toString());
|
s += QString("\"versions:%1\",").arg(ver.toString());
|
||||||
}
|
}
|
||||||
s.remove(s.length() - 1, 1); //remove last comma
|
s.remove(s.length() - 1, 1); // remove last comma
|
||||||
return s.isEmpty() ? QString() : s;
|
return s.isEmpty() ? QString() : s;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt | LiteLoader); }
|
inline auto validateModLoaders(ModLoaderTypes loaders) const -> bool { return loaders & (Forge | Fabric | Quilt); }
|
||||||
|
|
||||||
|
[[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override
|
||||||
|
{
|
||||||
|
return args.dependency.version.length() != 0 ? QString("%1/version/%2").arg(BuildConfig.MODRINTH_PROD_URL, args.dependency.version)
|
||||||
|
: QString("%1/project/%2/version?game_versions=[\"%3\"]&loaders=[\"%4\"]")
|
||||||
|
.arg(BuildConfig.MODRINTH_PROD_URL)
|
||||||
|
.arg(args.dependency.addonId.toString())
|
||||||
|
.arg(args.mcVersion.toString())
|
||||||
|
.arg(getModLoaderStrings(args.loader).join("\",\""));
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
@ -263,13 +263,13 @@ void ModrinthPackExportTask::finish()
|
|||||||
|
|
||||||
QByteArray ModrinthPackExportTask::generateIndex()
|
QByteArray ModrinthPackExportTask::generateIndex()
|
||||||
{
|
{
|
||||||
QJsonObject out;
|
QJsonObject obj;
|
||||||
out["formatVersion"] = 1;
|
obj["formatVersion"] = 1;
|
||||||
out["game"] = "minecraft";
|
obj["game"] = "minecraft";
|
||||||
out["name"] = name;
|
obj["name"] = name;
|
||||||
out["versionId"] = version;
|
obj["versionId"] = version;
|
||||||
if (!summary.isEmpty())
|
if (!summary.isEmpty())
|
||||||
out["summary"] = summary;
|
obj["summary"] = summary;
|
||||||
|
|
||||||
if (mcInstance) {
|
if (mcInstance) {
|
||||||
auto profile = mcInstance->getPackProfile();
|
auto profile = mcInstance->getPackProfile();
|
||||||
@ -290,40 +290,30 @@ QByteArray ModrinthPackExportTask::generateIndex()
|
|||||||
if (forge != nullptr)
|
if (forge != nullptr)
|
||||||
dependencies["forge"] = forge->m_version;
|
dependencies["forge"] = forge->m_version;
|
||||||
|
|
||||||
out["dependencies"] = dependencies;
|
obj["dependencies"] = dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonArray filesOut;
|
QJsonArray files;
|
||||||
for (auto iterator = resolvedFiles.constBegin(); iterator != resolvedFiles.constEnd(); iterator++) {
|
QMapIterator<QString, ResolvedFile> iterator(resolvedFiles);
|
||||||
QJsonObject fileOut;
|
while (iterator.hasNext()) {
|
||||||
|
iterator.next();
|
||||||
|
|
||||||
QString path = iterator.key();
|
|
||||||
const ResolvedFile& value = iterator.value();
|
const ResolvedFile& value = iterator.value();
|
||||||
|
|
||||||
// detect disabled mod
|
QJsonObject file;
|
||||||
const QFileInfo pathInfo(path);
|
file["path"] = iterator.key();
|
||||||
if (pathInfo.suffix() == "disabled") {
|
file["downloads"] = QJsonArray({ iterator.value().url });
|
||||||
// rename it
|
|
||||||
path = pathInfo.dir().filePath(pathInfo.completeBaseName());
|
|
||||||
// ...and make it optional
|
|
||||||
QJsonObject env;
|
|
||||||
env["client"] = "optional";
|
|
||||||
env["server"] = "optional";
|
|
||||||
fileOut["env"] = env;
|
|
||||||
}
|
|
||||||
|
|
||||||
fileOut["path"] = path;
|
|
||||||
fileOut["downloads"] = QJsonArray{ iterator.value().url };
|
|
||||||
|
|
||||||
QJsonObject hashes;
|
QJsonObject hashes;
|
||||||
hashes["sha1"] = value.sha1;
|
hashes["sha1"] = value.sha1;
|
||||||
hashes["sha512"] = value.sha512;
|
hashes["sha512"] = value.sha512;
|
||||||
fileOut["hashes"] = hashes;
|
|
||||||
|
|
||||||
fileOut["fileSize"] = value.size;
|
file["hashes"] = hashes;
|
||||||
filesOut << fileOut;
|
file["fileSize"] = value.size;
|
||||||
|
|
||||||
|
files << file;
|
||||||
}
|
}
|
||||||
out["files"] = filesOut;
|
obj["files"] = files;
|
||||||
|
|
||||||
return QJsonDocument(out).toJson(QJsonDocument::Compact);
|
return QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include "minecraft/MinecraftInstance.h"
|
||||||
#include "minecraft/PackProfile.h"
|
#include "minecraft/PackProfile.h"
|
||||||
#include "net/NetJob.h"
|
#include "modplatform/ModIndex.h"
|
||||||
|
|
||||||
static ModrinthAPI api;
|
static ModrinthAPI api;
|
||||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||||
@ -140,6 +140,28 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t
|
|||||||
file.version_number = Json::requireString(obj, "version_number");
|
file.version_number = Json::requireString(obj, "version_number");
|
||||||
file.changelog = Json::requireString(obj, "changelog");
|
file.changelog = Json::requireString(obj, "changelog");
|
||||||
|
|
||||||
|
auto dependencies = Json::ensureArray(obj, "dependencies");
|
||||||
|
for (auto d : dependencies) {
|
||||||
|
auto dep = Json::ensureObject(d);
|
||||||
|
ModPlatform::Dependency dependency;
|
||||||
|
dependency.addonId = Json::ensureString(dep, "project_id");
|
||||||
|
dependency.version = Json::ensureString(dep, "version_id");
|
||||||
|
auto depType = Json::requireString(dep, "dependency_type");
|
||||||
|
|
||||||
|
if (depType == "required")
|
||||||
|
dependency.type = ModPlatform::DependencyType::REQUIRED;
|
||||||
|
else if (depType == "optional")
|
||||||
|
dependency.type = ModPlatform::DependencyType::OPTIONAL;
|
||||||
|
else if (depType == "incompatible")
|
||||||
|
dependency.type = ModPlatform::DependencyType::INCOMPATIBLE;
|
||||||
|
else if (depType == "embedded")
|
||||||
|
dependency.type = ModPlatform::DependencyType::EMBEDDED;
|
||||||
|
else
|
||||||
|
dependency.type = ModPlatform::DependencyType::UNKNOWN;
|
||||||
|
|
||||||
|
file.dependencies.append(dependency);
|
||||||
|
}
|
||||||
|
|
||||||
auto files = Json::requireArray(obj, "files");
|
auto files = Json::requireArray(obj, "files");
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
@ -195,3 +217,22 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t
|
|||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Modrinth::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion
|
||||||
|
{
|
||||||
|
QVector<ModPlatform::IndexedVersion> versions;
|
||||||
|
|
||||||
|
for (auto versionIter : arr) {
|
||||||
|
auto obj = versionIter.toObject();
|
||||||
|
auto file = loadIndexedPackVersion(obj);
|
||||||
|
|
||||||
|
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid
|
||||||
|
versions.append(file);
|
||||||
|
}
|
||||||
|
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
||||||
|
// dates are in RFC 3339 format
|
||||||
|
return a.date > b.date;
|
||||||
|
};
|
||||||
|
std::sort(versions.begin(), versions.end(), orderSortPredicate);
|
||||||
|
return versions.length() != 0 ? versions.front() : ModPlatform::IndexedVersion();
|
||||||
|
}
|
@ -19,8 +19,8 @@
|
|||||||
|
|
||||||
#include "modplatform/ModIndex.h"
|
#include "modplatform/ModIndex.h"
|
||||||
|
|
||||||
#include "BaseInstance.h"
|
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
|
#include "BaseInstance.h"
|
||||||
|
|
||||||
namespace Modrinth {
|
namespace Modrinth {
|
||||||
|
|
||||||
@ -31,5 +31,6 @@ void loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
|||||||
const shared_qobject_ptr<QNetworkAccessManager>& network,
|
const shared_qobject_ptr<QNetworkAccessManager>& network,
|
||||||
const BaseInstance* inst);
|
const BaseInstance* inst);
|
||||||
auto loadIndexedPackVersion(QJsonObject& obj, QString hash_type = "sha512", QString filename_prefer = "") -> ModPlatform::IndexedVersion;
|
auto loadIndexedPackVersion(QJsonObject& obj, QString hash_type = "sha512", QString filename_prefer = "") -> ModPlatform::IndexedVersion;
|
||||||
|
auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion;
|
||||||
|
|
||||||
} // namespace Modrinth
|
} // namespace Modrinth
|
||||||
|
@ -42,7 +42,6 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QLibraryInfo>
|
#include <QLibraryInfo>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <locale>
|
|
||||||
|
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
@ -455,7 +454,6 @@ QVariant TranslationsModel::data(const QModelIndex& index, int role) const
|
|||||||
return QString("%1%").arg(lang.percentTranslated(), 3, 'f', 1);
|
return QString("%1%").arg(lang.percentTranslated(), 3, 'f', 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qWarning("TranslationModel::data not implemented when role is DisplayRole");
|
|
||||||
}
|
}
|
||||||
case Qt::ToolTipRole:
|
case Qt::ToolTipRole:
|
||||||
{
|
{
|
||||||
@ -528,34 +526,34 @@ Language * TranslationsModel::findLanguage(const QString& key)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TranslationsModel::setUseSystemLocale(bool useSystemLocale)
|
|
||||||
{
|
|
||||||
APPLICATION->settings()->set("UseSystemLocale", useSystemLocale);
|
|
||||||
QLocale::setDefault(QLocale(useSystemLocale ? QString::fromStdString(std::locale().name()) : defaultLangCode));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TranslationsModel::selectLanguage(QString key)
|
bool TranslationsModel::selectLanguage(QString key)
|
||||||
{
|
{
|
||||||
QString& langCode = key;
|
QString &langCode = key;
|
||||||
auto langPtr = findLanguage(key);
|
auto langPtr = findLanguage(key);
|
||||||
|
|
||||||
if (langCode.isEmpty()) {
|
if (langCode.isEmpty())
|
||||||
|
{
|
||||||
d->no_language_set = true;
|
d->no_language_set = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!langPtr) {
|
if(!langPtr)
|
||||||
|
{
|
||||||
qWarning() << "Selected invalid language" << key << ", defaulting to" << defaultLangCode;
|
qWarning() << "Selected invalid language" << key << ", defaulting to" << defaultLangCode;
|
||||||
langCode = defaultLangCode;
|
langCode = defaultLangCode;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
langCode = langPtr->key;
|
langCode = langPtr->key;
|
||||||
}
|
}
|
||||||
|
|
||||||
// uninstall existing translators if there are any
|
// uninstall existing translators if there are any
|
||||||
if (d->m_app_translator) {
|
if (d->m_app_translator)
|
||||||
|
{
|
||||||
QCoreApplication::removeTranslator(d->m_app_translator.get());
|
QCoreApplication::removeTranslator(d->m_app_translator.get());
|
||||||
d->m_app_translator.reset();
|
d->m_app_translator.reset();
|
||||||
}
|
}
|
||||||
if (d->m_qt_translator) {
|
if (d->m_qt_translator)
|
||||||
|
{
|
||||||
QCoreApplication::removeTranslator(d->m_qt_translator.get());
|
QCoreApplication::removeTranslator(d->m_qt_translator.get());
|
||||||
d->m_qt_translator.reset();
|
d->m_qt_translator.reset();
|
||||||
}
|
}
|
||||||
@ -565,9 +563,8 @@ bool TranslationsModel::selectLanguage(QString key)
|
|||||||
* In a multithreaded application, the default locale should be set at application startup, before any non-GUI threads are created.
|
* In a multithreaded application, the default locale should be set at application startup, before any non-GUI threads are created.
|
||||||
* This function is not reentrant.
|
* This function is not reentrant.
|
||||||
*/
|
*/
|
||||||
QLocale::setDefault(
|
QLocale locale = QLocale(langCode);
|
||||||
QLocale(APPLICATION->settings()->get("UseSystemLocale").toBool() ? QString::fromStdString(std::locale().name()) : langCode));
|
QLocale::setDefault(locale);
|
||||||
|
|
||||||
|
|
||||||
// if it's the default UI language, finish
|
// if it's the default UI language, finish
|
||||||
if(langCode == defaultLangCode)
|
if(langCode == defaultLangCode)
|
||||||
|
@ -20,16 +20,17 @@
|
|||||||
|
|
||||||
struct Language;
|
struct Language;
|
||||||
|
|
||||||
class TranslationsModel : public QAbstractListModel {
|
class TranslationsModel : public QAbstractListModel
|
||||||
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit TranslationsModel(QString path, QObject* parent = 0);
|
explicit TranslationsModel(QString path, QObject *parent = 0);
|
||||||
virtual ~TranslationsModel();
|
virtual ~TranslationsModel();
|
||||||
|
|
||||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
int columnCount(const QModelIndex& parent) const override;
|
int columnCount(const QModelIndex & parent) const override;
|
||||||
|
|
||||||
bool selectLanguage(QString key);
|
bool selectLanguage(QString key);
|
||||||
void updateLanguage(QString key);
|
void updateLanguage(QString key);
|
||||||
@ -37,27 +38,27 @@ class TranslationsModel : public QAbstractListModel {
|
|||||||
QString selectedLanguage();
|
QString selectedLanguage();
|
||||||
|
|
||||||
void downloadIndex();
|
void downloadIndex();
|
||||||
void setUseSystemLocale(bool useSystemLocale);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Language* findLanguage(const QString& key);
|
Language *findLanguage(const QString & key);
|
||||||
void reloadLocalFiles();
|
void reloadLocalFiles();
|
||||||
void downloadTranslation(QString key);
|
void downloadTranslation(QString key);
|
||||||
void downloadNext();
|
void downloadNext();
|
||||||
|
|
||||||
// hide copy constructor
|
// hide copy constructor
|
||||||
TranslationsModel(const TranslationsModel&) = delete;
|
TranslationsModel(const TranslationsModel &) = delete;
|
||||||
// hide assign op
|
// hide assign op
|
||||||
TranslationsModel& operator=(const TranslationsModel&) = delete;
|
TranslationsModel &operator=(const TranslationsModel &) = delete;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void indexReceived();
|
void indexReceived();
|
||||||
void indexFailed(QString reason);
|
void indexFailed(QString reason);
|
||||||
void dlFailed(QString reason);
|
void dlFailed(QString reason);
|
||||||
void dlGood();
|
void dlGood();
|
||||||
void translationDirChanged(const QString& path);
|
void translationDirChanged(const QString &path);
|
||||||
|
|
||||||
private: /* data */
|
|
||||||
|
private: /* data */
|
||||||
struct Private;
|
struct Private;
|
||||||
std::unique_ptr<Private> d;
|
std::unique_ptr<Private> d;
|
||||||
};
|
};
|
||||||
|
@ -1279,17 +1279,7 @@ void MainWindow::globalSettingsClosed()
|
|||||||
|
|
||||||
void MainWindow::on_actionEditInstance_triggered()
|
void MainWindow::on_actionEditInstance_triggered()
|
||||||
{
|
{
|
||||||
|
APPLICATION->showInstanceWindow(m_selectedInstance);
|
||||||
if (!m_selectedInstance)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (m_selectedInstance->canEdit()) {
|
|
||||||
APPLICATION->showInstanceWindow(m_selectedInstance);
|
|
||||||
} else {
|
|
||||||
CustomMessageBox::selectable(this, tr("Instance not editable"),
|
|
||||||
tr("This instance is not editable. It may be broken, invalid, or too old. Check logs for details."),
|
|
||||||
QMessageBox::Critical)->show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionManageAccounts_triggered()
|
void MainWindow::on_actionManageAccounts_triggered()
|
||||||
|
@ -66,8 +66,6 @@ ExportInstanceDialog::ExportInstanceDialog(InstancePtr instance, QWidget* parent
|
|||||||
auto prefix = QDir(instance->instanceRoot()).relativeFilePath(instance->gameRoot());
|
auto prefix = QDir(instance->instanceRoot()).relativeFilePath(instance->gameRoot());
|
||||||
proxyModel->ignoreFilesWithPath().insert({ FS::PathCombine(prefix, "logs"), FS::PathCombine(prefix, "crash-reports") });
|
proxyModel->ignoreFilesWithPath().insert({ FS::PathCombine(prefix, "logs"), FS::PathCombine(prefix, "crash-reports") });
|
||||||
proxyModel->ignoreFilesWithName().append({ ".DS_Store", "thumbs.db", "Thumbs.db" });
|
proxyModel->ignoreFilesWithName().append({ ".DS_Store", "thumbs.db", "Thumbs.db" });
|
||||||
proxyModel->ignoreFilesWithPath().insert(
|
|
||||||
{ FS::PathCombine(prefix, ".cache"), FS::PathCombine(prefix, ".fabric"), FS::PathCombine(prefix, ".quilt") });
|
|
||||||
loadPackIgnore();
|
loadPackIgnore();
|
||||||
|
|
||||||
ui->treeView->setModel(proxyModel);
|
ui->treeView->setModel(proxyModel);
|
||||||
|
@ -52,7 +52,7 @@ ExportMrPackDialog::ExportMrPackDialog(InstancePtr instance, QWidget* parent)
|
|||||||
// use the game root - everything outside cannot be exported
|
// use the game root - everything outside cannot be exported
|
||||||
const QDir root(instance->gameRoot());
|
const QDir root(instance->gameRoot());
|
||||||
proxy = new FileIgnoreProxy(instance->gameRoot(), this);
|
proxy = new FileIgnoreProxy(instance->gameRoot(), this);
|
||||||
proxy->ignoreFilesWithPath().insert({ "logs", "crash-reports", ".cache", ".fabric", ".quilt" });
|
proxy->ignoreFilesWithPath().insert({ "logs", "crash-reports" });
|
||||||
proxy->ignoreFilesWithName().append({ ".DS_Store", "thumbs.db", "Thumbs.db" });
|
proxy->ignoreFilesWithName().append({ ".DS_Store", "thumbs.db", "Thumbs.db" });
|
||||||
proxy->setSourceModel(model);
|
proxy->setSourceModel(model);
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ProgressDialog.h"
|
#include "ProgressDialog.h"
|
||||||
#include <QPoint>
|
|
||||||
#include "ui_ProgressDialog.h"
|
#include "ui_ProgressDialog.h"
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
@ -67,9 +66,8 @@ ProgressDialog::ProgressDialog(QWidget* parent) : QDialog(parent), ui(new Ui::Pr
|
|||||||
ui->taskProgressScrollArea->setHidden(true);
|
ui->taskProgressScrollArea->setHidden(true);
|
||||||
this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
setAttribute(Qt::WidgetAttribute::WA_QuitOnClose, true);
|
setAttribute(Qt::WidgetAttribute::WA_QuitOnClose, true);
|
||||||
changeProgress(0, 100);
|
|
||||||
updateSize(true);
|
|
||||||
setSkipButton(false);
|
setSkipButton(false);
|
||||||
|
changeProgress(0, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProgressDialog::setSkipButton(bool present, QString label)
|
void ProgressDialog::setSkipButton(bool present, QString label)
|
||||||
@ -95,39 +93,25 @@ ProgressDialog::~ProgressDialog()
|
|||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProgressDialog::updateSize(bool recenterParent)
|
void ProgressDialog::updateSize()
|
||||||
{
|
{
|
||||||
QSize lastSize = this->size();
|
QSize lastSize = this->size();
|
||||||
QPoint lastPos = this->pos();
|
QSize qSize = QSize(480, minimumSizeHint().height());
|
||||||
int minHeight = ui->globalStatusDetailsLabel->minimumSize().height() + (ui->verticalLayout->spacing() * 2);
|
|
||||||
minHeight += ui->globalProgressBar->minimumSize().height() + ui->verticalLayout->spacing();
|
|
||||||
if (!ui->taskProgressScrollArea->isHidden())
|
|
||||||
minHeight += ui->taskProgressScrollArea->minimumSizeHint().height() + ui->verticalLayout->spacing();
|
|
||||||
if (ui->skipButton->isVisible())
|
|
||||||
minHeight += ui->skipButton->height() + ui->verticalLayout->spacing();
|
|
||||||
minHeight = std::max(minHeight, 60);
|
|
||||||
QSize minSize = QSize(480, minHeight);
|
|
||||||
|
|
||||||
setMinimumSize(minSize);
|
// if the current window is too small
|
||||||
adjustSize();
|
if ((lastSize != qSize) && (lastSize.height() < qSize.height()))
|
||||||
|
|
||||||
QSize newSize = this->size();
|
|
||||||
// if the current window is a different size
|
|
||||||
auto parent = this->parentWidget();
|
|
||||||
if (recenterParent && parent) {
|
|
||||||
auto newX = std::max(0, parent->x() + ((parent->width() - newSize.width()) / 2));
|
|
||||||
auto newY = std::max(0, parent->y() + ((parent->height() - newSize.height()) / 2));
|
|
||||||
this->move(newX, newY);
|
|
||||||
}
|
|
||||||
else if (lastSize != newSize)
|
|
||||||
{
|
{
|
||||||
// center on old position after resize
|
resize(qSize);
|
||||||
QSize sizeDiff = lastSize - newSize; // last size was smaller, the results should be negative
|
|
||||||
auto newX = std::max(0, lastPos.x() + (sizeDiff.width() / 2));
|
// keep the dialog in the center after a resize
|
||||||
auto newY = std::max(0, lastPos.y() + (sizeDiff.height() / 2));
|
this->move(
|
||||||
this->move(newX, newY);
|
this->parentWidget()->x() + (this->parentWidget()->width() - this->width()) / 2,
|
||||||
|
this->parentWidget()->y() + (this->parentWidget()->height() - this->height()) / 2
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setMinimumSize(qSize);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ProgressDialog::execWithTask(Task* task)
|
int ProgressDialog::execWithTask(Task* task)
|
||||||
@ -217,9 +201,7 @@ void ProgressDialog::onTaskSucceeded()
|
|||||||
void ProgressDialog::changeStatus(const QString& status)
|
void ProgressDialog::changeStatus(const QString& status)
|
||||||
{
|
{
|
||||||
ui->globalStatusLabel->setText(task->getStatus());
|
ui->globalStatusLabel->setText(task->getStatus());
|
||||||
ui->globalStatusLabel->adjustSize();
|
|
||||||
ui->globalStatusDetailsLabel->setText(task->getDetails());
|
ui->globalStatusDetailsLabel->setText(task->getDetails());
|
||||||
ui->globalStatusDetailsLabel->adjustSize();
|
|
||||||
|
|
||||||
updateSize();
|
updateSize();
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ public:
|
|||||||
explicit ProgressDialog(QWidget *parent = 0);
|
explicit ProgressDialog(QWidget *parent = 0);
|
||||||
~ProgressDialog();
|
~ProgressDialog();
|
||||||
|
|
||||||
void updateSize(bool recenterParent = false);
|
void updateSize();
|
||||||
|
|
||||||
int execWithTask(Task* task);
|
int execWithTask(Task* task);
|
||||||
int execWithTask(std::unique_ptr<Task> &&task);
|
int execWithTask(std::unique_ptr<Task> &&task);
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ResourceDownloadDialog.h"
|
#include "ResourceDownloadDialog.h"
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -30,6 +32,10 @@
|
|||||||
#include "minecraft/mod/ShaderPackFolderModel.h"
|
#include "minecraft/mod/ShaderPackFolderModel.h"
|
||||||
#include "minecraft/mod/TexturePackFolderModel.h"
|
#include "minecraft/mod/TexturePackFolderModel.h"
|
||||||
|
|
||||||
|
#include "minecraft/mod/tasks/GetModDependenciesTask.h"
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
|
#include "ui/dialogs/ProgressDialog.h"
|
||||||
#include "ui/dialogs/ReviewMessageBox.h"
|
#include "ui/dialogs/ReviewMessageBox.h"
|
||||||
|
|
||||||
#include "ui/pages/modplatform/ResourcePage.h"
|
#include "ui/pages/modplatform/ResourcePage.h"
|
||||||
@ -37,8 +43,6 @@
|
|||||||
#include "ui/pages/modplatform/flame/FlameResourcePages.h"
|
#include "ui/pages/modplatform/flame/FlameResourcePages.h"
|
||||||
#include "ui/pages/modplatform/modrinth/ModrinthResourcePages.h"
|
#include "ui/pages/modplatform/modrinth/ModrinthResourcePages.h"
|
||||||
|
|
||||||
#include "modplatform/flame/FlameAPI.h"
|
|
||||||
#include "modplatform/modrinth/ModrinthAPI.h"
|
|
||||||
#include "ui/widgets/PageContainer.h"
|
#include "ui/widgets/PageContainer.h"
|
||||||
|
|
||||||
namespace ResourceDownload {
|
namespace ResourceDownload {
|
||||||
@ -119,18 +123,71 @@ void ResourceDownloadDialog::connectButtons()
|
|||||||
connect(HelpButton, &QPushButton::clicked, m_container, &PageContainer::help);
|
connect(HelpButton, &QPushButton::clicked, m_container, &PageContainer::help);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||||
|
|
||||||
|
QStringList getRequiredBy(QList<ResourceDownloadDialog::DownloadTaskPtr> tasks, ResourceDownloadDialog::DownloadTaskPtr pack)
|
||||||
|
{
|
||||||
|
auto addonId = pack->getPack()->addonId;
|
||||||
|
auto provider = pack->getPack()->provider;
|
||||||
|
auto version = pack->getVersionID();
|
||||||
|
auto req = QStringList();
|
||||||
|
for (auto& task : tasks) {
|
||||||
|
if (provider != task->getPack()->provider)
|
||||||
|
continue;
|
||||||
|
auto deps = task->getVersion().dependencies;
|
||||||
|
if (auto dep = std::find_if(deps.begin(), deps.end(),
|
||||||
|
[addonId, provider, version](const ModPlatform::Dependency& d) {
|
||||||
|
return d.type == ModPlatform::DependencyType::REQUIRED &&
|
||||||
|
(provider == ModPlatform::ResourceProvider::MODRINTH && d.addonId.toString().isEmpty()
|
||||||
|
? version == d.version
|
||||||
|
: d.addonId == addonId);
|
||||||
|
});
|
||||||
|
dep != deps.end()) {
|
||||||
|
req.append(task->getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
void ResourceDownloadDialog::confirm()
|
void ResourceDownloadDialog::confirm()
|
||||||
{
|
{
|
||||||
|
auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourcesString()));
|
||||||
|
confirm_dialog->retranslateUi(resourcesString());
|
||||||
|
|
||||||
|
if (auto task = getModDependenciesTask(); task) {
|
||||||
|
connect(task.get(), &Task::failed, this,
|
||||||
|
[&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
||||||
|
|
||||||
|
connect(task.get(), &Task::succeeded, this, [&]() {
|
||||||
|
QStringList warnings = task->warnings();
|
||||||
|
if (warnings.count()) {
|
||||||
|
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->exec();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check for updates
|
||||||
|
ProgressDialog progress_dialog(this);
|
||||||
|
progress_dialog.setSkipButton(true, tr("Abort"));
|
||||||
|
progress_dialog.setWindowTitle(tr("Checking for dependencies..."));
|
||||||
|
auto ret = progress_dialog.execWithTask(task.get());
|
||||||
|
|
||||||
|
// If the dialog was skipped / some download error happened
|
||||||
|
if (ret == QDialog::DialogCode::Rejected) {
|
||||||
|
QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
for (auto dep : task->getDependecies())
|
||||||
|
addResource(dep->pack, dep->version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto selected = getTasks();
|
auto selected = getTasks();
|
||||||
std::sort(selected.begin(), selected.end(), [](const DownloadTaskPtr& a, const DownloadTaskPtr& b) {
|
std::sort(selected.begin(), selected.end(), [](const DownloadTaskPtr& a, const DownloadTaskPtr& b) {
|
||||||
return QString::compare(a->getName(), b->getName(), Qt::CaseInsensitive) < 0;
|
return QString::compare(a->getName(), b->getName(), Qt::CaseInsensitive) < 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourcesString()));
|
|
||||||
confirm_dialog->retranslateUi(resourcesString());
|
|
||||||
|
|
||||||
for (auto& task : selected) {
|
for (auto& task : selected) {
|
||||||
confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath() });
|
confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath(),
|
||||||
|
ProviderCaps.name(task->getProvider()), getRequiredBy(selected, task) });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (confirm_dialog->exec()) {
|
if (confirm_dialog->exec()) {
|
||||||
@ -224,11 +281,8 @@ QList<BasePage*> ModDownloadDialog::getPages()
|
|||||||
{
|
{
|
||||||
QList<BasePage*> pages;
|
QList<BasePage*> pages;
|
||||||
|
|
||||||
auto loaders = static_cast<MinecraftInstance*>(m_instance)->getPackProfile()->getModLoaders().value();
|
pages.append(ModrinthModPage::create(this, *m_instance));
|
||||||
|
if (APPLICATION->capabilities() & Application::SupportsFlame)
|
||||||
if (ModrinthAPI::validateModLoaders(loaders))
|
|
||||||
pages.append(ModrinthModPage::create(this, *m_instance));
|
|
||||||
if (APPLICATION->capabilities() & Application::SupportsFlame && FlameAPI::validateModLoaders(loaders))
|
|
||||||
pages.append(FlameModPage::create(this, *m_instance));
|
pages.append(FlameModPage::create(this, *m_instance));
|
||||||
|
|
||||||
m_selectedPage = dynamic_cast<ModPage*>(pages[0]);
|
m_selectedPage = dynamic_cast<ModPage*>(pages[0]);
|
||||||
@ -236,6 +290,19 @@ QList<BasePage*> ModDownloadDialog::getPages()
|
|||||||
return pages;
|
return pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GetModDependenciesTask::Ptr ModDownloadDialog::getModDependenciesTask()
|
||||||
|
{
|
||||||
|
if (auto model = dynamic_cast<ModFolderModel*>(getBaseModel().get()); model) {
|
||||||
|
QList<std::shared_ptr<GetModDependenciesTask::PackDependency>> selectedVers;
|
||||||
|
for (auto& selected : getTasks()) {
|
||||||
|
selectedVers.append(std::make_shared<GetModDependenciesTask::PackDependency>(selected->getPack(), selected->getVersion()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return makeShared<GetModDependenciesTask>(this, m_instance, model, selectedVers);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
ResourcePackDownloadDialog::ResourcePackDownloadDialog(QWidget* parent,
|
ResourcePackDownloadDialog::ResourcePackDownloadDialog(QWidget* parent,
|
||||||
const std::shared_ptr<ResourcePackFolderModel>& resource_packs,
|
const std::shared_ptr<ResourcePackFolderModel>& resource_packs,
|
||||||
BaseInstance* instance)
|
BaseInstance* instance)
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include <QLayout>
|
#include <QLayout>
|
||||||
|
|
||||||
#include "QObjectPtr.h"
|
#include "QObjectPtr.h"
|
||||||
|
#include "minecraft/mod/tasks/GetModDependenciesTask.h"
|
||||||
#include "modplatform/ModIndex.h"
|
#include "modplatform/ModIndex.h"
|
||||||
#include "ui/pages/BasePageProvider.h"
|
#include "ui/pages/BasePageProvider.h"
|
||||||
|
|
||||||
@ -81,6 +82,8 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider {
|
|||||||
[[nodiscard]] virtual QString geometrySaveKey() const { return ""; }
|
[[nodiscard]] virtual QString geometrySaveKey() const { return ""; }
|
||||||
void setButtonStatus();
|
void setButtonStatus();
|
||||||
|
|
||||||
|
[[nodiscard]] virtual GetModDependenciesTask::Ptr getModDependenciesTask() { return nullptr; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const std::shared_ptr<ResourceFolderModel> m_base_model;
|
const std::shared_ptr<ResourceFolderModel> m_base_model;
|
||||||
|
|
||||||
@ -103,6 +106,7 @@ class ModDownloadDialog final : public ResourceDownloadDialog {
|
|||||||
[[nodiscard]] QString geometrySaveKey() const override { return "ModDownloadGeometry"; }
|
[[nodiscard]] QString geometrySaveKey() const override { return "ModDownloadGeometry"; }
|
||||||
|
|
||||||
QList<BasePage*> getPages() override;
|
QList<BasePage*> getPages() override;
|
||||||
|
GetModDependenciesTask::Ptr getModDependenciesTask() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BaseInstance* m_instance;
|
BaseInstance* m_instance;
|
||||||
|
@ -40,7 +40,8 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info)
|
|||||||
auto filenameItem = new QTreeWidgetItem(itemTop);
|
auto filenameItem = new QTreeWidgetItem(itemTop);
|
||||||
filenameItem->setText(0, tr("Filename: %1").arg(info.filename));
|
filenameItem->setText(0, tr("Filename: %1").arg(info.filename));
|
||||||
|
|
||||||
itemTop->insertChildren(0, { filenameItem });
|
auto childIndx = 0;
|
||||||
|
itemTop->insertChildren(childIndx++, { filenameItem });
|
||||||
|
|
||||||
if (!info.custom_file_path.isEmpty()) {
|
if (!info.custom_file_path.isEmpty()) {
|
||||||
auto customPathItem = new QTreeWidgetItem(itemTop);
|
auto customPathItem = new QTreeWidgetItem(itemTop);
|
||||||
@ -49,7 +50,31 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info)
|
|||||||
itemTop->insertChildren(1, { customPathItem });
|
itemTop->insertChildren(1, { customPathItem });
|
||||||
|
|
||||||
itemTop->setIcon(1, QIcon(APPLICATION->getThemedIcon("status-yellow")));
|
itemTop->setIcon(1, QIcon(APPLICATION->getThemedIcon("status-yellow")));
|
||||||
itemTop->setToolTip(1, tr("This file will be downloaded to a folder location different from the default, possibly due to its loader requiring it."));
|
itemTop->setToolTip(
|
||||||
|
childIndx++,
|
||||||
|
tr("This file will be downloaded to a folder location different from the default, possibly due to its loader requiring it."));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto providerItem = new QTreeWidgetItem(itemTop);
|
||||||
|
providerItem->setText(0, tr("Provider: %1").arg(info.provider));
|
||||||
|
|
||||||
|
itemTop->insertChildren(childIndx++, { providerItem });
|
||||||
|
|
||||||
|
if (!info.required_by.isEmpty()) {
|
||||||
|
auto requiredByItem = new QTreeWidgetItem(itemTop);
|
||||||
|
if (info.required_by.length() == 1) {
|
||||||
|
requiredByItem->setText(0, tr("Required by: %1").arg(info.required_by.back()));
|
||||||
|
} else {
|
||||||
|
requiredByItem->setText(0, tr("Required by:"));
|
||||||
|
auto i = 0;
|
||||||
|
for (auto req : info.required_by) {
|
||||||
|
auto reqItem = new QTreeWidgetItem(requiredByItem);
|
||||||
|
reqItem->setText(0, req);
|
||||||
|
reqItem->insertChildren(i++, { reqItem });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
itemTop->insertChildren(childIndx++, { requiredByItem });
|
||||||
}
|
}
|
||||||
|
|
||||||
ui->modTreeWidget->addTopLevelItem(itemTop);
|
ui->modTreeWidget->addTopLevelItem(itemTop);
|
||||||
|
@ -15,7 +15,9 @@ class ReviewMessageBox : public QDialog {
|
|||||||
using ResourceInformation = struct res_info {
|
using ResourceInformation = struct res_info {
|
||||||
QString name;
|
QString name;
|
||||||
QString filename;
|
QString filename;
|
||||||
QString custom_file_path {};
|
QString custom_file_path{};
|
||||||
|
QString provider;
|
||||||
|
QStringList required_by;
|
||||||
};
|
};
|
||||||
|
|
||||||
void appendResource(ResourceInformation&& info);
|
void appendResource(ResourceInformation&& info);
|
||||||
|
@ -1,16 +1,36 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* you may not use this file except in compliance with the License.
|
* it under the terms of the GNU General Public License as published by
|
||||||
* You may obtain a copy of the License at
|
* the Free Software Foundation, version 3.
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* You should have received a copy of the GNU General Public License
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
*
|
||||||
* See the License for the specific language governing permissions and
|
* This file incorporates work covered by the following copyright and
|
||||||
* limitations under the License.
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "VersionSelectDialog.h"
|
#include "VersionSelectDialog.h"
|
||||||
@ -22,15 +42,10 @@
|
|||||||
#include <QtWidgets/QVBoxLayout>
|
#include <QtWidgets/QVBoxLayout>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include "ui/dialogs/ProgressDialog.h"
|
|
||||||
#include "ui/widgets/VersionSelectWidget.h"
|
#include "ui/widgets/VersionSelectWidget.h"
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
|
||||||
|
|
||||||
#include "BaseVersion.h"
|
#include "BaseVersion.h"
|
||||||
#include "BaseVersionList.h"
|
#include "BaseVersionList.h"
|
||||||
#include "tasks/Task.h"
|
|
||||||
#include "Application.h"
|
|
||||||
#include "VersionProxyModel.h"
|
|
||||||
|
|
||||||
VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent, bool cancelable)
|
VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent, bool cancelable)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
@ -40,7 +55,7 @@ VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title,
|
|||||||
m_verticalLayout = new QVBoxLayout(this);
|
m_verticalLayout = new QVBoxLayout(this);
|
||||||
m_verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
|
m_verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
|
||||||
|
|
||||||
m_versionWidget = new VersionSelectWidget(parent);
|
m_versionWidget = new VersionSelectWidget(true, parent);
|
||||||
m_verticalLayout->addWidget(m_versionWidget);
|
m_verticalLayout->addWidget(m_versionWidget);
|
||||||
|
|
||||||
m_horizontalLayout = new QHBoxLayout();
|
m_horizontalLayout = new QHBoxLayout();
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user