Compare commits
26 Commits
develop
...
release-1.
Author | SHA1 | Date | |
---|---|---|---|
|
6e65cd4405 | ||
|
98fbb3613d | ||
|
2666beafb1 | ||
|
cf8a76be6b | ||
|
e96f482544 | ||
|
dc32f0671b | ||
|
d6d7794cd0 | ||
|
94d95d45d4 | ||
|
5e767a91d9 | ||
|
c89f8b4657 | ||
|
d7fc0f53d3 | ||
|
e81cf8a845 | ||
|
c116885ab1 | ||
|
d68d5ca23f | ||
|
6d94338a56 | ||
|
1e1a1cef05 | ||
|
34bab3e1b2 | ||
|
be6d6501e8 | ||
|
10a70732ce | ||
|
a725dc82a7 | ||
|
97ce8a94e9 | ||
|
85f0904872 | ||
|
cb0a5e42df | ||
|
a0c7fa30c0 | ||
|
b7490b479c | ||
|
0d35edbbf3 |
@ -1,17 +1,16 @@
|
|||||||
---
|
---
|
||||||
BasedOnStyle: Chromium
|
Language: Cpp
|
||||||
|
BasedOnStyle: Chromium
|
||||||
IndentWidth: 4
|
IndentWidth: 4
|
||||||
|
AlignConsecutiveMacros: false
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
AllowShortIfStatementsOnASingleLine: false
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
ColumnLimit: 140
|
|
||||||
---
|
|
||||||
Language: Cpp
|
|
||||||
AlignConsecutiveMacros: None
|
|
||||||
AlignConsecutiveAssignments: None
|
|
||||||
BraceWrapping:
|
BraceWrapping:
|
||||||
AfterFunction: true
|
AfterFunction: true
|
||||||
SplitEmptyFunction: false
|
SplitEmptyFunction: false
|
||||||
SplitEmptyRecord: false
|
SplitEmptyRecord: false
|
||||||
SplitEmptyNamespace: false
|
SplitEmptyNamespace: false
|
||||||
BreakBeforeBraces: Custom
|
BreakBeforeBraces: Custom
|
||||||
BreakConstructorInitializers: BeforeComma
|
BreakConstructorInitializers: BeforeComma
|
||||||
|
ColumnLimit: 140
|
||||||
Cpp11BracedListStyle: false
|
Cpp11BracedListStyle: false
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
Checks:
|
|
||||||
- modernize-use-using
|
|
||||||
|
|
||||||
SystemHeaders: false
|
|
@ -1,8 +0,0 @@
|
|||||||
# EditorConfig specs and documentation: https://EditorConfig.org
|
|
||||||
|
|
||||||
# top-most EditorConfig file
|
|
||||||
root = true
|
|
||||||
|
|
||||||
# C++ Code Style settings
|
|
||||||
[*.{c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}]
|
|
||||||
cpp_generate_documentation_comments = doxygen_slash_star
|
|
@ -1,4 +0,0 @@
|
|||||||
# .git-blame-ignore-revs
|
|
||||||
|
|
||||||
# tabs -> spaces
|
|
||||||
bbb3b3e6f6e3c0f95873f22e6d0a4aaf350f49d9
|
|
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@ -1 +1 @@
|
|||||||
open_collective: prismlauncher
|
open_collective: polymc
|
||||||
|
17
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
17
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -8,9 +8,9 @@ body:
|
|||||||
If you need help with running Minecraft, please visit us on our Discord before making a bug report.
|
If you need help with running Minecraft, please visit us on our Discord before making a bug report.
|
||||||
|
|
||||||
Before submitting a bug report, please make sure you have read this *entire* form, and that:
|
Before submitting a bug report, please make sure you have read this *entire* form, and that:
|
||||||
* You have read the [Prism Launcher wiki](https://prismlauncher.org/wiki/) and it has not answered your question.
|
* You have read the [PolyMC wiki](https://polymc.org/wiki/) and it has not answered your question.
|
||||||
* Your bug is not caused by Minecraft or any mods you have installed.
|
* Your bug is not caused by Minecraft or any mods you have installed.
|
||||||
* Your issue has not been reported before, [make sure to use the search function!](https://github.com/PrismLauncher/PrismLauncher/issues)
|
* Your issue has not been reported before, [make sure to use the search function!](https://github.com/PolyMC/PolyMC/issues)
|
||||||
|
|
||||||
**Do not forget to give your issue a descriptive title.** "Bug in the instance screen" makes it hard to distinguish issues at a glance.
|
**Do not forget to give your issue a descriptive title.** "Bug in the instance screen" makes it hard to distinguish issues at a glance.
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
@ -25,16 +25,9 @@ body:
|
|||||||
- Other
|
- Other
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Version of Prism Launcher
|
label: Version of PolyMC
|
||||||
description: The version of Prism Launcher used in the bug report.
|
description: The version of PolyMC used in the bug report.
|
||||||
placeholder: Prism Launcher 5.0
|
placeholder: PolyMC 1.3.2
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Version of Qt
|
|
||||||
description: The version of Qt used in the bug report. You can find it in Help -> About Prism Launcher -> About Qt.
|
|
||||||
placeholder: Qt 6.3.0
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
4
.github/ISSUE_TEMPLATE/config.yml
vendored
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,5 +1,5 @@
|
|||||||
blank_issues_enabled: true
|
blank_issues_enabled: true
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Prism Launcher Matrix Support Room
|
- name: PolyMC Matrix Support Room
|
||||||
url: https://matrix.to/#/#prism-support:matrix.org
|
url: https://matrix.to/#/#support:polymc.org
|
||||||
about: Please ask for support here before opening an issue.
|
about: Please ask for support here before opening an issue.
|
||||||
|
4
.github/ISSUE_TEMPLATE/rfc.yml
vendored
4
.github/ISSUE_TEMPLATE/rfc.yml
vendored
@ -6,7 +6,7 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
### Use this form to suggest a larger change for Prism Launcher.
|
### Use this form to suggest a larger change for PolyMC.
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Goal
|
label: Goal
|
||||||
@ -18,7 +18,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Motivation
|
label: Motivation
|
||||||
description: |
|
description: |
|
||||||
Introduce the topic. If this is a not-well-known section of Prism Launcher, a detailed explanation of the background is recommended.
|
Introduce the topic. If this is a not-well-known section of PolyMC, a detailed explanation of the background is recommended.
|
||||||
Some example points of discussion:
|
Some example points of discussion:
|
||||||
- What specific problems are you facing right now that you're trying to address?
|
- What specific problems are you facing right now that you're trying to address?
|
||||||
- Are there any previous discussions? Link to them and summarize them (don't force your readers to read them though!).
|
- Are there any previous discussions? Link to them and summarize them (don't force your readers to read them though!).
|
||||||
|
8
.github/ISSUE_TEMPLATE/suggestion.yml
vendored
8
.github/ISSUE_TEMPLATE/suggestion.yml
vendored
@ -5,25 +5,25 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
### Use this form to suggest a feature for Prism Launcher.
|
### Use this form to suggest a feature for PolyMC.
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Role
|
label: Role
|
||||||
description: In what way do you use Prism Launcher that needs this feature?
|
description: In what way do you use PolyMC that needs this feature?
|
||||||
placeholder: I play modded Minecraft.
|
placeholder: I play modded Minecraft.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Suggestion
|
label: Suggestion
|
||||||
description: What do you want Prism Launcher to do?
|
description: What do you want PolyMC to do?
|
||||||
placeholder: I want the cat button to meow.
|
placeholder: I want the cat button to meow.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Benefit
|
label: Benefit
|
||||||
description: Why do you need Prism Launcher to do this?
|
description: Why do you need PolyMC to do this?
|
||||||
placeholder: so that I can always hear a cat when I need to.
|
placeholder: so that I can always hear a cat when I need to.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
3
.github/codeql/codeql-config.yml
vendored
3
.github/codeql/codeql-config.yml
vendored
@ -1,3 +0,0 @@
|
|||||||
query-filters:
|
|
||||||
- exclude:
|
|
||||||
id: cpp/fixme-comment
|
|
9
.github/pull_request_template.md
vendored
9
.github/pull_request_template.md
vendored
@ -1,9 +0,0 @@
|
|||||||
<!--
|
|
||||||
Hey there! Thanks for your contribution.
|
|
||||||
|
|
||||||
Please make sure that your commits are signed off first.
|
|
||||||
If you don't know how that works, check out our contribution guidelines: https://github.com/PrismLauncher/PrismLauncher/blob/develop/CONTRIBUTING.md#signing-your-work
|
|
||||||
If you already created your commits, you can run `git rebase --signoff develop` to retroactively sign-off all your commits and `git push --force` to override what you have pushed already.
|
|
||||||
|
|
||||||
Note that signing and signing-off are two different things!
|
|
||||||
-->
|
|
32
.github/workflows/backport.yml
vendored
32
.github/workflows/backport.yml
vendored
@ -1,32 +0,0 @@
|
|||||||
name: Backport
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types: [closed, labeled]
|
|
||||||
|
|
||||||
# WARNING:
|
|
||||||
# When extending this action, be aware that $GITHUB_TOKEN allows write access to
|
|
||||||
# the GitHub repository. This means that it should not evaluate user input in a
|
|
||||||
# way that allows code injection.
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
backport:
|
|
||||||
permissions:
|
|
||||||
contents: write # for korthout/backport-action to create branch
|
|
||||||
pull-requests: write # for korthout/backport-action to create PR to backport
|
|
||||||
name: Backport Pull Request
|
|
||||||
if: github.repository_owner == 'PrismLauncher' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
|
||||||
- name: Create backport PRs
|
|
||||||
uses: korthout/backport-action@v2.1.0
|
|
||||||
with:
|
|
||||||
# Config README: https://github.com/korthout/backport-action#backport-action
|
|
||||||
pull_description: |-
|
|
||||||
Bot-based backport to `${target_branch}`, triggered by a label in #${pull_number}.
|
|
||||||
|
|
429
.github/workflows/build.yml
vendored
429
.github/workflows/build.yml
vendored
@ -7,29 +7,10 @@ on:
|
|||||||
description: Type of build (Debug, Release, RelWithDebInfo, MinSizeRel)
|
description: Type of build (Debug, Release, RelWithDebInfo, MinSizeRel)
|
||||||
type: string
|
type: string
|
||||||
default: Debug
|
default: Debug
|
||||||
is_qt_cached:
|
|
||||||
description: Enable Qt caching or not
|
|
||||||
type: string
|
|
||||||
default: true
|
|
||||||
secrets:
|
secrets:
|
||||||
SPARKLE_ED25519_KEY:
|
SPARKLE_ED25519_KEY:
|
||||||
description: Private key for signing Sparkle updates
|
description: Private key for signing Sparkle updates
|
||||||
required: false
|
required: false
|
||||||
WINDOWS_CODESIGN_CERT:
|
|
||||||
description: Certificate for signing Windows builds
|
|
||||||
required: false
|
|
||||||
WINDOWS_CODESIGN_PASSWORD:
|
|
||||||
description: Password for signing Windows builds
|
|
||||||
required: false
|
|
||||||
CACHIX_AUTH_TOKEN:
|
|
||||||
description: Private token for authenticating against Cachix cache
|
|
||||||
required: false
|
|
||||||
GPG_PRIVATE_KEY:
|
|
||||||
description: Private key for AppImage signing
|
|
||||||
required: false
|
|
||||||
GPG_PRIVATE_KEY_ID:
|
|
||||||
description: ID for the GPG_PRIVATE_KEY, to select the signing key
|
|
||||||
required: false
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@ -37,64 +18,34 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
|
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
qt_ver: 5
|
qt_ver: 5
|
||||||
|
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: linux
|
qt_host: linux
|
||||||
qt_arch: ""
|
qt_version: '6.2.4'
|
||||||
qt_version: "6.2.4"
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_modules: "qt5compat qtimageformats"
|
qt_path: /home/runner/work/PolyMC/Qt
|
||||||
qt_tools: ""
|
|
||||||
|
|
||||||
- os: windows-2022
|
- os: windows-2022
|
||||||
name: "Windows-MinGW-w64"
|
name: "Windows-Legacy"
|
||||||
msystem: clang64
|
msystem: mingw32
|
||||||
vcvars_arch: "amd64_x86"
|
|
||||||
|
|
||||||
- os: windows-2022
|
|
||||||
name: "Windows-MSVC"
|
|
||||||
msystem: ""
|
|
||||||
architecture: "x64"
|
|
||||||
vcvars_arch: "amd64"
|
|
||||||
qt_ver: 6
|
|
||||||
qt_host: windows
|
|
||||||
qt_arch: ''
|
|
||||||
qt_version: '6.6.0'
|
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
|
||||||
qt_tools: ''
|
|
||||||
|
|
||||||
- os: windows-2022
|
|
||||||
name: "Windows-MSVC-arm64"
|
|
||||||
msystem: ""
|
|
||||||
architecture: "arm64"
|
|
||||||
vcvars_arch: "amd64_arm64"
|
|
||||||
qt_ver: 6
|
|
||||||
qt_host: windows
|
|
||||||
qt_arch: 'win64_msvc2019_arm64'
|
|
||||||
qt_version: '6.6.0'
|
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
|
||||||
qt_tools: ''
|
|
||||||
|
|
||||||
- os: macos-12
|
|
||||||
name: macOS
|
|
||||||
macosx_deployment_target: 11.0
|
|
||||||
qt_ver: 6
|
|
||||||
qt_host: mac
|
|
||||||
qt_arch: ''
|
|
||||||
qt_version: '6.6.0'
|
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
|
||||||
qt_tools: ''
|
|
||||||
|
|
||||||
- os: macos-12
|
|
||||||
name: macOS-Legacy
|
|
||||||
macosx_deployment_target: 10.13
|
|
||||||
qt_ver: 5
|
qt_ver: 5
|
||||||
|
|
||||||
|
- os: windows-2022
|
||||||
|
name: "Windows"
|
||||||
|
msystem: mingw32
|
||||||
|
qt_ver: 6
|
||||||
|
|
||||||
|
- os: macos-12
|
||||||
|
macosx_deployment_target: 10.14
|
||||||
|
qt_ver: 6
|
||||||
qt_host: mac
|
qt_host: mac
|
||||||
qt_version: "5.15.2"
|
qt_version: '6.3.0'
|
||||||
qt_modules: ""
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_tools: ""
|
qt_path: /Users/runner/work/PolyMC/Qt
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
@ -105,61 +56,45 @@ jobs:
|
|||||||
INSTALL_APPIMAGE_DIR: "install-appdir"
|
INSTALL_APPIMAGE_DIR: "install-appdir"
|
||||||
BUILD_DIR: "build"
|
BUILD_DIR: "build"
|
||||||
CCACHE_VAR: ""
|
CCACHE_VAR: ""
|
||||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
##
|
##
|
||||||
# PREPARE
|
# PREPARE
|
||||||
##
|
##
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: "true"
|
submodules: 'true'
|
||||||
|
|
||||||
- name: "Setup MSYS2"
|
- name: 'Setup MSYS2'
|
||||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
if: runner.os == 'Windows'
|
||||||
uses: msys2/setup-msys2@v2
|
uses: msys2/setup-msys2@v2
|
||||||
with:
|
with:
|
||||||
msystem: ${{ matrix.msystem }}
|
msystem: ${{ matrix.msystem }}
|
||||||
update: true
|
update: true
|
||||||
install: >-
|
install: >-
|
||||||
git
|
git
|
||||||
mingw-w64-x86_64-binutils
|
|
||||||
pacboy: >-
|
pacboy: >-
|
||||||
toolchain:p
|
toolchain:p
|
||||||
cmake:p
|
cmake:p
|
||||||
extra-cmake-modules:p
|
extra-cmake-modules:p
|
||||||
ninja:p
|
ninja:p
|
||||||
qt6-base:p
|
qt${{ matrix.qt_ver }}-base:p
|
||||||
qt6-svg:p
|
qt${{ matrix.qt_ver }}-svg:p
|
||||||
qt6-imageformats:p
|
qt${{ matrix.qt_ver }}-imageformats:p
|
||||||
quazip-qt6:p
|
quazip-qt${{ matrix.qt_ver }}:p
|
||||||
ccache:p
|
ccache:p
|
||||||
qt6-5compat:p
|
nsis:p
|
||||||
cmark:p
|
${{ matrix.qt_ver == 6 && 'qt6-5compat:p' || '' }}
|
||||||
|
|
||||||
- name: Force newer ccache
|
|
||||||
if: runner.os == 'Windows' && matrix.msystem == '' && inputs.build_type == 'Debug'
|
|
||||||
run: |
|
|
||||||
choco install ccache --version 4.7.1
|
|
||||||
|
|
||||||
- name: Setup ccache
|
- name: Setup ccache
|
||||||
if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug'
|
if: runner.os != 'Windows' && inputs.build_type == 'Debug'
|
||||||
uses: hendrikmuhs/ccache-action@v1.2.10
|
uses: hendrikmuhs/ccache-action@v1.2.1
|
||||||
with:
|
with:
|
||||||
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }}
|
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}
|
||||||
|
|
||||||
- name: Retrieve ccache cache (Windows MinGW-w64)
|
- name: Setup ccache (Windows)
|
||||||
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
if: runner.os == 'Windows' && inputs.build_type == 'Debug'
|
||||||
uses: actions/cache@v3.3.2
|
|
||||||
with:
|
|
||||||
path: '${{ github.workspace }}\.ccache'
|
|
||||||
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ matrix.os }}-mingw-w64-ccache
|
|
||||||
|
|
||||||
- name: Setup ccache (Windows MinGW-w64)
|
|
||||||
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
ccache --set-config=cache_dir='${{ github.workspace }}\.ccache'
|
ccache --set-config=cache_dir='${{ github.workspace }}\.ccache'
|
||||||
@ -174,6 +109,15 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "CCACHE_VAR=ccache" >> $GITHUB_ENV
|
echo "CCACHE_VAR=ccache" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Retrieve ccache cache (Windows)
|
||||||
|
if: runner.os == 'Windows' && inputs.build_type == 'Debug'
|
||||||
|
uses: actions/cache@v3.0.2
|
||||||
|
with:
|
||||||
|
path: '${{ github.workspace }}\.ccache'
|
||||||
|
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ matrix.os }}-qt${{ matrix.qt_ver }}
|
||||||
|
|
||||||
- name: Set short version
|
- name: Set short version
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@ -184,7 +128,7 @@ jobs:
|
|||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get -y update
|
sudo apt-get -y update
|
||||||
sudo apt-get -y install ninja-build extra-cmake-modules scdoc appstream
|
sudo apt-get -y install ninja-build extra-cmake-modules scdoc
|
||||||
|
|
||||||
- name: Install Dependencies (macOS)
|
- name: Install Dependencies (macOS)
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
@ -196,44 +140,25 @@ jobs:
|
|||||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
|
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
|
||||||
|
|
||||||
- name: Install host Qt (Windows MSVC arm64)
|
- name: Cache Qt (macOS and AppImage)
|
||||||
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
id: cache-qt
|
||||||
uses: jurplel/install-qt-action@v3
|
if: matrix.qt_ver == 6 && runner.os != 'Windows'
|
||||||
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
aqtversion: "==3.1.*"
|
path: '${{ matrix.qt_path }}/${{ matrix.qt_version }}'
|
||||||
py7zrversion: ">=0.20.2"
|
key: ${{ matrix.qt_host }}-${{ matrix.qt_version }}-"${{ matrix.qt_modules }}"-qt_cache
|
||||||
version: ${{ matrix.qt_version }}
|
|
||||||
host: "windows"
|
|
||||||
target: "desktop"
|
|
||||||
arch: ""
|
|
||||||
modules: ${{ matrix.qt_modules }}
|
|
||||||
tools: ${{ matrix.qt_tools }}
|
|
||||||
cache: ${{ inputs.is_qt_cached }}
|
|
||||||
cache-key-prefix: host-qt-arm64-windows
|
|
||||||
dir: ${{ github.workspace }}\HostQt
|
|
||||||
set-env: false
|
|
||||||
|
|
||||||
- name: Install Qt (macOS, Linux, Qt 6 & Windows MSVC)
|
- name: Install Qt (macOS and AppImage)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS' || (runner.os == 'Windows' && matrix.msystem == '')
|
if: matrix.qt_ver == 6 && runner.os != 'Windows'
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v2
|
||||||
with:
|
with:
|
||||||
aqtversion: "==3.1.*"
|
version: ${{ matrix.qt_version }}
|
||||||
py7zrversion: ">=0.20.2"
|
host: ${{ matrix.qt_host }}
|
||||||
version: ${{ matrix.qt_version }}
|
target: 'desktop'
|
||||||
host: ${{ matrix.qt_host }}
|
modules: ${{ matrix.qt_modules }}
|
||||||
target: "desktop"
|
cached: ${{ steps.cache-qt.outputs.cache-hit }}
|
||||||
arch: ${{ matrix.qt_arch }}
|
aqtversion: ==2.1.*
|
||||||
modules: ${{ matrix.qt_modules }}
|
|
||||||
tools: ${{ matrix.qt_tools }}
|
|
||||||
cache: ${{ inputs.is_qt_cached }}
|
|
||||||
|
|
||||||
- name: Install MSVC (Windows MSVC)
|
|
||||||
if: runner.os == 'Windows' # We want this for MinGW builds as well, as we need SignTool
|
|
||||||
uses: ilammy/msvc-dev-cmd@v1
|
|
||||||
with:
|
|
||||||
vsversion: 2022
|
|
||||||
arch: ${{ matrix.vcvars_arch }}
|
|
||||||
|
|
||||||
- name: Prepare AppImage (Linux)
|
- name: Prepare AppImage (Linux)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
@ -242,55 +167,27 @@ jobs:
|
|||||||
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage"
|
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage"
|
||||||
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage"
|
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage"
|
||||||
|
|
||||||
wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage"
|
|
||||||
|
|
||||||
${{ github.workspace }}/.github/scripts/prepare_JREs.sh
|
${{ github.workspace }}/.github/scripts/prepare_JREs.sh
|
||||||
sudo apt install libopengl0
|
|
||||||
|
|
||||||
- name: Add QT_HOST_PATH var (Windows MSVC arm64)
|
|
||||||
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
|
||||||
run: |
|
|
||||||
echo "QT_HOST_PATH=${{ github.workspace }}\HostQt\Qt\${{ matrix.qt_version }}\msvc2019_64" >> $env:GITHUB_ENV
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# CONFIGURE
|
# CONFIGURE
|
||||||
##
|
##
|
||||||
|
|
||||||
- name: Configure CMake (macOS)
|
- name: Configure CMake (macOS)
|
||||||
if: runner.os == 'macOS' && matrix.qt_ver == 6
|
if: runner.os == 'macOS'
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DLauncher_BUILD_PLATFORM=macOS -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja
|
||||||
|
|
||||||
- name: Configure CMake (macOS-Legacy)
|
- name: Configure CMake (Windows)
|
||||||
if: runner.os == 'macOS' && matrix.qt_ver == 5
|
if: runner.os == 'Windows'
|
||||||
run: |
|
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja
|
|
||||||
|
|
||||||
- name: Configure CMake (Windows MinGW-w64)
|
|
||||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} -G Ninja
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja
|
||||||
|
|
||||||
- name: Configure CMake (Windows MSVC)
|
|
||||||
if: runner.os == 'Windows' && matrix.msystem == ''
|
|
||||||
run: |
|
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }}
|
|
||||||
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
|
|
||||||
if ("${{ env.CCACHE_VAR }}")
|
|
||||||
{
|
|
||||||
Copy-Item C:/ProgramData/chocolatey/lib/ccache/tools/ccache-4.7.1-windows-x86_64/ccache.exe -Destination C:/ProgramData/chocolatey/lib/ccache/tools/ccache-4.7.1-windows-x86_64/cl.exe
|
|
||||||
echo "CLToolExe=cl.exe" >> $env:GITHUB_ENV
|
|
||||||
echo "CLToolPath=C:/ProgramData/chocolatey/lib/ccache/tools/ccache-4.7.1-windows-x86_64/" >> $env:GITHUB_ENV
|
|
||||||
echo "TrackFileAccess=false" >> $env:GITHUB_ENV
|
|
||||||
}
|
|
||||||
# Needed for ccache, but also speeds up compile
|
|
||||||
echo "UseMultiToolTask=true" >> $env:GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Configure CMake (Linux)
|
- name: Configure CMake (Linux)
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -G Ninja
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja
|
||||||
|
|
||||||
##
|
##
|
||||||
# BUILD
|
# BUILD
|
||||||
@ -301,17 +198,12 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cmake --build ${{ env.BUILD_DIR }}
|
cmake --build ${{ env.BUILD_DIR }}
|
||||||
|
|
||||||
- name: Build (Windows MinGW-w64)
|
- name: Build (Windows)
|
||||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
if: runner.os == 'Windows'
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
cmake --build ${{ env.BUILD_DIR }}
|
cmake --build ${{ env.BUILD_DIR }}
|
||||||
|
|
||||||
- name: Build (Windows MSVC)
|
|
||||||
if: runner.os == 'Windows' && matrix.msystem == ''
|
|
||||||
run: |
|
|
||||||
cmake --build ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }}
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# TEST
|
# TEST
|
||||||
##
|
##
|
||||||
@ -319,18 +211,13 @@ jobs:
|
|||||||
- name: Test
|
- name: Test
|
||||||
if: runner.os != 'Windows'
|
if: runner.os != 'Windows'
|
||||||
run: |
|
run: |
|
||||||
ctest -E "^example64|example$" --test-dir build --output-on-failure
|
ctest --test-dir build --output-on-failure
|
||||||
|
|
||||||
- name: Test (Windows MinGW-w64)
|
- name: Test (Windows)
|
||||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
if: runner.os == 'Windows'
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
ctest -E "^example64|example$" --test-dir build --output-on-failure
|
ctest --test-dir build --output-on-failure
|
||||||
|
|
||||||
- name: Test (Windows MSVC)
|
|
||||||
if: runner.os == 'Windows' && matrix.msystem == '' && matrix.architecture != 'arm64'
|
|
||||||
run: |
|
|
||||||
ctest -E "^example64|example$" --test-dir build --output-on-failure -C ${{ inputs.build_type }}
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# PACKAGE BUILDS
|
# PACKAGE BUILDS
|
||||||
@ -342,18 +229,17 @@ jobs:
|
|||||||
cmake --install ${{ env.BUILD_DIR }}
|
cmake --install ${{ env.BUILD_DIR }}
|
||||||
|
|
||||||
cd ${{ env.INSTALL_DIR }}
|
cd ${{ env.INSTALL_DIR }}
|
||||||
chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
chmod +x "PolyMC.app/Contents/MacOS/polymc"
|
||||||
sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PolyMC.app/Contents/MacOS/polymc"
|
||||||
mv "PrismLauncher.app" "Prism Launcher.app"
|
tar -czf ../PolyMC.tar.gz *
|
||||||
tar -czf ../PrismLauncher.tar.gz *
|
|
||||||
|
|
||||||
- name: Make Sparkle signature (macOS)
|
- name: Make Sparkle signature (macOS)
|
||||||
if: matrix.name == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
run: |
|
run: |
|
||||||
if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then
|
if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then
|
||||||
brew install openssl@3
|
brew install openssl@3
|
||||||
echo '${{ secrets.SPARKLE_ED25519_KEY }}' > ed25519-priv.pem
|
echo '${{ secrets.SPARKLE_ED25519_KEY }}' > ed25519-priv.pem
|
||||||
signature=$(/usr/local/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.tar.gz -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
|
signature=$(/usr/local/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PolyMC.tar.gz -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
|
||||||
rm ed25519-priv.pem
|
rm ed25519-priv.pem
|
||||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||||
### Artifact Information :information_source:
|
### Artifact Information :information_source:
|
||||||
@ -366,109 +252,57 @@ jobs:
|
|||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Package (Windows MinGW-w64)
|
- name: Package (Windows)
|
||||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
if: runner.os == 'Windows'
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
cmake --install ${{ env.BUILD_DIR }}
|
cmake --install ${{ env.BUILD_DIR }}
|
||||||
touch ${{ env.INSTALL_DIR }}/manifest.txt
|
|
||||||
for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_DIR }}/manifest.txt
|
|
||||||
|
|
||||||
- name: Package (Windows MSVC)
|
|
||||||
if: runner.os == 'Windows' && matrix.msystem == ''
|
|
||||||
run: |
|
|
||||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }}
|
|
||||||
|
|
||||||
cd ${{ env.INSTALL_DIR }}
|
cd ${{ env.INSTALL_DIR }}
|
||||||
if ("${{ matrix.qt_ver }}" -eq "5")
|
if [ "${{ matrix.msystem }}" == "mingw32" ]; then
|
||||||
{
|
cp /mingw32/bin/libcrypto-1_1.dll /mingw32/bin/libssl-1_1.dll ./
|
||||||
Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll
|
elif [ "${{ matrix.msystem }}" == "mingw64" ]; then
|
||||||
Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll
|
cp /mingw64/bin/libcrypto-1_1-x64.dll /mingw64/bin/libssl-1_1-x64.dll ./
|
||||||
}
|
fi
|
||||||
cd ${{ github.workspace }}
|
|
||||||
|
|
||||||
Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
|
- name: Package (Windows, portable)
|
||||||
|
|
||||||
- name: Fetch codesign certificate (Windows)
|
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
shell: bash # yes, we are not using MSYS2 or PowerShell here
|
|
||||||
run: |
|
|
||||||
echo '${{ secrets.WINDOWS_CODESIGN_CERT }}' | base64 --decode > codesign.pfx
|
|
||||||
|
|
||||||
- name: Sign executable (Windows)
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
run: |
|
|
||||||
if (Get-Content ./codesign.pfx){
|
|
||||||
cd ${{ env.INSTALL_DIR }}
|
|
||||||
# We ship the exact same executable for portable and non-portable editions, so signing just once is fine
|
|
||||||
SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_updater.exe prismlauncher_filelink.exe
|
|
||||||
} else {
|
|
||||||
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
|
||||||
}
|
|
||||||
|
|
||||||
- name: Package (Windows MinGW-w64, portable)
|
|
||||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
|
cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||||
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
|
|
||||||
|
|
||||||
- name: Package (Windows MSVC, portable)
|
|
||||||
if: runner.os == 'Windows' && matrix.msystem == ''
|
|
||||||
run: |
|
|
||||||
cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
|
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
|
||||||
|
|
||||||
Get-ChildItem ${{ env.INSTALL_PORTABLE_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_PORTABLE_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
|
|
||||||
|
|
||||||
- name: Package (Windows, installer)
|
- name: Package (Windows, installer)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
cd ${{ env.INSTALL_DIR }}
|
cd ${{ env.INSTALL_DIR }}
|
||||||
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
|
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
|
||||||
|
|
||||||
- name: Sign installer (Windows)
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
run: |
|
|
||||||
if (Get-Content ./codesign.pfx){
|
|
||||||
SignTool sign /fd sha256 /td sha256 /f codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com PrismLauncher-Setup.exe
|
|
||||||
} else {
|
|
||||||
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
|
||||||
}
|
|
||||||
|
|
||||||
- name: Package (Linux)
|
- name: Package (Linux)
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
run: |
|
run: |
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }}
|
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }}
|
||||||
for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_DIR }}/manifest.txt
|
|
||||||
|
|
||||||
cd ${{ env.INSTALL_DIR }}
|
cd ${{ env.INSTALL_DIR }}
|
||||||
tar --owner root --group root -czf ../PrismLauncher.tar.gz *
|
tar --owner root --group root -czf ../PolyMC.tar.gz *
|
||||||
|
|
||||||
- name: Package (Linux, portable)
|
- name: Package (Linux, portable)
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
run: |
|
run: |
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
|
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||||
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
|
|
||||||
|
|
||||||
|
|
||||||
cd ${{ env.INSTALL_PORTABLE_DIR }}
|
cd ${{ env.INSTALL_PORTABLE_DIR }}
|
||||||
tar -czf ../PrismLauncher-portable.tar.gz *
|
tar -czf ../PolyMC-portable.tar.gz *
|
||||||
|
|
||||||
- name: Package AppImage (Linux)
|
- name: Package AppImage (Linux)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
|
||||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
|
||||||
run: |
|
run: |
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
|
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
|
||||||
|
|
||||||
mv ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.metainfo.xml ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.appdata.xml
|
export OUTPUT="PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
|
||||||
export "NO_APPSTREAM=1" # we have to skip appstream checking because appstream on ubuntu 20.04 is outdated
|
|
||||||
|
|
||||||
export OUTPUT="PrismLauncher-Linux-x86_64.AppImage"
|
|
||||||
|
|
||||||
chmod +x linuxdeploy-*.AppImage
|
chmod +x linuxdeploy-*.AppImage
|
||||||
|
|
||||||
@ -479,11 +313,10 @@ jobs:
|
|||||||
|
|
||||||
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk
|
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk
|
||||||
|
|
||||||
cp -r ${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
cp -r /home/runner/work/PolyMC/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
||||||
|
|
||||||
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||||
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}//usr/lib/
|
||||||
cp /usr/lib/x86_64-linux-gnu/libOpenGL.so.0* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
|
||||||
|
|
||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
|
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
|
||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server"
|
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server"
|
||||||
@ -492,24 +325,7 @@ jobs:
|
|||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
|
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
|
||||||
export LD_LIBRARY_PATH
|
export LD_LIBRARY_PATH
|
||||||
|
|
||||||
chmod +x AppImageUpdate-x86_64.AppImage
|
./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.polymc.PolyMC.svg
|
||||||
cp AppImageUpdate-x86_64.AppImage ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin
|
|
||||||
|
|
||||||
export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-x86_64.AppImage.zsync"
|
|
||||||
|
|
||||||
if [ '${{ secrets.GPG_PRIVATE_KEY_ID }}' != '' ]; then
|
|
||||||
export SIGN=1
|
|
||||||
export SIGN_KEY=${{ secrets.GPG_PRIVATE_KEY_ID }}
|
|
||||||
mkdir -p ~/.gnupg/
|
|
||||||
echo "$GPG_PRIVATE_KEY" > ~/.gnupg/private.key
|
|
||||||
gpg --import ~/.gnupg/private.key
|
|
||||||
else
|
|
||||||
echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
|
|
||||||
./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.prismlauncher.PrismLauncher.svg
|
|
||||||
|
|
||||||
mv "PrismLauncher-Linux-x86_64.AppImage" "PrismLauncher-Linux-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# UPLOAD BUILDS
|
# UPLOAD BUILDS
|
||||||
@ -519,92 +335,63 @@ jobs:
|
|||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PrismLauncher.tar.gz
|
path: PolyMC.tar.gz
|
||||||
|
|
||||||
- name: Upload binary zip (Windows)
|
- name: Upload binary zip (Windows)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PolyMC-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: ${{ env.INSTALL_DIR }}/**
|
path: ${{ env.INSTALL_DIR }}/**
|
||||||
|
|
||||||
- name: Upload binary zip (Windows, portable)
|
- name: Upload binary zip (Windows, portable)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PolyMC-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: ${{ env.INSTALL_PORTABLE_DIR }}/**
|
path: ${{ env.INSTALL_PORTABLE_DIR }}/**
|
||||||
|
|
||||||
- name: Upload installer (Windows)
|
- name: Upload installer (Windows)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PolyMC-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PrismLauncher-Setup.exe
|
path: PolyMC-Setup.exe
|
||||||
|
|
||||||
- name: Upload binary tarball (Linux, Qt 5)
|
- name: Upload binary tarball (Linux, Qt 5)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ runner.os }}-Qt5-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PrismLauncher.tar.gz
|
path: PolyMC.tar.gz
|
||||||
|
|
||||||
- name: Upload binary tarball (Linux, portable, Qt 5)
|
- name: Upload binary tarball (Linux, portable, Qt 5)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ runner.os }}-Qt5-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PolyMC-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PrismLauncher-portable.tar.gz
|
path: PolyMC-portable.tar.gz
|
||||||
|
|
||||||
- name: Upload binary tarball (Linux, Qt 6)
|
- name: Upload binary tarball (Linux, Qt 6)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver !=5
|
if: runner.os == 'Linux' && matrix.qt_ver !=5
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ runner.os }}-Qt6-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PolyMC-${{ runner.os }}-Qt6-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PrismLauncher.tar.gz
|
path: PolyMC.tar.gz
|
||||||
|
|
||||||
- name: Upload binary tarball (Linux, portable, Qt 6)
|
- name: Upload binary tarball (Linux, portable, Qt 6)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PolyMC-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PrismLauncher-portable.tar.gz
|
path: PolyMC-portable.tar.gz
|
||||||
|
|
||||||
- name: Upload AppImage (Linux)
|
- name: Upload AppImage (Linux)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||||
path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
path: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||||
|
|
||||||
- name: Upload AppImage Zsync (Linux)
|
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync
|
|
||||||
path: PrismLauncher-Linux-x86_64.AppImage.zsync
|
|
||||||
|
|
||||||
- name: ccache stats (Windows MinGW-w64)
|
|
||||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
|
||||||
shell: msys2 {0}
|
|
||||||
run: |
|
|
||||||
ccache -s
|
|
||||||
|
|
||||||
flatpak:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container:
|
|
||||||
image: bilelmoussaoui/flatpak-github-actions:kde-5.15-22.08
|
|
||||||
options: --privileged
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
if: inputs.build_type == 'Debug'
|
|
||||||
with:
|
|
||||||
submodules: "true"
|
|
||||||
- name: Build Flatpak (Linux)
|
|
||||||
if: inputs.build_type == 'Debug'
|
|
||||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
|
||||||
with:
|
|
||||||
bundle: "Prism Launcher.flatpak"
|
|
||||||
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
|
|
||||||
|
35
.github/workflows/codeql.yml
vendored
35
.github/workflows/codeql.yml
vendored
@ -1,35 +0,0 @@
|
|||||||
name: "CodeQL Code Scanning"
|
|
||||||
|
|
||||||
on: [ push, pull_request, workflow_dispatch ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
CodeQL:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: 'true'
|
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v2
|
|
||||||
with:
|
|
||||||
config-file: ./.github/codeql/codeql-config.yml
|
|
||||||
queries: security-and-quality
|
|
||||||
languages: cpp, java
|
|
||||||
|
|
||||||
- name: Install Dependencies
|
|
||||||
run:
|
|
||||||
sudo apt-get -y update
|
|
||||||
|
|
||||||
sudo apt-get -y install ninja-build extra-cmake-modules scdoc qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
|
|
||||||
|
|
||||||
- name: Configure and Build
|
|
||||||
run: |
|
|
||||||
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr -DLauncher_QT_VERSION_MAJOR=5 -G Ninja
|
|
||||||
|
|
||||||
cmake --build build
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v2
|
|
33
.github/workflows/trigger_builds.yml
vendored
33
.github/workflows/trigger_builds.yml
vendored
@ -3,35 +3,30 @@ name: Build Application
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- "renovate/**"
|
- 'stable'
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- "**.md"
|
- '**.md'
|
||||||
- "**/LICENSE"
|
- '**/LICENSE'
|
||||||
- "flake.lock"
|
- 'flake.lock'
|
||||||
- "packages/**"
|
- '**.nix'
|
||||||
- ".github/ISSUE_TEMPLATE/**"
|
- 'packages/**'
|
||||||
- ".markdownlint**"
|
- '.github/ISSUE_TEMPLATE/**'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- "**.md"
|
- '**.md'
|
||||||
- "**/LICENSE"
|
- '**/LICENSE'
|
||||||
- "flake.lock"
|
- 'flake.lock'
|
||||||
- "packages/**"
|
- '**.nix'
|
||||||
- ".github/ISSUE_TEMPLATE/**"
|
- 'packages/**'
|
||||||
- ".markdownlint**"
|
- '.github/ISSUE_TEMPLATE/**'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
build_debug:
|
build_debug:
|
||||||
name: Build Debug
|
name: Build Debug
|
||||||
uses: ./.github/workflows/build.yml
|
uses: ./.github/workflows/build.yml
|
||||||
with:
|
with:
|
||||||
build_type: Debug
|
build_type: Debug
|
||||||
is_qt_cached: true
|
|
||||||
secrets:
|
secrets:
|
||||||
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
||||||
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
|
||||||
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
|
||||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
|
||||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
|
||||||
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
|
||||||
|
88
.github/workflows/trigger_release.yml
vendored
88
.github/workflows/trigger_release.yml
vendored
@ -3,22 +3,17 @@ name: Build Application and Make Release
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- "*"
|
- '*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
build_release:
|
build_release:
|
||||||
name: Build Release
|
name: Build Release
|
||||||
uses: ./.github/workflows/build.yml
|
uses: ./.github/workflows/build.yml
|
||||||
with:
|
with:
|
||||||
build_type: Release
|
build_type: Release
|
||||||
is_qt_cached: false
|
|
||||||
secrets:
|
secrets:
|
||||||
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
||||||
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
|
||||||
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
|
||||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
|
||||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
|
||||||
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
|
||||||
|
|
||||||
create_release:
|
create_release:
|
||||||
needs: build_release
|
needs: build_release
|
||||||
@ -27,10 +22,10 @@ jobs:
|
|||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: "true"
|
submodules: 'true'
|
||||||
path: "PrismLauncher-source"
|
path: 'PolyMC-source'
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v3
|
||||||
- name: Grab and store version
|
- name: Grab and store version
|
||||||
@ -39,40 +34,25 @@ jobs:
|
|||||||
echo "VERSION=$tag_name" >> $GITHUB_ENV
|
echo "VERSION=$tag_name" >> $GITHUB_ENV
|
||||||
- name: Package artifacts properly
|
- name: Package artifacts properly
|
||||||
run: |
|
run: |
|
||||||
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
|
mv ${{ github.workspace }}/PolyMC-source PolyMC-${{ env.VERSION }}
|
||||||
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
mv PolyMC-Linux-Qt6-Portable*/PolyMC-portable.tar.gz PolyMC-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||||
mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
mv PolyMC-Linux-Qt6*/PolyMC.tar.gz PolyMC-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||||
mv PrismLauncher-Linux-Qt5-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
|
mv PolyMC-Linux-Portable*/PolyMC-portable.tar.gz PolyMC-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||||
mv PrismLauncher-Linux-Qt5*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz
|
mv PolyMC-Linux*/PolyMC.tar.gz PolyMC-Linux-${{ env.VERSION }}.tar.gz
|
||||||
mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage
|
mv PolyMC-*.AppImage/PolyMC-*.AppImage PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||||
mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
|
mv PolyMC-macOS*/PolyMC.tar.gz PolyMC-macOS-${{ env.VERSION }}.tar.gz
|
||||||
mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
|
||||||
mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
|
|
||||||
|
|
||||||
tar --exclude='.git' -czf PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }}
|
tar -czf PolyMC-${{ env.VERSION }}.tar.gz PolyMC-${{ env.VERSION }}
|
||||||
|
|
||||||
for d in PrismLauncher-Windows-MSVC*; do
|
for d in PolyMC-Windows-*; do
|
||||||
cd "${d}" || continue
|
cd "${d}" || continue
|
||||||
LEGACY="$(echo -n ${d} | grep -o Legacy || true)"
|
LEGACY="$(echo -n ${d} | grep -o Legacy || true)"
|
||||||
ARM64="$(echo -n ${d} | grep -o arm64 || true)"
|
|
||||||
INST="$(echo -n ${d} | grep -o Setup || true)"
|
INST="$(echo -n ${d} | grep -o Setup || true)"
|
||||||
PORT="$(echo -n ${d} | grep -o Portable || true)"
|
PORT="$(echo -n ${d} | grep -o Portable || true)"
|
||||||
NAME="PrismLauncher-Windows-MSVC"
|
NAME="PolyMC-Windows"
|
||||||
test -z "${LEGACY}" || NAME="${NAME}-Legacy"
|
test -z "${LEGACY}" || NAME="${NAME}-Legacy"
|
||||||
test -z "${ARM64}" || NAME="${NAME}-arm64"
|
|
||||||
test -z "${PORT}" || NAME="${NAME}-Portable"
|
test -z "${PORT}" || NAME="${NAME}-Portable"
|
||||||
test -z "${INST}" || mv PrismLauncher-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
|
test -z "${INST}" || mv PolyMC-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
|
||||||
test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" *
|
|
||||||
cd ..
|
|
||||||
done
|
|
||||||
|
|
||||||
for d in PrismLauncher-Windows-MinGW-w64*; do
|
|
||||||
cd "${d}" || continue
|
|
||||||
INST="$(echo -n ${d} | grep -o Setup || true)"
|
|
||||||
PORT="$(echo -n ${d} | grep -o Portable || true)"
|
|
||||||
NAME="PrismLauncher-Windows-MinGW-w64"
|
|
||||||
test -z "${PORT}" || NAME="${NAME}-Portable"
|
|
||||||
test -z "${INST}" || mv PrismLauncher-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
|
|
||||||
test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" *
|
test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" *
|
||||||
cd ..
|
cd ..
|
||||||
done
|
done
|
||||||
@ -80,28 +60,24 @@ jobs:
|
|||||||
- name: Create release
|
- name: Create release
|
||||||
id: create_release
|
id: create_release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
tag_name: ${{ github.ref }}
|
tag_name: ${{ github.ref }}
|
||||||
name: Prism Launcher ${{ env.VERSION }}
|
name: PolyMC ${{ env.VERSION }}
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: false
|
prerelease: false
|
||||||
files: |
|
files: |
|
||||||
PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz
|
PolyMC-Linux-${{ env.VERSION }}.tar.gz
|
||||||
PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
|
PolyMC-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||||
PrismLauncher-Linux-x86_64.AppImage
|
PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||||
PrismLauncher-Linux-x86_64.AppImage.zsync
|
PolyMC-Windows-Legacy-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
PolyMC-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||||
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
PolyMC-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||||
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip
|
PolyMC-Windows-Legacy-Portable-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip
|
PolyMC-Windows-Legacy-Setup-${{ env.VERSION }}.exe
|
||||||
PrismLauncher-Windows-MinGW-w64-Setup-${{ env.VERSION }}.exe
|
PolyMC-Windows-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.zip
|
PolyMC-Windows-Portable-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MSVC-arm64-Portable-${{ env.VERSION }}.zip
|
PolyMC-Windows-Setup-${{ env.VERSION }}.exe
|
||||||
PrismLauncher-Windows-MSVC-arm64-Setup-${{ env.VERSION }}.exe
|
PolyMC-macOS-${{ env.VERSION }}.tar.gz
|
||||||
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.zip
|
PolyMC-${{ env.VERSION }}.tar.gz
|
||||||
PrismLauncher-Windows-MSVC-Portable-${{ env.VERSION }}.zip
|
|
||||||
PrismLauncher-Windows-MSVC-Setup-${{ env.VERSION }}.exe
|
|
||||||
PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
|
|
||||||
PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
|
||||||
PrismLauncher-${{ env.VERSION }}.tar.gz
|
|
||||||
|
30
.github/workflows/update-flake.yml
vendored
30
.github/workflows/update-flake.yml
vendored
@ -1,30 +0,0 @@
|
|||||||
name: Update Flake Lockfile
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
# run weekly on sunday
|
|
||||||
- cron: "0 0 * * 0"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
update-flake:
|
|
||||||
if: github.repository == 'PrismLauncher/PrismLauncher'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: cachix/install-nix-action@6a9a9e84a173d90b3ffb42c5ddaf9ea033fad011 # v23
|
|
||||||
|
|
||||||
- uses: DeterminateSystems/update-flake-lock@v20
|
|
||||||
with:
|
|
||||||
commit-msg: "chore(nix): update lockfile"
|
|
||||||
pr-title: "chore(nix): update lockfile"
|
|
||||||
pr-labels: |
|
|
||||||
Linux
|
|
||||||
packaging
|
|
||||||
simple change
|
|
||||||
changelog:omit
|
|
7
.github/workflows/winget.yml
vendored
7
.github/workflows/winget.yml
vendored
@ -7,9 +7,8 @@ jobs:
|
|||||||
publish:
|
publish:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: vedantmgoyal2009/winget-releaser@v2
|
- uses: vedantmgoyal2009/winget-releaser@latest
|
||||||
with:
|
with:
|
||||||
identifier: PrismLauncher.PrismLauncher
|
identifier: PolyMC.PolyMC
|
||||||
version: ${{ github.event.release.tag_name }}
|
installers-regex: '\.exe$'
|
||||||
installers-regex: 'PrismLauncher-Windows-MSVC(:?-arm64|-Legacy)?-Setup-.+\.exe$'
|
|
||||||
token: ${{ secrets.WINGET_TOKEN }}
|
token: ${{ secrets.WINGET_TOKEN }}
|
||||||
|
11
.gitignore
vendored
11
.gitignore
vendored
@ -11,14 +11,10 @@ html/
|
|||||||
*.pro.user
|
*.pro.user
|
||||||
CMakeLists.txt.user
|
CMakeLists.txt.user
|
||||||
CMakeLists.txt.user.*
|
CMakeLists.txt.user.*
|
||||||
CMakeSettings.json
|
|
||||||
/CMakeFiles
|
|
||||||
CMakeCache.txt
|
|
||||||
/.project
|
/.project
|
||||||
/.settings
|
/.settings
|
||||||
/.idea
|
/.idea
|
||||||
/.vscode
|
/.vscode
|
||||||
/.vs
|
|
||||||
cmake-build-*/
|
cmake-build-*/
|
||||||
Debug
|
Debug
|
||||||
|
|
||||||
@ -46,13 +42,8 @@ run/
|
|||||||
.cache/
|
.cache/
|
||||||
|
|
||||||
# Nix/NixOS
|
# Nix/NixOS
|
||||||
.direnv/
|
result/
|
||||||
.pre-commit-config.yaml
|
|
||||||
result
|
|
||||||
|
|
||||||
# Flatpak
|
# Flatpak
|
||||||
.flatpak-builder
|
.flatpak-builder
|
||||||
flatbuild
|
flatbuild
|
||||||
|
|
||||||
# Snap
|
|
||||||
*.snap
|
|
||||||
|
26
.gitmodules
vendored
26
.gitmodules
vendored
@ -1,24 +1,8 @@
|
|||||||
|
[submodule "depends/libnbtplusplus"]
|
||||||
|
path = libraries/libnbtplusplus
|
||||||
|
url = https://github.com/PolyMC/libnbtplusplus.git
|
||||||
|
pushurl = git@github.com:PolyMC/libnbtplusplus.git
|
||||||
|
|
||||||
[submodule "libraries/quazip"]
|
[submodule "libraries/quazip"]
|
||||||
path = libraries/quazip
|
path = libraries/quazip
|
||||||
url = https://github.com/stachenov/quazip.git
|
url = https://github.com/stachenov/quazip.git
|
||||||
[submodule "libraries/tomlplusplus"]
|
|
||||||
path = libraries/tomlplusplus
|
|
||||||
url = https://github.com/marzer/tomlplusplus.git
|
|
||||||
[submodule "libraries/filesystem"]
|
|
||||||
path = libraries/filesystem
|
|
||||||
url = https://github.com/gulrak/filesystem
|
|
||||||
[submodule "libraries/libnbtplusplus"]
|
|
||||||
path = libraries/libnbtplusplus
|
|
||||||
url = https://github.com/PrismLauncher/libnbtplusplus.git
|
|
||||||
[submodule "libraries/zlib"]
|
|
||||||
path = libraries/zlib
|
|
||||||
url = https://github.com/madler/zlib.git
|
|
||||||
[submodule "libraries/extra-cmake-modules"]
|
|
||||||
path = libraries/extra-cmake-modules
|
|
||||||
url = https://github.com/KDE/extra-cmake-modules
|
|
||||||
[submodule "libraries/cmark"]
|
|
||||||
path = libraries/cmark
|
|
||||||
url = https://github.com/commonmark/cmark.git
|
|
||||||
[submodule "flatpak/shared-modules"]
|
|
||||||
path = flatpak/shared-modules
|
|
||||||
url = https://github.com/flathub/shared-modules.git
|
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
# MD013/line-length - Line length
|
|
||||||
MD013: false
|
|
||||||
|
|
||||||
# MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content
|
|
||||||
MD024:
|
|
||||||
siblings-only: true
|
|
||||||
|
|
||||||
# MD033/no-inline-html Inline HTML
|
|
||||||
MD033: false
|
|
||||||
|
|
||||||
# MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading
|
|
||||||
MD041: false
|
|
@ -1,2 +0,0 @@
|
|||||||
libraries/nbtplusplus
|
|
||||||
libraries/quazip
|
|
4
BUILD.md
4
BUILD.md
@ -1,3 +1,5 @@
|
|||||||
# Build Instructions
|
# Build Instructions
|
||||||
|
|
||||||
Full build instructions are available on [the website](https://prismlauncher.org/wiki/development/build-instructions/).
|
Build instructions are available on [the website](https://polymc.org/wiki/development/build-instructions/).
|
||||||
|
|
||||||
|
If you would like to contribute or fix an issue with the Build instructions you can do so [here](https://github.com/PolyMC/polymc.github.io/blob/master/src/wiki/development/build-instructions.md).
|
||||||
|
356
CMakeLists.txt
356
CMakeLists.txt
@ -1,5 +1,10 @@
|
|||||||
cmake_minimum_required(VERSION 3.15) # minimum version required by QuaZip
|
cmake_minimum_required(VERSION 3.15) # minimum version required by QuaZip
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
# In Qt 5.1+ we have our own main() function, don't autolink to qtmain on Windows
|
||||||
|
cmake_policy(SET CMP0020 OLD)
|
||||||
|
endif()
|
||||||
|
|
||||||
project(Launcher)
|
project(Launcher)
|
||||||
|
|
||||||
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
|
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
|
||||||
@ -27,58 +32,9 @@ set(CMAKE_C_STANDARD_REQUIRED true)
|
|||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
include(GenerateExportHeader)
|
include(GenerateExportHeader)
|
||||||
if(MSVC)
|
set(CMAKE_CXX_FLAGS "-Wall -pedantic -fstack-protector-strong --param=ssp-buffer-size=4 ${CMAKE_CXX_FLAGS}")
|
||||||
# /GS Adds buffer security checks, default on but incuded anyway to mirror gcc's fstack-protector flag
|
if(UNIX AND APPLE)
|
||||||
# /permissive- specify standards-conforming compiler behavior, also enabled by Qt6, default on with std:c++20
|
set(CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}")
|
||||||
# Use /W4 as /Wall includes unnesserey warnings such as added padding to structs
|
|
||||||
set(CMAKE_CXX_FLAGS "/GS /permissive- /W4 ${CMAKE_CXX_FLAGS}")
|
|
||||||
|
|
||||||
# /EHs Enables stack unwind semantics for standard C++ exceptions to ensure stackframes are unwound
|
|
||||||
# and object deconstructors are called when an exception is caught.
|
|
||||||
# without it memory leaks and a warning is printed
|
|
||||||
# /EHc tells the compiler to assume that functions declared as extern "C" never throw a C++ exception
|
|
||||||
# This appears to not always be a defualt compiler option in CMAKE
|
|
||||||
set(CMAKE_CXX_FLAGS "/EHsc ${CMAKE_CXX_FLAGS}")
|
|
||||||
|
|
||||||
# LINK accepts /SUBSYSTEM whics sets if we are a WINDOWS (gui) or a CONSOLE programs
|
|
||||||
# This implicitly selects an entrypoint specific to the subsystem selected
|
|
||||||
# qtmain/QtEntryPointLib provides the correct entrypoint (wWinMain) for gui programs
|
|
||||||
# Additinaly LINK autodetects we use a GUI so we can omit /SUBSYSTEM
|
|
||||||
# This allows tests to still use have console without using seperate linker flags
|
|
||||||
# /LTCG allows for linking wholy optimizated programs
|
|
||||||
# /MANIFEST:NO disables generating a manifest file, we instead provide our own
|
|
||||||
# /STACK sets the stack reserve size, ATL's pack list needs 3-4 MiB as of November 2022, provide 8 MiB
|
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "/LTCG /MANIFEST:NO /STACK:8388608 ${CMAKE_EXE_LINKER_FLAGS}")
|
|
||||||
|
|
||||||
# /GL enables whole program optimizations
|
|
||||||
# /Gw helps reduce binary size
|
|
||||||
# /Gy allows the compiler to package individual functions
|
|
||||||
# /guard:cf enables control flow guard
|
|
||||||
foreach(lang C CXX)
|
|
||||||
set("CMAKE_${lang}_FLAGS_RELEASE" "/GL /Gw /Gy /guard:cf")
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
# See https://github.com/ccache/ccache/issues/1040
|
|
||||||
# Note, CMake 3.25 replaces this with CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
|
|
||||||
# See https://cmake.org/cmake/help/v3.25/variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT.html
|
|
||||||
foreach(config DEBUG RELWITHDEBINFO)
|
|
||||||
foreach(lang C CXX)
|
|
||||||
set(flags_var "CMAKE_${lang}_FLAGS_${config}")
|
|
||||||
string(REGEX REPLACE "/Z[Ii]" "/Z7" ${flags_var} "${${flags_var}}")
|
|
||||||
endforeach()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
if(CMAKE_MSVC_RUNTIME_LIBRARY STREQUAL "MultiThreadedDLL")
|
|
||||||
set(CMAKE_MAP_IMPORTED_CONFIG_DEBUG Release "")
|
|
||||||
set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release "")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
set(CMAKE_CXX_FLAGS "-Wall -pedantic -fstack-protector-strong --param=ssp-buffer-size=4 ${CMAKE_CXX_FLAGS}")
|
|
||||||
|
|
||||||
# ATL's pack list needs more than the default 1 Mib stack on windows
|
|
||||||
if(WIN32)
|
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,8388608 ${CMAKE_EXE_LINKER_FLAGS}")
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Fix build with Qt 5.13
|
# Fix build with Qt 5.13
|
||||||
@ -86,63 +42,20 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y")
|
|||||||
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_BEFORE=0x050C00")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_BEFORE=0x050C00")
|
||||||
|
|
||||||
# Fix aarch64 build for toml++
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0")
|
|
||||||
|
|
||||||
# set CXXFLAGS for build targets
|
# set CXXFLAGS for build targets
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
|
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||||
|
|
||||||
option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF)
|
|
||||||
|
|
||||||
# If this is a Debug build turn on address sanitiser
|
|
||||||
if ((CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") AND DEBUG_ADDRESS_SANITIZER)
|
|
||||||
message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off")
|
|
||||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
|
||||||
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
|
||||||
# using clang with clang-cl front end
|
|
||||||
message(STATUS "Address Sanitizer available on Clang MSVC frontend")
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
|
|
||||||
else()
|
|
||||||
# AppleClang and Clang
|
|
||||||
message(STATUS "Address Sanitizer available on Clang")
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
|
||||||
endif()
|
|
||||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
|
||||||
# GCC
|
|
||||||
message(STATUS "Address Sanitizer available on GCC")
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
|
||||||
link_libraries("asan")
|
|
||||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|
||||||
message(STATUS "Address Sanitizer available on MSVC")
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
|
|
||||||
else()
|
|
||||||
message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
option(ENABLE_LTO "Enable Link Time Optimization" off)
|
option(ENABLE_LTO "Enable Link Time Optimization" off)
|
||||||
|
|
||||||
if(ENABLE_LTO)
|
if(ENABLE_LTO)
|
||||||
include(CheckIPOSupported)
|
include(CheckIPOSupported)
|
||||||
check_ipo_supported(RESULT ipo_supported OUTPUT ipo_error)
|
check_ipo_supported(RESULT ipo_supported OUTPUT ipo_error)
|
||||||
|
|
||||||
if(ipo_supported)
|
if(ipo_supported AND (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel"))
|
||||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
|
message(STATUS "IPO / LTO enabled")
|
||||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL TRUE)
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||||
if(CMAKE_BUILD_TYPE)
|
elseif(ipo_supported)
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
|
message(STATUS "Not enabling IPO / LTO on debug builds")
|
||||||
message(STATUS "IPO / LTO enabled")
|
|
||||||
else()
|
|
||||||
message(STATUS "Not enabling IPO / LTO on debug builds")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
message(STATUS "IPO / LTO will only be enabled for release builds")
|
|
||||||
endif()
|
|
||||||
else()
|
else()
|
||||||
message(STATUS "IPO / LTO not supported: <${ipo_error}>")
|
message(STATUS "IPO / LTO not supported: <${ipo_error}>")
|
||||||
endif()
|
endif()
|
||||||
@ -150,86 +63,59 @@ endif()
|
|||||||
|
|
||||||
option(BUILD_TESTING "Build the testing tree." ON)
|
option(BUILD_TESTING "Build the testing tree." ON)
|
||||||
|
|
||||||
find_package(ECM QUIET NO_MODULE)
|
find_package(ECM REQUIRED NO_MODULE)
|
||||||
if(NOT ECM_FOUND)
|
set(CMAKE_MODULE_PATH "${ECM_MODULE_PATH};${CMAKE_MODULE_PATH}")
|
||||||
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/extra-cmake-modules/CMakeLists.txt")
|
|
||||||
message(STATUS "Using bundled ECM")
|
|
||||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/libraries/extra-cmake-modules/modules;${CMAKE_MODULE_PATH}")
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR
|
|
||||||
" Could not find ECM\n \n"
|
|
||||||
" Either install ECM using the system package manager or clone submodules\n"
|
|
||||||
" Submodules can be cloned with 'git submodule update --init --recursive'")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
set(CMAKE_MODULE_PATH "${ECM_MODULE_PATH};${CMAKE_MODULE_PATH}")
|
|
||||||
endif()
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
include(ECMAddTests)
|
include(ECMAddTests)
|
||||||
if(BUILD_TESTING)
|
if (BUILD_TESTING)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
##################################### Set Application options #####################################
|
##################################### Set Application options #####################################
|
||||||
|
|
||||||
######## Set URLs ########
|
######## Set URLs ########
|
||||||
set(Launcher_NEWS_RSS_URL "https://prismlauncher.org/feed/feed.xml" CACHE STRING "URL to fetch Prism Launcher's news RSS feed from.")
|
set(Launcher_NEWS_RSS_URL "https://polymc.org/feed/feed.xml" CACHE STRING "URL to fetch PolyMC's news RSS feed from.")
|
||||||
set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'")
|
set(Launcher_NEWS_OPEN_URL "https://polymc.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'")
|
||||||
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
set(Launcher_HELP_URL "https://polymc.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
||||||
|
|
||||||
######## Set version numbers ########
|
######## Set version numbers ########
|
||||||
set(Launcher_VERSION_MAJOR 8)
|
set(Launcher_VERSION_MAJOR 1)
|
||||||
set(Launcher_VERSION_MINOR 0)
|
set(Launcher_VERSION_MINOR 4)
|
||||||
|
set(Launcher_VERSION_HOTFIX 2)
|
||||||
|
|
||||||
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
# Build number
|
||||||
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
|
set(Launcher_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
|
||||||
set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0")
|
|
||||||
|
|
||||||
# Build platform.
|
# Build platform.
|
||||||
set(Launcher_BUILD_PLATFORM "unknown" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.")
|
set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.")
|
||||||
|
|
||||||
# Github repo URL with releases for updater
|
# Channel list URL
|
||||||
set(Launcher_UPDATER_GITHUB_REPO "https://github.com/PrismLauncher/PrismLauncher" CACHE STRING "Base github URL for the updater.")
|
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
|
||||||
|
|
||||||
# Name to help updater identify valid artifacts
|
|
||||||
set(Launcher_BUILD_ARTIFACT "" CACHE STRING "Artifact name to help the updater identify valid artifacts.")
|
|
||||||
|
|
||||||
# The metadata server
|
# The metadata server
|
||||||
set(Launcher_META_URL "https://meta.prismlauncher.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
|
set(Launcher_META_URL "https://meta.polymc.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
|
||||||
|
|
||||||
# Imgur API Client ID
|
# Imgur API Client ID
|
||||||
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
|
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
|
||||||
|
|
||||||
# Bug tracker URL
|
# Bug tracker URL
|
||||||
set(Launcher_BUG_TRACKER_URL "https://github.com/PrismLauncher/PrismLauncher/issues" CACHE STRING "URL for the bug tracker.")
|
set(Launcher_BUG_TRACKER_URL "https://github.com/PolyMC/PolyMC/issues" CACHE STRING "URL for the bug tracker.")
|
||||||
|
|
||||||
# Translations Platform URL
|
# Translations Platform URL
|
||||||
set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/prismlauncher/launcher/" CACHE STRING "URL for the translations platform.")
|
set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/polymc/polymc/" CACHE STRING "URL for the translations platform.")
|
||||||
|
|
||||||
# Matrix Space
|
# Matrix Space
|
||||||
set(Launcher_MATRIX_URL "https://prismlauncher.org/matrix" CACHE STRING "URL to the Matrix Space")
|
set(Launcher_MATRIX_URL "https://matrix.to/#/#polymc:matrix.org" CACHE STRING "URL to the Matrix Space")
|
||||||
|
|
||||||
# Discord URL
|
# Discord URL
|
||||||
set(Launcher_DISCORD_URL "https://prismlauncher.org/discord" CACHE STRING "URL for the Discord guild.")
|
set(Launcher_DISCORD_URL "https://discord.gg/Z52pwxWCHP" CACHE STRING "URL for the Discord guild.")
|
||||||
|
|
||||||
# Subreddit URL
|
# Subreddit URL
|
||||||
set(Launcher_SUBREDDIT_URL "https://prismlauncher.org/reddit" CACHE STRING "URL for the subreddit.")
|
set(Launcher_SUBREDDIT_URL "https://www.reddit.com/r/PolyMCLauncher/" CACHE STRING "URL for the subreddit.")
|
||||||
|
|
||||||
# Builds
|
# Builds
|
||||||
set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
|
set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
|
||||||
set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against")
|
set(Launcher_QT_VERSION_MAJOR "5" CACHE STRING "Major Qt version to build against")
|
||||||
|
|
||||||
# Native libraries
|
|
||||||
if(UNIX AND APPLE)
|
|
||||||
set(Launcher_GLFW_LIBRARY_NAME "libglfw.dylib" CACHE STRING "Name of native glfw library")
|
|
||||||
set(Launcher_OPENAL_LIBRARY_NAME "libopenal.dylib" CACHE STRING "Name of native openal library")
|
|
||||||
elseif(UNIX)
|
|
||||||
set(Launcher_GLFW_LIBRARY_NAME "libglfw.so" CACHE STRING "Name of native glfw library")
|
|
||||||
set(Launcher_OPENAL_LIBRARY_NAME "libopenal.so" CACHE STRING "Name of native openal library")
|
|
||||||
elseif(WIN32)
|
|
||||||
set(Launcher_GLFW_LIBRARY_NAME "glfw.dll" CACHE STRING "Name of native glfw library")
|
|
||||||
set(Launcher_OPENAL_LIBRARY_NAME "OpenAL.dll" CACHE STRING "Name of native openal library")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# API Keys
|
# API Keys
|
||||||
# NOTE: These API keys are here for convenience. If you rebrand this software or intend to break the terms of service
|
# NOTE: These API keys are here for convenience. If you rebrand this software or intend to break the terms of service
|
||||||
@ -240,19 +126,13 @@ endif()
|
|||||||
|
|
||||||
# By using this key in your builds you accept the terms of use laid down in
|
# By using this key in your builds you accept the terms of use laid down in
|
||||||
# https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use
|
# https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use
|
||||||
set(Launcher_MSA_CLIENT_ID "c36a9fb6-4f2a-41ff-90bd-ae7cc92031eb" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
|
set(Launcher_MSA_CLIENT_ID "549033b2-1532-4d4e-ae77-1bbaa46f9d74" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
|
||||||
|
|
||||||
# By using this key in your builds you accept the terms and conditions laid down in
|
# By using this key in your builds you accept the terms and conditions laid down in
|
||||||
# https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions
|
# https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions
|
||||||
# NOTE: CurseForge requires you to change this if you make any kind of derivative work.
|
# NOTE: CurseForge requires you to change this if you make any kind of derivative work.
|
||||||
# This key was issued specifically for Prism Launcher
|
set(Launcher_CURSEFORGE_API_KEY "$2a$10$1Oqr2MX3O4n/ilhFGc597u8tfI3L2Hyr9/rtWDAMRjghSQV2QUuxq" CACHE STRING "API key for the CurseForge platform")
|
||||||
set(Launcher_CURSEFORGE_API_KEY "$2a$10$wuAJuNZuted3NORVmpgUC.m8sI.pv1tOPKZyBgLFGjxFp/br0lZCC" CACHE STRING "API key for the CurseForge platform")
|
|
||||||
|
|
||||||
set(Launcher_COMPILER_NAME ${CMAKE_CXX_COMPILER_ID})
|
|
||||||
set(Launcher_COMPILER_VERSION ${CMAKE_CXX_COMPILER_VERSION})
|
|
||||||
set(Launcher_COMPILER_TARGET_SYSTEM ${CMAKE_SYSTEM_NAME})
|
|
||||||
set(Launcher_COMPILER_TARGET_SYSTEM_VERSION ${CMAKE_SYSTEM_VERSION})
|
|
||||||
set(Launcher_COMPILER_TARGET_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR})
|
|
||||||
|
|
||||||
#### Check the current Git commit and branch
|
#### Check the current Git commit and branch
|
||||||
include(GetGitRevisionDescription)
|
include(GetGitRevisionDescription)
|
||||||
@ -263,21 +143,18 @@ message(STATUS "Git commit: ${Launcher_GIT_COMMIT}")
|
|||||||
message(STATUS "Git tag: ${Launcher_GIT_TAG}")
|
message(STATUS "Git tag: ${Launcher_GIT_TAG}")
|
||||||
message(STATUS "Git refspec: ${Launcher_GIT_REFSPEC}")
|
message(STATUS "Git refspec: ${Launcher_GIT_REFSPEC}")
|
||||||
|
|
||||||
|
set(Launcher_RELEASE_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
||||||
|
set(Launcher_RELEASE_VERSION_NAME4 "${Launcher_RELEASE_VERSION_NAME}.0")
|
||||||
|
set(Launcher_RELEASE_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},${Launcher_VERSION_HOTFIX},0")
|
||||||
string(TIMESTAMP TODAY "%Y-%m-%d")
|
string(TIMESTAMP TODAY "%Y-%m-%d")
|
||||||
set(Launcher_BUILD_TIMESTAMP "${TODAY}")
|
set(Launcher_RELEASE_TIMESTAMP "${TODAY}")
|
||||||
|
|
||||||
|
#### Custom target to just print the version.
|
||||||
|
add_custom_target(version echo "Version: ${Launcher_RELEASE_VERSION_NAME}")
|
||||||
|
add_custom_target(tcversion echo "\\#\\#teamcity[setParameter name=\\'env.LAUNCHER_VERSION\\' value=\\'${Launcher_RELEASE_VERSION_NAME}\\']")
|
||||||
|
|
||||||
################################ 3rd Party Libs ################################
|
################################ 3rd Party Libs ################################
|
||||||
|
|
||||||
# Successive configurations of cmake without cleaning the build dir will cause zlib fallback to fail due to cached values
|
|
||||||
# Record when fallback triggered and skip this find_package
|
|
||||||
if(NOT Launcher_FORCE_BUNDLED_LIBS AND NOT FORCE_BUNDLED_ZLIB)
|
|
||||||
find_package(ZLIB QUIET)
|
|
||||||
endif()
|
|
||||||
if(NOT ZLIB_FOUND)
|
|
||||||
set(FORCE_BUNDLED_ZLIB TRUE CACHE BOOL "")
|
|
||||||
mark_as_advanced(FORCE_BUNDLED_ZLIB)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Find the required Qt parts
|
# Find the required Qt parts
|
||||||
include(QtVersionlessBackport)
|
include(QtVersionlessBackport)
|
||||||
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
||||||
@ -296,7 +173,7 @@ if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
|||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUNICODE -D_UNICODE")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUNICODE -D_UNICODE")
|
||||||
elseif(Launcher_QT_VERSION_MAJOR EQUAL 6)
|
elseif(Launcher_QT_VERSION_MAJOR EQUAL 6)
|
||||||
set(QT_VERSION_MAJOR 6)
|
set(QT_VERSION_MAJOR 6)
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml Core5Compat)
|
find_package(Qt6 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml Core5Compat)
|
||||||
list(APPEND Launcher_QT_LIBS Qt6::Core5Compat)
|
list(APPEND Launcher_QT_LIBS Qt6::Core5Compat)
|
||||||
|
|
||||||
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
||||||
@ -310,49 +187,25 @@ else()
|
|||||||
message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported")
|
message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
include(ECMQueryQt)
|
||||||
include(ECMQueryQt)
|
ecm_query_qt(QT_PLUGINS_DIR QT_INSTALL_PLUGINS)
|
||||||
ecm_query_qt(QT_PLUGINS_DIR QT_INSTALL_PLUGINS)
|
ecm_query_qt(QT_LIBS_DIR QT_INSTALL_LIBS)
|
||||||
ecm_query_qt(QT_LIBS_DIR QT_INSTALL_LIBS)
|
ecm_query_qt(QT_LIBEXECS_DIR QT_INSTALL_LIBEXECS)
|
||||||
ecm_query_qt(QT_LIBEXECS_DIR QT_INSTALL_LIBEXECS)
|
ecm_query_qt(QT_DATA_DIR QT_HOST_DATA)
|
||||||
else()
|
set(QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs)
|
||||||
set(QT_PLUGINS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_PLUGINS})
|
|
||||||
set(QT_LIBS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBS})
|
|
||||||
set(QT_LIBEXECS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBEXECS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# NOTE: Qt 6 already sets this by default
|
# NOTE: Qt 6 already sets this by default
|
||||||
if (Qt5_POSITION_INDEPENDENT_CODE)
|
if (Qt5_POSITION_INDEPENDENT_CODE)
|
||||||
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
|
||||||
# Find toml++
|
|
||||||
find_package(tomlplusplus 3.2.0 QUIET)
|
|
||||||
|
|
||||||
# Find ghc_filesystem
|
|
||||||
find_package(ghc_filesystem QUIET)
|
|
||||||
|
|
||||||
# Find cmark
|
|
||||||
find_package(cmark QUIET)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include(ECMQtDeclareLoggingCategory)
|
|
||||||
|
|
||||||
####################################### Program Info #######################################
|
####################################### Program Info #######################################
|
||||||
|
|
||||||
set(Launcher_APP_BINARY_NAME "prismlauncher" CACHE STRING "Name of the Launcher binary")
|
set(Launcher_APP_BINARY_NAME "polymc" CACHE STRING "Name of the Launcher binary")
|
||||||
add_subdirectory(program_info)
|
add_subdirectory(program_info)
|
||||||
|
|
||||||
####################################### Install layout #######################################
|
####################################### Install layout #######################################
|
||||||
|
|
||||||
set(Launcher_ENABLE_UPDATER NO)
|
|
||||||
set(Launcher_BUILD_UPDATER NO)
|
|
||||||
|
|
||||||
if (NOT APPLE AND (NOT Launcher_UPDATER_GITHUB_REPO STREQUAL "" AND NOT Launcher_BUILD_ARTIFACT STREQUAL ""))
|
|
||||||
set(Launcher_BUILD_UPDATER YES)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT (UNIX AND APPLE))
|
if(NOT (UNIX AND APPLE))
|
||||||
# Install "portable.txt" if selected component is "portable"
|
# Install "portable.txt" if selected component is "portable"
|
||||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_Portable_File}" DESTINATION "." COMPONENT portable EXCLUDE_FROM_ALL)
|
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_Portable_File}" DESTINATION "." COMPONENT portable EXCLUDE_FROM_ALL)
|
||||||
@ -370,16 +223,16 @@ if(UNIX AND APPLE)
|
|||||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.app")
|
set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.app")
|
||||||
|
|
||||||
# Mac bundle settings
|
# Mac bundle settings
|
||||||
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_DisplayName}")
|
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_Name}")
|
||||||
set(MACOSX_BUNDLE_INFO_STRING "${Launcher_DisplayName}: A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.")
|
set(MACOSX_BUNDLE_INFO_STRING "${Launcher_Name}: A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.")
|
||||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.prismlauncher.${Launcher_Name}")
|
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.polymc.${Launcher_Name}")
|
||||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_NAME}")
|
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
||||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}")
|
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
||||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}")
|
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
||||||
set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns)
|
set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns)
|
||||||
set(MACOSX_BUNDLE_COPYRIGHT "© 2022-2023 ${Launcher_Copyright_Mac}")
|
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2021-2022 ${Launcher_Copyright}")
|
||||||
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=" CACHE STRING "Public key for Sparkle update feed")
|
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "idALcUIazingvKSSsEa9U7coDVxZVx/ORpOEE/QtJfg=")
|
||||||
set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml" CACHE STRING "URL for Sparkle update feed")
|
set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://polymc.org/feed/appcast.xml")
|
||||||
|
|
||||||
set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0/Sparkle-2.1.0.tar.xz" CACHE STRING "URL to Sparkle release archive")
|
set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0/Sparkle-2.1.0.tar.xz" CACHE STRING "URL to Sparkle release archive")
|
||||||
set(MACOSX_SPARKLE_SHA256 "bf6ac1caa9f8d321d5784859c88da874f28412f37fb327bc21b7b14c5d61ef94" CACHE STRING "SHA256 checksum for Sparkle release archive")
|
set(MACOSX_SPARKLE_SHA256 "bf6ac1caa9f8d321d5784859c88da874f28412f37fb327bc21b7b14c5d61ef94" CACHE STRING "SHA256 checksum for Sparkle release archive")
|
||||||
@ -388,38 +241,33 @@ if(UNIX AND APPLE)
|
|||||||
# directories to look for dependencies
|
# directories to look for dependencies
|
||||||
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${MACOSX_SPARKLE_DIR})
|
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${MACOSX_SPARKLE_DIR})
|
||||||
|
|
||||||
if(NOT MACOSX_SPARKLE_UPDATE_PUBLIC_KEY STREQUAL "" AND NOT MACOSX_SPARKLE_UPDATE_FEED_URL STREQUAL "")
|
|
||||||
set(Launcher_ENABLE_UPDATER YES)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# install as bundle
|
# install as bundle
|
||||||
set(INSTALL_BUNDLE "full" CACHE STRING "Use fixup_bundle to bundle dependencies")
|
set(INSTALL_BUNDLE "full")
|
||||||
|
|
||||||
# Add the icon
|
# Add the icon
|
||||||
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
|
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
|
||||||
|
|
||||||
elseif(UNIX)
|
elseif(UNIX)
|
||||||
include(KDEInstallDirs)
|
|
||||||
|
|
||||||
set(BINARY_DEST_DIR "bin")
|
set(BINARY_DEST_DIR "bin")
|
||||||
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
|
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
|
||||||
set(JARS_DEST_DIR "share/${Launcher_Name}")
|
set(JARS_DEST_DIR "share/jars")
|
||||||
|
set(LAUNCHER_DESKTOP_DEST_DIR "share/applications" CACHE STRING "Path to the desktop file directory")
|
||||||
|
set(LAUNCHER_METAINFO_DEST_DIR "share/metainfo" CACHE STRING "Path to the metainfo directory")
|
||||||
|
set(LAUNCHER_ICON_DEST_DIR "share/icons/hicolor/scalable/apps" CACHE STRING "Path to the scalable icon directory")
|
||||||
|
set(LAUNCHER_MAN_DEST_DIR "share/man/man6" CACHE STRING "Path to the man page directory")
|
||||||
|
|
||||||
# install as bundle with no dependencies included
|
# install as bundle with no dependencies included
|
||||||
set(INSTALL_BUNDLE "nodeps" CACHE STRING "Use fixup_bundle to bundle dependencies")
|
set(INSTALL_BUNDLE "nodeps")
|
||||||
|
|
||||||
# Set RPATH
|
# Set RPATH
|
||||||
SET(Launcher_BINARY_RPATH "$ORIGIN/")
|
SET(Launcher_BINARY_RPATH "$ORIGIN/")
|
||||||
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${KDE_INSTALL_APPDIR})
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${LAUNCHER_DESKTOP_DEST_DIR})
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${KDE_INSTALL_METAINFODIR})
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${LAUNCHER_METAINFO_DEST_DIR})
|
||||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION "${KDE_INSTALL_ICONDIR}/hicolor/scalable/apps")
|
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION ${LAUNCHER_ICON_DEST_DIR})
|
||||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
|
|
||||||
|
|
||||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "share/${Launcher_Name}")
|
|
||||||
|
|
||||||
if(Launcher_ManPage)
|
if(Launcher_ManPage)
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION ${LAUNCHER_MAN_DEST_DIR})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Install basic runner script if component "portable" is selected
|
# Install basic runner script if component "portable" is selected
|
||||||
@ -440,13 +288,11 @@ elseif(WIN32)
|
|||||||
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
||||||
|
|
||||||
# install as bundle
|
# install as bundle
|
||||||
set(INSTALL_BUNDLE "full" CACHE STRING "Use fixup_bundle to bundle dependencies")
|
set(INSTALL_BUNDLE "full")
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "Platform not supported")
|
message(FATAL_ERROR "Platform not supported")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
################################ Included Libs ################################
|
################################ Included Libs ################################
|
||||||
|
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
@ -458,34 +304,10 @@ option(NBT_BUILD_TESTS "Build NBT library tests" OFF) #FIXME: fix unit tests.
|
|||||||
add_subdirectory(libraries/libnbtplusplus)
|
add_subdirectory(libraries/libnbtplusplus)
|
||||||
|
|
||||||
add_subdirectory(libraries/systeminfo) # system information library
|
add_subdirectory(libraries/systeminfo) # system information library
|
||||||
|
add_subdirectory(libraries/hoedown) # markdown parser
|
||||||
add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
|
add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
|
||||||
add_subdirectory(libraries/javacheck) # java compatibility checker
|
add_subdirectory(libraries/javacheck) # java compatibility checker
|
||||||
if(FORCE_BUNDLED_ZLIB)
|
add_subdirectory(libraries/xz-embedded) # xz compression
|
||||||
message(STATUS "Using bundled zlib")
|
|
||||||
|
|
||||||
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) # Suppress cmake warnings and allow INTERPROCEDURAL_OPTIMIZATION for zlib
|
|
||||||
set(SKIP_INSTALL_ALL ON)
|
|
||||||
add_subdirectory(libraries/zlib EXCLUDE_FROM_ALL)
|
|
||||||
|
|
||||||
# On OS where unistd.h exists, zlib's generated header defines `Z_HAVE_UNISTD_H`, while the included header does not.
|
|
||||||
# We cannot safely undo the rename on those systems, and they generally have packages for zlib anyway.
|
|
||||||
check_include_file(unistd.h NEED_GENERATED_ZCONF)
|
|
||||||
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h.included" AND NOT NEED_GENERATED_ZCONF)
|
|
||||||
# zlib's cmake script renames a file, dirtying the submodule, see https://github.com/madler/zlib/issues/162
|
|
||||||
message(STATUS "Undoing Rename")
|
|
||||||
message(STATUS " ${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h")
|
|
||||||
file(RENAME "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h.included" "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/libraries/zlib" "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib" CACHE STRING "" FORCE)
|
|
||||||
set_target_properties(zlibstatic PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INCLUDE_DIR}")
|
|
||||||
add_library(ZLIB::ZLIB ALIAS zlibstatic)
|
|
||||||
set(ZLIB_LIBRARY ZLIB::ZLIB CACHE STRING "zlib library name")
|
|
||||||
|
|
||||||
find_package(ZLIB REQUIRED)
|
|
||||||
else()
|
|
||||||
message(STATUS "Using system zlib")
|
|
||||||
endif()
|
|
||||||
if (FORCE_BUNDLED_QUAZIP)
|
if (FORCE_BUNDLED_QUAZIP)
|
||||||
message(STATUS "Using bundled QuaZip")
|
message(STATUS "Using bundled QuaZip")
|
||||||
set(BUILD_SHARED_LIBS 0) # link statically to avoid conflicts.
|
set(BUILD_SHARED_LIBS 0) # link statically to avoid conflicts.
|
||||||
@ -496,39 +318,15 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
add_subdirectory(libraries/rainbow) # Qt extension for colors
|
add_subdirectory(libraries/rainbow) # Qt extension for colors
|
||||||
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
|
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
|
||||||
if(NOT tomlplusplus_FOUND)
|
add_subdirectory(libraries/classparser) # class parser library
|
||||||
message(STATUS "Using bundled tomlplusplus")
|
add_subdirectory(libraries/tomlc99) # toml parser
|
||||||
add_subdirectory(libraries/tomlplusplus) # toml parser
|
|
||||||
else()
|
|
||||||
message(STATUS "Using system tomlplusplus")
|
|
||||||
endif()
|
|
||||||
if(NOT cmark_FOUND)
|
|
||||||
message(STATUS "Using bundled cmark")
|
|
||||||
set(CMARK_STATIC ON CACHE BOOL "Build static libcmark library" FORCE)
|
|
||||||
set(CMARK_SHARED OFF CACHE BOOL "Build shared libcmark library" FORCE)
|
|
||||||
set(CMARK_TESTS OFF CACHE BOOL "Build cmark tests and enable testing" FORCE)
|
|
||||||
add_subdirectory(libraries/cmark EXCLUDE_FROM_ALL) # Markdown parser
|
|
||||||
add_library(cmark::cmark ALIAS cmark_static)
|
|
||||||
else()
|
|
||||||
message(STATUS "Using system cmark")
|
|
||||||
endif()
|
|
||||||
add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much
|
add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much
|
||||||
add_subdirectory(libraries/gamemode)
|
add_subdirectory(libraries/gamemode)
|
||||||
add_subdirectory(libraries/murmur2) # Hash for usage with the CurseForge API
|
add_subdirectory(libraries/murmur2) # Hash for usage with the CurseForge API
|
||||||
if (NOT ghc_filesystem_FOUND)
|
|
||||||
message(STATUS "Using bundled ghc_filesystem")
|
|
||||||
add_subdirectory(libraries/filesystem) # Implementation of std::filesystem for old C++, for usage in old macOS
|
|
||||||
else()
|
|
||||||
message(STATUS "Using system ghc_filesystem")
|
|
||||||
endif()
|
|
||||||
add_subdirectory(libraries/qdcss) # css parser
|
|
||||||
|
|
||||||
############################### Built Artifacts ###############################
|
############################### Built Artifacts ###############################
|
||||||
|
|
||||||
add_subdirectory(buildconfig)
|
add_subdirectory(buildconfig)
|
||||||
|
|
||||||
if(BUILD_TESTING)
|
|
||||||
add_subdirectory(tests)
|
|
||||||
endif()
|
|
||||||
# NOTE: this must always be last to appease the CMake deity of quirky install command evaluation order.
|
# NOTE: this must always be last to appease the CMake deity of quirky install command evaluation order.
|
||||||
add_subdirectory(launcher)
|
add_subdirectory(launcher)
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
# Contributor Covenant Code of Conduct
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
This is a modified version of the Contributor Covenant.
|
This is a modified version of the Contributor Covenant.
|
||||||
See commit history to see our changes.
|
See commit history to see our changes.
|
||||||
|
|
||||||
@ -63,7 +62,7 @@ representative at an online or offline event.
|
|||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
reported to the community leaders responsible for enforcement via email at
|
reported to the community leaders responsible for enforcement via email at
|
||||||
[coc@scrumplex.net](mailto:coc@scrumplex.net) (Email
|
[polymc-enforcement@scrumplex.net](mailto:polymc-enforcement@scrumplex.net) (Email
|
||||||
address subject to change).
|
address subject to change).
|
||||||
All complaints will be reviewed and investigated promptly and fairly.
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
@ -134,3 +133,4 @@ For answers to common questions about this code of conduct, see the FAQ at
|
|||||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||||
[FAQ]: https://www.contributor-covenant.org/faq
|
[FAQ]: https://www.contributor-covenant.org/faq
|
||||||
[translations]: https://www.contributor-covenant.org/translations
|
[translations]: https://www.contributor-covenant.org/translations
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ Try to follow the existing formatting.
|
|||||||
If there is no existing formatting, you may use `clang-format` with our included `.clang-format` configuration.
|
If there is no existing formatting, you may use `clang-format` with our included `.clang-format` configuration.
|
||||||
|
|
||||||
In general, in order of importance:
|
In general, in order of importance:
|
||||||
|
|
||||||
- Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
|
- Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
|
||||||
- Prefer readability over dogma.
|
- Prefer readability over dogma.
|
||||||
- Keep to the existing formatting.
|
- Keep to the existing formatting.
|
||||||
@ -19,7 +18,7 @@ In an effort to ensure that the code you contribute is actually compatible with
|
|||||||
|
|
||||||
This can be done by appending `-s` to your `git commit` call, or by manually appending the following text to your commit message:
|
This can be done by appending `-s` to your `git commit` call, or by manually appending the following text to your commit message:
|
||||||
|
|
||||||
```text
|
```
|
||||||
<commit message>
|
<commit message>
|
||||||
|
|
||||||
Signed-off-by: Author name <Author email>
|
Signed-off-by: Author name <Author email>
|
||||||
@ -27,43 +26,37 @@ Signed-off-by: Author name <Author email>
|
|||||||
|
|
||||||
By signing off your work, you agree to the terms below:
|
By signing off your work, you agree to the terms below:
|
||||||
|
|
||||||
```text
|
Developer's Certificate of Origin 1.1
|
||||||
Developer's Certificate of Origin 1.1
|
|
||||||
|
|
||||||
By making a contribution to this project, I certify that:
|
By making a contribution to this project, I certify that:
|
||||||
|
|
||||||
(a) The contribution was created in whole or in part by me and I
|
(a) The contribution was created in whole or in part by me and I
|
||||||
have the right to submit it under the open source license
|
have the right to submit it under the open source license
|
||||||
indicated in the file; or
|
indicated in the file; or
|
||||||
|
|
||||||
(b) The contribution is based upon previous work that, to the best
|
(b) The contribution is based upon previous work that, to the best
|
||||||
of my knowledge, is covered under an appropriate open source
|
of my knowledge, is covered under an appropriate open source
|
||||||
license and I have the right under that license to submit that
|
license and I have the right under that license to submit that
|
||||||
work with modifications, whether created in whole or in part
|
work with modifications, whether created in whole or in part
|
||||||
by me, under the same open source license (unless I am
|
by me, under the same open source license (unless I am
|
||||||
permitted to submit under a different license), as indicated
|
permitted to submit under a different license), as indicated
|
||||||
in the file; or
|
in the file; or
|
||||||
|
|
||||||
(c) The contribution was provided directly to me by some other
|
(c) The contribution was provided directly to me by some other
|
||||||
person who certified (a), (b) or (c) and I have not modified
|
person who certified (a), (b) or (c) and I have not modified
|
||||||
it.
|
it.
|
||||||
|
|
||||||
(d) I understand and agree that this project and the contribution
|
(d) I understand and agree that this project and the contribution
|
||||||
are public and that a record of the contribution (including all
|
are public and that a record of the contribution (including all
|
||||||
personal information I submit with it, including my sign-off) is
|
personal information I submit with it, including my sign-off) is
|
||||||
maintained indefinitely and may be redistributed consistent with
|
maintained indefinitely and may be redistributed consistent with
|
||||||
this project or the open source license(s) involved.
|
this project or the open source license(s) involved.
|
||||||
```
|
|
||||||
|
|
||||||
These terms will be enforced once you create a pull request, and you will be informed automatically if any of your commits aren't signed-off by you.
|
These terms will be enforced once you create a pull request, and you will be informed automatically if any of your commits aren't signed-off by you.
|
||||||
|
|
||||||
As a bonus, you can also [cryptographically sign your commits][gh-signing-commits] and enable [vigilant mode][gh-vigilant-mode] on GitHub.
|
As a bonus, you can also [cryptographically sign your commits][gh-signing-commits] and enable [vigilant mode][gh-vigilant-mode] on GitHub.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[gh-signing-commits]: https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits
|
[gh-signing-commits]: https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits
|
||||||
[gh-vigilant-mode]: https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits
|
[gh-vigilant-mode]: https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits
|
||||||
|
|
||||||
## Backporting to Release Branches
|
|
||||||
|
|
||||||
We use [automated backports](https://github.com/PrismLauncher/PrismLauncher/blob/develop/.github/workflows/backport.yml) to merge specific contributions from develop into `release` branches.
|
|
||||||
|
|
||||||
This is done when pull requests are merged and have labels such as `backport release-7.x` - which should be added along with the milestone for the release.
|
|
||||||
|
235
COPYING.md
235
COPYING.md
@ -1,38 +1,4 @@
|
|||||||
## Prism Launcher
|
# PolyMC
|
||||||
|
|
||||||
Prism Launcher - Minecraft Launcher
|
|
||||||
Copyright (C) 2022-2023 Prism Launcher Contributors
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, version 3.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
This file incorporates work covered by the following copyright and
|
|
||||||
permission notice:
|
|
||||||
|
|
||||||
Copyright 2013-2021 MultiMC Contributors
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
## PolyMC
|
|
||||||
|
|
||||||
PolyMC - Minecraft Launcher
|
PolyMC - Minecraft Launcher
|
||||||
Copyright (C) 2021-2022 PolyMC Contributors
|
Copyright (C) 2021-2022 PolyMC Contributors
|
||||||
@ -66,7 +32,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
## MinGW-w64 runtime (Windows)
|
# MinGW-w64 runtime (Windows)
|
||||||
|
|
||||||
Copyright (c) 2009, 2010, 2011, 2012, 2013 by the mingw-w64 project
|
Copyright (c) 2009, 2010, 2011, 2012, 2013 by the mingw-w64 project
|
||||||
|
|
||||||
@ -108,14 +74,14 @@
|
|||||||
|
|
||||||
Information on third party licenses used in MinGW-w64 can be found in its COPYING.MinGW-w64-runtime.txt.
|
Information on third party licenses used in MinGW-w64 can be found in its COPYING.MinGW-w64-runtime.txt.
|
||||||
|
|
||||||
## Qt 5/6
|
# Qt 5/6
|
||||||
|
|
||||||
Copyright (C) 2022 The Qt Company Ltd and other contributors.
|
Copyright (C) 2022 The Qt Company Ltd and other contributors.
|
||||||
Contact: https://www.qt.io/licensing
|
Contact: https://www.qt.io/licensing
|
||||||
|
|
||||||
Licensed under LGPL v3
|
Licensed under LGPL v3
|
||||||
|
|
||||||
## libnbt++
|
# libnbt++
|
||||||
|
|
||||||
libnbt++ - A library for the Minecraft Named Binary Tag format.
|
libnbt++ - A library for the Minecraft Named Binary Tag format.
|
||||||
Copyright (C) 2013, 2015 ljfa-ag
|
Copyright (C) 2013, 2015 ljfa-ag
|
||||||
@ -133,7 +99,7 @@
|
|||||||
You should have received a copy of the GNU Lesser General Public License
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
|
along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
## rainbow (KGuiAddons)
|
# rainbow (KGuiAddons)
|
||||||
|
|
||||||
Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
|
Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
|
||||||
Copyright (C) 2007 Olaf Schmidt <ojschmidt@kde.org>
|
Copyright (C) 2007 Olaf Schmidt <ojschmidt@kde.org>
|
||||||
@ -156,36 +122,25 @@
|
|||||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
Boston, MA 02110-1301, USA.
|
Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
## cmark
|
# Hoedown
|
||||||
|
|
||||||
Copyright (c) 2014, John MacFarlane
|
Copyright (c) 2008, Natacha Porté
|
||||||
|
Copyright (c) 2011, Vicent Martí
|
||||||
|
Copyright (c) 2014, Xavier Mendez, Devin Torres and the Hoedown authors
|
||||||
|
|
||||||
All rights reserved.
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
modification, are permitted provided that the following conditions are met:
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
# Batch icon set
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following
|
|
||||||
disclaimer in the documentation and/or other materials provided
|
|
||||||
with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
## Batch icon set
|
|
||||||
|
|
||||||
You are free to use Batch (the "icon set") or any part thereof (the "icons")
|
You are free to use Batch (the "icon set") or any part thereof (the "icons")
|
||||||
in any personal, open-source or commercial work without obligation of payment
|
in any personal, open-source or commercial work without obligation of payment
|
||||||
@ -201,7 +156,7 @@
|
|||||||
PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS,
|
PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS,
|
||||||
EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
## Material Design Icons
|
# Material Design Icons
|
||||||
|
|
||||||
Copyright (c) 2014, Austin Andrews (http://materialdesignicons.com/),
|
Copyright (c) 2014, Austin Andrews (http://materialdesignicons.com/),
|
||||||
with Reserved Font Name Material Design Icons.
|
with Reserved Font Name Material Design Icons.
|
||||||
@ -212,7 +167,7 @@
|
|||||||
This license is copied below, and is also available with a FAQ at:
|
This license is copied below, and is also available with a FAQ at:
|
||||||
http://scripts.sil.org/OFL
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
## Quazip
|
# Quazip
|
||||||
|
|
||||||
Copyright (C) 2005-2021 Sergey A. Tachenov
|
Copyright (C) 2005-2021 Sergey A. Tachenov
|
||||||
|
|
||||||
@ -236,7 +191,53 @@
|
|||||||
|
|
||||||
See COPYING file for the full LGPL text.
|
See COPYING file for the full LGPL text.
|
||||||
|
|
||||||
## launcher (`libraries/launcher`)
|
# xz-minidec
|
||||||
|
|
||||||
|
XZ decompressor
|
||||||
|
|
||||||
|
Authors: Lasse Collin <lasse.collin@tukaani.org>
|
||||||
|
Igor Pavlov <http://7-zip.org/>
|
||||||
|
|
||||||
|
This file has been put into the public domain.
|
||||||
|
You can do whatever you want with this file.
|
||||||
|
|
||||||
|
# ColumnResizer
|
||||||
|
|
||||||
|
Copyright (c) 2011-2016 Aurélien Gâteau and contributors.
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted (subject to the limitations in the
|
||||||
|
disclaimer below) provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
* The name of the contributors may not be used to endorse or
|
||||||
|
promote products derived from this software without specific prior
|
||||||
|
written permission.
|
||||||
|
|
||||||
|
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
|
||||||
|
GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
|
||||||
|
HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||||
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||||
|
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||||
|
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
# launcher (`libraries/launcher`)
|
||||||
|
|
||||||
PolyMC - Minecraft Launcher
|
PolyMC - Minecraft Launcher
|
||||||
Copyright (C) 2021-2022 PolyMC Contributors
|
Copyright (C) 2021-2022 PolyMC Contributors
|
||||||
@ -287,7 +288,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
## lionshead
|
# lionshead
|
||||||
|
|
||||||
Code has been taken from https://github.com/natefoo/lionshead and loosely
|
Code has been taken from https://github.com/natefoo/lionshead and loosely
|
||||||
translated to C++ laced with Qt.
|
translated to C++ laced with Qt.
|
||||||
@ -314,26 +315,32 @@
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
## tomlplusplus
|
# tomlc99
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
Copyright (c) 2017 CK Tan
|
||||||
|
https://github.com/cktan/tomlc99
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
in the Software without restriction, including without limitation the rights
|
||||||
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
The above copyright notice and this permission notice shall be included in all
|
||||||
Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
## O2 (Katabasis fork)
|
# O2 (Katabasis fork)
|
||||||
|
|
||||||
Copyright (c) 2012, Akos Polster
|
Copyright (c) 2012, Akos Polster
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
@ -387,67 +394,3 @@
|
|||||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
POSSIBILITY OF SUCH DAMAGE.
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
## gulrak/filesystem
|
|
||||||
|
|
||||||
Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
## Breeze icons
|
|
||||||
|
|
||||||
Copyright (C) 2014 Uri Herrera <uri_herrera@nitrux.in> and others
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 3 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
## Oxygen Icons
|
|
||||||
|
|
||||||
The Oxygen Icon Theme
|
|
||||||
Copyright (C) 2007 Nuno Pinheiro <nuno@oxygen-icons.org>
|
|
||||||
Copyright (C) 2007 David Vignoni <david@icon-king.com>
|
|
||||||
Copyright (C) 2007 David Miller <miller@oxygen-icons.org>
|
|
||||||
Copyright (C) 2007 Johann Ollivier Lapeyre <johann@oxygen-icons.org>
|
|
||||||
Copyright (C) 2007 Kenneth Wimer <kwwii@bootsplash.org>
|
|
||||||
Copyright (C) 2007 Riccardo Iaconelli <riccardo@oxygen-icons.org>
|
|
||||||
|
|
||||||
and others
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 3 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
134
README.md
134
README.md
@ -1,112 +1,98 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<picture>
|
<img src="./program_info/polymc-header-black.svg#gh-light-mode-only" alt="PolyMC logo" width="50%"/>
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="/program_info/org.prismlauncher.PrismLauncher.logo-darkmode.svg">
|
<img src="./program_info/polymc-header.svg#gh-dark-mode-only" alt="PolyMC logo" width="50%"/>
|
||||||
<source media="(prefers-color-scheme: light)" srcset="/program_info/org.prismlauncher.PrismLauncher.logo.svg">
|
|
||||||
<img alt="Prism Launcher" src="/program_info/org.prismlauncher.PrismLauncher.logo.svg" width="40%">
|
|
||||||
</picture>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
PolyMC is a custom launcher for Minecraft that focuses on predictability, long term stability and simplicity.
|
||||||
Prism Launcher is a custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.<br />
|
|
||||||
<br />This is a <b>fork</b> of the MultiMC Launcher and is <b>not</b> endorsed by it.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
## Installation
|
This is a **fork** of the MultiMC Launcher and not endorsed by MultiMC.
|
||||||
|
If you want to read about why this fork was created, check out [our FAQ page](https://polymc.org/wiki/overview/faq/).
|
||||||
|
<br>
|
||||||
|
|
||||||
<a href="https://repology.org/project/prismlauncher/versions">
|
# Installation
|
||||||
<img src="https://repology.org/badge/vertical-allrepos/prismlauncher.svg" alt="Packaging status" align="right">
|
|
||||||
</a>
|
|
||||||
|
|
||||||
- All downloads and instructions for Prism Launcher can be found on our [Website](https://prismlauncher.org/download).
|
- All downloads and instructions for PolyMC can be found [here](https://polymc.org/download/)
|
||||||
- Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) tab (this also includes the pull requests status).
|
- Last build status: https://github.com/PolyMC/PolyMC/actions
|
||||||
|
|
||||||
### Development Builds
|
|
||||||
|
|
||||||
Please understand that these builds are not intended for most users. There may be bugs, and other instabilities. You have been warned.
|
## Development Builds
|
||||||
|
|
||||||
There are development builds available through:
|
There are per-commit development builds available [here](https://github.com/PolyMC/PolyMC/actions). These have debug information in the binaries, so their file sizes are relatively larger.
|
||||||
|
Portable builds are provided for AppImage on Linux, Windows, and macOS.
|
||||||
|
|
||||||
- [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) (includes builds from pull requests opened by contribuitors)
|
For Debian and Arch, you can use these packages for the latest development versions:
|
||||||
- [nightly.link](https://nightly.link/PrismLauncher/PrismLauncher/workflows/trigger_builds/develop) (this will always point only to the latest version of develop)
|
[![polymc-git](https://img.shields.io/badge/aur-polymc--git-blue)](https://aur.archlinux.org/packages/polymc-git/)
|
||||||
|
[![polymc-git](https://img.shields.io/badge/mpr-polymc--git-orange)](https://mpr.makedeb.org/packages/polymc-git)
|
||||||
|
For flatpak, you can use [flathub-beta](https://discourse.flathub.org/t/how-to-use-flathub-beta/2111)
|
||||||
|
|
||||||
These have debug information in the binaries, so their file sizes are relatively larger.
|
# Help & Support
|
||||||
|
|
||||||
Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**.
|
Feel free to create an issue if you need help. However, you might find it easier to ask in the Discord server.
|
||||||
|
|
||||||
For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**, respectively, you can use these packages for the latest development versions:
|
[![PolyMC Discord](https://img.shields.io/discord/923671181020766230?label=PolyMC%20Discord)](https://discord.gg/xq7fxrgtMP)
|
||||||
|
|
||||||
[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?label=MPR&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git)<br />[![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?label=COPR&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?label=Gentoo&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher)
|
For people who don't want to use Discord, we have a Matrix Space which is bridged to the Discord server:
|
||||||
|
|
||||||
These packages are also available to all the distributions based on the ones mentioned above.
|
[![PolyMC Space](https://img.shields.io/matrix/polymc:matrix.org?label=PolyMC%20space)](https://matrix.to/#/#polymc:matrix.org)
|
||||||
|
|
||||||
## Community & Support
|
If there are any issues with the space or you are using a client that does not support the feature here are the individual rooms:
|
||||||
|
|
||||||
Feel free to create a GitHub issue if you find a bug or want to suggest a new feature. We have multiple community spaces where other community members can help you:
|
[![Development](https://img.shields.io/matrix/polymc-development:matrix.org?label=PolyMC%20Development)](https://matrix.to/#/#polymc-development:matrix.org)
|
||||||
|
[![Discussion](https://img.shields.io/matrix/polymc-discussion:matrix.org?label=PolyMC%20Discussion)](https://matrix.to/#/#polymc-discussion:matrix.org)
|
||||||
|
[![Github](https://img.shields.io/matrix/polymc-github:matrix.org?label=PolyMC%20Github)](https://matrix.to/#/#polymc-github:matrix.org)
|
||||||
|
[![Maintainers](https://img.shields.io/matrix/polymc-maintainers:matrix.org?label=PolyMC%20Maintainers)](https://matrix.to/#/#polymc-maintainers:matrix.org)
|
||||||
|
[![News](https://img.shields.io/matrix/polymc-news:matrix.org?label=PolyMC%20News)](https://matrix.to/#/#polymc-news:matrix.org)
|
||||||
|
[![Offtopic](https://img.shields.io/matrix/polymc-offtopic:matrix.org?label=PolyMC%20Offtopic)](https://matrix.to/#/#polymc-offtopic:matrix.org)
|
||||||
|
[![Support](https://img.shields.io/matrix/polymc-support:matrix.org?label=PolyMC%20Support)](https://matrix.to/#/#polymc-support:matrix.org)
|
||||||
|
[![Voice](https://img.shields.io/matrix/polymc-voice:matrix.org?label=PolyMC%20Voice)](https://matrix.to/#/#polymc-voice:matrix.org)
|
||||||
|
|
||||||
- **Our Discord server:**
|
We also have a subreddit you can post your issues and suggestions on:
|
||||||
|
|
||||||
[![Prism Launcher Discord server](https://discordapp.com/api/guilds/1031648380885147709/widget.png?style=banner3)](https://prismlauncher.org/discord)
|
[r/PolyMCLauncher](https://www.reddit.com/r/PolyMCLauncher/)
|
||||||
|
|
||||||
- **Our Matrix space:**
|
# Development
|
||||||
|
|
||||||
[![Prism Launcher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge&label=Matrix%20Space&logo=matrix&color=purple)](https://prismlauncher.org/matrix)
|
If you want to contribute to PolyMC you might find it useful to join our Discord Server or Matrix Space.
|
||||||
|
|
||||||
- **Our Subreddit:**
|
|
||||||
|
|
||||||
[![r/PrismLauncher](https://img.shields.io/reddit/subreddit-subscribers/prismlauncher?style=for-the-badge&logo=reddit)](https://prismlauncher.org/reddit)
|
|
||||||
|
|
||||||
## Translations
|
|
||||||
|
|
||||||
The translation effort for Prism Launcher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at <https://github.com/PrismLauncher/Translations>.
|
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
If you want to build Prism Launcher yourself, check the [Build Instructions](https://prismlauncher.org/wiki/development/build-instructions/).
|
If you want to build PolyMC yourself, check [Build Instructions](https://polymc.org/wiki/development/build-instructions/) for build instructions.
|
||||||
|
|
||||||
## Sponsors & Partners
|
## Translations
|
||||||
|
|
||||||
We thank all the wonderful backers over at Open Collective! Support Prism Launcher by [becoming a backer](https://opencollective.com/prismlauncher).
|
The translation effort for PolyMC is hosted on [Weblate](https://hosted.weblate.org/projects/polymc/polymc/) and information about translating PolyMC is available at https://github.com/PolyMC/Translations
|
||||||
|
|
||||||
[![OpenCollective Backers](https://opencollective.com/prismlauncher/backers.svg?width=890&limit=1000)](https://opencollective.com/prismlauncher#backers)
|
## Download information
|
||||||
|
|
||||||
Thanks to JetBrains for providing us a few licenses for all their products, as part of their [Open Source program](https://www.jetbrains.com/opensource/).
|
To modify download information or change packaging information send a pull request or issue to the website [here](https://github.com/PolyMC/polymc.github.io/tree/master/src/download).
|
||||||
|
|
||||||
[![JetBrains](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/opensource/)
|
|
||||||
|
|
||||||
Thanks to Weblate for hosting our translation efforts.
|
|
||||||
|
|
||||||
<a href="https://hosted.weblate.org/engage/prismlauncher/">
|
|
||||||
<img src="https://hosted.weblate.org/widgets/prismlauncher/-/open-graph.png" alt="Translation status" width="300" />
|
|
||||||
</a>
|
|
||||||
|
|
||||||
Thanks to Netlify for providing us their excellent web services, as part of their [Open Source program](https://www.netlify.com/open-source/).
|
|
||||||
|
|
||||||
<a href="https://www.netlify.com"> <img src="https://www.netlify.com/v3/img/components/netlify-color-accent.svg" alt="Deploys by Netlify" /> </a>
|
|
||||||
|
|
||||||
Thanks to the awesome people over at [MacStadium](https://www.macstadium.com/), for providing M1-Macs for development purposes!
|
|
||||||
|
|
||||||
<a href="https://www.macstadium.com"><img src="https://uploads-ssl.webflow.com/5ac3c046c82724970fc60918/5c019d917bba312af7553b49_MacStadium-developerlogo.png" alt="Powered by MacStadium" width="300"></a>
|
|
||||||
|
|
||||||
## Forking/Redistributing/Custom builds policy
|
## Forking/Redistributing/Custom builds policy
|
||||||
|
|
||||||
You are free to fork, redistribute and provide custom builds as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy:
|
We don't care what you do with your fork/custom build as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy:
|
||||||
|
- Make it clear that your fork is not PolyMC and is not endorsed by or affiliated with the PolyMC project (https://polymc.org).
|
||||||
- Make it clear that your fork is not Prism Launcher and is not endorsed by or affiliated with the Prism Launcher project (<https://prismlauncher.org>).
|
- Go through [CMakeLists.txt](CMakeLists.txt) and change PolyMC's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled).
|
||||||
- Go through [CMakeLists.txt](CMakeLists.txt) and change Prism Launcher's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled).
|
|
||||||
|
|
||||||
If you have any questions or want any clarification on the above conditions please make an issue and ask us.
|
If you have any questions or want any clarification on the above conditions please make an issue and ask us.
|
||||||
|
|
||||||
If you are just building Prism Launcher for your distribution, please make sure to set the `Launcher_BUILD_PLATFORM` to a slug representing your distribution. Examples are `archlinux`, `fedora` and `nixpkgs`.
|
Be aware that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions:
|
||||||
|
- [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
|
||||||
Note that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions:
|
- [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions)
|
||||||
|
|
||||||
- [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
|
|
||||||
- [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions)
|
|
||||||
|
|
||||||
If you do not agree with these terms and conditions, then remove the associated API keys from the [CMakeLists.txt](CMakeLists.txt) file by setting them to an empty string (`""`).
|
If you do not agree with these terms and conditions, then remove the associated API keys from the [CMakeLists.txt](CMakeLists.txt) file by setting them to an empty string (`""`).
|
||||||
|
|
||||||
## License [![https://github.com/PrismLauncher/PrismLauncher/blob/develop/LICENSE](https://img.shields.io/github/license/PrismLauncher/PrismLauncher?label=License&logo=gnu&color=C4282D)](LICENSE)
|
|
||||||
|
|
||||||
All launcher code is available under the GPL-3.0-only license.
|
All launcher code is available under the GPL-3.0-only license.
|
||||||
|
|
||||||
The logo and related assets are under the CC BY-SA 4.0 license.
|
The logo and related assets are under the CC BY-SA 4.0 license.
|
||||||
|
|
||||||
|
## Sponsors
|
||||||
|
Thank you to all our generous backers over at Open Collective! Support PolyMC by [becoming a backer](https://opencollective.com/polymc).
|
||||||
|
|
||||||
|
[![OpenCollective Backers](https://opencollective.com/polymc/backers.svg?width=890&limit=1000)](https://opencollective.com/polymc#backers)
|
||||||
|
|
||||||
|
Also, thanks to JetBrains for providing us a few licenses for all their products, as part of their [Open Source program](https://www.jetbrains.com/opensource/).
|
||||||
|
|
||||||
|
[![JetBrains](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/opensource/)
|
||||||
|
|
||||||
|
Additionally, thanks to the awesome people over at [MacStadium](https://www.macstadium.com/), for providing M1-Macs for development purposes!
|
||||||
|
|
||||||
|
<a href="https://www.macstadium.com"><img src="https://uploads-ssl.webflow.com/5ac3c046c82724970fc60918/5c019d917bba312af7553b49_MacStadium-developerlogo.png" alt="Powered by MacStadium" width="300"></a>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* PolyMC - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -33,7 +33,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <qstringliteral.h>
|
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
@ -43,14 +42,12 @@ Config::Config()
|
|||||||
{
|
{
|
||||||
// Name and copyright
|
// Name and copyright
|
||||||
LAUNCHER_NAME = "@Launcher_Name@";
|
LAUNCHER_NAME = "@Launcher_Name@";
|
||||||
LAUNCHER_APP_BINARY_NAME = "@Launcher_APP_BINARY_NAME@";
|
|
||||||
LAUNCHER_DISPLAYNAME = "@Launcher_DisplayName@";
|
LAUNCHER_DISPLAYNAME = "@Launcher_DisplayName@";
|
||||||
LAUNCHER_COPYRIGHT = "@Launcher_Copyright@";
|
LAUNCHER_COPYRIGHT = "@Launcher_Copyright@";
|
||||||
LAUNCHER_DOMAIN = "@Launcher_Domain@";
|
LAUNCHER_DOMAIN = "@Launcher_Domain@";
|
||||||
LAUNCHER_CONFIGFILE = "@Launcher_ConfigFile@";
|
LAUNCHER_CONFIGFILE = "@Launcher_ConfigFile@";
|
||||||
LAUNCHER_GIT = "@Launcher_Git@";
|
LAUNCHER_GIT = "@Launcher_Git@";
|
||||||
LAUNCHER_DESKTOPFILENAME = "@Launcher_DesktopFileName@";
|
LAUNCHER_DESKTOPFILENAME = "@Launcher_DesktopFileName@";
|
||||||
LAUNCHER_SVGFILENAME = "@Launcher_SVGFileName@";
|
|
||||||
|
|
||||||
USER_AGENT = "@Launcher_UserAgent@";
|
USER_AGENT = "@Launcher_UserAgent@";
|
||||||
USER_AGENT_UNCACHED = USER_AGENT + " (Uncached)";
|
USER_AGENT_UNCACHED = USER_AGENT + " (Uncached)";
|
||||||
@ -58,27 +55,18 @@ Config::Config()
|
|||||||
// Version information
|
// Version information
|
||||||
VERSION_MAJOR = @Launcher_VERSION_MAJOR@;
|
VERSION_MAJOR = @Launcher_VERSION_MAJOR@;
|
||||||
VERSION_MINOR = @Launcher_VERSION_MINOR@;
|
VERSION_MINOR = @Launcher_VERSION_MINOR@;
|
||||||
|
VERSION_HOTFIX = @Launcher_VERSION_HOTFIX@;
|
||||||
|
VERSION_BUILD = @Launcher_VERSION_BUILD@;
|
||||||
|
|
||||||
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
|
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
|
||||||
BUILD_ARTIFACT = "@Launcher_BUILD_ARTIFACT@";
|
UPDATER_BASE = "@Launcher_UPDATER_BASE@";
|
||||||
BUILD_DATE = "@Launcher_BUILD_TIMESTAMP@";
|
|
||||||
UPDATER_GITHUB_REPO = "@Launcher_UPDATER_GITHUB_REPO@";
|
|
||||||
|
|
||||||
COMPILER_NAME = "@Launcher_COMPILER_NAME@";
|
|
||||||
COMPILER_VERSION = "@Launcher_COMPILER_VERSION@";
|
|
||||||
|
|
||||||
COMPILER_TARGET_SYSTEM = "@Launcher_COMPILER_TARGET_SYSTEM@";
|
|
||||||
COMPILER_TARGET_SYSTEM_VERSION = "@Launcher_COMPILER_TARGET_SYSTEM_VERSION@";
|
|
||||||
COMPILER_TARGET_SYSTEM_PROCESSOR = "@Launcher_COMPILER_TARGET_PROCESSOR@";
|
|
||||||
|
|
||||||
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
|
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
|
||||||
MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
|
MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
|
||||||
|
|
||||||
if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
|
if (BUILD_PLATFORM == "macOS" && !MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
|
||||||
{
|
{
|
||||||
UPDATER_ENABLED = true;
|
UPDATER_ENABLED = true;
|
||||||
} else if(!UPDATER_GITHUB_REPO.isEmpty() && !BUILD_ARTIFACT.isEmpty()) {
|
|
||||||
UPDATER_ENABLED = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
||||||
@ -87,19 +75,19 @@ Config::Config()
|
|||||||
|
|
||||||
// Assume that builds outside of Git repos are "stable"
|
// Assume that builds outside of Git repos are "stable"
|
||||||
if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND")
|
if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND")
|
||||||
|| GIT_TAG == QStringLiteral("GITDIR-NOTFOUND")
|
|| GIT_TAG == QStringLiteral("GITDIR-NOTFOUND"))
|
||||||
|| GIT_REFSPEC == QStringLiteral("")
|
|
||||||
|| GIT_TAG == QStringLiteral("GIT-NOTFOUND"))
|
|
||||||
{
|
{
|
||||||
GIT_REFSPEC = "refs/heads/stable";
|
GIT_REFSPEC = "refs/heads/stable";
|
||||||
GIT_TAG = versionString();
|
GIT_TAG = versionString();
|
||||||
GIT_COMMIT = "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GIT_REFSPEC.startsWith("refs/heads/"))
|
if (GIT_REFSPEC.startsWith("refs/heads/"))
|
||||||
{
|
{
|
||||||
VERSION_CHANNEL = GIT_REFSPEC;
|
VERSION_CHANNEL = GIT_REFSPEC;
|
||||||
VERSION_CHANNEL.remove("refs/heads/");
|
VERSION_CHANNEL.remove("refs/heads/");
|
||||||
|
if(!UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty() && VERSION_BUILD >= 0) {
|
||||||
|
UPDATER_ENABLED = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (!GIT_COMMIT.isEmpty())
|
else if (!GIT_COMMIT.isEmpty())
|
||||||
{
|
{
|
||||||
@ -110,6 +98,7 @@ Config::Config()
|
|||||||
VERSION_CHANNEL = "unknown";
|
VERSION_CHANNEL = "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VERSION_STR = "@Launcher_VERSION_STRING@";
|
||||||
NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
|
NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
|
||||||
NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@";
|
NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@";
|
||||||
HELP_URL = "@Launcher_HELP_URL@";
|
HELP_URL = "@Launcher_HELP_URL@";
|
||||||
@ -118,9 +107,6 @@ Config::Config()
|
|||||||
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
|
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
|
||||||
META_URL = "@Launcher_META_URL@";
|
META_URL = "@Launcher_META_URL@";
|
||||||
|
|
||||||
GLFW_LIBRARY_NAME = "@Launcher_GLFW_LIBRARY_NAME@";
|
|
||||||
OPENAL_LIBRARY_NAME = "@Launcher_OPENAL_LIBRARY_NAME@";
|
|
||||||
|
|
||||||
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
|
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
|
||||||
TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@";
|
TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@";
|
||||||
MATRIX_URL = "@Launcher_MATRIX_URL@";
|
MATRIX_URL = "@Launcher_MATRIX_URL@";
|
||||||
@ -130,7 +116,7 @@ Config::Config()
|
|||||||
|
|
||||||
QString Config::versionString() const
|
QString Config::versionString() const
|
||||||
{
|
{
|
||||||
return QString("%1.%2").arg(VERSION_MAJOR).arg(VERSION_MINOR);
|
return QString("%1.%2.%3").arg(VERSION_MAJOR).arg(VERSION_MINOR).arg(VERSION_HOTFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Config::printableVersionString() const
|
QString Config::printableVersionString() const
|
||||||
@ -142,18 +128,11 @@ QString Config::printableVersionString() const
|
|||||||
{
|
{
|
||||||
vstr += "-" + VERSION_CHANNEL;
|
vstr += "-" + VERSION_CHANNEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if a build number is set, also add it to the end
|
||||||
|
if(VERSION_BUILD >= 0)
|
||||||
|
{
|
||||||
|
vstr += "+build." + QString::number(VERSION_BUILD);
|
||||||
|
}
|
||||||
return vstr;
|
return vstr;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Config::compilerID() const
|
|
||||||
{
|
|
||||||
if (COMPILER_VERSION.isEmpty())
|
|
||||||
return COMPILER_NAME;
|
|
||||||
return QStringLiteral("%1 - %2").arg(COMPILER_NAME).arg(COMPILER_VERSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Config::systemID() const
|
|
||||||
{
|
|
||||||
return QStringLiteral("%1 %2 %3").arg(COMPILER_TARGET_SYSTEM, COMPILER_TARGET_SYSTEM_VERSION, COMPILER_TARGET_SYSTEM_PROCESSOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* PolyMC - Minecraft Launcher
|
||||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -36,7 +35,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <QList>
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,19 +44,21 @@ class Config {
|
|||||||
public:
|
public:
|
||||||
Config();
|
Config();
|
||||||
QString LAUNCHER_NAME;
|
QString LAUNCHER_NAME;
|
||||||
QString LAUNCHER_APP_BINARY_NAME;
|
|
||||||
QString LAUNCHER_DISPLAYNAME;
|
QString LAUNCHER_DISPLAYNAME;
|
||||||
QString LAUNCHER_COPYRIGHT;
|
QString LAUNCHER_COPYRIGHT;
|
||||||
QString LAUNCHER_DOMAIN;
|
QString LAUNCHER_DOMAIN;
|
||||||
QString LAUNCHER_CONFIGFILE;
|
QString LAUNCHER_CONFIGFILE;
|
||||||
QString LAUNCHER_GIT;
|
QString LAUNCHER_GIT;
|
||||||
QString LAUNCHER_DESKTOPFILENAME;
|
QString LAUNCHER_DESKTOPFILENAME;
|
||||||
QString LAUNCHER_SVGFILENAME;
|
|
||||||
|
|
||||||
/// The major version number.
|
/// The major version number.
|
||||||
int VERSION_MAJOR;
|
int VERSION_MAJOR;
|
||||||
/// The minor version number.
|
/// The minor version number.
|
||||||
int VERSION_MINOR;
|
int VERSION_MINOR;
|
||||||
|
/// The hotfix number.
|
||||||
|
int VERSION_HOTFIX;
|
||||||
|
/// The build number.
|
||||||
|
int VERSION_BUILD;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The version channel
|
* The version channel
|
||||||
@ -68,32 +68,11 @@ class Config {
|
|||||||
|
|
||||||
bool UPDATER_ENABLED = false;
|
bool UPDATER_ENABLED = false;
|
||||||
|
|
||||||
/// A short string identifying this build's platform or distribution.
|
/// A short string identifying this build's platform. For example, "lin64" or "win32".
|
||||||
QString BUILD_PLATFORM;
|
QString BUILD_PLATFORM;
|
||||||
|
|
||||||
/// A short string identifying this build's valid artifacts int he updater. For example, "lin64" or "win32".
|
|
||||||
QString BUILD_ARTIFACT;
|
|
||||||
|
|
||||||
/// A string containing the build timestamp
|
|
||||||
QString BUILD_DATE;
|
|
||||||
|
|
||||||
/// A string identifying the compiler use to build
|
|
||||||
QString COMPILER_NAME;
|
|
||||||
|
|
||||||
/// A string identifying the compiler version used to build
|
|
||||||
QString COMPILER_VERSION;
|
|
||||||
|
|
||||||
/// A string identifying the compiler target system os
|
|
||||||
QString COMPILER_TARGET_SYSTEM;
|
|
||||||
|
|
||||||
/// A String identifying the compiler target system version
|
|
||||||
QString COMPILER_TARGET_SYSTEM_VERSION;
|
|
||||||
|
|
||||||
/// A String identifying the compiler target processor
|
|
||||||
QString COMPILER_TARGET_SYSTEM_PROCESSOR;
|
|
||||||
|
|
||||||
/// URL for the updater's channel
|
/// URL for the updater's channel
|
||||||
QString UPDATER_GITHUB_REPO;
|
QString UPDATER_BASE;
|
||||||
|
|
||||||
/// The public key used to sign releases for the Sparkle updater appcast
|
/// The public key used to sign releases for the Sparkle updater appcast
|
||||||
QString MAC_SPARKLE_PUB_KEY;
|
QString MAC_SPARKLE_PUB_KEY;
|
||||||
@ -116,6 +95,9 @@ class Config {
|
|||||||
/// The git refspec of this build
|
/// The git refspec of this build
|
||||||
QString GIT_REFSPEC;
|
QString GIT_REFSPEC;
|
||||||
|
|
||||||
|
/// This is printed on start to standard output
|
||||||
|
QString VERSION_STR;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is used to fetch the news RSS feed.
|
* This is used to fetch the news RSS feed.
|
||||||
* It defaults in CMakeLists.txt to "https://multimc.org/rss.xml"
|
* It defaults in CMakeLists.txt to "https://multimc.org/rss.xml"
|
||||||
@ -152,9 +134,6 @@ class Config {
|
|||||||
*/
|
*/
|
||||||
QString META_URL;
|
QString META_URL;
|
||||||
|
|
||||||
QString GLFW_LIBRARY_NAME;
|
|
||||||
QString OPENAL_LIBRARY_NAME;
|
|
||||||
|
|
||||||
QString BUG_TRACKER_URL;
|
QString BUG_TRACKER_URL;
|
||||||
QString TRANSLATIONS_URL;
|
QString TRANSLATIONS_URL;
|
||||||
QString MATRIX_URL;
|
QString MATRIX_URL;
|
||||||
@ -165,8 +144,8 @@ class Config {
|
|||||||
QString LIBRARY_BASE = "https://libraries.minecraft.net/";
|
QString LIBRARY_BASE = "https://libraries.minecraft.net/";
|
||||||
QString AUTH_BASE = "https://authserver.mojang.com/";
|
QString AUTH_BASE = "https://authserver.mojang.com/";
|
||||||
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
|
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
|
||||||
QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists
|
QString FMLLIBS_BASE_URL = "https://files.polymc.org/fmllibs/";
|
||||||
QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists
|
QString TRANSLATIONS_BASE_URL = "https://i18n.polymc.org/";
|
||||||
|
|
||||||
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
|
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
|
||||||
|
|
||||||
@ -183,9 +162,6 @@ class Config {
|
|||||||
|
|
||||||
QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2";
|
QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2";
|
||||||
QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2";
|
QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2";
|
||||||
QStringList MODRINTH_MRPACK_HOSTS{ "cdn.modrinth.com", "github.com", "raw.githubusercontent.com", "gitlab.com" };
|
|
||||||
|
|
||||||
QString FLAME_BASE_URL = "https://api.curseforge.com/v1";
|
|
||||||
|
|
||||||
QString versionString() const;
|
QString versionString() const;
|
||||||
/**
|
/**
|
||||||
@ -193,18 +169,6 @@ class Config {
|
|||||||
* \return The version number in string format (major.minor.revision.build).
|
* \return The version number in string format (major.minor.revision.build).
|
||||||
*/
|
*/
|
||||||
QString printableVersionString() const;
|
QString printableVersionString() const;
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Compiler ID String
|
|
||||||
* \return a string of the form "Name - Version" of just "Name" if the version is empty
|
|
||||||
*/
|
|
||||||
QString compilerID() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief System ID String
|
|
||||||
* \return a string of the form "OS Verison Processor"
|
|
||||||
*/
|
|
||||||
QString systemID() const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const Config BuildConfig;
|
extern const Config BuildConfig;
|
||||||
|
@ -1,155 +0,0 @@
|
|||||||
#
|
|
||||||
# Function to set compiler warnings with reasonable defaults at the project level.
|
|
||||||
# Taken from https://github.com/aminya/project_options/blob/main/src/CompilerWarnings.cmake
|
|
||||||
# under the folowing license:
|
|
||||||
#
|
|
||||||
# MIT License
|
|
||||||
#
|
|
||||||
# Copyright (c) 2022-2100 Amin Yahyaabadi
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
# SOFTWARE.
|
|
||||||
#
|
|
||||||
|
|
||||||
include_guard()
|
|
||||||
|
|
||||||
function(_set_project_warnings_add_target_link_option TARGET OPTIONS)
|
|
||||||
target_link_options(${_project_name} INTERFACE ${OPTIONS})
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
# Set the compiler warnings
|
|
||||||
#
|
|
||||||
# https://clang.llvm.org/docs/DiagnosticsReference.html
|
|
||||||
# https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md
|
|
||||||
function(
|
|
||||||
set_project_warnings
|
|
||||||
_project_name
|
|
||||||
MSVC_WARNINGS
|
|
||||||
CLANG_WARNINGS
|
|
||||||
GCC_WARNINGS
|
|
||||||
)
|
|
||||||
if("${MSVC_WARNINGS}" STREQUAL "")
|
|
||||||
set(MSVC_WARNINGS
|
|
||||||
/W4 # Baseline reasonable warnings
|
|
||||||
/w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data
|
|
||||||
/w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
|
||||||
/w14263 # 'function': member function does not override any base class virtual member function
|
|
||||||
/w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not
|
|
||||||
# be destructed correctly
|
|
||||||
/w14287 # 'operator': unsigned/negative constant mismatch
|
|
||||||
/we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside
|
|
||||||
# the for-loop scope
|
|
||||||
/w14296 # 'operator': expression is always 'boolean_value'
|
|
||||||
/w14311 # 'variable': pointer truncation from 'type1' to 'type2'
|
|
||||||
/w14545 # expression before comma evaluates to a function which is missing an argument list
|
|
||||||
/w14546 # function call before comma missing argument list
|
|
||||||
/w14547 # 'operator': operator before comma has no effect; expected operator with side-effect
|
|
||||||
/w14549 # 'operator': operator before comma has no effect; did you intend 'operator'?
|
|
||||||
/w14555 # expression has no effect; expected expression with side- effect
|
|
||||||
/w14619 # pragma warning: there is no warning number 'number'
|
|
||||||
/w14640 # Enable warning on thread un-safe static member initialization
|
|
||||||
/w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior.
|
|
||||||
/w14905 # wide string literal cast to 'LPSTR'
|
|
||||||
/w14906 # string literal cast to 'LPWSTR'
|
|
||||||
/w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied
|
|
||||||
/permissive- # standards conformance mode for MSVC compiler.
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if("${CLANG_WARNINGS}" STREQUAL "")
|
|
||||||
set(CLANG_WARNINGS
|
|
||||||
-Wall
|
|
||||||
-Wextra # reasonable and standard
|
|
||||||
-Wshadow # warn the user if a variable declaration shadows one from a parent context
|
|
||||||
-Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps
|
|
||||||
# catch hard to track down memory errors
|
|
||||||
-Wold-style-cast # warn for c-style casts
|
|
||||||
-Wcast-align # warn for potential performance problem casts
|
|
||||||
-Wunused # warn on anything being unused
|
|
||||||
-Woverloaded-virtual # warn if you overload (not override) a virtual function
|
|
||||||
-Wpedantic # warn if non-standard C++ is used
|
|
||||||
-Wconversion # warn on type conversions that may lose data
|
|
||||||
-Wsign-conversion # warn on sign conversions
|
|
||||||
-Wnull-dereference # warn if a null dereference is detected
|
|
||||||
-Wdouble-promotion # warn if float is implicit promoted to double
|
|
||||||
-Wformat=2 # warn on security issues around functions that format output (ie printf)
|
|
||||||
-Wimplicit-fallthrough # warn on statements that fallthrough without an explicit annotation
|
|
||||||
# -Wgnu-zero-variadic-macro-arguments (part of -pedantic) is triggered by every qCDebug() call and therefore results
|
|
||||||
# in a lot of noise. This warning is only notifying us that clang is emulating the GCC behaviour
|
|
||||||
# instead of the exact standard wording so we can safely ignore it
|
|
||||||
-Wno-gnu-zero-variadic-macro-arguments
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if("${GCC_WARNINGS}" STREQUAL "")
|
|
||||||
set(GCC_WARNINGS
|
|
||||||
${CLANG_WARNINGS}
|
|
||||||
-Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist
|
|
||||||
-Wduplicated-cond # warn if if / else chain has duplicated conditions
|
|
||||||
-Wduplicated-branches # warn if if / else branches have duplicated code
|
|
||||||
-Wlogical-op # warn about logical operations being used where bitwise were probably wanted
|
|
||||||
-Wuseless-cast # warn if you perform a cast to the same type
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(MSVC)
|
|
||||||
set(PROJECT_WARNINGS_CXX ${MSVC_WARNINGS})
|
|
||||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
|
|
||||||
set(PROJECT_WARNINGS_CXX ${CLANG_WARNINGS})
|
|
||||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|
||||||
set(PROJECT_WARNINGS_CXX ${GCC_WARNINGS})
|
|
||||||
else()
|
|
||||||
message(AUTHOR_WARNING "No compiler warnings set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'")
|
|
||||||
# TODO support Intel compiler
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Add C warnings
|
|
||||||
set(PROJECT_WARNINGS_C "${PROJECT_WARNINGS_CXX}")
|
|
||||||
list(
|
|
||||||
REMOVE_ITEM
|
|
||||||
PROJECT_WARNINGS_C
|
|
||||||
-Wnon-virtual-dtor
|
|
||||||
-Wold-style-cast
|
|
||||||
-Woverloaded-virtual
|
|
||||||
-Wuseless-cast
|
|
||||||
-Wextra-semi
|
|
||||||
)
|
|
||||||
|
|
||||||
target_compile_options(
|
|
||||||
${_project_name}
|
|
||||||
INTERFACE # C++ warnings
|
|
||||||
$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_WARNINGS_CXX}>
|
|
||||||
# C warnings
|
|
||||||
$<$<COMPILE_LANGUAGE:C>:${PROJECT_WARNINGS_C}>
|
|
||||||
)
|
|
||||||
|
|
||||||
# If we are using the compiler as a linker driver pass the warnings to it
|
|
||||||
# (most useful when using LTO or warnings as errors)
|
|
||||||
if(CMAKE_CXX_LINK_EXECUTABLE MATCHES "^<CMAKE_CXX_COMPILER>")
|
|
||||||
_set_project_warnings_add_target_link_option(
|
|
||||||
${_project_name} "$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_WARNINGS_CXX}>"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CMAKE_C_LINK_EXECUTABLE MATCHES "^<CMAKE_C_COMPILER>")
|
|
||||||
_set_project_warnings_add_target_link_option(
|
|
||||||
${_project_name} "$<$<COMPILE_LANGUAGE:C>:${PROJECT_WARNINGS_C}>"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
endfunction()
|
|
@ -44,39 +44,5 @@
|
|||||||
<string>${MACOSX_SPARKLE_UPDATE_PUBLIC_KEY}</string>
|
<string>${MACOSX_SPARKLE_UPDATE_PUBLIC_KEY}</string>
|
||||||
<key>SUFeedURL</key>
|
<key>SUFeedURL</key>
|
||||||
<string>${MACOSX_SPARKLE_UPDATE_FEED_URL}</string>
|
<string>${MACOSX_SPARKLE_UPDATE_FEED_URL}</string>
|
||||||
<key>CFBundleDocumentTypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeExtensions</key>
|
|
||||||
<array>
|
|
||||||
<string>zip</string>
|
|
||||||
<string>mrpack</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Prism Launcher instance</string>
|
|
||||||
<key>CFBundleTypeOSTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>TEXT</string>
|
|
||||||
<string>utxt</string>
|
|
||||||
<string>TUTX</string>
|
|
||||||
<string>****</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Viewer</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleURLTypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleURLName</key>
|
|
||||||
<string>Curseforge</string>
|
|
||||||
<key>CFBundleURLSchemes</key>
|
|
||||||
<array>
|
|
||||||
<string>curseforge</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
15
default.nix
15
default.nix
@ -1,14 +1 @@
|
|||||||
(
|
(import nix/flake-compat.nix).defaultNix
|
||||||
import
|
|
||||||
(
|
|
||||||
let
|
|
||||||
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
|
|
||||||
in
|
|
||||||
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
|
|
||||||
|
160
flake.lock
160
flake.lock
@ -3,11 +3,11 @@
|
|||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1696426674,
|
"lastModified": 1650374568,
|
||||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
"narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=",
|
||||||
"owner": "edolstra",
|
"owner": "edolstra",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
"rev": "b4a34015c698c7793d592d66adbab377907a2be8",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -16,101 +16,29 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-parts": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs-lib": "nixpkgs-lib"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1698882062,
|
|
||||||
"narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=",
|
|
||||||
"owner": "hercules-ci",
|
|
||||||
"repo": "flake-parts",
|
|
||||||
"rev": "8c9fa2545007b49a5db5f650ae91f227672c3877",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "hercules-ci",
|
|
||||||
"repo": "flake-parts",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-utils": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1685518550,
|
|
||||||
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gitignore": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"pre-commit-hooks",
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1660459072,
|
|
||||||
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
|
|
||||||
"owner": "hercules-ci",
|
|
||||||
"repo": "gitignore.nix",
|
|
||||||
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "hercules-ci",
|
|
||||||
"repo": "gitignore.nix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"libnbtplusplus": {
|
"libnbtplusplus": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1690036783,
|
"lastModified": 1650031308,
|
||||||
"narHash": "sha256-A5kTgICnx+Qdq3Fir/bKTfdTt/T1NQP2SC+nhN1ENug=",
|
"narHash": "sha256-TvVOjkUobYJD9itQYueELJX3wmecvEdCbJ0FinW2mL4=",
|
||||||
"owner": "PrismLauncher",
|
"owner": "PolyMC",
|
||||||
"repo": "libnbtplusplus",
|
"repo": "libnbtplusplus",
|
||||||
"rev": "a5e8fd52b8bf4ab5d5bcc042b2a247867589985f",
|
"rev": "2203af7eeb48c45398139b583615134efd8d407f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "PrismLauncher",
|
"owner": "PolyMC",
|
||||||
"repo": "libnbtplusplus",
|
"repo": "libnbtplusplus",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nix-filter": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1694857738,
|
|
||||||
"narHash": "sha256-bxxNyLHjhu0N8T3REINXQ2ZkJco0ABFPn6PIe2QUfqo=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "nix-filter",
|
|
||||||
"rev": "41fd48e00c22b4ced525af521ead8792402de0ea",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "nix-filter",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1699094435,
|
"lastModified": 1658119717,
|
||||||
"narHash": "sha256-YLZ5/KKZ1PyLrm2MO8UxRe4H3M0/oaYqNhSlq6FDeeA=",
|
"narHash": "sha256-4upOZIQQ7Bc4CprqnHsKnqYfw+arJeAuU+QcpjYBXW0=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "9d5d25bbfe8c0297ebe85324addcb5020ed1a454",
|
"rev": "9eb60f25aff0d2218c848dd4574a0ab5e296cabe",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -120,75 +48,11 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-lib": {
|
|
||||||
"locked": {
|
|
||||||
"dir": "lib",
|
|
||||||
"lastModified": 1698611440,
|
|
||||||
"narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"dir": "lib",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixos-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pre-commit-hooks": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-compat": [
|
|
||||||
"flake-compat"
|
|
||||||
],
|
|
||||||
"flake-utils": "flake-utils",
|
|
||||||
"gitignore": "gitignore",
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"nixpkgs-stable": [
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1698852633,
|
|
||||||
"narHash": "sha256-Hsc/cCHud8ZXLvmm8pxrXpuaPEeNaaUttaCvtdX/Wug=",
|
|
||||||
"owner": "cachix",
|
|
||||||
"repo": "pre-commit-hooks.nix",
|
|
||||||
"rev": "dec10399e5b56aa95fcd530e0338be72ad6462a0",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "cachix",
|
|
||||||
"repo": "pre-commit-hooks.nix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
"flake-parts": "flake-parts",
|
|
||||||
"libnbtplusplus": "libnbtplusplus",
|
"libnbtplusplus": "libnbtplusplus",
|
||||||
"nix-filter": "nix-filter",
|
"nixpkgs": "nixpkgs"
|
||||||
"nixpkgs": "nixpkgs",
|
|
||||||
"pre-commit-hooks": "pre-commit-hooks"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
59
flake.nix
59
flake.nix
@ -3,42 +3,35 @@
|
|||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
|
||||||
nix-filter.url = "github:numtide/nix-filter";
|
libnbtplusplus = { url = "github:PolyMC/libnbtplusplus"; flake = false; };
|
||||||
pre-commit-hooks = {
|
|
||||||
url = "github:cachix/pre-commit-hooks.nix";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
inputs.nixpkgs-stable.follows = "nixpkgs";
|
|
||||||
inputs.flake-compat.follows = "flake-compat";
|
|
||||||
};
|
|
||||||
flake-compat = {
|
|
||||||
url = "github:edolstra/flake-compat";
|
|
||||||
flake = false;
|
|
||||||
};
|
|
||||||
libnbtplusplus = {
|
|
||||||
url = "github:PrismLauncher/libnbtplusplus";
|
|
||||||
flake = false;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs = { self, nixpkgs, libnbtplusplus, ... }:
|
||||||
flake-parts,
|
let
|
||||||
pre-commit-hooks,
|
# User-friendly version number.
|
||||||
...
|
version = builtins.substring 0 8 self.lastModifiedDate;
|
||||||
} @ inputs:
|
|
||||||
flake-parts.lib.mkFlake {inherit inputs;} {
|
|
||||||
imports = [
|
|
||||||
pre-commit-hooks.flakeModule
|
|
||||||
|
|
||||||
./nix/dev.nix
|
# Supported systems (qtbase is currently broken for "aarch64-darwin")
|
||||||
./nix/distribution.nix
|
supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" ];
|
||||||
];
|
|
||||||
|
|
||||||
systems = [
|
# Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'.
|
||||||
"x86_64-linux"
|
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||||
"aarch64-linux"
|
|
||||||
"x86_64-darwin"
|
# Nixpkgs instantiated for supported systems.
|
||||||
"aarch64-darwin"
|
pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system});
|
||||||
];
|
|
||||||
|
packagesFn = pkgs: rec {
|
||||||
|
polymc = pkgs.libsForQt5.callPackage ./nix { inherit version self libnbtplusplus; };
|
||||||
|
polymc-qt6 = pkgs.qt6Packages.callPackage ./nix { inherit version self libnbtplusplus; };
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages = forAllSystems (system:
|
||||||
|
let packages = packagesFn pkgs.${system}; in
|
||||||
|
packages // { default = packages.polymc; }
|
||||||
|
);
|
||||||
|
|
||||||
|
overlay = final: packagesFn;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "libdecor",
|
|
||||||
"buildsystem": "meson",
|
|
||||||
"config-opts": [
|
|
||||||
"-Ddemo=false"
|
|
||||||
],
|
|
||||||
"sources": [
|
|
||||||
{
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://gitlab.freedesktop.org/libdecor/libdecor.git",
|
|
||||||
"commit": "73260393a97291c887e1074ab7f318e031be0ac6"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "patch",
|
|
||||||
"path": "patches/weird_libdecor.patch"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cleanup": [
|
|
||||||
"/include",
|
|
||||||
"/lib/pkgconfig"
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,158 +0,0 @@
|
|||||||
id: org.prismlauncher.PrismLauncher
|
|
||||||
runtime: org.kde.Platform
|
|
||||||
runtime-version: "5.15-22.08"
|
|
||||||
sdk: org.kde.Sdk
|
|
||||||
sdk-extensions:
|
|
||||||
- org.freedesktop.Sdk.Extension.openjdk17
|
|
||||||
- org.freedesktop.Sdk.Extension.openjdk8
|
|
||||||
|
|
||||||
command: prismlauncher
|
|
||||||
finish-args:
|
|
||||||
- --share=ipc
|
|
||||||
- --socket=x11
|
|
||||||
- --socket=wayland
|
|
||||||
- --device=all
|
|
||||||
- --share=network
|
|
||||||
- --socket=pulseaudio
|
|
||||||
# for Discord RPC mods
|
|
||||||
- --filesystem=xdg-run/app/com.discordapp.Discord:create
|
|
||||||
# Mod drag&drop
|
|
||||||
- --filesystem=xdg-download:ro
|
|
||||||
# FTBApp import
|
|
||||||
- --filesystem=~/.ftba:ro
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
- /lib/libGLU*
|
|
||||||
|
|
||||||
modules:
|
|
||||||
# Might be needed by some Controller mods (see https://github.com/isXander/Controlify/issues/31)
|
|
||||||
- shared-modules/libusb/libusb.json
|
|
||||||
|
|
||||||
# Needed for proper Wayland support
|
|
||||||
- libdecor.json
|
|
||||||
|
|
||||||
- name: prismlauncher
|
|
||||||
buildsystem: cmake-ninja
|
|
||||||
builddir: true
|
|
||||||
config-opts:
|
|
||||||
- -DLauncher_BUILD_PLATFORM=flatpak
|
|
||||||
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
|
||||||
- -DLauncher_QT_VERSION_MAJOR=5
|
|
||||||
build-options:
|
|
||||||
env:
|
|
||||||
JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17
|
|
||||||
JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac
|
|
||||||
sources:
|
|
||||||
- type: dir
|
|
||||||
path: ../
|
|
||||||
|
|
||||||
- name: openjdk
|
|
||||||
buildsystem: simple
|
|
||||||
build-commands:
|
|
||||||
- mkdir -p /app/jdk/
|
|
||||||
- /usr/lib/sdk/openjdk17/install.sh
|
|
||||||
- mv /app/jre /app/jdk/17
|
|
||||||
- /usr/lib/sdk/openjdk8/install.sh
|
|
||||||
- mv /app/jre /app/jdk/8
|
|
||||||
cleanup:
|
|
||||||
- /jre
|
|
||||||
|
|
||||||
- name: glfw
|
|
||||||
buildsystem: cmake-ninja
|
|
||||||
config-opts:
|
|
||||||
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
|
||||||
- -DBUILD_SHARED_LIBS:BOOL=ON
|
|
||||||
- -DGLFW_USE_WAYLAND=ON
|
|
||||||
sources:
|
|
||||||
- type: git
|
|
||||||
url: https://github.com/glfw/glfw.git
|
|
||||||
commit: 3fa2360720eeba1964df3c0ecf4b5df8648a8e52
|
|
||||||
- type: patch
|
|
||||||
path: patches/0003-Don-t-crash-on-calls-to-focus-or-icon.patch
|
|
||||||
- type: patch
|
|
||||||
path: patches/0005-Add-warning-about-being-an-unofficial-patch.patch
|
|
||||||
- type: patch
|
|
||||||
path: patches/0007-Platform-Prefer-Wayland-over-X11.patch
|
|
||||||
cleanup:
|
|
||||||
- /include
|
|
||||||
- /lib/cmake
|
|
||||||
- /lib/pkgconfig
|
|
||||||
|
|
||||||
- name: xrandr
|
|
||||||
buildsystem: autotools
|
|
||||||
sources:
|
|
||||||
- type: archive
|
|
||||||
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.2.tar.xz
|
|
||||||
sha256: c8bee4790d9058bacc4b6246456c58021db58a87ddda1a9d0139bf5f18f1f240
|
|
||||||
x-checker-data:
|
|
||||||
type: anitya
|
|
||||||
project-id: 14957
|
|
||||||
stable-only: true
|
|
||||||
url-template: https://xorg.freedesktop.org/archive/individual/app/xrandr-$version.tar.xz
|
|
||||||
cleanup:
|
|
||||||
- /share/man
|
|
||||||
- /bin/xkeystone
|
|
||||||
|
|
||||||
- name: gamemode
|
|
||||||
buildsystem: meson
|
|
||||||
config-opts:
|
|
||||||
- -Dwith-sd-bus-provider=no-daemon
|
|
||||||
- -Dwith-examples=false
|
|
||||||
post-install:
|
|
||||||
# gamemoderun is installed for users who want to use wrapper commands
|
|
||||||
# post-install is running inside the build dir, we need it from the source though
|
|
||||||
- install -Dm755 ../data/gamemoderun -t /app/bin
|
|
||||||
sources:
|
|
||||||
- type: archive
|
|
||||||
archive-type: tar-gzip
|
|
||||||
url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.7
|
|
||||||
sha256: 57ce73ba605d1cf12f8d13725006a895182308d93eba0f69f285648449641803
|
|
||||||
x-checker-data:
|
|
||||||
type: json
|
|
||||||
url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest
|
|
||||||
version-query: .tag_name
|
|
||||||
url-query: .tarball_url
|
|
||||||
timestamp-query: .published_at
|
|
||||||
cleanup:
|
|
||||||
- /include
|
|
||||||
- /lib/pkgconfig
|
|
||||||
- /lib/libgamemodeauto.a
|
|
||||||
|
|
||||||
- name: glxinfo
|
|
||||||
buildsystem: meson
|
|
||||||
config-opts:
|
|
||||||
- --bindir=/app/mesa-demos
|
|
||||||
- -Degl=disabled
|
|
||||||
- -Dglut=disabled
|
|
||||||
- -Dosmesa=disabled
|
|
||||||
- -Dvulkan=disabled
|
|
||||||
- -Dwayland=disabled
|
|
||||||
post-install:
|
|
||||||
- mv -v /app/mesa-demos/glxinfo /app/bin
|
|
||||||
sources:
|
|
||||||
- type: archive
|
|
||||||
url: https://archive.mesa3d.org/demos/mesa-demos-9.0.0.tar.xz
|
|
||||||
sha256: 3046a3d26a7b051af7ebdd257a5f23bfeb160cad6ed952329cdff1e9f1ed496b
|
|
||||||
x-checker-data:
|
|
||||||
type: anitya
|
|
||||||
project-id: 16781
|
|
||||||
stable-only: true
|
|
||||||
url-template: https://archive.mesa3d.org/demos/mesa-demos-$version.tar.xz
|
|
||||||
cleanup:
|
|
||||||
- /include
|
|
||||||
- /mesa-demos
|
|
||||||
- /share
|
|
||||||
modules:
|
|
||||||
- shared-modules/glu/glu-9.json
|
|
||||||
|
|
||||||
- name: enhance
|
|
||||||
buildsystem: simple
|
|
||||||
build-commands:
|
|
||||||
- install -Dm755 prime-run /app/bin/prime-run
|
|
||||||
- mv /app/bin/prismlauncher /app/bin/prismrun
|
|
||||||
- install -Dm755 prismlauncher /app/bin/prismlauncher
|
|
||||||
sources:
|
|
||||||
- type: file
|
|
||||||
path: prime-run
|
|
||||||
- type: file
|
|
||||||
path: prismlauncher
|
|
@ -1,24 +0,0 @@
|
|||||||
diff --git a/src/wl_window.c b/src/wl_window.c
|
|
||||||
index 52d3b9eb..4ac4eb5d 100644
|
|
||||||
--- a/src/wl_window.c
|
|
||||||
+++ b/src/wl_window.c
|
|
||||||
@@ -2117,8 +2117,7 @@ void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title)
|
|
||||||
void _glfwSetWindowIconWayland(_GLFWwindow* window,
|
|
||||||
int count, const GLFWimage* images)
|
|
||||||
{
|
|
||||||
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
|
||||||
- "Wayland: The platform does not support setting the window icon");
|
|
||||||
+ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the window icon\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos)
|
|
||||||
@@ -2361,8 +2360,7 @@ void _glfwRequestWindowAttentionWayland(_GLFWwindow* window)
|
|
||||||
|
|
||||||
void _glfwFocusWindowWayland(_GLFWwindow* window)
|
|
||||||
{
|
|
||||||
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
|
||||||
- "Wayland: The platform does not support setting the input focus");
|
|
||||||
+ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the input focus\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void _glfwSetWindowMonitorWayland(_GLFWwindow* window,
|
|
@ -1,17 +0,0 @@
|
|||||||
diff --git a/src/init.c b/src/init.c
|
|
||||||
index 06dbb3f2..a7c6da86 100644
|
|
||||||
--- a/src/init.c
|
|
||||||
+++ b/src/init.c
|
|
||||||
@@ -449,6 +449,12 @@ GLFWAPI int glfwInit(void)
|
|
||||||
_glfw.initialized = GLFW_TRUE;
|
|
||||||
|
|
||||||
glfwDefaultWindowHints();
|
|
||||||
+
|
|
||||||
+ fprintf(stderr, "!!! Patched GLFW from https://github.com/Admicos/minecraft-wayland\n"
|
|
||||||
+ "!!! If any issues with the window, or some issues with rendering, occur, "
|
|
||||||
+ "first try with the built-in GLFW, and if that solves the issue, report there first.\n"
|
|
||||||
+ "!!! Use outside Minecraft is untested, and things might break.\n");
|
|
||||||
+
|
|
||||||
return GLFW_TRUE;
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
|||||||
diff --git a/src/platform.c b/src/platform.c
|
|
||||||
index c5966ae7..3e7442f9 100644
|
|
||||||
--- a/src/platform.c
|
|
||||||
+++ b/src/platform.c
|
|
||||||
@@ -49,12 +49,12 @@ static const struct
|
|
||||||
#if defined(_GLFW_COCOA)
|
|
||||||
{ GLFW_PLATFORM_COCOA, _glfwConnectCocoa },
|
|
||||||
#endif
|
|
||||||
-#if defined(_GLFW_X11)
|
|
||||||
- { GLFW_PLATFORM_X11, _glfwConnectX11 },
|
|
||||||
-#endif
|
|
||||||
#if defined(_GLFW_WAYLAND)
|
|
||||||
{ GLFW_PLATFORM_WAYLAND, _glfwConnectWayland },
|
|
||||||
#endif
|
|
||||||
+#if defined(_GLFW_X11)
|
|
||||||
+ { GLFW_PLATFORM_X11, _glfwConnectX11 },
|
|
||||||
+#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
GLFWbool _glfwSelectPlatform(int desiredID, _GLFWplatform* platform)
|
|
@ -1,40 +0,0 @@
|
|||||||
diff --git a/src/libdecor.c b/src/libdecor.c
|
|
||||||
index a9c1106..1aa38b3 100644
|
|
||||||
--- a/src/libdecor.c
|
|
||||||
+++ b/src/libdecor.c
|
|
||||||
@@ -1391,22 +1391,32 @@ calculate_priority(const struct libdecor_plugin_description *plugin_description)
|
|
||||||
static bool
|
|
||||||
check_symbol_conflicts(const struct libdecor_plugin_description *plugin_description)
|
|
||||||
{
|
|
||||||
+ bool ret = true;
|
|
||||||
char * const *symbol;
|
|
||||||
+ void* main_prog = dlopen(NULL, RTLD_LAZY);
|
|
||||||
+ if (!main_prog) {
|
|
||||||
+ fprintf(stderr, "Plugin \"%s\" couldn't check conflicting symbols: \"%s\".\n",
|
|
||||||
+ plugin_description->description, dlerror());
|
|
||||||
+ return false;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
|
|
||||||
symbol = plugin_description->conflicting_symbols;
|
|
||||||
while (*symbol) {
|
|
||||||
dlerror();
|
|
||||||
- dlsym (RTLD_DEFAULT, *symbol);
|
|
||||||
+ dlsym (main_prog, *symbol);
|
|
||||||
if (!dlerror()) {
|
|
||||||
fprintf(stderr, "Plugin \"%s\" uses conflicting symbol \"%s\".\n",
|
|
||||||
plugin_description->description, *symbol);
|
|
||||||
- return false;
|
|
||||||
+ ret = false;
|
|
||||||
+ break;
|
|
||||||
}
|
|
||||||
|
|
||||||
symbol++;
|
|
||||||
}
|
|
||||||
|
|
||||||
- return true;
|
|
||||||
+ dlclose(main_prog);
|
|
||||||
+ return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct plugin_loader *
|
|
@ -1,4 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
export __NV_PRIME_RENDER_OFFLOAD=1 __VK_LAYER_NV_optimus=NVIDIA_only __GLX_VENDOR_LIBRARY_NAME=nvidia
|
|
||||||
exec "$@"
|
|
@ -1,11 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# discord RPC
|
|
||||||
for i in {0..9}; do
|
|
||||||
test -S "$XDG_RUNTIME_DIR"/discord-ipc-"$i" || ln -sf {app/com.discordapp.Discord,"$XDG_RUNTIME_DIR"}/discord-ipc-"$i";
|
|
||||||
done
|
|
||||||
|
|
||||||
export PATH="${PATH}${PATH:+:}/usr/lib/extensions/vulkan/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin"
|
|
||||||
export VK_LAYER_PATH="/usr/lib/extensions/vulkan/share/vulkan/implicit_layer.d/"
|
|
||||||
|
|
||||||
exec /app/bin/prismrun "$@"
|
|
@ -1 +0,0 @@
|
|||||||
Subproject commit 45094ca570be383d06df729b6972830ec63bd3df
|
|
@ -1,6 +0,0 @@
|
|||||||
builds:
|
|
||||||
exclude: []
|
|
||||||
include:
|
|
||||||
- "checks.x86_64-linux.*"
|
|
||||||
- "devShells.*.*"
|
|
||||||
- "packages.*.*"
|
|
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* PolyMC - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (C) 2022 Tayou <git@tayou.org>
|
|
||||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -38,17 +36,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDateTime>
|
#include <memory>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFlag>
|
#include <QFlag>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
#include <QDateTime>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <memory>
|
#include <updater/GoUpdate.h>
|
||||||
|
|
||||||
#include <BaseInstance.h>
|
#include <BaseInstance.h>
|
||||||
|
|
||||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||||
#include "ui/themes/CatPack.h"
|
|
||||||
|
|
||||||
class LaunchController;
|
class LaunchController;
|
||||||
class LocalPeer;
|
class LocalPeer;
|
||||||
@ -64,73 +62,97 @@ class AccountList;
|
|||||||
class IconList;
|
class IconList;
|
||||||
class QNetworkAccessManager;
|
class QNetworkAccessManager;
|
||||||
class JavaInstallList;
|
class JavaInstallList;
|
||||||
class ExternalUpdater;
|
class UpdateChecker;
|
||||||
class BaseProfilerFactory;
|
class BaseProfilerFactory;
|
||||||
class BaseDetachedToolFactory;
|
class BaseDetachedToolFactory;
|
||||||
class TranslationsModel;
|
class TranslationsModel;
|
||||||
class ITheme;
|
class ITheme;
|
||||||
class MCEditTool;
|
class MCEditTool;
|
||||||
class ThemeManager;
|
|
||||||
class IconTheme;
|
|
||||||
|
|
||||||
namespace Meta {
|
namespace Meta {
|
||||||
class Index;
|
class Index;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(APPLICATION)
|
#if defined(APPLICATION)
|
||||||
#undef APPLICATION
|
#undef APPLICATION
|
||||||
#endif
|
#endif
|
||||||
#define APPLICATION (static_cast<Application*>(QCoreApplication::instance()))
|
#define APPLICATION (static_cast<Application *>(QCoreApplication::instance()))
|
||||||
|
|
||||||
class Application : public QApplication {
|
class Application : public QApplication
|
||||||
|
{
|
||||||
// friends for the purpose of limiting access to deprecated stuff
|
// friends for the purpose of limiting access to deprecated stuff
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
enum Status { StartingUp, Failed, Succeeded, Initialized };
|
enum Status {
|
||||||
|
StartingUp,
|
||||||
|
Failed,
|
||||||
|
Succeeded,
|
||||||
|
Initialized
|
||||||
|
};
|
||||||
|
|
||||||
enum Capability {
|
enum Capability {
|
||||||
None = 0,
|
None = 0,
|
||||||
|
|
||||||
SupportsMSA = 1 << 0,
|
SupportsMSA = 1 << 0,
|
||||||
SupportsFlame = 1 << 1,
|
SupportsFlame = 1 << 1,
|
||||||
SupportsGameMode = 1 << 2,
|
|
||||||
SupportsMangoHud = 1 << 3,
|
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(Capabilities, Capability)
|
Q_DECLARE_FLAGS(Capabilities, Capability)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Application(int& argc, char** argv);
|
Application(int &argc, char **argv);
|
||||||
virtual ~Application();
|
virtual ~Application();
|
||||||
|
|
||||||
bool event(QEvent* event) override;
|
bool event(QEvent* event) override;
|
||||||
|
|
||||||
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 startTime.msecsTo(QDateTime::currentDateTime());
|
||||||
|
}
|
||||||
|
|
||||||
QIcon getThemedIcon(const QString& name);
|
QIcon getThemedIcon(const QString& name);
|
||||||
|
|
||||||
ThemeManager* themeManager() { return m_themeManager.get(); }
|
bool isFlatpak();
|
||||||
|
|
||||||
shared_qobject_ptr<ExternalUpdater> updater() { return m_updater; }
|
void setIconTheme(const QString& name);
|
||||||
|
|
||||||
void triggerUpdateCheck();
|
std::vector<ITheme *> getValidApplicationThemes();
|
||||||
|
|
||||||
|
void setApplicationTheme(const QString& name, bool initial);
|
||||||
|
|
||||||
|
shared_qobject_ptr<UpdateChecker> updateChecker() {
|
||||||
|
return m_updateChecker;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<TranslationsModel> translations();
|
std::shared_ptr<TranslationsModel> translations();
|
||||||
|
|
||||||
std::shared_ptr<JavaInstallList> javalist();
|
std::shared_ptr<JavaInstallList> javalist();
|
||||||
|
|
||||||
std::shared_ptr<InstanceList> instances() const { return m_instances; }
|
std::shared_ptr<InstanceList> instances() const {
|
||||||
|
return m_instances;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<IconList> icons() const { return m_icons; }
|
std::shared_ptr<IconList> icons() const {
|
||||||
|
return m_icons;
|
||||||
|
}
|
||||||
|
|
||||||
MCEditTool* mcedit() const { return m_mcedit.get(); }
|
MCEditTool *mcedit() const {
|
||||||
|
return m_mcedit.get();
|
||||||
|
}
|
||||||
|
|
||||||
shared_qobject_ptr<AccountList> accounts() const { return m_accounts; }
|
shared_qobject_ptr<AccountList> accounts() const {
|
||||||
|
return m_accounts;
|
||||||
|
}
|
||||||
|
|
||||||
Status status() const { return m_status; }
|
Status status() const {
|
||||||
|
return m_status;
|
||||||
|
}
|
||||||
|
|
||||||
const QMap<QString, std::shared_ptr<BaseProfilerFactory>>& profilers() const { return m_profilers; }
|
const QMap<QString, std::shared_ptr<BaseProfilerFactory>> &profilers() const {
|
||||||
|
return m_profilers;
|
||||||
|
}
|
||||||
|
|
||||||
void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
|
void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
|
||||||
|
|
||||||
@ -140,9 +162,7 @@ class Application : public QApplication {
|
|||||||
|
|
||||||
shared_qobject_ptr<Meta::Index> metadataIndex();
|
shared_qobject_ptr<Meta::Index> metadataIndex();
|
||||||
|
|
||||||
void updateCapabilities();
|
Capabilities currentCapabilities();
|
||||||
|
|
||||||
void detectLibraries();
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Finds and returns the full path to a jar file.
|
* Finds and returns the full path to a jar file.
|
||||||
@ -152,86 +172,73 @@ class Application : public QApplication {
|
|||||||
|
|
||||||
QString getMSAClientID();
|
QString getMSAClientID();
|
||||||
QString getFlameAPIKey();
|
QString getFlameAPIKey();
|
||||||
QString getModrinthAPIToken();
|
|
||||||
QString getUserAgent();
|
QString getUserAgent();
|
||||||
QString getUserAgentUncached();
|
QString getUserAgentUncached();
|
||||||
|
|
||||||
/// this is the root of the 'installation'. Used for automatic updates
|
/// this is the root of the 'installation'. Used for automatic updates
|
||||||
const QString& root() { return m_rootPath; }
|
const QString &root() {
|
||||||
|
return m_rootPath;
|
||||||
/// the data path the application is using
|
}
|
||||||
const QString& dataRoot() { return m_dataPath; }
|
|
||||||
|
|
||||||
bool isPortable() { return m_portable; }
|
|
||||||
|
|
||||||
const Capabilities capabilities() { return m_capabilities; }
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Opens a json file using either a system default editor, or, if not empty, the editor
|
* Opens a json file using either a system default editor, or, if not empty, the editor
|
||||||
* specified in the settings
|
* specified in the settings
|
||||||
*/
|
*/
|
||||||
bool openJsonEditor(const QString& filename);
|
bool openJsonEditor(const QString &filename);
|
||||||
|
|
||||||
InstanceWindow* showInstanceWindow(InstancePtr instance, QString page = QString());
|
InstanceWindow *showInstanceWindow(InstancePtr instance, QString page = QString());
|
||||||
MainWindow* showMainWindow(bool minimized = false);
|
MainWindow *showMainWindow(bool minimized = false);
|
||||||
|
|
||||||
void updateIsRunning(bool running);
|
void updateIsRunning(bool running);
|
||||||
bool updatesAreAllowed();
|
bool updatesAreAllowed();
|
||||||
|
|
||||||
void ShowGlobalSettings(class QWidget* parent, QString open_page = QString());
|
void ShowGlobalSettings(class QWidget * parent, QString open_page = QString());
|
||||||
|
|
||||||
int suitableMaxMem();
|
signals:
|
||||||
|
|
||||||
bool updaterEnabled();
|
|
||||||
QString updaterBinaryName();
|
|
||||||
|
|
||||||
QUrl normalizeImportUrl(QString const& url);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void updateAllowedChanged(bool status);
|
void updateAllowedChanged(bool status);
|
||||||
void globalSettingsAboutToOpen();
|
void globalSettingsAboutToOpen();
|
||||||
void globalSettingsClosed();
|
void globalSettingsClosed();
|
||||||
int currentCatChanged(int index);
|
|
||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
void clickedOnDock();
|
void clickedOnDock();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
bool launch(InstancePtr instance,
|
bool launch(
|
||||||
bool online = true,
|
InstancePtr instance,
|
||||||
bool demo = false,
|
bool online = true,
|
||||||
MinecraftServerTargetPtr serverToJoin = nullptr,
|
BaseProfilerFactory *profiler = nullptr,
|
||||||
MinecraftAccountPtr accountToUse = nullptr);
|
MinecraftServerTargetPtr serverToJoin = nullptr,
|
||||||
|
MinecraftAccountPtr accountToUse = nullptr
|
||||||
|
);
|
||||||
bool kill(InstancePtr instance);
|
bool kill(InstancePtr instance);
|
||||||
void closeCurrentWindow();
|
void closeCurrentWindow();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_windowClose();
|
void on_windowClose();
|
||||||
void messageReceived(const QByteArray& message);
|
void messageReceived(const QByteArray & message);
|
||||||
void controllerSucceeded();
|
void controllerSucceeded();
|
||||||
void controllerFailed(const QString& error);
|
void controllerFailed(const QString & error);
|
||||||
void setupWizardFinished(int status);
|
void setupWizardFinished(int status);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool handleDataMigration(const QString& currentData, const QString& oldData, const QString& name, const QString& configFile) const;
|
|
||||||
bool createSetupWizard();
|
bool createSetupWizard();
|
||||||
void performMainStartupAction();
|
void performMainStartupAction();
|
||||||
|
|
||||||
// sets the fatal error message and m_status to Failed.
|
// sets the fatal error message and m_status to Failed.
|
||||||
void showFatalErrorMessage(const QString& title, const QString& content);
|
void showFatalErrorMessage(const QString & title, const QString & content);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addRunningInstance();
|
void addRunningInstance();
|
||||||
void subRunningInstance();
|
void subRunningInstance();
|
||||||
bool shouldExitNow() const;
|
bool shouldExitNow() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QDateTime startTime;
|
QDateTime startTime;
|
||||||
|
|
||||||
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
||||||
|
|
||||||
shared_qobject_ptr<ExternalUpdater> m_updater;
|
shared_qobject_ptr<UpdateChecker> m_updateChecker;
|
||||||
shared_qobject_ptr<AccountList> m_accounts;
|
shared_qobject_ptr<AccountList> m_accounts;
|
||||||
|
|
||||||
shared_qobject_ptr<HttpMetaCache> m_metacache;
|
shared_qobject_ptr<HttpMetaCache> m_metacache;
|
||||||
@ -243,17 +250,14 @@ class Application : public QApplication {
|
|||||||
std::shared_ptr<JavaInstallList> m_javalist;
|
std::shared_ptr<JavaInstallList> m_javalist;
|
||||||
std::shared_ptr<TranslationsModel> m_translations;
|
std::shared_ptr<TranslationsModel> m_translations;
|
||||||
std::shared_ptr<GenericPageProvider> m_globalSettingsProvider;
|
std::shared_ptr<GenericPageProvider> m_globalSettingsProvider;
|
||||||
|
std::map<QString, std::unique_ptr<ITheme>> m_themes;
|
||||||
std::unique_ptr<MCEditTool> m_mcedit;
|
std::unique_ptr<MCEditTool> m_mcedit;
|
||||||
QSet<QString> m_features;
|
QSet<QString> m_features;
|
||||||
std::unique_ptr<ThemeManager> m_themeManager;
|
|
||||||
|
|
||||||
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
|
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
|
||||||
|
|
||||||
QString m_rootPath;
|
QString m_rootPath;
|
||||||
QString m_dataPath;
|
|
||||||
Status m_status = Application::StartingUp;
|
Status m_status = Application::StartingUp;
|
||||||
Capabilities m_capabilities;
|
|
||||||
bool m_portable = false;
|
|
||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive;
|
Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive;
|
||||||
@ -266,7 +270,7 @@ class Application : public QApplication {
|
|||||||
|
|
||||||
// FIXME: attach to instances instead.
|
// FIXME: attach to instances instead.
|
||||||
struct InstanceXtras {
|
struct InstanceXtras {
|
||||||
InstanceWindow* window = nullptr;
|
InstanceWindow * window = nullptr;
|
||||||
shared_qobject_ptr<LaunchController> controller;
|
shared_qobject_ptr<LaunchController> controller;
|
||||||
};
|
};
|
||||||
std::map<QString, InstanceXtras> m_instanceExtras;
|
std::map<QString, InstanceXtras> m_instanceExtras;
|
||||||
@ -277,21 +281,18 @@ class Application : public QApplication {
|
|||||||
bool m_updateRunning = false;
|
bool m_updateRunning = false;
|
||||||
|
|
||||||
// main window, if any
|
// main window, if any
|
||||||
MainWindow* m_mainWindow = nullptr;
|
MainWindow * m_mainWindow = nullptr;
|
||||||
|
|
||||||
// peer launcher instance connector - used to implement single instance launcher and signalling
|
// peer launcher instance connector - used to implement single instance launcher and signalling
|
||||||
LocalPeer* m_peerInstance = nullptr;
|
LocalPeer * m_peerInstance = nullptr;
|
||||||
|
|
||||||
SetupWizard* m_setupWizard = nullptr;
|
SetupWizard * m_setupWizard = nullptr;
|
||||||
|
public:
|
||||||
public:
|
|
||||||
QString m_detectedGLFWPath;
|
|
||||||
QString m_detectedOpenALPath;
|
|
||||||
QString m_instanceIdToLaunch;
|
QString m_instanceIdToLaunch;
|
||||||
QString m_serverToJoin;
|
QString m_serverToJoin;
|
||||||
QString m_profileToUse;
|
QString m_profileToUse;
|
||||||
bool m_liveCheck = false;
|
bool m_liveCheck = false;
|
||||||
QList<QUrl> m_urlsToImport;
|
QUrl m_zipToImport;
|
||||||
QString m_instanceIdToShowWindowOf;
|
|
||||||
std::unique_ptr<QFile> logFile;
|
std::unique_ptr<QFile> logFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* PolyMC - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -39,8 +39,7 @@
|
|||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
|
||||||
void ApplicationMessage::parse(const QByteArray& input)
|
void ApplicationMessage::parse(const QByteArray & input) {
|
||||||
{
|
|
||||||
auto doc = Json::requireDocument(input, "ApplicationMessage");
|
auto doc = Json::requireDocument(input, "ApplicationMessage");
|
||||||
auto root = Json::requireObject(doc, "ApplicationMessage");
|
auto root = Json::requireObject(doc, "ApplicationMessage");
|
||||||
|
|
||||||
@ -48,18 +47,17 @@ void ApplicationMessage::parse(const QByteArray& input)
|
|||||||
args.clear();
|
args.clear();
|
||||||
|
|
||||||
auto parsedArgs = root.value("args").toObject();
|
auto parsedArgs = root.value("args").toObject();
|
||||||
for (auto iter = parsedArgs.constBegin(); iter != parsedArgs.constEnd(); iter++) {
|
for(auto iter = parsedArgs.begin(); iter != parsedArgs.end(); iter++) {
|
||||||
args.insert(iter.key(), iter.value().toString());
|
args[iter.key()] = iter.value().toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray ApplicationMessage::serialize()
|
QByteArray ApplicationMessage::serialize() {
|
||||||
{
|
|
||||||
QJsonObject root;
|
QJsonObject root;
|
||||||
root.insert("command", command);
|
root.insert("command", command);
|
||||||
QJsonObject outArgs;
|
QJsonObject outArgs;
|
||||||
for (auto iter = args.constBegin(); iter != args.constEnd(); iter++) {
|
for (auto iter = args.begin(); iter != args.end(); iter++) {
|
||||||
outArgs.insert(iter.key(), iter.value());
|
outArgs[iter.key()] = iter.value();
|
||||||
}
|
}
|
||||||
root.insert("args", outArgs);
|
root.insert("args", outArgs);
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QByteArray>
|
|
||||||
#include <QHash>
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
struct ApplicationMessage {
|
struct ApplicationMessage {
|
||||||
QString command;
|
QString command;
|
||||||
QHash<QString, QString> args;
|
QMap<QString, QString> args;
|
||||||
|
|
||||||
QByteArray serialize();
|
QByteArray serialize();
|
||||||
void parse(const QByteArray& input);
|
void parse(const QByteArray & input);
|
||||||
};
|
};
|
||||||
|
@ -18,21 +18,27 @@
|
|||||||
#include "BaseInstaller.h"
|
#include "BaseInstaller.h"
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include "minecraft/MinecraftInstance.h"
|
||||||
|
|
||||||
BaseInstaller::BaseInstaller() {}
|
BaseInstaller::BaseInstaller()
|
||||||
|
{
|
||||||
|
|
||||||
bool BaseInstaller::isApplied(MinecraftInstance* on)
|
}
|
||||||
|
|
||||||
|
bool BaseInstaller::isApplied(MinecraftInstance *on)
|
||||||
{
|
{
|
||||||
return QFile::exists(filename(on->instanceRoot()));
|
return QFile::exists(filename(on->instanceRoot()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BaseInstaller::add(MinecraftInstance* to)
|
bool BaseInstaller::add(MinecraftInstance *to)
|
||||||
{
|
{
|
||||||
if (!patchesDir(to->instanceRoot()).exists()) {
|
if (!patchesDir(to->instanceRoot()).exists())
|
||||||
|
{
|
||||||
QDir(to->instanceRoot()).mkdir("patches");
|
QDir(to->instanceRoot()).mkdir("patches");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isApplied(to)) {
|
if (isApplied(to))
|
||||||
if (!remove(to)) {
|
{
|
||||||
|
if (!remove(to))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,16 +46,16 @@ bool BaseInstaller::add(MinecraftInstance* to)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BaseInstaller::remove(MinecraftInstance* from)
|
bool BaseInstaller::remove(MinecraftInstance *from)
|
||||||
{
|
{
|
||||||
return QFile::remove(filename(from->instanceRoot()));
|
return QFile::remove(filename(from->instanceRoot()));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BaseInstaller::filename(const QString& root) const
|
QString BaseInstaller::filename(const QString &root) const
|
||||||
{
|
{
|
||||||
return patchesDir(root).absoluteFilePath(id() + ".json");
|
return patchesDir(root).absoluteFilePath(id() + ".json");
|
||||||
}
|
}
|
||||||
QDir BaseInstaller::patchesDir(const QString& root) const
|
QDir BaseInstaller::patchesDir(const QString &root) const
|
||||||
{
|
{
|
||||||
return QDir(root + "/patches/");
|
return QDir(root + "/patches/");
|
||||||
}
|
}
|
||||||
|
@ -17,28 +17,28 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "BaseVersion.h"
|
|
||||||
|
|
||||||
class MinecraftInstance;
|
class MinecraftInstance;
|
||||||
class QDir;
|
class QDir;
|
||||||
class QString;
|
class QString;
|
||||||
class QObject;
|
class QObject;
|
||||||
class Task;
|
class Task;
|
||||||
class BaseVersion;
|
class BaseVersion;
|
||||||
|
typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
|
||||||
|
|
||||||
class BaseInstaller {
|
class BaseInstaller
|
||||||
public:
|
{
|
||||||
|
public:
|
||||||
BaseInstaller();
|
BaseInstaller();
|
||||||
virtual ~BaseInstaller(){};
|
virtual ~BaseInstaller(){};
|
||||||
bool isApplied(MinecraftInstance* on);
|
bool isApplied(MinecraftInstance *on);
|
||||||
|
|
||||||
virtual bool add(MinecraftInstance* to);
|
virtual bool add(MinecraftInstance *to);
|
||||||
virtual bool remove(MinecraftInstance* from);
|
virtual bool remove(MinecraftInstance *from);
|
||||||
|
|
||||||
virtual Task* createInstallTask(MinecraftInstance* instance, BaseVersion::Ptr version, QObject* parent) = 0;
|
virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersionPtr version, QObject *parent) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual QString id() const = 0;
|
virtual QString id() const = 0;
|
||||||
QString filename(const QString& root) const;
|
QString filename(const QString &root) const;
|
||||||
QDir patchesDir(const QString& root) const;
|
QDir patchesDir(const QString &root) const;
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* PolyMC - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -37,49 +36,39 @@
|
|||||||
|
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QJsonDocument>
|
#include <QDir>
|
||||||
#include <QJsonObject>
|
#include <QDebug>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
#include "settings/INISettingsObject.h"
|
#include "settings/INISettingsObject.h"
|
||||||
#include "settings/OverrideSetting.h"
|
|
||||||
#include "settings/Setting.h"
|
#include "settings/Setting.h"
|
||||||
|
#include "settings/OverrideSetting.h"
|
||||||
|
|
||||||
#include "BuildConfig.h"
|
|
||||||
#include "Commandline.h"
|
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
#include "Commandline.h"
|
||||||
|
#include "BuildConfig.h"
|
||||||
|
|
||||||
BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir) : QObject()
|
BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir)
|
||||||
|
: QObject()
|
||||||
{
|
{
|
||||||
m_settings = settings;
|
m_settings = settings;
|
||||||
m_global_settings = globalSettings;
|
|
||||||
m_rootDir = rootDir;
|
m_rootDir = rootDir;
|
||||||
|
|
||||||
m_settings->registerSetting("name", "Unnamed Instance");
|
m_settings->registerSetting("name", "Unnamed Instance");
|
||||||
m_settings->registerSetting("iconKey", "default");
|
m_settings->registerSetting("iconKey", "default");
|
||||||
m_settings->registerSetting("notes", "");
|
m_settings->registerSetting("notes", "");
|
||||||
|
|
||||||
m_settings->registerSetting("lastLaunchTime", 0);
|
m_settings->registerSetting("lastLaunchTime", 0);
|
||||||
m_settings->registerSetting("totalTimePlayed", 0);
|
m_settings->registerSetting("totalTimePlayed", 0);
|
||||||
m_settings->registerSetting("lastTimePlayed", 0);
|
m_settings->registerSetting("lastTimePlayed", 0);
|
||||||
|
|
||||||
m_settings->registerSetting("linkedInstances", "[]");
|
|
||||||
|
|
||||||
// Game time override
|
|
||||||
auto gameTimeOverride = m_settings->registerSetting("OverrideGameTime", false);
|
|
||||||
m_settings->registerOverride(globalSettings->getSetting("ShowGameTime"), gameTimeOverride);
|
|
||||||
m_settings->registerOverride(globalSettings->getSetting("RecordGameTime"), gameTimeOverride);
|
|
||||||
|
|
||||||
// NOTE: Sometimees InstanceType is already registered, as it was used to identify the type of
|
// NOTE: Sometimees InstanceType is already registered, as it was used to identify the type of
|
||||||
// a locally stored instance
|
// a locally stored instance
|
||||||
if (!m_settings->getSetting("InstanceType"))
|
if (!m_settings->getSetting("InstanceType"))
|
||||||
m_settings->registerSetting("InstanceType", "");
|
m_settings->registerSetting("InstanceType", "");
|
||||||
|
|
||||||
// Custom Commands
|
// Custom Commands
|
||||||
auto commandSetting = m_settings->registerSetting({ "OverrideCommands", "OverrideLaunchCmd" }, false);
|
auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
|
||||||
m_settings->registerOverride(globalSettings->getSetting("PreLaunchCommand"), commandSetting);
|
m_settings->registerOverride(globalSettings->getSetting("PreLaunchCommand"), commandSetting);
|
||||||
m_settings->registerOverride(globalSettings->getSetting("WrapperCommand"), commandSetting);
|
m_settings->registerOverride(globalSettings->getSetting("WrapperCommand"), commandSetting);
|
||||||
m_settings->registerOverride(globalSettings->getSetting("PostExitCommand"), commandSetting);
|
m_settings->registerOverride(globalSettings->getSetting("PostExitCommand"), commandSetting);
|
||||||
@ -101,8 +90,6 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
|
|||||||
m_settings->registerSetting("ManagedPackName", "");
|
m_settings->registerSetting("ManagedPackName", "");
|
||||||
m_settings->registerSetting("ManagedPackVersionID", "");
|
m_settings->registerSetting("ManagedPackVersionID", "");
|
||||||
m_settings->registerSetting("ManagedPackVersionName", "");
|
m_settings->registerSetting("ManagedPackVersionName", "");
|
||||||
|
|
||||||
m_settings->registerSetting("Profiler", "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BaseInstance::getPreLaunchCommand()
|
QString BaseInstance::getPreLaunchCommand()
|
||||||
@ -120,66 +107,53 @@ QString BaseInstance::getPostExitCommand()
|
|||||||
return settings()->get("PostExitCommand").toString();
|
return settings()->get("PostExitCommand").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BaseInstance::isManagedPack() const
|
bool BaseInstance::isManagedPack()
|
||||||
{
|
{
|
||||||
return m_settings->get("ManagedPack").toBool();
|
return settings()->get("ManagedPack").toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BaseInstance::getManagedPackType() const
|
QString BaseInstance::getManagedPackType()
|
||||||
{
|
{
|
||||||
return m_settings->get("ManagedPackType").toString();
|
return settings()->get("ManagedPackType").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BaseInstance::getManagedPackID() const
|
QString BaseInstance::getManagedPackID()
|
||||||
{
|
{
|
||||||
return m_settings->get("ManagedPackID").toString();
|
return settings()->get("ManagedPackID").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BaseInstance::getManagedPackName() const
|
QString BaseInstance::getManagedPackName()
|
||||||
{
|
{
|
||||||
return m_settings->get("ManagedPackName").toString();
|
return settings()->get("ManagedPackName").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BaseInstance::getManagedPackVersionID() const
|
QString BaseInstance::getManagedPackVersionID()
|
||||||
{
|
{
|
||||||
return m_settings->get("ManagedPackVersionID").toString();
|
return settings()->get("ManagedPackVersionID").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BaseInstance::getManagedPackVersionName() const
|
QString BaseInstance::getManagedPackVersionName()
|
||||||
{
|
{
|
||||||
return m_settings->get("ManagedPackVersionName").toString();
|
return settings()->get("ManagedPackVersionName").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseInstance::setManagedPack(const QString& type,
|
void BaseInstance::setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version)
|
||||||
const QString& id,
|
|
||||||
const QString& name,
|
|
||||||
const QString& versionId,
|
|
||||||
const QString& version)
|
|
||||||
{
|
{
|
||||||
m_settings->set("ManagedPack", true);
|
settings()->set("ManagedPack", true);
|
||||||
m_settings->set("ManagedPackType", type);
|
settings()->set("ManagedPackType", type);
|
||||||
m_settings->set("ManagedPackID", id);
|
settings()->set("ManagedPackID", id);
|
||||||
m_settings->set("ManagedPackName", name);
|
settings()->set("ManagedPackName", name);
|
||||||
m_settings->set("ManagedPackVersionID", versionId);
|
settings()->set("ManagedPackVersionID", versionId);
|
||||||
m_settings->set("ManagedPackVersionName", version);
|
settings()->set("ManagedPackVersionName", version);
|
||||||
}
|
|
||||||
|
|
||||||
void BaseInstance::copyManagedPack(BaseInstance& other)
|
|
||||||
{
|
|
||||||
m_settings->set("ManagedPack", other.isManagedPack());
|
|
||||||
m_settings->set("ManagedPackType", other.getManagedPackType());
|
|
||||||
m_settings->set("ManagedPackID", other.getManagedPackID());
|
|
||||||
m_settings->set("ManagedPackName", other.getManagedPackName());
|
|
||||||
m_settings->set("ManagedPackVersionID", other.getManagedPackVersionID());
|
|
||||||
m_settings->set("ManagedPackVersionName", other.getManagedPackVersionName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int BaseInstance::getConsoleMaxLines() const
|
int BaseInstance::getConsoleMaxLines() const
|
||||||
{
|
{
|
||||||
auto lineSetting = m_settings->getSetting("ConsoleMaxLines");
|
auto lineSetting = settings()->getSetting("ConsoleMaxLines");
|
||||||
bool conversionOk = false;
|
bool conversionOk = false;
|
||||||
int maxLines = lineSetting->get().toInt(&conversionOk);
|
int maxLines = lineSetting->get().toInt(&conversionOk);
|
||||||
if (!conversionOk) {
|
if(!conversionOk)
|
||||||
|
{
|
||||||
maxLines = lineSetting->defValue().toInt();
|
maxLines = lineSetting->defValue().toInt();
|
||||||
qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines;
|
qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines;
|
||||||
}
|
}
|
||||||
@ -188,44 +162,13 @@ int BaseInstance::getConsoleMaxLines() const
|
|||||||
|
|
||||||
bool BaseInstance::shouldStopOnConsoleOverflow() const
|
bool BaseInstance::shouldStopOnConsoleOverflow() const
|
||||||
{
|
{
|
||||||
return m_settings->get("ConsoleOverflowStop").toBool();
|
return settings()->get("ConsoleOverflowStop").toBool();
|
||||||
}
|
|
||||||
|
|
||||||
QStringList BaseInstance::getLinkedInstances() const
|
|
||||||
{
|
|
||||||
return m_settings->get("linkedInstances").toStringList();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseInstance::setLinkedInstances(const QStringList& list)
|
|
||||||
{
|
|
||||||
auto linkedInstances = m_settings->get("linkedInstances").toStringList();
|
|
||||||
m_settings->set("linkedInstances", list);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseInstance::addLinkedInstanceId(const QString& id)
|
|
||||||
{
|
|
||||||
auto linkedInstances = m_settings->get("linkedInstances").toStringList();
|
|
||||||
linkedInstances.append(id);
|
|
||||||
setLinkedInstances(linkedInstances);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BaseInstance::removeLinkedInstanceId(const QString& id)
|
|
||||||
{
|
|
||||||
auto linkedInstances = m_settings->get("linkedInstances").toStringList();
|
|
||||||
int numRemoved = linkedInstances.removeAll(id);
|
|
||||||
setLinkedInstances(linkedInstances);
|
|
||||||
return numRemoved > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BaseInstance::isLinkedToInstanceId(const QString& id) const
|
|
||||||
{
|
|
||||||
auto linkedInstances = m_settings->get("linkedInstances").toStringList();
|
|
||||||
return linkedInstances.contains(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseInstance::iconUpdated(QString key)
|
void BaseInstance::iconUpdated(QString key)
|
||||||
{
|
{
|
||||||
if (iconKey() == key) {
|
if(iconKey() == key)
|
||||||
|
{
|
||||||
emit propertiesChanged(this);
|
emit propertiesChanged(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -239,7 +182,8 @@ void BaseInstance::invalidate()
|
|||||||
void BaseInstance::changeStatus(BaseInstance::Status newStatus)
|
void BaseInstance::changeStatus(BaseInstance::Status newStatus)
|
||||||
{
|
{
|
||||||
Status status = currentStatus();
|
Status status = currentStatus();
|
||||||
if (status != newStatus) {
|
if(status != newStatus)
|
||||||
|
{
|
||||||
m_status = newStatus;
|
m_status = newStatus;
|
||||||
emit statusChanged(status, newStatus);
|
emit statusChanged(status, newStatus);
|
||||||
}
|
}
|
||||||
@ -262,19 +206,23 @@ bool BaseInstance::isRunning() const
|
|||||||
|
|
||||||
void BaseInstance::setRunning(bool running)
|
void BaseInstance::setRunning(bool running)
|
||||||
{
|
{
|
||||||
if (running == m_isRunning)
|
if(running == m_isRunning)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_isRunning = running;
|
m_isRunning = running;
|
||||||
|
|
||||||
if (!m_settings->get("RecordGameTime").toBool()) {
|
if(!m_settings->get("RecordGameTime").toBool())
|
||||||
|
{
|
||||||
emit runningStatusChanged(running);
|
emit runningStatusChanged(running);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (running) {
|
if(running)
|
||||||
|
{
|
||||||
m_timeStarted = QDateTime::currentDateTime();
|
m_timeStarted = QDateTime::currentDateTime();
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
QDateTime timeEnded = QDateTime::currentDateTime();
|
QDateTime timeEnded = QDateTime::currentDateTime();
|
||||||
|
|
||||||
qint64 current = settings()->get("totalTimePlayed").toLongLong();
|
qint64 current = settings()->get("totalTimePlayed").toLongLong();
|
||||||
@ -289,8 +237,9 @@ void BaseInstance::setRunning(bool running)
|
|||||||
|
|
||||||
int64_t BaseInstance::totalTimePlayed() const
|
int64_t BaseInstance::totalTimePlayed() const
|
||||||
{
|
{
|
||||||
qint64 current = m_settings->get("totalTimePlayed").toLongLong();
|
qint64 current = settings()->get("totalTimePlayed").toLongLong();
|
||||||
if (m_isRunning) {
|
if(m_isRunning)
|
||||||
|
{
|
||||||
QDateTime timeNow = QDateTime::currentDateTime();
|
QDateTime timeNow = QDateTime::currentDateTime();
|
||||||
return current + m_timeStarted.secsTo(timeNow);
|
return current + m_timeStarted.secsTo(timeNow);
|
||||||
}
|
}
|
||||||
@ -299,11 +248,12 @@ int64_t BaseInstance::totalTimePlayed() const
|
|||||||
|
|
||||||
int64_t BaseInstance::lastTimePlayed() const
|
int64_t BaseInstance::lastTimePlayed() const
|
||||||
{
|
{
|
||||||
if (m_isRunning) {
|
if(m_isRunning)
|
||||||
|
{
|
||||||
QDateTime timeNow = QDateTime::currentDateTime();
|
QDateTime timeNow = QDateTime::currentDateTime();
|
||||||
return m_timeStarted.secsTo(timeNow);
|
return m_timeStarted.secsTo(timeNow);
|
||||||
}
|
}
|
||||||
return m_settings->get("lastTimePlayed").toLongLong();
|
return settings()->get("lastTimePlayed").toLongLong();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseInstance::resetTimePlayed()
|
void BaseInstance::resetTimePlayed()
|
||||||
@ -322,10 +272,8 @@ QString BaseInstance::instanceRoot() const
|
|||||||
return m_rootDir;
|
return m_rootDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsObjectPtr BaseInstance::settings()
|
SettingsObjectPtr BaseInstance::settings() const
|
||||||
{
|
{
|
||||||
loadSpecificSettings();
|
|
||||||
|
|
||||||
return m_settings;
|
return m_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,14 +294,14 @@ qint64 BaseInstance::lastLaunch() const
|
|||||||
|
|
||||||
void BaseInstance::setLastLaunch(qint64 val)
|
void BaseInstance::setLastLaunch(qint64 val)
|
||||||
{
|
{
|
||||||
// FIXME: if no change, do not set. setting involves saving a file.
|
//FIXME: if no change, do not set. setting involves saving a file.
|
||||||
m_settings->set("lastLaunchTime", val);
|
m_settings->set("lastLaunchTime", val);
|
||||||
emit propertiesChanged(this);
|
emit propertiesChanged(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseInstance::setNotes(QString val)
|
void BaseInstance::setNotes(QString val)
|
||||||
{
|
{
|
||||||
// FIXME: if no change, do not set. setting involves saving a file.
|
//FIXME: if no change, do not set. setting involves saving a file.
|
||||||
m_settings->set("notes", val);
|
m_settings->set("notes", val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,7 +312,7 @@ QString BaseInstance::notes() const
|
|||||||
|
|
||||||
void BaseInstance::setIconKey(QString val)
|
void BaseInstance::setIconKey(QString val)
|
||||||
{
|
{
|
||||||
// FIXME: if no change, do not set. setting involves saving a file.
|
//FIXME: if no change, do not set. setting involves saving a file.
|
||||||
m_settings->set("iconKey", val);
|
m_settings->set("iconKey", val);
|
||||||
emit propertiesChanged(this);
|
emit propertiesChanged(this);
|
||||||
}
|
}
|
||||||
@ -376,7 +324,7 @@ QString BaseInstance::iconKey() const
|
|||||||
|
|
||||||
void BaseInstance::setName(QString val)
|
void BaseInstance::setName(QString val)
|
||||||
{
|
{
|
||||||
// FIXME: if no change, do not set. setting involves saving a file.
|
//FIXME: if no change, do not set. setting involves saving a file.
|
||||||
m_settings->set("name", val);
|
m_settings->set("name", val);
|
||||||
emit propertiesChanged(this);
|
emit propertiesChanged(this);
|
||||||
}
|
}
|
||||||
@ -388,11 +336,11 @@ QString BaseInstance::name() const
|
|||||||
|
|
||||||
QString BaseInstance::windowTitle() const
|
QString BaseInstance::windowTitle() const
|
||||||
{
|
{
|
||||||
return BuildConfig.LAUNCHER_DISPLAYNAME + ": " + name();
|
return BuildConfig.LAUNCHER_NAME + ": " + name().replace(QRegularExpression("\\s+"), " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: why is this here? move it to MinecraftInstance!!!
|
// FIXME: why is this here? move it to MinecraftInstance!!!
|
||||||
QStringList BaseInstance::extraArguments()
|
QStringList BaseInstance::extraArguments() const
|
||||||
{
|
{
|
||||||
return Commandline::splitArgs(settings()->get("JvmArgs").toString());
|
return Commandline::splitArgs(settings()->get("JvmArgs").toString());
|
||||||
}
|
}
|
||||||
@ -401,8 +349,3 @@ shared_qobject_ptr<LaunchTask> BaseInstance::getLaunchTask()
|
|||||||
{
|
{
|
||||||
return m_launchProcess;
|
return m_launchProcess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseInstance::updateRuntimeContext()
|
|
||||||
{
|
|
||||||
// NOOP
|
|
||||||
}
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* PolyMC - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -38,24 +37,22 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QMenu>
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QProcess>
|
|
||||||
#include <QSet>
|
|
||||||
#include "QObjectPtr.h"
|
#include "QObjectPtr.h"
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QProcess>
|
||||||
|
|
||||||
#include "settings/SettingsObject.h"
|
#include "settings/SettingsObject.h"
|
||||||
|
|
||||||
#include "BaseVersionList.h"
|
|
||||||
#include "MessageLevel.h"
|
|
||||||
#include "minecraft/auth/MinecraftAccount.h"
|
|
||||||
#include "pathmatcher/IPathMatcher.h"
|
|
||||||
#include "settings/INIFile.h"
|
#include "settings/INIFile.h"
|
||||||
|
#include "BaseVersionList.h"
|
||||||
|
#include "minecraft/auth/MinecraftAccount.h"
|
||||||
|
#include "MessageLevel.h"
|
||||||
|
#include "pathmatcher/IPathMatcher.h"
|
||||||
|
|
||||||
#include "net/Mode.h"
|
#include "net/Mode.h"
|
||||||
|
|
||||||
#include "RuntimeContext.h"
|
|
||||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||||
|
|
||||||
class QDir;
|
class QDir;
|
||||||
@ -64,7 +61,7 @@ class LaunchTask;
|
|||||||
class BaseInstance;
|
class BaseInstance;
|
||||||
|
|
||||||
// pointer for lazy people
|
// pointer for lazy people
|
||||||
using InstancePtr = std::shared_ptr<BaseInstance>;
|
typedef std::shared_ptr<BaseInstance> InstancePtr;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Base class for instances.
|
* \brief Base class for instances.
|
||||||
@ -74,21 +71,23 @@ using InstancePtr = std::shared_ptr<BaseInstance>;
|
|||||||
* To create a new instance type, create a new class inheriting from this class
|
* To create a new instance type, create a new class inheriting from this class
|
||||||
* and implement the pure virtual functions.
|
* and implement the pure virtual functions.
|
||||||
*/
|
*/
|
||||||
class BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance> {
|
class BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance>
|
||||||
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
protected:
|
protected:
|
||||||
/// no-touchy!
|
/// no-touchy!
|
||||||
BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir);
|
BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
|
||||||
|
|
||||||
public: /* types */
|
public: /* types */
|
||||||
enum class Status {
|
enum class Status
|
||||||
|
{
|
||||||
Present,
|
Present,
|
||||||
Gone // either nuked or invalidated
|
Gone // either nuked or invalidated
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// virtual destructor to make sure the destruction is COMPLETE
|
/// virtual destructor to make sure the destruction is COMPLETE
|
||||||
virtual ~BaseInstance() {}
|
virtual ~BaseInstance() {};
|
||||||
|
|
||||||
virtual void saveNow() = 0;
|
virtual void saveNow() = 0;
|
||||||
|
|
||||||
@ -117,7 +116,10 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
|||||||
QString instanceRoot() const;
|
QString instanceRoot() const;
|
||||||
|
|
||||||
/// Path to the instance's game root directory.
|
/// Path to the instance's game root directory.
|
||||||
virtual QString gameRoot() const { return instanceRoot(); }
|
virtual QString gameRoot() const
|
||||||
|
{
|
||||||
|
return instanceRoot();
|
||||||
|
}
|
||||||
|
|
||||||
/// Path to the instance's mods directory.
|
/// Path to the instance's mods directory.
|
||||||
virtual QString modsRoot() const = 0;
|
virtual QString modsRoot() const = 0;
|
||||||
@ -138,22 +140,24 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
|||||||
QString getPostExitCommand();
|
QString getPostExitCommand();
|
||||||
QString getWrapperCommand();
|
QString getWrapperCommand();
|
||||||
|
|
||||||
bool isManagedPack() const;
|
bool isManagedPack();
|
||||||
QString getManagedPackType() const;
|
QString getManagedPackType();
|
||||||
QString getManagedPackID() const;
|
QString getManagedPackID();
|
||||||
QString getManagedPackName() const;
|
QString getManagedPackName();
|
||||||
QString getManagedPackVersionID() const;
|
QString getManagedPackVersionID();
|
||||||
QString getManagedPackVersionName() const;
|
QString getManagedPackVersionName();
|
||||||
void setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version);
|
void setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version);
|
||||||
void copyManagedPack(BaseInstance& other);
|
|
||||||
|
|
||||||
/// guess log level from a line of game log
|
/// guess log level from a line of game log
|
||||||
virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString& line, MessageLevel::Enum level) { return level; }
|
virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level)
|
||||||
|
{
|
||||||
|
return level;
|
||||||
|
};
|
||||||
|
|
||||||
virtual QStringList extraArguments();
|
virtual QStringList extraArguments() const;
|
||||||
|
|
||||||
/// Traits. Normally inside the version, depends on instance implementation.
|
/// Traits. Normally inside the version, depends on instance implementation.
|
||||||
virtual QSet<QString> traits() const = 0;
|
virtual QSet <QString> traits() const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the time that the instance was last launched.
|
* Gets the time that the instance was last launched.
|
||||||
@ -166,24 +170,16 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
|||||||
/*!
|
/*!
|
||||||
* \brief Gets this instance's settings object.
|
* \brief Gets this instance's settings object.
|
||||||
* This settings object stores instance-specific settings.
|
* This settings object stores instance-specific settings.
|
||||||
*
|
|
||||||
* Note that this method is not const.
|
|
||||||
* It may call loadSpecificSettings() to ensure those are loaded.
|
|
||||||
*
|
|
||||||
* \return A pointer to this instance's settings object.
|
* \return A pointer to this instance's settings object.
|
||||||
*/
|
*/
|
||||||
virtual SettingsObjectPtr settings();
|
virtual SettingsObjectPtr settings() const;
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Loads settings specific to an instance type if they're not already loaded.
|
|
||||||
*/
|
|
||||||
virtual void loadSpecificSettings() = 0;
|
|
||||||
|
|
||||||
/// returns a valid update task
|
/// returns a valid update task
|
||||||
virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0;
|
virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0;
|
||||||
|
|
||||||
/// returns a valid launcher (task container)
|
/// returns a valid launcher (task container)
|
||||||
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0;
|
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(
|
||||||
|
AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0;
|
||||||
|
|
||||||
/// returns the current launch task (if any)
|
/// returns the current launch task (if any)
|
||||||
shared_qobject_ptr<LaunchTask> getLaunchTask();
|
shared_qobject_ptr<LaunchTask> getLaunchTask();
|
||||||
@ -210,35 +206,44 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
|||||||
virtual QString instanceConfigFolder() const = 0;
|
virtual QString instanceConfigFolder() const = 0;
|
||||||
|
|
||||||
/// get variables this instance exports
|
/// get variables this instance exports
|
||||||
virtual QMap<QString, QString> getVariables() = 0;
|
virtual QMap<QString, QString> getVariables() const = 0;
|
||||||
|
|
||||||
virtual QString typeName() const = 0;
|
virtual QString typeName() const = 0;
|
||||||
|
|
||||||
void updateRuntimeContext();
|
bool hasVersionBroken() const
|
||||||
RuntimeContext runtimeContext() const { return m_runtimeContext; }
|
{
|
||||||
|
return m_hasBrokenVersion;
|
||||||
bool hasVersionBroken() const { return m_hasBrokenVersion; }
|
}
|
||||||
void setVersionBroken(bool value)
|
void setVersionBroken(bool value)
|
||||||
{
|
{
|
||||||
if (m_hasBrokenVersion != value) {
|
if(m_hasBrokenVersion != value)
|
||||||
|
{
|
||||||
m_hasBrokenVersion = value;
|
m_hasBrokenVersion = value;
|
||||||
emit propertiesChanged(this);
|
emit propertiesChanged(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasUpdateAvailable() const { return m_hasUpdate; }
|
bool hasUpdateAvailable() const
|
||||||
|
{
|
||||||
|
return m_hasUpdate;
|
||||||
|
}
|
||||||
void setUpdateAvailable(bool value)
|
void setUpdateAvailable(bool value)
|
||||||
{
|
{
|
||||||
if (m_hasUpdate != value) {
|
if(m_hasUpdate != value)
|
||||||
|
{
|
||||||
m_hasUpdate = value;
|
m_hasUpdate = value;
|
||||||
emit propertiesChanged(this);
|
emit propertiesChanged(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasCrashed() const { return m_crashed; }
|
bool hasCrashed() const
|
||||||
|
{
|
||||||
|
return m_crashed;
|
||||||
|
}
|
||||||
void setCrashed(bool value)
|
void setCrashed(bool value)
|
||||||
{
|
{
|
||||||
if (m_crashed != value) {
|
if(m_crashed != value)
|
||||||
|
{
|
||||||
m_crashed = value;
|
m_crashed = value;
|
||||||
emit propertiesChanged(this);
|
emit propertiesChanged(this);
|
||||||
}
|
}
|
||||||
@ -248,8 +253,6 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
|||||||
virtual bool canEdit() const = 0;
|
virtual bool canEdit() const = 0;
|
||||||
virtual bool canExport() const = 0;
|
virtual bool canExport() const = 0;
|
||||||
|
|
||||||
virtual void populateLaunchMenu(QMenu* menu) = 0;
|
|
||||||
|
|
||||||
bool reloadSettings();
|
bool reloadSettings();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -262,56 +265,39 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
|||||||
int getConsoleMaxLines() const;
|
int getConsoleMaxLines() const;
|
||||||
bool shouldStopOnConsoleOverflow() const;
|
bool shouldStopOnConsoleOverflow() const;
|
||||||
|
|
||||||
QStringList getLinkedInstances() const;
|
protected:
|
||||||
void setLinkedInstances(const QStringList& list);
|
|
||||||
void addLinkedInstanceId(const QString& id);
|
|
||||||
bool removeLinkedInstanceId(const QString& id);
|
|
||||||
bool isLinkedToInstanceId(const QString& id) const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void changeStatus(Status newStatus);
|
void changeStatus(Status newStatus);
|
||||||
|
|
||||||
SettingsObjectPtr globalSettings() const { return m_global_settings.lock(); }
|
signals:
|
||||||
|
|
||||||
bool isSpecificSettingsLoaded() const { return m_specific_settings_loaded; }
|
|
||||||
void setSpecificSettingsLoaded(bool loaded) { m_specific_settings_loaded = loaded; }
|
|
||||||
|
|
||||||
signals:
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Signal emitted when properties relevant to the instance view change
|
* \brief Signal emitted when properties relevant to the instance view change
|
||||||
*/
|
*/
|
||||||
void propertiesChanged(BaseInstance* inst);
|
void propertiesChanged(BaseInstance *inst);
|
||||||
|
|
||||||
void launchTaskChanged(shared_qobject_ptr<LaunchTask>);
|
void launchTaskChanged(shared_qobject_ptr<LaunchTask>);
|
||||||
|
|
||||||
void runningStatusChanged(bool running);
|
void runningStatusChanged(bool running);
|
||||||
|
|
||||||
void profilerChanged();
|
|
||||||
|
|
||||||
void statusChanged(Status from, Status to);
|
void statusChanged(Status from, Status to);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void iconUpdated(QString key);
|
void iconUpdated(QString key);
|
||||||
|
|
||||||
protected: /* data */
|
protected: /* data */
|
||||||
QString m_rootDir;
|
QString m_rootDir;
|
||||||
SettingsObjectPtr m_settings;
|
SettingsObjectPtr m_settings;
|
||||||
// InstanceFlags m_flags;
|
// InstanceFlags m_flags;
|
||||||
bool m_isRunning = false;
|
bool m_isRunning = false;
|
||||||
shared_qobject_ptr<LaunchTask> m_launchProcess;
|
shared_qobject_ptr<LaunchTask> m_launchProcess;
|
||||||
QDateTime m_timeStarted;
|
QDateTime m_timeStarted;
|
||||||
RuntimeContext m_runtimeContext;
|
|
||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
Status m_status = Status::Present;
|
Status m_status = Status::Present;
|
||||||
bool m_crashed = false;
|
bool m_crashed = false;
|
||||||
bool m_hasUpdate = false;
|
bool m_hasUpdate = false;
|
||||||
bool m_hasBrokenVersion = false;
|
bool m_hasBrokenVersion = false;
|
||||||
|
|
||||||
SettingsObjectWeakPtr m_global_settings;
|
|
||||||
bool m_specific_settings_loaded = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>)
|
Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>)
|
||||||
// Q_DECLARE_METATYPE(BaseInstance::InstanceFlag)
|
//Q_DECLARE_METATYPE(BaseInstance::InstanceFlag)
|
||||||
// Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags)
|
//Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags)
|
||||||
|
@ -15,16 +15,16 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QMetaType>
|
|
||||||
#include <QString>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <QString>
|
||||||
|
#include <QMetaType>
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* An abstract base class for versions.
|
* An abstract base class for versions.
|
||||||
*/
|
*/
|
||||||
class BaseVersion {
|
class BaseVersion
|
||||||
public:
|
{
|
||||||
using Ptr = std::shared_ptr<BaseVersion>;
|
public:
|
||||||
virtual ~BaseVersion() {}
|
virtual ~BaseVersion() {}
|
||||||
/*!
|
/*!
|
||||||
* A string used to identify this version in config files.
|
* A string used to identify this version in config files.
|
||||||
@ -43,8 +43,17 @@ class BaseVersion {
|
|||||||
* the kind of version this is (Stable, Beta, Snapshot, whatever)
|
* the kind of version this is (Stable, Beta, Snapshot, whatever)
|
||||||
*/
|
*/
|
||||||
virtual QString typeString() const = 0;
|
virtual QString typeString() const = 0;
|
||||||
virtual bool operator<(BaseVersion& a) { return name() < a.name(); }
|
|
||||||
virtual bool operator>(BaseVersion& a) { return name() > a.name(); }
|
virtual bool operator<(BaseVersion &a)
|
||||||
|
{
|
||||||
|
return name() < a.name();
|
||||||
|
};
|
||||||
|
virtual bool operator>(BaseVersion &a)
|
||||||
|
{
|
||||||
|
return name() > a.name();
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(BaseVersion::Ptr)
|
typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(BaseVersionPtr)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* PolyMC - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -36,26 +36,29 @@
|
|||||||
#include "BaseVersionList.h"
|
#include "BaseVersionList.h"
|
||||||
#include "BaseVersion.h"
|
#include "BaseVersion.h"
|
||||||
|
|
||||||
BaseVersionList::BaseVersionList(QObject* parent) : QAbstractListModel(parent) {}
|
BaseVersionList::BaseVersionList(QObject *parent) : QAbstractListModel(parent)
|
||||||
|
|
||||||
BaseVersion::Ptr BaseVersionList::findVersion(const QString& descriptor)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < count(); i++) {
|
}
|
||||||
|
|
||||||
|
BaseVersionPtr BaseVersionList::findVersion(const QString &descriptor)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count(); i++)
|
||||||
|
{
|
||||||
if (at(i)->descriptor() == descriptor)
|
if (at(i)->descriptor() == descriptor)
|
||||||
return at(i);
|
return at(i);
|
||||||
}
|
}
|
||||||
return nullptr;
|
return BaseVersionPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseVersion::Ptr BaseVersionList::getRecommended() const
|
BaseVersionPtr BaseVersionList::getRecommended() const
|
||||||
{
|
{
|
||||||
if (count() <= 0)
|
if (count() <= 0)
|
||||||
return nullptr;
|
return BaseVersionPtr();
|
||||||
else
|
else
|
||||||
return at(0);
|
return at(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant BaseVersionList::data(const QModelIndex& index, int role) const
|
QVariant BaseVersionList::data(const QModelIndex &index, int role) const
|
||||||
{
|
{
|
||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
return QVariant();
|
return QVariant();
|
||||||
@ -63,40 +66,41 @@ QVariant BaseVersionList::data(const QModelIndex& index, int role) const
|
|||||||
if (index.row() > count())
|
if (index.row() > count())
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
||||||
BaseVersion::Ptr version = at(index.row());
|
BaseVersionPtr version = at(index.row());
|
||||||
|
|
||||||
switch (role) {
|
switch (role)
|
||||||
case VersionPointerRole:
|
{
|
||||||
return QVariant::fromValue(version);
|
case VersionPointerRole:
|
||||||
|
return QVariant::fromValue(version);
|
||||||
|
|
||||||
case VersionRole:
|
case VersionRole:
|
||||||
return version->name();
|
return version->name();
|
||||||
|
|
||||||
case VersionIdRole:
|
case VersionIdRole:
|
||||||
return version->descriptor();
|
return version->descriptor();
|
||||||
|
|
||||||
case TypeRole:
|
case TypeRole:
|
||||||
return version->typeString();
|
return version->typeString();
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseVersionList::RoleList BaseVersionList::providesRoles() const
|
BaseVersionList::RoleList BaseVersionList::providesRoles() const
|
||||||
{
|
{
|
||||||
return { VersionPointerRole, VersionRole, VersionIdRole, TypeRole };
|
return {VersionPointerRole, VersionRole, VersionIdRole, TypeRole};
|
||||||
}
|
}
|
||||||
|
|
||||||
int BaseVersionList::rowCount(const QModelIndex& parent) const
|
int BaseVersionList::rowCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
// Return count
|
// Return count
|
||||||
return parent.isValid() ? 0 : count();
|
return count();
|
||||||
}
|
}
|
||||||
|
|
||||||
int BaseVersionList::columnCount(const QModelIndex& parent) const
|
int BaseVersionList::columnCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
return parent.isValid() ? 0 : 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray> BaseVersionList::roleNames() const
|
QHash<int, QByteArray> BaseVersionList::roleNames() const
|
||||||
|
@ -15,13 +15,13 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
|
||||||
#include "BaseVersion.h"
|
#include "BaseVersion.h"
|
||||||
#include "QObjectPtr.h"
|
|
||||||
#include "tasks/Task.h"
|
#include "tasks/Task.h"
|
||||||
|
#include "QObjectPtr.h"
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Class that each instance type's version list derives from.
|
* \brief Class that each instance type's version list derives from.
|
||||||
@ -35,10 +35,12 @@
|
|||||||
* all have a default implementation, but they can be overridden by plugins to
|
* all have a default implementation, but they can be overridden by plugins to
|
||||||
* change the behavior of the list.
|
* change the behavior of the list.
|
||||||
*/
|
*/
|
||||||
class BaseVersionList : public QAbstractListModel {
|
class BaseVersionList : public QAbstractListModel
|
||||||
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
enum ModelRoles {
|
enum ModelRoles
|
||||||
|
{
|
||||||
VersionPointerRole = Qt::UserRole,
|
VersionPointerRole = Qt::UserRole,
|
||||||
VersionRole,
|
VersionRole,
|
||||||
VersionIdRole,
|
VersionIdRole,
|
||||||
@ -51,9 +53,9 @@ class BaseVersionList : public QAbstractListModel {
|
|||||||
ArchitectureRole,
|
ArchitectureRole,
|
||||||
SortRole
|
SortRole
|
||||||
};
|
};
|
||||||
using RoleList = QList<int>;
|
typedef QList<int> RoleList;
|
||||||
|
|
||||||
explicit BaseVersionList(QObject* parent = 0);
|
explicit BaseVersionList(QObject *parent = 0);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Gets a task that will reload the version list.
|
* \brief Gets a task that will reload the version list.
|
||||||
@ -64,19 +66,19 @@ class BaseVersionList : public QAbstractListModel {
|
|||||||
virtual Task::Ptr getLoadTask() = 0;
|
virtual Task::Ptr getLoadTask() = 0;
|
||||||
|
|
||||||
//! Checks whether or not the list is loaded. If this returns false, the list should be
|
//! Checks whether or not the list is loaded. If this returns false, the list should be
|
||||||
// loaded.
|
//loaded.
|
||||||
virtual bool isLoaded() = 0;
|
virtual bool isLoaded() = 0;
|
||||||
|
|
||||||
//! Gets the version at the given index.
|
//! Gets the version at the given index.
|
||||||
virtual const BaseVersion::Ptr at(int i) const = 0;
|
virtual const BaseVersionPtr at(int i) const = 0;
|
||||||
|
|
||||||
//! Returns the number of versions in the list.
|
//! Returns the number of versions in the list.
|
||||||
virtual int count() const = 0;
|
virtual int count() const = 0;
|
||||||
|
|
||||||
//////// List Model Functions ////////
|
//////// List Model Functions ////////
|
||||||
QVariant data(const QModelIndex& index, int role) const override;
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
int rowCount(const QModelIndex& parent) const override;
|
int rowCount(const QModelIndex &parent) const override;
|
||||||
int columnCount(const QModelIndex& parent) const override;
|
int columnCount(const QModelIndex &parent) const override;
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
//! which roles are provided by this version list?
|
//! which roles are provided by this version list?
|
||||||
@ -88,20 +90,21 @@ class BaseVersionList : public QAbstractListModel {
|
|||||||
* \return A const pointer to the version with the given descriptor. NULL if
|
* \return A const pointer to the version with the given descriptor. NULL if
|
||||||
* one doesn't exist.
|
* one doesn't exist.
|
||||||
*/
|
*/
|
||||||
virtual BaseVersion::Ptr findVersion(const QString& descriptor);
|
virtual BaseVersionPtr findVersion(const QString &descriptor);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Gets the recommended version from this list
|
* \brief Gets the recommended version from this list
|
||||||
* If the list doesn't support recommended versions, this works exactly as getLatestStable
|
* If the list doesn't support recommended versions, this works exactly as getLatestStable
|
||||||
*/
|
*/
|
||||||
virtual BaseVersion::Ptr getRecommended() const;
|
virtual BaseVersionPtr getRecommended() const;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Sorts the version list.
|
* Sorts the version list.
|
||||||
*/
|
*/
|
||||||
virtual void sortVersions() = 0;
|
virtual void sortVersions() = 0;
|
||||||
|
|
||||||
protected slots:
|
protected
|
||||||
|
slots:
|
||||||
/*!
|
/*!
|
||||||
* Updates this list with the given list of versions.
|
* Updates this list with the given list of versions.
|
||||||
* This is done by copying each version in the given list and inserting it
|
* This is done by copying each version in the given list and inserting it
|
||||||
@ -114,5 +117,5 @@ class BaseVersionList : public QAbstractListModel {
|
|||||||
* then copies the versions and sets their parents correctly.
|
* then copies the versions and sets their parents correctly.
|
||||||
* \param versions List of versions whose parents should be set.
|
* \param versions List of versions whose parents should be set.
|
||||||
*/
|
*/
|
||||||
virtual void updateListData(QList<BaseVersion::Ptr> versions) = 0;
|
virtual void updateListData(QList<BaseVersionPtr> versions) = 0;
|
||||||
};
|
};
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* PolyMC - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -41,7 +41,8 @@
|
|||||||
* @file libutil/src/cmdutils.cpp
|
* @file libutil/src/cmdutils.cpp
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Commandline {
|
namespace Commandline
|
||||||
|
{
|
||||||
|
|
||||||
// commandline splitter
|
// commandline splitter
|
||||||
QStringList splitArgs(QString args)
|
QStringList splitArgs(QString args)
|
||||||
@ -50,15 +51,19 @@ QStringList splitArgs(QString args)
|
|||||||
QString current;
|
QString current;
|
||||||
bool escape = false;
|
bool escape = false;
|
||||||
QChar inquotes;
|
QChar inquotes;
|
||||||
for (int i = 0; i < args.length(); i++) {
|
for (int i = 0; i < args.length(); i++)
|
||||||
|
{
|
||||||
QChar cchar = args.at(i);
|
QChar cchar = args.at(i);
|
||||||
|
|
||||||
// \ escaped
|
// \ escaped
|
||||||
if (escape) {
|
if (escape)
|
||||||
|
{
|
||||||
current += cchar;
|
current += cchar;
|
||||||
escape = false;
|
escape = false;
|
||||||
// in "quotes"
|
// in "quotes"
|
||||||
} else if (!inquotes.isNull()) {
|
}
|
||||||
|
else if (!inquotes.isNull())
|
||||||
|
{
|
||||||
if (cchar == '\\')
|
if (cchar == '\\')
|
||||||
escape = true;
|
escape = true;
|
||||||
else if (cchar == inquotes)
|
else if (cchar == inquotes)
|
||||||
@ -66,13 +71,18 @@ QStringList splitArgs(QString args)
|
|||||||
else
|
else
|
||||||
current += cchar;
|
current += cchar;
|
||||||
// otherwise
|
// otherwise
|
||||||
} else {
|
}
|
||||||
if (cchar == ' ') {
|
else
|
||||||
if (!current.isEmpty()) {
|
{
|
||||||
|
if (cchar == ' ')
|
||||||
|
{
|
||||||
|
if (!current.isEmpty())
|
||||||
|
{
|
||||||
argv << current;
|
argv << current;
|
||||||
current.clear();
|
current.clear();
|
||||||
}
|
}
|
||||||
} else if (cchar == '"' || cchar == '\'')
|
}
|
||||||
|
else if (cchar == '"' || cchar == '\'')
|
||||||
inquotes = cchar;
|
inquotes = cchar;
|
||||||
else
|
else
|
||||||
current += cchar;
|
current += cchar;
|
||||||
@ -82,4 +92,412 @@ QStringList splitArgs(QString args)
|
|||||||
argv << current;
|
argv << current;
|
||||||
return argv;
|
return argv;
|
||||||
}
|
}
|
||||||
} // namespace Commandline
|
|
||||||
|
Parser::Parser(FlagStyle::Enum flagStyle, ArgumentStyle::Enum argStyle)
|
||||||
|
{
|
||||||
|
m_flagStyle = flagStyle;
|
||||||
|
m_argStyle = argStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// styles setter/getter
|
||||||
|
void Parser::setArgumentStyle(ArgumentStyle::Enum style)
|
||||||
|
{
|
||||||
|
m_argStyle = style;
|
||||||
|
}
|
||||||
|
ArgumentStyle::Enum Parser::argumentStyle()
|
||||||
|
{
|
||||||
|
return m_argStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::setFlagStyle(FlagStyle::Enum style)
|
||||||
|
{
|
||||||
|
m_flagStyle = style;
|
||||||
|
}
|
||||||
|
FlagStyle::Enum Parser::flagStyle()
|
||||||
|
{
|
||||||
|
return m_flagStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup methods
|
||||||
|
void Parser::addSwitch(QString name, bool def)
|
||||||
|
{
|
||||||
|
if (m_params.contains(name))
|
||||||
|
throw "Name not unique";
|
||||||
|
|
||||||
|
OptionDef *param = new OptionDef;
|
||||||
|
param->type = otSwitch;
|
||||||
|
param->name = name;
|
||||||
|
param->metavar = QString("<%1>").arg(name);
|
||||||
|
param->def = def;
|
||||||
|
|
||||||
|
m_options[name] = param;
|
||||||
|
m_params[name] = (CommonDef *)param;
|
||||||
|
m_optionList.append(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::addOption(QString name, QVariant def)
|
||||||
|
{
|
||||||
|
if (m_params.contains(name))
|
||||||
|
throw "Name not unique";
|
||||||
|
|
||||||
|
OptionDef *param = new OptionDef;
|
||||||
|
param->type = otOption;
|
||||||
|
param->name = name;
|
||||||
|
param->metavar = QString("<%1>").arg(name);
|
||||||
|
param->def = def;
|
||||||
|
|
||||||
|
m_options[name] = param;
|
||||||
|
m_params[name] = (CommonDef *)param;
|
||||||
|
m_optionList.append(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::addArgument(QString name, bool required, QVariant def)
|
||||||
|
{
|
||||||
|
if (m_params.contains(name))
|
||||||
|
throw "Name not unique";
|
||||||
|
|
||||||
|
PositionalDef *param = new PositionalDef;
|
||||||
|
param->name = name;
|
||||||
|
param->def = def;
|
||||||
|
param->required = required;
|
||||||
|
param->metavar = name;
|
||||||
|
|
||||||
|
m_positionals.append(param);
|
||||||
|
m_params[name] = (CommonDef *)param;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::addDocumentation(QString name, QString doc, QString metavar)
|
||||||
|
{
|
||||||
|
if (!m_params.contains(name))
|
||||||
|
throw "Name does not exist";
|
||||||
|
|
||||||
|
CommonDef *param = m_params[name];
|
||||||
|
param->doc = doc;
|
||||||
|
if (!metavar.isNull())
|
||||||
|
param->metavar = metavar;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::addShortOpt(QString name, QChar flag)
|
||||||
|
{
|
||||||
|
if (!m_params.contains(name))
|
||||||
|
throw "Name does not exist";
|
||||||
|
if (!m_options.contains(name))
|
||||||
|
throw "Name is not an Option or Swtich";
|
||||||
|
|
||||||
|
OptionDef *param = m_options[name];
|
||||||
|
m_flags[flag] = param;
|
||||||
|
param->flag = flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
// help methods
|
||||||
|
QString Parser::compileHelp(QString progName, int helpIndent, bool useFlags)
|
||||||
|
{
|
||||||
|
QStringList help;
|
||||||
|
help << compileUsage(progName, useFlags) << "\r\n";
|
||||||
|
|
||||||
|
// positionals
|
||||||
|
if (!m_positionals.isEmpty())
|
||||||
|
{
|
||||||
|
help << "\r\n";
|
||||||
|
help << "Positional arguments:\r\n";
|
||||||
|
QListIterator<PositionalDef *> it2(m_positionals);
|
||||||
|
while (it2.hasNext())
|
||||||
|
{
|
||||||
|
PositionalDef *param = it2.next();
|
||||||
|
help << " " << param->metavar;
|
||||||
|
help << " " << QString(helpIndent - param->metavar.length() - 1, ' ');
|
||||||
|
help << param->doc << "\r\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options
|
||||||
|
if (!m_optionList.isEmpty())
|
||||||
|
{
|
||||||
|
help << "\r\n";
|
||||||
|
QString optPrefix, flagPrefix;
|
||||||
|
getPrefix(optPrefix, flagPrefix);
|
||||||
|
|
||||||
|
help << "Options & Switches:\r\n";
|
||||||
|
QListIterator<OptionDef *> it(m_optionList);
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
OptionDef *option = it.next();
|
||||||
|
help << " ";
|
||||||
|
int nameLength = optPrefix.length() + option->name.length();
|
||||||
|
if (!option->flag.isNull())
|
||||||
|
{
|
||||||
|
nameLength += 3 + flagPrefix.length();
|
||||||
|
help << flagPrefix << option->flag << ", ";
|
||||||
|
}
|
||||||
|
help << optPrefix << option->name;
|
||||||
|
if (option->type == otOption)
|
||||||
|
{
|
||||||
|
QString arg = QString("%1%2").arg(
|
||||||
|
((m_argStyle == ArgumentStyle::Equals) ? "=" : " "), option->metavar);
|
||||||
|
nameLength += arg.length();
|
||||||
|
help << arg;
|
||||||
|
}
|
||||||
|
help << " " << QString(helpIndent - nameLength - 1, ' ');
|
||||||
|
help << option->doc << "\r\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return help.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Parser::compileUsage(QString progName, bool useFlags)
|
||||||
|
{
|
||||||
|
QStringList usage;
|
||||||
|
usage << "Usage: " << progName;
|
||||||
|
|
||||||
|
QString optPrefix, flagPrefix;
|
||||||
|
getPrefix(optPrefix, flagPrefix);
|
||||||
|
|
||||||
|
// options
|
||||||
|
QListIterator<OptionDef *> it(m_optionList);
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
OptionDef *option = it.next();
|
||||||
|
usage << " [";
|
||||||
|
if (!option->flag.isNull() && useFlags)
|
||||||
|
usage << flagPrefix << option->flag;
|
||||||
|
else
|
||||||
|
usage << optPrefix << option->name;
|
||||||
|
if (option->type == otOption)
|
||||||
|
usage << ((m_argStyle == ArgumentStyle::Equals) ? "=" : " ") << option->metavar;
|
||||||
|
usage << "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
// arguments
|
||||||
|
QListIterator<PositionalDef *> it2(m_positionals);
|
||||||
|
while (it2.hasNext())
|
||||||
|
{
|
||||||
|
PositionalDef *param = it2.next();
|
||||||
|
usage << " " << (param->required ? "<" : "[");
|
||||||
|
usage << param->metavar;
|
||||||
|
usage << (param->required ? ">" : "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
return usage.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsing
|
||||||
|
QHash<QString, QVariant> Parser::parse(QStringList argv)
|
||||||
|
{
|
||||||
|
QHash<QString, QVariant> map;
|
||||||
|
|
||||||
|
QStringListIterator it(argv);
|
||||||
|
QString programName = it.next();
|
||||||
|
|
||||||
|
QString optionPrefix;
|
||||||
|
QString flagPrefix;
|
||||||
|
QListIterator<PositionalDef *> positionals(m_positionals);
|
||||||
|
QStringList expecting;
|
||||||
|
|
||||||
|
getPrefix(optionPrefix, flagPrefix);
|
||||||
|
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
QString arg = it.next();
|
||||||
|
|
||||||
|
if (!expecting.isEmpty())
|
||||||
|
// we were expecting an argument
|
||||||
|
{
|
||||||
|
QString name = expecting.first();
|
||||||
|
/*
|
||||||
|
if (map.contains(name))
|
||||||
|
throw ParsingError(
|
||||||
|
QString("Option %2%1 was given multiple times").arg(name, optionPrefix));
|
||||||
|
*/
|
||||||
|
map[name] = QVariant(arg);
|
||||||
|
|
||||||
|
expecting.removeFirst();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.startsWith(optionPrefix))
|
||||||
|
// we have an option
|
||||||
|
{
|
||||||
|
// qDebug("Found option %s", qPrintable(arg));
|
||||||
|
|
||||||
|
QString name = arg.mid(optionPrefix.length());
|
||||||
|
QString equals;
|
||||||
|
|
||||||
|
if ((m_argStyle == ArgumentStyle::Equals ||
|
||||||
|
m_argStyle == ArgumentStyle::SpaceAndEquals) &&
|
||||||
|
name.contains("="))
|
||||||
|
{
|
||||||
|
int i = name.indexOf("=");
|
||||||
|
equals = name.mid(i + 1);
|
||||||
|
name = name.left(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_options.contains(name))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
if (map.contains(name))
|
||||||
|
throw ParsingError(QString("Option %2%1 was given multiple times")
|
||||||
|
.arg(name, optionPrefix));
|
||||||
|
*/
|
||||||
|
OptionDef *option = m_options[name];
|
||||||
|
if (option->type == otSwitch)
|
||||||
|
map[name] = true;
|
||||||
|
else // if (option->type == otOption)
|
||||||
|
{
|
||||||
|
if (m_argStyle == ArgumentStyle::Space)
|
||||||
|
expecting.append(name);
|
||||||
|
else if (!equals.isNull())
|
||||||
|
map[name] = equals;
|
||||||
|
else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
|
||||||
|
expecting.append(name);
|
||||||
|
else
|
||||||
|
throw ParsingError(QString("Option %2%1 reqires an argument.")
|
||||||
|
.arg(name, optionPrefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw ParsingError(QString("Unknown Option %2%1").arg(name, optionPrefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.startsWith(flagPrefix))
|
||||||
|
// we have (a) flag(s)
|
||||||
|
{
|
||||||
|
// qDebug("Found flags %s", qPrintable(arg));
|
||||||
|
|
||||||
|
QString flags = arg.mid(flagPrefix.length());
|
||||||
|
QString equals;
|
||||||
|
|
||||||
|
if ((m_argStyle == ArgumentStyle::Equals ||
|
||||||
|
m_argStyle == ArgumentStyle::SpaceAndEquals) &&
|
||||||
|
flags.contains("="))
|
||||||
|
{
|
||||||
|
int i = flags.indexOf("=");
|
||||||
|
equals = flags.mid(i + 1);
|
||||||
|
flags = flags.left(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < flags.length(); i++)
|
||||||
|
{
|
||||||
|
QChar flag = flags.at(i);
|
||||||
|
|
||||||
|
if (!m_flags.contains(flag))
|
||||||
|
throw ParsingError(QString("Unknown flag %2%1").arg(flag, flagPrefix));
|
||||||
|
|
||||||
|
OptionDef *option = m_flags[flag];
|
||||||
|
/*
|
||||||
|
if (map.contains(option->name))
|
||||||
|
throw ParsingError(QString("Option %2%1 was given multiple times")
|
||||||
|
.arg(option->name, optionPrefix));
|
||||||
|
*/
|
||||||
|
if (option->type == otSwitch)
|
||||||
|
map[option->name] = true;
|
||||||
|
else // if (option->type == otOption)
|
||||||
|
{
|
||||||
|
if (m_argStyle == ArgumentStyle::Space)
|
||||||
|
expecting.append(option->name);
|
||||||
|
else if (!equals.isNull())
|
||||||
|
if (i == flags.length() - 1)
|
||||||
|
map[option->name] = equals;
|
||||||
|
else
|
||||||
|
throw ParsingError(QString("Flag %4%2 of Argument-requiring Option "
|
||||||
|
"%1 not last flag in %4%3")
|
||||||
|
.arg(option->name, flag, flags, flagPrefix));
|
||||||
|
else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
|
||||||
|
expecting.append(option->name);
|
||||||
|
else
|
||||||
|
throw ParsingError(QString("Option %1 reqires an argument. (flag %3%2)")
|
||||||
|
.arg(option->name, flag, flagPrefix));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// must be a positional argument
|
||||||
|
if (!positionals.hasNext())
|
||||||
|
throw ParsingError(QString("Don't know what to do with '%1'").arg(arg));
|
||||||
|
|
||||||
|
PositionalDef *param = positionals.next();
|
||||||
|
|
||||||
|
map[param->name] = arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we're missing something
|
||||||
|
if (!expecting.isEmpty())
|
||||||
|
throw ParsingError(QString("Was still expecting arguments for %2%1").arg(
|
||||||
|
expecting.join(QString(", ") + optionPrefix), optionPrefix));
|
||||||
|
|
||||||
|
while (positionals.hasNext())
|
||||||
|
{
|
||||||
|
PositionalDef *param = positionals.next();
|
||||||
|
if (param->required)
|
||||||
|
throw ParsingError(
|
||||||
|
QString("Missing required positional argument '%1'").arg(param->name));
|
||||||
|
else
|
||||||
|
map[param->name] = param->def;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill out gaps
|
||||||
|
QListIterator<OptionDef *> iter(m_optionList);
|
||||||
|
while (iter.hasNext())
|
||||||
|
{
|
||||||
|
OptionDef *option = iter.next();
|
||||||
|
if (!map.contains(option->name))
|
||||||
|
map[option->name] = option->def;
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear defs
|
||||||
|
void Parser::clear()
|
||||||
|
{
|
||||||
|
m_flags.clear();
|
||||||
|
m_params.clear();
|
||||||
|
m_options.clear();
|
||||||
|
|
||||||
|
QMutableListIterator<OptionDef *> it(m_optionList);
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
OptionDef *option = it.next();
|
||||||
|
it.remove();
|
||||||
|
delete option;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMutableListIterator<PositionalDef *> it2(m_positionals);
|
||||||
|
while (it2.hasNext())
|
||||||
|
{
|
||||||
|
PositionalDef *arg = it2.next();
|
||||||
|
it2.remove();
|
||||||
|
delete arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
Parser::~Parser()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPrefix
|
||||||
|
void Parser::getPrefix(QString &opt, QString &flag)
|
||||||
|
{
|
||||||
|
if (m_flagStyle == FlagStyle::Windows)
|
||||||
|
opt = flag = "/";
|
||||||
|
else if (m_flagStyle == FlagStyle::Unix)
|
||||||
|
opt = flag = "-";
|
||||||
|
// else if (m_flagStyle == FlagStyle::GNU)
|
||||||
|
else
|
||||||
|
{
|
||||||
|
opt = "--";
|
||||||
|
flag = "-";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsingError
|
||||||
|
ParsingError::ParsingError(const QString &what) : std::runtime_error(what.toStdString())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,7 +17,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QHash>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,7 +30,8 @@
|
|||||||
* @brief commandline parsing and processing utilities
|
* @brief commandline parsing and processing utilities
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Commandline {
|
namespace Commandline
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief split a string into argv items like a shell would do
|
* @brief split a string into argv items like a shell would do
|
||||||
@ -33,4 +39,212 @@ namespace Commandline {
|
|||||||
* @return a QStringList containing all arguments
|
* @return a QStringList containing all arguments
|
||||||
*/
|
*/
|
||||||
QStringList splitArgs(QString args);
|
QStringList splitArgs(QString args);
|
||||||
} // namespace Commandline
|
|
||||||
|
/**
|
||||||
|
* @brief The FlagStyle enum
|
||||||
|
* Specifies how flags are decorated
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FlagStyle
|
||||||
|
{
|
||||||
|
enum Enum
|
||||||
|
{
|
||||||
|
GNU, /**< --option and -o (GNU Style) */
|
||||||
|
Unix, /**< -option and -o (Unix Style) */
|
||||||
|
Windows, /**< /option and /o (Windows Style) */
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
Default = Windows
|
||||||
|
#else
|
||||||
|
Default = GNU
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The ArgumentStyle enum
|
||||||
|
*/
|
||||||
|
namespace ArgumentStyle
|
||||||
|
{
|
||||||
|
enum Enum
|
||||||
|
{
|
||||||
|
Space, /**< --option value */
|
||||||
|
Equals, /**< --option=value */
|
||||||
|
SpaceAndEquals, /**< --option[= ]value */
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
Default = Equals
|
||||||
|
#else
|
||||||
|
Default = SpaceAndEquals
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The ParsingError class
|
||||||
|
*/
|
||||||
|
class ParsingError : public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ParsingError(const QString &what);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The Parser class
|
||||||
|
*/
|
||||||
|
class Parser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Parser constructor
|
||||||
|
* @param flagStyle the FlagStyle to use in this Parser
|
||||||
|
* @param argStyle the ArgumentStyle to use in this Parser
|
||||||
|
*/
|
||||||
|
Parser(FlagStyle::Enum flagStyle = FlagStyle::Default,
|
||||||
|
ArgumentStyle::Enum argStyle = ArgumentStyle::Default);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief set the flag style
|
||||||
|
* @param style
|
||||||
|
*/
|
||||||
|
void setFlagStyle(FlagStyle::Enum style);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get the flag style
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
FlagStyle::Enum flagStyle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief set the argument style
|
||||||
|
* @param style
|
||||||
|
*/
|
||||||
|
void setArgumentStyle(ArgumentStyle::Enum style);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get the argument style
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
ArgumentStyle::Enum argumentStyle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief define a boolean switch
|
||||||
|
* @param name the parameter name
|
||||||
|
* @param def the default value
|
||||||
|
*/
|
||||||
|
void addSwitch(QString name, bool def = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief define an option that takes an additional argument
|
||||||
|
* @param name the parameter name
|
||||||
|
* @param def the default value
|
||||||
|
*/
|
||||||
|
void addOption(QString name, QVariant def = QVariant());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief define a positional argument
|
||||||
|
* @param name the parameter name
|
||||||
|
* @param required wether this argument is required
|
||||||
|
* @param def the default value
|
||||||
|
*/
|
||||||
|
void addArgument(QString name, bool required = true, QVariant def = QVariant());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief adds a flag to an existing parameter
|
||||||
|
* @param name the (existing) parameter name
|
||||||
|
* @param flag the flag character
|
||||||
|
* @see addSwitch addArgument addOption
|
||||||
|
* Note: any one parameter can only have one flag
|
||||||
|
*/
|
||||||
|
void addShortOpt(QString name, QChar flag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief adds documentation to a Parameter
|
||||||
|
* @param name the parameter name
|
||||||
|
* @param metavar a string to be displayed as placeholder for the value
|
||||||
|
* @param doc a QString containing the documentation
|
||||||
|
* Note: on positional arguments, metavar replaces the name as displayed.
|
||||||
|
* on options , metavar replaces the value placeholder
|
||||||
|
*/
|
||||||
|
void addDocumentation(QString name, QString doc, QString metavar = QString());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief generate a help message
|
||||||
|
* @param progName the program name to use in the help message
|
||||||
|
* @param helpIndent how much the parameter documentation should be indented
|
||||||
|
* @param flagsInUsage whether we should use flags instead of options in the usage
|
||||||
|
* @return a help message
|
||||||
|
*/
|
||||||
|
QString compileHelp(QString progName, int helpIndent = 22, bool flagsInUsage = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief generate a short usage message
|
||||||
|
* @param progName the program name to use in the usage message
|
||||||
|
* @param useFlags whether we should use flags instead of options
|
||||||
|
* @return a usage message
|
||||||
|
*/
|
||||||
|
QString compileUsage(QString progName, bool useFlags = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief parse
|
||||||
|
* @param argv a QStringList containing the program ARGV
|
||||||
|
* @return a QHash mapping argument names to their values
|
||||||
|
*/
|
||||||
|
QHash<QString, QVariant> parse(QStringList argv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief clear all definitions
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
~Parser();
|
||||||
|
|
||||||
|
private:
|
||||||
|
FlagStyle::Enum m_flagStyle;
|
||||||
|
ArgumentStyle::Enum m_argStyle;
|
||||||
|
|
||||||
|
enum OptionType
|
||||||
|
{
|
||||||
|
otSwitch,
|
||||||
|
otOption
|
||||||
|
};
|
||||||
|
|
||||||
|
// Important: the common part MUST BE COMMON ON ALL THREE structs
|
||||||
|
struct CommonDef
|
||||||
|
{
|
||||||
|
QString name;
|
||||||
|
QString doc;
|
||||||
|
QString metavar;
|
||||||
|
QVariant def;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OptionDef
|
||||||
|
{
|
||||||
|
// common
|
||||||
|
QString name;
|
||||||
|
QString doc;
|
||||||
|
QString metavar;
|
||||||
|
QVariant def;
|
||||||
|
// option
|
||||||
|
OptionType type;
|
||||||
|
QChar flag;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PositionalDef
|
||||||
|
{
|
||||||
|
// common
|
||||||
|
QString name;
|
||||||
|
QString doc;
|
||||||
|
QString metavar;
|
||||||
|
QVariant def;
|
||||||
|
// positional
|
||||||
|
bool required;
|
||||||
|
};
|
||||||
|
|
||||||
|
QHash<QString, OptionDef *> m_options;
|
||||||
|
QHash<QChar, OptionDef *> m_flags;
|
||||||
|
QHash<QString, CommonDef *> m_params;
|
||||||
|
QList<PositionalDef *> m_positionals;
|
||||||
|
QList<OptionDef *> m_optionList;
|
||||||
|
|
||||||
|
void getPrefix(QString &opt, QString &flag);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
|
||||||
|
|
||||||
#include "DataMigrationTask.h"
|
|
||||||
|
|
||||||
#include "FileSystem.h"
|
|
||||||
|
|
||||||
#include <QDirIterator>
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QMap>
|
|
||||||
|
|
||||||
#include <QtConcurrent>
|
|
||||||
|
|
||||||
DataMigrationTask::DataMigrationTask(QObject* parent,
|
|
||||||
const QString& sourcePath,
|
|
||||||
const QString& targetPath,
|
|
||||||
const IPathMatcher::Ptr pathMatcher)
|
|
||||||
: Task(parent), m_sourcePath(sourcePath), m_targetPath(targetPath), m_pathMatcher(pathMatcher), m_copy(sourcePath, targetPath)
|
|
||||||
{
|
|
||||||
m_copy.matcher(m_pathMatcher.get()).whitelist(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataMigrationTask::executeTask()
|
|
||||||
{
|
|
||||||
setStatus(tr("Scanning files..."));
|
|
||||||
|
|
||||||
// 1. Scan
|
|
||||||
// Check how many files we gotta copy
|
|
||||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] {
|
|
||||||
return m_copy(true); // dry run to collect amount of files
|
|
||||||
});
|
|
||||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::dryRunFinished);
|
|
||||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &DataMigrationTask::dryRunAborted);
|
|
||||||
m_copyFutureWatcher.setFuture(m_copyFuture);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataMigrationTask::dryRunFinished()
|
|
||||||
{
|
|
||||||
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::dryRunFinished);
|
|
||||||
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &DataMigrationTask::dryRunAborted);
|
|
||||||
|
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
|
||||||
if (!m_copyFuture.isValid() || !m_copyFuture.result()) {
|
|
||||||
#else
|
|
||||||
if (!m_copyFuture.result()) {
|
|
||||||
#endif
|
|
||||||
emitFailed(tr("Failed to scan source path."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Copy
|
|
||||||
// Actually copy all files now.
|
|
||||||
m_toCopy = m_copy.totalCopied();
|
|
||||||
connect(&m_copy, &FS::copy::fileCopied, [&, this](const QString& relativeName) {
|
|
||||||
QString shortenedName = relativeName;
|
|
||||||
// shorten the filename to hopefully fit into one line
|
|
||||||
if (shortenedName.length() > 50)
|
|
||||||
shortenedName = relativeName.left(20) + "…" + relativeName.right(29);
|
|
||||||
setProgress(m_copy.totalCopied(), m_toCopy);
|
|
||||||
setStatus(tr("Copying %1…").arg(shortenedName));
|
|
||||||
});
|
|
||||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] {
|
|
||||||
return m_copy(false); // actually copy now
|
|
||||||
});
|
|
||||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::copyFinished);
|
|
||||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &DataMigrationTask::copyAborted);
|
|
||||||
m_copyFutureWatcher.setFuture(m_copyFuture);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataMigrationTask::dryRunAborted()
|
|
||||||
{
|
|
||||||
emitFailed(tr("Aborted"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataMigrationTask::copyFinished()
|
|
||||||
{
|
|
||||||
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::copyFinished);
|
|
||||||
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &DataMigrationTask::copyAborted);
|
|
||||||
|
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
|
||||||
if (!m_copyFuture.isValid() || !m_copyFuture.result()) {
|
|
||||||
#else
|
|
||||||
if (!m_copyFuture.result()) {
|
|
||||||
#endif
|
|
||||||
emitFailed(tr("Some paths could not be copied!"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emitSucceeded();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataMigrationTask::copyAborted()
|
|
||||||
{
|
|
||||||
emitFailed(tr("Aborted"));
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "FileSystem.h"
|
|
||||||
#include "pathmatcher/IPathMatcher.h"
|
|
||||||
#include "tasks/Task.h"
|
|
||||||
|
|
||||||
#include <QFuture>
|
|
||||||
#include <QFutureWatcher>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Migrate existing data from other MMC-like launchers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class DataMigrationTask : public Task {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit DataMigrationTask(QObject* parent, const QString& sourcePath, const QString& targetPath, const IPathMatcher::Ptr pathmatcher);
|
|
||||||
~DataMigrationTask() override = default;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void executeTask() override;
|
|
||||||
|
|
||||||
protected slots:
|
|
||||||
void dryRunFinished();
|
|
||||||
void dryRunAborted();
|
|
||||||
void copyFinished();
|
|
||||||
void copyAborted();
|
|
||||||
|
|
||||||
private:
|
|
||||||
const QString& m_sourcePath;
|
|
||||||
const QString& m_targetPath;
|
|
||||||
const IPathMatcher::Ptr m_pathMatcher;
|
|
||||||
|
|
||||||
FS::copy m_copy;
|
|
||||||
int m_toCopy = 0;
|
|
||||||
QFuture<bool> m_copyFuture;
|
|
||||||
QFutureWatcher<bool> m_copyFutureWatcher;
|
|
||||||
};
|
|
@ -1,21 +1,33 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class DefaultVariable {
|
class DefaultVariable
|
||||||
public:
|
{
|
||||||
DefaultVariable(const T& value) { defaultValue = value; }
|
public:
|
||||||
DefaultVariable<T>& operator=(const T& value)
|
DefaultVariable(const T & value)
|
||||||
|
{
|
||||||
|
defaultValue = value;
|
||||||
|
}
|
||||||
|
DefaultVariable<T> & operator =(const T & value)
|
||||||
{
|
{
|
||||||
currentValue = value;
|
currentValue = value;
|
||||||
is_default = currentValue == defaultValue;
|
is_default = currentValue == defaultValue;
|
||||||
is_explicit = true;
|
is_explicit = true;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
operator const T&() const { return is_default ? defaultValue : currentValue; }
|
operator const T &() const
|
||||||
bool isDefault() const { return is_default; }
|
{
|
||||||
bool isExplicit() const { return is_explicit; }
|
return is_default ? defaultValue : currentValue;
|
||||||
|
}
|
||||||
private:
|
bool isDefault() const
|
||||||
|
{
|
||||||
|
return is_default;
|
||||||
|
}
|
||||||
|
bool isExplicit() const
|
||||||
|
{
|
||||||
|
return is_explicit;
|
||||||
|
}
|
||||||
|
private:
|
||||||
T currentValue;
|
T currentValue;
|
||||||
T defaultValue;
|
T defaultValue;
|
||||||
bool is_default = true;
|
bool is_default = true;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* PolyMC - Minecraft Launcher
|
||||||
* Copyright (C) 2022 dada513 <dada513@protonmail.com>
|
* Copyright (C) 2022 dada513 <dada513@protonmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -33,37 +33,41 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
#include "DesktopServices.h"
|
#include "DesktopServices.h"
|
||||||
#include <QDebug>
|
|
||||||
#include <QDesktopServices>
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QDesktopServices>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
#include <QDebug>
|
||||||
|
#include "Application.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing.
|
* This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing.
|
||||||
*/
|
*/
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool IndirectOpen(T callable, qint64* pid_forked = nullptr)
|
bool IndirectOpen(T callable, qint64 *pid_forked = nullptr)
|
||||||
{
|
{
|
||||||
auto pid = fork();
|
auto pid = fork();
|
||||||
if (pid_forked) {
|
if(pid_forked)
|
||||||
if (pid > 0)
|
{
|
||||||
|
if(pid > 0)
|
||||||
*pid_forked = pid;
|
*pid_forked = pid;
|
||||||
else
|
else
|
||||||
*pid_forked = 0;
|
*pid_forked = 0;
|
||||||
}
|
}
|
||||||
if (pid == -1) {
|
if(pid == -1)
|
||||||
|
{
|
||||||
qWarning() << "IndirectOpen failed to fork: " << errno;
|
qWarning() << "IndirectOpen failed to fork: " << errno;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// child - do the stuff
|
// child - do the stuff
|
||||||
if (pid == 0) {
|
if(pid == 0)
|
||||||
|
{
|
||||||
// unset all this garbage so it doesn't get passed to the child process
|
// unset all this garbage so it doesn't get passed to the child process
|
||||||
qunsetenv("LD_PRELOAD");
|
qunsetenv("LD_PRELOAD");
|
||||||
qunsetenv("LD_LIBRARY_PATH");
|
qunsetenv("LD_LIBRARY_PATH");
|
||||||
@ -79,14 +83,19 @@ bool IndirectOpen(T callable, qint64* pid_forked = nullptr)
|
|||||||
|
|
||||||
// die. now. do not clean up anything, it would just hang forever.
|
// die. now. do not clean up anything, it would just hang forever.
|
||||||
_exit(status ? 0 : 1);
|
_exit(status ? 0 : 1);
|
||||||
} else {
|
}
|
||||||
// parent - assume it worked.
|
else
|
||||||
|
{
|
||||||
|
//parent - assume it worked.
|
||||||
int status;
|
int status;
|
||||||
while (waitpid(pid, &status, 0)) {
|
while (waitpid(pid, &status, 0))
|
||||||
if (WIFEXITED(status)) {
|
{
|
||||||
|
if(WIFEXITED(status))
|
||||||
|
{
|
||||||
return WEXITSTATUS(status) == 0;
|
return WEXITSTATUS(status) == 0;
|
||||||
}
|
}
|
||||||
if (WIFSIGNALED(status)) {
|
if(WIFSIGNALED(status))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,19 +105,26 @@ bool IndirectOpen(T callable, qint64* pid_forked = nullptr)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace DesktopServices {
|
namespace DesktopServices {
|
||||||
bool openDirectory(const QString& path, [[maybe_unused]] bool ensureExists)
|
bool openDirectory(const QString &path, bool ensureExists)
|
||||||
{
|
{
|
||||||
qDebug() << "Opening directory" << path;
|
qDebug() << "Opening directory" << path;
|
||||||
QDir parentPath;
|
QDir parentPath;
|
||||||
QDir dir(path);
|
QDir dir(path);
|
||||||
if (ensureExists && !dir.exists()) {
|
if (!dir.exists())
|
||||||
|
{
|
||||||
parentPath.mkpath(dir.absolutePath());
|
parentPath.mkpath(dir.absolutePath());
|
||||||
}
|
}
|
||||||
auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); };
|
auto f = [&]()
|
||||||
|
{
|
||||||
|
return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
|
||||||
|
};
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||||
if (!isSandbox()) {
|
if(!APPLICATION->isFlatpak())
|
||||||
|
{
|
||||||
return IndirectOpen(f);
|
return IndirectOpen(f);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
return f();
|
return f();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@ -116,14 +132,20 @@ bool openDirectory(const QString& path, [[maybe_unused]] bool ensureExists)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool openFile(const QString& path)
|
bool openFile(const QString &path)
|
||||||
{
|
{
|
||||||
qDebug() << "Opening file" << path;
|
qDebug() << "Opening file" << path;
|
||||||
auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(path)); };
|
auto f = [&]()
|
||||||
|
{
|
||||||
|
return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||||
|
};
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||||
if (!isSandbox()) {
|
if(!APPLICATION->isFlatpak())
|
||||||
|
{
|
||||||
return IndirectOpen(f);
|
return IndirectOpen(f);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
return f();
|
return f();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@ -131,29 +153,41 @@ bool openFile(const QString& path)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool openFile(const QString& application, const QString& path, const QString& workingDirectory, qint64* pid)
|
bool openFile(const QString &application, const QString &path, const QString &workingDirectory, qint64 *pid)
|
||||||
{
|
{
|
||||||
qDebug() << "Opening file" << path << "using" << application;
|
qDebug() << "Opening file" << path << "using" << application;
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
||||||
if (!isSandbox()) {
|
if(!APPLICATION->isFlatpak())
|
||||||
return IndirectOpen([&]() { return QProcess::startDetached(application, QStringList() << path, workingDirectory); }, pid);
|
{
|
||||||
} else {
|
return IndirectOpen([&]()
|
||||||
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
|
{
|
||||||
|
return QProcess::startDetached(application, QStringList() << path, workingDirectory);
|
||||||
|
}, pid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
|
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool run(const QString& application, const QStringList& args, const QString& workingDirectory, qint64* pid)
|
bool run(const QString &application, const QStringList &args, const QString &workingDirectory, qint64 *pid)
|
||||||
{
|
{
|
||||||
qDebug() << "Running" << application << "with args" << args.join(' ');
|
qDebug() << "Running" << application << "with args" << args.join(' ');
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||||
if (!isSandbox()) {
|
if(!APPLICATION->isFlatpak())
|
||||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
{
|
||||||
return IndirectOpen([&]() { return QProcess::startDetached(application, args, workingDirectory); }, pid);
|
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
||||||
} else {
|
return IndirectOpen([&]()
|
||||||
|
{
|
||||||
|
return QProcess::startDetached(application, args, workingDirectory);
|
||||||
|
}, pid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
return QProcess::startDetached(application, args, workingDirectory, pid);
|
return QProcess::startDetached(application, args, workingDirectory, pid);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@ -161,14 +195,20 @@ bool run(const QString& application, const QStringList& args, const QString& wor
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool openUrl(const QUrl& url)
|
bool openUrl(const QUrl &url)
|
||||||
{
|
{
|
||||||
qDebug() << "Opening URL" << url.toString();
|
qDebug() << "Opening URL" << url.toString();
|
||||||
auto f = [&]() { return QDesktopServices::openUrl(url); };
|
auto f = [&]()
|
||||||
|
{
|
||||||
|
return QDesktopServices::openUrl(url);
|
||||||
|
};
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||||
if (!isSandbox()) {
|
if(!APPLICATION->isFlatpak())
|
||||||
|
{
|
||||||
return IndirectOpen(f);
|
return IndirectOpen(f);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
return f();
|
return f();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@ -176,27 +216,4 @@ bool openUrl(const QUrl& url)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isFlatpak()
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_LINUX
|
|
||||||
return QFile::exists("/.flatpak-info");
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSnap()
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_LINUX
|
|
||||||
return getenv("SNAP");
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isSandbox()
|
|
||||||
{
|
|
||||||
return isSnap() || isFlatpak();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace DesktopServices
|
|
||||||
|
@ -1,50 +1,36 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This wraps around QDesktopServices and adds workarounds where needed
|
* This wraps around QDesktopServices and adds workarounds where needed
|
||||||
* Use this instead of QDesktopServices!
|
* Use this instead of QDesktopServices!
|
||||||
*/
|
*/
|
||||||
namespace DesktopServices {
|
namespace DesktopServices
|
||||||
/**
|
{
|
||||||
* Open a file in whatever application is applicable
|
/**
|
||||||
*/
|
* Open a file in whatever application is applicable
|
||||||
bool openFile(const QString& path);
|
*/
|
||||||
|
bool openFile(const QString &path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open a file in the specified application
|
* Open a file in the specified application
|
||||||
*/
|
*/
|
||||||
bool openFile(const QString& application, const QString& path, const QString& workingDirectory = QString(), qint64* pid = 0);
|
bool openFile(const QString &application, const QString &path, const QString & workingDirectory = QString(), qint64 *pid = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run an application
|
* Run an application
|
||||||
*/
|
*/
|
||||||
bool run(const QString& application, const QStringList& args, const QString& workingDirectory = QString(), qint64* pid = 0);
|
bool run(const QString &application,const QStringList &args, const QString & workingDirectory = QString(), qint64 *pid = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open a directory
|
* Open a directory
|
||||||
*/
|
*/
|
||||||
bool openDirectory(const QString& path, bool ensureExists = false);
|
bool openDirectory(const QString &path, bool ensureExists = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the URL, most likely in a browser. Maybe.
|
* Open the URL, most likely in a browser. Maybe.
|
||||||
*/
|
*/
|
||||||
bool openUrl(const QUrl& url);
|
bool openUrl(const QUrl &url);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Determine whether the launcher is running in a Flatpak environment
|
|
||||||
*/
|
|
||||||
bool isFlatpak();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the launcher is running in a Snap environment
|
|
||||||
*/
|
|
||||||
bool isSnap();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the launcher is running in a sandboxed (Flatpak or Snap) environment
|
|
||||||
*/
|
|
||||||
bool isSandbox();
|
|
||||||
} // namespace DesktopServices
|
|
||||||
|
@ -2,18 +2,31 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QDebug>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
|
||||||
class Exception : public std::exception {
|
class Exception : public std::exception
|
||||||
public:
|
{
|
||||||
Exception(const QString& message) : std::exception(), m_message(message) { qCritical() << "Exception:" << message; }
|
public:
|
||||||
Exception(const Exception& other) : std::exception(), m_message(other.cause()) {}
|
Exception(const QString &message) : std::exception(), m_message(message)
|
||||||
|
{
|
||||||
|
qCritical() << "Exception:" << message;
|
||||||
|
}
|
||||||
|
Exception(const Exception &other)
|
||||||
|
: std::exception(), m_message(other.cause())
|
||||||
|
{
|
||||||
|
}
|
||||||
virtual ~Exception() noexcept {}
|
virtual ~Exception() noexcept {}
|
||||||
const char* what() const noexcept { return m_message.toLatin1().constData(); }
|
const char *what() const noexcept
|
||||||
QString cause() const { return m_message; }
|
{
|
||||||
|
return m_message.toLatin1().constData();
|
||||||
|
}
|
||||||
|
QString cause() const
|
||||||
|
{
|
||||||
|
return m_message;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_message;
|
QString m_message;
|
||||||
};
|
};
|
||||||
|
@ -4,24 +4,31 @@
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
inline void clamp(T& current, T min, T max)
|
inline void clamp(T& current, T min, T max)
|
||||||
{
|
{
|
||||||
if (current < min) {
|
if (current < min)
|
||||||
|
{
|
||||||
current = min;
|
current = min;
|
||||||
} else if (current > max) {
|
}
|
||||||
|
else if(current > max)
|
||||||
|
{
|
||||||
current = max;
|
current = max;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// List of numbers from min to max. Next is exponent times bigger than previous.
|
// List of numbers from min to max. Next is exponent times bigger than previous.
|
||||||
|
|
||||||
class ExponentialSeries {
|
class ExponentialSeries
|
||||||
public:
|
{
|
||||||
|
public:
|
||||||
ExponentialSeries(unsigned min, unsigned max, unsigned exponent = 2)
|
ExponentialSeries(unsigned min, unsigned max, unsigned exponent = 2)
|
||||||
{
|
{
|
||||||
m_current = m_min = min;
|
m_current = m_min = min;
|
||||||
m_max = max;
|
m_max = max;
|
||||||
m_exponent = exponent;
|
m_exponent = exponent;
|
||||||
}
|
}
|
||||||
void reset() { m_current = m_min; }
|
void reset()
|
||||||
|
{
|
||||||
|
m_current = m_min;
|
||||||
|
}
|
||||||
unsigned operator()()
|
unsigned operator()()
|
||||||
{
|
{
|
||||||
unsigned retval = m_current;
|
unsigned retval = m_current;
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
|
||||||
/*
|
|
||||||
* Prism Launcher - Minecraft Launcher
|
|
||||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "FastFileIconProvider.h"
|
|
||||||
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QStyle>
|
|
||||||
|
|
||||||
QIcon FastFileIconProvider::icon(const QFileInfo& info) const
|
|
||||||
{
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
|
|
||||||
bool link = info.isSymbolicLink() || info.isAlias() || info.isShortcut();
|
|
||||||
#else
|
|
||||||
// in versions prior to 6.4 we don't have access to isAlias
|
|
||||||
bool link = info.isSymLink();
|
|
||||||
#endif
|
|
||||||
QStyle::StandardPixmap icon;
|
|
||||||
|
|
||||||
if (info.isDir()) {
|
|
||||||
if (link)
|
|
||||||
icon = QStyle::SP_DirLinkIcon;
|
|
||||||
else
|
|
||||||
icon = QStyle::SP_DirIcon;
|
|
||||||
} else {
|
|
||||||
if (link)
|
|
||||||
icon = QStyle::SP_FileLinkIcon;
|
|
||||||
else
|
|
||||||
icon = QStyle::SP_FileIcon;
|
|
||||||
}
|
|
||||||
|
|
||||||
return QApplication::style()->standardIcon(icon);
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
|
||||||
/*
|
|
||||||
* Prism Launcher - Minecraft Launcher
|
|
||||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QFileIconProvider>
|
|
||||||
|
|
||||||
class FastFileIconProvider : public QFileIconProvider {
|
|
||||||
public:
|
|
||||||
QIcon icon(const QFileInfo& info) const override;
|
|
||||||
};
|
|
@ -1,276 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
|
||||||
/*
|
|
||||||
* Prism Launcher - Minecraft Launcher
|
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
|
||||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* This file incorporates work covered by the following copyright and
|
|
||||||
* permission notice:
|
|
||||||
*
|
|
||||||
* Copyright 2013-2021 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "FileIgnoreProxy.h"
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QFileSystemModel>
|
|
||||||
#include <QSortFilterProxyModel>
|
|
||||||
#include <QStack>
|
|
||||||
#include <algorithm>
|
|
||||||
#include "FileSystem.h"
|
|
||||||
#include "SeparatorPrefixTree.h"
|
|
||||||
#include "StringUtils.h"
|
|
||||||
|
|
||||||
FileIgnoreProxy::FileIgnoreProxy(QString root, QObject* parent) : QSortFilterProxyModel(parent), root(root) {}
|
|
||||||
// NOTE: Sadly, we have to do sorting ourselves.
|
|
||||||
bool FileIgnoreProxy::lessThan(const QModelIndex& left, const QModelIndex& right) const
|
|
||||||
{
|
|
||||||
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
|
|
||||||
if (!fsm) {
|
|
||||||
return QSortFilterProxyModel::lessThan(left, right);
|
|
||||||
}
|
|
||||||
bool asc = sortOrder() == Qt::AscendingOrder ? true : false;
|
|
||||||
|
|
||||||
QFileInfo leftFileInfo = fsm->fileInfo(left);
|
|
||||||
QFileInfo rightFileInfo = fsm->fileInfo(right);
|
|
||||||
|
|
||||||
if (!leftFileInfo.isDir() && rightFileInfo.isDir()) {
|
|
||||||
return !asc;
|
|
||||||
}
|
|
||||||
if (leftFileInfo.isDir() && !rightFileInfo.isDir()) {
|
|
||||||
return asc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sort and proxy model breaks the original model...
|
|
||||||
if (sortColumn() == 0) {
|
|
||||||
return StringUtils::naturalCompare(leftFileInfo.fileName(), rightFileInfo.fileName(), Qt::CaseInsensitive) < 0;
|
|
||||||
}
|
|
||||||
if (sortColumn() == 1) {
|
|
||||||
auto leftSize = leftFileInfo.size();
|
|
||||||
auto rightSize = rightFileInfo.size();
|
|
||||||
if ((leftSize == rightSize) || (leftFileInfo.isDir() && rightFileInfo.isDir())) {
|
|
||||||
return StringUtils::naturalCompare(leftFileInfo.fileName(), rightFileInfo.fileName(), Qt::CaseInsensitive) < 0 ? asc : !asc;
|
|
||||||
}
|
|
||||||
return leftSize < rightSize;
|
|
||||||
}
|
|
||||||
return QSortFilterProxyModel::lessThan(left, right);
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::ItemFlags FileIgnoreProxy::flags(const QModelIndex& index) const
|
|
||||||
{
|
|
||||||
if (!index.isValid())
|
|
||||||
return Qt::NoItemFlags;
|
|
||||||
|
|
||||||
auto sourceIndex = mapToSource(index);
|
|
||||||
Qt::ItemFlags flags = sourceIndex.flags();
|
|
||||||
if (index.column() == 0) {
|
|
||||||
flags |= Qt::ItemIsUserCheckable;
|
|
||||||
if (sourceIndex.model()->hasChildren(sourceIndex)) {
|
|
||||||
flags |= Qt::ItemIsAutoTristate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant FileIgnoreProxy::data(const QModelIndex& index, int role) const
|
|
||||||
{
|
|
||||||
QModelIndex sourceIndex = mapToSource(index);
|
|
||||||
|
|
||||||
if (index.column() == 0 && role == Qt::CheckStateRole) {
|
|
||||||
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
|
|
||||||
auto blockedPath = relPath(fsm->filePath(sourceIndex));
|
|
||||||
auto cover = blocked.cover(blockedPath);
|
|
||||||
if (!cover.isNull()) {
|
|
||||||
return QVariant(Qt::Unchecked);
|
|
||||||
} else if (blocked.exists(blockedPath)) {
|
|
||||||
return QVariant(Qt::PartiallyChecked);
|
|
||||||
} else {
|
|
||||||
return QVariant(Qt::Checked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sourceIndex.data(role);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileIgnoreProxy::setData(const QModelIndex& index, const QVariant& value, int role)
|
|
||||||
{
|
|
||||||
if (index.column() == 0 && role == Qt::CheckStateRole) {
|
|
||||||
Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt());
|
|
||||||
return setFilterState(index, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex sourceIndex = mapToSource(index);
|
|
||||||
return QSortFilterProxyModel::sourceModel()->setData(sourceIndex, value, role);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString FileIgnoreProxy::relPath(const QString& path) const
|
|
||||||
{
|
|
||||||
return QDir(root).relativeFilePath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileIgnoreProxy::setFilterState(QModelIndex index, Qt::CheckState state)
|
|
||||||
{
|
|
||||||
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
|
|
||||||
|
|
||||||
if (!fsm) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex sourceIndex = mapToSource(index);
|
|
||||||
auto blockedPath = relPath(fsm->filePath(sourceIndex));
|
|
||||||
bool changed = false;
|
|
||||||
if (state == Qt::Unchecked) {
|
|
||||||
// blocking a path
|
|
||||||
auto& node = blocked.insert(blockedPath);
|
|
||||||
// get rid of all blocked nodes below
|
|
||||||
node.clear();
|
|
||||||
changed = true;
|
|
||||||
} else if (state == Qt::Checked || state == Qt::PartiallyChecked) {
|
|
||||||
if (!blocked.remove(blockedPath)) {
|
|
||||||
auto cover = blocked.cover(blockedPath);
|
|
||||||
qDebug() << "Blocked by cover" << cover;
|
|
||||||
// uncover
|
|
||||||
blocked.remove(cover);
|
|
||||||
// block all contents, except for any cover
|
|
||||||
QModelIndex rootIndex = fsm->index(FS::PathCombine(root, cover));
|
|
||||||
QModelIndex doing = rootIndex;
|
|
||||||
int row = 0;
|
|
||||||
QStack<QModelIndex> todo;
|
|
||||||
while (1) {
|
|
||||||
auto node = fsm->index(row, 0, doing);
|
|
||||||
if (!node.isValid()) {
|
|
||||||
if (!todo.size()) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
doing = todo.pop();
|
|
||||||
row = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto relpath = relPath(fsm->filePath(node));
|
|
||||||
if (blockedPath.startsWith(relpath)) // cover found?
|
|
||||||
{
|
|
||||||
// continue processing cover later
|
|
||||||
todo.push(node);
|
|
||||||
} else {
|
|
||||||
// or just block this one.
|
|
||||||
blocked.insert(relpath);
|
|
||||||
}
|
|
||||||
row++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if (changed) {
|
|
||||||
// update the thing
|
|
||||||
emit dataChanged(index, index, { Qt::CheckStateRole });
|
|
||||||
// update everything above index
|
|
||||||
QModelIndex up = index.parent();
|
|
||||||
while (1) {
|
|
||||||
if (!up.isValid())
|
|
||||||
break;
|
|
||||||
emit dataChanged(up, up, { Qt::CheckStateRole });
|
|
||||||
up = up.parent();
|
|
||||||
}
|
|
||||||
// and everything below the index
|
|
||||||
QModelIndex doing = index;
|
|
||||||
int row = 0;
|
|
||||||
QStack<QModelIndex> todo;
|
|
||||||
while (1) {
|
|
||||||
auto node = this->index(row, 0, doing);
|
|
||||||
if (!node.isValid()) {
|
|
||||||
if (!todo.size()) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
doing = todo.pop();
|
|
||||||
row = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
emit dataChanged(node, node, { Qt::CheckStateRole });
|
|
||||||
todo.push(node);
|
|
||||||
row++;
|
|
||||||
}
|
|
||||||
// siblings and unrelated nodes are ignored
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileIgnoreProxy::shouldExpand(QModelIndex index)
|
|
||||||
{
|
|
||||||
QModelIndex sourceIndex = mapToSource(index);
|
|
||||||
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
|
|
||||||
if (!fsm) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto blockedPath = relPath(fsm->filePath(sourceIndex));
|
|
||||||
auto found = blocked.find(blockedPath);
|
|
||||||
if (found) {
|
|
||||||
return !found->leaf();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileIgnoreProxy::setBlockedPaths(QStringList paths)
|
|
||||||
{
|
|
||||||
beginResetModel();
|
|
||||||
blocked.clear();
|
|
||||||
blocked.insert(paths);
|
|
||||||
endResetModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileIgnoreProxy::filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(source_parent)
|
|
||||||
|
|
||||||
// adjust the columns you want to filter out here
|
|
||||||
// return false for those that will be hidden
|
|
||||||
if (source_column == 2 || source_column == 3)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileIgnoreProxy::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
|
|
||||||
{
|
|
||||||
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
|
||||||
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
|
|
||||||
|
|
||||||
auto fileInfo = fsm->fileInfo(index);
|
|
||||||
return !ignoreFile(fileInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const
|
|
||||||
{
|
|
||||||
return m_ignoreFiles.contains(fileInfo.fileName()) || m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath()));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileIgnoreProxy::filterFile(const QString& fileName) const
|
|
||||||
{
|
|
||||||
return blocked.covers(fileName) || ignoreFile(QFileInfo(QDir(root), fileName));
|
|
||||||
}
|
|
@ -1,85 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
|
||||||
/*
|
|
||||||
* Prism Launcher - Minecraft Launcher
|
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
|
||||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* This file incorporates work covered by the following copyright and
|
|
||||||
* permission notice:
|
|
||||||
*
|
|
||||||
* Copyright 2013-2021 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QSortFilterProxyModel>
|
|
||||||
#include "SeparatorPrefixTree.h"
|
|
||||||
|
|
||||||
class FileIgnoreProxy : public QSortFilterProxyModel {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
FileIgnoreProxy(QString root, QObject* parent);
|
|
||||||
// NOTE: Sadly, we have to do sorting ourselves.
|
|
||||||
bool lessThan(const QModelIndex& left, const QModelIndex& right) const;
|
|
||||||
|
|
||||||
virtual Qt::ItemFlags flags(const QModelIndex& index) const;
|
|
||||||
|
|
||||||
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
|
||||||
virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
|
|
||||||
|
|
||||||
QString relPath(const QString& path) const;
|
|
||||||
|
|
||||||
bool setFilterState(QModelIndex index, Qt::CheckState state);
|
|
||||||
|
|
||||||
bool shouldExpand(QModelIndex index);
|
|
||||||
|
|
||||||
void setBlockedPaths(QStringList paths);
|
|
||||||
|
|
||||||
inline const SeparatorPrefixTree<'/'>& blockedPaths() const { return blocked; }
|
|
||||||
inline SeparatorPrefixTree<'/'>& blockedPaths() { return blocked; }
|
|
||||||
|
|
||||||
// list of file names that need to be removed completely from model
|
|
||||||
inline QStringList& ignoreFilesWithName() { return m_ignoreFiles; }
|
|
||||||
// list of relative paths that need to be removed completely from model
|
|
||||||
inline SeparatorPrefixTree<'/'>& ignoreFilesWithPath() { return m_ignoreFilePaths; }
|
|
||||||
|
|
||||||
bool filterFile(const QString& fileName) const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const;
|
|
||||||
bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
|
|
||||||
|
|
||||||
bool ignoreFile(QFileInfo file) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const QString root;
|
|
||||||
SeparatorPrefixTree<'/'> blocked;
|
|
||||||
QStringList m_ignoreFiles;
|
|
||||||
SeparatorPrefixTree<'/'> m_ignoreFilePaths;
|
|
||||||
};
|
|
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* PolyMC - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
|
||||||
* Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -40,46 +38,32 @@
|
|||||||
#include "Exception.h"
|
#include "Exception.h"
|
||||||
#include "pathmatcher/IPathMatcher.h"
|
#include "pathmatcher/IPathMatcher.h"
|
||||||
|
|
||||||
#include <system_error>
|
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFlags>
|
#include <QFlags>
|
||||||
#include <QLocalServer>
|
|
||||||
#include <QObject>
|
|
||||||
#include <QPair>
|
|
||||||
#include <QThread>
|
|
||||||
|
|
||||||
namespace FS {
|
namespace FS
|
||||||
|
{
|
||||||
|
|
||||||
class FileSystemException : public ::Exception {
|
class FileSystemException : public ::Exception
|
||||||
public:
|
{
|
||||||
FileSystemException(const QString& message) : Exception(message) {}
|
public:
|
||||||
|
FileSystemException(const QString &message) : Exception(message) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* write data to a file safely
|
* write data to a file safely
|
||||||
*/
|
*/
|
||||||
void write(const QString& filename, const QByteArray& data);
|
void write(const QString &filename, const QByteArray &data);
|
||||||
|
|
||||||
/**
|
|
||||||
* append data to a file safely
|
|
||||||
*/
|
|
||||||
void appendSafe(const QString& filename, const QByteArray& data);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* append data to a file
|
|
||||||
*/
|
|
||||||
void append(const QString& filename, const QByteArray& data);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* read data from a file safely\
|
* read data from a file safely\
|
||||||
*/
|
*/
|
||||||
QByteArray read(const QString& filename);
|
QByteArray read(const QString &filename);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the last changed timestamp of an existing file
|
* Update the last changed timestamp of an existing file
|
||||||
*/
|
*/
|
||||||
bool updateTimestamp(const QString& filename);
|
bool updateTimestamp(const QString & filename);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates all the folders in a path for the specified path
|
* Creates all the folders in a path for the specified path
|
||||||
@ -93,224 +77,49 @@ bool ensureFilePathExists(QString filenamepath);
|
|||||||
*/
|
*/
|
||||||
bool ensureFolderPathExists(QString filenamepath);
|
bool ensureFolderPathExists(QString filenamepath);
|
||||||
|
|
||||||
/**
|
class copy
|
||||||
* @brief Copies a directory and it's contents from src to dest
|
{
|
||||||
*/
|
public:
|
||||||
class copy : public QObject {
|
copy(const QString & src, const QString & dst)
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
copy(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent)
|
|
||||||
{
|
{
|
||||||
m_src.setPath(src);
|
m_src.setPath(src);
|
||||||
m_dst.setPath(dst);
|
m_dst.setPath(dst);
|
||||||
}
|
}
|
||||||
copy& followSymlinks(const bool follow)
|
copy & followSymlinks(const bool follow)
|
||||||
{
|
{
|
||||||
m_followSymlinks = follow;
|
m_followSymlinks = follow;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
copy& matcher(const IPathMatcher* filter)
|
copy & blacklist(const IPathMatcher * filter)
|
||||||
{
|
{
|
||||||
m_matcher = filter;
|
m_blacklist = filter;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
copy& whitelist(bool whitelist)
|
bool operator()()
|
||||||
{
|
{
|
||||||
m_whitelist = whitelist;
|
return operator()(QString());
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
copy& overwrite(const bool overwrite)
|
|
||||||
{
|
|
||||||
m_overwrite = overwrite;
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
private:
|
||||||
|
bool operator()(const QString &offset);
|
||||||
|
|
||||||
qsizetype totalCopied() { return m_copied; }
|
private:
|
||||||
qsizetype totalFailed() { return m_failedPaths.length(); }
|
|
||||||
QStringList failed() { return m_failedPaths; }
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void fileCopied(const QString& relativeName);
|
|
||||||
void copyFailed(const QString& relativeName);
|
|
||||||
// TODO: maybe add a "shouldCopy" signal in the future?
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool operator()(const QString& offset, bool dryRun = false);
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool m_followSymlinks = true;
|
bool m_followSymlinks = true;
|
||||||
const IPathMatcher* m_matcher = nullptr;
|
const IPathMatcher * m_blacklist = nullptr;
|
||||||
bool m_whitelist = false;
|
|
||||||
bool m_overwrite = false;
|
|
||||||
QDir m_src;
|
QDir m_src;
|
||||||
QDir m_dst;
|
QDir m_dst;
|
||||||
qsizetype m_copied;
|
|
||||||
QStringList m_failedPaths;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LinkPair {
|
|
||||||
QString src;
|
|
||||||
QString dst;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LinkResult {
|
|
||||||
QString src;
|
|
||||||
QString dst;
|
|
||||||
QString err_msg;
|
|
||||||
int err_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ExternalLinkFileProcess : public QThread {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
ExternalLinkFileProcess(QString server, bool useHardLinks, QObject* parent = nullptr)
|
|
||||||
: QThread(parent), m_useHardLinks(useHardLinks), m_server(server)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void run() override
|
|
||||||
{
|
|
||||||
runLinkFile();
|
|
||||||
emit processExited();
|
|
||||||
}
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void processExited();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void runLinkFile();
|
|
||||||
|
|
||||||
bool m_useHardLinks = false;
|
|
||||||
|
|
||||||
QString m_server;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief links (a file / a directory and it's contents) from src to dest
|
|
||||||
*/
|
|
||||||
class create_link : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
create_link(const QList<LinkPair> path_pairs, QObject* parent = nullptr) : QObject(parent) { m_path_pairs.append(path_pairs); }
|
|
||||||
create_link(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent)
|
|
||||||
{
|
|
||||||
LinkPair pair = { src, dst };
|
|
||||||
m_path_pairs.append(pair);
|
|
||||||
}
|
|
||||||
create_link& useHardLinks(const bool useHard)
|
|
||||||
{
|
|
||||||
m_useHardLinks = useHard;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
create_link& matcher(const IPathMatcher* filter)
|
|
||||||
{
|
|
||||||
m_matcher = filter;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
create_link& whitelist(bool whitelist)
|
|
||||||
{
|
|
||||||
m_whitelist = whitelist;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
create_link& linkRecursively(bool recursive)
|
|
||||||
{
|
|
||||||
m_recursive = recursive;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
create_link& setMaxDepth(int depth)
|
|
||||||
{
|
|
||||||
m_max_depth = depth;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
create_link& debug(bool d)
|
|
||||||
{
|
|
||||||
m_debug = d;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::error_code getOSError() { return m_os_err; }
|
|
||||||
|
|
||||||
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
|
||||||
|
|
||||||
int totalLinked() { return m_linked; }
|
|
||||||
|
|
||||||
void runPrivileged() { runPrivileged(QString()); }
|
|
||||||
void runPrivileged(const QString& offset);
|
|
||||||
|
|
||||||
QList<LinkResult> getResults() { return m_path_results; }
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void fileLinked(const QString& srcName, const QString& dstName);
|
|
||||||
void linkFailed(const QString& srcName, const QString& dstName, const QString& err_msg, int err_value);
|
|
||||||
void finished();
|
|
||||||
void finishedPrivileged(bool gotResults);
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool operator()(const QString& offset, bool dryRun = false);
|
|
||||||
void make_link_list(const QString& offset);
|
|
||||||
bool make_links();
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool m_useHardLinks = false;
|
|
||||||
const IPathMatcher* m_matcher = nullptr;
|
|
||||||
bool m_whitelist = false;
|
|
||||||
bool m_recursive = true;
|
|
||||||
|
|
||||||
/// @brief >= -1 = infinite, 0 = link files at src/* to dest/*, 1 = link files at src/*/* to dest/*/*, etc.
|
|
||||||
int m_max_depth = -1;
|
|
||||||
|
|
||||||
QList<LinkPair> m_path_pairs;
|
|
||||||
QList<LinkResult> m_path_results;
|
|
||||||
QList<LinkPair> m_links_to_make;
|
|
||||||
|
|
||||||
int m_linked;
|
|
||||||
bool m_debug = false;
|
|
||||||
std::error_code m_os_err;
|
|
||||||
|
|
||||||
QLocalServer m_linkServer;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief moves a file by renaming it
|
|
||||||
* @param source source file path
|
|
||||||
* @param dest destination filepath
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
bool move(const QString& source, const QString& dest);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a folder recursively
|
* Delete a folder recursively
|
||||||
*/
|
*/
|
||||||
bool deletePath(QString path);
|
bool deletePath(QString path);
|
||||||
|
|
||||||
/**
|
QString PathCombine(const QString &path1, const QString &path2);
|
||||||
* Trash a folder / file
|
QString PathCombine(const QString &path1, const QString &path2, const QString &path3);
|
||||||
*/
|
QString PathCombine(const QString &path1, const QString &path2, const QString &path3, const QString &path4);
|
||||||
bool trash(QString path, QString* pathInTrash = nullptr);
|
|
||||||
|
|
||||||
QString PathCombine(const QString& path1, const QString& path2);
|
QString AbsolutePath(QString path);
|
||||||
QString PathCombine(const QString& path1, const QString& path2, const QString& path3);
|
|
||||||
QString PathCombine(const QString& path1, const QString& path2, const QString& path3, const QString& path4);
|
|
||||||
|
|
||||||
QString AbsolutePath(const QString& path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief depth of path. "foo.txt" -> 0 , "bar/foo.txt" -> 1, /baz/bar/foo.txt -> 2, etc.
|
|
||||||
*
|
|
||||||
* @param path path to measure
|
|
||||||
* @return int number of components before base path
|
|
||||||
*/
|
|
||||||
int pathDepth(const QString& path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief cut off segments of path until it is a max of length depth
|
|
||||||
*
|
|
||||||
* @param path path to truncate
|
|
||||||
* @param depth max depth of new path
|
|
||||||
* @return QString truncated path
|
|
||||||
*/
|
|
||||||
QString pathTruncate(const QString& path, int depth);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve an executable
|
* Resolve an executable
|
||||||
@ -347,202 +156,4 @@ QString getDesktopDir();
|
|||||||
// Overrides one folder with the contents of another, preserving items exclusive to the first folder
|
// Overrides one folder with the contents of another, preserving items exclusive to the first folder
|
||||||
// Equivalent to doing QDir::rename, but allowing for overrides
|
// Equivalent to doing QDir::rename, but allowing for overrides
|
||||||
bool overrideFolder(QString overwritten_path, QString override_path);
|
bool overrideFolder(QString overwritten_path, QString override_path);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Creates a shortcut to the specified target file at the specified destination path.
|
|
||||||
*/
|
|
||||||
bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon);
|
|
||||||
|
|
||||||
enum class FilesystemType {
|
|
||||||
FAT,
|
|
||||||
NTFS,
|
|
||||||
REFS,
|
|
||||||
EXT,
|
|
||||||
EXT_2_OLD,
|
|
||||||
EXT_2_3_4,
|
|
||||||
XFS,
|
|
||||||
BTRFS,
|
|
||||||
NFS,
|
|
||||||
ZFS,
|
|
||||||
APFS,
|
|
||||||
HFS,
|
|
||||||
HFSPLUS,
|
|
||||||
HFSX,
|
|
||||||
FUSEBLK,
|
|
||||||
F2FS,
|
|
||||||
UNKNOWN
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Ordered Mapping of enum types to reported filesystem names
|
|
||||||
* this mapping is non exsaustive, it just attempts to capture the filesystems which could be reasonalbly be in use .
|
|
||||||
* all string values are in uppercase, use `QString.toUpper()` or equivalent during lookup.
|
|
||||||
*
|
|
||||||
* QMap is ordered
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static const QMap<FilesystemType, QStringList> s_filesystem_type_names = { { FilesystemType::FAT, { "FAT" } },
|
|
||||||
{ FilesystemType::NTFS, { "NTFS" } },
|
|
||||||
{ FilesystemType::REFS, { "REFS" } },
|
|
||||||
{ FilesystemType::EXT_2_OLD, { "EXT_2_OLD", "EXT2_OLD" } },
|
|
||||||
{ FilesystemType::EXT_2_3_4,
|
|
||||||
{ "EXT2/3/4", "EXT_2_3_4", "EXT2", "EXT3", "EXT4" } },
|
|
||||||
{ FilesystemType::EXT, { "EXT" } },
|
|
||||||
{ FilesystemType::XFS, { "XFS" } },
|
|
||||||
{ FilesystemType::BTRFS, { "BTRFS" } },
|
|
||||||
{ FilesystemType::NFS, { "NFS" } },
|
|
||||||
{ FilesystemType::ZFS, { "ZFS" } },
|
|
||||||
{ FilesystemType::APFS, { "APFS" } },
|
|
||||||
{ FilesystemType::HFS, { "HFS" } },
|
|
||||||
{ FilesystemType::HFSPLUS, { "HFSPLUS" } },
|
|
||||||
{ FilesystemType::HFSX, { "HFSX" } },
|
|
||||||
{ FilesystemType::FUSEBLK, { "FUSEBLK" } },
|
|
||||||
{ FilesystemType::F2FS, { "F2FS" } },
|
|
||||||
{ FilesystemType::UNKNOWN, { "UNKNOWN" } } };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the string name of Filesystem enum object
|
|
||||||
*
|
|
||||||
* @param type
|
|
||||||
* @return QString
|
|
||||||
*/
|
|
||||||
QString getFilesystemTypeName(FilesystemType type);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the Filesystem enum object from a name
|
|
||||||
* Does a lookup of the type name and returns an exact match
|
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* @return FilesystemType
|
|
||||||
*/
|
|
||||||
FilesystemType getFilesystemType(const QString& name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the Filesystem enum object from a name
|
|
||||||
* Does a fuzzy lookup of the type name and returns an apropreate match
|
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* @return FilesystemType
|
|
||||||
*/
|
|
||||||
FilesystemType getFilesystemTypeFuzzy(const QString& name);
|
|
||||||
|
|
||||||
struct FilesystemInfo {
|
|
||||||
FilesystemType fsType = FilesystemType::UNKNOWN;
|
|
||||||
QString fsTypeName;
|
|
||||||
int blockSize;
|
|
||||||
qint64 bytesAvailable;
|
|
||||||
qint64 bytesFree;
|
|
||||||
qint64 bytesTotal;
|
|
||||||
QString name;
|
|
||||||
QString rootPath;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief path to the near ancestor that exists
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
QString nearestExistentAncestor(const QString& path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief colect information about the filesystem under a file
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
FilesystemInfo statFS(const QString& path);
|
|
||||||
|
|
||||||
static const QList<FilesystemType> s_clone_filesystems = { FilesystemType::BTRFS, FilesystemType::APFS, FilesystemType::ZFS,
|
|
||||||
FilesystemType::XFS, FilesystemType::REFS };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief if the Filesystem is reflink/clone capable
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
bool canCloneOnFS(const QString& path);
|
|
||||||
bool canCloneOnFS(const FilesystemInfo& info);
|
|
||||||
bool canCloneOnFS(FilesystemType type);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief if the Filesystems are reflink/clone capable and both are on the same device
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
bool canClone(const QString& src, const QString& dst);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Copies a directory and it's contents from src to dest
|
|
||||||
*/
|
|
||||||
class clone : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
clone(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent)
|
|
||||||
{
|
|
||||||
m_src.setPath(src);
|
|
||||||
m_dst.setPath(dst);
|
|
||||||
}
|
|
||||||
clone& matcher(const IPathMatcher* filter)
|
|
||||||
{
|
|
||||||
m_matcher = filter;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
clone& whitelist(bool whitelist)
|
|
||||||
{
|
|
||||||
m_whitelist = whitelist;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
|
||||||
|
|
||||||
qsizetype totalCloned() { return m_cloned; }
|
|
||||||
qsizetype totalFailed() { return m_failedClones.length(); }
|
|
||||||
|
|
||||||
QList<QPair<QString, QString>> failed() { return m_failedClones; }
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void fileCloned(const QString& src, const QString& dst);
|
|
||||||
void cloneFailed(const QString& src, const QString& dst);
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool operator()(const QString& offset, bool dryRun = false);
|
|
||||||
|
|
||||||
private:
|
|
||||||
const IPathMatcher* m_matcher = nullptr;
|
|
||||||
bool m_whitelist = false;
|
|
||||||
QDir m_src;
|
|
||||||
QDir m_dst;
|
|
||||||
qsizetype m_cloned;
|
|
||||||
QList<QPair<QString, QString>> m_failedClones;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief clone/reflink file from src to dst
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
bool clone_file(const QString& src, const QString& dst, std::error_code& ec);
|
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
bool win_ioctl_clone(const std::wstring& src_path, const std::wstring& dst_path, std::error_code& ec);
|
|
||||||
#elif defined(Q_OS_LINUX)
|
|
||||||
bool linux_ficlone(const std::string& src_path, const std::string& dst_path, std::error_code& ec);
|
|
||||||
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
|
||||||
bool macos_bsd_clonefile(const std::string& src_path, const std::string& dst_path, std::error_code& ec);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const QList<FilesystemType> s_non_link_filesystems = {
|
|
||||||
FilesystemType::FAT,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief if the Filesystem is symlink capable
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
bool canLinkOnFS(const QString& path);
|
|
||||||
bool canLinkOnFS(const FilesystemInfo& info);
|
|
||||||
bool canLinkOnFS(FilesystemType type);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief if the Filesystem is symlink capable on both ends
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
bool canLink(const QString& src, const QString& dst);
|
|
||||||
|
|
||||||
uintmax_t hardLinkCount(const QString& path);
|
|
||||||
|
|
||||||
} // namespace FS
|
|
||||||
|
122
launcher/FileSystem_test.cpp
Normal file
122
launcher/FileSystem_test.cpp
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
#include <QTest>
|
||||||
|
#include <QTemporaryDir>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
#include "FileSystem.h"
|
||||||
|
|
||||||
|
class FileSystemTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
const QString bothSlash = "/foo/";
|
||||||
|
const QString trailingSlash = "foo/";
|
||||||
|
const QString leadingSlash = "/foo";
|
||||||
|
|
||||||
|
private
|
||||||
|
slots:
|
||||||
|
void test_pathCombine()
|
||||||
|
{
|
||||||
|
QCOMPARE(QString("/foo/foo"), FS::PathCombine(bothSlash, bothSlash));
|
||||||
|
QCOMPARE(QString("foo/foo"), FS::PathCombine(trailingSlash, trailingSlash));
|
||||||
|
QCOMPARE(QString("/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash));
|
||||||
|
|
||||||
|
QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(bothSlash, bothSlash, bothSlash));
|
||||||
|
QCOMPARE(QString("foo/foo/foo"), FS::PathCombine(trailingSlash, trailingSlash, trailingSlash));
|
||||||
|
QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash, leadingSlash));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_PathCombine1_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("result");
|
||||||
|
QTest::addColumn<QString>("path1");
|
||||||
|
QTest::addColumn<QString>("path2");
|
||||||
|
|
||||||
|
QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc/def" << "ghi/jkl";
|
||||||
|
QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/def/" << "ghi/jkl";
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
QTest::newRow("win native, from C:") << "C:/abc" << "C:" << "abc";
|
||||||
|
QTest::newRow("win native 1") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def" << "ghi\\jkl";
|
||||||
|
QTest::newRow("win native 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def\\" << "ghi\\jkl";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_PathCombine1()
|
||||||
|
{
|
||||||
|
QFETCH(QString, result);
|
||||||
|
QFETCH(QString, path1);
|
||||||
|
QFETCH(QString, path2);
|
||||||
|
|
||||||
|
QCOMPARE(FS::PathCombine(path1, path2), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_PathCombine2_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("result");
|
||||||
|
QTest::addColumn<QString>("path1");
|
||||||
|
QTest::addColumn<QString>("path2");
|
||||||
|
QTest::addColumn<QString>("path3");
|
||||||
|
|
||||||
|
QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc" << "def" << "ghi/jkl";
|
||||||
|
QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/" << "def" << "ghi/jkl";
|
||||||
|
QTest::newRow("qt 3") << "/abc/def/ghi/jkl" << "/abc" << "def/" << "ghi/jkl";
|
||||||
|
QTest::newRow("qt 4") << "/abc/def/ghi/jkl" << "/abc/" << "def/" << "ghi/jkl";
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
QTest::newRow("win 1") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def" << "ghi\\jkl";
|
||||||
|
QTest::newRow("win 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
|
||||||
|
QTest::newRow("win 3") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def\\" << "ghi\\jkl";
|
||||||
|
QTest::newRow("win 4") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_PathCombine2()
|
||||||
|
{
|
||||||
|
QFETCH(QString, result);
|
||||||
|
QFETCH(QString, path1);
|
||||||
|
QFETCH(QString, path2);
|
||||||
|
QFETCH(QString, path3);
|
||||||
|
|
||||||
|
QCOMPARE(FS::PathCombine(path1, path2, path3), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_copy()
|
||||||
|
{
|
||||||
|
QString folder = QFINDTESTDATA("testdata/test_folder");
|
||||||
|
auto f = [&folder]()
|
||||||
|
{
|
||||||
|
QTemporaryDir tempDir;
|
||||||
|
tempDir.setAutoRemove(true);
|
||||||
|
qDebug() << "From:" << folder << "To:" << tempDir.path();
|
||||||
|
|
||||||
|
QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
|
||||||
|
qDebug() << tempDir.path();
|
||||||
|
qDebug() << target_dir.path();
|
||||||
|
FS::copy c(folder, target_dir.path());
|
||||||
|
c();
|
||||||
|
|
||||||
|
for(auto entry: target_dir.entryList())
|
||||||
|
{
|
||||||
|
qDebug() << entry;
|
||||||
|
}
|
||||||
|
QVERIFY(target_dir.entryList().contains("pack.mcmeta"));
|
||||||
|
QVERIFY(target_dir.entryList().contains("assets"));
|
||||||
|
};
|
||||||
|
|
||||||
|
// first try variant without trailing /
|
||||||
|
QVERIFY(!folder.endsWith('/'));
|
||||||
|
f();
|
||||||
|
|
||||||
|
// then variant with trailing /
|
||||||
|
folder.append('/');
|
||||||
|
QVERIFY(folder.endsWith('/'));
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_getDesktop()
|
||||||
|
{
|
||||||
|
QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QTEST_GUILESS_MAIN(FileSystemTest)
|
||||||
|
|
||||||
|
#include "FileSystem_test.moc"
|
@ -1,33 +1,28 @@
|
|||||||
#include "Filter.h"
|
#include "Filter.h"
|
||||||
|
|
||||||
Filter::~Filter() {}
|
Filter::~Filter(){}
|
||||||
|
|
||||||
ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern) {}
|
ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern){}
|
||||||
ContainsFilter::~ContainsFilter() {}
|
ContainsFilter::~ContainsFilter(){}
|
||||||
bool ContainsFilter::accepts(const QString& value)
|
bool ContainsFilter::accepts(const QString& value)
|
||||||
{
|
{
|
||||||
return value.contains(pattern);
|
return value.contains(pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern) {}
|
ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern){}
|
||||||
ExactFilter::~ExactFilter() {}
|
ExactFilter::~ExactFilter(){}
|
||||||
bool ExactFilter::accepts(const QString& value)
|
bool ExactFilter::accepts(const QString& value)
|
||||||
{
|
{
|
||||||
return value == pattern;
|
return value == pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExactIfPresentFilter::ExactIfPresentFilter(const QString& pattern) : pattern(pattern) {}
|
RegexpFilter::RegexpFilter(const QString& regexp, bool invert)
|
||||||
bool ExactIfPresentFilter::accepts(const QString& value)
|
:invert(invert)
|
||||||
{
|
|
||||||
return value.isEmpty() || value == pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
RegexpFilter::RegexpFilter(const QString& regexp, bool invert) : invert(invert)
|
|
||||||
{
|
{
|
||||||
pattern.setPattern(regexp);
|
pattern.setPattern(regexp);
|
||||||
pattern.optimize();
|
pattern.optimize();
|
||||||
}
|
}
|
||||||
RegexpFilter::~RegexpFilter() {}
|
RegexpFilter::~RegexpFilter(){}
|
||||||
bool RegexpFilter::accepts(const QString& value)
|
bool RegexpFilter::accepts(const QString& value)
|
||||||
{
|
{
|
||||||
auto match = pattern.match(value);
|
auto match = pattern.match(value);
|
||||||
|
@ -1,51 +1,42 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QRegularExpression>
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
class Filter {
|
class Filter
|
||||||
public:
|
{
|
||||||
|
public:
|
||||||
virtual ~Filter();
|
virtual ~Filter();
|
||||||
virtual bool accepts(const QString& value) = 0;
|
virtual bool accepts(const QString & value) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ContainsFilter : public Filter {
|
class ContainsFilter: public Filter
|
||||||
public:
|
{
|
||||||
ContainsFilter(const QString& pattern);
|
public:
|
||||||
|
ContainsFilter(const QString &pattern);
|
||||||
virtual ~ContainsFilter();
|
virtual ~ContainsFilter();
|
||||||
bool accepts(const QString& value) override;
|
bool accepts(const QString & value) override;
|
||||||
|
private:
|
||||||
private:
|
|
||||||
QString pattern;
|
QString pattern;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ExactFilter : public Filter {
|
class ExactFilter: public Filter
|
||||||
public:
|
{
|
||||||
ExactFilter(const QString& pattern);
|
public:
|
||||||
|
ExactFilter(const QString &pattern);
|
||||||
virtual ~ExactFilter();
|
virtual ~ExactFilter();
|
||||||
bool accepts(const QString& value) override;
|
bool accepts(const QString & value) override;
|
||||||
|
private:
|
||||||
private:
|
|
||||||
QString pattern;
|
QString pattern;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ExactIfPresentFilter : public Filter {
|
class RegexpFilter: public Filter
|
||||||
public:
|
{
|
||||||
ExactIfPresentFilter(const QString& pattern);
|
public:
|
||||||
~ExactIfPresentFilter() override = default;
|
RegexpFilter(const QString ®exp, bool invert);
|
||||||
bool accepts(const QString& value) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString pattern;
|
|
||||||
};
|
|
||||||
|
|
||||||
class RegexpFilter : public Filter {
|
|
||||||
public:
|
|
||||||
RegexpFilter(const QString& regexp, bool invert);
|
|
||||||
virtual ~RegexpFilter();
|
virtual ~RegexpFilter();
|
||||||
bool accepts(const QString& value) override;
|
bool accepts(const QString & value) override;
|
||||||
|
private:
|
||||||
private:
|
|
||||||
QRegularExpression pattern;
|
QRegularExpression pattern;
|
||||||
bool invert = false;
|
bool invert = false;
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* PolyMC - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -37,9 +37,10 @@
|
|||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
|
||||||
bool GZip::unzip(const QByteArray& compressedBytes, QByteArray& uncompressedBytes)
|
bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes)
|
||||||
{
|
{
|
||||||
if (compressedBytes.size() == 0) {
|
if (compressedBytes.size() == 0)
|
||||||
|
{
|
||||||
uncompressedBytes = compressedBytes;
|
uncompressedBytes = compressedBytes;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -50,37 +51,42 @@ bool GZip::unzip(const QByteArray& compressedBytes, QByteArray& uncompressedByte
|
|||||||
|
|
||||||
z_stream strm;
|
z_stream strm;
|
||||||
memset(&strm, 0, sizeof(strm));
|
memset(&strm, 0, sizeof(strm));
|
||||||
strm.next_in = (Bytef*)compressedBytes.data();
|
strm.next_in = (Bytef *)compressedBytes.data();
|
||||||
strm.avail_in = compressedBytes.size();
|
strm.avail_in = compressedBytes.size();
|
||||||
|
|
||||||
bool done = false;
|
bool done = false;
|
||||||
|
|
||||||
if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) {
|
if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int err = Z_OK;
|
int err = Z_OK;
|
||||||
|
|
||||||
while (!done) {
|
while (!done)
|
||||||
|
{
|
||||||
// If our output buffer is too small
|
// If our output buffer is too small
|
||||||
if (strm.total_out >= uncompLength) {
|
if (strm.total_out >= uncompLength)
|
||||||
|
{
|
||||||
uncompressedBytes.resize(uncompLength * 2);
|
uncompressedBytes.resize(uncompLength * 2);
|
||||||
uncompLength *= 2;
|
uncompLength *= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
strm.next_out = reinterpret_cast<Bytef*>((uncompressedBytes.data() + strm.total_out));
|
strm.next_out = (Bytef *)(uncompressedBytes.data() + strm.total_out);
|
||||||
strm.avail_out = uncompLength - strm.total_out;
|
strm.avail_out = uncompLength - strm.total_out;
|
||||||
|
|
||||||
// Inflate another chunk.
|
// Inflate another chunk.
|
||||||
err = inflate(&strm, Z_SYNC_FLUSH);
|
err = inflate(&strm, Z_SYNC_FLUSH);
|
||||||
if (err == Z_STREAM_END)
|
if (err == Z_STREAM_END)
|
||||||
done = true;
|
done = true;
|
||||||
else if (err != Z_OK) {
|
else if (err != Z_OK)
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inflateEnd(&strm) != Z_OK || !done) {
|
if (inflateEnd(&strm) != Z_OK || !done)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,9 +94,10 @@ bool GZip::unzip(const QByteArray& compressedBytes, QByteArray& uncompressedByte
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GZip::zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes)
|
bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
|
||||||
{
|
{
|
||||||
if (uncompressedBytes.size() == 0) {
|
if (uncompressedBytes.size() == 0)
|
||||||
|
{
|
||||||
compressedBytes = uncompressedBytes;
|
compressedBytes = uncompressedBytes;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -102,7 +109,8 @@ bool GZip::zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes)
|
|||||||
z_stream zs;
|
z_stream zs;
|
||||||
memset(&zs, 0, sizeof(zs));
|
memset(&zs, 0, sizeof(zs));
|
||||||
|
|
||||||
if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (16 + MAX_WBITS), 8, Z_DEFAULT_STRATEGY) != Z_OK) {
|
if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (16 + MAX_WBITS), 8, Z_DEFAULT_STRATEGY) != Z_OK)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,12 +122,14 @@ bool GZip::zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes)
|
|||||||
|
|
||||||
unsigned offset = 0;
|
unsigned offset = 0;
|
||||||
unsigned temp = 0;
|
unsigned temp = 0;
|
||||||
do {
|
do
|
||||||
|
{
|
||||||
auto remaining = compressedBytes.size() - offset;
|
auto remaining = compressedBytes.size() - offset;
|
||||||
if (remaining < 1) {
|
if(remaining < 1)
|
||||||
|
{
|
||||||
compressedBytes.resize(compressedBytes.size() * 2);
|
compressedBytes.resize(compressedBytes.size() * 2);
|
||||||
}
|
}
|
||||||
zs.next_out = reinterpret_cast<Bytef*>((compressedBytes.data() + offset));
|
zs.next_out = (Bytef *) (compressedBytes.data() + offset);
|
||||||
temp = zs.avail_out = compressedBytes.size() - offset;
|
temp = zs.avail_out = compressedBytes.size() - offset;
|
||||||
ret = deflate(&zs, Z_FINISH);
|
ret = deflate(&zs, Z_FINISH);
|
||||||
offset += temp - zs.avail_out;
|
offset += temp - zs.avail_out;
|
||||||
@ -127,11 +137,13 @@ bool GZip::zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes)
|
|||||||
|
|
||||||
compressedBytes.resize(offset);
|
compressedBytes.resize(offset);
|
||||||
|
|
||||||
if (deflateEnd(&zs) != Z_OK) {
|
if (deflateEnd(&zs) != Z_OK)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret != Z_STREAM_END) {
|
if (ret != Z_STREAM_END)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
|
||||||
class GZip {
|
class GZip
|
||||||
public:
|
{
|
||||||
static bool unzip(const QByteArray& compressedBytes, QByteArray& uncompressedBytes);
|
public:
|
||||||
static bool zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes);
|
static bool unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes);
|
||||||
|
static bool zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
#include <QTest>
|
#include <QTest>
|
||||||
|
|
||||||
#include <GZip.h>
|
#include "GZip.h"
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
void fib(int& prev, int& cur)
|
void fib(int &prev, int &cur)
|
||||||
{
|
{
|
||||||
auto ret = prev + cur;
|
auto ret = prev + cur;
|
||||||
prev = cur;
|
prev = cur;
|
||||||
cur = ret;
|
cur = ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
class GZipTest : public QObject {
|
class GZipTest : public QObject
|
||||||
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
private slots:
|
private
|
||||||
|
slots:
|
||||||
|
|
||||||
void test_Through()
|
void test_Through()
|
||||||
{
|
{
|
||||||
@ -22,11 +24,12 @@ class GZipTest : public QObject {
|
|||||||
QByteArray compressed;
|
QByteArray compressed;
|
||||||
QByteArray decompressed;
|
QByteArray decompressed;
|
||||||
std::default_random_engine eng((std::random_device())());
|
std::default_random_engine eng((std::random_device())());
|
||||||
std::uniform_int_distribution<uint16_t> idis(0, std::numeric_limits<uint8_t>::max());
|
std::uniform_int_distribution<uint8_t> idis(0, std::numeric_limits<uint8_t>::max());
|
||||||
|
|
||||||
// initialize random buffer
|
// initialize random buffer
|
||||||
for (int i = 0; i < size; i++) {
|
for(int i = 0; i < size; i++)
|
||||||
random.append(static_cast<char>(idis(eng)));
|
{
|
||||||
|
random.append((char)idis(eng));
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize fibonacci
|
// initialize fibonacci
|
||||||
@ -34,7 +37,8 @@ class GZipTest : public QObject {
|
|||||||
int cur = 1;
|
int cur = 1;
|
||||||
|
|
||||||
// test if fibonacci long random buffers pass through GZip
|
// test if fibonacci long random buffers pass through GZip
|
||||||
do {
|
do
|
||||||
|
{
|
||||||
QByteArray copy = random;
|
QByteArray copy = random;
|
||||||
copy.resize(cur);
|
copy.resize(cur);
|
||||||
compressed.clear();
|
compressed.clear();
|
76
launcher/HoeDown.h
Normal file
76
launcher/HoeDown.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/* 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 <hoedown/html.h>
|
||||||
|
#include <hoedown/document.h>
|
||||||
|
#include <QString>
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hoedown wrapper, because dealing with resource lifetime in C is stupid
|
||||||
|
*/
|
||||||
|
class HoeDown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class buffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
buffer(size_t unit = 4096)
|
||||||
|
{
|
||||||
|
buf = hoedown_buffer_new(unit);
|
||||||
|
}
|
||||||
|
~buffer()
|
||||||
|
{
|
||||||
|
hoedown_buffer_free(buf);
|
||||||
|
}
|
||||||
|
const char * cstr()
|
||||||
|
{
|
||||||
|
return hoedown_buffer_cstr(buf);
|
||||||
|
}
|
||||||
|
void put(QByteArray input)
|
||||||
|
{
|
||||||
|
hoedown_buffer_put(buf, (uint8_t *) input.data(), input.size());
|
||||||
|
}
|
||||||
|
const uint8_t * data() const
|
||||||
|
{
|
||||||
|
return buf->data;
|
||||||
|
}
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
return buf->size;
|
||||||
|
}
|
||||||
|
hoedown_buffer * buf;
|
||||||
|
} ib, ob;
|
||||||
|
HoeDown()
|
||||||
|
{
|
||||||
|
renderer = hoedown_html_renderer_new((hoedown_html_flags) 0,0);
|
||||||
|
document = hoedown_document_new(renderer, (hoedown_extensions) 0, 8);
|
||||||
|
}
|
||||||
|
~HoeDown()
|
||||||
|
{
|
||||||
|
hoedown_document_free(document);
|
||||||
|
hoedown_html_renderer_free(renderer);
|
||||||
|
}
|
||||||
|
QString process(QByteArray input)
|
||||||
|
{
|
||||||
|
ib.put(input);
|
||||||
|
hoedown_document_render(document, ob.buf, ib.data(), ib.size());
|
||||||
|
return ob.cstr();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
hoedown_document * document;
|
||||||
|
hoedown_renderer * renderer;
|
||||||
|
};
|
@ -1,192 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by marcelohdez on 10/22/22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "InstanceCopyPrefs.h"
|
|
||||||
|
|
||||||
bool InstanceCopyPrefs::allTrue() const
|
|
||||||
{
|
|
||||||
return copySaves && keepPlaytime && copyGameOptions && copyResourcePacks && copyShaderPacks && copyServers && copyMods &&
|
|
||||||
copyScreenshots;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a single RegEx string of the selected folders/files to filter out (ex: ".minecraft/saves|.minecraft/server.dat")
|
|
||||||
QString InstanceCopyPrefs::getSelectedFiltersAsRegex() const
|
|
||||||
{
|
|
||||||
return getSelectedFiltersAsRegex({});
|
|
||||||
}
|
|
||||||
QString InstanceCopyPrefs::getSelectedFiltersAsRegex(const QStringList& additionalFilters) const
|
|
||||||
{
|
|
||||||
QStringList filters;
|
|
||||||
|
|
||||||
if (!copySaves)
|
|
||||||
filters << "saves";
|
|
||||||
|
|
||||||
if (!copyGameOptions)
|
|
||||||
filters << "options.txt";
|
|
||||||
|
|
||||||
if (!copyResourcePacks)
|
|
||||||
filters << "resourcepacks"
|
|
||||||
<< "texturepacks";
|
|
||||||
|
|
||||||
if (!copyShaderPacks)
|
|
||||||
filters << "shaderpacks";
|
|
||||||
|
|
||||||
if (!copyServers)
|
|
||||||
filters << "servers.dat"
|
|
||||||
<< "servers.dat_old"
|
|
||||||
<< "server-resource-packs";
|
|
||||||
|
|
||||||
if (!copyMods)
|
|
||||||
filters << "coremods"
|
|
||||||
<< "mods"
|
|
||||||
<< "config";
|
|
||||||
|
|
||||||
if (!copyScreenshots)
|
|
||||||
filters << "screenshots";
|
|
||||||
|
|
||||||
for (auto filter : additionalFilters) {
|
|
||||||
filters << filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have any filters to add, join them as a single regex string to return:
|
|
||||||
if (!filters.isEmpty()) {
|
|
||||||
const QString MC_ROOT = "[.]?minecraft/";
|
|
||||||
// Ensure first filter starts with root, then join other filters with OR regex before root (ex: ".minecraft/saves|.minecraft/mods"):
|
|
||||||
return MC_ROOT + filters.join("|" + MC_ROOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ======= Getters =======
|
|
||||||
bool InstanceCopyPrefs::isCopySavesEnabled() const
|
|
||||||
{
|
|
||||||
return copySaves;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InstanceCopyPrefs::isKeepPlaytimeEnabled() const
|
|
||||||
{
|
|
||||||
return keepPlaytime;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InstanceCopyPrefs::isCopyGameOptionsEnabled() const
|
|
||||||
{
|
|
||||||
return copyGameOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InstanceCopyPrefs::isCopyResourcePacksEnabled() const
|
|
||||||
{
|
|
||||||
return copyResourcePacks;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InstanceCopyPrefs::isCopyShaderPacksEnabled() const
|
|
||||||
{
|
|
||||||
return copyShaderPacks;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InstanceCopyPrefs::isCopyServersEnabled() const
|
|
||||||
{
|
|
||||||
return copyServers;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InstanceCopyPrefs::isCopyModsEnabled() const
|
|
||||||
{
|
|
||||||
return copyMods;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InstanceCopyPrefs::isCopyScreenshotsEnabled() const
|
|
||||||
{
|
|
||||||
return copyScreenshots;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InstanceCopyPrefs::isUseSymLinksEnabled() const
|
|
||||||
{
|
|
||||||
return useSymLinks;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InstanceCopyPrefs::isUseHardLinksEnabled() const
|
|
||||||
{
|
|
||||||
return useHardLinks;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InstanceCopyPrefs::isLinkRecursivelyEnabled() const
|
|
||||||
{
|
|
||||||
return linkRecursively;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InstanceCopyPrefs::isDontLinkSavesEnabled() const
|
|
||||||
{
|
|
||||||
return dontLinkSaves;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InstanceCopyPrefs::isUseCloneEnabled() const
|
|
||||||
{
|
|
||||||
return useClone;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ======= Setters =======
|
|
||||||
void InstanceCopyPrefs::enableCopySaves(bool b)
|
|
||||||
{
|
|
||||||
copySaves = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceCopyPrefs::enableKeepPlaytime(bool b)
|
|
||||||
{
|
|
||||||
keepPlaytime = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceCopyPrefs::enableCopyGameOptions(bool b)
|
|
||||||
{
|
|
||||||
copyGameOptions = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceCopyPrefs::enableCopyResourcePacks(bool b)
|
|
||||||
{
|
|
||||||
copyResourcePacks = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceCopyPrefs::enableCopyShaderPacks(bool b)
|
|
||||||
{
|
|
||||||
copyShaderPacks = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceCopyPrefs::enableCopyServers(bool b)
|
|
||||||
{
|
|
||||||
copyServers = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceCopyPrefs::enableCopyMods(bool b)
|
|
||||||
{
|
|
||||||
copyMods = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceCopyPrefs::enableCopyScreenshots(bool b)
|
|
||||||
{
|
|
||||||
copyScreenshots = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceCopyPrefs::enableUseSymLinks(bool b)
|
|
||||||
{
|
|
||||||
useSymLinks = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceCopyPrefs::enableLinkRecursively(bool b)
|
|
||||||
{
|
|
||||||
linkRecursively = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceCopyPrefs::enableUseHardLinks(bool b)
|
|
||||||
{
|
|
||||||
useHardLinks = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceCopyPrefs::enableDontLinkSaves(bool b)
|
|
||||||
{
|
|
||||||
dontLinkSaves = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceCopyPrefs::enableUseClone(bool b)
|
|
||||||
{
|
|
||||||
useClone = b;
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by marcelohdez on 10/22/22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QStringList>
|
|
||||||
|
|
||||||
struct InstanceCopyPrefs {
|
|
||||||
public:
|
|
||||||
[[nodiscard]] bool allTrue() const;
|
|
||||||
[[nodiscard]] QString getSelectedFiltersAsRegex() const;
|
|
||||||
[[nodiscard]] QString getSelectedFiltersAsRegex(const QStringList& additionalFilters) const;
|
|
||||||
// Getters
|
|
||||||
[[nodiscard]] bool isCopySavesEnabled() const;
|
|
||||||
[[nodiscard]] bool isKeepPlaytimeEnabled() const;
|
|
||||||
[[nodiscard]] bool isCopyGameOptionsEnabled() const;
|
|
||||||
[[nodiscard]] bool isCopyResourcePacksEnabled() const;
|
|
||||||
[[nodiscard]] bool isCopyShaderPacksEnabled() const;
|
|
||||||
[[nodiscard]] bool isCopyServersEnabled() const;
|
|
||||||
[[nodiscard]] bool isCopyModsEnabled() const;
|
|
||||||
[[nodiscard]] bool isCopyScreenshotsEnabled() const;
|
|
||||||
[[nodiscard]] bool isUseSymLinksEnabled() const;
|
|
||||||
[[nodiscard]] bool isLinkRecursivelyEnabled() const;
|
|
||||||
[[nodiscard]] bool isUseHardLinksEnabled() const;
|
|
||||||
[[nodiscard]] bool isDontLinkSavesEnabled() const;
|
|
||||||
[[nodiscard]] bool isUseCloneEnabled() const;
|
|
||||||
// Setters
|
|
||||||
void enableCopySaves(bool b);
|
|
||||||
void enableKeepPlaytime(bool b);
|
|
||||||
void enableCopyGameOptions(bool b);
|
|
||||||
void enableCopyResourcePacks(bool b);
|
|
||||||
void enableCopyShaderPacks(bool b);
|
|
||||||
void enableCopyServers(bool b);
|
|
||||||
void enableCopyMods(bool b);
|
|
||||||
void enableCopyScreenshots(bool b);
|
|
||||||
void enableUseSymLinks(bool b);
|
|
||||||
void enableLinkRecursively(bool b);
|
|
||||||
void enableUseHardLinks(bool b);
|
|
||||||
void enableDontLinkSaves(bool b);
|
|
||||||
void enableUseClone(bool b);
|
|
||||||
|
|
||||||
protected: // data
|
|
||||||
bool copySaves = true;
|
|
||||||
bool keepPlaytime = true;
|
|
||||||
bool copyGameOptions = true;
|
|
||||||
bool copyResourcePacks = true;
|
|
||||||
bool copyShaderPacks = true;
|
|
||||||
bool copyServers = true;
|
|
||||||
bool copyMods = true;
|
|
||||||
bool copyScreenshots = true;
|
|
||||||
bool useSymLinks = false;
|
|
||||||
bool linkRecursively = false;
|
|
||||||
bool useHardLinks = false;
|
|
||||||
bool dontLinkSaves = false;
|
|
||||||
bool useClone = false;
|
|
||||||
};
|
|
@ -1,34 +1,19 @@
|
|||||||
#include "InstanceCopyTask.h"
|
#include "InstanceCopyTask.h"
|
||||||
#include <QDebug>
|
#include "settings/INISettingsObject.h"
|
||||||
#include <QtConcurrentRun>
|
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "NullInstance.h"
|
#include "NullInstance.h"
|
||||||
#include "pathmatcher/RegexpMatcher.h"
|
#include "pathmatcher/RegexpMatcher.h"
|
||||||
#include "settings/INISettingsObject.h"
|
#include <QtConcurrentRun>
|
||||||
|
|
||||||
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs)
|
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime)
|
||||||
{
|
{
|
||||||
m_origInstance = origInstance;
|
m_origInstance = origInstance;
|
||||||
m_keepPlaytime = prefs.isKeepPlaytimeEnabled();
|
m_keepPlaytime = keepPlaytime;
|
||||||
m_useLinks = prefs.isUseSymLinksEnabled();
|
|
||||||
m_linkRecursively = prefs.isLinkRecursivelyEnabled();
|
|
||||||
m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled();
|
|
||||||
m_copySaves = prefs.isLinkRecursivelyEnabled() && prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
|
|
||||||
m_useClone = prefs.isUseCloneEnabled();
|
|
||||||
|
|
||||||
QString filters = prefs.getSelectedFiltersAsRegex();
|
if(!copySaves)
|
||||||
if (m_useLinks || m_useHardLinks) {
|
{
|
||||||
if (!filters.isEmpty())
|
|
||||||
filters += "|";
|
|
||||||
filters += "instance.cfg";
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "CopyFilters:" << filters;
|
|
||||||
|
|
||||||
if (!filters.isEmpty()) {
|
|
||||||
// Set regex filter:
|
|
||||||
// FIXME: get this from the original instance type...
|
// FIXME: get this from the original instance type...
|
||||||
auto matcherReal = new RegexpMatcher(filters);
|
auto matcherReal = new RegexpMatcher("[.]?minecraft/saves");
|
||||||
matcherReal->caseSensitive(false);
|
matcherReal->caseSensitive(false);
|
||||||
m_matcher.reset(matcherReal);
|
m_matcher.reset(matcherReal);
|
||||||
}
|
}
|
||||||
@ -38,88 +23,10 @@ void InstanceCopyTask::executeTask()
|
|||||||
{
|
{
|
||||||
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
|
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
|
||||||
|
|
||||||
auto copySaves = [&]() {
|
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
|
||||||
QFileInfo mcDir(FS::PathCombine(m_stagingPath, "minecraft"));
|
folderCopy.followSymlinks(false).blacklist(m_matcher.get());
|
||||||
QFileInfo dotMCDir(FS::PathCombine(m_stagingPath, ".minecraft"));
|
|
||||||
|
|
||||||
QString staging_mc_dir;
|
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy);
|
||||||
if (mcDir.exists() && !dotMCDir.exists())
|
|
||||||
staging_mc_dir = mcDir.filePath();
|
|
||||||
else
|
|
||||||
staging_mc_dir = dotMCDir.filePath();
|
|
||||||
|
|
||||||
FS::copy savesCopy(FS::PathCombine(m_origInstance->gameRoot(), "saves"), FS::PathCombine(staging_mc_dir, "saves"));
|
|
||||||
savesCopy.followSymlinks(true);
|
|
||||||
|
|
||||||
return savesCopy();
|
|
||||||
};
|
|
||||||
|
|
||||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this, copySaves] {
|
|
||||||
if (m_useClone) {
|
|
||||||
FS::clone folderClone(m_origInstance->instanceRoot(), m_stagingPath);
|
|
||||||
folderClone.matcher(m_matcher.get());
|
|
||||||
|
|
||||||
return folderClone();
|
|
||||||
} else if (m_useLinks || m_useHardLinks) {
|
|
||||||
FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath);
|
|
||||||
int depth = m_linkRecursively ? -1 : 0; // we need to at least link the top level instead of the instance folder
|
|
||||||
folderLink.linkRecursively(true).setMaxDepth(depth).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
|
|
||||||
|
|
||||||
bool there_were_errors = false;
|
|
||||||
|
|
||||||
if (!folderLink()) {
|
|
||||||
#if defined Q_OS_WIN32
|
|
||||||
if (!m_useHardLinks) {
|
|
||||||
qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
|
|
||||||
|
|
||||||
qDebug() << "attempting to run with privelage";
|
|
||||||
|
|
||||||
QEventLoop loop;
|
|
||||||
bool got_priv_results = false;
|
|
||||||
|
|
||||||
connect(&folderLink, &FS::create_link::finishedPrivileged, this, [&](bool gotResults) {
|
|
||||||
if (!gotResults) {
|
|
||||||
qDebug() << "Privileged run exited without results!";
|
|
||||||
}
|
|
||||||
got_priv_results = gotResults;
|
|
||||||
loop.quit();
|
|
||||||
});
|
|
||||||
folderLink.runPrivileged();
|
|
||||||
|
|
||||||
loop.exec(); // wait for the finished signal
|
|
||||||
|
|
||||||
for (auto result : folderLink.getResults()) {
|
|
||||||
if (result.err_value != 0) {
|
|
||||||
there_were_errors = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_copySaves) {
|
|
||||||
there_were_errors |= !copySaves();
|
|
||||||
}
|
|
||||||
|
|
||||||
return got_priv_results && !there_were_errors;
|
|
||||||
} else {
|
|
||||||
qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_copySaves) {
|
|
||||||
there_were_errors |= !copySaves();
|
|
||||||
}
|
|
||||||
|
|
||||||
return !there_were_errors;
|
|
||||||
} else {
|
|
||||||
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
|
|
||||||
folderCopy.followSymlinks(false).matcher(m_matcher.get());
|
|
||||||
|
|
||||||
return folderCopy();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished);
|
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished);
|
||||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &InstanceCopyTask::copyAborted);
|
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &InstanceCopyTask::copyAborted);
|
||||||
m_copyFutureWatcher.setFuture(m_copyFuture);
|
m_copyFutureWatcher.setFuture(m_copyFuture);
|
||||||
@ -128,41 +35,20 @@ void InstanceCopyTask::executeTask()
|
|||||||
void InstanceCopyTask::copyFinished()
|
void InstanceCopyTask::copyFinished()
|
||||||
{
|
{
|
||||||
auto successful = m_copyFuture.result();
|
auto successful = m_copyFuture.result();
|
||||||
if (!successful) {
|
if(!successful)
|
||||||
|
{
|
||||||
emitFailed(tr("Instance folder copy failed."));
|
emitFailed(tr("Instance folder copy failed."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: shouldn't this be able to report errors?
|
// FIXME: shouldn't this be able to report errors?
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
||||||
|
|
||||||
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
|
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
|
||||||
inst->setName(name());
|
inst->setName(m_instName);
|
||||||
inst->setIconKey(m_instIcon);
|
inst->setIconKey(m_instIcon);
|
||||||
if (!m_keepPlaytime) {
|
if(!m_keepPlaytime) {
|
||||||
inst->resetTimePlayed();
|
inst->resetTimePlayed();
|
||||||
}
|
}
|
||||||
if (m_useLinks)
|
|
||||||
inst->addLinkedInstanceId(m_origInstance->id());
|
|
||||||
if (m_useLinks) {
|
|
||||||
auto allowed_symlinks_file = QFileInfo(FS::PathCombine(inst->gameRoot(), "allowed_symlinks.txt"));
|
|
||||||
|
|
||||||
QByteArray allowed_symlinks;
|
|
||||||
if (allowed_symlinks_file.exists()) {
|
|
||||||
allowed_symlinks.append(FS::read(allowed_symlinks_file.filePath()));
|
|
||||||
if (allowed_symlinks.right(1) != "\n")
|
|
||||||
allowed_symlinks.append("\n"); // we want to be on a new line
|
|
||||||
}
|
|
||||||
allowed_symlinks.append(m_origInstance->gameRoot().toUtf8());
|
|
||||||
allowed_symlinks.append("\n");
|
|
||||||
if (allowed_symlinks_file.isSymLink())
|
|
||||||
FS::deletePath(
|
|
||||||
allowed_symlinks_file
|
|
||||||
.filePath()); // we dont want to modify the original. also make sure the resulting file is not itself a link.
|
|
||||||
|
|
||||||
FS::write(allowed_symlinks_file.filePath(), allowed_symlinks);
|
|
||||||
}
|
|
||||||
|
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,37 +1,31 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
#include "net/NetJob.h"
|
||||||
|
#include <QUrl>
|
||||||
#include <QFuture>
|
#include <QFuture>
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
#include <QUrl>
|
|
||||||
#include "BaseInstance.h"
|
|
||||||
#include "BaseVersion.h"
|
|
||||||
#include "InstanceCopyPrefs.h"
|
|
||||||
#include "InstanceTask.h"
|
|
||||||
#include "net/NetJob.h"
|
|
||||||
#include "settings/SettingsObject.h"
|
#include "settings/SettingsObject.h"
|
||||||
#include "tasks/Task.h"
|
#include "BaseVersion.h"
|
||||||
|
#include "BaseInstance.h"
|
||||||
|
#include "InstanceTask.h"
|
||||||
|
|
||||||
class InstanceCopyTask : public InstanceTask {
|
class InstanceCopyTask : public InstanceTask
|
||||||
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs);
|
explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//! Entry point for tasks.
|
//! Entry point for tasks.
|
||||||
virtual void executeTask() override;
|
virtual void executeTask() override;
|
||||||
void copyFinished();
|
void copyFinished();
|
||||||
void copyAborted();
|
void copyAborted();
|
||||||
|
|
||||||
private:
|
private: /* data */
|
||||||
/* data */
|
|
||||||
InstancePtr m_origInstance;
|
InstancePtr m_origInstance;
|
||||||
QFuture<bool> m_copyFuture;
|
QFuture<bool> m_copyFuture;
|
||||||
QFutureWatcher<bool> m_copyFutureWatcher;
|
QFutureWatcher<bool> m_copyFutureWatcher;
|
||||||
std::unique_ptr<IPathMatcher> m_matcher;
|
std::unique_ptr<IPathMatcher> m_matcher;
|
||||||
bool m_keepPlaytime;
|
bool m_keepPlaytime;
|
||||||
bool m_useLinks = false;
|
|
||||||
bool m_useHardLinks = false;
|
|
||||||
bool m_copySaves = false;
|
|
||||||
bool m_linkRecursively = false;
|
|
||||||
bool m_useClone = false;
|
|
||||||
};
|
};
|
||||||
|
@ -1,60 +1,40 @@
|
|||||||
#include "InstanceCreationTask.h"
|
#include "InstanceCreationTask.h"
|
||||||
|
#include "settings/INISettingsObject.h"
|
||||||
|
#include "FileSystem.h"
|
||||||
|
|
||||||
#include <QDebug>
|
//FIXME: remove this
|
||||||
#include <QFile>
|
#include "minecraft/MinecraftInstance.h"
|
||||||
|
#include "minecraft/PackProfile.h"
|
||||||
|
|
||||||
InstanceCreationTask::InstanceCreationTask() = default;
|
InstanceCreationTask::InstanceCreationTask(BaseVersionPtr version)
|
||||||
|
{
|
||||||
|
m_version = version;
|
||||||
|
m_usingLoader = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceCreationTask::InstanceCreationTask(BaseVersionPtr version, QString loader, BaseVersionPtr loaderVersion)
|
||||||
|
{
|
||||||
|
m_version = version;
|
||||||
|
m_usingLoader = true;
|
||||||
|
m_loader = loader;
|
||||||
|
m_loaderVersion = loaderVersion;
|
||||||
|
}
|
||||||
|
|
||||||
void InstanceCreationTask::executeTask()
|
void InstanceCreationTask::executeTask()
|
||||||
{
|
{
|
||||||
setAbortable(true);
|
setStatus(tr("Creating instance from version %1").arg(m_version->name()));
|
||||||
|
{
|
||||||
if (updateInstance()) {
|
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
||||||
emitSucceeded();
|
instanceSettings->suspendSave();
|
||||||
return;
|
MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
|
||||||
|
auto components = inst.getPackProfile();
|
||||||
|
components->buildingFromScratch();
|
||||||
|
components->setComponentVersion("net.minecraft", m_version->descriptor(), true);
|
||||||
|
if(m_usingLoader)
|
||||||
|
components->setComponentVersion(m_loader, m_loaderVersion->descriptor());
|
||||||
|
inst.setName(m_instName);
|
||||||
|
inst.setIconKey(m_instIcon);
|
||||||
|
instanceSettings->resumeSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
// When the user aborted in the update stage.
|
|
||||||
if (m_abort) {
|
|
||||||
emitAborted();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!createInstance()) {
|
|
||||||
if (m_abort)
|
|
||||||
return;
|
|
||||||
|
|
||||||
qWarning() << "Instance creation failed!";
|
|
||||||
if (!m_error_message.isEmpty()) {
|
|
||||||
qWarning() << "Reason: " << m_error_message;
|
|
||||||
emitFailed(tr("Error while creating new instance:\n%1").arg(m_error_message));
|
|
||||||
} else {
|
|
||||||
emitFailed(tr("Error while creating new instance."));
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is set, it means we're updating an instance. So, we now need to remove the
|
|
||||||
// files scheduled to, and we'd better not let the user abort in the middle of it, since it'd
|
|
||||||
// put the instance in an invalid state.
|
|
||||||
if (shouldOverride()) {
|
|
||||||
setAbortable(false);
|
|
||||||
setStatus(tr("Removing old conflicting files..."));
|
|
||||||
qDebug() << "Removing old files";
|
|
||||||
|
|
||||||
for (auto path : m_files_to_remove) {
|
|
||||||
if (!QFile::exists(path))
|
|
||||||
continue;
|
|
||||||
qDebug() << "Removing" << path;
|
|
||||||
if (!QFile::remove(path)) {
|
|
||||||
qCritical() << "Couldn't remove the old conflicting files.";
|
|
||||||
emitFailed(tr("Failed to remove old conflicting files."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
@ -1,46 +1,26 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
#include "net/NetJob.h"
|
||||||
|
#include <QUrl>
|
||||||
|
#include "settings/SettingsObject.h"
|
||||||
#include "BaseVersion.h"
|
#include "BaseVersion.h"
|
||||||
#include "InstanceTask.h"
|
#include "InstanceTask.h"
|
||||||
|
|
||||||
class InstanceCreationTask : public InstanceTask {
|
class InstanceCreationTask : public InstanceTask
|
||||||
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
InstanceCreationTask();
|
explicit InstanceCreationTask(BaseVersionPtr version);
|
||||||
virtual ~InstanceCreationTask() = default;
|
explicit InstanceCreationTask(BaseVersionPtr version, QString loader, BaseVersionPtr loaderVersion);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void executeTask() final override;
|
//! Entry point for tasks.
|
||||||
|
virtual void executeTask() override;
|
||||||
|
|
||||||
/**
|
private: /* data */
|
||||||
* Tries to update an already existing instance.
|
BaseVersionPtr m_version;
|
||||||
*
|
bool m_usingLoader;
|
||||||
* This can be implemented by subclasses to provide a way of updating an already existing
|
QString m_loader;
|
||||||
* instance, according to that implementation's concept of 'identity' (i.e. instances that
|
BaseVersionPtr m_loaderVersion;
|
||||||
* are updates / downgrades of one another).
|
|
||||||
*
|
|
||||||
* If this returns true, createInstance() will not run, so you should do all update steps in here.
|
|
||||||
* Otherwise, createInstance() is run as normal.
|
|
||||||
*/
|
|
||||||
virtual bool updateInstance() { return false; };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance.
|
|
||||||
*
|
|
||||||
* Returns whether the instance creation was successful (true) or not (false).
|
|
||||||
*/
|
|
||||||
virtual bool createInstance() { return false; };
|
|
||||||
|
|
||||||
QString getError() const { return m_error_message; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void setError(const QString& message) { m_error_message = message; };
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool m_abort = false;
|
|
||||||
|
|
||||||
QStringList m_files_to_remove;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString m_error_message;
|
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* PolyMC - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||||
*
|
*
|
||||||
@ -35,83 +35,76 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "InstanceImportTask.h"
|
#include "InstanceImportTask.h"
|
||||||
|
#include <QtConcurrentRun>
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "BaseInstance.h"
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "MMCZip.h"
|
#include "MMCZip.h"
|
||||||
#include "NullInstance.h"
|
#include "NullInstance.h"
|
||||||
|
|
||||||
#include "QObjectPtr.h"
|
|
||||||
#include "icons/IconList.h"
|
#include "icons/IconList.h"
|
||||||
#include "icons/IconUtils.h"
|
#include "icons/IconUtils.h"
|
||||||
|
#include "settings/INISettingsObject.h"
|
||||||
|
|
||||||
#include "modplatform/flame/FlameInstanceCreationTask.h"
|
// FIXME: this does not belong here, it's Minecraft/Flame specific
|
||||||
#include "modplatform/modrinth/ModrinthInstanceCreationTask.h"
|
#include <quazip/quazipdir.h>
|
||||||
|
#include "Json.h"
|
||||||
|
#include "minecraft/MinecraftInstance.h"
|
||||||
|
#include "minecraft/PackProfile.h"
|
||||||
|
#include "modplatform/flame/FileResolvingTask.h"
|
||||||
|
#include "modplatform/flame/PackManifest.h"
|
||||||
|
#include "modplatform/modrinth/ModrinthPackManifest.h"
|
||||||
#include "modplatform/technic/TechnicPackProcessor.h"
|
#include "modplatform/technic/TechnicPackProcessor.h"
|
||||||
|
|
||||||
#include "settings/INISettingsObject.h"
|
#include "Application.h"
|
||||||
#include "tasks/Task.h"
|
#include "icons/IconList.h"
|
||||||
|
#include "net/ChecksumValidator.h"
|
||||||
|
|
||||||
#include "net/ApiDownload.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
|
#include "ui/dialogs/ScrollMessageBox.h"
|
||||||
|
|
||||||
#include <QtConcurrentRun>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include <quazip/quazipdir.h>
|
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent)
|
||||||
|
{
|
||||||
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent, QMap<QString, QString>&& extra_info)
|
m_sourceUrl = sourceUrl;
|
||||||
: m_sourceUrl(sourceUrl), m_extra_info(extra_info), m_parent(parent)
|
m_parent = parent;
|
||||||
{}
|
}
|
||||||
|
|
||||||
bool InstanceImportTask::abort()
|
bool InstanceImportTask::abort()
|
||||||
{
|
{
|
||||||
if (!canAbort())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (m_filesNetJob)
|
if (m_filesNetJob)
|
||||||
m_filesNetJob->abort();
|
m_filesNetJob->abort();
|
||||||
if (m_extractFuture.isRunning()) {
|
m_extractFuture.cancel();
|
||||||
// NOTE: The tasks created by QtConcurrent::run() can't actually get cancelled,
|
|
||||||
// but we can use this call to check the state when the extraction finishes.
|
|
||||||
m_extractFuture.cancel();
|
|
||||||
m_extractFuture.waitForFinished();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task::abort();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::executeTask()
|
void InstanceImportTask::executeTask()
|
||||||
{
|
{
|
||||||
setAbortable(true);
|
if (m_sourceUrl.isLocalFile())
|
||||||
|
{
|
||||||
if (m_sourceUrl.isLocalFile()) {
|
|
||||||
m_archivePath = m_sourceUrl.toLocalFile();
|
m_archivePath = m_sourceUrl.toLocalFile();
|
||||||
processZipPack();
|
processZipPack();
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
||||||
m_downloadRequired = true;
|
m_downloadRequired = true;
|
||||||
|
|
||||||
downloadFromUrl();
|
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
|
||||||
|
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
||||||
|
entry->setStale(true);
|
||||||
|
m_filesNetJob = new NetJob(tr("Modpack download"), APPLICATION->network());
|
||||||
|
m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
|
||||||
|
m_archivePath = entry->getFullPath();
|
||||||
|
auto job = m_filesNetJob.get();
|
||||||
|
connect(job, &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
||||||
|
connect(job, &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
||||||
|
connect(job, &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
||||||
|
m_filesNetJob->start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::downloadFromUrl()
|
|
||||||
{
|
|
||||||
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
|
|
||||||
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
|
||||||
entry->setStale(true);
|
|
||||||
m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network()));
|
|
||||||
m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry));
|
|
||||||
m_archivePath = entry->getFullPath();
|
|
||||||
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
|
|
||||||
m_filesNetJob->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceImportTask::downloadSucceeded()
|
void InstanceImportTask::downloadSucceeded()
|
||||||
{
|
{
|
||||||
processZipPack();
|
processZipPack();
|
||||||
@ -126,13 +119,7 @@ void InstanceImportTask::downloadFailed(QString reason)
|
|||||||
|
|
||||||
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
|
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
|
||||||
{
|
{
|
||||||
setProgress(current, total);
|
setProgress(current / 2, total);
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceImportTask::downloadAborted()
|
|
||||||
{
|
|
||||||
emitAborted();
|
|
||||||
m_filesNetJob.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::processZipPack()
|
void InstanceImportTask::processZipPack()
|
||||||
@ -143,7 +130,8 @@ void InstanceImportTask::processZipPack()
|
|||||||
|
|
||||||
// open the zip and find relevant files in it
|
// open the zip and find relevant files in it
|
||||||
m_packZip.reset(new QuaZip(m_archivePath));
|
m_packZip.reset(new QuaZip(m_archivePath));
|
||||||
if (!m_packZip->open(QuaZip::mdUnzip)) {
|
if (!m_packZip->open(QuaZip::mdUnzip))
|
||||||
|
{
|
||||||
emitFailed(tr("Unable to open supplied modpack zip file."));
|
emitFailed(tr("Unable to open supplied modpack zip file."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -157,81 +145,96 @@ void InstanceImportTask::processZipPack()
|
|||||||
|
|
||||||
// NOTE: Prioritize modpack platforms that aren't searched for recursively.
|
// NOTE: Prioritize modpack platforms that aren't searched for recursively.
|
||||||
// Especially Flame has a very common filename for its manifest, which may appear inside overrides for example
|
// Especially Flame has a very common filename for its manifest, which may appear inside overrides for example
|
||||||
if (modrinthFound) {
|
if(modrinthFound)
|
||||||
|
{
|
||||||
// process as Modrinth pack
|
// process as Modrinth pack
|
||||||
qDebug() << "Modrinth:" << modrinthFound;
|
qDebug() << "Modrinth:" << modrinthFound;
|
||||||
m_modpackType = ModpackType::Modrinth;
|
m_modpackType = ModpackType::Modrinth;
|
||||||
} else if (technicFound) {
|
}
|
||||||
|
else if (technicFound)
|
||||||
|
{
|
||||||
// process as Technic pack
|
// process as Technic pack
|
||||||
qDebug() << "Technic:" << technicFound;
|
qDebug() << "Technic:" << technicFound;
|
||||||
extractDir.mkpath(".minecraft");
|
extractDir.mkpath(".minecraft");
|
||||||
extractDir.cd(".minecraft");
|
extractDir.cd(".minecraft");
|
||||||
m_modpackType = ModpackType::Technic;
|
m_modpackType = ModpackType::Technic;
|
||||||
} else {
|
}
|
||||||
QStringList paths_to_ignore{ "overrides/" };
|
else
|
||||||
|
{
|
||||||
|
QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
|
||||||
|
QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json");
|
||||||
|
|
||||||
if (QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg", paths_to_ignore); !mmcRoot.isNull()) {
|
if (!mmcRoot.isNull())
|
||||||
|
{
|
||||||
// process as MultiMC instance/pack
|
// process as MultiMC instance/pack
|
||||||
qDebug() << "MultiMC:" << mmcRoot;
|
qDebug() << "MultiMC:" << mmcRoot;
|
||||||
root = mmcRoot;
|
root = mmcRoot;
|
||||||
m_modpackType = ModpackType::MultiMC;
|
m_modpackType = ModpackType::MultiMC;
|
||||||
} else if (QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json", paths_to_ignore);
|
}
|
||||||
!flameRoot.isNull()) {
|
else if(!flameRoot.isNull())
|
||||||
|
{
|
||||||
// process as Flame pack
|
// process as Flame pack
|
||||||
qDebug() << "Flame:" << flameRoot;
|
qDebug() << "Flame:" << flameRoot;
|
||||||
root = flameRoot;
|
root = flameRoot;
|
||||||
m_modpackType = ModpackType::Flame;
|
m_modpackType = ModpackType::Flame;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_modpackType == ModpackType::Unknown) {
|
if(m_modpackType == ModpackType::Unknown)
|
||||||
|
{
|
||||||
emitFailed(tr("Archive does not contain a recognized modpack type."));
|
emitFailed(tr("Archive does not contain a recognized modpack type."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure we extract just the pack
|
// make sure we extract just the pack
|
||||||
m_extractFuture =
|
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
|
||||||
QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
|
|
||||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
|
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
|
||||||
|
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &InstanceImportTask::extractAborted);
|
||||||
m_extractFutureWatcher.setFuture(m_extractFuture);
|
m_extractFutureWatcher.setFuture(m_extractFuture);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::extractFinished()
|
void InstanceImportTask::extractFinished()
|
||||||
{
|
{
|
||||||
m_packZip.reset();
|
m_packZip.reset();
|
||||||
|
if (!m_extractFuture.result())
|
||||||
if (m_extractFuture.isCanceled())
|
{
|
||||||
return;
|
|
||||||
if (!m_extractFuture.result().has_value()) {
|
|
||||||
emitFailed(tr("Failed to extract modpack"));
|
emitFailed(tr("Failed to extract modpack"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDir extractDir(m_stagingPath);
|
QDir extractDir(m_stagingPath);
|
||||||
|
|
||||||
qDebug() << "Fixing permissions for extracted pack files...";
|
qDebug() << "Fixing permissions for extracted pack files...";
|
||||||
QDirIterator it(extractDir, QDirIterator::Subdirectories);
|
QDirIterator it(extractDir, QDirIterator::Subdirectories);
|
||||||
while (it.hasNext()) {
|
while (it.hasNext())
|
||||||
|
{
|
||||||
auto filepath = it.next();
|
auto filepath = it.next();
|
||||||
QFileInfo file(filepath);
|
QFileInfo file(filepath);
|
||||||
auto permissions = QFile::permissions(filepath);
|
auto permissions = QFile::permissions(filepath);
|
||||||
auto origPermissions = permissions;
|
auto origPermissions = permissions;
|
||||||
if (file.isDir()) {
|
if(file.isDir())
|
||||||
|
{
|
||||||
// Folder +rwx for current user
|
// Folder +rwx for current user
|
||||||
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser;
|
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// File +rw for current user
|
// File +rw for current user
|
||||||
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
|
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
|
||||||
}
|
}
|
||||||
if (origPermissions != permissions) {
|
if(origPermissions != permissions)
|
||||||
if (!QFile::setPermissions(filepath, permissions)) {
|
{
|
||||||
|
if(!QFile::setPermissions(filepath, permissions))
|
||||||
|
{
|
||||||
logWarning(tr("Could not fix permissions for %1").arg(filepath));
|
logWarning(tr("Could not fix permissions for %1").arg(filepath));
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
qDebug() << "Fixed" << filepath;
|
qDebug() << "Fixed" << filepath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (m_modpackType) {
|
switch(m_modpackType)
|
||||||
|
{
|
||||||
case ModpackType::MultiMC:
|
case ModpackType::MultiMC:
|
||||||
processMultiMC();
|
processMultiMC();
|
||||||
return;
|
return;
|
||||||
@ -250,58 +253,300 @@ void InstanceImportTask::extractFinished()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InstanceImportTask::extractAborted()
|
||||||
|
{
|
||||||
|
emitFailed(tr("Instance import has been aborted."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void InstanceImportTask::processFlame()
|
void InstanceImportTask::processFlame()
|
||||||
{
|
{
|
||||||
shared_qobject_ptr<FlameCreationTask> inst_creation_task = nullptr;
|
const static QMap<QString,QString> forgemap = {
|
||||||
if (!m_extra_info.isEmpty()) {
|
{"1.2.5", "3.4.9.171"},
|
||||||
auto pack_id_it = m_extra_info.constFind("pack_id");
|
{"1.4.2", "6.0.1.355"},
|
||||||
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
|
{"1.4.7", "6.6.2.534"},
|
||||||
auto pack_id = pack_id_it.value();
|
{"1.5.2", "7.8.1.737"}
|
||||||
|
};
|
||||||
auto pack_version_id_it = m_extra_info.constFind("pack_version_id");
|
Flame::Manifest pack;
|
||||||
Q_ASSERT(pack_version_id_it != m_extra_info.constEnd());
|
try
|
||||||
auto pack_version_id = pack_version_id_it.value();
|
{
|
||||||
|
QString configPath = FS::PathCombine(m_stagingPath, "manifest.json");
|
||||||
QString original_instance_id;
|
Flame::loadManifest(pack, configPath);
|
||||||
auto original_instance_id_it = m_extra_info.constFind("original_instance_id");
|
QFile::remove(configPath);
|
||||||
if (original_instance_id_it != m_extra_info.constEnd())
|
}
|
||||||
original_instance_id = original_instance_id_it.value();
|
catch (const JSONValidationError &e)
|
||||||
|
{
|
||||||
inst_creation_task =
|
emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
|
||||||
makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
return;
|
||||||
} else {
|
}
|
||||||
// FIXME: Find a way to get IDs in directly imported ZIPs
|
if(!pack.overrides.isEmpty())
|
||||||
inst_creation_task = makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, QString(), QString());
|
{
|
||||||
|
QString overridePath = FS::PathCombine(m_stagingPath, pack.overrides);
|
||||||
|
if (QFile::exists(overridePath))
|
||||||
|
{
|
||||||
|
QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
|
||||||
|
if (!QFile::rename(overridePath, mcPath))
|
||||||
|
{
|
||||||
|
emitFailed(tr("Could not rename the overrides folder:\n") + pack.overrides);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logWarning(tr("The specified overrides folder (%1) is missing. Maybe the modpack was already used before?").arg(pack.overrides));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inst_creation_task->setName(*this);
|
QString forgeVersion;
|
||||||
inst_creation_task->setIcon(m_instIcon);
|
QString fabricVersion;
|
||||||
inst_creation_task->setGroup(m_instGroup);
|
// TODO: is Quilt relevant here?
|
||||||
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
|
for(auto &loader: pack.minecraft.modLoaders)
|
||||||
|
{
|
||||||
|
auto id = loader.id;
|
||||||
|
if(id.startsWith("forge-"))
|
||||||
|
{
|
||||||
|
id.remove("forge-");
|
||||||
|
forgeVersion = id;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(id.startsWith("fabric-"))
|
||||||
|
{
|
||||||
|
id.remove("fabric-");
|
||||||
|
fabricVersion = id;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
|
||||||
|
}
|
||||||
|
|
||||||
connect(inst_creation_task.get(), &Task::succeeded, this, [this, inst_creation_task] {
|
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||||
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
|
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
||||||
emitSucceeded();
|
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
||||||
|
auto mcVersion = pack.minecraft.version;
|
||||||
|
// Hack to correct some 'special sauce'...
|
||||||
|
if(mcVersion.endsWith('.'))
|
||||||
|
{
|
||||||
|
mcVersion.remove(QRegularExpression("[.]+$"));
|
||||||
|
logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack."));
|
||||||
|
}
|
||||||
|
auto components = instance.getPackProfile();
|
||||||
|
components->buildingFromScratch();
|
||||||
|
components->setComponentVersion("net.minecraft", mcVersion, true);
|
||||||
|
if(!forgeVersion.isEmpty())
|
||||||
|
{
|
||||||
|
// FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata.
|
||||||
|
if(forgeVersion == "recommended")
|
||||||
|
{
|
||||||
|
if(forgemap.contains(mcVersion))
|
||||||
|
{
|
||||||
|
forgeVersion = forgemap[mcVersion];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logWarning(tr("Could not map recommended Forge version for Minecraft %1").arg(mcVersion));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
components->setComponentVersion("net.minecraftforge", forgeVersion);
|
||||||
|
}
|
||||||
|
if(!fabricVersion.isEmpty())
|
||||||
|
{
|
||||||
|
components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion);
|
||||||
|
}
|
||||||
|
if (m_instIcon != "default")
|
||||||
|
{
|
||||||
|
instance.setIconKey(m_instIcon);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(pack.name.contains("Direwolf20"))
|
||||||
|
{
|
||||||
|
instance.setIconKey("steve");
|
||||||
|
}
|
||||||
|
else if(pack.name.contains("FTB") || pack.name.contains("Feed The Beast"))
|
||||||
|
{
|
||||||
|
instance.setIconKey("ftb_logo");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// default to something other than the MultiMC default to distinguish these
|
||||||
|
instance.setIconKey("flame");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods");
|
||||||
|
QFileInfo jarmodsInfo(jarmodsPath);
|
||||||
|
if(jarmodsInfo.isDir())
|
||||||
|
{
|
||||||
|
// install all the jar mods
|
||||||
|
qDebug() << "Found jarmods:";
|
||||||
|
QDir jarmodsDir(jarmodsPath);
|
||||||
|
QStringList jarMods;
|
||||||
|
for (auto info: jarmodsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
|
||||||
|
{
|
||||||
|
qDebug() << info.fileName();
|
||||||
|
jarMods.push_back(info.absoluteFilePath());
|
||||||
|
}
|
||||||
|
auto profile = instance.getPackProfile();
|
||||||
|
profile->installJarMods(jarMods);
|
||||||
|
// nuke the original files
|
||||||
|
FS::deletePath(jarmodsPath);
|
||||||
|
}
|
||||||
|
instance.setName(m_instName);
|
||||||
|
m_modIdResolver = new Flame::FileResolvingTask(APPLICATION->network(), pack);
|
||||||
|
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]()
|
||||||
|
{
|
||||||
|
auto results = m_modIdResolver->getResults();
|
||||||
|
//first check for blocked mods
|
||||||
|
QString text;
|
||||||
|
auto anyBlocked = false;
|
||||||
|
for(const auto& result: results.files.values()) {
|
||||||
|
if (!result.resolved || result.url.isEmpty()) {
|
||||||
|
text += QString("%1: <a href='%2'>%2</a><br/>").arg(result.fileName, result.websiteUrl);
|
||||||
|
anyBlocked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(anyBlocked) {
|
||||||
|
qWarning() << "Blocked mods found, displaying mod list";
|
||||||
|
|
||||||
|
auto message_dialog = new ScrollMessageBox(m_parent,
|
||||||
|
tr("Blocked mods found"),
|
||||||
|
tr("The following mods were blocked on third party launchers.<br/>"
|
||||||
|
"You will need to manually download them and add them to the modpack"),
|
||||||
|
text);
|
||||||
|
message_dialog->setModal(true);
|
||||||
|
|
||||||
|
if (message_dialog->exec()) {
|
||||||
|
m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
|
||||||
|
for (const auto &result: m_modIdResolver->getResults().files) {
|
||||||
|
QString filename = result.fileName;
|
||||||
|
if (!result.required) {
|
||||||
|
filename += ".disabled";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
|
||||||
|
auto path = FS::PathCombine(m_stagingPath, relpath);
|
||||||
|
|
||||||
|
switch (result.type) {
|
||||||
|
case Flame::File::Type::Folder: {
|
||||||
|
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
|
||||||
|
// fall-through intentional, we treat these as plain old mods and dump them wherever.
|
||||||
|
}
|
||||||
|
case Flame::File::Type::SingleFile:
|
||||||
|
case Flame::File::Type::Mod: {
|
||||||
|
if (!result.url.isEmpty()) {
|
||||||
|
qDebug() << "Will download" << result.url << "to" << path;
|
||||||
|
auto dl = Net::Download::makeFile(result.url, path);
|
||||||
|
m_filesNetJob->addNetAction(dl);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Flame::File::Type::Modpack:
|
||||||
|
logWarning(
|
||||||
|
tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(
|
||||||
|
relpath));
|
||||||
|
break;
|
||||||
|
case Flame::File::Type::Cmod2:
|
||||||
|
case Flame::File::Type::Ctoc:
|
||||||
|
case Flame::File::Type::Unknown:
|
||||||
|
logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_modIdResolver.reset();
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() {
|
||||||
|
m_filesNetJob.reset();
|
||||||
|
emitSucceeded();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason) {
|
||||||
|
m_filesNetJob.reset();
|
||||||
|
emitFailed(reason);
|
||||||
|
});
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
|
||||||
|
setProgress(current, total);
|
||||||
|
});
|
||||||
|
setStatus(tr("Downloading mods..."));
|
||||||
|
m_filesNetJob->start();
|
||||||
|
} else {
|
||||||
|
m_modIdResolver.reset();
|
||||||
|
emitFailed("Canceled");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//TODO extract to function ?
|
||||||
|
m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
|
||||||
|
for (const auto &result: m_modIdResolver->getResults().files) {
|
||||||
|
QString filename = result.fileName;
|
||||||
|
if (!result.required) {
|
||||||
|
filename += ".disabled";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
|
||||||
|
auto path = FS::PathCombine(m_stagingPath, relpath);
|
||||||
|
|
||||||
|
switch (result.type) {
|
||||||
|
case Flame::File::Type::Folder: {
|
||||||
|
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
|
||||||
|
// fall-through intentional, we treat these as plain old mods and dump them wherever.
|
||||||
|
}
|
||||||
|
case Flame::File::Type::SingleFile:
|
||||||
|
case Flame::File::Type::Mod: {
|
||||||
|
if (!result.url.isEmpty()) {
|
||||||
|
qDebug() << "Will download" << result.url << "to" << path;
|
||||||
|
auto dl = Net::Download::makeFile(result.url, path);
|
||||||
|
m_filesNetJob->addNetAction(dl);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Flame::File::Type::Modpack:
|
||||||
|
logWarning(
|
||||||
|
tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(
|
||||||
|
relpath));
|
||||||
|
break;
|
||||||
|
case Flame::File::Type::Cmod2:
|
||||||
|
case Flame::File::Type::Ctoc:
|
||||||
|
case Flame::File::Type::Unknown:
|
||||||
|
logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_modIdResolver.reset();
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() {
|
||||||
|
m_filesNetJob.reset();
|
||||||
|
emitSucceeded();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason) {
|
||||||
|
m_filesNetJob.reset();
|
||||||
|
emitFailed(reason);
|
||||||
|
});
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
|
||||||
|
setProgress(current, total);
|
||||||
|
});
|
||||||
|
setStatus(tr("Downloading mods..."));
|
||||||
|
m_filesNetJob->start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason)
|
||||||
|
{
|
||||||
|
m_modIdResolver.reset();
|
||||||
|
emitFailed(tr("Unable to resolve mod IDs:\n") + reason);
|
||||||
});
|
});
|
||||||
connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed);
|
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, [&](qint64 current, qint64 total)
|
||||||
connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress);
|
{
|
||||||
connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
setProgress(current, total);
|
||||||
connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus);
|
});
|
||||||
connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails);
|
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, [&](QString status)
|
||||||
|
{
|
||||||
connect(this, &Task::aborted, inst_creation_task.get(), &InstanceCreationTask::abort);
|
setStatus(status);
|
||||||
connect(inst_creation_task.get(), &Task::aborted, this, &Task::abort);
|
});
|
||||||
connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
|
m_modIdResolver->start();
|
||||||
|
|
||||||
inst_creation_task->start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::processTechnic()
|
void InstanceImportTask::processTechnic()
|
||||||
{
|
{
|
||||||
shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor{ new Technic::TechnicPackProcessor };
|
shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor = new Technic::TechnicPackProcessor();
|
||||||
connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &InstanceImportTask::emitSucceeded);
|
connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &InstanceImportTask::emitSucceeded);
|
||||||
connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &InstanceImportTask::emitFailed);
|
connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &InstanceImportTask::emitFailed);
|
||||||
packProcessor->run(m_globalSettings, name(), m_instIcon, m_stagingPath);
|
packProcessor->run(m_globalSettings, m_instName, m_instIcon, m_stagingPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::processMultiMC()
|
void InstanceImportTask::processMultiMC()
|
||||||
@ -315,7 +560,7 @@ void InstanceImportTask::processMultiMC()
|
|||||||
instance.resetTimePlayed();
|
instance.resetTimePlayed();
|
||||||
|
|
||||||
// set a new nice name
|
// set a new nice name
|
||||||
instance.setName(name());
|
instance.setName(m_instName);
|
||||||
|
|
||||||
// if the icon was specified by user, use that. otherwise pull icon from the pack
|
// if the icon was specified by user, use that. otherwise pull icon from the pack
|
||||||
if (m_instIcon != "default") {
|
if (m_instIcon != "default") {
|
||||||
@ -336,56 +581,198 @@ void InstanceImportTask::processMultiMC()
|
|||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://docs.modrinth.com/docs/modpacks/format_definition/
|
||||||
void InstanceImportTask::processModrinth()
|
void InstanceImportTask::processModrinth()
|
||||||
{
|
{
|
||||||
ModrinthCreationTask* inst_creation_task = nullptr;
|
std::vector<Modrinth::File> files;
|
||||||
if (!m_extra_info.isEmpty()) {
|
QString minecraftVersion, fabricVersion, quiltVersion, forgeVersion;
|
||||||
auto pack_id_it = m_extra_info.constFind("pack_id");
|
try {
|
||||||
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
|
QString indexPath = FS::PathCombine(m_stagingPath, "modrinth.index.json");
|
||||||
auto pack_id = pack_id_it.value();
|
auto doc = Json::requireDocument(indexPath);
|
||||||
|
auto obj = Json::requireObject(doc, "modrinth.index.json");
|
||||||
|
int formatVersion = Json::requireInteger(obj, "formatVersion", "modrinth.index.json");
|
||||||
|
if (formatVersion == 1) {
|
||||||
|
auto game = Json::requireString(obj, "game", "modrinth.index.json");
|
||||||
|
if (game != "minecraft") {
|
||||||
|
throw JSONValidationError("Unknown game: " + game);
|
||||||
|
}
|
||||||
|
|
||||||
QString pack_version_id;
|
auto jsonFiles = Json::requireIsArrayOf<QJsonObject>(obj, "files", "modrinth.index.json");
|
||||||
auto pack_version_id_it = m_extra_info.constFind("pack_version_id");
|
bool had_optional = false;
|
||||||
if (pack_version_id_it != m_extra_info.constEnd())
|
for (auto modInfo : jsonFiles) {
|
||||||
pack_version_id = pack_version_id_it.value();
|
Modrinth::File file;
|
||||||
|
file.path = Json::requireString(modInfo, "path");
|
||||||
|
|
||||||
QString original_instance_id;
|
auto env = Json::ensureObject(modInfo, "env");
|
||||||
auto original_instance_id_it = m_extra_info.constFind("original_instance_id");
|
// 'env' field is optional
|
||||||
if (original_instance_id_it != m_extra_info.constEnd())
|
if (!env.isEmpty()) {
|
||||||
original_instance_id = original_instance_id_it.value();
|
QString support = Json::ensureString(env, "client", "unsupported");
|
||||||
|
if (support == "unsupported") {
|
||||||
|
continue;
|
||||||
|
} else if (support == "optional") {
|
||||||
|
// TODO: Make a review dialog for choosing which ones the user wants!
|
||||||
|
if (!had_optional) {
|
||||||
|
had_optional = true;
|
||||||
|
auto info = CustomMessageBox::selectable(
|
||||||
|
m_parent, tr("Optional mod detected!"),
|
||||||
|
tr("One or more mods from this modpack are optional. They will be downloaded, but disabled by default!"),
|
||||||
|
QMessageBox::Information);
|
||||||
|
info->exec();
|
||||||
|
}
|
||||||
|
|
||||||
inst_creation_task =
|
if (file.path.endsWith(".jar"))
|
||||||
new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
file.path += ".disabled";
|
||||||
} else {
|
}
|
||||||
QString pack_id;
|
}
|
||||||
if (!m_sourceUrl.isEmpty()) {
|
|
||||||
QRegularExpression regex(R"(data\/([^\/]*)\/versions)");
|
QJsonObject hashes = Json::requireObject(modInfo, "hashes");
|
||||||
pack_id = regex.match(m_sourceUrl.toString()).captured(1);
|
QString hash;
|
||||||
|
QCryptographicHash::Algorithm hashAlgorithm;
|
||||||
|
hash = Json::ensureString(hashes, "sha1");
|
||||||
|
hashAlgorithm = QCryptographicHash::Sha1;
|
||||||
|
if (hash.isEmpty()) {
|
||||||
|
hash = Json::ensureString(hashes, "sha512");
|
||||||
|
hashAlgorithm = QCryptographicHash::Sha512;
|
||||||
|
if (hash.isEmpty()) {
|
||||||
|
hash = Json::ensureString(hashes, "sha256");
|
||||||
|
hashAlgorithm = QCryptographicHash::Sha256;
|
||||||
|
if (hash.isEmpty()) {
|
||||||
|
throw JSONValidationError("No hash found for: " + file.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file.hash = QByteArray::fromHex(hash.toLatin1());
|
||||||
|
file.hashAlgorithm = hashAlgorithm;
|
||||||
|
|
||||||
|
// Do not use requireUrl, which uses StrictMode, instead use QUrl's default TolerantMode
|
||||||
|
// (as Modrinth seems to incorrectly handle spaces)
|
||||||
|
|
||||||
|
auto download_arr = Json::ensureArray(modInfo, "downloads");
|
||||||
|
for(auto download : download_arr) {
|
||||||
|
qWarning() << download.toString();
|
||||||
|
bool is_last = download.toString() == download_arr.last().toString();
|
||||||
|
|
||||||
|
auto download_url = QUrl(download.toString());
|
||||||
|
|
||||||
|
if (!download_url.isValid()) {
|
||||||
|
qDebug() << QString("Download URL (%1) for %2 is not a correctly formatted URL")
|
||||||
|
.arg(download_url.toString(), file.path);
|
||||||
|
if(is_last && file.downloads.isEmpty())
|
||||||
|
throw JSONValidationError(tr("Download URL for %1 is not a correctly formatted URL").arg(file.path));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
file.downloads.push_back(download_url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
files.push_back(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dependencies = Json::requireObject(obj, "dependencies", "modrinth.index.json");
|
||||||
|
for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) {
|
||||||
|
QString name = it.key();
|
||||||
|
if (name == "minecraft") {
|
||||||
|
minecraftVersion = Json::requireString(*it, "Minecraft version");
|
||||||
|
}
|
||||||
|
else if (name == "fabric-loader") {
|
||||||
|
fabricVersion = Json::requireString(*it, "Fabric Loader version");
|
||||||
|
}
|
||||||
|
else if (name == "quilt-loader") {
|
||||||
|
quiltVersion = Json::requireString(*it, "Quilt Loader version");
|
||||||
|
}
|
||||||
|
else if (name == "forge") {
|
||||||
|
forgeVersion = Json::requireString(*it, "Forge version");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw JSONValidationError("Unknown dependency type: " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw JSONValidationError(QStringLiteral("Unknown format version: %s").arg(formatVersion));
|
||||||
}
|
}
|
||||||
|
QFile::remove(indexPath);
|
||||||
|
} catch (const JSONValidationError& e) {
|
||||||
|
emitFailed(tr("Could not understand pack index:\n") + e.cause());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mcPath = FS::PathCombine(m_stagingPath, ".minecraft");
|
||||||
|
|
||||||
// FIXME: Find a way to get the ID in directly imported ZIPs
|
auto override_path = FS::PathCombine(m_stagingPath, "overrides");
|
||||||
inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id);
|
if (QFile::exists(override_path)) {
|
||||||
|
if (!QFile::rename(override_path, mcPath)) {
|
||||||
|
emitFailed(tr("Could not rename the overrides folder:\n") + "overrides");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inst_creation_task->setName(*this);
|
// Do client overrides
|
||||||
inst_creation_task->setIcon(m_instIcon);
|
auto client_override_path = FS::PathCombine(m_stagingPath, "client-overrides");
|
||||||
inst_creation_task->setGroup(m_instGroup);
|
if (QFile::exists(client_override_path)) {
|
||||||
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
|
if (!FS::overrideFolder(mcPath, client_override_path)) {
|
||||||
|
emitFailed(tr("Could not rename the client overrides folder:\n") + "client overrides");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] {
|
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||||
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
|
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
||||||
emitSucceeded();
|
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
||||||
|
auto components = instance.getPackProfile();
|
||||||
|
components->buildingFromScratch();
|
||||||
|
components->setComponentVersion("net.minecraft", minecraftVersion, true);
|
||||||
|
if (!fabricVersion.isEmpty())
|
||||||
|
components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion);
|
||||||
|
if (!quiltVersion.isEmpty())
|
||||||
|
components->setComponentVersion("org.quiltmc.quilt-loader", quiltVersion);
|
||||||
|
if (!forgeVersion.isEmpty())
|
||||||
|
components->setComponentVersion("net.minecraftforge", forgeVersion);
|
||||||
|
if (m_instIcon != "default")
|
||||||
|
{
|
||||||
|
instance.setIconKey(m_instIcon);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
instance.setIconKey("modrinth");
|
||||||
|
}
|
||||||
|
instance.setName(m_instName);
|
||||||
|
instance.saveNow();
|
||||||
|
|
||||||
|
m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
|
||||||
|
for (auto file : files)
|
||||||
|
{
|
||||||
|
auto path = FS::PathCombine(m_stagingPath, ".minecraft", file.path);
|
||||||
|
qDebug() << "Will try to download" << file.downloads.front() << "to" << path;
|
||||||
|
auto dl = Net::Download::makeFile(file.downloads.dequeue(), path);
|
||||||
|
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
||||||
|
m_filesNetJob->addNetAction(dl);
|
||||||
|
|
||||||
|
if (file.downloads.size() > 0) {
|
||||||
|
// FIXME: This really needs to be put into a ConcurrentTask of
|
||||||
|
// MultipleOptionsTask's , once those exist :)
|
||||||
|
connect(dl.get(), &NetAction::failed, [this, &file, path, dl]{
|
||||||
|
auto dl = Net::Download::makeFile(file.downloads.dequeue(), path);
|
||||||
|
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
||||||
|
m_filesNetJob->addNetAction(dl);
|
||||||
|
dl->succeeded();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
|
||||||
|
{
|
||||||
|
m_filesNetJob.reset();
|
||||||
|
emitSucceeded();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::failed, [&](const QString &reason)
|
||||||
|
{
|
||||||
|
m_filesNetJob.reset();
|
||||||
|
emitFailed(reason);
|
||||||
});
|
});
|
||||||
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
|
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
|
||||||
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress);
|
{
|
||||||
connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
setProgress(current, total);
|
||||||
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus);
|
});
|
||||||
connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails);
|
setStatus(tr("Downloading mods..."));
|
||||||
connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater);
|
m_filesNetJob->start();
|
||||||
|
|
||||||
connect(this, &Task::aborted, inst_creation_task, &InstanceCreationTask::abort);
|
|
||||||
connect(inst_creation_task, &Task::aborted, this, &Task::abort);
|
|
||||||
connect(inst_creation_task, &Task::abortStatusChanged, this, &Task::setAbortable);
|
|
||||||
|
|
||||||
inst_creation_task->start();
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* PolyMC - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -35,49 +35,55 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "InstanceTask.h"
|
||||||
|
#include "net/NetJob.h"
|
||||||
|
#include <QUrl>
|
||||||
#include <QFuture>
|
#include <QFuture>
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
#include <QUrl>
|
#include "settings/SettingsObject.h"
|
||||||
#include "InstanceTask.h"
|
|
||||||
#include "QObjectPtr.h"
|
#include "QObjectPtr.h"
|
||||||
#include "modplatform/flame/PackManifest.h"
|
#include "modplatform/flame/PackManifest.h"
|
||||||
#include "net/NetJob.h"
|
|
||||||
#include "settings/SettingsObject.h"
|
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
class QuaZip;
|
class QuaZip;
|
||||||
namespace Flame {
|
namespace Flame
|
||||||
class FileResolvingTask;
|
{
|
||||||
|
class FileResolvingTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
class InstanceImportTask : public InstanceTask {
|
class InstanceImportTask : public InstanceTask
|
||||||
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {});
|
explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
bool canAbort() const override { return true; }
|
||||||
bool abort() override;
|
bool abort() override;
|
||||||
const QVector<Flame::File>& getBlockedFiles() const { return m_blockedMods; }
|
const QVector<Flame::File> &getBlockedFiles() const
|
||||||
|
{
|
||||||
|
return m_blockedMods;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//! Entry point for tasks.
|
//! Entry point for tasks.
|
||||||
virtual void executeTask() override;
|
virtual void executeTask() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void processZipPack();
|
void processZipPack();
|
||||||
void processMultiMC();
|
void processMultiMC();
|
||||||
void processTechnic();
|
void processTechnic();
|
||||||
void processFlame();
|
void processFlame();
|
||||||
void processModrinth();
|
void processModrinth();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void downloadSucceeded();
|
void downloadSucceeded();
|
||||||
void downloadFailed(QString reason);
|
void downloadFailed(QString reason);
|
||||||
void downloadProgressChanged(qint64 current, qint64 total);
|
void downloadProgressChanged(qint64 current, qint64 total);
|
||||||
void downloadAborted();
|
|
||||||
void extractFinished();
|
void extractFinished();
|
||||||
|
void extractAborted();
|
||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
NetJob::Ptr m_filesNetJob;
|
NetJob::Ptr m_filesNetJob;
|
||||||
shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver;
|
shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver;
|
||||||
QUrl m_sourceUrl;
|
QUrl m_sourceUrl;
|
||||||
@ -87,7 +93,7 @@ class InstanceImportTask : public InstanceTask {
|
|||||||
QFuture<std::optional<QStringList>> m_extractFuture;
|
QFuture<std::optional<QStringList>> m_extractFuture;
|
||||||
QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher;
|
QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher;
|
||||||
QVector<Flame::File> m_blockedMods;
|
QVector<Flame::File> m_blockedMods;
|
||||||
enum class ModpackType {
|
enum class ModpackType{
|
||||||
Unknown,
|
Unknown,
|
||||||
MultiMC,
|
MultiMC,
|
||||||
Technic,
|
Technic,
|
||||||
@ -95,11 +101,6 @@ class InstanceImportTask : public InstanceTask {
|
|||||||
Modrinth,
|
Modrinth,
|
||||||
} m_modpackType = ModpackType::Unknown;
|
} m_modpackType = ModpackType::Unknown;
|
||||||
|
|
||||||
// Extra info we might need, that's available before, but can't be derived from
|
//FIXME: nuke
|
||||||
// the source URL / the resource it points to alone.
|
|
||||||
QMap<QString, QString> m_extra_info;
|
|
||||||
|
|
||||||
// FIXME: nuke
|
|
||||||
QWidget* m_parent;
|
QWidget* m_parent;
|
||||||
void downloadFromUrl();
|
|
||||||
};
|
};
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,122 +1,111 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
/* Copyright 2013-2021 MultiMC Contributors
|
||||||
/*
|
|
||||||
* Prism Launcher - Minecraft Launcher
|
|
||||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* it under the terms of the GNU General Public License as published by
|
* you may not use this file except in compliance with the License.
|
||||||
* the Free Software Foundation, version 3.
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
*
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* This file incorporates work covered by the following copyright and
|
* See the License for the specific language governing permissions and
|
||||||
* permission notice:
|
* limitations under the License.
|
||||||
*
|
|
||||||
* Copyright 2013-2021 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
|
||||||
#include <QList>
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QPair>
|
#include <QAbstractListModel>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QStack>
|
#include <QList>
|
||||||
|
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
|
|
||||||
|
#include "QObjectPtr.h"
|
||||||
|
|
||||||
class QFileSystemWatcher;
|
class QFileSystemWatcher;
|
||||||
class InstanceTask;
|
class InstanceTask;
|
||||||
struct InstanceName;
|
|
||||||
|
|
||||||
using InstanceId = QString;
|
using InstanceId = QString;
|
||||||
using GroupId = QString;
|
using GroupId = QString;
|
||||||
using InstanceLocator = std::pair<InstancePtr, int>;
|
using InstanceLocator = std::pair<InstancePtr, int>;
|
||||||
|
|
||||||
enum class InstCreateError { NoCreateError = 0, NoSuchVersion, UnknownCreateError, InstExists, CantCreateDir };
|
enum class InstCreateError
|
||||||
|
{
|
||||||
enum class GroupsState { NotLoaded, Steady, Dirty };
|
NoCreateError = 0,
|
||||||
|
NoSuchVersion,
|
||||||
struct TrashHistoryItem {
|
UnknownCreateError,
|
||||||
QString id;
|
InstExists,
|
||||||
QString polyPath;
|
CantCreateDir
|
||||||
QString trashPath;
|
|
||||||
QString groupName;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class InstanceList : public QAbstractListModel {
|
enum class GroupsState
|
||||||
|
{
|
||||||
|
NotLoaded,
|
||||||
|
Steady,
|
||||||
|
Dirty
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class InstanceList : public QAbstractListModel
|
||||||
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit InstanceList(SettingsObjectPtr settings, const QString& instDir, QObject* parent = 0);
|
explicit InstanceList(SettingsObjectPtr settings, const QString & instDir, QObject *parent = 0);
|
||||||
virtual ~InstanceList();
|
virtual ~InstanceList();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QModelIndex index(int row, int column = 0, const QModelIndex& parent = QModelIndex()) const override;
|
QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override;
|
||||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
QVariant data(const QModelIndex& index, int role) const override;
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||||
|
|
||||||
bool setData(const QModelIndex& index, const QVariant& value, int role) override;
|
bool setData(const QModelIndex & index, const QVariant & value, int role) override;
|
||||||
|
|
||||||
enum AdditionalRoles {
|
enum AdditionalRoles
|
||||||
|
{
|
||||||
GroupRole = Qt::UserRole,
|
GroupRole = Qt::UserRole,
|
||||||
InstancePointerRole = 0x34B1CB48, ///< Return pointer to real instance
|
InstancePointerRole = 0x34B1CB48, ///< Return pointer to real instance
|
||||||
InstanceIDRole = 0x34B1CB49 ///< Return id if the instance
|
InstanceIDRole = 0x34B1CB49 ///< Return id if the instance
|
||||||
};
|
};
|
||||||
/*!
|
/*!
|
||||||
* \brief Error codes returned by functions in the InstanceList class.
|
* \brief Error codes returned by functions in the InstanceList class.
|
||||||
* NoError Indicates that no error occurred.
|
* NoError Indicates that no error occurred.
|
||||||
* UnknownError indicates that an unspecified error occurred.
|
* UnknownError indicates that an unspecified error occurred.
|
||||||
*/
|
*/
|
||||||
enum InstListError { NoError = 0, UnknownError };
|
enum InstListError
|
||||||
|
{
|
||||||
|
NoError = 0,
|
||||||
|
UnknownError
|
||||||
|
};
|
||||||
|
|
||||||
InstancePtr at(int i) const { return m_instances.at(i); }
|
InstancePtr at(int i) const
|
||||||
|
{
|
||||||
|
return m_instances.at(i);
|
||||||
|
}
|
||||||
|
|
||||||
int count() const { return m_instances.count(); }
|
int count() const
|
||||||
|
{
|
||||||
|
return m_instances.count();
|
||||||
|
}
|
||||||
|
|
||||||
InstListError loadList();
|
InstListError loadList();
|
||||||
void saveNow();
|
void saveNow();
|
||||||
|
|
||||||
/* O(n) */
|
|
||||||
InstancePtr getInstanceById(QString id) const;
|
InstancePtr getInstanceById(QString id) const;
|
||||||
/* O(n) */
|
QModelIndex getInstanceIndexById(const QString &id) const;
|
||||||
InstancePtr getInstanceByManagedName(const QString& managed_name) const;
|
|
||||||
QModelIndex getInstanceIndexById(const QString& id) const;
|
|
||||||
QStringList getGroups();
|
QStringList getGroups();
|
||||||
bool isGroupCollapsed(const QString& groupName);
|
bool isGroupCollapsed(const QString &groupName);
|
||||||
|
|
||||||
GroupId getInstanceGroup(const InstanceId& id) const;
|
GroupId getInstanceGroup(const InstanceId & id) const;
|
||||||
void setInstanceGroup(const InstanceId& id, GroupId name);
|
void setInstanceGroup(const InstanceId & id, const GroupId& name);
|
||||||
|
|
||||||
void deleteGroup(const GroupId& name);
|
void deleteGroup(const GroupId & name);
|
||||||
void renameGroup(const GroupId& src, const GroupId& dst);
|
void deleteInstance(const InstanceId & id);
|
||||||
bool trashInstance(const InstanceId& id);
|
|
||||||
bool trashedSomething();
|
|
||||||
void undoTrashInstance();
|
|
||||||
void deleteInstance(const InstanceId& id);
|
|
||||||
|
|
||||||
// Wrap an instance creation task in some more task machinery and make it ready to be used
|
// Wrap an instance creation task in some more task machinery and make it ready to be used
|
||||||
Task* wrapInstanceTask(InstanceTask* task);
|
Task * wrapInstanceTask(InstanceTask * task);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new empty staging area for instance creation and @return a path/key top commit it later.
|
* Create a new empty staging area for instance creation and @return a path/key top commit it later.
|
||||||
@ -127,16 +116,14 @@ class InstanceList : public QAbstractListModel {
|
|||||||
/**
|
/**
|
||||||
* Commit the staging area given by @keyPath to the provider - used when creation succeeds.
|
* Commit the staging area given by @keyPath to the provider - used when creation succeeds.
|
||||||
* Used by instance manipulation tasks.
|
* Used by instance manipulation tasks.
|
||||||
* should_override is used when another similar instance already exists, and we want to override it
|
|
||||||
* - for instance, when updating it.
|
|
||||||
*/
|
*/
|
||||||
bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, QString groupName, const InstanceTask&);
|
bool commitStagedInstance(const QString & keyPath, const QString& instanceName, const QString & groupName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy a previously created staging area given by @keyPath - used when creation fails.
|
* Destroy a previously created staging area given by @keyPath - used when creation fails.
|
||||||
* Used by instance manipulation tasks.
|
* Used by instance manipulation tasks.
|
||||||
*/
|
*/
|
||||||
bool destroyStagingPath(const QString& keyPath);
|
bool destroyStagingPath(const QString & keyPath);
|
||||||
|
|
||||||
int getTotalPlayTime();
|
int getTotalPlayTime();
|
||||||
|
|
||||||
@ -144,61 +131,53 @@ class InstanceList : public QAbstractListModel {
|
|||||||
|
|
||||||
Qt::DropActions supportedDropActions() const override;
|
Qt::DropActions supportedDropActions() const override;
|
||||||
|
|
||||||
bool canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const override;
|
bool canDropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) const override;
|
||||||
|
|
||||||
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override;
|
bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) override;
|
||||||
|
|
||||||
QStringList mimeTypes() const override;
|
QStringList mimeTypes() const override;
|
||||||
QMimeData* mimeData(const QModelIndexList& indexes) const override;
|
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
||||||
|
|
||||||
QStringList getLinkedInstancesById(const QString& id) const;
|
signals:
|
||||||
|
|
||||||
signals:
|
|
||||||
void dataIsInvalid();
|
void dataIsInvalid();
|
||||||
void instancesChanged();
|
void instancesChanged();
|
||||||
void instanceSelectRequest(QString instanceId);
|
void instanceSelectRequest(QString instanceId);
|
||||||
void groupsChanged(QSet<QString> groups);
|
void groupsChanged(QSet<QString> groups);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void on_InstFolderChanged(const Setting& setting, QVariant value);
|
void on_InstFolderChanged(const Setting &setting, QVariant value);
|
||||||
void on_GroupStateChanged(const QString& group, bool collapsed);
|
void on_GroupStateChanged(const QString &group, bool collapsed);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void propertiesChanged(BaseInstance* inst);
|
void propertiesChanged(BaseInstance *inst);
|
||||||
void providerUpdated();
|
void providerUpdated();
|
||||||
void instanceDirContentsChanged(const QString& path);
|
void instanceDirContentsChanged(const QString &path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int getInstIndex(BaseInstance* inst) const;
|
int getInstIndex(BaseInstance *inst) const;
|
||||||
void updateTotalPlayTime();
|
void updateTotalPlayTime();
|
||||||
void suspendWatch();
|
void suspendWatch();
|
||||||
void resumeWatch();
|
void resumeWatch();
|
||||||
void add(const QList<InstancePtr>& list);
|
void add(const QList<InstancePtr> &list);
|
||||||
void loadGroupList();
|
void loadGroupList();
|
||||||
void saveGroupList();
|
void saveGroupList();
|
||||||
QList<InstanceId> discoverInstances();
|
QList<InstanceId> discoverInstances();
|
||||||
InstancePtr loadInstance(const InstanceId& id);
|
InstancePtr loadInstance(const InstanceId& id);
|
||||||
|
|
||||||
void increaseGroupCount(const QString& group);
|
private:
|
||||||
void decreaseGroupCount(const QString& group);
|
|
||||||
|
|
||||||
private:
|
|
||||||
int m_watchLevel = 0;
|
int m_watchLevel = 0;
|
||||||
int totalPlayTime = 0;
|
int totalPlayTime = 0;
|
||||||
bool m_dirty = false;
|
bool m_dirty = false;
|
||||||
QList<InstancePtr> m_instances;
|
QList<InstancePtr> m_instances;
|
||||||
// id -> refs
|
QSet<QString> m_groupNameCache;
|
||||||
QMap<QString, int> m_groupNameCache;
|
|
||||||
|
|
||||||
SettingsObjectPtr m_globalSettings;
|
SettingsObjectPtr m_globalSettings;
|
||||||
QString m_instDir;
|
QString m_instDir;
|
||||||
QFileSystemWatcher* m_watcher;
|
QFileSystemWatcher * m_watcher;
|
||||||
// FIXME: this is so inefficient that looking at it is almost painful.
|
// FIXME: this is so inefficient that looking at it is almost painful.
|
||||||
QSet<QString> m_collapsedGroups;
|
QSet<QString> m_collapsedGroups;
|
||||||
QMap<InstanceId, GroupId> m_instanceGroupIndex;
|
QMap<InstanceId, GroupId> m_instanceGroupIndex;
|
||||||
QSet<InstanceId> instanceSet;
|
QSet<InstanceId> instanceSet;
|
||||||
bool m_groupsLoaded = false;
|
bool m_groupsLoaded = false;
|
||||||
bool m_instancesProbed = false;
|
bool m_instancesProbed = false;
|
||||||
|
|
||||||
QStack<TrashHistoryItem> m_trashHistory;
|
|
||||||
};
|
};
|
||||||
|
@ -1,43 +1,45 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <FileSystem.h>
|
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include "minecraft/MinecraftInstance.h"
|
||||||
|
#include <FileSystem.h>
|
||||||
#include "ui/pages/BasePage.h"
|
#include "ui/pages/BasePage.h"
|
||||||
#include "ui/pages/BasePageProvider.h"
|
#include "ui/pages/BasePageProvider.h"
|
||||||
#include "ui/pages/instance/InstanceSettingsPage.h"
|
|
||||||
#include "ui/pages/instance/LogPage.h"
|
#include "ui/pages/instance/LogPage.h"
|
||||||
#include "ui/pages/instance/ManagedPackPage.h"
|
|
||||||
#include "ui/pages/instance/ModFolderPage.h"
|
|
||||||
#include "ui/pages/instance/NotesPage.h"
|
|
||||||
#include "ui/pages/instance/OtherLogsPage.h"
|
|
||||||
#include "ui/pages/instance/ResourcePackPage.h"
|
|
||||||
#include "ui/pages/instance/ScreenshotsPage.h"
|
|
||||||
#include "ui/pages/instance/ServersPage.h"
|
|
||||||
#include "ui/pages/instance/ShaderPackPage.h"
|
|
||||||
#include "ui/pages/instance/TexturePackPage.h"
|
|
||||||
#include "ui/pages/instance/VersionPage.h"
|
#include "ui/pages/instance/VersionPage.h"
|
||||||
|
#include "ui/pages/instance/ModFolderPage.h"
|
||||||
|
#include "ui/pages/instance/ResourcePackPage.h"
|
||||||
|
#include "ui/pages/instance/TexturePackPage.h"
|
||||||
|
#include "ui/pages/instance/ShaderPackPage.h"
|
||||||
|
#include "ui/pages/instance/NotesPage.h"
|
||||||
|
#include "ui/pages/instance/ScreenshotsPage.h"
|
||||||
|
#include "ui/pages/instance/InstanceSettingsPage.h"
|
||||||
|
#include "ui/pages/instance/OtherLogsPage.h"
|
||||||
#include "ui/pages/instance/WorldListPage.h"
|
#include "ui/pages/instance/WorldListPage.h"
|
||||||
|
#include "ui/pages/instance/ServersPage.h"
|
||||||
|
#include "ui/pages/instance/GameOptionsPage.h"
|
||||||
|
|
||||||
class InstancePageProvider : protected QObject, public BasePageProvider {
|
class InstancePageProvider : public QObject, public BasePageProvider
|
||||||
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit InstancePageProvider(InstancePtr parent) { inst = parent; }
|
explicit InstancePageProvider(InstancePtr parent)
|
||||||
|
|
||||||
virtual ~InstancePageProvider(){};
|
|
||||||
virtual QList<BasePage*> getPages() override
|
|
||||||
{
|
{
|
||||||
QList<BasePage*> values;
|
inst = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~InstancePageProvider() {};
|
||||||
|
virtual QList<BasePage *> getPages() override
|
||||||
|
{
|
||||||
|
QList<BasePage *> values;
|
||||||
values.append(new LogPage(inst));
|
values.append(new LogPage(inst));
|
||||||
std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst);
|
std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst);
|
||||||
values.append(new VersionPage(onesix.get()));
|
values.append(new VersionPage(onesix.get()));
|
||||||
values.append(ManagedPackPage::createPage(onesix.get()));
|
|
||||||
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList());
|
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList());
|
||||||
modsPage->setFilter("%1 (*.zip *.jar *.litemod *.nilmod)");
|
modsPage->setFilter("%1 (*.zip *.jar *.litemod)");
|
||||||
values.append(modsPage);
|
values.append(modsPage);
|
||||||
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList()));
|
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList()));
|
||||||
values.append(new NilModFolderPage(onesix.get(), onesix->nilModList()));
|
values.append(new ResourcePackPage(onesix.get()));
|
||||||
values.append(new ResourcePackPage(onesix.get(), onesix->resourcePackList()));
|
values.append(new TexturePackPage(onesix.get()));
|
||||||
values.append(new TexturePackPage(onesix.get(), onesix->texturePackList()));
|
values.append(new ShaderPackPage(onesix.get()));
|
||||||
values.append(new ShaderPackPage(onesix.get(), onesix->shaderPackList()));
|
|
||||||
values.append(new NotesPage(onesix.get()));
|
values.append(new NotesPage(onesix.get()));
|
||||||
values.append(new WorldListPage(onesix.get(), onesix->worldList()));
|
values.append(new WorldListPage(onesix.get(), onesix->worldList()));
|
||||||
values.append(new ServersPage(onesix));
|
values.append(new ServersPage(onesix));
|
||||||
@ -45,14 +47,18 @@ class InstancePageProvider : protected QObject, public BasePageProvider {
|
|||||||
values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots")));
|
values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots")));
|
||||||
values.append(new InstanceSettingsPage(onesix.get()));
|
values.append(new InstanceSettingsPage(onesix.get()));
|
||||||
auto logMatcher = inst->getLogFileMatcher();
|
auto logMatcher = inst->getLogFileMatcher();
|
||||||
if (logMatcher) {
|
if(logMatcher)
|
||||||
|
{
|
||||||
values.append(new OtherLogsPage(inst->getLogFileRoot(), logMatcher));
|
values.append(new OtherLogsPage(inst->getLogFileRoot(), logMatcher));
|
||||||
}
|
}
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual QString dialogTitle() override { return tr("Edit Instance (%1)").arg(inst->name()); }
|
virtual QString dialogTitle() override
|
||||||
|
{
|
||||||
protected:
|
return tr("Edit Instance (%1)").arg(inst->name());
|
||||||
|
}
|
||||||
|
protected:
|
||||||
InstancePtr inst;
|
InstancePtr inst;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,78 +1,9 @@
|
|||||||
#include "InstanceTask.h"
|
#include "InstanceTask.h"
|
||||||
|
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
InstanceTask::InstanceTask()
|
||||||
|
|
||||||
InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name)
|
|
||||||
{
|
{
|
||||||
auto dialog =
|
|
||||||
CustomMessageBox::selectable(parent, QObject::tr("Change instance name"),
|
|
||||||
QObject::tr("The instance's name seems to include the old version. Would you like to update it?\n\n"
|
|
||||||
"Old name: %1\n"
|
|
||||||
"New name: %2")
|
|
||||||
.arg(old_name, new_name),
|
|
||||||
QMessageBox::Question, QMessageBox::No | QMessageBox::Yes);
|
|
||||||
auto result = dialog->exec();
|
|
||||||
|
|
||||||
if (result == QMessageBox::Yes)
|
|
||||||
return InstanceNameChange::ShouldChange;
|
|
||||||
return InstanceNameChange::ShouldKeep;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name)
|
InstanceTask::~InstanceTask()
|
||||||
{
|
{
|
||||||
auto info = CustomMessageBox::selectable(
|
|
||||||
parent, QObject::tr("Similar modpack was found!"),
|
|
||||||
QObject::tr(
|
|
||||||
"One or more of your instances are from this same modpack%1. Do you want to create a "
|
|
||||||
"separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before "
|
|
||||||
"updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).")
|
|
||||||
.arg(original_version_name),
|
|
||||||
QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort);
|
|
||||||
info->setButtonText(QMessageBox::Ok, QObject::tr("Update existing instance"));
|
|
||||||
info->setButtonText(QMessageBox::Abort, QObject::tr("Create new instance"));
|
|
||||||
info->setButtonText(QMessageBox::Reset, QObject::tr("Cancel"));
|
|
||||||
|
|
||||||
info->exec();
|
|
||||||
|
|
||||||
if (info->clickedButton() == info->button(QMessageBox::Ok))
|
|
||||||
return ShouldUpdate::Update;
|
|
||||||
if (info->clickedButton() == info->button(QMessageBox::Abort))
|
|
||||||
return ShouldUpdate::SkipUpdating;
|
|
||||||
return ShouldUpdate::Cancel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString InstanceName::name() const
|
|
||||||
{
|
|
||||||
if (!m_modified_name.isEmpty())
|
|
||||||
return modifiedName();
|
|
||||||
if (!m_original_version.isEmpty())
|
|
||||||
return QString("%1 %2").arg(m_original_name, m_original_version);
|
|
||||||
|
|
||||||
return m_original_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString InstanceName::originalName() const
|
|
||||||
{
|
|
||||||
return m_original_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString InstanceName::modifiedName() const
|
|
||||||
{
|
|
||||||
if (!m_modified_name.isEmpty())
|
|
||||||
return m_modified_name;
|
|
||||||
return m_original_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString InstanceName::version() const
|
|
||||||
{
|
|
||||||
return m_original_version;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceName::setName(InstanceName& other)
|
|
||||||
{
|
|
||||||
m_original_name = other.m_original_name;
|
|
||||||
m_original_version = other.m_original_version;
|
|
||||||
m_modified_name = other.m_modified_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
InstanceTask::InstanceTask() : Task(), InstanceName() {}
|
|
||||||
|
@ -1,72 +1,52 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "settings/SettingsObject.h"
|
|
||||||
#include "tasks/Task.h"
|
#include "tasks/Task.h"
|
||||||
|
#include "settings/SettingsObject.h"
|
||||||
|
|
||||||
/* Helpers */
|
class InstanceTask : public Task
|
||||||
enum class InstanceNameChange { ShouldChange, ShouldKeep };
|
{
|
||||||
[[nodiscard]] InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name);
|
|
||||||
enum class ShouldUpdate { Update, SkipUpdating, Cancel };
|
|
||||||
[[nodiscard]] ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name);
|
|
||||||
|
|
||||||
struct InstanceName {
|
|
||||||
public:
|
|
||||||
InstanceName() = default;
|
|
||||||
InstanceName(QString name, QString version) : m_original_name(std::move(name)), m_original_version(std::move(version)) {}
|
|
||||||
|
|
||||||
[[nodiscard]] QString modifiedName() const;
|
|
||||||
[[nodiscard]] QString originalName() const;
|
|
||||||
[[nodiscard]] QString name() const;
|
|
||||||
[[nodiscard]] QString version() const;
|
|
||||||
|
|
||||||
void setName(QString name) { m_modified_name = name; }
|
|
||||||
void setName(InstanceName& other);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
QString m_original_name;
|
|
||||||
QString m_original_version;
|
|
||||||
|
|
||||||
QString m_modified_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
class InstanceTask : public Task, public InstanceName {
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
InstanceTask();
|
explicit InstanceTask();
|
||||||
~InstanceTask() override = default;
|
virtual ~InstanceTask();
|
||||||
|
|
||||||
void setParentSettings(SettingsObjectPtr settings) { m_globalSettings = settings; }
|
void setParentSettings(SettingsObjectPtr settings)
|
||||||
|
|
||||||
void setStagingPath(const QString& stagingPath) { m_stagingPath = stagingPath; }
|
|
||||||
|
|
||||||
void setIcon(const QString& icon) { m_instIcon = icon; }
|
|
||||||
|
|
||||||
void setGroup(const QString& group) { m_instGroup = group; }
|
|
||||||
QString group() const { return m_instGroup; }
|
|
||||||
|
|
||||||
[[nodiscard]] bool shouldConfirmUpdate() const { return m_confirm_update; }
|
|
||||||
void setConfirmUpdate(bool confirm) { m_confirm_update = confirm; }
|
|
||||||
|
|
||||||
bool shouldOverride() const { return m_override_existing; }
|
|
||||||
|
|
||||||
[[nodiscard]] QString originalInstanceID() const { return m_original_instance_id; };
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void setOverride(bool override, QString instance_id_to_override = {})
|
|
||||||
{
|
{
|
||||||
m_override_existing = override;
|
m_globalSettings = settings;
|
||||||
if (!instance_id_to_override.isEmpty())
|
|
||||||
m_original_instance_id = instance_id_to_override;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected: /* data */
|
void setStagingPath(const QString &stagingPath)
|
||||||
|
{
|
||||||
|
m_stagingPath = stagingPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setName(const QString &name)
|
||||||
|
{
|
||||||
|
m_instName = name;
|
||||||
|
}
|
||||||
|
QString name() const
|
||||||
|
{
|
||||||
|
return m_instName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setIcon(const QString &icon)
|
||||||
|
{
|
||||||
|
m_instIcon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setGroup(const QString &group)
|
||||||
|
{
|
||||||
|
m_instGroup = group;
|
||||||
|
}
|
||||||
|
QString group() const
|
||||||
|
{
|
||||||
|
return m_instGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected: /* data */
|
||||||
SettingsObjectPtr m_globalSettings;
|
SettingsObjectPtr m_globalSettings;
|
||||||
|
QString m_instName;
|
||||||
QString m_instIcon;
|
QString m_instIcon;
|
||||||
QString m_instGroup;
|
QString m_instGroup;
|
||||||
QString m_stagingPath;
|
QString m_stagingPath;
|
||||||
|
|
||||||
bool m_override_existing = false;
|
|
||||||
bool m_confirm_update = true;
|
|
||||||
|
|
||||||
QString m_original_instance_id;
|
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* PolyMC - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -36,42 +36,46 @@
|
|||||||
#include "JavaCommon.h"
|
#include "JavaCommon.h"
|
||||||
#include "java/JavaUtils.h"
|
#include "java/JavaUtils.h"
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
|
#include <MMCStrings.h>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget* parent)
|
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
|
||||||
{
|
{
|
||||||
if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegularExpression("-Xm[sx]")) || jvmargs.contains("-XX-MaxHeapSize") ||
|
if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegularExpression("-Xm[sx]"))
|
||||||
jvmargs.contains("-XX:InitialHeapSize")) {
|
|| jvmargs.contains("-XX-MaxHeapSize") || jvmargs.contains("-XX:InitialHeapSize"))
|
||||||
|
{
|
||||||
auto warnStr = QObject::tr(
|
auto warnStr = QObject::tr(
|
||||||
"You tried to manually set a JVM memory option (using \"-XX:PermSize\", \"-XX-MaxHeapSize\", \"-XX:InitialHeapSize\", \"-Xmx\" "
|
"You tried to manually set a JVM memory option (using \"-XX:PermSize\", \"-XX-MaxHeapSize\", \"-XX:InitialHeapSize\", \"-Xmx\" or \"-Xms\").\n"
|
||||||
"or \"-Xms\").\n"
|
|
||||||
"There are dedicated boxes for these in the settings (Java tab, in the Memory group at the top).\n"
|
"There are dedicated boxes for these in the settings (Java tab, in the Memory group at the top).\n"
|
||||||
"This message will be displayed until you remove them from the JVM arguments.");
|
"This message will be displayed until you remove them from the JVM arguments.");
|
||||||
CustomMessageBox::selectable(parent, QObject::tr("JVM arguments warning"), warnStr, QMessageBox::Warning)->exec();
|
CustomMessageBox::selectable(
|
||||||
|
parent, QObject::tr("JVM arguments warning"),
|
||||||
|
warnStr,
|
||||||
|
QMessageBox::Warning)->exec();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// block lunacy with passing required version to the JVM
|
// block lunacy with passing required version to the JVM
|
||||||
if (jvmargs.contains(QRegularExpression("-version:.*"))) {
|
if (jvmargs.contains(QRegularExpression("-version:.*"))) {
|
||||||
auto warnStr = QObject::tr(
|
auto warnStr = QObject::tr(
|
||||||
"You tried to pass required Java version argument to the JVM (using \"-version:xxx\"). This is not safe and will not be "
|
"You tried to pass required Java version argument to the JVM (using \"-version:xxx\"). This is not safe and will not be allowed.\n"
|
||||||
"allowed.\n"
|
|
||||||
"This message will be displayed until you remove this from the JVM arguments.");
|
"This message will be displayed until you remove this from the JVM arguments.");
|
||||||
CustomMessageBox::selectable(parent, QObject::tr("JVM arguments warning"), warnStr, QMessageBox::Warning)->exec();
|
CustomMessageBox::selectable(
|
||||||
|
parent, QObject::tr("JVM arguments warning"),
|
||||||
|
warnStr,
|
||||||
|
QMessageBox::Warning)->exec();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JavaCommon::javaWasOk(QWidget* parent, const JavaCheckResult& result)
|
void JavaCommon::javaWasOk(QWidget *parent, JavaCheckResult result)
|
||||||
{
|
{
|
||||||
QString text;
|
QString text;
|
||||||
text += QObject::tr(
|
text += QObject::tr("Java test succeeded!<br />Platform reported: %1<br />Java version "
|
||||||
"Java test succeeded!<br />Platform reported: %1<br />Java version "
|
"reported: %2<br />Java vendor "
|
||||||
"reported: %2<br />Java vendor "
|
"reported: %3<br />").arg(result.realPlatform, result.javaVersion.toString(), result.javaVendor);
|
||||||
"reported: %3<br />")
|
if (result.errorLog.size())
|
||||||
.arg(result.realPlatform, result.javaVersion.toString(), result.javaVendor);
|
{
|
||||||
if (result.errorLog.size()) {
|
|
||||||
auto htmlError = result.errorLog;
|
auto htmlError = result.errorLog;
|
||||||
htmlError.replace('\n', "<br />");
|
htmlError.replace('\n', "<br />");
|
||||||
text += QObject::tr("<br />Warnings:<br /><font color=\"orange\">%1</font>").arg(htmlError);
|
text += QObject::tr("<br />Warnings:<br /><font color=\"orange\">%1</font>").arg(htmlError);
|
||||||
@ -79,7 +83,7 @@ void JavaCommon::javaWasOk(QWidget* parent, const JavaCheckResult& result)
|
|||||||
CustomMessageBox::selectable(parent, QObject::tr("Java test success"), text, QMessageBox::Information)->show();
|
CustomMessageBox::selectable(parent, QObject::tr("Java test success"), text, QMessageBox::Information)->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JavaCommon::javaArgsWereBad(QWidget* parent, const JavaCheckResult& result)
|
void JavaCommon::javaArgsWereBad(QWidget *parent, JavaCheckResult result)
|
||||||
{
|
{
|
||||||
auto htmlError = result.errorLog;
|
auto htmlError = result.errorLog;
|
||||||
QString text;
|
QString text;
|
||||||
@ -89,7 +93,7 @@ void JavaCommon::javaArgsWereBad(QWidget* parent, const JavaCheckResult& result)
|
|||||||
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
|
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JavaCommon::javaBinaryWasBad(QWidget* parent, const JavaCheckResult& result)
|
void JavaCommon::javaBinaryWasBad(QWidget *parent, JavaCheckResult result)
|
||||||
{
|
{
|
||||||
QString text;
|
QString text;
|
||||||
text += QObject::tr(
|
text += QObject::tr(
|
||||||
@ -98,7 +102,7 @@ void JavaCommon::javaBinaryWasBad(QWidget* parent, const JavaCheckResult& result
|
|||||||
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
|
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JavaCommon::javaCheckNotFound(QWidget* parent)
|
void JavaCommon::javaCheckNotFound(QWidget *parent)
|
||||||
{
|
{
|
||||||
QString text;
|
QString text;
|
||||||
text += QObject::tr("Java checker library could not be found. Please check your installation.");
|
text += QObject::tr("Java checker library could not be found. Please check your installation.");
|
||||||
@ -107,7 +111,8 @@ void JavaCommon::javaCheckNotFound(QWidget* parent)
|
|||||||
|
|
||||||
void JavaCommon::TestCheck::run()
|
void JavaCommon::TestCheck::run()
|
||||||
{
|
{
|
||||||
if (!JavaCommon::checkJVMArgs(m_args, m_parent)) {
|
if (!JavaCommon::checkJVMArgs(m_args, m_parent))
|
||||||
|
{
|
||||||
emit finished();
|
emit finished();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -117,25 +122,29 @@ void JavaCommon::TestCheck::run()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
checker.reset(new JavaChecker());
|
checker.reset(new JavaChecker());
|
||||||
connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinished);
|
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
|
||||||
|
SLOT(checkFinished(JavaCheckResult)));
|
||||||
checker->m_path = m_path;
|
checker->m_path = m_path;
|
||||||
checker->performCheck();
|
checker->performCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JavaCommon::TestCheck::checkFinished(JavaCheckResult result)
|
void JavaCommon::TestCheck::checkFinished(JavaCheckResult result)
|
||||||
{
|
{
|
||||||
if (result.validity != JavaCheckResult::Validity::Valid) {
|
if (result.validity != JavaCheckResult::Validity::Valid)
|
||||||
|
{
|
||||||
javaBinaryWasBad(m_parent, result);
|
javaBinaryWasBad(m_parent, result);
|
||||||
emit finished();
|
emit finished();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
checker.reset(new JavaChecker());
|
checker.reset(new JavaChecker());
|
||||||
connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinishedWithArgs);
|
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
|
||||||
|
SLOT(checkFinishedWithArgs(JavaCheckResult)));
|
||||||
checker->m_path = m_path;
|
checker->m_path = m_path;
|
||||||
checker->m_args = m_args;
|
checker->m_args = m_args;
|
||||||
checker->m_minMem = m_minMem;
|
checker->m_minMem = m_minMem;
|
||||||
checker->m_maxMem = m_maxMem;
|
checker->m_maxMem = m_maxMem;
|
||||||
if (result.javaVersion.requiresPermGen()) {
|
if (result.javaVersion.requiresPermGen())
|
||||||
|
{
|
||||||
checker->m_permGen = m_permGen;
|
checker->m_permGen = m_permGen;
|
||||||
}
|
}
|
||||||
checker->performCheck();
|
checker->performCheck();
|
||||||
@ -143,7 +152,8 @@ void JavaCommon::TestCheck::checkFinished(JavaCheckResult result)
|
|||||||
|
|
||||||
void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result)
|
void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result)
|
||||||
{
|
{
|
||||||
if (result.validity == JavaCheckResult::Validity::Valid) {
|
if (result.validity == JavaCheckResult::Validity::Valid)
|
||||||
|
{
|
||||||
javaWasOk(m_parent, result);
|
javaWasOk(m_parent, result);
|
||||||
emit finished();
|
emit finished();
|
||||||
return;
|
return;
|
||||||
@ -151,3 +161,4 @@ void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result)
|
|||||||
javaArgsWereBad(m_parent, result);
|
javaArgsWereBad(m_parent, result);
|
||||||
emit finished();
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,42 +6,45 @@ class QWidget;
|
|||||||
/**
|
/**
|
||||||
* Common UI bits for the java pages to use.
|
* Common UI bits for the java pages to use.
|
||||||
*/
|
*/
|
||||||
namespace JavaCommon {
|
namespace JavaCommon
|
||||||
bool checkJVMArgs(QString args, QWidget* parent);
|
{
|
||||||
|
bool checkJVMArgs(QString args, QWidget *parent);
|
||||||
|
|
||||||
// Show a dialog saying that the Java binary was usable
|
// Show a dialog saying that the Java binary was usable
|
||||||
void javaWasOk(QWidget* parent, const JavaCheckResult& result);
|
void javaWasOk(QWidget *parent, JavaCheckResult result);
|
||||||
// Show a dialog saying that the Java binary was not usable because of bad options
|
// Show a dialog saying that the Java binary was not usable because of bad options
|
||||||
void javaArgsWereBad(QWidget* parent, const JavaCheckResult& result);
|
void javaArgsWereBad(QWidget *parent, JavaCheckResult result);
|
||||||
// Show a dialog saying that the Java binary was not usable
|
// Show a dialog saying that the Java binary was not usable
|
||||||
void javaBinaryWasBad(QWidget* parent, const JavaCheckResult& result);
|
void javaBinaryWasBad(QWidget *parent, JavaCheckResult result);
|
||||||
// Show a dialog if we couldn't find Java Checker
|
// Show a dialog if we couldn't find Java Checker
|
||||||
void javaCheckNotFound(QWidget* parent);
|
void javaCheckNotFound(QWidget *parent);
|
||||||
|
|
||||||
class TestCheck : public QObject {
|
class TestCheck : public QObject
|
||||||
Q_OBJECT
|
{
|
||||||
public:
|
Q_OBJECT
|
||||||
TestCheck(QWidget* parent, QString path, QString args, int minMem, int maxMem, int permGen)
|
public:
|
||||||
: m_parent(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen)
|
TestCheck(QWidget *parent, QString path, QString args, int minMem, int maxMem, int permGen)
|
||||||
{}
|
:m_parent(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen)
|
||||||
virtual ~TestCheck(){};
|
{
|
||||||
|
}
|
||||||
|
virtual ~TestCheck() {};
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void finished();
|
void finished();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void checkFinished(JavaCheckResult result);
|
void checkFinished(JavaCheckResult result);
|
||||||
void checkFinishedWithArgs(JavaCheckResult result);
|
void checkFinishedWithArgs(JavaCheckResult result);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<JavaChecker> checker;
|
std::shared_ptr<JavaChecker> checker;
|
||||||
QWidget* m_parent = nullptr;
|
QWidget *m_parent = nullptr;
|
||||||
QString m_path;
|
QString m_path;
|
||||||
QString m_args;
|
QString m_args;
|
||||||
int m_minMem = 0;
|
int m_minMem = 0;
|
||||||
int m_maxMem = 0;
|
int m_maxMem = 0;
|
||||||
int m_permGen = 64;
|
int m_permGen = 64;
|
||||||
};
|
};
|
||||||
} // namespace JavaCommon
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* PolyMC - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -37,246 +37,257 @@
|
|||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
namespace Json {
|
namespace Json
|
||||||
void write(const QJsonDocument& doc, const QString& filename)
|
{
|
||||||
|
void write(const QJsonDocument &doc, const QString &filename)
|
||||||
{
|
{
|
||||||
FS::write(filename, doc.toJson());
|
FS::write(filename, doc.toJson());
|
||||||
}
|
}
|
||||||
void write(const QJsonObject& object, const QString& filename)
|
void write(const QJsonObject &object, const QString &filename)
|
||||||
{
|
{
|
||||||
write(QJsonDocument(object), filename);
|
write(QJsonDocument(object), filename);
|
||||||
}
|
}
|
||||||
void write(const QJsonArray& array, const QString& filename)
|
void write(const QJsonArray &array, const QString &filename)
|
||||||
{
|
{
|
||||||
write(QJsonDocument(array), filename);
|
write(QJsonDocument(array), filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray toText(const QJsonObject& obj)
|
QByteArray toText(const QJsonObject &obj)
|
||||||
{
|
{
|
||||||
return QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
return QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
||||||
}
|
}
|
||||||
QByteArray toText(const QJsonArray& array)
|
QByteArray toText(const QJsonArray &array)
|
||||||
{
|
{
|
||||||
return QJsonDocument(array).toJson(QJsonDocument::Compact);
|
return QJsonDocument(array).toJson(QJsonDocument::Compact);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isBinaryJson(const QByteArray& data)
|
static bool isBinaryJson(const QByteArray &data)
|
||||||
{
|
{
|
||||||
decltype(QJsonDocument::BinaryFormatTag) tag = QJsonDocument::BinaryFormatTag;
|
decltype(QJsonDocument::BinaryFormatTag) tag = QJsonDocument::BinaryFormatTag;
|
||||||
return memcmp(data.constData(), &tag, sizeof(QJsonDocument::BinaryFormatTag)) == 0;
|
return memcmp(data.constData(), &tag, sizeof(QJsonDocument::BinaryFormatTag)) == 0;
|
||||||
}
|
}
|
||||||
QJsonDocument requireDocument(const QByteArray& data, const QString& what)
|
QJsonDocument requireDocument(const QByteArray &data, const QString &what)
|
||||||
{
|
{
|
||||||
if (isBinaryJson(data)) {
|
if (isBinaryJson(data))
|
||||||
|
{
|
||||||
// FIXME: Is this needed?
|
// FIXME: Is this needed?
|
||||||
throw JsonException(what + ": Invalid JSON. Binary JSON unsupported");
|
throw JsonException(what + ": Invalid JSON. Binary JSON unsupported");
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
QJsonParseError error;
|
QJsonParseError error;
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(data, &error);
|
QJsonDocument doc = QJsonDocument::fromJson(data, &error);
|
||||||
if (error.error != QJsonParseError::NoError) {
|
if (error.error != QJsonParseError::NoError)
|
||||||
|
{
|
||||||
throw JsonException(what + ": Error parsing JSON: " + error.errorString());
|
throw JsonException(what + ": Error parsing JSON: " + error.errorString());
|
||||||
}
|
}
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QJsonDocument requireDocument(const QString& filename, const QString& what)
|
QJsonDocument requireDocument(const QString &filename, const QString &what)
|
||||||
{
|
{
|
||||||
return requireDocument(FS::read(filename), what);
|
return requireDocument(FS::read(filename), what);
|
||||||
}
|
}
|
||||||
QJsonObject requireObject(const QJsonDocument& doc, const QString& what)
|
QJsonObject requireObject(const QJsonDocument &doc, const QString &what)
|
||||||
{
|
{
|
||||||
if (!doc.isObject()) {
|
if (!doc.isObject())
|
||||||
|
{
|
||||||
throw JsonException(what + " is not an object");
|
throw JsonException(what + " is not an object");
|
||||||
}
|
}
|
||||||
return doc.object();
|
return doc.object();
|
||||||
}
|
}
|
||||||
QJsonArray requireArray(const QJsonDocument& doc, const QString& what)
|
QJsonArray requireArray(const QJsonDocument &doc, const QString &what)
|
||||||
{
|
{
|
||||||
if (!doc.isArray()) {
|
if (!doc.isArray())
|
||||||
|
{
|
||||||
throw JsonException(what + " is not an array");
|
throw JsonException(what + " is not an array");
|
||||||
}
|
}
|
||||||
return doc.array();
|
return doc.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeString(QJsonObject& to, const QString& key, const QString& value)
|
void writeString(QJsonObject &to, const QString &key, const QString &value)
|
||||||
{
|
{
|
||||||
if (!value.isEmpty()) {
|
if (!value.isEmpty())
|
||||||
|
{
|
||||||
to.insert(key, value);
|
to.insert(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeStringList(QJsonObject& to, const QString& key, const QStringList& values)
|
void writeStringList(QJsonObject &to, const QString &key, const QStringList &values)
|
||||||
{
|
{
|
||||||
if (!values.isEmpty()) {
|
if (!values.isEmpty())
|
||||||
|
{
|
||||||
QJsonArray array;
|
QJsonArray array;
|
||||||
for (auto value : values) {
|
for(auto value: values)
|
||||||
|
{
|
||||||
array.append(value);
|
array.append(value);
|
||||||
}
|
}
|
||||||
to.insert(key, array);
|
to.insert(key, array);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template<>
|
||||||
QJsonValue toJson<QUrl>(const QUrl& url)
|
QJsonValue toJson<QUrl>(const QUrl &url)
|
||||||
{
|
{
|
||||||
return QJsonValue(url.toString(QUrl::FullyEncoded));
|
return QJsonValue(url.toString(QUrl::FullyEncoded));
|
||||||
}
|
}
|
||||||
template <>
|
template<>
|
||||||
QJsonValue toJson<QByteArray>(const QByteArray& data)
|
QJsonValue toJson<QByteArray>(const QByteArray &data)
|
||||||
{
|
{
|
||||||
return QJsonValue(QString::fromLatin1(data.toHex()));
|
return QJsonValue(QString::fromLatin1(data.toHex()));
|
||||||
}
|
}
|
||||||
template <>
|
template<>
|
||||||
QJsonValue toJson<QDateTime>(const QDateTime& datetime)
|
QJsonValue toJson<QDateTime>(const QDateTime &datetime)
|
||||||
{
|
{
|
||||||
return QJsonValue(datetime.toString(Qt::ISODate));
|
return QJsonValue(datetime.toString(Qt::ISODate));
|
||||||
}
|
}
|
||||||
template <>
|
template<>
|
||||||
QJsonValue toJson<QDir>(const QDir& dir)
|
QJsonValue toJson<QDir>(const QDir &dir)
|
||||||
{
|
{
|
||||||
return QDir::current().relativeFilePath(dir.absolutePath());
|
return QDir::current().relativeFilePath(dir.absolutePath());
|
||||||
}
|
}
|
||||||
template <>
|
template<>
|
||||||
QJsonValue toJson<QUuid>(const QUuid& uuid)
|
QJsonValue toJson<QUuid>(const QUuid &uuid)
|
||||||
{
|
{
|
||||||
return uuid.toString();
|
return uuid.toString();
|
||||||
}
|
}
|
||||||
template <>
|
template<>
|
||||||
QJsonValue toJson<QVariant>(const QVariant& variant)
|
QJsonValue toJson<QVariant>(const QVariant &variant)
|
||||||
{
|
{
|
||||||
return QJsonValue::fromVariant(variant);
|
return QJsonValue::fromVariant(variant);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
|
||||||
QByteArray requireIsType<QByteArray>(const QJsonValue& value, const QString& what)
|
template<> QByteArray requireIsType<QByteArray>(const QJsonValue &value, const QString &what)
|
||||||
{
|
{
|
||||||
const QString string = ensureIsType<QString>(value, what);
|
const QString string = ensureIsType<QString>(value, what);
|
||||||
// ensure that the string can be safely cast to Latin1
|
// ensure that the string can be safely cast to Latin1
|
||||||
if (string != QString::fromLatin1(string.toLatin1())) {
|
if (string != QString::fromLatin1(string.toLatin1()))
|
||||||
|
{
|
||||||
throw JsonException(what + " is not encodable as Latin1");
|
throw JsonException(what + " is not encodable as Latin1");
|
||||||
}
|
}
|
||||||
return QByteArray::fromHex(string.toLatin1());
|
return QByteArray::fromHex(string.toLatin1());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template<> QJsonArray requireIsType<QJsonArray>(const QJsonValue &value, const QString &what)
|
||||||
QJsonArray requireIsType<QJsonArray>(const QJsonValue& value, const QString& what)
|
|
||||||
{
|
{
|
||||||
if (!value.isArray()) {
|
if (!value.isArray())
|
||||||
|
{
|
||||||
throw JsonException(what + " is not an array");
|
throw JsonException(what + " is not an array");
|
||||||
}
|
}
|
||||||
return value.toArray();
|
return value.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
|
||||||
QString requireIsType<QString>(const QJsonValue& value, const QString& what)
|
template<> QString requireIsType<QString>(const QJsonValue &value, const QString &what)
|
||||||
{
|
{
|
||||||
if (!value.isString()) {
|
if (!value.isString())
|
||||||
|
{
|
||||||
throw JsonException(what + " is not a string");
|
throw JsonException(what + " is not a string");
|
||||||
}
|
}
|
||||||
return value.toString();
|
return value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template<> bool requireIsType<bool>(const QJsonValue &value, const QString &what)
|
||||||
bool requireIsType<bool>(const QJsonValue& value, const QString& what)
|
|
||||||
{
|
{
|
||||||
if (!value.isBool()) {
|
if (!value.isBool())
|
||||||
|
{
|
||||||
throw JsonException(what + " is not a bool");
|
throw JsonException(what + " is not a bool");
|
||||||
}
|
}
|
||||||
return value.toBool();
|
return value.toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template<> double requireIsType<double>(const QJsonValue &value, const QString &what)
|
||||||
double requireIsType<double>(const QJsonValue& value, const QString& what)
|
|
||||||
{
|
{
|
||||||
if (!value.isDouble()) {
|
if (!value.isDouble())
|
||||||
|
{
|
||||||
throw JsonException(what + " is not a double");
|
throw JsonException(what + " is not a double");
|
||||||
}
|
}
|
||||||
return value.toDouble();
|
return value.toDouble();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template<> int requireIsType<int>(const QJsonValue &value, const QString &what)
|
||||||
int requireIsType<int>(const QJsonValue& value, const QString& what)
|
|
||||||
{
|
{
|
||||||
const double doubl = requireIsType<double>(value, what);
|
const double doubl = requireIsType<double>(value, what);
|
||||||
if (fmod(doubl, 1) != 0) {
|
if (fmod(doubl, 1) != 0)
|
||||||
|
{
|
||||||
throw JsonException(what + " is not an integer");
|
throw JsonException(what + " is not an integer");
|
||||||
}
|
}
|
||||||
return int(doubl);
|
return int(doubl);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template<> QDateTime requireIsType<QDateTime>(const QJsonValue &value, const QString &what)
|
||||||
QDateTime requireIsType<QDateTime>(const QJsonValue& value, const QString& what)
|
|
||||||
{
|
{
|
||||||
const QString string = requireIsType<QString>(value, what);
|
const QString string = requireIsType<QString>(value, what);
|
||||||
const QDateTime datetime = QDateTime::fromString(string, Qt::ISODate);
|
const QDateTime datetime = QDateTime::fromString(string, Qt::ISODate);
|
||||||
if (!datetime.isValid()) {
|
if (!datetime.isValid())
|
||||||
|
{
|
||||||
throw JsonException(what + " is not a ISO formatted date/time value");
|
throw JsonException(what + " is not a ISO formatted date/time value");
|
||||||
}
|
}
|
||||||
return datetime;
|
return datetime;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template<> QUrl requireIsType<QUrl>(const QJsonValue &value, const QString &what)
|
||||||
QUrl requireIsType<QUrl>(const QJsonValue& value, const QString& what)
|
|
||||||
{
|
{
|
||||||
const QString string = ensureIsType<QString>(value, what);
|
const QString string = ensureIsType<QString>(value, what);
|
||||||
if (string.isEmpty()) {
|
if (string.isEmpty())
|
||||||
|
{
|
||||||
return QUrl();
|
return QUrl();
|
||||||
}
|
}
|
||||||
const QUrl url = QUrl(string, QUrl::StrictMode);
|
const QUrl url = QUrl(string, QUrl::StrictMode);
|
||||||
if (!url.isValid()) {
|
if (!url.isValid())
|
||||||
|
{
|
||||||
throw JsonException(what + " is not a correctly formatted URL");
|
throw JsonException(what + " is not a correctly formatted URL");
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template<> QDir requireIsType<QDir>(const QJsonValue &value, const QString &what)
|
||||||
QDir requireIsType<QDir>(const QJsonValue& value, const QString& what)
|
|
||||||
{
|
{
|
||||||
const QString string = requireIsType<QString>(value, what);
|
const QString string = requireIsType<QString>(value, what);
|
||||||
// FIXME: does not handle invalid characters!
|
// FIXME: does not handle invalid characters!
|
||||||
return QDir::current().absoluteFilePath(string);
|
return QDir::current().absoluteFilePath(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template<> QUuid requireIsType<QUuid>(const QJsonValue &value, const QString &what)
|
||||||
QUuid requireIsType<QUuid>(const QJsonValue& value, const QString& what)
|
|
||||||
{
|
{
|
||||||
const QString string = requireIsType<QString>(value, what);
|
const QString string = requireIsType<QString>(value, what);
|
||||||
const QUuid uuid = QUuid(string);
|
const QUuid uuid = QUuid(string);
|
||||||
if (uuid.toString() != string) // converts back => valid
|
if (uuid.toString() != string) // converts back => valid
|
||||||
{
|
{
|
||||||
throw JsonException(what + " is not a valid UUID");
|
throw JsonException(what + " is not a valid UUID");
|
||||||
}
|
}
|
||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template<> QJsonObject requireIsType<QJsonObject>(const QJsonValue &value, const QString &what)
|
||||||
QJsonObject requireIsType<QJsonObject>(const QJsonValue& value, const QString& what)
|
|
||||||
{
|
{
|
||||||
if (!value.isObject()) {
|
if (!value.isObject())
|
||||||
|
{
|
||||||
throw JsonException(what + " is not an object");
|
throw JsonException(what + " is not an object");
|
||||||
}
|
}
|
||||||
return value.toObject();
|
return value.toObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template<> QVariant requireIsType<QVariant>(const QJsonValue &value, const QString &what)
|
||||||
QVariant requireIsType<QVariant>(const QJsonValue& value, const QString& what)
|
|
||||||
{
|
{
|
||||||
if (value.isNull() || value.isUndefined()) {
|
if (value.isNull() || value.isUndefined())
|
||||||
|
{
|
||||||
throw JsonException(what + " is null or undefined");
|
throw JsonException(what + " is null or undefined");
|
||||||
}
|
}
|
||||||
return value.toVariant();
|
return value.toVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template<> QJsonValue requireIsType<QJsonValue>(const QJsonValue &value, const QString &what)
|
||||||
QJsonValue requireIsType<QJsonValue>(const QJsonValue& value, const QString& what)
|
|
||||||
{
|
{
|
||||||
if (value.isNull() || value.isUndefined()) {
|
if (value.isNull() || value.isUndefined())
|
||||||
|
{
|
||||||
throw JsonException(what + " is null or undefined");
|
throw JsonException(what + " is null or undefined");
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Json
|
}
|
||||||
|
202
launcher/Json.h
202
launcher/Json.h
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* PolyMC - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -35,71 +35,74 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QJsonArray>
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonArray>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
#include <QDateTime>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <QDir>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "Exception.h"
|
#include "Exception.h"
|
||||||
|
|
||||||
namespace Json {
|
namespace Json
|
||||||
class JsonException : public ::Exception {
|
{
|
||||||
public:
|
class JsonException : public ::Exception
|
||||||
JsonException(const QString& message) : Exception(message) {}
|
{
|
||||||
|
public:
|
||||||
|
JsonException(const QString &message) : Exception(message) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @throw FileSystemException
|
/// @throw FileSystemException
|
||||||
void write(const QJsonDocument& doc, const QString& filename);
|
void write(const QJsonDocument &doc, const QString &filename);
|
||||||
/// @throw FileSystemException
|
/// @throw FileSystemException
|
||||||
void write(const QJsonObject& object, const QString& filename);
|
void write(const QJsonObject &object, const QString &filename);
|
||||||
/// @throw FileSystemException
|
/// @throw FileSystemException
|
||||||
void write(const QJsonArray& array, const QString& filename);
|
void write(const QJsonArray &array, const QString &filename);
|
||||||
|
|
||||||
QByteArray toText(const QJsonObject& obj);
|
QByteArray toText(const QJsonObject &obj);
|
||||||
QByteArray toText(const QJsonArray& array);
|
QByteArray toText(const QJsonArray &array);
|
||||||
|
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
QJsonDocument requireDocument(const QByteArray& data, const QString& what = "Document");
|
QJsonDocument requireDocument(const QByteArray &data, const QString &what = "Document");
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
QJsonDocument requireDocument(const QString& filename, const QString& what = "Document");
|
QJsonDocument requireDocument(const QString &filename, const QString &what = "Document");
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
QJsonObject requireObject(const QJsonDocument& doc, const QString& what = "Document");
|
QJsonObject requireObject(const QJsonDocument &doc, const QString &what = "Document");
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
QJsonArray requireArray(const QJsonDocument& doc, const QString& what = "Document");
|
QJsonArray requireArray(const QJsonDocument &doc, const QString &what = "Document");
|
||||||
|
|
||||||
/////////////////// WRITING ////////////////////
|
/////////////////// WRITING ////////////////////
|
||||||
|
|
||||||
void writeString(QJsonObject& to, const QString& key, const QString& value);
|
void writeString(QJsonObject & to, const QString &key, const QString &value);
|
||||||
void writeStringList(QJsonObject& to, const QString& key, const QStringList& values);
|
void writeStringList(QJsonObject & to, const QString &key, const QStringList &values);
|
||||||
|
|
||||||
template <typename T>
|
template<typename T>
|
||||||
QJsonValue toJson(const T& t)
|
QJsonValue toJson(const T &t)
|
||||||
{
|
{
|
||||||
return QJsonValue(t);
|
return QJsonValue(t);
|
||||||
}
|
}
|
||||||
template <>
|
template<>
|
||||||
QJsonValue toJson<QUrl>(const QUrl& url);
|
QJsonValue toJson<QUrl>(const QUrl &url);
|
||||||
template <>
|
template<>
|
||||||
QJsonValue toJson<QByteArray>(const QByteArray& data);
|
QJsonValue toJson<QByteArray>(const QByteArray &data);
|
||||||
template <>
|
template<>
|
||||||
QJsonValue toJson<QDateTime>(const QDateTime& datetime);
|
QJsonValue toJson<QDateTime>(const QDateTime &datetime);
|
||||||
template <>
|
template<>
|
||||||
QJsonValue toJson<QDir>(const QDir& dir);
|
QJsonValue toJson<QDir>(const QDir &dir);
|
||||||
template <>
|
template<>
|
||||||
QJsonValue toJson<QUuid>(const QUuid& uuid);
|
QJsonValue toJson<QUuid>(const QUuid &uuid);
|
||||||
template <>
|
template<>
|
||||||
QJsonValue toJson<QVariant>(const QVariant& variant);
|
QJsonValue toJson<QVariant>(const QVariant &variant);
|
||||||
|
|
||||||
template <typename T>
|
template<typename T>
|
||||||
QJsonArray toJsonArray(const QList<T>& container)
|
QJsonArray toJsonArray(const QList<T> &container)
|
||||||
{
|
{
|
||||||
QJsonArray array;
|
QJsonArray array;
|
||||||
for (const T item : container) {
|
for (const T item : container)
|
||||||
|
{
|
||||||
array.append(toJson<T>(item));
|
array.append(toJson<T>(item));
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
@ -109,110 +112,106 @@ QJsonArray toJsonArray(const QList<T>& container)
|
|||||||
|
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T requireIsType(const QJsonValue& value, const QString& what = "Value");
|
T requireIsType(const QJsonValue &value, const QString &what = "Value");
|
||||||
|
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
template <>
|
template<> double requireIsType<double>(const QJsonValue &value, const QString &what);
|
||||||
double requireIsType<double>(const QJsonValue& value, const QString& what);
|
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
template <>
|
template<> bool requireIsType<bool>(const QJsonValue &value, const QString &what);
|
||||||
bool requireIsType<bool>(const QJsonValue& value, const QString& what);
|
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
template <>
|
template<> int requireIsType<int>(const QJsonValue &value, const QString &what);
|
||||||
int requireIsType<int>(const QJsonValue& value, const QString& what);
|
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
template <>
|
template<> QJsonObject requireIsType<QJsonObject>(const QJsonValue &value, const QString &what);
|
||||||
QJsonObject requireIsType<QJsonObject>(const QJsonValue& value, const QString& what);
|
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
template <>
|
template<> QJsonArray requireIsType<QJsonArray>(const QJsonValue &value, const QString &what);
|
||||||
QJsonArray requireIsType<QJsonArray>(const QJsonValue& value, const QString& what);
|
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
template <>
|
template<> QJsonValue requireIsType<QJsonValue>(const QJsonValue &value, const QString &what);
|
||||||
QJsonValue requireIsType<QJsonValue>(const QJsonValue& value, const QString& what);
|
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
template <>
|
template<> QByteArray requireIsType<QByteArray>(const QJsonValue &value, const QString &what);
|
||||||
QByteArray requireIsType<QByteArray>(const QJsonValue& value, const QString& what);
|
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
template <>
|
template<> QDateTime requireIsType<QDateTime>(const QJsonValue &value, const QString &what);
|
||||||
QDateTime requireIsType<QDateTime>(const QJsonValue& value, const QString& what);
|
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
template <>
|
template<> QVariant requireIsType<QVariant>(const QJsonValue &value, const QString &what);
|
||||||
QVariant requireIsType<QVariant>(const QJsonValue& value, const QString& what);
|
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
template <>
|
template<> QString requireIsType<QString>(const QJsonValue &value, const QString &what);
|
||||||
QString requireIsType<QString>(const QJsonValue& value, const QString& what);
|
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
template <>
|
template<> QUuid requireIsType<QUuid>(const QJsonValue &value, const QString &what);
|
||||||
QUuid requireIsType<QUuid>(const QJsonValue& value, const QString& what);
|
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
template <>
|
template<> QDir requireIsType<QDir>(const QJsonValue &value, const QString &what);
|
||||||
QDir requireIsType<QDir>(const QJsonValue& value, const QString& what);
|
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
template <>
|
template<> QUrl requireIsType<QUrl>(const QJsonValue &value, const QString &what);
|
||||||
QUrl requireIsType<QUrl>(const QJsonValue& value, const QString& what);
|
|
||||||
|
|
||||||
// the following functions are higher level functions, that make use of the above functions for
|
// the following functions are higher level functions, that make use of the above functions for
|
||||||
// type conversion
|
// type conversion
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T ensureIsType(const QJsonValue& value, const T default_ = T(), const QString& what = "Value")
|
T ensureIsType(const QJsonValue &value, const T default_ = T(), const QString &what = "Value")
|
||||||
{
|
{
|
||||||
if (value.isUndefined() || value.isNull()) {
|
if (value.isUndefined() || value.isNull())
|
||||||
|
{
|
||||||
return default_;
|
return default_;
|
||||||
}
|
}
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
return requireIsType<T>(value, what);
|
return requireIsType<T>(value, what);
|
||||||
} catch (const JsonException&) {
|
}
|
||||||
|
catch (const JsonException &)
|
||||||
|
{
|
||||||
return default_;
|
return default_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T requireIsType(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__")
|
T requireIsType(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__")
|
||||||
{
|
{
|
||||||
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
|
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
|
||||||
if (!parent.contains(key)) {
|
if (!parent.contains(key))
|
||||||
|
{
|
||||||
throw JsonException(localWhat + "s parent does not contain " + localWhat);
|
throw JsonException(localWhat + "s parent does not contain " + localWhat);
|
||||||
}
|
}
|
||||||
return requireIsType<T>(parent.value(key), localWhat);
|
return requireIsType<T>(parent.value(key), localWhat);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T ensureIsType(const QJsonObject& parent, const QString& key, const T default_ = T(), const QString& what = "__placeholder__")
|
T ensureIsType(const QJsonObject &parent, const QString &key, const T default_ = T(), const QString &what = "__placeholder__")
|
||||||
{
|
{
|
||||||
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
|
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
|
||||||
if (!parent.contains(key)) {
|
if (!parent.contains(key))
|
||||||
|
{
|
||||||
return default_;
|
return default_;
|
||||||
}
|
}
|
||||||
return ensureIsType<T>(parent.value(key), default_, localWhat);
|
return ensureIsType<T>(parent.value(key), default_, localWhat);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
QVector<T> requireIsArrayOf(const QJsonDocument& doc)
|
QVector<T> requireIsArrayOf(const QJsonDocument &doc)
|
||||||
{
|
{
|
||||||
const QJsonArray array = requireArray(doc);
|
const QJsonArray array = requireArray(doc);
|
||||||
QVector<T> out;
|
QVector<T> out;
|
||||||
for (const QJsonValue val : array) {
|
for (const QJsonValue val : array)
|
||||||
|
{
|
||||||
out.append(requireIsType<T>(val, "Document"));
|
out.append(requireIsType<T>(val, "Document"));
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
QVector<T> ensureIsArrayOf(const QJsonValue& value, const QString& what = "Value")
|
QVector<T> ensureIsArrayOf(const QJsonValue &value, const QString &what = "Value")
|
||||||
{
|
{
|
||||||
const QJsonArray array = ensureIsType<QJsonArray>(value, QJsonArray(), what);
|
const QJsonArray array = ensureIsType<QJsonArray>(value, QJsonArray(), what);
|
||||||
QVector<T> out;
|
QVector<T> out;
|
||||||
for (const QJsonValue val : array) {
|
for (const QJsonValue val : array)
|
||||||
|
{
|
||||||
out.append(requireIsType<T>(val, what));
|
out.append(requireIsType<T>(val, what));
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
QVector<T> ensureIsArrayOf(const QJsonValue& value, const QVector<T> default_, const QString& what = "Value")
|
QVector<T> ensureIsArrayOf(const QJsonValue &value, const QVector<T> default_, const QString &what = "Value")
|
||||||
{
|
{
|
||||||
if (value.isUndefined()) {
|
if (value.isUndefined())
|
||||||
|
{
|
||||||
return default_;
|
return default_;
|
||||||
}
|
}
|
||||||
return ensureIsArrayOf<T>(value, what);
|
return ensureIsArrayOf<T>(value, what);
|
||||||
@ -220,46 +219,45 @@ QVector<T> ensureIsArrayOf(const QJsonValue& value, const QVector<T> default_, c
|
|||||||
|
|
||||||
/// @throw JsonException
|
/// @throw JsonException
|
||||||
template <typename T>
|
template <typename T>
|
||||||
QVector<T> requireIsArrayOf(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__")
|
QVector<T> requireIsArrayOf(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__")
|
||||||
{
|
{
|
||||||
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
|
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
|
||||||
if (!parent.contains(key)) {
|
if (!parent.contains(key))
|
||||||
|
{
|
||||||
throw JsonException(localWhat + "s parent does not contain " + localWhat);
|
throw JsonException(localWhat + "s parent does not contain " + localWhat);
|
||||||
}
|
}
|
||||||
return ensureIsArrayOf<T>(parent.value(key), localWhat);
|
return ensureIsArrayOf<T>(parent.value(key), localWhat);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
QVector<T> ensureIsArrayOf(const QJsonObject& parent,
|
QVector<T> ensureIsArrayOf(const QJsonObject &parent, const QString &key,
|
||||||
const QString& key,
|
const QVector<T> &default_ = QVector<T>(), const QString &what = "__placeholder__")
|
||||||
const QVector<T>& default_ = QVector<T>(),
|
|
||||||
const QString& what = "__placeholder__")
|
|
||||||
{
|
{
|
||||||
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
|
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
|
||||||
if (!parent.contains(key)) {
|
if (!parent.contains(key))
|
||||||
|
{
|
||||||
return default_;
|
return default_;
|
||||||
}
|
}
|
||||||
return ensureIsArrayOf<T>(parent.value(key), default_, localWhat);
|
return ensureIsArrayOf<T>(parent.value(key), default_, localWhat);
|
||||||
}
|
}
|
||||||
|
|
||||||
// this macro part could be replaced by variadic functions that just pass on their arguments, but that wouldn't work well with IDE helpers
|
// this macro part could be replaced by variadic functions that just pass on their arguments, but that wouldn't work well with IDE helpers
|
||||||
#define JSON_HELPERFUNCTIONS(NAME, TYPE) \
|
#define JSON_HELPERFUNCTIONS(NAME, TYPE) \
|
||||||
inline TYPE require##NAME(const QJsonValue& value, const QString& what = "Value") \
|
inline TYPE require##NAME(const QJsonValue &value, const QString &what = "Value") \
|
||||||
{ \
|
{ \
|
||||||
return requireIsType<TYPE>(value, what); \
|
return requireIsType<TYPE>(value, what); \
|
||||||
} \
|
} \
|
||||||
inline TYPE ensure##NAME(const QJsonValue& value, const TYPE default_ = TYPE(), const QString& what = "Value") \
|
inline TYPE ensure##NAME(const QJsonValue &value, const TYPE default_ = TYPE(), const QString &what = "Value") \
|
||||||
{ \
|
{ \
|
||||||
return ensureIsType<TYPE>(value, default_, what); \
|
return ensureIsType<TYPE>(value, default_, what); \
|
||||||
} \
|
} \
|
||||||
inline TYPE require##NAME(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__") \
|
inline TYPE require##NAME(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") \
|
||||||
{ \
|
{ \
|
||||||
return requireIsType<TYPE>(parent, key, what); \
|
return requireIsType<TYPE>(parent, key, what); \
|
||||||
} \
|
} \
|
||||||
inline TYPE ensure##NAME(const QJsonObject& parent, const QString& key, const TYPE default_ = TYPE(), \
|
inline TYPE ensure##NAME(const QJsonObject &parent, const QString &key, const TYPE default_ = TYPE(), const QString &what = "__placeholder") \
|
||||||
const QString& what = "__placeholder") \
|
{ \
|
||||||
{ \
|
return ensureIsType<TYPE>(parent, key, default_, what); \
|
||||||
return ensureIsType<TYPE>(parent, key, default_, what); \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HELPERFUNCTIONS(Array, QJsonArray)
|
JSON_HELPERFUNCTIONS(Array, QJsonArray)
|
||||||
@ -278,5 +276,5 @@ JSON_HELPERFUNCTIONS(Variant, QVariant)
|
|||||||
|
|
||||||
#undef JSON_HELPERFUNCTIONS
|
#undef JSON_HELPERFUNCTIONS
|
||||||
|
|
||||||
} // namespace Json
|
}
|
||||||
using JSONValidationError = Json::JsonException;
|
using JSONValidationError = Json::JsonException;
|
||||||
|
@ -1,26 +1,42 @@
|
|||||||
#include "KonamiCode.h"
|
#include "KonamiCode.h"
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const std::array<Qt::Key, 10> konamiCode = { { Qt::Key_Up, Qt::Key_Up, Qt::Key_Down, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right,
|
const std::array<Qt::Key, 10> konamiCode =
|
||||||
Qt::Key_Left, Qt::Key_Right, Qt::Key_B, Qt::Key_A } };
|
{
|
||||||
|
{
|
||||||
|
Qt::Key_Up, Qt::Key_Up,
|
||||||
|
Qt::Key_Down, Qt::Key_Down,
|
||||||
|
Qt::Key_Left, Qt::Key_Right,
|
||||||
|
Qt::Key_Left, Qt::Key_Right,
|
||||||
|
Qt::Key_B, Qt::Key_A
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
KonamiCode::KonamiCode(QObject* parent) : QObject(parent)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
KonamiCode::KonamiCode(QObject* parent) : QObject(parent) {}
|
|
||||||
|
|
||||||
void KonamiCode::input(QEvent* event)
|
void KonamiCode::input(QEvent* event)
|
||||||
{
|
{
|
||||||
if (event->type() == QEvent::KeyPress) {
|
if( event->type() == QEvent::KeyPress )
|
||||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
{
|
||||||
|
QKeyEvent *keyEvent = static_cast<QKeyEvent*>( event );
|
||||||
auto key = Qt::Key(keyEvent->key());
|
auto key = Qt::Key(keyEvent->key());
|
||||||
if (key == konamiCode[m_progress]) {
|
if(key == konamiCode[m_progress])
|
||||||
m_progress++;
|
{
|
||||||
} else {
|
m_progress ++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
m_progress = 0;
|
m_progress = 0;
|
||||||
}
|
}
|
||||||
if (m_progress == static_cast<int>(konamiCode.size())) {
|
if(m_progress == static_cast<int>(konamiCode.size()))
|
||||||
|
{
|
||||||
m_progress = 0;
|
m_progress = 0;
|
||||||
emit triggered();
|
emit triggered();
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,16 @@
|
|||||||
|
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
|
|
||||||
class KonamiCode : public QObject {
|
class KonamiCode : public QObject
|
||||||
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
KonamiCode(QObject* parent = 0);
|
KonamiCode(QObject *parent = 0);
|
||||||
void input(QEvent* event);
|
void input(QEvent *event);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void triggered();
|
void triggered();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_progress = 0;
|
int m_progress = 0;
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* PolyMC - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -35,41 +34,44 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "LaunchController.h"
|
#include "LaunchController.h"
|
||||||
#include "Application.h"
|
|
||||||
#include "minecraft/auth/AccountList.h"
|
#include "minecraft/auth/AccountList.h"
|
||||||
|
#include "Application.h"
|
||||||
|
|
||||||
#include "ui/InstanceWindow.h"
|
|
||||||
#include "ui/MainWindow.h"
|
#include "ui/MainWindow.h"
|
||||||
|
#include "ui/InstanceWindow.h"
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
#include "ui/dialogs/EditAccountDialog.h"
|
|
||||||
#include "ui/dialogs/ProfileSelectDialog.h"
|
#include "ui/dialogs/ProfileSelectDialog.h"
|
||||||
#include "ui/dialogs/ProfileSetupDialog.h"
|
|
||||||
#include "ui/dialogs/ProgressDialog.h"
|
#include "ui/dialogs/ProgressDialog.h"
|
||||||
|
#include "ui/dialogs/EditAccountDialog.h"
|
||||||
|
#include "ui/dialogs/ProfileSetupDialog.h"
|
||||||
|
|
||||||
#include <QHostAddress>
|
|
||||||
#include <QHostInfo>
|
|
||||||
#include <QInputDialog>
|
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QList>
|
#include <QInputDialog>
|
||||||
#include <QPushButton>
|
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <QHostInfo>
|
||||||
|
#include <QList>
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QPushButton>
|
||||||
|
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
#include "JavaCommon.h"
|
#include "JavaCommon.h"
|
||||||
#include "launch/steps/TextPrint.h"
|
|
||||||
#include "minecraft/auth/AccountTask.h"
|
|
||||||
#include "tasks/Task.h"
|
#include "tasks/Task.h"
|
||||||
|
#include "minecraft/auth/AccountTask.h"
|
||||||
|
#include "launch/steps/TextPrint.h"
|
||||||
|
|
||||||
LaunchController::LaunchController(QObject* parent) : Task(parent) {}
|
LaunchController::LaunchController(QObject *parent) : Task(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void LaunchController::executeTask()
|
void LaunchController::executeTask()
|
||||||
{
|
{
|
||||||
if (!m_instance) {
|
if (!m_instance)
|
||||||
|
{
|
||||||
emitFailed(tr("No instance specified!"));
|
emitFailed(tr("No instance specified!"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget)) {
|
if(!JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget)) {
|
||||||
emitFailed(tr("Invalid Java arguments specified. Please fix this first."));
|
emitFailed(tr("Invalid Java arguments specified. Please fix this first."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -79,43 +81,46 @@ void LaunchController::executeTask()
|
|||||||
|
|
||||||
void LaunchController::decideAccount()
|
void LaunchController::decideAccount()
|
||||||
{
|
{
|
||||||
if (m_accountToUse) {
|
if(m_accountToUse) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find an account to use.
|
// Find an account to use.
|
||||||
auto accounts = APPLICATION->accounts();
|
auto accounts = APPLICATION->accounts();
|
||||||
if (accounts->count() <= 0) {
|
if (accounts->count() <= 0)
|
||||||
|
{
|
||||||
// Tell the user they need to log in at least one account in order to play.
|
// Tell the user they need to log in at least one account in order to play.
|
||||||
auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"),
|
auto reply = CustomMessageBox::selectable(
|
||||||
tr("In order to play Minecraft, you must have at least one Microsoft "
|
m_parentWidget,
|
||||||
"account which owns Minecraft logged in. "
|
tr("No Accounts"),
|
||||||
"Would you like to open the account manager to add an account now?"),
|
tr("In order to play Minecraft, you must have at least one Mojang or Microsoft "
|
||||||
QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)
|
"account logged in. "
|
||||||
->exec();
|
"Would you like to open the account manager to add an account now?"),
|
||||||
|
QMessageBox::Information,
|
||||||
|
QMessageBox::Yes | QMessageBox::No
|
||||||
|
)->exec();
|
||||||
|
|
||||||
if (reply == QMessageBox::Yes) {
|
if (reply == QMessageBox::Yes)
|
||||||
|
{
|
||||||
// Open the account manager.
|
// Open the account manager.
|
||||||
APPLICATION->ShowGlobalSettings(m_parentWidget, "accounts");
|
APPLICATION->ShowGlobalSettings(m_parentWidget, "accounts");
|
||||||
} else if (reply == QMessageBox::No) {
|
}
|
||||||
|
else if (reply == QMessageBox::No)
|
||||||
|
{
|
||||||
// Do not open "profile select" dialog.
|
// Do not open "profile select" dialog.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select the account to use. If the instance has a specific account set, that will be used. Otherwise, the default account will be used
|
m_accountToUse = accounts->defaultAccount();
|
||||||
auto instanceAccountId = m_instance->settings()->get("InstanceAccountId").toString();
|
if (!m_accountToUse)
|
||||||
auto instanceAccountIndex = accounts->findAccountByProfileId(instanceAccountId);
|
{
|
||||||
if (instanceAccountIndex == -1 || instanceAccountId.isEmpty()) {
|
|
||||||
m_accountToUse = accounts->defaultAccount();
|
|
||||||
} else {
|
|
||||||
m_accountToUse = accounts->at(instanceAccountIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_accountToUse) {
|
|
||||||
// If no default account is set, ask the user which one to use.
|
// If no default account is set, ask the user which one to use.
|
||||||
ProfileSelectDialog selectDialog(tr("Which account would you like to use?"), ProfileSelectDialog::GlobalDefaultCheckbox,
|
ProfileSelectDialog selectDialog(
|
||||||
m_parentWidget);
|
tr("Which account would you like to use?"),
|
||||||
|
ProfileSelectDialog::GlobalDefaultCheckbox,
|
||||||
|
m_parentWidget
|
||||||
|
);
|
||||||
|
|
||||||
selectDialog.exec();
|
selectDialog.exec();
|
||||||
|
|
||||||
@ -129,12 +134,13 @@ void LaunchController::decideAccount()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LaunchController::login()
|
|
||||||
{
|
void LaunchController::login() {
|
||||||
decideAccount();
|
decideAccount();
|
||||||
|
|
||||||
// if no account is selected, we bail
|
// if no account is selected, we bail
|
||||||
if (!m_accountToUse) {
|
if (!m_accountToUse)
|
||||||
|
{
|
||||||
emitFailed(tr("No account selected for launch."));
|
emitFailed(tr("No account selected for launch."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -143,11 +149,15 @@ void LaunchController::login()
|
|||||||
bool tryagain = true;
|
bool tryagain = true;
|
||||||
unsigned int tries = 0;
|
unsigned int tries = 0;
|
||||||
|
|
||||||
while (tryagain) {
|
while (tryagain)
|
||||||
|
{
|
||||||
if (tries > 0 && tries % 3 == 0) {
|
if (tries > 0 && tries % 3 == 0) {
|
||||||
auto result =
|
auto result = QMessageBox::question(
|
||||||
QMessageBox::question(m_parentWidget, tr("Continue launch?"),
|
m_parentWidget,
|
||||||
tr("It looks like we couldn't launch after %1 tries. Do you want to continue trying?").arg(tries));
|
tr("Continue launch?"),
|
||||||
|
tr("It looks like we couldn't launch after %1 tries. Do you want to continue trying?")
|
||||||
|
.arg(tries)
|
||||||
|
);
|
||||||
|
|
||||||
if (result == QMessageBox::No) {
|
if (result == QMessageBox::No) {
|
||||||
emitAborted();
|
emitAborted();
|
||||||
@ -157,52 +167,57 @@ void LaunchController::login()
|
|||||||
tries++;
|
tries++;
|
||||||
m_session = std::make_shared<AuthSession>();
|
m_session = std::make_shared<AuthSession>();
|
||||||
m_session->wants_online = m_online;
|
m_session->wants_online = m_online;
|
||||||
m_session->demo = m_demo;
|
|
||||||
m_accountToUse->fillSession(m_session);
|
m_accountToUse->fillSession(m_session);
|
||||||
|
|
||||||
// Launch immediately in true offline mode
|
// Launch immediately in true offline mode
|
||||||
if (m_accountToUse->isOffline()) {
|
if(m_accountToUse->isOffline()) {
|
||||||
launchInstance();
|
launchInstance();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (m_accountToUse->accountState()) {
|
switch(m_accountToUse->accountState()) {
|
||||||
case AccountState::Offline: {
|
case AccountState::Offline: {
|
||||||
m_session->wants_online = false;
|
m_session->wants_online = false;
|
||||||
|
// NOTE: fallthrough is intentional
|
||||||
}
|
}
|
||||||
/* fallthrough */
|
|
||||||
case AccountState::Online: {
|
case AccountState::Online: {
|
||||||
if (!m_session->wants_online) {
|
if(!m_session->wants_online) {
|
||||||
// we ask the user for a player name
|
// we ask the user for a player name
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
|
||||||
QString message = tr("Choose your offline mode player name.");
|
|
||||||
if (m_session->demo) {
|
|
||||||
message = tr("Choose your demo mode player name.");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString();
|
QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString();
|
||||||
QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName;
|
QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName;
|
||||||
QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok);
|
QString name = QInputDialog::getText(
|
||||||
if (!ok) {
|
m_parentWidget,
|
||||||
|
tr("Player name"),
|
||||||
|
tr("Choose your offline mode player name."),
|
||||||
|
QLineEdit::Normal,
|
||||||
|
usedname,
|
||||||
|
&ok
|
||||||
|
);
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
tryagain = false;
|
tryagain = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (name.length()) {
|
if (name.length())
|
||||||
|
{
|
||||||
usedname = name;
|
usedname = name;
|
||||||
APPLICATION->settings()->set("LastOfflinePlayerName", usedname);
|
APPLICATION->settings()->set("LastOfflinePlayerName", usedname);
|
||||||
}
|
}
|
||||||
m_session->MakeOffline(usedname);
|
m_session->MakeOffline(usedname);
|
||||||
// offline flavored game from here :3
|
// offline flavored game from here :3
|
||||||
}
|
}
|
||||||
if (m_accountToUse->ownsMinecraft()) {
|
if(m_accountToUse->ownsMinecraft()) {
|
||||||
if (!m_accountToUse->hasProfile()) {
|
if(!m_accountToUse->hasProfile()) {
|
||||||
// Now handle setting up a profile name here...
|
// Now handle setting up a profile name here...
|
||||||
ProfileSetupDialog dialog(m_accountToUse, m_parentWidget);
|
ProfileSetupDialog dialog(m_accountToUse, m_parentWidget);
|
||||||
if (dialog.exec() == QDialog::Accepted) {
|
if (dialog.exec() == QDialog::Accepted)
|
||||||
|
{
|
||||||
tryagain = true;
|
tryagain = true;
|
||||||
continue;
|
continue;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
emitFailed(tr("Received undetermined session status during login."));
|
emitFailed(tr("Received undetermined session status during login."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -210,24 +225,24 @@ void LaunchController::login()
|
|||||||
// we own Minecraft, there is a profile, it's all ready to go!
|
// we own Minecraft, there is a profile, it's all ready to go!
|
||||||
launchInstance();
|
launchInstance();
|
||||||
return;
|
return;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// play demo ?
|
// play demo ?
|
||||||
QMessageBox box(m_parentWidget);
|
QMessageBox box(m_parentWidget);
|
||||||
box.setWindowTitle(tr("Play demo?"));
|
box.setWindowTitle(tr("Play demo?"));
|
||||||
box.setText(
|
box.setText(tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play the demo?"));
|
||||||
tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play "
|
|
||||||
"the demo?"));
|
|
||||||
box.setIcon(QMessageBox::Warning);
|
box.setIcon(QMessageBox::Warning);
|
||||||
auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole);
|
auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole);
|
||||||
auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole);
|
auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole);
|
||||||
box.setDefaultButton(cancelButton);
|
box.setDefaultButton(cancelButton);
|
||||||
|
|
||||||
box.exec();
|
box.exec();
|
||||||
if (box.clickedButton() == demoButton) {
|
if(box.clickedButton() == demoButton) {
|
||||||
// play demo here
|
// play demo here
|
||||||
m_session->MakeDemo();
|
m_session->MakeDemo();
|
||||||
launchInstance();
|
launchInstance();
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
emitFailed(tr("Launch cancelled - account does not own Minecraft."));
|
emitFailed(tr("Launch cancelled - account does not own Minecraft."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -237,12 +252,13 @@ void LaunchController::login()
|
|||||||
// This means some sort of soft error that we can fix with a refresh ... so let's refresh.
|
// This means some sort of soft error that we can fix with a refresh ... so let's refresh.
|
||||||
case AccountState::Unchecked: {
|
case AccountState::Unchecked: {
|
||||||
m_accountToUse->refresh();
|
m_accountToUse->refresh();
|
||||||
|
// NOTE: fallthrough intentional
|
||||||
}
|
}
|
||||||
/* fallthrough */
|
|
||||||
case AccountState::Working: {
|
case AccountState::Working: {
|
||||||
// refresh is in progress, we need to wait for it to finish to proceed.
|
// refresh is in progress, we need to wait for it to finish to proceed.
|
||||||
ProgressDialog progDialog(m_parentWidget);
|
ProgressDialog progDialog(m_parentWidget);
|
||||||
if (m_online) {
|
if (m_online)
|
||||||
|
{
|
||||||
progDialog.setSkipButton(true, tr("Play Offline"));
|
progDialog.setSkipButton(true, tr("Play Offline"));
|
||||||
}
|
}
|
||||||
auto task = m_accountToUse->currentTask();
|
auto task = m_accountToUse->currentTask();
|
||||||
@ -257,24 +273,37 @@ void LaunchController::login()
|
|||||||
*/
|
*/
|
||||||
case AccountState::Expired: {
|
case AccountState::Expired: {
|
||||||
auto errorString = tr("The account has expired and needs to be logged into manually again.");
|
auto errorString = tr("The account has expired and needs to be logged into manually again.");
|
||||||
QMessageBox::warning(m_parentWidget, tr("Account refresh failed"), errorString, QMessageBox::StandardButton::Ok,
|
QMessageBox::warning(
|
||||||
QMessageBox::StandardButton::Ok);
|
m_parentWidget,
|
||||||
|
tr("Account refresh failed"),
|
||||||
|
errorString,
|
||||||
|
QMessageBox::StandardButton::Ok,
|
||||||
|
QMessageBox::StandardButton::Ok
|
||||||
|
);
|
||||||
emitFailed(errorString);
|
emitFailed(errorString);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case AccountState::Disabled: {
|
case AccountState::Disabled: {
|
||||||
auto errorString = tr("The launcher's client identification has changed. Please remove this account and add it again.");
|
auto errorString = tr("The launcher's client identification has changed. Please remove this account and add it again.");
|
||||||
QMessageBox::warning(m_parentWidget, tr("Client identification changed"), errorString, QMessageBox::StandardButton::Ok,
|
QMessageBox::warning(
|
||||||
QMessageBox::StandardButton::Ok);
|
m_parentWidget,
|
||||||
|
tr("Client identification changed"),
|
||||||
|
errorString,
|
||||||
|
QMessageBox::StandardButton::Ok,
|
||||||
|
QMessageBox::StandardButton::Ok
|
||||||
|
);
|
||||||
emitFailed(errorString);
|
emitFailed(errorString);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case AccountState::Gone: {
|
case AccountState::Gone: {
|
||||||
auto errorString =
|
auto errorString = tr("The account no longer exists on the servers. It may have been migrated, in which case please add the new account you migrated this one to.");
|
||||||
tr("The account no longer exists on the servers. It may have been migrated, in which case please add the new account "
|
QMessageBox::warning(
|
||||||
"you migrated this one to.");
|
m_parentWidget,
|
||||||
QMessageBox::warning(m_parentWidget, tr("Account gone"), errorString, QMessageBox::StandardButton::Ok,
|
tr("Account gone"),
|
||||||
QMessageBox::StandardButton::Ok);
|
errorString,
|
||||||
|
QMessageBox::StandardButton::Ok,
|
||||||
|
QMessageBox::StandardButton::Ok
|
||||||
|
);
|
||||||
emitFailed(errorString);
|
emitFailed(errorString);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -288,45 +317,48 @@ void LaunchController::launchInstance()
|
|||||||
Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL");
|
Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL");
|
||||||
Q_ASSERT_X(m_session.get() != nullptr, "launchInstance", "session is NULL");
|
Q_ASSERT_X(m_session.get() != nullptr, "launchInstance", "session is NULL");
|
||||||
|
|
||||||
if (!m_instance->reloadSettings()) {
|
if(!m_instance->reloadSettings())
|
||||||
|
{
|
||||||
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't load the instance profile."));
|
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't load the instance profile."));
|
||||||
emitFailed(tr("Couldn't load the instance profile."));
|
emitFailed(tr("Couldn't load the instance profile."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_launcher = m_instance->createLaunchTask(m_session, m_serverToJoin);
|
m_launcher = m_instance->createLaunchTask(m_session, m_serverToJoin);
|
||||||
if (!m_launcher) {
|
if (!m_launcher)
|
||||||
|
{
|
||||||
emitFailed(tr("Couldn't instantiate a launcher."));
|
emitFailed(tr("Couldn't instantiate a launcher."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto console = qobject_cast<InstanceWindow*>(m_parentWidget);
|
auto console = qobject_cast<InstanceWindow *>(m_parentWidget);
|
||||||
auto showConsole = m_instance->settings()->get("ShowConsole").toBool();
|
auto showConsole = m_instance->settings()->get("ShowConsole").toBool();
|
||||||
if (!console && showConsole) {
|
if(!console && showConsole)
|
||||||
|
{
|
||||||
APPLICATION->showInstanceWindow(m_instance);
|
APPLICATION->showInstanceWindow(m_instance);
|
||||||
}
|
}
|
||||||
connect(m_launcher.get(), &LaunchTask::readyForLaunch, this, &LaunchController::readyForLaunch);
|
connect(m_launcher.get(), &LaunchTask::readyForLaunch, this, &LaunchController::readyForLaunch);
|
||||||
connect(m_launcher.get(), &LaunchTask::succeeded, this, &LaunchController::onSucceeded);
|
connect(m_launcher.get(), &LaunchTask::succeeded, this, &LaunchController::onSucceeded);
|
||||||
connect(m_launcher.get(), &LaunchTask::failed, this, &LaunchController::onFailed);
|
connect(m_launcher.get(), &LaunchTask::failed, this, &LaunchController::onFailed);
|
||||||
connect(m_launcher.get(), &LaunchTask::requestProgress, this, &LaunchController::onProgressRequested);
|
connect(m_launcher.get(), &LaunchTask::requestProgress, this, &LaunchController::onProgressRequested);
|
||||||
|
|
||||||
// Prepend Online and Auth Status
|
// Prepend Online and Auth Status
|
||||||
QString online_mode;
|
QString online_mode;
|
||||||
if (m_session->wants_online) {
|
if(m_session->wants_online) {
|
||||||
online_mode = "online";
|
online_mode = "online";
|
||||||
|
|
||||||
// Prepend Server Status
|
// Prepend Server Status
|
||||||
QStringList servers = { "authserver.mojang.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" };
|
QStringList servers = {"authserver.mojang.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com"};
|
||||||
QString resolved_servers = "";
|
QString resolved_servers = "";
|
||||||
QHostInfo host_info;
|
QHostInfo host_info;
|
||||||
|
|
||||||
for (QString server : servers) {
|
for(QString server : servers) {
|
||||||
host_info = QHostInfo::fromName(server);
|
host_info = QHostInfo::fromName(server);
|
||||||
resolved_servers = resolved_servers + server + " resolves to:\n [";
|
resolved_servers = resolved_servers + server + " resolves to:\n [";
|
||||||
if (!host_info.addresses().isEmpty()) {
|
if(!host_info.addresses().isEmpty()) {
|
||||||
for (QHostAddress address : host_info.addresses()) {
|
for(QHostAddress address : host_info.addresses()) {
|
||||||
resolved_servers = resolved_servers + address.toString();
|
resolved_servers = resolved_servers + address.toString();
|
||||||
if (!host_info.addresses().endsWith(address)) {
|
if(!host_info.addresses().endsWith(address)) {
|
||||||
resolved_servers = resolved_servers + ", ";
|
resolved_servers = resolved_servers + ", ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -335,52 +367,51 @@ void LaunchController::launchInstance()
|
|||||||
}
|
}
|
||||||
resolved_servers = resolved_servers + "]\n\n";
|
resolved_servers = resolved_servers + "]\n\n";
|
||||||
}
|
}
|
||||||
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), resolved_servers, MessageLevel::Launcher));
|
m_launcher->prependStep(new TextPrint(m_launcher.get(), resolved_servers, MessageLevel::Launcher));
|
||||||
} else {
|
} else {
|
||||||
online_mode = m_demo ? "demo" : "offline";
|
online_mode = "offline";
|
||||||
}
|
}
|
||||||
|
|
||||||
m_launcher->prependStep(
|
m_launcher->prependStep(new TextPrint(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher));
|
||||||
makeShared<TextPrint>(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher));
|
|
||||||
|
|
||||||
// Prepend Version
|
// Prepend Version
|
||||||
{
|
m_launcher->prependStep(new TextPrint(m_launcher.get(), BuildConfig.LAUNCHER_NAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher));
|
||||||
auto versionString = QString("%1 version: %2 (%3)")
|
|
||||||
.arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString(), BuildConfig.BUILD_PLATFORM);
|
|
||||||
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), versionString + "\n\n", MessageLevel::Launcher));
|
|
||||||
}
|
|
||||||
m_launcher->start();
|
m_launcher->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LaunchController::readyForLaunch()
|
void LaunchController::readyForLaunch()
|
||||||
{
|
{
|
||||||
if (!m_profiler) {
|
if (!m_profiler)
|
||||||
|
{
|
||||||
m_launcher->proceed();
|
m_launcher->proceed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString error;
|
QString error;
|
||||||
if (!m_profiler->check(&error)) {
|
if (!m_profiler->check(&error))
|
||||||
|
{
|
||||||
m_launcher->abort();
|
m_launcher->abort();
|
||||||
|
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't start profiler: %1").arg(error));
|
||||||
emitFailed("Profiler startup failed!");
|
emitFailed("Profiler startup failed!");
|
||||||
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Profiler check for %1 failed: %2").arg(m_profiler->name(), error));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
BaseProfiler* profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this);
|
BaseProfiler *profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this);
|
||||||
|
|
||||||
connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString& message) {
|
connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString & message)
|
||||||
QMessageBox msg(m_parentWidget);
|
{
|
||||||
|
QMessageBox msg;
|
||||||
msg.setText(tr("The game launch is delayed until you press the "
|
msg.setText(tr("The game launch is delayed until you press the "
|
||||||
"button. This is the right time to setup the profiler, as the "
|
"button. This is the right time to setup the profiler, as the "
|
||||||
"profiler server is running now.\n\n%1")
|
"profiler server is running now.\n\n%1").arg(message));
|
||||||
.arg(message));
|
|
||||||
msg.setWindowTitle(tr("Waiting."));
|
msg.setWindowTitle(tr("Waiting."));
|
||||||
msg.setIcon(QMessageBox::Information);
|
msg.setIcon(QMessageBox::Information);
|
||||||
msg.addButton(tr("&Launch"), QMessageBox::AcceptRole);
|
msg.addButton(tr("Launch"), QMessageBox::AcceptRole);
|
||||||
|
msg.setModal(true);
|
||||||
msg.exec();
|
msg.exec();
|
||||||
m_launcher->proceed();
|
m_launcher->proceed();
|
||||||
});
|
});
|
||||||
connect(profilerInstance, &BaseProfiler::abortLaunch, [this](const QString& message) {
|
connect(profilerInstance, &BaseProfiler::abortLaunch, [this](const QString & message)
|
||||||
|
{
|
||||||
QMessageBox msg;
|
QMessageBox msg;
|
||||||
msg.setText(tr("Couldn't start the profiler: %1").arg(message));
|
msg.setText(tr("Couldn't start the profiler: %1").arg(message));
|
||||||
msg.setWindowTitle(tr("Error"));
|
msg.setWindowTitle(tr("Error"));
|
||||||
@ -401,7 +432,8 @@ void LaunchController::onSucceeded()
|
|||||||
|
|
||||||
void LaunchController::onFailed(QString reason)
|
void LaunchController::onFailed(QString reason)
|
||||||
{
|
{
|
||||||
if (m_instance->settings()->get("ShowConsoleOnError").toBool()) {
|
if(m_instance->settings()->get("ShowConsoleOnError").toBool())
|
||||||
|
{
|
||||||
APPLICATION->showInstanceWindow(m_instance, "console");
|
APPLICATION->showInstanceWindow(m_instance, "console");
|
||||||
}
|
}
|
||||||
emitFailed(reason);
|
emitFailed(reason);
|
||||||
@ -417,18 +449,21 @@ void LaunchController::onProgressRequested(Task* task)
|
|||||||
|
|
||||||
bool LaunchController::abort()
|
bool LaunchController::abort()
|
||||||
{
|
{
|
||||||
if (!m_launcher) {
|
if(!m_launcher)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!m_launcher->canAbort()) {
|
if(!m_launcher->canAbort())
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto response = CustomMessageBox::selectable(m_parentWidget, tr("Kill Minecraft?"),
|
auto response = CustomMessageBox::selectable(
|
||||||
tr("This can cause the instance to get corrupted and should only be used if Minecraft "
|
m_parentWidget, tr("Kill Minecraft?"),
|
||||||
"is frozen for some reason"),
|
tr("This can cause the instance to get corrupted and should only be used if Minecraft "
|
||||||
QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)
|
"is frozen for some reason"),
|
||||||
->exec();
|
QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)->exec();
|
||||||
if (response == QMessageBox::Yes) {
|
if (response == QMessageBox::Yes)
|
||||||
|
{
|
||||||
return m_launcher->abort();
|
return m_launcher->abort();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user