Compare commits

..

21 Commits

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

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

View File

@ -27,14 +27,7 @@ body:
attributes: attributes:
label: Version of PolyMC label: Version of PolyMC
description: The version of PolyMC used in the bug report. description: The version of PolyMC used in the bug report.
placeholder: PolyMC 1.4.1 placeholder: PolyMC 1.2.2
validations:
required: true
- type: textarea
attributes:
label: Version of Qt
description: The version of Qt used in the bug report. You can find it in Help -> About PolyMC -> About Qt.
placeholder: Qt 6.3.0
validations: validations:
required: true required: true
- type: textarea - type: textarea

View File

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

2
.github/dco.yml vendored
View File

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

19
.github/workflows/backport.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: Backport PR to stable
on:
pull_request:
branches: [ develop ]
types: [ closed ]
jobs:
release_pull_request:
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'backport')
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Backport PR by cherry-pick-ing
uses: Nathanmalnoury/gh-backport-action@master
with:
pr_branch: 'stable'
github_token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -7,10 +7,6 @@ on:
description: Type of build (Debug, Release, RelWithDebInfo, MinSizeRel) description: Type of build (Debug, Release, RelWithDebInfo, MinSizeRel)
type: string type: string
default: Debug default: Debug
secrets:
SPARKLE_ED25519_KEY:
description: Private key for signing Sparkle updates
required: false
jobs: jobs:
build: build:
@ -20,39 +16,20 @@ jobs:
include: include:
- os: ubuntu-20.04 - os: ubuntu-20.04
qt_ver: 5
- os: ubuntu-20.04 - os: ubuntu-20.04
qt_ver: 6 appimage: true
qt_host: linux
qt_version: '6.2.4'
qt_modules: 'qt5compat qtimageformats'
- os: windows-2022 - os: windows-2022
name: "Windows-Legacy" name: "Windows-i686"
msystem: mingw32 msystem: mingw32
qt_ver: 5
- os: windows-2022 - os: windows-2022
name: "Windows" name: "Windows-x86_64"
msystem: mingw32 msystem: mingw64
qt_ver: 6
- os: macos-12 - os: macos-11
name: macOS
macosx_deployment_target: 10.15
qt_ver: 6
qt_host: mac
qt_version: '6.3.0'
qt_modules: 'qt5compat qtimageformats'
- os: macos-12
name: macOS-Legacy
macosx_deployment_target: 10.13 macosx_deployment_target: 10.13
qt_ver: 5
qt_host: mac
qt_version: '5.15.2'
qt_modules: ''
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@ -73,14 +50,6 @@ jobs:
with: with:
submodules: 'true' 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' - name: 'Setup MSYS2'
if: runner.os == 'Windows' if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2 uses: msys2/setup-msys2@v2
@ -92,21 +61,16 @@ jobs:
pacboy: >- pacboy: >-
toolchain:p toolchain:p
cmake:p cmake:p
extra-cmake-modules:p
ninja:p ninja:p
qt${{ matrix.qt_ver }}-base:p qt5:p
qt${{ matrix.qt_ver }}-svg:p
qt${{ matrix.qt_ver }}-imageformats:p
quazip-qt${{ matrix.qt_ver }}:p
ccache:p ccache:p
nsis:p nsis:p
${{ matrix.qt_ver == 6 && 'qt6-5compat:p' || '' }}
- name: Setup ccache - name: Setup ccache
if: runner.os != 'Windows' && inputs.build_type == 'Debug' if: runner.os != 'Windows' && inputs.build_type == 'Debug'
uses: hendrikmuhs/ccache-action@v1.2.1 uses: hendrikmuhs/ccache-action@v1.2.1
with: with:
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }} key: ${{ matrix.os }}-${{ matrix.appimage }}
- name: Setup ccache (Windows) - name: Setup ccache (Windows)
if: runner.os == 'Windows' && inputs.build_type == 'Debug' if: runner.os == 'Windows' && inputs.build_type == 'Debug'
@ -129,9 +93,9 @@ jobs:
uses: actions/cache@v3.0.2 uses: actions/cache@v3.0.2
with: with:
path: '${{ github.workspace }}\.ccache' path: '${{ github.workspace }}\.ccache'
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }} key: ${{ matrix.os }}-${{ matrix.msystem }}
restore-keys: | restore-keys: |
${{ matrix.os }}-qt${{ matrix.qt_ver }} ${{ matrix.os }}-${{ matrix.msystem }}
- name: Set short version - name: Set short version
shell: bash shell: bash
@ -139,36 +103,28 @@ jobs:
ver_short=`git rev-parse --short HEAD` ver_short=`git rev-parse --short HEAD`
echo "VERSION=$ver_short" >> $GITHUB_ENV echo "VERSION=$ver_short" >> $GITHUB_ENV
- name: Install Dependencies (Linux) - name: Install Qt (macOS)
if: runner.os == 'macOS'
run: |
brew update
brew install qt@5 ninja
- name: Update Qt (AppImage)
if: runner.os == 'Linux' && matrix.appimage == true
run: |
sudo add-apt-repository ppa:savoury1/qt-5-15
sudo add-apt-repository ppa:savoury1/kde-5-80
sudo add-apt-repository ppa:savoury1/gpg
sudo add-apt-repository ppa:savoury1/ffmpeg4
- name: Install Qt (Linux)
if: runner.os == 'Linux' if: runner.os == 'Linux'
run: | run: |
sudo apt-get -y update sudo apt-get -y update
sudo apt-get -y install ninja-build extra-cmake-modules scdoc sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 ninja-build qt5-image-formats-plugins
- name: Install Dependencies (macOS)
if: runner.os == 'macOS'
run: |
brew update
brew install ninja extra-cmake-modules
- name: Install Qt (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 6
run: |
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
- name: Install 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'
modules: ${{ matrix.qt_modules }}
cache: true
cache-key-prefix: ${{ matrix.qt_host }}-${{ matrix.qt_version }}-"${{ matrix.qt_modules }}"-qt_cache
- name: Prepare AppImage (Linux) - name: Prepare AppImage (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 5 if: runner.os == 'Linux' && matrix.appimage == true
run: | run: |
wget "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage" wget "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage"
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage" wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage"
@ -181,25 +137,20 @@ jobs:
## ##
- name: Configure CMake (macOS) - name: Configure CMake (macOS)
if: runner.os == 'macOS' && matrix.qt_ver == 6 if: runner.os == 'macOS'
run: | run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DQt5_DIR=/usr/local/opt/qt@5 -DCMAKE_PREFIX_PATH=/usr/local/opt/qt@5 -DLauncher_BUILD_PLATFORM=macOS -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -G Ninja
- name: Configure CMake (macOS-Legacy)
if: runner.os == 'macOS' && matrix.qt_ver == 5
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) - name: Configure CMake (Windows)
if: runner.os == 'Windows' if: runner.os == 'Windows'
shell: msys2 {0} shell: msys2 {0}
run: | run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -G Ninja
- name: Configure CMake (Linux) - name: Configure CMake (Linux)
if: runner.os == 'Linux' if: runner.os == 'Linux'
run: | run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -G Ninja
## ##
# BUILD # BUILD
@ -216,29 +167,6 @@ jobs:
run: | run: |
cmake --build ${{ env.BUILD_DIR }} cmake --build ${{ env.BUILD_DIR }}
##
# TEST
##
- name: Test
if: runner.os != 'Windows'
run: |
ctest --test-dir build --output-on-failure
- name: Test (Windows)
if: runner.os == 'Windows'
shell: msys2 {0}
run: |
ctest --test-dir build --output-on-failure
##
# CODE SCAN
##
- name: Perform CodeQL Analysis
if: runner.os == 'Linux' && matrix.qt_ver == 6
uses: github/codeql-action/analyze@v2
## ##
# PACKAGE BUILDS # PACKAGE BUILDS
## ##
@ -249,28 +177,9 @@ jobs:
cmake --install ${{ env.BUILD_DIR }} cmake --install ${{ env.BUILD_DIR }}
cd ${{ env.INSTALL_DIR }} cd ${{ env.INSTALL_DIR }}
chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher" chmod +x "PolyMC.app/Contents/MacOS/polymc"
sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PrismLauncher.app/Contents/MacOS/prismlauncher" sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PolyMC.app/Contents/MacOS/polymc"
tar -czf ../PrismLauncher.tar.gz * tar -czf ../PolyMC.tar.gz *
- name: Make Sparkle signature (macOS)
if: matrix.name == 'macOS'
run: |
if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then
brew install openssl@3
echo '${{ secrets.SPARKLE_ED25519_KEY }}' > ed25519-priv.pem
signature=$(/usr/local/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.tar.gz -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
rm ed25519-priv.pem
cat >> $GITHUB_STEP_SUMMARY << EOF
### Artifact Information :information_source:
- :memo: Sparkle Signature (ed25519): \`$signature\`
EOF
else
cat >> $GITHUB_STEP_SUMMARY << EOF
### Artifact Information :information_source:
- :warning: Sparkle Signature (ed25519): No private key available (likely a pull request or fork)
EOF
fi
- name: Package (Windows) - name: Package (Windows)
if: runner.os == 'Windows' if: runner.os == 'Windows'
@ -279,8 +188,10 @@ jobs:
cmake --install ${{ env.BUILD_DIR }} cmake --install ${{ env.BUILD_DIR }}
cd ${{ env.INSTALL_DIR }} cd ${{ env.INSTALL_DIR }}
if [ "${{ matrix.qt_ver }}" == "5" ]; then if [ "${{ matrix.msystem }}" == "mingw32" ]; then
cp /mingw32/bin/libcrypto-1_1.dll /mingw32/bin/libssl-1_1.dll ./ cp /mingw32/bin/libcrypto-1_1.dll /mingw32/bin/libssl-1_1.dll ./
elif [ "${{ matrix.msystem }}" == "mingw64" ]; then
cp /mingw64/bin/libcrypto-1_1-x64.dll /mingw64/bin/libssl-1_1-x64.dll ./
fi fi
- name: Package (Windows, portable) - name: Package (Windows, portable)
@ -298,44 +209,38 @@ jobs:
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi" makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
- name: Package (Linux) - name: Package (Linux)
if: runner.os == 'Linux' if: runner.os == 'Linux' && matrix.appimage != true
run: | run: |
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }} cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }}
cd ${{ env.INSTALL_DIR }} cd ${{ env.INSTALL_DIR }}
tar --owner root --group root -czf ../PrismLauncher.tar.gz * tar --owner root --group root -czf ../PolyMC.tar.gz *
- name: Package (Linux, portable) - name: Package (Linux, portable)
if: runner.os == 'Linux' if: runner.os == 'Linux' && matrix.appimage != true
run: | run: |
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
cd ${{ env.INSTALL_PORTABLE_DIR }} cd ${{ env.INSTALL_PORTABLE_DIR }}
tar -czf ../PrismLauncher-portable.tar.gz * tar -czf ../PolyMC-portable.tar.gz *
- name: Package AppImage (Linux) - name: Package AppImage (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 5 if: runner.os == 'Linux' && matrix.appimage == true
shell: bash shell: bash
run: | run: |
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
export OUTPUT="PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage" export OUTPUT="PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
chmod +x linuxdeploy-*.AppImage chmod +x linuxdeploy-*.AppImage
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-{8,17}-openjdk mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-{8,17}-openjdk
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
cp -r ${{ github.workspace }}/JREs/jre8/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk cp -r ${{ github.workspace }}/JREs/jre8/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk
cp -r /home/runner/work/PrismLauncher/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}//usr/lib/
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib" LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server" LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server"
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64" LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64"
@ -343,7 +248,7 @@ jobs:
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib" LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
export LD_LIBRARY_PATH export LD_LIBRARY_PATH
./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.prismlauncher.PrismLauncher.svg ./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.polymc.PolyMC.svg
## ##
# UPLOAD BUILDS # UPLOAD BUILDS
@ -353,63 +258,49 @@ jobs:
if: runner.os == 'macOS' if: runner.os == 'macOS'
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }} name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher.tar.gz path: PolyMC.tar.gz
- name: Upload binary zip (Windows) - name: Upload binary zip (Windows)
if: runner.os == 'Windows' if: runner.os == 'Windows'
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }} name: PolyMC-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
path: ${{ env.INSTALL_DIR }}/** path: ${{ env.INSTALL_DIR }}/**
- name: Upload binary zip (Windows, portable) - name: Upload binary zip (Windows, portable)
if: runner.os == 'Windows' if: runner.os == 'Windows'
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: PrismLauncher-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }} name: PolyMC-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: ${{ env.INSTALL_PORTABLE_DIR }}/** path: ${{ env.INSTALL_PORTABLE_DIR }}/**
- name: Upload installer (Windows) - name: Upload installer (Windows)
if: runner.os == 'Windows' if: runner.os == 'Windows'
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }} name: PolyMC-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher-Setup.exe path: PolyMC-Setup.exe
- name: Upload binary tarball (Linux, Qt 5) - name: Upload binary tarball (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 6 if: runner.os == 'Linux' && matrix.appimage != true
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }} name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher.tar.gz path: PolyMC.tar.gz
- name: Upload binary tarball (Linux, portable, Qt 5) - name: Upload binary tarball (Linux, portable)
if: runner.os == 'Linux' && matrix.qt_ver != 6 if: runner.os == 'Linux' && matrix.appimage != true
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: PrismLauncher-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }} name: PolyMC-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher-portable.tar.gz path: PolyMC-portable.tar.gz
- name: Upload binary tarball (Linux, Qt 6)
if: runner.os == 'Linux' && matrix.qt_ver !=5
uses: actions/upload-artifact@v3
with:
name: PrismLauncher-${{ runner.os }}-Qt6-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher.tar.gz
- name: Upload binary tarball (Linux, portable, Qt 6)
if: runner.os == 'Linux' && matrix.qt_ver != 5
uses: actions/upload-artifact@v3
with:
name: PrismLauncher-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher-portable.tar.gz
- name: Upload AppImage (Linux) - name: Upload AppImage (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 5 if: runner.os == 'Linux' && matrix.appimage == true
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage path: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage

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

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

View File

@ -11,7 +11,6 @@ on:
- '**.nix' - '**.nix'
- 'packages/**' - 'packages/**'
- '.github/ISSUE_TEMPLATE/**' - '.github/ISSUE_TEMPLATE/**'
- '.markdownlint**'
pull_request: pull_request:
paths-ignore: paths-ignore:
- '**.md' - '**.md'
@ -20,7 +19,6 @@ on:
- '**.nix' - '**.nix'
- 'packages/**' - 'packages/**'
- '.github/ISSUE_TEMPLATE/**' - '.github/ISSUE_TEMPLATE/**'
- '.markdownlint**'
workflow_dispatch: workflow_dispatch:
jobs: jobs:
@ -30,5 +28,3 @@ jobs:
uses: ./.github/workflows/build.yml uses: ./.github/workflows/build.yml
with: with:
build_type: Debug build_type: Debug
secrets:
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}

View File

@ -12,8 +12,6 @@ jobs:
uses: ./.github/workflows/build.yml uses: ./.github/workflows/build.yml
with: with:
build_type: Release build_type: Release
secrets:
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
create_release: create_release:
needs: build_release needs: build_release
@ -25,7 +23,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
submodules: 'true' submodules: 'true'
path: 'PrismLauncher-source' path: 'PolyMC-source'
- name: Download artifacts - name: Download artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
- name: Grab and store version - name: Grab and store version
@ -34,26 +32,22 @@ jobs:
echo "VERSION=$tag_name" >> $GITHUB_ENV echo "VERSION=$tag_name" >> $GITHUB_ENV
- name: Package artifacts properly - name: Package artifacts properly
run: | run: |
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }} mv ${{ github.workspace }}/PolyMC-source PolyMC-${{ env.VERSION }}
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz mv PolyMC-Linux-Portable*/PolyMC-portable.tar.gz PolyMC-Linux-Portable-${{ env.VERSION }}.tar.gz
mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz mv PolyMC-Linux*/PolyMC.tar.gz PolyMC-Linux-${{ env.VERSION }}.tar.gz
mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz mv PolyMC-*.AppImage/PolyMC-*.AppImage PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz mv PolyMC-macOS*/PolyMC.tar.gz PolyMC-macOS-${{ env.VERSION }}.tar.gz
mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage
mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
tar -czf PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }} tar -czf PolyMC-${{ env.VERSION }}.tar.gz PolyMC-${{ env.VERSION }}
for d in PrismLauncher-Windows-*; do for d in PolyMC-Windows-*; do
cd "${d}" || continue cd "${d}" || continue
LEGACY="$(echo -n ${d} | grep -o Legacy || true)" ARCH="$(echo -n ${d} | cut -d '-' -f 3)"
INST="$(echo -n ${d} | grep -o Setup || true)" INST="$(echo -n ${d} | grep -o Setup || true)"
PORT="$(echo -n ${d} | grep -o Portable || true)" PORT="$(echo -n ${d} | grep -o Portable || true)"
NAME="PrismLauncher-Windows" NAME="PolyMC-Windows-${ARCH}"
test -z "${LEGACY}" || NAME="${NAME}-Legacy"
test -z "${PORT}" || NAME="${NAME}-Portable" test -z "${PORT}" || NAME="${NAME}-Portable"
test -z "${INST}" || mv PrismLauncher-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe test -z "${INST}" || mv PolyMC-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" * test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" *
cd .. cd ..
done done
@ -65,21 +59,18 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
tag_name: ${{ github.ref }} tag_name: ${{ github.ref }}
name: PrismLauncher ${{ env.VERSION }} name: PolyMC ${{ env.VERSION }}
draft: true draft: true
prerelease: false prerelease: false
files: | files: |
PrismLauncher-Linux-${{ env.VERSION }}.tar.gz PolyMC-Linux-${{ env.VERSION }}.tar.gz
PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz PolyMC-Linux-Portable-${{ env.VERSION }}.tar.gz
PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
PrismLauncher-Windows-Legacy-${{ env.VERSION }}.zip PolyMC-Windows-i686-${{ env.VERSION }}.zip
PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz PolyMC-Windows-i686-Portable-${{ env.VERSION }}.zip
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz PolyMC-Windows-i686-Setup-${{ env.VERSION }}.exe
PrismLauncher-Windows-Legacy-Portable-${{ env.VERSION }}.zip PolyMC-Windows-x86_64-${{ env.VERSION }}.zip
PrismLauncher-Windows-Legacy-Setup-${{ env.VERSION }}.exe PolyMC-Windows-x86_64-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-${{ env.VERSION }}.zip PolyMC-Windows-x86_64-Setup-${{ env.VERSION }}.exe
PrismLauncher-Windows-Portable-${{ env.VERSION }}.zip PolyMC-macOS-${{ env.VERSION }}.tar.gz
PrismLauncher-Windows-Setup-${{ env.VERSION }}.exe PolyMC-${{ env.VERSION }}.tar.gz
PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
PrismLauncher-${{ env.VERSION }}.tar.gz

View File

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

14
.gitmodules vendored
View File

@ -1,12 +1,8 @@
[submodule "depends/libnbtplusplus"]
path = libraries/libnbtplusplus
url = https://github.com/PolyMC/libnbtplusplus.git
pushurl = git@github.com:PolyMC/libnbtplusplus.git
[submodule "libraries/quazip"] [submodule "libraries/quazip"]
path = libraries/quazip path = libraries/quazip
url = https://github.com/stachenov/quazip.git url = https://github.com/stachenov/quazip.git
[submodule "libraries/tomlplusplus"]
path = libraries/tomlplusplus
url = https://github.com/marzer/tomlplusplus.git
[submodule "libraries/filesystem"]
path = libraries/filesystem
url = https://github.com/gulrak/filesystem
[submodule "libraries/libnbtplusplus"]
path = libraries/libnbtplusplus
url = https://github.com/PrismLauncher/libnbtplusplus.git

View File

@ -1,12 +0,0 @@
# MD013/line-length - Line length
MD013: false
# MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content
MD024:
siblings-only: true
# MD033/no-inline-html Inline HTML
MD033: false
# MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading
MD041: false

View File

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

View File

@ -1,53 +1,5 @@
# Build Instructions # Build Instructions
Full build instructions will be available on [the website](https://prismlauncher.org/wiki/development/build-instructions/). Build instructions are available on [the website](https://polymc.org/wiki/development/build-instructions/).
If you would like to contribute or fix an issue with the Build instructions you will be able to do so [here](https://github.com/PrismLauncher/website/blob/master/src/wiki/development/build-instructions.md).
## Getting the source
Clone the source code using git, and grab all the submodules. This is generic for all platforms you want to build on.
```
git clone --recursive https://github.com/PrismLauncher/PrismLauncher
cd PrismLauncher
```
## Linux
This guide will mostly mention dependant packages by their Debian naming and commands are done by a user in the sudoers file.
### Dependencies
- A C++ compiler capable of building C++17 code (can be found in the package `build-essential`).
- Qt Development tools 5.12 or newer (on Debian 11 or Debian-based distributions, `qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5`).
- `cmake` 3.15 or newer.
- `extra-cmake-modules`.
- zlib (`zlib1g-dev` on Debian 11 or Debian-based distributions).
- Java Development Kit (Java JDK) (`openjdk-17-jdk` on Debian 11 or Debian-based distributions).
- Mesa GL headers (`libgl1-mesa-dev` on Debian 11 or Debian-based distributions).
- (Optional) `scdoc` to generate man pages.
In conclusion, to check if all you need is installed (including optional):
```
sudo apt install build-essential qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 cmake extra-cmake-modules zlib1g-dev openjdk-17-jdk libgl1-mesa-dev scdoc
```
### Compiling
#### Building and installing on the system
This is usually the suggested way to build the client.
```
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="/usr" -DENABLE_LTO=ON
cmake --build build -j$(nproc)
sudo cmake --install build
```
#### Building a portable binary
```
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=install
cmake --build build -j$(nproc)
cmake --install build
cmake --install build --component portable
```
If you would like to contribute or fix an issue with the Build instructions you can do so [here](https://github.com/PolyMC/polymc.github.io/blob/master/src/wiki/development/build-instructions.md).

View File

@ -6,12 +6,14 @@ if(WIN32)
endif() endif()
project(Launcher) project(Launcher)
include(CTest)
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD) string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
if(IS_IN_SOURCE_BUILD) if(IS_IN_SOURCE_BUILD)
message(FATAL_ERROR "You are building the Launcher in-source. Please separate the build tree from the source tree.") message(FATAL_ERROR "You are building the Launcher in-source. Please separate the build tree from the source tree.")
endif() endif()
##################################### Set CMake options ##################################### ##################################### Set CMake options #####################################
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON) set(CMAKE_AUTORCC ON)
@ -29,10 +31,13 @@ set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/jars)
######## Set compiler flags ######## ######## Set compiler flags ########
set(CMAKE_CXX_STANDARD_REQUIRED true) set(CMAKE_CXX_STANDARD_REQUIRED true)
set(CMAKE_C_STANDARD_REQUIRED true) set(CMAKE_C_STANDARD_REQUIRED true)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 11)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
include(GenerateExportHeader) include(GenerateExportHeader)
set(CMAKE_CXX_FLAGS "-Wall -pedantic -fstack-protector-strong --param=ssp-buffer-size=4 ${CMAKE_CXX_FLAGS}") set(CMAKE_CXX_FLAGS "-Wall -pedantic -Werror -Wno-deprecated-declarations -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 ${CMAKE_CXX_FLAGS}")
if(UNIX AND APPLE)
set(CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}")
endif()
# Fix build with Qt 5.13 # Fix build with Qt 5.13
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y")
@ -58,30 +63,20 @@ if(ENABLE_LTO)
endif() endif()
endif() endif()
option(BUILD_TESTING "Build the testing tree." ON)
find_package(ECM REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH "${ECM_MODULE_PATH};${CMAKE_MODULE_PATH}")
include(CTest)
include(ECMAddTests)
if(BUILD_TESTING)
enable_testing()
endif()
##################################### Set Application options ##################################### ##################################### Set Application options #####################################
######## Set URLs ######## ######## Set URLs ########
set(Launcher_NEWS_RSS_URL "https://prismlauncher.org/feed/feed.xml" CACHE STRING "URL to fetch PrismLauncher's news RSS feed from.") set(Launcher_NEWS_RSS_URL "https://polymc.org/feed/feed.xml" CACHE STRING "URL to fetch PolyMC's news RSS feed from.")
set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'") set(Launcher_NEWS_OPEN_URL "https://polymc.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'")
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help") set(Launcher_HELP_URL "https://polymc.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
######## Set version numbers ######## ######## Set version numbers ########
set(Launcher_VERSION_MAJOR 5) set(Launcher_VERSION_MAJOR 1)
set(Launcher_VERSION_MINOR 0) set(Launcher_VERSION_MINOR 3)
set(Launcher_VERSION_HOTFIX 2)
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}") # Build number
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0") set(Launcher_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0")
# Build platform. # Build platform.
set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.") set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.")
@ -90,25 +85,27 @@ set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the plat
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.") set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
# The metadata server # The metadata server
set(Launcher_META_URL "https://meta.prismlauncher.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.") set(Launcher_META_URL "https://meta.polymc.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
# Imgur API Client ID # Imgur API Client ID
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application") set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
# Bug tracker URL # Bug tracker URL
set(Launcher_BUG_TRACKER_URL "https://github.com/PrismLauncher/PrismLauncher/issues" CACHE STRING "URL for the bug tracker.") set(Launcher_BUG_TRACKER_URL "https://github.com/PolyMC/PolyMC/issues" CACHE STRING "URL for the bug tracker.")
# Translations Platform URL # Translations Platform URL
set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/prismlauncher/launcher/" CACHE STRING "URL for the translations platform.") set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/polymc/polymc/" CACHE STRING "URL for the translations platform.")
# Matrix Space # Matrix Space
set(Launcher_MATRIX_URL "https://matrix.to/#/#prismlauncher:matrix.org" CACHE STRING "URL to the Matrix Space") set(Launcher_MATRIX_URL "https://matrix.to/#/#polymc:matrix.org" CACHE STRING "URL to the Matrix Space")
# Discord URL # Discord URL
set(Launcher_DISCORD_URL "https://discord.gg/prismlauncher" CACHE STRING "URL for the Discord guild.") set(Launcher_DISCORD_URL "https://discord.gg/Z52pwxWCHP" CACHE STRING "URL for the Discord guild.")
# Subreddit URL # Subreddit URL
set(Launcher_SUBREDDIT_URL "https://www.reddit.com/r/PrismLauncher/" CACHE STRING "URL for the subreddit.") set(Launcher_SUBREDDIT_URL "https://www.reddit.com/r/PolyMCLauncher/" CACHE STRING "URL for the subreddit.")
# Builds # Builds
set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules") set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
@ -123,31 +120,34 @@ set(Launcher_QT_VERSION_MAJOR "5" CACHE STRING "Major Qt version to build agains
# By using this key in your builds you accept the terms of use laid down in # By using this key in your builds you accept the terms of use laid down in
# https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use # https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use
set(Launcher_MSA_CLIENT_ID "c36a9fb6-4f2a-41ff-90bd-ae7cc92031eb" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application") set(Launcher_MSA_CLIENT_ID "549033b2-1532-4d4e-ae77-1bbaa46f9d74" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
# By using this key in your builds you accept the terms and conditions laid down in # By using this key in your builds you accept the terms and conditions laid down in
# https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions # https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions
# NOTE: CurseForge requires you to change this if you make any kind of derivative work. # NOTE: CurseForge requires you to change this if you make any kind of derivative work.
# This key was issued specifically for Prism Launcher set(Launcher_CURSEFORGE_API_KEY "$2a$10$1Oqr2MX3O4n/ilhFGc597u8tfI3L2Hyr9/rtWDAMRjghSQV2QUuxq" CACHE STRING "CurseForge API Key")
set(Launcher_CURSEFORGE_API_KEY "$2a$10$wuAJuNZuted3NORVmpgUC.m8sI.pv1tOPKZyBgLFGjxFp/br0lZCC" CACHE STRING "API key for the CurseForge platform")
#### Check the current Git commit and branch #### Check the current Git commit and branch
include(GetGitRevisionDescription) include(GetGitRevisionDescription)
git_get_exact_tag(Launcher_GIT_TAG)
get_git_head_revision(Launcher_GIT_REFSPEC Launcher_GIT_COMMIT) get_git_head_revision(Launcher_GIT_REFSPEC Launcher_GIT_COMMIT)
message(STATUS "Git commit: ${Launcher_GIT_COMMIT}") message(STATUS "Git commit: ${Launcher_GIT_COMMIT}")
message(STATUS "Git tag: ${Launcher_GIT_TAG}")
message(STATUS "Git refspec: ${Launcher_GIT_REFSPEC}") message(STATUS "Git refspec: ${Launcher_GIT_REFSPEC}")
set(Launcher_RELEASE_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
set(Launcher_RELEASE_VERSION_NAME4 "${Launcher_RELEASE_VERSION_NAME}.0")
set(Launcher_RELEASE_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},${Launcher_VERSION_HOTFIX},0")
string(TIMESTAMP TODAY "%Y-%m-%d") string(TIMESTAMP TODAY "%Y-%m-%d")
set(Launcher_BUILD_TIMESTAMP "${TODAY}") set(Launcher_RELEASE_TIMESTAMP "${TODAY}")
#### Custom target to just print the version.
add_custom_target(version echo "Version: ${Launcher_RELEASE_VERSION_NAME}")
add_custom_target(tcversion echo "\\#\\#teamcity[setParameter name=\\'env.LAUNCHER_VERSION\\' value=\\'${Launcher_RELEASE_VERSION_NAME}\\']")
################################ 3rd Party Libs ################################ ################################ 3rd Party Libs ################################
# Find the required Qt parts # Find the required Qt parts
include(QtVersionlessBackport)
if(Launcher_QT_VERSION_MAJOR EQUAL 5) if(Launcher_QT_VERSION_MAJOR EQUAL 5)
set(QT_VERSION_MAJOR 5) set(QT_VERSION_MAJOR 5)
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml) find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml)
@ -159,48 +159,26 @@ if(Launcher_QT_VERSION_MAJOR EQUAL 5)
set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE) set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE)
set(FORCE_BUNDLED_QUAZIP 1) set(FORCE_BUNDLED_QUAZIP 1)
endif() endif()
# Qt 6 sets these by default. Notably causes Windows APIs to use UNICODE strings.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUNICODE -D_UNICODE")
elseif(Launcher_QT_VERSION_MAJOR EQUAL 6)
set(QT_VERSION_MAJOR 6)
find_package(Qt6 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml Core5Compat)
list(APPEND Launcher_QT_LIBS Qt6::Core5Compat)
if(NOT Launcher_FORCE_BUNDLED_LIBS)
find_package(QuaZip-Qt6 1.3 QUIET)
endif()
if (NOT QuaZip-Qt6_FOUND)
set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE)
set(FORCE_BUNDLED_QUAZIP 1)
endif()
else() else()
message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported") message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported")
endif() endif()
include(ECMQueryQt) # The Qt5 cmake files don't provide its install paths, so ask qmake.
ecm_query_qt(QT_PLUGINS_DIR QT_INSTALL_PLUGINS) include(QMakeQuery)
ecm_query_qt(QT_LIBS_DIR QT_INSTALL_LIBS) query_qmake(QT_INSTALL_PLUGINS QT_PLUGINS_DIR)
ecm_query_qt(QT_LIBEXECS_DIR QT_INSTALL_LIBEXECS) query_qmake(QT_INSTALL_IMPORTS QT_IMPORTS_DIR)
ecm_query_qt(QT_DATA_DIR QT_HOST_DATA) query_qmake(QT_INSTALL_LIBS QT_LIBS_DIR)
query_qmake(QT_INSTALL_LIBEXECS QT_LIBEXECS_DIR)
query_qmake(QT_HOST_DATA QT_DATA_DIR)
set(QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs) set(QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs)
# NOTE: Qt 6 already sets this by default
if (Qt5_POSITION_INDEPENDENT_CODE) if (Qt5_POSITION_INDEPENDENT_CODE)
SET(CMAKE_POSITION_INDEPENDENT_CODE ON) SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif() endif()
if(NOT Launcher_FORCE_BUNDLED_LIBS)
# Find toml++
find_package(tomlplusplus 3.2.0 QUIET)
# Find ghc_filesystem
find_package(ghc_filesystem QUIET)
endif()
####################################### Program Info ####################################### ####################################### Program Info #######################################
set(Launcher_APP_BINARY_NAME "prismlauncher" CACHE STRING "Name of the Launcher binary") set(Launcher_APP_BINARY_NAME "polymc" CACHE STRING "Name of the Launcher binary")
add_subdirectory(program_info) add_subdirectory(program_info)
####################################### Install layout ####################################### ####################################### Install layout #######################################
@ -214,7 +192,6 @@ if(UNIX AND APPLE)
set(BINARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS") set(BINARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
set(LIBRARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS") set(LIBRARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
set(PLUGIN_DEST_DIR "${Launcher_Name}.app/Contents/MacOS") set(PLUGIN_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
set(FRAMEWORK_DEST_DIR "${Launcher_Name}.app/Contents/Frameworks")
set(RESOURCES_DEST_DIR "${Launcher_Name}.app/Contents/Resources") set(RESOURCES_DEST_DIR "${Launcher_Name}.app/Contents/Resources")
set(JARS_DEST_DIR "${Launcher_Name}.app/Contents/MacOS/jars") set(JARS_DEST_DIR "${Launcher_Name}.app/Contents/MacOS/jars")
@ -224,21 +201,15 @@ if(UNIX AND APPLE)
# Mac bundle settings # Mac bundle settings
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_Name}") set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_Name}")
set(MACOSX_BUNDLE_INFO_STRING "${Launcher_Name}: A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.") set(MACOSX_BUNDLE_INFO_STRING "${Launcher_Name}: A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.prismlauncher.${Launcher_Name}") set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.polymc.${Launcher_Name}")
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns) set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns)
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2021-2022 ${Launcher_Copyright}") set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2021-2022 ${Launcher_Copyright}")
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=")
set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml")
set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0/Sparkle-2.1.0.tar.xz" CACHE STRING "URL to Sparkle release archive")
set(MACOSX_SPARKLE_SHA256 "bf6ac1caa9f8d321d5784859c88da874f28412f37fb327bc21b7b14c5d61ef94" CACHE STRING "SHA256 checksum for Sparkle release archive")
set(MACOSX_SPARKLE_DIR "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
# directories to look for dependencies # directories to look for dependencies
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${MACOSX_SPARKLE_DIR}) set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
# install as bundle # install as bundle
set(INSTALL_BUNDLE "full") set(INSTALL_BUNDLE "full")
@ -261,13 +232,13 @@ elseif(UNIX)
# Set RPATH # Set RPATH
SET(Launcher_BINARY_RPATH "$ORIGIN/") SET(Launcher_BINARY_RPATH "$ORIGIN/")
# jars path is determined on runtime, relative to "Application root path", generally /usr or the root of the portable bundle
set(Launcher_APP_BINARY_DEFS "-DLAUNCHER_JARS_LOCATION=${JARS_DEST_DIR}")
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${LAUNCHER_DESKTOP_DEST_DIR}) 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_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${LAUNCHER_METAINFO_DEST_DIR})
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION ${LAUNCHER_ICON_DEST_DIR}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION ${LAUNCHER_ICON_DEST_DIR})
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_ManPage} DESTINATION ${LAUNCHER_MAN_DEST_DIR} RENAME "${Launcher_APP_BINARY_NAME}.6")
if(Launcher_ManPage)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION ${LAUNCHER_MAN_DEST_DIR})
endif()
# Install basic runner script if component "portable" is selected # Install basic runner script if component "portable" is selected
configure_file(launcher/Launcher.in "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" @ONLY) configure_file(launcher/Launcher.in "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" @ONLY)
@ -306,6 +277,7 @@ add_subdirectory(libraries/systeminfo) # system information library
add_subdirectory(libraries/hoedown) # markdown parser add_subdirectory(libraries/hoedown) # markdown parser
add_subdirectory(libraries/launcher) # java based launcher part for Minecraft add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
add_subdirectory(libraries/javacheck) # java compatibility checker add_subdirectory(libraries/javacheck) # java compatibility checker
add_subdirectory(libraries/xz-embedded) # xz compression
if (FORCE_BUNDLED_QUAZIP) if (FORCE_BUNDLED_QUAZIP)
message(STATUS "Using bundled QuaZip") message(STATUS "Using bundled QuaZip")
set(BUILD_SHARED_LIBS 0) # link statically to avoid conflicts. set(BUILD_SHARED_LIBS 0) # link statically to avoid conflicts.
@ -315,31 +287,16 @@ else()
message(STATUS "Using system QuaZip") message(STATUS "Using system QuaZip")
endif() endif()
add_subdirectory(libraries/rainbow) # Qt extension for colors add_subdirectory(libraries/rainbow) # Qt extension for colors
add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
if(NOT tomlplusplus_FOUND) add_subdirectory(libraries/classparser) # class parser library
message(STATUS "Using bundled tomlplusplus") add_subdirectory(libraries/optional-bare)
add_subdirectory(libraries/tomlplusplus) # toml parser add_subdirectory(libraries/tomlc99) # toml parser
else()
message(STATUS "Using system tomlplusplus")
endif()
add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much
add_subdirectory(libraries/gamemode)
add_subdirectory(libraries/murmur2) # Hash for usage with the CurseForge API
if (NOT ghc_filesystem_FOUND)
message(STATUS "Using bundled ghc_filesystem")
set(GHC_FILESYSTEM_WITH_INSTALL OFF) # Workaround ghc::filesystem bug
add_subdirectory(libraries/filesystem) # Implementation of std::filesystem for old C++, for usage in old macOS
add_library(ghcFilesystem::ghc_filesystem ALIAS ghc_filesystem)
else()
message(STATUS "Using system ghc_filesystem")
endif()
############################### Built Artifacts ############################### ############################### Built Artifacts ###############################
add_subdirectory(buildconfig) add_subdirectory(buildconfig)
if(BUILD_TESTING)
add_subdirectory(tests)
endif()
# NOTE: this must always be last to appease the CMake deity of quirky install command evaluation order. # NOTE: this must always be last to appease the CMake deity of quirky install command evaluation order.
add_subdirectory(launcher) add_subdirectory(launcher)

View File

@ -1,5 +1,4 @@
# Contributor Covenant Code of Conduct # Contributor Covenant Code of Conduct
This is a modified version of the Contributor Covenant. This is a modified version of the Contributor Covenant.
See commit history to see our changes. See commit history to see our changes.
@ -63,7 +62,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement via email at reported to the community leaders responsible for enforcement via email at
[coc@scrumplex.net](mailto:coc@scrumplex.net) (Email [polymc-enforcement@scrumplex.net](mailto:polymc-enforcement@scrumplex.net) (Email
address subject to change). address subject to change).
All complaints will be reviewed and investigated promptly and fairly. All complaints will be reviewed and investigated promptly and fairly.
@ -134,3 +133,4 @@ For answers to common questions about this code of conduct, see the FAQ at
[Mozilla CoC]: https://github.com/mozilla/diversity [Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq [FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations [translations]: https://www.contributor-covenant.org/translations

View File

@ -1,63 +0,0 @@
# Contributions Guidelines
## Code formatting
Try to follow the existing formatting.
If there is no existing formatting, you may use `clang-format` with our included `.clang-format` configuration.
In general, in order of importance:
- Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
- Prefer readability over dogma.
- Keep to the existing formatting.
- Indent with 4 space unless it's in a submodule.
- Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
## Signing your work
In an effort to ensure that the code you contribute is actually compatible with the licenses in this codebase, we require you to sign-off all your contributions.
This can be done by appending `-s` to your `git commit` call, or by manually appending the following text to your commit message:
```
<commit message>
Signed-off-by: Author name <Author email>
```
By signing off your work, you agree to the terms below:
```
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
```
These terms will be enforced once you create a pull request, and you will be informed automatically if any of your commits aren't signed-off by you.
As a bonus, you can also [cryptographically sign your commits][gh-signing-commits] and enable [vigilant mode][gh-vigilant-mode] on GitHub.
[gh-signing-commits]: https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits
[gh-vigilant-mode]: https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits

View File

@ -1,121 +1,64 @@
## Prism Launcher # PolyMC
Prism Launcher - Minecraft Launcher Copyright (C) 2012-2021 MultiMC Contributors
Copyright (C) 2022 Prism Launcher Contributors Copyright (C) 2021-2022 PolyMC Contributors
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3. the Free Software Foundation, version 3.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
This file incorporates work covered by the following copyright and # Launcher (https://github.com/MultiMC/Launcher)
permission notice: Copyright 2012-2021 MultiMC Contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Copyright 2013-2021 MultiMC Contributors http://www.apache.org/licenses/LICENSE-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Unless required by applicable law or agreed to in writing, software
you may not use this file except in compliance with the License. distributed under the License is distributed on an "AS IS" BASIS,
You may obtain a copy of the License at WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
http://www.apache.org/licenses/LICENSE-2.0 # MinGW runtime (Windows)
Unless required by applicable law or agreed to in writing, software Copyright (c) 2012 MinGW.org project
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
## PolyMC Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
PolyMC - Minecraft Launcher The above copyright notice, this permission notice and the below disclaimer
Copyright (C) 2021-2022 PolyMC Contributors shall be included in all copies or substantial portions of the Software.
This program is free software: you can redistribute it and/or modify THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
it under the terms of the GNU General Public License as published by IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
the Free Software Foundation, version 3. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
This program is distributed in the hope that it will be useful, # Qt 5
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
along with this program. If not, see <https://www.gnu.org/licenses/>. Contact: http://www.qt-project.org/legal
This file incorporates work covered by the following copyright and Licensed under LGPL v2.1
permission notice:
Copyright 2013-2021 MultiMC Contributors # libnbt++
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
## MinGW-w64 runtime (Windows)
Copyright (c) 2009, 2010, 2011, 2012, 2013 by the mingw-w64 project
This license has been certified as open source. It has also been designated
as GPL compatible by the Free Software Foundation (FSF).
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions in source code must retain the accompanying copyright
notice, this list of conditions, and the following disclaimer.
2. Redistributions in binary form must reproduce the accompanying
copyright notice, this list of conditions, and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
3. Names of the copyright holders must not be used to endorse or promote
products derived from this software without prior written permission
from the copyright holders.
4. The right to distribute this software or to use it for any purpose does
not give you the right to use Servicemarks (sm) or Trademarks (tm) of
the copyright holders. Use of them is covered by separate agreement
with the copyright holders.
5. If any files are modified, you must cause the modified files to carry
prominent notices stating that you changed the files and the date of
any change.
Disclaimer
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Information on third party licenses used in MinGW-w64 can be found in its COPYING.MinGW-w64-runtime.txt.
## Qt 5/6
Copyright (C) 2022 The Qt Company Ltd and other contributors.
Contact: https://www.qt.io/licensing
Licensed under LGPL v3
## libnbt++
libnbt++ - A library for the Minecraft Named Binary Tag format. libnbt++ - A library for the Minecraft Named Binary Tag format.
Copyright (C) 2013, 2015 ljfa-ag Copyright (C) 2013, 2015 ljfa-ag
@ -133,7 +76,7 @@
You should have received a copy of the GNU Lesser General Public License You should have received a copy of the GNU Lesser General Public License
along with libnbt++. If not, see <http://www.gnu.org/licenses/>. along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
## rainbow (KGuiAddons) # rainbow (KGuiAddons)
Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net> Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
Copyright (C) 2007 Olaf Schmidt <ojschmidt@kde.org> Copyright (C) 2007 Olaf Schmidt <ojschmidt@kde.org>
@ -156,7 +99,7 @@
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. Boston, MA 02110-1301, USA.
## Hoedown # Hoedown
Copyright (c) 2008, Natacha Porté Copyright (c) 2008, Natacha Porté
Copyright (c) 2011, Vicent Martí Copyright (c) 2011, Vicent Martí
@ -174,7 +117,7 @@
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
## Batch icon set # Batch icon set
You are free to use Batch (the "icon set") or any part thereof (the "icons") You are free to use Batch (the "icon set") or any part thereof (the "icons")
in any personal, open-source or commercial work without obligation of payment in any personal, open-source or commercial work without obligation of payment
@ -190,7 +133,7 @@
PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS,
EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
## Material Design Icons # Material Design Icons
Copyright (c) 2014, Austin Andrews (http://materialdesignicons.com/), Copyright (c) 2014, Austin Andrews (http://materialdesignicons.com/),
with Reserved Font Name Material Design Icons. with Reserved Font Name Material Design Icons.
@ -201,82 +144,76 @@
This license is copied below, and is also available with a FAQ at: This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL http://scripts.sil.org/OFL
## Quazip # Quazip
Copyright (C) 2005-2021 Sergey A. Tachenov Copyright (C) 2005-2011 Sergey A. Tachenov
The QuaZip library is licensed under the GNU Lesser General Public This program is free software; you can redistribute it and/or modify it
License V2.1 plus a static linking exception. under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at
your option) any later version.
The original ZIP/UNZIP package (MiniZip) is copyrighted by Gilles This program is distributed in the hope that it will be useful, but
Vollant and contributors, see quazip/(un)zip.h files for details. WITHOUT ANY WARRANTY; without even the implied warranty of
Basically it's the zlib license. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
General Public License for more details.
STATIC LINKING EXCEPTION You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software Foundation,
The copyright holders give you permission to link this library with Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
independent modules to produce an executable, regardless of the license
terms of these independent modules, and to copy and distribute the
resulting executable under terms of your choice, provided that you also
meet, for each linked independent module, the terms and conditions of
the license of that module. An independent module is a module which is
not derived from or based on this library. If you modify this library,
you must extend this exception to your version of the library.
See COPYING file for the full LGPL text. See COPYING file for the full LGPL text.
## launcher (`libraries/launcher`) Original ZIP package is copyrighted by Gilles Vollant, see
quazip/(un)zip.h files for details, basically it's zlib license.
PolyMC - Minecraft Launcher # xz-minidec
Copyright (C) 2021-2022 PolyMC Contributors
This program is free software: you can redistribute it and/or modify XZ decompressor
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3.
This program is distributed in the hope that it will be useful, Authors: Lasse Collin <lasse.collin@tukaani.org>
but WITHOUT ANY WARRANTY; without even the implied warranty of Igor Pavlov <http://7-zip.org/>
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Linking this library statically or dynamically with other modules is This file has been put into the public domain.
making a combined work based on this library. Thus, the terms and You can do whatever you want with this file.
conditions of the GNU General Public License cover the whole
combination.
As a special exception, the copyright holders of this library give # ColumnResizer
you permission to link this library with independent modules to
produce an executable, regardless of the license terms of these
independent modules, and to copy and distribute the resulting
executable under terms of your choice, provided that you also meet,
for each linked independent module, the terms and conditions of the
license of that module. An independent module is a module which is
not derived from or based on this library. If you modify this
library, you may extend this exception to your version of the
library, but you are not obliged to do so. If you do not wish to do
so, delete this exception statement from your version.
You should have received a copy of the GNU General Public License Copyright (c) 2011-2016 Aurélien Gâteau and contributors.
along with this program. If not, see <https://www.gnu.org/licenses/>.
This file incorporates work covered by the following copyright and All rights reserved.
permission notice:
Copyright 2013-2021 MultiMC Contributors Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the
disclaimer below) provided that the following conditions are met:
Licensed under the Apache License, Version 2.0 (the "License"); * Redistributions of source code must retain the above copyright
you may not use this file except in compliance with the License. notice, this list of conditions and the following disclaimer.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
Unless required by applicable law or agreed to in writing, software * The name of the contributors may not be used to endorse or
distributed under the License is distributed on an "AS IS" BASIS, promote products derived from this software without specific prior
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. written permission.
See the License for the specific language governing permissions and
limitations under the License.
## lionshead NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# lionshead
Code has been taken from https://github.com/natefoo/lionshead and loosely Code has been taken from https://github.com/natefoo/lionshead and loosely
translated to C++ laced with Qt. translated to C++ laced with Qt.
@ -303,26 +240,60 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
## tomlplusplus # optional-bare
Code from https://github.com/martinmoene/optional-bare/
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
# tomlc99
MIT License MIT License
Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> Copyright (c) 2017 CK Tan
https://github.com/cktan/tomlc99
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated Permission is hereby granted, free of charge, to any person obtaining a copy
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the of this software and associated documentation files (the "Software"), to deal
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to in the Software without restriction, including without limitation the rights
permit persons to whom the Software is furnished to do so, subject to the following conditions: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the The above copyright notice and this permission notice shall be included in all
Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## O2 (Katabasis fork) # O2 (Katabasis fork)
Copyright (c) 2012, Akos Polster Copyright (c) 2012, Akos Polster
All rights reserved. All rights reserved.
@ -347,54 +318,3 @@
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
## Gamemode
Copyright (c) 2017-2022, Feral Interactive
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
## gulrak/filesystem
Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,29 +1,92 @@
<p align="center"> <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/polymc-header-black.svg#gh-light-mode-only" alt="PolyMC logo"/>
<img src="./program_info/org.prismlauncher.PrismLauncher.logo-darkmode.svg#gh-dark-mode-only" alt="Prism Launcher logo" width="50%"/> <img src="./program_info/polymc-header.svg#gh-dark-mode-only" alt="PolyMC logo"/>
</p> </p>
<br>
Prism Launcher is a custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once. PolyMC is a custom launcher for Minecraft that focuses on predictability, long term stability and simplicity.
We are working on a website and other media, for more info we have a [Discord server](https://discord.gg/prismlauncher). This is a **fork** of the MultiMC Launcher and not endorsed by MultiMC.
If you want to read about why this fork was created, check out [our FAQ page](https://polymc.org/wiki/overview/faq/).
<br>
## Installation # Installation
- All downloads and instructions for Prism Launcher will soon be available. - All downloads and instructions for PolyMC can be found [here](https://polymc.org/download/)
- Last build status can be found [here](https://github.com/PrismLauncher/PrismLauncher/actions). - Last build status: https://github.com/PolyMC/PolyMC/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. ## Development Builds
There are per-commit development builds available [here](https://github.com/PolyMC/PolyMC/actions). These have debug information in the binaries, so their file sizes are relatively larger.
Portable builds are provided for AppImage on Linux, Windows, and macOS. Portable builds are provided for AppImage on Linux, Windows, and macOS.
## Help & Support For Debian and Arch, you can use these packages for the latest development versions:
[![polymc-git](https://img.shields.io/badge/aur-polymc--git-blue)](https://aur.archlinux.org/packages/polymc-git/)
[![polymc-git](https://img.shields.io/badge/mpr-polymc--git-orange)](https://mpr.makedeb.org/packages/polymc-git)
For flatpak, you can use [flathub-beta](https://discourse.flathub.org/t/how-to-use-flathub-beta/2111)
# Help & Support
[![Join the Discord Server](https://discordapp.com/api/guilds/1031648380885147709/widget.png?style=banner3)](https://discord.gg/hX4g537UNE) Feel free to create an issue if you need help. However, you might find it easier to ask in the Discord server.
[![PolyMC Discord](https://img.shields.io/discord/923671181020766230?label=PolyMC%20Discord)](https://discord.gg/xq7fxrgtMP)
## License For people who don't want to use Discord, we have a Matrix Space which is bridged to the Discord server:
[![PolyMC Space](https://img.shields.io/matrix/polymc:matrix.org?label=PolyMC%20space)](https://matrix.to/#/#polymc:matrix.org)
If there are any issues with the space or you are using a client that does not support the feature here are the individual rooms:
[![Development](https://img.shields.io/matrix/polymc-development:matrix.org?label=PolyMC%20Development)](https://matrix.to/#/#polymc-development:matrix.org)
[![Discussion](https://img.shields.io/matrix/polymc-discussion:matrix.org?label=PolyMC%20Discussion)](https://matrix.to/#/#polymc-discussion:matrix.org)
[![Github](https://img.shields.io/matrix/polymc-github:matrix.org?label=PolyMC%20Github)](https://matrix.to/#/#polymc-github:matrix.org)
[![Maintainers](https://img.shields.io/matrix/polymc-maintainers:matrix.org?label=PolyMC%20Maintainers)](https://matrix.to/#/#polymc-maintainers:matrix.org)
[![News](https://img.shields.io/matrix/polymc-news:matrix.org?label=PolyMC%20News)](https://matrix.to/#/#polymc-news:matrix.org)
[![Offtopic](https://img.shields.io/matrix/polymc-offtopic:matrix.org?label=PolyMC%20Offtopic)](https://matrix.to/#/#polymc-offtopic:matrix.org)
[![Support](https://img.shields.io/matrix/polymc-support:matrix.org?label=PolyMC%20Support)](https://matrix.to/#/#polymc-support:matrix.org)
[![Voice](https://img.shields.io/matrix/polymc-voice:matrix.org?label=PolyMC%20Voice)](https://matrix.to/#/#polymc-voice:matrix.org)
we also have a subreddit you can post your issues and suggestions on:
[r/PolyMCLauncher](https://www.reddit.com/r/PolyMCLauncher/)
# Development
If you want to contribute to PolyMC you might find it useful to join our Discord Server or Matrix Space.
## Building
If you want to build PolyMC yourself, check [Build Instructions](https://polymc.org/wiki/development/build-instructions/) for build instructions.
## Code formatting
Just follow the existing formatting.
In general, in order of importance:
- Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
- Prefer readability over dogma.
- Keep to the existing formatting.
- Indent with 4 space unless it's in a submodule.
- Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
## Translations
The translation effort for PolyMC is hosted on [Weblate](https://hosted.weblate.org/projects/polymc/polymc/) and information about translating PolyMC is available at https://github.com/PolyMC/Translations
## Download information
To modify download information or change packaging information send a pull request or issue to the website [Here](https://github.com/PolyMC/polymc.github.io/blob/master/src/download.md)
## Forking/Redistributing/Custom builds policy
Do whatever you want, we don't care. Just follow the license. If you have any questions about this feel free to ask in an issue.
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.
All launcher code is available under the GPL-3.0-only license. All launcher code is available under the GPL-3.0-only license.
The logo and related assets are under the CC BY-SA 4.0 license.

View File

@ -55,36 +55,23 @@ Config::Config()
// Version information // Version information
VERSION_MAJOR = @Launcher_VERSION_MAJOR@; VERSION_MAJOR = @Launcher_VERSION_MAJOR@;
VERSION_MINOR = @Launcher_VERSION_MINOR@; VERSION_MINOR = @Launcher_VERSION_MINOR@;
VERSION_HOTFIX = @Launcher_VERSION_HOTFIX@;
VERSION_BUILD = @Launcher_VERSION_BUILD@;
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@"; BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
BUILD_DATE = "@Launcher_BUILD_TIMESTAMP@";
UPDATER_BASE = "@Launcher_UPDATER_BASE@"; UPDATER_BASE = "@Launcher_UPDATER_BASE@";
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
if (BUILD_PLATFORM == "macOS" && !MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
{
UPDATER_ENABLED = true;
}
GIT_COMMIT = "@Launcher_GIT_COMMIT@"; GIT_COMMIT = "@Launcher_GIT_COMMIT@";
GIT_TAG = "@Launcher_GIT_TAG@";
GIT_REFSPEC = "@Launcher_GIT_REFSPEC@"; GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";
if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND"))
// Assume that builds outside of Git repos are "stable"
if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND")
|| GIT_TAG == QStringLiteral("GITDIR-NOTFOUND"))
{ {
GIT_REFSPEC = "refs/heads/stable"; VERSION_CHANNEL = QStringLiteral("stable");
GIT_TAG = versionString();
} }
else if(GIT_REFSPEC.startsWith("refs/heads/"))
if (GIT_REFSPEC.startsWith("refs/heads/"))
{ {
VERSION_CHANNEL = GIT_REFSPEC; VERSION_CHANNEL = GIT_REFSPEC;
VERSION_CHANNEL.remove("refs/heads/"); VERSION_CHANNEL.remove("refs/heads/");
if(!UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty()) { if(!UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty() && VERSION_BUILD >= 0) {
UPDATER_ENABLED = true; UPDATER_ENABLED = true;
} }
} }
@ -94,15 +81,16 @@ Config::Config()
} }
else else
{ {
VERSION_CHANNEL = "unknown"; VERSION_CHANNEL = QObject::tr("unknown");
} }
VERSION_STR = "@Launcher_VERSION_STRING@";
NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@"; NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@"; NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@";
HELP_URL = "@Launcher_HELP_URL@"; HELP_URL = "@Launcher_HELP_URL@";
IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@"; IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@"; MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@";
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@"; CURSEFORGE_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
META_URL = "@Launcher_META_URL@"; META_URL = "@Launcher_META_URL@";
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@"; BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
@ -112,19 +100,20 @@ Config::Config()
SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@"; SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@";
} }
QString Config::versionString() const
{
return QString("%1.%2").arg(VERSION_MAJOR).arg(VERSION_MINOR);
}
QString Config::printableVersionString() const QString Config::printableVersionString() const
{ {
QString vstr = versionString(); QString vstr = QString("%1.%2.%3").arg(QString::number(VERSION_MAJOR), QString::number(VERSION_MINOR), QString::number(VERSION_HOTFIX));
// If the build is not a main release, append the channel // If the build is not a main release, append the channel
if(VERSION_CHANNEL != "stable" && GIT_TAG != vstr) if(VERSION_CHANNEL != "stable")
{ {
vstr += "-" + VERSION_CHANNEL; vstr += "-" + VERSION_CHANNEL;
} }
// if a build number is set, also add it to the end
if(VERSION_BUILD >= 0)
{
vstr += "-" + QString::number(VERSION_BUILD);
}
return vstr; return vstr;
} }

View File

@ -55,6 +55,10 @@ class Config {
int VERSION_MAJOR; int VERSION_MAJOR;
/// The minor version number. /// The minor version number.
int VERSION_MINOR; int VERSION_MINOR;
/// The hotfix number.
int VERSION_HOTFIX;
/// The build number.
int VERSION_BUILD;
/** /**
* The version channel * The version channel
@ -67,18 +71,9 @@ class Config {
/// A short string identifying this build's platform. For example, "lin64" or "win32". /// A short string identifying this build's platform. For example, "lin64" or "win32".
QString BUILD_PLATFORM; QString BUILD_PLATFORM;
/// A string containing the build timestamp
QString BUILD_DATE;
/// URL for the updater's channel /// URL for the updater's channel
QString UPDATER_BASE; QString UPDATER_BASE;
/// The public key used to sign releases for the Sparkle updater appcast
QString MAC_SPARKLE_PUB_KEY;
/// URL for the Sparkle updater's appcast
QString MAC_SPARKLE_APPCAST_URL;
/// User-Agent to use. /// User-Agent to use.
QString USER_AGENT; QString USER_AGENT;
@ -88,12 +83,12 @@ class Config {
/// The git commit hash of this build /// The git commit hash of this build
QString GIT_COMMIT; QString GIT_COMMIT;
/// The git tag of this build
QString GIT_TAG;
/// The git refspec of this build /// The git refspec of this build
QString GIT_REFSPEC; QString GIT_REFSPEC;
/// This is printed on start to standard output
QString VERSION_STR;
/** /**
* This is used to fetch the news RSS feed. * This is used to fetch the news RSS feed.
* It defaults in CMakeLists.txt to "https://multimc.org/rss.xml" * It defaults in CMakeLists.txt to "https://multimc.org/rss.xml"
@ -123,7 +118,7 @@ class Config {
/** /**
* Client API key for CurseForge * Client API key for CurseForge
*/ */
QString FLAME_API_KEY; QString CURSEFORGE_API_KEY;
/** /**
* Metadata repository URL prefix * Metadata repository URL prefix
@ -140,8 +135,8 @@ class Config {
QString LIBRARY_BASE = "https://libraries.minecraft.net/"; QString LIBRARY_BASE = "https://libraries.minecraft.net/";
QString AUTH_BASE = "https://authserver.mojang.com/"; QString AUTH_BASE = "https://authserver.mojang.com/";
QString IMGUR_BASE_URL = "https://api.imgur.com/3/"; QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists QString FMLLIBS_BASE_URL = "https://files.polymc.org/fmllibs/";
QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists QString TRANSLATIONS_BASE_URL = "https://i18n.polymc.org/";
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/"; QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
@ -159,7 +154,6 @@ class Config {
QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2"; QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2";
QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2"; QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2";
QString versionString() const;
/** /**
* \brief Converts the Version to a string. * \brief Converts the Version to a string.
* \return The version number in string format (major.minor.revision.build). * \return The version number in string format (major.minor.revision.build).

View File

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

View File

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

View File

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

View File

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

View File

@ -40,9 +40,5 @@
<true/> <true/>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string> <string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>SUPublicEDKey</key>
<string>${MACOSX_SPARKLE_UPDATE_PUBLIC_KEY}</string>
<key>SUFeedURL</key>
<string>${MACOSX_SPARKLE_UPDATE_FEED_URL}</string>
</dict> </dict>
</plist> </plist>

14
cmake/QMakeQuery.cmake Normal file
View File

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

View File

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

View File

@ -1,97 +0,0 @@
#=============================================================================
# Copyright 2005-2011 Kitware, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# * Neither the name of Kitware, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#=============================================================================
# From Qt5CoreMacros.cmake
function(qt_generate_moc)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_generate_moc(${ARGV})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_generate_moc(${ARGV})
endif()
endfunction()
function(qt_wrap_cpp outfiles)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_wrap_cpp("${outfiles}" ${ARGN})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_wrap_cpp("${outfiles}" ${ARGN})
endif()
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
endfunction()
function(qt_add_binary_resources)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_add_binary_resources(${ARGV})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_add_binary_resources(${ARGV})
endif()
endfunction()
function(qt_add_resources outfiles)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_add_resources("${outfiles}" ${ARGN})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_add_resources("${outfiles}" ${ARGN})
endif()
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
endfunction()
function(qt_add_big_resources outfiles)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_add_big_resources(${outfiles} ${ARGN})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_add_big_resources(${outfiles} ${ARGN})
endif()
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
endfunction()
function(qt_import_plugins)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_import_plugins(${ARGV})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_import_plugins(${ARGV})
endif()
endfunction()
# From Qt5WidgetsMacros.cmake
function(qt_wrap_ui outfiles)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_wrap_ui("${outfiles}" ${ARGN})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_wrap_ui("${outfiles}" ${ARGN})
endif()
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
endfunction()

50
cmake/UnitTest.cmake Normal file
View File

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

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

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

View File

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

View File

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

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

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

35
flake.lock generated
View File

@ -19,26 +19,26 @@
"libnbtplusplus": { "libnbtplusplus": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1650031308, "lastModified": 1591558203,
"narHash": "sha256-TvVOjkUobYJD9itQYueELJX3wmecvEdCbJ0FinW2mL4=", "narHash": "sha256-QgvNvaoFflCXEPCCFBCeZvYTpuiwScBG7EosUgFwFNQ=",
"owner": "PrismLauncher", "owner": "multimc",
"repo": "libnbtplusplus", "repo": "libnbtplusplus",
"rev": "2203af7eeb48c45398139b583615134efd8d407f", "rev": "dc72a20b7efd304d12af2025223fad07b4b78464",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "PrismLauncher", "owner": "multimc",
"repo": "libnbtplusplus", "repo": "libnbtplusplus",
"type": "github" "type": "github"
} }
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1666057921, "lastModified": 1654665288,
"narHash": "sha256-VpQqtXdj6G7cH//SvoprjR7XT3KS7p+tCVebGK1N6tE=", "narHash": "sha256-7blJpfoZEu7GKb84uh3io/5eSJNdaagXD9d15P9iQMs=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "88eab1e431cabd0ed621428d8b40d425a07af39f", "rev": "43ecbe7840d155fa933ee8a500fb00dbbc651fc8",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -52,24 +52,7 @@
"inputs": { "inputs": {
"flake-compat": "flake-compat", "flake-compat": "flake-compat",
"libnbtplusplus": "libnbtplusplus", "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

@ -4,35 +4,36 @@
inputs = { inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
libnbtplusplus = { url = "github:PrismLauncher/libnbtplusplus"; flake = false; }; libnbtplusplus = { url = "github:multimc/libnbtplusplus"; flake = false; };
tomlplusplus = { url = "github:marzer/tomlplusplus"; flake = false; };
}; };
outputs = { self, nixpkgs, libnbtplusplus, tomlplusplus, ... }: outputs = { self, nixpkgs, libnbtplusplus, ... }:
let let
# User-friendly version number. # Generate a user-friendly version number.
version = builtins.substring 0 8 self.lastModifiedDate; version = builtins.substring 0 8 self.lastModifiedDate;
# Supported systems (qtbase is currently broken for "aarch64-darwin") # System types to support (qtbase is currently broken for "aarch64-darwin")
supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" ]; supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" ];
# Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'. # Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'.
forAllSystems = nixpkgs.lib.genAttrs supportedSystems; forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
# Nixpkgs instantiated for supported systems. # Nixpkgs instantiated for supported system types.
pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system}); pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system});
packagesFn = pkgs: rec {
prismlauncher = pkgs.libsForQt5.callPackage ./nix { inherit version self libnbtplusplus tomlplusplus; };
prismlauncher-qt6 = pkgs.qt6Packages.callPackage ./nix { inherit version self libnbtplusplus tomlplusplus; };
};
in in
{ {
packages = forAllSystems (system: packages = forAllSystems (system: rec {
let packages = packagesFn pkgs.${system}; in polymc = pkgs.${system}.libsForQt5.callPackage ./nix { inherit version self libnbtplusplus; };
packages // { default = packages.prismlauncher; } polymc-qt6 = pkgs.${system}.qt6Packages.callPackage ./nix { inherit version self libnbtplusplus; };
);
default = polymc;
});
overlay = final: packagesFn; defaultPackage = forAllSystems (system: self.packages.${system}.default);
apps = forAllSystems (system: rec { polymc = { type = "app"; program = "${self.defaultPackage.${system}}/bin/polymc"; }; default = polymc; });
defaultApp = forAllSystems (system: self.apps.${system}.default);
overlay = final: prev: { polymc = self.defaultPackage.${final.system}; };
}; };
} }

View File

@ -60,10 +60,6 @@
#include "ui/themes/BrightTheme.h" #include "ui/themes/BrightTheme.h"
#include "ui/themes/CustomTheme.h" #include "ui/themes/CustomTheme.h"
#ifdef Q_OS_WIN
#include "ui/WinDarkmode.h"
#endif
#include "ui/setupwizard/SetupWizard.h" #include "ui/setupwizard/SetupWizard.h"
#include "ui/setupwizard/LanguageWizardPage.h" #include "ui/setupwizard/LanguageWizardPage.h"
#include "ui/setupwizard/JavaWizardPage.h" #include "ui/setupwizard/JavaWizardPage.h"
@ -78,7 +74,6 @@
#include <iostream> #include <iostream>
#include <QAccessible> #include <QAccessible>
#include <QCommandLineParser>
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
@ -89,7 +84,6 @@
#include <QDebug> #include <QDebug>
#include <QStyleFactory> #include <QStyleFactory>
#include <QWindow> #include <QWindow>
#include <QIcon>
#include "InstanceList.h" #include "InstanceList.h"
@ -105,23 +99,20 @@
#include "tools/JVisualVM.h" #include "tools/JVisualVM.h"
#include "tools/MCEditTool.h" #include "tools/MCEditTool.h"
#include <xdgicon.h>
#include "settings/INISettingsObject.h" #include "settings/INISettingsObject.h"
#include "settings/Setting.h" #include "settings/Setting.h"
#include "translations/TranslationsModel.h" #include "translations/TranslationsModel.h"
#include "meta/Index.h" #include "meta/Index.h"
#include <Commandline.h>
#include <FileSystem.h> #include <FileSystem.h>
#include <DesktopServices.h> #include <DesktopServices.h>
#include <LocalPeer.h> #include <LocalPeer.h>
#include <sys.h> #include <sys.h>
#ifdef Q_OS_LINUX
#include <dlfcn.h>
#include "gamemode_client.h"
#endif
#if defined Q_OS_WIN32 #if defined Q_OS_WIN32
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
@ -136,6 +127,12 @@
static const QLatin1String liveCheckFile("live.check"); static const QLatin1String liveCheckFile("live.check");
using namespace Commandline;
#define MACOS_HINT "If you are on macOS Sierra, you might have to move the app to your /Applications or ~/Applications folder. "\
"This usually fixes the problem and you can move the application elsewhere afterwards.\n"\
"\n"
namespace { namespace {
void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{ {
@ -227,7 +224,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
setOrganizationName(BuildConfig.LAUNCHER_NAME); setOrganizationName(BuildConfig.LAUNCHER_NAME);
setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN); setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN);
setApplicationName(BuildConfig.LAUNCHER_NAME); setApplicationName(BuildConfig.LAUNCHER_NAME);
setApplicationDisplayName(QString("%1 %2").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString())); setApplicationDisplayName(BuildConfig.LAUNCHER_DISPLAYNAME);
setApplicationVersion(BuildConfig.printableVersionString()); setApplicationVersion(BuildConfig.printableVersionString());
setDesktopFileName(BuildConfig.LAUNCHER_DESKTOPFILENAME); setDesktopFileName(BuildConfig.LAUNCHER_DESKTOPFILENAME);
startTime = QDateTime::currentDateTime(); startTime = QDateTime::currentDateTime();
@ -236,27 +233,80 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
this->setQuitOnLastWindowClosed(false); this->setQuitOnLastWindowClosed(false);
// Commandline parsing // Commandline parsing
QCommandLineParser parser; QHash<QString, QVariant> args;
parser.setApplicationDescription(BuildConfig.LAUNCHER_DISPLAYNAME); {
Parser parser(FlagStyle::GNU, ArgumentStyle::SpaceAndEquals);
parser.addOptions({ // --help
{{"d", "dir"}, "Use a custom path as application root (use '.' for current directory)", "directory"}, parser.addSwitch("help");
{{"l", "launch"}, "Launch the specified instance (by instance ID)", "instance"}, parser.addShortOpt("help", 'h');
{{"s", "server"}, "Join the specified server on launch (only valid in combination with --launch)", "address"}, parser.addDocumentation("help", "Display this help and exit.");
{{"a", "profile"}, "Use the account specified by its profile name (only valid in combination with --launch)", "profile"}, // --version
{"alive", "Write a small '" + liveCheckFile + "' file after the launcher starts"}, parser.addSwitch("version");
{{"I", "import"}, "Import instance from specified zip (local path or URL)", "file"} parser.addShortOpt("version", 'V');
}); parser.addDocumentation("version", "Display program version and exit.");
parser.addHelpOption(); // --dir
parser.addVersionOption(); parser.addOption("dir");
parser.addShortOpt("dir", 'd');
parser.addDocumentation("dir", "Use the supplied folder as application root instead of the binary location (use '.' for current)");
// --launch
parser.addOption("launch");
parser.addShortOpt("launch", 'l');
parser.addDocumentation("launch", "Launch the specified instance (by instance ID)");
// --server
parser.addOption("server");
parser.addShortOpt("server", 's');
parser.addDocumentation("server", "Join the specified server on launch (only valid in combination with --launch)");
// --profile
parser.addOption("profile");
parser.addShortOpt("profile", 'a');
parser.addDocumentation("profile", "Use the account specified by its profile name (only valid in combination with --launch)");
// --alive
parser.addSwitch("alive");
parser.addDocumentation("alive", "Write a small '" + liveCheckFile + "' file after the launcher starts");
// --import
parser.addOption("import");
parser.addShortOpt("import", 'I');
parser.addDocumentation("import", "Import instance from specified zip (local path or URL)");
parser.process(arguments()); // parse the arguments
try
{
args = parser.parse(arguments());
}
catch (const ParsingError &e)
{
std::cerr << "CommandLineError: " << e.what() << std::endl;
if(argc > 0)
std::cerr << "Try '" << argv[0] << " -h' to get help on command line parameters."
<< std::endl;
m_status = Application::Failed;
return;
}
m_instanceIdToLaunch = parser.value("launch"); // display help and exit
m_serverToJoin = parser.value("server"); if (args["help"].toBool())
m_profileToUse = parser.value("profile"); {
m_liveCheck = parser.isSet("alive"); std::cout << qPrintable(parser.compileHelp(arguments()[0]));
m_zipToImport = parser.value("import"); m_status = Application::Succeeded;
return;
}
// display version and exit
if (args["version"].toBool())
{
std::cout << "Version " << BuildConfig.printableVersionString().toStdString() << std::endl;
std::cout << "Git " << BuildConfig.GIT_COMMIT.toStdString() << std::endl;
m_status = Application::Succeeded;
return;
}
}
m_instanceIdToLaunch = args["launch"].toString();
m_serverToJoin = args["server"].toString();
m_profileToUse = args["profile"].toString();
m_liveCheck = args["alive"].toBool();
m_zipToImport = args["import"].toUrl();
// error if --launch is missing with --server or --profile // error if --launch is missing with --server or --profile
if((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) if((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty())
@ -271,7 +321,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
{ {
// Root path is used for updates and portable data // Root path is used for updates and portable data
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
QDir foo(FS::PathCombine(binPath, "..")); // typically portable-root or /usr QDir foo(FS::PathCombine(binPath, "..")); // typically portable-root or /usr
m_rootPath = foo.absolutePath(); m_rootPath = foo.absolutePath();
#elif defined(Q_OS_WIN32) #elif defined(Q_OS_WIN32)
@ -282,12 +332,16 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
// on macOS, touch the root to force Finder to reload the .app metadata (and fix any icon change issues) // on macOS, touch the root to force Finder to reload the .app metadata (and fix any icon change issues)
FS::updateTimestamp(m_rootPath); FS::updateTimestamp(m_rootPath);
#endif #endif
#ifdef LAUNCHER_JARS_LOCATION
m_jarsPath = TOSTRING(LAUNCHER_JARS_LOCATION);
#endif
} }
QString adjustedBy; QString adjustedBy;
QString dataPath; QString dataPath;
// change folder // change folder
QString dirParam = parser.value("dir"); QString dirParam = args["dir"].toString();
if (!dirParam.isEmpty()) if (!dirParam.isEmpty())
{ {
// the dir param. it makes multimc data path point to whatever the user specified // the dir param. it makes multimc data path point to whatever the user specified
@ -301,12 +355,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
dataPath = foo.absolutePath(); dataPath = foo.absolutePath();
adjustedBy = "Persistent data path"; 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 #ifdef Q_OS_LINUX
// TODO: this should be removed in a future version // TODO: this should be removed in a future version
// TODO: provide a migration path similar to macOS migration // TODO: provide a migration path similar to macOS migration
@ -332,6 +380,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
QString( QString(
"The launcher data folder could not be created.\n" "The launcher data folder could not be created.\n"
"\n" "\n"
#if defined(Q_OS_MAC)
MACOS_HINT
#endif
"Make sure you have the right permissions to the launcher data folder and any folder needed to access it.\n" "Make sure you have the right permissions to the launcher data folder and any folder needed to access it.\n"
"(%1)\n" "(%1)\n"
"\n" "\n"
@ -347,6 +398,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
QString( QString(
"The launcher data folder could not be opened.\n" "The launcher data folder could not be opened.\n"
"\n" "\n"
#if defined(Q_OS_MAC)
MACOS_HINT
#endif
"Make sure you have the right permissions to the launcher data folder.\n" "Make sure you have the right permissions to the launcher data folder.\n"
"(%1)\n" "(%1)\n"
"\n" "\n"
@ -427,6 +481,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
QString( QString(
"The launcher couldn't create a log file - the data folder is not writable.\n" "The launcher couldn't create a log file - the data folder is not writable.\n"
"\n" "\n"
#if defined(Q_OS_MAC)
MACOS_HINT
#endif
"Make sure you have write permissions to the data folder.\n" "Make sure you have write permissions to the data folder.\n"
"(%1)\n" "(%1)\n"
"\n" "\n"
@ -488,10 +545,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
// Initialize application settings // Initialize application settings
{ {
// Provide a fallback for migration from PolyMC m_settings.reset(new INISettingsObject(BuildConfig.LAUNCHER_CONFIGFILE, this));
m_settings.reset(new INISettingsObject({ BuildConfig.LAUNCHER_CONFIGFILE, "polymc.cfg", "multimc.cfg" }, this));
// Updates // Updates
// Multiple channels are separated by spaces
m_settings->registerSetting("UpdateChannel", BuildConfig.VERSION_CHANNEL); m_settings->registerSetting("UpdateChannel", BuildConfig.VERSION_CHANNEL);
m_settings->registerSetting("AutoUpdate", true); m_settings->registerSetting("AutoUpdate", true);
@ -569,7 +624,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_settings->registerSetting("JavaPath", ""); m_settings->registerSetting("JavaPath", "");
m_settings->registerSetting("JavaTimestamp", 0); m_settings->registerSetting("JavaTimestamp", 0);
m_settings->registerSetting("JavaArchitecture", ""); m_settings->registerSetting("JavaArchitecture", "");
m_settings->registerSetting("JavaRealArchitecture", "");
m_settings->registerSetting("JavaVersion", ""); m_settings->registerSetting("JavaVersion", "");
m_settings->registerSetting("JavaVendor", ""); m_settings->registerSetting("JavaVendor", "");
m_settings->registerSetting("LastHostname", ""); m_settings->registerSetting("LastHostname", "");
@ -581,11 +635,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_settings->registerSetting("UseNativeOpenAL", false); m_settings->registerSetting("UseNativeOpenAL", false);
m_settings->registerSetting("UseNativeGLFW", false); m_settings->registerSetting("UseNativeGLFW", false);
// Peformance related options
m_settings->registerSetting("EnableFeralGamemode", false);
m_settings->registerSetting("EnableMangoHud", false);
m_settings->registerSetting("UseDiscreteGpu", false);
// Game time // Game time
m_settings->registerSetting("ShowGameTime", true); m_settings->registerSetting("ShowGameTime", true);
m_settings->registerSetting("ShowGlobalGameTime", true); m_settings->registerSetting("ShowGlobalGameTime", true);
@ -594,9 +643,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
// Minecraft launch method // Minecraft launch method
m_settings->registerSetting("MCLaunchMethod", "LauncherPart"); m_settings->registerSetting("MCLaunchMethod", "LauncherPart");
// Minecraft mods
m_settings->registerSetting("ModMetadataDisabled", false);
// Minecraft offline player name // Minecraft offline player name
m_settings->registerSetting("LastOfflinePlayerName", ""); m_settings->registerSetting("LastOfflinePlayerName", "");
@ -628,8 +674,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_settings->registerSetting("UpdateDialogGeometry", ""); m_settings->registerSetting("UpdateDialogGeometry", "");
m_settings->registerSetting("ModDownloadGeometry", "");
// HACK: This code feels so stupid is there a less stupid way of doing this? // HACK: This code feels so stupid is there a less stupid way of doing this?
{ {
m_settings->registerSetting("PastebinURL", ""); m_settings->registerSetting("PastebinURL", "");
@ -661,21 +705,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_settings->registerSetting("CloseAfterLaunch", false); m_settings->registerSetting("CloseAfterLaunch", false);
m_settings->registerSetting("QuitAfterGameStop", false); m_settings->registerSetting("QuitAfterGameStop", false);
// Custom Microsoft Authentication Client ID // Custom MSA credentials
m_settings->registerSetting("MSAClientIDOverride", ""); m_settings->registerSetting("MSAClientIDOverride", "");
m_settings->registerSetting("CFKeyOverride", "");
// Custom Flame API Key
{
m_settings->registerSetting("CFKeyOverride", "");
m_settings->registerSetting("FlameKeyOverride", "");
QString flameKey = m_settings->get("CFKeyOverride").toString();
if (!flameKey.isEmpty())
m_settings->set("FlameKeyOverride", flameKey);
m_settings->reset("CFKeyOverride");
}
m_settings->registerSetting("UserAgentOverride", "");
// Init page provider // Init page provider
{ {
@ -724,7 +756,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
auto platform = getIdealPlatform(BuildConfig.BUILD_PLATFORM); auto platform = getIdealPlatform(BuildConfig.BUILD_PLATFORM);
auto channelUrl = BuildConfig.UPDATER_BASE + platform + "/channels.json"; auto channelUrl = BuildConfig.UPDATER_BASE + platform + "/channels.json";
qDebug() << "Initializing updater with platform: " << platform << " -- " << channelUrl; qDebug() << "Initializing updater with platform: " << platform << " -- " << channelUrl;
m_updateChecker.reset(new UpdateChecker(m_network, channelUrl, BuildConfig.VERSION_CHANNEL)); m_updateChecker.reset(new UpdateChecker(m_network, channelUrl, BuildConfig.VERSION_CHANNEL, BuildConfig.VERSION_BUILD));
qDebug() << "<> Updater started."; qDebug() << "<> Updater started.";
} }
@ -814,9 +846,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_metacache->addBase("ModpacksCHPacks", QDir("cache/ModpacksCHPacks").absolutePath()); m_metacache->addBase("ModpacksCHPacks", QDir("cache/ModpacksCHPacks").absolutePath());
m_metacache->addBase("TechnicPacks", QDir("cache/TechnicPacks").absolutePath()); m_metacache->addBase("TechnicPacks", QDir("cache/TechnicPacks").absolutePath());
m_metacache->addBase("FlamePacks", QDir("cache/FlamePacks").absolutePath()); m_metacache->addBase("FlamePacks", QDir("cache/FlamePacks").absolutePath());
m_metacache->addBase("FlameMods", QDir("cache/FlameMods").absolutePath());
m_metacache->addBase("ModrinthPacks", QDir("cache/ModrinthPacks").absolutePath()); m_metacache->addBase("ModrinthPacks", QDir("cache/ModrinthPacks").absolutePath());
m_metacache->addBase("ModrinthModpacks", QDir("cache/ModrinthModpacks").absolutePath());
m_metacache->addBase("root", QDir::currentPath()); m_metacache->addBase("root", QDir::currentPath());
m_metacache->addBase("translations", QDir("translations").absolutePath()); m_metacache->addBase("translations", QDir("translations").absolutePath());
m_metacache->addBase("icons", QDir("cache/icons").absolutePath()); m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
@ -867,13 +897,10 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
qDebug() << "<> Application theme set."; qDebug() << "<> Application theme set.";
} }
updateCapabilities();
if(createSetupWizard()) if(createSetupWizard())
{ {
return; return;
} }
performMainStartupAction(); performMainStartupAction();
} }
@ -982,7 +1009,7 @@ void Application::performMainStartupAction()
qDebug() << " Launching with account" << m_profileToUse; qDebug() << " Launching with account" << m_profileToUse;
} }
launch(inst, true, false, nullptr, serverToJoin, accountToUse); launch(inst, true, nullptr, serverToJoin, accountToUse);
return; return;
} }
} }
@ -1086,7 +1113,6 @@ void Application::messageReceived(const QByteArray& message)
launch( launch(
instance, instance,
true, true,
false,
nullptr, nullptr,
serverObject, serverObject,
accountObject accountObject
@ -1141,15 +1167,6 @@ void Application::setApplicationTheme(const QString& name, bool initial)
{ {
auto & theme = (*themeIter).second; auto & theme = (*themeIter).second;
theme->apply(initial); theme->apply(initial);
#ifdef Q_OS_WIN
if (m_mainWindow) {
if (QString::compare(theme->id(), "dark") == 0) {
WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), true);
} else {
WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), false);
}
}
#endif
} }
else else
{ {
@ -1159,15 +1176,15 @@ void Application::setApplicationTheme(const QString& name, bool initial)
void Application::setIconTheme(const QString& name) void Application::setIconTheme(const QString& name)
{ {
QIcon::setThemeName(name); XdgIcon::setThemeName(name);
} }
QIcon Application::getThemedIcon(const QString& name) QIcon Application::getThemedIcon(const QString& name)
{ {
if(name == "logo") { if(name == "logo") {
return QIcon(":/org.prismlauncher.PrismLauncher.svg"); // FIXME: Make this a BuildConfig variable return QIcon(":/org.polymc.PolyMC.svg");
} }
return QIcon::fromTheme(name); return XdgIcon::fromTheme(name);
} }
bool Application::openJsonEditor(const QString &filename) bool Application::openJsonEditor(const QString &filename)
@ -1187,7 +1204,6 @@ bool Application::openJsonEditor(const QString &filename)
bool Application::launch( bool Application::launch(
InstancePtr instance, InstancePtr instance,
bool online, bool online,
bool demo,
BaseProfilerFactory *profiler, BaseProfilerFactory *profiler,
MinecraftServerTargetPtr serverToJoin, MinecraftServerTargetPtr serverToJoin,
MinecraftAccountPtr accountToUse MinecraftAccountPtr accountToUse
@ -1211,7 +1227,6 @@ bool Application::launch(
controller.reset(new LaunchController()); controller.reset(new LaunchController());
controller->setInstance(instance); controller->setInstance(instance);
controller->setOnline(online); controller->setOnline(online);
controller->setDemo(demo);
controller->setProfiler(profiler); controller->setProfiler(profiler);
controller->setServerToJoin(serverToJoin); controller->setServerToJoin(serverToJoin);
controller->setAccountToUse(accountToUse); controller->setAccountToUse(accountToUse);
@ -1225,9 +1240,6 @@ bool Application::launch(
} }
connect(controller.get(), &LaunchController::succeeded, this, &Application::controllerSucceeded); connect(controller.get(), &LaunchController::succeeded, this, &Application::controllerSucceeded);
connect(controller.get(), &LaunchController::failed, this, &Application::controllerFailed); connect(controller.get(), &LaunchController::failed, this, &Application::controllerFailed);
connect(controller.get(), &LaunchController::aborted, this, [this] {
controllerFailed(tr("Aborted"));
});
addRunningInstance(); addRunningInstance();
controller->start(); controller->start();
return true; return true;
@ -1382,13 +1394,6 @@ MainWindow* Application::showMainWindow(bool minimized)
m_mainWindow = new MainWindow(); m_mainWindow = new MainWindow();
m_mainWindow->restoreState(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowState").toByteArray())); m_mainWindow->restoreState(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowState").toByteArray()));
m_mainWindow->restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowGeometry").toByteArray())); m_mainWindow->restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowGeometry").toByteArray()));
#ifdef Q_OS_WIN
if (QString::compare(settings()->get("ApplicationTheme").toString(), "dark") == 0) {
WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), true);
} else {
WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), false);
}
#endif
if(minimized) if(minimized)
{ {
m_mainWindow->showMinimized(); m_mainWindow->showMinimized();
@ -1541,48 +1546,13 @@ shared_qobject_ptr<Meta::Index> Application::metadataIndex()
return m_metadataIndex; return m_metadataIndex;
} }
void Application::updateCapabilities() QString Application::getJarsPath()
{ {
m_capabilities = None; if(m_jarsPath.isEmpty())
if (!getMSAClientID().isEmpty())
m_capabilities |= SupportsMSA;
if (!getFlameAPIKey().isEmpty())
m_capabilities |= SupportsFlame;
#ifdef Q_OS_LINUX
if (gamemode_query_status() >= 0)
m_capabilities |= SupportsGameMode;
{ {
void *dummy = dlopen("libMangoHud_dlsym.so", RTLD_LAZY); return FS::PathCombine(QCoreApplication::applicationDirPath(), "jars");
// try normal variant as well
if (dummy == NULL)
dummy = dlopen("libMangoHud.so", RTLD_LAZY);
if (dummy != NULL) {
dlclose(dummy);
m_capabilities |= SupportsMangoHud;
}
} }
#endif return FS::PathCombine(m_rootPath, m_jarsPath);
}
QString Application::getJarPath(QString jarFile)
{
QStringList potentialPaths = {
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
FS::PathCombine(m_rootPath, "share/jars"),
#endif
FS::PathCombine(m_rootPath, "jars"),
FS::PathCombine(applicationDirPath(), "jars")
};
for(QString p : potentialPaths)
{
QString jarPath = FS::PathCombine(p, jarFile);
if (QFileInfo(jarPath).isFile())
return jarPath;
}
return {};
} }
QString Application::getMSAClientID() QString Application::getMSAClientID()
@ -1595,33 +1565,12 @@ QString Application::getMSAClientID()
return BuildConfig.MSA_CLIENT_ID; return BuildConfig.MSA_CLIENT_ID;
} }
QString Application::getFlameAPIKey() QString Application::getCurseKey()
{ {
QString keyOverride = m_settings->get("FlameKeyOverride").toString(); QString keyOverride = m_settings->get("CFKeyOverride").toString();
if (!keyOverride.isEmpty()) { if (!keyOverride.isEmpty()) {
return keyOverride; return keyOverride;
} }
return BuildConfig.FLAME_API_KEY; return BuildConfig.CURSEFORGE_API_KEY;
}
QString Application::getUserAgent()
{
QString uaOverride = m_settings->get("UserAgentOverride").toString();
if (!uaOverride.isEmpty()) {
return uaOverride.replace("$LAUNCHER_VER", BuildConfig.printableVersionString());
}
return BuildConfig.USER_AGENT;
}
QString Application::getUserAgentUncached()
{
QString uaOverride = m_settings->get("UserAgentOverride").toString();
if (!uaOverride.isEmpty()) {
uaOverride += " (Uncached)";
return uaOverride.replace("$LAUNCHER_VER", BuildConfig.printableVersionString());
}
return BuildConfig.USER_AGENT_UNCACHED;
} }

View File

@ -90,16 +90,6 @@ public:
Initialized Initialized
}; };
enum Capability {
None = 0,
SupportsMSA = 1 << 0,
SupportsFlame = 1 << 1,
SupportsGameMode = 1 << 2,
SupportsMangoHud = 1 << 3,
};
Q_DECLARE_FLAGS(Capabilities, Capability)
public: public:
Application(int &argc, char **argv); Application(int &argc, char **argv);
virtual ~Application(); virtual ~Application();
@ -164,28 +154,16 @@ public:
shared_qobject_ptr<Meta::Index> metadataIndex(); shared_qobject_ptr<Meta::Index> metadataIndex();
void updateCapabilities(); QString getJarsPath();
/*!
* Finds and returns the full path to a jar file.
* Returns a null-string if it could not be found.
*/
QString getJarPath(QString jarFile);
QString getMSAClientID(); QString getMSAClientID();
QString getFlameAPIKey(); QString getCurseKey();
QString getUserAgent();
QString getUserAgentUncached();
/// this is the root of the 'installation'. Used for automatic updates /// this is the root of the 'installation'. Used for automatic updates
const QString &root() { const QString &root() {
return m_rootPath; return m_rootPath;
} }
const Capabilities capabilities() {
return m_capabilities;
}
/*! /*!
* Opens a json file using either a system default editor, or, if not empty, the editor * Opens a json file using either a system default editor, or, if not empty, the editor
* specified in the settings * specified in the settings
@ -213,7 +191,6 @@ public slots:
bool launch( bool launch(
InstancePtr instance, InstancePtr instance,
bool online = true, bool online = true,
bool demo = false,
BaseProfilerFactory *profiler = nullptr, BaseProfilerFactory *profiler = nullptr,
MinecraftServerTargetPtr serverToJoin = nullptr, MinecraftServerTargetPtr serverToJoin = nullptr,
MinecraftAccountPtr accountToUse = nullptr MinecraftAccountPtr accountToUse = nullptr
@ -259,13 +236,13 @@ private:
std::shared_ptr<GenericPageProvider> m_globalSettingsProvider; std::shared_ptr<GenericPageProvider> m_globalSettingsProvider;
std::map<QString, std::unique_ptr<ITheme>> m_themes; std::map<QString, std::unique_ptr<ITheme>> m_themes;
std::unique_ptr<MCEditTool> m_mcedit; std::unique_ptr<MCEditTool> m_mcedit;
QString m_jarsPath;
QSet<QString> m_features; QSet<QString> m_features;
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers; QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
QString m_rootPath; QString m_rootPath;
Status m_status = Application::StartingUp; Status m_status = Application::StartingUp;
Capabilities m_capabilities;
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive; Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive;

View File

@ -1,47 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ApplicationMessage.h" #include "ApplicationMessage.h"
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include "Json.h"
void ApplicationMessage::parse(const QByteArray & input) { void ApplicationMessage::parse(const QByteArray & input) {
auto doc = Json::requireDocument(input, "ApplicationMessage"); auto doc = QJsonDocument::fromBinaryData(input);
auto root = Json::requireObject(doc, "ApplicationMessage"); auto root = doc.object();
command = root.value("command").toString(); command = root.value("command").toString();
args.clear(); args.clear();
@ -61,5 +25,7 @@ QByteArray ApplicationMessage::serialize() {
} }
root.insert("args", outArgs); root.insert("args", outArgs);
return Json::toText(root); QJsonDocument out;
out.setObject(root);
return out.toBinaryData();
} }

View File

@ -2,7 +2,6 @@
/* /*
* PolyMC - Minecraft Launcher * PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -39,7 +38,6 @@
#include <QFileInfo> #include <QFileInfo>
#include <QDir> #include <QDir>
#include <QDebug> #include <QDebug>
#include <QRegularExpression>
#include "settings/INISettingsObject.h" #include "settings/INISettingsObject.h"
#include "settings/Setting.h" #include "settings/Setting.h"
@ -53,22 +51,15 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
: QObject() : QObject()
{ {
m_settings = settings; m_settings = settings;
m_global_settings = globalSettings;
m_rootDir = rootDir; m_rootDir = rootDir;
m_settings->registerSetting("name", "Unnamed Instance"); m_settings->registerSetting("name", "Unnamed Instance");
m_settings->registerSetting("iconKey", "default"); m_settings->registerSetting("iconKey", "default");
m_settings->registerSetting("notes", ""); m_settings->registerSetting("notes", "");
m_settings->registerSetting("lastLaunchTime", 0); m_settings->registerSetting("lastLaunchTime", 0);
m_settings->registerSetting("totalTimePlayed", 0); m_settings->registerSetting("totalTimePlayed", 0);
m_settings->registerSetting("lastTimePlayed", 0); m_settings->registerSetting("lastTimePlayed", 0);
// Game time override
auto gameTimeOverride = m_settings->registerSetting("OverrideGameTime", false);
m_settings->registerOverride(globalSettings->getSetting("ShowGameTime"), gameTimeOverride);
m_settings->registerOverride(globalSettings->getSetting("RecordGameTime"), gameTimeOverride);
// NOTE: Sometimees InstanceType is already registered, as it was used to identify the type of // NOTE: Sometimees InstanceType is already registered, as it was used to identify the type of
// a locally stored instance // a locally stored instance
if (!m_settings->getSetting("InstanceType")) if (!m_settings->getSetting("InstanceType"))
@ -89,14 +80,6 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
m_settings->registerPassthrough(globalSettings->getSetting("ConsoleMaxLines"), nullptr); m_settings->registerPassthrough(globalSettings->getSetting("ConsoleMaxLines"), nullptr);
m_settings->registerPassthrough(globalSettings->getSetting("ConsoleOverflowStop"), nullptr); m_settings->registerPassthrough(globalSettings->getSetting("ConsoleOverflowStop"), nullptr);
// Managed Packs
m_settings->registerSetting("ManagedPack", false);
m_settings->registerSetting("ManagedPackType", "");
m_settings->registerSetting("ManagedPackID", "");
m_settings->registerSetting("ManagedPackName", "");
m_settings->registerSetting("ManagedPackVersionID", "");
m_settings->registerSetting("ManagedPackVersionName", "");
} }
QString BaseInstance::getPreLaunchCommand() QString BaseInstance::getPreLaunchCommand()
@ -114,59 +97,9 @@ QString BaseInstance::getPostExitCommand()
return settings()->get("PostExitCommand").toString(); return settings()->get("PostExitCommand").toString();
} }
bool BaseInstance::isManagedPack() const
{
return m_settings->get("ManagedPack").toBool();
}
QString BaseInstance::getManagedPackType() const
{
return m_settings->get("ManagedPackType").toString();
}
QString BaseInstance::getManagedPackID() const
{
return m_settings->get("ManagedPackID").toString();
}
QString BaseInstance::getManagedPackName() const
{
return m_settings->get("ManagedPackName").toString();
}
QString BaseInstance::getManagedPackVersionID() const
{
return m_settings->get("ManagedPackVersionID").toString();
}
QString BaseInstance::getManagedPackVersionName() const
{
return m_settings->get("ManagedPackVersionName").toString();
}
void BaseInstance::setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version)
{
m_settings->set("ManagedPack", true);
m_settings->set("ManagedPackType", type);
m_settings->set("ManagedPackID", id);
m_settings->set("ManagedPackName", name);
m_settings->set("ManagedPackVersionID", versionId);
m_settings->set("ManagedPackVersionName", version);
}
void BaseInstance::copyManagedPack(BaseInstance& other)
{
m_settings->set("ManagedPack", other.isManagedPack());
m_settings->set("ManagedPackType", other.getManagedPackType());
m_settings->set("ManagedPackID", other.getManagedPackID());
m_settings->set("ManagedPackName", other.getManagedPackName());
m_settings->set("ManagedPackVersionID", other.getManagedPackVersionID());
m_settings->set("ManagedPackVersionName", other.getManagedPackVersionName());
}
int BaseInstance::getConsoleMaxLines() const int BaseInstance::getConsoleMaxLines() const
{ {
auto lineSetting = m_settings->getSetting("ConsoleMaxLines"); auto lineSetting = settings()->getSetting("ConsoleMaxLines");
bool conversionOk = false; bool conversionOk = false;
int maxLines = lineSetting->get().toInt(&conversionOk); int maxLines = lineSetting->get().toInt(&conversionOk);
if(!conversionOk) if(!conversionOk)
@ -179,7 +112,7 @@ int BaseInstance::getConsoleMaxLines() const
bool BaseInstance::shouldStopOnConsoleOverflow() const bool BaseInstance::shouldStopOnConsoleOverflow() const
{ {
return m_settings->get("ConsoleOverflowStop").toBool(); return settings()->get("ConsoleOverflowStop").toBool();
} }
void BaseInstance::iconUpdated(QString key) void BaseInstance::iconUpdated(QString key)
@ -254,7 +187,7 @@ void BaseInstance::setRunning(bool running)
int64_t BaseInstance::totalTimePlayed() const int64_t BaseInstance::totalTimePlayed() const
{ {
qint64 current = m_settings->get("totalTimePlayed").toLongLong(); qint64 current = settings()->get("totalTimePlayed").toLongLong();
if(m_isRunning) if(m_isRunning)
{ {
QDateTime timeNow = QDateTime::currentDateTime(); QDateTime timeNow = QDateTime::currentDateTime();
@ -270,7 +203,7 @@ int64_t BaseInstance::lastTimePlayed() const
QDateTime timeNow = QDateTime::currentDateTime(); QDateTime timeNow = QDateTime::currentDateTime();
return m_timeStarted.secsTo(timeNow); return m_timeStarted.secsTo(timeNow);
} }
return m_settings->get("lastTimePlayed").toLongLong(); return settings()->get("lastTimePlayed").toLongLong();
} }
void BaseInstance::resetTimePlayed() void BaseInstance::resetTimePlayed()
@ -289,10 +222,8 @@ QString BaseInstance::instanceRoot() const
return m_rootDir; return m_rootDir;
} }
SettingsObjectPtr BaseInstance::settings() SettingsObjectPtr BaseInstance::settings() const
{ {
loadSpecificSettings();
return m_settings; return m_settings;
} }
@ -355,11 +286,11 @@ QString BaseInstance::name() const
QString BaseInstance::windowTitle() const QString BaseInstance::windowTitle() const
{ {
return BuildConfig.LAUNCHER_DISPLAYNAME + ": " + name().replace(QRegularExpression("\\s+"), " "); return BuildConfig.LAUNCHER_NAME + ": " + name().replace(QRegExp("[ \n\r\t]+"), " ");
} }
// FIXME: why is this here? move it to MinecraftInstance!!! // FIXME: why is this here? move it to MinecraftInstance!!!
QStringList BaseInstance::extraArguments() QStringList BaseInstance::extraArguments() const
{ {
return Commandline::splitArgs(settings()->get("JvmArgs").toString()); return Commandline::splitArgs(settings()->get("JvmArgs").toString());
} }
@ -368,8 +299,3 @@ shared_qobject_ptr<LaunchTask> BaseInstance::getLaunchTask()
{ {
return m_launchProcess; return m_launchProcess;
} }
void BaseInstance::updateRuntimeContext()
{
// NOOP
}

View File

@ -2,7 +2,6 @@
/* /*
* PolyMC - Minecraft Launcher * PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -54,7 +53,6 @@
#include "net/Mode.h" #include "net/Mode.h"
#include "minecraft/launch/MinecraftServerTarget.h" #include "minecraft/launch/MinecraftServerTarget.h"
#include "RuntimeContext.h"
class QDir; class QDir;
class Task; class Task;
@ -141,22 +139,13 @@ public:
QString getPostExitCommand(); QString getPostExitCommand();
QString getWrapperCommand(); QString getWrapperCommand();
bool isManagedPack() const;
QString getManagedPackType() const;
QString getManagedPackID() const;
QString getManagedPackName() const;
QString getManagedPackVersionID() const;
QString getManagedPackVersionName() const;
void setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version);
void copyManagedPack(BaseInstance& other);
/// guess log level from a line of game log /// guess log level from a line of game log
virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level) virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level)
{ {
return level; return level;
}; };
virtual QStringList extraArguments(); virtual QStringList extraArguments() const;
/// Traits. Normally inside the version, depends on instance implementation. /// Traits. Normally inside the version, depends on instance implementation.
virtual QSet <QString> traits() const = 0; virtual QSet <QString> traits() const = 0;
@ -172,18 +161,9 @@ public:
/*! /*!
* \brief Gets this instance's settings object. * \brief Gets this instance's settings object.
* This settings object stores instance-specific settings. * This settings object stores instance-specific settings.
*
* Note that this method is not const.
* It may call loadSpecificSettings() to ensure those are loaded.
*
* \return A pointer to this instance's settings object. * \return A pointer to this instance's settings object.
*/ */
virtual SettingsObjectPtr settings(); virtual SettingsObjectPtr settings() const;
/*!
* \brief Loads settings specific to an instance type if they're not already loaded.
*/
virtual void loadSpecificSettings() = 0;
/// returns a valid update task /// returns a valid update task
virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0; virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0;
@ -199,7 +179,6 @@ public:
* Create envrironment variables for running the instance * Create envrironment variables for running the instance
*/ */
virtual QProcessEnvironment createEnvironment() = 0; virtual QProcessEnvironment createEnvironment() = 0;
virtual QProcessEnvironment createLaunchEnvironment() = 0;
/*! /*!
* Returns a matcher that can maps relative paths within the instance to whether they are 'log files' * Returns a matcher that can maps relative paths within the instance to whether they are 'log files'
@ -217,16 +196,10 @@ public:
virtual QString instanceConfigFolder() const = 0; virtual QString instanceConfigFolder() const = 0;
/// get variables this instance exports /// get variables this instance exports
virtual QMap<QString, QString> getVariables() = 0; virtual QMap<QString, QString> getVariables() const = 0;
virtual QString typeName() const = 0; virtual QString typeName() const = 0;
void updateRuntimeContext();
RuntimeContext runtimeContext() const
{
return m_runtimeContext;
}
bool hasVersionBroken() const bool hasVersionBroken() const
{ {
return m_hasBrokenVersion; return m_hasBrokenVersion;
@ -285,11 +258,6 @@ public:
protected: protected:
void changeStatus(Status newStatus); void changeStatus(Status newStatus);
SettingsObjectPtr globalSettings() const { return m_global_settings.lock(); };
bool isSpecificSettingsLoaded() const { return m_specific_settings_loaded; }
void setSpecificSettingsLoaded(bool loaded) { m_specific_settings_loaded = loaded; }
signals: signals:
/*! /*!
* \brief Signal emitted when properties relevant to the instance view change * \brief Signal emitted when properties relevant to the instance view change
@ -312,17 +280,12 @@ protected: /* data */
bool m_isRunning = false; bool m_isRunning = false;
shared_qobject_ptr<LaunchTask> m_launchProcess; shared_qobject_ptr<LaunchTask> m_launchProcess;
QDateTime m_timeStarted; QDateTime m_timeStarted;
RuntimeContext m_runtimeContext;
private: /* data */ private: /* data */
Status m_status = Status::Present; Status m_status = Status::Present;
bool m_crashed = false; bool m_crashed = false;
bool m_hasUpdate = false; bool m_hasUpdate = false;
bool m_hasBrokenVersion = false; bool m_hasBrokenVersion = false;
SettingsObjectWeakPtr m_global_settings;
bool m_specific_settings_loaded = false;
}; };
Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>) Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>)

View File

@ -1,36 +1,16 @@
// SPDX-License-Identifier: GPL-3.0-only /* Copyright 2013-2021 MultiMC Contributors
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * Licensed under the Apache License, Version 2.0 (the "License");
* it under the terms of the GNU General Public License as published by * you may not use this file except in compliance with the License.
* the Free Software Foundation, version 3. * You may obtain a copy of the License at
* *
* This program is distributed in the hope that it will be useful, * http://www.apache.org/licenses/LICENSE-2.0
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * Unless required by applicable law or agreed to in writing, software
* along with this program. If not, see <https://www.gnu.org/licenses/>. * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* This file incorporates work covered by the following copyright and * See the License for the specific language governing permissions and
* permission notice: * limitations under the License.
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
#include "BaseVersionList.h" #include "BaseVersionList.h"
@ -71,7 +51,7 @@ QVariant BaseVersionList::data(const QModelIndex &index, int role) const
switch (role) switch (role)
{ {
case VersionPointerRole: case VersionPointerRole:
return QVariant::fromValue(version); return qVariantFromValue(version);
case VersionRole: case VersionRole:
return version->name(); return version->name();

View File

@ -4,6 +4,8 @@ project(application)
######## Sources and headers ######## ######## Sources and headers ########
include (UnitTest)
set(CORE_SOURCES set(CORE_SOURCES
# LOGIC - Base classes and infrastructure # LOGIC - Base classes and infrastructure
BaseInstaller.h BaseInstaller.h
@ -26,7 +28,6 @@ set(CORE_SOURCES
MMCZip.cpp MMCZip.cpp
MMCStrings.h MMCStrings.h
MMCStrings.cpp MMCStrings.cpp
RuntimeContext.h
# Basic instance manipulation tasks (derived from InstanceTask) # Basic instance manipulation tasks (derived from InstanceTask)
InstanceCreationTask.h InstanceCreationTask.h
@ -89,6 +90,17 @@ set(CORE_SOURCES
MMCTime.cpp MMCTime.cpp
) )
add_unit_test(FileSystem
SOURCES FileSystem_test.cpp
LIBS Launcher_logic
DATA testdata
)
add_unit_test(GZip
SOURCES GZip_test.cpp
LIBS Launcher_logic
)
set(PATHMATCHER_SOURCES set(PATHMATCHER_SOURCES
# Path matchers # Path matchers
pathmatcher/FSTreeMatcher.h pathmatcher/FSTreeMatcher.h
@ -112,7 +124,6 @@ set(NET_SOURCES
net/NetAction.h net/NetAction.h
net/NetJob.cpp net/NetJob.cpp
net/NetJob.h net/NetJob.h
net/NetUtils.h
net/PasteUpload.cpp net/PasteUpload.cpp
net/PasteUpload.h net/PasteUpload.h
net/Sink.h net/Sink.h
@ -153,13 +164,19 @@ set(UPDATE_SOURCES
updater/UpdateChecker.cpp updater/UpdateChecker.cpp
updater/DownloadTask.h updater/DownloadTask.h
updater/DownloadTask.cpp updater/DownloadTask.cpp
updater/ExternalUpdater.h
) )
set(MAC_UPDATE_SOURCES add_unit_test(UpdateChecker
updater/MacSparkleUpdater.h SOURCES updater/UpdateChecker_test.cpp
updater/MacSparkleUpdater.mm LIBS Launcher_logic
) DATA updater/testdata
)
add_unit_test(DownloadTask
SOURCES updater/DownloadTask_test.cpp
LIBS Launcher_logic
DATA updater/testdata
)
# Backend for the news bar... there's usually no news. # Backend for the news bar... there's usually no news.
set(NEWS_SOURCES set(NEWS_SOURCES
@ -289,6 +306,8 @@ set(MINECRAFT_SOURCES
minecraft/Rule.h minecraft/Rule.h
minecraft/OneSixVersionFormat.cpp minecraft/OneSixVersionFormat.cpp
minecraft/OneSixVersionFormat.h minecraft/OneSixVersionFormat.h
minecraft/OpSys.cpp
minecraft/OpSys.h
minecraft/ParseUtils.cpp minecraft/ParseUtils.cpp
minecraft/ParseUtils.h minecraft/ParseUtils.h
minecraft/ProfileUtils.cpp minecraft/ProfileUtils.cpp
@ -296,8 +315,6 @@ set(MINECRAFT_SOURCES
minecraft/Library.cpp minecraft/Library.cpp
minecraft/Library.h minecraft/Library.h
minecraft/MojangDownloadInfo.h minecraft/MojangDownloadInfo.h
minecraft/VanillaInstanceCreationTask.cpp
minecraft/VanillaInstanceCreationTask.h
minecraft/VersionFile.cpp minecraft/VersionFile.cpp
minecraft/VersionFile.h minecraft/VersionFile.h
minecraft/VersionFilterData.h minecraft/VersionFilterData.h
@ -307,36 +324,19 @@ set(MINECRAFT_SOURCES
minecraft/WorldList.h minecraft/WorldList.h
minecraft/WorldList.cpp minecraft/WorldList.cpp
minecraft/mod/MetadataHandler.h
minecraft/mod/Mod.h minecraft/mod/Mod.h
minecraft/mod/Mod.cpp minecraft/mod/Mod.cpp
minecraft/mod/ModDetails.h minecraft/mod/ModDetails.h
minecraft/mod/ModFolderModel.h minecraft/mod/ModFolderModel.h
minecraft/mod/ModFolderModel.cpp minecraft/mod/ModFolderModel.cpp
minecraft/mod/Resource.h minecraft/mod/ModFolderLoadTask.h
minecraft/mod/Resource.cpp minecraft/mod/ModFolderLoadTask.cpp
minecraft/mod/ResourceFolderModel.h minecraft/mod/LocalModParseTask.h
minecraft/mod/ResourceFolderModel.cpp minecraft/mod/LocalModParseTask.cpp
minecraft/mod/ResourcePack.h
minecraft/mod/ResourcePack.cpp
minecraft/mod/ResourcePackFolderModel.h minecraft/mod/ResourcePackFolderModel.h
minecraft/mod/ResourcePackFolderModel.cpp minecraft/mod/ResourcePackFolderModel.cpp
minecraft/mod/TexturePack.h
minecraft/mod/TexturePack.cpp
minecraft/mod/TexturePackFolderModel.h minecraft/mod/TexturePackFolderModel.h
minecraft/mod/TexturePackFolderModel.cpp minecraft/mod/TexturePackFolderModel.cpp
minecraft/mod/ShaderPackFolderModel.h
minecraft/mod/tasks/BasicFolderLoadTask.h
minecraft/mod/tasks/ModFolderLoadTask.h
minecraft/mod/tasks/ModFolderLoadTask.cpp
minecraft/mod/tasks/LocalModParseTask.h
minecraft/mod/tasks/LocalModParseTask.cpp
minecraft/mod/tasks/LocalModUpdateTask.h
minecraft/mod/tasks/LocalModUpdateTask.cpp
minecraft/mod/tasks/LocalResourcePackParseTask.h
minecraft/mod/tasks/LocalResourcePackParseTask.cpp
minecraft/mod/tasks/LocalTexturePackParseTask.h
minecraft/mod/tasks/LocalTexturePackParseTask.cpp
# Assets # Assets
minecraft/AssetsUtils.h minecraft/AssetsUtils.h
@ -354,6 +354,52 @@ set(MINECRAFT_SOURCES
mojang/PackageManifest.cpp mojang/PackageManifest.cpp
minecraft/Agent.h) minecraft/Agent.h)
add_unit_test(GradleSpecifier
SOURCES minecraft/GradleSpecifier_test.cpp
LIBS Launcher_logic
)
if(BUILD_TESTING)
add_executable(PackageManifest
mojang/PackageManifest_test.cpp
)
target_link_libraries(PackageManifest
Launcher_logic
Qt5::Test
)
target_include_directories(PackageManifest
PRIVATE ../cmake/UnitTest/
)
add_test(
NAME PackageManifest
COMMAND PackageManifest
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
endif()
add_unit_test(MojangVersionFormat
SOURCES minecraft/MojangVersionFormat_test.cpp
LIBS Launcher_logic
DATA minecraft/testdata
)
add_unit_test(Library
SOURCES minecraft/Library_test.cpp
LIBS Launcher_logic
)
# FIXME: shares data with FileSystem test
add_unit_test(ModFolderModel
SOURCES minecraft/mod/ModFolderModel_test.cpp
DATA testdata
LIBS Launcher_logic
)
add_unit_test(ParseUtils
SOURCES minecraft/ParseUtils_test.cpp
LIBS Launcher_logic
)
# the screenshots feature # the screenshots feature
set(SCREENSHOTS_SOURCES set(SCREENSHOTS_SOURCES
screenshots/Screenshot.h screenshots/Screenshot.h
@ -367,14 +413,15 @@ set(TASKS_SOURCES
# Tasks # Tasks
tasks/Task.h tasks/Task.h
tasks/Task.cpp tasks/Task.cpp
tasks/ConcurrentTask.h
tasks/ConcurrentTask.cpp
tasks/SequentialTask.h tasks/SequentialTask.h
tasks/SequentialTask.cpp tasks/SequentialTask.cpp
tasks/MultipleOptionsTask.h
tasks/MultipleOptionsTask.cpp
) )
add_unit_test(Task
SOURCES tasks/Task_test.cpp
LIBS Launcher_logic
)
set(SETTINGS_SOURCES set(SETTINGS_SOURCES
# Settings # Settings
settings/INIFile.cpp settings/INIFile.cpp
@ -391,6 +438,11 @@ set(SETTINGS_SOURCES
settings/SettingsObject.h settings/SettingsObject.h
) )
add_unit_test(INIFile
SOURCES settings/INIFile_test.cpp
LIBS Launcher_logic
)
set(JAVA_SOURCES set(JAVA_SOURCES
java/JavaChecker.h java/JavaChecker.h
java/JavaChecker.cpp java/JavaChecker.cpp
@ -406,6 +458,11 @@ set(JAVA_SOURCES
java/JavaVersion.cpp java/JavaVersion.cpp
) )
add_unit_test(JavaVersion
SOURCES java/JavaVersion_test.cpp
LIBS Launcher_logic
)
set(TRANSLATIONS_SOURCES set(TRANSLATIONS_SOURCES
translations/TranslationsModel.h translations/TranslationsModel.h
translations/TranslationsModel.cpp translations/TranslationsModel.cpp
@ -442,26 +499,13 @@ set(META_SOURCES
) )
set(API_SOURCES set(API_SOURCES
modplatform/ModIndex.h
modplatform/ModIndex.cpp
modplatform/ModAPI.h modplatform/ModAPI.h
modplatform/EnsureMetadataTask.h
modplatform/EnsureMetadataTask.cpp
modplatform/CheckUpdateTask.h
modplatform/flame/FlameAPI.h modplatform/flame/FlameAPI.h
modplatform/flame/FlameAPI.cpp
modplatform/modrinth/ModrinthAPI.h modplatform/modrinth/ModrinthAPI.h
modplatform/modrinth/ModrinthAPI.cpp
modplatform/helpers/NetworkModAPI.h modplatform/helpers/NetworkModAPI.h
modplatform/helpers/NetworkModAPI.cpp modplatform/helpers/NetworkModAPI.cpp
modplatform/helpers/HashUtils.h
modplatform/helpers/HashUtils.cpp
modplatform/helpers/OverrideUtils.h
modplatform/helpers/OverrideUtils.cpp
) )
set(FTB_SOURCES set(FTB_SOURCES
@ -485,10 +529,6 @@ set(FLAME_SOURCES
modplatform/flame/PackManifest.cpp modplatform/flame/PackManifest.cpp
modplatform/flame/FileResolvingTask.h modplatform/flame/FileResolvingTask.h
modplatform/flame/FileResolvingTask.cpp modplatform/flame/FileResolvingTask.cpp
modplatform/flame/FlameCheckUpdate.cpp
modplatform/flame/FlameCheckUpdate.h
modplatform/flame/FlameInstanceCreationTask.h
modplatform/flame/FlameInstanceCreationTask.cpp
) )
set(MODRINTH_SOURCES set(MODRINTH_SOURCES
@ -496,10 +536,6 @@ set(MODRINTH_SOURCES
modplatform/modrinth/ModrinthPackIndex.h modplatform/modrinth/ModrinthPackIndex.h
modplatform/modrinth/ModrinthPackManifest.cpp modplatform/modrinth/ModrinthPackManifest.cpp
modplatform/modrinth/ModrinthPackManifest.h modplatform/modrinth/ModrinthPackManifest.h
modplatform/modrinth/ModrinthCheckUpdate.cpp
modplatform/modrinth/ModrinthCheckUpdate.h
modplatform/modrinth/ModrinthInstanceCreationTask.cpp
modplatform/modrinth/ModrinthInstanceCreationTask.h
) )
set(MODPACKSCH_SOURCES set(MODPACKSCH_SOURCES
@ -509,12 +545,6 @@ set(MODPACKSCH_SOURCES
modplatform/modpacksch/FTBPackManifest.cpp modplatform/modpacksch/FTBPackManifest.cpp
) )
set(PACKWIZ_SOURCES
modplatform/packwiz/Packwiz.h
modplatform/packwiz/Packwiz.cpp
)
set(TECHNIC_SOURCES set(TECHNIC_SOURCES
modplatform/technic/SingleZipPackInstallTask.h modplatform/technic/SingleZipPackInstallTask.h
modplatform/technic/SingleZipPackInstallTask.cpp modplatform/technic/SingleZipPackInstallTask.cpp
@ -537,6 +567,11 @@ set(ATLAUNCHER_SOURCES
modplatform/atlauncher/ATLShareCode.h modplatform/atlauncher/ATLShareCode.h
) )
add_unit_test(Index
SOURCES meta/Index_test.cpp
LIBS Launcher_logic
)
################################ COMPILE ################################ ################################ COMPILE ################################
# we need zlib # we need zlib
@ -563,15 +598,10 @@ set(LOGIC_SOURCES
${FLAME_SOURCES} ${FLAME_SOURCES}
${MODRINTH_SOURCES} ${MODRINTH_SOURCES}
${MODPACKSCH_SOURCES} ${MODPACKSCH_SOURCES}
${PACKWIZ_SOURCES}
${TECHNIC_SOURCES} ${TECHNIC_SOURCES}
${ATLAUNCHER_SOURCES} ${ATLAUNCHER_SOURCES}
) )
if(APPLE)
set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES})
endif()
SET(LAUNCHER_SOURCES SET(LAUNCHER_SOURCES
# Application base # Application base
Application.h Application.h
@ -667,8 +697,6 @@ SET(LAUNCHER_SOURCES
ui/pages/BasePageProvider.h ui/pages/BasePageProvider.h
# GUI - instance pages # GUI - instance pages
ui/pages/instance/ExternalResourcesPage.cpp
ui/pages/instance/ExternalResourcesPage.h
ui/pages/instance/GameOptionsPage.cpp ui/pages/instance/GameOptionsPage.cpp
ui/pages/instance/GameOptionsPage.h ui/pages/instance/GameOptionsPage.h
ui/pages/instance/VersionPage.cpp ui/pages/instance/VersionPage.cpp
@ -730,8 +758,6 @@ SET(LAUNCHER_SOURCES
ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h
ui/pages/modplatform/atlauncher/AtlPage.cpp ui/pages/modplatform/atlauncher/AtlPage.cpp
ui/pages/modplatform/atlauncher/AtlPage.h ui/pages/modplatform/atlauncher/AtlPage.h
ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.cpp
ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.h
ui/pages/modplatform/ftb/FtbFilterModel.cpp ui/pages/modplatform/ftb/FtbFilterModel.cpp
ui/pages/modplatform/ftb/FtbFilterModel.h ui/pages/modplatform/ftb/FtbFilterModel.h
@ -799,8 +825,6 @@ SET(LAUNCHER_SOURCES
ui/dialogs/NewComponentDialog.h ui/dialogs/NewComponentDialog.h
ui/dialogs/NewInstanceDialog.cpp ui/dialogs/NewInstanceDialog.cpp
ui/dialogs/NewInstanceDialog.h ui/dialogs/NewInstanceDialog.h
ui/dialogs/NewsDialog.cpp
ui/dialogs/NewsDialog.h
ui/pagedialog/PageDialog.cpp ui/pagedialog/PageDialog.cpp
ui/pagedialog/PageDialog.h ui/pagedialog/PageDialog.h
ui/dialogs/ProgressDialog.cpp ui/dialogs/ProgressDialog.cpp
@ -817,12 +841,6 @@ SET(LAUNCHER_SOURCES
ui/dialogs/ModDownloadDialog.h ui/dialogs/ModDownloadDialog.h
ui/dialogs/ScrollMessageBox.cpp ui/dialogs/ScrollMessageBox.cpp
ui/dialogs/ScrollMessageBox.h ui/dialogs/ScrollMessageBox.h
ui/dialogs/BlockedModsDialog.cpp
ui/dialogs/BlockedModsDialog.h
ui/dialogs/ChooseProviderDialog.h
ui/dialogs/ChooseProviderDialog.cpp
ui/dialogs/ModUpdateDialog.cpp
ui/dialogs/ModUpdateDialog.h
# GUI - widgets # GUI - widgets
ui/widgets/Common.cpp ui/widgets/Common.cpp
@ -845,8 +863,8 @@ SET(LAUNCHER_SOURCES
ui/widgets/LineSeparator.h ui/widgets/LineSeparator.h
ui/widgets/LogView.cpp ui/widgets/LogView.cpp
ui/widgets/LogView.h ui/widgets/LogView.h
ui/widgets/InfoFrame.cpp ui/widgets/MCModInfoFrame.cpp
ui/widgets/InfoFrame.h ui/widgets/MCModInfoFrame.h
ui/widgets/ModFilterWidget.cpp ui/widgets/ModFilterWidget.cpp
ui/widgets/ModFilterWidget.h ui/widgets/ModFilterWidget.h
ui/widgets/ModListView.cpp ui/widgets/ModListView.cpp
@ -854,12 +872,6 @@ SET(LAUNCHER_SOURCES
ui/widgets/PageContainer.cpp ui/widgets/PageContainer.cpp
ui/widgets/PageContainer.h ui/widgets/PageContainer.h
ui/widgets/PageContainer_p.h ui/widgets/PageContainer_p.h
ui/widgets/ProjectDescriptionPage.h
ui/widgets/ProjectDescriptionPage.cpp
ui/widgets/VariableSizedImageObject.h
ui/widgets/VariableSizedImageObject.cpp
ui/widgets/ProjectItem.h
ui/widgets/ProjectItem.cpp
ui/widgets/VersionListView.cpp ui/widgets/VersionListView.cpp
ui/widgets/VersionListView.h ui/widgets/VersionListView.h
ui/widgets/VersionSelectWidget.cpp ui/widgets/VersionSelectWidget.cpp
@ -883,17 +895,7 @@ SET(LAUNCHER_SOURCES
ui/instanceview/VisualGroup.h ui/instanceview/VisualGroup.h
) )
if(WIN32) qt5_wrap_ui(LAUNCHER_UI
set(LAUNCHER_SOURCES
${LAUNCHER_SOURCES}
# GUI - dark titlebar for Windows 10/11
ui/WinDarkmode.h
ui/WinDarkmode.cpp
)
endif()
qt_wrap_ui(LAUNCHER_UI
ui/setupwizard/PasteWizardPage.ui ui/setupwizard/PasteWizardPage.ui
ui/pages/global/AccountListPage.ui ui/pages/global/AccountListPage.ui
ui/pages/global/JavaPage.ui ui/pages/global/JavaPage.ui
@ -902,7 +904,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/pages/global/ProxyPage.ui ui/pages/global/ProxyPage.ui
ui/pages/global/MinecraftPage.ui ui/pages/global/MinecraftPage.ui
ui/pages/global/ExternalToolsPage.ui ui/pages/global/ExternalToolsPage.ui
ui/pages/instance/ExternalResourcesPage.ui ui/pages/instance/ModFolderPage.ui
ui/pages/instance/NotesPage.ui ui/pages/instance/NotesPage.ui
ui/pages/instance/LogPage.ui ui/pages/instance/LogPage.ui
ui/pages/instance/ServersPage.ui ui/pages/instance/ServersPage.ui
@ -924,7 +926,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/pages/modplatform/technic/TechnicPage.ui ui/pages/modplatform/technic/TechnicPage.ui
ui/widgets/InstanceCardWidget.ui ui/widgets/InstanceCardWidget.ui
ui/widgets/CustomCommands.ui ui/widgets/CustomCommands.ui
ui/widgets/InfoFrame.ui ui/widgets/MCModInfoFrame.ui
ui/widgets/ModFilterWidget.ui ui/widgets/ModFilterWidget.ui
ui/dialogs/CopyInstanceDialog.ui ui/dialogs/CopyInstanceDialog.ui
ui/dialogs/ProfileSetupDialog.ui ui/dialogs/ProfileSetupDialog.ui
@ -932,7 +934,6 @@ qt_wrap_ui(LAUNCHER_UI
ui/dialogs/NewInstanceDialog.ui ui/dialogs/NewInstanceDialog.ui
ui/dialogs/UpdateDialog.ui ui/dialogs/UpdateDialog.ui
ui/dialogs/NewComponentDialog.ui ui/dialogs/NewComponentDialog.ui
ui/dialogs/NewsDialog.ui
ui/dialogs/ProfileSelectDialog.ui ui/dialogs/ProfileSelectDialog.ui
ui/dialogs/SkinUploadDialog.ui ui/dialogs/SkinUploadDialog.ui
ui/dialogs/ExportInstanceDialog.ui ui/dialogs/ExportInstanceDialog.ui
@ -944,11 +945,9 @@ qt_wrap_ui(LAUNCHER_UI
ui/dialogs/EditAccountDialog.ui ui/dialogs/EditAccountDialog.ui
ui/dialogs/ReviewMessageBox.ui ui/dialogs/ReviewMessageBox.ui
ui/dialogs/ScrollMessageBox.ui ui/dialogs/ScrollMessageBox.ui
ui/dialogs/BlockedModsDialog.ui
ui/dialogs/ChooseProviderDialog.ui
) )
qt_add_resources(LAUNCHER_RESOURCES qt5_add_resources(LAUNCHER_RESOURCES
resources/backgrounds/backgrounds.qrc resources/backgrounds/backgrounds.qrc
resources/multimc/multimc.qrc resources/multimc/multimc.qrc
resources/pe_dark/pe_dark.qrc resources/pe_dark/pe_dark.qrc
@ -969,56 +968,30 @@ endif()
# Add executable # Add executable
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES}) add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES})
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(Launcher_logic target_link_libraries(Launcher_logic
systeminfo systeminfo
Launcher_murmur2 Launcher_classparser
nbt++ nbt++
${ZLIB_LIBRARIES} ${ZLIB_LIBRARIES}
tomlplusplus::tomlplusplus optional-bare
tomlc99
BuildConfig BuildConfig
Katabasis Katabasis
Qt${QT_VERSION_MAJOR}::Widgets
ghcFilesystem::ghc_filesystem
)
if (UNIX AND NOT CYGWIN AND NOT APPLE)
target_link_libraries(Launcher_logic
gamemode
)
endif()
target_link_libraries(Launcher_logic
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Xml
Qt${QT_VERSION_MAJOR}::Network
Qt${QT_VERSION_MAJOR}::Concurrent
Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Widgets
${Launcher_QT_LIBS}
) )
target_link_libraries(Launcher_logic target_link_libraries(Launcher_logic
Qt5::Core
Qt5::Xml
Qt5::Network
Qt5::Concurrent
Qt5::Gui
)
target_link_libraries(Launcher_logic
Launcher_iconfix
QuaZip::QuaZip QuaZip::QuaZip
hoedown hoedown
LocalPeer LocalPeer
Launcher_rainbow Launcher_rainbow
) )
if(APPLE)
set(CMAKE_MACOSX_RPATH 1)
set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/")
file(DOWNLOAD ${MACOSX_SPARKLE_DOWNLOAD_URL} ${CMAKE_BINARY_DIR}/Sparkle.tar.xz EXPECTED_HASH SHA256=${MACOSX_SPARKLE_SHA256})
file(ARCHIVE_EXTRACT INPUT ${CMAKE_BINARY_DIR}/Sparkle.tar.xz DESTINATION ${CMAKE_BINARY_DIR}/frameworks/Sparkle)
find_library(SPARKLE_FRAMEWORK Sparkle "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
target_link_libraries(Launcher_logic
"-framework AppKit"
"-framework Carbon"
"-framework Foundation"
"-framework ApplicationServices"
)
target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK})
endif()
target_link_libraries(Launcher_logic) target_link_libraries(Launcher_logic)
@ -1041,16 +1014,8 @@ install(TARGETS ${Launcher_Name}
BUNDLE DESTINATION "." COMPONENT Runtime BUNDLE DESTINATION "." COMPONENT Runtime
LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime
RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
) )
if (UNIX AND APPLE)
# Add Sparkle updater
# It has to be copied here instead of just allowing fixup_bundle to install it, otherwise essential parts of
# the framework aren't installed
install(DIRECTORY ${MACOSX_SPARKLE_DIR}/Sparkle.framework DESTINATION ${FRAMEWORK_DEST_DIR} USE_SOURCE_PERMISSIONS)
endif()
#### The bundle mess! #### #### The bundle mess! ####
# Bundle utilities are used to complete the portable packages - they add all the libraries that would otherwise be missing on the target system. # Bundle utilities are used to complete the portable packages - they add all the libraries that would otherwise be missing on the target system.
# NOTE: it seems that this absolutely has to be here, and nowhere else. # NOTE: it seems that this absolutely has to be here, and nowhere else.
@ -1091,14 +1056,6 @@ if(INSTALL_BUNDLE STREQUAL "full")
COMPONENT Runtime COMPONENT Runtime
) )
endif() 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() else()
# Image formats # Image formats
install( install(
@ -1141,16 +1098,6 @@ if(INSTALL_BUNDLE STREQUAL "full")
REGEX "\\.dSYM" EXCLUDE REGEX "\\.dSYM" EXCLUDE
) )
endif() 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() endif()
configure_file( configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in" "${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"

View File

@ -1,38 +1,18 @@
// SPDX-License-Identifier: GPL-3.0-only /* Copyright 2013-2021 MultiMC Contributors
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
* *
* This program is distributed in the hope that it will be useful, * Licensed under the Apache License, Version 2.0 (the "License");
* but WITHOUT ANY WARRANTY; without even the implied warranty of * you may not use this file except in compliance with the License.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * You may obtain a copy of the License at
* GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * http://www.apache.org/licenses/LICENSE-2.0
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* *
* This file incorporates work covered by the following copyright and * Unless required by applicable law or agreed to in writing, software
* permission notice: * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* Copyright 2013-2021 MultiMC Contributors * See the License for the specific language governing permissions and
* * limitations under the License.
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
#include "Commandline.h" #include "Commandline.h"
@ -67,7 +47,7 @@ QStringList splitArgs(QString args)
if (cchar == '\\') if (cchar == '\\')
escape = true; escape = true;
else if (cchar == inquotes) else if (cchar == inquotes)
inquotes = QChar::Null; inquotes = 0;
else else
current += cchar; current += cchar;
// otherwise // otherwise
@ -92,4 +72,412 @@ QStringList splitArgs(QString args)
argv << current; argv << current;
return argv; return argv;
} }
Parser::Parser(FlagStyle::Enum flagStyle, ArgumentStyle::Enum argStyle)
{
m_flagStyle = flagStyle;
m_argStyle = argStyle;
} }
// styles setter/getter
void Parser::setArgumentStyle(ArgumentStyle::Enum style)
{
m_argStyle = style;
}
ArgumentStyle::Enum Parser::argumentStyle()
{
return m_argStyle;
}
void Parser::setFlagStyle(FlagStyle::Enum style)
{
m_flagStyle = style;
}
FlagStyle::Enum Parser::flagStyle()
{
return m_flagStyle;
}
// setup methods
void Parser::addSwitch(QString name, bool def)
{
if (m_params.contains(name))
throw "Name not unique";
OptionDef *param = new OptionDef;
param->type = otSwitch;
param->name = name;
param->metavar = QString("<%1>").arg(name);
param->def = def;
m_options[name] = param;
m_params[name] = (CommonDef *)param;
m_optionList.append(param);
}
void Parser::addOption(QString name, QVariant def)
{
if (m_params.contains(name))
throw "Name not unique";
OptionDef *param = new OptionDef;
param->type = otOption;
param->name = name;
param->metavar = QString("<%1>").arg(name);
param->def = def;
m_options[name] = param;
m_params[name] = (CommonDef *)param;
m_optionList.append(param);
}
void Parser::addArgument(QString name, bool required, QVariant def)
{
if (m_params.contains(name))
throw "Name not unique";
PositionalDef *param = new PositionalDef;
param->name = name;
param->def = def;
param->required = required;
param->metavar = name;
m_positionals.append(param);
m_params[name] = (CommonDef *)param;
}
void Parser::addDocumentation(QString name, QString doc, QString metavar)
{
if (!m_params.contains(name))
throw "Name does not exist";
CommonDef *param = m_params[name];
param->doc = doc;
if (!metavar.isNull())
param->metavar = metavar;
}
void Parser::addShortOpt(QString name, QChar flag)
{
if (!m_params.contains(name))
throw "Name does not exist";
if (!m_options.contains(name))
throw "Name is not an Option or Swtich";
OptionDef *param = m_options[name];
m_flags[flag] = param;
param->flag = flag;
}
// help methods
QString Parser::compileHelp(QString progName, int helpIndent, bool useFlags)
{
QStringList help;
help << compileUsage(progName, useFlags) << "\r\n";
// positionals
if (!m_positionals.isEmpty())
{
help << "\r\n";
help << "Positional arguments:\r\n";
QListIterator<PositionalDef *> it2(m_positionals);
while (it2.hasNext())
{
PositionalDef *param = it2.next();
help << " " << param->metavar;
help << " " << QString(helpIndent - param->metavar.length() - 1, ' ');
help << param->doc << "\r\n";
}
}
// Options
if (!m_optionList.isEmpty())
{
help << "\r\n";
QString optPrefix, flagPrefix;
getPrefix(optPrefix, flagPrefix);
help << "Options & Switches:\r\n";
QListIterator<OptionDef *> it(m_optionList);
while (it.hasNext())
{
OptionDef *option = it.next();
help << " ";
int nameLength = optPrefix.length() + option->name.length();
if (!option->flag.isNull())
{
nameLength += 3 + flagPrefix.length();
help << flagPrefix << option->flag << ", ";
}
help << optPrefix << option->name;
if (option->type == otOption)
{
QString arg = QString("%1%2").arg(
((m_argStyle == ArgumentStyle::Equals) ? "=" : " "), option->metavar);
nameLength += arg.length();
help << arg;
}
help << " " << QString(helpIndent - nameLength - 1, ' ');
help << option->doc << "\r\n";
}
}
return help.join("");
}
QString Parser::compileUsage(QString progName, bool useFlags)
{
QStringList usage;
usage << "Usage: " << progName;
QString optPrefix, flagPrefix;
getPrefix(optPrefix, flagPrefix);
// options
QListIterator<OptionDef *> it(m_optionList);
while (it.hasNext())
{
OptionDef *option = it.next();
usage << " [";
if (!option->flag.isNull() && useFlags)
usage << flagPrefix << option->flag;
else
usage << optPrefix << option->name;
if (option->type == otOption)
usage << ((m_argStyle == ArgumentStyle::Equals) ? "=" : " ") << option->metavar;
usage << "]";
}
// arguments
QListIterator<PositionalDef *> it2(m_positionals);
while (it2.hasNext())
{
PositionalDef *param = it2.next();
usage << " " << (param->required ? "<" : "[");
usage << param->metavar;
usage << (param->required ? ">" : "]");
}
return usage.join("");
}
// parsing
QHash<QString, QVariant> Parser::parse(QStringList argv)
{
QHash<QString, QVariant> map;
QStringListIterator it(argv);
QString programName = it.next();
QString optionPrefix;
QString flagPrefix;
QListIterator<PositionalDef *> positionals(m_positionals);
QStringList expecting;
getPrefix(optionPrefix, flagPrefix);
while (it.hasNext())
{
QString arg = it.next();
if (!expecting.isEmpty())
// we were expecting an argument
{
QString name = expecting.first();
/*
if (map.contains(name))
throw ParsingError(
QString("Option %2%1 was given multiple times").arg(name, optionPrefix));
*/
map[name] = QVariant(arg);
expecting.removeFirst();
continue;
}
if (arg.startsWith(optionPrefix))
// we have an option
{
// qDebug("Found option %s", qPrintable(arg));
QString name = arg.mid(optionPrefix.length());
QString equals;
if ((m_argStyle == ArgumentStyle::Equals ||
m_argStyle == ArgumentStyle::SpaceAndEquals) &&
name.contains("="))
{
int i = name.indexOf("=");
equals = name.mid(i + 1);
name = name.left(i);
}
if (m_options.contains(name))
{
/*
if (map.contains(name))
throw ParsingError(QString("Option %2%1 was given multiple times")
.arg(name, optionPrefix));
*/
OptionDef *option = m_options[name];
if (option->type == otSwitch)
map[name] = true;
else // if (option->type == otOption)
{
if (m_argStyle == ArgumentStyle::Space)
expecting.append(name);
else if (!equals.isNull())
map[name] = equals;
else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
expecting.append(name);
else
throw ParsingError(QString("Option %2%1 reqires an argument.")
.arg(name, optionPrefix));
}
continue;
}
throw ParsingError(QString("Unknown Option %2%1").arg(name, optionPrefix));
}
if (arg.startsWith(flagPrefix))
// we have (a) flag(s)
{
// qDebug("Found flags %s", qPrintable(arg));
QString flags = arg.mid(flagPrefix.length());
QString equals;
if ((m_argStyle == ArgumentStyle::Equals ||
m_argStyle == ArgumentStyle::SpaceAndEquals) &&
flags.contains("="))
{
int i = flags.indexOf("=");
equals = flags.mid(i + 1);
flags = flags.left(i);
}
for (int i = 0; i < flags.length(); i++)
{
QChar flag = flags.at(i);
if (!m_flags.contains(flag))
throw ParsingError(QString("Unknown flag %2%1").arg(flag, flagPrefix));
OptionDef *option = m_flags[flag];
/*
if (map.contains(option->name))
throw ParsingError(QString("Option %2%1 was given multiple times")
.arg(option->name, optionPrefix));
*/
if (option->type == otSwitch)
map[option->name] = true;
else // if (option->type == otOption)
{
if (m_argStyle == ArgumentStyle::Space)
expecting.append(option->name);
else if (!equals.isNull())
if (i == flags.length() - 1)
map[option->name] = equals;
else
throw ParsingError(QString("Flag %4%2 of Argument-requiring Option "
"%1 not last flag in %4%3")
.arg(option->name, flag, flags, flagPrefix));
else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
expecting.append(option->name);
else
throw ParsingError(QString("Option %1 reqires an argument. (flag %3%2)")
.arg(option->name, flag, flagPrefix));
}
}
continue;
}
// must be a positional argument
if (!positionals.hasNext())
throw ParsingError(QString("Don't know what to do with '%1'").arg(arg));
PositionalDef *param = positionals.next();
map[param->name] = arg;
}
// check if we're missing something
if (!expecting.isEmpty())
throw ParsingError(QString("Was still expecting arguments for %2%1").arg(
expecting.join(QString(", ") + optionPrefix), optionPrefix));
while (positionals.hasNext())
{
PositionalDef *param = positionals.next();
if (param->required)
throw ParsingError(
QString("Missing required positional argument '%1'").arg(param->name));
else
map[param->name] = param->def;
}
// fill out gaps
QListIterator<OptionDef *> iter(m_optionList);
while (iter.hasNext())
{
OptionDef *option = iter.next();
if (!map.contains(option->name))
map[option->name] = option->def;
}
return map;
}
// clear defs
void Parser::clear()
{
m_flags.clear();
m_params.clear();
m_options.clear();
QMutableListIterator<OptionDef *> it(m_optionList);
while (it.hasNext())
{
OptionDef *option = it.next();
it.remove();
delete option;
}
QMutableListIterator<PositionalDef *> it2(m_positionals);
while (it2.hasNext())
{
PositionalDef *arg = it2.next();
it2.remove();
delete arg;
}
}
// Destructor
Parser::~Parser()
{
clear();
}
// getPrefix
void Parser::getPrefix(QString &opt, QString &flag)
{
if (m_flagStyle == FlagStyle::Windows)
opt = flag = "/";
else if (m_flagStyle == FlagStyle::Unix)
opt = flag = "-";
// else if (m_flagStyle == FlagStyle::GNU)
else
{
opt = "--";
flag = "-";
}
}
// ParsingError
ParsingError::ParsingError(const QString &what) : std::runtime_error(what.toStdString())
{
}
}

View File

@ -17,7 +17,12 @@
#pragma once #pragma once
#include <exception>
#include <stdexcept>
#include <QString> #include <QString>
#include <QVariant>
#include <QHash>
#include <QStringList> #include <QStringList>
/** /**
@ -34,4 +39,212 @@ namespace Commandline
* @return a QStringList containing all arguments * @return a QStringList containing all arguments
*/ */
QStringList splitArgs(QString args); QStringList splitArgs(QString args);
/**
* @brief The FlagStyle enum
* Specifies how flags are decorated
*/
namespace FlagStyle
{
enum Enum
{
GNU, /**< --option and -o (GNU Style) */
Unix, /**< -option and -o (Unix Style) */
Windows, /**< /option and /o (Windows Style) */
#ifdef Q_OS_WIN32
Default = Windows
#else
Default = GNU
#endif
};
}
/**
* @brief The ArgumentStyle enum
*/
namespace ArgumentStyle
{
enum Enum
{
Space, /**< --option value */
Equals, /**< --option=value */
SpaceAndEquals, /**< --option[= ]value */
#ifdef Q_OS_WIN32
Default = Equals
#else
Default = SpaceAndEquals
#endif
};
}
/**
* @brief The ParsingError class
*/
class ParsingError : public std::runtime_error
{
public:
ParsingError(const QString &what);
};
/**
* @brief The Parser class
*/
class Parser
{
public:
/**
* @brief Parser constructor
* @param flagStyle the FlagStyle to use in this Parser
* @param argStyle the ArgumentStyle to use in this Parser
*/
Parser(FlagStyle::Enum flagStyle = FlagStyle::Default,
ArgumentStyle::Enum argStyle = ArgumentStyle::Default);
/**
* @brief set the flag style
* @param style
*/
void setFlagStyle(FlagStyle::Enum style);
/**
* @brief get the flag style
* @return
*/
FlagStyle::Enum flagStyle();
/**
* @brief set the argument style
* @param style
*/
void setArgumentStyle(ArgumentStyle::Enum style);
/**
* @brief get the argument style
* @return
*/
ArgumentStyle::Enum argumentStyle();
/**
* @brief define a boolean switch
* @param name the parameter name
* @param def the default value
*/
void addSwitch(QString name, bool def = false);
/**
* @brief define an option that takes an additional argument
* @param name the parameter name
* @param def the default value
*/
void addOption(QString name, QVariant def = QVariant());
/**
* @brief define a positional argument
* @param name the parameter name
* @param required wether this argument is required
* @param def the default value
*/
void addArgument(QString name, bool required = true, QVariant def = QVariant());
/**
* @brief adds a flag to an existing parameter
* @param name the (existing) parameter name
* @param flag the flag character
* @see addSwitch addArgument addOption
* Note: any one parameter can only have one flag
*/
void addShortOpt(QString name, QChar flag);
/**
* @brief adds documentation to a Parameter
* @param name the parameter name
* @param metavar a string to be displayed as placeholder for the value
* @param doc a QString containing the documentation
* Note: on positional arguments, metavar replaces the name as displayed.
* on options , metavar replaces the value placeholder
*/
void addDocumentation(QString name, QString doc, QString metavar = QString());
/**
* @brief generate a help message
* @param progName the program name to use in the help message
* @param helpIndent how much the parameter documentation should be indented
* @param flagsInUsage whether we should use flags instead of options in the usage
* @return a help message
*/
QString compileHelp(QString progName, int helpIndent = 22, bool flagsInUsage = true);
/**
* @brief generate a short usage message
* @param progName the program name to use in the usage message
* @param useFlags whether we should use flags instead of options
* @return a usage message
*/
QString compileUsage(QString progName, bool useFlags = true);
/**
* @brief parse
* @param argv a QStringList containing the program ARGV
* @return a QHash mapping argument names to their values
*/
QHash<QString, QVariant> parse(QStringList argv);
/**
* @brief clear all definitions
*/
void clear();
~Parser();
private:
FlagStyle::Enum m_flagStyle;
ArgumentStyle::Enum m_argStyle;
enum OptionType
{
otSwitch,
otOption
};
// Important: the common part MUST BE COMMON ON ALL THREE structs
struct CommonDef
{
QString name;
QString doc;
QString metavar;
QVariant def;
};
struct OptionDef
{
// common
QString name;
QString doc;
QString metavar;
QVariant def;
// option
OptionType type;
QChar flag;
};
struct PositionalDef
{
// common
QString name;
QString doc;
QString metavar;
QVariant def;
// positional
bool required;
};
QHash<QString, OptionDef *> m_options;
QHash<QChar, OptionDef *> m_flags;
QHash<QString, CommonDef *> m_params;
QList<PositionalDef *> m_positionals;
QList<OptionDef *> m_optionList;
void getPrefix(QString &opt, QString &flag);
};
} }

View File

@ -1,134 +1,77 @@
// SPDX-License-Identifier: GPL-3.0-only // Licensed under the Apache-2.0 license. See README.md for details.
/*
* PolyMC - 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 "FileSystem.h" #include "FileSystem.h"
#include <QDebug>
#include <QDir> #include <QDir>
#include <QDirIterator>
#include <QFile> #include <QFile>
#include <QFileInfo>
#include <QSaveFile> #include <QSaveFile>
#include <QFileInfo>
#include <QDebug>
#include <QUrl>
#include <QStandardPaths> #include <QStandardPaths>
#include <QTextStream> #include <QTextStream>
#include <QUrl>
#if defined Q_OS_WIN32 #if defined Q_OS_WIN32
#include <objbase.h> #include <windows.h>
#include <objidl.h> #include <string>
#include <shlguid.h> #include <sys/utime.h>
#include <shlobj.h> #include <winnls.h>
#include <shobjidl.h> #include <shobjidl.h>
#include <sys/utime.h> #include <objbase.h>
#include <windows.h> #include <objidl.h>
#include <winnls.h> #include <shlguid.h>
#include <string> #include <shlobj.h>
#else #else
#include <utime.h> #include <utime.h>
#endif
// Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header
#ifdef __APPLE__
#include <Availability.h> // for deployment target to support pre-catalina targets without std::fs
#endif // __APPLE__
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#define GHC_USE_STD_FS
#include <filesystem>
namespace fs = std::filesystem;
#endif // MacOS min version check
#endif // Other OSes version check
#ifndef GHC_USE_STD_FS
#include <ghc/filesystem.hpp>
namespace fs = ghc::filesystem;
#endif
#if defined Q_OS_WIN32
std::wstring toStdString(QString s)
{
return s.toStdWString();
}
#else
std::string toStdString(QString s)
{
return s.toStdString();
}
#endif #endif
namespace FS { namespace FS {
void ensureExists(const QDir& dir) void ensureExists(const QDir &dir)
{ {
if (!QDir().mkpath(dir.absolutePath())) { if (!QDir().mkpath(dir.absolutePath()))
throw FileSystemException("Unable to create folder " + dir.dirName() + " (" + dir.absolutePath() + ")"); {
throw FileSystemException("Unable to create folder " + dir.dirName() + " (" +
dir.absolutePath() + ")");
} }
} }
void write(const QString& filename, const QByteArray& data) void write(const QString &filename, const QByteArray &data)
{ {
ensureExists(QFileInfo(filename).dir()); ensureExists(QFileInfo(filename).dir());
QSaveFile file(filename); QSaveFile file(filename);
if (!file.open(QSaveFile::WriteOnly)) { if (!file.open(QSaveFile::WriteOnly))
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString()); {
throw FileSystemException("Couldn't open " + filename + " for writing: " +
file.errorString());
} }
if (data.size() != file.write(data)) { if (data.size() != file.write(data))
throw FileSystemException("Error writing data to " + filename + ": " + file.errorString()); {
throw FileSystemException("Error writing data to " + filename + ": " +
file.errorString());
} }
if (!file.commit()) { if (!file.commit())
throw FileSystemException("Error while committing data to " + filename + ": " + file.errorString()); {
throw FileSystemException("Error while committing data to " + filename + ": " +
file.errorString());
} }
} }
QByteArray read(const QString& filename) QByteArray read(const QString &filename)
{ {
QFile file(filename); QFile file(filename);
if (!file.open(QFile::ReadOnly)) { if (!file.open(QFile::ReadOnly))
throw FileSystemException("Unable to open " + filename + " for reading: " + file.errorString()); {
throw FileSystemException("Unable to open " + filename + " for reading: " +
file.errorString());
} }
const qint64 size = file.size(); const qint64 size = file.size();
QByteArray data(int(size), 0); QByteArray data(int(size), 0);
const qint64 ret = file.read(data.data(), size); const qint64 ret = file.read(data.data(), size);
if (ret == -1 || ret != size) { if (ret == -1 || ret != size)
throw FileSystemException("Error reading data from " + filename + ": " + file.errorString()); {
throw FileSystemException("Error reading data from " + filename + ": " +
file.errorString());
} }
return data; return data;
} }
@ -162,91 +105,149 @@ bool ensureFolderPathExists(QString foldernamepath)
return success; return success;
} }
bool copy::operator()(const QString& offset) bool copy::operator()(const QString &offset)
{ {
using copy_opts = fs::copy_options; //NOTE always deep copy on windows. the alternatives are too messy.
#if defined Q_OS_WIN32
// NOTE always deep copy on windows. the alternatives are too messy.
#if defined Q_OS_WIN32
m_followSymlinks = true; m_followSymlinks = true;
#endif #endif
auto src = PathCombine(m_src.absolutePath(), offset); auto src = PathCombine(m_src.absolutePath(), offset);
auto dst = PathCombine(m_dst.absolutePath(), offset); auto dst = PathCombine(m_dst.absolutePath(), offset);
std::error_code err; QFileInfo currentSrc(src);
if (!currentSrc.exists())
return false;
fs::copy_options opt = copy_opts::none; if(!m_followSymlinks && currentSrc.isSymLink())
{
// The default behavior is to follow symlinks qDebug() << "creating symlink" << src << " - " << dst;
if (!m_followSymlinks) if (!ensureFilePathExists(dst))
opt |= copy_opts::copy_symlinks; {
qWarning() << "Cannot create path!";
return false;
// We can't use copy_opts::recursive because we need to take into account the }
// blacklisted paths, so we iterate over the source directory, and if there's no blacklist return QFile::link(currentSrc.symLinkTarget(), dst);
// match, we copy the file. }
QDir src_dir(src); else if(currentSrc.isFile())
QDirIterator source_it(src, QDir::Filter::Files | QDir::Filter::Hidden, QDirIterator::Subdirectories); {
qDebug() << "copying file" << src << " - " << dst;
while (source_it.hasNext()) { if (!ensureFilePathExists(dst))
auto src_path = source_it.next(); {
auto relative_path = src_dir.relativeFilePath(src_path); qWarning() << "Cannot create path!";
return false;
if (m_blacklist && m_blacklist->matches(relative_path)) }
continue; return QFile::copy(src, dst);
}
auto dst_path = PathCombine(dst, relative_path); else if(currentSrc.isDir())
ensureFilePathExists(dst_path); {
qDebug() << "recursing" << offset;
fs::copy(toStdString(src_path), toStdString(dst_path), opt, err); if (!ensureFolderPathExists(dst))
if (err) { {
qWarning() << "Failed to copy files:" << QString::fromStdString(err.message()); qWarning() << "Cannot create path!";
qDebug() << "Source file:" << src_path; return false;
qDebug() << "Destination file:" << dst_path; }
QDir currentDir(src);
for(auto & f : currentDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System))
{
auto inner_offset = PathCombine(offset, f);
// ignore and skip stuff that matches the blacklist.
if(m_blacklist && m_blacklist->matches(inner_offset))
{
continue;
}
if(!operator()(inner_offset))
{
qWarning() << "Failed to copy" << inner_offset;
return false;
}
} }
} }
else
return err.value() == 0; {
qCritical() << "Copy ERROR: Unknown filesystem object:" << src;
return false;
}
return true;
} }
bool deletePath(QString path) bool deletePath(QString path)
{ {
std::error_code err; bool OK = true;
QFileInfo finfo(path);
fs::remove_all(toStdString(path), err); if(finfo.isFile()) {
return QFile::remove(path);
if (err) {
qWarning() << "Failed to remove files:" << QString::fromStdString(err.message());
} }
return err.value() == 0; QDir dir(path);
}
bool trash(QString path, QString *pathInTrash = nullptr) if (!dir.exists())
{ {
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) return OK;
return false; }
auto allEntries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden |
QDir::AllDirs | QDir::Files,
QDir::DirsFirst);
for(auto & info: allEntries)
{
#if defined Q_OS_WIN32
QString nativePath = QDir::toNativeSeparators(info.absoluteFilePath());
auto wString = nativePath.toStdWString();
DWORD dwAttrs = GetFileAttributesW(wString.c_str());
// Windows: check for junctions, reparse points and other nasty things of that sort
if(dwAttrs & FILE_ATTRIBUTE_REPARSE_POINT)
{
if (info.isFile())
{
OK &= QFile::remove(info.absoluteFilePath());
}
else if (info.isDir())
{
OK &= dir.rmdir(info.absoluteFilePath());
}
}
#else #else
return QFile::moveToTrash(path, pathInTrash); // We do not trust Qt with reparse points, but do trust it with unix symlinks.
if(info.isSymLink())
{
OK &= QFile::remove(info.absoluteFilePath());
}
#endif #endif
else if (info.isDir())
{
OK &= deletePath(info.absoluteFilePath());
}
else if (info.isFile())
{
OK &= QFile::remove(info.absoluteFilePath());
}
else
{
OK = false;
qCritical() << "Delete ERROR: Unknown filesystem object:" << info.absoluteFilePath();
}
}
OK &= dir.rmdir(dir.absolutePath());
return OK;
} }
QString PathCombine(const QString& path1, const QString& path2)
QString PathCombine(const QString & path1, const QString & path2)
{ {
if (!path1.size()) if(!path1.size())
return path2; return path2;
if (!path2.size()) if(!path2.size())
return path1; return path1;
return QDir::cleanPath(path1 + QDir::separator() + path2); return QDir::cleanPath(path1 + QDir::separator() + path2);
} }
QString PathCombine(const QString& path1, const QString& path2, const QString& path3) QString PathCombine(const QString & path1, const QString & path2, const QString & path3)
{ {
return PathCombine(PathCombine(path1, path2), path3); return PathCombine(PathCombine(path1, path2), path3);
} }
QString PathCombine(const QString& path1, const QString& path2, const QString& path3, const QString& path4) QString PathCombine(const QString & path1, const QString & path2, const QString & path3, const QString & path4)
{ {
return PathCombine(PathCombine(path1, path2, path3), path4); return PathCombine(PathCombine(path1, path2, path3), path4);
} }
@ -258,14 +259,17 @@ QString AbsolutePath(QString path)
QString ResolveExecutable(QString path) QString ResolveExecutable(QString path)
{ {
if (path.isEmpty()) { if (path.isEmpty())
{
return QString(); return QString();
} }
if (!path.contains('/')) { if(!path.contains('/'))
{
path = QStandardPaths::findExecutable(path); path = QStandardPaths::findExecutable(path);
} }
QFileInfo pathInfo(path); QFileInfo pathInfo(path);
if (!pathInfo.exists() || !pathInfo.isExecutable()) { if(!pathInfo.exists() || !pathInfo.isExecutable())
{
return QString(); return QString();
} }
return pathInfo.absoluteFilePath(); return pathInfo.absoluteFilePath();
@ -285,9 +289,12 @@ QString NormalizePath(QString path)
QDir b(path); QDir b(path);
QString newAbsolute = b.absolutePath(); QString newAbsolute = b.absolutePath();
if (newAbsolute.startsWith(currentAbsolute)) { if (newAbsolute.startsWith(currentAbsolute))
{
return a.relativeFilePath(newAbsolute); return a.relativeFilePath(newAbsolute);
} else { }
else
{
return newAbsolute; return newAbsolute;
} }
} }
@ -296,8 +303,10 @@ QString badFilenameChars = "\"\\/?<>:;*|!+\r\n";
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith) QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
{ {
for (int i = 0; i < string.length(); i++) { for (int i = 0; i < string.length(); i++)
if (badFilenameChars.contains(string[i])) { {
if (badFilenameChars.contains(string[i]))
{
string[i] = replaceWith; string[i] = replaceWith;
} }
} }
@ -309,11 +318,15 @@ QString DirNameFromString(QString string, QString inDir)
int num = 0; int num = 0;
QString baseName = RemoveInvalidFilenameChars(string, '-'); QString baseName = RemoveInvalidFilenameChars(string, '-');
QString dirName; QString dirName;
do { do
if (num == 0) { {
if(num == 0)
{
dirName = baseName; dirName = baseName;
} else { }
dirName = baseName + "(" + QString::number(num) + ")"; else
{
dirName = baseName + QString::number(num);;
} }
// If it's over 9000 // If it's over 9000
@ -332,13 +345,63 @@ bool checkProblemticPathJava(QDir folder)
return pathfoldername.contains("!", Qt::CaseInsensitive); return pathfoldername.contains("!", Qt::CaseInsensitive);
} }
// Win32 crap
#if defined Q_OS_WIN
bool called_coinit = false;
HRESULT CreateLink(LPCSTR linkPath, LPCSTR targetPath, LPCSTR args)
{
HRESULT hres;
if (!called_coinit)
{
hres = CoInitialize(NULL);
called_coinit = true;
if (!SUCCEEDED(hres))
{
qWarning("Failed to initialize COM. Error 0x%08lX", hres);
return hres;
}
}
IShellLink *link;
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink,
(LPVOID *)&link);
if (SUCCEEDED(hres))
{
IPersistFile *persistFile;
link->SetPath(targetPath);
link->SetArguments(args);
hres = link->QueryInterface(IID_IPersistFile, (LPVOID *)&persistFile);
if (SUCCEEDED(hres))
{
WCHAR wstr[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, linkPath, -1, wstr, MAX_PATH);
hres = persistFile->Save(wstr, TRUE);
persistFile->Release();
}
link->Release();
}
return hres;
}
#endif
QString getDesktopDir() QString getDesktopDir()
{ {
return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
} }
// Cross-platform Shortcut creation // Cross-platform Shortcut creation
bool createShortCut(QString location, QString dest, QStringList args, QString name, QString icon) bool createShortCut(QString location, QString dest, QStringList args, QString name,
QString icon)
{ {
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
location = PathCombine(location, name + ".desktop"); location = PathCombine(location, name + ".desktop");
@ -363,7 +426,8 @@ bool createShortCut(QString location, QString dest, QStringList args, QString na
stream.flush(); stream.flush();
f.close(); f.close();
f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther); f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup |
QFileDevice::ExeOther);
return true; return true;
#elif defined Q_OS_WIN #elif defined Q_OS_WIN
@ -391,24 +455,46 @@ bool createShortCut(QString location, QString dest, QStringList args, QString na
#endif #endif
} }
QStringList listFolderPaths(QDir root)
{
auto createAbsPath = [](QFileInfo const& entry) { return FS::PathCombine(entry.path(), entry.fileName()); };
QStringList entries;
root.refresh();
for (auto entry : root.entryInfoList(QDir::Filter::Files)) {
entries.append(createAbsPath(entry));
}
for (auto entry : root.entryInfoList(QDir::Filter::AllDirs | QDir::Filter::NoDotAndDotDot)) {
entries.append(listFolderPaths(createAbsPath(entry)));
}
return entries;
}
bool overrideFolder(QString overwritten_path, QString override_path) bool overrideFolder(QString overwritten_path, QString override_path)
{ {
using copy_opts = fs::copy_options;
if (!FS::ensureFolderPathExists(overwritten_path)) if (!FS::ensureFolderPathExists(overwritten_path))
return false; return false;
std::error_code err; QStringList paths_to_override;
fs::copy_options opt = copy_opts::recursive | copy_opts::overwrite_existing; QDir root_override (override_path);
for (auto file : listFolderPaths(root_override)) {
QString destination = file;
destination.replace(override_path, overwritten_path);
fs::copy(toStdString(override_path), toStdString(overwritten_path), opt, err); qDebug() << QString("Applying override %1 in %2").arg(file, destination);
if (err) { if (QFile::exists(destination))
qCritical() << QString("Failed to apply override from %1 to %2").arg(override_path, overwritten_path); QFile::remove(destination);
qCritical() << "Reason:" << QString::fromStdString(err.message()); if (!QFile::rename(file, destination)) {
qCritical() << QString("Failed to apply override from %1 to %2").arg(file, destination);
return false;
}
} }
return err.value() == 0; return true;
} }
} }

View File

@ -1,37 +1,4 @@
// SPDX-License-Identifier: GPL-3.0-only // Licensed under the Apache-2.0 license. See README.md for details.
/*
* PolyMC - 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 #pragma once
@ -41,27 +8,29 @@
#include <QDir> #include <QDir>
#include <QFlags> #include <QFlags>
namespace FS { namespace FS
{
class FileSystemException : public ::Exception { class FileSystemException : public ::Exception
public: {
FileSystemException(const QString& message) : Exception(message) {} public:
FileSystemException(const QString &message) : Exception(message) {}
}; };
/** /**
* write data to a file safely * write data to a file safely
*/ */
void write(const QString& filename, const QByteArray& data); void write(const QString &filename, const QByteArray &data);
/** /**
* read data from a file safely\ * read data from a file safely\
*/ */
QByteArray read(const QString& filename); QByteArray read(const QString &filename);
/** /**
* Update the last changed timestamp of an existing file * Update the last changed timestamp of an existing file
*/ */
bool updateTimestamp(const QString& filename); bool updateTimestamp(const QString & filename);
/** /**
* Creates all the folders in a path for the specified path * Creates all the folders in a path for the specified path
@ -75,31 +44,35 @@ bool ensureFilePathExists(QString filenamepath);
*/ */
bool ensureFolderPathExists(QString filenamepath); bool ensureFolderPathExists(QString filenamepath);
class copy { class copy
public: {
copy(const QString& src, const QString& dst) public:
copy(const QString & src, const QString & dst)
{ {
m_src.setPath(src); m_src = src;
m_dst.setPath(dst); m_dst = dst;
} }
copy& followSymlinks(const bool follow) copy & followSymlinks(const bool follow)
{ {
m_followSymlinks = follow; m_followSymlinks = follow;
return *this; return *this;
} }
copy& blacklist(const IPathMatcher* filter) copy & blacklist(const IPathMatcher * filter)
{ {
m_blacklist = filter; m_blacklist = filter;
return *this; return *this;
} }
bool operator()() { return operator()(QString()); } bool operator()()
{
return operator()(QString());
}
private: private:
bool operator()(const QString& offset); bool operator()(const QString &offset);
private: private:
bool m_followSymlinks = true; bool m_followSymlinks = true;
const IPathMatcher* m_blacklist = nullptr; const IPathMatcher * m_blacklist = nullptr;
QDir m_src; QDir m_src;
QDir m_dst; QDir m_dst;
}; };
@ -109,14 +82,9 @@ class copy {
*/ */
bool deletePath(QString path); bool deletePath(QString path);
/** QString PathCombine(const QString &path1, const QString &path2);
* Trash a folder / file QString PathCombine(const QString &path1, const QString &path2, const QString &path3);
*/ QString PathCombine(const QString &path1, const QString &path2, const QString &path3, const QString &path4);
bool trash(QString path, QString *pathInTrash);
QString PathCombine(const QString& path1, const QString& path2);
QString PathCombine(const QString& path1, const QString& path2, const QString& path3);
QString PathCombine(const QString& path1, const QString& path2, const QString& path3, const QString& path4);
QString AbsolutePath(QString path); QString AbsolutePath(QString path);
@ -152,6 +120,11 @@ bool checkProblemticPathJava(QDir folder);
// Get the Directory representing the User's Desktop // Get the Directory representing the User's Desktop
QString getDesktopDir(); QString getDesktopDir();
// Create a shortcut at *location*, pointing to *dest* called with the arguments *args*
// call it *name* and assign it the icon *icon*
// return true if operation succeeded
bool createShortCut(QString location, QString dest, QStringList args, QString name, QString iconLocation);
// Overrides one folder with the contents of another, preserving items exclusive to the first folder // Overrides one folder with the contents of another, preserving items exclusive to the first folder
// Equivalent to doing QDir::rename, but allowing for overrides // Equivalent to doing QDir::rename, but allowing for overrides
bool overrideFolder(QString overwritten_path, QString override_path); bool overrideFolder(QString overwritten_path, QString override_path);

View File

@ -1,10 +1,9 @@
#include <QTest> #include <QTest>
#include <QTemporaryDir> #include <QTemporaryDir>
#include <QStandardPaths> #include <QStandardPaths>
#include "TestUtil.h"
#include <FileSystem.h> #include "FileSystem.h"
#include <pathmatcher/RegexpMatcher.h>
class FileSystemTest : public QObject class FileSystemTest : public QObject
{ {
@ -82,7 +81,7 @@ slots:
void test_copy() void test_copy()
{ {
QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); QString folder = QFINDTESTDATA("data/test_folder");
auto f = [&folder]() auto f = [&folder]()
{ {
QTemporaryDir tempDir; QTemporaryDir tempDir;
@ -113,80 +112,51 @@ slots:
f(); f();
} }
void test_copy_with_blacklist()
{
QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder");
auto f = [&folder]()
{
QTemporaryDir tempDir;
tempDir.setAutoRemove(true);
qDebug() << "From:" << folder << "To:" << tempDir.path();
QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
qDebug() << tempDir.path();
qDebug() << target_dir.path();
FS::copy c(folder, target_dir.path());
c.blacklist(new RegexpMatcher("[.]?mcmeta"));
c();
for(auto entry: target_dir.entryList())
{
qDebug() << entry;
}
QVERIFY(!target_dir.entryList().contains("pack.mcmeta"));
QVERIFY(target_dir.entryList().contains("assets"));
};
// first try variant without trailing /
QVERIFY(!folder.endsWith('/'));
f();
// then variant with trailing /
folder.append('/');
QVERIFY(folder.endsWith('/'));
f();
}
void test_copy_with_dot_hidden()
{
QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder");
auto f = [&folder]()
{
QTemporaryDir tempDir;
tempDir.setAutoRemove(true);
qDebug() << "From:" << folder << "To:" << tempDir.path();
QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
qDebug() << tempDir.path();
qDebug() << target_dir.path();
FS::copy c(folder, target_dir.path());
c();
auto filter = QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden;
for (auto entry: target_dir.entryList(filter)) {
qDebug() << entry;
}
QVERIFY(target_dir.entryList(filter).contains(".secret_folder"));
target_dir.cd(".secret_folder");
QVERIFY(target_dir.entryList(filter).contains(".secret_file.txt"));
};
// first try variant without trailing /
QVERIFY(!folder.endsWith('/'));
f();
// then variant with trailing /
folder.append('/');
QVERIFY(folder.endsWith('/'));
f();
}
void test_getDesktop() void test_getDesktop()
{ {
QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
} }
// this is only valid on linux
// FIXME: implement on windows, OSX, then test.
#if defined(Q_OS_LINUX)
void test_createShortcut_data()
{
QTest::addColumn<QString>("location");
QTest::addColumn<QString>("dest");
QTest::addColumn<QStringList>("args");
QTest::addColumn<QString>("name");
QTest::addColumn<QString>("iconLocation");
QTest::addColumn<QByteArray>("result");
QTest::newRow("unix") << QDir::currentPath()
<< "asdfDest"
<< (QStringList() << "arg1" << "arg2")
<< "asdf"
<< QString()
#if defined(Q_OS_LINUX)
<< GET_TEST_FILE("data/FileSystem-test_createShortcut-unix")
#elif defined(Q_OS_WIN)
<< QByteArray()
#endif
;
}
void test_createShortcut()
{
QFETCH(QString, location);
QFETCH(QString, dest);
QFETCH(QStringList, args);
QFETCH(QString, name);
QFETCH(QString, iconLocation);
QFETCH(QByteArray, result);
QVERIFY(FS::createShortCut(location, dest, args, name, iconLocation));
QCOMPARE(QString::fromLocal8Bit(TestsInternal::readFile(location + QDir::separator() + name + ".desktop")), QString::fromLocal8Bit(result));
//QDir().remove(location);
}
#endif
}; };
QTEST_GUILESS_MAIN(FileSystemTest) QTEST_GUILESS_MAIN(FileSystemTest)

View File

@ -1,38 +1,3 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "GZip.h" #include "GZip.h"
#include <zlib.h> #include <zlib.h>
#include <QByteArray> #include <QByteArray>
@ -102,7 +67,7 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
return true; return true;
} }
unsigned compLength = qMin(uncompressedBytes.size(), 16); unsigned compLength = std::min(uncompressedBytes.size(), 16);
compressedBytes.clear(); compressedBytes.clear();
compressedBytes.resize(compLength); compressedBytes.resize(compLength);
@ -147,4 +112,4 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
return false; return false;
} }
return true; return true;
} }

View File

@ -1,6 +1,7 @@
#include <QTest> #include <QTest>
#include "TestUtil.h"
#include <GZip.h> #include "GZip.h"
#include <random> #include <random>
void fib(int &prev, int &cur) void fib(int &prev, int &cur)

View File

@ -44,7 +44,7 @@ void InstanceCopyTask::copyFinished()
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg")); auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath)); InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
inst->setName(name()); inst->setName(m_instName);
inst->setIconKey(m_instIcon); inst->setIconKey(m_instIcon);
if(!m_keepPlaytime) { if(!m_keepPlaytime) {
inst->resetTimePlayed(); inst->resetTimePlayed();

View File

@ -1,56 +1,40 @@
#include "InstanceCreationTask.h" #include "InstanceCreationTask.h"
#include "settings/INISettingsObject.h"
#include "FileSystem.h"
#include <QDebug> //FIXME: remove this
#include <QFile> #include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h"
InstanceCreationTask::InstanceCreationTask() = default; InstanceCreationTask::InstanceCreationTask(BaseVersionPtr version)
{
m_version = version;
m_usingLoader = false;
}
InstanceCreationTask::InstanceCreationTask(BaseVersionPtr version, QString loader, BaseVersionPtr loaderVersion)
{
m_version = version;
m_usingLoader = true;
m_loader = loader;
m_loaderVersion = loaderVersion;
}
void InstanceCreationTask::executeTask() void InstanceCreationTask::executeTask()
{ {
setAbortable(true); setStatus(tr("Creating instance from version %1").arg(m_version->name()));
{
if (updateInstance()) { auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
emitSucceeded(); instanceSettings->suspendSave();
return; MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
auto components = inst.getPackProfile();
components->buildingFromScratch();
components->setComponentVersion("net.minecraft", m_version->descriptor(), true);
if(m_usingLoader)
components->setComponentVersion(m_loader, m_loaderVersion->descriptor());
inst.setName(m_instName);
inst.setIconKey(m_instIcon);
instanceSettings->resumeSave();
} }
// When the user aborted in the update stage.
if (m_abort) {
emitAborted();
return;
}
if (!createInstance()) {
if (m_abort)
return;
qWarning() << "Instance creation failed!";
if (!m_error_message.isEmpty())
qWarning() << "Reason: " << m_error_message;
emitFailed(tr("Error while creating new instance."));
return;
}
// If this is set, it means we're updating an instance. So, we now need to remove the
// files scheduled to, and we'd better not let the user abort in the middle of it, since it'd
// put the instance in an invalid state.
if (shouldOverride()) {
setAbortable(false);
setStatus(tr("Removing old conflicting files..."));
qDebug() << "Removing old files";
for (auto path : m_files_to_remove) {
if (!QFile::exists(path))
continue;
qDebug() << "Removing" << path;
if (!QFile::remove(path)) {
qCritical() << "Couldn't remove the old conflicting files.";
emitFailed(tr("Failed to remove old conflicting files."));
return;
}
}
}
emitSucceeded(); emitSucceeded();
return;
} }

View File

@ -1,46 +1,26 @@
#pragma once #pragma once
#include "tasks/Task.h"
#include "net/NetJob.h"
#include <QUrl>
#include "settings/SettingsObject.h"
#include "BaseVersion.h" #include "BaseVersion.h"
#include "InstanceTask.h" #include "InstanceTask.h"
class InstanceCreationTask : public InstanceTask { class InstanceCreationTask : public InstanceTask
{
Q_OBJECT Q_OBJECT
public: public:
InstanceCreationTask(); explicit InstanceCreationTask(BaseVersionPtr version);
virtual ~InstanceCreationTask() = default; explicit InstanceCreationTask(BaseVersionPtr version, QString loader, BaseVersionPtr loaderVersion);
protected: protected:
void executeTask() final override; //! Entry point for tasks.
virtual void executeTask() override;
/** private: /* data */
* Tries to update an already existing instance. BaseVersionPtr m_version;
* bool m_usingLoader;
* This can be implemented by subclasses to provide a way of updating an already existing QString m_loader;
* instance, according to that implementation's concept of 'identity' (i.e. instances that BaseVersionPtr m_loaderVersion;
* are updates / downgrades of one another).
*
* If this returns true, createInstance() will not run, so you should do all update steps in here.
* Otherwise, createInstance() is run as normal.
*/
virtual bool updateInstance() { return false; };
/**
* Creates a new instance.
*
* Returns whether the instance creation was successful (true) or not (false).
*/
virtual bool createInstance() { return false; };
QString getError() const { return m_error_message; }
protected:
void setError(QString message) { m_error_message = message; };
protected:
bool m_abort = false;
QStringList m_files_to_remove;
private:
QString m_error_message;
}; };

View File

@ -35,25 +35,34 @@
*/ */
#include "InstanceImportTask.h" #include "InstanceImportTask.h"
#include <QtConcurrentRun>
#include "Application.h" #include "Application.h"
#include "BaseInstance.h"
#include "FileSystem.h" #include "FileSystem.h"
#include "MMCZip.h" #include "MMCZip.h"
#include "NullInstance.h" #include "NullInstance.h"
#include "icons/IconList.h" #include "icons/IconList.h"
#include "icons/IconUtils.h" #include "icons/IconUtils.h"
#include "modplatform/technic/TechnicPackProcessor.h"
#include "modplatform/modrinth/ModrinthInstanceCreationTask.h"
#include "modplatform/flame/FlameInstanceCreationTask.h"
#include "settings/INISettingsObject.h" #include "settings/INISettingsObject.h"
#include <QtConcurrentRun> // FIXME: this does not belong here, it's Minecraft/Flame specific
#include <algorithm>
#include <quazip/quazipdir.h> #include <quazip/quazipdir.h>
#include "Json.h"
#include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h"
#include "modplatform/flame/FileResolvingTask.h"
#include "modplatform/flame/PackManifest.h"
#include "modplatform/modrinth/ModrinthPackManifest.h"
#include "modplatform/technic/TechnicPackProcessor.h"
#include "Application.h"
#include "icons/IconList.h"
#include "net/ChecksumValidator.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui/dialogs/ScrollMessageBox.h"
#include <algorithm>
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent) InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent)
{ {
@ -63,41 +72,35 @@ InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent)
bool InstanceImportTask::abort() bool InstanceImportTask::abort()
{ {
if (!canAbort())
return false;
if (m_filesNetJob) if (m_filesNetJob)
m_filesNetJob->abort(); m_filesNetJob->abort();
m_extractFuture.cancel(); m_extractFuture.cancel();
return Task::abort(); return false;
} }
void InstanceImportTask::executeTask() void InstanceImportTask::executeTask()
{ {
setAbortable(true); if (m_sourceUrl.isLocalFile())
{
if (m_sourceUrl.isLocalFile()) {
m_archivePath = m_sourceUrl.toLocalFile(); m_archivePath = m_sourceUrl.toLocalFile();
processZipPack(); processZipPack();
} else { }
else
{
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString())); setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
m_downloadRequired = true; m_downloadRequired = true;
const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path()); const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
auto entry = APPLICATION->metacache()->resolveEntry("general", path); auto entry = APPLICATION->metacache()->resolveEntry("general", path);
entry->setStale(true); entry->setStale(true);
m_archivePath = entry->getFullPath();
m_filesNetJob = new NetJob(tr("Modpack download"), APPLICATION->network()); m_filesNetJob = new NetJob(tr("Modpack download"), APPLICATION->network());
m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry)); m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
m_archivePath = entry->getFullPath();
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded); auto job = m_filesNetJob.get();
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged); connect(job, &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed); connect(job, &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted); connect(job, &NetJob::failed, this, &InstanceImportTask::downloadFailed);
m_filesNetJob->start(); m_filesNetJob->start();
} }
} }
@ -116,13 +119,7 @@ void InstanceImportTask::downloadFailed(QString reason)
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total) void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
{ {
setProgress(current, total); setProgress(current / 2, total);
}
void InstanceImportTask::downloadAborted()
{
emitAborted();
m_filesNetJob.reset();
} }
void InstanceImportTask::processZipPack() void InstanceImportTask::processZipPack()
@ -258,31 +255,291 @@ void InstanceImportTask::extractFinished()
void InstanceImportTask::extractAborted() void InstanceImportTask::extractAborted()
{ {
emitAborted(); emitFailed(tr("Instance import has been aborted."));
return;
} }
void InstanceImportTask::processFlame() void InstanceImportTask::processFlame()
{ {
auto* inst_creation_task = new FlameCreationTask(m_stagingPath, m_globalSettings, m_parent); const static QMap<QString,QString> forgemap = {
{"1.2.5", "3.4.9.171"},
{"1.4.2", "6.0.1.355"},
{"1.4.7", "6.6.2.534"},
{"1.5.2", "7.8.1.737"}
};
Flame::Manifest pack;
try
{
QString configPath = FS::PathCombine(m_stagingPath, "manifest.json");
Flame::loadManifest(pack, configPath);
QFile::remove(configPath);
}
catch (const JSONValidationError &e)
{
emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
return;
}
if(!pack.overrides.isEmpty())
{
QString overridePath = FS::PathCombine(m_stagingPath, pack.overrides);
if (QFile::exists(overridePath))
{
QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
if (!QFile::rename(overridePath, mcPath))
{
emitFailed(tr("Could not rename the overrides folder:\n") + pack.overrides);
return;
}
}
else
{
logWarning(tr("The specified overrides folder (%1) is missing. Maybe the modpack was already used before?").arg(pack.overrides));
}
}
inst_creation_task->setName(*this); QString forgeVersion;
inst_creation_task->setIcon(m_instIcon); QString fabricVersion;
inst_creation_task->setGroup(m_instGroup); // TODO: is Quilt relevant here?
for(auto &loader: pack.minecraft.modLoaders)
connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] { {
setOverride(inst_creation_task->shouldOverride()); auto id = loader.id;
emitSucceeded(); if(id.startsWith("forge-"))
{
id.remove("forge-");
forgeVersion = id;
continue;
}
if(id.startsWith("fabric-"))
{
id.remove("fabric-");
fabricVersion = id;
continue;
}
logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
}
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
auto mcVersion = pack.minecraft.version;
// Hack to correct some 'special sauce'...
if(mcVersion.endsWith('.'))
{
mcVersion.remove(QRegExp("[.]+$"));
logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack."));
}
auto components = instance.getPackProfile();
components->buildingFromScratch();
components->setComponentVersion("net.minecraft", mcVersion, true);
if(!forgeVersion.isEmpty())
{
// FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata.
if(forgeVersion == "recommended")
{
if(forgemap.contains(mcVersion))
{
forgeVersion = forgemap[mcVersion];
}
else
{
logWarning(tr("Could not map recommended Forge version for Minecraft %1").arg(mcVersion));
}
}
components->setComponentVersion("net.minecraftforge", forgeVersion);
}
if(!fabricVersion.isEmpty())
{
components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion);
}
if (m_instIcon != "default")
{
instance.setIconKey(m_instIcon);
}
else
{
if(pack.name.contains("Direwolf20"))
{
instance.setIconKey("steve");
}
else if(pack.name.contains("FTB") || pack.name.contains("Feed The Beast"))
{
instance.setIconKey("ftb_logo");
}
else
{
// default to something other than the MultiMC default to distinguish these
instance.setIconKey("flame");
}
}
QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods");
QFileInfo jarmodsInfo(jarmodsPath);
if(jarmodsInfo.isDir())
{
// install all the jar mods
qDebug() << "Found jarmods:";
QDir jarmodsDir(jarmodsPath);
QStringList jarMods;
for (auto info: jarmodsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
{
qDebug() << info.fileName();
jarMods.push_back(info.absoluteFilePath());
}
auto profile = instance.getPackProfile();
profile->installJarMods(jarMods);
// nuke the original files
FS::deletePath(jarmodsPath);
}
instance.setName(m_instName);
m_modIdResolver = new Flame::FileResolvingTask(APPLICATION->network(), pack);
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]()
{
auto results = m_modIdResolver->getResults();
//first check for blocked mods
QString text;
auto anyBlocked = false;
for(const auto& result: results.files.values()) {
if (!result.resolved || result.url.isEmpty()) {
text += QString("%1: <a href='%2'>%2</a><br/>").arg(result.fileName, result.websiteUrl);
anyBlocked = true;
}
}
if(anyBlocked) {
qWarning() << "Blocked mods found, displaying mod list";
auto message_dialog = new ScrollMessageBox(m_parent,
tr("Blocked mods found"),
tr("The following mods were blocked on third party launchers.<br/>"
"You will need to manually download them and add them to the modpack"),
text);
message_dialog->setModal(true);
message_dialog->show();
connect(message_dialog, &QDialog::rejected, [&]() {
m_modIdResolver.reset();
emitFailed("Canceled");
});
connect(message_dialog, &QDialog::accepted, [&]() {
m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
for (const auto &result: m_modIdResolver->getResults().files) {
QString filename = result.fileName;
if (!result.required) {
filename += ".disabled";
}
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
auto path = FS::PathCombine(m_stagingPath, relpath);
switch (result.type) {
case Flame::File::Type::Folder: {
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
// fall-through intentional, we treat these as plain old mods and dump them wherever.
}
case Flame::File::Type::SingleFile:
case Flame::File::Type::Mod: {
if (!result.url.isEmpty()) {
qDebug() << "Will download" << result.url << "to" << path;
auto dl = Net::Download::makeFile(result.url, path);
m_filesNetJob->addNetAction(dl);
}
break;
}
case Flame::File::Type::Modpack:
logWarning(
tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(
relpath));
break;
case Flame::File::Type::Cmod2:
case Flame::File::Type::Ctoc:
case Flame::File::Type::Unknown:
logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
break;
}
}
m_modIdResolver.reset();
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() {
m_filesNetJob.reset();
emitSucceeded();
}
);
connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason) {
m_filesNetJob.reset();
emitFailed(reason);
});
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
setProgress(current, total);
});
setStatus(tr("Downloading mods..."));
m_filesNetJob->start();
});
}else{
//TODO extract to function ?
m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
for (const auto &result: m_modIdResolver->getResults().files) {
QString filename = result.fileName;
if (!result.required) {
filename += ".disabled";
}
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
auto path = FS::PathCombine(m_stagingPath, relpath);
switch (result.type) {
case Flame::File::Type::Folder: {
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
// fall-through intentional, we treat these as plain old mods and dump them wherever.
}
case Flame::File::Type::SingleFile:
case Flame::File::Type::Mod: {
if (!result.url.isEmpty()) {
qDebug() << "Will download" << result.url << "to" << path;
auto dl = Net::Download::makeFile(result.url, path);
m_filesNetJob->addNetAction(dl);
}
break;
}
case Flame::File::Type::Modpack:
logWarning(
tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(
relpath));
break;
case Flame::File::Type::Cmod2:
case Flame::File::Type::Ctoc:
case Flame::File::Type::Unknown:
logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
break;
}
}
m_modIdResolver.reset();
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() {
m_filesNetJob.reset();
emitSucceeded();
}
);
connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason) {
m_filesNetJob.reset();
emitFailed(reason);
});
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
setProgress(current, total);
});
setStatus(tr("Downloading mods..."));
m_filesNetJob->start();
}
}
);
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason)
{
m_modIdResolver.reset();
emitFailed(tr("Unable to resolve mod IDs:\n") + reason);
}); });
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed); connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, [&](qint64 current, qint64 total)
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress); {
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus); setProgress(current, total);
connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater); });
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, [&](QString status)
connect(this, &Task::aborted, inst_creation_task, &InstanceCreationTask::abort); {
connect(inst_creation_task, &Task::aborted, this, &Task::abort); setStatus(status);
connect(inst_creation_task, &Task::abortStatusChanged, this, &Task::setAbortable); });
m_modIdResolver->start();
inst_creation_task->start();
} }
void InstanceImportTask::processTechnic() void InstanceImportTask::processTechnic()
@ -290,7 +547,7 @@ void InstanceImportTask::processTechnic()
shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor = new Technic::TechnicPackProcessor(); shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor = new Technic::TechnicPackProcessor();
connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &InstanceImportTask::emitSucceeded); connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &InstanceImportTask::emitSucceeded);
connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &InstanceImportTask::emitFailed); connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &InstanceImportTask::emitFailed);
packProcessor->run(m_globalSettings, name(), m_instIcon, m_stagingPath); packProcessor->run(m_globalSettings, m_instName, m_instIcon, m_stagingPath);
} }
void InstanceImportTask::processMultiMC() void InstanceImportTask::processMultiMC()
@ -304,7 +561,7 @@ void InstanceImportTask::processMultiMC()
instance.resetTimePlayed(); instance.resetTimePlayed();
// set a new nice name // set a new nice name
instance.setName(name()); instance.setName(m_instName);
// if the icon was specified by user, use that. otherwise pull icon from the pack // if the icon was specified by user, use that. otherwise pull icon from the pack
if (m_instIcon != "default") { if (m_instIcon != "default") {
@ -325,26 +582,198 @@ void InstanceImportTask::processMultiMC()
emitSucceeded(); emitSucceeded();
} }
// https://docs.modrinth.com/docs/modpacks/format_definition/
void InstanceImportTask::processModrinth() void InstanceImportTask::processModrinth()
{ {
auto* inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, m_sourceUrl.toString()); std::vector<Modrinth::File> files;
QString minecraftVersion, fabricVersion, quiltVersion, forgeVersion;
try {
QString indexPath = FS::PathCombine(m_stagingPath, "modrinth.index.json");
auto doc = Json::requireDocument(indexPath);
auto obj = Json::requireObject(doc, "modrinth.index.json");
int formatVersion = Json::requireInteger(obj, "formatVersion", "modrinth.index.json");
if (formatVersion == 1) {
auto game = Json::requireString(obj, "game", "modrinth.index.json");
if (game != "minecraft") {
throw JSONValidationError("Unknown game: " + game);
}
inst_creation_task->setName(*this); auto jsonFiles = Json::requireIsArrayOf<QJsonObject>(obj, "files", "modrinth.index.json");
inst_creation_task->setIcon(m_instIcon); bool had_optional = false;
inst_creation_task->setGroup(m_instGroup); for (auto modInfo : jsonFiles) {
Modrinth::File file;
file.path = Json::requireString(modInfo, "path");
auto env = Json::ensureObject(modInfo, "env");
// 'env' field is optional
if (!env.isEmpty()) {
QString support = Json::ensureString(env, "client", "unsupported");
if (support == "unsupported") {
continue;
} else if (support == "optional") {
// TODO: Make a review dialog for choosing which ones the user wants!
if (!had_optional) {
had_optional = true;
auto info = CustomMessageBox::selectable(
m_parent, tr("Optional mod detected!"),
tr("One or more mods from this modpack are optional. They will be downloaded, but disabled by default!"),
QMessageBox::Information);
info->exec();
}
if (file.path.endsWith(".jar"))
file.path += ".disabled";
}
}
QJsonObject hashes = Json::requireObject(modInfo, "hashes");
QString hash;
QCryptographicHash::Algorithm hashAlgorithm;
hash = Json::ensureString(hashes, "sha1");
hashAlgorithm = QCryptographicHash::Sha1;
if (hash.isEmpty()) {
hash = Json::ensureString(hashes, "sha512");
hashAlgorithm = QCryptographicHash::Sha512;
if (hash.isEmpty()) {
hash = Json::ensureString(hashes, "sha256");
hashAlgorithm = QCryptographicHash::Sha256;
if (hash.isEmpty()) {
throw JSONValidationError("No hash found for: " + file.path);
}
}
}
file.hash = QByteArray::fromHex(hash.toLatin1());
file.hashAlgorithm = hashAlgorithm;
// Do not use requireUrl, which uses StrictMode, instead use QUrl's default TolerantMode
// (as Modrinth seems to incorrectly handle spaces)
auto download_arr = Json::ensureArray(modInfo, "downloads");
for(auto download : download_arr) {
qWarning() << download.toString();
bool is_last = download.toString() == download_arr.last().toString();
auto download_url = QUrl(download.toString());
if (!download_url.isValid()) {
qDebug() << QString("Download URL (%1) for %2 is not a correctly formatted URL")
.arg(download_url.toString(), file.path);
if(is_last && file.downloads.isEmpty())
throw JSONValidationError(tr("Download URL for %1 is not a correctly formatted URL").arg(file.path));
}
else {
file.downloads.push_back(download_url);
}
}
files.push_back(file);
}
auto dependencies = Json::requireObject(obj, "dependencies", "modrinth.index.json");
for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) {
QString name = it.key();
if (name == "minecraft") {
minecraftVersion = Json::requireString(*it, "Minecraft version");
}
else if (name == "fabric-loader") {
fabricVersion = Json::requireString(*it, "Fabric Loader version");
}
else if (name == "quilt-loader") {
quiltVersion = Json::requireString(*it, "Quilt Loader version");
}
else if (name == "forge") {
forgeVersion = Json::requireString(*it, "Forge version");
}
else {
throw JSONValidationError("Unknown dependency type: " + name);
}
}
} else {
throw JSONValidationError(QStringLiteral("Unknown format version: %s").arg(formatVersion));
}
QFile::remove(indexPath);
} catch (const JSONValidationError& e) {
emitFailed(tr("Could not understand pack index:\n") + e.cause());
return;
}
connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] { auto mcPath = FS::PathCombine(m_stagingPath, ".minecraft");
setOverride(inst_creation_task->shouldOverride());
emitSucceeded(); auto override_path = FS::PathCombine(m_stagingPath, "overrides");
if (QFile::exists(override_path)) {
if (!QFile::rename(override_path, mcPath)) {
emitFailed(tr("Could not rename the overrides folder:\n") + "overrides");
return;
}
}
// Do client overrides
auto client_override_path = FS::PathCombine(m_stagingPath, "client-overrides");
if (QFile::exists(client_override_path)) {
if (!FS::overrideFolder(mcPath, client_override_path)) {
emitFailed(tr("Could not rename the client overrides folder:\n") + "client overrides");
return;
}
}
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
auto components = instance.getPackProfile();
components->buildingFromScratch();
components->setComponentVersion("net.minecraft", minecraftVersion, true);
if (!fabricVersion.isEmpty())
components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion, true);
if (!quiltVersion.isEmpty())
components->setComponentVersion("org.quiltmc.quilt-loader", quiltVersion, true);
if (!forgeVersion.isEmpty())
components->setComponentVersion("net.minecraftforge", forgeVersion, true);
if (m_instIcon != "default")
{
instance.setIconKey(m_instIcon);
}
else
{
instance.setIconKey("modrinth");
}
instance.setName(m_instName);
instance.saveNow();
m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
for (auto file : files)
{
auto path = FS::PathCombine(m_stagingPath, ".minecraft", file.path);
qDebug() << "Will try to download" << file.downloads.front() << "to" << path;
auto dl = Net::Download::makeFile(file.downloads.dequeue(), path);
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
m_filesNetJob->addNetAction(dl);
if (file.downloads.size() > 0) {
// FIXME: This really needs to be put into a ConcurrentTask of
// MultipleOptionsTask's , once those exist :)
connect(dl.get(), &NetAction::failed, [this, &file, path, dl]{
auto dl = Net::Download::makeFile(file.downloads.dequeue(), path);
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
m_filesNetJob->addNetAction(dl);
dl->succeeded();
});
}
}
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
{
m_filesNetJob.reset();
emitSucceeded();
}
);
connect(m_filesNetJob.get(), &NetJob::failed, [&](const QString &reason)
{
m_filesNetJob.reset();
emitFailed(reason);
}); });
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed); connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress); {
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus); setProgress(current, total);
connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater); });
setStatus(tr("Downloading mods..."));
connect(this, &Task::aborted, inst_creation_task, &InstanceCreationTask::abort); m_filesNetJob->start();
connect(inst_creation_task, &Task::aborted, this, &Task::abort);
connect(inst_creation_task, &Task::abortStatusChanged, this, &Task::setAbortable);
inst_creation_task->start();
} }

View File

@ -44,7 +44,7 @@
#include "QObjectPtr.h" #include "QObjectPtr.h"
#include "modplatform/flame/PackManifest.h" #include "modplatform/flame/PackManifest.h"
#include <optional> #include <nonstd/optional>
class QuaZip; class QuaZip;
namespace Flame namespace Flame
@ -58,6 +58,7 @@ class InstanceImportTask : public InstanceTask
public: public:
explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr); explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr);
bool canAbort() const override { return true; }
bool abort() override; bool abort() override;
const QVector<Flame::File> &getBlockedFiles() const const QVector<Flame::File> &getBlockedFiles() const
{ {
@ -79,7 +80,6 @@ private slots:
void downloadSucceeded(); void downloadSucceeded();
void downloadFailed(QString reason); void downloadFailed(QString reason);
void downloadProgressChanged(qint64 current, qint64 total); void downloadProgressChanged(qint64 current, qint64 total);
void downloadAborted();
void extractFinished(); void extractFinished();
void extractAborted(); void extractAborted();
@ -90,8 +90,8 @@ private: /* data */
QString m_archivePath; QString m_archivePath;
bool m_downloadRequired = false; bool m_downloadRequired = false;
std::unique_ptr<QuaZip> m_packZip; std::unique_ptr<QuaZip> m_packZip;
QFuture<std::optional<QStringList>> m_extractFuture; QFuture<nonstd::optional<QStringList>> m_extractFuture;
QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher; QFutureWatcher<nonstd::optional<QStringList>> m_extractFutureWatcher;
QVector<Flame::File> m_blockedMods; QVector<Flame::File> m_blockedMods;
enum class ModpackType{ enum class ModpackType{
Unknown, Unknown,

File diff suppressed because it is too large Load Diff

View File

@ -19,15 +19,13 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QSet> #include <QSet>
#include <QList> #include <QList>
#include <QStack>
#include <QPair>
#include "BaseInstance.h" #include "BaseInstance.h"
#include "QObjectPtr.h"
class QFileSystemWatcher; class QFileSystemWatcher;
class InstanceTask; class InstanceTask;
struct InstanceName;
using InstanceId = QString; using InstanceId = QString;
using GroupId = QString; using GroupId = QString;
using InstanceLocator = std::pair<InstancePtr, int>; using InstanceLocator = std::pair<InstancePtr, int>;
@ -48,12 +46,6 @@ enum class GroupsState
Dirty Dirty
}; };
struct TrashHistoryItem {
QString id;
QString polyPath;
QString trashPath;
QString groupName;
};
class InstanceList : public QAbstractListModel class InstanceList : public QAbstractListModel
{ {
@ -101,10 +93,7 @@ public:
InstListError loadList(); InstListError loadList();
void saveNow(); void saveNow();
/* O(n) */
InstancePtr getInstanceById(QString id) const; InstancePtr getInstanceById(QString id) const;
/* O(n) */
InstancePtr getInstanceByManagedName(const QString& managed_name) const;
QModelIndex getInstanceIndexById(const QString &id) const; QModelIndex getInstanceIndexById(const QString &id) const;
QStringList getGroups(); QStringList getGroups();
bool isGroupCollapsed(const QString &groupName); bool isGroupCollapsed(const QString &groupName);
@ -113,9 +102,6 @@ public:
void setInstanceGroup(const InstanceId & id, const GroupId& name); void setInstanceGroup(const InstanceId & id, const GroupId& name);
void deleteGroup(const GroupId & name); void deleteGroup(const GroupId & name);
bool trashInstance(const InstanceId &id);
bool trashedSomething();
void undoTrashInstance();
void deleteInstance(const InstanceId & id); void deleteInstance(const InstanceId & id);
// Wrap an instance creation task in some more task machinery and make it ready to be used // Wrap an instance creation task in some more task machinery and make it ready to be used
@ -130,10 +116,8 @@ public:
/** /**
* Commit the staging area given by @keyPath to the provider - used when creation succeeds. * Commit the staging area given by @keyPath to the provider - used when creation succeeds.
* Used by instance manipulation tasks. * Used by instance manipulation tasks.
* should_override is used when another similar instance already exists, and we want to override it
* - for instance, when updating it.
*/ */
bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, const QString& groupName, bool should_override); bool commitStagedInstance(const QString & keyPath, const QString& instanceName, const QString & groupName);
/** /**
* Destroy a previously created staging area given by @keyPath - used when creation fails. * Destroy a previously created staging area given by @keyPath - used when creation fails.
@ -196,6 +180,4 @@ private:
QSet<InstanceId> instanceSet; QSet<InstanceId> instanceSet;
bool m_groupsLoaded = false; bool m_groupsLoaded = false;
bool m_instancesProbed = false; bool m_instancesProbed = false;
QStack<TrashHistoryItem> m_trashHistory;
}; };

View File

@ -33,13 +33,13 @@ public:
values.append(new LogPage(inst)); values.append(new LogPage(inst));
std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst); std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst);
values.append(new VersionPage(onesix.get())); values.append(new VersionPage(onesix.get()));
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList()); auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList(), "mods", "loadermods", tr("Mods"), "Loader-mods");
modsPage->setFilter("%1 (*.zip *.jar *.litemod)"); modsPage->setFilter("%1 (*.zip *.jar *.litemod)");
values.append(modsPage); values.append(modsPage);
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList())); values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList(), "coremods", "coremods", tr("Core mods"), "Core-mods"));
values.append(new ResourcePackPage(onesix.get(), onesix->resourcePackList())); values.append(new ResourcePackPage(onesix.get()));
values.append(new TexturePackPage(onesix.get(), onesix->texturePackList())); values.append(new TexturePackPage(onesix.get()));
values.append(new ShaderPackPage(onesix.get(), onesix->shaderPackList())); values.append(new ShaderPackPage(onesix.get()));
values.append(new NotesPage(onesix.get())); values.append(new NotesPage(onesix.get()));
values.append(new WorldListPage(onesix.get(), onesix->worldList())); values.append(new WorldListPage(onesix.get(), onesix->worldList()));
values.append(new ServersPage(onesix)); values.append(new ServersPage(onesix));

View File

@ -1,52 +1,9 @@
#include "InstanceTask.h" #include "InstanceTask.h"
#include "ui/dialogs/CustomMessageBox.h" InstanceTask::InstanceTask()
InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name)
{ {
auto dialog =
CustomMessageBox::selectable(parent, QObject::tr("Change instance name"),
QObject::tr("The instance's name seems to include the old version. Would you like to update it?\n\n"
"Old name: %1\n"
"New name: %2")
.arg(old_name, new_name),
QMessageBox::Question, QMessageBox::No | QMessageBox::Yes);
auto result = dialog->exec();
if (result == QMessageBox::Yes)
return InstanceNameChange::ShouldChange;
return InstanceNameChange::ShouldKeep;
} }
QString InstanceName::name() const InstanceTask::~InstanceTask()
{ {
if (!m_modified_name.isEmpty())
return modifiedName();
return QString("%1 %2").arg(m_original_name, m_original_version);
} }
QString InstanceName::originalName() const
{
return m_original_name;
}
QString InstanceName::modifiedName() const
{
if (!m_modified_name.isEmpty())
return m_modified_name;
return m_original_name;
}
QString InstanceName::version() const
{
return m_original_version;
}
void InstanceName::setName(InstanceName& other)
{
m_original_name = other.m_original_name;
m_original_version = other.m_original_version;
m_modified_name = other.m_modified_name;
}
InstanceTask::InstanceTask() : Task(), InstanceName() {}

View File

@ -1,57 +1,52 @@
#pragma once #pragma once
#include "settings/SettingsObject.h"
#include "tasks/Task.h" #include "tasks/Task.h"
#include "settings/SettingsObject.h"
/* Helpers */ class InstanceTask : public Task
enum class InstanceNameChange { ShouldChange, ShouldKeep }; {
[[nodiscard]] InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name);
struct InstanceName {
public:
InstanceName() = default;
InstanceName(QString name, QString version) : m_original_name(std::move(name)), m_original_version(std::move(version)) {}
[[nodiscard]] QString modifiedName() const;
[[nodiscard]] QString originalName() const;
[[nodiscard]] QString name() const;
[[nodiscard]] QString version() const;
void setName(QString name) { m_modified_name = name; }
void setName(InstanceName& other);
protected:
QString m_original_name;
QString m_original_version;
QString m_modified_name;
};
class InstanceTask : public Task, public InstanceName {
Q_OBJECT Q_OBJECT
public: public:
InstanceTask(); explicit InstanceTask();
~InstanceTask() override = default; virtual ~InstanceTask();
void setParentSettings(SettingsObjectPtr settings) { m_globalSettings = settings; } void setParentSettings(SettingsObjectPtr settings)
{
m_globalSettings = settings;
}
void setStagingPath(const QString& stagingPath) { m_stagingPath = stagingPath; } void setStagingPath(const QString &stagingPath)
{
m_stagingPath = stagingPath;
}
void setIcon(const QString& icon) { m_instIcon = icon; } void setName(const QString &name)
{
m_instName = name;
}
QString name() const
{
return m_instName;
}
void setGroup(const QString& group) { m_instGroup = group; } void setIcon(const QString &icon)
QString group() const { return m_instGroup; } {
m_instIcon = icon;
}
bool shouldOverride() const { return m_override_existing; } void setGroup(const QString &group)
{
m_instGroup = group;
}
QString group() const
{
return m_instGroup;
}
protected: protected: /* data */
void setOverride(bool override) { m_override_existing = override; }
protected: /* data */
SettingsObjectPtr m_globalSettings; SettingsObjectPtr m_globalSettings;
QString m_instName;
QString m_instIcon; QString m_instIcon;
QString m_instGroup; QString m_instGroup;
QString m_stagingPath; QString m_stagingPath;
bool m_override_existing = false;
}; };

View File

@ -1,47 +1,10 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - 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 "JavaCommon.h" #include "JavaCommon.h"
#include "java/JavaUtils.h"
#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/CustomMessageBox.h"
#include <MMCStrings.h> #include <MMCStrings.h>
#include <QRegularExpression>
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent) bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
{ {
if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegularExpression("-Xm[sx]")) if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegExp("-Xm[sx]"))
|| jvmargs.contains("-XX-MaxHeapSize") || jvmargs.contains("-XX:InitialHeapSize")) || jvmargs.contains("-XX-MaxHeapSize") || jvmargs.contains("-XX:InitialHeapSize"))
{ {
auto warnStr = QObject::tr( auto warnStr = QObject::tr(
@ -55,7 +18,7 @@ bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
return false; return false;
} }
// block lunacy with passing required version to the JVM // block lunacy with passing required version to the JVM
if (jvmargs.contains(QRegularExpression("-version:.*"))) { if (jvmargs.contains(QRegExp("-version:.*"))) {
auto warnStr = QObject::tr( auto warnStr = QObject::tr(
"You tried to pass required Java version argument to the JVM (using \"-version:xxx\"). This is not safe and will not be allowed.\n" "You tried to pass required Java version argument to the JVM (using \"-version:xxx\"). This is not safe and will not be allowed.\n"
"This message will be displayed until you remove this from the JVM arguments."); "This message will be displayed until you remove this from the JVM arguments.");
@ -102,13 +65,6 @@ void JavaCommon::javaBinaryWasBad(QWidget *parent, JavaCheckResult result)
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show(); CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
} }
void JavaCommon::javaCheckNotFound(QWidget *parent)
{
QString text;
text += QObject::tr("Java checker library could not be found. Please check your installation.");
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
}
void JavaCommon::TestCheck::run() void JavaCommon::TestCheck::run()
{ {
if (!JavaCommon::checkJVMArgs(m_args, m_parent)) if (!JavaCommon::checkJVMArgs(m_args, m_parent))
@ -116,11 +72,6 @@ void JavaCommon::TestCheck::run()
emit finished(); emit finished();
return; return;
} }
if (JavaUtils::getJavaCheckPath().isEmpty()) {
javaCheckNotFound(m_parent);
emit finished();
return;
}
checker.reset(new JavaChecker()); checker.reset(new JavaChecker());
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
SLOT(checkFinished(JavaCheckResult))); SLOT(checkFinished(JavaCheckResult)));

View File

@ -10,14 +10,12 @@ namespace JavaCommon
{ {
bool checkJVMArgs(QString args, QWidget *parent); bool checkJVMArgs(QString args, QWidget *parent);
// Show a dialog saying that the Java binary was usable
void javaWasOk(QWidget *parent, JavaCheckResult result);
// Show a dialog saying that the Java binary was not usable because of bad options
void javaArgsWereBad(QWidget *parent, JavaCheckResult result);
// Show a dialog saying that the Java binary was not usable // Show a dialog saying that the Java binary was not usable
void javaBinaryWasBad(QWidget *parent, JavaCheckResult result); void javaBinaryWasBad(QWidget *parent, JavaCheckResult result);
// Show a dialog if we couldn't find Java Checker // Show a dialog saying that the Java binary was not usable because of bad options
void javaCheckNotFound(QWidget *parent); void javaArgsWereBad(QWidget *parent, JavaCheckResult result);
// Show a dialog saying that the Java binary was usable
void javaWasOk(QWidget *parent, JavaCheckResult result);
class TestCheck : public QObject class TestCheck : public QObject
{ {

View File

@ -1,37 +1,4 @@
// SPDX-License-Identifier: GPL-3.0-only // Licensed under the Apache-2.0 license. See README.md for details.
/*
* PolyMC - 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 "Json.h" #include "Json.h"
@ -55,6 +22,14 @@ void write(const QJsonArray &array, const QString &filename)
write(QJsonDocument(array), filename); write(QJsonDocument(array), filename);
} }
QByteArray toBinary(const QJsonObject &obj)
{
return QJsonDocument(obj).toBinaryData();
}
QByteArray toBinary(const QJsonArray &array)
{
return QJsonDocument(array).toBinaryData();
}
QByteArray toText(const QJsonObject &obj) QByteArray toText(const QJsonObject &obj)
{ {
return QJsonDocument(obj).toJson(QJsonDocument::Compact); return QJsonDocument(obj).toJson(QJsonDocument::Compact);
@ -73,8 +48,12 @@ QJsonDocument requireDocument(const QByteArray &data, const QString &what)
{ {
if (isBinaryJson(data)) if (isBinaryJson(data))
{ {
// FIXME: Is this needed? QJsonDocument doc = QJsonDocument::fromBinaryData(data);
throw JsonException(what + ": Invalid JSON. Binary JSON unsupported"); if (doc.isNull())
{
throw JsonException(what + ": Invalid JSON (binary JSON detected)");
}
return doc;
} }
else else
{ {

View File

@ -1,37 +1,4 @@
// SPDX-License-Identifier: GPL-3.0-only // Licensed under the Apache-2.0 license. See README.md for details.
/*
* PolyMC - 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 #pragma once
@ -62,6 +29,8 @@ void write(const QJsonObject &object, const QString &filename);
/// @throw FileSystemException /// @throw FileSystemException
void write(const QJsonArray &array, const QString &filename); void write(const QJsonArray &array, const QString &filename);
QByteArray toBinary(const QJsonObject &obj);
QByteArray toBinary(const QJsonArray &array);
QByteArray toText(const QJsonObject &obj); QByteArray toText(const QJsonObject &obj);
QByteArray toText(const QJsonArray &array); QByteArray toText(const QJsonArray &array);

View File

@ -93,8 +93,8 @@ void LaunchController::decideAccount()
auto reply = CustomMessageBox::selectable( auto reply = CustomMessageBox::selectable(
m_parentWidget, m_parentWidget,
tr("No Accounts"), tr("No Accounts"),
tr("In order to play Minecraft, you must have at least one Microsoft or Mojang " tr("In order to play Minecraft, you must have at least one Mojang or Microsoft "
"account logged in. Mojang accounts can only be used offline. " "account logged in. "
"Would you like to open the account manager to add an account now?"), "Would you like to open the account manager to add an account now?"),
QMessageBox::Information, QMessageBox::Information,
QMessageBox::Yes | QMessageBox::No QMessageBox::Yes | QMessageBox::No
@ -145,29 +145,18 @@ void LaunchController::login() {
return; return;
} }
// we try empty password first :)
QString password;
// we loop until the user succeeds in logging in or gives up // we loop until the user succeeds in logging in or gives up
bool tryagain = true; bool tryagain = true;
unsigned int tries = 0; // the failure. the default failure.
const QString needLoginAgain = tr("Your account is currently not logged in. Please enter your password to log in again. <br /> <br /> This could be caused by a password change.");
QString failReason = needLoginAgain;
while (tryagain) while (tryagain)
{ {
if (tries > 0 && tries % 3 == 0) {
auto result = QMessageBox::question(
m_parentWidget,
tr("Continue launch?"),
tr("It looks like we couldn't launch after %1 tries. Do you want to continue trying?")
.arg(tries)
);
if (result == QMessageBox::No) {
emitAborted();
return;
}
}
tries++;
m_session = std::make_shared<AuthSession>(); m_session = std::make_shared<AuthSession>();
m_session->wants_online = m_online; m_session->wants_online = m_online;
m_session->demo = m_demo;
m_accountToUse->fillSession(m_session); m_accountToUse->fillSession(m_session);
// Launch immediately in true offline mode // Launch immediately in true offline mode
@ -185,18 +174,12 @@ void LaunchController::login() {
if(!m_session->wants_online) { if(!m_session->wants_online) {
// we ask the user for a player name // we ask the user for a player name
bool ok = false; bool ok = false;
QString message = tr("Choose your offline mode player name.");
if(m_session->demo) {
message = tr("Choose your demo mode player name.");
}
QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString(); QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString();
QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName; QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName;
QString name = QInputDialog::getText( QString name = QInputDialog::getText(
m_parentWidget, m_parentWidget,
tr("Player name"), tr("Player name"),
message, tr("Choose your offline mode player name."),
QLineEdit::Normal, QLineEdit::Normal,
usedname, usedname,
&ok &ok
@ -376,13 +359,13 @@ void LaunchController::launchInstance()
} }
m_launcher->prependStep(new TextPrint(m_launcher.get(), resolved_servers, MessageLevel::Launcher)); m_launcher->prependStep(new TextPrint(m_launcher.get(), resolved_servers, MessageLevel::Launcher));
} else { } else {
online_mode = m_demo ? "demo" : "offline"; online_mode = "offline";
} }
m_launcher->prependStep(new TextPrint(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher)); m_launcher->prependStep(new TextPrint(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher));
// Prepend Version // Prepend Version
m_launcher->prependStep(new TextPrint(m_launcher.get(), BuildConfig.LAUNCHER_DISPLAYNAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher)); m_launcher->prependStep(new TextPrint(m_launcher.get(), BuildConfig.LAUNCHER_NAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher));
m_launcher->start(); m_launcher->start();
} }

View File

@ -63,10 +63,6 @@ public:
m_online = online; m_online = online;
} }
void setDemo(bool demo) {
m_demo = demo;
}
void setProfiler(BaseProfilerFactory *profiler) { void setProfiler(BaseProfilerFactory *profiler) {
m_profiler = profiler; m_profiler = profiler;
} }
@ -105,7 +101,6 @@ private slots:
private: private:
BaseProfilerFactory *m_profiler = nullptr; BaseProfilerFactory *m_profiler = nullptr;
bool m_online = true; bool m_online = true;
bool m_demo = false;
InstancePtr m_instance; InstancePtr m_instance;
QWidget * m_parentWidget = nullptr; QWidget * m_parentWidget = nullptr;
InstanceWindow *m_console = nullptr; InstanceWindow *m_console = nullptr;

View File

@ -18,17 +18,13 @@ LAUNCHER_NAME=@Launcher_APP_BINARY_NAME@
LAUNCHER_DIR="$(dirname "$(readlink -f "$0")")" LAUNCHER_DIR="$(dirname "$(readlink -f "$0")")"
echo "Launcher Dir: ${LAUNCHER_DIR}" echo "Launcher Dir: ${LAUNCHER_DIR}"
# Set up env. # Set up env - filter out input LD_ variables but pass them in under different names
# Pass our custom variables separately so that the launcher can remove them for child processes export GAME_LIBRARY_PATH=${GAME_LIBRARY_PATH-${LD_LIBRARY_PATH}}
export LAUNCHER_LD_LIBRARY_PATH="${LAUNCHER_DIR}/lib@LIB_SUFFIX@" export GAME_PRELOAD=${GAME_PRELOAD-${LD_PRELOAD}}
export LAUNCHER_LD_PRELOAD="" export LD_LIBRARY_PATH="${LAUNCHER_DIR}/lib@LIB_SUFFIX@":$LAUNCHER_LIBRARY_PATH
export LAUNCHER_QT_PLUGIN_PATH="${LAUNCHER_DIR}/plugins" export LD_PRELOAD=$LAUNCHER_PRELOAD
export LAUNCHER_QT_FONTPATH="${LAUNCHER_DIR}/fonts" export QT_PLUGIN_PATH="${LAUNCHER_DIR}/plugins"
export QT_FONTPATH="${LAUNCHER_DIR}/fonts"
export LD_LIBRARY_PATH="$LAUNCHER_LD_LIBRARY_PATH:$LD_LIBRARY_PATH"
export LD_PRELOAD="$LAUNCHER_LD_PRELOAD:$LD_PRELOAD"
export QT_PLUGIN_PATH="$LAUNCHER_QT_PLUGIN_PATH:$QT_PLUGIN_PATH"
export QT_FONTPATH="$LAUNCHER_QT_FONTPATH:$QT_FONTPATH"
# Detect missing dependencies... # Detect missing dependencies...
DEPS_LIST=`ldd "${LAUNCHER_DIR}"/plugins/*/*.so 2>/dev/null | grep "not found" | sort -u | awk -vORS=", " '{ print $1 }'` DEPS_LIST=`ldd "${LAUNCHER_DIR}"/plugins/*/*.so 2>/dev/null | grep "not found" | sort -u | awk -vORS=", " '{ print $1 }'`

View File

@ -1,42 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - 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 "LoggedProcess.h" #include "LoggedProcess.h"
#include <QDebug>
#include <QTextDecoder>
#include "MessageLevel.h" #include "MessageLevel.h"
#include <QDebug>
LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent) LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent)
{ {
@ -44,11 +8,7 @@ LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent)
connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut); connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut);
connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr); connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr);
connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(on_exit(int,QProcess::ExitStatus))); connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(on_exit(int,QProcess::ExitStatus)));
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
connect(this, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError)));
#else
connect(this, SIGNAL(error(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError))); connect(this, SIGNAL(error(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError)));
#endif
connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange); connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange);
} }
@ -60,26 +20,25 @@ LoggedProcess::~LoggedProcess()
} }
} }
QStringList reprocess(const QByteArray& data, QTextDecoder& decoder) QStringList reprocess(const QByteArray & data, QString & leftover)
{ {
auto str = decoder.toUnicode(data); QString str = leftover + QString::fromLocal8Bit(data);
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed, QString::SkipEmptyParts); str.remove('\r');
#else QStringList lines = str.split("\n");
auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed, Qt::SkipEmptyParts); leftover = lines.takeLast();
#endif
return lines; return lines;
} }
void LoggedProcess::on_stdErr() void LoggedProcess::on_stdErr()
{ {
auto lines = reprocess(readAllStandardError(), m_err_decoder); auto lines = reprocess(readAllStandardError(), m_err_leftover);
emit log(lines, MessageLevel::StdErr); emit log(lines, MessageLevel::StdErr);
} }
void LoggedProcess::on_stdOut() void LoggedProcess::on_stdOut()
{ {
auto lines = reprocess(readAllStandardOutput(), m_out_decoder); auto lines = reprocess(readAllStandardOutput(), m_out_leftover);
emit log(lines, MessageLevel::StdOut); emit log(lines, MessageLevel::StdOut);
} }
@ -88,6 +47,18 @@ void LoggedProcess::on_exit(int exit_code, QProcess::ExitStatus status)
// save the exit code // save the exit code
m_exit_code = exit_code; m_exit_code = exit_code;
// Flush console window
if (!m_err_leftover.isEmpty())
{
emit log({m_err_leftover}, MessageLevel::StdErr);
m_err_leftover.clear();
}
if (!m_out_leftover.isEmpty())
{
emit log({m_err_leftover}, MessageLevel::StdOut);
m_out_leftover.clear();
}
// based on state, send signals // based on state, send signals
if (!m_is_aborting) if (!m_is_aborting)
{ {
@ -186,6 +157,19 @@ void LoggedProcess::on_stateChange(QProcess::ProcessState state)
} }
} }
#if defined Q_OS_WIN32
#include <windows.h>
#endif
qint64 LoggedProcess::processId() const
{
#ifdef Q_OS_WIN
return pid() ? pid()->dwProcessId : 0;
#else
return pid();
#endif
}
void LoggedProcess::setDetachable(bool detachable) void LoggedProcess::setDetachable(bool detachable)
{ {
m_is_detachable = detachable; m_is_detachable = detachable;

View File

@ -1,42 +1,21 @@
// SPDX-License-Identifier: GPL-3.0-only /* Copyright 2013-2021 MultiMC Contributors
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * Licensed under the Apache License, Version 2.0 (the "License");
* it under the terms of the GNU General Public License as published by * you may not use this file except in compliance with the License.
* the Free Software Foundation, version 3. * You may obtain a copy of the License at
* *
* This program is distributed in the hope that it will be useful, * http://www.apache.org/licenses/LICENSE-2.0
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * Unless required by applicable law or agreed to in writing, software
* along with this program. If not, see <https://www.gnu.org/licenses/>. * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* This file incorporates work covered by the following copyright and * See the License for the specific language governing permissions and
* permission notice: * limitations under the License.
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
#pragma once #pragma once
#include <QProcess> #include <QProcess>
#include <QTextDecoder>
#include "MessageLevel.h" #include "MessageLevel.h"
/* /*
@ -64,6 +43,7 @@ public:
State state() const; State state() const;
int exitCode() const; int exitCode() const;
qint64 processId() const;
void setDetachable(bool detachable); void setDetachable(bool detachable);
@ -89,8 +69,8 @@ private:
void changeState(LoggedProcess::State state); void changeState(LoggedProcess::State state);
private: private:
QTextDecoder m_err_decoder = QTextDecoder(QTextCodec::codecForLocale()); QString m_err_leftover;
QTextDecoder m_out_decoder = QTextDecoder(QTextCodec::codecForLocale()); QString m_out_leftover;
bool m_killed = false; bool m_killed = false;
State m_state = NotRunning; State m_state = NotRunning;
int m_exit_code = 0; int m_exit_code = 0;

View File

@ -127,7 +127,7 @@ bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList
} }
// ours // ours
bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods) bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods)
{ {
QuaZip zipOut(targetJarPath); QuaZip zipOut(targetJarPath);
if (!zipOut.open(QuaZip::mdCreate)) if (!zipOut.open(QuaZip::mdCreate))
@ -141,41 +141,42 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
QSet<QString> addedFiles; QSet<QString> addedFiles;
// Modify the jar // Modify the jar
// This needs to be done in reverse-order to ensure we respect the loading order of components QListIterator<Mod> i(mods);
for (auto i = mods.crbegin(); i != mods.crend(); i++) i.toBack();
while (i.hasPrevious())
{ {
const auto* mod = *i; const Mod &mod = i.previous();
// do not merge disabled mods. // do not merge disabled mods.
if (!mod->enabled()) if (!mod.enabled())
continue; continue;
if (mod->type() == ResourceType::ZIPFILE) if (mod.type() == Mod::MOD_ZIPFILE)
{ {
if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles)) if (!mergeZipFiles(&zipOut, mod.filename(), addedFiles))
{ {
zipOut.close(); zipOut.close();
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
return false; return false;
} }
} }
else if (mod->type() == ResourceType::SINGLEFILE) else if (mod.type() == Mod::MOD_SINGLEFILE)
{ {
// FIXME: buggy - does not work with addedFiles // FIXME: buggy - does not work with addedFiles
auto filename = mod->fileinfo(); auto filename = mod.filename();
if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName()))
{ {
zipOut.close(); zipOut.close();
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
return false; return false;
} }
addedFiles.insert(filename.fileName()); addedFiles.insert(filename.fileName());
} }
else if (mod->type() == ResourceType::FOLDER) else if (mod.type() == Mod::MOD_FOLDER)
{ {
// untested, but seems to be unused / not possible to reach // untested, but seems to be unused / not possible to reach
// FIXME: buggy - does not work with addedFiles // FIXME: buggy - does not work with addedFiles
auto filename = mod->fileinfo(); auto filename = mod.filename();
QString what_to_zip = filename.absoluteFilePath(); QString what_to_zip = filename.absoluteFilePath();
QDir dir(what_to_zip); QDir dir(what_to_zip);
dir.cdUp(); dir.cdUp();
@ -192,7 +193,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
{ {
zipOut.close(); zipOut.close();
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
return false; return false;
} }
qDebug() << "Adding folder " << filename.fileName() << " from " qDebug() << "Adding folder " << filename.fileName() << " from "
@ -203,7 +204,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
// Make sure we do not continue launching when something is missing or undefined... // Make sure we do not continue launching when something is missing or undefined...
zipOut.close(); zipOut.close();
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to add unknown mod type" << mod->fileinfo().fileName() << "to the jar."; qCritical() << "Failed to add unknown mod type" << mod.filename().fileName() << "to the jar.";
return false; return false;
} }
} }
@ -268,7 +269,7 @@ bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & re
// ours // ours
std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target) nonstd::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target)
{ {
QDir directory(target); QDir directory(target);
QStringList extracted; QStringList extracted;
@ -277,7 +278,7 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su
auto numEntries = zip->getEntriesCount(); auto numEntries = zip->getEntriesCount();
if(numEntries < 0) { if(numEntries < 0) {
qWarning() << "Failed to enumerate files in archive"; qWarning() << "Failed to enumerate files in archive";
return std::nullopt; return nonstd::nullopt;
} }
else if(numEntries == 0) { else if(numEntries == 0) {
qDebug() << "Extracting empty archives seems odd..."; qDebug() << "Extracting empty archives seems odd...";
@ -286,7 +287,7 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su
else if (!zip->goToFirstFile()) else if (!zip->goToFirstFile())
{ {
qWarning() << "Failed to seek to first file in zip"; qWarning() << "Failed to seek to first file in zip";
return std::nullopt; return nonstd::nullopt;
} }
do do
@ -304,7 +305,7 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su
QString path; QString path;
if(name.contains('/') && !name.endsWith('/')){ if(name.contains('/') && !name.endsWith('/')){
path = name.section('/', 0, -2) + "/"; path = name.section('/', 0, -2) + "/";
FS::ensureFolderPathExists(FS::PathCombine(target, path)); FS::ensureFolderPathExists(path);
name = name.split('/').last(); name = name.split('/').last();
} }
@ -323,7 +324,7 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su
{ {
qWarning() << "Failed to extract file" << original_name << "to" << absFilePath; qWarning() << "Failed to extract file" << original_name << "to" << absFilePath;
JlCompress::removeFile(extracted); JlCompress::removeFile(extracted);
return std::nullopt; return nonstd::nullopt;
} }
extracted.append(absFilePath); extracted.append(absFilePath);
@ -341,7 +342,7 @@ bool MMCZip::extractRelFile(QuaZip *zip, const QString &file, const QString &tar
} }
// ours // ours
std::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString dir) nonstd::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString dir)
{ {
QuaZip zip(fileCompressed); QuaZip zip(fileCompressed);
if (!zip.open(QuaZip::mdUnzip)) if (!zip.open(QuaZip::mdUnzip))
@ -352,13 +353,13 @@ std::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString di
return QStringList(); return QStringList();
} }
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();; qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();;
return std::nullopt; return nonstd::nullopt;
} }
return MMCZip::extractSubDir(&zip, "", dir); return MMCZip::extractSubDir(&zip, "", dir);
} }
// ours // ours
std::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString subdir, QString dir) nonstd::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString subdir, QString dir)
{ {
QuaZip zip(fileCompressed); QuaZip zip(fileCompressed);
if (!zip.open(QuaZip::mdUnzip)) if (!zip.open(QuaZip::mdUnzip))
@ -369,7 +370,7 @@ std::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString su
return QStringList(); return QStringList();
} }
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();; qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();;
return std::nullopt; return nonstd::nullopt;
} }
return MMCZip::extractSubDir(&zip, subdir, dir); return MMCZip::extractSubDir(&zip, subdir, dir);
} }
@ -420,7 +421,7 @@ bool MMCZip::collectFileListRecursively(const QString& rootDir, const QString& s
continue; continue;
} }
files->append(e); // we want the original paths for MMCZip::compressDirFiles files->append(e.filePath()); // we want the original paths for MMCZip::compressDirFiles
} }
return true; return true;
} }

View File

@ -42,7 +42,7 @@
#include <functional> #include <functional>
#include <quazip/JlCompress.h> #include <quazip/JlCompress.h>
#include <optional> #include <nonstd/optional>
namespace MMCZip namespace MMCZip
{ {
@ -75,7 +75,7 @@ namespace MMCZip
/** /**
* take a source jar, add mods to it, resulting in target jar * take a source jar, add mods to it, resulting in target jar
*/ */
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods); bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods);
/** /**
* Find a single file in archive by file name (not path) * Find a single file in archive by file name (not path)
@ -95,7 +95,7 @@ namespace MMCZip
/** /**
* Extract a subdirectory from an archive * Extract a subdirectory from an archive
*/ */
std::optional<QStringList> extractSubDir(QuaZip *zip, const QString & subdir, const QString &target); nonstd::optional<QStringList> extractSubDir(QuaZip *zip, const QString & subdir, const QString &target);
bool extractRelFile(QuaZip *zip, const QString & file, const QString &target); bool extractRelFile(QuaZip *zip, const QString & file, const QString &target);
@ -106,7 +106,7 @@ namespace MMCZip
* \param dir The directory to extract to, the current directory if left empty. * \param dir The directory to extract to, the current directory if left empty.
* \return The list of the full paths of the files extracted, empty on failure. * \return The list of the full paths of the files extracted, empty on failure.
*/ */
std::optional<QStringList> extractDir(QString fileCompressed, QString dir); nonstd::optional<QStringList> extractDir(QString fileCompressed, QString dir);
/** /**
* Extract a subdirectory from an archive * Extract a subdirectory from an archive
@ -116,7 +116,7 @@ namespace MMCZip
* \param dir The directory to extract to, the current directory if left empty. * \param dir The directory to extract to, the current directory if left empty.
* \return The list of the full paths of the files extracted, empty on failure. * \return The list of the full paths of the files extracted, empty on failure.
*/ */
std::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QString dir); nonstd::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QString dir);
/** /**
* Extract a single file from an archive into a directory * Extract a single file from an archive into a directory

View File

@ -1,56 +1,25 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
* 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/>.
*/
#include "ModDownloadTask.h" #include "ModDownloadTask.h"
#include "Application.h" #include "Application.h"
#include "minecraft/mod/ModFolderModel.h"
ModDownloadTask::ModDownloadTask(ModPlatform::IndexedPack mod, ModPlatform::IndexedVersion version, const std::shared_ptr<ModFolderModel> mods, bool is_indexed) ModDownloadTask::ModDownloadTask(const QUrl sourceUrl,const QString filename, const std::shared_ptr<ModFolderModel> mods)
: m_mod(mod), m_mod_version(version), mods(mods) : m_sourceUrl(sourceUrl), mods(mods), filename(filename) {
{ }
if (is_indexed) {
m_update_task.reset(new LocalModUpdateTask(mods->indexDir(), m_mod, m_mod_version));
connect(m_update_task.get(), &LocalModUpdateTask::hasOldMod, this, &ModDownloadTask::hasOldMod);
addTask(m_update_task); void ModDownloadTask::executeTask() {
} setStatus(tr("Downloading mod:\n%1").arg(m_sourceUrl.toString()));
m_filesNetJob.reset(new NetJob(tr("Mod download"), APPLICATION->network())); m_filesNetJob.reset(new NetJob(tr("Mod download"), APPLICATION->network()));
m_filesNetJob->setStatus(tr("Downloading mod:\n%1").arg(m_mod_version.downloadUrl)); m_filesNetJob->addNetAction(Net::Download::makeFile(m_sourceUrl, mods->dir().absoluteFilePath(filename)));
m_filesNetJob->addNetAction(Net::Download::makeFile(m_mod_version.downloadUrl, mods->dir().absoluteFilePath(getFilename())));
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ModDownloadTask::downloadSucceeded); connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ModDownloadTask::downloadSucceeded);
connect(m_filesNetJob.get(), &NetJob::progress, this, &ModDownloadTask::downloadProgressChanged); connect(m_filesNetJob.get(), &NetJob::progress, this, &ModDownloadTask::downloadProgressChanged);
connect(m_filesNetJob.get(), &NetJob::failed, this, &ModDownloadTask::downloadFailed); connect(m_filesNetJob.get(), &NetJob::failed, this, &ModDownloadTask::downloadFailed);
m_filesNetJob->start();
addTask(m_filesNetJob);
} }
void ModDownloadTask::downloadSucceeded() void ModDownloadTask::downloadSucceeded()
{ {
emitSucceeded();
m_filesNetJob.reset(); m_filesNetJob.reset();
auto name = std::get<0>(to_delete);
auto filename = std::get<1>(to_delete);
if (!name.isEmpty() && filename != m_mod_version.fileName) {
mods->uninstallMod(filename, true);
}
} }
void ModDownloadTask::downloadFailed(QString reason) void ModDownloadTask::downloadFailed(QString reason)
@ -64,9 +33,7 @@ void ModDownloadTask::downloadProgressChanged(qint64 current, qint64 total)
emit progress(current, total); emit progress(current, total);
} }
// This indirection is done so that we don't delete a mod before being sure it was bool ModDownloadTask::abort() {
// downloaded successfully! return m_filesNetJob->abort();
void ModDownloadTask::hasOldMod(QString name, QString filename)
{
to_delete = {name, filename};
} }

View File

@ -1,56 +1,34 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
* 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/>.
*/
#pragma once #pragma once
#include "QObjectPtr.h"
#include "tasks/Task.h"
#include "minecraft/mod/ModFolderModel.h"
#include "net/NetJob.h" #include "net/NetJob.h"
#include "tasks/SequentialTask.h" #include <QUrl>
#include "modplatform/ModIndex.h"
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
class ModFolderModel; class ModDownloadTask : public Task {
class ModDownloadTask : public SequentialTask {
Q_OBJECT Q_OBJECT
public: public:
explicit ModDownloadTask(ModPlatform::IndexedPack mod, ModPlatform::IndexedVersion version, const std::shared_ptr<ModFolderModel> mods, bool is_indexed = true); explicit ModDownloadTask(const QUrl sourceUrl, const QString filename, const std::shared_ptr<ModFolderModel> mods);
const QString& getFilename() const { return m_mod_version.fileName; } const QString& getFilename() const { return filename; }
public slots:
bool abort() override;
protected:
//! Entry point for tasks.
void executeTask() override;
private: private:
ModPlatform::IndexedPack m_mod; QUrl m_sourceUrl;
ModPlatform::IndexedVersion m_mod_version;
const std::shared_ptr<ModFolderModel> mods;
NetJob::Ptr m_filesNetJob; NetJob::Ptr m_filesNetJob;
LocalModUpdateTask::Ptr m_update_task; const std::shared_ptr<ModFolderModel> mods;
const QString filename;
void downloadProgressChanged(qint64 current, qint64 total); void downloadProgressChanged(qint64 current, qint64 total);
void downloadFailed(QString reason); void downloadFailed(QString reason);
void downloadSucceeded(); void downloadSucceeded();
std::tuple<QString, QString> to_delete {"", ""};
private slots:
void hasOldMod(QString name, QString filename);
}; };

View File

@ -1,38 +1,3 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - 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 #pragma once
#include "BaseInstance.h" #include "BaseInstance.h"
#include "launch/LaunchTask.h" #include "launch/LaunchTask.h"
@ -50,10 +15,6 @@ public:
void saveNow() override void saveNow() override
{ {
} }
void loadSpecificSettings() override
{
setSpecificSettingsLoaded(true);
}
QString getStatusbarDescription() override QString getStatusbarDescription() override
{ {
return tr("Unknown instance type"); return tr("Unknown instance type");
@ -78,11 +39,7 @@ public:
{ {
return QProcessEnvironment(); return QProcessEnvironment();
} }
QProcessEnvironment createLaunchEnvironment() override QMap<QString, QString> getVariables() const override
{
return QProcessEnvironment();
}
QMap<QString, QString> getVariables() override
{ {
return QMap<QString, QString>(); return QMap<QString, QString>();
} }
@ -119,8 +76,4 @@ public:
QString modsRoot() const override { QString modsRoot() const override {
return QString(); return QString();
} }
void updateRuntimeContext()
{
// NOOP
}
}; };

View File

@ -1,37 +1,89 @@
#pragma once #pragma once
#include <QObject>
#include <QSharedPointer>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <QObject>
namespace details
{
struct DeleteQObjectLater
{
void operator()(QObject *obj) const
{
obj->deleteLater();
}
};
}
/** /**
* A unique pointer class with unique pointer semantics intended for derivates of QObject * A unique pointer class with unique pointer semantics intended for derivates of QObject
* Calls deleteLater() instead of destroying the contained object immediately * Calls deleteLater() instead of destroying the contained object immediately
*/ */
template <typename T> template<typename T> using unique_qobject_ptr = std::unique_ptr<T, details::DeleteQObjectLater>;
using unique_qobject_ptr = QScopedPointer<T, QScopedPointerDeleteLater>;
/** /**
* A shared pointer class with shared pointer semantics intended for derivates of QObject * A shared pointer class with shared pointer semantics intended for derivates of QObject
* Calls deleteLater() instead of destroying the contained object immediately * Calls deleteLater() instead of destroying the contained object immediately
*/ */
template <typename T> template <typename T>
class shared_qobject_ptr : public QSharedPointer<T> { class shared_qobject_ptr
public: {
constexpr shared_qobject_ptr() : QSharedPointer<T>() {} public:
constexpr shared_qobject_ptr(T* ptr) : QSharedPointer<T>(ptr, &QObject::deleteLater) {} shared_qobject_ptr(){}
constexpr shared_qobject_ptr(std::nullptr_t null_ptr) : QSharedPointer<T>(null_ptr, &QObject::deleteLater) {} shared_qobject_ptr(T * wrap)
template <typename Derived>
constexpr shared_qobject_ptr(const shared_qobject_ptr<Derived>& other) : QSharedPointer<T>(other)
{}
void reset() { QSharedPointer<T>::reset(); }
void reset(const shared_qobject_ptr<T>& other)
{ {
shared_qobject_ptr<T> t(other); reset(wrap);
this->swap(t);
} }
shared_qobject_ptr(const shared_qobject_ptr<T>& other)
{
m_ptr = other.m_ptr;
}
template<typename Derived>
shared_qobject_ptr(const shared_qobject_ptr<Derived> &other)
{
m_ptr = other.unwrap();
}
public:
void reset(T * wrap)
{
using namespace std::placeholders;
m_ptr.reset(wrap, std::bind(&QObject::deleteLater, _1));
}
void reset(const shared_qobject_ptr<T> &other)
{
m_ptr = other.m_ptr;
}
void reset()
{
m_ptr.reset();
}
T * get() const
{
return m_ptr.get();
}
T * operator->() const
{
return m_ptr.get();
}
T & operator*() const
{
return *m_ptr.get();
}
operator bool() const
{
return m_ptr.get() != nullptr;
}
const std::shared_ptr <T> unwrap() const
{
return m_ptr;
}
bool operator==(const shared_qobject_ptr<T>& other) {
return m_ptr == other.m_ptr;
}
bool operator!=(const shared_qobject_ptr<T>& other) {
return m_ptr != other.m_ptr;
}
private:
std::shared_ptr <T> m_ptr;
}; };

View File

@ -1,88 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - 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/>.
*/
#pragma once
#include <QSet>
#include <QString>
#include "settings/SettingsObject.h"
struct RuntimeContext {
QString javaArchitecture;
QString javaRealArchitecture;
QString javaPath;
QString system;
QString mappedJavaRealArchitecture() const
{
if (javaRealArchitecture == "amd64")
return "x86_64";
if (javaRealArchitecture == "i386" || javaRealArchitecture == "i686")
return "x86";
if (javaRealArchitecture == "aarch64")
return "arm64";
if (javaRealArchitecture == "arm" || javaRealArchitecture == "armhf")
return "arm32";
return javaRealArchitecture;
}
void updateFromInstanceSettings(SettingsObjectPtr instanceSettings)
{
javaArchitecture = instanceSettings->get("JavaArchitecture").toString();
javaRealArchitecture = instanceSettings->get("JavaRealArchitecture").toString();
javaPath = instanceSettings->get("JavaPath").toString();
system = currentSystem();
}
QString getClassifier() const { return system + "-" + mappedJavaRealArchitecture(); }
// "Legacy" refers to the fact that Mojang assumed that these are the only two architectures
bool isLegacyArch() const
{
const QString mapped = mappedJavaRealArchitecture();
return mapped == "x86_64" || mapped == "x86";
}
bool classifierMatches(QString target) const
{
// try to match precise classifier "[os]-[arch]"
bool x = target == getClassifier();
// try to match imprecise classifier on legacy architectures "[os]"
if (!x && isLegacyArch())
x = target == system;
return x;
}
static QString currentSystem()
{
#if defined(Q_OS_LINUX)
return "linux";
#elif defined(Q_OS_MACOS)
return "osx";
#elif defined(Q_OS_WINDOWS)
return "windows";
#elif defined(Q_OS_FREEBSD)
return "freebsd";
#elif defined(Q_OS_OPENBSD)
return "openbsd";
#else
return "unknown";
#endif
}
};

View File

@ -93,7 +93,7 @@ void UpdateController::installUpdates()
qDebug() << "Installing updates."; qDebug() << "Installing updates.";
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
QString finishCmd = QApplication::applicationFilePath(); QString finishCmd = QApplication::applicationFilePath();
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined (Q_OS_OPENBSD) #elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
QString finishCmd = FS::PathCombine(m_root, BuildConfig.LAUNCHER_NAME); QString finishCmd = FS::PathCombine(m_root, BuildConfig.LAUNCHER_NAME);
#elif defined Q_OS_MAC #elif defined Q_OS_MAC
QString finishCmd = QApplication::applicationFilePath(); QString finishCmd = QApplication::applicationFilePath();
@ -358,7 +358,7 @@ void UpdateController::fail()
msg = QObject::tr( msg = QObject::tr(
"Couldn't replace file %1. Changes will be reverted.\n" "Couldn't replace file %1. Changes will be reverted.\n"
"See the %2 log file for details." "See the %2 log file for details."
).arg(m_failedFile, BuildConfig.LAUNCHER_DISPLAYNAME); ).arg(m_failedFile, BuildConfig.LAUNCHER_NAME);
doRollback = true; doRollback = true;
QMessageBox::critical(m_parent, failTitle, msg); QMessageBox::critical(m_parent, failTitle, msg);
break; break;
@ -368,7 +368,7 @@ void UpdateController::fail()
msg = QObject::tr( msg = QObject::tr(
"Couldn't remove file %1. Changes will be reverted.\n" "Couldn't remove file %1. Changes will be reverted.\n"
"See the %2 log file for details." "See the %2 log file for details."
).arg(m_failedFile, BuildConfig.LAUNCHER_DISPLAYNAME); ).arg(m_failedFile, BuildConfig.LAUNCHER_NAME);
doRollback = true; doRollback = true;
QMessageBox::critical(m_parent, failTitle, msg); QMessageBox::critical(m_parent, failTitle, msg);
break; break;
@ -399,7 +399,7 @@ void UpdateController::fail()
{ {
msg = QObject::tr("The rollback failed too.\n" msg = QObject::tr("The rollback failed too.\n"
"You will have to repair %1 manually.\n" "You will have to repair %1 manually.\n"
"Please let us know why and how this happened.").arg(BuildConfig.LAUNCHER_DISPLAYNAME); "Please let us know why and how this happened.").arg(BuildConfig.LAUNCHER_NAME);
QMessageBox::critical(m_parent, rollFailTitle, msg); QMessageBox::critical(m_parent, rollFailTitle, msg);
qApp->quit(); qApp->quit();
} }

View File

@ -1,42 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - 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 #pragma once
#include <QString> #include <QString>
#include <QStringView>
#include <QList> #include <QList>
class QUrl; class QUrl;
@ -75,21 +39,13 @@ private:
break; break;
} }
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
auto numPart = QStringView{m_fullString}.left(cutoff);
#else
auto numPart = m_fullString.leftRef(cutoff); auto numPart = m_fullString.leftRef(cutoff);
#endif
if(numPart.size()) if(numPart.size())
{ {
numValid = true; numValid = true;
m_numPart = numPart.toInt(); m_numPart = numPart.toInt();
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
auto stringPart = QStringView{m_fullString}.mid(cutoff);
#else
auto stringPart = m_fullString.midRef(cutoff); auto stringPart = m_fullString.midRef(cutoff);
#endif
if(stringPart.size()) if(stringPart.size())
{ {
m_stringPart = stringPart.toString(); m_stringPart = stringPart.toString();

View File

@ -1,38 +1,3 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - 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 "VersionProxyModel.h" #include "VersionProxyModel.h"
#include "Application.h" #include "Application.h"
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
@ -243,8 +208,7 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
{ {
return APPLICATION->getThemedIcon("bug"); return APPLICATION->getThemedIcon("bug");
} }
QPixmap pixmap; auto pixmap = QPixmapCache::find("placeholder");
QPixmapCache::find("placeholder", &pixmap);
if(!pixmap) if(!pixmap)
{ {
QPixmap px(16,16); QPixmap px(16,16);
@ -252,7 +216,7 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
QPixmapCache::insert("placeholder", px); QPixmapCache::insert("placeholder", px);
return px; return px;
} }
return pixmap; return *pixmap;
} }
} }
default: default:

View File

@ -15,7 +15,7 @@
#include <QTest> #include <QTest>
#include <TestUtil.h> #include "TestUtil.h"
#include <Version.h> #include <Version.h>
class ModUtilsTest : public QObject class ModUtilsTest : public QObject

View File

@ -1,36 +1,16 @@
// SPDX-License-Identifier: GPL-3.0-only /* Copyright 2013-2021 MultiMC Contributors
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * Licensed under the Apache License, Version 2.0 (the "License");
* it under the terms of the GNU General Public License as published by * you may not use this file except in compliance with the License.
* the Free Software Foundation, version 3. * You may obtain a copy of the License at
* *
* This program is distributed in the hope that it will be useful, * http://www.apache.org/licenses/LICENSE-2.0
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * Unless required by applicable law or agreed to in writing, software
* along with this program. If not, see <https://www.gnu.org/licenses/>. * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* This file incorporates work covered by the following copyright and * See the License for the specific language governing permissions and
* permission notice: * limitations under the License.
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
#include "IconList.h" #include "IconList.h"
@ -106,11 +86,7 @@ void IconList::directoryChanged(const QString &path)
QString &foo = (*it); QString &foo = (*it);
foo = m_dir.filePath(foo); foo = m_dir.filePath(foo);
} }
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QSet<QString> new_set(new_list.begin(), new_list.end());
#else
auto new_set = new_list.toSet(); auto new_set = new_list.toSet();
#endif
QList<QString> current_list; QList<QString> current_list;
for (auto &it : icons) for (auto &it : icons)
{ {
@ -118,11 +94,7 @@ void IconList::directoryChanged(const QString &path)
continue; continue;
current_list.push_back(it.m_images[IconType::FileBased].filename); current_list.push_back(it.m_images[IconType::FileBased].filename);
} }
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QSet<QString> current_set(current_list.begin(), current_list.end());
#else
QSet<QString> current_set = current_list.toSet(); QSet<QString> current_set = current_list.toSet();
#endif
QSet<QString> to_remove = current_set; QSet<QString> to_remove = current_set;
to_remove -= new_set; to_remove -= new_set;

View File

@ -1,41 +1,21 @@
// SPDX-License-Identifier: GPL-3.0-only /* Copyright 2013-2021 MultiMC Contributors
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * Licensed under the Apache License, Version 2.0 (the "License");
* it under the terms of the GNU General Public License as published by * you may not use this file except in compliance with the License.
* the Free Software Foundation, version 3. * You may obtain a copy of the License at
* *
* This program is distributed in the hope that it will be useful, * http://www.apache.org/licenses/LICENSE-2.0
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * Unless required by applicable law or agreed to in writing, software
* along with this program. If not, see <https://www.gnu.org/licenses/>. * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* This file incorporates work covered by the following copyright and * See the License for the specific language governing permissions and
* permission notice: * limitations under the License.
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
#include "MMCIcon.h" #include "MMCIcon.h"
#include <QFileInfo> #include <QFileInfo>
#include <QIcon> #include <xdgicon.h>
IconType operator--(IconType &t, int) IconType operator--(IconType &t, int)
{ {
@ -83,7 +63,7 @@ QIcon MMCIcon::icon() const
if(!icon.isNull()) if(!icon.isNull())
return icon; return icon;
// FIXME: inject this. // FIXME: inject this.
return QIcon::fromTheme(m_images[m_current_type].key); return XdgIcon::fromTheme(m_images[m_current_type].key);
} }
void MMCIcon::remove(IconType rm_type) void MMCIcon::remove(IconType rm_type)

View File

@ -1,38 +1,3 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - 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 "JavaChecker.h" #include "JavaChecker.h"
#include <QFile> #include <QFile>
@ -51,13 +16,7 @@ JavaChecker::JavaChecker(QObject *parent) : QObject(parent)
void JavaChecker::performCheck() void JavaChecker::performCheck()
{ {
QString checkerJar = JavaUtils::getJavaCheckPath(); QString checkerJar = FS::PathCombine(APPLICATION->getJarsPath(), "JavaCheck.jar");
if (checkerJar.isEmpty())
{
qDebug() << "Java checker library could not be found. Please check your installation.";
return;
}
QStringList args; QStringList args;
@ -88,11 +47,7 @@ void JavaChecker::performCheck()
qDebug() << "Running java checker: " + m_path + args.join(" ");; qDebug() << "Running java checker: " + m_path + args.join(" ");;
connect(process.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus))); connect(process.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus)));
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
connect(process.get(), SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError)));
#else
connect(process.get(), SIGNAL(error(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError))); connect(process.get(), SIGNAL(error(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError)));
#endif
connect(process.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(stdoutReady())); connect(process.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(stdoutReady()));
connect(process.get(), SIGNAL(readyReadStandardError()), this, SLOT(stderrReady())); connect(process.get(), SIGNAL(readyReadStandardError()), this, SLOT(stderrReady()));
connect(&killTimer, SIGNAL(timeout()), SLOT(timeout())); connect(&killTimer, SIGNAL(timeout()), SLOT(timeout()));
@ -144,12 +99,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
bool success = true; bool success = true;
QMap<QString, QString> results; QMap<QString, QString> results;
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QStringList lines = m_stdout.split("\n", Qt::SkipEmptyParts);
#else
QStringList lines = m_stdout.split("\n", QString::SkipEmptyParts); QStringList lines = m_stdout.split("\n", QString::SkipEmptyParts);
#endif
for(QString line : lines) for(QString line : lines)
{ {
line = line.trimmed(); line = line.trimmed();
@ -158,11 +108,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
continue; continue;
} }
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
auto parts = line.split('=', Qt::SkipEmptyParts);
#else
auto parts = line.split('=', QString::SkipEmptyParts); auto parts = line.split('=', QString::SkipEmptyParts);
#endif
if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty()) if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty())
{ {
continue; continue;

View File

@ -1,40 +1,21 @@
// SPDX-License-Identifier: GPL-3.0-only /* Copyright 2013-2021 MultiMC Contributors
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * Licensed under the Apache License, Version 2.0 (the "License");
* it under the terms of the GNU General Public License as published by * you may not use this file except in compliance with the License.
* the Free Software Foundation, version 3. * You may obtain a copy of the License at
* *
* This program is distributed in the hope that it will be useful, * http://www.apache.org/licenses/LICENSE-2.0
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * Unless required by applicable law or agreed to in writing, software
* along with this program. If not, see <https://www.gnu.org/licenses/>. * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* This file incorporates work covered by the following copyright and * See the License for the specific language governing permissions and
* permission notice: * limitations under the License.
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
#include <QtNetwork> #include <QtNetwork>
#include <QtXml> #include <QtXml>
#include <QRegExp>
#include <QDebug> #include <QDebug>
@ -100,7 +81,7 @@ QVariant JavaInstallList::data(const QModelIndex &index, int role) const
switch (role) switch (role)
{ {
case VersionPointerRole: case VersionPointerRole:
return QVariant::fromValue(m_vlist[index.row()]); return qVariantFromValue(m_vlist[index.row()]);
case VersionIdRole: case VersionIdRole:
return version->descriptor(); return version->descriptor();
case VersionRole: case VersionRole:

View File

@ -1,36 +1,16 @@
// SPDX-License-Identifier: GPL-3.0-only /* Copyright 2013-2021 MultiMC Contributors
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * Licensed under the Apache License, Version 2.0 (the "License");
* it under the terms of the GNU General Public License as published by * you may not use this file except in compliance with the License.
* the Free Software Foundation, version 3. * You may obtain a copy of the License at
* *
* This program is distributed in the hope that it will be useful, * http://www.apache.org/licenses/LICENSE-2.0
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * Unless required by applicable law or agreed to in writing, software
* along with this program. If not, see <https://www.gnu.org/licenses/>. * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* This file incorporates work covered by the following copyright and * See the License for the specific language governing permissions and
* permission notice: * limitations under the License.
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
#include <QStringList> #include <QStringList>
@ -44,7 +24,6 @@
#include "java/JavaUtils.h" #include "java/JavaUtils.h"
#include "java/JavaInstallList.h" #include "java/JavaInstallList.h"
#include "FileSystem.h" #include "FileSystem.h"
#include "Application.h"
#define IBUS "@im=ibus" #define IBUS "@im=ibus"
@ -52,24 +31,25 @@ JavaUtils::JavaUtils()
{ {
} }
QString stripVariableEntries(QString name, QString target, QString remove) #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
static QString processLD_LIBRARY_PATH(const QString & LD_LIBRARY_PATH)
{ {
char delimiter = ':'; QDir mmcBin(QCoreApplication::applicationDirPath());
#ifdef Q_OS_WIN32 auto items = LD_LIBRARY_PATH.split(':');
delimiter = ';'; QStringList final;
#endif for(auto & item: items)
{
auto targetItems = target.split(delimiter); QDir test(item);
auto toRemove = remove.split(delimiter); if(test == mmcBin)
{
for (QString item : toRemove) { qDebug() << "Env:LD_LIBRARY_PATH ignoring path" << item;
bool removed = targetItems.removeOne(item); continue;
if (!removed) }
qWarning() << "Entry" << item final.append(item);
<< "could not be stripped from variable" << name;
} }
return targetItems.join(delimiter); return final.join(':');
} }
#endif
QProcessEnvironment CleanEnviroment() QProcessEnvironment CleanEnviroment()
{ {
@ -88,16 +68,6 @@ QProcessEnvironment CleanEnviroment()
"JAVA_OPTIONS", "JAVA_OPTIONS",
"JAVA_TOOL_OPTIONS" "JAVA_TOOL_OPTIONS"
}; };
QStringList stripped =
{
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
"LD_LIBRARY_PATH",
"LD_PRELOAD",
#endif
"QT_PLUGIN_PATH",
"QT_FONTPATH"
};
for(auto key: rawenv.keys()) for(auto key: rawenv.keys())
{ {
auto value = rawenv.value(key); auto value = rawenv.value(key);
@ -107,22 +77,19 @@ QProcessEnvironment CleanEnviroment()
qDebug() << "Env: ignoring" << key << value; qDebug() << "Env: ignoring" << key << value;
continue; continue;
} }
// filter PolyMC-related things
// These are used to strip the original variables if(key.startsWith("QT_"))
// If there is "LD_LIBRARY_PATH" and "LAUNCHER_LD_LIBRARY_PATH", we want to
// remove all values in "LAUNCHER_LD_LIBRARY_PATH" from "LD_LIBRARY_PATH"
if(key.startsWith("LAUNCHER_"))
{ {
qDebug() << "Env: ignoring" << key << value; qDebug() << "Env: ignoring" << key << value;
continue; continue;
} }
if(stripped.contains(key)) #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
// Do not pass LD_* variables to java. They were intended for PolyMC
if(key.startsWith("LD_"))
{ {
QString newValue = stripVariableEntries(key, value, rawenv.value("LAUNCHER_" + key)); qDebug() << "Env: ignoring" << key << value;
continue;
qDebug() << "Env: stripped" << key << value << "to" << newValue;
} }
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
// Strip IBus // Strip IBus
// IBus is a Linux IME framework. For some reason, it breaks MC? // IBus is a Linux IME framework. For some reason, it breaks MC?
if (key == "XMODIFIERS" && value.contains(IBUS)) if (key == "XMODIFIERS" && value.contains(IBUS))
@ -131,12 +98,22 @@ QProcessEnvironment CleanEnviroment()
value.replace(IBUS, ""); value.replace(IBUS, "");
qDebug() << "Env: stripped" << IBUS << "from" << save << ":" << value; qDebug() << "Env: stripped" << IBUS << "from" << save << ":" << value;
} }
if(key == "GAME_PRELOAD")
{
env.insert("LD_PRELOAD", value);
continue;
}
if(key == "GAME_LIBRARY_PATH")
{
env.insert("LD_LIBRARY_PATH", processLD_LIBRARY_PATH(value));
continue;
}
#endif #endif
// qDebug() << "Env: " << key << value; // qDebug() << "Env: " << key << value;
env.insert(key, value); env.insert(key, value);
} }
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
// HACK: Workaround for QTBUG-42500 // HACK: Workaround for QTBUG42500
if(!env.contains("LD_LIBRARY_PATH")) if(!env.contains("LD_LIBRARY_PATH"))
{ {
env.insert("LD_LIBRARY_PATH", ""); env.insert("LD_LIBRARY_PATH", "");
@ -174,17 +151,11 @@ JavaInstallPtr JavaUtils::GetDefaultJava()
QStringList addJavasFromEnv(QList<QString> javas) QStringList addJavasFromEnv(QList<QString> javas)
{ {
auto env = qEnvironmentVariable("PRISMLAUNCHER_JAVA_PATHS"); // FIXME: use launcher name from buildconfig QByteArray env = qgetenv("POLYMC_JAVA_PATHS");
#if defined(Q_OS_WIN32) #if defined(Q_OS_WIN32)
QList<QString> javaPaths = env.replace("\\", "/").split(QLatin1String(";")); QList<QString> javaPaths = QString::fromLocal8Bit(env).replace("\\", "/").split(QLatin1String(";"));
auto envPath = qEnvironmentVariable("PATH");
QList<QString> javaPathsfromPath = envPath.replace("\\", "/").split(QLatin1String(";"));
for (QString string : javaPathsfromPath) {
javaPaths.append(string + "/javaw.exe");
}
#else #else
QList<QString> javaPaths = env.split(QLatin1String(":")); QList<QString> javaPaths = QString::fromLocal8Bit(env).split(QLatin1String(":"));
#endif #endif
for(QString i : javaPaths) for(QString i : javaPaths)
{ {
@ -205,17 +176,25 @@ QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString
archType = "32"; archType = "32";
HKEY jreKey; HKEY jreKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyName.toStdWString().c_str(), 0, if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName.toStdString().c_str(), 0,
KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) == ERROR_SUCCESS) KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) == ERROR_SUCCESS)
{ {
// Read the current type version from the registry. // Read the current type version from the registry.
// This will be used to find any key that contains the JavaHome value. // This will be used to find any key that contains the JavaHome value.
char *value = new char[0];
DWORD valueSz = 0;
if (RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz) ==
ERROR_MORE_DATA)
{
value = new char[valueSz];
RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz);
}
WCHAR subKeyName[255]; TCHAR subKeyName[255];
DWORD subKeyNameSize, numSubKeys, retCode; DWORD subKeyNameSize, numSubKeys, retCode;
// Get the number of subkeys // Get the number of subkeys
RegQueryInfoKeyW(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, RegQueryInfoKey(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL,
NULL, NULL); NULL, NULL);
// Iterate until RegEnumKeyEx fails // Iterate until RegEnumKeyEx fails
@ -224,37 +203,34 @@ QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString
for (DWORD i = 0; i < numSubKeys; i++) for (DWORD i = 0; i < numSubKeys; i++)
{ {
subKeyNameSize = 255; subKeyNameSize = 255;
retCode = RegEnumKeyExW(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL, retCode = RegEnumKeyEx(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL,
NULL); NULL);
QString newSubkeyName = QString::fromWCharArray(subKeyName);
if (retCode == ERROR_SUCCESS) if (retCode == ERROR_SUCCESS)
{ {
// Now open the registry key for the version that we just got. // Now open the registry key for the version that we just got.
QString newKeyName = keyName + "\\" + newSubkeyName + subkeySuffix; QString newKeyName = keyName + "\\" + subKeyName + subkeySuffix;
HKEY newKey; HKEY newKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, newKeyName.toStdWString().c_str(), 0, if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, newKeyName.toStdString().c_str(), 0,
KEY_READ | KEY_WOW64_64KEY, &newKey) == ERROR_SUCCESS) KEY_READ | KEY_WOW64_64KEY, &newKey) == ERROR_SUCCESS)
{ {
// Read the JavaHome value to find where Java is installed. // Read the JavaHome value to find where Java is installed.
DWORD valueSz = 0; value = new char[0];
if (RegQueryValueExW(newKey, keyJavaDir.toStdWString().c_str(), NULL, NULL, NULL, valueSz = 0;
&valueSz) == ERROR_SUCCESS) if (RegQueryValueEx(newKey, keyJavaDir.toStdString().c_str(), NULL, NULL, (BYTE *)value,
&valueSz) == ERROR_MORE_DATA)
{ {
WCHAR *value = new WCHAR[valueSz]; value = new char[valueSz];
RegQueryValueExW(newKey, keyJavaDir.toStdWString().c_str(), NULL, NULL, (BYTE *)value, RegQueryValueEx(newKey, keyJavaDir.toStdString().c_str(), NULL, NULL, (BYTE *)value,
&valueSz); &valueSz);
QString newValue = QString::fromWCharArray(value);
delete [] value;
// Now, we construct the version object and add it to the list. // Now, we construct the version object and add it to the list.
JavaInstallPtr javaVersion(new JavaInstall()); JavaInstallPtr javaVersion(new JavaInstall());
javaVersion->id = newSubkeyName; javaVersion->id = subKeyName;
javaVersion->arch = archType; javaVersion->arch = archType;
javaVersion->path = javaVersion->path =
QDir(FS::PathCombine(newValue, "bin")).absoluteFilePath("javaw.exe"); QDir(FS::PathCombine(value, "bin")).absoluteFilePath("javaw.exe");
javas.append(javaVersion); javas.append(javaVersion);
} }
@ -441,7 +417,7 @@ QList<QString> JavaUtils::FindJavaPaths()
scanJavaDir("/usr/lib/jvm"); scanJavaDir("/usr/lib/jvm");
scanJavaDir("/usr/lib64/jvm"); scanJavaDir("/usr/lib64/jvm");
scanJavaDir("/usr/lib32/jvm"); scanJavaDir("/usr/lib32/jvm");
// javas stored in Prism Launcher's folder // javas stored in PolyMC's folder
scanJavaDir("java"); scanJavaDir("java");
// manually installed JDKs in /opt // manually installed JDKs in /opt
scanJavaDir("/opt/jdk"); scanJavaDir("/opt/jdk");
@ -461,8 +437,3 @@ QList<QString> JavaUtils::FindJavaPaths()
return addJavasFromEnv(javas); return addJavasFromEnv(javas);
} }
#endif #endif
QString JavaUtils::getJavaCheckPath()
{
return APPLICATION->getJarPath("JavaCheck.jar");
}

View File

@ -24,7 +24,6 @@
#include <windows.h> #include <windows.h>
#endif #endif
QString stripVariableEntries(QString name, QString target, QString remove);
QProcessEnvironment CleanEnviroment(); QProcessEnvironment CleanEnviroment();
class JavaUtils : public QObject class JavaUtils : public QObject
@ -40,6 +39,4 @@ public:
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
QList<JavaInstallPtr> FindJavaFromRegistryKey(DWORD keyType, QString keyName, QString keyJavaDir, QString subkeySuffix = ""); QList<JavaInstallPtr> FindJavaFromRegistryKey(DWORD keyType, QString keyName, QString keyJavaDir, QString subkeySuffix = "");
#endif #endif
static QString getJavaCheckPath();
}; };

View File

@ -1,6 +1,7 @@
#include <QTest> #include <QTest>
#include "TestUtil.h"
#include <java/JavaVersion.h> #include "java/JavaVersion.h"
class JavaVersionTest : public QObject class JavaVersionTest : public QObject
{ {

View File

@ -282,22 +282,18 @@ void LaunchTask::emitFailed(QString reason)
Task::emitFailed(reason); Task::emitFailed(reason);
} }
void LaunchTask::substituteVariables(QStringList &args) const QString LaunchTask::substituteVariables(const QString &cmd) const
{ {
auto env = m_instance->createEnvironment(); QString out = cmd;
auto variables = m_instance->getVariables();
for (auto key : env.keys()) for (auto it = variables.begin(); it != variables.end(); ++it)
{ {
args.replaceInStrings("$" + key, env.value(key)); out.replace("$" + it.key(), it.value());
} }
} auto env = QProcessEnvironment::systemEnvironment();
for (auto var : env.keys())
void LaunchTask::substituteVariables(QString &cmd) const
{
auto env = m_instance->createEnvironment();
for (auto key : env.keys())
{ {
cmd.replace("$" + key, env.value(key)); out.replace("$" + var, env.value(var));
} }
return out;
} }

View File

@ -1,38 +1,18 @@
// SPDX-License-Identifier: GPL-3.0-only /* Copyright 2013-2021 MultiMC Contributors
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
* *
* This program is distributed in the hope that it will be useful, * Licensed under the Apache License, Version 2.0 (the "License");
* but WITHOUT ANY WARRANTY; without even the implied warranty of * you may not use this file except in compliance with the License.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * You may obtain a copy of the License at
* GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * http://www.apache.org/licenses/LICENSE-2.0
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* *
* This file incorporates work covered by the following copyright and * Unless required by applicable law or agreed to in writing, software
* permission notice: * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* Copyright 2013-2021 MultiMC Contributors * See the License for the specific language governing permissions and
* * limitations under the License.
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
#pragma once #pragma once
@ -105,8 +85,7 @@ public: /* methods */
shared_qobject_ptr<LogModel> getLogModel(); shared_qobject_ptr<LogModel> getLogModel();
public: public:
void substituteVariables(QStringList &args) const; QString substituteVariables(const QString &cmd) const;
void substituteVariables(QString &cmd) const;
QString censorPrivateInfo(QString in); QString censorPrivateInfo(QString in);
protected: /* methods */ protected: /* methods */

View File

@ -34,7 +34,6 @@
*/ */
#include "CheckJava.h" #include "CheckJava.h"
#include "java/JavaUtils.h"
#include <launch/LaunchTask.h> #include <launch/LaunchTask.h>
#include <FileSystem.h> #include <FileSystem.h>
#include <QStandardPaths> #include <QStandardPaths>
@ -72,26 +71,15 @@ void CheckJava::executeTask()
emit logLine("Java path is:\n" + m_javaPath + "\n\n", MessageLevel::Launcher); emit logLine("Java path is:\n" + m_javaPath + "\n\n", MessageLevel::Launcher);
} }
if (JavaUtils::getJavaCheckPath().isEmpty())
{
const char *reason = QT_TR_NOOP("Java checker library could not be found. Please check your installation.");
emit logLine(tr(reason), MessageLevel::Fatal);
emitFailed(tr(reason));
return;
}
QFileInfo javaInfo(realJavaPath); QFileInfo javaInfo(realJavaPath);
qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch(); qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch();
auto storedUnixTime = settings->get("JavaTimestamp").toLongLong(); auto storedUnixTime = settings->get("JavaTimestamp").toLongLong();
auto storedArchitecture = settings->get("JavaArchitecture").toString(); auto storedArchitecture = settings->get("JavaArchitecture").toString();
auto storedRealArchitecture = settings->get("JavaRealArchitecture").toString();
auto storedVersion = settings->get("JavaVersion").toString(); auto storedVersion = settings->get("JavaVersion").toString();
auto storedVendor = settings->get("JavaVendor").toString(); auto storedVendor = settings->get("JavaVendor").toString();
m_javaUnixTime = javaUnixTime; m_javaUnixTime = javaUnixTime;
// if timestamps are not the same, or something is missing, check! // if timestamps are not the same, or something is missing, check!
if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0 || storedVendor.size() == 0)
|| storedArchitecture.size() == 0 || storedRealArchitecture.size() == 0
|| storedVendor.size() == 0)
{ {
m_JavaChecker = new JavaChecker(); m_JavaChecker = new JavaChecker();
emit logLine(QString("Checking Java version..."), MessageLevel::Launcher); emit logLine(QString("Checking Java version..."), MessageLevel::Launcher);
@ -104,9 +92,8 @@ void CheckJava::executeTask()
{ {
auto verString = instance->settings()->get("JavaVersion").toString(); auto verString = instance->settings()->get("JavaVersion").toString();
auto archString = instance->settings()->get("JavaArchitecture").toString(); auto archString = instance->settings()->get("JavaArchitecture").toString();
auto realArchString = settings->get("JavaRealArchitecture").toString();
auto vendorString = instance->settings()->get("JavaVendor").toString(); auto vendorString = instance->settings()->get("JavaVendor").toString();
printJavaInfo(verString, archString, realArchString, vendorString); printJavaInfo(verString, archString, vendorString);
} }
emitSucceeded(); emitSucceeded();
} }
@ -121,6 +108,7 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
emit logLine(QString("Could not start java:"), MessageLevel::Error); emit logLine(QString("Could not start java:"), MessageLevel::Error);
emit logLines(result.errorLog.split('\n'), MessageLevel::Error); emit logLines(result.errorLog.split('\n'), MessageLevel::Error);
emit logLine(QString("\nCheck your Java settings."), MessageLevel::Launcher); emit logLine(QString("\nCheck your Java settings."), MessageLevel::Launcher);
printSystemInfo(false, false);
emitFailed(QString("Could not start java!")); emitFailed(QString("Could not start java!"));
return; return;
} }
@ -129,16 +117,17 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
emit logLine(QString("Java checker returned some invalid data we don't understand:"), MessageLevel::Error); emit logLine(QString("Java checker returned some invalid data we don't understand:"), MessageLevel::Error);
emit logLines(result.outLog.split('\n'), MessageLevel::Warning); emit logLines(result.outLog.split('\n'), MessageLevel::Warning);
emit logLine("\nMinecraft might not start properly.", MessageLevel::Launcher); emit logLine("\nMinecraft might not start properly.", MessageLevel::Launcher);
printSystemInfo(false, false);
emitSucceeded(); emitSucceeded();
return; return;
} }
case JavaCheckResult::Validity::Valid: case JavaCheckResult::Validity::Valid:
{ {
auto instance = m_parent->instance(); auto instance = m_parent->instance();
printJavaInfo(result.javaVersion.toString(), result.mojangPlatform, result.realPlatform, result.javaVendor); printJavaInfo(result.javaVersion.toString(), result.realPlatform, result.javaVendor);
printSystemInfo(true, result.is_64bit);
instance->settings()->set("JavaVersion", result.javaVersion.toString()); instance->settings()->set("JavaVersion", result.javaVersion.toString());
instance->settings()->set("JavaArchitecture", result.mojangPlatform); instance->settings()->set("JavaArchitecture", result.mojangPlatform);
instance->settings()->set("JavaRealArchitecture", result.realPlatform);
instance->settings()->set("JavaVendor", result.javaVendor); instance->settings()->set("JavaVendor", result.javaVendor);
instance->settings()->set("JavaTimestamp", m_javaUnixTime); instance->settings()->set("JavaTimestamp", m_javaUnixTime);
emitSucceeded(); emitSucceeded();
@ -147,8 +136,24 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
} }
} }
void CheckJava::printJavaInfo(const QString& version, const QString& architecture, const QString& realArchitecture, const QString & vendor) void CheckJava::printJavaInfo(const QString& version, const QString& architecture, const QString & vendor)
{ {
emit logLine(QString("Java is version %1, using %2 (%3) architecture, from %4.\n\n") emit logLine(QString("Java is version %1, using %2 architecture, from %3.\n\n").arg(version, architecture, vendor), MessageLevel::Launcher);
.arg(version, architecture, realArchitecture, vendor), MessageLevel::Launcher); }
void CheckJava::printSystemInfo(bool javaIsKnown, bool javaIs64bit)
{
auto cpu64 = Sys::isCPU64bit();
auto system64 = Sys::isSystem64bit();
if(cpu64 != system64)
{
emit logLine(QString("Your CPU architecture is not matching your system architecture. You might want to install a 64bit Operating System.\n\n"), MessageLevel::Error);
}
if(javaIsKnown)
{
if(javaIs64bit != system64)
{
emit logLine(QString("Your Java architecture is not matching your system architecture. You might want to install a 64bit Java version.\n\n"), MessageLevel::Error);
}
}
} }

View File

@ -35,7 +35,7 @@ private slots:
void checkJavaFinished(JavaCheckResult result); void checkJavaFinished(JavaCheckResult result);
private: private:
void printJavaInfo(const QString & version, const QString & architecture, const QString & realArchitecture, const QString & vendor); void printJavaInfo(const QString & version, const QString & architecture, const QString & vendor);
void printSystemInfo(bool javaIsKnown, bool javaIs64bit); void printSystemInfo(bool javaIsKnown, bool javaIs64bit);
private: private:

View File

@ -1,36 +1,16 @@
// SPDX-License-Identifier: GPL-3.0-only /* Copyright 2013-2021 MultiMC Contributors
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * Licensed under the Apache License, Version 2.0 (the "License");
* it under the terms of the GNU General Public License as published by * you may not use this file except in compliance with the License.
* the Free Software Foundation, version 3. * You may obtain a copy of the License at
* *
* This program is distributed in the hope that it will be useful, * http://www.apache.org/licenses/LICENSE-2.0
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * Unless required by applicable law or agreed to in writing, software
* along with this program. If not, see <https://www.gnu.org/licenses/>. * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* This file incorporates work covered by the following copyright and * See the License for the specific language governing permissions and
* permission notice: * limitations under the License.
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
#include "PostLaunchCommand.h" #include "PostLaunchCommand.h"
@ -47,20 +27,9 @@ PostLaunchCommand::PostLaunchCommand(LaunchTask *parent) : LaunchStep(parent)
void PostLaunchCommand::executeTask() void PostLaunchCommand::executeTask()
{ {
//FIXME: where to put this? QString postlaunch_cmd = m_parent->substituteVariables(m_command);
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) emit logLine(tr("Running Post-Launch command: %1").arg(postlaunch_cmd), MessageLevel::Launcher);
auto args = QProcess::splitCommand(m_command); m_process.start(postlaunch_cmd);
m_parent->substituteVariables(args);
emit logLine(tr("Running Post-Launch command: %1").arg(args.join(' ')), MessageLevel::Launcher);
const QString program = args.takeFirst();
m_process.start(program, args);
#else
m_parent->substituteVariables(m_command);
emit logLine(tr("Running Post-Launch command: %1").arg(m_command), MessageLevel::Launcher);
m_process.start(m_command);
#endif
} }
void PostLaunchCommand::on_state(LoggedProcess::State state) void PostLaunchCommand::on_state(LoggedProcess::State state)

View File

@ -1,36 +1,16 @@
// SPDX-License-Identifier: GPL-3.0-only /* Copyright 2013-2021 MultiMC Contributors
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * Licensed under the Apache License, Version 2.0 (the "License");
* it under the terms of the GNU General Public License as published by * you may not use this file except in compliance with the License.
* the Free Software Foundation, version 3. * You may obtain a copy of the License at
* *
* This program is distributed in the hope that it will be useful, * http://www.apache.org/licenses/LICENSE-2.0
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * Unless required by applicable law or agreed to in writing, software
* along with this program. If not, see <https://www.gnu.org/licenses/>. * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* This file incorporates work covered by the following copyright and * See the License for the specific language governing permissions and
* permission notice: * limitations under the License.
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
#include "PreLaunchCommand.h" #include "PreLaunchCommand.h"
@ -48,19 +28,9 @@ PreLaunchCommand::PreLaunchCommand(LaunchTask *parent) : LaunchStep(parent)
void PreLaunchCommand::executeTask() void PreLaunchCommand::executeTask()
{ {
//FIXME: where to put this? //FIXME: where to put this?
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) QString prelaunch_cmd = m_parent->substituteVariables(m_command);
auto args = QProcess::splitCommand(m_command); emit logLine(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd), MessageLevel::Launcher);
m_parent->substituteVariables(args); m_process.start(prelaunch_cmd);
emit logLine(tr("Running Pre-Launch command: %1").arg(args.join(' ')), MessageLevel::Launcher);
const QString program = args.takeFirst();
m_process.start(program, args);
#else
m_parent->substituteVariables(m_command);
emit logLine(tr("Running Pre-Launch command: %1").arg(m_command), MessageLevel::Launcher);
m_process.start(m_command);
#endif
} }
void PreLaunchCommand::on_state(LoggedProcess::State state) void PreLaunchCommand::on_state(LoggedProcess::State state)

View File

@ -1,38 +1,3 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - 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 "Application.h" #include "Application.h"
// #define BREAK_INFINITE_LOOP // #define BREAK_INFINITE_LOOP
@ -59,10 +24,8 @@ int main(int argc, char *argv[])
return 42; return 42;
#endif #endif
#if QT_VERSION <= QT_VERSION_CHECK(6, 0, 0)
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
// initialize Qt // initialize Qt
Application app(argc, argv); Application app(argc, argv);
@ -75,7 +38,7 @@ int main(int argc, char *argv[])
Q_INIT_RESOURCE(multimc); Q_INIT_RESOURCE(multimc);
Q_INIT_RESOURCE(backgrounds); Q_INIT_RESOURCE(backgrounds);
Q_INIT_RESOURCE(documents); Q_INIT_RESOURCE(documents);
Q_INIT_RESOURCE(prismlauncher); Q_INIT_RESOURCE(polymc);
Q_INIT_RESOURCE(pe_dark); Q_INIT_RESOURCE(pe_dark);
Q_INIT_RESOURCE(pe_light); Q_INIT_RESOURCE(pe_light);

View File

@ -1,7 +1,8 @@
#include <QTest> #include <QTest>
#include "TestUtil.h"
#include <meta/Index.h> #include "meta/Index.h"
#include <meta/VersionList.h> #include "meta/VersionList.h"
class IndexTest : public QObject class IndexTest : public QObject
{ {

View File

@ -140,13 +140,6 @@ VersionPtr VersionList::getVersion(const QString &version)
return out; return out;
} }
bool VersionList::hasVersion(QString version) const
{
auto ver = std::find_if(m_versions.constBegin(), m_versions.constEnd(),
[&](Meta::VersionPtr const& a){ return a->version() == version; });
return (ver != m_versions.constEnd());
}
void VersionList::setName(const QString &name) void VersionList::setName(const QString &name)
{ {
m_name = name; m_name = name;

View File

@ -66,7 +66,6 @@ public:
QString humanReadable() const; QString humanReadable() const;
VersionPtr getVersion(const QString &version); VersionPtr getVersion(const QString &version);
bool hasVersion(QString version) const;
QVector<VersionPtr> versions() const QVector<VersionPtr> versions() const
{ {

View File

@ -1,38 +1,3 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - 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 <meta/VersionList.h> #include <meta/VersionList.h>
#include <meta/Index.h> #include <meta/Index.h>
#include "Component.h" #include "Component.h"
@ -95,7 +60,7 @@ void Component::applyTo(LaunchProfile* profile)
auto vfile = getVersionFile(); auto vfile = getVersionFile();
if(vfile) if(vfile)
{ {
vfile->applyTo(profile, m_parent->runtimeContext()); vfile->applyTo(profile);
} }
else else
{ {

View File

@ -197,10 +197,6 @@ void ComponentUpdateTask::loadComponents()
{ {
remoteLoadFailed(taskIndex, error); remoteLoadFailed(taskIndex, error);
}); });
connect(indexLoadTask.get(), &Task::aborted, [=]()
{
remoteLoadFailed(taskIndex, tr("Aborted"));
});
taskIndex++; taskIndex++;
} }
} }
@ -247,10 +243,6 @@ void ComponentUpdateTask::loadComponents()
{ {
remoteLoadFailed(taskIndex, error); remoteLoadFailed(taskIndex, error);
}); });
connect(loadTask.get(), &Task::aborted, [=]()
{
remoteLoadFailed(taskIndex, tr("Aborted"));
});
RemoteLoadStatus status; RemoteLoadStatus status;
status.type = loadType; status.type = loadType;
status.PackProfileIndex = componentIndex; status.PackProfileIndex = componentIndex;

View File

@ -1,43 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - 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 #pragma once
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <QRegularExpression>
#include "DefaultVariable.h" #include "DefaultVariable.h"
struct GradleSpecifier struct GradleSpecifier
@ -61,21 +25,20 @@ struct GradleSpecifier
4 "jdk15" 4 "jdk15"
5 "jar" 5 "jar"
*/ */
QRegularExpression matcher(QRegularExpression::anchoredPattern("([^:@]+):([^:@]+):([^:@]+)" "(?::([^:@]+))?" "(?:@([^:@]+))?")); QRegExp matcher("([^:@]+):([^:@]+):([^:@]+)" "(?::([^:@]+))?" "(?:@([^:@]+))?");
QRegularExpressionMatch match = matcher.match(value); m_valid = matcher.exactMatch(value);
m_valid = match.hasMatch();
if(!m_valid) { if(!m_valid) {
m_invalidValue = value; m_invalidValue = value;
return *this; return *this;
} }
auto elements = match.captured(); auto elements = matcher.capturedTexts();
m_groupId = match.captured(1); m_groupId = elements[1];
m_artifactId = match.captured(2); m_artifactId = elements[2];
m_version = match.captured(3); m_version = elements[3];
m_classifier = match.captured(4); m_classifier = elements[4];
if(match.lastCapturedIndex() >= 5) if(!elements[5].isEmpty())
{ {
m_extension = match.captured(5); m_extension = elements[5];
} }
return *this; return *this;
} }

View File

@ -1,6 +1,7 @@
#include <QTest> #include <QTest>
#include "TestUtil.h"
#include <minecraft/GradleSpecifier.h> #include "minecraft/GradleSpecifier.h"
class GradleSpecifierTest : public QObject class GradleSpecifierTest : public QObject
{ {

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