Compare commits

..

49 Commits
6.1 ... 5.2

Author SHA1 Message Date
790e077ca8 Merge pull request #457 from Scrumplex/chore-bump-5.2 2022-11-15 17:15:09 +01:00
10ac6cb266 chore: bump version
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
2022-11-15 16:51:56 +01:00
4c9859b16e Merge pull request #445 from flowln/optimize_edit_instance 2022-11-15 14:47:36 +01:00
4b2c7a8a64 Merge pull request #406 from Scrumplex/fix-clear-metadata 2022-11-15 14:47:36 +01:00
0120d170b7 Merge pull request #397 from flowln/windows_mod_updater_fixes_maybe
Fixes https://github.com/PrismLauncher/PrismLauncher/issues/226
2022-11-15 14:47:36 +01:00
79617d0c50 Merge pull request #415 from TayouVR/fix-main-warning 2022-11-15 14:47:36 +01:00
6e58635bfb Merge pull request #374 from flowln/fix_lto 2022-11-15 14:47:36 +01:00
3aee1d60d8 Merge pull request #379 from TheLastRar/win-stacksize 2022-11-15 14:47:36 +01:00
a1fbf2b137 Merge pull request #346 from ryanccn/more-user-friendly-java-warning-text-ahaha 2022-11-15 14:47:36 +01:00
0b98a0884e Merge pull request #368 from PrismLauncher/credits 2022-11-15 14:47:36 +01:00
1582bb57ed Merge pull request #365 from Scrumplex/fix-logo 2022-11-15 14:47:36 +01:00
d8044ababe Merge pull request #332 from Scrumplex/chore-bump-5.1 2022-11-01 11:01:49 +01:00
32b526b729 Merge pull request #333 from flowln/fix_atl_packs_post_modpack_update 2022-11-01 11:01:15 +01:00
7f6515dbe4 Merge pull request #329 from flowln/only_safe_workarounds 2022-11-01 11:01:15 +01:00
a39390b8b4 Merge pull request #359 from Chrono-byte/develop 2022-11-01 09:48:13 +01:00
63a3dd1919 Merge pull request #351 from Scrumplex/fix-trash 2022-10-31 22:58:09 +01:00
7a5a4de6ea Merge pull request #354 from Scrumplex/translations-maybe
Improve display names of certain languages
2022-10-31 11:31:47 +01:00
664d4e701e Merge pull request #352 from TheLastRar/Win-setDarkWinTitlebar-10OrGreater 2022-10-31 11:31:47 +01:00
a4ba8d8288 Merge pull request #353 from FayneAldan/accounts-consistency 2022-10-31 01:42:20 +01:00
392bf7a97b Merge pull request #342 from fn2006/prism-svg-fix 2022-10-29 23:11:37 +02:00
34687049b1 Merge pull request #338 from Scrumplex/fix-credits-1 2022-10-29 16:15:58 +02:00
9337ec6706 Merge pull request #173 from Scrumplex/fix-icons 2022-10-29 00:36:15 +02:00
5bcb6962c4 chore: bump version
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
2022-10-28 21:54:10 +02:00
0617b43190 Merge pull request #322 from DioEgizio/64bit-it 2022-10-28 21:34:59 +02:00
ed28234cfb Merge pull request #319 from Scrumplex/fix-avoid-mr-segfault 2022-10-28 21:34:59 +02:00
9c4455ca03 Merge pull request #301 from DioEgizio/clang-attempt
feat(actions): use clang32 for building on windows
2022-10-28 21:34:59 +02:00
9ec7837275 Merge pull request #318 from TheLastRar/manifest-platform
Fix: Don't specify x86 in manifest
2022-10-28 21:34:59 +02:00
549b5a6488 Merge pull request #231 from tobimori/patch-1 2022-10-28 21:34:59 +02:00
2652f37453 Merge pull request #206 from flowln/changelog_height_fix 2022-10-28 21:34:59 +02:00
0eaff22145 Merge pull request #283 from flowln/fix_abort_on_autosearch 2022-10-28 21:34:59 +02:00
2f5393b9d0 Merge pull request #281 from Scrumplex/fix-nsis-displayname 2022-10-28 21:34:59 +02:00
e28480a8e4 Merge pull request #274 from Protrikk/patch-1 2022-10-28 21:34:59 +02:00
fcef6321fc Merge pull request #228 from bensuperpc/change_cast 2022-10-28 21:34:59 +02:00
35e792c5de Merge pull request #240 from jn64/fix/version-label-width 2022-10-28 21:34:59 +02:00
c08b632b51 Merge pull request #234 from AliceDTRH/fix/dedupejava 2022-10-28 21:34:59 +02:00
cac800bfd8 Merge pull request #233 from jamierocks/atl-fix-aborting-installs 2022-10-28 21:34:59 +02:00
2eb8173951 Merge pull request #224 from jamierocks/atl-abort-close-optional-mods-dialog 2022-10-28 21:34:59 +02:00
75abf2c124 Merge pull request #225 from Scrumplex/fix-segfault-fileresolver 2022-10-28 21:34:59 +02:00
d40a18d6c5 Merge pull request #218 from getchoo/change-jars-path 2022-10-28 21:34:59 +02:00
a74fdc588c Merge pull request #185 from flowln/fix_blocked_mods_crash 2022-10-28 21:34:59 +02:00
25b0ec6eff Merge pull request #147 from Minion3665/enhancement/update-nix-derivation 2022-10-28 21:34:59 +02:00
04e8982d33 Merge pull request #198 from PrismLauncher/renovate/hendrikmuhs-ccache-action-1.x 2022-10-28 21:34:59 +02:00
58bd449db8 Merge pull request #197 from PrismLauncher/renovate/actions-cache-3.x 2022-10-28 21:34:59 +02:00
c984e9b5d6 Merge pull request #202 from Heath123/patch-1 2022-10-28 21:34:59 +02:00
ddd319369a Merge pull request #39 from Sebbl0508/mod_dialog_fontsize 2022-10-28 21:34:59 +02:00
de3c336213 Merge pull request #184 from Chrono-byte/develop 2022-10-28 21:34:59 +02:00
6e94e9bff1 Merge pull request #148 from ZombieNub/prismlauncher-rename 2022-10-28 21:34:59 +02:00
93b8d9e454 Merge pull request #123 from MMK21Hub/patch-1 2022-10-28 21:34:59 +02:00
6d46081864 Merge pull request #100 from TheEvilSkeleton/improve-approachability 2022-10-28 21:34:59 +02:00
495 changed files with 2628 additions and 27836 deletions

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.
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 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.
- type: dropdown
@ -25,15 +25,15 @@ body:
- Other
- type: textarea
attributes:
label: Version of Prism Launcher
description: The version of Prism Launcher used in the bug report.
placeholder: Prism Launcher 5.0
label: Version of PolyMC
description: The version of PolyMC used in the bug report.
placeholder: PolyMC 1.4.1
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.
description: The version of Qt used in the bug report. You can find it in Help -> About PolyMC -> About Qt.
placeholder: Qt 6.3.0
validations:
required: true

View File

@ -1,5 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: Prism Launcher Matrix Support Room
url: https://matrix.to/#/#prism-support:matrix.org
- name: PolyMC Matrix Support Room
url: https://matrix.to/#/#support:polymc.org
about: Please ask for support here before opening an issue.

View File

@ -6,7 +6,7 @@ body:
- type: markdown
attributes:
value: |
### Use this form to suggest a larger change for Prism Launcher.
### Use this form to suggest a larger change for PolyMC.
- type: textarea
attributes:
label: Goal
@ -18,7 +18,7 @@ body:
attributes:
label: Motivation
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:
- 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!).

View File

@ -5,25 +5,25 @@ body:
- type: markdown
attributes:
value: |
### Use this form to suggest a feature for Prism Launcher.
### Use this form to suggest a feature for PolyMC.
- type: input
attributes:
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.
validations:
required: true
- type: input
attributes:
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.
validations:
required: true
- type: input
attributes:
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.
validations:
required: 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

@ -7,17 +7,10 @@ on:
description: Type of build (Debug, Release, RelWithDebInfo, MinSizeRel)
type: string
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
CACHIX_AUTH_TOKEN:
description: Private token for authenticating against Cachix cache
required: false
jobs:
build:
@ -32,60 +25,26 @@ jobs:
- os: ubuntu-20.04
qt_ver: 6
qt_host: linux
qt_arch: ''
qt_version: '6.2.4'
qt_modules: 'qt5compat qtimageformats'
qt_tools: ''
- os: windows-2022
name: "Windows-MinGW-w64"
msystem: clang64
- os: windows-2022
name: "Windows-MSVC-Legacy"
msystem: ''
architecture: 'win32'
vcvars_arch: 'amd64_x86'
name: "Windows-Legacy"
msystem: clang32
qt_ver: 5
qt_host: windows
qt_arch: 'win32_msvc2019'
qt_version: '5.15.2'
qt_modules: ''
qt_tools: 'tools_openssl_x86'
- os: windows-2022
name: "Windows-MSVC"
msystem: ''
architecture: 'x64'
vcvars_arch: 'amd64'
name: "Windows"
msystem: clang64
qt_ver: 6
qt_host: windows
qt_arch: ''
qt_version: '6.4.0'
qt_modules: 'qt5compat qtimageformats'
qt_tools: ''
- os: windows-2022
name: "Windows-MSVC-arm64"
msystem: ''
architecture: 'arm64'
vcvars_arch: 'amd64_arm64'
qt_ver: 6
qt_host: windows
qt_arch: 'win64_msvc2019_arm64'
qt_version: '6.4.0'
qt_modules: 'qt5compat qtimageformats'
qt_tools: ''
- os: macos-12
name: macOS
macosx_deployment_target: 10.15
qt_ver: 6
qt_host: mac
qt_arch: ''
qt_version: '6.3.0'
qt_modules: 'qt5compat qtimageformats'
qt_tools: ''
- os: macos-12
name: macOS-Legacy
@ -94,7 +53,6 @@ jobs:
qt_host: mac
qt_version: '5.15.2'
qt_modules: ''
qt_tools: ''
runs-on: ${{ matrix.os }}
@ -115,8 +73,16 @@ jobs:
with:
submodules: 'true'
- name: Initialize CodeQL
if: runner.os == 'Linux' && matrix.qt_ver == 6
uses: github/codeql-action/init@v2
with:
config-file: ./.github/codeql/codeql-config.yml
queries: security-and-quality
languages: cpp, java
- name: 'Setup MSYS2'
if: runner.os == 'Windows' && matrix.msystem != ''
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.msystem }}
@ -129,26 +95,21 @@ jobs:
cmake:p
extra-cmake-modules:p
ninja:p
qt6-base:p
qt6-svg:p
qt6-imageformats:p
quazip-qt6:p
qt${{ matrix.qt_ver }}-base:p
qt${{ matrix.qt_ver }}-svg:p
qt${{ matrix.qt_ver }}-imageformats:p
quazip-qt${{ matrix.qt_ver }}:p
ccache:p
qt6-5compat:p
- name: Force newer ccache
if: runner.os == 'Windows' && matrix.msystem == '' && inputs.build_type == 'Debug'
run: |
choco install ccache --version 4.7.1
${{ matrix.qt_ver == 6 && 'qt6-5compat:p' || '' }}
- name: Setup ccache
if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug'
uses: hendrikmuhs/ccache-action@v1.2.5
if: runner.os != 'Windows' && inputs.build_type == 'Debug'
uses: hendrikmuhs/ccache-action@v1.2.3
with:
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }}
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}
- name: Setup ccache (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
- name: Setup ccache (Windows)
if: runner.os == 'Windows' && inputs.build_type == 'Debug'
shell: msys2 {0}
run: |
ccache --set-config=cache_dir='${{ github.workspace }}\.ccache'
@ -163,14 +124,14 @@ jobs:
run: |
echo "CCACHE_VAR=ccache" >> $GITHUB_ENV
- name: Retrieve ccache cache (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
- name: Retrieve ccache cache (Windows)
if: runner.os == 'Windows' && inputs.build_type == 'Debug'
uses: actions/cache@v3.0.11
with:
path: '${{ github.workspace }}\.ccache'
key: ${{ matrix.os }}-mingw-w64
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}
restore-keys: |
${{ matrix.os }}-mingw-w64
${{ matrix.os }}-qt${{ matrix.qt_ver }}
- name: Set short version
shell: bash
@ -194,40 +155,17 @@ jobs:
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:
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 == '')
- name: Install Qt (macOS and AppImage)
if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS'
uses: jurplel/install-qt-action@v3
with:
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' && matrix.msystem == ''
uses: ilammy/msvc-dev-cmd@v1
with:
vsversion: 2022
arch: ${{ matrix.vcvars_arch }}
cache: true
cache-key-prefix: ${{ matrix.qt_host }}-${{ matrix.qt_version }}-"${{ matrix.qt_modules }}"-qt_cache
- name: Prepare AppImage (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 5
@ -238,11 +176,6 @@ jobs:
${{ github.workspace }}/.github/scripts/prepare_JREs.sh
- 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
##
@ -257,26 +190,11 @@ jobs:
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=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja
- name: Configure CMake (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != ''
- name: Configure CMake (Windows)
if: runner.os == 'Windows'
shell: msys2 {0}
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=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja
- name: Configure CMake (Windows MSVC)
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=${{ matrix.name }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
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
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja
- name: Configure CMake (Linux)
if: runner.os == 'Linux'
@ -292,17 +210,12 @@ jobs:
run: |
cmake --build ${{ env.BUILD_DIR }}
- name: Build (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != ''
- name: Build (Windows)
if: runner.os == 'Windows'
shell: msys2 {0}
run: |
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
##
@ -310,18 +223,21 @@ jobs:
- name: Test
if: runner.os != 'Windows'
run: |
ctest -E "^example64|example$" --test-dir build --output-on-failure
ctest --test-dir build --output-on-failure
- name: Test (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != ''
- name: Test (Windows)
if: runner.os == 'Windows'
shell: msys2 {0}
run: |
ctest -E "^example64|example$" --test-dir build --output-on-failure
ctest --test-dir build --output-on-failure
- name: Test (Windows MSVC)
if: runner.os == 'Windows' && matrix.msystem == '' && matrix.architecture != 'arm64'
run: |
ctest -E "^example64|example$" --test-dir build --output-on-failure -C ${{ inputs.build_type }}
##
# CODE SCAN
##
- name: Perform CodeQL Analysis
if: runner.os == 'Linux' && matrix.qt_ver == 6
uses: github/codeql-action/analyze@v2
##
# PACKAGE BUILDS
@ -357,37 +273,24 @@ jobs:
EOF
fi
- name: Package (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != ''
- name: Package (Windows)
if: runner.os == 'Windows'
shell: msys2 {0}
run: |
cmake --install ${{ env.BUILD_DIR }}
- name: Package (Windows MSVC)
if: runner.os == 'Windows' && matrix.msystem == ''
run: |
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }}
cd ${{ env.INSTALL_DIR }}
if ("${{ matrix.qt_ver }}" -eq "5")
{
Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll
Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll
}
if [ "${{ matrix.qt_ver }}" == "5" ]; then
cp /clang32/bin/libcrypto-1_1.dll /clang32/bin/libssl-1_1.dll ./
fi
- name: Package (Windows MinGW-w64, portable)
if: runner.os == 'Windows' && matrix.msystem != ''
- name: Package (Windows, portable)
if: runner.os == 'Windows'
shell: msys2 {0}
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
- 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
- name: Package (Windows, installer)
if: runner.os == 'Windows'
run: |
@ -508,76 +411,5 @@ jobs:
with:
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
snap:
runs-on: ubuntu-20.04
steps:
- name: Checkout
if: inputs.build_type == 'Debug'
uses: actions/checkout@v3
with:
submodules: 'true'
- name: Set short version
shell: bash
if: inputs.build_type == 'Debug'
run: |
ver_short=`git rev-parse --short HEAD`
echo "VERSION=$ver_short" >> $GITHUB_ENV
- name: Package Snap (Linux)
id: snapcraft
if: inputs.build_type == 'Debug'
uses: snapcore/action-build@v1
- name: Upload Snap (Linux)
if: inputs.build_type == 'Debug'
uses: actions/upload-artifact@v3
with:
name: prismlauncher_${{ env.VERSION }}_amd64.snap
path: ${{ steps.snapcraft.outputs.snap }}
flatpak:
runs-on: ubuntu-latest
container:
image: bilelmoussaoui/flatpak-github-actions:kde-5.15-22.08
options: --privileged
steps:
- name: Checkout
uses: actions/checkout@v3
if: inputs.build_type == 'Debug'
with:
submodules: 'true'
- name: Build Flatpak (Linux)
if: inputs.build_type == 'Debug'
uses: flatpak/flatpak-github-actions/flatpak-builder@v4
with:
bundle: "Prism Launcher.flatpak"
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
cache-key: flatpak-${{ github.sha }}-x86_64
nix:
runs-on: ubuntu-latest
strategy:
matrix:
package:
- prismlauncher
- prismlauncher-qt5
steps:
- name: Clone repository
if: inputs.build_type == 'Debug'
uses: actions/checkout@v3
with:
submodules: 'true'
- name: Install nix
if: inputs.build_type == 'Debug'
uses: cachix/install-nix-action@v18
with:
install_url: https://nixos.org/nix/install
extra_nix_config: |
auto-optimise-store = true
experimental-features = nix-command flakes
- uses: cachix/cachix-action@v12
if: inputs.build_type == 'Debug'
with:
name: prismlauncher
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- name: Build
if: inputs.build_type == 'Debug'
run: nix build .#${{ matrix.package }} --print-build-logs

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

View File

@ -3,11 +3,12 @@ name: Build Application
on:
push:
branches-ignore:
- 'renovate/**'
- 'stable'
paths-ignore:
- '**.md'
- '**/LICENSE'
- 'flake.lock'
- '**.nix'
- 'packages/**'
- '.github/ISSUE_TEMPLATE/**'
- '.markdownlint**'
@ -16,6 +17,7 @@ on:
- '**.md'
- '**/LICENSE'
- 'flake.lock'
- '**.nix'
- 'packages/**'
- '.github/ISSUE_TEMPLATE/**'
- '.markdownlint**'
@ -28,7 +30,5 @@ jobs:
uses: ./.github/workflows/build.yml
with:
build_type: Debug
is_qt_cached: true
secrets:
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}

View File

@ -12,7 +12,6 @@ jobs:
uses: ./.github/workflows/build.yml
with:
build_type: Release
is_qt_cached: false
secrets:
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
@ -46,26 +45,13 @@ jobs:
tar -czf PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }}
for d in PrismLauncher-Windows-MSVC*; do
for d in PrismLauncher-Windows-*; do
cd "${d}" || continue
LEGACY="$(echo -n ${d} | grep -o Legacy || true)"
ARM64="$(echo -n ${d} | grep -o arm64 || true)"
INST="$(echo -n ${d} | grep -o Setup || true)"
PORT="$(echo -n ${d} | grep -o Portable || true)"
NAME="PrismLauncher-Windows-MSVC"
NAME="PrismLauncher-Windows"
test -z "${LEGACY}" || NAME="${NAME}-Legacy"
test -z "${ARM64}" || NAME="${NAME}-arm64"
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" *
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" *
@ -86,20 +72,14 @@ jobs:
PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage
PrismLauncher-Windows-Legacy-${{ env.VERSION }}.zip
PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip
PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-MinGW-w64-Setup-${{ env.VERSION }}.exe
PrismLauncher-Windows-MSVC-Legacy-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-Legacy-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-Legacy-Setup-${{ env.VERSION }}.exe
PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-arm64-Portable-${{ env.VERSION }}.zip
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-Windows-Legacy-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-Legacy-Setup-${{ env.VERSION }}.exe
PrismLauncher-Windows-${{ env.VERSION }}.zip
PrismLauncher-Windows-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-Setup-${{ env.VERSION }}.exe
PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
PrismLauncher-${{ env.VERSION }}.tar.gz

View File

@ -11,5 +11,5 @@ jobs:
with:
identifier: PrismLauncher.PrismLauncher
version: ${{ github.event.release.tag_name }}
installers-regex: 'PrismLauncher-Windows-MSVC(:?-arm64|-Legacy)?-Setup-.+\.exe$'
installers-regex: 'PrismLauncher-Windows-Setup-.+\.exe$'
token: ${{ secrets.WINGET_TOKEN }}

3
.gitignore vendored
View File

@ -47,6 +47,3 @@ result/
# Flatpak
.flatpak-builder
flatbuild
# Snap
*.snap

6
.gitmodules vendored
View File

@ -10,9 +10,3 @@
[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

View File

@ -1,5 +1,10 @@
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)
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
@ -27,51 +32,11 @@ set(CMAKE_C_STANDARD_REQUIRED true)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_C_STANDARD 11)
include(GenerateExportHeader)
if(MSVC)
# /GS Adds buffer security checks, default on but incuded anyway to mirror gcc's fstack-protector flag
# /permissive- specify standards-conforming compiler behavior, also enabled by Qt6, default on with std:c++20
# Use /W4 as /Wall includes unnesserey warnings such as added padding to structs
set(CMAKE_CXX_FLAGS "/GS /permissive- /W4 ${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "-Wall -pedantic -fstack-protector-strong --param=ssp-buffer-size=4 ${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()
# ATL's packlist 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()
# Fix build with Qt 5.13
@ -79,9 +44,6 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y")
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(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
@ -91,18 +53,11 @@ if(ENABLE_LTO)
include(CheckIPOSupported)
check_ipo_supported(RESULT ipo_supported OUTPUT ipo_error)
if(ipo_supported)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL TRUE)
if(CMAKE_BUILD_TYPE)
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
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()
if(ipo_supported AND (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel"))
message(STATUS "IPO / LTO enabled")
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
elseif(ipo_supported)
message(STATUS "Not enabling IPO / LTO on debug builds")
else()
message(STATUS "IPO / LTO not supported: <${ipo_error}>")
endif()
@ -110,20 +65,8 @@ 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()
find_package(ECM REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH "${ECM_MODULE_PATH};${CMAKE_MODULE_PATH}")
include(CTest)
include(ECMAddTests)
if(BUILD_TESTING)
@ -138,8 +81,8 @@ set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL th
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
######## Set version numbers ########
set(Launcher_VERSION_MAJOR 6)
set(Launcher_VERSION_MINOR 1)
set(Launcher_VERSION_MAJOR 5)
set(Launcher_VERSION_MINOR 2)
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
@ -208,10 +151,6 @@ set(Launcher_BUILD_TIMESTAMP "${TODAY}")
################################ 3rd Party Libs ################################
if(NOT Launcher_FORCE_BUNDLED_LIBS)
find_package(ZLIB QUIET)
endif()
# Find the required Qt parts
include(QtVersionlessBackport)
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
@ -230,7 +169,7 @@ if(Launcher_QT_VERSION_MAJOR EQUAL 5)
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)
find_package(Qt6 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml Core5Compat)
list(APPEND Launcher_QT_LIBS Qt6::Core5Compat)
if(NOT Launcher_FORCE_BUNDLED_LIBS)
@ -244,16 +183,12 @@ else()
message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported")
endif()
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
include(ECMQueryQt)
ecm_query_qt(QT_PLUGINS_DIR QT_INSTALL_PLUGINS)
ecm_query_qt(QT_LIBS_DIR QT_INSTALL_LIBS)
ecm_query_qt(QT_LIBEXECS_DIR QT_INSTALL_LIBEXECS)
else()
set(QT_PLUGINS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_PLUGINS})
set(QT_LIBS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBS})
set(QT_LIBEXECS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBEXECS})
endif()
include(ECMQueryQt)
ecm_query_qt(QT_PLUGINS_DIR QT_INSTALL_PLUGINS)
ecm_query_qt(QT_LIBS_DIR QT_INSTALL_LIBS)
ecm_query_qt(QT_LIBEXECS_DIR QT_INSTALL_LIBEXECS)
ecm_query_qt(QT_DATA_DIR QT_HOST_DATA)
set(QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs)
# NOTE: Qt 6 already sets this by default
if (Qt5_POSITION_INDEPENDENT_CODE)
@ -317,11 +252,13 @@ if(UNIX AND APPLE)
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
elseif(UNIX)
include(KDEInstallDirs)
set(BINARY_DEST_DIR "bin")
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
set(JARS_DEST_DIR "share/${Launcher_APP_BINARY_NAME}")
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
set(INSTALL_BUNDLE "nodeps")
@ -329,13 +266,12 @@ elseif(UNIX)
# Set RPATH
SET(Launcher_BINARY_RPATH "$ORIGIN/")
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${KDE_INSTALL_APPDIR})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${KDE_INSTALL_METAINFODIR})
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_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${LAUNCHER_DESKTOP_DEST_DIR})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${LAUNCHER_METAINFO_DEST_DIR})
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION ${LAUNCHER_ICON_DEST_DIR})
if(Launcher_ManPage)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION ${LAUNCHER_MAN_DEST_DIR})
endif()
# Install basic runner script if component "portable" is selected
@ -375,21 +311,6 @@ 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/javacheck) # java compatibility checker
if(NOT ZLIB_FOUND)
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)
set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib" "${CMAKE_CURRENT_BINARY_DIR}/libraries/zlib" CACHE STRING "")
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)
message(STATUS "Using bundled QuaZip")
set(BUILD_SHARED_LIBS 0) # link statically to avoid conflicts.

View File

@ -398,45 +398,3 @@
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/>.

View File

@ -1,102 +1,29 @@
<p align="left">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="/program_info/org.prismlauncher.PrismLauncher.logo-darkmode.svg">
<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="50%">
</picture>
<p align="center">
<img src="./program_info/org.prismlauncher.PrismLauncher.logo.svg#gh-light-mode-only" alt="Prism Launcher logo" width="50%"/>
<img src="./program_info/org.prismlauncher.PrismLauncher.logo-darkmode.svg#gh-dark-mode-only" alt="Prism Launcher logo" width="50%"/>
</p>
Prism Launcher is a custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.
This is a **fork** of the MultiMC Launcher and is not endorsed by MultiMC.
We are working on a website and other media, for more info we have a [Discord server](https://discord.gg/prismlauncher).
## Installation
<a href="https://repology.org/project/prismlauncher/versions">
<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/).
- Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions).
- All downloads and instructions for Prism Launcher will soon be available.
- Last build status can be found [here](https://github.com/PrismLauncher/PrismLauncher/actions).
### Development Builds
There are development builds available [here](https://github.com/PrismLauncher/PrismLauncher/actions). These have debug information in the binaries, so their file sizes are relatively larger.
Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**.
Portable builds are provided for AppImage on Linux, Windows, and macOS.
For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**, respectively, you can use these packages for the latest development versions:
## Help & Support
[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?style=flat-square&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git/) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?style=flat-square&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git/) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?style=flat-square&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git) [![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?style=flat-square&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?style=flat-square&logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?style=flat-square&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher)
## Community & Support
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.
#### Join our Discord server:
[![Prism Launcher Discord server](https://discordapp.com/api/guilds/1031648380885147709/widget.png?style=banner2)](https://discord.gg/prismlauncher)
#### Join our Matrix space:
[![PrismLauncher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge&logo=matrix)](https://matrix.to/#/#prismlauncher:matrix.org)
#### Join our Subreddit:
[![r/PrismLauncher](https://img.shields.io/reddit/subreddit-subscribers/prismlauncher?style=for-the-badge&logo=reddit)](https://www.reddit.com/r/PrismLauncher/)
## Building
If you want to build Prism Launcher yourself, check the [Build Instructions](https://prismlauncher.org/wiki/development/build-instructions/).
## Translations
The translation effort for PrismLauncher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at <https://github.com/PrismLauncher/Translations>
## Forking/Redistributing/Custom builds policy
We don't care what you do with your fork/custom build as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy:
- Make it clear that your fork is not PrismLauncher and is not endorsed by or affiliated with the PrismLauncher project (<https://prismlauncher.org>).
- Go through [CMakeLists.txt](CMakeLists.txt) and change PrismLauncher's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled).
If you have any questions or want any clarification on the above conditions please make an issue and ask us.
Be aware that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions:
- [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
- [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 (`""`).
## Sponsors & Partners
We thank all the wonderful backers over at Open Collective! Support Prism Launcher by [becoming a backer](https://opencollective.com/prismlauncher).
[![OpenCollective Backers](https://opencollective.com/prismlauncher/backers.svg?width=890&limit=1000)](https://opencollective.com/prismlauncher#backers)
Thanks to JetBrains for providing us a few licenses for all their products, as part of their [Open Source program](https://www.jetbrains.com/opensource/).
[![JetBrains](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/opensource/)
Thanks to Weblate for hosting our translation efforts.
<a href="https://hosted.weblate.org/engage/prismlauncher/">
<img src="https://hosted.weblate.org/widgets/prismlauncher/-/open-graph.png" alt="Translation status" width="300" />
</a>
Thanks to Netlify for providing us their excellent web services, as part of their [Open Source program](https://www.netlify.com/open-source/)
<a href="https://www.netlify.com"> <img src="https://www.netlify.com/v3/img/components/netlify-color-accent.svg" alt="Deploys by Netlify" /> </a>
Thanks to the awesome people over at [MacStadium](https://www.macstadium.com/), for providing M1-Macs for development purposes!
<a href="https://www.macstadium.com"><img src="https://uploads-ssl.webflow.com/5ac3c046c82724970fc60918/5c019d917bba312af7553b49_MacStadium-developerlogo.png" alt="Powered by MacStadium" width="300"></a>
[![Join the Discord Server](https://discordapp.com/api/guilds/1031648380885147709/widget.png?style=banner3)](https://discord.gg/hX4g537UNE)
## License
All launcher code is available under the GPL-3.0-only license.
![https://github.com/PrismLauncher/PrismLauncher/blob/develop/LICENSE](https://img.shields.io/github/license/PrismLauncher/PrismLauncher?style=for-the-badge&logo=gnu&color=C4282D)
The logo and related assets are under the CC BY-SA 4.0 license.

View File

@ -161,8 +161,6 @@ class Config {
QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2";
QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2";
QString FLAME_BASE_URL = "https://api.curseforge.com/v1";
QString versionString() const;
/**
* \brief Converts the Version to a string.

View File

@ -44,28 +44,5 @@
<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>
</dict>
</plist>

31
flake.lock generated
View File

@ -3,11 +3,11 @@
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1668681692,
"narHash": "sha256-Ht91NGdewz8IQLtWZ9LCeNXMSXHUss+9COoqu6JLmXU=",
"lastModified": 1650374568,
"narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "009399224d5e398d03b22badca40a37ac85412a1",
"rev": "b4a34015c698c7793d592d66adbab377907a2be8",
"type": "github"
},
"original": {
@ -34,11 +34,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1671417167,
"narHash": "sha256-JkHam6WQOwZN1t2C2sbp1TqMv3TVRjzrdoejqfefwrM=",
"lastModified": 1666057921,
"narHash": "sha256-VpQqtXdj6G7cH//SvoprjR7XT3KS7p+tCVebGK1N6tE=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "bb31220cca6d044baa6dc2715b07497a2a7c4bc7",
"rev": "88eab1e431cabd0ed621428d8b40d425a07af39f",
"type": "github"
},
"original": {
@ -52,7 +52,24 @@
"inputs": {
"flake-compat": "flake-compat",
"libnbtplusplus": "libnbtplusplus",
"nixpkgs": "nixpkgs"
"nixpkgs": "nixpkgs",
"tomlplusplus": "tomlplusplus"
}
},
"tomlplusplus": {
"flake": false,
"locked": {
"lastModified": 1666091090,
"narHash": "sha256-djpMCFPvkJcfynV8WnsYdtwLq+J7jpV1iM4C6TojiyM=",
"owner": "marzer",
"repo": "tomlplusplus",
"rev": "1e4a3833d013aee08f58c5b31c69f709afc69f73",
"type": "github"
},
"original": {
"owner": "marzer",
"repo": "tomlplusplus",
"type": "github"
}
}
},

View File

@ -5,9 +5,10 @@
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
libnbtplusplus = { url = "github:PrismLauncher/libnbtplusplus"; flake = false; };
tomlplusplus = { url = "github:marzer/tomlplusplus"; flake = false; };
};
outputs = { self, nixpkgs, libnbtplusplus, ... }:
outputs = { self, nixpkgs, libnbtplusplus, tomlplusplus, ... }:
let
# User-friendly version number.
version = builtins.substring 0 8 self.lastModifiedDate;
@ -22,8 +23,8 @@
pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system});
packagesFn = pkgs: rec {
prismlauncher-qt5 = pkgs.libsForQt5.callPackage ./nix { inherit version self libnbtplusplus; };
prismlauncher = pkgs.qt6Packages.callPackage ./nix { inherit version self libnbtplusplus; };
prismlauncher = pkgs.libsForQt5.callPackage ./nix { inherit version self libnbtplusplus tomlplusplus; };
prismlauncher-qt6 = pkgs.qt6Packages.callPackage ./nix { inherit version self libnbtplusplus tomlplusplus; };
};
in
{

View File

@ -1,83 +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
add-extensions:
com.valvesoftware.Steam.Utility.gamescope:
version: stable
add-ld-path: lib
no-autodownload: true
autodelete: false
directory: utils/gamescope
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
modules:
- name: prismlauncher
buildsystem: cmake-ninja
config-opts:
- -DLauncher_BUILD_PLATFORM=flatpak
- -DCMAKE_BUILD_TYPE=Debug
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: xrandr
buildsystem: autotools
sources:
- type: archive
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.1.tar.xz
sha256: 7bc76daf9d72f8aff885efad04ce06b90488a1a169d118dea8a2b661832e8762
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: git
url: https://github.com/FeralInteractive/gamemode
tag: "1.7"
commit: 4dc99dff76218718763a6b07fc1900fa6d1dafd9
- name: enhance
buildsystem: simple
build-commands:
- mkdir -p /app/utils/gamescope
- 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: ../flatpak/prime-run
- type: file
path: ../flatpak/prismlauncher

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:+:}/app/utils/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin"
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}${LD_LIBRARY_PATH:+:}/usr/lib/extensions/vulkan/MangoHud/\$LIB/"
exec /app/bin/prismrun "$@"

View File

@ -1,12 +1,8 @@
// SPDX-FileCopyrightText: 2022 Sefa Eyeoglu <contact@scrumplex.net>
//
// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0
// 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 Lenny McLennington <lenny@sneed.church>
* Copyright (C) 2022 Tayou <tayou@gmx.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
@ -41,14 +37,10 @@
#include "Application.h"
#include "BuildConfig.h"
#include "DataMigrationTask.h"
#include "net/PasteUpload.h"
#include "pathmatcher/MultiMatcher.h"
#include "pathmatcher/SimplePrefixMatcher.h"
#include "ui/MainWindow.h"
#include "ui/InstanceWindow.h"
#include "ui/dialogs/ProgressDialog.h"
#include "ui/instanceview/AccessibleInstanceView.h"
#include "ui/pages/BasePageProvider.h"
@ -62,6 +54,12 @@
#include "ui/pages/global/APIPage.h"
#include "ui/pages/global/CustomCommandsPage.h"
#include "ui/themes/ITheme.h"
#include "ui/themes/SystemTheme.h"
#include "ui/themes/DarkTheme.h"
#include "ui/themes/BrightTheme.h"
#include "ui/themes/CustomTheme.h"
#ifdef Q_OS_WIN
#include "ui/WinDarkmode.h"
#include <versionhelpers.h>
@ -76,8 +74,6 @@
#include "ui/pagedialog/PageDialog.h"
#include "ui/themes/ThemeManager.h"
#include "ApplicationMessage.h"
#include <iostream>
@ -97,7 +93,6 @@
#include <QIcon>
#include "InstanceList.h"
#include "MTPixmapCache.h"
#include <minecraft/auth/AccountList.h>
#include "icons/IconList.h"
@ -126,7 +121,6 @@
#ifdef Q_OS_LINUX
#include <dlfcn.h>
#include "gamemode_client.h"
#include "MangoHud.h"
#endif
@ -143,8 +137,6 @@
static const QLatin1String liveCheckFile("live.check");
PixmapCache* PixmapCache::s_instance = nullptr;
namespace {
void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
@ -237,7 +229,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN);
setApplicationName(BuildConfig.LAUNCHER_NAME);
setApplicationDisplayName(QString("%1 %2").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString()));
setApplicationVersion(BuildConfig.printableVersionString() + "\n" + BuildConfig.GIT_COMMIT);
setApplicationVersion(BuildConfig.printableVersionString());
setDesktopFileName(BuildConfig.LAUNCHER_DESKTOPFILENAME);
startTime = QDateTime::currentDateTime();
@ -254,8 +246,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
{{"s", "server"}, "Join the specified server on launch (only valid in combination with --launch)", "address"},
{{"a", "profile"}, "Use the account specified by its profile name (only valid in combination with --launch)", "profile"},
{"alive", "Write a small '" + liveCheckFile + "' file after the launcher starts"},
{{"I", "import"}, "Import instance from specified zip (local path or URL)", "file"},
{"show", "Opens the window for the specified instance (by instance ID)", "show"}
{{"I", "import"}, "Import instance from specified zip (local path or URL)", "file"}
});
parser.addHelpOption();
parser.addVersionOption();
@ -267,7 +258,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_profileToUse = parser.value("profile");
m_liveCheck = parser.isSet("alive");
m_zipToImport = parser.value("import");
m_instanceIdToShowWindowOf = parser.value("show");
// error if --launch is missing with --server or --profile
if((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty())
@ -312,6 +302,22 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
dataPath = foo.absolutePath();
adjustedBy = "Persistent data path";
QDir polymcData(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation), "PolyMC"));
if (polymcData.exists()) {
dataPath = polymcData.absolutePath();
adjustedBy = "PolyMC data path";
}
#ifdef Q_OS_LINUX
// TODO: this should be removed in a future version
// TODO: provide a migration path similar to macOS migration
QDir bar(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation), "polymc"));
if (bar.exists()) {
dataPath = bar.absolutePath();
adjustedBy = "Legacy data path";
}
#endif
#ifndef Q_OS_MACOS
if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) {
dataPath = m_rootPath;
@ -434,15 +440,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
qDebug() << "<> Log initialized.";
}
{
bool migrated = false;
if (!migrated)
migrated = handleDataMigration(dataPath, FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "../../PolyMC"), "PolyMC", "polymc.cfg");
if (!migrated)
migrated = handleDataMigration(dataPath, FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "../../multimc"), "MultiMC", "multimc.cfg");
}
{
qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT;
@ -502,7 +499,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
// Theming
m_settings->registerSetting("IconTheme", QString("pe_colored"));
m_settings->registerSetting("ApplicationTheme", QString("system"));
m_settings->registerSetting("BackgroundCat", QString("kitteh"));
// Remembered state
m_settings->registerSetting("LastUsedGroupForNewInstance", QString());
@ -567,7 +563,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
// Memory
m_settings->registerSetting({"MinMemAlloc", "MinMemoryAlloc"}, 512);
m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, suitableMaxMem());
m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, 4096);
m_settings->registerSetting("PermGen", 128);
// Java Settings
@ -615,8 +611,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
// The cat
m_settings->registerSetting("TheCat", false);
m_settings->registerSetting("ToolbarsLocked", false);
m_settings->registerSetting("InstSortMode", "Name");
m_settings->registerSetting("SelectedInstance", QString());
@ -697,9 +691,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_globalSettingsProvider->addPage<AccountListPage>();
m_globalSettingsProvider->addPage<APIPage>();
}
PixmapCache::setInstance(new PixmapCache(this));
qDebug() << "<> Settings loaded.";
}
@ -756,8 +747,29 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
qDebug() << "<> Instance icons intialized.";
}
// Themes
m_themeManager = std::make_unique<ThemeManager>(m_mainWindow);
// Icon themes
{
// TODO: icon themes and instance icons do not mesh well together. Rearrange and fix discrepancies!
// set icon theme search path!
auto searchPaths = QIcon::themeSearchPaths();
searchPaths.append("iconthemes");
QIcon::setThemeSearchPaths(searchPaths);
qDebug() << "<> Icon themes initialized.";
}
// Initialize widget themes
{
auto insertTheme = [this](ITheme * theme)
{
m_themes.insert(std::make_pair(theme->id(), std::unique_ptr<ITheme>(theme)));
};
auto darkTheme = new DarkTheme();
insertTheme(new SystemTheme());
insertTheme(darkTheme);
insertTheme(new BrightTheme());
insertTheme(new CustomTheme(darkTheme, "custom"));
qDebug() << "<> Widget themes initialized.";
}
// initialize and load all instances
{
@ -922,24 +934,18 @@ bool Application::createSetupWizard()
return false;
}
bool Application::event(QEvent* event)
{
bool Application::event(QEvent* event) {
#ifdef Q_OS_MACOS
if (event->type() == QEvent::ApplicationStateChange) {
auto ev = static_cast<QApplicationStateChangeEvent*>(event);
if (m_prevAppState == Qt::ApplicationActive && ev->applicationState() == Qt::ApplicationActive) {
if (m_prevAppState == Qt::ApplicationActive
&& ev->applicationState() == Qt::ApplicationActive) {
emit clickedOnDock();
}
m_prevAppState = ev->applicationState();
}
#endif
if (event->type() == QEvent::FileOpen) {
auto ev = static_cast<QFileOpenEvent*>(event);
m_mainWindow->droppedURLs({ ev->url() });
}
return QApplication::event(event);
}
@ -981,16 +987,6 @@ void Application::performMainStartupAction()
return;
}
}
if(!m_instanceIdToShowWindowOf.isEmpty())
{
auto inst = instances()->getInstanceById(m_instanceIdToShowWindowOf);
if(inst)
{
qDebug() << "<> Showing window of instance " << m_instanceIdToShowWindowOf;
showInstanceWindow(inst);
return;
}
}
if(!m_mainWindow)
{
// normal main window
@ -1117,19 +1113,45 @@ std::shared_ptr<JavaInstallList> Application::javalist()
return m_javalist;
}
QList<ITheme*> Application::getValidApplicationThemes()
std::vector<ITheme *> Application::getValidApplicationThemes()
{
return m_themeManager->getValidApplicationThemes();
std::vector<ITheme *> ret;
auto iter = m_themes.cbegin();
while (iter != m_themes.cend())
{
ret.push_back((*iter).second.get());
iter++;
}
return ret;
}
void Application::setApplicationTheme(const QString& name, bool initial)
{
m_themeManager->setApplicationTheme(name, initial);
auto systemPalette = qApp->palette();
auto themeIter = m_themes.find(name);
if(themeIter != m_themes.end())
{
auto & theme = (*themeIter).second;
theme->apply(initial);
#ifdef Q_OS_WIN
if (m_mainWindow && IsWindows10OrGreater()) {
if (QString::compare(theme->id(), "dark") == 0) {
WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), true);
} else {
WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), false);
}
}
#endif
}
else
{
qWarning() << "Tried to set invalid theme:" << name;
}
}
void Application::setIconTheme(const QString& name)
{
m_themeManager->setIconTheme(name);
QIcon::setThemeName(name);
}
QIcon Application::getThemedIcon(const QString& name)
@ -1526,8 +1548,17 @@ void Application::updateCapabilities()
if (gamemode_query_status() >= 0)
m_capabilities |= SupportsGameMode;
if (!MangoHud::getLibraryString().isEmpty())
m_capabilities |= SupportsMangoHud;
{
void *dummy = dlopen("libMangoHud_dlsym.so", RTLD_LAZY);
// try normal variant as well
if (dummy == NULL)
dummy = dlopen("libMangoHud.so", RTLD_LAZY);
if (dummy != NULL) {
dlclose(dummy);
m_capabilities |= SupportsMangoHud;
}
}
#endif
}
@ -1589,102 +1620,3 @@ QString Application::getUserAgentUncached()
return BuildConfig.USER_AGENT_UNCACHED;
}
int Application::suitableMaxMem()
{
float totalRAM = (float)Sys::getSystemRam() / (float)Sys::mebibyte;
int maxMemoryAlloc;
// If totalRAM < 6GB, use (totalRAM / 1.5), else 4GB
if (totalRAM < (4096 * 1.5))
maxMemoryAlloc = (int) (totalRAM / 1.5);
else
maxMemoryAlloc = 4096;
return maxMemoryAlloc;
}
bool Application::handleDataMigration(const QString& currentData,
const QString& oldData,
const QString& name,
const QString& configFile) const
{
QString nomigratePath = FS::PathCombine(currentData, name + "_nomigrate.txt");
QStringList configPaths = { FS::PathCombine(oldData, configFile), FS::PathCombine(oldData, BuildConfig.LAUNCHER_CONFIGFILE) };
QLocale locale;
// Is there a valid config at the old location?
bool configExists = false;
for (QString configPath : configPaths) {
configExists |= QFileInfo::exists(configPath);
}
if (!configExists || QFileInfo::exists(nomigratePath)) {
qDebug() << "<> No migration needed from" << name;
return false;
}
QString message;
bool currentExists = QFileInfo::exists(FS::PathCombine(currentData, BuildConfig.LAUNCHER_CONFIGFILE));
if (currentExists) {
message = tr("Old data from %1 was found, but you already have existing data for %2. Sadly you will need to migrate yourself. Do "
"you want to be reminded of the pending data migration next time you start %2?")
.arg(name, BuildConfig.LAUNCHER_DISPLAYNAME);
} else {
message = tr("It looks like you used %1 before. Do you want to migrate your data to the new location of %2?")
.arg(name, BuildConfig.LAUNCHER_DISPLAYNAME);
QFileInfo logInfo(FS::PathCombine(oldData, name + "-0.log"));
if (logInfo.exists()) {
QString lastModified = logInfo.lastModified().toString(locale.dateFormat());
message = tr("It looks like you used %1 on %2 before. Do you want to migrate your data to the new location of %3?")
.arg(name, lastModified, BuildConfig.LAUNCHER_DISPLAYNAME);
}
}
QMessageBox::StandardButton askMoveDialogue =
QMessageBox::question(nullptr, BuildConfig.LAUNCHER_DISPLAYNAME, message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
auto setDoNotMigrate = [&nomigratePath] {
QFile file(nomigratePath);
file.open(QIODevice::WriteOnly);
};
// create no-migrate file if user doesn't want to migrate
if (askMoveDialogue != QMessageBox::Yes) {
qDebug() << "<> Migration declined for" << name;
setDoNotMigrate();
return currentExists; // cancel further migrations, if we already have a data directory
}
if (!currentExists) {
// Migrate!
auto matcher = std::make_shared<MultiMatcher>();
matcher->add(std::make_shared<SimplePrefixMatcher>(configFile));
matcher->add(std::make_shared<SimplePrefixMatcher>(
BuildConfig.LAUNCHER_CONFIGFILE)); // it's possible that we already used that directory before
matcher->add(std::make_shared<SimplePrefixMatcher>("accounts.json"));
matcher->add(std::make_shared<SimplePrefixMatcher>("accounts/"));
matcher->add(std::make_shared<SimplePrefixMatcher>("assets/"));
matcher->add(std::make_shared<SimplePrefixMatcher>("icons/"));
matcher->add(std::make_shared<SimplePrefixMatcher>("instances/"));
matcher->add(std::make_shared<SimplePrefixMatcher>("libraries/"));
matcher->add(std::make_shared<SimplePrefixMatcher>("mods/"));
matcher->add(std::make_shared<SimplePrefixMatcher>("themes/"));
ProgressDialog diag;
DataMigrationTask task(nullptr, oldData, currentData, matcher);
if (diag.execWithTask(&task)) {
qDebug() << "<> Migration succeeded";
setDoNotMigrate();
} else {
QString reason = task.failReason();
QMessageBox::critical(nullptr, BuildConfig.LAUNCHER_DISPLAYNAME, tr("Migration failed! Reason: %1").arg(reason));
}
} else {
qWarning() << "<> Migration was skipped, due to existing data";
}
return true;
}

View File

@ -1,8 +1,7 @@
// 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 Tayou <tayou@gmx.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
@ -69,7 +68,6 @@ class BaseDetachedToolFactory;
class TranslationsModel;
class ITheme;
class MCEditTool;
class ThemeManager;
namespace Meta {
class Index;
@ -120,7 +118,7 @@ public:
void setIconTheme(const QString& name);
QList<ITheme*> getValidApplicationThemes();
std::vector<ITheme *> getValidApplicationThemes();
void setApplicationTheme(const QString& name, bool initial);
@ -200,8 +198,6 @@ public:
void ShowGlobalSettings(class QWidget * parent, QString open_page = QString());
int suitableMaxMem();
signals:
void updateAllowedChanged(bool status);
void globalSettingsAboutToOpen();
@ -231,7 +227,6 @@ private slots:
void setupWizardFinished(int status);
private:
bool handleDataMigration(const QString & currentData, const QString & oldData, const QString & name, const QString & configFile) const;
bool createSetupWizard();
void performMainStartupAction();
@ -260,9 +255,9 @@ private:
std::shared_ptr<JavaInstallList> m_javalist;
std::shared_ptr<TranslationsModel> m_translations;
std::shared_ptr<GenericPageProvider> m_globalSettingsProvider;
std::map<QString, std::unique_ptr<ITheme>> m_themes;
std::unique_ptr<MCEditTool> m_mcedit;
QSet<QString> m_features;
std::unique_ptr<ThemeManager> m_themeManager;
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
@ -304,7 +299,6 @@ public:
QString m_profileToUse;
bool m_liveCheck = false;
QUrl m_zipToImport;
QString m_instanceIdToShowWindowOf;
std::unique_ptr<QFile> logFile;
};

View File

@ -47,8 +47,8 @@ void ApplicationMessage::parse(const QByteArray & input) {
args.clear();
auto parsedArgs = root.value("args").toObject();
for(auto iter = parsedArgs.constBegin(); iter != parsedArgs.constEnd(); iter++) {
args.insert(iter.key(), iter.value().toString());
for(auto iter = parsedArgs.begin(); iter != parsedArgs.end(); iter++) {
args[iter.key()] = iter.value().toString();
}
}
@ -56,8 +56,8 @@ QByteArray ApplicationMessage::serialize() {
QJsonObject root;
root.insert("command", command);
QJsonObject outArgs;
for (auto iter = args.constBegin(); iter != args.constEnd(); iter++) {
outArgs.insert(iter.key(), iter.value());
for (auto iter = args.begin(); iter != args.end(); iter++) {
outArgs[iter.key()] = iter.value();
}
root.insert("args", outArgs);

View File

@ -1,12 +1,12 @@
#pragma once
#include <QString>
#include <QHash>
#include <QMap>
#include <QByteArray>
struct ApplicationMessage {
QString command;
QHash<QString, QString> args;
QMap<QString, QString> args;
QByteArray serialize();
void parse(const QByteArray & input);

View File

@ -151,7 +151,7 @@ public:
void copyManagedPack(BaseInstance& other);
/// guess log level from a line of game log
virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString &line, MessageLevel::Enum level)
virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level)
{
return level;
};

View File

@ -95,12 +95,12 @@ BaseVersionList::RoleList BaseVersionList::providesRoles() const
int BaseVersionList::rowCount(const QModelIndex &parent) const
{
// Return count
return parent.isValid() ? 0 : count();
return count();
}
int BaseVersionList::columnCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : 1;
return 1;
}
QHash<int, QByteArray> BaseVersionList::roleNames() const

View File

@ -31,8 +31,6 @@ set(CORE_SOURCES
# Basic instance manipulation tasks (derived from InstanceTask)
InstanceCreationTask.h
InstanceCreationTask.cpp
InstanceCopyPrefs.h
InstanceCopyPrefs.cpp
InstanceCopyTask.h
InstanceCopyTask.cpp
InstanceImportTask.h
@ -89,18 +87,7 @@ set(CORE_SOURCES
# Time
MMCTime.h
MMCTime.cpp
MTPixmapCache.h
)
if (UNIX AND NOT CYGWIN AND NOT APPLE)
set(CORE_SOURCES
${CORE_SOURCES}
# MangoHud
MangoHud.h
MangoHud.cpp
)
endif()
set(PATHMATCHER_SOURCES
# Path matchers
@ -108,7 +95,6 @@ set(PATHMATCHER_SOURCES
pathmatcher/IPathMatcher.h
pathmatcher/MultiMatcher.h
pathmatcher/RegexpMatcher.h
pathmatcher/SimplePrefixMatcher.h
)
set(NET_SOURCES
@ -553,6 +539,9 @@ set(ATLAUNCHER_SOURCES
################################ COMPILE ################################
# we need zlib
find_package(ZLIB REQUIRED)
set(LOGIC_SOURCES
${CORE_SOURCES}
${PATHMATCHER_SOURCES}
@ -587,8 +576,6 @@ SET(LAUNCHER_SOURCES
# Application base
Application.h
Application.cpp
DataMigrationTask.h
DataMigrationTask.cpp
UpdateController.cpp
UpdateController.h
ApplicationMessage.h
@ -612,12 +599,9 @@ SET(LAUNCHER_SOURCES
resources/pe_light/pe_light.qrc
resources/pe_colored/pe_colored.qrc
resources/pe_blue/pe_blue.qrc
resources/breeze_dark/breeze_dark.qrc
resources/breeze_light/breeze_light.qrc
resources/OSX/OSX.qrc
resources/iOS/iOS.qrc
resources/flat/flat.qrc
resources/flat_white/flat_white.qrc
resources/documents/documents.qrc
../${Launcher_Branding_LogoQRC}
@ -665,8 +649,6 @@ SET(LAUNCHER_SOURCES
ui/themes/ITheme.h
ui/themes/SystemTheme.cpp
ui/themes/SystemTheme.h
ui/themes/ThemeManager.cpp
ui/themes/ThemeManager.h
# Processes
LaunchController.h
@ -691,8 +673,6 @@ SET(LAUNCHER_SOURCES
ui/pages/instance/GameOptionsPage.h
ui/pages/instance/VersionPage.cpp
ui/pages/instance/VersionPage.h
ui/pages/instance/ManagedPackPage.cpp
ui/pages/instance/ManagedPackPage.h
ui/pages/instance/TexturePackPage.h
ui/pages/instance/ResourcePackPage.h
ui/pages/instance/ShaderPackPage.h
@ -809,8 +789,6 @@ SET(LAUNCHER_SOURCES
ui/dialogs/ExportInstanceDialog.h
ui/dialogs/IconPickerDialog.cpp
ui/dialogs/IconPickerDialog.h
ui/dialogs/ImportResourcePackDialog.cpp
ui/dialogs/ImportResourcePackDialog.h
ui/dialogs/LoginDialog.cpp
ui/dialogs/LoginDialog.h
ui/dialogs/MSALoginDialog.cpp
@ -932,7 +910,6 @@ qt_wrap_ui(LAUNCHER_UI
ui/pages/instance/OtherLogsPage.ui
ui/pages/instance/InstanceSettingsPage.ui
ui/pages/instance/VersionPage.ui
ui/pages/instance/ManagedPackPage.ui
ui/pages/instance/WorldListPage.ui
ui/pages/instance/ScreenshotsPage.ui
ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui
@ -960,7 +937,6 @@ qt_wrap_ui(LAUNCHER_UI
ui/dialogs/SkinUploadDialog.ui
ui/dialogs/ExportInstanceDialog.ui
ui/dialogs/IconPickerDialog.ui
ui/dialogs/ImportResourcePackDialog.ui
ui/dialogs/MSALoginDialog.ui
ui/dialogs/OfflineLoginDialog.ui
ui/dialogs/AboutDialog.ui
@ -979,8 +955,6 @@ qt_add_resources(LAUNCHER_RESOURCES
resources/pe_light/pe_light.qrc
resources/pe_colored/pe_colored.qrc
resources/pe_blue/pe_blue.qrc
resources/breeze_dark/breeze_dark.qrc
resources/breeze_light/breeze_light.qrc
resources/OSX/OSX.qrc
resources/iOS/iOS.qrc
resources/flat/flat.qrc
@ -1087,99 +1061,96 @@ if(INSTALL_BUNDLE STREQUAL "full")
COMPONENT Runtime
)
# Bundle plugins
# Image formats
install(
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
CONFIGURATIONS Debug RelWithDebInfo ""
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "tga|tiff|mng" EXCLUDE
)
install(
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
CONFIGURATIONS Release MinSizeRel
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "tga|tiff|mng" EXCLUDE
REGEX "d\\." EXCLUDE
REGEX "_debug\\." EXCLUDE
REGEX "\\.dSYM" EXCLUDE
)
# Icon engines
install(
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
CONFIGURATIONS Debug RelWithDebInfo ""
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "fontawesome" EXCLUDE
)
install(
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
CONFIGURATIONS Release MinSizeRel
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "fontawesome" EXCLUDE
REGEX "d\\." EXCLUDE
REGEX "_debug\\." EXCLUDE
REGEX "\\.dSYM" EXCLUDE
)
# Platform plugins
install(
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
CONFIGURATIONS Debug RelWithDebInfo ""
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "minimal|linuxfb|offscreen" EXCLUDE
)
install(
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
CONFIGURATIONS Release MinSizeRel
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "minimal|linuxfb|offscreen" EXCLUDE
REGEX "[^2]d\\." EXCLUDE
REGEX "_debug\\." EXCLUDE
REGEX "\\.dSYM" EXCLUDE
)
# Style plugins
if(EXISTS "${QT_PLUGINS_DIR}/styles")
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
# Image formats
install(
DIRECTORY "${QT_PLUGINS_DIR}/styles"
CONFIGURATIONS Debug RelWithDebInfo ""
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "tga|tiff|mng" EXCLUDE
)
# Icon engines
install(
DIRECTORY "${QT_PLUGINS_DIR}/styles"
CONFIGURATIONS Release MinSizeRel
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "fontawesome" EXCLUDE
)
# Platform plugins
install(
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "minimal|linuxfb|offscreen" EXCLUDE
)
# Style plugins
if(EXISTS "${QT_PLUGINS_DIR}/styles")
install(
DIRECTORY "${QT_PLUGINS_DIR}/styles"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
)
endif()
# TLS plugins (Qt 6 only)
if(EXISTS "${QT_PLUGINS_DIR}/tls")
install(
DIRECTORY "${QT_PLUGINS_DIR}/tls"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
)
endif()
else()
# Image formats
install(
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "tga|tiff|mng" EXCLUDE
REGEX "d\\." EXCLUDE
REGEX "_debug\\." EXCLUDE
REGEX "\\.dSYM" EXCLUDE
)
endif()
# TLS plugins (Qt 6 only)
if(EXISTS "${QT_PLUGINS_DIR}/tls")
# Icon engines
install(
DIRECTORY "${QT_PLUGINS_DIR}/tls"
CONFIGURATIONS Debug RelWithDebInfo ""
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
PATTERN "*qopensslbackend*" EXCLUDE
PATTERN "*qcertonlybackend*" EXCLUDE
)
install(
DIRECTORY "${QT_PLUGINS_DIR}/tls"
CONFIGURATIONS Release MinSizeRel
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "dd\\." EXCLUDE
REGEX "fontawesome" EXCLUDE
REGEX "d\\." EXCLUDE
REGEX "_debug\\." EXCLUDE
REGEX "\\.dSYM" EXCLUDE
PATTERN "*qopensslbackend*" EXCLUDE
PATTERN "*qcertonlybackend*" EXCLUDE
)
# Platform plugins
install(
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "minimal|linuxfb|offscreen" EXCLUDE
REGEX "d\\." EXCLUDE
REGEX "_debug\\." EXCLUDE
REGEX "\\.dSYM" EXCLUDE
)
# Style plugins
if(EXISTS "${QT_PLUGINS_DIR}/styles")
install(
DIRECTORY "${QT_PLUGINS_DIR}/styles"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "d\\." EXCLUDE
REGEX "_debug\\." EXCLUDE
REGEX "\\.dSYM" EXCLUDE
)
endif()
# TLS plugins (Qt 6 only)
if(EXISTS "${QT_PLUGINS_DIR}/tls")
install(
DIRECTORY "${QT_PLUGINS_DIR}/tls"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "_debug\\." EXCLUDE
REGEX "\\.dSYM" EXCLUDE
)
endif()
endif()
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"

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

@ -49,7 +49,6 @@
#include "StringUtils.h"
#if defined Q_OS_WIN32
#define WIN32_LEAN_AND_MEAN
#include <objbase.h>
#include <objidl.h>
#include <shlguid.h>
@ -150,13 +149,9 @@ bool ensureFolderPathExists(QString foldernamepath)
return success;
}
/// @brief Copies a directory and it's contents from src to dest
/// @param offset subdirectory form src to copy to dest
/// @return if there was an error during the filecopy
bool copy::operator()(const QString& offset, bool dryRun)
bool copy::operator()(const QString& offset)
{
using copy_opts = fs::copy_options;
m_copied = 0; // reset counter
// NOTE always deep copy on windows. the alternatives are too messy.
#if defined Q_OS_WIN32
@ -176,21 +171,18 @@ bool copy::operator()(const QString& offset, bool dryRun)
// Function that'll do the actual copying
auto copy_file = [&](QString src_path, QString relative_dst_path) {
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
if (m_blacklist && m_blacklist->matches(relative_dst_path))
return;
auto dst_path = PathCombine(dst, relative_dst_path);
if (!dryRun) {
ensureFilePathExists(dst_path);
fs::copy(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), opt, err);
}
ensureFilePathExists(dst_path);
fs::copy(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), opt, err);
if (err) {
qWarning() << "Failed to copy files:" << QString::fromStdString(err.message());
qDebug() << "Source file:" << src_path;
qDebug() << "Destination file:" << dst_path;
}
m_copied++;
emit fileCopied(relative_dst_path);
};
// We can't use copy_opts::recursive because we need to take into account the
@ -344,35 +336,12 @@ QString getDesktopDir()
}
// Cross-platform Shortcut creation
bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon)
bool createShortCut(QString location, QString dest, QStringList args, QString name, QString icon)
{
#if defined(Q_OS_MACOS)
destination += ".command";
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
location = PathCombine(location, name + ".desktop");
QFile f(destination);
f.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream stream(&f);
QString argstring;
if (!args.empty())
argstring = " \"" + args.join("\" \"") + "\"";
stream << "#!/bin/bash"
<< "\n";
stream << "\""
<< target
<< "\" "
<< argstring
<< "\n";
stream.flush();
f.close();
f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther);
return true;
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
QFile f(destination);
QFile f(location);
f.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream stream(&f);
@ -384,12 +353,10 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
<< "\n";
stream << "Type=Application"
<< "\n";
stream << "Exec=\"" << target.toLocal8Bit() << "\"" << argstring.toLocal8Bit() << "\n";
stream << "TryExec=" << dest.toLocal8Bit() << "\n";
stream << "Exec=" << dest.toLocal8Bit() << argstring.toLocal8Bit() << "\n";
stream << "Name=" << name.toLocal8Bit() << "\n";
if (!icon.isEmpty())
{
stream << "Icon=" << icon.toLocal8Bit() << "\n";
}
stream << "Icon=" << icon.toLocal8Bit() << "\n";
stream.flush();
f.close();
@ -397,132 +364,25 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther);
return true;
#elif defined(Q_OS_WIN)
QFileInfo targetInfo(target);
#elif defined Q_OS_WIN
// TODO: Fix
// QFile file(PathCombine(location, name + ".lnk"));
// WCHAR *file_w;
// WCHAR *dest_w;
// WCHAR *args_w;
// file.fileName().toWCharArray(file_w);
// dest.toWCharArray(dest_w);
if (!targetInfo.exists())
{
qWarning() << "Target file does not exist!";
return false;
}
// QString argStr;
// for (int i = 0; i < args.count(); i++)
// {
// argStr.append(args[i]);
// argStr.append(" ");
// }
// argStr.toWCharArray(args_w);
target = targetInfo.absoluteFilePath();
if (target.length() >= MAX_PATH)
{
qWarning() << "Target file path is too long!";
return false;
}
if (!icon.isEmpty() && icon.length() >= MAX_PATH)
{
qWarning() << "Icon path is too long!";
return false;
}
destination += ".lnk";
if (destination.length() >= MAX_PATH)
{
qWarning() << "Destination path is too long!";
return false;
}
QString argStr;
int argCount = args.count();
for (int i = 0; i < argCount; i++)
{
if (args[i].contains(' '))
{
argStr.append('"').append(args[i]).append('"');
}
else
{
argStr.append(args[i]);
}
if (i < argCount - 1)
{
argStr.append(" ");
}
}
if (argStr.length() >= MAX_PATH)
{
qWarning() << "Arguments string is too long!";
return false;
}
HRESULT hres;
// ...yes, you need to initialize the entire COM stack just to make a shortcut
hres = CoInitialize(nullptr);
if (FAILED(hres))
{
qWarning() << "Failed to initialize COM!";
return false;
}
WCHAR wsz[MAX_PATH];
IShellLink* psl;
// create an IShellLink instance - this stores the shortcut's attributes
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres))
{
wmemset(wsz, 0, MAX_PATH);
target.toWCharArray(wsz);
psl->SetPath(wsz);
wmemset(wsz, 0, MAX_PATH);
argStr.toWCharArray(wsz);
psl->SetArguments(wsz);
wmemset(wsz, 0, MAX_PATH);
targetInfo.absolutePath().toWCharArray(wsz);
psl->SetWorkingDirectory(wsz); // "Starts in" attribute
if (!icon.isEmpty())
{
wmemset(wsz, 0, MAX_PATH);
icon.toWCharArray(wsz);
psl->SetIconLocation(wsz, 0);
}
// query an IPersistFile interface from our IShellLink instance
// this is the interface that will actually let us save the shortcut to disk!
IPersistFile* ppf;
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
if (SUCCEEDED(hres))
{
wmemset(wsz, 0, MAX_PATH);
destination.toWCharArray(wsz);
hres = ppf->Save(wsz, TRUE);
if (FAILED(hres))
{
qWarning() << "IPresistFile->Save() failed";
qWarning() << "hres = " << hres;
}
ppf->Release();
}
else
{
qWarning() << "Failed to query IPersistFile interface from IShellLink instance";
qWarning() << "hres = " << hres;
}
psl->Release();
}
else
{
qWarning() << "Failed to create IShellLink instance";
qWarning() << "hres = " << hres;
}
// go away COM, nobody likes you
CoUninitialize();
return SUCCEEDED(hres);
// return SUCCEEDED(CreateLink(file_w, dest_w, args_w));
return false;
#else
qWarning("Desktop Shortcuts not supported on your platform!");
return false;

View File

@ -40,7 +40,6 @@
#include <QDir>
#include <QFlags>
#include <QObject>
namespace FS {
@ -76,11 +75,9 @@ bool ensureFilePathExists(QString filenamepath);
*/
bool ensureFolderPathExists(QString filenamepath);
/// @brief Copies a directory and it's contents from src to dest
class copy : public QObject {
Q_OBJECT
class copy {
public:
copy(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent)
copy(const QString& src, const QString& dst)
{
m_src.setPath(src);
m_dst.setPath(dst);
@ -90,35 +87,21 @@ class copy : public QObject {
m_followSymlinks = follow;
return *this;
}
copy& matcher(const IPathMatcher* filter)
copy& blacklist(const IPathMatcher* filter)
{
m_matcher = filter;
m_blacklist = filter;
return *this;
}
copy& whitelist(bool whitelist)
{
m_whitelist = whitelist;
return *this;
}
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
int totalCopied() { return m_copied; }
signals:
void fileCopied(const QString& relativeName);
// TODO: maybe add a "shouldCopy" signal in the future?
bool operator()() { return operator()(QString()); }
private:
bool operator()(const QString& offset, bool dryRun = false);
bool operator()(const QString& offset);
private:
bool m_followSymlinks = true;
const IPathMatcher* m_matcher = nullptr;
bool m_whitelist = false;
const IPathMatcher* m_blacklist = nullptr;
QDir m_src;
QDir m_dst;
int m_copied;
};
/**
@ -172,9 +155,4 @@ QString getDesktopDir();
// Overrides one folder with the contents of another, preserving items exclusive to the first folder
// Equivalent to doing QDir::rename, but allowing for overrides
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);
}

View File

@ -1,135 +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
{
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";
// 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;
}
// ======= 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;
}

View File

@ -1,41 +0,0 @@
//
// Created by marcelohdez on 10/22/22.
//
#pragma once
#include <QStringList>
struct InstanceCopyPrefs {
public:
[[nodiscard]] bool allTrue() const;
[[nodiscard]] QString getSelectedFiltersAsRegex() 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;
// 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);
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;
};

View File

@ -5,17 +5,15 @@
#include "pathmatcher/RegexpMatcher.h"
#include <QtConcurrentRun>
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs)
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime)
{
m_origInstance = origInstance;
m_keepPlaytime = prefs.isKeepPlaytimeEnabled();
m_keepPlaytime = keepPlaytime;
QString filters = prefs.getSelectedFiltersAsRegex();
if (!filters.isEmpty())
if(!copySaves)
{
// Set regex filter:
// FIXME: get this from the original instance type...
auto matcherReal = new RegexpMatcher(filters);
auto matcherReal = new RegexpMatcher("[.]?minecraft/saves");
matcherReal->caseSensitive(false);
m_matcher.reset(matcherReal);
}
@ -25,12 +23,10 @@ void InstanceCopyTask::executeTask()
{
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this]{
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
folderCopy.followSymlinks(false).matcher(m_matcher.get());
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
folderCopy.followSymlinks(false).blacklist(m_matcher.get());
return folderCopy();
});
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy);
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished);
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &InstanceCopyTask::copyAborted);
m_copyFutureWatcher.setFuture(m_copyFuture);

View File

@ -1,21 +1,20 @@
#pragma once
#include "tasks/Task.h"
#include "net/NetJob.h"
#include <QUrl>
#include <QFuture>
#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 "tasks/Task.h"
#include "BaseVersion.h"
#include "BaseInstance.h"
#include "InstanceTask.h"
class InstanceCopyTask : public InstanceTask
{
Q_OBJECT
public:
explicit InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs);
explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime);
protected:
//! Entry point for tasks.
@ -23,8 +22,7 @@ protected:
void copyFinished();
void copyAborted();
private:
/* data */
private: /* data */
InstancePtr m_origInstance;
QFuture<bool> m_copyFuture;
QFutureWatcher<bool> m_copyFutureWatcher;

View File

@ -25,13 +25,9 @@ void InstanceCreationTask::executeTask()
return;
qWarning() << "Instance creation failed!";
if (!m_error_message.isEmpty()) {
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."));
}
emitFailed(tr("Error while creating new instance."));
return;
}

View File

@ -55,9 +55,11 @@
#include <quazip/quazipdir.h>
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent, QMap<QString, QString>&& extra_info)
: m_sourceUrl(sourceUrl), m_extra_info(extra_info), m_parent(parent)
{}
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent)
{
m_sourceUrl = sourceUrl;
m_parent = parent;
}
bool InstanceImportTask::abort()
{
@ -162,14 +164,18 @@ void InstanceImportTask::processZipPack()
}
else
{
QStringList paths_to_ignore { "overrides/" };
QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json");
if (QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg", paths_to_ignore); !mmcRoot.isNull()) {
if (!mmcRoot.isNull())
{
// process as MultiMC instance/pack
qDebug() << "MultiMC:" << mmcRoot;
root = mmcRoot;
m_modpackType = ModpackType::MultiMC;
} else if (QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json", paths_to_ignore); !flameRoot.isNull()) {
}
else if(!flameRoot.isNull())
{
// process as Flame pack
qDebug() << "Flame:" << flameRoot;
root = flameRoot;
@ -257,34 +263,14 @@ void InstanceImportTask::extractAborted()
void InstanceImportTask::processFlame()
{
FlameCreationTask* inst_creation_task = nullptr;
if (!m_extra_info.isEmpty()) {
auto pack_id_it = m_extra_info.constFind("pack_id");
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
auto pack_id = pack_id_it.value();
auto pack_version_id_it = m_extra_info.constFind("pack_version_id");
Q_ASSERT(pack_version_id_it != m_extra_info.constEnd());
auto pack_version_id = pack_version_id_it.value();
QString original_instance_id;
auto original_instance_id_it = m_extra_info.constFind("original_instance_id");
if (original_instance_id_it != m_extra_info.constEnd())
original_instance_id = original_instance_id_it.value();
inst_creation_task = new FlameCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
} else {
// FIXME: Find a way to get IDs in directly imported ZIPs
inst_creation_task = new FlameCreationTask(m_stagingPath, m_globalSettings, m_parent, {}, {});
}
auto* inst_creation_task = new FlameCreationTask(m_stagingPath, m_globalSettings, m_parent);
inst_creation_task->setName(*this);
inst_creation_task->setIcon(m_instIcon);
inst_creation_task->setGroup(m_instGroup);
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] {
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
setOverride(inst_creation_task->shouldOverride());
emitSucceeded();
});
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
@ -341,41 +327,14 @@ void InstanceImportTask::processMultiMC()
void InstanceImportTask::processModrinth()
{
ModrinthCreationTask* inst_creation_task = nullptr;
if (!m_extra_info.isEmpty()) {
auto pack_id_it = m_extra_info.constFind("pack_id");
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
auto pack_id = pack_id_it.value();
QString pack_version_id;
auto pack_version_id_it = m_extra_info.constFind("pack_version_id");
if (pack_version_id_it != m_extra_info.constEnd())
pack_version_id = pack_version_id_it.value();
QString original_instance_id;
auto original_instance_id_it = m_extra_info.constFind("original_instance_id");
if (original_instance_id_it != m_extra_info.constEnd())
original_instance_id = original_instance_id_it.value();
inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
} else {
QString pack_id;
if (!m_sourceUrl.isEmpty()) {
QRegularExpression regex(R"(data\/(.*)\/versions)");
pack_id = regex.match(m_sourceUrl.toString()).captured(1);
}
// FIXME: Find a way to get the ID in directly imported ZIPs
inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id);
}
auto* inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, m_sourceUrl.toString());
inst_creation_task->setName(*this);
inst_creation_task->setIcon(m_instIcon);
inst_creation_task->setGroup(m_instGroup);
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] {
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
setOverride(inst_creation_task->shouldOverride());
emitSucceeded();
});
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);

View File

@ -56,7 +56,7 @@ class InstanceImportTask : public InstanceTask
{
Q_OBJECT
public:
explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {});
explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr);
bool abort() override;
const QVector<Flame::File> &getBlockedFiles() const
@ -101,10 +101,6 @@ private: /* data */
Modrinth,
} m_modpackType = ModpackType::Unknown;
// Extra info we might need, that's available before, but can't be derived from
// the source URL / the resource it points to alone.
QMap<QString, QString> m_extra_info;
//FIXME: nuke
QWidget* m_parent;
};

View File

@ -816,7 +816,7 @@ class InstanceStaging : public Task {
void childSucceded()
{
unsigned sleepTime = backoff();
if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get()))
if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, m_child->shouldOverride()))
{
emitSucceeded();
return;
@ -880,22 +880,25 @@ QString InstanceList::getStagedInstancePath()
return path;
}
bool InstanceList::commitStagedInstance(const QString& path, InstanceName const& instanceName, const QString& groupName, InstanceTask const& commiting)
bool InstanceList::commitStagedInstance(const QString& path, InstanceName const& instanceName, const QString& groupName, bool should_override)
{
QDir dir;
QString instID;
InstancePtr inst;
auto should_override = commiting.shouldOverride();
if (should_override) {
instID = commiting.originalInstanceID();
// This is to avoid problems when the instance folder gets manually renamed
if ((inst = getInstanceByManagedName(instanceName.originalName()))) {
instID = QFileInfo(inst->instanceRoot()).fileName();
} else if ((inst = getInstanceByManagedName(instanceName.modifiedName()))) {
instID = QFileInfo(inst->instanceRoot()).fileName();
} else {
instID = FS::RemoveInvalidFilenameChars(instanceName.modifiedName(), '-');
}
} else {
instID = FS::DirNameFromString(instanceName.modifiedName(), m_instDir);
}
Q_ASSERT(!instID.isEmpty());
{
WatchLock lock(m_watcher, m_instDir);
QString destination = FS::PathCombine(m_instDir, instID);

View File

@ -133,7 +133,7 @@ public:
* should_override is used when another similar instance already exists, and we want to override it
* - for instance, when updating it.
*/
bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, const QString& groupName, const InstanceTask&);
bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, const QString& groupName, bool should_override);
/**
* Destroy a previously created staging area given by @keyPath - used when creation fails.

View File

@ -5,7 +5,6 @@
#include "ui/pages/BasePageProvider.h"
#include "ui/pages/instance/LogPage.h"
#include "ui/pages/instance/VersionPage.h"
#include "ui/pages/instance/ManagedPackPage.h"
#include "ui/pages/instance/ModFolderPage.h"
#include "ui/pages/instance/ResourcePackPage.h"
#include "ui/pages/instance/TexturePackPage.h"
@ -34,7 +33,6 @@ public:
values.append(new LogPage(inst));
std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst);
values.append(new VersionPage(onesix.get()));
values.append(ManagedPackPage::createPage(onesix.get()));
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList());
modsPage->setFilter("%1 (*.zip *.jar *.litemod)");
values.append(modsPage);

View File

@ -18,29 +18,6 @@ InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& ol
return InstanceNameChange::ShouldKeep;
}
ShouldUpdate askIfShouldUpdate(QWidget *parent, QString original_version_name)
{
auto info = CustomMessageBox::selectable(
parent, QObject::tr("Similar modpack was found!"),
QObject::tr("One or more of your instances are from this same modpack%1. Do you want to create a "
"separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before "
"updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).")
.arg(original_version_name),
QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort);
info->setButtonText(QMessageBox::Ok, QObject::tr("Update existing instance"));
info->setButtonText(QMessageBox::Abort, QObject::tr("Create new instance"));
info->setButtonText(QMessageBox::Reset, QObject::tr("Cancel"));
info->exec();
if (info->clickedButton() == info->button(QMessageBox::Ok))
return ShouldUpdate::Update;
if (info->clickedButton() == info->button(QMessageBox::Abort))
return ShouldUpdate::SkipUpdating;
return ShouldUpdate::Cancel;
}
QString InstanceName::name() const
{
if (!m_modified_name.isEmpty())

View File

@ -6,8 +6,6 @@
/* Helpers */
enum class InstanceNameChange { ShouldChange, ShouldKeep };
[[nodiscard]] InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name);
enum class ShouldUpdate { Update, SkipUpdating, Cancel };
[[nodiscard]] ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name);
struct InstanceName {
public:
@ -44,20 +42,10 @@ class InstanceTask : public Task, public InstanceName {
void setGroup(const QString& group) { m_instGroup = group; }
QString group() const { return m_instGroup; }
[[nodiscard]] bool shouldConfirmUpdate() const { return m_confirm_update; }
void setConfirmUpdate(bool confirm) { m_confirm_update = confirm; }
bool shouldOverride() const { return m_override_existing; }
[[nodiscard]] QString originalInstanceID() const { return m_original_instance_id; };
protected:
void setOverride(bool override, QString instance_id_to_override = {})
{
m_override_existing = override;
if (!instance_id_to_override.isEmpty())
m_original_instance_id = instance_id_to_override;
}
void setOverride(bool override) { m_override_existing = override; }
protected: /* data */
SettingsObjectPtr m_globalSettings;
@ -66,7 +54,4 @@ class InstanceTask : public Task, public InstanceName {
QString m_stagingPath;
bool m_override_existing = false;
bool m_confirm_update = true;
QString m_original_instance_id;
};

View File

@ -28,11 +28,11 @@ QString Time::prettifyDuration(int64_t duration) {
int days = (int) (duration / 24);
if((hours == 0)&&(days == 0))
{
return QObject::tr("%1min %2s").arg(minutes).arg(seconds);
return QObject::tr("%1m %2s").arg(minutes).arg(seconds);
}
if (days == 0)
{
return QObject::tr("%1h %2min").arg(hours).arg(minutes);
return QObject::tr("%1h %2m").arg(hours).arg(minutes);
}
return QObject::tr("%1d %2h %3min").arg(days).arg(hours).arg(minutes);
return QObject::tr("%1d %2h %3m").arg(days).arg(hours).arg(minutes);
}

View File

@ -39,7 +39,6 @@
#include "MMCZip.h"
#include "FileSystem.h"
#include <QCoreApplication>
#include <QDebug>
// ours
@ -229,27 +228,23 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
}
// ours
QString MMCZip::findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root)
QString MMCZip::findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root)
{
QuaZipDir rootDir(zip, root);
for (auto&& fileName : rootDir.entryList(QDir::Files)) {
if (fileName == what)
for(auto fileName: rootDir.entryList(QDir::Files))
{
if(fileName == what)
return root;
QCoreApplication::processEvents();
}
// Recurse the search to non-ignored subfolders
for (auto&& fileName : rootDir.entryList(QDir::Dirs)) {
if (ignore_paths.contains(fileName))
continue;
QString result = findFolderOfFileInZip(zip, what, ignore_paths, root + fileName);
if (!result.isEmpty())
for(auto fileName: rootDir.entryList(QDir::Dirs))
{
QString result = findFolderOfFileInZip(zip, what, root + fileName);
if(!result.isEmpty())
{
return result;
}
}
return {};
return QString();
}
// ours

View File

@ -80,11 +80,9 @@ namespace MMCZip
/**
* Find a single file in archive by file name (not path)
*
* \param ignore_paths paths to skip when recursing the search
*
* \return the path prefix where the file is
*/
QString findFolderOfFileInZip(QuaZip * zip, const QString & what, const QStringList& ignore_paths = {}, const QString &root = QString(""));
QString findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root = QString(""));
/**
* Find a multiple files of the same name in archive by file name

View File

@ -1,95 +0,0 @@
#pragma once
#include <QCoreApplication>
#include <QPixmapCache>
#include <QThread>
#define GET_TYPE() \
Qt::ConnectionType type; \
if (QThread::currentThread() != QCoreApplication::instance()->thread()) \
type = Qt::BlockingQueuedConnection; \
else \
type = Qt::DirectConnection;
#define DEFINE_FUNC_NO_PARAM(NAME, RET_TYPE) \
static RET_TYPE NAME() \
{ \
RET_TYPE ret; \
GET_TYPE() \
QMetaObject::invokeMethod(s_instance, "_" #NAME, type, Q_RETURN_ARG(RET_TYPE, ret)); \
return ret; \
}
#define DEFINE_FUNC_ONE_PARAM(NAME, RET_TYPE, PARAM_1_TYPE) \
static RET_TYPE NAME(PARAM_1_TYPE p1) \
{ \
RET_TYPE ret; \
GET_TYPE() \
QMetaObject::invokeMethod(s_instance, "_" #NAME, type, Q_RETURN_ARG(RET_TYPE, ret), Q_ARG(PARAM_1_TYPE, p1)); \
return ret; \
}
#define DEFINE_FUNC_TWO_PARAM(NAME, RET_TYPE, PARAM_1_TYPE, PARAM_2_TYPE) \
static RET_TYPE NAME(PARAM_1_TYPE p1, PARAM_2_TYPE p2) \
{ \
RET_TYPE ret; \
GET_TYPE() \
QMetaObject::invokeMethod(s_instance, "_" #NAME, type, Q_RETURN_ARG(RET_TYPE, ret), Q_ARG(PARAM_1_TYPE, p1), \
Q_ARG(PARAM_2_TYPE, p2)); \
return ret; \
}
/** A wrapper around QPixmapCache with thread affinity with the main thread.
*/
class PixmapCache final : public QObject {
Q_OBJECT
public:
PixmapCache(QObject* parent) : QObject(parent) {}
~PixmapCache() override = default;
static PixmapCache& instance() { return *s_instance; }
static void setInstance(PixmapCache* i) { s_instance = i; }
public:
DEFINE_FUNC_NO_PARAM(cacheLimit, int)
DEFINE_FUNC_NO_PARAM(clear, bool)
DEFINE_FUNC_TWO_PARAM(find, bool, const QString&, QPixmap*)
DEFINE_FUNC_TWO_PARAM(find, bool, const QPixmapCache::Key&, QPixmap*)
DEFINE_FUNC_TWO_PARAM(insert, bool, const QString&, const QPixmap&)
DEFINE_FUNC_ONE_PARAM(insert, QPixmapCache::Key, const QPixmap&)
DEFINE_FUNC_ONE_PARAM(remove, bool, const QString&)
DEFINE_FUNC_ONE_PARAM(remove, bool, const QPixmapCache::Key&)
DEFINE_FUNC_TWO_PARAM(replace, bool, const QPixmapCache::Key&, const QPixmap&)
DEFINE_FUNC_ONE_PARAM(setCacheLimit, bool, int)
// NOTE: Every function returns something non-void to simplify the macros.
private slots:
int _cacheLimit() { return QPixmapCache::cacheLimit(); }
bool _clear()
{
QPixmapCache::clear();
return true;
}
bool _find(const QString& key, QPixmap* pixmap) { return QPixmapCache::find(key, pixmap); }
bool _find(const QPixmapCache::Key& key, QPixmap* pixmap) { return QPixmapCache::find(key, pixmap); }
bool _insert(const QString& key, const QPixmap& pixmap) { return QPixmapCache::insert(key, pixmap); }
QPixmapCache::Key _insert(const QPixmap& pixmap) { return QPixmapCache::insert(pixmap); }
bool _remove(const QString& key)
{
QPixmapCache::remove(key);
return true;
}
bool _remove(const QPixmapCache::Key& key)
{
QPixmapCache::remove(key);
return true;
}
bool _replace(const QPixmapCache::Key& key, const QPixmap& pixmap) { return QPixmapCache::replace(key, pixmap); }
bool _setCacheLimit(int n)
{
QPixmapCache::setCacheLimit(n);
return true;
}
private:
static PixmapCache* s_instance;
};

View File

@ -1,90 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PrismLauncher - Minecraft Launcher
* Copyright (C) 2022 Jan Drögehoff <sentrycraft123@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <QStringList>
#include <QDir>
#include <QString>
#include <QtGlobal>
#include "MangoHud.h"
#include "FileSystem.h"
#include "Json.h"
namespace MangoHud {
QString getLibraryString()
{
/*
* Check for vulkan layers in this order:
*
* $VK_LAYER_PATH
* $XDG_DATA_DIRS (/usr/local/share/:/usr/share/)
* $XDG_DATA_HOME (~/.local/share)
* /etc
* $XDG_CONFIG_DIRS (/etc/xdg)
* $XDG_CONFIG_HOME (~/.config)
*/
QStringList vkLayerList;
{
QString home = QDir::homePath();
QString vkLayerPath = qEnvironmentVariable("VK_LAYER_PATH");
if (!vkLayerPath.isEmpty()) {
vkLayerList << vkLayerPath;
}
QStringList xdgDataDirs = qEnvironmentVariable("XDG_DATA_DIRS", "/usr/local/share/:/usr/share/").split(QLatin1String(":"));
for (QString dir : xdgDataDirs) {
vkLayerList << FS::PathCombine(dir, "vulkan", "implicit_layer.d");
}
QString xdgDataHome = qEnvironmentVariable("XDG_DATA_HOME");
if (xdgDataHome.isEmpty()) {
xdgDataHome = FS::PathCombine(home, ".local", "share");
}
vkLayerList << FS::PathCombine(xdgDataHome, "vulkan", "implicit_layer.d");
vkLayerList << "/etc";
QStringList xdgConfigDirs = qEnvironmentVariable("XDG_CONFIG_DIRS", "/etc/xdg").split(QLatin1String(":"));
for (QString dir : xdgConfigDirs) {
vkLayerList << FS::PathCombine(dir, "vulkan", "implicit_layer.d");
}
QString xdgConfigHome = qEnvironmentVariable("XDG_CONFIG_HOME");
if (xdgConfigHome.isEmpty()) {
xdgConfigHome = FS::PathCombine(home, ".config");
}
vkLayerList << FS::PathCombine(xdgConfigHome, "vulkan", "implicit_layer.d");
}
for (QString vkLayer : vkLayerList) {
QString filePath = FS::PathCombine(vkLayer, "MangoHud.json");
if (!QFile::exists(filePath))
continue;
auto conf = Json::requireDocument(filePath, vkLayer);
auto confObject = Json::requireObject(conf, vkLayer);
auto layer = Json::ensureObject(confObject, "layer");
return Json::ensureString(layer, "library_path");
}
return QString();
}
} // namespace MangoHud

View File

@ -1,27 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PrismLauncher - Minecraft Launcher
* Copyright (C) 2022 Jan Drögehoff <sentrycraft123@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <QString>
#include <QStringList>
namespace MangoHud {
QString getLibraryString();
}

View File

@ -311,14 +311,14 @@ QModelIndex VersionProxyModel::index(int row, int column, const QModelIndex &par
int VersionProxyModel::columnCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : m_columns.size();
return m_columns.size();
}
int VersionProxyModel::rowCount(const QModelIndex &parent) const
{
if(sourceModel())
{
return sourceModel()->rowCount(parent);
return sourceModel()->rowCount();
}
return 0;
}

View File

@ -242,7 +242,7 @@ Qt::DropActions IconList::supportedDropActions() const
return Qt::CopyAction;
}
bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, [[maybe_unused]] int row, [[maybe_unused]] int column, [[maybe_unused]] const QModelIndex &parent)
bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
if (action == Qt::IgnoreAction)
return true;
@ -302,7 +302,7 @@ QVariant IconList::data(const QModelIndex &index, int role) const
int IconList::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : icons.size();
return icons.size();
}
void IconList::installIcons(const QStringList &iconFiles)

View File

@ -439,28 +439,19 @@ QList<QString> JavaUtils::FindJavaPaths()
javas.append(FS::PathCombine(prefix, "bin/java"));
}
};
// java installed in a snap is installed in the standard directory, but underneath $SNAP
auto snap = qEnvironmentVariable("SNAP");
auto scanJavaDirs = [&](const QString & dirPath)
{
scanJavaDir(dirPath);
if (!snap.isNull()) {
scanJavaDir(snap + dirPath);
}
};
// oracle RPMs
scanJavaDirs("/usr/java");
scanJavaDir("/usr/java");
// general locations used by distro packaging
scanJavaDirs("/usr/lib/jvm");
scanJavaDirs("/usr/lib64/jvm");
scanJavaDirs("/usr/lib32/jvm");
scanJavaDir("/usr/lib/jvm");
scanJavaDir("/usr/lib64/jvm");
scanJavaDir("/usr/lib32/jvm");
// javas stored in Prism Launcher's folder
scanJavaDirs("java");
scanJavaDir("java");
// manually installed JDKs in /opt
scanJavaDirs("/opt/jdk");
scanJavaDirs("/opt/jdks");
scanJavaDir("/opt/jdk");
scanJavaDir("/opt/jdks");
// flatpak
scanJavaDirs("/app/jdk");
scanJavaDir("/app/jdk");
javas = addJavasFromEnv(javas);
javas.removeDuplicates();
return javas;

View File

@ -81,12 +81,9 @@ int main(int argc, char *argv[])
Q_INIT_RESOURCE(pe_light);
Q_INIT_RESOURCE(pe_blue);
Q_INIT_RESOURCE(pe_colored);
Q_INIT_RESOURCE(breeze_dark);
Q_INIT_RESOURCE(breeze_light);
Q_INIT_RESOURCE(OSX);
Q_INIT_RESOURCE(iOS);
Q_INIT_RESOURCE(flat);
Q_INIT_RESOURCE(flat_white);
return app.exec();
}
case Application::Failed:

View File

@ -58,11 +58,11 @@ QVariant Index::data(const QModelIndex &index, int role) const
}
int Index::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : m_lists.size();
return m_lists.size();
}
int Index::columnCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : 1;
return 1;
}
QVariant Index::headerData(int section, Qt::Orientation orientation, int role) const
{

View File

@ -60,6 +60,11 @@ struct Require
QString suggests;
};
inline Q_DECL_PURE_FUNCTION uint qHash(const Require &key, uint seed = 0) Q_DECL_NOTHROW
{
return qHash(key.uid, seed);
}
using RequireSet = std::set<Require>;
void parseIndex(const QJsonObject &obj, Index *ptr);

View File

@ -10,7 +10,7 @@ typedef std::shared_ptr<Agent> AgentPtr;
class Agent {
public:
Agent(LibraryPtr library, const QString &argument)
Agent(LibraryPtr library, QString &argument)
{
m_library = library;
m_argument = argument;

View File

@ -1,9 +1,8 @@
// 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 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 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
@ -88,10 +87,6 @@
#include "minecraft/gameoptions/GameOptions.h"
#include "minecraft/update/FoldersTask.h"
#ifdef Q_OS_LINUX
#include "MangoHud.h"
#endif
#define IBUS "@im=ibus"
// all of this because keeping things compatible with deprecated old settings
@ -441,17 +436,6 @@ QStringList MinecraftInstance::javaArguments()
return args;
}
QString MinecraftInstance::getLauncher()
{
auto profile = m_components->getProfile();
// use legacy launcher if the traits are set
if (profile->getTraits().contains("legacyLaunch") || profile->getTraits().contains("alphaLaunch"))
return "legacy";
return "standard";
}
QMap<QString, QString> MinecraftInstance::getVariables()
{
QMap<QString, QString> out;
@ -486,22 +470,9 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
#ifdef Q_OS_LINUX
if (settings()->get("EnableMangoHud").toBool() && APPLICATION->capabilities() & Application::SupportsMangoHud)
{
auto preload = env.value("LD_PRELOAD", "") + ":libMangoHud_dlsym.so:libMangoHud.so";
auto preloadList = env.value("LD_PRELOAD").split(QLatin1String(":"));
auto libPaths = env.value("LD_LIBRARY_PATH").split(QLatin1String(":"));
auto mangoHudLibString = MangoHud::getLibraryString();
if (!mangoHudLibString.isEmpty())
{
QFileInfo mangoHudLib(mangoHudLibString);
// dlsym variant is only needed for OpenGL and not included in the vulkan layer
preloadList << "libMangoHud_dlsym.so" << mangoHudLib.fileName();
libPaths << mangoHudLib.absolutePath();
}
env.insert("LD_PRELOAD", preloadList.join(QLatin1String(":")));
env.insert("LD_LIBRARY_PATH", libPaths.join(QLatin1String(":")));
env.insert("LD_PRELOAD", preload);
env.insert("MANGOHUD", "1");
}
@ -656,13 +627,26 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS
launchScript += "sessionId " + session->session + "\n";
}
// libraries and class path.
{
QStringList jars, nativeJars;
profile->getLibraryFiles(runtimeContext(), jars, nativeJars, getLocalLibraryPath(), binRoot());
for(auto file: jars)
{
launchScript += "cp " + file + "\n";
}
for(auto file: nativeJars)
{
launchScript += "ext " + file + "\n";
}
launchScript += "natives " + getNativePath() + "\n";
}
for (auto trait : profile->getTraits())
{
launchScript += "traits " + trait + "\n";
}
launchScript += "launcher " + getLauncher() + "\n";
launchScript += "launcher onesix\n";
// qDebug() << "Generated launch script:" << launchScript;
return launchScript;
}
@ -798,8 +782,6 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr
out << "Window size: " + QString::number(width) + " x " + QString::number(height);
}
out << "";
out << "Launcher: " + getLauncher();
out << "";
return out;
}
@ -1127,6 +1109,8 @@ std::shared_ptr<ResourcePackFolderModel> MinecraftInstance::resourcePackList() c
if (!m_resource_pack_list)
{
m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir()));
m_resource_pack_list->enableInteraction(!isRunning());
connect(this, &BaseInstance::runningStatusChanged, m_resource_pack_list.get(), &ResourcePackFolderModel::disableInteraction);
}
return m_resource_pack_list;
}
@ -1136,6 +1120,8 @@ std::shared_ptr<TexturePackFolderModel> MinecraftInstance::texturePackList() con
if (!m_texture_pack_list)
{
m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir()));
m_texture_pack_list->disableInteraction(isRunning());
connect(this, &BaseInstance::runningStatusChanged, m_texture_pack_list.get(), &ModFolderModel::disableInteraction);
}
return m_texture_pack_list;
}
@ -1145,6 +1131,8 @@ std::shared_ptr<ShaderPackFolderModel> MinecraftInstance::shaderPackList() const
if (!m_shader_pack_list)
{
m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir()));
m_shader_pack_list->disableInteraction(isRunning());
connect(this, &BaseInstance::runningStatusChanged, m_shader_pack_list.get(), &ModFolderModel::disableInteraction);
}
return m_shader_pack_list;
}

View File

@ -1,8 +1,7 @@
// 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 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
@ -131,7 +130,6 @@ public:
QString createLaunchScript(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin);
/// get arguments passed to java
QStringList javaArguments();
QString getLauncher();
/// get variables for launch command variable substitution/environment
QMap<QString, QString> getVariables() override;

View File

@ -135,7 +135,7 @@ QJsonObject libDownloadInfoToJson(MojangLibraryDownloadInfo::Ptr libinfo)
{
out.insert("artifact", downloadInfoToJson(libinfo->artifact));
}
if(!libinfo->classifiers.isEmpty())
if(libinfo->classifiers.size())
{
QJsonObject classifiersOut;
for(auto iter = libinfo->classifiers.begin(); iter != libinfo->classifiers.end(); iter++)
@ -297,7 +297,7 @@ void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObj
{
out.insert("assetIndex", assetIndexToJson(in->mojangAssetIndex));
}
if(!in->mojangDownloads.isEmpty())
if(in->mojangDownloads.size())
{
QJsonObject downloadsOut;
for(auto iter = in->mojangDownloads.begin(); iter != in->mojangDownloads.end(); iter++)
@ -306,15 +306,6 @@ void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObj
}
out.insert("downloads", downloadsOut);
}
if(!in->compatibleJavaMajors.isEmpty())
{
QJsonArray compatibleJavaMajorsOut;
for(auto compatibleJavaMajor : in->compatibleJavaMajors)
{
compatibleJavaMajorsOut.append(compatibleJavaMajor);
}
out.insert("compatibleJavaMajors", compatibleJavaMajorsOut);
}
}
QJsonDocument MojangVersionFormat::versionFileToJson(const VersionFilePtr &patch)
@ -405,7 +396,7 @@ QJsonObject MojangVersionFormat::libraryToJson(Library *library)
iter++;
}
libRoot.insert("natives", nativeList);
if (!library->m_extractExcludes.isEmpty())
if (library->m_extractExcludes.size())
{
QJsonArray excludes;
QJsonObject extract;
@ -417,7 +408,7 @@ QJsonObject MojangVersionFormat::libraryToJson(Library *library)
libRoot.insert("extract", extract);
}
}
if (!library->m_rules.isEmpty())
if (library->m_rules.size())
{
QJsonArray allRules;
for (auto &rule : library->m_rules)

View File

@ -63,13 +63,13 @@ LibraryPtr OneSixVersionFormat::libraryFromJson(ProblemContainer & problems, con
QJsonObject OneSixVersionFormat::libraryToJson(Library *library)
{
QJsonObject libRoot = MojangVersionFormat::libraryToJson(library);
if (!library->m_absoluteURL.isEmpty())
if (library->m_absoluteURL.size())
libRoot.insert("MMC-absoluteUrl", library->m_absoluteURL);
if (!library->m_hint.isEmpty())
if (library->m_hint.size())
libRoot.insert("MMC-hint", library->m_hint);
if (!library->m_filename.isEmpty())
if (library->m_filename.size())
libRoot.insert("MMC-filename", library->m_filename);
if (!library->m_displayname.isEmpty())
if (library->m_displayname.size())
libRoot.insert("MMC-displayname", library->m_displayname);
return libRoot;
}
@ -225,10 +225,11 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc
{
QJsonObject agentObj = requireObject(agentVal);
auto lib = libraryFromJson(*out, agentObj, filename);
QString arg = "";
readString(agentObj, "argument", arg);
if (agentObj.contains("argument"))
{
readString(agentObj, "argument", arg);
}
AgentPtr agent(new Agent(lib, arg));
out->agents.append(agent);
}
@ -331,20 +332,6 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch
writeString(root, "appletClass", patch->appletClass);
writeStringList(root, "+tweakers", patch->addTweakers);
writeStringList(root, "+traits", patch->traits.values());
writeStringList(root, "+jvmArgs", patch->addnJvmArguments);
if (!patch->agents.isEmpty())
{
QJsonArray array;
for (auto value: patch->agents)
{
QJsonObject agentOut = OneSixVersionFormat::libraryToJson(value->library().get());
if (!value->argument().isEmpty())
agentOut.insert("argument", value->argument());
array.append(agentOut);
}
root.insert("+agents", array);
}
if (!patch->libraries.isEmpty())
{
QJsonArray array;

View File

@ -1,8 +1,7 @@
// 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 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
@ -48,6 +47,7 @@
#include "Exception.h"
#include "minecraft/OneSixVersionFormat.h"
#include "FileSystem.h"
#include "meta/Index.h"
#include "minecraft/MinecraftInstance.h"
#include "Json.h"
@ -55,6 +55,7 @@
#include "PackProfile_p.h"
#include "ComponentUpdateTask.h"
#include "Application.h"
#include "modplatform/ModAPI.h"
static const QMap<QString, ModAPI::ModLoaderType> modloaderMapping{
@ -612,7 +613,7 @@ QVariant PackProfile::data(const QModelIndex &index, int role) const
bool PackProfile::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(index.parent()))
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(index))
{
return false;
}
@ -674,12 +675,12 @@ Qt::ItemFlags PackProfile::flags(const QModelIndex &index) const
int PackProfile::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : d->components.size();
return d->components.size();
}
int PackProfile::columnCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : NUM_COLUMNS;
return NUM_COLUMNS;
}
void PackProfile::move(const int index, const MoveDirection direction)
@ -737,11 +738,6 @@ void PackProfile::installCustomJar(QString selectedFile)
installCustomJar_internal(selectedFile);
}
void PackProfile::installAgents(QStringList selectedFiles)
{
installAgents_internal(selectedFiles);
}
bool PackProfile::installEmpty(const QString& uid, const QString& name)
{
QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches");
@ -836,14 +832,18 @@ bool PackProfile::installJarMods_internal(QStringList filepaths)
for(auto filepath:filepaths)
{
QFileInfo sourceInfo(filepath);
QString id = QUuid::createUuid().toString(QUuid::WithoutBraces);
auto uuid = QUuid::createUuid();
QString id = uuid.toString().remove('{').remove('}');
QString target_filename = id + ".jar";
QString target_id = "custom.jarmod." + id;
QString target_id = "org.multimc.jarmod." + id;
QString target_name = sourceInfo.completeBaseName() + " (jar mod)";
QString finalPath = FS::PathCombine(d->m_instance->jarModsDir(), target_filename);
QFileInfo targetInfo(finalPath);
Q_ASSERT(!targetInfo.exists());
if(targetInfo.exists())
{
return false;
}
if (!QFile::copy(sourceInfo.absoluteFilePath(),QFileInfo(finalPath).absoluteFilePath()))
{
@ -852,7 +852,7 @@ bool PackProfile::installJarMods_internal(QStringList filepaths)
auto f = std::make_shared<VersionFile>();
auto jarMod = std::make_shared<Library>();
jarMod->setRawName(GradleSpecifier("custom.jarmods:" + id + ":1"));
jarMod->setRawName(GradleSpecifier("org.multimc.jarmods:" + id + ":1"));
jarMod->setFilename(target_filename);
jarMod->setDisplayName(sourceInfo.completeBaseName());
jarMod->setHint("local");
@ -892,7 +892,7 @@ bool PackProfile::installCustomJar_internal(QString filepath)
return false;
}
auto specifier = GradleSpecifier("custom:customjar:1");
auto specifier = GradleSpecifier("org.multimc:customjar:1");
QFileInfo sourceInfo(filepath);
QString target_filename = specifier.getFileName();
QString target_id = specifier.artifactId();
@ -939,64 +939,6 @@ bool PackProfile::installCustomJar_internal(QString filepath)
return true;
}
bool PackProfile::installAgents_internal(QStringList filepaths)
{
// FIXME code duplication
const QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches");
if (!FS::ensureFolderPathExists(patchDir))
return false;
const QString libDir = d->m_instance->getLocalLibraryPath();
if (!FS::ensureFolderPathExists(libDir))
return false;
for (const QString& source : filepaths) {
const QFileInfo sourceInfo(source);
const QString id = QUuid::createUuid().toString(QUuid::WithoutBraces);
const QString targetBaseName = id + ".jar";
const QString targetId = "custom.agent." + id;
const QString targetName = sourceInfo.completeBaseName() + " (agent)";
const QString target = FS::PathCombine(d->m_instance->getLocalLibraryPath(), targetBaseName);
const QFileInfo targetInfo(target);
Q_ASSERT(!targetInfo.exists());
if (!QFile::copy(source, target))
return false;
auto versionFile = std::make_shared<VersionFile>();
auto agent = std::make_shared<Library>();
agent->setRawName("custom.agents:" + id + ":1");
agent->setFilename(targetBaseName);
agent->setDisplayName(sourceInfo.completeBaseName());
agent->setHint("local");
versionFile->agents.append(std::make_shared<Agent>(agent, QString()));
versionFile->name = targetName;
versionFile->uid = targetId;
QFile patchFile(FS::PathCombine(patchDir, targetId + ".json"));
if (!patchFile.open(QFile::WriteOnly)) {
qCritical() << "Error opening" << patchFile.fileName() << "for reading:" << patchFile.errorString();
return false;
}
patchFile.write(OneSixVersionFormat::versionFileToJson(versionFile).toJson());
patchFile.close();
appendComponent(new Component(this, versionFile->uid, versionFile));
}
scheduleSave();
invalidateLaunchProfile();
return true;
}
std::shared_ptr<LaunchProfile> PackProfile::getProfile() const
{
if(!d->m_profile)

View File

@ -1,8 +1,7 @@
// 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 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
@ -86,9 +85,6 @@ public:
/// install a jar/zip as a replacement for the main jar
void installCustomJar(QString selectedFile);
/// install Java agent files
void installAgents(QStringList selectedFiles);
enum MoveDirection { MoveUp, MoveDown };
/// move component file # up or down the list
void move(const int index, const MoveDirection direction);
@ -171,7 +167,6 @@ private:
bool load();
bool installJarMods_internal(QStringList filepaths);
bool installCustomJar_internal(QString filepath);
bool installAgents_internal(QStringList filepaths);
bool removeComponent_internal(ComponentPtr patch);
private: /* data */

View File

@ -104,7 +104,7 @@ public:
class ImplicitRule : public Rule
{
protected:
virtual bool applies(const Library *, [[maybe_unused]] const RuntimeContext & runtimeContext)
virtual bool applies(const Library *, const RuntimeContext & runtimeContext)
{
return true;
}

View File

@ -173,7 +173,7 @@ bool WorldList::resetIcon(int row)
int WorldList::columnCount(const QModelIndex &parent) const
{
return parent.isValid()? 0 : 4;
return 4;
}
QVariant WorldList::data(const QModelIndex &index, int role) const
@ -398,8 +398,8 @@ void WorldList::installWorld(QFileInfo filename)
w.install(m_dir.absolutePath());
}
bool WorldList::dropMimeData(const QMimeData *data, Qt::DropAction action, [[maybe_unused]] int row, [[maybe_unused]] int column,
[[maybe_unused]] const QModelIndex &parent)
bool WorldList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
const QModelIndex &parent)
{
if (action == Qt::IgnoreAction)
return true;

View File

@ -54,7 +54,7 @@ public:
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const
{
return parent.isValid() ? 0 : static_cast<int>(size());
return size();
};
virtual QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const;

View File

@ -408,20 +408,20 @@ QVariant AccountList::headerData(int section, Qt::Orientation orientation, int r
}
}
int AccountList::rowCount(const QModelIndex &parent) const
int AccountList::rowCount(const QModelIndex &) const
{
// Return count
return parent.isValid() ? 0 : count();
return count();
}
int AccountList::columnCount(const QModelIndex &parent) const
int AccountList::columnCount(const QModelIndex &) const
{
return parent.isValid() ? 0 : NUM_COLUMNS;
return NUM_COLUMNS;
}
Qt::ItemFlags AccountList::flags(const QModelIndex &index) const
{
if (index.row() < 0 || index.row() >= rowCount(index.parent()) || !index.isValid())
if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
{
return Qt::NoItemFlags;
}

View File

@ -144,7 +144,7 @@ QVariant ModFolderModel::headerData(int section, Qt::Orientation orientation, in
int ModFolderModel::columnCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : NUM_COLUMNS;
return NUM_COLUMNS;
}
Task* ModFolderModel::createUpdateTask()

View File

@ -20,7 +20,6 @@ ResourceFolderModel::ResourceFolderModel(QDir dir, QObject* parent) : QAbstractL
m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &ResourceFolderModel::directoryChanged);
connect(&m_helper_thread_task, &ConcurrentTask::finished, this, [this]{ m_helper_thread_task.clear(); });
}
ResourceFolderModel::~ResourceFolderModel()
@ -276,11 +275,7 @@ void ResourceFolderModel::resolveResource(Resource* res)
connect(
task, &Task::finished, this, [=] { m_active_parse_tasks.remove(ticket); }, Qt::ConnectionType::QueuedConnection);
m_helper_thread_task.addTask(task);
if (!m_helper_thread_task.isRunning()) {
QThreadPool::globalInstance()->start(&m_helper_thread_task);
}
QThreadPool::globalInstance()->start(task);
}
void ResourceFolderModel::onUpdateSucceeded()
@ -431,7 +426,7 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const
bool ResourceFolderModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
int row = index.row();
if (row < 0 || row >= rowCount(index.parent()) || !index.isValid())
if (row < 0 || row >= rowCount(index) || !index.isValid())
return false;
if (role == Qt::CheckStateRole)

View File

@ -10,7 +10,6 @@
#include "Resource.h"
#include "tasks/Task.h"
#include "tasks/ConcurrentTask.h"
class QSortFilterProxyModel;
@ -91,8 +90,8 @@ class ResourceFolderModel : public QAbstractListModel {
/* Basic columns */
enum Columns { ACTIVE_COLUMN = 0, NAME_COLUMN, DATE_COLUMN, NUM_COLUMNS };
[[nodiscard]] int rowCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : static_cast<int>(size()); }
[[nodiscard]] int columnCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : NUM_COLUMNS; };
[[nodiscard]] int rowCount(const QModelIndex& = {}) const override { return size(); }
[[nodiscard]] int columnCount(const QModelIndex& = {}) const override { return NUM_COLUMNS; };
[[nodiscard]] Qt::DropActions supportedDropActions() const override;
@ -177,7 +176,7 @@ class ResourceFolderModel : public QAbstractListModel {
* if the resource is complex and has more stuff to parse.
*/
virtual void onParseSucceeded(int ticket, QString resource_id);
virtual void onParseFailed(int ticket, QString resource_id) { Q_UNUSED(ticket); Q_UNUSED(resource_id); }
virtual void onParseFailed(int ticket, QString resource_id) {}
protected:
// Represents the relationship between a column's index (represented by the list index), and it's sorting key.
@ -198,7 +197,6 @@ class ResourceFolderModel : public QAbstractListModel {
// Represents the relationship between a resource's internal ID and it's row position on the model.
QMap<QString, int> m_resources_index;
ConcurrentTask m_helper_thread_task;
QMap<int, Task::Ptr> m_active_parse_tasks;
std::atomic<int> m_next_resolution_ticket = 0;
};

View File

@ -1,11 +1,9 @@
#include "ResourcePack.h"
#include <QCoreApplication>
#include <QDebug>
#include <QMap>
#include <QRegularExpression>
#include "MTPixmapCache.h"
#include "Version.h"
#include "minecraft/mod/tasks/LocalResourcePackParseTask.h"
@ -17,7 +15,7 @@ static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = {
{ 3, { Version("1.11"), Version("1.12.2") } }, { 4, { Version("1.13"), Version("1.14.4") } },
{ 5, { Version("1.15"), Version("1.16.1") } }, { 6, { Version("1.16.2"), Version("1.16.5") } },
{ 7, { Version("1.17"), Version("1.17.1") } }, { 8, { Version("1.18"), Version("1.18.2") } },
{ 9, { Version("1.19"), Version("1.19.2") } }, { 11, { Version("1.19.3"), Version("1.19.3") } },
{ 9, { Version("1.19"), Version("1.19.2") } },
};
void ResourcePack::setPackFormat(int new_format_id)
@ -45,22 +43,16 @@ void ResourcePack::setImage(QImage new_image)
Q_ASSERT(!new_image.isNull());
if (m_pack_image_cache_key.key.isValid())
PixmapCache::instance().remove(m_pack_image_cache_key.key);
QPixmapCache::remove(m_pack_image_cache_key.key);
m_pack_image_cache_key.key = PixmapCache::instance().insert(QPixmap::fromImage(new_image));
m_pack_image_cache_key.key = QPixmapCache::insert(QPixmap::fromImage(new_image));
m_pack_image_cache_key.was_ever_used = true;
// This can happen if the pixmap is too big to fit in the cache :c
if (!m_pack_image_cache_key.key.isValid()) {
qWarning() << "Could not insert a image cache entry! Ignoring it.";
m_pack_image_cache_key.was_ever_used = false;
}
}
QPixmap ResourcePack::image(QSize size)
{
QPixmap cached_image;
if (PixmapCache::instance().find(m_pack_image_cache_key.key, &cached_image)) {
if (QPixmapCache::find(m_pack_image_cache_key.key, &cached_image)) {
if (size.isNull())
return cached_image;
return cached_image.scaled(size);
@ -122,8 +114,3 @@ bool ResourcePack::applyFilter(QRegularExpression filter) const
return Resource::applyFilter(filter);
}
bool ResourcePack::valid() const
{
return m_pack_format != 0;
}

View File

@ -42,8 +42,6 @@ class ResourcePack : public Resource {
/** Thread-safe. */
void setImage(QImage new_image);
bool valid() const override;
[[nodiscard]] auto compare(Resource const& other, SortType type) const -> std::pair<int, bool> override;
[[nodiscard]] bool applyFilter(QRegularExpression filter) const override;

View File

@ -137,7 +137,7 @@ QVariant ResourcePackFolderModel::headerData(int section, Qt::Orientation orient
int ResourcePackFolderModel::columnCount(const QModelIndex& parent) const
{
return parent.isValid() ? 0 : NUM_COLUMNS;
return NUM_COLUMNS;
}
Task* ResourcePackFolderModel::createUpdateTask()

View File

@ -62,8 +62,3 @@ QPixmap TexturePack::image(QSize size)
TexturePackUtils::process(*this);
return image(size);
}
bool TexturePack::valid() const
{
return m_description != nullptr;
}

View File

@ -48,8 +48,6 @@ class TexturePack : public Resource {
/** Thread-safe. */
void setImage(QImage new_image);
bool valid() const override;
protected:
mutable QMutex m_data_lock;

View File

@ -121,7 +121,7 @@ ModDetails ReadMCModTOML(QByteArray contents)
return {};
}
auto modsTable = tomlModsTable0->as_table();
if (!modsTable) {
if (!tomlModsTable0) {
qWarning() << "Corrupted mods.toml? [[mods]] was not a table!";
return {};
}

View File

@ -28,14 +28,14 @@
namespace ResourcePackUtils {
bool process(ResourcePack& pack, ProcessingLevel level)
bool process(ResourcePack& pack)
{
switch (pack.type()) {
case ResourceType::FOLDER:
ResourcePackUtils::processFolder(pack, level);
ResourcePackUtils::processFolder(pack);
return true;
case ResourceType::ZIPFILE:
ResourcePackUtils::processZIP(pack, level);
ResourcePackUtils::processZIP(pack);
return true;
default:
qWarning() << "Invalid type for resource pack parse task!";
@ -43,7 +43,7 @@ bool process(ResourcePack& pack, ProcessingLevel level)
}
}
void processFolder(ResourcePack& pack, ProcessingLevel level)
void processFolder(ResourcePack& pack)
{
Q_ASSERT(pack.type() == ResourceType::FOLDER);
@ -60,9 +60,6 @@ void processFolder(ResourcePack& pack, ProcessingLevel level)
mcmeta_file.close();
}
if (level == ProcessingLevel::BasicInfoOnly)
return;
QFileInfo image_file_info(FS::PathCombine(pack.fileinfo().filePath(), "pack.png"));
if (image_file_info.isFile()) {
QFile mcmeta_file(image_file_info.filePath());
@ -77,7 +74,7 @@ void processFolder(ResourcePack& pack, ProcessingLevel level)
}
}
void processZIP(ResourcePack& pack, ProcessingLevel level)
void processZIP(ResourcePack& pack)
{
Q_ASSERT(pack.type() == ResourceType::ZIPFILE);
@ -101,11 +98,6 @@ void processZIP(ResourcePack& pack, ProcessingLevel level)
file.close();
}
if (level == ProcessingLevel::BasicInfoOnly) {
zip.close();
return;
}
if (zip.setCurrentFile("pack.png")) {
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Failed to open file in zip.";
@ -146,13 +138,6 @@ void processPackPNG(ResourcePack& pack, QByteArray&& raw_data)
qWarning() << "Failed to parse pack.png.";
}
}
bool validate(QFileInfo file)
{
ResourcePack rp{ file };
return ResourcePackUtils::process(rp, ProcessingLevel::BasicInfoOnly) && rp.valid();
}
} // namespace ResourcePackUtils
LocalResourcePackParseTask::LocalResourcePackParseTask(int token, ResourcePack& rp)
@ -167,6 +152,8 @@ bool LocalResourcePackParseTask::abort()
void LocalResourcePackParseTask::executeTask()
{
Q_ASSERT(m_resource_pack.valid());
if (!ResourcePackUtils::process(m_resource_pack))
return;

View File

@ -26,19 +26,13 @@
#include "tasks/Task.h"
namespace ResourcePackUtils {
bool process(ResourcePack& pack);
enum class ProcessingLevel { Full, BasicInfoOnly };
bool process(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full);
void processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full);
void processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full);
void processZIP(ResourcePack& pack);
void processFolder(ResourcePack& pack);
void processMCMeta(ResourcePack& pack, QByteArray&& raw_data);
void processPackPNG(ResourcePack& pack, QByteArray&& raw_data);
/** Checks whether a file is valid as a resource pack or not. */
bool validate(QFileInfo file);
} // namespace ResourcePackUtils
class LocalResourcePackParseTask : public Task {

View File

@ -28,14 +28,14 @@
namespace TexturePackUtils {
bool process(TexturePack& pack, ProcessingLevel level)
bool process(TexturePack& pack)
{
switch (pack.type()) {
case ResourceType::FOLDER:
TexturePackUtils::processFolder(pack, level);
TexturePackUtils::processFolder(pack);
return true;
case ResourceType::ZIPFILE:
TexturePackUtils::processZIP(pack, level);
TexturePackUtils::processZIP(pack);
return true;
default:
qWarning() << "Invalid type for resource pack parse task!";
@ -43,7 +43,7 @@ bool process(TexturePack& pack, ProcessingLevel level)
}
}
void processFolder(TexturePack& pack, ProcessingLevel level)
void processFolder(TexturePack& pack)
{
Q_ASSERT(pack.type() == ResourceType::FOLDER);
@ -60,9 +60,6 @@ void processFolder(TexturePack& pack, ProcessingLevel level)
mcmeta_file.close();
}
if (level == ProcessingLevel::BasicInfoOnly)
return;
QFileInfo image_file_info(FS::PathCombine(pack.fileinfo().filePath(), "pack.png"));
if (image_file_info.isFile()) {
QFile mcmeta_file(image_file_info.filePath());
@ -77,7 +74,7 @@ void processFolder(TexturePack& pack, ProcessingLevel level)
}
}
void processZIP(TexturePack& pack, ProcessingLevel level)
void processZIP(TexturePack& pack)
{
Q_ASSERT(pack.type() == ResourceType::ZIPFILE);
@ -101,11 +98,6 @@ void processZIP(TexturePack& pack, ProcessingLevel level)
file.close();
}
if (level == ProcessingLevel::BasicInfoOnly) {
zip.close();
return;
}
if (zip.setCurrentFile("pack.png")) {
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Failed to open file in zip.";
@ -137,13 +129,6 @@ void processPackPNG(TexturePack& pack, QByteArray&& raw_data)
qWarning() << "Failed to parse pack.png.";
}
}
bool validate(QFileInfo file)
{
TexturePack rp{ file };
return TexturePackUtils::process(rp, ProcessingLevel::BasicInfoOnly) && rp.valid();
}
} // namespace TexturePackUtils
LocalTexturePackParseTask::LocalTexturePackParseTask(int token, TexturePack& rp)
@ -158,6 +143,8 @@ bool LocalTexturePackParseTask::abort()
void LocalTexturePackParseTask::executeTask()
{
Q_ASSERT(m_texture_pack.valid());
if (!TexturePackUtils::process(m_texture_pack))
return;

View File

@ -27,19 +27,13 @@
#include "tasks/Task.h"
namespace TexturePackUtils {
bool process(TexturePack& pack);
enum class ProcessingLevel { Full, BasicInfoOnly };
bool process(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full);
void processZIP(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full);
void processFolder(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full);
void processZIP(TexturePack& pack);
void processFolder(TexturePack& pack);
void processPackTXT(TexturePack& pack, QByteArray&& raw_data);
void processPackPNG(TexturePack& pack, QByteArray&& raw_data);
/** Checks whether a file is valid as a texture pack or not. */
bool validate(QFileInfo file);
} // namespace TexturePackUtils
class LocalTexturePackParseTask : public Task {

View File

@ -12,7 +12,7 @@ LibrariesTask::LibrariesTask(MinecraftInstance * inst)
void LibrariesTask::executeTask()
{
setStatus(tr("Downloading required library files..."));
setStatus(tr("Getting the library files from Mojang..."));
qDebug() << m_inst->name() << ": downloading libraries";
MinecraftInstance *inst = (MinecraftInstance *)m_inst;

View File

@ -39,7 +39,7 @@
#include <QList>
#include <list>
#include "../Version.h"
#include "Version.h"
#include "net/NetJob.h"
namespace ModPlatform {

View File

@ -42,25 +42,12 @@ void Flame::FileResolvingTask::executeTask()
void Flame::FileResolvingTask::netJobFinished()
{
setProgress(1, 3);
int index = 0;
// job to check modrinth for blocked projects
m_checkJob = new NetJob("Modrinth check", m_network);
blockedProjects = QMap<File *,QByteArray *>();
QJsonDocument doc;
QJsonArray array;
try {
doc = Json::requireDocument(*result);
array = Json::requireArray(doc.object()["data"]);
} catch (Json::JsonException& e) {
qCritical() << "Non-JSON data returned from the CF API";
qCritical() << e.cause();
emitFailed(tr("Invalid data returned from the API."));
return;
}
auto doc = Json::requireDocument(*result);
auto array = Json::requireArray(doc.object()["data"]);
for (QJsonValueRef file : array) {
auto fileid = Json::requireInteger(Json::requireObject(file)["id"]);
auto& out = m_toProcess.files[fileid];
@ -81,6 +68,7 @@ void Flame::FileResolvingTask::netJobFinished()
blockedProjects.insert(&out, output);
}
}
index++;
}
connect(m_checkJob.get(), &NetJob::finished, this, &Flame::FileResolvingTask::modrinthCheckFinished);

View File

@ -1,38 +1,3 @@
// 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 "FlameInstanceCreationTask.h"
#include "modplatform/flame/FlameAPI.h"
@ -81,19 +46,13 @@ bool FlameCreationTask::updateInstance()
auto instance_list = APPLICATION->instances();
// FIXME: How to handle situations when there's more than one install already for a given modpack?
InstancePtr inst;
if (auto original_id = originalInstanceID(); !original_id.isEmpty()) {
inst = instance_list->getInstanceById(original_id);
Q_ASSERT(inst);
} else {
inst = instance_list->getInstanceByManagedName(originalName());
auto inst = instance_list->getInstanceByManagedName(originalName());
if (!inst) {
inst = instance_list->getInstanceById(originalName());
if (!inst) {
inst = instance_list->getInstanceById(originalName());
if (!inst)
return false;
}
if (!inst)
return false;
}
QString index_path(FS::PathCombine(m_stagingPath, "manifest.json"));
@ -108,14 +67,24 @@ bool FlameCreationTask::updateInstance()
auto version_id = inst->getManagedPackVersionName();
auto version_str = !version_id.isEmpty() ? tr(" (version %1)").arg(version_id) : "";
if (shouldConfirmUpdate()) {
auto should_update = askIfShouldUpdate(m_parent, version_str);
if (should_update == ShouldUpdate::SkipUpdating)
return false;
if (should_update == ShouldUpdate::Cancel) {
m_abort = true;
return false;
}
auto info = CustomMessageBox::selectable(
m_parent, tr("Similar modpack was found!"),
tr("One or more of your instances are from this same modpack%1. Do you want to create a "
"separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before "
"updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).")
.arg(version_str), QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort);
info->setButtonText(QMessageBox::Ok, tr("Update existing instance"));
info->setButtonText(QMessageBox::Abort, tr("Create new instance"));
info->setButtonText(QMessageBox::Reset, tr("Cancel"));
info->exec();
if (info->clickedButton() == info->button(QMessageBox::Abort))
return false;
if (info->clickedButton() == info->button(QMessageBox::Reset)) {
m_abort = true;
return false;
}
QDir old_inst_dir(inst->instanceRoot());
@ -228,10 +197,10 @@ bool FlameCreationTask::updateInstance()
m_process_update_file_info_job = nullptr;
} else {
// We don't have an old index file, so we may duplicate stuff!
auto dialog = CustomMessageBox::selectable(m_parent, tr("No index file."),
tr("We couldn't find a suitable index file for the older version. This may cause some "
"of the files to be duplicated. Do you want to continue?"),
QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Cancel);
auto dialog = CustomMessageBox::selectable(m_parent,
tr("No index file."),
tr("We couldn't find a suitable index file for the older version. This may cause some of the files to be duplicated. Do you want to continue?"),
QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Cancel);
if (dialog->exec() == QDialog::DialogCode::Rejected) {
m_abort = true;
@ -239,7 +208,7 @@ bool FlameCreationTask::updateInstance()
}
}
setOverride(true, inst->id());
setOverride(true);
qDebug() << "Will override instance!";
m_instance = inst;
@ -361,9 +330,7 @@ bool FlameCreationTask::createInstance()
FS::deletePath(jarmodsPath);
}
// Don't add managed info to packs without an ID (most likely imported from ZIP)
if (!m_managed_id.isEmpty())
instance.setManagedPack("flame", m_managed_id, m_pack.name, m_managed_version_id, m_pack.version);
instance.setManagedPack("flame", {}, m_pack.name, {}, m_pack.version);
instance.setName(name());
m_mod_id_resolver = new Flame::FileResolvingTask(APPLICATION->network(), m_pack);
@ -371,7 +338,6 @@ bool FlameCreationTask::createInstance()
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason) {
m_mod_id_resolver.reset();
setError(tr("Unable to resolve mod IDs:\n") + reason);
loop.quit();
});
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::progress, this, &FlameCreationTask::setProgress);
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::status, this, &FlameCreationTask::setStatus);
@ -387,6 +353,14 @@ bool FlameCreationTask::createInstance()
setAbortable(false);
auto inst = m_instance.value();
// Only change the name if it didn't use a custom name, so that the previous custom name
// is preserved, but if we're using the original one, we update the version string.
// NOTE: This needs to come before the copyManagedPack call!
if (inst->name().contains(inst->getManagedPackVersionName())) {
if (askForChangingInstanceName(m_parent, inst->name(), instance.name()) == InstanceNameChange::ShouldChange)
inst->setName(instance.name());
}
inst->copyManagedPack(instance);
}
@ -398,36 +372,27 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop)
auto results = m_mod_id_resolver->getResults();
// first check for blocked mods
QList<BlockedMod> blocked_mods;
QString text;
QList<QUrl> urls;
auto anyBlocked = false;
for (const auto& result : results.files.values()) {
if (!result.resolved || result.url.isEmpty()) {
BlockedMod blocked_mod;
blocked_mod.name = result.fileName;
blocked_mod.websiteUrl = result.websiteUrl;
blocked_mod.hash = result.hash;
blocked_mod.matched = false;
blocked_mod.localPath = "";
blocked_mod.targetFolder = result.targetFolder;
blocked_mods.append(blocked_mod);
text += QString("%1: <a href='%2'>%2</a><br/>").arg(result.fileName, result.websiteUrl);
urls.append(QUrl(result.websiteUrl));
anyBlocked = true;
}
}
if (anyBlocked) {
qWarning() << "Blocked mods found, displaying mod list";
BlockedModsDialog message_dialog(m_parent, tr("Blocked mods found"),
tr("The following files are not available for download in third party launchers.<br/>"
"You will need to manually download them and add them to the instance."),
blocked_mods);
auto message_dialog = new BlockedModsDialog(m_parent, tr("Blocked mods found"),
tr("The following mods were blocked on third party launchers.<br/>"
"You will need to manually download them and add them to the modpack"),
text,
urls);
message_dialog->setModal(true);
message_dialog.setModal(true);
if (message_dialog.exec()) {
qDebug() << "Post dialog blocked mods list: " << blocked_mods;
copyBlockedMods(blocked_mods);
if (message_dialog->exec()) {
setupDownloadJob(loop);
} else {
m_mod_id_resolver.reset();
@ -439,38 +404,6 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop)
}
}
/// @brief copy the matched blocked mods to the instance staging area
/// @param blocked_mods list of the blocked mods and their matched paths
void FlameCreationTask::copyBlockedMods(QList<BlockedMod> const& blocked_mods)
{
setStatus(tr("Copying Blocked Mods..."));
setAbortable(false);
int i = 0;
int total = blocked_mods.length();
setProgress(i, total);
for (auto const& mod : blocked_mods) {
if (!mod.matched) {
qDebug() << mod.name << "was not matched to a local file, skipping copy";
continue;
}
auto dest_path = FS::PathCombine(m_stagingPath, "minecraft", mod.targetFolder, mod.name);
setStatus(tr("Copying Blocked Mods (%1 out of %2 are done)").arg(QString::number(i), QString::number(total)));
qDebug() << "Will try to copy" << mod.localPath << "to" << dest_path;
if (!FS::copy(mod.localPath, dest_path)()) {
qDebug() << "Copy of" << mod.localPath << "to" << dest_path << "Failed";
}
i++;
setProgress(i, total);
}
setAbortable(true);
}
void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
{
m_files_job = new NetJob(tr("Mod download"), APPLICATION->network());
@ -509,12 +442,14 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
}
m_mod_id_resolver.reset();
connect(m_files_job.get(), &NetJob::succeeded, this, [&]() { m_files_job.reset(); });
connect(m_files_job.get(), &NetJob::succeeded, this, [&]() {
m_files_job.reset();
});
connect(m_files_job.get(), &NetJob::failed, [&](QString reason) {
m_files_job.reset();
setError(reason);
});
connect(m_files_job.get(), &NetJob::progress, this, &FlameCreationTask::setProgress);
connect(m_files_job.get(), &NetJob::progress, [&](qint64 current, qint64 total) { setProgress(current, total); });
connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit);
setStatus(tr("Downloading mods..."));

View File

@ -1,38 +1,3 @@
// 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.
*/
#pragma once
#include "InstanceCreationTask.h"
@ -45,27 +10,15 @@
#include "net/NetJob.h"
#include "ui/dialogs/BlockedModsDialog.h"
class FlameCreationTask final : public InstanceCreationTask {
Q_OBJECT
public:
FlameCreationTask(const QString& staging_path,
SettingsObjectPtr global_settings,
QWidget* parent,
QString id,
QString version_id,
QString original_instance_id = {})
: InstanceCreationTask()
, m_parent(parent)
, m_managed_id(std::move(id))
, m_managed_version_id(std::move(version_id))
FlameCreationTask(const QString& staging_path, SettingsObjectPtr global_settings, QWidget* parent)
: InstanceCreationTask(), m_parent(parent)
{
setStagingPath(staging_path);
setParentSettings(global_settings);
m_original_instance_id = std::move(original_instance_id);
}
bool abort() override;
@ -76,7 +29,6 @@ class FlameCreationTask final : public InstanceCreationTask {
private slots:
void idResolverSucceeded(QEventLoop&);
void setupDownloadJob(QEventLoop&);
void copyBlockedMods(QList<BlockedMod> const& blocked_mods);
private:
QWidget* m_parent = nullptr;
@ -88,7 +40,5 @@ class FlameCreationTask final : public InstanceCreationTask {
NetJob* m_process_update_file_info_job = nullptr;
NetJob::Ptr m_files_job = nullptr;
QString m_managed_id, m_managed_version_id;
std::optional<InstancePtr> m_instance;
};

View File

@ -36,18 +36,6 @@ Hasher::Ptr createFlameHasher(QString file_path)
return new FlameHasher(file_path);
}
Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::Provider provider)
{
return new BlockedModHasher(file_path, provider);
}
Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::Provider provider, QString type)
{
auto hasher = new BlockedModHasher(file_path, provider);
hasher->useHashType(type);
return hasher;
}
void ModrinthHasher::executeTask()
{
QFile file(m_path);
@ -91,50 +79,4 @@ void FlameHasher::executeTask()
}
}
BlockedModHasher::BlockedModHasher(QString file_path, ModPlatform::Provider provider)
: Hasher(file_path), provider(provider) {
setObjectName(QString("BlockedModHasher: %1").arg(file_path));
hash_type = ProviderCaps.hashType(provider).first();
}
void BlockedModHasher::executeTask()
{
QFile file(m_path);
try {
file.open(QFile::ReadOnly);
} catch (FS::FileSystemException& e) {
qCritical() << QString("Failed to open JAR file in %1").arg(m_path);
qCritical() << QString("Reason: ") << e.cause();
emitFailed("Failed to open file for hashing.");
return;
}
m_hash = ProviderCaps.hash(provider, &file, hash_type);
file.close();
if (m_hash.isEmpty()) {
emitFailed("Empty hash!");
} else {
emitSucceeded();
}
}
QStringList BlockedModHasher::getHashTypes() {
return ProviderCaps.hashType(provider);
}
bool BlockedModHasher::useHashType(QString type) {
auto types = ProviderCaps.hashType(provider);
if (types.contains(type)) {
hash_type = type;
return true;
}
qDebug() << "Bad hash type " << type << " for provider";
return false;
}
} // namespace Hashing

View File

@ -40,23 +40,8 @@ class ModrinthHasher : public Hasher {
void executeTask() override;
};
class BlockedModHasher : public Hasher {
public:
BlockedModHasher(QString file_path, ModPlatform::Provider provider);
void executeTask() override;
QStringList getHashTypes();
bool useHashType(QString type);
private:
ModPlatform::Provider provider;
QString hash_type;
};
Hasher::Ptr createHasher(QString file_path, ModPlatform::Provider provider);
Hasher::Ptr createFlameHasher(QString file_path);
Hasher::Ptr createModrinthHasher(QString file_path);
Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::Provider provider);
Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::Provider provider, QString type);
} // namespace Hashing

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 flowln <flowlnlnln@gmail.com>
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
@ -176,6 +176,8 @@ void PackInstallTask::resolveMods()
void PackInstallTask::onResolveModsSucceeded()
{
QString text;
QList<QUrl> urls;
auto anyBlocked = false;
Flame::Manifest results = m_mod_id_resolver_task->getResults();
@ -189,16 +191,11 @@ void PackInstallTask::onResolveModsSucceeded()
// First check for blocked mods
if (!results_file.resolved || results_file.url.isEmpty()) {
BlockedMod blocked_mod;
blocked_mod.name = local_file.name;
blocked_mod.websiteUrl = results_file.websiteUrl;
blocked_mod.hash = results_file.hash;
blocked_mod.matched = false;
blocked_mod.localPath = "";
blocked_mod.targetFolder = results_file.targetFolder;
m_blocked_mods.append(blocked_mod);
QString type(local_file.type);
type[0] = type[0].toUpper();
text += QString("%1: %2 - <a href='%3'>%3</a><br/>").arg(type, local_file.name, results_file.websiteUrl);
urls.append(QUrl(results_file.websiteUrl));
anyBlocked = true;
} else {
local_file.url = results_file.url.toString();
@ -210,20 +207,16 @@ void PackInstallTask::onResolveModsSucceeded()
if (anyBlocked) {
qDebug() << "Blocked files found, displaying file list";
BlockedModsDialog message_dialog(m_parent, tr("Blocked files found"),
tr("The following files are not available for download in third party launchers.<br/>"
"You will need to manually download them and add them to the instance."),
m_blocked_mods);
auto message_dialog = new BlockedModsDialog(m_parent, tr("Blocked files found"),
tr("The following files are not available for download in third party launchers.<br/>"
"You will need to manually download them and add them to the instance."),
text,
urls);
message_dialog.setModal(true);
if (message_dialog.exec() == QDialog::Accepted) {
qDebug() << "Post dialog blocked mods list: " << m_blocked_mods;
if (message_dialog->exec() == QDialog::Accepted)
createInstance();
} else {
else
abort();
}
} else {
createInstance();
}
@ -327,9 +320,6 @@ void PackInstallTask::downloadPack()
void PackInstallTask::onModDownloadSucceeded()
{
m_net_job.reset();
if (!m_blocked_mods.isEmpty()) {
copyBlockedMods();
}
emitSucceeded();
}
@ -353,35 +343,4 @@ void PackInstallTask::onModDownloadFailed(QString reason)
emitFailed(reason);
}
/// @brief copy the matched blocked mods to the instance staging area
void PackInstallTask::copyBlockedMods()
{
setStatus(tr("Copying Blocked Mods..."));
setAbortable(false);
int i = 0;
int total = m_blocked_mods.length();
setProgress(i, total);
for (auto const& mod : m_blocked_mods) {
if (!mod.matched) {
qDebug() << mod.name << "was not matched to a local file, skipping copy";
continue;
}
auto dest_path = FS::PathCombine(m_stagingPath, ".minecraft", mod.targetFolder, mod.name);
setStatus(tr("Copying Blocked Mods (%1 out of %2 are done)").arg(QString::number(i), QString::number(total)));
qDebug() << "Will try to copy" << mod.localPath << "to" << dest_path;
if (!FS::copy(mod.localPath, dest_path)()) {
qDebug() << "Copy of" << mod.localPath << "to" << dest_path << "Failed";
}
i++;
setProgress(i, total);
}
setAbortable(true);
}
} // namespace ModpacksCH

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 flowln <flowlnlnln@gmail.com>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
@ -43,7 +43,6 @@
#include "QObjectPtr.h"
#include "modplatform/flame/FileResolvingTask.h"
#include "net/NetJob.h"
#include "ui/dialogs/BlockedModsDialog.h"
#include <QWidget>
@ -77,7 +76,6 @@ private:
void resolveMods();
void createInstance();
void downloadPack();
void copyBlockedMods();
private:
NetJob::Ptr m_net_job = nullptr;
@ -92,7 +90,6 @@ private:
Version m_version;
QMap<QString, QString> m_files_to_copy;
QList<BlockedMod> m_blocked_mods;
//FIXME: nuke
QWidget* m_parent;

View File

@ -33,19 +33,13 @@ bool ModrinthCreationTask::updateInstance()
auto instance_list = APPLICATION->instances();
// FIXME: How to handle situations when there's more than one install already for a given modpack?
InstancePtr inst;
if (auto original_id = originalInstanceID(); !original_id.isEmpty()) {
inst = instance_list->getInstanceById(original_id);
Q_ASSERT(inst);
} else {
inst = instance_list->getInstanceByManagedName(originalName());
auto inst = instance_list->getInstanceByManagedName(originalName());
if (!inst) {
inst = instance_list->getInstanceById(originalName());
if (!inst) {
inst = instance_list->getInstanceById(originalName());
if (!inst)
return false;
}
if (!inst)
return false;
}
QString index_path = FS::PathCombine(m_stagingPath, "modrinth.index.json");
@ -55,14 +49,25 @@ bool ModrinthCreationTask::updateInstance()
auto version_name = inst->getManagedPackVersionName();
auto version_str = !version_name.isEmpty() ? tr(" (version %1)").arg(version_name) : "";
if (shouldConfirmUpdate()) {
auto should_update = askIfShouldUpdate(m_parent, version_str);
if (should_update == ShouldUpdate::SkipUpdating)
return false;
if (should_update == ShouldUpdate::Cancel) {
m_abort = true;
return false;
}
auto info = CustomMessageBox::selectable(
m_parent, tr("Similar modpack was found!"),
tr("One or more of your instances are from this same modpack%1. Do you want to create a "
"separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before "
"updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).")
.arg(version_str),
QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort);
info->setButtonText(QMessageBox::Ok, tr("Create new instance"));
info->setButtonText(QMessageBox::Abort, tr("Update existing instance"));
info->setButtonText(QMessageBox::Reset, tr("Cancel"));
info->exec();
if (info->clickedButton() == info->button(QMessageBox::Ok))
return false;
if (info->clickedButton() == info->button(QMessageBox::Reset)) {
m_abort = true;
return false;
}
// Remove repeated files, we don't need to download them!
@ -144,7 +149,7 @@ bool ModrinthCreationTask::updateInstance()
}
setOverride(true, inst->id());
setOverride(true);
qDebug() << "Will override instance!";
m_instance = inst;
@ -202,14 +207,14 @@ bool ModrinthCreationTask::createInstance()
auto components = instance.getPackProfile();
components->buildingFromScratch();
components->setComponentVersion("net.minecraft", m_minecraft_version, true);
components->setComponentVersion("net.minecraft", minecraftVersion, true);
if (!m_fabric_version.isEmpty())
components->setComponentVersion("net.fabricmc.fabric-loader", m_fabric_version);
if (!m_quilt_version.isEmpty())
components->setComponentVersion("org.quiltmc.quilt-loader", m_quilt_version);
if (!m_forge_version.isEmpty())
components->setComponentVersion("net.minecraftforge", m_forge_version);
if (!fabricVersion.isEmpty())
components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion);
if (!quiltVersion.isEmpty())
components->setComponentVersion("org.quiltmc.quilt-loader", quiltVersion);
if (!forgeVersion.isEmpty())
components->setComponentVersion("net.minecraftforge", forgeVersion);
if (m_instIcon != "default") {
instance.setIconKey(m_instIcon);
@ -217,9 +222,7 @@ bool ModrinthCreationTask::createInstance()
instance.setIconKey("modrinth");
}
// Don't add managed info to packs without an ID (most likely imported from ZIP)
if (!m_managed_id.isEmpty())
instance.setManagedPack("modrinth", m_managed_id, m_managed_name, m_managed_version_id, version());
instance.setManagedPack("modrinth", getManagedPackID(), m_managed_name, m_managed_version_id, version());
instance.setName(name());
instance.saveNow();
@ -279,7 +282,7 @@ bool ModrinthCreationTask::createInstance()
return ended_well;
}
bool ModrinthCreationTask::parseManifest(const QString& index_path, std::vector<Modrinth::File>& files, bool set_internal_data, bool show_optional_dialog)
bool ModrinthCreationTask::parseManifest(const QString& index_path, std::vector<Modrinth::File>& files, bool set_managed_info, bool show_optional_dialog)
{
try {
auto doc = Json::requireDocument(index_path);
@ -291,9 +294,8 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, std::vector<
throw JSONValidationError("Unknown game: " + game);
}
if (set_internal_data) {
if (m_managed_version_id.isEmpty())
m_managed_version_id = Json::ensureString(obj, "versionId", {}, "Managed ID");
if (set_managed_info) {
m_managed_version_id = Json::ensureString(obj, "versionId", {}, "Managed ID");
m_managed_name = Json::ensureString(obj, "name", {}, "Managed Name");
}
@ -367,21 +369,19 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, std::vector<
files.push_back(file);
}
if (set_internal_data) {
auto dependencies = Json::requireObject(obj, "dependencies", "modrinth.index.json");
for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) {
QString name = it.key();
if (name == "minecraft") {
m_minecraft_version = Json::requireString(*it, "Minecraft version");
} else if (name == "fabric-loader") {
m_fabric_version = Json::requireString(*it, "Fabric Loader version");
} else if (name == "quilt-loader") {
m_quilt_version = Json::requireString(*it, "Quilt Loader version");
} else if (name == "forge") {
m_forge_version = Json::requireString(*it, "Forge version");
} else {
throw JSONValidationError("Unknown dependency type: " + name);
}
auto dependencies = Json::requireObject(obj, "dependencies", "modrinth.index.json");
for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) {
QString name = it.key();
if (name == "minecraft") {
minecraftVersion = Json::requireString(*it, "Minecraft version");
} else if (name == "fabric-loader") {
fabricVersion = Json::requireString(*it, "Fabric Loader version");
} else if (name == "quilt-loader") {
quiltVersion = Json::requireString(*it, "Quilt Loader version");
} else if (name == "forge") {
forgeVersion = Json::requireString(*it, "Forge version");
} else {
throw JSONValidationError("Unknown dependency type: " + name);
}
}
} else {
@ -395,3 +395,13 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, std::vector<
return true;
}
QString ModrinthCreationTask::getManagedPackID() const
{
if (!m_source_url.isEmpty()) {
QRegularExpression regex(R"(data\/(.*)\/versions)");
return regex.match(m_source_url).captured(1);
}
return {};
}

View File

@ -14,21 +14,11 @@ class ModrinthCreationTask final : public InstanceCreationTask {
Q_OBJECT
public:
ModrinthCreationTask(QString staging_path,
SettingsObjectPtr global_settings,
QWidget* parent,
QString id,
QString version_id = {},
QString original_instance_id = {})
: InstanceCreationTask()
, m_parent(parent)
, m_managed_id(std::move(id))
, m_managed_version_id(std::move(version_id))
ModrinthCreationTask(QString staging_path, SettingsObjectPtr global_settings, QWidget* parent, QString source_url = {})
: InstanceCreationTask(), m_parent(parent), m_source_url(std::move(source_url))
{
setStagingPath(staging_path);
setParentSettings(global_settings);
m_original_instance_id = std::move(original_instance_id);
}
bool abort() override;
@ -37,13 +27,15 @@ class ModrinthCreationTask final : public InstanceCreationTask {
bool createInstance() override;
private:
bool parseManifest(const QString&, std::vector<Modrinth::File>&, bool set_internal_data = true, bool show_optional_dialog = true);
bool parseManifest(const QString&, std::vector<Modrinth::File>&, bool set_managed_info = true, bool show_optional_dialog = true);
QString getManagedPackID() const;
private:
QWidget* m_parent = nullptr;
QString m_minecraft_version, m_fabric_version, m_quilt_version, m_forge_version;
QString minecraftVersion, fabricVersion, quiltVersion, forgeVersion;
QString m_managed_id, m_managed_version_id, m_managed_name;
QString m_source_url;
std::vector<Modrinth::File> m_files;
NetJob::Ptr m_files_job;

View File

@ -128,7 +128,6 @@ auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion
file.name = Json::requireString(obj, "name");
file.version = Json::requireString(obj, "version_number");
file.changelog = Json::ensureString(obj, "changelog");
file.id = Json::requireString(obj, "id");
file.project_id = Json::requireString(obj, "project_id");
@ -141,7 +140,7 @@ auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion
for (auto file_iter : files) {
File indexed_file;
auto parent = Json::requireObject(file_iter);
auto is_primary = Json::ensureBoolean(parent, (const QString)QStringLiteral("primary"), false);
auto is_primary = Json::ensureBoolean(parent, "primary", false);
if (!is_primary) {
auto filename = Json::ensureString(parent, "filename");
// Checking suffix here is fine because it's the response from Modrinth,

View File

@ -80,7 +80,6 @@ struct ModpackExtra {
struct ModpackVersion {
QString name;
QString version;
QString changelog;
QString id;
QString project_id;

View File

@ -47,7 +47,7 @@
auto MetaEntry::getFullPath() -> QString
{
// FIXME: make local?
return FS::PathCombine(m_basePath, m_relativePath);
return FS::PathCombine(basePath, relativePath);
}
HttpMetaCache::HttpMetaCache(QString path) : QObject(), m_index_file(path)
@ -99,7 +99,7 @@ auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString ex
return staleEntry(base, resource_path);
}
if (!expected_etag.isEmpty() && expected_etag != entry->m_etag) {
if (!expected_etag.isEmpty() && expected_etag != entry->etag) {
// if the etag doesn't match expected, we disown the entry
selected_base.entry_list.remove(resource_path);
return staleEntry(base, resource_path);
@ -107,17 +107,17 @@ auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString ex
// if the file changed, check md5sum
qint64 file_last_changed = finfo.lastModified().toUTC().toMSecsSinceEpoch();
if (file_last_changed != entry->m_local_changed_timestamp) {
if (file_last_changed != entry->local_changed_timestamp) {
QFile input(real_path);
input.open(QIODevice::ReadOnly);
QString md5sum = QCryptographicHash::hash(input.readAll(), QCryptographicHash::Md5).toHex().constData();
if (entry->m_md5sum != md5sum) {
if (entry->md5sum != md5sum) {
selected_base.entry_list.remove(resource_path);
return staleEntry(base, resource_path);
}
// md5sums matched... keep entry and save the new state to file
entry->m_local_changed_timestamp = file_last_changed;
entry->local_changed_timestamp = file_last_changed;
SaveEventually();
}
@ -130,23 +130,23 @@ auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString ex
}
// entry passed all the checks we cared about.
entry->m_basePath = getBasePath(base);
entry->basePath = getBasePath(base);
return entry;
}
auto HttpMetaCache::updateEntry(MetaEntryPtr stale_entry) -> bool
{
if (!m_entries.contains(stale_entry->m_baseId)) {
qCritical() << "Cannot add entry with unknown base: " << stale_entry->m_baseId.toLocal8Bit();
if (!m_entries.contains(stale_entry->baseId)) {
qCritical() << "Cannot add entry with unknown base: " << stale_entry->baseId.toLocal8Bit();
return false;
}
if (stale_entry->m_stale) {
if (stale_entry->stale) {
qCritical() << "Cannot add stale entry: " << stale_entry->getFullPath().toLocal8Bit();
return false;
}
m_entries[stale_entry->m_baseId].entry_list[stale_entry->m_relativePath] = stale_entry;
m_entries[stale_entry->baseId].entry_list[stale_entry->relativePath] = stale_entry;
SaveEventually();
return true;
@ -157,7 +157,7 @@ auto HttpMetaCache::evictEntry(MetaEntryPtr entry) -> bool
if (!entry)
return false;
entry->m_stale = true;
entry->stale = true;
SaveEventually();
return true;
}
@ -169,7 +169,7 @@ void HttpMetaCache::evictAll()
qDebug() << "Evicting base" << base;
for (MetaEntryPtr entry : map.entry_list) {
if (!evictEntry(entry))
qWarning() << "Unexpected missing cache entry" << entry->m_basePath;
qWarning() << "Unexpected missing cache entry" << entry->basePath;
}
}
}
@ -177,10 +177,10 @@ void HttpMetaCache::evictAll()
auto HttpMetaCache::staleEntry(QString base, QString resource_path) -> MetaEntryPtr
{
auto foo = new MetaEntry();
foo->m_baseId = base;
foo->m_basePath = getBasePath(base);
foo->m_relativePath = resource_path;
foo->m_stale = true;
foo->baseId = base;
foo->basePath = getBasePath(base);
foo->relativePath = resource_path;
foo->stale = true;
return MetaEntryPtr(foo);
}
@ -235,23 +235,23 @@ void HttpMetaCache::Load()
auto& entrymap = m_entries[base];
auto foo = new MetaEntry();
foo->m_baseId = base;
foo->m_relativePath = Json::ensureString(element_obj, "path");
foo->m_md5sum = Json::ensureString(element_obj, "md5sum");
foo->m_etag = Json::ensureString(element_obj, "etag");
foo->m_local_changed_timestamp = Json::ensureDouble(element_obj, "last_changed_timestamp");
foo->m_remote_changed_timestamp = Json::ensureString(element_obj, "remote_changed_timestamp");
foo->baseId = base;
foo->relativePath = Json::ensureString(element_obj, "path");
foo->md5sum = Json::ensureString(element_obj, "md5sum");
foo->etag = Json::ensureString(element_obj, "etag");
foo->local_changed_timestamp = Json::ensureDouble(element_obj, "last_changed_timestamp");
foo->remote_changed_timestamp = Json::ensureString(element_obj, "remote_changed_timestamp");
foo->makeEternal(Json::ensureBoolean(element_obj, (const QString)QStringLiteral("eternal"), false));
foo->makeEternal(Json::ensureBoolean(element_obj, "eternal", false));
if (!foo->isEternal()) {
foo->m_current_age = Json::ensureDouble(element_obj, "current_age");
foo->m_max_age = Json::ensureDouble(element_obj, "max_age");
foo->current_age = Json::ensureDouble(element_obj, "current_age");
foo->max_age = Json::ensureDouble(element_obj, "max_age");
}
// presumed innocent until closer examination
foo->m_stale = false;
foo->stale = false;
entrymap.entry_list[foo->m_relativePath] = MetaEntryPtr(foo);
entrymap.entry_list[foo->relativePath] = MetaEntryPtr(foo);
}
}
@ -276,23 +276,23 @@ void HttpMetaCache::SaveNow()
for (auto group : m_entries) {
for (auto entry : group.entry_list) {
// do not save stale entries. they are dead.
if (entry->m_stale) {
if (entry->stale) {
continue;
}
QJsonObject entryObj;
Json::writeString(entryObj, "base", entry->m_baseId);
Json::writeString(entryObj, "path", entry->m_relativePath);
Json::writeString(entryObj, "md5sum", entry->m_md5sum);
Json::writeString(entryObj, "etag", entry->m_etag);
entryObj.insert("last_changed_timestamp", QJsonValue(double(entry->m_local_changed_timestamp)));
if (!entry->m_remote_changed_timestamp.isEmpty())
entryObj.insert("remote_changed_timestamp", QJsonValue(entry->m_remote_changed_timestamp));
Json::writeString(entryObj, "base", entry->baseId);
Json::writeString(entryObj, "path", entry->relativePath);
Json::writeString(entryObj, "md5sum", entry->md5sum);
Json::writeString(entryObj, "etag", entry->etag);
entryObj.insert("last_changed_timestamp", QJsonValue(double(entry->local_changed_timestamp)));
if (!entry->remote_changed_timestamp.isEmpty())
entryObj.insert("remote_changed_timestamp", QJsonValue(entry->remote_changed_timestamp));
if (entry->isEternal()) {
entryObj.insert("eternal", true);
} else {
entryObj.insert("current_age", QJsonValue(double(entry->m_current_age)));
entryObj.insert("max_age", QJsonValue(double(entry->m_max_age)));
entryObj.insert("current_age", QJsonValue(double(entry->current_age)));
entryObj.insert("max_age", QJsonValue(double(entry->max_age)));
}
entriesArr.append(entryObj);
}

View File

@ -49,47 +49,47 @@ class MetaEntry {
MetaEntry() = default;
public:
auto isStale() -> bool { return m_stale; }
void setStale(bool stale) { m_stale = stale; }
auto isStale() -> bool { return stale; }
void setStale(bool stale) { this->stale = stale; }
auto getFullPath() -> QString;
auto getRemoteChangedTimestamp() -> QString { return m_remote_changed_timestamp; }
void setRemoteChangedTimestamp(QString remote_changed_timestamp) { m_remote_changed_timestamp = remote_changed_timestamp; }
void setLocalChangedTimestamp(qint64 timestamp) { m_local_changed_timestamp = timestamp; }
auto getRemoteChangedTimestamp() -> QString { return remote_changed_timestamp; }
void setRemoteChangedTimestamp(QString remote_changed_timestamp) { this->remote_changed_timestamp = remote_changed_timestamp; }
void setLocalChangedTimestamp(qint64 timestamp) { local_changed_timestamp = timestamp; }
auto getETag() -> QString { return m_etag; }
void setETag(QString etag) { m_etag = etag; }
auto getETag() -> QString { return etag; }
void setETag(QString etag) { this->etag = etag; }
auto getMD5Sum() -> QString { return m_md5sum; }
void setMD5Sum(QString md5sum) { m_md5sum = md5sum; }
auto getMD5Sum() -> QString { return md5sum; }
void setMD5Sum(QString md5sum) { this->md5sum = md5sum; }
/* Whether the entry expires after some time (false) or not (true). */
void makeEternal(bool eternal) { m_is_eternal = eternal; }
[[nodiscard]] bool isEternal() const { return m_is_eternal; }
void makeEternal(bool eternal) { is_eternal = eternal; }
[[nodiscard]] bool isEternal() const { return is_eternal; }
auto getCurrentAge() -> qint64 { return m_current_age; }
void setCurrentAge(qint64 age) { m_current_age = age; }
auto getCurrentAge() -> qint64 { return current_age; }
void setCurrentAge(qint64 age) { current_age = age; }
auto getMaximumAge() -> qint64 { return m_max_age; }
void setMaximumAge(qint64 age) { m_max_age = age; }
auto getMaximumAge() -> qint64 { return max_age; }
void setMaximumAge(qint64 age) { max_age = age; }
bool isExpired(qint64 offset) { return !m_is_eternal && (m_current_age >= m_max_age - offset); };
bool isExpired(qint64 offset) { return !is_eternal && (current_age >= max_age - offset); };
protected:
QString m_baseId;
QString m_basePath;
QString m_relativePath;
QString m_md5sum;
QString m_etag;
QString baseId;
QString basePath;
QString relativePath;
QString md5sum;
QString etag;
qint64 m_local_changed_timestamp = 0;
QString m_remote_changed_timestamp; // QString for now, RFC 2822 encoded time
qint64 m_current_age = 0;
qint64 m_max_age = 0;
bool m_is_eternal = false;
qint64 local_changed_timestamp = 0;
QString remote_changed_timestamp; // QString for now, RFC 2822 encoded time
qint64 current_age = 0;
qint64 max_age = 0;
bool is_eternal = false;
bool m_stale = true;
bool stale = true;
};
using MetaEntryPtr = std::shared_ptr<MetaEntry>;

View File

@ -123,7 +123,7 @@ auto NetJob::getFailedFiles() -> QList<QString>
void NetJob::updateState()
{
emit progress(m_done.count(), totalSize());
emit progress(m_done.count(), m_total_size);
setStatus(tr("Executing %1 task(s) (%2 out of %3 are done)")
.arg(QString::number(m_doing.count()), QString::number(m_done.count()), QString::number(totalSize())));
.arg(QString::number(m_doing.count()), QString::number(m_done.count()), QString::number(m_total_size)));
}

View File

@ -1,25 +0,0 @@
// SPDX-FileCopyrightText: 2022 Sefa Eyeoglu <contact@scrumplex.net>
//
// SPDX-License-Identifier: GPL-3.0-only
#include <QRegularExpression>
#include "IPathMatcher.h"
class SimplePrefixMatcher : public IPathMatcher {
public:
virtual ~SimplePrefixMatcher(){};
SimplePrefixMatcher(const QString& prefix)
{
m_prefix = prefix;
m_isPrefix = prefix.endsWith('/');
}
virtual bool matches(const QString& string) const override
{
if (m_isPrefix)
return string.startsWith(m_prefix);
return string == m_prefix;
}
QString m_prefix;
bool m_isPrefix = false;
};

View File

@ -34,11 +34,5 @@
<file>scalable/status-yellow.svg</file>
<file>scalable/viewfolder.svg</file>
<file>scalable/worlds.svg</file>
<file>scalable/delete.svg</file>
<file>scalable/tag.svg</file>
<file>scalable/export.svg</file>
<file>scalable/rename.svg</file>
<file>scalable/launch.svg</file>
<file>scalable/shortcut.svg</file>
</qresource>
</RCC>

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