Merge remote-tracking branch 'upstream/release-9.x' into develop
This commit is contained in:
parent
8940008b41
commit
48689d1b79
116
.github/workflows/build.yml
vendored
116
.github/workflows/build.yml
vendored
@ -39,9 +39,6 @@ on:
|
|||||||
APPLE_NOTARIZE_PASSWORD:
|
APPLE_NOTARIZE_PASSWORD:
|
||||||
description: Password used for notarizing macOS builds
|
description: Password used for notarizing macOS builds
|
||||||
required: false
|
required: false
|
||||||
CACHIX_AUTH_TOKEN:
|
|
||||||
description: Private token for authenticating against Cachix cache
|
|
||||||
required: false
|
|
||||||
GPG_PRIVATE_KEY:
|
GPG_PRIVATE_KEY:
|
||||||
description: Private key for AppImage signing
|
description: Private key for AppImage signing
|
||||||
required: false
|
required: false
|
||||||
@ -68,6 +65,9 @@ jobs:
|
|||||||
qt_arch: ""
|
qt_arch: ""
|
||||||
qt_version: "6.5.3"
|
qt_version: "6.5.3"
|
||||||
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||||
|
linuxdeploy_hash: "4648f278ab3ef31f819e67c30d50f462640e5365a77637d7e6f2ad9fd0b4522a linuxdeploy-x86_64.AppImage"
|
||||||
|
linuxdeploy_qt_hash: "15106be885c1c48a021198e7e1e9a48ce9d02a86dd0a1848f00bdbf3c1c92724 linuxdeploy-plugin-qt-x86_64.AppImage"
|
||||||
|
appimageupdate_hash: "f1747cf60058e99f1bb9099ee9787d16c10241313b7acec81810ea1b1e568c11 AppImageUpdate-x86_64.AppImage"
|
||||||
|
|
||||||
- os: windows-2022
|
- os: windows-2022
|
||||||
name: "Windows-MinGW-w64"
|
name: "Windows-MinGW-w64"
|
||||||
@ -80,9 +80,9 @@ jobs:
|
|||||||
architecture: "x64"
|
architecture: "x64"
|
||||||
vcvars_arch: "amd64"
|
vcvars_arch: "amd64"
|
||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: windows
|
qt_host: "windows"
|
||||||
qt_arch: ""
|
qt_arch: "win64_msvc2022_64"
|
||||||
qt_version: "6.7.3"
|
qt_version: "6.8.1"
|
||||||
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||||
nscurl_tag: "v24.9.26.122"
|
nscurl_tag: "v24.9.26.122"
|
||||||
nscurl_sha256: "AEE6C4BE3CB6455858E9C1EE4B3AFE0DB9960FA03FE99CCDEDC28390D57CCBB0"
|
nscurl_sha256: "AEE6C4BE3CB6455858E9C1EE4B3AFE0DB9960FA03FE99CCDEDC28390D57CCBB0"
|
||||||
@ -93,9 +93,9 @@ jobs:
|
|||||||
architecture: "arm64"
|
architecture: "arm64"
|
||||||
vcvars_arch: "amd64_arm64"
|
vcvars_arch: "amd64_arm64"
|
||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: windows
|
qt_host: "windows"
|
||||||
qt_arch: "win64_msvc2019_arm64"
|
qt_arch: "win64_msvc2022_arm64_cross_compiled"
|
||||||
qt_version: "6.7.3"
|
qt_version: "6.8.1"
|
||||||
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||||
nscurl_tag: "v24.9.26.122"
|
nscurl_tag: "v24.9.26.122"
|
||||||
nscurl_sha256: "AEE6C4BE3CB6455858E9C1EE4B3AFE0DB9960FA03FE99CCDEDC28390D57CCBB0"
|
nscurl_sha256: "AEE6C4BE3CB6455858E9C1EE4B3AFE0DB9960FA03FE99CCDEDC28390D57CCBB0"
|
||||||
@ -106,7 +106,7 @@ jobs:
|
|||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: mac
|
qt_host: mac
|
||||||
qt_arch: ""
|
qt_arch: ""
|
||||||
qt_version: "6.7.3"
|
qt_version: "6.8.1"
|
||||||
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||||
|
|
||||||
- os: macos-14
|
- os: macos-14
|
||||||
@ -216,14 +216,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Install host Qt (Windows MSVC arm64)
|
- name: Install host Qt (Windows MSVC arm64)
|
||||||
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v4
|
||||||
with:
|
with:
|
||||||
aqtversion: "==3.1.*"
|
aqtversion: "==3.1.*"
|
||||||
py7zrversion: ">=0.20.2"
|
py7zrversion: ">=0.20.2"
|
||||||
version: ${{ matrix.qt_version }}
|
version: ${{ matrix.qt_version }}
|
||||||
host: "windows"
|
host: "windows"
|
||||||
target: "desktop"
|
target: "desktop"
|
||||||
arch: ""
|
arch: ${{ matrix.qt_arch }}
|
||||||
modules: ${{ matrix.qt_modules }}
|
modules: ${{ matrix.qt_modules }}
|
||||||
cache: ${{ inputs.is_qt_cached }}
|
cache: ${{ inputs.is_qt_cached }}
|
||||||
cache-key-prefix: host-qt-arm64-windows
|
cache-key-prefix: host-qt-arm64-windows
|
||||||
@ -232,7 +232,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Install Qt (macOS, Linux & Windows MSVC)
|
- name: Install Qt (macOS, Linux & Windows MSVC)
|
||||||
if: matrix.msystem == ''
|
if: matrix.msystem == ''
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v4
|
||||||
with:
|
with:
|
||||||
aqtversion: "==3.1.*"
|
aqtversion: "==3.1.*"
|
||||||
py7zrversion: ">=0.20.2"
|
py7zrversion: ">=0.20.2"
|
||||||
@ -252,19 +252,26 @@ jobs:
|
|||||||
|
|
||||||
- name: Prepare AppImage (Linux)
|
- name: Prepare AppImage (Linux)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
|
env:
|
||||||
|
APPIMAGEUPDATE_HASH: ${{ matrix.appimageupdate_hash }}
|
||||||
|
LINUXDEPLOY_HASH: ${{ matrix.linuxdeploy_hash }}
|
||||||
|
LINUXDEPLOY_QT_HASH: ${{ matrix.linuxdeploy_qt_hash }}
|
||||||
run: |
|
run: |
|
||||||
wget "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage"
|
wget "https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20250213-2/linuxdeploy-x86_64.AppImage"
|
||||||
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage"
|
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/1-alpha-20250213-1/linuxdeploy-plugin-qt-x86_64.AppImage"
|
||||||
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage"
|
|
||||||
|
|
||||||
wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage"
|
wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/2.0.0-alpha-1-20241225/AppImageUpdate-x86_64.AppImage"
|
||||||
|
|
||||||
|
sha256sum -c - <<< "$LINUXDEPLOY_HASH"
|
||||||
|
sha256sum -c - <<< "$LINUXDEPLOY_QT_HASH"
|
||||||
|
sha256sum -c - <<< "$APPIMAGEUPDATE_HASH"
|
||||||
|
|
||||||
sudo apt install libopengl0 libfuse2
|
sudo apt install libopengl0 libfuse2
|
||||||
|
|
||||||
- name: Add QT_HOST_PATH var (Windows MSVC arm64)
|
- name: Add QT_HOST_PATH var (Windows MSVC arm64)
|
||||||
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||||
run: |
|
run: |
|
||||||
echo "QT_HOST_PATH=${{ github.workspace }}\HostQt\Qt\${{ matrix.qt_version }}\msvc2019_64" >> $env:GITHUB_ENV
|
echo "QT_HOST_PATH=${{ github.workspace }}\HostQt\Qt\${{ matrix.qt_version }}\msvc2022_64" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
- name: Setup java (macOS)
|
- name: Setup java (macOS)
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
@ -629,76 +636,3 @@ jobs:
|
|||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
ccache -s
|
ccache -s
|
||||||
|
|
||||||
flatpak:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container:
|
|
||||||
image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.8
|
|
||||||
options: --privileged
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
if: inputs.build_type == 'Debug'
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
|
|
||||||
- name: Set short version
|
|
||||||
shell: bash
|
|
||||||
run: echo "VERSION=${GITHUB_SHA::7}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Build Flatpak (Linux)
|
|
||||||
if: inputs.build_type == 'Debug'
|
|
||||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
|
||||||
with:
|
|
||||||
bundle: ShatteredPrism-${{ runner.os }}-${{ env.VERSION }}-Flatpak.flatpak
|
|
||||||
manifest-path: flatpak/org.lunaislazier.ShatteredPrism.yml
|
|
||||||
|
|
||||||
nix:
|
|
||||||
name: Nix (${{ matrix.system }})
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: ubuntu-22.04
|
|
||||||
system: x86_64-linux
|
|
||||||
|
|
||||||
- os: macos-13
|
|
||||||
system: x86_64-darwin
|
|
||||||
|
|
||||||
- os: macos-14
|
|
||||||
system: aarch64-darwin
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install Nix
|
|
||||||
uses: cachix/install-nix-action@v30
|
|
||||||
|
|
||||||
# For PRs
|
|
||||||
- name: Setup Nix Magic Cache
|
|
||||||
uses: DeterminateSystems/magic-nix-cache-action@v8
|
|
||||||
|
|
||||||
# For in-tree builds
|
|
||||||
- name: Setup Cachix
|
|
||||||
uses: cachix/cachix-action@v15
|
|
||||||
with:
|
|
||||||
name: unmojang
|
|
||||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
|
||||||
|
|
||||||
- name: Run flake checks
|
|
||||||
run: |
|
|
||||||
nix flake check --print-build-logs --show-trace
|
|
||||||
|
|
||||||
- name: Build debug package
|
|
||||||
if: ${{ inputs.build_type == 'Debug' }}
|
|
||||||
run: |
|
|
||||||
nix build --print-build-logs .#shatteredprism-debug
|
|
||||||
|
|
||||||
- name: Build release package
|
|
||||||
if: ${{ inputs.build_type != 'Debug' }}
|
|
||||||
run: |
|
|
||||||
nix build --print-build-logs .#shatteredprism
|
|
||||||
|
62
.github/workflows/flatpak.yml
vendored
Normal file
62
.github/workflows/flatpak.yml
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
name: Flatpak
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
- "**/LICENSE"
|
||||||
|
- ".github/ISSUE_TEMPLATE/**"
|
||||||
|
- ".markdownlint**"
|
||||||
|
- "nix/**"
|
||||||
|
# We don't do anything with these artifacts on releases. They go to Flathub
|
||||||
|
tags-ignore:
|
||||||
|
- "*"
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
- "**/LICENSE"
|
||||||
|
- ".github/ISSUE_TEMPLATE/**"
|
||||||
|
- ".markdownlint**"
|
||||||
|
- "nix/**"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build (${{ matrix.arch }})
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
arch: x86_64
|
||||||
|
|
||||||
|
- os: ubuntu-22.04-arm
|
||||||
|
arch: aarch64
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
container:
|
||||||
|
image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.8
|
||||||
|
options: --privileged
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Set short version
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "VERSION=${GITHUB_SHA::7}" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
- name: Build Flatpak
|
||||||
|
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
||||||
|
with:
|
||||||
|
bundle: FjordLauncher-${{ runner.os }}-${{ env.VERSION }}-Flatpak.flatpak
|
||||||
|
manifest-path: flatpak/org.fjordlauncher.FjordLauncher.yml
|
||||||
|
arch: ${{ matrix.arch }}
|
88
.github/workflows/nix.yml
vendored
Normal file
88
.github/workflows/nix.yml
vendored
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
name: Nix
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
- "**/LICENSE"
|
||||||
|
- ".github/ISSUE_TEMPLATE/**"
|
||||||
|
- ".markdownlint**"
|
||||||
|
- "flatpak/**"
|
||||||
|
tags:
|
||||||
|
- "*"
|
||||||
|
pull_request_target:
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
- "**/LICENSE"
|
||||||
|
- ".github/ISSUE_TEMPLATE/**"
|
||||||
|
- ".markdownlint**"
|
||||||
|
- "flatpak/**"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
env:
|
||||||
|
DEBUG: ${{ github.ref_type != 'tag' }}
|
||||||
|
USE_DETERMINATE: ${{ github.event_name == 'pull_request' }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build (${{ matrix.system }})
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
system: x86_64-linux
|
||||||
|
|
||||||
|
- os: ubuntu-22.04-arm
|
||||||
|
system: aarch64-linux
|
||||||
|
|
||||||
|
- os: macos-13
|
||||||
|
system: x86_64-darwin
|
||||||
|
|
||||||
|
- os: macos-14
|
||||||
|
system: aarch64-darwin
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Nix
|
||||||
|
uses: DeterminateSystems/nix-installer-action@v16
|
||||||
|
with:
|
||||||
|
determinate: ${{ env.USE_DETERMINATE }}
|
||||||
|
|
||||||
|
# For PRs
|
||||||
|
- name: Setup Nix Magic Cache
|
||||||
|
if: ${{ env.USE_DETERMINATE }}
|
||||||
|
uses: DeterminateSystems/flakehub-cache-action@v1
|
||||||
|
|
||||||
|
# For in-tree builds
|
||||||
|
- name: Setup Cachix
|
||||||
|
if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}
|
||||||
|
uses: cachix/cachix-action@v15
|
||||||
|
with:
|
||||||
|
name: fjordlauncher
|
||||||
|
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||||
|
|
||||||
|
- name: Run Flake checks
|
||||||
|
run: |
|
||||||
|
nix flake check --print-build-logs --show-trace
|
||||||
|
|
||||||
|
- name: Build debug package
|
||||||
|
if: ${{ env.DEBUG }}
|
||||||
|
run: |
|
||||||
|
nix build --print-build-logs .#fjordlauncher-debug
|
||||||
|
|
||||||
|
- name: Build release package
|
||||||
|
if: ${{ !env.DEBUG }}
|
||||||
|
run: |
|
||||||
|
nix build --print-build-logs .#fjordlauncher
|
45
.github/workflows/publish.yml
vendored
Normal file
45
.github/workflows/publish.yml
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
name: Publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [ released ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
flakehub:
|
||||||
|
name: FlakeHub
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.ref }}
|
||||||
|
|
||||||
|
- name: Install Nix
|
||||||
|
uses: cachix/install-nix-action@v30
|
||||||
|
|
||||||
|
- name: Publish on FlakeHub
|
||||||
|
uses: determinatesystems/flakehub-push@v5
|
||||||
|
with:
|
||||||
|
visibility: "public"
|
||||||
|
|
||||||
|
winget:
|
||||||
|
name: Winget
|
||||||
|
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Publish on Winget
|
||||||
|
uses: vedantmgoyal2009/winget-releaser@v2
|
||||||
|
with:
|
||||||
|
identifier: PrismLauncher.PrismLauncher
|
||||||
|
version: ${{ github.event.release.tag_name }}
|
||||||
|
installers-regex: 'PrismLauncher-Windows-MSVC(:?-arm64|-Legacy)?-Setup-.+\.exe$'
|
||||||
|
token: ${{ secrets.WINGET_TOKEN }}
|
1
.github/workflows/trigger_builds.yml
vendored
1
.github/workflows/trigger_builds.yml
vendored
@ -38,6 +38,5 @@ jobs:
|
|||||||
APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
|
APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
|
||||||
APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
|
APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
|
||||||
APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
|
APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
|
||||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
|
||||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||||
|
1
.github/workflows/trigger_release.yml
vendored
1
.github/workflows/trigger_release.yml
vendored
@ -23,7 +23,6 @@ jobs:
|
|||||||
APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
|
APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
|
||||||
APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
|
APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
|
||||||
APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
|
APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
|
||||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
|
||||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||||
|
|
||||||
|
15
.github/workflows/winget.yml
vendored
15
.github/workflows/winget.yml
vendored
@ -1,15 +0,0 @@
|
|||||||
name: Publish to WinGet
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [released]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish:
|
|
||||||
runs-on: windows-latest
|
|
||||||
steps:
|
|
||||||
- uses: vedantmgoyal2009/winget-releaser@v2
|
|
||||||
with:
|
|
||||||
identifier: PrismLauncher.PrismLauncher
|
|
||||||
version: ${{ github.event.release.tag_name }}
|
|
||||||
installers-regex: 'PrismLauncher-Windows-MSVC(:?-arm64|-Legacy)?-Setup-.+\.exe$'
|
|
||||||
token: ${{ secrets.WINGET_TOKEN }}
|
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -47,8 +47,12 @@ run/
|
|||||||
|
|
||||||
# Nix/NixOS
|
# Nix/NixOS
|
||||||
.direnv/
|
.direnv/
|
||||||
.pre-commit-config.yaml
|
## Used when manually invoking stdenv phases
|
||||||
|
outputs/
|
||||||
|
## Regular artifacts
|
||||||
result
|
result
|
||||||
|
result-*
|
||||||
|
repl-result-*
|
||||||
|
|
||||||
# Flatpak
|
# Flatpak
|
||||||
.flatpak-builder
|
.flatpak-builder
|
||||||
|
@ -78,6 +78,13 @@ else()
|
|||||||
# ATL's pack list needs more than the default 1 Mib stack on windows
|
# ATL's pack list needs more than the default 1 Mib stack on windows
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,8388608 ${CMAKE_EXE_LINKER_FLAGS}")
|
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,8388608 ${CMAKE_EXE_LINKER_FLAGS}")
|
||||||
|
|
||||||
|
# -ffunction-sections and -fdata-sections help reduce binary size
|
||||||
|
# -mguard=cf enables Control Flow Guard
|
||||||
|
# TODO: Look into -gc-sections to further reduce binary size
|
||||||
|
foreach(lang C CXX)
|
||||||
|
set("CMAKE_${lang}_FLAGS_RELEASE" "-ffunction-sections -fdata-sections -mguard=cf")
|
||||||
|
endforeach()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -106,14 +113,14 @@ if ((CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebI
|
|||||||
else()
|
else()
|
||||||
# AppleClang and Clang
|
# AppleClang and Clang
|
||||||
message(STATUS "Address Sanitizer available on Clang")
|
message(STATUS "Address Sanitizer available on Clang")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=null")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=null")
|
||||||
endif()
|
endif()
|
||||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||||
# GCC
|
# GCC
|
||||||
message(STATUS "Address Sanitizer available on GCC")
|
message(STATUS "Address Sanitizer available on GCC")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover")
|
||||||
link_libraries("asan")
|
link_libraries("asan")
|
||||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||||
message(STATUS "Address Sanitizer available on MSVC")
|
message(STATUS "Address Sanitizer available on MSVC")
|
||||||
@ -181,7 +188,7 @@ set(Launcher_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" CACHE S
|
|||||||
|
|
||||||
######## Set version numbers ########
|
######## Set version numbers ########
|
||||||
set(Launcher_VERSION_MAJOR 1)
|
set(Launcher_VERSION_MAJOR 1)
|
||||||
set(Launcher_VERSION_MINOR 6)
|
set(Launcher_VERSION_MINOR 7)
|
||||||
|
|
||||||
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
||||||
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
|
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
|
||||||
|
13
default.nix
13
default.nix
@ -1,9 +1,4 @@
|
|||||||
(import (
|
(import (fetchTarball {
|
||||||
let
|
url = "https://github.com/edolstra/flake-compat/archive/ff81ac966bb2cae68946d5ed5fc4994f96d0ffec.tar.gz";
|
||||||
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
|
sha256 = "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=";
|
||||||
in
|
}) { src = ./.; }).defaultNix
|
||||||
fetchTarball {
|
|
||||||
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
|
|
||||||
sha256 = lock.nodes.flake-compat.locked.narHash;
|
|
||||||
}
|
|
||||||
) { src = ./.; }).defaultNix
|
|
||||||
|
131
flake.nix
131
flake.nix
@ -15,28 +15,6 @@
|
|||||||
url = "github:PrismLauncher/libnbtplusplus";
|
url = "github:PrismLauncher/libnbtplusplus";
|
||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
nix-filter.url = "github:numtide/nix-filter";
|
|
||||||
|
|
||||||
/*
|
|
||||||
Inputs below this are optional and can be removed
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
inputs.shatteredprism = {
|
|
||||||
url = "github:lunaislazier/ShatteredPrism";
|
|
||||||
inputs = {
|
|
||||||
flake-compat.follows = "";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
*/
|
|
||||||
|
|
||||||
flake-compat = {
|
|
||||||
url = "github:edolstra/flake-compat";
|
|
||||||
flake = false;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs =
|
outputs =
|
||||||
@ -44,9 +22,8 @@
|
|||||||
self,
|
self,
|
||||||
nixpkgs,
|
nixpkgs,
|
||||||
libnbtplusplus,
|
libnbtplusplus,
|
||||||
nix-filter,
|
|
||||||
...
|
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
inherit (nixpkgs) lib;
|
inherit (nixpkgs) lib;
|
||||||
|
|
||||||
@ -58,27 +35,108 @@
|
|||||||
forAllSystems = lib.genAttrs systems;
|
forAllSystems = lib.genAttrs systems;
|
||||||
nixpkgsFor = forAllSystems (system: nixpkgs.legacyPackages.${system});
|
nixpkgsFor = forAllSystems (system: nixpkgs.legacyPackages.${system});
|
||||||
in
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
checks = forAllSystems (
|
checks = forAllSystems (
|
||||||
system:
|
system:
|
||||||
|
|
||||||
let
|
let
|
||||||
checks' = nixpkgsFor.${system}.callPackage ./nix/checks.nix { inherit self; };
|
pkgs = nixpkgsFor.${system};
|
||||||
|
llvm = pkgs.llvmPackages_19;
|
||||||
in
|
in
|
||||||
lib.filterAttrs (_: lib.isDerivation) checks'
|
|
||||||
|
{
|
||||||
|
formatting =
|
||||||
|
pkgs.runCommand "check-formatting"
|
||||||
|
{
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
deadnix
|
||||||
|
llvm.clang-tools
|
||||||
|
markdownlint-cli
|
||||||
|
nixfmt-rfc-style
|
||||||
|
statix
|
||||||
|
];
|
||||||
|
}
|
||||||
|
''
|
||||||
|
cd ${self}
|
||||||
|
|
||||||
|
echo "Running clang-format...."
|
||||||
|
clang-format --dry-run --style='file' --Werror */**.{c,cc,cpp,h,hh,hpp}
|
||||||
|
|
||||||
|
echo "Running deadnix..."
|
||||||
|
deadnix --fail
|
||||||
|
|
||||||
|
echo "Running markdownlint..."
|
||||||
|
markdownlint --dot .
|
||||||
|
|
||||||
|
echo "Running nixfmt..."
|
||||||
|
find -type f -name '*.nix' -exec nixfmt --check {} +
|
||||||
|
|
||||||
|
echo "Running statix"
|
||||||
|
statix check .
|
||||||
|
|
||||||
|
touch $out
|
||||||
|
'';
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
devShells = forAllSystems (
|
devShells = forAllSystems (
|
||||||
system:
|
system:
|
||||||
|
|
||||||
let
|
let
|
||||||
pkgs = nixpkgsFor.${system};
|
pkgs = nixpkgsFor.${system};
|
||||||
|
llvm = pkgs.llvmPackages_19;
|
||||||
|
|
||||||
|
packages' = self.packages.${system};
|
||||||
|
|
||||||
|
# Re-use our package wrapper to wrap our development environment
|
||||||
|
qt-wrapper-env = packages'.shatteredprism.overrideAttrs (old: {
|
||||||
|
name = "qt-wrapper-env";
|
||||||
|
|
||||||
|
# Required to use script-based makeWrapper below
|
||||||
|
strictDeps = true;
|
||||||
|
|
||||||
|
# We don't need/want the unwrapped Fjord package
|
||||||
|
paths = [ ];
|
||||||
|
|
||||||
|
nativeBuildInputs = old.nativeBuildInputs or [ ] ++ [
|
||||||
|
# Ensure the wrapper is script based so it can be sourced
|
||||||
|
pkgs.makeWrapper
|
||||||
|
];
|
||||||
|
|
||||||
|
# Inspired by https://discourse.nixos.org/t/python-qt-woes/11808/10
|
||||||
|
buildCommand = ''
|
||||||
|
makeQtWrapper ${lib.getExe pkgs.runtimeShellPackage} "$out"
|
||||||
|
sed -i '/^exec/d' "$out"
|
||||||
|
'';
|
||||||
|
});
|
||||||
in
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
default = pkgs.mkShell {
|
default = pkgs.mkShell {
|
||||||
inputsFrom = [ self.packages.${system}.shatteredprism-unwrapped ];
|
inputsFrom = [ packages'.shatteredprism-unwrapped ];
|
||||||
buildInputs = with pkgs; [
|
|
||||||
|
packages = with pkgs; [
|
||||||
ccache
|
ccache
|
||||||
ninja
|
llvm.clang-tools
|
||||||
];
|
];
|
||||||
|
|
||||||
|
cmakeBuildType = "Debug";
|
||||||
|
cmakeFlags = [ "-GNinja" ] ++ packages'.shatteredprism.cmakeFlags;
|
||||||
|
dontFixCmake = true;
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
echo "Sourcing ${qt-wrapper-env}"
|
||||||
|
source ${qt-wrapper-env}
|
||||||
|
|
||||||
|
git submodule update --init --force
|
||||||
|
|
||||||
|
if [ ! -f compile_commands.json ]; then
|
||||||
|
cmakeConfigurePhase
|
||||||
|
cd ..
|
||||||
|
ln -s "$cmakeBuildDir"/compile_commands.json compile_commands.json
|
||||||
|
fi
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -89,7 +147,6 @@
|
|||||||
shatteredprism-unwrapped = prev.callPackage ./nix/unwrapped.nix {
|
shatteredprism-unwrapped = prev.callPackage ./nix/unwrapped.nix {
|
||||||
inherit
|
inherit
|
||||||
libnbtplusplus
|
libnbtplusplus
|
||||||
nix-filter
|
|
||||||
self
|
self
|
||||||
;
|
;
|
||||||
};
|
};
|
||||||
@ -99,6 +156,7 @@
|
|||||||
|
|
||||||
packages = forAllSystems (
|
packages = forAllSystems (
|
||||||
system:
|
system:
|
||||||
|
|
||||||
let
|
let
|
||||||
pkgs = nixpkgsFor.${system};
|
pkgs = nixpkgsFor.${system};
|
||||||
|
|
||||||
@ -111,6 +169,7 @@
|
|||||||
default = shatteredPackages.shatteredprism;
|
default = shatteredPackages.shatteredprism;
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
|
||||||
# Only output them if they're available on the current system
|
# Only output them if they're available on the current system
|
||||||
lib.filterAttrs (_: lib.meta.availableOn pkgs.stdenv.hostPlatform) packages
|
lib.filterAttrs (_: lib.meta.availableOn pkgs.stdenv.hostPlatform) packages
|
||||||
);
|
);
|
||||||
@ -118,16 +177,18 @@
|
|||||||
# We put these under legacyPackages as they are meant for CI, not end user consumption
|
# We put these under legacyPackages as they are meant for CI, not end user consumption
|
||||||
legacyPackages = forAllSystems (
|
legacyPackages = forAllSystems (
|
||||||
system:
|
system:
|
||||||
|
|
||||||
let
|
let
|
||||||
shatteredPackages = self.packages.${system};
|
packages' = self.packages.${system};
|
||||||
legacyPackages = self.legacyPackages.${system};
|
legacyPackages' = self.legacyPackages.${system};
|
||||||
in
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
shatteredprism-debug = shatteredPackages.shatteredprism.override {
|
shatteredprism-debug = packages'.shatteredprism.override {
|
||||||
shatteredprism-unwrapped = legacyPackages.shatteredprism-unwrapped-debug;
|
shatteredprism-unwrapped = legacyPackages'.shatteredprism-unwrapped-debug;
|
||||||
};
|
};
|
||||||
|
|
||||||
shatteredprism-unwrapped-debug = shatteredPackages.shatteredprism-unwrapped.overrideAttrs {
|
shatteredprism-unwrapped-debug = packages'.shatteredprism-unwrapped.overrideAttrs {
|
||||||
cmakeBuildType = "Debug";
|
cmakeBuildType = "Debug";
|
||||||
dontStrip = true;
|
dontStrip = true;
|
||||||
};
|
};
|
||||||
|
@ -160,6 +160,7 @@
|
|||||||
|
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <QStyleHints>
|
||||||
#include "WindowsConsole.h"
|
#include "WindowsConsole.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -231,7 +232,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
setApplicationDisplayName(QString("%1 %2").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString()));
|
setApplicationDisplayName(QString("%1 %2").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString()));
|
||||||
setApplicationVersion(BuildConfig.printableVersionString() + "\n" + BuildConfig.GIT_COMMIT);
|
setApplicationVersion(BuildConfig.printableVersionString() + "\n" + BuildConfig.GIT_COMMIT);
|
||||||
setDesktopFileName(BuildConfig.LAUNCHER_DESKTOPFILENAME);
|
setDesktopFileName(BuildConfig.LAUNCHER_DESKTOPFILENAME);
|
||||||
startTime = QDateTime::currentDateTime();
|
m_startTime = QDateTime::currentDateTime();
|
||||||
|
|
||||||
// Don't quit on hiding the last window
|
// Don't quit on hiding the last window
|
||||||
this->setQuitOnLastWindowClosed(false);
|
this->setQuitOnLastWindowClosed(false);
|
||||||
@ -1124,8 +1125,16 @@ bool Application::createSetupWizard()
|
|||||||
// set default theme after going into theme wizard
|
// set default theme after going into theme wizard
|
||||||
if (!validIcons)
|
if (!validIcons)
|
||||||
settings()->set("IconTheme", QString("pe_colored"));
|
settings()->set("IconTheme", QString("pe_colored"));
|
||||||
if (!validWidgets)
|
if (!validWidgets) {
|
||||||
settings()->set("ApplicationTheme", QString("system"));
|
#if defined(Q_OS_WIN32) && QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
|
||||||
|
const QString style =
|
||||||
|
QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark ? QStringLiteral("dark") : QStringLiteral("bright");
|
||||||
|
#else
|
||||||
|
const QString style = QStringLiteral("system");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
settings()->set("ApplicationTheme", style);
|
||||||
|
}
|
||||||
|
|
||||||
m_themeManager->applyCurrentlySelectedTheme(true);
|
m_themeManager->applyCurrentlySelectedTheme(true);
|
||||||
|
|
||||||
@ -1192,6 +1201,9 @@ bool Application::event(QEvent* event)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (event->type() == QEvent::FileOpen) {
|
if (event->type() == QEvent::FileOpen) {
|
||||||
|
if (!m_mainWindow) {
|
||||||
|
showMainWindow(false);
|
||||||
|
}
|
||||||
auto ev = static_cast<QFileOpenEvent*>(event);
|
auto ev = static_cast<QFileOpenEvent*>(event);
|
||||||
m_mainWindow->processURLs({ ev->url() });
|
m_mainWindow->processURLs({ ev->url() });
|
||||||
}
|
}
|
||||||
@ -1350,6 +1362,9 @@ void Application::messageReceived(const QByteArray& message)
|
|||||||
qWarning() << "Received" << command << "message without a zip path/URL.";
|
qWarning() << "Received" << command << "message without a zip path/URL.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!m_mainWindow) {
|
||||||
|
showMainWindow(false);
|
||||||
|
}
|
||||||
m_mainWindow->processURLs({ normalizeImportUrl(url) });
|
m_mainWindow->processURLs({ normalizeImportUrl(url) });
|
||||||
} else if (command == "launch") {
|
} else if (command == "launch") {
|
||||||
QString id = received.args["id"];
|
QString id = received.args["id"];
|
||||||
|
@ -112,7 +112,7 @@ class Application : public QApplication {
|
|||||||
|
|
||||||
std::shared_ptr<SettingsObject> settings() const { return m_settings; }
|
std::shared_ptr<SettingsObject> settings() const { return m_settings; }
|
||||||
|
|
||||||
qint64 timeSinceStart() const { return startTime.msecsTo(QDateTime::currentDateTime()); }
|
qint64 timeSinceStart() const { return m_startTime.msecsTo(QDateTime::currentDateTime()); }
|
||||||
|
|
||||||
QIcon getThemedIcon(const QString& name);
|
QIcon getThemedIcon(const QString& name);
|
||||||
|
|
||||||
@ -236,7 +236,7 @@ class Application : public QApplication {
|
|||||||
bool shouldExitNow() const;
|
bool shouldExitNow() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QDateTime startTime;
|
QDateTime m_startTime;
|
||||||
|
|
||||||
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
||||||
|
|
||||||
|
@ -1066,8 +1066,6 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/dialogs/CopyInstanceDialog.h
|
ui/dialogs/CopyInstanceDialog.h
|
||||||
ui/dialogs/CustomMessageBox.cpp
|
ui/dialogs/CustomMessageBox.cpp
|
||||||
ui/dialogs/CustomMessageBox.h
|
ui/dialogs/CustomMessageBox.h
|
||||||
ui/dialogs/EditAccountDialog.cpp
|
|
||||||
ui/dialogs/EditAccountDialog.h
|
|
||||||
ui/dialogs/ExportInstanceDialog.cpp
|
ui/dialogs/ExportInstanceDialog.cpp
|
||||||
ui/dialogs/ExportInstanceDialog.h
|
ui/dialogs/ExportInstanceDialog.h
|
||||||
ui/dialogs/ExportPackDialog.cpp
|
ui/dialogs/ExportPackDialog.cpp
|
||||||
@ -1257,7 +1255,6 @@ qt_wrap_ui(LAUNCHER_UI
|
|||||||
ui/dialogs/OfflineLoginDialog.ui
|
ui/dialogs/OfflineLoginDialog.ui
|
||||||
ui/dialogs/AuthlibInjectorLoginDialog.ui
|
ui/dialogs/AuthlibInjectorLoginDialog.ui
|
||||||
ui/dialogs/AboutDialog.ui
|
ui/dialogs/AboutDialog.ui
|
||||||
ui/dialogs/EditAccountDialog.ui
|
|
||||||
ui/dialogs/ReviewMessageBox.ui
|
ui/dialogs/ReviewMessageBox.ui
|
||||||
ui/dialogs/ScrollMessageBox.ui
|
ui/dialogs/ScrollMessageBox.ui
|
||||||
ui/dialogs/BlockedModsDialog.ui
|
ui/dialogs/BlockedModsDialog.ui
|
||||||
|
@ -307,6 +307,7 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw)
|
|||||||
if (!replacing) {
|
if (!replacing) {
|
||||||
roles.clear();
|
roles.clear();
|
||||||
filterModel->setSourceModel(replacing);
|
filterModel->setSourceModel(replacing);
|
||||||
|
endResetModel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +171,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
|||||||
auto os_arch = results["os.arch"];
|
auto os_arch = results["os.arch"];
|
||||||
auto java_version = results["java.version"];
|
auto java_version = results["java.version"];
|
||||||
auto java_vendor = results["java.vendor"];
|
auto java_vendor = results["java.vendor"];
|
||||||
bool is_64 = os_arch == "x86_64" || os_arch == "amd64" || os_arch == "aarch64" || os_arch == "arm64";
|
bool is_64 = os_arch == "x86_64" || os_arch == "amd64" || os_arch == "aarch64" || os_arch == "arm64" || os_arch == "riscv64";
|
||||||
|
|
||||||
result.validity = Result::Validity::Valid;
|
result.validity = Result::Validity::Valid;
|
||||||
result.is_64bit = is_64;
|
result.is_64bit = is_64;
|
||||||
|
@ -254,20 +254,60 @@ void LaunchTask::emitFailed(QString reason)
|
|||||||
Task::emitFailed(reason);
|
Task::emitFailed(reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LaunchTask::substituteVariables(QStringList& args) const
|
QString expandVariables(const QString& input, QProcessEnvironment dict)
|
||||||
{
|
{
|
||||||
auto env = m_instance->createEnvironment();
|
QString result = input;
|
||||||
|
|
||||||
for (auto key : env.keys()) {
|
enum { base, maybeBrace, variable, brace } state = base;
|
||||||
args.replaceInStrings("$" + key, env.value(key));
|
int startIdx = -1;
|
||||||
|
for (int i = 0; i < result.length();) {
|
||||||
|
QChar c = result.at(i++);
|
||||||
|
switch (state) {
|
||||||
|
case base:
|
||||||
|
if (c == '$')
|
||||||
|
state = maybeBrace;
|
||||||
|
break;
|
||||||
|
case maybeBrace:
|
||||||
|
if (c == '{') {
|
||||||
|
state = brace;
|
||||||
|
startIdx = i;
|
||||||
|
} else if (c.isLetterOrNumber() || c == '_') {
|
||||||
|
state = variable;
|
||||||
|
startIdx = i - 1;
|
||||||
|
} else {
|
||||||
|
state = base;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case brace:
|
||||||
|
if (c == '}') {
|
||||||
|
const auto res = dict.value(result.mid(startIdx, i - 1 - startIdx), "");
|
||||||
|
if (!res.isEmpty()) {
|
||||||
|
result.replace(startIdx - 2, i - startIdx + 2, res);
|
||||||
|
i = startIdx - 2 + res.length();
|
||||||
|
}
|
||||||
|
state = base;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case variable:
|
||||||
|
if (!c.isLetterOrNumber() && c != '_') {
|
||||||
|
const auto res = dict.value(result.mid(startIdx, i - startIdx - 1), "");
|
||||||
|
if (!res.isEmpty()) {
|
||||||
|
result.replace(startIdx - 1, i - startIdx, res);
|
||||||
|
i = startIdx - 1 + res.length();
|
||||||
|
}
|
||||||
|
state = base;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state == variable) {
|
||||||
|
if (const auto res = dict.value(result.mid(startIdx), ""); !res.isEmpty())
|
||||||
|
result.replace(startIdx - 1, result.length() - startIdx + 1, res);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LaunchTask::substituteVariables(QString& cmd) const
|
QString LaunchTask::substituteVariables(QString& cmd, bool isLaunch) const
|
||||||
{
|
{
|
||||||
auto env = m_instance->createEnvironment();
|
return expandVariables(cmd, isLaunch ? m_instance->createLaunchEnvironment() : m_instance->createEnvironment());
|
||||||
|
|
||||||
for (auto key : env.keys()) {
|
|
||||||
cmd.replace("$" + key, env.value(key));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -87,8 +87,7 @@ class LaunchTask : public Task {
|
|||||||
shared_qobject_ptr<LogModel> getLogModel();
|
shared_qobject_ptr<LogModel> getLogModel();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void substituteVariables(QStringList& args) const;
|
QString substituteVariables(QString& cmd, bool isLaunch = false) const;
|
||||||
void substituteVariables(QString& cmd) const;
|
|
||||||
QString censorPrivateInfo(QString in);
|
QString censorPrivateInfo(QString in);
|
||||||
|
|
||||||
protected: /* methods */
|
protected: /* methods */
|
||||||
|
@ -32,7 +32,7 @@ class LogModel : public QAbstractListModel {
|
|||||||
|
|
||||||
private /* types */:
|
private /* types */:
|
||||||
struct entry {
|
struct entry {
|
||||||
MessageLevel::Enum level;
|
MessageLevel::Enum level = MessageLevel::Enum::Unknown;
|
||||||
QString line;
|
QString line;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -47,19 +47,15 @@ PostLaunchCommand::PostLaunchCommand(LaunchTask* parent) : LaunchStep(parent)
|
|||||||
|
|
||||||
void PostLaunchCommand::executeTask()
|
void PostLaunchCommand::executeTask()
|
||||||
{
|
{
|
||||||
// FIXME: where to put this?
|
auto cmd = m_parent->substituteVariables(m_command);
|
||||||
|
emit logLine(tr("Running Post-Launch command: %1").arg(cmd), MessageLevel::Launcher);
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||||
auto args = QProcess::splitCommand(m_command);
|
auto args = QProcess::splitCommand(cmd);
|
||||||
m_parent->substituteVariables(args);
|
|
||||||
|
|
||||||
emit logLine(tr("Running Post-Launch command: %1").arg(args.join(' ')), MessageLevel::Launcher);
|
|
||||||
const QString program = args.takeFirst();
|
const QString program = args.takeFirst();
|
||||||
m_process.start(program, args);
|
m_process.start(program, args);
|
||||||
#else
|
#else
|
||||||
m_parent->substituteVariables(m_command);
|
m_process.start(cmd);
|
||||||
|
|
||||||
emit logLine(tr("Running Post-Launch command: %1").arg(m_command), MessageLevel::Launcher);
|
|
||||||
m_process.start(m_command);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,19 +47,14 @@ PreLaunchCommand::PreLaunchCommand(LaunchTask* parent) : LaunchStep(parent)
|
|||||||
|
|
||||||
void PreLaunchCommand::executeTask()
|
void PreLaunchCommand::executeTask()
|
||||||
{
|
{
|
||||||
// FIXME: where to put this?
|
auto cmd = m_parent->substituteVariables(m_command);
|
||||||
|
emit logLine(tr("Running Pre-Launch command: %1").arg(cmd), MessageLevel::Launcher);
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||||
auto args = QProcess::splitCommand(m_command);
|
auto args = QProcess::splitCommand(cmd);
|
||||||
m_parent->substituteVariables(args);
|
|
||||||
|
|
||||||
emit logLine(tr("Running Pre-Launch command: %1").arg(args.join(' ')), MessageLevel::Launcher);
|
|
||||||
const QString program = args.takeFirst();
|
const QString program = args.takeFirst();
|
||||||
m_process.start(program, args);
|
m_process.start(program, args);
|
||||||
#else
|
#else
|
||||||
m_parent->substituteVariables(m_command);
|
m_process.start(cmd);
|
||||||
|
|
||||||
emit logLine(tr("Running Pre-Launch command: %1").arg(m_command), MessageLevel::Launcher);
|
|
||||||
m_process.start(m_command);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,6 +654,7 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
|
|||||||
// dlsym variant is only needed for OpenGL and not included in the vulkan layer
|
// dlsym variant is only needed for OpenGL and not included in the vulkan layer
|
||||||
appendLib("libMangoHud_dlsym.so");
|
appendLib("libMangoHud_dlsym.so");
|
||||||
appendLib("libMangoHud_opengl.so");
|
appendLib("libMangoHud_opengl.so");
|
||||||
|
appendLib("libMangoHud_shim.so");
|
||||||
preloadList << mangoHudLibString;
|
preloadList << mangoHudLibString;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1131,13 +1132,6 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
|
|||||||
process->appendStep(step);
|
process->appendStep(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
// run pre-launch command if that's needed
|
|
||||||
if (getPreLaunchCommand().size()) {
|
|
||||||
auto step = makeShared<PreLaunchCommand>(pptr);
|
|
||||||
step->setWorkingDirectory(gameRoot());
|
|
||||||
process->appendStep(step);
|
|
||||||
}
|
|
||||||
|
|
||||||
// load meta
|
// load meta
|
||||||
{
|
{
|
||||||
auto mode = session->status != AuthSession::PlayableOffline ? Net::Mode::Online : Net::Mode::Offline;
|
auto mode = session->status != AuthSession::PlayableOffline ? Net::Mode::Online : Net::Mode::Offline;
|
||||||
@ -1150,6 +1144,13 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
|
|||||||
process->appendStep(makeShared<CheckJava>(pptr));
|
process->appendStep(makeShared<CheckJava>(pptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// run pre-launch command if that's needed
|
||||||
|
if (getPreLaunchCommand().size()) {
|
||||||
|
auto step = makeShared<PreLaunchCommand>(pptr);
|
||||||
|
step->setWorkingDirectory(gameRoot());
|
||||||
|
process->appendStep(step);
|
||||||
|
}
|
||||||
|
|
||||||
// if we aren't in offline mode,.
|
// if we aren't in offline mode,.
|
||||||
if (session->status != AuthSession::PlayableOffline) {
|
if (session->status != AuthSession::PlayableOffline) {
|
||||||
if (!session->demo) {
|
if (!session->demo) {
|
||||||
|
@ -8,7 +8,10 @@ void MinecraftLoadAndCheck::executeTask()
|
|||||||
{
|
{
|
||||||
// add offline metadata load task
|
// add offline metadata load task
|
||||||
auto components = m_inst->getPackProfile();
|
auto components = m_inst->getPackProfile();
|
||||||
components->reload(m_netmode);
|
if (auto result = components->reload(m_netmode); !result) {
|
||||||
|
emitFailed(result.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_task = components->getCurrentTask();
|
m_task = components->getCurrentTask();
|
||||||
|
|
||||||
if (!m_task) {
|
if (!m_task) {
|
||||||
|
@ -180,29 +180,32 @@ static bool savePackProfile(const QString& filename, const ComponentContainer& c
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read the given file into component containers
|
// Read the given file into component containers
|
||||||
static bool loadPackProfile(PackProfile* parent,
|
static PackProfile::Result loadPackProfile(PackProfile* parent,
|
||||||
const QString& filename,
|
const QString& filename,
|
||||||
const QString& componentJsonPattern,
|
const QString& componentJsonPattern,
|
||||||
ComponentContainer& container)
|
ComponentContainer& container)
|
||||||
{
|
{
|
||||||
QFile componentsFile(filename);
|
QFile componentsFile(filename);
|
||||||
if (!componentsFile.exists()) {
|
if (!componentsFile.exists()) {
|
||||||
qCWarning(instanceProfileC) << "Components file" << filename << "doesn't exist. This should never happen.";
|
auto message = QObject::tr("Components file %1 doesn't exist. This should never happen.").arg(filename);
|
||||||
return false;
|
qCWarning(instanceProfileC) << message;
|
||||||
|
return PackProfile::Result::Error(message);
|
||||||
}
|
}
|
||||||
if (!componentsFile.open(QFile::ReadOnly)) {
|
if (!componentsFile.open(QFile::ReadOnly)) {
|
||||||
qCCritical(instanceProfileC) << "Couldn't open" << componentsFile.fileName() << " for reading:" << componentsFile.errorString();
|
auto message = QObject::tr("Couldn't open %1 for reading: %2").arg(componentsFile.fileName(), componentsFile.errorString());
|
||||||
|
qCCritical(instanceProfileC) << message;
|
||||||
qCWarning(instanceProfileC) << "Ignoring overridden order";
|
qCWarning(instanceProfileC) << "Ignoring overridden order";
|
||||||
return false;
|
return PackProfile::Result::Error(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// and it's valid JSON
|
// and it's valid JSON
|
||||||
QJsonParseError error;
|
QJsonParseError error;
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(componentsFile.readAll(), &error);
|
QJsonDocument doc = QJsonDocument::fromJson(componentsFile.readAll(), &error);
|
||||||
if (error.error != QJsonParseError::NoError) {
|
if (error.error != QJsonParseError::NoError) {
|
||||||
qCCritical(instanceProfileC) << "Couldn't parse" << componentsFile.fileName() << ":" << error.errorString();
|
auto message = QObject::tr("Couldn't parse %1 as json: %2").arg(componentsFile.fileName(), error.errorString());
|
||||||
|
qCCritical(instanceProfileC) << message;
|
||||||
qCWarning(instanceProfileC) << "Ignoring overridden order";
|
qCWarning(instanceProfileC) << "Ignoring overridden order";
|
||||||
return false;
|
return PackProfile::Result::Error(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// and then read it and process it if all above is true.
|
// and then read it and process it if all above is true.
|
||||||
@ -219,11 +222,13 @@ static bool loadPackProfile(PackProfile* parent,
|
|||||||
container.append(componentFromJsonV1(parent, componentJsonPattern, comp_obj));
|
container.append(componentFromJsonV1(parent, componentJsonPattern, comp_obj));
|
||||||
}
|
}
|
||||||
} catch ([[maybe_unused]] const JSONValidationError& err) {
|
} catch ([[maybe_unused]] const JSONValidationError& err) {
|
||||||
qCCritical(instanceProfileC) << "Couldn't parse" << componentsFile.fileName() << ": bad file format";
|
auto message = QObject::tr("Couldn't parse %1 : bad file format").arg(componentsFile.fileName());
|
||||||
|
qCCritical(instanceProfileC) << message;
|
||||||
|
qCWarning(instanceProfileC) << "error:" << err.what();
|
||||||
container.clear();
|
container.clear();
|
||||||
return false;
|
return PackProfile::Result::Error(message);
|
||||||
}
|
}
|
||||||
return true;
|
return PackProfile::Result::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
// END: component file format
|
// END: component file format
|
||||||
@ -290,16 +295,16 @@ void PackProfile::save_internal()
|
|||||||
d->dirty = false;
|
d->dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PackProfile::load()
|
PackProfile::Result PackProfile::load()
|
||||||
{
|
{
|
||||||
auto filename = componentsFilePath();
|
auto filename = componentsFilePath();
|
||||||
|
|
||||||
// load the new component list and swap it with the current one...
|
// load the new component list and swap it with the current one...
|
||||||
ComponentContainer newComponents;
|
ComponentContainer newComponents;
|
||||||
if (!loadPackProfile(this, filename, patchesPattern(), newComponents)) {
|
if (auto result = loadPackProfile(this, filename, patchesPattern(), newComponents); !result) {
|
||||||
qCritical() << d->m_instance->name() << "|" << "Failed to load the component config";
|
qCritical() << d->m_instance->name() << "|" << "Failed to load the component config";
|
||||||
return false;
|
return result;
|
||||||
} else {
|
}
|
||||||
// FIXME: actually use fine-grained updates, not this...
|
// FIXME: actually use fine-grained updates, not this...
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
// disconnect all the old components
|
// disconnect all the old components
|
||||||
@ -319,15 +324,14 @@ bool PackProfile::load()
|
|||||||
}
|
}
|
||||||
endResetModel();
|
endResetModel();
|
||||||
d->loaded = true;
|
d->loaded = true;
|
||||||
return true;
|
return Result::Success();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PackProfile::reload(Net::Mode netmode)
|
PackProfile::Result PackProfile::reload(Net::Mode netmode)
|
||||||
{
|
{
|
||||||
// Do not reload when the update/resolve task is running. It is in control.
|
// Do not reload when the update/resolve task is running. It is in control.
|
||||||
if (d->m_updateTask) {
|
if (d->m_updateTask) {
|
||||||
return;
|
return Result::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
// flush any scheduled saves to not lose state
|
// flush any scheduled saves to not lose state
|
||||||
@ -336,9 +340,11 @@ void PackProfile::reload(Net::Mode netmode)
|
|||||||
// FIXME: differentiate when a reapply is required by propagating state from components
|
// FIXME: differentiate when a reapply is required by propagating state from components
|
||||||
invalidateLaunchProfile();
|
invalidateLaunchProfile();
|
||||||
|
|
||||||
if (load()) {
|
if (auto result = load(); !result) {
|
||||||
resolve(netmode);
|
return result;
|
||||||
}
|
}
|
||||||
|
resolve(netmode);
|
||||||
|
return Result::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
Task::Ptr PackProfile::getCurrentTask()
|
Task::Ptr PackProfile::getCurrentTask()
|
||||||
|
@ -62,6 +62,19 @@ class PackProfile : public QAbstractListModel {
|
|||||||
public:
|
public:
|
||||||
enum Columns { NameColumn = 0, VersionColumn, NUM_COLUMNS };
|
enum Columns { NameColumn = 0, VersionColumn, NUM_COLUMNS };
|
||||||
|
|
||||||
|
struct Result {
|
||||||
|
bool success;
|
||||||
|
QString error;
|
||||||
|
|
||||||
|
// Implicit conversion to bool
|
||||||
|
operator bool() const { return success; }
|
||||||
|
|
||||||
|
// Factory methods for convenience
|
||||||
|
static Result Success() { return { true, "" }; }
|
||||||
|
|
||||||
|
static Result Error(const QString& errorMessage) { return { false, errorMessage }; }
|
||||||
|
};
|
||||||
|
|
||||||
explicit PackProfile(MinecraftInstance* instance);
|
explicit PackProfile(MinecraftInstance* instance);
|
||||||
virtual ~PackProfile();
|
virtual ~PackProfile();
|
||||||
|
|
||||||
@ -102,7 +115,7 @@ class PackProfile : public QAbstractListModel {
|
|||||||
bool revertToBase(int index);
|
bool revertToBase(int index);
|
||||||
|
|
||||||
/// reload the list, reload all components, resolve dependencies
|
/// reload the list, reload all components, resolve dependencies
|
||||||
void reload(Net::Mode netmode);
|
Result reload(Net::Mode netmode);
|
||||||
|
|
||||||
// reload all components, resolve dependencies
|
// reload all components, resolve dependencies
|
||||||
void resolve(Net::Mode netmode);
|
void resolve(Net::Mode netmode);
|
||||||
@ -169,7 +182,7 @@ class PackProfile : public QAbstractListModel {
|
|||||||
void disableInteraction(bool disable);
|
void disableInteraction(bool disable);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool load();
|
Result load();
|
||||||
bool installJarMods_internal(QStringList filepaths);
|
bool installJarMods_internal(QStringList filepaths);
|
||||||
bool installCustomJar_internal(QString filepath);
|
bool installCustomJar_internal(QString filepath);
|
||||||
bool installAgents_internal(QStringList filepaths);
|
bool installAgents_internal(QStringList filepaths);
|
||||||
|
@ -99,44 +99,44 @@ MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(sile
|
|||||||
</script>
|
</script>
|
||||||
)XXX")
|
)XXX")
|
||||||
.arg(BuildConfig.LOGIN_CALLBACK_URL));
|
.arg(BuildConfig.LOGIN_CALLBACK_URL));
|
||||||
oauth2.setReplyHandler(replyHandler);
|
m_oauth2.setReplyHandler(replyHandler);
|
||||||
} else {
|
} else {
|
||||||
oauth2.setReplyHandler(new CustomOAuthOobReplyHandler(this));
|
m_oauth2.setReplyHandler(new CustomOAuthOobReplyHandler(this));
|
||||||
}
|
}
|
||||||
oauth2.setAuthorizationUrl(QUrl("https://login.live.com/oauth20_connect.srf"));
|
m_oauth2.setAuthorizationUrl(QUrl("https://login.live.com/oauth20_connect.srf"));
|
||||||
oauth2.setAccessTokenUrl(QUrl("https://login.live.com/oauth20_token.srf"));
|
m_oauth2.setAccessTokenUrl(QUrl("https://login.live.com/oauth20_token.srf"));
|
||||||
const auto& scope = "service::user.auth.xboxlive.com::MBI_SSL";
|
const auto& scope = "service::user.auth.xboxlive.com::MBI_SSL";
|
||||||
oauth2.setScope(scope);
|
m_oauth2.setScope(scope);
|
||||||
// QOAuth2AuthorizationCodeFlow doesn't pass a "scope" when refreshing access tokens, but Microsoft expects it.
|
// QOAuth2AuthorizationCodeFlow doesn't pass a "scope" when refreshing access tokens, but Microsoft expects it.
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
oauth2.setModifyParametersFunction([&](QAbstractOAuth::Stage stage, QVariantMap* parameters) {
|
m_oauth2.setModifyParametersFunction([&](QAbstractOAuth::Stage stage, QVariantMap* parameters) {
|
||||||
if (stage == QAbstractOAuth::Stage::RefreshingAccessToken) {
|
if (stage == QAbstractOAuth::Stage::RefreshingAccessToken) {
|
||||||
(*parameters)["scope"] = scope;
|
(*parameters)["scope"] = scope;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
#else
|
#else
|
||||||
oauth2.setModifyParametersFunction([&](QAbstractOAuth::Stage stage, QMultiMap<QString, QVariant>* parameters) {
|
m_oauth2.setModifyParametersFunction([&](QAbstractOAuth::Stage stage, QMultiMap<QString, QVariant>* parameters) {
|
||||||
if (stage == QAbstractOAuth::Stage::RefreshingAccessToken) {
|
if (stage == QAbstractOAuth::Stage::RefreshingAccessToken) {
|
||||||
(*parameters).insert("scope", scope);
|
(*parameters).insert("scope", scope);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
oauth2.setClientIdentifier(m_clientId);
|
m_oauth2.setClientIdentifier(m_clientId);
|
||||||
oauth2.setNetworkAccessManager(APPLICATION->network().get());
|
m_oauth2.setNetworkAccessManager(APPLICATION->network().get());
|
||||||
|
|
||||||
connect(&oauth2, &QOAuth2AuthorizationCodeFlow::granted, this, [this] {
|
connect(&m_oauth2, &QOAuth2AuthorizationCodeFlow::granted, this, [this] {
|
||||||
m_data->msaClientID = oauth2.clientIdentifier();
|
m_data->msaClientID = m_oauth2.clientIdentifier();
|
||||||
m_data->msaToken.issueInstant = QDateTime::currentDateTimeUtc();
|
m_data->msaToken.issueInstant = QDateTime::currentDateTimeUtc();
|
||||||
m_data->msaToken.notAfter = oauth2.expirationAt();
|
m_data->msaToken.notAfter = m_oauth2.expirationAt();
|
||||||
m_data->msaToken.extra = oauth2.extraTokens();
|
m_data->msaToken.extra = m_oauth2.extraTokens();
|
||||||
m_data->msaToken.refresh_token = oauth2.refreshToken();
|
m_data->msaToken.refresh_token = m_oauth2.refreshToken();
|
||||||
m_data->msaToken.token = oauth2.token();
|
m_data->msaToken.token = m_oauth2.token();
|
||||||
emit finished(AccountTaskState::STATE_WORKING, tr("Got "));
|
emit finished(AccountTaskState::STATE_WORKING, tr("Got "));
|
||||||
});
|
});
|
||||||
connect(&oauth2, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, this, &MSAStep::authorizeWithBrowser);
|
connect(&m_oauth2, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, this, &MSAStep::authorizeWithBrowser);
|
||||||
connect(&oauth2, &QOAuth2AuthorizationCodeFlow::requestFailed, this, [this, silent](const QAbstractOAuth2::Error err) {
|
connect(&m_oauth2, &QOAuth2AuthorizationCodeFlow::requestFailed, this, [this, silent](const QAbstractOAuth2::Error err) {
|
||||||
auto state = AccountTaskState::STATE_FAILED_HARD;
|
auto state = AccountTaskState::STATE_FAILED_HARD;
|
||||||
if (oauth2.status() == QAbstractOAuth::Status::Granted || silent) {
|
if (m_oauth2.status() == QAbstractOAuth::Status::Granted || silent) {
|
||||||
if (err == QAbstractOAuth2::Error::NetworkError) {
|
if (err == QAbstractOAuth2::Error::NetworkError) {
|
||||||
state = AccountTaskState::STATE_OFFLINE;
|
state = AccountTaskState::STATE_OFFLINE;
|
||||||
} else {
|
} else {
|
||||||
@ -150,16 +150,16 @@ MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(sile
|
|||||||
qWarning() << message;
|
qWarning() << message;
|
||||||
emit finished(state, message);
|
emit finished(state, message);
|
||||||
});
|
});
|
||||||
connect(&oauth2, &QOAuth2AuthorizationCodeFlow::error, this,
|
connect(&m_oauth2, &QOAuth2AuthorizationCodeFlow::error, this,
|
||||||
[this](const QString& error, const QString& errorDescription, const QUrl& uri) {
|
[this](const QString& error, const QString& errorDescription, const QUrl& uri) {
|
||||||
qWarning() << "Failed to login because" << error << errorDescription;
|
qWarning() << "Failed to login because" << error << errorDescription;
|
||||||
emit finished(AccountTaskState::STATE_FAILED_HARD, errorDescription);
|
emit finished(AccountTaskState::STATE_FAILED_HARD, errorDescription);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(&oauth2, &QOAuth2AuthorizationCodeFlow::extraTokensChanged, this,
|
connect(&m_oauth2, &QOAuth2AuthorizationCodeFlow::extraTokensChanged, this,
|
||||||
[this](const QVariantMap& tokens) { m_data->msaToken.extra = tokens; });
|
[this](const QVariantMap& tokens) { m_data->msaToken.extra = tokens; });
|
||||||
|
|
||||||
connect(&oauth2, &QOAuth2AuthorizationCodeFlow::clientIdentifierChanged, this,
|
connect(&m_oauth2, &QOAuth2AuthorizationCodeFlow::clientIdentifierChanged, this,
|
||||||
[this](const QString& clientIdentifier) { m_data->msaClientID = clientIdentifier; });
|
[this](const QString& clientIdentifier) { m_data->msaClientID = clientIdentifier; });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,20 +180,20 @@ void MSAStep::perform()
|
|||||||
emit finished(AccountTaskState::STATE_DISABLED, tr("Microsoft user authentication failed - refresh token is empty."));
|
emit finished(AccountTaskState::STATE_DISABLED, tr("Microsoft user authentication failed - refresh token is empty."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
oauth2.setRefreshToken(m_data->msaToken.refresh_token);
|
m_oauth2.setRefreshToken(m_data->msaToken.refresh_token);
|
||||||
oauth2.refreshAccessToken();
|
m_oauth2.refreshAccessToken();
|
||||||
} else {
|
} else {
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) // QMultiMap param changed in 6.0
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) // QMultiMap param changed in 6.0
|
||||||
oauth2.setModifyParametersFunction(
|
m_oauth2.setModifyParametersFunction(
|
||||||
[](QAbstractOAuth::Stage stage, QMultiMap<QString, QVariant>* map) { map->insert("prompt", "select_account"); });
|
[](QAbstractOAuth::Stage stage, QMultiMap<QString, QVariant>* map) { map->insert("prompt", "select_account"); });
|
||||||
#else
|
#else
|
||||||
oauth2.setModifyParametersFunction(
|
m_oauth2.setModifyParametersFunction(
|
||||||
[](QAbstractOAuth::Stage stage, QMap<QString, QVariant>* map) { map->insert("prompt", "select_account"); });
|
[](QAbstractOAuth::Stage stage, QMap<QString, QVariant>* map) { map->insert("prompt", "select_account"); });
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
*m_data = AccountData();
|
*m_data = AccountData();
|
||||||
m_data->msaClientID = m_clientId;
|
m_data->msaClientID = m_clientId;
|
||||||
oauth2.grant();
|
m_oauth2.grant();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,5 +55,5 @@ class MSAStep : public AuthStep {
|
|||||||
private:
|
private:
|
||||||
bool m_silent;
|
bool m_silent;
|
||||||
QString m_clientId;
|
QString m_clientId;
|
||||||
QOAuth2AuthorizationCodeFlow oauth2;
|
QOAuth2AuthorizationCodeFlow m_oauth2;
|
||||||
};
|
};
|
||||||
|
@ -132,6 +132,7 @@ void LauncherPartLaunch::executeTask()
|
|||||||
|
|
||||||
QString wrapperCommandStr = instance->getWrapperCommand().trimmed();
|
QString wrapperCommandStr = instance->getWrapperCommand().trimmed();
|
||||||
if (!wrapperCommandStr.isEmpty()) {
|
if (!wrapperCommandStr.isEmpty()) {
|
||||||
|
wrapperCommandStr = m_parent->substituteVariables(wrapperCommandStr);
|
||||||
auto wrapperArgs = Commandline::splitArgs(wrapperCommandStr);
|
auto wrapperArgs = Commandline::splitArgs(wrapperCommandStr);
|
||||||
auto wrapperCommand = wrapperArgs.takeFirst();
|
auto wrapperCommand = wrapperArgs.takeFirst();
|
||||||
auto realWrapperCommand = QStandardPaths::findExecutable(wrapperCommand);
|
auto realWrapperCommand = QStandardPaths::findExecutable(wrapperCommand);
|
||||||
@ -171,6 +172,7 @@ void LauncherPartLaunch::on_state(LoggedProcess::State state)
|
|||||||
case LoggedProcess::Aborted:
|
case LoggedProcess::Aborted:
|
||||||
case LoggedProcess::Crashed: {
|
case LoggedProcess::Crashed: {
|
||||||
m_parent->setPid(-1);
|
m_parent->setPid(-1);
|
||||||
|
m_parent->instance()->setMinecraftRunning(false);
|
||||||
emitFailed(tr("Game crashed."));
|
emitFailed(tr("Game crashed."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -293,13 +293,13 @@ ModDetails ReadFabricModInfo(QByteArray contents)
|
|||||||
// https://github.com/QuiltMC/rfcs/blob/master/specification/0002-quilt.mod.json.md
|
// https://github.com/QuiltMC/rfcs/blob/master/specification/0002-quilt.mod.json.md
|
||||||
ModDetails ReadQuiltModInfo(QByteArray contents)
|
ModDetails ReadQuiltModInfo(QByteArray contents)
|
||||||
{
|
{
|
||||||
|
ModDetails details;
|
||||||
|
try {
|
||||||
QJsonParseError jsonError;
|
QJsonParseError jsonError;
|
||||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
|
||||||
auto object = Json::requireObject(jsonDoc, "quilt.mod.json");
|
auto object = Json::requireObject(jsonDoc, "quilt.mod.json");
|
||||||
auto schemaVersion = Json::ensureInteger(object.value("schema_version"), 0, "Quilt schema_version");
|
auto schemaVersion = Json::ensureInteger(object.value("schema_version"), 0, "Quilt schema_version");
|
||||||
|
|
||||||
ModDetails details;
|
|
||||||
|
|
||||||
// https://github.com/QuiltMC/rfcs/blob/be6ba280d785395fefa90a43db48e5bfc1d15eb4/specification/0002-quilt.mod.json.md
|
// https://github.com/QuiltMC/rfcs/blob/be6ba280d785395fefa90a43db48e5bfc1d15eb4/specification/0002-quilt.mod.json.md
|
||||||
if (schemaVersion == 1) {
|
if (schemaVersion == 1) {
|
||||||
auto modInfo = Json::requireObject(object.value("quilt_loader"), "Quilt mod info");
|
auto modInfo = Json::requireObject(object.value("quilt_loader"), "Quilt mod info");
|
||||||
@ -342,8 +342,8 @@ ModDetails ReadQuiltModInfo(QByteArray contents)
|
|||||||
details.licenses.append(ModLicense(license.toString()));
|
details.licenses.append(ModLicense(license.toString()));
|
||||||
} else if (license.isObject()) {
|
} else if (license.isObject()) {
|
||||||
auto obj = license.toObject();
|
auto obj = license.toObject();
|
||||||
details.licenses.append(ModLicense(obj.value("name").toString(), obj.value("id").toString(), obj.value("url").toString(),
|
details.licenses.append(ModLicense(obj.value("name").toString(), obj.value("id").toString(),
|
||||||
obj.value("description").toString()));
|
obj.value("url").toString(), obj.value("description").toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,6 +374,10 @@ ModDetails ReadQuiltModInfo(QByteArray contents)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} catch (const Exception& e) {
|
||||||
|
qWarning() << "Unable to parse mod info:" << e.cause();
|
||||||
|
}
|
||||||
return details;
|
return details;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ class ResourceAPI {
|
|||||||
std::optional<QString> search;
|
std::optional<QString> search;
|
||||||
std::optional<SortingMethod> sorting;
|
std::optional<SortingMethod> sorting;
|
||||||
std::optional<ModPlatform::ModLoaderTypes> loaders;
|
std::optional<ModPlatform::ModLoaderTypes> loaders;
|
||||||
std::optional<std::list<Version> > versions;
|
std::optional<std::list<Version>> versions;
|
||||||
std::optional<QString> side;
|
std::optional<QString> side;
|
||||||
std::optional<QStringList> categoryIds;
|
std::optional<QStringList> categoryIds;
|
||||||
};
|
};
|
||||||
@ -168,11 +168,23 @@ class ResourceAPI {
|
|||||||
protected:
|
protected:
|
||||||
[[nodiscard]] inline QString debugName() const { return "External resource API"; }
|
[[nodiscard]] inline QString debugName() const { return "External resource API"; }
|
||||||
|
|
||||||
[[nodiscard]] inline auto getGameVersionsString(std::list<Version> mcVersions) const -> QString
|
[[nodiscard]] inline QString mapMCVersionToModrinth(Version v) const
|
||||||
|
{
|
||||||
|
static const QString preString = " Pre-Release ";
|
||||||
|
auto verStr = v.toString();
|
||||||
|
|
||||||
|
if (verStr.contains(preString)) {
|
||||||
|
verStr.replace(preString, "-pre");
|
||||||
|
}
|
||||||
|
verStr.replace(" ", "-");
|
||||||
|
return verStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline QString getGameVersionsString(std::list<Version> mcVersions) const
|
||||||
{
|
{
|
||||||
QString s;
|
QString s;
|
||||||
for (auto& ver : mcVersions) {
|
for (auto& ver : mcVersions) {
|
||||||
s += QString("\"%1\",").arg(ver.toString());
|
s += QString("\"%1\",").arg(mapMCVersionToModrinth(ver));
|
||||||
}
|
}
|
||||||
s.remove(s.length() - 1, 1); // remove last comma
|
s.remove(s.length() - 1, 1); // remove last comma
|
||||||
return s;
|
return s;
|
||||||
|
@ -112,6 +112,8 @@ void Flame::FileResolvingTask::netJobFinished()
|
|||||||
auto obj = Json::requireObject(file);
|
auto obj = Json::requireObject(file);
|
||||||
auto version = FlameMod::loadIndexedPackVersion(obj);
|
auto version = FlameMod::loadIndexedPackVersion(obj);
|
||||||
auto fileid = version.fileId.toInt();
|
auto fileid = version.fileId.toInt();
|
||||||
|
Q_ASSERT(fileid != 0);
|
||||||
|
Q_ASSERT(m_manifest.files.contains(fileid));
|
||||||
m_manifest.files[fileid].version = version;
|
m_manifest.files[fileid].version = version;
|
||||||
auto url = QUrl(version.downloadUrl, QUrl::TolerantMode);
|
auto url = QUrl(version.downloadUrl, QUrl::TolerantMode);
|
||||||
if (!url.isValid() && "sha1" == version.hash_type && !version.hash.isEmpty()) {
|
if (!url.isValid() && "sha1" == version.hash_type && !version.hash.isEmpty()) {
|
||||||
|
@ -270,6 +270,7 @@ std::optional<ModPlatform::IndexedVersion> FlameAPI::getLatestVersion(QList<ModP
|
|||||||
QList<ModPlatform::ModLoaderType> instanceLoaders,
|
QList<ModPlatform::ModLoaderType> instanceLoaders,
|
||||||
ModPlatform::ModLoaderTypes modLoaders)
|
ModPlatform::ModLoaderTypes modLoaders)
|
||||||
{
|
{
|
||||||
|
static const auto noLoader = ModPlatform::ModLoaderType(0);
|
||||||
QHash<ModPlatform::ModLoaderType, ModPlatform::IndexedVersion> bestMatch;
|
QHash<ModPlatform::ModLoaderType, ModPlatform::IndexedVersion> bestMatch;
|
||||||
auto checkVersion = [&bestMatch](const ModPlatform::IndexedVersion& version, const ModPlatform::ModLoaderType& loader) {
|
auto checkVersion = [&bestMatch](const ModPlatform::IndexedVersion& version, const ModPlatform::ModLoaderType& loader) {
|
||||||
if (bestMatch.contains(loader)) {
|
if (bestMatch.contains(loader)) {
|
||||||
@ -284,7 +285,7 @@ std::optional<ModPlatform::IndexedVersion> FlameAPI::getLatestVersion(QList<ModP
|
|||||||
for (auto file_tmp : versions) {
|
for (auto file_tmp : versions) {
|
||||||
auto loaders = ModPlatform::modLoaderTypesToList(file_tmp.loaders);
|
auto loaders = ModPlatform::modLoaderTypesToList(file_tmp.loaders);
|
||||||
if (loaders.isEmpty()) {
|
if (loaders.isEmpty()) {
|
||||||
checkVersion(file_tmp, ModPlatform::ModLoaderType(0));
|
checkVersion(file_tmp, noLoader);
|
||||||
} else {
|
} else {
|
||||||
for (auto loader : loaders) {
|
for (auto loader : loaders) {
|
||||||
checkVersion(file_tmp, loader);
|
checkVersion(file_tmp, loader);
|
||||||
@ -293,11 +294,19 @@ std::optional<ModPlatform::IndexedVersion> FlameAPI::getLatestVersion(QList<ModP
|
|||||||
}
|
}
|
||||||
// edge case: mod has installed for forge but the instance is fabric => fabric version will be prioritizated on update
|
// edge case: mod has installed for forge but the instance is fabric => fabric version will be prioritizated on update
|
||||||
auto currentLoaders = instanceLoaders + ModPlatform::modLoaderTypesToList(modLoaders);
|
auto currentLoaders = instanceLoaders + ModPlatform::modLoaderTypesToList(modLoaders);
|
||||||
currentLoaders.append(ModPlatform::ModLoaderType(0)); // add a fallback in case the versions do not define a loader
|
currentLoaders.append(noLoader); // add a fallback in case the versions do not define a loader
|
||||||
|
|
||||||
for (auto loader : currentLoaders) {
|
for (auto loader : currentLoaders) {
|
||||||
if (bestMatch.contains(loader)) {
|
if (bestMatch.contains(loader)) {
|
||||||
return bestMatch.value(loader);
|
auto bestForLoader = bestMatch.value(loader);
|
||||||
|
// awkward case where the mod has only two loaders and one of them is not specified
|
||||||
|
if (loader != noLoader && bestMatch.contains(noLoader) && bestMatch.size() == 2) {
|
||||||
|
auto bestForNoLoader = bestMatch.value(noLoader);
|
||||||
|
if (bestForNoLoader.date > bestForLoader.date) {
|
||||||
|
return bestForNoLoader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestForLoader;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
|
@ -105,9 +105,6 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
|||||||
auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> ModPlatform::IndexedVersion
|
auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> ModPlatform::IndexedVersion
|
||||||
{
|
{
|
||||||
auto versionArray = Json::requireArray(obj, "gameVersions");
|
auto versionArray = Json::requireArray(obj, "gameVersions");
|
||||||
if (versionArray.isEmpty()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
ModPlatform::IndexedVersion file;
|
ModPlatform::IndexedVersion file;
|
||||||
for (auto mcVer : versionArray) {
|
for (auto mcVer : versionArray) {
|
||||||
|
@ -45,7 +45,7 @@ static void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest)
|
|||||||
|
|
||||||
Flame::File file;
|
Flame::File file;
|
||||||
loadFileV1(file, obj);
|
loadFileV1(file, obj);
|
||||||
|
Q_ASSERT(file.projectId != 0);
|
||||||
pack.files.insert(file.fileId, file);
|
pack.files.insert(file.fileId, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ Task::Ptr ModrinthAPI::latestVersion(QString hash,
|
|||||||
if (mcVersions.has_value()) {
|
if (mcVersions.has_value()) {
|
||||||
QStringList game_versions;
|
QStringList game_versions;
|
||||||
for (auto& ver : mcVersions.value()) {
|
for (auto& ver : mcVersions.value()) {
|
||||||
game_versions.append(ver.toString());
|
game_versions.append(mapMCVersionToModrinth(ver));
|
||||||
}
|
}
|
||||||
Json::writeStringList(body_obj, "game_versions", game_versions);
|
Json::writeStringList(body_obj, "game_versions", game_versions);
|
||||||
}
|
}
|
||||||
@ -87,7 +87,7 @@ Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes,
|
|||||||
if (mcVersions.has_value()) {
|
if (mcVersions.has_value()) {
|
||||||
QStringList game_versions;
|
QStringList game_versions;
|
||||||
for (auto& ver : mcVersions.value()) {
|
for (auto& ver : mcVersions.value()) {
|
||||||
game_versions.append(ver.toString());
|
game_versions.append(mapMCVersionToModrinth(ver));
|
||||||
}
|
}
|
||||||
Json::writeStringList(body_obj, "game_versions", game_versions);
|
Json::writeStringList(body_obj, "game_versions", game_versions);
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,21 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static inline QString mapMCVersionFromModrinth(QString v)
|
||||||
|
{
|
||||||
|
static const QString preString = " Pre-Release ";
|
||||||
|
bool pre = false;
|
||||||
|
if (v.contains("-pre")) {
|
||||||
|
pre = true;
|
||||||
|
v.replace("-pre", preString);
|
||||||
|
}
|
||||||
|
v.replace("-", " ");
|
||||||
|
if (pre) {
|
||||||
|
v.replace(" Pre Release ", preString);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] static QString resourceTypeParameter(ModPlatform::ResourceType type)
|
[[nodiscard]] static QString resourceTypeParameter(ModPlatform::ResourceType type)
|
||||||
{
|
{
|
||||||
@ -170,7 +185,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
{
|
{
|
||||||
QString s;
|
QString s;
|
||||||
for (auto& ver : mcVersions) {
|
for (auto& ver : mcVersions) {
|
||||||
s += QString("\"versions:%1\",").arg(ver.toString());
|
s += QString("\"versions:%1\",").arg(mapMCVersionToModrinth(ver));
|
||||||
}
|
}
|
||||||
s.remove(s.length() - 1, 1); // remove last comma
|
s.remove(s.length() - 1, 1); // remove last comma
|
||||||
return s.isEmpty() ? QString() : s;
|
return s.isEmpty() ? QString() : s;
|
||||||
@ -187,7 +202,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
: QString("%1/project/%2/version?game_versions=[\"%3\"]&loaders=[\"%4\"]")
|
: QString("%1/project/%2/version?game_versions=[\"%3\"]&loaders=[\"%4\"]")
|
||||||
.arg(BuildConfig.MODRINTH_PROD_URL)
|
.arg(BuildConfig.MODRINTH_PROD_URL)
|
||||||
.arg(args.dependency.addonId.toString())
|
.arg(args.dependency.addonId.toString())
|
||||||
.arg(args.mcVersion.toString())
|
.arg(mapMCVersionToModrinth(args.mcVersion))
|
||||||
.arg(getModLoaderStrings(args.loader).join("\",\""));
|
.arg(getModLoaderStrings(args.loader).join("\",\""));
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -131,9 +131,7 @@ void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArra
|
|||||||
pack.versionsLoaded = true;
|
pack.versionsLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Modrinth::loadIndexedPackVersion(QJsonObject& obj,
|
ModPlatform::IndexedVersion Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_type, QString preferred_file_name)
|
||||||
QString preferred_hash_type,
|
|
||||||
QString preferred_file_name) -> ModPlatform::IndexedVersion
|
|
||||||
{
|
{
|
||||||
ModPlatform::IndexedVersion file;
|
ModPlatform::IndexedVersion file;
|
||||||
|
|
||||||
@ -145,7 +143,7 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj,
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
for (auto mcVer : versionArray) {
|
for (auto mcVer : versionArray) {
|
||||||
file.mcVersion.append(mcVer.toString());
|
file.mcVersion.append(ModrinthAPI::mapMCVersionFromModrinth(mcVer.toString()));
|
||||||
}
|
}
|
||||||
auto loaders = Json::requireArray(obj, "loaders");
|
auto loaders = Json::requireArray(obj, "loaders");
|
||||||
for (auto loader : loaders) {
|
for (auto loader : loaders) {
|
||||||
@ -247,9 +245,9 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj,
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Modrinth::loadDependencyVersions([[maybe_unused]] const ModPlatform::Dependency& m,
|
ModPlatform::IndexedVersion Modrinth::loadDependencyVersions([[maybe_unused]] const ModPlatform::Dependency& m,
|
||||||
QJsonArray& arr,
|
QJsonArray& arr,
|
||||||
const BaseInstance* inst) -> ModPlatform::IndexedVersion
|
const BaseInstance* inst)
|
||||||
{
|
{
|
||||||
auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile();
|
auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile();
|
||||||
QString mcVersion = profile->getComponentVersion("net.minecraft");
|
QString mcVersion = profile->getComponentVersion("net.minecraft");
|
||||||
|
@ -40,9 +40,6 @@
|
|||||||
|
|
||||||
#include "modplatform/modrinth/ModrinthAPI.h"
|
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||||
|
|
||||||
#include "minecraft/MinecraftInstance.h"
|
|
||||||
#include "minecraft/PackProfile.h"
|
|
||||||
|
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
|
|
||||||
static ModrinthAPI api;
|
static ModrinthAPI api;
|
||||||
@ -134,6 +131,7 @@ auto loadIndexedVersion(QJsonObject& obj) -> ModpackVersion
|
|||||||
auto gameVersions = Json::ensureArray(obj, "game_versions");
|
auto gameVersions = Json::ensureArray(obj, "game_versions");
|
||||||
if (!gameVersions.isEmpty()) {
|
if (!gameVersions.isEmpty()) {
|
||||||
file.gameVersion = Json::ensureString(gameVersions[0]);
|
file.gameVersion = Json::ensureString(gameVersions[0]);
|
||||||
|
file.gameVersion = ModrinthAPI::mapMCVersionFromModrinth(file.gameVersion);
|
||||||
}
|
}
|
||||||
auto loaders = Json::requireArray(obj, "loaders");
|
auto loaders = Json::requireArray(obj, "loaders");
|
||||||
for (auto loader : loaders) {
|
for (auto loader : loaders) {
|
||||||
|
@ -54,7 +54,7 @@ Task::State FileSink::init(QNetworkRequest& request)
|
|||||||
return Task::State::Failed;
|
return Task::State::Failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
wroteAnyData = false;
|
m_wroteAnyData = false;
|
||||||
m_output_file.reset(new PSaveFile(m_filename));
|
m_output_file.reset(new PSaveFile(m_filename));
|
||||||
if (!m_output_file->open(QIODevice::WriteOnly)) {
|
if (!m_output_file->open(QIODevice::WriteOnly)) {
|
||||||
qCCritical(taskNetLogC) << "Could not open " + m_filename + " for writing";
|
qCCritical(taskNetLogC) << "Could not open " + m_filename + " for writing";
|
||||||
@ -72,17 +72,19 @@ Task::State FileSink::write(QByteArray& data)
|
|||||||
qCCritical(taskNetLogC) << "Failed writing into " + m_filename;
|
qCCritical(taskNetLogC) << "Failed writing into " + m_filename;
|
||||||
m_output_file->cancelWriting();
|
m_output_file->cancelWriting();
|
||||||
m_output_file.reset();
|
m_output_file.reset();
|
||||||
wroteAnyData = false;
|
m_wroteAnyData = false;
|
||||||
return Task::State::Failed;
|
return Task::State::Failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
wroteAnyData = true;
|
m_wroteAnyData = true;
|
||||||
return Task::State::Running;
|
return Task::State::Running;
|
||||||
}
|
}
|
||||||
|
|
||||||
Task::State FileSink::abort()
|
Task::State FileSink::abort()
|
||||||
{
|
{
|
||||||
|
if (m_output_file) {
|
||||||
m_output_file->cancelWriting();
|
m_output_file->cancelWriting();
|
||||||
|
}
|
||||||
failAllValidators();
|
failAllValidators();
|
||||||
return Task::State::Failed;
|
return Task::State::Failed;
|
||||||
}
|
}
|
||||||
@ -100,7 +102,7 @@ Task::State FileSink::finalize(QNetworkReply& reply)
|
|||||||
|
|
||||||
// if we wrote any data to the save file, we try to commit the data to the real file.
|
// if we wrote any data to the save file, we try to commit the data to the real file.
|
||||||
// if it actually got a proper file, we write it even if it was empty
|
// if it actually got a proper file, we write it even if it was empty
|
||||||
if (gotFile || wroteAnyData) {
|
if (gotFile || m_wroteAnyData) {
|
||||||
// ask validators for data consistency
|
// ask validators for data consistency
|
||||||
// we only do this for actual downloads, not 'your data is still the same' cache hits
|
// we only do this for actual downloads, not 'your data is still the same' cache hits
|
||||||
if (!finalizeAllValidators(reply))
|
if (!finalizeAllValidators(reply))
|
||||||
|
@ -58,7 +58,7 @@ class FileSink : public Sink {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString m_filename;
|
QString m_filename;
|
||||||
bool wroteAnyData = false;
|
bool m_wroteAnyData = false;
|
||||||
std::unique_ptr<PSaveFile> m_output_file;
|
std::unique_ptr<PSaveFile> m_output_file;
|
||||||
};
|
};
|
||||||
} // namespace Net
|
} // namespace Net
|
||||||
|
@ -78,7 +78,7 @@ Task::State MetaCacheSink::finalizeCache(QNetworkReply& reply)
|
|||||||
{
|
{
|
||||||
QFileInfo output_file_info(m_filename);
|
QFileInfo output_file_info(m_filename);
|
||||||
|
|
||||||
if (wroteAnyData) {
|
if (m_wroteAnyData) {
|
||||||
m_entry->setMD5Sum(m_md5Node->hash().toHex().constData());
|
m_entry->setMD5Sum(m_md5Node->hash().toHex().constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ void NetRequest::executeTask()
|
|||||||
emit finished();
|
emit finished();
|
||||||
return;
|
return;
|
||||||
case State::Running:
|
case State::Running:
|
||||||
qCDebug(logCat) << getUid().toString() << "Runninng " << m_url.toString();
|
qCDebug(logCat) << getUid().toString() << "Running " << m_url.toString();
|
||||||
break;
|
break;
|
||||||
case State::Inactive:
|
case State::Inactive:
|
||||||
case State::Failed:
|
case State::Failed:
|
||||||
|
@ -52,6 +52,30 @@
|
|||||||
#include <settings/SettingsObject.h>
|
#include <settings/SettingsObject.h>
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
|
|
||||||
|
constexpr int MaxMclogsLines = 25000;
|
||||||
|
constexpr int InitialMclogsLines = 10000;
|
||||||
|
constexpr int FinalMclogsLines = 14900;
|
||||||
|
|
||||||
|
QString truncateLogForMclogs(const QString& logContent)
|
||||||
|
{
|
||||||
|
QStringList lines = logContent.split("\n");
|
||||||
|
if (lines.size() > MaxMclogsLines) {
|
||||||
|
QString truncatedLog = lines.mid(0, InitialMclogsLines).join("\n");
|
||||||
|
truncatedLog +=
|
||||||
|
"\n\n\n\n\n\n\n\n\n\n"
|
||||||
|
"------------------------------------------------------------\n"
|
||||||
|
"----------------------- Log truncated ----------------------\n"
|
||||||
|
"------------------------------------------------------------\n"
|
||||||
|
"----- Middle portion omitted to fit mclo.gs size limits ----\n"
|
||||||
|
"------------------------------------------------------------\n"
|
||||||
|
"\n\n\n\n\n\n\n\n\n\n";
|
||||||
|
truncatedLog += lines.mid(lines.size() - FinalMclogsLines - 1).join("\n");
|
||||||
|
return truncatedLog;
|
||||||
|
}
|
||||||
|
return logContent;
|
||||||
|
}
|
||||||
|
|
||||||
QString GuiUtil::fetchFlameKey(QWidget* parentWidget)
|
QString GuiUtil::fetchFlameKey(QWidget* parentWidget)
|
||||||
{
|
{
|
||||||
ProgressDialog prog(parentWidget);
|
ProgressDialog prog(parentWidget);
|
||||||
@ -78,6 +102,7 @@ std::optional<QString> GuiUtil::uploadPaste(const QString& name, const QString&
|
|||||||
ProgressDialog dialog(parentWidget);
|
ProgressDialog dialog(parentWidget);
|
||||||
auto pasteTypeSetting = static_cast<PasteUpload::PasteType>(APPLICATION->settings()->get("PastebinType").toInt());
|
auto pasteTypeSetting = static_cast<PasteUpload::PasteType>(APPLICATION->settings()->get("PastebinType").toInt());
|
||||||
auto pasteCustomAPIBaseSetting = APPLICATION->settings()->get("PastebinCustomAPIBase").toString();
|
auto pasteCustomAPIBaseSetting = APPLICATION->settings()->get("PastebinCustomAPIBase").toString();
|
||||||
|
bool shouldTruncate = false;
|
||||||
|
|
||||||
{
|
{
|
||||||
QUrl baseUrl;
|
QUrl baseUrl;
|
||||||
@ -97,10 +122,36 @@ std::optional<QString> GuiUtil::uploadPaste(const QString& name, const QString&
|
|||||||
|
|
||||||
if (response != QMessageBox::Yes)
|
if (response != QMessageBox::Yes)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
if (baseUrl.toString() == "https://api.mclo.gs" && text.count("\n") > MaxMclogsLines) {
|
||||||
|
auto truncateResponse = CustomMessageBox::selectable(
|
||||||
|
parentWidget, QObject::tr("Confirm Truncation"),
|
||||||
|
QObject::tr("The log has %1 lines, exceeding mclo.gs' limit of %2.\n"
|
||||||
|
"The launcher can keep the first %3 and last %4 lines, trimming the middle.\n\n"
|
||||||
|
"If you choose 'No', mclo.gs will only keep the first %2 lines, cutting off "
|
||||||
|
"potentially useful info like crashes at the end.\n\n"
|
||||||
|
"Proceed with truncation?")
|
||||||
|
.arg(text.count("\n"))
|
||||||
|
.arg(MaxMclogsLines)
|
||||||
|
.arg(InitialMclogsLines)
|
||||||
|
.arg(FinalMclogsLines),
|
||||||
|
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::No)
|
||||||
|
->exec();
|
||||||
|
|
||||||
|
if (truncateResponse == QMessageBox::Cancel) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
shouldTruncate = truncateResponse == QMessageBox::Yes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<PasteUpload> paste(new PasteUpload(parentWidget, text, pasteCustomAPIBaseSetting, pasteTypeSetting));
|
QString textToUpload = text;
|
||||||
|
if (shouldTruncate) {
|
||||||
|
textToUpload = truncateLogForMclogs(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<PasteUpload> paste(new PasteUpload(parentWidget, textToUpload, pasteCustomAPIBaseSetting, pasteTypeSetting));
|
||||||
|
|
||||||
dialog.execWithTask(paste.get());
|
dialog.execWithTask(paste.get());
|
||||||
if (!paste->wasSuccessful()) {
|
if (!paste->wasSuccessful()) {
|
||||||
|
@ -979,6 +979,14 @@ void MainWindow::processURLs(QList<QUrl> urls)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (APPLICATION->instances()->count() <= 0) {
|
||||||
|
CustomMessageBox::selectable(this, tr("No instance!"),
|
||||||
|
tr("No instance available to add the resource to.\nPlease create a new instance before "
|
||||||
|
"attempting to install this resource again."),
|
||||||
|
QMessageBox::Critical)
|
||||||
|
->show();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
ImportResourceDialog dlg(localFileName, type, this);
|
ImportResourceDialog dlg(localFileName, type, this);
|
||||||
|
|
||||||
if (dlg.exec() != QDialog::Accepted)
|
if (dlg.exec() != QDialog::Accepted)
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "EditAccountDialog.h"
|
|
||||||
#include <DesktopServices.h>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QUrl>
|
|
||||||
#include "ui_EditAccountDialog.h"
|
|
||||||
|
|
||||||
EditAccountDialog::EditAccountDialog(const QString& text, QWidget* parent, int flags) : QDialog(parent), ui(new Ui::EditAccountDialog)
|
|
||||||
{
|
|
||||||
ui->setupUi(this);
|
|
||||||
|
|
||||||
ui->label->setText(text);
|
|
||||||
ui->label->setVisible(!text.isEmpty());
|
|
||||||
|
|
||||||
ui->userTextBox->setEnabled(flags & UsernameField);
|
|
||||||
ui->passTextBox->setEnabled(flags & PasswordField);
|
|
||||||
|
|
||||||
ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
|
|
||||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
|
|
||||||
}
|
|
||||||
|
|
||||||
EditAccountDialog::~EditAccountDialog()
|
|
||||||
{
|
|
||||||
delete ui;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditAccountDialog::on_label_linkActivated(const QString& link)
|
|
||||||
{
|
|
||||||
DesktopServices::openUrl(QUrl(link));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditAccountDialog::setUsername(const QString& user) const
|
|
||||||
{
|
|
||||||
ui->userTextBox->setText(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString EditAccountDialog::username() const
|
|
||||||
{
|
|
||||||
return ui->userTextBox->text();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditAccountDialog::setPassword(const QString& pass) const
|
|
||||||
{
|
|
||||||
ui->passTextBox->setText(pass);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString EditAccountDialog::password() const
|
|
||||||
{
|
|
||||||
return ui->passTextBox->text();
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QDialog>
|
|
||||||
|
|
||||||
namespace Ui {
|
|
||||||
class EditAccountDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
class EditAccountDialog : public QDialog {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit EditAccountDialog(const QString& text = "", QWidget* parent = 0, int flags = UsernameField | PasswordField);
|
|
||||||
~EditAccountDialog();
|
|
||||||
|
|
||||||
void setUsername(const QString& user) const;
|
|
||||||
void setPassword(const QString& pass) const;
|
|
||||||
|
|
||||||
QString username() const;
|
|
||||||
QString password() const;
|
|
||||||
|
|
||||||
enum Flags {
|
|
||||||
NoFlags = 0,
|
|
||||||
|
|
||||||
//! Specifies that the dialog should have a username field.
|
|
||||||
UsernameField,
|
|
||||||
|
|
||||||
//! Specifies that the dialog should have a password field.
|
|
||||||
PasswordField,
|
|
||||||
};
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void on_label_linkActivated(const QString& link);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ui::EditAccountDialog* ui;
|
|
||||||
};
|
|
@ -1,94 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>EditAccountDialog</class>
|
|
||||||
<widget class="QDialog" name="EditAccountDialog">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>400</width>
|
|
||||||
<height>148</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Login</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="placeholderText">
|
|
||||||
<string>Email</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="passTextBox">
|
|
||||||
<property name="echoMode">
|
|
||||||
<enum>QLineEdit::Password</enum>
|
|
||||||
</property>
|
|
||||||
<property name="placeholderText">
|
|
||||||
<string>Password</string>
|
|
||||||
</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>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>accepted()</signal>
|
|
||||||
<receiver>EditAccountDialog</receiver>
|
|
||||||
<slot>accept()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>248</x>
|
|
||||||
<y>254</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>157</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>rejected()</signal>
|
|
||||||
<receiver>EditAccountDialog</receiver>
|
|
||||||
<slot>reject()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>316</x>
|
|
||||||
<y>260</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>286</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
</connections>
|
|
||||||
</ui>
|
|
@ -30,6 +30,9 @@ Choose your name carefully:</string>
|
|||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
<property name="buddy">
|
<property name="buddy">
|
||||||
<cstring>nameEdit</cstring>
|
<cstring>nameEdit</cstring>
|
||||||
</property>
|
</property>
|
||||||
|
@ -207,7 +207,7 @@
|
|||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="label_8">
|
<widget class="QLabel" name="label_8">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string><html><head/><body><p>Note: you only need to set this to access private data. Read the <a href="https://docs.modrinth.com/#section/Authentication">documentation</a> for more information.</p></body></html></string>
|
<string><html><head/><body><p>Note: you only need to set this to access private data. Read the <a href="https://docs.modrinth.com/api/#authentication">documentation</a> for more information.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="openExternalLinks">
|
<property name="openExternalLinks">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
@ -66,7 +66,7 @@ class AccountListPage : public QMainWindow, public BasePage {
|
|||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
QString id() const override { return "accounts"; }
|
QString id() const override { return "accounts"; }
|
||||||
QString helpPage() const override { return "/getting-started/adding-an-account"; }
|
QString helpPage() const override { return "getting-started/adding-an-account"; }
|
||||||
void retranslate() override;
|
void retranslate() override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
@ -93,6 +93,11 @@ InstanceSettingsPage::InstanceSettingsPage(BaseInstance* inst, QWidget* parent)
|
|||||||
ui->serverJoinAddress->setEnabled(true);
|
ui->serverJoinAddress->setEnabled(true);
|
||||||
ui->serverJoinAddressButton->setStyleSheet("QRadioButton::indicator { width: 0px; height: 0px; }");
|
ui->serverJoinAddressButton->setStyleSheet("QRadioButton::indicator { width: 0px; height: 0px; }");
|
||||||
}
|
}
|
||||||
|
connect(ui->javaPathTextBox, &QLineEdit::textChanged, [this](QString newValue) {
|
||||||
|
if (m_instance->settings()->get("JavaPath").toString() != newValue) {
|
||||||
|
m_instance->settings()->set("AutomaticJava", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
|
||||||
|
@ -245,7 +245,6 @@ ModrinthManagedPackPage::ModrinthManagedPackPage(BaseInstance* inst, InstanceWin
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MODRINTH
|
// MODRINTH
|
||||||
|
|
||||||
void ModrinthManagedPackPage::parseManagedPack()
|
void ModrinthManagedPackPage::parseManagedPack()
|
||||||
{
|
{
|
||||||
qDebug() << "Parsing Modrinth pack";
|
qDebug() << "Parsing Modrinth pack";
|
||||||
@ -338,6 +337,25 @@ void ModrinthManagedPackPage::suggestVersion()
|
|||||||
ManagedPackPage::suggestVersion();
|
ManagedPackPage::suggestVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Called when the update task has completed.
|
||||||
|
/// Internally handles the closing of the instance window if the update was successful and shows a message box.
|
||||||
|
/// @param did_succeed Whether the update task was successful.
|
||||||
|
void ManagedPackPage::onUpdateTaskCompleted(bool did_succeed) const
|
||||||
|
{
|
||||||
|
// Close the window if the update was successful
|
||||||
|
if (did_succeed) {
|
||||||
|
if (m_instance_window != nullptr)
|
||||||
|
m_instance_window->close();
|
||||||
|
|
||||||
|
CustomMessageBox::selectable(nullptr, tr("Update Successful"), tr("The instance updated to pack version %1 successfully.").arg(m_inst->getManagedPackVersionName()), QMessageBox::Information)
|
||||||
|
->show();
|
||||||
|
} else {
|
||||||
|
CustomMessageBox::selectable(nullptr, tr("Update Failed"), tr("The instance failed to update to pack version %1. Please check launcher logs for more information.").arg(m_inst->getManagedPackVersionName()), QMessageBox::Critical)
|
||||||
|
->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void ModrinthManagedPackPage::update()
|
void ModrinthManagedPackPage::update()
|
||||||
{
|
{
|
||||||
auto index = ui->versionsComboBox->currentIndex();
|
auto index = ui->versionsComboBox->currentIndex();
|
||||||
@ -363,10 +381,9 @@ void ModrinthManagedPackPage::update()
|
|||||||
extracted->setIcon(m_inst->iconKey());
|
extracted->setIcon(m_inst->iconKey());
|
||||||
extracted->setConfirmUpdate(false);
|
extracted->setConfirmUpdate(false);
|
||||||
|
|
||||||
|
// Run our task then handle the result
|
||||||
auto did_succeed = runUpdateTask(extracted);
|
auto did_succeed = runUpdateTask(extracted);
|
||||||
|
onUpdateTaskCompleted(did_succeed);
|
||||||
if (m_instance_window && did_succeed)
|
|
||||||
m_instance_window->close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModrinthManagedPackPage::updateFromFile()
|
void ModrinthManagedPackPage::updateFromFile()
|
||||||
@ -386,14 +403,12 @@ void ModrinthManagedPackPage::updateFromFile()
|
|||||||
extracted->setIcon(m_inst->iconKey());
|
extracted->setIcon(m_inst->iconKey());
|
||||||
extracted->setConfirmUpdate(false);
|
extracted->setConfirmUpdate(false);
|
||||||
|
|
||||||
|
// Run our task then handle the result
|
||||||
auto did_succeed = runUpdateTask(extracted);
|
auto did_succeed = runUpdateTask(extracted);
|
||||||
|
onUpdateTaskCompleted(did_succeed);
|
||||||
if (m_instance_window && did_succeed)
|
|
||||||
m_instance_window->close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FLAME
|
// FLAME
|
||||||
|
|
||||||
FlameManagedPackPage::FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent)
|
FlameManagedPackPage::FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent)
|
||||||
: ManagedPackPage(inst, instance_window, parent)
|
: ManagedPackPage(inst, instance_window, parent)
|
||||||
{
|
{
|
||||||
@ -531,9 +546,7 @@ void FlameManagedPackPage::update()
|
|||||||
extracted->setConfirmUpdate(false);
|
extracted->setConfirmUpdate(false);
|
||||||
|
|
||||||
auto did_succeed = runUpdateTask(extracted);
|
auto did_succeed = runUpdateTask(extracted);
|
||||||
|
onUpdateTaskCompleted(did_succeed);
|
||||||
if (m_instance_window && did_succeed)
|
|
||||||
m_instance_window->close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlameManagedPackPage::updateFromFile()
|
void FlameManagedPackPage::updateFromFile()
|
||||||
@ -555,8 +568,6 @@ void FlameManagedPackPage::updateFromFile()
|
|||||||
extracted->setConfirmUpdate(false);
|
extracted->setConfirmUpdate(false);
|
||||||
|
|
||||||
auto did_succeed = runUpdateTask(extracted);
|
auto did_succeed = runUpdateTask(extracted);
|
||||||
|
onUpdateTaskCompleted(did_succeed);
|
||||||
if (m_instance_window && did_succeed)
|
|
||||||
m_instance_window->close();
|
|
||||||
}
|
}
|
||||||
#include "ManagedPackPage.moc"
|
#include "ManagedPackPage.moc"
|
||||||
|
@ -94,6 +94,8 @@ class ManagedPackPage : public QWidget, public BasePage {
|
|||||||
BaseInstance* m_inst;
|
BaseInstance* m_inst;
|
||||||
|
|
||||||
bool m_loaded = false;
|
bool m_loaded = false;
|
||||||
|
|
||||||
|
void onUpdateTaskCompleted(bool did_succeed) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Simple page for when we aren't a managed pack. */
|
/** Simple page for when we aren't a managed pack. */
|
||||||
|
@ -252,8 +252,11 @@ void VersionPage::updateButtons(int row)
|
|||||||
bool VersionPage::reloadPackProfile()
|
bool VersionPage::reloadPackProfile()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
m_profile->reload(Net::Mode::Online);
|
auto result = m_profile->reload(Net::Mode::Online);
|
||||||
return true;
|
if (!result) {
|
||||||
|
QMessageBox::critical(this, tr("Error"), result.error);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
} catch (const Exception& e) {
|
} catch (const Exception& e) {
|
||||||
QMessageBox::critical(this, tr("Error"), e.cause());
|
QMessageBox::critical(this, tr("Error"), e.cause());
|
||||||
return false;
|
return false;
|
||||||
|
@ -40,18 +40,29 @@
|
|||||||
#include "HintOverrideProxyStyle.h"
|
#include "HintOverrideProxyStyle.h"
|
||||||
#include "ThemeManager.h"
|
#include "ThemeManager.h"
|
||||||
|
|
||||||
SystemTheme::SystemTheme(const QString& styleName, const QPalette& palette, bool isDefaultTheme)
|
// See https://github.com/MultiMC/Launcher/issues/1790
|
||||||
|
// or https://github.com/PrismLauncher/PrismLauncher/issues/490
|
||||||
|
static const QStringList S_NATIVE_STYLES{ "windows11", "windowsvista", "macos", "system", "windows" };
|
||||||
|
|
||||||
|
SystemTheme::SystemTheme(const QString& styleName, const QPalette& defaultPalette, bool isDefaultTheme)
|
||||||
{
|
{
|
||||||
themeName = isDefaultTheme ? "system" : styleName;
|
m_themeName = isDefaultTheme ? "system" : styleName;
|
||||||
widgetTheme = styleName;
|
m_widgetTheme = styleName;
|
||||||
colorPalette = palette;
|
// NOTE: SystemTheme is reconstructed on page refresh. We can't accurately determine the system palette here
|
||||||
|
// See also S_NATIVE_STYLES comment
|
||||||
|
if (S_NATIVE_STYLES.contains(m_themeName)) {
|
||||||
|
m_colorPalette = defaultPalette;
|
||||||
|
} else {
|
||||||
|
auto style = QStyleFactory::create(styleName);
|
||||||
|
m_colorPalette = style->standardPalette();
|
||||||
|
delete style;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemTheme::apply(bool initial)
|
void SystemTheme::apply(bool initial)
|
||||||
{
|
{
|
||||||
// See https://github.com/MultiMC/Launcher/issues/1790
|
// See S_NATIVE_STYLES comment
|
||||||
// or https://github.com/PrismLauncher/PrismLauncher/issues/490
|
if (initial && S_NATIVE_STYLES.contains(m_themeName)) {
|
||||||
if (initial) {
|
|
||||||
QApplication::setStyle(new HintOverrideProxyStyle(QStyleFactory::create(qtTheme())));
|
QApplication::setStyle(new HintOverrideProxyStyle(QStyleFactory::create(qtTheme())));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -61,35 +72,35 @@ void SystemTheme::apply(bool initial)
|
|||||||
|
|
||||||
QString SystemTheme::id()
|
QString SystemTheme::id()
|
||||||
{
|
{
|
||||||
return themeName;
|
return m_themeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString SystemTheme::name()
|
QString SystemTheme::name()
|
||||||
{
|
{
|
||||||
if (themeName.toLower() == "windowsvista") {
|
if (m_themeName.toLower() == "windowsvista") {
|
||||||
return QObject::tr("Windows Vista");
|
return QObject::tr("Windows Vista");
|
||||||
} else if (themeName.toLower() == "windows") {
|
} else if (m_themeName.toLower() == "windows") {
|
||||||
return QObject::tr("Windows 9x");
|
return QObject::tr("Windows 9x");
|
||||||
} else if (themeName.toLower() == "windows11") {
|
} else if (m_themeName.toLower() == "windows11") {
|
||||||
return QObject::tr("Windows 11");
|
return QObject::tr("Windows 11");
|
||||||
} else if (themeName.toLower() == "system") {
|
} else if (m_themeName.toLower() == "system") {
|
||||||
return QObject::tr("System");
|
return QObject::tr("System");
|
||||||
} else {
|
} else {
|
||||||
return themeName;
|
return m_themeName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString SystemTheme::tooltip()
|
QString SystemTheme::tooltip()
|
||||||
{
|
{
|
||||||
if (themeName.toLower() == "windowsvista") {
|
if (m_themeName.toLower() == "windowsvista") {
|
||||||
return QObject::tr("Widget style trying to look like your win32 theme");
|
return QObject::tr("Widget style trying to look like your win32 theme");
|
||||||
} else if (themeName.toLower() == "windows") {
|
} else if (m_themeName.toLower() == "windows") {
|
||||||
return QObject::tr("Windows 9x inspired widget style");
|
return QObject::tr("Windows 9x inspired widget style");
|
||||||
} else if (themeName.toLower() == "windows11") {
|
} else if (m_themeName.toLower() == "windows11") {
|
||||||
return QObject::tr("WinUI 3 inspired Qt widget style");
|
return QObject::tr("WinUI 3 inspired Qt widget style");
|
||||||
} else if (themeName.toLower() == "fusion") {
|
} else if (m_themeName.toLower() == "fusion") {
|
||||||
return QObject::tr("The default Qt widget style");
|
return QObject::tr("The default Qt widget style");
|
||||||
} else if (themeName.toLower() == "system") {
|
} else if (m_themeName.toLower() == "system") {
|
||||||
return QObject::tr("Your current system theme");
|
return QObject::tr("Your current system theme");
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
@ -98,12 +109,12 @@ QString SystemTheme::tooltip()
|
|||||||
|
|
||||||
QString SystemTheme::qtTheme()
|
QString SystemTheme::qtTheme()
|
||||||
{
|
{
|
||||||
return widgetTheme;
|
return m_widgetTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPalette SystemTheme::colorScheme()
|
QPalette SystemTheme::colorScheme()
|
||||||
{
|
{
|
||||||
return colorPalette;
|
return m_colorPalette;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString SystemTheme::appStyleSheet()
|
QString SystemTheme::appStyleSheet()
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
class SystemTheme : public ITheme {
|
class SystemTheme : public ITheme {
|
||||||
public:
|
public:
|
||||||
SystemTheme(const QString& styleName, const QPalette& palette, bool isDefaultTheme);
|
SystemTheme(const QString& styleName, const QPalette& defaultPalette, bool isDefaultTheme);
|
||||||
virtual ~SystemTheme() {}
|
virtual ~SystemTheme() {}
|
||||||
void apply(bool initial) override;
|
void apply(bool initial) override;
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ class SystemTheme : public ITheme {
|
|||||||
QColor fadeColor() override;
|
QColor fadeColor() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPalette colorPalette;
|
QPalette m_colorPalette;
|
||||||
QString widgetTheme;
|
QString m_widgetTheme;
|
||||||
QString themeName;
|
QString m_themeName;
|
||||||
};
|
};
|
||||||
|
@ -68,8 +68,8 @@ class ThemeManager {
|
|||||||
QDir m_applicationThemeFolder{ "themes" };
|
QDir m_applicationThemeFolder{ "themes" };
|
||||||
QDir m_catPacksFolder{ "catpacks" };
|
QDir m_catPacksFolder{ "catpacks" };
|
||||||
std::map<QString, std::unique_ptr<CatPack>> m_catPacks;
|
std::map<QString, std::unique_ptr<CatPack>> m_catPacks;
|
||||||
QString m_defaultStyle;
|
|
||||||
QPalette m_defaultPalette;
|
QPalette m_defaultPalette;
|
||||||
|
QString m_defaultStyle;
|
||||||
LogColors m_logColors;
|
LogColors m_logColors;
|
||||||
|
|
||||||
void initializeThemes();
|
void initializeThemes();
|
||||||
|
@ -40,7 +40,7 @@ class CheckComboModel : public QIdentityProxyModel {
|
|||||||
{
|
{
|
||||||
if (role == Qt::CheckStateRole) {
|
if (role == Qt::CheckStateRole) {
|
||||||
auto txt = QIdentityProxyModel::data(index, Qt::DisplayRole).toString();
|
auto txt = QIdentityProxyModel::data(index, Qt::DisplayRole).toString();
|
||||||
return checked.contains(txt) ? Qt::Checked : Qt::Unchecked;
|
return m_checked.contains(txt) ? Qt::Checked : Qt::Unchecked;
|
||||||
}
|
}
|
||||||
if (role == Qt::DisplayRole)
|
if (role == Qt::DisplayRole)
|
||||||
return QIdentityProxyModel::data(index, Qt::DisplayRole);
|
return QIdentityProxyModel::data(index, Qt::DisplayRole);
|
||||||
@ -50,10 +50,10 @@ class CheckComboModel : public QIdentityProxyModel {
|
|||||||
{
|
{
|
||||||
if (role == Qt::CheckStateRole) {
|
if (role == Qt::CheckStateRole) {
|
||||||
auto txt = QIdentityProxyModel::data(index, Qt::DisplayRole).toString();
|
auto txt = QIdentityProxyModel::data(index, Qt::DisplayRole).toString();
|
||||||
if (checked.contains(txt)) {
|
if (m_checked.contains(txt)) {
|
||||||
checked.removeOne(txt);
|
m_checked.removeOne(txt);
|
||||||
} else {
|
} else {
|
||||||
checked.push_back(txt);
|
m_checked.push_back(txt);
|
||||||
}
|
}
|
||||||
emit dataChanged(index, index);
|
emit dataChanged(index, index);
|
||||||
emit checkStateChanged();
|
emit checkStateChanged();
|
||||||
@ -61,13 +61,13 @@ class CheckComboModel : public QIdentityProxyModel {
|
|||||||
}
|
}
|
||||||
return QIdentityProxyModel::setData(index, value, role);
|
return QIdentityProxyModel::setData(index, value, role);
|
||||||
}
|
}
|
||||||
QStringList getChecked() { return checked; }
|
QStringList getChecked() { return m_checked; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void checkStateChanged();
|
void checkStateChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QStringList checked;
|
QStringList m_checked;
|
||||||
};
|
};
|
||||||
|
|
||||||
CheckComboBox::CheckComboBox(QWidget* parent) : QComboBox(parent), m_separator(", ")
|
CheckComboBox::CheckComboBox(QWidget* parent) : QComboBox(parent), m_separator(", ")
|
||||||
@ -92,7 +92,7 @@ void CheckComboBox::setSourceModel(QAbstractItemModel* new_model)
|
|||||||
|
|
||||||
void CheckComboBox::hidePopup()
|
void CheckComboBox::hidePopup()
|
||||||
{
|
{
|
||||||
if (!containerMousePress)
|
if (!m_containerMousePress)
|
||||||
QComboBox::hidePopup();
|
QComboBox::hidePopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,7 +138,7 @@ bool CheckComboBox::eventFilter(QObject* receiver, QEvent* event)
|
|||||||
}
|
}
|
||||||
case QEvent::MouseButtonPress: {
|
case QEvent::MouseButtonPress: {
|
||||||
auto ev = static_cast<QMouseEvent*>(event);
|
auto ev = static_cast<QMouseEvent*>(event);
|
||||||
containerMousePress = ev && view()->indexAt(ev->pos()).isValid();
|
m_containerMousePress = ev && view()->indexAt(ev->pos()).isValid();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QEvent::Wheel:
|
case QEvent::Wheel:
|
||||||
|
@ -60,5 +60,5 @@ class CheckComboBox : public QComboBox {
|
|||||||
private:
|
private:
|
||||||
QString m_default_text;
|
QString m_default_text;
|
||||||
QString m_separator;
|
QString m_separator;
|
||||||
bool containerMousePress;
|
bool m_containerMousePress = false;
|
||||||
};
|
};
|
@ -47,6 +47,9 @@
|
|||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
@ -68,6 +71,9 @@
|
|||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
@ -38,9 +38,6 @@ Example:
|
|||||||
# Note that this may break the reproducibility mentioned above, and you might not be able to access the binary cache
|
# Note that this may break the reproducibility mentioned above, and you might not be able to access the binary cache
|
||||||
#
|
#
|
||||||
# inputs.nixpkgs.follows = "nixpkgs";
|
# inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
|
||||||
# This is not required for Flakes
|
|
||||||
inputs.flake-compat.follows = "";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -86,9 +83,6 @@ Example:
|
|||||||
# Note that this may break the reproducibility mentioned above, and you might not be able to access the binary cache
|
# Note that this may break the reproducibility mentioned above, and you might not be able to access the binary cache
|
||||||
#
|
#
|
||||||
# inputs.nixpkgs.follows = "nixpkgs";
|
# inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
|
||||||
# This is not required for Flakes
|
|
||||||
inputs.flake-compat.follows = "";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
{
|
|
||||||
runCommand,
|
|
||||||
deadnix,
|
|
||||||
llvmPackages_18,
|
|
||||||
markdownlint-cli,
|
|
||||||
nixfmt-rfc-style,
|
|
||||||
statix,
|
|
||||||
self,
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
formatting =
|
|
||||||
runCommand "check-formatting"
|
|
||||||
{
|
|
||||||
nativeBuildInputs = [
|
|
||||||
deadnix
|
|
||||||
llvmPackages_18.clang-tools
|
|
||||||
markdownlint-cli
|
|
||||||
nixfmt-rfc-style
|
|
||||||
statix
|
|
||||||
];
|
|
||||||
}
|
|
||||||
''
|
|
||||||
cd ${self}
|
|
||||||
|
|
||||||
echo "Running clang-format...."
|
|
||||||
clang-format --dry-run --style='file' --Werror */**.{c,cc,cpp,h,hh,hpp}
|
|
||||||
|
|
||||||
echo "Running deadnix..."
|
|
||||||
deadnix --fail
|
|
||||||
|
|
||||||
echo "Running markdownlint..."
|
|
||||||
markdownlint --dot .
|
|
||||||
|
|
||||||
echo "Running nixfmt..."
|
|
||||||
nixfmt --check .
|
|
||||||
|
|
||||||
echo "Running statix"
|
|
||||||
statix check .
|
|
||||||
|
|
||||||
touch $out
|
|
||||||
'';
|
|
||||||
}
|
|
@ -3,7 +3,7 @@
|
|||||||
stdenv,
|
stdenv,
|
||||||
cmake,
|
cmake,
|
||||||
cmark,
|
cmark,
|
||||||
apple-sdk_11,
|
darwin,
|
||||||
extra-cmake-modules,
|
extra-cmake-modules,
|
||||||
gamemode,
|
gamemode,
|
||||||
ghc_filesystem,
|
ghc_filesystem,
|
||||||
@ -11,32 +11,54 @@
|
|||||||
kdePackages,
|
kdePackages,
|
||||||
libnbtplusplus,
|
libnbtplusplus,
|
||||||
ninja,
|
ninja,
|
||||||
nix-filter,
|
|
||||||
self,
|
self,
|
||||||
stripJavaArchivesHook,
|
stripJavaArchivesHook,
|
||||||
tomlplusplus,
|
tomlplusplus,
|
||||||
zlib,
|
zlib,
|
||||||
|
|
||||||
msaClientID ? null,
|
msaClientID ? null,
|
||||||
gamemodeSupport ? stdenv.hostPlatform.isLinux,
|
gamemodeSupport ? stdenv.hostPlatform.isLinux,
|
||||||
}:
|
}:
|
||||||
|
|
||||||
assert lib.assertMsg (
|
assert lib.assertMsg (
|
||||||
gamemodeSupport -> stdenv.hostPlatform.isLinux
|
gamemodeSupport -> stdenv.hostPlatform.isLinux
|
||||||
) "gamemodeSupport is only available on Linux.";
|
) "gamemodeSupport is only available on Linux.";
|
||||||
|
|
||||||
|
let
|
||||||
|
date =
|
||||||
|
let
|
||||||
|
# YYYYMMDD
|
||||||
|
date' = lib.substring 0 8 self.lastModifiedDate;
|
||||||
|
year = lib.substring 0 4 date';
|
||||||
|
month = lib.substring 4 2 date';
|
||||||
|
date = lib.substring 6 2 date';
|
||||||
|
in
|
||||||
|
if (self ? "lastModifiedDate") then
|
||||||
|
lib.concatStringsSep "-" [
|
||||||
|
year
|
||||||
|
month
|
||||||
|
date
|
||||||
|
]
|
||||||
|
else
|
||||||
|
"unknown";
|
||||||
|
in
|
||||||
|
|
||||||
stdenv.mkDerivation {
|
stdenv.mkDerivation {
|
||||||
pname = "shatteredprism-unwrapped";
|
pname = "shatteredprism-unwrapped";
|
||||||
version = self.shortRev or self.dirtyShortRev or "unknown";
|
version = "1.7-unstable-${date}";
|
||||||
|
|
||||||
src = nix-filter.lib {
|
src = lib.fileset.toSource {
|
||||||
root = self;
|
root = ../.;
|
||||||
include = [
|
fileset = lib.fileset.unions [
|
||||||
"buildconfig"
|
|
||||||
"cmake"
|
|
||||||
"launcher"
|
|
||||||
"libraries"
|
|
||||||
"program_info"
|
|
||||||
"tests"
|
|
||||||
../COPYING.md
|
|
||||||
../CMakeLists.txt
|
../CMakeLists.txt
|
||||||
|
../COPYING.md
|
||||||
|
|
||||||
|
../buildconfig
|
||||||
|
../cmake
|
||||||
|
../launcher
|
||||||
|
../libraries
|
||||||
|
../program_info
|
||||||
|
../tests
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -63,7 +85,7 @@ stdenv.mkDerivation {
|
|||||||
tomlplusplus
|
tomlplusplus
|
||||||
zlib
|
zlib
|
||||||
]
|
]
|
||||||
++ lib.optionals stdenv.hostPlatform.isDarwin [ apple-sdk_11 ]
|
++ lib.optionals stdenv.hostPlatform.isDarwin [ darwin.apple_sdk.frameworks.Cocoa ]
|
||||||
++ lib.optional gamemodeSupport gamemode;
|
++ lib.optional gamemodeSupport gamemode;
|
||||||
|
|
||||||
hardeningEnable = lib.optionals stdenv.hostPlatform.isLinux [ "pie" ];
|
hardeningEnable = lib.optionals stdenv.hostPlatform.isLinux [ "pie" ];
|
||||||
|
Loading…
Reference in New Issue
Block a user