Compare commits

..

21 Commits

Author SHA1 Message Date
Sefa Eyeoglu
404b9599fd
Merge pull request #781 from Scrumplex/chore-bump-1.3.2 2022-06-12 18:51:04 +02:00
flow
4a803e4721
Merge pull request #783 from Scrumplex/fix-avoid-settings-register-warnings
Avoid re-registering InstanceType
2022-06-12 18:50:38 +02:00
Sefa Eyeoglu
2bcc81a75b
chore: bump version 2022-06-12 18:23:58 +02:00
Sefa Eyeoglu
0be9244ade
Merge pull request #679 from kthchew/fix/win-install-version 2022-06-12 18:23:39 +02:00
Sefa Eyeoglu
a852d29262
Merge pull request #771 from flowln/modrinth_multiple_downloads 2022-06-12 15:41:16 +02:00
Sefa Eyeoglu
7145a55f83
Merge pull request #632 from ryanccn/macos-app-heuristic 2022-06-12 15:41:16 +02:00
dada513
4f8915da6c
Merge pull request #780 from flowln/guo_ext_2 2022-06-12 15:41:16 +02:00
dada513
973c4327e3
Merge pull request #773 from vancez/fix-launch-button 2022-06-12 15:41:16 +02:00
dada513
820951bce1
Merge pull request #770 from flowln/technic_links 2022-06-12 15:41:16 +02:00
dada513
c8257c632a
Merge pull request #764 from Scrumplex/fix-compile-flags 2022-06-12 15:41:16 +02:00
dada513
a4d69cce43
Merge pull request #720 from icelimetea/fix-legacy-issues 2022-06-12 15:41:16 +02:00
Sefa Eyeoglu
dfe2e5f44e
Merge pull request #709 from Scrumplex/fix-instancetypes
Always store InstanceType
2022-06-12 15:41:16 +02:00
Sefa Eyeoglu
dbdd1c83d9
Merge pull request #732 from MrMelon54/develop 2022-06-12 15:41:16 +02:00
txtsd
2519bb96f9
Merge pull request #762 from muscaln/flake
Bump flake lock
2022-06-12 15:41:16 +02:00
txtsd
f169173afa
Merge pull request #734 from babbaj/extra-jdks
nix: add package argument for extra jdks
2022-06-12 15:41:16 +02:00
flow
1c70b29b3d
Merge pull request #685 from kthchew/fix/technic-quilt
Add Quilt support for Technic modpacks
2022-06-12 15:41:16 +02:00
Sefa Eyeoglu
75b7ca814a
Merge pull request #718 from DioEgizio/revert-unfunny-pr 2022-06-12 15:41:16 +02:00
TheOPtimal
b6b33862ed
Prepare for Nix 2.7 (#286)
* Prepare for Nix 2.7

* Fix embarassing oopsie
2022-06-12 15:25:42 +02:00
Sefa Eyeoglu
0dbd70ba9f
Merge pull request #701 from jn64/patch-1
Add "mc" keyword to desktop file
2022-06-12 15:25:42 +02:00
Sefa Eyeoglu
7a5a1a9af7
Merge pull request #692 from glowiak/patch-6 2022-06-12 15:25:42 +02:00
Sefa Eyeoglu
938f222791
Merge pull request #682 from istudyatuni/fix-add-account-behaviour 2022-06-12 15:25:42 +02:00
1503 changed files with 55306 additions and 103569 deletions

View File

@ -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

View File

@ -1,4 +0,0 @@
Checks:
- modernize-use-using
SystemHeaders: false

View File

@ -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

2
.envrc
View File

@ -1,2 +0,0 @@
use flake
watch_file nix/*.nix

View File

@ -1,4 +0,0 @@
# .git-blame-ignore-revs
# tabs -> spaces
bbb3b3e6f6e3c0f95873f22e6d0a4aaf350f49d9

2
.github/FUNDING.yml vendored
View File

@ -1 +1 @@
open_collective: prismlauncher open_collective: polymc

View File

@ -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.2.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

View File

@ -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.

View File

@ -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!).

View File

@ -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

View File

@ -1,3 +0,0 @@
query-filters:
- exclude:
id: cpp/fixme-comment

2
.github/dco.yml vendored
View File

@ -1,2 +0,0 @@
allowRemediationCommits:
individual: true

View File

@ -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!
-->

View File

@ -1,32 +1,19 @@
name: Backport name: Backport PR to stable
on: on:
pull_request_target: pull_request:
types: [closed, labeled] branches: [ develop ]
types: [ closed ]
# 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: jobs:
backport: release_pull_request:
permissions: if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'backport')
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 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - name: checkout
with: uses: actions/checkout@v3
ref: ${{ github.event.pull_request.head.sha }} with:
- name: Create backport PRs fetch-depth: 0
uses: korthout/backport-action@v2.1.0 - name: Backport PR by cherry-pick-ing
with: uses: Nathanmalnoury/gh-backport-action@master
# Config README: https://github.com/korthout/backport-action#backport-action with:
pull_description: |- pr_branch: 'stable'
Bot-based backport to `${target_branch}`, triggered by a label in #${pull_number}. github_token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -7,29 +7,6 @@ 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:
SPARKLE_ED25519_KEY:
description: Private key for signing Sparkle updates
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 +14,22 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- os: ubuntu-20.04
qt_ver: 5
- os: ubuntu-20.04 - os: ubuntu-20.04
qt_ver: 6
qt_host: linux - os: ubuntu-20.04
qt_arch: "" appimage: true
qt_version: "6.2.4"
qt_modules: "qt5compat qtimageformats"
qt_tools: ""
- os: windows-2022 - os: windows-2022
name: "Windows-MinGW-w64" name: "Windows-i686"
msystem: clang64 msystem: mingw32
vcvars_arch: "amd64_x86"
- os: windows-2022 - os: windows-2022
name: "Windows-MSVC" name: "Windows-x86_64"
msystem: "" msystem: mingw64
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 - os: macos-11
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 macosx_deployment_target: 10.13
qt_ver: 5
qt_host: mac
qt_version: "5.15.2"
qt_modules: ""
qt_tools: ""
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@ -105,61 +40,40 @@ 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
ninja:p ninja:p
qt6-base:p qt5:p
qt6-svg:p
qt6-imageformats:p
quazip-qt6:p
ccache:p ccache:p
qt6-5compat:p nsis:p
cmark: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 }}-${{ matrix.appimage }}
- 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,123 +88,69 @@ 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 }}-${{ matrix.msystem }}
restore-keys: |
${{ matrix.os }}-${{ matrix.msystem }}
- name: Set short version - name: Set short version
shell: bash shell: bash
run: | run: |
ver_short=`git rev-parse --short HEAD` ver_short=`git rev-parse --short HEAD`
echo "VERSION=$ver_short" >> $GITHUB_ENV echo "VERSION=$ver_short" >> $GITHUB_ENV
- name: Install Dependencies (Linux) - name: Install Qt (macOS)
if: runner.os == 'macOS'
run: |
brew update
brew install qt@5 ninja
- name: Update Qt (AppImage)
if: runner.os == 'Linux' && matrix.appimage == true
run: |
sudo add-apt-repository ppa:savoury1/qt-5-15
sudo add-apt-repository ppa:savoury1/kde-5-80
sudo add-apt-repository ppa:savoury1/gpg
sudo add-apt-repository ppa:savoury1/ffmpeg4
- name: Install Qt (Linux)
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 qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 ninja-build qt5-image-formats-plugins
- name: Install Dependencies (macOS)
if: runner.os == 'macOS'
run: |
brew update
brew install ninja extra-cmake-modules
- name: Install Qt (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 6
run: |
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
- name: Install host Qt (Windows MSVC arm64)
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
uses: jurplel/install-qt-action@v3
with:
aqtversion: "==3.1.*"
py7zrversion: ">=0.20.2"
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)
if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS' || (runner.os == 'Windows' && matrix.msystem == '')
uses: jurplel/install-qt-action@v3
with:
aqtversion: "==3.1.*"
py7zrversion: ">=0.20.2"
version: ${{ matrix.qt_version }}
host: ${{ matrix.qt_host }}
target: "desktop"
arch: ${{ matrix.qt_arch }}
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.appimage == true
run: | run: |
wget "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage" wget "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage"
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage" wget "https://github.com/linuxdeploy/linuxdeploy-plugin-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 -DQt5_DIR=/usr/local/opt/qt@5 -DCMAKE_PREFIX_PATH=/usr/local/opt/qt@5 -DLauncher_BUILD_PLATFORM=macOS -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -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 }} -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 }} -G Ninja
## ##
# BUILD # BUILD
@ -301,37 +161,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
##
- name: Test
if: runner.os != 'Windows'
run: |
ctest -E "^example64|example$" --test-dir build --output-on-failure
- name: Test (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != ''
shell: msys2 {0}
run: |
ctest -E "^example64|example$" --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,149 +177,70 @@ 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: Package (Windows)
if: matrix.name == 'macOS' if: runner.os == 'Windows'
run: |
if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then
brew install openssl@3
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)
rm ed25519-priv.pem
cat >> $GITHUB_STEP_SUMMARY << EOF
### Artifact Information :information_source:
- :memo: Sparkle Signature (ed25519): \`$signature\`
EOF
else
cat >> $GITHUB_STEP_SUMMARY << EOF
### Artifact Information :information_source:
- :warning: Sparkle Signature (ed25519): No private key available (likely a pull request or fork)
EOF
fi
- name: Package (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != ''
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' && matrix.appimage != true
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' && matrix.appimage != true
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.appimage == true
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
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-{8,17}-openjdk mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-{8,17}-openjdk
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
cp -r ${{ github.workspace }}/JREs/jre8/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk cp -r ${{ github.workspace }}/JREs/jre8/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk
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 /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/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"
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64" LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64"
@ -492,24 +248,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 +258,49 @@ 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)
if: runner.os == 'Linux' && matrix.qt_ver != 6 if: runner.os == 'Linux' && matrix.appimage != true
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)
if: runner.os == 'Linux' && matrix.qt_ver != 6 if: runner.os == 'Linux' && matrix.appimage != true
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)
if: runner.os == 'Linux' && matrix.qt_ver !=5
uses: actions/upload-artifact@v3
with:
name: PrismLauncher-${{ runner.os }}-Qt6-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher.tar.gz
- name: Upload binary tarball (Linux, portable, Qt 6)
if: runner.os == 'Linux' && matrix.qt_ver != 5
uses: actions/upload-artifact@v3
with:
name: PrismLauncher-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher-portable.tar.gz
- name: Upload AppImage (Linux) - name: Upload AppImage (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 5 if: runner.os == 'Linux' && matrix.appimage == true
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

View File

@ -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

61
.github/workflows/pr-comment.yml vendored Normal file
View File

@ -0,0 +1,61 @@
name: Comment on pull request
on:
workflow_run:
workflows: ['Build Application']
types: [completed]
jobs:
pr_comment:
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v5
with:
# This snippet is public-domain, taken from
# https://github.com/oprypin/nightly.link/blob/master/.github/workflows/pr-comment.yml
script: |
async function upsertComment(owner, repo, issue_number, purpose, body) {
const {data: comments} = await github.rest.issues.listComments(
{owner, repo, issue_number});
const marker = `<!-- bot: ${purpose} -->`;
body = marker + "\n" + body;
const existing = comments.filter((c) => c.body.includes(marker));
if (existing.length > 0) {
const last = existing[existing.length - 1];
core.info(`Updating comment ${last.id}`);
await github.rest.issues.updateComment({
owner, repo,
body,
comment_id: last.id,
});
} else {
core.info(`Creating a comment in issue / PR #${issue_number}`);
await github.rest.issues.createComment({issue_number, body, owner, repo});
}
}
const {owner, repo} = context.repo;
const run_id = ${{github.event.workflow_run.id}};
const pull_requests = ${{ toJSON(github.event.workflow_run.pull_requests) }};
if (!pull_requests.length) {
return core.error("This workflow doesn't match any pull requests!");
}
const artifacts = await github.paginate(
github.rest.actions.listWorkflowRunArtifacts, {owner, repo, run_id});
if (!artifacts.length) {
return core.error(`No artifacts found`);
}
let body = `Download the artifacts for this pull request:\n`;
for (const art of artifacts) {
body += `\n* [${art.name}.zip](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
}
core.info("Review thread message body:", body);
for (const pr of pull_requests) {
await upsertComment(owner, repo, pr.number,
"nightly-link", body);
}

View File

@ -3,35 +3,28 @@ 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:
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 }}

View File

@ -3,22 +3,15 @@ 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:
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 +20,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 +32,22 @@ 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-Portable*/PolyMC-portable.tar.gz PolyMC-Linux-Portable-${{ env.VERSION }}.tar.gz
mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz mv PolyMC-Linux*/PolyMC.tar.gz PolyMC-Linux-${{ env.VERSION }}.tar.gz
mv PrismLauncher-Linux-Qt5-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz mv PolyMC-*.AppImage/PolyMC-*.AppImage PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
mv PrismLauncher-Linux-Qt5*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz mv PolyMC-macOS*/PolyMC.tar.gz PolyMC-macOS-${{ env.VERSION }}.tar.gz
mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage
mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
mv PrismLauncher-macOS*/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)" ARCH="$(echo -n ${d} | cut -d '-' -f 3)"
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-${ARCH}"
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 +55,22 @@ jobs:
- name: Create release - name: Create release
id: create_release id: create_release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
token: ${{ secrets.GITHUB_TOKEN }}
tag_name: ${{ github.ref }} tag_name: ${{ github.ref }}
name: Prism Launcher ${{ env.VERSION }} name: 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-i686-${{ env.VERSION }}.zip
PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz PolyMC-Windows-i686-Portable-${{ env.VERSION }}.zip
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz PolyMC-Windows-i686-Setup-${{ env.VERSION }}.exe
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip PolyMC-Windows-x86_64-${{ env.VERSION }}.zip
PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip PolyMC-Windows-x86_64-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-MinGW-w64-Setup-${{ env.VERSION }}.exe PolyMC-Windows-x86_64-Setup-${{ env.VERSION }}.exe
PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.zip PolyMC-macOS-${{ env.VERSION }}.tar.gz
PrismLauncher-Windows-MSVC-arm64-Portable-${{ env.VERSION }}.zip PolyMC-${{ env.VERSION }}.tar.gz
PrismLauncher-Windows-MSVC-arm64-Setup-${{ env.VERSION }}.exe
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.zip
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

View File

@ -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

View File

@ -1,15 +0,0 @@
name: Publish to WinGet
on:
release:
types: [released]
jobs:
publish:
runs-on: windows-latest
steps:
- uses: vedantmgoyal2009/winget-releaser@v2
with:
identifier: PrismLauncher.PrismLauncher
version: ${{ github.event.release.tag_name }}
installers-regex: 'PrismLauncher-Windows-MSVC(:?-arm64|-Legacy)?-Setup-.+\.exe$'
token: ${{ secrets.WINGET_TOKEN }}

11
.gitignore vendored
View File

@ -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
View File

@ -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

View File

@ -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

View File

@ -1,2 +0,0 @@
libraries/nbtplusplus
libraries/quazip

View File

@ -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).

View File

@ -1,12 +1,19 @@
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)
include(CTest)
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)
if(IS_IN_SOURCE_BUILD) if(IS_IN_SOURCE_BUILD)
message(FATAL_ERROR "You are building the Launcher in-source. Please separate the build tree from the source tree.") message(FATAL_ERROR "You are building the Launcher in-source. Please separate the build tree from the source tree.")
endif() endif()
##################################### Set CMake options ##################################### ##################################### Set CMake options #####################################
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON) set(CMAKE_AUTORCC ON)
@ -24,61 +31,12 @@ set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/jars)
######## Set compiler flags ######## ######## Set compiler flags ########
set(CMAKE_CXX_STANDARD_REQUIRED true) set(CMAKE_CXX_STANDARD_REQUIRED true)
set(CMAKE_C_STANDARD_REQUIRED true) set(CMAKE_C_STANDARD_REQUIRED true)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 11)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
include(GenerateExportHeader) include(GenerateExportHeader)
if(MSVC) set(CMAKE_CXX_FLAGS "-Wall -pedantic -Werror -Wno-deprecated-declarations -D_GLIBCXX_USE_CXX11_ABI=0 -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,150 +44,72 @@ 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()
endif() endif()
option(BUILD_TESTING "Build the testing tree." ON)
find_package(ECM QUIET NO_MODULE)
if(NOT ECM_FOUND)
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(ECMAddTests)
if(BUILD_TESTING)
enable_testing()
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 3)
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,46 +120,34 @@ 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 "CurseForge API Key")
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)
git_get_exact_tag(Launcher_GIT_TAG)
get_git_head_revision(Launcher_GIT_REFSPEC Launcher_GIT_COMMIT) get_git_head_revision(Launcher_GIT_REFSPEC Launcher_GIT_COMMIT)
message(STATUS "Git commit: ${Launcher_GIT_COMMIT}") message(STATUS "Git commit: ${Launcher_GIT_COMMIT}")
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)
if(Launcher_QT_VERSION_MAJOR EQUAL 5) if(Launcher_QT_VERSION_MAJOR EQUAL 5)
set(QT_VERSION_MAJOR 5) set(QT_VERSION_MAJOR 5)
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml) find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml)
@ -291,68 +159,30 @@ if(Launcher_QT_VERSION_MAJOR EQUAL 5)
set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE) set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE)
set(FORCE_BUNDLED_QUAZIP 1) set(FORCE_BUNDLED_QUAZIP 1)
endif() endif()
# Qt 6 sets these by default. Notably causes Windows APIs to use UNICODE strings.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUNICODE -D_UNICODE")
elseif(Launcher_QT_VERSION_MAJOR EQUAL 6)
set(QT_VERSION_MAJOR 6)
find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml Core5Compat)
list(APPEND Launcher_QT_LIBS Qt6::Core5Compat)
if(NOT Launcher_FORCE_BUNDLED_LIBS)
find_package(QuaZip-Qt6 1.3 QUIET)
endif()
if (NOT QuaZip-Qt6_FOUND)
set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE)
set(FORCE_BUNDLED_QUAZIP 1)
endif()
else() 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) # The Qt5 cmake files don't provide its install paths, so ask qmake.
include(ECMQueryQt) include(QMakeQuery)
ecm_query_qt(QT_PLUGINS_DIR QT_INSTALL_PLUGINS) query_qmake(QT_INSTALL_PLUGINS QT_PLUGINS_DIR)
ecm_query_qt(QT_LIBS_DIR QT_INSTALL_LIBS) query_qmake(QT_INSTALL_IMPORTS QT_IMPORTS_DIR)
ecm_query_qt(QT_LIBEXECS_DIR QT_INSTALL_LIBEXECS) query_qmake(QT_INSTALL_LIBS QT_LIBS_DIR)
else() query_qmake(QT_INSTALL_LIBEXECS QT_LIBEXECS_DIR)
set(QT_PLUGINS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_PLUGINS}) query_qmake(QT_HOST_DATA QT_DATA_DIR)
set(QT_LIBS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBS}) set(QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs)
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
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)
@ -362,7 +192,6 @@ if(UNIX AND APPLE)
set(BINARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS") set(BINARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
set(LIBRARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS") set(LIBRARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
set(PLUGIN_DEST_DIR "${Launcher_Name}.app/Contents/MacOS") set(PLUGIN_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
set(FRAMEWORK_DEST_DIR "${Launcher_Name}.app/Contents/Frameworks")
set(RESOURCES_DEST_DIR "${Launcher_Name}.app/Contents/Resources") set(RESOURCES_DEST_DIR "${Launcher_Name}.app/Contents/Resources")
set(JARS_DEST_DIR "${Launcher_Name}.app/Contents/MacOS/jars") set(JARS_DEST_DIR "${Launcher_Name}.app/Contents/MacOS/jars")
@ -370,57 +199,46 @@ 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_FEED_URL "https://prismlauncher.org/feed/appcast.xml" CACHE STRING "URL for Sparkle update feed")
set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.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_DIR "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
# 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})
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}) # jars path is determined on runtime, relative to "Application root path", generally /usr or the root of the portable bundle
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${KDE_INSTALL_METAINFODIR}) set(Launcher_APP_BINARY_DEFS "-DLAUNCHER_JARS_LOCATION=${JARS_DEST_DIR}")
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION "${KDE_INSTALL_ICONDIR}/hicolor/scalable/apps")
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "share/${Launcher_Name}") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${LAUNCHER_DESKTOP_DEST_DIR})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${LAUNCHER_METAINFO_DEST_DIR})
if(Launcher_ManPage) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION ${LAUNCHER_ICON_DEST_DIR})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6") install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_ManPage} DESTINATION ${LAUNCHER_MAN_DEST_DIR} RENAME "${Launcher_APP_BINARY_NAME}.6")
endif()
# Install basic runner script if component "portable" is selected # Install basic runner script if component "portable" is selected
configure_file(launcher/Launcher.in "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" @ONLY) configure_file(launcher/Launcher.in "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" @ONLY)
@ -440,13 +258,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 +274,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.
@ -495,40 +287,16 @@ else()
message(STATUS "Using system QuaZip") message(STATUS "Using system QuaZip")
endif() endif()
add_subdirectory(libraries/rainbow) # Qt extension for colors add_subdirectory(libraries/rainbow) # Qt extension for colors
add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader
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/optional-bare)
add_subdirectory(libraries/tomlplusplus) # toml parser add_subdirectory(libraries/tomlc99) # 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/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)

View File

@ -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

View File

@ -1,69 +0,0 @@
# Contributions Guidelines
## Code formatting
Try to follow the existing formatting.
If there is no existing formatting, you may use `clang-format` with our included `.clang-format` configuration.
In general, in order of importance:
- Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
- Prefer readability over dogma.
- Keep to the existing formatting.
- Indent with 4 space unless it's in a submodule.
- Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
## Signing your work
In an effort to ensure that the code you contribute is actually compatible with the licenses in this codebase, we require you to sign-off all your contributions.
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>
Signed-off-by: Author name <Author email>
```
By signing off your work, you agree to the terms below:
```text
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(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
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
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.
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-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.

View File

@ -1,121 +1,64 @@
## Prism Launcher # PolyMC
Prism Launcher - Minecraft Launcher Copyright (C) 2012-2021 MultiMC Contributors
Copyright (C) 2022-2023 Prism Launcher Contributors Copyright (C) 2021-2022 PolyMC Contributors
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3. the Free Software Foundation, version 3.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
This file incorporates work covered by the following copyright and # Launcher (https://github.com/MultiMC/Launcher)
permission notice: Copyright 2012-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
Copyright 2013-2021 MultiMC Contributors http://www.apache.org/licenses/LICENSE-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Unless required by applicable law or agreed to in writing, software
you may not use this file except in compliance with the License. distributed under the License is distributed on an "AS IS" BASIS,
You may obtain a copy of the License at 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.
http://www.apache.org/licenses/LICENSE-2.0 # MinGW runtime (Windows)
Unless required by applicable law or agreed to in writing, software Copyright (c) 2012 MinGW.org project
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 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:
PolyMC - Minecraft Launcher The above copyright notice, this permission notice and the below disclaimer
Copyright (C) 2021-2022 PolyMC Contributors shall be included in all copies or substantial portions of the Software.
This program is free software: you can redistribute it and/or modify THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
it under the terms of the GNU General Public License as published by IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
the Free Software Foundation, version 3. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
This program is distributed in the hope that it will be useful, # Qt 5
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 Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
along with this program. If not, see <https://www.gnu.org/licenses/>. Contact: http://www.qt-project.org/legal
This file incorporates work covered by the following copyright and Licensed under LGPL v2.1
permission notice:
Copyright 2013-2021 MultiMC Contributors # libnbt++
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.
## MinGW-w64 runtime (Windows)
Copyright (c) 2009, 2010, 2011, 2012, 2013 by the mingw-w64 project
This license has been certified as open source. It has also been designated
as GPL compatible by the Free Software Foundation (FSF).
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions in source code must retain the accompanying copyright
notice, this list of conditions, and the following disclaimer.
2. Redistributions in binary form must reproduce the accompanying
copyright notice, this list of conditions, and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
3. Names of the copyright holders must not be used to endorse or promote
products derived from this software without prior written permission
from the copyright holders.
4. The right to distribute this software or to use it for any purpose does
not give you the right to use Servicemarks (sm) or Trademarks (tm) of
the copyright holders. Use of them is covered by separate agreement
with the copyright holders.
5. If any files are modified, you must cause the modified files to carry
prominent notices stating that you changed the files and the date of
any change.
Disclaimer
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED
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 HOLDERS 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.
Information on third party licenses used in MinGW-w64 can be found in its COPYING.MinGW-w64-runtime.txt.
## Qt 5/6
Copyright (C) 2022 The Qt Company Ltd and other contributors.
Contact: https://www.qt.io/licensing
Licensed under LGPL v3
## 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 +76,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 +99,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 +133,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,82 +144,76 @@
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-2011 Sergey A. Tachenov
The QuaZip library is licensed under the GNU Lesser General Public This program is free software; you can redistribute it and/or modify it
License V2.1 plus a static linking exception. under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at
your option) any later version.
The original ZIP/UNZIP package (MiniZip) is copyrighted by Gilles This program is distributed in the hope that it will be useful, but
Vollant and contributors, see quazip/(un)zip.h files for details. WITHOUT ANY WARRANTY; without even the implied warranty of
Basically it's the zlib license. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
General Public License for more details.
STATIC LINKING EXCEPTION You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software Foundation,
The copyright holders give you permission to link this library with Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
independent modules to produce an executable, regardless of the license
terms of these independent modules, and to copy and distribute the
resulting executable under terms of your choice, provided that you also
meet, for each linked independent module, the terms and conditions of
the license of that module. An independent module is a module which is
not derived from or based on this library. If you modify this library,
you must extend this exception to your version of the library.
See COPYING file for the full LGPL text. See COPYING file for the full LGPL text.
## launcher (`libraries/launcher`) Original ZIP package is copyrighted by Gilles Vollant, see
quazip/(un)zip.h files for details, basically it's zlib license.
PolyMC - Minecraft Launcher # xz-minidec
Copyright (C) 2021-2022 PolyMC Contributors
This program is free software: you can redistribute it and/or modify XZ decompressor
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, Authors: Lasse Collin <lasse.collin@tukaani.org>
but WITHOUT ANY WARRANTY; without even the implied warranty of Igor Pavlov <http://7-zip.org/>
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Linking this library statically or dynamically with other modules is This file has been put into the public domain.
making a combined work based on this library. Thus, the terms and You can do whatever you want with this file.
conditions of the GNU General Public License cover the whole
combination.
As a special exception, the copyright holders of this library give # ColumnResizer
you permission to link this library with independent modules to
produce an executable, regardless of the license terms of these
independent modules, and to copy and distribute the resulting
executable under terms of your choice, provided that you also meet,
for each linked independent module, the terms and conditions of the
license of that module. An independent module is a module which is
not derived from or based on this library. If you modify this
library, you may extend this exception to your version of the
library, but you are not obliged to do so. If you do not wish to do
so, delete this exception statement from your version.
You should have received a copy of the GNU General Public License Copyright (c) 2011-2016 Aurélien Gâteau and contributors.
along with this program. If not, see <https://www.gnu.org/licenses/>.
This file incorporates work covered by the following copyright and All rights reserved.
permission notice:
Copyright 2013-2021 MultiMC Contributors 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:
Licensed under the Apache License, Version 2.0 (the "License"); * Redistributions of source code must retain the above copyright
you may not use this file except in compliance with the License. notice, this list of conditions and the following disclaimer.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 * 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.
Unless required by applicable law or agreed to in writing, software * The name of the contributors may not be used to endorse or
distributed under the License is distributed on an "AS IS" BASIS, promote products derived from this software without specific prior
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. written permission.
See the License for the specific language governing permissions and
limitations under the License.
## lionshead 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.
# 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 +240,60 @@
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 # optional-bare
Code from https://github.com/martinmoene/optional-bare/
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
# 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.
@ -358,96 +318,3 @@
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 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 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. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
## Gamemode
Copyright (c) 2017-2022, Feral Interactive
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted 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.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
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.
## 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/>.

128
README.md
View File

@ -1,112 +1,92 @@
<p align="center"> <p align="center">
<picture> <img src="./program_info/polymc-header-black.svg#gh-light-mode-only" alt="PolyMC logo"/>
<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"/>
<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>
<br>
<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 ## Code formatting
We thank all the wonderful backers over at Open Collective! Support Prism Launcher by [becoming a backer](https://opencollective.com/prismlauncher). Just follow the existing formatting.
[![OpenCollective Backers](https://opencollective.com/prismlauncher/backers.svg?width=890&limit=1000)](https://opencollective.com/prismlauncher#backers) In general, in order of importance:
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/). - Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
- Prefer readability over dogma.
- Keep to the existing formatting.
- Indent with 4 space unless it's in a submodule.
- Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
[![JetBrains](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/opensource/) ## Translations
Thanks to Weblate for hosting our translation efforts. 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
<a href="https://hosted.weblate.org/engage/prismlauncher/"> ## Download information
<img src="https://hosted.weblate.org/widgets/prismlauncher/-/open-graph.png" alt="Translation status" width="300" /> 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/blob/master/src/download.md)
</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: Do whatever you want, we don't care. Just follow the license. If you have any questions about this feel free to ask in an issue.
- 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>). 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:
- 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). - [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 have any questions or want any clarification on the above conditions please make an issue and ask us. If you do not agree with these terms and conditions, then remove the associated API keys from the [CMakeLists.txt](CMakeLists.txt) file.
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`.
Note that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions:
- [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
- [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 (`""`).
## 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.

View File

@ -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,48 +55,25 @@ 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_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
{
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@";
GIT_TAG = "@Launcher_GIT_TAG@";
GIT_REFSPEC = "@Launcher_GIT_REFSPEC@"; GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";
if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND"))
// Assume that builds outside of Git repos are "stable"
if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND")
|| GIT_TAG == QStringLiteral("GITDIR-NOTFOUND")
|| GIT_REFSPEC == QStringLiteral("")
|| GIT_TAG == QStringLiteral("GIT-NOTFOUND"))
{ {
GIT_REFSPEC = "refs/heads/stable"; VERSION_CHANNEL = QStringLiteral("stable");
GIT_TAG = versionString();
GIT_COMMIT = "";
} }
else 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())
{ {
@ -107,20 +81,18 @@ Config::Config()
} }
else else
{ {
VERSION_CHANNEL = "unknown"; VERSION_CHANNEL = QObject::tr("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@";
IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@"; IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@"; MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@";
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@"; CURSEFORGE_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@";
@ -128,32 +100,20 @@ Config::Config()
SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@"; SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@";
} }
QString Config::versionString() const
{
return QString("%1.%2").arg(VERSION_MAJOR).arg(VERSION_MINOR);
}
QString Config::printableVersionString() const QString Config::printableVersionString() const
{ {
QString vstr = versionString(); QString vstr = QString("%1.%2.%3").arg(QString::number(VERSION_MAJOR), QString::number(VERSION_MINOR), QString::number(VERSION_HOTFIX));
// If the build is not a main release, append the channel // If the build is not a main release, append the channel
if(VERSION_CHANNEL != "stable" && GIT_TAG != vstr) if(VERSION_CHANNEL != "stable")
{ {
vstr += "-" + VERSION_CHANNEL; vstr += "-" + VERSION_CHANNEL;
} }
// if a build number is set, also add it to the end
if(VERSION_BUILD >= 0)
{
vstr += "-" + 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);
}

View File

@ -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,38 +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
QString MAC_SPARKLE_PUB_KEY;
/// URL for the Sparkle updater's appcast
QString MAC_SPARKLE_APPCAST_URL;
/// User-Agent to use. /// User-Agent to use.
QString USER_AGENT; QString USER_AGENT;
@ -110,12 +83,12 @@ class Config {
/// The git commit hash of this build /// The git commit hash of this build
QString GIT_COMMIT; QString GIT_COMMIT;
/// The git tag of this build
QString GIT_TAG;
/// 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"
@ -145,16 +118,13 @@ class Config {
/** /**
* Client API key for CurseForge * Client API key for CurseForge
*/ */
QString FLAME_API_KEY; QString CURSEFORGE_API_KEY;
/** /**
* Metadata repository URL prefix * Metadata repository URL prefix
*/ */
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 +135,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,28 +153,12 @@ 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;
/** /**
* \brief Converts the Version to a string. * \brief Converts the Version to a string.
* \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;

View File

@ -7,5 +7,5 @@ add_library(BuildConfig STATIC
${CMAKE_CURRENT_BINARY_DIR}/BuildConfig.cpp ${CMAKE_CURRENT_BINARY_DIR}/BuildConfig.cpp
) )
target_link_libraries(BuildConfig Qt${QT_VERSION_MAJOR}::Core) target_link_libraries(BuildConfig Qt5::Core)
target_include_directories(BuildConfig PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") target_include_directories(BuildConfig PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")

View File

@ -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()

View File

@ -1,100 +0,0 @@
# SPDX-FileCopyrightText: 2014 Rohan Garg <rohan16garg@gmail.com>
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
# SPDX-FileCopyrightText: 2014-2016 Aleix Pol <aleixpol@kde.org>
# SPDX-FileCopyrightText: 2017 Friedrich W. H. Kossebau <kossebau@kde.org>
# SPDX-FileCopyrightText: 2022 Ahmad Samir <a.samir78@gmail.com>
#
# SPDX-License-Identifier: BSD-3-Clause
#[=======================================================================[.rst:
ECMQueryQt
---------------
This module can be used to query the installation paths used by Qt.
For Qt5 this uses ``qmake``, and for Qt6 this used ``qtpaths`` (the latter has built-in
support to query the paths of a target platform when cross-compiling).
This module defines the following function:
::
ecm_query_qt(<result_variable> <qt_variable> [TRY])
Passing ``TRY`` will result in the method not making the build fail if the executable
used for querying has not been found, but instead simply print a warning message and
return an empty string.
Example usage:
.. code-block:: cmake
include(ECMQueryQt)
ecm_query_qt(bin_dir QT_INSTALL_BINS)
If the call succeeds ``${bin_dir}`` will be set to ``<prefix>/path/to/bin/dir`` (e.g.
``/usr/lib64/qt/bin/``).
Since: 5.93
#]=======================================================================]
include(${CMAKE_CURRENT_LIST_DIR}/QtVersionOption.cmake)
include(CheckLanguage)
check_language(CXX)
if (CMAKE_CXX_COMPILER)
# Enable the CXX language to let CMake look for config files in library dirs.
# See: https://gitlab.kitware.com/cmake/cmake/-/issues/23266
enable_language(CXX)
endif()
if (QT_MAJOR_VERSION STREQUAL "5")
# QUIET to accommodate the TRY option
find_package(Qt${QT_MAJOR_VERSION}Core QUIET)
if(TARGET Qt5::qmake)
get_target_property(_qmake_executable_default Qt5::qmake LOCATION)
set(QUERY_EXECUTABLE ${_qmake_executable_default}
CACHE FILEPATH "Location of the Qt5 qmake executable")
set(_exec_name_text "Qt5 qmake")
set(_cli_option "-query")
endif()
elseif(QT_MAJOR_VERSION STREQUAL "6")
# QUIET to accommodate the TRY option
find_package(Qt6 COMPONENTS CoreTools QUIET CONFIG)
if (TARGET Qt6::qtpaths)
get_target_property(_qtpaths_executable Qt6::qtpaths LOCATION)
set(QUERY_EXECUTABLE ${_qtpaths_executable}
CACHE FILEPATH "Location of the Qt6 qtpaths executable")
set(_exec_name_text "Qt6 qtpaths")
set(_cli_option "--query")
endif()
endif()
function(ecm_query_qt result_variable qt_variable)
set(options TRY)
set(oneValueArgs)
set(multiValueArgs)
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT QUERY_EXECUTABLE)
if(ARGS_TRY)
set(${result_variable} "" PARENT_SCOPE)
message(STATUS "No ${_exec_name_text} executable found. Can't check ${qt_variable}")
return()
else()
message(FATAL_ERROR "No ${_exec_name_text} executable found. Can't check ${qt_variable} as required")
endif()
endif()
execute_process(
COMMAND ${QUERY_EXECUTABLE} ${_cli_option} "${qt_variable}"
RESULT_VARIABLE return_code
OUTPUT_VARIABLE output
)
if(return_code EQUAL 0)
string(STRIP "${output}" output)
file(TO_CMAKE_PATH "${output}" output_path)
set(${result_variable} "${output_path}" PARENT_SCOPE)
else()
message(WARNING "Failed call: ${_command} \"${qt_variable}\"")
message(FATAL_ERROR "${_exec_name_text} call failed: ${return_code}")
endif()
endfunction()

View File

@ -3,7 +3,7 @@
# These functions force a re-configure on each git commit so that you can # These functions force a re-configure on each git commit so that you can
# trust the values of the variables in your build system. # trust the values of the variables in your build system.
# #
# get_git_head_revision(<refspecvar> <hashvar> [ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR]) # get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
# #
# Returns the refspec and sha hash of the current head revision # Returns the refspec and sha hash of the current head revision
# #
@ -12,33 +12,20 @@
# Returns the results of git describe on the source tree, and adjusting # Returns the results of git describe on the source tree, and adjusting
# the output so that it tests false if an error occurs. # the output so that it tests false if an error occurs.
# #
# git_describe_working_tree(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe on the working tree (--dirty option),
# and adjusting the output so that it tests false if an error occurs.
#
# git_get_exact_tag(<var> [<additional arguments to git describe> ...]) # git_get_exact_tag(<var> [<additional arguments to git describe> ...])
# #
# Returns the results of git describe --exact-match on the source tree, # Returns the results of git describe --exact-match on the source tree,
# and adjusting the output so that it tests false if there was no exact # and adjusting the output so that it tests false if there was no exact
# matching tag. # matching tag.
# #
# git_local_changes(<var>)
#
# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes.
# Uses the return code of "git diff-index --quiet HEAD --".
# Does not regard untracked files.
#
# Requires CMake 2.6 or newer (uses the 'function' command) # Requires CMake 2.6 or newer (uses the 'function' command)
# #
# Original Author: # Original Author:
# 2009-2020 Ryan Pavlik <ryan.pavlik@gmail.com> <abiryan@ryand.net> # 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com # http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
# #
# Copyright 2009-2013, Iowa State University. # Copyright Iowa State University 2009-2010.
# Copyright 2013-2020, Ryan Pavlik
# Copyright 2013-2020, Contributors
# SPDX-License-Identifier: BSL-1.0
# Distributed under the Boost Software License, Version 1.0. # Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at # (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt) # http://www.boost.org/LICENSE_1_0.txt)
@ -52,124 +39,45 @@ set(__get_git_revision_description YES)
# to find the path to this module rather than the path to a calling list file # to find the path to this module rather than the path to a calling list file
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
# Function _git_find_closest_git_dir finds the next closest .git directory function(get_git_head_revision _refspecvar _hashvar)
# that is part of any directory in the path defined by _start_dir. set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
# The result is returned in the parent scope variable whose name is passed set(GIT_DIR "${GIT_PARENT_DIR}/.git")
# as variable _git_dir_var. If no .git directory can be found, the while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories
# function returns an empty string via _git_dir_var. set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
# get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
# neither foo nor bar contain a file/directory .git. This wil return
# C:/bla/.git
#
function(_git_find_closest_git_dir _start_dir _git_dir_var)
set(cur_dir "${_start_dir}")
set(git_dir "${_start_dir}/.git")
while(NOT EXISTS "${git_dir}")
# .git dir not found, search parent directories
set(git_previous_parent "${cur_dir}")
get_filename_component(cur_dir "${cur_dir}" DIRECTORY)
if(cur_dir STREQUAL git_previous_parent)
# We have reached the root directory, we are not in git # We have reached the root directory, we are not in git
set(${_git_dir_var} set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
"" set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
PARENT_SCOPE)
return() return()
endif() endif()
set(git_dir "${cur_dir}/.git") set(GIT_DIR "${GIT_PARENT_DIR}/.git")
endwhile() endwhile()
set(${_git_dir_var} # check if this is a submodule
"${git_dir}"
PARENT_SCOPE)
endfunction()
function(get_git_head_revision _refspecvar _hashvar)
_git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR)
if("${ARGN}" STREQUAL "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR")
set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR TRUE)
else()
set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR FALSE)
endif()
if(NOT "${GIT_DIR}" STREQUAL "")
file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}"
"${GIT_DIR}")
if("${_relative_to_source_dir}" MATCHES "[.][.]" AND NOT ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR)
# We've gone above the CMake root dir.
set(GIT_DIR "")
endif()
endif()
if("${GIT_DIR}" STREQUAL "")
set(${_refspecvar}
"GITDIR-NOTFOUND"
PARENT_SCOPE)
set(${_hashvar}
"GITDIR-NOTFOUND"
PARENT_SCOPE)
return()
endif()
# Check if the current source dir is a git submodule or a worktree.
# In both cases .git is a file instead of a directory.
#
if(NOT IS_DIRECTORY ${GIT_DIR}) if(NOT IS_DIRECTORY ${GIT_DIR})
# The following git command will return a non empty string that file(READ ${GIT_DIR} submodule)
# points to the super project working tree if the current string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule})
# source dir is inside a git submodule. get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
# Otherwise the command will return an empty string. get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
#
execute_process(
COMMAND "${GIT_EXECUTABLE}" rev-parse
--show-superproject-working-tree
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT "${out}" STREQUAL "")
# If out is empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule
file(READ ${GIT_DIR} submodule)
string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE
${submodule})
string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE)
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE}
ABSOLUTE)
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
else()
# GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree
file(READ ${GIT_DIR} worktree_ref)
# The .git directory contains a path to the worktree information directory
# inside the parent git repo of the worktree.
#
string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir
${worktree_ref})
string(STRIP ${git_worktree_dir} git_worktree_dir)
_git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR)
set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD")
endif()
else()
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
endif() endif()
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
if(NOT EXISTS "${GIT_DATA}") if(NOT EXISTS "${GIT_DATA}")
file(MAKE_DIRECTORY "${GIT_DATA}") file(MAKE_DIRECTORY "${GIT_DATA}")
endif() endif()
if(NOT EXISTS "${HEAD_SOURCE_FILE}") if(NOT EXISTS "${GIT_DIR}/HEAD")
return() return()
endif() endif()
set(HEAD_FILE "${GIT_DATA}/HEAD") set(HEAD_FILE "${GIT_DATA}/HEAD")
configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY) configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
"${GIT_DATA}/grabRef.cmake" @ONLY) "${GIT_DATA}/grabRef.cmake"
@ONLY)
include("${GIT_DATA}/grabRef.cmake") include("${GIT_DATA}/grabRef.cmake")
set(${_refspecvar} set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
"${HEAD_REF}" set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
PARENT_SCOPE)
set(${_hashvar}
"${HEAD_HASH}"
PARENT_SCOPE)
endfunction() endfunction()
function(git_describe _var) function(git_describe _var)
@ -178,107 +86,45 @@ function(git_describe _var)
endif() endif()
get_git_head_revision(refspec hash) get_git_head_revision(refspec hash)
if(NOT GIT_FOUND) if(NOT GIT_FOUND)
set(${_var} set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
"GIT-NOTFOUND"
PARENT_SCOPE)
return() return()
endif() endif()
if(NOT hash) if(NOT hash)
set(${_var} set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
"HEAD-HASH-NOTFOUND"
PARENT_SCOPE)
return() return()
endif() endif()
# TODO sanitize # TODO sanitize
#if((${ARGN}" MATCHES "&&") OR #if((${ARGN}" MATCHES "&&") OR
# (ARGN MATCHES "||") OR # (ARGN MATCHES "||") OR
# (ARGN MATCHES "\\;")) # (ARGN MATCHES "\\;"))
# message("Please report the following error to the project!") # message("Please report the following error to the project!")
# message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
#endif() #endif()
#message(STATUS "Arguments to execute_process: ${ARGN}") #message(STATUS "Arguments to execute_process: ${ARGN}")
execute_process( execute_process(COMMAND
COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN} "${GIT_EXECUTABLE}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" describe
RESULT_VARIABLE res ${hash}
OUTPUT_VARIABLE out ${ARGN}
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) WORKING_DIRECTORY
"${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE
res
OUTPUT_VARIABLE
out
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0) if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND") set(out "${out}-${res}-NOTFOUND")
endif() endif()
set(${_var} set(${_var} "${out}" PARENT_SCOPE)
"${out}"
PARENT_SCOPE)
endfunction()
function(git_describe_working_tree _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
if(NOT GIT_FOUND)
set(${_var}
"GIT-NOTFOUND"
PARENT_SCOPE)
return()
endif()
execute_process(
COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE res
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var}
"${out}"
PARENT_SCOPE)
endfunction() endfunction()
function(git_get_exact_tag _var) function(git_get_exact_tag _var)
git_describe(out --exact-match ${ARGN}) git_describe(out --exact-match ${ARGN})
set(${_var} set(${_var} "${out}" PARENT_SCOPE)
"${out}"
PARENT_SCOPE)
endfunction()
function(git_local_changes _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
get_git_head_revision(refspec hash)
if(NOT GIT_FOUND)
set(${_var}
"GIT-NOTFOUND"
PARENT_SCOPE)
return()
endif()
if(NOT hash)
set(${_var}
"HEAD-HASH-NOTFOUND"
PARENT_SCOPE)
return()
endif()
execute_process(
COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD --
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE res
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(res EQUAL 0)
set(${_var}
"CLEAN"
PARENT_SCOPE)
else()
set(${_var}
"DIRTY"
PARENT_SCOPE)
endif()
endfunction() endfunction()

View File

@ -8,12 +8,10 @@
# http://academic.cleardefinition.com # http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC # Iowa State University HCI Graduate Program/VRAC
# #
# Copyright 2009-2012, Iowa State University # Copyright Iowa State University 2009-2010.
# Copyright 2011-2015, Contributors
# Distributed under the Boost Software License, Version 1.0. # Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at # (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt) # http://www.boost.org/LICENSE_1_0.txt)
# SPDX-License-Identifier: BSL-1.0
set(HEAD_HASH) set(HEAD_HASH)
@ -21,23 +19,23 @@ file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
if(HEAD_CONTENTS MATCHES "ref") if(HEAD_CONTENTS MATCHES "ref")
# named branch # named branch
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
if(EXISTS "@GIT_DIR@/${HEAD_REF}") if(EXISTS "@GIT_DIR@/${HEAD_REF}")
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
else() else()
configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}")
set(HEAD_HASH "${CMAKE_MATCH_1}") set(HEAD_HASH "${CMAKE_MATCH_1}")
endif() endif()
endif() endif()
else() else()
# detached HEAD # detached HEAD
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
endif() endif()
if(NOT HEAD_HASH) if(NOT HEAD_HASH)
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
string(STRIP "${HEAD_HASH}" HEAD_HASH) string(STRIP "${HEAD_HASH}" HEAD_HASH)
endif() endif()

View File

@ -40,43 +40,5 @@
<true/> <true/>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string> <string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>SUPublicEDKey</key>
<string>${MACOSX_SPARKLE_UPDATE_PUBLIC_KEY}</string>
<key>SUFeedURL</key>
<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>

14
cmake/QMakeQuery.cmake Normal file
View File

@ -0,0 +1,14 @@
if(__QMAKEQUERY_CMAKE__)
return()
endif()
set(__QMAKEQUERY_CMAKE__ TRUE)
get_target_property(QMAKE_EXECUTABLE Qt5::qmake LOCATION)
function(QUERY_QMAKE VAR RESULT)
exec_program(${QMAKE_EXECUTABLE} ARGS "-query ${VAR}" RETURN_VALUE return_code OUTPUT_VARIABLE output )
if(NOT return_code)
file(TO_CMAKE_PATH "${output}" output)
set(${RESULT} ${output} PARENT_SCOPE)
endif(NOT return_code)
endfunction(QUERY_QMAKE)

View File

@ -1,38 +0,0 @@
#.rst:
# QtVersionOption
# ---------------
#
# Adds a build option to select the major Qt version if necessary,
# that is, if the major Qt version has not yet been determined otherwise
# (e.g. by a corresponding find_package() call).
#
# This module is typically included by other modules requiring knowledge
# about the major Qt version.
#
# ``QT_MAJOR_VERSION`` is defined to either be "5" or "6".
#
#
# Since 5.82.0.
#=============================================================================
# SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
#
# SPDX-License-Identifier: BSD-3-Clause
if (DEFINED QT_MAJOR_VERSION)
return()
endif()
if (TARGET Qt5::Core)
set(QT_MAJOR_VERSION 5)
elseif (TARGET Qt6::Core)
set(QT_MAJOR_VERSION 6)
else()
option(BUILD_WITH_QT6 "Build against Qt 6" OFF)
if (BUILD_WITH_QT6)
set(QT_MAJOR_VERSION 6)
else()
set(QT_MAJOR_VERSION 5)
endif()
endif()

View File

@ -1,97 +0,0 @@
#=============================================================================
# Copyright 2005-2011 Kitware, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted 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.
#
# * Neither the name of Kitware, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# 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
# HOLDER 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.
#=============================================================================
# From Qt5CoreMacros.cmake
function(qt_generate_moc)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_generate_moc(${ARGV})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_generate_moc(${ARGV})
endif()
endfunction()
function(qt_wrap_cpp outfiles)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_wrap_cpp("${outfiles}" ${ARGN})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_wrap_cpp("${outfiles}" ${ARGN})
endif()
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
endfunction()
function(qt_add_binary_resources)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_add_binary_resources(${ARGV})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_add_binary_resources(${ARGV})
endif()
endfunction()
function(qt_add_resources outfiles)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_add_resources("${outfiles}" ${ARGN})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_add_resources("${outfiles}" ${ARGN})
endif()
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
endfunction()
function(qt_add_big_resources outfiles)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_add_big_resources(${outfiles} ${ARGN})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_add_big_resources(${outfiles} ${ARGN})
endif()
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
endfunction()
function(qt_import_plugins)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_import_plugins(${ARGV})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_import_plugins(${ARGV})
endif()
endfunction()
# From Qt5WidgetsMacros.cmake
function(qt_wrap_ui outfiles)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_wrap_ui("${outfiles}" ${ARGN})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_wrap_ui("${outfiles}" ${ARGN})
endif()
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
endfunction()

50
cmake/UnitTest.cmake Normal file
View File

@ -0,0 +1,50 @@
find_package(Qt5Test REQUIRED)
set(TEST_RESOURCE_PATH ${CMAKE_CURRENT_LIST_DIR})
message(${TEST_RESOURCE_PATH})
function(add_unit_test name)
if(BUILD_TESTING)
set(options "")
set(oneValueArgs DATA)
set(multiValueArgs SOURCES LIBS)
cmake_parse_arguments(OPT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
if(WIN32)
add_executable(${name}_test ${OPT_SOURCES} ${TEST_RESOURCE_PATH}/UnitTest/test.rc)
else()
add_executable(${name}_test ${OPT_SOURCES})
endif()
if(NOT "${OPT_DATA}" STREQUAL "")
set(TEST_DATA_PATH "${CMAKE_CURRENT_BINARY_DIR}/data")
set(TEST_DATA_PATH_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${OPT_DATA}")
message("From ${TEST_DATA_PATH_SRC} to ${TEST_DATA_PATH}")
string(REGEX REPLACE "[/\\:]" "_" DATA_TARGET_NAME "${TEST_DATA_PATH_SRC}")
if(UNIX)
# on unix we get the third / from the filename
set(TEST_DATA_URL "file://${TEST_DATA_PATH}")
else()
# we don't on windows, so we have to add it ourselves
set(TEST_DATA_URL "file:///${TEST_DATA_PATH}")
endif()
if(NOT TARGET "${DATA_TARGET_NAME}")
add_custom_target(${DATA_TARGET_NAME})
add_dependencies(${name}_test ${DATA_TARGET_NAME})
add_custom_command(
TARGET ${DATA_TARGET_NAME}
COMMAND ${CMAKE_COMMAND} "-DTEST_DATA_URL=${TEST_DATA_URL}" -DSOURCE=${TEST_DATA_PATH_SRC} -DDESTINATION=${TEST_DATA_PATH} -P ${TEST_RESOURCE_PATH}/UnitTest/generate_test_data.cmake
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
endif()
endif()
target_link_libraries(${name}_test Qt5::Test ${OPT_LIBS})
target_include_directories(${name}_test PRIVATE "${TEST_RESOURCE_PATH}/UnitTest/")
add_test(NAME ${name} COMMAND ${name}_test WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
endif()
endfunction()

28
cmake/UnitTest/TestUtil.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include <QFile>
#include <QCoreApplication>
#include <QTest>
#include <QDir>
#define expandstr(s) expandstr2(s)
#define expandstr2(s) #s
class TestsInternal
{
public:
static QByteArray readFile(const QString &fileName)
{
QFile f(fileName);
f.open(QFile::ReadOnly);
return f.readAll();
}
static QString readFileUtf8(const QString &fileName)
{
return QString::fromUtf8(readFile(fileName));
}
};
#define GET_TEST_FILE(file) TestsInternal::readFile(QFINDTESTDATA(file))
#define GET_TEST_FILE_UTF8(file) TestsInternal::readFileUtf8(QFINDTESTDATA(file))

View File

@ -0,0 +1,23 @@
# Copy files from source directory to destination directory, substituting any
# variables. Create destination directory if it does not exist.
function(configure_files srcDir destDir)
make_directory(${destDir})
file(GLOB templateFiles RELATIVE ${srcDir} ${srcDir}/*)
foreach(templateFile ${templateFiles})
set(srcTemplatePath ${srcDir}/${templateFile})
if(NOT IS_DIRECTORY ${srcTemplatePath})
configure_file(
${srcTemplatePath}
${destDir}/${templateFile}
@ONLY
NEWLINE_STYLE LF
)
else()
configure_files("${srcTemplatePath}" "${destDir}/${templateFile}")
endif()
endforeach()
endfunction()
configure_files(${SOURCE} ${DESTINATION})

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity name="Launcher.Test.0" type="win32" version="5.0.0.0" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"/>
</dependentAssembly>
</dependency>
<description>Custom Minecraft launcher for managing multiple installs.</description>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!--The ID below indicates app support for Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!--The ID below indicates app support for Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!--The ID below indicates app support for Windows Developer Preview / Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
</application>
</compatibility>
</assembly>

28
cmake/UnitTest/test.rc Normal file
View File

@ -0,0 +1,28 @@
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
1 RT_MANIFEST "test.manifest"
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,0
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "000004b0"
BEGIN
VALUE "CompanyName", "MultiMC & PolyMC Contributors"
VALUE "FileDescription", "Testcase"
VALUE "FileVersion", "1.0.0.0"
VALUE "ProductName", "Launcher Testcase"
VALUE "ProductVersion", "5"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0000, 0x04b0 // Unicode
END
END

View File

@ -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

View File

@ -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": 1591558203,
"narHash": "sha256-A5kTgICnx+Qdq3Fir/bKTfdTt/T1NQP2SC+nhN1ENug=", "narHash": "sha256-QgvNvaoFflCXEPCCFBCeZvYTpuiwScBG7EosUgFwFNQ=",
"owner": "PrismLauncher", "owner": "multimc",
"repo": "libnbtplusplus", "repo": "libnbtplusplus",
"rev": "a5e8fd52b8bf4ab5d5bcc042b2a247867589985f", "rev": "dc72a20b7efd304d12af2025223fad07b4b78464",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "PrismLauncher", "owner": "multimc",
"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": 1654665288,
"narHash": "sha256-YLZ5/KKZ1PyLrm2MO8UxRe4H3M0/oaYqNhSlq6FDeeA=", "narHash": "sha256-7blJpfoZEu7GKb84uh3io/5eSJNdaagXD9d15P9iQMs=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "9d5d25bbfe8c0297ebe85324addcb5020ed1a454", "rev": "43ecbe7840d155fa933ee8a500fb00dbbc651fc8",
"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"
} }
} }
}, },

View File

@ -3,42 +3,37 @@
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:multimc/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, # Generate a 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 # System types to support (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 system types.
"aarch64-darwin" pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system});
]; in
{
packages = forAllSystems (system: rec {
polymc = pkgs.${system}.libsForQt5.callPackage ./nix { inherit version self libnbtplusplus; };
polymc-qt6 = pkgs.${system}.qt6Packages.callPackage ./nix { inherit version self libnbtplusplus; };
default = polymc;
});
defaultPackage = forAllSystems (system: self.packages.${system}.default);
apps = forAllSystems (system: rec { polymc = { type = "app"; program = "${self.defaultPackage.${system}}/bin/polymc"; }; default = polymc; });
defaultApp = forAllSystems (system: self.apps.${system}.default);
overlay = final: prev: { polymc = self.defaultPackage.${final.system}; };
}; };
} }

View File

@ -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"
]
}

View File

@ -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

View File

@ -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,

View File

@ -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;
}

View File

@ -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)

View File

@ -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 *

View File

@ -1,4 +0,0 @@
#!/bin/sh
export __NV_PRIME_RENDER_OFFLOAD=1 __VK_LAYER_NV_optimus=NVIDIA_only __GLX_VENDOR_LIBRARY_NAME=nvidia
exec "$@"

View File

@ -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

View File

@ -1,6 +0,0 @@
builds:
exclude: []
include:
- "checks.x86_64-linux.*"
- "devShells.*.*"
- "packages.*.*"

File diff suppressed because it is too large Load Diff

View File

@ -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,89 @@ 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,
enum Capability { Failed,
None = 0, Succeeded,
Initialized
SupportsMSA = 1 << 0,
SupportsFlame = 1 << 1,
SupportsGameMode = 1 << 2,
SupportsMangoHud = 1 << 3,
}; };
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,98 +154,75 @@ class Application : public QApplication {
shared_qobject_ptr<Meta::Index> metadataIndex(); shared_qobject_ptr<Meta::Index> metadataIndex();
void updateCapabilities(); QString getJarsPath();
void detectLibraries();
/*!
* Finds and returns the full path to a jar file.
* Returns a null-string if it could not be found.
*/
QString getJarPath(QString jarFile);
QString getMSAClientID(); QString getMSAClientID();
QString getFlameAPIKey(); QString getCurseKey();
QString getModrinthAPIToken();
QString getUserAgent();
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 +234,15 @@ 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;
QString m_jarsPath;
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 +255,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 +266,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;
}; };

View File

@ -1,67 +1,31 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* 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 "ApplicationMessage.h" #include "ApplicationMessage.h"
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include "Json.h"
void ApplicationMessage::parse(const QByteArray& input) void ApplicationMessage::parse(const QByteArray & input) {
{ auto doc = QJsonDocument::fromBinaryData(input);
auto doc = Json::requireDocument(input, "ApplicationMessage"); auto root = doc.object();
auto root = Json::requireObject(doc, "ApplicationMessage");
command = root.value("command").toString(); command = root.value("command").toString();
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);
return Json::toText(root); QJsonDocument out;
out.setObject(root);
return out.toBinaryData();
} }

View File

@ -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);
}; };

View File

@ -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/");
} }

View File

@ -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;
}; };

View File

@ -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 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 +35,38 @@
#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 "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);
@ -93,16 +80,6 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
m_settings->registerPassthrough(globalSettings->getSetting("ConsoleMaxLines"), nullptr); m_settings->registerPassthrough(globalSettings->getSetting("ConsoleMaxLines"), nullptr);
m_settings->registerPassthrough(globalSettings->getSetting("ConsoleOverflowStop"), nullptr); m_settings->registerPassthrough(globalSettings->getSetting("ConsoleOverflowStop"), nullptr);
// Managed Packs
m_settings->registerSetting("ManagedPack", false);
m_settings->registerSetting("ManagedPackType", "");
m_settings->registerSetting("ManagedPackID", "");
m_settings->registerSetting("ManagedPackName", "");
m_settings->registerSetting("ManagedPackVersionID", "");
m_settings->registerSetting("ManagedPackVersionName", "");
m_settings->registerSetting("Profiler", "");
} }
QString BaseInstance::getPreLaunchCommand() QString BaseInstance::getPreLaunchCommand()
@ -120,66 +97,13 @@ QString BaseInstance::getPostExitCommand()
return settings()->get("PostExitCommand").toString(); return settings()->get("PostExitCommand").toString();
} }
bool BaseInstance::isManagedPack() const
{
return m_settings->get("ManagedPack").toBool();
}
QString BaseInstance::getManagedPackType() const
{
return m_settings->get("ManagedPackType").toString();
}
QString BaseInstance::getManagedPackID() const
{
return m_settings->get("ManagedPackID").toString();
}
QString BaseInstance::getManagedPackName() const
{
return m_settings->get("ManagedPackName").toString();
}
QString BaseInstance::getManagedPackVersionID() const
{
return m_settings->get("ManagedPackVersionID").toString();
}
QString BaseInstance::getManagedPackVersionName() const
{
return m_settings->get("ManagedPackVersionName").toString();
}
void BaseInstance::setManagedPack(const QString& type,
const QString& id,
const QString& name,
const QString& versionId,
const QString& version)
{
m_settings->set("ManagedPack", true);
m_settings->set("ManagedPackType", type);
m_settings->set("ManagedPackID", id);
m_settings->set("ManagedPackName", name);
m_settings->set("ManagedPackVersionID", versionId);
m_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 +112,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 +132,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 +156,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 +187,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 +198,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 +222,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 +244,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 +262,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 +274,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 +286,11 @@ QString BaseInstance::name() const
QString BaseInstance::windowTitle() const QString BaseInstance::windowTitle() const
{ {
return BuildConfig.LAUNCHER_DISPLAYNAME + ": " + name(); return BuildConfig.LAUNCHER_NAME + ": " + name().replace(QRegExp("[ \n\r\t]+"), " ");
} }
// 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 +299,3 @@ shared_qobject_ptr<LaunchTask> BaseInstance::getLaunchTask()
{ {
return m_launchProcess; return m_launchProcess;
} }
void BaseInstance::updateRuntimeContext()
{
// NOOP
}

View File

@ -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 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 +36,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 +60,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 +70,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 +115,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 +139,16 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
QString getPostExitCommand(); QString getPostExitCommand();
QString getWrapperCommand(); QString getWrapperCommand();
bool isManagedPack() const;
QString getManagedPackType() const;
QString getManagedPackID() const;
QString getManagedPackName() const;
QString getManagedPackVersionID() const;
QString getManagedPackVersionName() const;
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 +161,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();
@ -192,7 +179,6 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
* Create envrironment variables for running the instance * Create envrironment variables for running the instance
*/ */
virtual QProcessEnvironment createEnvironment() = 0; virtual QProcessEnvironment createEnvironment() = 0;
virtual QProcessEnvironment createLaunchEnvironment() = 0;
/*! /*!
* Returns a matcher that can maps relative paths within the instance to whether they are 'log files' * Returns a matcher that can maps relative paths within the instance to whether they are 'log files'
@ -210,35 +196,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 +243,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 +255,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)

View File

@ -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)

View File

@ -1,61 +1,44 @@
// SPDX-License-Identifier: GPL-3.0-only /* Copyright 2013-2021 MultiMC Contributors
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* 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.
*/ */
#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 +46,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 qVariantFromValue(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

View File

@ -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

View File

@ -1,38 +1,18 @@
// SPDX-License-Identifier: GPL-3.0-only /* Copyright 2013-2021 MultiMC Contributors
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
* 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, * Licensed under the Apache License, Version 2.0 (the "License");
* but WITHOUT ANY WARRANTY; without even the implied warranty of * you may not use this file except in compliance with the License.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * You may obtain a copy of the License at
* GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * http://www.apache.org/licenses/LICENSE-2.0
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* *
* This file incorporates work covered by the following copyright and * Unless required by applicable law or agreed to in writing, software
* permission notice: * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* Copyright 2013-2021 MultiMC Contributors * See the License for the specific language governing permissions and
* * limitations under the License.
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*
* 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 "Commandline.h" #include "Commandline.h"
@ -41,7 +21,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,29 +31,38 @@ 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)
inquotes = QChar::Null; inquotes = 0;
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 +72,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())
{
}
}

View File

@ -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);
};
}

View File

@ -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"));
}

View File

@ -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;
};

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;
}; };

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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));
}

View File

@ -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

View File

@ -1,85 +1,36 @@
// SPDX-License-Identifier: GPL-3.0-only // Licensed under the Apache-2.0 license. See README.md for details.
/*
* Prism Launcher - Minecraft Launcher
* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once #pragma once
#include "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 +44,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 = src;
m_dst.setPath(dst); m_dst = 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
@ -344,205 +120,12 @@ bool checkProblemticPathJava(QDir folder);
// Get the Directory representing the User's Desktop // Get the Directory representing the User's Desktop
QString getDesktopDir(); QString getDesktopDir();
// Create a shortcut at *location*, pointing to *dest* called with the arguments *args*
// call it *name* and assign it the icon *icon*
// return true if operation succeeded
bool createShortCut(QString location, QString dest, QStringList args, QString name, QString iconLocation);
// 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

View File

@ -0,0 +1,164 @@
#include <QTest>
#include <QTemporaryDir>
#include <QStandardPaths>
#include "TestUtil.h"
#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("data/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));
}
// this is only valid on linux
// FIXME: implement on windows, OSX, then test.
#if defined(Q_OS_LINUX)
void test_createShortcut_data()
{
QTest::addColumn<QString>("location");
QTest::addColumn<QString>("dest");
QTest::addColumn<QStringList>("args");
QTest::addColumn<QString>("name");
QTest::addColumn<QString>("iconLocation");
QTest::addColumn<QByteArray>("result");
QTest::newRow("unix") << QDir::currentPath()
<< "asdfDest"
<< (QStringList() << "arg1" << "arg2")
<< "asdf"
<< QString()
#if defined(Q_OS_LINUX)
<< GET_TEST_FILE("data/FileSystem-test_createShortcut-unix")
#elif defined(Q_OS_WIN)
<< QByteArray()
#endif
;
}
void test_createShortcut()
{
QFETCH(QString, location);
QFETCH(QString, dest);
QFETCH(QStringList, args);
QFETCH(QString, name);
QFETCH(QString, iconLocation);
QFETCH(QByteArray, result);
QVERIFY(FS::createShortCut(location, dest, args, name, iconLocation));
QCOMPARE(QString::fromLocal8Bit(TestsInternal::readFile(location + QDir::separator() + name + ".desktop")), QString::fromLocal8Bit(result));
//QDir().remove(location);
}
#endif
};
QTEST_GUILESS_MAIN(FileSystemTest)
#include "FileSystem_test.moc"

View File

@ -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);

View File

@ -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 &regexp, 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;
}; };

View File

@ -1,45 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* 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 "GZip.h" #include "GZip.h"
#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 +16,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,21 +59,23 @@ 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;
} }
unsigned compLength = qMin(uncompressedBytes.size(), 16); unsigned compLength = std::min(uncompressedBytes.size(), 16);
compressedBytes.clear(); compressedBytes.clear();
compressedBytes.resize(compLength); compressedBytes.resize(compLength);
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 +87,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,12 +102,14 @@ 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;
} }

View File

@ -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);
}; };

View File

@ -1,18 +1,21 @@
#include <QTest> #include <QTest>
#include "TestUtil.h"
#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 +25,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 +38,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
View 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;
};

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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();
} }

View File

@ -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;
}; };

View File

@ -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;
} }

View File

@ -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;
}; };

Some files were not shown because too many files have changed in this diff Show More