mirror of
https://github.com/PrismLauncher/PrismLauncher
synced 2026-04-23 09:05:03 +00:00
Merge branch 'develop' into desysteminfo
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
This commit is contained in:
commit
19aab36a4d
12
.github/actions/package/linux/action.yml
vendored
12
.github/actions/package/linux/action.yml
vendored
|
|
@ -13,10 +13,6 @@ inputs:
|
|||
description: Name of the uploaded artifact
|
||||
required: true
|
||||
default: Linux
|
||||
cmake-preset:
|
||||
description: Base CMake preset previously used for the build
|
||||
required: true
|
||||
default: linux
|
||||
qt-version:
|
||||
description: Version of Qt to use
|
||||
required: true
|
||||
|
|
@ -62,7 +58,7 @@ runs:
|
|||
|
||||
GPG_PRIVATE_KEY: ${{ inputs.gpg-private-key }}
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
|
||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
|
||||
|
||||
mv ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.metainfo.xml ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.appdata.xml
|
||||
export "NO_APPSTREAM=1" # we have to skip appstream checking because appstream on ubuntu 20.04 is outdated
|
||||
|
|
@ -107,12 +103,10 @@ runs:
|
|||
env:
|
||||
BUILD_DIR: build
|
||||
|
||||
CMAKE_PRESET: ${{ inputs.cmake-preset }}
|
||||
|
||||
INSTALL_PORTABLE_DIR: install-portable
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
|
||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||
|
||||
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
|
||||
cd ${{ env.INSTALL_PORTABLE_DIR }}
|
||||
|
|
|
|||
2
.github/actions/package/macos/action.yml
vendored
2
.github/actions/package/macos/action.yml
vendored
|
|
@ -59,7 +59,7 @@ runs:
|
|||
BUILD_DIR: build
|
||||
INSTALL_DIR: install
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }}
|
||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }}
|
||||
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
||||
|
|
|
|||
102
.github/actions/package/windows/action.yml
vendored
102
.github/actions/package/windows/action.yml
vendored
|
|
@ -15,12 +15,15 @@ inputs:
|
|||
msystem:
|
||||
description: MSYS2 subsystem to use
|
||||
required: false
|
||||
windows-codesign-cert:
|
||||
description: Certificate for signing Windows builds
|
||||
required: false
|
||||
windows-codesign-password:
|
||||
description: Password for signing Windows builds
|
||||
required: false
|
||||
azure-client-id:
|
||||
description: Client ID for the Azure Signer Application
|
||||
required: true
|
||||
azure-tenant-id:
|
||||
description: Tenant ID for the Azure Signer Application
|
||||
required: true
|
||||
azure-subscription-id:
|
||||
description: Subscription ID for the Azure Signer Application
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
|
|
@ -33,7 +36,7 @@ runs:
|
|||
BUILD_DIR: build
|
||||
INSTALL_DIR: install
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }}
|
||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }}
|
||||
touch ${{ env.INSTALL_DIR }}/manifest.txt
|
||||
for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_DIR }}/manifest.txt
|
||||
|
||||
|
|
@ -50,23 +53,45 @@ runs:
|
|||
|
||||
Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
|
||||
|
||||
- name: Fetch codesign certificate
|
||||
shell: bash # yes, we are not using MSYS2 or PowerShell here
|
||||
run: |
|
||||
echo '${{ inputs.windows-codesign-cert }}' | base64 --decode > codesign.pfx
|
||||
|
||||
- name: Sign executable
|
||||
- name: Emit warning for unsigned builds
|
||||
if: ${{ github.ref_name != 'develop' || inputs.azure-client-id == '' }}
|
||||
shell: pwsh
|
||||
env:
|
||||
INSTALL_DIR: install
|
||||
run: |
|
||||
if (Get-Content ./codesign.pfx){
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
# We ship the exact same executable for portable and non-portable editions, so signing just once is fine
|
||||
SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ inputs.windows-codesign-password }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_updater.exe prismlauncher_filelink.exe
|
||||
} else {
|
||||
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||
}
|
||||
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Login to Azure
|
||||
if: ${{ github.ref_name == 'develop' && inputs.azure-client-id != '' }}
|
||||
uses: azure/login@v2
|
||||
with:
|
||||
client-id: ${{ inputs.azure-client-id }}
|
||||
tenant-id: ${{ inputs.azure-tenant-id }}
|
||||
subscription-id: ${{ inputs.azure-subscription-id }}
|
||||
|
||||
- name: Sign executables
|
||||
if: ${{ github.ref_name == 'develop' && inputs.azure-client-id != '' }}
|
||||
uses: azure/trusted-signing-action@v0
|
||||
with:
|
||||
endpoint: https://eus.codesigning.azure.net/
|
||||
trusted-signing-account-name: PrismLauncher
|
||||
certificate-profile-name: PrismLauncher
|
||||
|
||||
files: |
|
||||
${{ github.workspace }}\install\prismlauncher.exe
|
||||
${{ github.workspace }}\install\prismlauncher_filelink.exe
|
||||
${{ github.workspace }}\install\prismlauncher_updater.exe
|
||||
|
||||
# TODO(@getchoo): Is this all really needed???
|
||||
# https://github.com/Azure/trusted-signing-action/blob/fc390cf8ed0f14e248a542af1d838388a47c7a7c/docs/OIDC.md
|
||||
exclude-environment-credential: true
|
||||
exclude-workload-identity-credential: true
|
||||
exclude-managed-identity-credential: true
|
||||
exclude-shared-token-cache-credential: true
|
||||
exclude-visual-studio-credential: true
|
||||
exclude-visual-studio-code-credential: true
|
||||
exclude-azure-cli-credential: false
|
||||
exclude-azure-powershell-credential: true
|
||||
exclude-azure-developer-cli-credential: true
|
||||
exclude-interactive-browser-credential: true
|
||||
|
||||
- name: Package (MinGW, portable)
|
||||
if: ${{ inputs.msystem != '' }}
|
||||
|
|
@ -77,7 +102,7 @@ runs:
|
|||
INSTALL_PORTABLE_DIR: install-portable
|
||||
run: |
|
||||
cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
|
||||
|
||||
- name: Package (MSVC, portable)
|
||||
|
|
@ -89,7 +114,7 @@ runs:
|
|||
INSTALL_PORTABLE_DIR: install-portable
|
||||
run: |
|
||||
cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -115,13 +140,28 @@ runs:
|
|||
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
|
||||
|
||||
- name: Sign installer
|
||||
shell: pwsh
|
||||
run: |
|
||||
if (Get-Content ./codesign.pfx){
|
||||
SignTool sign /fd sha256 /td sha256 /f codesign.pfx /p '${{ inputs.windows-codesign-password }}' /tr http://timestamp.digicert.com PrismLauncher-Setup.exe
|
||||
} else {
|
||||
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||
}
|
||||
if: ${{ github.ref_name == 'develop' && inputs.azure-client-id != '' }}
|
||||
uses: azure/trusted-signing-action@v0
|
||||
with:
|
||||
endpoint: https://eus.codesigning.azure.net/
|
||||
trusted-signing-account-name: PrismLauncher
|
||||
certificate-profile-name: PrismLauncher
|
||||
|
||||
files: |
|
||||
${{ github.workspace }}\PrismLauncher-Setup.exe
|
||||
|
||||
# TODO(@getchoo): Is this all really needed???
|
||||
# https://github.com/Azure/trusted-signing-action/blob/fc390cf8ed0f14e248a542af1d838388a47c7a7c/docs/OIDC.md
|
||||
exclude-environment-credential: true
|
||||
exclude-workload-identity-credential: true
|
||||
exclude-managed-identity-credential: true
|
||||
exclude-shared-token-cache-credential: true
|
||||
exclude-visual-studio-credential: true
|
||||
exclude-visual-studio-code-credential: true
|
||||
exclude-azure-cli-credential: false
|
||||
exclude-azure-powershell-credential: true
|
||||
exclude-azure-developer-cli-credential: true
|
||||
exclude-interactive-browser-credential: true
|
||||
|
||||
- name: Upload binary zip
|
||||
uses: actions/upload-artifact@v5
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ runs:
|
|||
sudo apt-get -y install \
|
||||
dpkg-dev \
|
||||
ninja-build extra-cmake-modules scdoc \
|
||||
libqrencode-dev \
|
||||
appstream libxcb-cursor-dev
|
||||
|
||||
- name: Setup AppImage tooling
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ runs:
|
|||
qt6-5compat:p
|
||||
qt6-networkauth:p
|
||||
cmark:p
|
||||
qrencode:p
|
||||
tomlplusplus:p
|
||||
quazip-qt6:p
|
||||
|
||||
|
|
|
|||
60
.github/workflows/build.yml
vendored
60
.github/workflows/build.yml
vendored
|
|
@ -70,6 +70,8 @@ jobs:
|
|||
name: Build (${{ matrix.artifact-name }})
|
||||
|
||||
permissions:
|
||||
# Required for Azure Trusted Signing
|
||||
id-token: write
|
||||
# Required for vcpkg binary cache
|
||||
packages: write
|
||||
|
||||
|
|
@ -79,7 +81,7 @@ jobs:
|
|||
include:
|
||||
- os: ubuntu-22.04
|
||||
artifact-name: Linux
|
||||
base-cmake-preset: linux
|
||||
cmake-preset: linux
|
||||
|
||||
# NOTE(@getchoo): Yes, we're intentionally using 24.04 here!!!
|
||||
#
|
||||
|
|
@ -87,34 +89,34 @@ jobs:
|
|||
# *for the same version* are compiled against 24.04 on ARM, and *not* 22.04 like x64
|
||||
- os: ubuntu-24.04-arm
|
||||
artifact-name: Linux-aarch64
|
||||
base-cmake-preset: linux
|
||||
cmake-preset: linux
|
||||
|
||||
- os: windows-2022
|
||||
artifact-name: Windows-MinGW-w64
|
||||
base-cmake-preset: windows_mingw
|
||||
cmake-preset: windows_mingw
|
||||
msystem: CLANG64
|
||||
vcvars-arch: amd64_x86
|
||||
|
||||
- os: windows-11-arm
|
||||
artifact-name: Windows-MinGW-arm64
|
||||
base-cmake-preset: windows_mingw
|
||||
cmake-preset: windows_mingw
|
||||
msystem: CLANGARM64
|
||||
vcvars-arch: arm64
|
||||
|
||||
- os: windows-2022
|
||||
artifact-name: Windows-MSVC
|
||||
base-cmake-preset: windows_msvc
|
||||
cmake-preset: windows_msvc
|
||||
# TODO(@getchoo): This is the default in setup-dependencies/windows. Why isn't it working?!?!
|
||||
vcvars-arch: amd64
|
||||
|
||||
- os: windows-11-arm
|
||||
artifact-name: Windows-MSVC-arm64
|
||||
base-cmake-preset: windows_msvc
|
||||
cmake-preset: windows_msvc
|
||||
vcvars-arch: arm64
|
||||
|
||||
- os: macos-14
|
||||
artifact-name: macOS
|
||||
base-cmake-preset: ${{ (inputs.build-type || 'Debug') == 'Debug' && 'macos_universal' || 'macos' }}
|
||||
cmake-preset: macos_universal
|
||||
macosx-deployment-target: 12.0
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
|
@ -124,6 +126,11 @@ jobs:
|
|||
shell: ${{ matrix.msystem != '' && 'msys2 {0}' || 'bash' }}
|
||||
|
||||
env:
|
||||
ARTIFACT_NAME: ${{ matrix.artifact-name }}-Qt6
|
||||
BUILD_PLATFORM: official
|
||||
BUILD_TYPE: ${{ inputs.build-type || 'Debug' }}
|
||||
CMAKE_PRESET: ${{ matrix.cmake-preset }}
|
||||
|
||||
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx-deployment-target }}
|
||||
|
||||
steps:
|
||||
|
|
@ -140,7 +147,7 @@ jobs:
|
|||
id: setup-dependencies
|
||||
uses: ./.github/actions/setup-dependencies
|
||||
with:
|
||||
build-type: ${{ inputs.build-type || 'Debug' }}
|
||||
build-type: ${{ env.BUILD_TYPE }}
|
||||
artifact-name: ${{ matrix.artifact-name }}
|
||||
msystem: ${{ matrix.msystem }}
|
||||
vcvars-arch: ${{ matrix.vcvars-arch }}
|
||||
|
|
@ -150,22 +157,17 @@ jobs:
|
|||
# BUILD
|
||||
##
|
||||
|
||||
- name: Get CMake preset
|
||||
id: cmake-preset
|
||||
env:
|
||||
BASE_CMAKE_PRESET: ${{ matrix.base-cmake-preset }}
|
||||
PRESET_TYPE: ${{ (inputs.build-type || 'Debug') == 'Debug' && 'debug' || 'ci' }}
|
||||
- name: Configure project
|
||||
run: |
|
||||
echo preset="$BASE_CMAKE_PRESET"_"$PRESET_TYPE" >> "$GITHUB_OUTPUT"
|
||||
cmake --preset "$CMAKE_PRESET"
|
||||
|
||||
- name: Run CMake workflow
|
||||
env:
|
||||
CMAKE_PRESET: ${{ steps.cmake-preset.outputs.preset }}
|
||||
|
||||
ARTIFACT_NAME: ${{ matrix.artifact-name }}-Qt6
|
||||
BUILD_PLATFORM: official
|
||||
- name: Run build
|
||||
run: |
|
||||
cmake --workflow --preset "$CMAKE_PRESET"
|
||||
cmake --build --preset "$CMAKE_PRESET" --config "$BUILD_TYPE"
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
ctest --preset "$CMAKE_PRESET" --build-config "$BUILD_TYPE"
|
||||
|
||||
##
|
||||
# PACKAGE
|
||||
|
|
@ -184,7 +186,6 @@ jobs:
|
|||
version: ${{ steps.short-version.outputs.version }}
|
||||
build-type: ${{ steps.setup-dependencies.outputs.build-type }}
|
||||
artifact-name: ${{ matrix.artifact-name }}
|
||||
cmake-preset: ${{ steps.cmake-preset.outputs.preset }}
|
||||
qt-version: ${{ steps.setup-dependencies.outputs.qt-version }}
|
||||
|
||||
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
|
|
@ -198,13 +199,13 @@ jobs:
|
|||
build-type: ${{ steps.setup-dependencies.outputs.build-type }}
|
||||
artifact-name: ${{ matrix.artifact-name }}
|
||||
|
||||
apple-codesign-cert: ${{ secrets.APPLE-CODESIGN-CERT }}
|
||||
apple-codesign-password: ${{ secrets.APPLE-CODESIGN_PASSWORD }}
|
||||
apple-codesign-id: ${{ secrets.APPLE-CODESIGN_ID }}
|
||||
apple-codesign-cert: ${{ secrets.APPLE_CODESIGN_CERT }}
|
||||
apple-codesign-password: ${{ secrets.APPLE_CODESIGN_PASSWORD }}
|
||||
apple-codesign-id: ${{ secrets.APPLE_CODESIGN_ID }}
|
||||
apple-notarize-apple-id: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
|
||||
apple-notarize-team-id: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
|
||||
apple-notarize-password: ${{ secrets.APPLE-NOTARIZE_PASSWORD }}
|
||||
sparkle-ed25519-key: ${{ secrets.SPARKLE-ED25519_KEY }}
|
||||
apple-notarize-password: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
|
||||
sparkle-ed25519-key: ${{ secrets.SPARKLE_ED25519_KEY }}
|
||||
|
||||
- name: Package (Windows)
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
|
|
@ -215,5 +216,6 @@ jobs:
|
|||
artifact-name: ${{ matrix.artifact-name }}
|
||||
msystem: ${{ matrix.msystem }}
|
||||
|
||||
windows-codesign-cert: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
||||
windows-codesign-password: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
azure-subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||
|
|
|
|||
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
|
|
@ -78,8 +78,8 @@ jobs:
|
|||
|
||||
- name: Configure and Build
|
||||
run: |
|
||||
cmake --preset linux_debug
|
||||
cmake --build --preset linux_debug
|
||||
cmake --preset linux
|
||||
cmake --build --preset linux --config Debug
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
|
|
|
|||
2
.github/workflows/update-flake.yml
vendored
2
.github/workflows/update-flake.yml
vendored
|
|
@ -17,7 +17,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: cachix/install-nix-action@fd24c48048070c1be9acd18c9d369a83f0fe94d7 # v31
|
||||
- uses: cachix/install-nix-action@0b0e072294b088b73964f1d72dfdac0951439dbd # v31
|
||||
|
||||
- uses: DeterminateSystems/update-flake-lock@v27
|
||||
with:
|
||||
|
|
|
|||
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -19,6 +19,3 @@
|
|||
[submodule "flatpak/shared-modules"]
|
||||
path = flatpak/shared-modules
|
||||
url = https://github.com/flathub/shared-modules.git
|
||||
[submodule "libraries/qrcodegenerator"]
|
||||
path = libraries/qrcodegenerator
|
||||
url = https://github.com/nayuki/QR-Code-generator
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}"
|
|||
|
||||
# Export compile commands for debug builds if we can (useful in LSPs like clangd)
|
||||
# https://cmake.org/cmake/help/v3.31/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html
|
||||
if(CMAKE_GENERATOR STREQUAL "Unix Makefiles" OR CMAKE_GENERATOR STREQUAL "Ninja" AND CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
if(CMAKE_GENERATOR STREQUAL "Unix Makefiles" OR CMAKE_GENERATOR MATCHES "^Ninja")
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
endif()
|
||||
|
||||
|
|
@ -345,12 +345,24 @@ if(Launcher_QT_VERSION_MAJOR EQUAL 6)
|
|||
set(QT_LIBEXECS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBEXECS})
|
||||
endif()
|
||||
|
||||
# Find libqrencode
|
||||
## NOTE(@getchoo): Never use pkg-config with MSVC since the vcpkg port makes our install bundle fail to find the dll
|
||||
if(MSVC)
|
||||
find_path(LIBQRENCODE_INCLUDE_DIR qrencode.h REQUIRED)
|
||||
find_library(LIBQRENCODE_LIBRARY_RELEASE qrencode REQUIRED)
|
||||
find_library(LIBQRENCODE_LIBRARY_DEBUG qrencoded)
|
||||
set(LIBQRENCODE_LIBRARIES optimized ${LIBQRENCODE_LIBRARY_RELEASE} debug ${LIBQRENCODE_LIBRARY_DEBUG})
|
||||
else()
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(libqrencode REQUIRED IMPORTED_TARGET libqrencode)
|
||||
endif()
|
||||
|
||||
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
||||
# Find toml++
|
||||
find_package(tomlplusplus 3.2.0 QUIET)
|
||||
# Fallback to pkg-config (if available) if CMake files aren't found
|
||||
if(NOT tomlplusplus_FOUND)
|
||||
find_package(PkgConfig)
|
||||
find_package(PkgConfig QUIET)
|
||||
if(PkgConfig_FOUND)
|
||||
pkg_check_modules(tomlplusplus IMPORTED_TARGET tomlplusplus>=3.2.0)
|
||||
endif()
|
||||
|
|
@ -359,9 +371,6 @@ if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
|||
|
||||
# Find cmark
|
||||
find_package(cmark QUIET)
|
||||
|
||||
# Find qrcodegencpp-cmake
|
||||
find_package(qrcodegencpp QUIET)
|
||||
endif()
|
||||
|
||||
include(ECMQtDeclareLoggingCategory)
|
||||
|
|
@ -405,8 +414,8 @@ if(UNIX AND APPLE)
|
|||
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=" CACHE STRING "Public key for Sparkle update feed")
|
||||
set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml" CACHE STRING "URL for Sparkle update feed")
|
||||
|
||||
set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.6.4/Sparkle-2.6.4.tar.xz" CACHE STRING "URL to Sparkle release archive")
|
||||
set(MACOSX_SPARKLE_SHA256 "50612a06038abc931f16011d7903b8326a362c1074dabccb718404ce8e585f0b" CACHE STRING "SHA256 checksum for Sparkle release archive")
|
||||
set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.8.0/Sparkle-2.8.0.tar.xz" CACHE STRING "URL to Sparkle release archive")
|
||||
set(MACOSX_SPARKLE_SHA256 "fd5681ee92bf238aaac2d08214ceaf0cc8976e452d7f882d80bac1e61581f3b1" CACHE STRING "SHA256 checksum for Sparkle release archive")
|
||||
set(MACOSX_SPARKLE_DIR "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
|
||||
|
||||
if(NOT MACOSX_SPARKLE_UPDATE_PUBLIC_KEY STREQUAL "" AND NOT MACOSX_SPARKLE_UPDATE_FEED_URL STREQUAL "")
|
||||
|
|
@ -522,18 +531,6 @@ if(NOT cmark_FOUND)
|
|||
else()
|
||||
message(STATUS "Using system cmark")
|
||||
endif()
|
||||
if(NOT qrcodegencpp_FOUND)
|
||||
set(QRCODE_SOURCES
|
||||
libraries/qrcodegenerator/cpp/qrcodegen.cpp
|
||||
libraries/qrcodegenerator/cpp/qrcodegen.hpp
|
||||
)
|
||||
add_library(qrcodegenerator STATIC ${QRCODE_SOURCES})
|
||||
target_include_directories(qrcodegenerator PUBLIC "libraries/qrcodegenerator/cpp/" )
|
||||
generate_export_header(qrcodegenerator)
|
||||
else()
|
||||
add_library(qrcodegenerator ALIAS qrcodegencpp::qrcodegencpp)
|
||||
message(STATUS "Using system qrcodegencpp-cmake")
|
||||
endif()
|
||||
add_subdirectory(libraries/gamemode)
|
||||
add_subdirectory(libraries/murmur2) # Hash for usage with the CurseForge API
|
||||
add_subdirectory(libraries/qdcss) # css parser
|
||||
|
|
|
|||
|
|
@ -5,10 +5,210 @@
|
|||
"major": 3,
|
||||
"minor": 28
|
||||
},
|
||||
"include": [
|
||||
"cmake/linuxPreset.json",
|
||||
"cmake/macosPreset.json",
|
||||
"cmake/windowsMinGWPreset.json",
|
||||
"cmake/windowsMSVCPreset.json"
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "base",
|
||||
"hidden": true,
|
||||
"binaryDir": "build",
|
||||
"installDir": "install",
|
||||
"generator": "Ninja Multi-Config",
|
||||
"cacheVariables": {
|
||||
"Launcher_BUILD_ARTIFACT": "$penv{ARTIFACT_NAME}",
|
||||
"Launcher_BUILD_PLATFORM": "$penv{BUILD_PLATFORM}",
|
||||
"Launcher_ENABLE_JAVA_DOWNLOADER": "ON",
|
||||
"ENABLE_LTO": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux",
|
||||
"displayName": "Linux",
|
||||
"inherits": [
|
||||
"base"
|
||||
],
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "macos",
|
||||
"displayName": "macOS",
|
||||
"inherits": [
|
||||
"base"
|
||||
],
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Darwin"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "macos_universal",
|
||||
"displayName": "macOS (Universal Binary)",
|
||||
"inherits": [
|
||||
"macos"
|
||||
],
|
||||
"cacheVariables": {
|
||||
"CMAKE_OSX_ARCHITECTURES": "x86_64;arm64",
|
||||
"VCPKG_TARGET_TRIPLET": "universal-osx"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows_mingw",
|
||||
"displayName": "Windows (MinGW)",
|
||||
"inherits": [
|
||||
"base"
|
||||
],
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc",
|
||||
"displayName": "Windows (MSVC)",
|
||||
"inherits": [
|
||||
"base"
|
||||
],
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "linux",
|
||||
"displayName": "Linux",
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Linux"
|
||||
},
|
||||
"configurePreset": "linux"
|
||||
},
|
||||
{
|
||||
"name": "macos",
|
||||
"displayName": "macOS",
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Darwin"
|
||||
},
|
||||
"configurePreset": "macos"
|
||||
},
|
||||
{
|
||||
"name": "macos_universal",
|
||||
"displayName": "macOS (Universal Binary)",
|
||||
"inherits": [
|
||||
"macos"
|
||||
],
|
||||
"configurePreset": "macos_universal"
|
||||
},
|
||||
{
|
||||
"name": "windows_mingw",
|
||||
"displayName": "Windows (MinGW)",
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
},
|
||||
"configurePreset": "windows_mingw"
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc",
|
||||
"displayName": "Windows (MSVC)",
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
},
|
||||
"configurePreset": "windows_msvc"
|
||||
}
|
||||
],
|
||||
"testPresets": [
|
||||
{
|
||||
"name": "base",
|
||||
"hidden": true,
|
||||
"output": {
|
||||
"outputOnFailure": true
|
||||
},
|
||||
"execution": {
|
||||
"noTestsAction": "error"
|
||||
},
|
||||
"filter": {
|
||||
"exclude": {
|
||||
"name": "^example64|example$"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux",
|
||||
"displayName": "Linux",
|
||||
"inherits": [
|
||||
"base"
|
||||
],
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Linux"
|
||||
},
|
||||
"configurePreset": "linux"
|
||||
},
|
||||
{
|
||||
"name": "macos",
|
||||
"displayName": "macOS",
|
||||
"inherits": [
|
||||
"base"
|
||||
],
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Darwin"
|
||||
},
|
||||
"configurePreset": "macos"
|
||||
},
|
||||
{
|
||||
"name": "macos_universal",
|
||||
"displayName": "macOS (Universal Binary)",
|
||||
"inherits": [
|
||||
"base"
|
||||
],
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Darwin"
|
||||
},
|
||||
"configurePreset": "macos_universal"
|
||||
},
|
||||
{
|
||||
"name": "windows_mingw",
|
||||
"displayName": "Windows (MinGW)",
|
||||
"inherits": [
|
||||
"base"
|
||||
],
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
},
|
||||
"configurePreset": "windows_mingw"
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc",
|
||||
"displayName": "Windows (MSVC)",
|
||||
"inherits": [
|
||||
"base"
|
||||
],
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
},
|
||||
"configurePreset": "windows_msvc"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
21
COPYING.md
21
COPYING.md
|
|
@ -404,14 +404,23 @@
|
|||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
## QR-Code-generator (`libraries/qrcodegenerator`)
|
||||
## libqrencode (`fukuchi/libqrencode`)
|
||||
|
||||
Copyright © 2024 Project Nayuki. (MIT License)
|
||||
https://www.nayuki.io/page/qr-code-generator-library
|
||||
Copyright (C) 2020 libqrencode Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
- The Software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the Software or the use or other dealings in the Software.
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
## vcpkg (`cmake/vcpkg-ports`)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
{
|
||||
"$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
|
||||
"version": 8,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "base",
|
||||
"hidden": true,
|
||||
"binaryDir": "build",
|
||||
"installDir": "install",
|
||||
"cacheVariables": {
|
||||
"Launcher_BUILD_ARTIFACT": "$penv{ARTIFACT_NAME}",
|
||||
"Launcher_BUILD_PLATFORM": "$penv{BUILD_PLATFORM}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "base_debug",
|
||||
"hidden": true,
|
||||
"inherits": [
|
||||
"base"
|
||||
],
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "base_release",
|
||||
"hidden": true,
|
||||
"inherits": [
|
||||
"base"
|
||||
],
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"ENABLE_LTO": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "base_ci",
|
||||
"hidden": true,
|
||||
"inherits": [
|
||||
"base_release"
|
||||
],
|
||||
"cacheVariables": {
|
||||
"Launcher_FORCE_BUNDLED_LIBS": "ON"
|
||||
}
|
||||
}
|
||||
],
|
||||
"testPresets": [
|
||||
{
|
||||
"name": "base",
|
||||
"hidden": true,
|
||||
"output": {
|
||||
"outputOnFailure": true
|
||||
},
|
||||
"execution": {
|
||||
"noTestsAction": "error"
|
||||
},
|
||||
"filter": {
|
||||
"exclude": {
|
||||
"name": "^example64|example$"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "base_debug",
|
||||
"hidden": true,
|
||||
"inherits": [
|
||||
"base"
|
||||
],
|
||||
"output": {
|
||||
"debug": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "base_release",
|
||||
"hidden": true,
|
||||
"inherits": [
|
||||
"base"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
{
|
||||
"$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
|
||||
"version": 8,
|
||||
"include": [
|
||||
"commonPresets.json"
|
||||
],
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "linux_base",
|
||||
"hidden": true,
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Linux"
|
||||
},
|
||||
"generator": "Ninja",
|
||||
"cacheVariables": {
|
||||
"Launcher_ENABLE_JAVA_DOWNLOADER": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux_debug",
|
||||
"inherits": [
|
||||
"base_debug",
|
||||
"linux_base"
|
||||
],
|
||||
"displayName": "Linux (Debug)"
|
||||
},
|
||||
{
|
||||
"name": "linux_release",
|
||||
"inherits": [
|
||||
"base_release",
|
||||
"linux_base"
|
||||
],
|
||||
"displayName": "Linux (Release)"
|
||||
},
|
||||
{
|
||||
"name": "linux_ci",
|
||||
"inherits": [
|
||||
"base_ci",
|
||||
"linux_base"
|
||||
],
|
||||
"displayName": "Linux (CI)",
|
||||
"installDir": "/usr"
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "linux_base",
|
||||
"hidden": true,
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux_debug",
|
||||
"inherits": [
|
||||
"linux_base"
|
||||
],
|
||||
"displayName": "Linux (Debug)",
|
||||
"configurePreset": "linux_debug"
|
||||
},
|
||||
{
|
||||
"name": "linux_release",
|
||||
"inherits": [
|
||||
"linux_base"
|
||||
],
|
||||
"displayName": "Linux (Release)",
|
||||
"configurePreset": "linux_release"
|
||||
},
|
||||
{
|
||||
"name": "linux_ci",
|
||||
"inherits": [
|
||||
"linux_base"
|
||||
],
|
||||
"displayName": "Linux (CI)",
|
||||
"configurePreset": "linux_ci"
|
||||
}
|
||||
],
|
||||
"testPresets": [
|
||||
{
|
||||
"name": "linux_base",
|
||||
"hidden": true,
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux_debug",
|
||||
"inherits": [
|
||||
"base_debug",
|
||||
"linux_base"
|
||||
],
|
||||
"displayName": "Linux (Debug)",
|
||||
"configurePreset": "linux_debug"
|
||||
},
|
||||
{
|
||||
"name": "linux_release",
|
||||
"inherits": [
|
||||
"base_release",
|
||||
"linux_base"
|
||||
],
|
||||
"displayName": "Linux (Release)",
|
||||
"configurePreset": "linux_release"
|
||||
},
|
||||
{
|
||||
"name": "linux_ci",
|
||||
"inherits": [
|
||||
"base_release",
|
||||
"linux_base"
|
||||
],
|
||||
"displayName": "Linux (CI)",
|
||||
"configurePreset": "linux_ci"
|
||||
}
|
||||
],
|
||||
"workflowPresets": [
|
||||
{
|
||||
"name": "linux_debug",
|
||||
"displayName": "Linux (Debug)",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "linux_debug"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "linux_debug"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "linux_debug"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "linux",
|
||||
"displayName": "Linux (Release)",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "linux_release"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "linux_release"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "linux_release"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "linux_ci",
|
||||
"displayName": "Linux (CI)",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "linux_ci"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "linux_ci"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "linux_ci"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,269 +0,0 @@
|
|||
{
|
||||
"$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
|
||||
"version": 8,
|
||||
"include": [
|
||||
"commonPresets.json"
|
||||
],
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "macos_base",
|
||||
"hidden": true,
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Darwin"
|
||||
},
|
||||
"generator": "Ninja"
|
||||
},
|
||||
{
|
||||
"name": "macos_universal_base",
|
||||
"hidden": true,
|
||||
"inherits": [
|
||||
"macos_base"
|
||||
],
|
||||
"cacheVariables": {
|
||||
"CMAKE_OSX_ARCHITECTURES": "x86_64;arm64",
|
||||
"VCPKG_TARGET_TRIPLET": "universal-osx"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "macos_debug",
|
||||
"inherits": [
|
||||
"base_debug",
|
||||
"macos_base"
|
||||
],
|
||||
"displayName": "macOS (Debug)"
|
||||
},
|
||||
{
|
||||
"name": "macos_release",
|
||||
"inherits": [
|
||||
"base_release",
|
||||
"macos_base"
|
||||
],
|
||||
"displayName": "macOS (Release)"
|
||||
},
|
||||
{
|
||||
"name": "macos_universal_debug",
|
||||
"inherits": [
|
||||
"base_debug",
|
||||
"macos_universal_base"
|
||||
],
|
||||
"displayName": "macOS (Universal Binary, Debug)"
|
||||
},
|
||||
{
|
||||
"name": "macos_universal_release",
|
||||
"inherits": [
|
||||
"base_release",
|
||||
"macos_universal_base"
|
||||
],
|
||||
"displayName": "macOS (Universal Binary, Release)"
|
||||
},
|
||||
{
|
||||
"name": "macos_ci",
|
||||
"inherits": [
|
||||
"base_ci",
|
||||
"macos_universal_base"
|
||||
],
|
||||
"displayName": "macOS (CI)"
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "macos_base",
|
||||
"hidden": true,
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Darwin"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "macos_debug",
|
||||
"inherits": [
|
||||
"macos_base"
|
||||
],
|
||||
"displayName": "macOS (Debug)",
|
||||
"configurePreset": "macos_debug"
|
||||
},
|
||||
{
|
||||
"name": "macos_release",
|
||||
"inherits": [
|
||||
"macos_base"
|
||||
],
|
||||
"displayName": "macOS (Release)",
|
||||
"configurePreset": "macos_release"
|
||||
},
|
||||
{
|
||||
"name": "macos_universal_debug",
|
||||
"inherits": [
|
||||
"macos_base"
|
||||
],
|
||||
"displayName": "macOS (Universal Binary, Debug)",
|
||||
"configurePreset": "macos_universal_debug"
|
||||
},
|
||||
{
|
||||
"name": "macos_universal_release",
|
||||
"inherits": [
|
||||
"macos_base"
|
||||
],
|
||||
"displayName": "macOS (Universal Binary, Release)",
|
||||
"configurePreset": "macos_universal_release"
|
||||
},
|
||||
{
|
||||
"name": "macos_ci",
|
||||
"inherits": [
|
||||
"macos_base"
|
||||
],
|
||||
"displayName": "macOS (CI)",
|
||||
"configurePreset": "macos_ci"
|
||||
}
|
||||
],
|
||||
"testPresets": [
|
||||
{
|
||||
"name": "macos_base",
|
||||
"hidden": true,
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Darwin"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "macos_debug",
|
||||
"inherits": [
|
||||
"base_debug",
|
||||
"macos_base"
|
||||
],
|
||||
"displayName": "MacOS (Debug)",
|
||||
"configurePreset": "macos_debug"
|
||||
},
|
||||
{
|
||||
"name": "macos_release",
|
||||
"inherits": [
|
||||
"base_release",
|
||||
"macos_base"
|
||||
],
|
||||
"displayName": "macOS (Release)",
|
||||
"configurePreset": "macos_release"
|
||||
},
|
||||
{
|
||||
"name": "macos_universal_debug",
|
||||
"inherits": [
|
||||
"base_debug",
|
||||
"macos_base"
|
||||
],
|
||||
"displayName": "MacOS (Universal Binary, Debug)",
|
||||
"configurePreset": "macos_universal_debug"
|
||||
},
|
||||
{
|
||||
"name": "macos_universal_release",
|
||||
"inherits": [
|
||||
"base_release",
|
||||
"macos_base"
|
||||
],
|
||||
"displayName": "macOS (Universal Binary, Release)",
|
||||
"configurePreset": "macos_universal_release"
|
||||
},
|
||||
{
|
||||
"name": "macos_ci",
|
||||
"inherits": [
|
||||
"base_release",
|
||||
"macos_base"
|
||||
],
|
||||
"displayName": "macOS (CI)",
|
||||
"configurePreset": "macos_ci"
|
||||
}
|
||||
],
|
||||
"workflowPresets": [
|
||||
{
|
||||
"name": "macos_debug",
|
||||
"displayName": "macOS (Debug)",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "macos_debug"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "macos_debug"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "macos_debug"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "macos",
|
||||
"displayName": "macOS (Release)",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "macos_release"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "macos_release"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "macos_release"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "macos_universal_debug",
|
||||
"displayName": "macOS (Universal Binary, Debug)",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "macos_universal_debug"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "macos_universal_debug"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "macos_universal_debug"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "macos_universal",
|
||||
"displayName": "macOS (Universal Binary, Release)",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "macos_universal_release"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "macos_universal_release"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "macos_universal_release"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "macos_ci",
|
||||
"displayName": "macOS (CI)",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "macos_ci"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "macos_ci"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "macos_ci"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,281 +0,0 @@
|
|||
{
|
||||
"$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
|
||||
"version": 8,
|
||||
"include": [
|
||||
"commonPresets.json"
|
||||
],
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "windows_msvc_base",
|
||||
"hidden": true,
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
},
|
||||
"generator": "Ninja"
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_arm64_cross_base",
|
||||
"hidden": true,
|
||||
"inherits": [
|
||||
"windows_msvc_base"
|
||||
],
|
||||
"cacheVariables": {
|
||||
"CMAKE_SYSTEM_NAME": "${hostSystemName}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_debug",
|
||||
"inherits": [
|
||||
"base_debug",
|
||||
"windows_msvc_base"
|
||||
],
|
||||
"displayName": "Windows MSVC (Debug)"
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_release",
|
||||
"inherits": [
|
||||
"base_release",
|
||||
"windows_msvc_base"
|
||||
],
|
||||
"displayName": "Windows MSVC (Release)"
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_arm64_cross_debug",
|
||||
"inherits": [
|
||||
"base_debug",
|
||||
"windows_msvc_arm64_cross_base"
|
||||
],
|
||||
"displayName": "Windows MSVC (ARM64 cross, Debug)"
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_arm64_cross_release",
|
||||
"inherits": [
|
||||
"base_release",
|
||||
"windows_msvc_arm64_cross_base"
|
||||
],
|
||||
"displayName": "Windows MSVC (ARM64 cross, Release)"
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_ci",
|
||||
"inherits": [
|
||||
"base_ci",
|
||||
"windows_msvc_base"
|
||||
],
|
||||
"displayName": "Windows MSVC (CI)"
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_arm64_cross_ci",
|
||||
"inherits": [
|
||||
"base_ci",
|
||||
"windows_msvc_arm64_cross_base"
|
||||
],
|
||||
"displayName": "Windows MSVC (ARM64 cross, CI)"
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "windows_msvc_base",
|
||||
"hidden": true,
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_debug",
|
||||
"inherits": [
|
||||
"windows_msvc_base"
|
||||
],
|
||||
"displayName": "Windows MSVC (Debug)",
|
||||
"configurePreset": "windows_msvc_debug",
|
||||
"configuration": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_release",
|
||||
"inherits": [
|
||||
"windows_msvc_base"
|
||||
],
|
||||
"displayName": "Windows MSVC (Release)",
|
||||
"configurePreset": "windows_msvc_release",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_arm64_cross_debug",
|
||||
"inherits": [
|
||||
"windows_msvc_base"
|
||||
],
|
||||
"displayName": "Windows MSVC (ARM64 cross, Debug)",
|
||||
"configurePreset": "windows_msvc_arm64_cross_debug",
|
||||
"configuration": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_arm64_cross_release",
|
||||
"inherits": [
|
||||
"windows_msvc_base"
|
||||
],
|
||||
"displayName": "Windows MSVC (ARM64 cross, Release)",
|
||||
"configurePreset": "windows_msvc_arm64_cross_release",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_ci",
|
||||
"inherits": [
|
||||
"windows_msvc_base"
|
||||
],
|
||||
"displayName": "Windows MSVC (CI)",
|
||||
"configurePreset": "windows_msvc_ci",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_arm64_cross_ci",
|
||||
"inherits": [
|
||||
"windows_msvc_base"
|
||||
],
|
||||
"displayName": "Windows MSVC (ARM64 cross, CI)",
|
||||
"configurePreset": "windows_msvc_arm64_cross_ci",
|
||||
"configuration": "Release"
|
||||
}
|
||||
],
|
||||
"testPresets": [
|
||||
{
|
||||
"name": "windows_msvc_base",
|
||||
"hidden": true,
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_debug",
|
||||
"inherits": [
|
||||
"base_debug",
|
||||
"windows_msvc_base"
|
||||
],
|
||||
"displayName": "Windows MSVC (Debug)",
|
||||
"configurePreset": "windows_msvc_debug",
|
||||
"configuration": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_release",
|
||||
"inherits": [
|
||||
"base_release",
|
||||
"windows_msvc_base"
|
||||
],
|
||||
"displayName": "Windows MSVC (Release)",
|
||||
"configurePreset": "windows_msvc_release",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_ci",
|
||||
"inherits": [
|
||||
"base_release",
|
||||
"windows_msvc_base"
|
||||
],
|
||||
"displayName": "Windows MSVC (CI)",
|
||||
"configurePreset": "windows_msvc_ci",
|
||||
"configuration": "Release"
|
||||
}
|
||||
],
|
||||
"workflowPresets": [
|
||||
{
|
||||
"name": "windows_msvc_debug",
|
||||
"displayName": "Windows MSVC (Debug)",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "windows_msvc_debug"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "windows_msvc_debug"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "windows_msvc_debug"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc",
|
||||
"displayName": "Windows MSVC (Release)",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "windows_msvc_release"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "windows_msvc_release"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "windows_msvc_release"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_arm64_cross_debug",
|
||||
"displayName": "Windows MSVC (ARM64 cross, Debug)",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "windows_msvc_arm64_cross_debug"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "windows_msvc_arm64_cross_debug"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_arm64_cross",
|
||||
"displayName": "Windows MSVC (ARM64 cross, Release)",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "windows_msvc_arm64_cross_release"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "windows_msvc_arm64_cross_release"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_ci",
|
||||
"displayName": "Windows MSVC (CI)",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "windows_msvc_ci"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "windows_msvc_ci"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "windows_msvc_ci"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "windows_msvc_arm64_cross_ci",
|
||||
"displayName": "Windows MSVC (ARM64 cross, CI)",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "windows_msvc_arm64_cross_ci"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "windows_msvc_arm64_cross_ci"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,177 +0,0 @@
|
|||
{
|
||||
"$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
|
||||
"version": 8,
|
||||
"include": [
|
||||
"commonPresets.json"
|
||||
],
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "windows_mingw_base",
|
||||
"hidden": true,
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
},
|
||||
"generator": "Ninja"
|
||||
},
|
||||
{
|
||||
"name": "windows_mingw_debug",
|
||||
"inherits": [
|
||||
"base_debug",
|
||||
"windows_mingw_base"
|
||||
],
|
||||
"displayName": "Windows MinGW (Debug)"
|
||||
},
|
||||
{
|
||||
"name": "windows_mingw_release",
|
||||
"inherits": [
|
||||
"base_release",
|
||||
"windows_mingw_base"
|
||||
],
|
||||
"displayName": "Windows MinGW (Release)"
|
||||
},
|
||||
{
|
||||
"name": "windows_mingw_ci",
|
||||
"inherits": [
|
||||
"base_ci",
|
||||
"windows_mingw_base"
|
||||
],
|
||||
"displayName": "Windows MinGW (CI)"
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "windows_mingw_base",
|
||||
"hidden": true,
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows_mingw_debug",
|
||||
"inherits": [
|
||||
"windows_mingw_base"
|
||||
],
|
||||
"displayName": "Windows MinGW (Debug)",
|
||||
"configurePreset": "windows_mingw_debug"
|
||||
},
|
||||
{
|
||||
"name": "windows_mingw_release",
|
||||
"inherits": [
|
||||
"windows_mingw_base"
|
||||
],
|
||||
"displayName": "Windows MinGW (Release)",
|
||||
"configurePreset": "windows_mingw_release"
|
||||
},
|
||||
{
|
||||
"name": "windows_mingw_ci",
|
||||
"inherits": [
|
||||
"windows_mingw_base"
|
||||
],
|
||||
"displayName": "Windows MinGW (CI)",
|
||||
"configurePreset": "windows_mingw_ci"
|
||||
}
|
||||
],
|
||||
"testPresets": [
|
||||
{
|
||||
"name": "windows_mingw_base",
|
||||
"hidden": true,
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
},
|
||||
"filter": {
|
||||
"exclude": {
|
||||
"name": "^example64|example$"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows_mingw_debug",
|
||||
"inherits": [
|
||||
"base_debug",
|
||||
"windows_mingw_base"
|
||||
],
|
||||
"displayName": "Windows MinGW (Debug)",
|
||||
"configurePreset": "windows_mingw_debug"
|
||||
},
|
||||
{
|
||||
"name": "windows_mingw_release",
|
||||
"inherits": [
|
||||
"base_release",
|
||||
"windows_mingw_base"
|
||||
],
|
||||
"displayName": "Windows MinGW (Release)",
|
||||
"configurePreset": "windows_mingw_release"
|
||||
},
|
||||
{
|
||||
"name": "windows_mingw_ci",
|
||||
"inherits": [
|
||||
"base_release",
|
||||
"windows_mingw_base"
|
||||
],
|
||||
"displayName": "Windows MinGW (CI)",
|
||||
"configurePreset": "windows_mingw_ci"
|
||||
}
|
||||
],
|
||||
"workflowPresets": [
|
||||
{
|
||||
"name": "windows_mingw_debug",
|
||||
"displayName": "Windows MinGW (Debug)",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "windows_mingw_debug"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "windows_mingw_debug"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "windows_mingw_debug"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "windows_mingw",
|
||||
"displayName": "Windows MinGW (Release)",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "windows_mingw_release"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "windows_mingw_release"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "windows_mingw_release"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "windows_mingw_ci",
|
||||
"displayName": "Windows MinGW (CI)",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "windows_mingw_ci"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "windows_mingw_ci"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "windows_mingw_ci"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
25
flake.lock
25
flake.lock
|
|
@ -18,11 +18,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1761907660,
|
||||
"narHash": "sha256-kJ8lIZsiPOmbkJypG+B5sReDXSD1KGu2VEPNqhRa/ew=",
|
||||
"lastModified": 1762977756,
|
||||
"narHash": "sha256-4PqRErxfe+2toFJFgcRKZ0UI9NSIOJa+7RXVtBhy4KE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2fb006b87f04c4d3bdf08cfdbc7fab9c13d94a15",
|
||||
"rev": "c5ae371f1a6a7fd27823bc500d9390b38c05fa55",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -32,27 +32,10 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"qrcodegenerator": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1737616857,
|
||||
"narHash": "sha256-6SugPt0lp1Gz7nV23FLmsmpfzgFItkSw7jpGftsDPWc=",
|
||||
"owner": "nayuki",
|
||||
"repo": "QR-Code-generator",
|
||||
"rev": "2c9044de6b049ca25cb3cd1649ed7e27aa055138",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nayuki",
|
||||
"repo": "QR-Code-generator",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"libnbtplusplus": "libnbtplusplus",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"qrcodegenerator": "qrcodegenerator"
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -15,11 +15,6 @@
|
|||
url = "github:PrismLauncher/libnbtplusplus";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
qrcodegenerator = {
|
||||
url = "github:nayuki/QR-Code-generator";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
|
||||
outputs =
|
||||
|
|
@ -27,7 +22,6 @@
|
|||
self,
|
||||
nixpkgs,
|
||||
libnbtplusplus,
|
||||
qrcodegenerator,
|
||||
}:
|
||||
|
||||
let
|
||||
|
|
@ -175,7 +169,6 @@
|
|||
prismlauncher-unwrapped = prev.callPackage ./nix/unwrapped.nix {
|
||||
inherit
|
||||
libnbtplusplus
|
||||
qrcodegenerator
|
||||
self
|
||||
;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -108,8 +108,6 @@
|
|||
#include "icons/IconList.h"
|
||||
#include "net/HttpMetaCache.h"
|
||||
|
||||
#include "java/JavaInstallList.h"
|
||||
|
||||
#include "updater/ExternalUpdater.h"
|
||||
|
||||
#include "tools/JProfiler.h"
|
||||
|
|
@ -127,7 +125,6 @@
|
|||
#include <LocalPeer.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <QStringLiteral>
|
||||
#include "SysInfo.h"
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
|
|
@ -708,6 +705,16 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
m_settings->registerSetting("SkinsDir", "skins");
|
||||
m_settings->registerSetting("JavaDir", "java");
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
// Folder security-scoped bookmarks
|
||||
m_settings->registerSetting("InstanceDirBookmark", "");
|
||||
m_settings->registerSetting("CentralModsDirBookmark", "");
|
||||
m_settings->registerSetting("IconsDirBookmark", "");
|
||||
m_settings->registerSetting("DownloadsDirBookmark", "");
|
||||
m_settings->registerSetting("SkinsDirBookmark", "");
|
||||
m_settings->registerSetting("JavaDirBookmark", "");
|
||||
#endif
|
||||
|
||||
// Editors
|
||||
m_settings->registerSetting("JsonEditor", QString());
|
||||
|
||||
|
|
@ -958,12 +965,27 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||
// Themes
|
||||
m_themeManager = std::make_unique<ThemeManager>();
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
// for macOS: getting directory settings will generate URL security-scoped bookmarks if needed and not present
|
||||
// this facilitates a smooth transition from a non-sandboxed version of the launcher, that likely can access the directory,
|
||||
// and a sandboxed version that can't access the directory without a bookmark
|
||||
// this section can likely be removed once the sandboxed version has been released for a while and migrations aren't done anymore
|
||||
{
|
||||
m_settings->get("InstanceDir");
|
||||
m_settings->get("CentralModsDir");
|
||||
m_settings->get("IconsDir");
|
||||
m_settings->get("DownloadsDir");
|
||||
m_settings->get("SkinsDir");
|
||||
m_settings->get("JavaDir");
|
||||
}
|
||||
#endif
|
||||
|
||||
// initialize and load all instances
|
||||
{
|
||||
auto InstDirSetting = m_settings->getSetting("InstanceDir");
|
||||
// instance path: check for problems with '!' in instance path and warn the user in the log
|
||||
// and remember that we have to show him a dialog when the gui starts (if it does so)
|
||||
QString instDir = InstDirSetting->get().toString();
|
||||
QString instDir = m_settings->get("InstanceDir").toString();
|
||||
qInfo() << "Instance path : " << instDir;
|
||||
if (FS::checkProblemticPathJava(QDir(instDir))) {
|
||||
qWarning() << "Your instance path contains \'!\' and this is known to cause java problems!";
|
||||
|
|
|
|||
|
|
@ -1053,8 +1053,6 @@ SET(LAUNCHER_SOURCES
|
|||
ui/dialogs/ImportResourceDialog.h
|
||||
ui/dialogs/MSALoginDialog.cpp
|
||||
ui/dialogs/MSALoginDialog.h
|
||||
ui/dialogs/OfflineLoginDialog.cpp
|
||||
ui/dialogs/OfflineLoginDialog.h
|
||||
ui/dialogs/NewComponentDialog.cpp
|
||||
ui/dialogs/NewComponentDialog.h
|
||||
ui/dialogs/NewInstanceDialog.cpp
|
||||
|
|
@ -1081,6 +1079,8 @@ SET(LAUNCHER_SOURCES
|
|||
ui/dialogs/ResourceUpdateDialog.h
|
||||
ui/dialogs/InstallLoaderDialog.cpp
|
||||
ui/dialogs/InstallLoaderDialog.h
|
||||
ui/dialogs/ChooseOfflineNameDialog.cpp
|
||||
ui/dialogs/ChooseOfflineNameDialog.h
|
||||
|
||||
ui/dialogs/skins/SkinManageDialog.cpp
|
||||
ui/dialogs/skins/SkinManageDialog.h
|
||||
|
|
@ -1173,6 +1173,15 @@ if (NOT Apple)
|
|||
)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
set(LAUNCHER_SOURCES
|
||||
${LAUNCHER_SOURCES}
|
||||
|
||||
macsandbox/SecurityBookmarkFileAccess.h
|
||||
macsandbox/SecurityBookmarkFileAccess.mm
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(LAUNCHER_SOURCES
|
||||
console/WindowsConsole.h
|
||||
|
|
@ -1234,13 +1243,13 @@ qt_wrap_ui(LAUNCHER_UI
|
|||
ui/dialogs/IconPickerDialog.ui
|
||||
ui/dialogs/ImportResourceDialog.ui
|
||||
ui/dialogs/MSALoginDialog.ui
|
||||
ui/dialogs/OfflineLoginDialog.ui
|
||||
ui/dialogs/AboutDialog.ui
|
||||
ui/dialogs/ReviewMessageBox.ui
|
||||
ui/dialogs/ScrollMessageBox.ui
|
||||
ui/dialogs/BlockedModsDialog.ui
|
||||
ui/dialogs/ChooseProviderDialog.ui
|
||||
ui/dialogs/skins/SkinManageDialog.ui
|
||||
ui/dialogs/ChooseOfflineNameDialog.ui
|
||||
)
|
||||
|
||||
qt_wrap_ui(PRISM_UPDATE_UI
|
||||
|
|
@ -1296,8 +1305,15 @@ target_link_libraries(Launcher_logic
|
|||
qdcss
|
||||
BuildConfig
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
qrcodegenerator
|
||||
)
|
||||
|
||||
if(TARGET PkgConfig::libqrencode)
|
||||
target_link_libraries(Launcher_logic PkgConfig::libqrencode)
|
||||
else()
|
||||
target_include_directories(Launcher_logic PRIVATE ${LIBQRENCODE_INCLUDE_DIR})
|
||||
target_link_libraries(Launcher_logic ${LIBQRENCODE_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(TARGET PkgConfig::tomlplusplus)
|
||||
target_link_libraries(Launcher_logic PkgConfig::tomlplusplus)
|
||||
else()
|
||||
|
|
|
|||
|
|
@ -59,10 +59,8 @@
|
|||
#if defined Q_OS_WIN32
|
||||
#define NOMINMAX
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <objbase.h>
|
||||
#include <objidl.h>
|
||||
#include <shlguid.h>
|
||||
#include <shlobj.h>
|
||||
#include <shobjidl.h>
|
||||
#include <sys/utime.h>
|
||||
#include <versionhelpers.h>
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ QJsonValue toJson<QVariant>(const QVariant& variant)
|
|||
template <>
|
||||
QByteArray requireIsType<QByteArray>(const QJsonValue& value, const QString& what)
|
||||
{
|
||||
const QString string = ensureIsType<QString>(value, what);
|
||||
const QString string = value.toString(what);
|
||||
// ensure that the string can be safely cast to Latin1
|
||||
if (string != QString::fromLatin1(string.toLatin1())) {
|
||||
throw JsonException(what + " is not encodable as Latin1");
|
||||
|
|
@ -221,7 +221,7 @@ QDateTime requireIsType<QDateTime>(const QJsonValue& value, const QString& what)
|
|||
template <>
|
||||
QUrl requireIsType<QUrl>(const QJsonValue& value, const QString& what)
|
||||
{
|
||||
const QString string = ensureIsType<QString>(value, what);
|
||||
const QString string = value.toString(what);
|
||||
if (string.isEmpty()) {
|
||||
return QUrl();
|
||||
}
|
||||
|
|
@ -287,7 +287,7 @@ QStringList toStringList(const QString& jsonString)
|
|||
if (parseError.error != QJsonParseError::NoError || !doc.isArray())
|
||||
return {};
|
||||
try {
|
||||
return ensureIsArrayOf<QString>(doc.array(), "");
|
||||
return requireIsArrayOf<QString>(doc);
|
||||
} catch (Json::JsonException& e) {
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,18 +153,6 @@ QUrl requireIsType<QUrl>(const QJsonValue& value, const QString& what);
|
|||
|
||||
// the following functions are higher level functions, that make use of the above functions for
|
||||
// type conversion
|
||||
template <typename T>
|
||||
T ensureIsType(const QJsonValue& value, const T default_ = T(), const QString& what = "Value")
|
||||
{
|
||||
if (value.isUndefined() || value.isNull()) {
|
||||
return default_;
|
||||
}
|
||||
try {
|
||||
return requireIsType<T>(value, what);
|
||||
} catch (const JsonException&) {
|
||||
return default_;
|
||||
}
|
||||
}
|
||||
|
||||
/// @throw JsonException
|
||||
template <typename T>
|
||||
|
|
@ -177,16 +165,6 @@ T requireIsType(const QJsonObject& parent, const QString& key, const QString& wh
|
|||
return requireIsType<T>(parent.value(key), localWhat);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T ensureIsType(const QJsonObject& parent, const QString& key, const T default_ = T(), const QString& what = "__placeholder__")
|
||||
{
|
||||
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
|
||||
if (!parent.contains(key)) {
|
||||
return default_;
|
||||
}
|
||||
return ensureIsType<T>(parent.value(key), default_, localWhat);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QList<T> requireIsArrayOf(const QJsonDocument& doc)
|
||||
{
|
||||
|
|
@ -198,26 +176,6 @@ QList<T> requireIsArrayOf(const QJsonDocument& doc)
|
|||
return out;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QList<T> ensureIsArrayOf(const QJsonValue& value, const QString& what = "Value")
|
||||
{
|
||||
const QJsonArray array = ensureIsType<QJsonArray>(value, QJsonArray(), what);
|
||||
QList<T> out;
|
||||
for (const QJsonValue val : array) {
|
||||
out.append(requireIsType<T>(val, what));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QList<T> ensureIsArrayOf(const QJsonValue& value, const QList<T> default_, const QString& what = "Value")
|
||||
{
|
||||
if (value.isUndefined()) {
|
||||
return default_;
|
||||
}
|
||||
return ensureIsArrayOf<T>(value, what);
|
||||
}
|
||||
|
||||
/// @throw JsonException
|
||||
template <typename T>
|
||||
QList<T> requireIsArrayOf(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__")
|
||||
|
|
@ -226,20 +184,13 @@ QList<T> requireIsArrayOf(const QJsonObject& parent, const QString& key, const Q
|
|||
if (!parent.contains(key)) {
|
||||
throw JsonException(localWhat + "s parent does not contain " + localWhat);
|
||||
}
|
||||
return ensureIsArrayOf<T>(parent.value(key), localWhat);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QList<T> ensureIsArrayOf(const QJsonObject& parent,
|
||||
const QString& key,
|
||||
const QList<T>& default_ = QList<T>(),
|
||||
const QString& what = "__placeholder__")
|
||||
{
|
||||
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
|
||||
if (!parent.contains(key)) {
|
||||
return default_;
|
||||
const QJsonArray array = parent[key].toArray();
|
||||
QList<T> out;
|
||||
for (const QJsonValue val : array) {
|
||||
out.append(requireIsType<T>(val, "Document"));
|
||||
}
|
||||
return ensureIsArrayOf<T>(parent.value(key), default_, localWhat);
|
||||
return out;
|
||||
}
|
||||
|
||||
// this macro part could be replaced by variadic functions that just pass on their arguments, but that wouldn't work well with IDE helpers
|
||||
|
|
@ -248,18 +199,9 @@ QList<T> ensureIsArrayOf(const QJsonObject& parent,
|
|||
{ \
|
||||
return requireIsType<TYPE>(value, what); \
|
||||
} \
|
||||
inline TYPE ensure##NAME(const QJsonValue& value, const TYPE default_ = TYPE(), const QString& what = "Value") \
|
||||
{ \
|
||||
return ensureIsType<TYPE>(value, default_, what); \
|
||||
} \
|
||||
inline TYPE require##NAME(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__") \
|
||||
{ \
|
||||
return requireIsType<TYPE>(parent, key, what); \
|
||||
} \
|
||||
inline TYPE ensure##NAME(const QJsonObject& parent, const QString& key, const TYPE default_ = TYPE(), \
|
||||
const QString& what = "__placeholder") \
|
||||
{ \
|
||||
return ensureIsType<TYPE>(parent, key, default_, what); \
|
||||
}
|
||||
|
||||
JSON_HELPERFUNCTIONS(Array, QJsonArray)
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@
|
|||
#include "JavaCommon.h"
|
||||
#include "launch/steps/TextPrint.h"
|
||||
#include "tasks/Task.h"
|
||||
#include "ui/dialogs/ChooseOfflineNameDialog.h"
|
||||
|
||||
LaunchController::LaunchController() : Task() {}
|
||||
|
||||
|
|
@ -157,10 +158,15 @@ QString LaunchController::askOfflineName(QString playerName, bool demo, bool& ok
|
|||
|
||||
QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString();
|
||||
QString usedname = lastOfflinePlayerName.isEmpty() ? playerName : lastOfflinePlayerName;
|
||||
QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok);
|
||||
if (!ok)
|
||||
|
||||
ChooseOfflineNameDialog dialog(message, m_parentWidget);
|
||||
dialog.setWindowTitle(tr("Player name"));
|
||||
dialog.setUsername(usedname);
|
||||
if (dialog.exec() != QDialog::Accepted) {
|
||||
return {};
|
||||
if (name.length()) {
|
||||
}
|
||||
|
||||
if (const QString name = dialog.getUsername(); !name.isEmpty()) {
|
||||
usedname = name;
|
||||
APPLICATION->settings()->set("LastOfflinePlayerName", usedname);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,8 +111,8 @@ QString getLibraryString()
|
|||
try {
|
||||
auto conf = Json::requireDocument(filePath, vkLayer);
|
||||
auto confObject = Json::requireObject(conf, vkLayer);
|
||||
auto layer = Json::ensureObject(confObject, "layer");
|
||||
QString libraryName = Json::ensureString(layer, "library_path");
|
||||
auto layer = confObject["layer"].toObject();
|
||||
QString libraryName = layer["library_path"].toString();
|
||||
|
||||
if (libraryName.isEmpty()) {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -52,27 +52,27 @@ MetadataPtr parseJavaMeta(const QJsonObject& in)
|
|||
{
|
||||
auto meta = std::make_shared<Metadata>();
|
||||
|
||||
meta->m_name = Json::ensureString(in, "name", "");
|
||||
meta->vendor = Json::ensureString(in, "vendor", "");
|
||||
meta->url = Json::ensureString(in, "url", "");
|
||||
meta->releaseTime = timeFromS3Time(Json::ensureString(in, "releaseTime", ""));
|
||||
meta->downloadType = parseDownloadType(Json::ensureString(in, "downloadType", ""));
|
||||
meta->packageType = Json::ensureString(in, "packageType", "");
|
||||
meta->runtimeOS = Json::ensureString(in, "runtimeOS", "unknown");
|
||||
meta->m_name = in["name"].toString("");
|
||||
meta->vendor = in["vendor"].toString("");
|
||||
meta->url = in["url"].toString("");
|
||||
meta->releaseTime = timeFromS3Time(in["releaseTime"].toString(""));
|
||||
meta->downloadType = parseDownloadType(in["downloadType"].toString(""));
|
||||
meta->packageType = in["packageType"].toString("");
|
||||
meta->runtimeOS = in["runtimeOS"].toString("unknown");
|
||||
|
||||
if (in.contains("checksum")) {
|
||||
auto obj = Json::requireObject(in, "checksum");
|
||||
meta->checksumHash = Json::ensureString(obj, "hash", "");
|
||||
meta->checksumType = Json::ensureString(obj, "type", "");
|
||||
meta->checksumHash = obj["hash"].toString("");
|
||||
meta->checksumType = obj["type"].toString("");
|
||||
}
|
||||
|
||||
if (in.contains("version")) {
|
||||
auto obj = Json::requireObject(in, "version");
|
||||
auto name = Json::ensureString(obj, "name", "");
|
||||
auto major = Json::ensureInteger(obj, "major", 0);
|
||||
auto minor = Json::ensureInteger(obj, "minor", 0);
|
||||
auto security = Json::ensureInteger(obj, "security", 0);
|
||||
auto build = Json::ensureInteger(obj, "build", 0);
|
||||
auto name = obj["name"].toString("");
|
||||
auto major = obj["major"].toInteger();
|
||||
auto minor = obj["minor"].toInteger();
|
||||
auto security = obj["security"].toInteger();
|
||||
auto build = obj["build"].toInteger();
|
||||
meta->version = JavaVersion(major, minor, security, build, name);
|
||||
}
|
||||
return meta;
|
||||
|
|
|
|||
|
|
@ -77,27 +77,27 @@ void ManifestDownloadTask::downloadJava(const QJsonDocument& doc)
|
|||
// valid json doc, begin making jre spot
|
||||
FS::ensureFolderPathExists(m_final_path);
|
||||
std::vector<File> toDownload;
|
||||
auto list = Json::ensureObject(Json::ensureObject(doc.object()), "files");
|
||||
auto list = doc.object()["files"].toObject();
|
||||
for (const auto& paths : list.keys()) {
|
||||
auto file = FS::PathCombine(m_final_path, paths);
|
||||
|
||||
const QJsonObject& meta = Json::ensureObject(list, paths);
|
||||
auto type = Json::ensureString(meta, "type");
|
||||
const QJsonObject& meta = list[paths].toObject();
|
||||
auto type = meta["type"].toString();
|
||||
if (type == "directory") {
|
||||
FS::ensureFolderPathExists(file);
|
||||
} else if (type == "link") {
|
||||
// this is *nix only !
|
||||
auto path = Json::ensureString(meta, "target");
|
||||
auto path = meta["target"].toString();
|
||||
if (!path.isEmpty()) {
|
||||
QFile::link(path, file);
|
||||
}
|
||||
} else if (type == "file") {
|
||||
// TODO download compressed version if it exists ?
|
||||
auto raw = Json::ensureObject(Json::ensureObject(meta, "downloads"), "raw");
|
||||
auto isExec = Json::ensureBoolean(meta, "executable", false);
|
||||
auto url = Json::ensureString(raw, "url");
|
||||
auto raw = meta["downloads"].toObject()["raw"].toObject();
|
||||
auto isExec = meta["executable"].toBool();
|
||||
auto url = raw["url"].toString();
|
||||
if (!url.isEmpty() && QUrl(url).isValid()) {
|
||||
auto f = File{ file, url, QByteArray::fromHex(Json::ensureString(raw, "sha1").toLatin1()), isExec };
|
||||
auto f = File{ file, url, QByteArray::fromHex(raw["sha1"].toString().toLatin1()), isExec };
|
||||
toDownload.push_back(f);
|
||||
}
|
||||
}
|
||||
|
|
@ -134,4 +134,4 @@ bool ManifestDownloadTask::abort()
|
|||
emitAborted();
|
||||
return aborted;
|
||||
};
|
||||
} // namespace Java
|
||||
} // namespace Java
|
||||
|
|
|
|||
|
|
@ -234,29 +234,40 @@ bool LaunchTask::parseXmlLogs(QString const& line, MessageLevel::Enum level)
|
|||
auto& model = *getLogModel();
|
||||
model.append(MessageLevel::Error, tr("[Log4j Parse Error] Failed to parse log4j log event: %1").arg(err.value().errMessage));
|
||||
return false;
|
||||
} else {
|
||||
if (!items.isEmpty()) {
|
||||
auto& model = *getLogModel();
|
||||
for (auto const& item : items) {
|
||||
if (std::holds_alternative<LogParser::LogEntry>(item)) {
|
||||
auto entry = std::get<LogParser::LogEntry>(item);
|
||||
auto msg = QString("[%1] [%2/%3] [%4]: %5")
|
||||
.arg(entry.timestamp.toString("HH:mm:ss"))
|
||||
.arg(entry.thread)
|
||||
.arg(entry.levelText)
|
||||
.arg(entry.logger)
|
||||
.arg(entry.message);
|
||||
msg = censorPrivateInfo(msg);
|
||||
model.append(entry.level, msg);
|
||||
} else if (std::holds_alternative<LogParser::PlainText>(item)) {
|
||||
auto msg = std::get<LogParser::PlainText>(item).message;
|
||||
level = LogParser::guessLevel(msg, model.previousLevel());
|
||||
msg = censorPrivateInfo(msg);
|
||||
model.append(level, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (items.isEmpty())
|
||||
return true;
|
||||
|
||||
auto model = getLogModel();
|
||||
for (auto const& item : items) {
|
||||
if (std::holds_alternative<LogParser::LogEntry>(item)) {
|
||||
auto entry = std::get<LogParser::LogEntry>(item);
|
||||
auto msg = QString("[%1] [%2/%3] [%4]: %5")
|
||||
.arg(entry.timestamp.toString("HH:mm:ss"))
|
||||
.arg(entry.thread)
|
||||
.arg(entry.levelText)
|
||||
.arg(entry.logger)
|
||||
.arg(entry.message);
|
||||
msg = censorPrivateInfo(msg);
|
||||
model->append(entry.level, msg);
|
||||
} else if (std::holds_alternative<LogParser::PlainText>(item)) {
|
||||
auto msg = std::get<LogParser::PlainText>(item).message;
|
||||
|
||||
MessageLevel::Enum newLevel = MessageLevel::fromLine(msg);
|
||||
|
||||
if (newLevel == MessageLevel::Unknown)
|
||||
newLevel = LogParser::guessLevel(line);
|
||||
|
||||
if (newLevel == MessageLevel::Unknown)
|
||||
newLevel = model->previousLevel();
|
||||
|
||||
msg = censorPrivateInfo(msg);
|
||||
|
||||
model->append(newLevel, msg);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -273,23 +284,10 @@ void LaunchTask::onLogLine(QString line, MessageLevel::Enum level)
|
|||
return;
|
||||
}
|
||||
|
||||
// if the launcher part set a log level, use it
|
||||
auto innerLevel = MessageLevel::fromLine(line);
|
||||
if (innerLevel != MessageLevel::Unknown) {
|
||||
level = innerLevel;
|
||||
}
|
||||
|
||||
auto& model = *getLogModel();
|
||||
|
||||
// If the level is still undetermined, guess level
|
||||
if (level == MessageLevel::Unknown) {
|
||||
level = LogParser::guessLevel(line, model.previousLevel());
|
||||
}
|
||||
|
||||
// censor private user info
|
||||
line = censorPrivateInfo(line);
|
||||
|
||||
model.append(level, line);
|
||||
getLogModel()->append(level, line);
|
||||
}
|
||||
|
||||
void LaunchTask::emitSucceeded()
|
||||
|
|
|
|||
|
|
@ -169,8 +169,8 @@ bool LogModel::isOverFlow()
|
|||
|
||||
MessageLevel::Enum LogModel::previousLevel()
|
||||
{
|
||||
if (!m_content.isEmpty()) {
|
||||
return m_content.last().level;
|
||||
if (m_numLines > 0) {
|
||||
return m_content[m_numLines - 1].level;
|
||||
}
|
||||
return MessageLevel::Unknown;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ std::optional<LogParser::ParsedItem> LogParser::parseLog4J()
|
|||
throw std::runtime_error("unreachable: already verified this was a complete log4j:Event");
|
||||
}
|
||||
|
||||
MessageLevel::Enum LogParser::guessLevel(const QString& line, MessageLevel::Enum level)
|
||||
MessageLevel::Enum LogParser::guessLevel(const QString& line)
|
||||
{
|
||||
static const QRegularExpression LINE_WITH_LEVEL("^\\[(?<timestamp>[0-9:]+)\\] \\[[^/]+/(?<level>[^\\]]+)\\]");
|
||||
auto match = LINE_WITH_LEVEL.match(line);
|
||||
|
|
@ -328,24 +328,26 @@ MessageLevel::Enum LogParser::guessLevel(const QString& line, MessageLevel::Enum
|
|||
// New style logs from log4j
|
||||
QString timestamp = match.captured("timestamp");
|
||||
QString levelStr = match.captured("level");
|
||||
level = MessageLevel::getLevel(levelStr);
|
||||
|
||||
return MessageLevel::getLevel(levelStr);
|
||||
} else {
|
||||
// Old style forge logs
|
||||
if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") || line.contains("[FINER]") ||
|
||||
line.contains("[FINEST]"))
|
||||
level = MessageLevel::Info;
|
||||
return MessageLevel::Info;
|
||||
if (line.contains("[SEVERE]") || line.contains("[STDERR]"))
|
||||
level = MessageLevel::Error;
|
||||
return MessageLevel::Error;
|
||||
if (line.contains("[WARNING]"))
|
||||
level = MessageLevel::Warning;
|
||||
return MessageLevel::Warning;
|
||||
if (line.contains("[DEBUG]"))
|
||||
level = MessageLevel::Debug;
|
||||
return MessageLevel::Debug;
|
||||
}
|
||||
if (level != MessageLevel::Unknown)
|
||||
return level;
|
||||
|
||||
if (line.contains("overwriting existing"))
|
||||
return MessageLevel::Fatal;
|
||||
|
||||
return MessageLevel::Info;
|
||||
if (line == "---- Minecraft Crash Report ----")
|
||||
return MessageLevel::Error;
|
||||
|
||||
return MessageLevel::Unknown;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class LogParser {
|
|||
std::optional<Error> getError();
|
||||
|
||||
/// guess log level from a line of game log
|
||||
static MessageLevel::Enum guessLevel(const QString& line, MessageLevel::Enum level);
|
||||
static MessageLevel::Enum guessLevel(const QString& line);
|
||||
|
||||
protected:
|
||||
std::optional<LogEntry> parseAttributes();
|
||||
|
|
|
|||
89
launcher/macsandbox/SecurityBookmarkFileAccess.h
Normal file
89
launcher/macsandbox/SecurityBookmarkFileAccess.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2024 Kenneth Chew <79120643+kthchew@users.noreply.github.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FILEACCESS_H
|
||||
#define FILEACCESS_H
|
||||
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QSet>
|
||||
Q_FORWARD_DECLARE_OBJC_CLASS(NSData);
|
||||
Q_FORWARD_DECLARE_OBJC_CLASS(NSURL);
|
||||
Q_FORWARD_DECLARE_OBJC_CLASS(NSString);
|
||||
Q_FORWARD_DECLARE_OBJC_CLASS(NSAutoreleasePool);
|
||||
Q_FORWARD_DECLARE_OBJC_CLASS(NSMutableDictionary);
|
||||
Q_FORWARD_DECLARE_OBJC_CLASS(NSMutableSet);
|
||||
class QString;
|
||||
class QByteArray;
|
||||
class QUrl;
|
||||
|
||||
class SecurityBookmarkFileAccess {
|
||||
/// The keys are bookmarks and the values are URLs.
|
||||
NSMutableDictionary* m_bookmarks;
|
||||
/// The keys are paths and the values are bookmarks.
|
||||
NSMutableDictionary* m_paths;
|
||||
/// Contains URLs that are currently being accessed.
|
||||
NSMutableSet* m_activeURLs;
|
||||
|
||||
bool m_readOnly;
|
||||
|
||||
NSURL* securityScopedBookmarkToNSURL(QByteArray& bookmark, bool& isStale);
|
||||
public:
|
||||
/// \param readOnly A boolean indicating whether the bookmark should be read-only.
|
||||
SecurityBookmarkFileAccess(bool readOnly = false);
|
||||
~SecurityBookmarkFileAccess();
|
||||
|
||||
/// Get a security scoped bookmark from a URL.
|
||||
///
|
||||
/// The URL must be accessible before calling this function. That is, call `startAccessingSecurityScopedResource()` before calling
|
||||
/// this function. Note that this is called implicitly if the user selects the directory from a file picker.
|
||||
/// \param url The URL to get the security scoped bookmark from.
|
||||
/// \return A QByteArray containing the security scoped bookmark.
|
||||
QByteArray urlToSecurityScopedBookmark(const QUrl& url);
|
||||
/// Get a security scoped bookmark from a path.
|
||||
///
|
||||
/// The path must be accessible before calling this function. That is, call `startAccessingSecurityScopedResource()` before calling
|
||||
/// this function. Note that this is called implicitly if the user selects the directory from a file picker.
|
||||
/// \param path The path to get the security scoped bookmark from.
|
||||
/// \return A QByteArray containing the security scoped bookmark.
|
||||
QByteArray pathToSecurityScopedBookmark(const QString& path);
|
||||
/// Get a QUrl from a security scoped bookmark. If the bookmark is stale, isStale will be set to true and the bookmark will be updated.
|
||||
///
|
||||
/// You must check whether the URL is valid before using it.
|
||||
/// \param bookmark The security scoped bookmark to get the URL from.
|
||||
/// \param isStale A boolean that will be set to true if the bookmark is stale.
|
||||
/// \return The URL from the security scoped bookmark.
|
||||
QUrl securityScopedBookmarkToURL(QByteArray& bookmark, bool& isStale);
|
||||
|
||||
/// Makes the file or directory at the path pointed to by the bookmark accessible. Unlike `startAccessingSecurityScopedResource()`, this
|
||||
/// class ensures that only one "access" is active at a time. Calling this function again after the security-scoped resource has
|
||||
/// already been used will do nothing, and a single call to `stopUsingSecurityScopedBookmark()` will release the resource provided that
|
||||
/// this is the only `SecurityBookmarkFileAccess` accessing the resource.
|
||||
///
|
||||
/// If the bookmark is stale, `isStale` will be set to true and the bookmark will be updated. Stored copies of the bookmark need to be
|
||||
/// updated.
|
||||
/// \param bookmark The security scoped bookmark to start accessing.
|
||||
/// \param isStale A boolean that will be set to true if the bookmark is stale.
|
||||
/// \return A boolean indicating whether the bookmark was successfully accessed.
|
||||
bool startUsingSecurityScopedBookmark(QByteArray& bookmark, bool& isStale);
|
||||
void stopUsingSecurityScopedBookmark(QByteArray& bookmark);
|
||||
|
||||
/// Returns true if access to the `path` is currently being maintained by this object.
|
||||
bool isAccessingPath(const QString& path);
|
||||
};
|
||||
|
||||
#endif //FILEACCESS_H
|
||||
172
launcher/macsandbox/SecurityBookmarkFileAccess.mm
Normal file
172
launcher/macsandbox/SecurityBookmarkFileAccess.mm
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2024 Kenneth Chew <79120643+kthchew@users.noreply.github.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SecurityBookmarkFileAccess.h"
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <QByteArray>
|
||||
#include <QUrl>
|
||||
|
||||
QByteArray SecurityBookmarkFileAccess::urlToSecurityScopedBookmark(const QUrl& url)
|
||||
{
|
||||
if (!url.isLocalFile())
|
||||
return {};
|
||||
|
||||
NSError* error = nil;
|
||||
NSURL* nsurl = [url.toNSURL() absoluteURL];
|
||||
NSData* bookmark;
|
||||
if ([m_paths objectForKey:[nsurl path]]) {
|
||||
bookmark = m_paths[[nsurl path]];
|
||||
} else {
|
||||
bookmark = [nsurl bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope |
|
||||
(m_readOnly ? NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess : 0)
|
||||
includingResourceValuesForKeys:nil
|
||||
relativeToURL:nil
|
||||
error:&error];
|
||||
}
|
||||
if (error) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// remove/reapply access to ensure that write access is immediately cut off for read-only bookmarks
|
||||
// sometimes you need to call this twice to actually stop access (extra calls aren't harmful)
|
||||
[nsurl stopAccessingSecurityScopedResource];
|
||||
[nsurl stopAccessingSecurityScopedResource];
|
||||
nsurl = [NSURL URLByResolvingBookmarkData:bookmark
|
||||
options:NSURLBookmarkResolutionWithSecurityScope |
|
||||
(m_readOnly ? NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess : 0)
|
||||
relativeToURL:nil
|
||||
bookmarkDataIsStale:nil
|
||||
error:&error];
|
||||
m_paths[[nsurl path]] = bookmark;
|
||||
m_bookmarks[bookmark] = nsurl;
|
||||
|
||||
QByteArray qBookmark = QByteArray::fromNSData(bookmark);
|
||||
bool isStale = false;
|
||||
startUsingSecurityScopedBookmark(qBookmark, isStale);
|
||||
|
||||
return qBookmark;
|
||||
}
|
||||
|
||||
SecurityBookmarkFileAccess::SecurityBookmarkFileAccess(bool readOnly) : m_readOnly(readOnly)
|
||||
{
|
||||
m_bookmarks = [NSMutableDictionary new];
|
||||
m_paths = [NSMutableDictionary new];
|
||||
m_activeURLs = [NSMutableSet new];
|
||||
}
|
||||
|
||||
SecurityBookmarkFileAccess::~SecurityBookmarkFileAccess()
|
||||
{
|
||||
for (NSURL* url : m_activeURLs) {
|
||||
[url stopAccessingSecurityScopedResource];
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray SecurityBookmarkFileAccess::pathToSecurityScopedBookmark(const QString& path)
|
||||
{
|
||||
return urlToSecurityScopedBookmark(QUrl::fromLocalFile(path));
|
||||
}
|
||||
|
||||
NSURL* SecurityBookmarkFileAccess::securityScopedBookmarkToNSURL(QByteArray& bookmark, bool& isStale)
|
||||
{
|
||||
NSError* error = nil;
|
||||
BOOL localStale = NO;
|
||||
NSURL* nsurl = [NSURL URLByResolvingBookmarkData:bookmark.toNSData()
|
||||
options:NSURLBookmarkResolutionWithSecurityScope |
|
||||
(m_readOnly ? NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess : 0)
|
||||
relativeToURL:nil
|
||||
bookmarkDataIsStale:&localStale
|
||||
error:&error];
|
||||
if (error) {
|
||||
return nil;
|
||||
}
|
||||
isStale = localStale;
|
||||
if (isStale) {
|
||||
NSData* nsBookmark = [nsurl bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope |
|
||||
(m_readOnly ? NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess : 0)
|
||||
includingResourceValuesForKeys:nil
|
||||
relativeToURL:nil
|
||||
error:&error];
|
||||
if (error) {
|
||||
return nil;
|
||||
}
|
||||
bookmark = QByteArray::fromNSData(nsBookmark);
|
||||
}
|
||||
|
||||
NSData* nsBookmark = bookmark.toNSData();
|
||||
m_paths[[nsurl path]] = nsBookmark;
|
||||
m_bookmarks[nsBookmark] = nsurl;
|
||||
|
||||
return nsurl;
|
||||
}
|
||||
|
||||
QUrl SecurityBookmarkFileAccess::securityScopedBookmarkToURL(QByteArray& bookmark, bool& isStale)
|
||||
{
|
||||
if (bookmark.isEmpty())
|
||||
return {};
|
||||
|
||||
NSURL* url = securityScopedBookmarkToNSURL(bookmark, isStale);
|
||||
if (!url)
|
||||
return {};
|
||||
|
||||
return QUrl::fromNSURL(url);
|
||||
}
|
||||
|
||||
bool SecurityBookmarkFileAccess::startUsingSecurityScopedBookmark(QByteArray& bookmark, bool& isStale)
|
||||
{
|
||||
NSURL* url = [m_bookmarks objectForKey:bookmark.toNSData()] ? m_bookmarks[bookmark.toNSData()]
|
||||
: securityScopedBookmarkToNSURL(bookmark, isStale);
|
||||
if ([m_activeURLs containsObject:url])
|
||||
return false;
|
||||
|
||||
[url stopAccessingSecurityScopedResource];
|
||||
if ([url startAccessingSecurityScopedResource]) {
|
||||
[m_activeURLs addObject:url];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SecurityBookmarkFileAccess::stopUsingSecurityScopedBookmark(QByteArray& bookmark)
|
||||
{
|
||||
if (![m_bookmarks objectForKey:bookmark.toNSData()])
|
||||
return;
|
||||
NSURL* url = m_bookmarks[bookmark.toNSData()];
|
||||
|
||||
if ([m_activeURLs containsObject:url]) {
|
||||
[url stopAccessingSecurityScopedResource];
|
||||
[url stopAccessingSecurityScopedResource];
|
||||
|
||||
[m_activeURLs removeObject:url];
|
||||
[m_paths removeObjectForKey:[url path]];
|
||||
[m_bookmarks removeObjectForKey:bookmark.toNSData()];
|
||||
}
|
||||
}
|
||||
|
||||
bool SecurityBookmarkFileAccess::isAccessingPath(const QString& path)
|
||||
{
|
||||
NSData* bookmark = [m_paths objectForKey:path.toNSString()];
|
||||
if (!bookmark && path.endsWith('/')) {
|
||||
bookmark = [m_paths objectForKey:path.left(path.length() - 1).toNSString()];
|
||||
}
|
||||
if (!bookmark) {
|
||||
return false;
|
||||
}
|
||||
NSURL* url = [m_bookmarks objectForKey:bookmark];
|
||||
return [m_activeURLs containsObject:url];
|
||||
}
|
||||
|
|
@ -35,34 +35,8 @@
|
|||
|
||||
#include "Application.h"
|
||||
|
||||
// #define BREAK_INFINITE_LOOP
|
||||
// #define BREAK_EXCEPTION
|
||||
// #define BREAK_RETURN
|
||||
|
||||
#ifdef BREAK_INFINITE_LOOP
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#endif
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
#ifdef BREAK_INFINITE_LOOP
|
||||
while (true) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
}
|
||||
#endif
|
||||
#ifdef BREAK_EXCEPTION
|
||||
throw 42;
|
||||
#endif
|
||||
#ifdef BREAK_RETURN
|
||||
return 42;
|
||||
#endif
|
||||
|
||||
#if QT_VERSION <= QT_VERSION_CHECK(6, 0, 0)
|
||||
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
#endif
|
||||
|
||||
// initialize Qt
|
||||
Application app(argc, argv);
|
||||
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ static std::shared_ptr<Index> parseIndexInternal(const QJsonObject& obj)
|
|||
lists.reserve(objects.size());
|
||||
std::transform(objects.begin(), objects.end(), std::back_inserter(lists), [](const QJsonObject& obj) {
|
||||
VersionList::Ptr list = std::make_shared<VersionList>(requireString(obj, "uid"));
|
||||
list->setName(ensureString(obj, "name", QString()));
|
||||
list->setSha256(ensureString(obj, "sha256", QString()));
|
||||
list->setName(obj["name"].toString());
|
||||
list->setSha256(obj["sha256"].toString());
|
||||
return list;
|
||||
});
|
||||
return std::make_shared<Index>(lists);
|
||||
|
|
@ -52,14 +52,14 @@ static Version::Ptr parseCommonVersion(const QString& uid, const QJsonObject& ob
|
|||
{
|
||||
Version::Ptr version = std::make_shared<Version>(uid, requireString(obj, "version"));
|
||||
version->setTime(QDateTime::fromString(requireString(obj, "releaseTime"), Qt::ISODate).toMSecsSinceEpoch() / 1000);
|
||||
version->setType(ensureString(obj, "type", QString()));
|
||||
version->setRecommended(ensureBoolean(obj, QString("recommended"), false));
|
||||
version->setVolatile(ensureBoolean(obj, QString("volatile"), false));
|
||||
version->setType(obj["type"].toString());
|
||||
version->setRecommended(obj["recommended"].toBool());
|
||||
version->setVolatile(obj["volatile"].toBool());
|
||||
RequireSet reqs, conflicts;
|
||||
parseRequires(obj, &reqs, "requires");
|
||||
parseRequires(obj, &conflicts, "conflicts");
|
||||
version->setRequires(reqs, conflicts);
|
||||
if (auto sha256 = ensureString(obj, "sha256", QString()); !sha256.isEmpty()) {
|
||||
if (auto sha256 = obj["sha256"].toString(); !sha256.isEmpty()) {
|
||||
version->setSha256(sha256);
|
||||
}
|
||||
return version;
|
||||
|
|
@ -89,7 +89,7 @@ static VersionList::Ptr parseVersionListInternal(const QJsonObject& obj)
|
|||
});
|
||||
|
||||
VersionList::Ptr list = std::make_shared<VersionList>(uid);
|
||||
list->setName(ensureString(obj, "name", QString()));
|
||||
list->setName(obj["name"].toString());
|
||||
list->setVersions(versions);
|
||||
return list;
|
||||
}
|
||||
|
|
@ -171,8 +171,8 @@ void parseRequires(const QJsonObject& obj, RequireSet* ptr, const char* keyName)
|
|||
while (iter != reqArray.end()) {
|
||||
auto reqObject = requireObject(*iter);
|
||||
auto uid = requireString(reqObject, "uid");
|
||||
auto equals = ensureString(reqObject, "equals", QString());
|
||||
auto suggests = ensureString(reqObject, "suggests", QString());
|
||||
auto equals = reqObject["equals"].toString();
|
||||
auto suggests = reqObject["suggests"].toString();
|
||||
ptr->insert({ uid, equals, suggests });
|
||||
iter++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -259,8 +259,8 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument& doc
|
|||
|
||||
if (root.contains("runtimes")) {
|
||||
out->runtimes = {};
|
||||
for (auto runtime : ensureArray(root, "runtimes")) {
|
||||
out->runtimes.append(Java::parseJavaMeta(ensureObject(runtime)));
|
||||
for (auto runtime : root["runtimes"].toArray()) {
|
||||
out->runtimes.append(Java::parseJavaMeta(runtime.toObject()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -130,18 +130,18 @@ static ComponentPtr componentFromJsonV1(PackProfile* parent, const QString& comp
|
|||
auto uid = Json::requireString(obj.value("uid"));
|
||||
auto filePath = componentJsonPattern.arg(uid);
|
||||
auto component = makeShared<Component>(parent, uid);
|
||||
component->m_version = Json::ensureString(obj.value("version"));
|
||||
component->m_dependencyOnly = Json::ensureBoolean(obj.value("dependencyOnly"), false);
|
||||
component->m_important = Json::ensureBoolean(obj.value("important"), false);
|
||||
component->m_version = obj.value("version").toString();
|
||||
component->m_dependencyOnly = obj.value("dependencyOnly").toBool();
|
||||
component->m_important = obj.value("important").toBool();
|
||||
|
||||
// cached
|
||||
// TODO @RESILIENCE: ignore invalid values/structure here?
|
||||
component->m_cachedVersion = Json::ensureString(obj.value("cachedVersion"));
|
||||
component->m_cachedName = Json::ensureString(obj.value("cachedName"));
|
||||
component->m_cachedVersion = obj.value("cachedVersion").toString();
|
||||
component->m_cachedName = obj.value("cachedName").toString();
|
||||
Meta::parseRequires(obj, &component->m_cachedRequires, "cachedRequires");
|
||||
Meta::parseRequires(obj, &component->m_cachedConflicts, "cachedConflicts");
|
||||
component->m_cachedVolatile = Json::ensureBoolean(obj.value("volatile"), false);
|
||||
bool disabled = Json::ensureBoolean(obj.value("disabled"), false);
|
||||
component->m_cachedVolatile = obj.value("volatile").toBool();
|
||||
bool disabled = obj.value("disabled").toBool();
|
||||
component->setEnabled(!disabled);
|
||||
return component;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@
|
|||
|
||||
#include <QDateTime>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
#include <QVariantMap>
|
||||
|
||||
enum class Validity { None, Assumed, Certain };
|
||||
|
|
|
|||
|
|
@ -105,9 +105,8 @@ DeviceAuthorizationResponse parseDeviceAuthorizationResponse(const QByteArray& d
|
|||
}
|
||||
auto obj = doc.object();
|
||||
return {
|
||||
Json::ensureString(obj, "device_code"), Json::ensureString(obj, "user_code"), Json::ensureString(obj, "verification_uri"),
|
||||
Json::ensureInteger(obj, "expires_in"), Json::ensureInteger(obj, "interval"), Json::ensureString(obj, "error"),
|
||||
Json::ensureString(obj, "error_description"),
|
||||
obj["device_code"].toString(), obj["user_code"].toString(), obj["verification_uri"].toString(), obj["expires_in"].toInt(),
|
||||
obj["interval"].toInt(), obj["error"].toString(), obj["error_description"].toString(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -217,12 +216,12 @@ AuthenticationResponse parseAuthenticationResponse(const QByteArray& data)
|
|||
return {};
|
||||
}
|
||||
auto obj = doc.object();
|
||||
return { Json::ensureString(obj, "access_token"),
|
||||
Json::ensureString(obj, "token_type"),
|
||||
Json::ensureString(obj, "refresh_token"),
|
||||
Json::ensureInteger(obj, "expires_in"),
|
||||
Json::ensureString(obj, "error"),
|
||||
Json::ensureString(obj, "error_description"),
|
||||
return { obj["access_token"].toString(),
|
||||
obj["token_type"].toString(),
|
||||
obj["refresh_token"].toString(),
|
||||
obj["expires_in"].toInt(),
|
||||
obj["error"].toString(),
|
||||
obj["error_description"].toString(),
|
||||
obj.toVariantMap() };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@ class QSortFilterProxyModel;
|
|||
|
||||
/* A macro to define useful functions to handle Resource* -> T* more easily on derived classes */
|
||||
#define RESOURCE_HELPERS(T) \
|
||||
T& at(int index) \
|
||||
T& at(int index) \
|
||||
{ \
|
||||
return *static_cast<T*>(m_resources[index].get()); \
|
||||
} \
|
||||
const T& at(int index) const \
|
||||
const T& at(int index) const \
|
||||
{ \
|
||||
return *static_cast<const T*>(m_resources.at(index).get()); \
|
||||
} \
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ bool processMCMeta(DataPack* pack, QByteArray&& raw_data)
|
|||
auto json_doc = QJsonDocument::fromJson(raw_data);
|
||||
auto pack_obj = Json::requireObject(json_doc.object(), "pack", {});
|
||||
|
||||
pack->setPackFormat(Json::ensureInteger(pack_obj, "pack_format", 0));
|
||||
pack->setPackFormat(pack_obj["pack_format"].toInt());
|
||||
pack->setDescription(DataPackUtils::processComponent(pack_obj.value("description")));
|
||||
} catch (Json::JsonException& e) {
|
||||
qWarning() << "JsonException: " << e.what() << e.cause();
|
||||
|
|
@ -192,19 +192,19 @@ bool processMCMeta(DataPack* pack, QByteArray&& raw_data)
|
|||
QString buildStyle(const QJsonObject& obj)
|
||||
{
|
||||
QStringList styles;
|
||||
if (auto color = Json::ensureString(obj, "color"); !color.isEmpty()) {
|
||||
if (auto color = obj["color"].toString(); !color.isEmpty()) {
|
||||
styles << QString("color: %1;").arg(color);
|
||||
}
|
||||
if (obj.contains("bold")) {
|
||||
QString weight = "normal";
|
||||
if (Json::ensureBoolean(obj, "bold", false)) {
|
||||
if (obj["bold"].toBool()) {
|
||||
weight = "bold";
|
||||
}
|
||||
styles << QString("font-weight: %1;").arg(weight);
|
||||
}
|
||||
if (obj.contains("italic")) {
|
||||
QString style = "normal";
|
||||
if (Json::ensureBoolean(obj, "italic", false)) {
|
||||
if (obj["italic"].toBool()) {
|
||||
style = "italic";
|
||||
}
|
||||
styles << QString("font-style: %1;").arg(style);
|
||||
|
|
@ -223,10 +223,10 @@ QString processComponent(const QJsonArray& value, bool strikethrough, bool under
|
|||
|
||||
QString processComponent(const QJsonObject& obj, bool strikethrough, bool underline)
|
||||
{
|
||||
underline = Json::ensureBoolean(obj, "underlined", underline);
|
||||
strikethrough = Json::ensureBoolean(obj, "strikethrough", strikethrough);
|
||||
underline = obj["underlined"].toBool(underline);
|
||||
strikethrough = obj["strikethrough"].toBool(strikethrough);
|
||||
|
||||
QString result = Json::ensureString(obj, "text");
|
||||
QString result = obj["text"].toString();
|
||||
if (underline) {
|
||||
result = QString("<u>%1</u>").arg(result);
|
||||
}
|
||||
|
|
@ -234,14 +234,14 @@ QString processComponent(const QJsonObject& obj, bool strikethrough, bool underl
|
|||
result = QString("<s>%1</s>").arg(result);
|
||||
}
|
||||
// the extra needs to be a array
|
||||
result += processComponent(Json::ensureArray(obj, "extra"), strikethrough, underline);
|
||||
result += processComponent(obj["extra"].toArray(), strikethrough, underline);
|
||||
if (auto style = buildStyle(obj); !style.isEmpty()) {
|
||||
result = QString("<span %1>%2</span>").arg(style, result);
|
||||
}
|
||||
if (obj.contains("clickEvent")) {
|
||||
auto click_event = Json::ensureObject(obj, "clickEvent");
|
||||
auto action = Json::ensureString(click_event, "action");
|
||||
auto value = Json::ensureString(click_event, "value");
|
||||
auto click_event = obj["clickEvent"].toObject();
|
||||
auto action = click_event["action"].toString();
|
||||
auto value = click_event["value"].toString();
|
||||
if (action == "open_url" && !value.isEmpty()) {
|
||||
result = QString("<a href=\"%1\">%2</a>").arg(value, result);
|
||||
}
|
||||
|
|
@ -366,4 +366,4 @@ void LocalDataPackParseTask::executeTask()
|
|||
}
|
||||
|
||||
emitSucceeded();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,11 +75,11 @@ ModDetails ReadMCModInfo(QByteArray contents)
|
|||
val = jsonDoc.object().value("modListVersion");
|
||||
}
|
||||
|
||||
int version = Json::ensureInteger(val, -1);
|
||||
int version = val.toInt(-1);
|
||||
|
||||
// Some mods set the number with "", so it's a String instead
|
||||
if (version < 0)
|
||||
version = Json::ensureString(val, "").toInt();
|
||||
version = val.toString("").toInt();
|
||||
|
||||
if (version != 2) {
|
||||
qWarning() << QString(R"(The value of 'modListVersion' is "%1" (expected "2")! The file may be corrupted.)").arg(version);
|
||||
|
|
@ -298,7 +298,7 @@ ModDetails ReadQuiltModInfo(QByteArray contents)
|
|||
QJsonParseError jsonError;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
|
||||
auto object = Json::requireObject(jsonDoc, "quilt.mod.json");
|
||||
auto schemaVersion = Json::ensureInteger(object.value("schema_version"), 0, "Quilt schema_version");
|
||||
auto schemaVersion = object.value("schema_version").toInt();
|
||||
|
||||
// https://github.com/QuiltMC/rfcs/blob/be6ba280d785395fefa90a43db48e5bfc1d15eb4/specification/0002-quilt.mod.json.md
|
||||
if (schemaVersion == 1) {
|
||||
|
|
@ -307,17 +307,17 @@ ModDetails ReadQuiltModInfo(QByteArray contents)
|
|||
details.mod_id = Json::requireString(modInfo.value("id"), "Mod ID");
|
||||
details.version = Json::requireString(modInfo.value("version"), "Mod version");
|
||||
|
||||
auto modMetadata = Json::ensureObject(modInfo.value("metadata"));
|
||||
auto modMetadata = modInfo.value("metadata").toObject();
|
||||
|
||||
details.name = Json::ensureString(modMetadata.value("name"), details.mod_id);
|
||||
details.description = Json::ensureString(modMetadata.value("description"));
|
||||
details.name = modMetadata.value("name").toString(details.mod_id);
|
||||
details.description = modMetadata.value("description").toString();
|
||||
|
||||
auto modContributors = Json::ensureObject(modMetadata.value("contributors"));
|
||||
auto modContributors = modMetadata.value("contributors").toObject();
|
||||
|
||||
// We don't really care about the role of a contributor here
|
||||
details.authors += modContributors.keys();
|
||||
|
||||
auto modContact = Json::ensureObject(modMetadata.value("contact"));
|
||||
auto modContact = modMetadata.value("contact").toObject();
|
||||
|
||||
if (modContact.contains("homepage")) {
|
||||
details.homeurl = Json::requireString(modContact.value("homepage"));
|
||||
|
|
|
|||
|
|
@ -75,9 +75,9 @@ bool SkinList::update()
|
|||
try {
|
||||
auto doc = Json::requireDocument(manifestInfo.absoluteFilePath(), "SkinList JSON file");
|
||||
const auto root = doc.object();
|
||||
auto skins = Json::ensureArray(root, "skins");
|
||||
auto skins = root["skins"].toArray();
|
||||
for (auto jSkin : skins) {
|
||||
SkinModel s(m_dir, Json::ensureObject(jSkin));
|
||||
SkinModel s(m_dir, jSkin.toObject());
|
||||
if (s.isValid()) {
|
||||
newSkins << s;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,12 +21,17 @@
|
|||
#include <QPainter>
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "Json.h"
|
||||
|
||||
static QImage improveSkin(const QImage& skin)
|
||||
static QImage improveSkin(QImage skin)
|
||||
{
|
||||
// It seems some older skins may use this format, which can't be drawn onto
|
||||
// https://github.com/PrismLauncher/PrismLauncher/issues/4032
|
||||
// https://doc.qt.io/qt-6/qpainter.html#begin
|
||||
if (skin.format() == QImage::Format_Indexed8) {
|
||||
skin = skin.convertToFormat(QImage::Format_RGB32);
|
||||
}
|
||||
if (skin.size() == QSize(64, 32)) { // old format
|
||||
QImage newSkin = QImage(QSize(64, 64), skin.format());
|
||||
auto newSkin = QImage(QSize(64, 64), skin.format());
|
||||
newSkin.fill(Qt::transparent);
|
||||
QPainter p(&newSkin);
|
||||
p.drawImage(QPoint(0, 0), skin.copy(QRect(0, 0, 64, 32))); // copy head
|
||||
|
|
@ -102,15 +107,15 @@ SkinModel::SkinModel(QString path) : m_path(path), m_texture(getSkin(path)), m_m
|
|||
}
|
||||
|
||||
SkinModel::SkinModel(QDir skinDir, QJsonObject obj)
|
||||
: m_capeId(Json::ensureString(obj, "capeId")), m_model(Model::CLASSIC), m_url(Json::ensureString(obj, "url"))
|
||||
: m_capeId(obj["capeId"].toString()), m_model(Model::CLASSIC), m_url(obj["url"].toString())
|
||||
{
|
||||
auto name = Json::ensureString(obj, "name");
|
||||
auto name = obj["name"].toString();
|
||||
|
||||
if (auto model = Json::ensureString(obj, "model"); model == "SLIM") {
|
||||
if (auto model = obj["model"].toString(); model == "SLIM") {
|
||||
m_model = Model::SLIM;
|
||||
}
|
||||
m_path = skinDir.absoluteFilePath(name) + ".png";
|
||||
m_texture = QImage(getSkin(m_path));
|
||||
m_texture = getSkin(m_path);
|
||||
m_preview = generatePreviews(m_texture, m_model == Model::SLIM);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -374,8 +374,8 @@ Task::Ptr EnsureMetadataTask::flameVersionsTask()
|
|||
}
|
||||
|
||||
for (auto match : data_arr) {
|
||||
auto match_obj = Json::ensureObject(match, {});
|
||||
auto file_obj = Json::ensureObject(match_obj, "file", {});
|
||||
auto match_obj = match.toObject();
|
||||
auto file_obj = match_obj["file"].toObject();
|
||||
|
||||
if (match_obj.isEmpty() || file_obj.isEmpty()) {
|
||||
qWarning() << "Fingerprint match is empty!";
|
||||
|
|
@ -383,7 +383,7 @@ Task::Ptr EnsureMetadataTask::flameVersionsTask()
|
|||
return;
|
||||
}
|
||||
|
||||
auto fingerprint = QString::number(Json::ensureVariant(file_obj, "fileFingerprint").toUInt());
|
||||
auto fingerprint = QString::number(file_obj["fileFingerprint"].toInteger());
|
||||
auto resource = m_resources.find(fingerprint);
|
||||
if (resource == m_resources.end()) {
|
||||
qWarning() << "Invalid fingerprint from the API response.";
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@ static const QMap<QString, IndexedVersionType::VersionType> s_indexed_version_ty
|
|||
{ "alpha", IndexedVersionType::VersionType::Alpha }
|
||||
};
|
||||
|
||||
static const QList<ModLoaderType> loaderList = { NeoForge, Forge, Cauldron, LiteLoader, Quilt, Fabric, Babric, BTA, LegacyFabric, Ornithe, Rift };
|
||||
static const QList<ModLoaderType> loaderList = { NeoForge, Forge, Cauldron, LiteLoader, Quilt, Fabric,
|
||||
Babric, BTA, LegacyFabric, Ornithe, Rift };
|
||||
|
||||
QList<ModLoaderType> modLoaderTypesToList(ModLoaderTypes flags)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -67,7 +67,10 @@ Task::Ptr ResourceAPI::searchProjects(SearchArgs&& args, Callback<QList<ModPlatf
|
|||
}
|
||||
callbacks.on_fail(reason, network_error_code);
|
||||
});
|
||||
QObject::connect(netJob.get(), &NetJob::aborted, [callbacks] { callbacks.on_abort(); });
|
||||
QObject::connect(netJob.get(), &NetJob::aborted, [callbacks] {
|
||||
if (callbacks.on_abort != nullptr)
|
||||
callbacks.on_abort();
|
||||
});
|
||||
|
||||
return netJob;
|
||||
}
|
||||
|
|
@ -80,7 +83,7 @@ Task::Ptr ResourceAPI::getProjectVersions(VersionSearchArgs&& args, Callback<QVe
|
|||
|
||||
auto versions_url = versions_url_optional.value();
|
||||
|
||||
auto netJob = makeShared<NetJob>(QString("%1::Versions").arg(args.pack.name), APPLICATION->network());
|
||||
auto netJob = makeShared<NetJob>(QString("%1::Versions").arg(args.pack->name), APPLICATION->network());
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
|
||||
netJob->addNetAction(Net::ApiDownload::makeByteArray(versions_url, response));
|
||||
|
|
@ -97,14 +100,14 @@ Task::Ptr ResourceAPI::getProjectVersions(VersionSearchArgs&& args, Callback<QVe
|
|||
|
||||
QVector<ModPlatform::IndexedVersion> unsortedVersions;
|
||||
try {
|
||||
auto arr = doc.isObject() ? Json::ensureArray(doc.object(), "data") : doc.array();
|
||||
auto arr = doc.isObject() ? doc.object()["data"].toArray() : doc.array();
|
||||
|
||||
for (auto versionIter : arr) {
|
||||
auto obj = versionIter.toObject();
|
||||
|
||||
auto file = loadIndexedPackVersion(obj, args.resourceType);
|
||||
if (!file.addonId.isValid())
|
||||
file.addonId = args.pack.addonId;
|
||||
file.addonId = args.pack->addonId;
|
||||
|
||||
if (file.fileId.isValid() && !file.downloadUrl.isEmpty()) // Heuristic to check if the returned value is valid
|
||||
unsortedVersions.append(file);
|
||||
|
|
@ -135,15 +138,18 @@ Task::Ptr ResourceAPI::getProjectVersions(VersionSearchArgs&& args, Callback<QVe
|
|||
}
|
||||
callbacks.on_fail(reason, network_error_code);
|
||||
});
|
||||
QObject::connect(netJob.get(), &NetJob::aborted, [callbacks] { callbacks.on_abort(); });
|
||||
QObject::connect(netJob.get(), &NetJob::aborted, [callbacks] {
|
||||
if (callbacks.on_abort != nullptr)
|
||||
callbacks.on_abort();
|
||||
});
|
||||
|
||||
return netJob;
|
||||
}
|
||||
|
||||
Task::Ptr ResourceAPI::getProjectInfo(ProjectInfoArgs&& args, Callback<ModPlatform::IndexedPack>&& callbacks) const
|
||||
Task::Ptr ResourceAPI::getProjectInfo(ProjectInfoArgs&& args, Callback<ModPlatform::IndexedPack::Ptr>&& callbacks) const
|
||||
{
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
auto job = getProject(args.pack.addonId.toString(), response);
|
||||
auto job = getProject(args.pack->addonId.toString(), response);
|
||||
|
||||
QObject::connect(job.get(), &NetJob::succeeded, [this, response, callbacks, args] {
|
||||
auto pack = args.pack;
|
||||
|
|
@ -159,8 +165,8 @@ Task::Ptr ResourceAPI::getProjectInfo(ProjectInfoArgs&& args, Callback<ModPlatfo
|
|||
auto obj = Json::requireObject(doc);
|
||||
if (obj.contains("data"))
|
||||
obj = Json::requireObject(obj, "data");
|
||||
loadIndexedPack(pack, obj);
|
||||
loadExtraPackInfo(pack, obj);
|
||||
loadIndexedPack(*pack, obj);
|
||||
loadExtraPackInfo(*pack, obj);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qDebug() << doc;
|
||||
qWarning() << "Error while reading " << debugName() << " resource info: " << e.cause();
|
||||
|
|
@ -182,7 +188,10 @@ Task::Ptr ResourceAPI::getProjectInfo(ProjectInfoArgs&& args, Callback<ModPlatfo
|
|||
}
|
||||
callbacks.on_fail(reason, network_error_code);
|
||||
});
|
||||
QObject::connect(job.get(), &NetJob::aborted, [callbacks] { callbacks.on_abort(); });
|
||||
QObject::connect(job.get(), &NetJob::aborted, [callbacks] {
|
||||
if (callbacks.on_abort != nullptr)
|
||||
callbacks.on_abort();
|
||||
});
|
||||
return job;
|
||||
}
|
||||
|
||||
|
|
@ -213,7 +222,7 @@ Task::Ptr ResourceAPI::getDependencyVersion(DependencySearchArgs&& args, Callbac
|
|||
if (args.dependency.version.length() != 0 && doc.isObject()) {
|
||||
arr.append(doc.object());
|
||||
} else {
|
||||
arr = doc.isObject() ? Json::ensureArray(doc.object(), "data") : doc.array();
|
||||
arr = doc.isObject() ? doc.object()["data"].toArray() : doc.array();
|
||||
}
|
||||
|
||||
QVector<ModPlatform::IndexedVersion> versions;
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class ResourceAPI {
|
|||
};
|
||||
|
||||
struct VersionSearchArgs {
|
||||
ModPlatform::IndexedPack pack;
|
||||
ModPlatform::IndexedPack::Ptr pack;
|
||||
|
||||
std::optional<std::list<Version>> mcVersions;
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders;
|
||||
|
|
@ -96,7 +96,7 @@ class ResourceAPI {
|
|||
};
|
||||
|
||||
struct ProjectInfoArgs {
|
||||
ModPlatform::IndexedPack pack;
|
||||
ModPlatform::IndexedPack::Ptr pack;
|
||||
};
|
||||
|
||||
struct DependencySearchArgs {
|
||||
|
|
@ -115,7 +115,7 @@ class ResourceAPI {
|
|||
virtual Task::Ptr getProject(QString addonId, std::shared_ptr<QByteArray> response) const;
|
||||
virtual Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const = 0;
|
||||
|
||||
virtual Task::Ptr getProjectInfo(ProjectInfoArgs&&, Callback<ModPlatform::IndexedPack>&&) const;
|
||||
virtual Task::Ptr getProjectInfo(ProjectInfoArgs&&, Callback<ModPlatform::IndexedPack::Ptr>&&) const;
|
||||
Task::Ptr getProjectVersions(VersionSearchArgs&& args, Callback<QVector<ModPlatform::IndexedVersion>>&& callbacks) const;
|
||||
virtual Task::Ptr getDependencyVersion(DependencySearchArgs&&, Callback<ModPlatform::IndexedVersion>&&) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ enum class ResourceType { Mod, ResourcePack, ShaderPack, Modpack, DataPack, Worl
|
|||
|
||||
namespace ResourceTypeUtils {
|
||||
static const std::set<ResourceType> VALID_RESOURCES = { ResourceType::DataPack, ResourceType::ResourcePack, ResourceType::TexturePack,
|
||||
ResourceType::ShaderPack, ResourceType::World, ResourceType::Mod };
|
||||
ResourceType::ShaderPack, ResourceType::World, ResourceType::Mod };
|
||||
QString getName(ResourceType type);
|
||||
} // namespace ResourceTypeUtils
|
||||
} // namespace ModPlatform
|
||||
|
|
@ -40,8 +40,8 @@ void ATLauncher::loadIndexedPack(ATLauncher::IndexedPack& m, QJsonObject& obj)
|
|||
loadIndexedVersion(version, versionObj);
|
||||
m.versions.append(version);
|
||||
}
|
||||
m.system = Json::ensureBoolean(obj, QString("system"), false);
|
||||
m.description = Json::ensureString(obj, "description", "");
|
||||
m.system = obj["system"].toBool();
|
||||
m.description = obj["description"].toString("");
|
||||
|
||||
static const QRegularExpression s_regex("[^A-Za-z0-9]");
|
||||
m.safeName = Json::requireString(obj, "name").replace(s_regex, "").toLower() + ".png";
|
||||
|
|
|
|||
|
|
@ -100,20 +100,20 @@ static ATLauncher::ModType parseModType(QString rawType)
|
|||
static void loadVersionLoader(ATLauncher::VersionLoader& p, QJsonObject& obj)
|
||||
{
|
||||
p.type = Json::requireString(obj, "type");
|
||||
p.choose = Json::ensureBoolean(obj, QString("choose"), false);
|
||||
p.choose = obj["choose"].toBool();
|
||||
|
||||
auto metadata = Json::requireObject(obj, "metadata");
|
||||
p.latest = Json::ensureBoolean(metadata, QString("latest"), false);
|
||||
p.recommended = Json::ensureBoolean(metadata, QString("recommended"), false);
|
||||
p.latest = metadata["latest"].toBool();
|
||||
p.recommended = metadata["recommended"].toBool();
|
||||
|
||||
// Minecraft Forge
|
||||
if (p.type == "forge") {
|
||||
p.version = Json::ensureString(metadata, "version", "");
|
||||
if (p.type == "forge" || p.type == "neoforge") {
|
||||
p.version = metadata["version"].toString("");
|
||||
}
|
||||
|
||||
// Fabric Loader
|
||||
if (p.type == "fabric") {
|
||||
p.version = Json::ensureString(metadata, "loader", "");
|
||||
p.version = metadata["loader"].toString("");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -126,7 +126,7 @@ static void loadVersionLibrary(ATLauncher::VersionLibrary& p, QJsonObject& obj)
|
|||
p.download_raw = Json::requireString(obj, "download");
|
||||
p.download = parseDownloadType(p.download_raw);
|
||||
|
||||
p.server = Json::ensureString(obj, "server", "");
|
||||
p.server = obj["server"].toString("");
|
||||
}
|
||||
|
||||
static void loadVersionConfigs(ATLauncher::VersionConfigs& p, QJsonObject& obj)
|
||||
|
|
@ -141,7 +141,7 @@ static void loadVersionMod(ATLauncher::VersionMod& p, QJsonObject& obj)
|
|||
p.version = Json::requireString(obj, "version");
|
||||
p.url = Json::requireString(obj, "url");
|
||||
p.file = Json::requireString(obj, "file");
|
||||
p.md5 = Json::ensureString(obj, "md5", "");
|
||||
p.md5 = obj["md5"].toString("");
|
||||
|
||||
p.download_raw = Json::requireString(obj, "download");
|
||||
p.download = parseDownloadType(p.download_raw);
|
||||
|
|
@ -161,7 +161,7 @@ static void loadVersionMod(ATLauncher::VersionMod& p, QJsonObject& obj)
|
|||
if (obj.contains("extractTo")) {
|
||||
p.extractTo_raw = Json::requireString(obj, "extractTo");
|
||||
p.extractTo = parseModType(p.extractTo_raw);
|
||||
p.extractFolder = Json::ensureString(obj, "extractFolder", "").replace("%s%", "/");
|
||||
p.extractFolder = obj["extractFolder"].toString("").replace("%s%", "/");
|
||||
}
|
||||
|
||||
if (obj.contains("decompType")) {
|
||||
|
|
@ -170,23 +170,23 @@ static void loadVersionMod(ATLauncher::VersionMod& p, QJsonObject& obj)
|
|||
p.decompFile = Json::requireString(obj, "decompFile");
|
||||
}
|
||||
|
||||
p.description = Json::ensureString(obj, QString("description"), "");
|
||||
p.optional = Json::ensureBoolean(obj, QString("optional"), false);
|
||||
p.recommended = Json::ensureBoolean(obj, QString("recommended"), false);
|
||||
p.selected = Json::ensureBoolean(obj, QString("selected"), false);
|
||||
p.hidden = Json::ensureBoolean(obj, QString("hidden"), false);
|
||||
p.library = Json::ensureBoolean(obj, QString("library"), false);
|
||||
p.group = Json::ensureString(obj, QString("group"), "");
|
||||
p.description = obj["description"].toString("");
|
||||
p.optional = obj["optional"].toBool();
|
||||
p.recommended = obj["recommended"].toBool();
|
||||
p.selected = obj["selected"].toBool();
|
||||
p.hidden = obj["hidden"].toBool();
|
||||
p.library = obj["library"].toBool();
|
||||
p.group = obj["group"].toString("");
|
||||
if (obj.contains("depends")) {
|
||||
auto dependsArr = Json::requireArray(obj, "depends");
|
||||
for (const auto depends : dependsArr) {
|
||||
p.depends.append(Json::requireString(depends));
|
||||
}
|
||||
}
|
||||
p.colour = Json::ensureString(obj, QString("colour"), "");
|
||||
p.warning = Json::ensureString(obj, QString("warning"), "");
|
||||
p.colour = obj["colour"].toString("");
|
||||
p.warning = obj["warning"].toString("");
|
||||
|
||||
p.client = Json::ensureBoolean(obj, QString("client"), false);
|
||||
p.client = obj["client"].toBool();
|
||||
|
||||
// computed
|
||||
p.effectively_hidden = p.hidden || p.library;
|
||||
|
|
@ -194,20 +194,20 @@ static void loadVersionMod(ATLauncher::VersionMod& p, QJsonObject& obj)
|
|||
|
||||
static void loadVersionMessages(ATLauncher::VersionMessages& m, QJsonObject& obj)
|
||||
{
|
||||
m.install = Json::ensureString(obj, "install", "");
|
||||
m.update = Json::ensureString(obj, "update", "");
|
||||
m.install = obj["install"].toString("");
|
||||
m.update = obj["update"].toString("");
|
||||
}
|
||||
|
||||
static void loadVersionMainClass(ATLauncher::PackVersionMainClass& m, QJsonObject& obj)
|
||||
{
|
||||
m.mainClass = Json::ensureString(obj, "mainClass", "");
|
||||
m.depends = Json::ensureString(obj, "depends", "");
|
||||
m.mainClass = obj["mainClass"].toString("");
|
||||
m.depends = obj["depends"].toString("");
|
||||
}
|
||||
|
||||
static void loadVersionExtraArguments(ATLauncher::PackVersionExtraArguments& a, QJsonObject& obj)
|
||||
{
|
||||
a.arguments = Json::ensureString(obj, "arguments", "");
|
||||
a.depends = Json::ensureString(obj, "depends", "");
|
||||
a.arguments = obj["arguments"].toString("");
|
||||
a.depends = obj["depends"].toString("");
|
||||
}
|
||||
|
||||
static void loadVersionKeep(ATLauncher::VersionKeep& k, QJsonObject& obj)
|
||||
|
|
@ -272,7 +272,7 @@ void ATLauncher::loadVersion(PackVersion& v, QJsonObject& obj)
|
|||
{
|
||||
v.version = Json::requireString(obj, "version");
|
||||
v.minecraft = Json::requireString(obj, "minecraft");
|
||||
v.noConfigs = Json::ensureBoolean(obj, QString("noConfigs"), false);
|
||||
v.noConfigs = obj["noConfigs"].toBool();
|
||||
|
||||
if (obj.contains("mainClass")) {
|
||||
auto main = Json::requireObject(obj, "mainClass");
|
||||
|
|
@ -314,22 +314,22 @@ void ATLauncher::loadVersion(PackVersion& v, QJsonObject& obj)
|
|||
loadVersionConfigs(v.configs, configsObj);
|
||||
}
|
||||
|
||||
auto colourObj = Json::ensureObject(obj, "colours");
|
||||
auto colourObj = obj["colours"].toObject();
|
||||
for (const auto& key : colourObj.keys()) {
|
||||
v.colours[key] = Json::requireString(colourObj.value(key), "colour");
|
||||
}
|
||||
|
||||
auto warningsObj = Json::ensureObject(obj, "warnings");
|
||||
auto warningsObj = obj["warnings"].toObject();
|
||||
for (const auto& key : warningsObj.keys()) {
|
||||
v.warnings[key] = Json::requireString(warningsObj.value(key), "warning");
|
||||
}
|
||||
|
||||
auto messages = Json::ensureObject(obj, "messages");
|
||||
auto messages = obj["messages"].toObject();
|
||||
loadVersionMessages(v.messages, messages);
|
||||
|
||||
auto keeps = Json::ensureObject(obj, "keeps");
|
||||
auto keeps = obj["keeps"].toObject();
|
||||
loadVersionKeeps(v.keeps, keeps);
|
||||
|
||||
auto deletes = Json::ensureObject(obj, "deletes");
|
||||
auto deletes = obj["deletes"].toObject();
|
||||
loadVersionDeletes(v.deletes, deletes);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ QString FlameAPI::getModFileChangelog(int modId, int fileId)
|
|||
return;
|
||||
}
|
||||
|
||||
changelog = Json::ensureString(doc.object(), "data");
|
||||
changelog = doc.object()["data"].toString();
|
||||
});
|
||||
|
||||
QObject::connect(netJob.get(), &NetJob::finished, [&lock] { lock.quit(); });
|
||||
|
|
@ -92,7 +92,7 @@ QString FlameAPI::getModDescription(int modId)
|
|||
return;
|
||||
}
|
||||
|
||||
description = Json::ensureString(doc.object(), "data");
|
||||
description = doc.object()["data"].toString();
|
||||
});
|
||||
|
||||
QObject::connect(netJob.get(), &NetJob::finished, [&lock] { lock.quit(); });
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ class FlameAPI : public ResourceAPI {
|
|||
|
||||
std::optional<QString> getVersionsURL(VersionSearchArgs const& args) const override
|
||||
{
|
||||
auto addonId = args.pack.addonId.toString();
|
||||
auto addonId = args.pack->addonId.toString();
|
||||
QString url = QString(BuildConfig.FLAME_BASE_URL + "/mods/%1/files?pageSize=10000").arg(addonId);
|
||||
|
||||
if (args.mcVersions.has_value())
|
||||
|
|
@ -140,7 +140,7 @@ class FlameAPI : public ResourceAPI {
|
|||
return url;
|
||||
}
|
||||
|
||||
QJsonArray documentToArray(QJsonDocument& obj) const override { return Json::ensureArray(obj.object(), "data"); }
|
||||
QJsonArray documentToArray(QJsonDocument& obj) const override { return obj.object()["data"].toArray(); }
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) const override { FlameMod::loadIndexedPack(m, obj); }
|
||||
ModPlatform::IndexedVersion loadIndexedPackVersion(QJsonObject& obj, ModPlatform::ResourceType resourceType) const override
|
||||
{
|
||||
|
|
@ -160,10 +160,7 @@ class FlameAPI : public ResourceAPI {
|
|||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, [[maybe_unused]] QJsonObject&) const override { FlameMod::loadBody(m); }
|
||||
|
||||
private:
|
||||
std::optional<QString> getInfoURL(QString const& id) const override
|
||||
{
|
||||
return QString(BuildConfig.FLAME_BASE_URL + "/mods/%1").arg(id);
|
||||
}
|
||||
std::optional<QString> getInfoURL(QString const& id) const override { return QString(BuildConfig.FLAME_BASE_URL + "/mods/%1").arg(id); }
|
||||
std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override
|
||||
{
|
||||
auto addonId = args.dependency.addonId.toString();
|
||||
|
|
|
|||
|
|
@ -46,7 +46,9 @@ void FlameCheckUpdate::executeTask()
|
|||
connect(netJob, &Task::stepProgress, this, &FlameCheckUpdate::propagateStepProgress);
|
||||
connect(netJob, &Task::details, this, &FlameCheckUpdate::setDetails);
|
||||
for (auto* resource : m_resources) {
|
||||
auto versionsUrlOptional = api.getVersionsURL({ { resource->metadata()->project_id.toString() }, m_gameVersions });
|
||||
auto project = std::make_shared<ModPlatform::IndexedPack>();
|
||||
project->addonId = resource->metadata()->project_id.toString();
|
||||
auto versionsUrlOptional = api.getVersionsURL({ project, m_gameVersions });
|
||||
if (!versionsUrlOptional.has_value())
|
||||
continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,23 +15,26 @@ void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
|||
pack.provider = ModPlatform::ResourceProvider::FLAME;
|
||||
pack.name = Json::requireString(obj, "name");
|
||||
pack.slug = Json::requireString(obj, "slug");
|
||||
pack.websiteUrl = Json::ensureString(Json::ensureObject(obj, "links"), "websiteUrl", "");
|
||||
pack.description = Json::ensureString(obj, "summary", "");
|
||||
pack.websiteUrl = obj["links"].toObject()["websiteUrl"].toString("");
|
||||
pack.description = obj["summary"].toString("");
|
||||
|
||||
QJsonObject logo = Json::ensureObject(obj, "logo");
|
||||
pack.logoName = Json::ensureString(logo, "title");
|
||||
pack.logoUrl = Json::ensureString(logo, "thumbnailUrl");
|
||||
QJsonObject logo = obj["logo"].toObject();
|
||||
pack.logoName = logo["title"].toString();
|
||||
pack.logoUrl = logo["thumbnailUrl"].toString();
|
||||
if (pack.logoUrl.isEmpty()) {
|
||||
pack.logoUrl = Json::ensureString(logo, "url");
|
||||
pack.logoUrl = logo["url"].toString();
|
||||
}
|
||||
|
||||
auto authors = Json::ensureArray(obj, "authors");
|
||||
for (auto authorIter : authors) {
|
||||
auto author = Json::requireObject(authorIter);
|
||||
ModPlatform::ModpackAuthor packAuthor;
|
||||
packAuthor.name = Json::requireString(author, "name");
|
||||
packAuthor.url = Json::requireString(author, "url");
|
||||
pack.authors.append(packAuthor);
|
||||
auto authors = obj["authors"].toArray();
|
||||
if (!authors.isEmpty()) {
|
||||
pack.authors.clear();
|
||||
for (auto authorIter : authors) {
|
||||
auto author = Json::requireObject(authorIter);
|
||||
ModPlatform::ModpackAuthor packAuthor;
|
||||
packAuthor.name = Json::requireString(author, "name");
|
||||
packAuthor.url = Json::requireString(author, "url");
|
||||
pack.authors.append(packAuthor);
|
||||
}
|
||||
}
|
||||
|
||||
pack.extraDataLoaded = false;
|
||||
|
|
@ -40,17 +43,17 @@ void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
|||
|
||||
void FlameMod::loadURLs(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||
{
|
||||
auto links_obj = Json::ensureObject(obj, "links");
|
||||
auto links_obj = obj["links"].toObject();
|
||||
|
||||
pack.extraData.issuesUrl = Json::ensureString(links_obj, "issuesUrl");
|
||||
pack.extraData.issuesUrl = links_obj["issuesUrl"].toString();
|
||||
if (pack.extraData.issuesUrl.endsWith('/'))
|
||||
pack.extraData.issuesUrl.chop(1);
|
||||
|
||||
pack.extraData.sourceUrl = Json::ensureString(links_obj, "sourceUrl");
|
||||
pack.extraData.sourceUrl = links_obj["sourceUrl"].toString();
|
||||
if (pack.extraData.sourceUrl.endsWith('/'))
|
||||
pack.extraData.sourceUrl.chop(1);
|
||||
|
||||
pack.extraData.wikiUrl = Json::ensureString(links_obj, "wikiUrl");
|
||||
pack.extraData.wikiUrl = links_obj["wikiUrl"].toString();
|
||||
if (pack.extraData.wikiUrl.endsWith('/'))
|
||||
pack.extraData.wikiUrl.chop(1);
|
||||
|
||||
|
|
@ -136,7 +139,7 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
|
|||
file.fileId = Json::requireInteger(obj, "id");
|
||||
file.date = Json::requireString(obj, "fileDate");
|
||||
file.version = Json::requireString(obj, "displayName");
|
||||
file.downloadUrl = Json::ensureString(obj, "downloadUrl");
|
||||
file.downloadUrl = obj["downloadUrl"].toString();
|
||||
file.fileName = Json::requireString(obj, "fileName");
|
||||
file.fileName = FS::RemoveInvalidPathChars(file.fileName);
|
||||
|
||||
|
|
@ -156,11 +159,11 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
|
|||
}
|
||||
file.version_type = ModPlatform::IndexedVersionType(ver_type);
|
||||
|
||||
auto hash_list = Json::ensureArray(obj, "hashes");
|
||||
auto hash_list = obj["hashes"].toArray();
|
||||
for (auto h : hash_list) {
|
||||
auto hash_entry = Json::ensureObject(h);
|
||||
auto hash_entry = h.toObject();
|
||||
auto hash_types = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::FLAME);
|
||||
auto hash_algo = enumToString(Json::ensureInteger(hash_entry, "algo", 1, "algorithm"));
|
||||
auto hash_algo = enumToString(hash_entry["algo"].toInt(1));
|
||||
if (hash_types.contains(hash_algo)) {
|
||||
file.hash = Json::requireString(hash_entry, "value");
|
||||
file.hash_type = hash_algo;
|
||||
|
|
@ -168,9 +171,9 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
|
|||
}
|
||||
}
|
||||
|
||||
auto dependencies = Json::ensureArray(obj, "dependencies");
|
||||
auto dependencies = obj["dependencies"].toArray();
|
||||
for (auto d : dependencies) {
|
||||
auto dep = Json::ensureObject(d);
|
||||
auto dep = d.toObject();
|
||||
ModPlatform::Dependency dependency;
|
||||
dependency.addonId = Json::requireInteger(dep, "modId");
|
||||
switch (Json::requireInteger(dep, "relationType")) {
|
||||
|
|
|
|||
|
|
@ -199,8 +199,8 @@ void FlamePackExportTask::makeApiRequest()
|
|||
return;
|
||||
}
|
||||
for (auto match : dataArr) {
|
||||
auto matchObj = Json::ensureObject(match, {});
|
||||
auto fileObj = Json::ensureObject(matchObj, "file", {});
|
||||
auto matchObj = match.toObject();
|
||||
auto fileObj = matchObj["file"].toObject();
|
||||
|
||||
if (matchObj.isEmpty() || fileObj.isEmpty()) {
|
||||
qWarning() << "Fingerprint match is empty!";
|
||||
|
|
@ -208,7 +208,7 @@ void FlamePackExportTask::makeApiRequest()
|
|||
return;
|
||||
}
|
||||
|
||||
auto fingerprint = QString::number(Json::ensureVariant(fileObj, "fileFingerprint").toUInt());
|
||||
auto fingerprint = QString::number(fileObj["fileFingerprint"].toInteger());
|
||||
auto mod = pendingHashes.find(fingerprint);
|
||||
if (mod == pendingHashes.end()) {
|
||||
qWarning() << "Invalid fingerprint from the API response.";
|
||||
|
|
@ -216,7 +216,7 @@ void FlamePackExportTask::makeApiRequest()
|
|||
}
|
||||
|
||||
setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(mod->name));
|
||||
if (Json::ensureBoolean(fileObj, "isAvailable", false, "isAvailable"))
|
||||
if (fileObj["isAvailable"].toBool())
|
||||
resolvedFiles.insert(mod->path, { Json::requireInteger(fileObj, "modId"), Json::requireInteger(fileObj, "id"),
|
||||
mod->enabled, mod->isMod });
|
||||
}
|
||||
|
|
@ -429,4 +429,4 @@ QByteArray FlamePackExportTask::generateHTML()
|
|||
}
|
||||
content = "<ul>" + content + "</ul>";
|
||||
return content.toUtf8();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@ static void loadFileV1(Flame::File& f, QJsonObject& file)
|
|||
{
|
||||
f.projectId = Json::requireInteger(file, "projectID");
|
||||
f.fileId = Json::requireInteger(file, "fileID");
|
||||
f.required = Json::ensureBoolean(file, QString("required"), true);
|
||||
f.required = file["required"].toBool(true);
|
||||
}
|
||||
|
||||
static void loadModloaderV1(Flame::Modloader& m, QJsonObject& modLoader)
|
||||
{
|
||||
m.id = Json::requireString(modLoader, "id");
|
||||
m.primary = Json::ensureBoolean(modLoader, QString("primary"), false);
|
||||
m.primary = modLoader["primary"].toBool();
|
||||
}
|
||||
|
||||
static void loadMinecraftV1(Flame::Minecraft& m, QJsonObject& minecraft)
|
||||
|
|
@ -19,15 +19,15 @@ static void loadMinecraftV1(Flame::Minecraft& m, QJsonObject& minecraft)
|
|||
m.version = Json::requireString(minecraft, "version");
|
||||
// extra libraries... apparently only used for a custom Minecraft launcher in the 1.2.5 FTB retro pack
|
||||
// intended use is likely hardcoded in the 'Flame' client, the manifest says nothing
|
||||
m.libraries = Json::ensureString(minecraft, QString("libraries"), QString());
|
||||
auto arr = Json::ensureArray(minecraft, "modLoaders", QJsonArray());
|
||||
m.libraries = minecraft["libraries"].toString();
|
||||
auto arr = minecraft["modLoaders"].toArray();
|
||||
for (QJsonValueRef item : arr) {
|
||||
auto obj = Json::requireObject(item);
|
||||
Flame::Modloader loader;
|
||||
loadModloaderV1(loader, obj);
|
||||
m.modLoaders.append(loader);
|
||||
}
|
||||
m.recommendedRAM = Json::ensureInteger(minecraft, "recommendedRam", 0);
|
||||
m.recommendedRAM = minecraft["recommendedRam"].toInt();
|
||||
}
|
||||
|
||||
static void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest)
|
||||
|
|
@ -36,11 +36,11 @@ static void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest)
|
|||
|
||||
loadMinecraftV1(pack.minecraft, mc);
|
||||
|
||||
pack.name = Json::ensureString(manifest, QString("name"), "Unnamed");
|
||||
pack.version = Json::ensureString(manifest, QString("version"), QString());
|
||||
pack.author = Json::ensureString(manifest, QString("author"), "Anonymous");
|
||||
pack.name = manifest["name"].toString("Unnamed");
|
||||
pack.version = manifest["version"].toString();
|
||||
pack.author = manifest["author"].toString("Anonymous");
|
||||
|
||||
auto arr = Json::ensureArray(manifest, "files", QJsonArray());
|
||||
auto arr = manifest["files"].toArray();
|
||||
for (auto item : arr) {
|
||||
auto obj = Json::requireObject(item);
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ static void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest)
|
|||
pack.files.insert(file.fileId, file);
|
||||
}
|
||||
|
||||
pack.overrides = Json::ensureString(manifest, "overrides", "overrides");
|
||||
pack.overrides = manifest["overrides"].toString("overrides");
|
||||
|
||||
pack.is_loaded = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ Modpack parseDirectory(QString path)
|
|||
modpack.name = Json::requireString(root, "name", "name");
|
||||
modpack.version = Json::requireString(root, "version", "version");
|
||||
modpack.mcVersion = Json::requireString(root, "mcVersion", "mcVersion");
|
||||
modpack.jvmArgs = Json::ensureVariant(root, "jvmArgs", {}, "jvmArgs");
|
||||
modpack.jvmArgs = root["jvmArgs"].toVariant();
|
||||
modpack.totalPlayTime = Json::requireInteger(root, "totalPlayTime", "totalPlayTime");
|
||||
} catch (const Exception& e) {
|
||||
qDebug() << "Couldn't load ftb instance json: " << e.cause();
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@
|
|||
#include "meta/VersionList.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
#include "net/NetJob.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace LegacyFTB {
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ QList<ModPlatform::Category> ModrinthAPI::loadCategories(std::shared_ptr<QByteAr
|
|||
for (auto val : arr) {
|
||||
auto cat = Json::requireObject(val);
|
||||
auto name = Json::requireString(cat, "name");
|
||||
if (Json::ensureString(cat, "project_type", "") == projectType)
|
||||
if (cat["project_type"].toString() == projectType)
|
||||
categories.push_back({ name, name });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,8 @@ class ModrinthAPI : public ResourceAPI {
|
|||
{
|
||||
QStringList l;
|
||||
for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Fabric, ModPlatform::Quilt, ModPlatform::LiteLoader,
|
||||
ModPlatform::DataPack, ModPlatform::Babric, ModPlatform::BTA, ModPlatform::LegacyFabric, ModPlatform::Ornithe, ModPlatform::Rift }) {
|
||||
ModPlatform::DataPack, ModPlatform::Babric, ModPlatform::BTA, ModPlatform::LegacyFabric, ModPlatform::Ornithe,
|
||||
ModPlatform::Rift }) {
|
||||
if (types & loader) {
|
||||
l << getModLoaderAsString(loader);
|
||||
}
|
||||
|
|
@ -188,7 +189,7 @@ class ModrinthAPI : public ResourceAPI {
|
|||
get_arguments.append(QString("loaders=[\"%1\"]").arg(getModLoaderStrings(args.loaders.value()).join("\",\"")));
|
||||
|
||||
return QString("%1/project/%2/version%3%4")
|
||||
.arg(BuildConfig.MODRINTH_PROD_URL, args.pack.addonId.toString(), get_arguments.isEmpty() ? "" : "?", get_arguments.join('&'));
|
||||
.arg(BuildConfig.MODRINTH_PROD_URL, args.pack->addonId.toString(), get_arguments.isEmpty() ? "" : "?", get_arguments.join('&'));
|
||||
};
|
||||
|
||||
QString getGameVersionsArray(std::list<Version> mcVersions) const
|
||||
|
|
@ -204,7 +205,8 @@ class ModrinthAPI : public ResourceAPI {
|
|||
static inline auto validateModLoaders(ModPlatform::ModLoaderTypes loaders) -> bool
|
||||
{
|
||||
return loaders & (ModPlatform::NeoForge | ModPlatform::Forge | ModPlatform::Fabric | ModPlatform::Quilt | ModPlatform::LiteLoader |
|
||||
ModPlatform::DataPack | ModPlatform::Babric | ModPlatform::BTA | ModPlatform::LegacyFabric | ModPlatform::Ornithe | ModPlatform::Rift);
|
||||
ModPlatform::DataPack | ModPlatform::Babric | ModPlatform::BTA | ModPlatform::LegacyFabric |
|
||||
ModPlatform::Ornithe | ModPlatform::Rift);
|
||||
}
|
||||
|
||||
std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ bool ModrinthCreationTask::createInstance()
|
|||
auto root_modpack_url = QUrl::fromLocalFile(root_modpack_path);
|
||||
// TODO make this work with other sorts of resource
|
||||
QHash<QString, Resource*> resources;
|
||||
for (auto file : m_files) {
|
||||
for (auto& file : m_files) {
|
||||
auto fileName = file.path;
|
||||
fileName = FS::RemoveInvalidPathChars(fileName);
|
||||
auto file_path = FS::PathCombine(root_modpack_path, fileName);
|
||||
|
|
@ -371,8 +371,8 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path,
|
|||
|
||||
if (set_internal_data) {
|
||||
if (m_managed_version_id.isEmpty())
|
||||
m_managed_version_id = Json::ensureString(obj, "versionId", {}, "Managed ID");
|
||||
m_managed_name = Json::ensureString(obj, "name", {}, "Managed Name");
|
||||
m_managed_version_id = obj["versionId"].toString();
|
||||
m_managed_name = obj["name"].toString();
|
||||
}
|
||||
|
||||
auto jsonFiles = Json::requireIsArrayOf<QJsonObject>(obj, "files", "modrinth.index.json");
|
||||
|
|
@ -381,10 +381,10 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path,
|
|||
File file;
|
||||
file.path = Json::requireString(modInfo, "path").replace("\\", "/");
|
||||
|
||||
auto env = Json::ensureObject(modInfo, "env");
|
||||
auto env = modInfo["env"].toObject();
|
||||
// 'env' field is optional
|
||||
if (!env.isEmpty()) {
|
||||
QString support = Json::ensureString(env, "client", "unsupported");
|
||||
QString support = env["client"].toString("unsupported");
|
||||
if (support == "unsupported") {
|
||||
continue;
|
||||
} else if (support == "optional") {
|
||||
|
|
@ -399,7 +399,7 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path,
|
|||
// Do not use requireUrl, which uses StrictMode, instead use QUrl's default TolerantMode
|
||||
// (as Modrinth seems to incorrectly handle spaces)
|
||||
|
||||
auto download_arr = Json::ensureArray(modInfo, "downloads");
|
||||
auto download_arr = modInfo["downloads"].toArray();
|
||||
for (auto download : download_arr) {
|
||||
qWarning() << download.toString();
|
||||
bool is_last = download.toString() == download_arr.last().toString();
|
||||
|
|
|
|||
|
|
@ -33,34 +33,36 @@ bool shouldDownloadOnSide(QString side)
|
|||
return side == "required" || side == "optional";
|
||||
}
|
||||
|
||||
// https://docs.modrinth.com/api-spec/#tag/projects/operation/getProject
|
||||
// https://docs.modrinth.com/api/operations/getproject/
|
||||
void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||
{
|
||||
pack.addonId = Json::ensureString(obj, "project_id");
|
||||
pack.addonId = obj["project_id"].toString();
|
||||
if (pack.addonId.toString().isEmpty())
|
||||
pack.addonId = Json::requireString(obj, "id");
|
||||
|
||||
pack.provider = ModPlatform::ResourceProvider::MODRINTH;
|
||||
pack.name = Json::requireString(obj, "title");
|
||||
|
||||
pack.slug = Json::ensureString(obj, "slug", "");
|
||||
pack.slug = obj["slug"].toString("");
|
||||
if (!pack.slug.isEmpty())
|
||||
pack.websiteUrl = "https://modrinth.com/mod/" + pack.slug;
|
||||
else
|
||||
pack.websiteUrl = "";
|
||||
|
||||
pack.description = Json::ensureString(obj, "description", "");
|
||||
pack.description = obj["description"].toString("");
|
||||
|
||||
pack.logoUrl = Json::ensureString(obj, "icon_url", "");
|
||||
pack.logoName = QString("%1.%2").arg(Json::ensureString(obj, "slug"), QFileInfo(QUrl(pack.logoUrl).fileName()).suffix());
|
||||
pack.logoUrl = obj["icon_url"].toString("");
|
||||
pack.logoName = QString("%1.%2").arg(obj["slug"].toString(), QFileInfo(QUrl(pack.logoUrl).fileName()).suffix());
|
||||
|
||||
ModPlatform::ModpackAuthor modAuthor;
|
||||
modAuthor.name = Json::ensureString(obj, "author", QObject::tr("No author(s)"));
|
||||
modAuthor.url = api.getAuthorURL(modAuthor.name);
|
||||
pack.authors.append(modAuthor);
|
||||
if (obj.contains("author")) {
|
||||
ModPlatform::ModpackAuthor modAuthor;
|
||||
modAuthor.name = obj["author"].toString();
|
||||
modAuthor.url = api.getAuthorURL(modAuthor.name);
|
||||
pack.authors = { modAuthor };
|
||||
}
|
||||
|
||||
auto client = shouldDownloadOnSide(Json::ensureString(obj, "client_side"));
|
||||
auto server = shouldDownloadOnSide(Json::ensureString(obj, "server_side"));
|
||||
auto client = shouldDownloadOnSide(obj["client_side"].toString());
|
||||
auto server = shouldDownloadOnSide(obj["server_side"].toString());
|
||||
|
||||
if (server && client) {
|
||||
pack.side = ModPlatform::Side::UniversalSide;
|
||||
|
|
@ -76,38 +78,38 @@ void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
|||
|
||||
void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||
{
|
||||
pack.extraData.issuesUrl = Json::ensureString(obj, "issues_url");
|
||||
pack.extraData.issuesUrl = obj["issues_url"].toString();
|
||||
if (pack.extraData.issuesUrl.endsWith('/'))
|
||||
pack.extraData.issuesUrl.chop(1);
|
||||
|
||||
pack.extraData.sourceUrl = Json::ensureString(obj, "source_url");
|
||||
pack.extraData.sourceUrl = obj["source_url"].toString();
|
||||
if (pack.extraData.sourceUrl.endsWith('/'))
|
||||
pack.extraData.sourceUrl.chop(1);
|
||||
|
||||
pack.extraData.wikiUrl = Json::ensureString(obj, "wiki_url");
|
||||
pack.extraData.wikiUrl = obj["wiki_url"].toString();
|
||||
if (pack.extraData.wikiUrl.endsWith('/'))
|
||||
pack.extraData.wikiUrl.chop(1);
|
||||
|
||||
pack.extraData.discordUrl = Json::ensureString(obj, "discord_url");
|
||||
pack.extraData.discordUrl = obj["discord_url"].toString();
|
||||
if (pack.extraData.discordUrl.endsWith('/'))
|
||||
pack.extraData.discordUrl.chop(1);
|
||||
|
||||
auto donate_arr = Json::ensureArray(obj, "donation_urls");
|
||||
auto donate_arr = obj["donation_urls"].toArray();
|
||||
for (auto d : donate_arr) {
|
||||
auto d_obj = Json::requireObject(d);
|
||||
|
||||
ModPlatform::DonationData donate;
|
||||
|
||||
donate.id = Json::ensureString(d_obj, "id");
|
||||
donate.platform = Json::ensureString(d_obj, "platform");
|
||||
donate.url = Json::ensureString(d_obj, "url");
|
||||
donate.id = d_obj["id"].toString();
|
||||
donate.platform = d_obj["platform"].toString();
|
||||
donate.url = d_obj["url"].toString();
|
||||
|
||||
pack.extraData.donate.append(donate);
|
||||
}
|
||||
|
||||
pack.extraData.status = Json::ensureString(obj, "status");
|
||||
pack.extraData.status = obj["status"].toString();
|
||||
|
||||
pack.extraData.body = Json::ensureString(obj, "body").remove("<br>");
|
||||
pack.extraData.body = obj["body"].toString().remove("<br>");
|
||||
|
||||
pack.extraDataLoaded = true;
|
||||
}
|
||||
|
|
@ -147,12 +149,12 @@ ModPlatform::IndexedVersion Modrinth::loadIndexedPackVersion(QJsonObject& obj, Q
|
|||
|
||||
file.changelog = Json::requireString(obj, "changelog");
|
||||
|
||||
auto dependencies = Json::ensureArray(obj, "dependencies");
|
||||
auto dependencies = obj["dependencies"].toArray();
|
||||
for (auto d : dependencies) {
|
||||
auto dep = Json::ensureObject(d);
|
||||
auto dep = d.toObject();
|
||||
ModPlatform::Dependency dependency;
|
||||
dependency.addonId = Json::ensureString(dep, "project_id");
|
||||
dependency.version = Json::ensureString(dep, "version_id");
|
||||
dependency.addonId = dep["project_id"].toString();
|
||||
dependency.version = dep["version_id"].toString();
|
||||
auto depType = Json::requireString(dep, "dependency_type");
|
||||
|
||||
if (depType == "required")
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ void loadPack(Pack& v, QJsonObject& obj)
|
|||
static void loadPackBuildMod(PackBuildMod& b, QJsonObject& obj)
|
||||
{
|
||||
b.name = Json::requireString(obj, "name");
|
||||
b.version = Json::ensureString(obj, "version", "");
|
||||
b.version = obj["version"].toString("");
|
||||
b.md5 = Json::requireString(obj, "md5");
|
||||
b.url = Json::requireString(obj, "url");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings,
|
|||
try {
|
||||
QJsonDocument doc = Json::requireDocument(data);
|
||||
QJsonObject root = Json::requireObject(doc, "version.json");
|
||||
QString packMinecraftVersion = Json::ensureString(root, "inheritsFrom", QString(), "");
|
||||
QString packMinecraftVersion = root["inheritsFrom"].toString();
|
||||
if (packMinecraftVersion.isEmpty()) {
|
||||
if (fmlMinecraftVersion.isEmpty()) {
|
||||
emit failed(tr("Could not understand \"version.json\":\ninheritsFrom is missing"));
|
||||
|
|
@ -151,21 +151,21 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings,
|
|||
packMinecraftVersion = fmlMinecraftVersion;
|
||||
}
|
||||
components->setComponentVersion("net.minecraft", packMinecraftVersion, true);
|
||||
for (auto library : Json::ensureArray(root, "libraries", {})) {
|
||||
for (auto library : root["libraries"].toArray()) {
|
||||
if (!library.isObject()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto libraryObject = Json::ensureObject(library, {}, "");
|
||||
auto libraryName = Json::ensureString(libraryObject, "name", "", "");
|
||||
auto libraryObject = library.toObject();
|
||||
auto libraryName = libraryObject["name"].toString();
|
||||
|
||||
if (libraryName.startsWith("net.neoforged.fancymodloader:")) { // it is neoforge
|
||||
// no easy way to get the version from the libs so use the arguments
|
||||
auto arguments = Json::ensureObject(root, "arguments", {});
|
||||
auto arguments = root["arguments"].toObject();
|
||||
bool isVersionArg = false;
|
||||
QString neoforgeVersion;
|
||||
for (auto arg : Json::ensureArray(arguments, "game", {})) {
|
||||
auto argument = Json::ensureString(arg, "");
|
||||
for (auto arg : arguments["game"].toArray()) {
|
||||
auto argument = arg.toString("");
|
||||
if (isVersionArg) {
|
||||
neoforgeVersion = argument;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -248,15 +248,15 @@ void HttpMetaCache::Load()
|
|||
auto root = json.object();
|
||||
|
||||
// check file version first
|
||||
auto version_val = Json::ensureString(root, "version");
|
||||
auto version_val = root["version"].toString();
|
||||
if (version_val != "1")
|
||||
return;
|
||||
|
||||
// read the entry array
|
||||
auto array = Json::ensureArray(root, "entries");
|
||||
auto array = root["entries"].toArray();
|
||||
for (auto element : array) {
|
||||
auto element_obj = Json::ensureObject(element);
|
||||
auto base = Json::ensureString(element_obj, "base");
|
||||
auto element_obj = element.toObject();
|
||||
auto base = element_obj["base"].toString();
|
||||
if (!m_entries.contains(base))
|
||||
continue;
|
||||
|
||||
|
|
@ -264,16 +264,16 @@ void HttpMetaCache::Load()
|
|||
|
||||
auto foo = new MetaEntry();
|
||||
foo->m_baseId = base;
|
||||
foo->m_relativePath = Json::ensureString(element_obj, "path");
|
||||
foo->m_md5sum = Json::ensureString(element_obj, "md5sum");
|
||||
foo->m_etag = Json::ensureString(element_obj, "etag");
|
||||
foo->m_local_changed_timestamp = Json::ensureDouble(element_obj, "last_changed_timestamp");
|
||||
foo->m_remote_changed_timestamp = Json::ensureString(element_obj, "remote_changed_timestamp");
|
||||
foo->m_relativePath = element_obj["path"].toString();
|
||||
foo->m_md5sum = element_obj["md5sum"].toString();
|
||||
foo->m_etag = element_obj["etag"].toString();
|
||||
foo->m_local_changed_timestamp = element_obj["last_changed_timestamp"].toDouble();
|
||||
foo->m_remote_changed_timestamp = element_obj["remote_changed_timestamp"].toString();
|
||||
|
||||
foo->makeEternal(Json::ensureBoolean(element_obj, (const QString)QStringLiteral("eternal"), false));
|
||||
foo->makeEternal(element_obj[QStringLiteral("eternal")].toBool());
|
||||
if (!foo->isEternal()) {
|
||||
foo->m_current_age = Json::ensureDouble(element_obj, "current_age");
|
||||
foo->m_max_age = Json::ensureDouble(element_obj, "max_age");
|
||||
foo->m_current_age = element_obj["current_age"].toDouble();
|
||||
foo->m_max_age = element_obj["max_age"].toDouble();
|
||||
}
|
||||
|
||||
// presumed innocent until closer examination
|
||||
|
|
|
|||
|
|
@ -252,7 +252,6 @@
|
|||
<file>scalable/discord.svg</file>
|
||||
|
||||
<!-- flat instance icons CC BY-SA 4.0, Santiago Cézar -->
|
||||
<file alias="128x128/flame.png">scalable/instances/flame.svg</file>
|
||||
<file>scalable/instances/chicken.svg</file>
|
||||
<file>scalable/instances/creeper.svg</file>
|
||||
<file>scalable/instances/enderpearl.svg</file>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,12 @@
|
|||
#include "settings/Setting.h"
|
||||
|
||||
#include <QVariant>
|
||||
#include <QDir>
|
||||
#include <utility>
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
#include "macsandbox/SecurityBookmarkFileAccess.h"
|
||||
#endif
|
||||
|
||||
SettingsObject::SettingsObject(QObject* parent) : QObject(parent) {}
|
||||
|
||||
|
|
@ -78,9 +84,17 @@ std::shared_ptr<Setting> SettingsObject::getSetting(const QString& id) const
|
|||
return m_settings[id];
|
||||
}
|
||||
|
||||
QVariant SettingsObject::get(const QString& id) const
|
||||
QVariant SettingsObject::get(const QString& id)
|
||||
{
|
||||
auto setting = getSetting(id);
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
// for macOS, use a security scoped bookmark for the paths
|
||||
if (id.endsWith("Dir")) {
|
||||
return { getPathFromBookmark(id) };
|
||||
}
|
||||
#endif
|
||||
|
||||
return (setting ? setting->get() : QVariant());
|
||||
}
|
||||
|
||||
|
|
@ -90,11 +104,105 @@ bool SettingsObject::set(const QString& id, QVariant value)
|
|||
if (!setting) {
|
||||
qCritical() << QString("Error changing setting %1. Setting doesn't exist.").arg(id);
|
||||
return false;
|
||||
} else {
|
||||
setting->set(value);
|
||||
}
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
// for macOS, keep a security scoped bookmark for the paths
|
||||
if (value.userType() == QMetaType::QString && id.endsWith("Dir")) {
|
||||
setPathWithBookmark(id, value.toString());
|
||||
}
|
||||
#endif
|
||||
|
||||
setting->set(std::move(value));
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
QString SettingsObject::getPathFromBookmark(const QString& id)
|
||||
{
|
||||
auto setting = getSetting(id);
|
||||
if (!setting) {
|
||||
qCritical() << QString("Error changing setting %1. Setting doesn't exist.").arg(id);
|
||||
return "";
|
||||
}
|
||||
|
||||
// there is no need to use bookmarks if the default value is used or the directory is within the data directory (already can access)
|
||||
if (setting->get() == setting->defValue() || QDir(setting->get().toString()).absolutePath().startsWith(QDir::current().absolutePath())) {
|
||||
return setting->get().toString();
|
||||
}
|
||||
|
||||
auto bookmarkId = id + "Bookmark";
|
||||
auto bookmarkSetting = getSetting(bookmarkId);
|
||||
if (!bookmarkSetting) {
|
||||
qCritical() << QString("Error changing setting %1. Bookmark setting doesn't exist.").arg(id);
|
||||
return "";
|
||||
}
|
||||
|
||||
QByteArray bookmark = bookmarkSetting->get().toByteArray();
|
||||
if (bookmark.isEmpty()) {
|
||||
qDebug() << "Creating bookmark for" << id << "at" << setting->get().toString();
|
||||
setPathWithBookmark(id, setting->get().toString());
|
||||
return setting->get().toString();
|
||||
}
|
||||
bool stale;
|
||||
QUrl url = m_sandboxedFileAccess.securityScopedBookmarkToURL(bookmark, stale);
|
||||
if (url.isValid()) {
|
||||
if (stale) {
|
||||
setting->set(url.path());
|
||||
bookmarkSetting->set(bookmark);
|
||||
}
|
||||
|
||||
m_sandboxedFileAccess.startUsingSecurityScopedBookmark(bookmark, stale);
|
||||
// already did a stale check, no need to do it again
|
||||
|
||||
// convert to relative path to current directory if `url` is a descendant of the current directory
|
||||
QDir currentDir = QDir::current().absolutePath();
|
||||
return url.path().startsWith(currentDir.absolutePath()) ? currentDir.relativeFilePath(url.path()) : url.path();
|
||||
}
|
||||
|
||||
return setting->get().toString();
|
||||
}
|
||||
|
||||
bool SettingsObject::setPathWithBookmark(const QString& id, const QString& path)
|
||||
{
|
||||
auto setting = getSetting(id);
|
||||
if (!setting) {
|
||||
qCritical() << QString("Error changing setting %1. Setting doesn't exist.").arg(id);
|
||||
return false;
|
||||
}
|
||||
|
||||
QDir dir(path);
|
||||
if (!dir.exists()) {
|
||||
qCritical() << QString("Error changing setting %1. Path doesn't exist.").arg(id);
|
||||
return false;
|
||||
}
|
||||
QString absolutePath = dir.absolutePath();
|
||||
QString bookmarkId = id + "Bookmark";
|
||||
std::shared_ptr<Setting> bookmarkSetting = getSetting(bookmarkId);
|
||||
// there is no need to use bookmarks if the default value is used or the directory is within the data directory (already can access)
|
||||
if (path == setting->defValue().toString() || absolutePath.startsWith(QDir::current().absolutePath())) {
|
||||
bookmarkSetting->reset();
|
||||
return true;
|
||||
}
|
||||
QByteArray bytes = m_sandboxedFileAccess.pathToSecurityScopedBookmark(absolutePath);
|
||||
if (bytes.isEmpty()) {
|
||||
qCritical() << QString("Failed to create bookmark for %1 - no access?").arg(id);
|
||||
// TODO: show an alert to the user asking them to reselect the directory
|
||||
return false;
|
||||
}
|
||||
auto oldBookmark = bookmarkSetting->get().toByteArray();
|
||||
m_sandboxedFileAccess.stopUsingSecurityScopedBookmark(oldBookmark);
|
||||
if (!bytes.isEmpty() && bookmarkSetting) {
|
||||
bookmarkSetting->set(bytes);
|
||||
bool stale;
|
||||
m_sandboxedFileAccess.startUsingSecurityScopedBookmark(bytes, stale);
|
||||
// just created the bookmark, it shouldn't be stale
|
||||
}
|
||||
|
||||
setting->set(path);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void SettingsObject::reset(const QString& id) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@
|
|||
#include <QVariant>
|
||||
#include <memory>
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
#include "macsandbox/SecurityBookmarkFileAccess.h"
|
||||
#endif
|
||||
|
||||
class Setting;
|
||||
class SettingsObject;
|
||||
|
||||
|
|
@ -119,7 +123,27 @@ class SettingsObject : public QObject {
|
|||
* \return The setting's value as a QVariant.
|
||||
* If no setting with the given ID exists, returns an invalid QVariant.
|
||||
*/
|
||||
QVariant get(const QString& id) const;
|
||||
QVariant get(const QString& id);
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
/*!
|
||||
* \brief Get the path to the file or directory represented by the bookmark stored in the associated setting.
|
||||
* \param id The setting ID of the relevant directory - this should not include "Bookmark" at the end.
|
||||
* \return A path to the file or directory represented by the bookmark.
|
||||
* If a bookmark is not valid or stored, use default logic (directly return the stored path).
|
||||
* This can attempt to create a bookmark if the path is accessible and the bookmark is not valid.
|
||||
*/
|
||||
QString getPathFromBookmark(const QString& id);
|
||||
/*!
|
||||
* \brief Set a security-scoped bookmark to the provided path for the associated setting.
|
||||
* \param id The setting ID of the relevant directory - this should not include "Bookmark" at the end.
|
||||
* \param path The new desired path.
|
||||
* \return A boolean indicating whether a bookmark was successfully set.
|
||||
* The path needs to be accessible to the launcher before calling this function. For example,
|
||||
* it could come from a user selection in an open panel.
|
||||
*/
|
||||
bool setPathWithBookmark(const QString& id, const QString& path);
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* \brief Sets the value of the setting with the given ID.
|
||||
|
|
@ -207,6 +231,9 @@ class SettingsObject : public QObject {
|
|||
|
||||
private:
|
||||
QMap<QString, std::shared_ptr<Setting>> m_settings;
|
||||
#ifdef Q_OS_MACOS
|
||||
SecurityBookmarkFileAccess m_sandboxedFileAccess;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool m_suspendSave = false;
|
||||
|
|
|
|||
|
|
@ -251,8 +251,8 @@ void readIndex(const QString& path, QMap<QString, Language>& languages)
|
|||
Language lang(iter.key());
|
||||
|
||||
auto langObj = Json::requireObject(iter.value());
|
||||
lang.setTranslationStats(Json::ensureInteger(langObj, "translated", 0), Json::ensureInteger(langObj, "untranslated", 0),
|
||||
Json::ensureInteger(langObj, "fuzzy", 0));
|
||||
lang.setTranslationStats(langObj["translated"].toInt(), langObj["untranslated"].toInt(),
|
||||
langObj["fuzzy"].toInt());
|
||||
lang.file_name = Json::requireString(langObj, "file");
|
||||
lang.file_sha1 = Json::requireString(langObj, "sha1");
|
||||
lang.file_size = Json::requireInteger(langObj, "size");
|
||||
|
|
|
|||
|
|
@ -967,7 +967,7 @@ void MainWindow::processURLs(QList<QUrl> urls)
|
|||
connect(job.get(), &Task::succeeded, this, [this, array, addonId, fileId, &dl_url, &version] {
|
||||
qDebug() << "Returned CFURL Json:\n" << array->toStdString().c_str();
|
||||
auto doc = Json::requireDocument(*array);
|
||||
auto data = Json::ensureObject(Json::ensureObject(doc.object()), "data");
|
||||
auto data = doc.object()["data"].toObject();
|
||||
// No way to find out if it's a mod or a modpack before here
|
||||
// And also we need to check if it ends with .zip, instead of any better way
|
||||
version = FlameMod::loadIndexedPackVersion(data);
|
||||
|
|
|
|||
74
launcher/ui/dialogs/ChooseOfflineNameDialog.cpp
Normal file
74
launcher/ui/dialogs/ChooseOfflineNameDialog.cpp
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2025 Octol1ttle <l1ttleofficial@outlook.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 "ChooseOfflineNameDialog.h"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "ui_ChooseOfflineNameDialog.h"
|
||||
|
||||
ChooseOfflineNameDialog::ChooseOfflineNameDialog(const QString& message, QWidget* parent) : QDialog(parent), ui(new Ui::ChooseOfflineNameDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->label->setText(message);
|
||||
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
|
||||
|
||||
const QRegularExpression usernameRegExp("^[A-Za-z0-9_]{3,16}$");
|
||||
m_usernameValidator = new QRegularExpressionValidator(usernameRegExp, this);
|
||||
ui->usernameTextBox->setValidator(m_usernameValidator);
|
||||
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
}
|
||||
|
||||
ChooseOfflineNameDialog::~ChooseOfflineNameDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
QString ChooseOfflineNameDialog::getUsername() const
|
||||
{
|
||||
return ui->usernameTextBox->text();
|
||||
}
|
||||
|
||||
void ChooseOfflineNameDialog::setUsername(const QString& username) const
|
||||
{
|
||||
ui->usernameTextBox->setText(username);
|
||||
updateAcceptAllowed(username);
|
||||
}
|
||||
|
||||
void ChooseOfflineNameDialog::updateAcceptAllowed(const QString& username) const
|
||||
{
|
||||
const bool allowed = ui->allowInvalidUsernames->isChecked() ? !username.isEmpty() : ui->usernameTextBox->hasAcceptableInput();
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(allowed);
|
||||
}
|
||||
|
||||
void ChooseOfflineNameDialog::on_usernameTextBox_textEdited(const QString& newText) const
|
||||
{
|
||||
updateAcceptAllowed(newText);
|
||||
}
|
||||
|
||||
void ChooseOfflineNameDialog::on_allowInvalidUsernames_checkStateChanged(const Qt::CheckState checkState) const
|
||||
{
|
||||
ui->usernameTextBox->setValidator(checkState == Qt::Checked ? nullptr : m_usernameValidator);
|
||||
updateAcceptAllowed(getUsername());
|
||||
}
|
||||
50
launcher/ui/dialogs/ChooseOfflineNameDialog.h
Normal file
50
launcher/ui/dialogs/ChooseOfflineNameDialog.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2025 Octol1ttle <l1ttleofficial@outlook.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 <QDialog>
|
||||
#include <QRegularExpressionValidator>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui {
|
||||
class ChooseOfflineNameDialog;
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class ChooseOfflineNameDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ChooseOfflineNameDialog(const QString& message, QWidget* parent = nullptr);
|
||||
~ChooseOfflineNameDialog() override;
|
||||
|
||||
QString getUsername() const;
|
||||
void setUsername(const QString& username) const;
|
||||
|
||||
private:
|
||||
void updateAcceptAllowed(const QString& username) const;
|
||||
|
||||
protected slots:
|
||||
void on_usernameTextBox_textEdited(const QString& newText) const;
|
||||
void on_allowInvalidUsernames_checkStateChanged(Qt::CheckState checkState) const;
|
||||
|
||||
private:
|
||||
Ui::ChooseOfflineNameDialog* ui;
|
||||
QRegularExpressionValidator* m_usernameValidator;
|
||||
};
|
||||
58
launcher/ui/dialogs/ChooseOfflineNameDialog.ui
Normal file
58
launcher/ui/dialogs/ChooseOfflineNameDialog.ui
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ChooseOfflineNameDialog</class>
|
||||
<widget class="QDialog" name="ChooseOfflineNameDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>158</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Choose Offline Name</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Message label placeholder.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="usernameTextBox">
|
||||
<property name="placeholderText">
|
||||
<string>Username</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="allowInvalidUsernames">
|
||||
<property name="toolTip">
|
||||
<string>A username is valid only if it is from 3 to 16 characters in length, uses English letters, numbers, and underscores. An invalid username may prevent joining servers and singleplayer worlds.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Allow invalid usernames</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
@ -50,7 +50,7 @@
|
|||
#include <QUrl>
|
||||
#include <QtWidgets/QPushButton>
|
||||
|
||||
#include "qrcodegen.hpp"
|
||||
#include "qrencode.h"
|
||||
|
||||
MSALoginDialog::MSALoginDialog(QWidget* parent) : QDialog(parent), ui(new Ui::MSALoginDialog)
|
||||
{
|
||||
|
|
@ -146,27 +146,32 @@ void MSALoginDialog::authorizeWithBrowser(const QUrl& url)
|
|||
m_url = url;
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/21400254/how-to-draw-a-qr-code-with-qt-in-native-c-c
|
||||
void paintQR(QPainter& painter, const QSize sz, const QString& data, QColor fg)
|
||||
void paintQR(QPainter& painter, const QSize canvasSize, const QString& data, QColor fg)
|
||||
{
|
||||
// NOTE: At this point you will use the API to get the encoding and format you want, instead of my hardcoded stuff:
|
||||
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(data.toUtf8().constData(), qrcodegen::QrCode::Ecc::LOW);
|
||||
const int s = qr.getSize() > 0 ? qr.getSize() : 1;
|
||||
const double w = sz.width();
|
||||
const double h = sz.height();
|
||||
const double aspect = w / h;
|
||||
const double size = ((aspect > 1.0) ? h : w);
|
||||
const double scale = size / (s + 2);
|
||||
// NOTE: For performance reasons my implementation only draws the foreground parts in supplied color.
|
||||
// It expects background to be prepared already (in white or whatever is preferred).
|
||||
const auto* qr = QRcode_encodeString(data.toUtf8().constData(), 0, QRecLevel::QR_ECLEVEL_M, QRencodeMode::QR_MODE_8, 1);
|
||||
if (!qr) {
|
||||
qWarning() << "Unable to encode" << data << "as QR code";
|
||||
return;
|
||||
}
|
||||
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(fg);
|
||||
for (int y = 0; y < s; y++) {
|
||||
for (int x = 0; x < s; x++) {
|
||||
const int color = qr.getModule(x, y); // 0 for white, 1 for black
|
||||
if (0 != color) {
|
||||
const double rx1 = (x + 1) * scale, ry1 = (y + 1) * scale;
|
||||
QRectF r(rx1, ry1, scale, scale);
|
||||
|
||||
// Make sure the QR code fits in the canvas with some padding
|
||||
const auto qrSize = qr->width;
|
||||
const auto canvasWidth = canvasSize.width();
|
||||
const auto canvasHeight = canvasSize.height();
|
||||
const auto scale = 0.8 * std::min(canvasWidth / qrSize, canvasHeight / qrSize);
|
||||
|
||||
// Find an offset to center it in the canvas
|
||||
const auto offsetX = (canvasWidth - qrSize * scale) / 2;
|
||||
const auto offsetY = (canvasHeight - qrSize * scale) / 2;
|
||||
|
||||
for (int y = 0; y < qrSize; y++) {
|
||||
for (int x = 0; x < qrSize; x++) {
|
||||
auto shouldFillIn = qr->data[y * qrSize + x] & 1;
|
||||
if (shouldFillIn) {
|
||||
QRectF r(offsetX + x * scale, offsetY + y * scale, scale, scale);
|
||||
painter.drawRects(&r, 1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,105 +0,0 @@
|
|||
#include "OfflineLoginDialog.h"
|
||||
#include "ui_OfflineLoginDialog.h"
|
||||
|
||||
#include <QtWidgets/QPushButton>
|
||||
|
||||
OfflineLoginDialog::OfflineLoginDialog(QWidget* parent) : QDialog(parent), ui(new Ui::OfflineLoginDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->progressBar->setVisible(false);
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
|
||||
ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
|
||||
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
}
|
||||
|
||||
OfflineLoginDialog::~OfflineLoginDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
// Stage 1: User interaction
|
||||
void OfflineLoginDialog::accept()
|
||||
{
|
||||
setUserInputsEnabled(false);
|
||||
ui->progressBar->setVisible(true);
|
||||
|
||||
// Setup the login task and start it
|
||||
m_account = MinecraftAccount::createOffline(ui->userTextBox->text());
|
||||
m_loginTask = m_account->login();
|
||||
connect(m_loginTask.get(), &Task::failed, this, &OfflineLoginDialog::onTaskFailed);
|
||||
connect(m_loginTask.get(), &Task::succeeded, this, &OfflineLoginDialog::onTaskSucceeded);
|
||||
connect(m_loginTask.get(), &Task::status, this, &OfflineLoginDialog::onTaskStatus);
|
||||
connect(m_loginTask.get(), &Task::progress, this, &OfflineLoginDialog::onTaskProgress);
|
||||
m_loginTask->start();
|
||||
}
|
||||
|
||||
void OfflineLoginDialog::setUserInputsEnabled(bool enable)
|
||||
{
|
||||
ui->userTextBox->setEnabled(enable);
|
||||
ui->buttonBox->setEnabled(enable);
|
||||
}
|
||||
|
||||
void OfflineLoginDialog::on_allowLongUsernames_stateChanged(int value)
|
||||
{
|
||||
if (value == Qt::Checked) {
|
||||
ui->userTextBox->setMaxLength(INT_MAX);
|
||||
} else {
|
||||
ui->userTextBox->setMaxLength(16);
|
||||
}
|
||||
}
|
||||
|
||||
// Enable the OK button only when the textbox contains something.
|
||||
void OfflineLoginDialog::on_userTextBox_textEdited(const QString& newText)
|
||||
{
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!newText.isEmpty());
|
||||
}
|
||||
|
||||
void OfflineLoginDialog::onTaskFailed(const QString& reason)
|
||||
{
|
||||
// Set message
|
||||
auto lines = reason.split('\n');
|
||||
QString processed;
|
||||
for (auto line : lines) {
|
||||
if (line.size()) {
|
||||
processed += "<font color='red'>" + line + "</font><br />";
|
||||
} else {
|
||||
processed += "<br />";
|
||||
}
|
||||
}
|
||||
ui->label->setText(processed);
|
||||
|
||||
// Re-enable user-interaction
|
||||
setUserInputsEnabled(true);
|
||||
ui->progressBar->setVisible(false);
|
||||
}
|
||||
|
||||
void OfflineLoginDialog::onTaskSucceeded()
|
||||
{
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void OfflineLoginDialog::onTaskStatus(const QString& status)
|
||||
{
|
||||
ui->label->setText(status);
|
||||
}
|
||||
|
||||
void OfflineLoginDialog::onTaskProgress(qint64 current, qint64 total)
|
||||
{
|
||||
ui->progressBar->setMaximum(total);
|
||||
ui->progressBar->setValue(current);
|
||||
}
|
||||
|
||||
// Public interface
|
||||
MinecraftAccountPtr OfflineLoginDialog::newAccount(QWidget* parent, QString msg)
|
||||
{
|
||||
OfflineLoginDialog dlg(parent);
|
||||
dlg.ui->label->setText(msg);
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
return dlg.m_account;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
#include "minecraft/auth/MinecraftAccount.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
namespace Ui {
|
||||
class OfflineLoginDialog;
|
||||
}
|
||||
|
||||
class OfflineLoginDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~OfflineLoginDialog();
|
||||
|
||||
static MinecraftAccountPtr newAccount(QWidget* parent, QString message);
|
||||
|
||||
private:
|
||||
explicit OfflineLoginDialog(QWidget* parent = 0);
|
||||
|
||||
void setUserInputsEnabled(bool enable);
|
||||
|
||||
protected slots:
|
||||
void accept();
|
||||
|
||||
void onTaskFailed(const QString& reason);
|
||||
void onTaskSucceeded();
|
||||
void onTaskStatus(const QString& status);
|
||||
void onTaskProgress(qint64 current, qint64 total);
|
||||
|
||||
void on_userTextBox_textEdited(const QString& newText);
|
||||
void on_allowLongUsernames_stateChanged(int value);
|
||||
|
||||
private:
|
||||
Ui::OfflineLoginDialog* ui;
|
||||
MinecraftAccountPtr m_account;
|
||||
Task::Ptr m_loginTask;
|
||||
};
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OfflineLoginDialog</class>
|
||||
<widget class="QDialog" name="OfflineLoginDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>150</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Add Account</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string notr="true">Message label placeholder.</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="userTextBox">
|
||||
<property name="maxLength">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Username</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="allowLongUsernames">
|
||||
<property name="toolTip">
|
||||
<string>Usernames longer than 16 characters cannot be used for LAN games or offline-mode servers.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Allow long usernames</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progressBar">
|
||||
<property name="value">
|
||||
<number>69</number>
|
||||
</property>
|
||||
<property name="textVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
@ -461,13 +461,12 @@ void ResourceUpdateDialog::appendResource(CheckUpdateTask::Update const& info, Q
|
|||
auto requiredByItem = new QTreeWidgetItem(item_top);
|
||||
if (requiredBy.length() == 1) {
|
||||
requiredByItem->setText(0, tr("Required by: %1").arg(requiredBy.back()));
|
||||
requiredByItem->setData(0, Qt::UserRole, requiredBy.back());
|
||||
} else {
|
||||
requiredByItem->setText(0, tr("Required by:"));
|
||||
auto i = 0;
|
||||
for (auto req : requiredBy) {
|
||||
auto reqItem = new QTreeWidgetItem(requiredByItem);
|
||||
reqItem->setText(0, req);
|
||||
reqItem->insertChildren(i++, { reqItem });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,18 +68,14 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info)
|
|||
filenameItem->setText(0, tr("Filename: %1").arg(info.filename));
|
||||
filenameItem->setData(0, Qt::UserRole, info.filename);
|
||||
|
||||
auto childIndx = 0;
|
||||
itemTop->insertChildren(childIndx++, { filenameItem });
|
||||
|
||||
if (!info.custom_file_path.isEmpty()) {
|
||||
auto customPathItem = new QTreeWidgetItem(itemTop);
|
||||
customPathItem->setText(0, tr("This download will be placed in: %1").arg(info.custom_file_path));
|
||||
|
||||
itemTop->insertChildren(1, { customPathItem });
|
||||
customPathItem->setData(0, Qt::UserRole, info.custom_file_path);
|
||||
|
||||
itemTop->setIcon(1, QIcon(QIcon::fromTheme("status-yellow")));
|
||||
itemTop->setToolTip(
|
||||
childIndx++,
|
||||
1,
|
||||
tr("This file will be downloaded to a folder location different from the default, possibly due to its loader requiring it."));
|
||||
}
|
||||
|
||||
|
|
@ -87,23 +83,19 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info)
|
|||
providerItem->setText(0, tr("Provider: %1").arg(info.provider));
|
||||
providerItem->setData(0, Qt::UserRole, 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()));
|
||||
requiredByItem->setData(0, Qt::UserRole, 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->toggleDepsButton->show();
|
||||
m_deps << itemTop;
|
||||
}
|
||||
|
|
@ -112,8 +104,6 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info)
|
|||
versionTypeItem->setText(0, tr("Version Type: %1").arg(info.version_type));
|
||||
versionTypeItem->setData(0, Qt::UserRole, info.version_type);
|
||||
|
||||
itemTop->insertChildren(childIndx++, { versionTypeItem });
|
||||
|
||||
ui->modTreeWidget->addTopLevelItem(itemTop);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -481,7 +481,7 @@ void SkinManageDialog::on_userBtn_clicked()
|
|||
return;
|
||||
}
|
||||
const auto root = doc.object();
|
||||
auto id = Json::ensureString(root, "id");
|
||||
auto id = root["id"].toString();
|
||||
if (!id.isEmpty()) {
|
||||
getProfile->setUrl("https://sessionserver.mojang.com/session/minecraft/profile/" + id);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -202,6 +202,13 @@ void SkinOpenGLWindow::resizeGL(int w, int h)
|
|||
|
||||
void SkinOpenGLWindow::paintGL()
|
||||
{
|
||||
// Adjust the viewport to account for fractional scaling
|
||||
qreal dpr = devicePixelRatio();
|
||||
if (dpr != 1.f) {
|
||||
QSize scaledSize = size() * dpr;
|
||||
glViewport(0, 0, scaledSize.width(), scaledSize.height());
|
||||
}
|
||||
|
||||
// Clear color and depth buffer
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
|
|
@ -235,6 +242,13 @@ void SkinOpenGLWindow::paintGL()
|
|||
|
||||
m_scene->draw(m_modelProgram);
|
||||
m_modelProgram->release();
|
||||
|
||||
// Redraw the first frame; this is necessary because the pixel ratio for Wayland fractional scaling is not negotiated properly on the
|
||||
// first frame
|
||||
if (m_isFirstFrame) {
|
||||
m_isFirstFrame = false;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void SkinOpenGLWindow::updateScene(SkinModel* skin)
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ class SkinOpenGLWindow : public QOpenGLWindow, protected QOpenGLFunctions {
|
|||
float m_yaw = 90; // Horizontal rotation angle
|
||||
float m_pitch = 0; // Vertical rotation angle
|
||||
|
||||
bool m_isFirstFrame = true;
|
||||
|
||||
opengl::BoxGeometry* m_background = nullptr;
|
||||
QOpenGLTexture* m_backgroundTexture = nullptr;
|
||||
QColor m_baseColor;
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@
|
|||
#include <QDebug>
|
||||
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui/dialogs/ChooseOfflineNameDialog.h"
|
||||
#include "ui/dialogs/MSALoginDialog.h"
|
||||
#include "ui/dialogs/OfflineLoginDialog.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
|
|
@ -149,10 +149,13 @@ void AccountListPage::on_actionAddOffline_triggered()
|
|||
return;
|
||||
}
|
||||
|
||||
MinecraftAccountPtr account =
|
||||
OfflineLoginDialog::newAccount(this, tr("Please enter your desired username to add your offline account."));
|
||||
ChooseOfflineNameDialog dialog(tr("Please enter your desired username to add your offline account."), this);
|
||||
if (dialog.exec() != QDialog::Accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (account) {
|
||||
if (const MinecraftAccountPtr account = MinecraftAccount::createOffline(dialog.getUsername())) {
|
||||
account->login()->start(); // The task will complete here.
|
||||
m_accounts->addAccount(account);
|
||||
if (m_accounts->count() == 1) {
|
||||
m_accounts->setDefaultAccount(account);
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@
|
|||
#include <QDesktopServices>
|
||||
#include <QUrl>
|
||||
#include <QUrlQuery>
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "ui_ManagedPackPage.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QListView>
|
||||
#include <QProxyStyle>
|
||||
#include <QStyleFactory>
|
||||
#include <memory>
|
||||
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
|
|
@ -284,7 +286,8 @@ void ModrinthManagedPackPage::parseManagedPack()
|
|||
};
|
||||
callbacks.on_fail = [this](QString reason, int) { setFailState(); };
|
||||
callbacks.on_abort = [this]() { setFailState(); };
|
||||
m_fetch_job = m_api.getProjectVersions({ m_pack, {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
|
||||
m_fetch_job = m_api.getProjectVersions(
|
||||
{ std::make_shared<ModPlatform::IndexedPack>(m_pack), {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
|
||||
|
||||
ui->changelogTextBrowser->setText(tr("Fetching changelogs..."));
|
||||
|
||||
|
|
@ -455,7 +458,8 @@ void FlameManagedPackPage::parseManagedPack()
|
|||
};
|
||||
callbacks.on_fail = [this](QString reason, int) { setFailState(); };
|
||||
callbacks.on_abort = [this]() { setFailState(); };
|
||||
m_fetch_job = m_api.getProjectVersions({ m_pack, {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
|
||||
m_fetch_job = m_api.getProjectVersions(
|
||||
{ std::make_shared<ModPlatform::IndexedPack>(m_pack), {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
|
||||
|
||||
m_fetch_job->start();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -287,16 +287,10 @@ void OtherLogsPage::reload()
|
|||
if (!m_instance) {
|
||||
level = MessageLevel::fromLauncherLine(lineTemp);
|
||||
} else {
|
||||
// if the launcher part set a log level, use it
|
||||
auto innerLevel = MessageLevel::fromLine(lineTemp);
|
||||
if (innerLevel != MessageLevel::Unknown) {
|
||||
level = innerLevel;
|
||||
}
|
||||
level = LogParser::guessLevel(line);
|
||||
|
||||
// If the level is still undetermined, guess level
|
||||
if (level == MessageLevel::StdErr || level == MessageLevel::StdOut || level == MessageLevel::Unknown) {
|
||||
level = LogParser::guessLevel(line, last);
|
||||
}
|
||||
if (level == MessageLevel::Unknown)
|
||||
level = last;
|
||||
}
|
||||
|
||||
last = level;
|
||||
|
|
|
|||
|
|
@ -23,14 +23,14 @@ ResourceAPI::SearchArgs DataPackResourceModel::createSearchArguments()
|
|||
|
||||
ResourceAPI::VersionSearchArgs DataPackResourceModel::createVersionsArguments(const QModelIndex& entry)
|
||||
{
|
||||
auto& pack = m_packs[entry.row()];
|
||||
return { *pack, {}, ModPlatform::ModLoaderType::DataPack };
|
||||
auto pack = m_packs[entry.row()];
|
||||
return { pack, {}, ModPlatform::ModLoaderType::DataPack };
|
||||
}
|
||||
|
||||
ResourceAPI::ProjectInfoArgs DataPackResourceModel::createInfoArguments(const QModelIndex& entry)
|
||||
{
|
||||
auto& pack = m_packs[entry.row()];
|
||||
return { *pack };
|
||||
auto pack = m_packs[entry.row()];
|
||||
return { pack };
|
||||
}
|
||||
|
||||
void DataPackResourceModel::searchWithTerm(const QString& term, unsigned int sort)
|
||||
|
|
|
|||
|
|
@ -141,13 +141,13 @@ void ImportPage::updateState()
|
|||
connect(job.get(), &NetJob::succeeded, this, [this, array, addonId, fileId] {
|
||||
qDebug() << "Returned CFURL Json:\n" << array->toStdString().c_str();
|
||||
auto doc = Json::requireDocument(*array);
|
||||
auto data = Json::ensureObject(Json::ensureObject(doc.object()), "data");
|
||||
auto data = doc.object()["data"].toObject();
|
||||
// No way to find out if it's a mod or a modpack before here
|
||||
// And also we need to check if it ends with .zip, instead of any better way
|
||||
auto fileName = Json::ensureString(data, "fileName");
|
||||
auto fileName = data["fileName"].toString();
|
||||
if (fileName.endsWith(".zip")) {
|
||||
// Have to use ensureString then use QUrl to get proper url encoding
|
||||
auto dl_url = QUrl(Json::ensureString(data, "downloadUrl", "", "downloadUrl"));
|
||||
auto dl_url = QUrl(data["downloadUrl"].toString(""));
|
||||
if (!dl_url.isValid()) {
|
||||
CustomMessageBox::selectable(
|
||||
this, tr("Error"),
|
||||
|
|
@ -158,7 +158,7 @@ void ImportPage::updateState()
|
|||
}
|
||||
|
||||
QFileInfo dl_file(dl_url.fileName());
|
||||
QString pack_name = Json::ensureString(data, "displayName", dl_file.completeBaseName(), "displayName");
|
||||
QString pack_name = data["displayName"].toString(dl_file.completeBaseName());
|
||||
|
||||
QMap<QString, QString> extra_info;
|
||||
extra_info.insert("pack_id", addonId);
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments()
|
|||
|
||||
ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(const QModelIndex& entry)
|
||||
{
|
||||
auto& pack = *m_packs[entry.row()];
|
||||
auto pack = m_packs[entry.row()];
|
||||
auto profile = static_cast<MinecraftInstance const&>(m_base_instance).getPackProfile();
|
||||
|
||||
Q_ASSERT(profile);
|
||||
|
|
@ -67,7 +67,7 @@ ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(const QModelInd
|
|||
|
||||
ResourceAPI::ProjectInfoArgs ModModel::createInfoArguments(const QModelIndex& entry)
|
||||
{
|
||||
auto& pack = *m_packs[entry.row()];
|
||||
auto pack = m_packs[entry.row()];
|
||||
return { pack };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ void ResourceModel::search()
|
|||
if (m_search_term.startsWith("#")) {
|
||||
auto projectId = m_search_term.mid(1);
|
||||
if (!projectId.isEmpty()) {
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack> callbacks;
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack::Ptr> callbacks;
|
||||
|
||||
callbacks.on_fail = [this](QString reason, int) {
|
||||
if (!s_running_models.constFind(this).value())
|
||||
|
|
@ -159,7 +159,9 @@ void ResourceModel::search()
|
|||
return;
|
||||
searchRequestForOneSucceeded(pack);
|
||||
};
|
||||
if (auto job = m_api->getProjectInfo({ projectId }, std::move(callbacks)); job)
|
||||
auto project = std::make_shared<ModPlatform::IndexedPack>();
|
||||
project->addonId = projectId;
|
||||
if (auto job = m_api->getProjectInfo({ project }, std::move(callbacks)); job)
|
||||
runSearchJob(job);
|
||||
return;
|
||||
}
|
||||
|
|
@ -219,7 +221,7 @@ void ResourceModel::loadEntry(const QModelIndex& entry)
|
|||
|
||||
if (!pack->extraDataLoaded) {
|
||||
auto args{ createInfoArguments(entry) };
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack> callbacks{};
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack::Ptr> callbacks{};
|
||||
|
||||
callbacks.on_succeed = [this, entry](auto& newpack) {
|
||||
if (!s_running_models.constFind(this).value())
|
||||
|
|
@ -388,12 +390,12 @@ void ResourceModel::searchRequestSucceeded(QList<ModPlatform::IndexedPack::Ptr>&
|
|||
endInsertRows();
|
||||
}
|
||||
|
||||
void ResourceModel::searchRequestForOneSucceeded(ModPlatform::IndexedPack& pack)
|
||||
void ResourceModel::searchRequestForOneSucceeded(ModPlatform::IndexedPack::Ptr pack)
|
||||
{
|
||||
m_search_state = SearchState::Finished;
|
||||
|
||||
beginInsertRows(QModelIndex(), m_packs.size(), m_packs.size() + 1);
|
||||
m_packs.append(std::make_shared<ModPlatform::IndexedPack>(pack));
|
||||
m_packs.append(pack);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
|
|
@ -448,18 +450,17 @@ void ResourceModel::versionRequestSucceeded(QVector<ModPlatform::IndexedVersion>
|
|||
emit versionListUpdated(index);
|
||||
}
|
||||
|
||||
void ResourceModel::infoRequestSucceeded(ModPlatform::IndexedPack& pack, const QModelIndex& index)
|
||||
void ResourceModel::infoRequestSucceeded(ModPlatform::IndexedPack::Ptr pack, const QModelIndex& index)
|
||||
{
|
||||
auto current_pack = data(index, Qt::UserRole).value<ModPlatform::IndexedPack::Ptr>();
|
||||
|
||||
// Check if the index is still valid for this resource or not
|
||||
if (pack.addonId != current_pack->addonId)
|
||||
if (pack->addonId != current_pack->addonId)
|
||||
return;
|
||||
|
||||
*current_pack = pack;
|
||||
// Cache info :^)
|
||||
QVariant new_pack;
|
||||
new_pack.setValue(current_pack);
|
||||
new_pack.setValue(pack);
|
||||
if (!setData(index, new_pack, Qt::UserRole)) {
|
||||
qWarning() << "Failed to cache resource info!";
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -138,13 +138,13 @@ class ResourceModel : public QAbstractListModel {
|
|||
private:
|
||||
/* Default search request callbacks */
|
||||
void searchRequestSucceeded(QList<ModPlatform::IndexedPack::Ptr>&);
|
||||
void searchRequestForOneSucceeded(ModPlatform::IndexedPack&);
|
||||
void searchRequestForOneSucceeded(ModPlatform::IndexedPack::Ptr);
|
||||
void searchRequestFailed(QString reason, int network_error_code);
|
||||
void searchRequestAborted();
|
||||
|
||||
void versionRequestSucceeded(QVector<ModPlatform::IndexedVersion>&, QVariant, const QModelIndex&);
|
||||
|
||||
void infoRequestSucceeded(ModPlatform::IndexedPack&, const QModelIndex&);
|
||||
void infoRequestSucceeded(ModPlatform::IndexedPack::Ptr, const QModelIndex&);
|
||||
|
||||
signals:
|
||||
void versionListUpdated(const QModelIndex& index);
|
||||
|
|
|
|||
|
|
@ -25,14 +25,14 @@ ResourceAPI::SearchArgs ResourcePackResourceModel::createSearchArguments()
|
|||
|
||||
ResourceAPI::VersionSearchArgs ResourcePackResourceModel::createVersionsArguments(const QModelIndex& entry)
|
||||
{
|
||||
auto& pack = m_packs[entry.row()];
|
||||
return { *pack, {}, {}, ModPlatform::ResourceType::ResourcePack };
|
||||
auto pack = m_packs[entry.row()];
|
||||
return { pack, {}, {}, ModPlatform::ResourceType::ResourcePack };
|
||||
}
|
||||
|
||||
ResourceAPI::ProjectInfoArgs ResourcePackResourceModel::createInfoArguments(const QModelIndex& entry)
|
||||
{
|
||||
auto& pack = m_packs[entry.row()];
|
||||
return { *pack };
|
||||
auto pack = m_packs[entry.row()];
|
||||
return { pack };
|
||||
}
|
||||
|
||||
void ResourcePackResourceModel::searchWithTerm(const QString& term, unsigned int sort)
|
||||
|
|
|
|||
|
|
@ -22,14 +22,14 @@ ResourceAPI::SearchArgs ShaderPackResourceModel::createSearchArguments()
|
|||
|
||||
ResourceAPI::VersionSearchArgs ShaderPackResourceModel::createVersionsArguments(const QModelIndex& entry)
|
||||
{
|
||||
auto& pack = m_packs[entry.row()];
|
||||
return { *pack, {}, {}, ModPlatform::ResourceType::ShaderPack };
|
||||
auto pack = m_packs[entry.row()];
|
||||
return { pack, {}, {}, ModPlatform::ResourceType::ShaderPack };
|
||||
}
|
||||
|
||||
ResourceAPI::ProjectInfoArgs ShaderPackResourceModel::createInfoArguments(const QModelIndex& entry)
|
||||
{
|
||||
auto& pack = m_packs[entry.row()];
|
||||
return { *pack };
|
||||
auto pack = m_packs[entry.row()];
|
||||
return { pack };
|
||||
}
|
||||
|
||||
void ShaderPackResourceModel::searchWithTerm(const QString& term, unsigned int sort)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <Version.h>
|
||||
|
||||
#include <QtMath>
|
||||
#include <memory>
|
||||
|
||||
namespace Flame {
|
||||
|
||||
|
|
@ -167,7 +168,7 @@ void ListModel::performPaginatedSearch()
|
|||
if (m_currentSearchTerm.startsWith("#")) {
|
||||
auto projectId = m_currentSearchTerm.mid(1);
|
||||
if (!projectId.isEmpty()) {
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack> callbacks;
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack::Ptr> callbacks;
|
||||
|
||||
callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); };
|
||||
callbacks.on_succeed = [this](auto& pack) { searchRequestForOneSucceeded(pack); };
|
||||
|
|
@ -175,7 +176,9 @@ void ListModel::performPaginatedSearch()
|
|||
qCritical() << "Search task aborted by an unknown reason!";
|
||||
searchRequestFailed("Aborted");
|
||||
};
|
||||
if (auto job = api.getProjectInfo({ { projectId } }, std::move(callbacks)); job) {
|
||||
auto project = std::make_shared<ModPlatform::IndexedPack>();
|
||||
project->addonId = projectId;
|
||||
if (auto job = api.getProjectInfo({ project }, std::move(callbacks)); job) {
|
||||
m_jobPtr = job;
|
||||
m_jobPtr->start();
|
||||
}
|
||||
|
|
@ -189,6 +192,10 @@ void ListModel::performPaginatedSearch()
|
|||
|
||||
callbacks.on_succeed = [this](auto& doc) { searchRequestFinished(doc); };
|
||||
callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); };
|
||||
callbacks.on_abort = [this] {
|
||||
qCritical() << "Search task aborted by an unknown reason!";
|
||||
searchRequestFailed("Aborted");
|
||||
};
|
||||
|
||||
auto netJob = api.searchProjects({ ModPlatform::ResourceType::Modpack, m_nextSearchOffset, m_currentSearchTerm, sort, m_filter->loaders,
|
||||
m_filter->versions, ModPlatform::Side::NoSide, m_filter->categoryIds, m_filter->openSource },
|
||||
|
|
@ -241,12 +248,12 @@ void Flame::ListModel::searchRequestFinished(QList<ModPlatform::IndexedPack::Ptr
|
|||
endInsertRows();
|
||||
}
|
||||
|
||||
void Flame::ListModel::searchRequestForOneSucceeded(ModPlatform::IndexedPack& pack)
|
||||
void Flame::ListModel::searchRequestForOneSucceeded(ModPlatform::IndexedPack::Ptr pack)
|
||||
{
|
||||
m_jobPtr.reset();
|
||||
|
||||
beginInsertRows(QModelIndex(), m_modpacks.size(), m_modpacks.size() + 1);
|
||||
m_modpacks.append(std::make_shared<ModPlatform::IndexedPack>(pack));
|
||||
m_modpacks.append(pack);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class ListModel : public QAbstractListModel {
|
|||
|
||||
void searchRequestFinished(QList<ModPlatform::IndexedPack::Ptr>&);
|
||||
void searchRequestFailed(QString reason);
|
||||
void searchRequestForOneSucceeded(ModPlatform::IndexedPack&);
|
||||
void searchRequestForOneSucceeded(ModPlatform::IndexedPack::Ptr);
|
||||
|
||||
private:
|
||||
void requestLogo(QString file, QString url);
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelInde
|
|||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec();
|
||||
};
|
||||
|
||||
auto netJob = api.getProjectVersions({ *m_current, {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
|
||||
auto netJob = api.getProjectVersions({ m_current, {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
|
||||
|
||||
m_job = netJob;
|
||||
netJob->start();
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@
|
|||
#include "net/ApiDownload.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <memory>
|
||||
|
||||
namespace Modrinth {
|
||||
|
||||
|
|
@ -137,7 +138,7 @@ void ModpackListModel::performPaginatedSearch()
|
|||
if (m_currentSearchTerm.startsWith("#")) {
|
||||
auto projectId = m_currentSearchTerm.mid(1);
|
||||
if (!projectId.isEmpty()) {
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack> callbacks;
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack::Ptr> callbacks;
|
||||
|
||||
callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); };
|
||||
callbacks.on_succeed = [this](auto& pack) { searchRequestForOneSucceeded(pack); };
|
||||
|
|
@ -145,7 +146,9 @@ void ModpackListModel::performPaginatedSearch()
|
|||
qCritical() << "Search task aborted by an unknown reason!";
|
||||
searchRequestFailed("Aborted");
|
||||
};
|
||||
if (auto job = api.getProjectInfo({ projectId }, std::move(callbacks)); job) {
|
||||
auto project = std::make_shared<ModPlatform::IndexedPack>();
|
||||
project->addonId = projectId;
|
||||
if (auto job = api.getProjectInfo({ project }, std::move(callbacks)); job) {
|
||||
m_jobPtr = job;
|
||||
m_jobPtr->start();
|
||||
}
|
||||
|
|
@ -159,6 +162,10 @@ void ModpackListModel::performPaginatedSearch()
|
|||
|
||||
callbacks.on_succeed = [this](auto& doc) { searchRequestFinished(doc); };
|
||||
callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); };
|
||||
callbacks.on_abort = [this] {
|
||||
qCritical() << "Search task aborted by an unknown reason!";
|
||||
searchRequestFailed("Aborted");
|
||||
};
|
||||
|
||||
auto netJob = api.searchProjects({ ModPlatform::ResourceType::Modpack, m_nextSearchOffset, m_currentSearchTerm, sort, m_filter->loaders,
|
||||
m_filter->versions, ModPlatform::Side::NoSide, m_filter->categoryIds, m_filter->openSource },
|
||||
|
|
@ -300,12 +307,12 @@ void ModpackListModel::searchRequestFinished(QList<ModPlatform::IndexedPack::Ptr
|
|||
endInsertRows();
|
||||
}
|
||||
|
||||
void ModpackListModel::searchRequestForOneSucceeded(ModPlatform::IndexedPack& pack)
|
||||
void ModpackListModel::searchRequestForOneSucceeded(ModPlatform::IndexedPack::Ptr pack)
|
||||
{
|
||||
m_jobPtr.reset();
|
||||
|
||||
beginInsertRows(QModelIndex(), m_modpacks.size(), m_modpacks.size() + 1);
|
||||
m_modpacks.append(std::make_shared<ModPlatform::IndexedPack>(pack));
|
||||
m_modpacks.append(pack);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ class ModpackListModel : public QAbstractListModel {
|
|||
public slots:
|
||||
void searchRequestFinished(QList<ModPlatform::IndexedPack::Ptr>& doc_all);
|
||||
void searchRequestFailed(QString reason);
|
||||
void searchRequestForOneSucceeded(ModPlatform::IndexedPack&);
|
||||
void searchRequestForOneSucceeded(ModPlatform::IndexedPack::Ptr);
|
||||
|
||||
protected slots:
|
||||
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
|
|||
|
||||
if (!m_current->extraDataLoaded) {
|
||||
qDebug() << "Loading modrinth modpack information";
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack> callbacks;
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack::Ptr> callbacks;
|
||||
|
||||
auto id = m_current->addonId;
|
||||
callbacks.on_fail = [this](QString reason, int) {
|
||||
|
|
@ -157,10 +157,8 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
|
|||
return; // wrong request?
|
||||
}
|
||||
|
||||
*m_current = pack;
|
||||
|
||||
QVariant current_updated;
|
||||
current_updated.setValue(m_current);
|
||||
current_updated.setValue(pack);
|
||||
|
||||
if (!m_model->setData(curr, current_updated, Qt::UserRole))
|
||||
qWarning() << "Failed to cache extra info for the current pack!";
|
||||
|
|
@ -168,7 +166,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
|
|||
suggestCurrent();
|
||||
updateUI();
|
||||
};
|
||||
if (auto netJob = m_api.getProjectInfo({ { m_current->addonId } }, std::move(callbacks)); netJob) {
|
||||
if (auto netJob = m_api.getProjectInfo({ m_current }, std::move(callbacks)); netJob) {
|
||||
m_job = netJob;
|
||||
m_job->start();
|
||||
}
|
||||
|
|
@ -220,7 +218,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
|
|||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec();
|
||||
};
|
||||
|
||||
auto netJob = m_api.getProjectVersions({ *m_current, {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
|
||||
auto netJob = m_api.getProjectVersions({ m_current, {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
|
||||
|
||||
m_job2 = netJob;
|
||||
m_job2->start();
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue