Compare commits
26 Commits
release-5.
...
1.4.2
Author | SHA1 | Date | |
---|---|---|---|
6e65cd4405 | |||
98fbb3613d | |||
2666beafb1 | |||
cf8a76be6b | |||
e96f482544 | |||
dc32f0671b | |||
d6d7794cd0 | |||
94d95d45d4 | |||
5e767a91d9 | |||
c89f8b4657 | |||
d7fc0f53d3 | |||
e81cf8a845 | |||
c116885ab1 | |||
d68d5ca23f | |||
6d94338a56 | |||
1e1a1cef05 | |||
34bab3e1b2 | |||
be6d6501e8 | |||
10a70732ce | |||
a725dc82a7 | |||
97ce8a94e9 | |||
85f0904872 | |||
cb0a5e42df | |||
a0c7fa30c0 | |||
b7490b479c | |||
0d35edbbf3 |
9
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
9
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -27,14 +27,7 @@ body:
|
||||
attributes:
|
||||
label: Version of PolyMC
|
||||
description: The version of PolyMC used in the bug report.
|
||||
placeholder: PolyMC 1.4.1
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Version of Qt
|
||||
description: The version of Qt used in the bug report. You can find it in Help -> About PolyMC -> About Qt.
|
||||
placeholder: Qt 6.3.0
|
||||
placeholder: PolyMC 1.3.2
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
3
.github/codeql/codeql-config.yml
vendored
3
.github/codeql/codeql-config.yml
vendored
@ -1,3 +0,0 @@
|
||||
query-filters:
|
||||
- exclude:
|
||||
id: cpp/fixme-comment
|
126
.github/workflows/build.yml
vendored
126
.github/workflows/build.yml
vendored
@ -27,32 +27,25 @@ jobs:
|
||||
qt_host: linux
|
||||
qt_version: '6.2.4'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
qt_path: /home/runner/work/PolyMC/Qt
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-Legacy"
|
||||
msystem: clang32
|
||||
msystem: mingw32
|
||||
qt_ver: 5
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows"
|
||||
msystem: clang64
|
||||
msystem: mingw32
|
||||
qt_ver: 6
|
||||
|
||||
- os: macos-12
|
||||
name: macOS
|
||||
macosx_deployment_target: 10.15
|
||||
macosx_deployment_target: 10.14
|
||||
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
|
||||
qt_ver: 5
|
||||
qt_host: mac
|
||||
qt_version: '5.15.2'
|
||||
qt_modules: ''
|
||||
qt_path: /Users/runner/work/PolyMC/Qt
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
@ -73,14 +66,6 @@ jobs:
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- name: Initialize CodeQL
|
||||
if: runner.os == 'Linux' && matrix.qt_ver == 6
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
queries: security-and-quality
|
||||
languages: cpp, java
|
||||
|
||||
- name: 'Setup MSYS2'
|
||||
if: runner.os == 'Windows'
|
||||
uses: msys2/setup-msys2@v2
|
||||
@ -89,7 +74,6 @@ jobs:
|
||||
update: true
|
||||
install: >-
|
||||
git
|
||||
mingw-w64-x86_64-binutils
|
||||
pacboy: >-
|
||||
toolchain:p
|
||||
cmake:p
|
||||
@ -100,11 +84,12 @@ jobs:
|
||||
qt${{ matrix.qt_ver }}-imageformats:p
|
||||
quazip-qt${{ matrix.qt_ver }}:p
|
||||
ccache:p
|
||||
nsis:p
|
||||
${{ matrix.qt_ver == 6 && 'qt6-5compat:p' || '' }}
|
||||
|
||||
- name: Setup ccache
|
||||
if: runner.os != 'Windows' && inputs.build_type == 'Debug'
|
||||
uses: hendrikmuhs/ccache-action@v1.2.3
|
||||
uses: hendrikmuhs/ccache-action@v1.2.1
|
||||
with:
|
||||
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}
|
||||
|
||||
@ -126,7 +111,7 @@ jobs:
|
||||
|
||||
- name: Retrieve ccache cache (Windows)
|
||||
if: runner.os == 'Windows' && inputs.build_type == 'Debug'
|
||||
uses: actions/cache@v3.0.11
|
||||
uses: actions/cache@v3.0.2
|
||||
with:
|
||||
path: '${{ github.workspace }}\.ccache'
|
||||
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}
|
||||
@ -156,16 +141,24 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
|
||||
|
||||
- name: Cache Qt (macOS and AppImage)
|
||||
id: cache-qt
|
||||
if: matrix.qt_ver == 6 && runner.os != 'Windows'
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: '${{ matrix.qt_path }}/${{ matrix.qt_version }}'
|
||||
key: ${{ matrix.qt_host }}-${{ matrix.qt_version }}-"${{ matrix.qt_modules }}"-qt_cache
|
||||
|
||||
- name: Install Qt (macOS and AppImage)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS'
|
||||
uses: jurplel/install-qt-action@v3
|
||||
if: matrix.qt_ver == 6 && runner.os != 'Windows'
|
||||
uses: jurplel/install-qt-action@v2
|
||||
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
|
||||
cached: ${{ steps.cache-qt.outputs.cache-hit }}
|
||||
aqtversion: ==2.1.*
|
||||
|
||||
- name: Prepare AppImage (Linux)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||
@ -181,20 +174,15 @@ jobs:
|
||||
##
|
||||
|
||||
- name: Configure CMake (macOS)
|
||||
if: runner.os == 'macOS' && matrix.qt_ver == 6
|
||||
if: runner.os == 'macOS'
|
||||
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
|
||||
|
||||
- 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
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DLauncher_BUILD_PLATFORM=macOS -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja
|
||||
|
||||
- name: Configure CMake (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja
|
||||
|
||||
- name: Configure CMake (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
@ -231,14 +219,6 @@ jobs:
|
||||
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
|
||||
##
|
||||
@ -249,18 +229,17 @@ jobs:
|
||||
cmake --install ${{ env.BUILD_DIR }}
|
||||
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
||||
sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
||||
mv "PrismLauncher.app" "Prism Launcher.app"
|
||||
tar -czf ../PrismLauncher.tar.gz *
|
||||
chmod +x "PolyMC.app/Contents/MacOS/polymc"
|
||||
sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PolyMC.app/Contents/MacOS/polymc"
|
||||
tar -czf ../PolyMC.tar.gz *
|
||||
|
||||
- name: Make Sparkle signature (macOS)
|
||||
if: matrix.name == 'macOS'
|
||||
if: runner.os == '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)
|
||||
signature=$(/usr/local/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PolyMC.tar.gz -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
|
||||
rm ed25519-priv.pem
|
||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||
### Artifact Information :information_source:
|
||||
@ -280,8 +259,10 @@ jobs:
|
||||
cmake --install ${{ env.BUILD_DIR }}
|
||||
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
if [ "${{ matrix.qt_ver }}" == "5" ]; then
|
||||
cp /clang32/bin/libcrypto-1_1.dll /clang32/bin/libssl-1_1.dll ./
|
||||
if [ "${{ matrix.msystem }}" == "mingw32" ]; then
|
||||
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
|
||||
|
||||
- name: Package (Windows, portable)
|
||||
@ -293,6 +274,7 @@ jobs:
|
||||
|
||||
- name: Package (Windows, installer)
|
||||
if: runner.os == 'Windows'
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
|
||||
@ -303,7 +285,7 @@ jobs:
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ 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)
|
||||
if: runner.os == 'Linux'
|
||||
@ -312,7 +294,7 @@ jobs:
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||
|
||||
cd ${{ env.INSTALL_PORTABLE_DIR }}
|
||||
tar -czf ../PrismLauncher-portable.tar.gz *
|
||||
tar -czf ../PolyMC-portable.tar.gz *
|
||||
|
||||
- name: Package AppImage (Linux)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||
@ -320,7 +302,7 @@ jobs:
|
||||
run: |
|
||||
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
|
||||
|
||||
@ -331,7 +313,7 @@ jobs:
|
||||
|
||||
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 -r /home/runner/work/PolyMC/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
||||
|
||||
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}//usr/lib/
|
||||
@ -343,7 +325,7 @@ jobs:
|
||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
|
||||
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
|
||||
@ -353,63 +335,63 @@ jobs:
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PrismLauncher.tar.gz
|
||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PolyMC.tar.gz
|
||||
|
||||
- name: Upload binary zip (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
name: PolyMC-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: ${{ env.INSTALL_DIR }}/**
|
||||
|
||||
- name: Upload binary zip (Windows, portable)
|
||||
if: runner.os == 'Windows'
|
||||
uses: actions/upload-artifact@v3
|
||||
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 }}/**
|
||||
|
||||
- name: Upload installer (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PrismLauncher-Setup.exe
|
||||
name: PolyMC-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PolyMC-Setup.exe
|
||||
|
||||
- name: Upload binary tarball (Linux, Qt 5)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PrismLauncher.tar.gz
|
||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PolyMC.tar.gz
|
||||
|
||||
- name: Upload binary tarball (Linux, portable, Qt 5)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PrismLauncher-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PrismLauncher-portable.tar.gz
|
||||
name: PolyMC-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
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: PolyMC-${{ runner.os }}-Qt6-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PolyMC.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: PolyMC-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PolyMC-portable.tar.gz
|
||||
|
||||
- name: Upload AppImage (Linux)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||
path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||
path: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||
|
||||
|
||||
|
2
.github/workflows/trigger_builds.yml
vendored
2
.github/workflows/trigger_builds.yml
vendored
@ -11,7 +11,6 @@ on:
|
||||
- '**.nix'
|
||||
- 'packages/**'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
- '.markdownlint**'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
@ -20,7 +19,6 @@ on:
|
||||
- '**.nix'
|
||||
- 'packages/**'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
- '.markdownlint**'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
54
.github/workflows/trigger_release.yml
vendored
54
.github/workflows/trigger_release.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'true'
|
||||
path: 'PrismLauncher-source'
|
||||
path: 'PolyMC-source'
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
- name: Grab and store version
|
||||
@ -34,26 +34,25 @@ jobs:
|
||||
echo "VERSION=$tag_name" >> $GITHUB_ENV
|
||||
- name: Package artifacts properly
|
||||
run: |
|
||||
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
|
||||
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ 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
|
||||
mv ${{ github.workspace }}/PolyMC-source PolyMC-${{ env.VERSION }}
|
||||
mv PolyMC-Linux-Qt6-Portable*/PolyMC-portable.tar.gz PolyMC-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||
mv PolyMC-Linux-Qt6*/PolyMC.tar.gz PolyMC-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||
mv PolyMC-Linux-Portable*/PolyMC-portable.tar.gz PolyMC-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||
mv PolyMC-Linux*/PolyMC.tar.gz PolyMC-Linux-${{ env.VERSION }}.tar.gz
|
||||
mv PolyMC-*.AppImage/PolyMC-*.AppImage PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||
mv PolyMC-macOS*/PolyMC.tar.gz PolyMC-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
|
||||
LEGACY="$(echo -n ${d} | grep -o Legacy || true)"
|
||||
INST="$(echo -n ${d} | grep -o Setup || true)"
|
||||
PORT="$(echo -n ${d} | grep -o Portable || true)"
|
||||
NAME="PrismLauncher-Windows"
|
||||
NAME="PolyMC-Windows"
|
||||
test -z "${LEGACY}" || NAME="${NAME}-Legacy"
|
||||
test -z "${PORT}" || NAME="${NAME}-Portable"
|
||||
test -z "${INST}" || mv PrismLauncher-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
|
||||
test -z "${INST}" || mv PolyMC-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
|
||||
test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" *
|
||||
cd ..
|
||||
done
|
||||
@ -65,21 +64,20 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
name: Prism Launcher ${{ env.VERSION }}
|
||||
name: PolyMC ${{ env.VERSION }}
|
||||
draft: true
|
||||
prerelease: false
|
||||
files: |
|
||||
PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||
PrismLauncher-Windows-Legacy-${{ env.VERSION }}.zip
|
||||
PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Windows-Legacy-Portable-${{ env.VERSION }}.zip
|
||||
PrismLauncher-Windows-Legacy-Setup-${{ env.VERSION }}.exe
|
||||
PrismLauncher-Windows-${{ env.VERSION }}.zip
|
||||
PrismLauncher-Windows-Portable-${{ env.VERSION }}.zip
|
||||
PrismLauncher-Windows-Setup-${{ env.VERSION }}.exe
|
||||
PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-${{ env.VERSION }}.tar.gz
|
||||
PolyMC-Linux-${{ env.VERSION }}.tar.gz
|
||||
PolyMC-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||
PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||
PolyMC-Windows-Legacy-${{ env.VERSION }}.zip
|
||||
PolyMC-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||
PolyMC-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||
PolyMC-Windows-Legacy-Portable-${{ env.VERSION }}.zip
|
||||
PolyMC-Windows-Legacy-Setup-${{ env.VERSION }}.exe
|
||||
PolyMC-Windows-${{ env.VERSION }}.zip
|
||||
PolyMC-Windows-Portable-${{ env.VERSION }}.zip
|
||||
PolyMC-Windows-Setup-${{ env.VERSION }}.exe
|
||||
PolyMC-macOS-${{ env.VERSION }}.tar.gz
|
||||
PolyMC-${{ env.VERSION }}.tar.gz
|
||||
|
7
.github/workflows/winget.yml
vendored
7
.github/workflows/winget.yml
vendored
@ -7,9 +7,8 @@ jobs:
|
||||
publish:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: vedantmgoyal2009/winget-releaser@v1
|
||||
- uses: vedantmgoyal2009/winget-releaser@latest
|
||||
with:
|
||||
identifier: PrismLauncher.PrismLauncher
|
||||
version: ${{ github.event.release.tag_name }}
|
||||
installers-regex: 'PrismLauncher-Windows-Setup-.+\.exe$'
|
||||
identifier: PolyMC.PolyMC
|
||||
installers-regex: '\.exe$'
|
||||
token: ${{ secrets.WINGET_TOKEN }}
|
||||
|
14
.gitmodules
vendored
14
.gitmodules
vendored
@ -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"]
|
||||
path = libraries/quazip
|
||||
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
|
||||
|
@ -1,12 +0,0 @@
|
||||
# MD013/line-length - Line length
|
||||
MD013: false
|
||||
|
||||
# MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content
|
||||
MD024:
|
||||
siblings-only: true
|
||||
|
||||
# MD033/no-inline-html Inline HTML
|
||||
MD033: false
|
||||
|
||||
# MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading
|
||||
MD041: false
|
@ -1,2 +0,0 @@
|
||||
libraries/nbtplusplus
|
||||
libraries/quazip
|
52
BUILD.md
52
BUILD.md
@ -1,53 +1,5 @@
|
||||
# Build Instructions
|
||||
|
||||
Full build instructions will be available on [the website](https://prismlauncher.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
|
||||
```
|
||||
Build instructions are available on [the website](https://polymc.org/wiki/development/build-instructions/).
|
||||
|
||||
If you would like to contribute or fix an issue with the Build instructions you can do so [here](https://github.com/PolyMC/polymc.github.io/blob/master/src/wiki/development/build-instructions.md).
|
||||
|
100
CMakeLists.txt
100
CMakeLists.txt
@ -33,10 +33,8 @@ set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
include(GenerateExportHeader)
|
||||
set(CMAKE_CXX_FLAGS "-Wall -pedantic -fstack-protector-strong --param=ssp-buffer-size=4 ${CMAKE_CXX_FLAGS}")
|
||||
|
||||
# ATL's packlist needs more than the default 1 Mib stack on windows
|
||||
if(WIN32)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,8388608 ${CMAKE_EXE_LINKER_FLAGS}")
|
||||
if(UNIX AND APPLE)
|
||||
set(CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}")
|
||||
endif()
|
||||
|
||||
# Fix build with Qt 5.13
|
||||
@ -69,24 +67,24 @@ find_package(ECM REQUIRED NO_MODULE)
|
||||
set(CMAKE_MODULE_PATH "${ECM_MODULE_PATH};${CMAKE_MODULE_PATH}")
|
||||
include(CTest)
|
||||
include(ECMAddTests)
|
||||
if(BUILD_TESTING)
|
||||
if (BUILD_TESTING)
|
||||
enable_testing()
|
||||
endif()
|
||||
|
||||
##################################### Set Application options #####################################
|
||||
|
||||
######## Set URLs ########
|
||||
set(Launcher_NEWS_RSS_URL "https://prismlauncher.org/feed/feed.xml" CACHE STRING "URL to fetch Prism Launcher's news RSS feed from.")
|
||||
set(Launcher_NEWS_OPEN_URL "https://prismlauncher.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_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://polymc.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'")
|
||||
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(Launcher_VERSION_MAJOR 5)
|
||||
set(Launcher_VERSION_MINOR 2)
|
||||
set(Launcher_VERSION_MAJOR 1)
|
||||
set(Launcher_VERSION_MINOR 4)
|
||||
set(Launcher_VERSION_HOTFIX 2)
|
||||
|
||||
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
||||
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
|
||||
set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0")
|
||||
# Build number
|
||||
set(Launcher_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
|
||||
|
||||
# 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.")
|
||||
@ -95,25 +93,25 @@ set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the plat
|
||||
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
|
||||
|
||||
# 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
|
||||
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
|
||||
|
||||
# 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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
|
||||
@ -128,13 +126,12 @@ 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
|
||||
# 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
|
||||
# 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.
|
||||
# This key was issued specifically for Prism Launcher
|
||||
set(Launcher_CURSEFORGE_API_KEY "$2a$10$wuAJuNZuted3NORVmpgUC.m8sI.pv1tOPKZyBgLFGjxFp/br0lZCC" CACHE STRING "API key for the CurseForge platform")
|
||||
set(Launcher_CURSEFORGE_API_KEY "$2a$10$1Oqr2MX3O4n/ilhFGc597u8tfI3L2Hyr9/rtWDAMRjghSQV2QUuxq" CACHE STRING "API key for the CurseForge platform")
|
||||
|
||||
|
||||
#### Check the current Git commit and branch
|
||||
@ -146,8 +143,15 @@ message(STATUS "Git commit: ${Launcher_GIT_COMMIT}")
|
||||
message(STATUS "Git tag: ${Launcher_GIT_TAG}")
|
||||
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")
|
||||
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 ################################
|
||||
|
||||
@ -195,17 +199,9 @@ if (Qt5_POSITION_INDEPENDENT_CODE)
|
||||
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
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 #######################################
|
||||
|
||||
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)
|
||||
|
||||
####################################### Install layout #######################################
|
||||
@ -227,16 +223,16 @@ if(UNIX AND APPLE)
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.app")
|
||||
|
||||
# Mac bundle settings
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_DisplayName}")
|
||||
set(MACOSX_BUNDLE_INFO_STRING "${Launcher_DisplayName}: A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.")
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.prismlauncher.${Launcher_Name}")
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_NAME}")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}")
|
||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_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_GUI_IDENTIFIER "org.polymc.${Launcher_Name}")
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
||||
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_COPYRIGHT "© 2022 ${Launcher_Copyright_Mac}")
|
||||
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=")
|
||||
set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml")
|
||||
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2021-2022 ${Launcher_Copyright}")
|
||||
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "idALcUIazingvKSSsEa9U7coDVxZVx/ORpOEE/QtJfg=")
|
||||
set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://polymc.org/feed/appcast.xml")
|
||||
|
||||
set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0/Sparkle-2.1.0.tar.xz" CACHE STRING "URL to Sparkle release archive")
|
||||
set(MACOSX_SPARKLE_SHA256 "bf6ac1caa9f8d321d5784859c88da874f28412f37fb327bc21b7b14c5d61ef94" CACHE STRING "SHA256 checksum for Sparkle release archive")
|
||||
@ -254,7 +250,7 @@ if(UNIX AND APPLE)
|
||||
elseif(UNIX)
|
||||
set(BINARY_DEST_DIR "bin")
|
||||
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
|
||||
set(JARS_DEST_DIR "share/${Launcher_APP_BINARY_NAME}")
|
||||
set(JARS_DEST_DIR "share/jars")
|
||||
set(LAUNCHER_DESKTOP_DEST_DIR "share/applications" CACHE STRING "Path to the desktop file directory")
|
||||
set(LAUNCHER_METAINFO_DEST_DIR "share/metainfo" CACHE STRING "Path to the metainfo directory")
|
||||
set(LAUNCHER_ICON_DEST_DIR "share/icons/hicolor/scalable/apps" CACHE STRING "Path to the scalable icon directory")
|
||||
@ -311,6 +307,7 @@ add_subdirectory(libraries/systeminfo) # system information library
|
||||
add_subdirectory(libraries/hoedown) # markdown parser
|
||||
add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
|
||||
add_subdirectory(libraries/javacheck) # java compatibility checker
|
||||
add_subdirectory(libraries/xz-embedded) # xz compression
|
||||
if (FORCE_BUNDLED_QUAZIP)
|
||||
message(STATUS "Using bundled QuaZip")
|
||||
set(BUILD_SHARED_LIBS 0) # link statically to avoid conflicts.
|
||||
@ -321,30 +318,15 @@ else()
|
||||
endif()
|
||||
add_subdirectory(libraries/rainbow) # Qt extension for colors
|
||||
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
|
||||
if(NOT tomlplusplus_FOUND)
|
||||
message(STATUS "Using bundled tomlplusplus")
|
||||
add_subdirectory(libraries/tomlplusplus) # toml parser
|
||||
else()
|
||||
message(STATUS "Using system tomlplusplus")
|
||||
endif()
|
||||
add_subdirectory(libraries/classparser) # class parser library
|
||||
add_subdirectory(libraries/tomlc99) # toml parser
|
||||
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 ###############################
|
||||
|
||||
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.
|
||||
add_subdirectory(launcher)
|
||||
|
@ -1,5 +1,4 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
This is a modified version of the Contributor Covenant.
|
||||
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
|
||||
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).
|
||||
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
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
|
||||
|
@ -6,7 +6,6 @@ Try to follow the existing formatting.
|
||||
If there is no existing formatting, you may use `clang-format` with our included `.clang-format` configuration.
|
||||
|
||||
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.
|
||||
@ -27,37 +26,37 @@ 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
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
(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
|
||||
(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.
|
||||
(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.
|
||||
```
|
||||
(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
|
||||
|
156
COPYING.md
156
COPYING.md
@ -1,38 +1,4 @@
|
||||
## Prism Launcher
|
||||
|
||||
Prism Launcher - Minecraft Launcher
|
||||
Copyright (C) 2022 Prism Launcher Contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, version 3.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice:
|
||||
|
||||
Copyright 2013-2021 MultiMC Contributors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
## PolyMC
|
||||
# PolyMC
|
||||
|
||||
PolyMC - Minecraft Launcher
|
||||
Copyright (C) 2021-2022 PolyMC Contributors
|
||||
@ -66,7 +32,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
## MinGW-w64 runtime (Windows)
|
||||
# MinGW-w64 runtime (Windows)
|
||||
|
||||
Copyright (c) 2009, 2010, 2011, 2012, 2013 by the mingw-w64 project
|
||||
|
||||
@ -108,14 +74,14 @@
|
||||
|
||||
Information on third party licenses used in MinGW-w64 can be found in its COPYING.MinGW-w64-runtime.txt.
|
||||
|
||||
## Qt 5/6
|
||||
# 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++
|
||||
|
||||
libnbt++ - A library for the Minecraft Named Binary Tag format.
|
||||
Copyright (C) 2013, 2015 ljfa-ag
|
||||
@ -133,7 +99,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
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 Olaf Schmidt <ojschmidt@kde.org>
|
||||
@ -156,7 +122,7 @@
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
|
||||
## Hoedown
|
||||
# Hoedown
|
||||
|
||||
Copyright (c) 2008, Natacha Porté
|
||||
Copyright (c) 2011, Vicent Martí
|
||||
@ -174,7 +140,7 @@
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
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")
|
||||
in any personal, open-source or commercial work without obligation of payment
|
||||
@ -190,7 +156,7 @@
|
||||
PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS,
|
||||
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/),
|
||||
with Reserved Font Name Material Design Icons.
|
||||
@ -201,7 +167,7 @@
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
## Quazip
|
||||
# Quazip
|
||||
|
||||
Copyright (C) 2005-2021 Sergey A. Tachenov
|
||||
|
||||
@ -225,7 +191,53 @@
|
||||
|
||||
See COPYING file for the full LGPL text.
|
||||
|
||||
## launcher (`libraries/launcher`)
|
||||
# xz-minidec
|
||||
|
||||
XZ decompressor
|
||||
|
||||
Authors: Lasse Collin <lasse.collin@tukaani.org>
|
||||
Igor Pavlov <http://7-zip.org/>
|
||||
|
||||
This file has been put into the public domain.
|
||||
You can do whatever you want with this file.
|
||||
|
||||
# ColumnResizer
|
||||
|
||||
Copyright (c) 2011-2016 Aurélien Gâteau and contributors.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted (subject to the limitations in the
|
||||
disclaimer below) provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* The name of the contributors may not be used to endorse or
|
||||
promote products derived from this software without specific prior
|
||||
written permission.
|
||||
|
||||
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
|
||||
GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
|
||||
HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# launcher (`libraries/launcher`)
|
||||
|
||||
PolyMC - Minecraft Launcher
|
||||
Copyright (C) 2021-2022 PolyMC Contributors
|
||||
@ -276,7 +288,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
## lionshead
|
||||
# lionshead
|
||||
|
||||
Code has been taken from https://github.com/natefoo/lionshead and loosely
|
||||
translated to C++ laced with Qt.
|
||||
@ -303,26 +315,32 @@
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
## tomlplusplus
|
||||
# tomlc99
|
||||
|
||||
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
|
||||
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:
|
||||
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 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.
|
||||
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.
|
||||
|
||||
## O2 (Katabasis fork)
|
||||
# O2 (Katabasis fork)
|
||||
|
||||
Copyright (c) 2012, Akos Polster
|
||||
All rights reserved.
|
||||
@ -376,25 +394,3 @@
|
||||
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.
|
||||
|
95
README.md
95
README.md
@ -1,29 +1,98 @@
|
||||
<p align="center">
|
||||
<img src="./program_info/org.prismlauncher.PrismLauncher.logo.svg#gh-light-mode-only" alt="Prism Launcher logo" width="50%"/>
|
||||
<img src="./program_info/org.prismlauncher.PrismLauncher.logo-darkmode.svg#gh-dark-mode-only" alt="Prism Launcher logo" width="50%"/>
|
||||
<p align="center">
|
||||
<img src="./program_info/polymc-header-black.svg#gh-light-mode-only" alt="PolyMC logo" width="50%"/>
|
||||
<img src="./program_info/polymc-header.svg#gh-dark-mode-only" alt="PolyMC logo" width="50%"/>
|
||||
</p>
|
||||
|
||||
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.
|
||||
- Last build status can be found [here](https://github.com/PrismLauncher/PrismLauncher/actions).
|
||||
- All downloads and instructions for PolyMC can be found [here](https://polymc.org/download/)
|
||||
- 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.
|
||||
|
||||
## Help & Support
|
||||
For Debian and Arch, you can use these packages for the latest development versions:
|
||||
[](https://aur.archlinux.org/packages/polymc-git/)
|
||||
[](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
|
||||
|
||||
[](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.
|
||||
|
||||
[](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:
|
||||
|
||||
[](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:
|
||||
|
||||
[](https://matrix.to/#/#polymc-development:matrix.org)
|
||||
[](https://matrix.to/#/#polymc-discussion:matrix.org)
|
||||
[](https://matrix.to/#/#polymc-github:matrix.org)
|
||||
[](https://matrix.to/#/#polymc-maintainers:matrix.org)
|
||||
[](https://matrix.to/#/#polymc-news:matrix.org)
|
||||
[](https://matrix.to/#/#polymc-offtopic:matrix.org)
|
||||
[](https://matrix.to/#/#polymc-support:matrix.org)
|
||||
[](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.
|
||||
|
||||
## 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/tree/master/src/download).
|
||||
|
||||
## Forking/Redistributing/Custom builds policy
|
||||
|
||||
We don't care what you do with your fork/custom build as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy:
|
||||
- Make it clear that your fork is not PolyMC and is not endorsed by or affiliated with the PolyMC project (https://polymc.org).
|
||||
- Go through [CMakeLists.txt](CMakeLists.txt) and change PolyMC's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled).
|
||||
|
||||
If you have any questions or want any clarification on the above conditions please make an issue and ask us.
|
||||
|
||||
Be aware that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions:
|
||||
- [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
|
||||
- [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions)
|
||||
|
||||
If you do not agree with these terms and conditions, then remove the associated API keys from the [CMakeLists.txt](CMakeLists.txt) file by setting them to an empty string (`""`).
|
||||
|
||||
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.
|
||||
|
||||
## Sponsors
|
||||
Thank you to all our generous backers over at Open Collective! Support PolyMC by [becoming a backer](https://opencollective.com/polymc).
|
||||
|
||||
[](https://opencollective.com/polymc#backers)
|
||||
|
||||
Also, thanks to JetBrains for providing us a few licenses for all their products, as part of their [Open Source program](https://www.jetbrains.com/opensource/).
|
||||
|
||||
[](https://www.jetbrains.com/opensource/)
|
||||
|
||||
Additionally, thanks to the awesome people over at [MacStadium](https://www.macstadium.com/), for providing M1-Macs for development purposes!
|
||||
|
||||
<a href="https://www.macstadium.com"><img src="https://uploads-ssl.webflow.com/5ac3c046c82724970fc60918/5c019d917bba312af7553b49_MacStadium-developerlogo.png" alt="Powered by MacStadium" width="300"></a>
|
||||
|
@ -42,14 +42,12 @@ Config::Config()
|
||||
{
|
||||
// Name and copyright
|
||||
LAUNCHER_NAME = "@Launcher_Name@";
|
||||
LAUNCHER_APP_BINARY_NAME = "@Launcher_APP_BINARY_NAME@";
|
||||
LAUNCHER_DISPLAYNAME = "@Launcher_DisplayName@";
|
||||
LAUNCHER_COPYRIGHT = "@Launcher_Copyright@";
|
||||
LAUNCHER_DOMAIN = "@Launcher_Domain@";
|
||||
LAUNCHER_CONFIGFILE = "@Launcher_ConfigFile@";
|
||||
LAUNCHER_GIT = "@Launcher_Git@";
|
||||
LAUNCHER_DESKTOPFILENAME = "@Launcher_DesktopFileName@";
|
||||
LAUNCHER_SVGFILENAME = "@Launcher_SVGFileName@";
|
||||
|
||||
USER_AGENT = "@Launcher_UserAgent@";
|
||||
USER_AGENT_UNCACHED = USER_AGENT + " (Uncached)";
|
||||
@ -57,9 +55,10 @@ Config::Config()
|
||||
// Version information
|
||||
VERSION_MAJOR = @Launcher_VERSION_MAJOR@;
|
||||
VERSION_MINOR = @Launcher_VERSION_MINOR@;
|
||||
VERSION_HOTFIX = @Launcher_VERSION_HOTFIX@;
|
||||
VERSION_BUILD = @Launcher_VERSION_BUILD@;
|
||||
|
||||
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
|
||||
BUILD_DATE = "@Launcher_BUILD_TIMESTAMP@";
|
||||
UPDATER_BASE = "@Launcher_UPDATER_BASE@";
|
||||
|
||||
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
|
||||
@ -86,7 +85,7 @@ Config::Config()
|
||||
{
|
||||
VERSION_CHANNEL = GIT_REFSPEC;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -99,6 +98,7 @@ Config::Config()
|
||||
VERSION_CHANNEL = "unknown";
|
||||
}
|
||||
|
||||
VERSION_STR = "@Launcher_VERSION_STRING@";
|
||||
NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
|
||||
NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@";
|
||||
HELP_URL = "@Launcher_HELP_URL@";
|
||||
@ -116,7 +116,7 @@ Config::Config()
|
||||
|
||||
QString Config::versionString() const
|
||||
{
|
||||
return QString("%1.%2").arg(VERSION_MAJOR).arg(VERSION_MINOR);
|
||||
return QString("%1.%2.%3").arg(VERSION_MAJOR).arg(VERSION_MINOR).arg(VERSION_HOTFIX);
|
||||
}
|
||||
|
||||
QString Config::printableVersionString() const
|
||||
@ -128,5 +128,11 @@ QString Config::printableVersionString() const
|
||||
{
|
||||
vstr += "-" + VERSION_CHANNEL;
|
||||
}
|
||||
|
||||
// if a build number is set, also add it to the end
|
||||
if(VERSION_BUILD >= 0)
|
||||
{
|
||||
vstr += "+build." + QString::number(VERSION_BUILD);
|
||||
}
|
||||
return vstr;
|
||||
}
|
||||
|
@ -44,19 +44,21 @@ class Config {
|
||||
public:
|
||||
Config();
|
||||
QString LAUNCHER_NAME;
|
||||
QString LAUNCHER_APP_BINARY_NAME;
|
||||
QString LAUNCHER_DISPLAYNAME;
|
||||
QString LAUNCHER_COPYRIGHT;
|
||||
QString LAUNCHER_DOMAIN;
|
||||
QString LAUNCHER_CONFIGFILE;
|
||||
QString LAUNCHER_GIT;
|
||||
QString LAUNCHER_DESKTOPFILENAME;
|
||||
QString LAUNCHER_SVGFILENAME;
|
||||
|
||||
/// The major version number.
|
||||
int VERSION_MAJOR;
|
||||
/// The minor version number.
|
||||
int VERSION_MINOR;
|
||||
/// The hotfix number.
|
||||
int VERSION_HOTFIX;
|
||||
/// The build number.
|
||||
int VERSION_BUILD;
|
||||
|
||||
/**
|
||||
* The version channel
|
||||
@ -69,9 +71,6 @@ class Config {
|
||||
/// A short string identifying this build's platform. For example, "lin64" or "win32".
|
||||
QString BUILD_PLATFORM;
|
||||
|
||||
/// A string containing the build timestamp
|
||||
QString BUILD_DATE;
|
||||
|
||||
/// URL for the updater's channel
|
||||
QString UPDATER_BASE;
|
||||
|
||||
@ -96,6 +95,9 @@ class Config {
|
||||
/// The git refspec of this build
|
||||
QString GIT_REFSPEC;
|
||||
|
||||
/// This is printed on start to standard output
|
||||
QString VERSION_STR;
|
||||
|
||||
/**
|
||||
* This is used to fetch the news RSS feed.
|
||||
* It defaults in CMakeLists.txt to "https://multimc.org/rss.xml"
|
||||
@ -142,8 +144,8 @@ class Config {
|
||||
QString LIBRARY_BASE = "https://libraries.minecraft.net/";
|
||||
QString AUTH_BASE = "https://authserver.mojang.com/";
|
||||
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
|
||||
QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists
|
||||
QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists
|
||||
QString FMLLIBS_BASE_URL = "https://files.polymc.org/fmllibs/";
|
||||
QString TRANSLATIONS_BASE_URL = "https://i18n.polymc.org/";
|
||||
|
||||
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
|
||||
|
||||
|
29
flake.lock
generated
29
flake.lock
generated
@ -21,24 +21,24 @@
|
||||
"locked": {
|
||||
"lastModified": 1650031308,
|
||||
"narHash": "sha256-TvVOjkUobYJD9itQYueELJX3wmecvEdCbJ0FinW2mL4=",
|
||||
"owner": "PrismLauncher",
|
||||
"owner": "PolyMC",
|
||||
"repo": "libnbtplusplus",
|
||||
"rev": "2203af7eeb48c45398139b583615134efd8d407f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "PrismLauncher",
|
||||
"owner": "PolyMC",
|
||||
"repo": "libnbtplusplus",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1666057921,
|
||||
"narHash": "sha256-VpQqtXdj6G7cH//SvoprjR7XT3KS7p+tCVebGK1N6tE=",
|
||||
"lastModified": 1658119717,
|
||||
"narHash": "sha256-4upOZIQQ7Bc4CprqnHsKnqYfw+arJeAuU+QcpjYBXW0=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "88eab1e431cabd0ed621428d8b40d425a07af39f",
|
||||
"rev": "9eb60f25aff0d2218c848dd4574a0ab5e296cabe",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -52,24 +52,7 @@
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"libnbtplusplus": "libnbtplusplus",
|
||||
"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"
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
11
flake.nix
11
flake.nix
@ -4,11 +4,10 @@
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
|
||||
libnbtplusplus = { url = "github:PrismLauncher/libnbtplusplus"; flake = false; };
|
||||
tomlplusplus = { url = "github:marzer/tomlplusplus"; flake = false; };
|
||||
libnbtplusplus = { url = "github:PolyMC/libnbtplusplus"; flake = false; };
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, libnbtplusplus, tomlplusplus, ... }:
|
||||
outputs = { self, nixpkgs, libnbtplusplus, ... }:
|
||||
let
|
||||
# User-friendly version number.
|
||||
version = builtins.substring 0 8 self.lastModifiedDate;
|
||||
@ -23,14 +22,14 @@
|
||||
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; };
|
||||
polymc = pkgs.libsForQt5.callPackage ./nix { inherit version self libnbtplusplus; };
|
||||
polymc-qt6 = pkgs.qt6Packages.callPackage ./nix { inherit version self libnbtplusplus; };
|
||||
};
|
||||
in
|
||||
{
|
||||
packages = forAllSystems (system:
|
||||
let packages = packagesFn pkgs.${system}; in
|
||||
packages // { default = packages.prismlauncher; }
|
||||
packages // { default = packages.polymc; }
|
||||
);
|
||||
|
||||
overlay = final: packagesFn;
|
||||
|
@ -60,11 +60,6 @@
|
||||
#include "ui/themes/BrightTheme.h"
|
||||
#include "ui/themes/CustomTheme.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "ui/WinDarkmode.h"
|
||||
#include <versionhelpers.h>
|
||||
#endif
|
||||
|
||||
#include "ui/setupwizard/SetupWizard.h"
|
||||
#include "ui/setupwizard/LanguageWizardPage.h"
|
||||
#include "ui/setupwizard/JavaWizardPage.h"
|
||||
@ -79,7 +74,6 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <QAccessible>
|
||||
#include <QCommandLineParser>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QNetworkAccessManager>
|
||||
@ -112,17 +106,13 @@
|
||||
#include "translations/TranslationsModel.h"
|
||||
#include "meta/Index.h"
|
||||
|
||||
#include <Commandline.h>
|
||||
#include <FileSystem.h>
|
||||
#include <DesktopServices.h>
|
||||
#include <LocalPeer.h>
|
||||
|
||||
#include <sys.h>
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#include <dlfcn.h>
|
||||
#include "gamemode_client.h"
|
||||
#endif
|
||||
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
@ -137,6 +127,12 @@
|
||||
|
||||
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 {
|
||||
void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||
{
|
||||
@ -237,27 +233,80 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
this->setQuitOnLastWindowClosed(false);
|
||||
|
||||
// Commandline parsing
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(BuildConfig.LAUNCHER_DISPLAYNAME);
|
||||
QHash<QString, QVariant> args;
|
||||
{
|
||||
Parser parser(FlagStyle::GNU, ArgumentStyle::SpaceAndEquals);
|
||||
|
||||
parser.addOptions({
|
||||
{{"d", "dir"}, "Use a custom path as application root (use '.' for current directory)", "directory"},
|
||||
{{"l", "launch"}, "Launch the specified instance (by instance ID)", "instance"},
|
||||
{{"s", "server"}, "Join the specified server on launch (only valid in combination with --launch)", "address"},
|
||||
{{"a", "profile"}, "Use the account specified by its profile name (only valid in combination with --launch)", "profile"},
|
||||
{"alive", "Write a small '" + liveCheckFile + "' file after the launcher starts"},
|
||||
{{"I", "import"}, "Import instance from specified zip (local path or URL)", "file"}
|
||||
});
|
||||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
// --help
|
||||
parser.addSwitch("help");
|
||||
parser.addShortOpt("help", 'h');
|
||||
parser.addDocumentation("help", "Display this help and exit.");
|
||||
// --version
|
||||
parser.addSwitch("version");
|
||||
parser.addShortOpt("version", 'V');
|
||||
parser.addDocumentation("version", "Display program version and exit.");
|
||||
// --dir
|
||||
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");
|
||||
m_serverToJoin = parser.value("server");
|
||||
m_profileToUse = parser.value("profile");
|
||||
m_liveCheck = parser.isSet("alive");
|
||||
m_zipToImport = parser.value("import");
|
||||
// display help and exit
|
||||
if (args["help"].toBool())
|
||||
{
|
||||
std::cout << qPrintable(parser.compileHelp(arguments()[0]));
|
||||
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
|
||||
if((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty())
|
||||
@ -288,7 +337,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
QString adjustedBy;
|
||||
QString dataPath;
|
||||
// change folder
|
||||
QString dirParam = parser.value("dir");
|
||||
QString dirParam = args["dir"].toString();
|
||||
if (!dirParam.isEmpty())
|
||||
{
|
||||
// the dir param. it makes multimc data path point to whatever the user specified
|
||||
@ -302,12 +351,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
dataPath = foo.absolutePath();
|
||||
adjustedBy = "Persistent data path";
|
||||
|
||||
QDir polymcData(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation), "PolyMC"));
|
||||
if (polymcData.exists()) {
|
||||
dataPath = polymcData.absolutePath();
|
||||
adjustedBy = "PolyMC data path";
|
||||
}
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
// TODO: this should be removed in a future version
|
||||
// TODO: provide a migration path similar to macOS migration
|
||||
@ -333,6 +376,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
QString(
|
||||
"The launcher data folder could not be created.\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"
|
||||
"(%1)\n"
|
||||
"\n"
|
||||
@ -348,6 +394,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
QString(
|
||||
"The launcher data folder could not be opened.\n"
|
||||
"\n"
|
||||
#if defined(Q_OS_MAC)
|
||||
MACOS_HINT
|
||||
#endif
|
||||
"Make sure you have the right permissions to the launcher data folder.\n"
|
||||
"(%1)\n"
|
||||
"\n"
|
||||
@ -428,6 +477,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
QString(
|
||||
"The launcher couldn't create a log file - the data folder is not writable.\n"
|
||||
"\n"
|
||||
#if defined(Q_OS_MAC)
|
||||
MACOS_HINT
|
||||
#endif
|
||||
"Make sure you have write permissions to the data folder.\n"
|
||||
"(%1)\n"
|
||||
"\n"
|
||||
@ -489,8 +541,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
|
||||
// Initialize application settings
|
||||
{
|
||||
// Provide a fallback for migration from PolyMC
|
||||
m_settings.reset(new INISettingsObject({ BuildConfig.LAUNCHER_CONFIGFILE, "polymc.cfg", "multimc.cfg" }, this));
|
||||
m_settings.reset(new INISettingsObject(BuildConfig.LAUNCHER_CONFIGFILE, this));
|
||||
// Updates
|
||||
// Multiple channels are separated by spaces
|
||||
m_settings->registerSetting("UpdateChannel", BuildConfig.VERSION_CHANNEL);
|
||||
@ -629,8 +680,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
|
||||
m_settings->registerSetting("UpdateDialogGeometry", "");
|
||||
|
||||
m_settings->registerSetting("ModDownloadGeometry", "");
|
||||
|
||||
// HACK: This code feels so stupid is there a less stupid way of doing this?
|
||||
{
|
||||
m_settings->registerSetting("PastebinURL", "");
|
||||
@ -725,7 +774,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
auto platform = getIdealPlatform(BuildConfig.BUILD_PLATFORM);
|
||||
auto channelUrl = BuildConfig.UPDATER_BASE + platform + "/channels.json";
|
||||
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.";
|
||||
}
|
||||
|
||||
@ -817,7 +866,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
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("ModrinthModpacks", QDir("cache/ModrinthModpacks").absolutePath());
|
||||
m_metacache->addBase("root", QDir::currentPath());
|
||||
m_metacache->addBase("translations", QDir("translations").absolutePath());
|
||||
m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
|
||||
@ -868,13 +916,10 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
qDebug() << "<> Application theme set.";
|
||||
}
|
||||
|
||||
updateCapabilities();
|
||||
|
||||
if(createSetupWizard())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
performMainStartupAction();
|
||||
}
|
||||
|
||||
@ -983,7 +1028,7 @@ void Application::performMainStartupAction()
|
||||
qDebug() << " Launching with account" << m_profileToUse;
|
||||
}
|
||||
|
||||
launch(inst, true, false, nullptr, serverToJoin, accountToUse);
|
||||
launch(inst, true, nullptr, serverToJoin, accountToUse);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1087,7 +1132,6 @@ void Application::messageReceived(const QByteArray& message)
|
||||
launch(
|
||||
instance,
|
||||
true,
|
||||
false,
|
||||
nullptr,
|
||||
serverObject,
|
||||
accountObject
|
||||
@ -1125,6 +1169,15 @@ std::vector<ITheme *> Application::getValidApplicationThemes()
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Application::isFlatpak()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
return QFile::exists("/.flatpak-info");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Application::setApplicationTheme(const QString& name, bool initial)
|
||||
{
|
||||
auto systemPalette = qApp->palette();
|
||||
@ -1133,15 +1186,6 @@ void Application::setApplicationTheme(const QString& name, bool initial)
|
||||
{
|
||||
auto & theme = (*themeIter).second;
|
||||
theme->apply(initial);
|
||||
#ifdef Q_OS_WIN
|
||||
if (m_mainWindow && IsWindows10OrGreater()) {
|
||||
if (QString::compare(theme->id(), "dark") == 0) {
|
||||
WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), true);
|
||||
} else {
|
||||
WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1157,7 +1201,7 @@ void Application::setIconTheme(const QString& name)
|
||||
QIcon Application::getThemedIcon(const QString& name)
|
||||
{
|
||||
if(name == "logo") {
|
||||
return QIcon(":/" + BuildConfig.LAUNCHER_SVGFILENAME);
|
||||
return QIcon(":/org.polymc.PolyMC.svg");
|
||||
}
|
||||
return QIcon::fromTheme(name);
|
||||
}
|
||||
@ -1179,7 +1223,6 @@ bool Application::openJsonEditor(const QString &filename)
|
||||
bool Application::launch(
|
||||
InstancePtr instance,
|
||||
bool online,
|
||||
bool demo,
|
||||
BaseProfilerFactory *profiler,
|
||||
MinecraftServerTargetPtr serverToJoin,
|
||||
MinecraftAccountPtr accountToUse
|
||||
@ -1203,7 +1246,6 @@ bool Application::launch(
|
||||
controller.reset(new LaunchController());
|
||||
controller->setInstance(instance);
|
||||
controller->setOnline(online);
|
||||
controller->setDemo(demo);
|
||||
controller->setProfiler(profiler);
|
||||
controller->setServerToJoin(serverToJoin);
|
||||
controller->setAccountToUse(accountToUse);
|
||||
@ -1374,16 +1416,6 @@ MainWindow* Application::showMainWindow(bool minimized)
|
||||
m_mainWindow = new MainWindow();
|
||||
m_mainWindow->restoreState(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowState").toByteArray()));
|
||||
m_mainWindow->restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowGeometry").toByteArray()));
|
||||
#ifdef Q_OS_WIN
|
||||
if (IsWindows10OrGreater())
|
||||
{
|
||||
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)
|
||||
{
|
||||
m_mainWindow->showMinimized();
|
||||
@ -1536,37 +1568,21 @@ shared_qobject_ptr<Meta::Index> Application::metadataIndex()
|
||||
return m_metadataIndex;
|
||||
}
|
||||
|
||||
void Application::updateCapabilities()
|
||||
Application::Capabilities Application::currentCapabilities()
|
||||
{
|
||||
m_capabilities = None;
|
||||
Capabilities c;
|
||||
if (!getMSAClientID().isEmpty())
|
||||
m_capabilities |= SupportsMSA;
|
||||
c |= 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);
|
||||
// try normal variant as well
|
||||
if (dummy == NULL)
|
||||
dummy = dlopen("libMangoHud.so", RTLD_LAZY);
|
||||
|
||||
if (dummy != NULL) {
|
||||
dlclose(dummy);
|
||||
m_capabilities |= SupportsMangoHud;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
c |= SupportsFlame;
|
||||
return c;
|
||||
}
|
||||
|
||||
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/" + BuildConfig.LAUNCHER_APP_BINARY_NAME),
|
||||
FS::PathCombine(m_rootPath, "share/jars"),
|
||||
#endif
|
||||
FS::PathCombine(m_rootPath, "jars"),
|
||||
FS::PathCombine(applicationDirPath(), "jars")
|
||||
|
@ -95,8 +95,6 @@ public:
|
||||
|
||||
SupportsMSA = 1 << 0,
|
||||
SupportsFlame = 1 << 1,
|
||||
SupportsGameMode = 1 << 2,
|
||||
SupportsMangoHud = 1 << 3,
|
||||
};
|
||||
Q_DECLARE_FLAGS(Capabilities, Capability)
|
||||
|
||||
@ -116,6 +114,8 @@ public:
|
||||
|
||||
QIcon getThemedIcon(const QString& name);
|
||||
|
||||
bool isFlatpak();
|
||||
|
||||
void setIconTheme(const QString& name);
|
||||
|
||||
std::vector<ITheme *> getValidApplicationThemes();
|
||||
@ -162,7 +162,7 @@ public:
|
||||
|
||||
shared_qobject_ptr<Meta::Index> metadataIndex();
|
||||
|
||||
void updateCapabilities();
|
||||
Capabilities currentCapabilities();
|
||||
|
||||
/*!
|
||||
* Finds and returns the full path to a jar file.
|
||||
@ -180,10 +180,6 @@ public:
|
||||
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
|
||||
* specified in the settings
|
||||
@ -211,7 +207,6 @@ public slots:
|
||||
bool launch(
|
||||
InstancePtr instance,
|
||||
bool online = true,
|
||||
bool demo = false,
|
||||
BaseProfilerFactory *profiler = nullptr,
|
||||
MinecraftServerTargetPtr serverToJoin = nullptr,
|
||||
MinecraftAccountPtr accountToUse = nullptr
|
||||
@ -263,7 +258,6 @@ private:
|
||||
|
||||
QString m_rootPath;
|
||||
Status m_status = Application::StartingUp;
|
||||
Capabilities m_capabilities;
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive;
|
||||
|
@ -17,14 +17,13 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "BaseVersion.h"
|
||||
|
||||
class MinecraftInstance;
|
||||
class QDir;
|
||||
class QString;
|
||||
class QObject;
|
||||
class Task;
|
||||
class BaseVersion;
|
||||
typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
|
||||
|
||||
class BaseInstaller
|
||||
{
|
||||
@ -36,7 +35,7 @@ public:
|
||||
virtual bool add(MinecraftInstance *to);
|
||||
virtual bool remove(MinecraftInstance *from);
|
||||
|
||||
virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersion::Ptr version, QObject *parent) = 0;
|
||||
virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersionPtr version, QObject *parent) = 0;
|
||||
|
||||
protected:
|
||||
virtual QString id() const = 0;
|
||||
|
@ -53,22 +53,15 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
|
||||
: QObject()
|
||||
{
|
||||
m_settings = settings;
|
||||
m_global_settings = globalSettings;
|
||||
m_rootDir = rootDir;
|
||||
|
||||
m_settings->registerSetting("name", "Unnamed Instance");
|
||||
m_settings->registerSetting("iconKey", "default");
|
||||
m_settings->registerSetting("notes", "");
|
||||
|
||||
m_settings->registerSetting("lastLaunchTime", 0);
|
||||
m_settings->registerSetting("totalTimePlayed", 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
|
||||
// a locally stored instance
|
||||
if (!m_settings->getSetting("InstanceType"))
|
||||
@ -114,59 +107,49 @@ QString BaseInstance::getPostExitCommand()
|
||||
return settings()->get("PostExitCommand").toString();
|
||||
}
|
||||
|
||||
bool BaseInstance::isManagedPack() const
|
||||
bool BaseInstance::isManagedPack()
|
||||
{
|
||||
return m_settings->get("ManagedPack").toBool();
|
||||
return settings()->get("ManagedPack").toBool();
|
||||
}
|
||||
|
||||
QString BaseInstance::getManagedPackType() const
|
||||
QString BaseInstance::getManagedPackType()
|
||||
{
|
||||
return m_settings->get("ManagedPackType").toString();
|
||||
return settings()->get("ManagedPackType").toString();
|
||||
}
|
||||
|
||||
QString BaseInstance::getManagedPackID() const
|
||||
QString BaseInstance::getManagedPackID()
|
||||
{
|
||||
return m_settings->get("ManagedPackID").toString();
|
||||
return settings()->get("ManagedPackID").toString();
|
||||
}
|
||||
|
||||
QString BaseInstance::getManagedPackName() const
|
||||
QString BaseInstance::getManagedPackName()
|
||||
{
|
||||
return m_settings->get("ManagedPackName").toString();
|
||||
return settings()->get("ManagedPackName").toString();
|
||||
}
|
||||
|
||||
QString BaseInstance::getManagedPackVersionID() const
|
||||
QString BaseInstance::getManagedPackVersionID()
|
||||
{
|
||||
return m_settings->get("ManagedPackVersionID").toString();
|
||||
return settings()->get("ManagedPackVersionID").toString();
|
||||
}
|
||||
|
||||
QString BaseInstance::getManagedPackVersionName() const
|
||||
QString BaseInstance::getManagedPackVersionName()
|
||||
{
|
||||
return m_settings->get("ManagedPackVersionName").toString();
|
||||
return settings()->get("ManagedPackVersionName").toString();
|
||||
}
|
||||
|
||||
void BaseInstance::setManagedPack(const QString& type, 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());
|
||||
settings()->set("ManagedPack", true);
|
||||
settings()->set("ManagedPackType", type);
|
||||
settings()->set("ManagedPackID", id);
|
||||
settings()->set("ManagedPackName", name);
|
||||
settings()->set("ManagedPackVersionID", versionId);
|
||||
settings()->set("ManagedPackVersionName", version);
|
||||
}
|
||||
|
||||
int BaseInstance::getConsoleMaxLines() const
|
||||
{
|
||||
auto lineSetting = m_settings->getSetting("ConsoleMaxLines");
|
||||
auto lineSetting = settings()->getSetting("ConsoleMaxLines");
|
||||
bool conversionOk = false;
|
||||
int maxLines = lineSetting->get().toInt(&conversionOk);
|
||||
if(!conversionOk)
|
||||
@ -179,7 +162,7 @@ int BaseInstance::getConsoleMaxLines() const
|
||||
|
||||
bool BaseInstance::shouldStopOnConsoleOverflow() const
|
||||
{
|
||||
return m_settings->get("ConsoleOverflowStop").toBool();
|
||||
return settings()->get("ConsoleOverflowStop").toBool();
|
||||
}
|
||||
|
||||
void BaseInstance::iconUpdated(QString key)
|
||||
@ -254,7 +237,7 @@ void BaseInstance::setRunning(bool running)
|
||||
|
||||
int64_t BaseInstance::totalTimePlayed() const
|
||||
{
|
||||
qint64 current = m_settings->get("totalTimePlayed").toLongLong();
|
||||
qint64 current = settings()->get("totalTimePlayed").toLongLong();
|
||||
if(m_isRunning)
|
||||
{
|
||||
QDateTime timeNow = QDateTime::currentDateTime();
|
||||
@ -270,7 +253,7 @@ int64_t BaseInstance::lastTimePlayed() const
|
||||
QDateTime timeNow = QDateTime::currentDateTime();
|
||||
return m_timeStarted.secsTo(timeNow);
|
||||
}
|
||||
return m_settings->get("lastTimePlayed").toLongLong();
|
||||
return settings()->get("lastTimePlayed").toLongLong();
|
||||
}
|
||||
|
||||
void BaseInstance::resetTimePlayed()
|
||||
@ -289,10 +272,8 @@ QString BaseInstance::instanceRoot() const
|
||||
return m_rootDir;
|
||||
}
|
||||
|
||||
SettingsObjectPtr BaseInstance::settings()
|
||||
SettingsObjectPtr BaseInstance::settings() const
|
||||
{
|
||||
loadSpecificSettings();
|
||||
|
||||
return m_settings;
|
||||
}
|
||||
|
||||
@ -355,11 +336,11 @@ QString BaseInstance::name() const
|
||||
|
||||
QString BaseInstance::windowTitle() const
|
||||
{
|
||||
return BuildConfig.LAUNCHER_DISPLAYNAME + ": " + name().replace(QRegularExpression("\\s+"), " ");
|
||||
return BuildConfig.LAUNCHER_NAME + ": " + name().replace(QRegularExpression("\\s+"), " ");
|
||||
}
|
||||
|
||||
// FIXME: why is this here? move it to MinecraftInstance!!!
|
||||
QStringList BaseInstance::extraArguments()
|
||||
QStringList BaseInstance::extraArguments() const
|
||||
{
|
||||
return Commandline::splitArgs(settings()->get("JvmArgs").toString());
|
||||
}
|
||||
@ -368,8 +349,3 @@ shared_qobject_ptr<LaunchTask> BaseInstance::getLaunchTask()
|
||||
{
|
||||
return m_launchProcess;
|
||||
}
|
||||
|
||||
void BaseInstance::updateRuntimeContext()
|
||||
{
|
||||
// NOOP
|
||||
}
|
||||
|
@ -54,7 +54,6 @@
|
||||
#include "net/Mode.h"
|
||||
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
#include "RuntimeContext.h"
|
||||
|
||||
class QDir;
|
||||
class Task;
|
||||
@ -141,14 +140,13 @@ public:
|
||||
QString getPostExitCommand();
|
||||
QString getWrapperCommand();
|
||||
|
||||
bool isManagedPack() const;
|
||||
QString getManagedPackType() const;
|
||||
QString getManagedPackID() const;
|
||||
QString getManagedPackName() const;
|
||||
QString getManagedPackVersionID() const;
|
||||
QString getManagedPackVersionName() const;
|
||||
bool isManagedPack();
|
||||
QString getManagedPackType();
|
||||
QString getManagedPackID();
|
||||
QString getManagedPackName();
|
||||
QString getManagedPackVersionID();
|
||||
QString getManagedPackVersionName();
|
||||
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
|
||||
virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level)
|
||||
@ -156,7 +154,7 @@ public:
|
||||
return level;
|
||||
};
|
||||
|
||||
virtual QStringList extraArguments();
|
||||
virtual QStringList extraArguments() const;
|
||||
|
||||
/// Traits. Normally inside the version, depends on instance implementation.
|
||||
virtual QSet <QString> traits() const = 0;
|
||||
@ -172,18 +170,9 @@ public:
|
||||
/*!
|
||||
* \brief Gets this instance's settings object.
|
||||
* 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.
|
||||
*/
|
||||
virtual SettingsObjectPtr settings();
|
||||
|
||||
/*!
|
||||
* \brief Loads settings specific to an instance type if they're not already loaded.
|
||||
*/
|
||||
virtual void loadSpecificSettings() = 0;
|
||||
virtual SettingsObjectPtr settings() const;
|
||||
|
||||
/// returns a valid update task
|
||||
virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0;
|
||||
@ -217,16 +206,10 @@ public:
|
||||
virtual QString instanceConfigFolder() const = 0;
|
||||
|
||||
/// get variables this instance exports
|
||||
virtual QMap<QString, QString> getVariables() = 0;
|
||||
virtual QMap<QString, QString> getVariables() const = 0;
|
||||
|
||||
virtual QString typeName() const = 0;
|
||||
|
||||
void updateRuntimeContext();
|
||||
RuntimeContext runtimeContext() const
|
||||
{
|
||||
return m_runtimeContext;
|
||||
}
|
||||
|
||||
bool hasVersionBroken() const
|
||||
{
|
||||
return m_hasBrokenVersion;
|
||||
@ -285,11 +268,6 @@ public:
|
||||
protected:
|
||||
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:
|
||||
/*!
|
||||
* \brief Signal emitted when properties relevant to the instance view change
|
||||
@ -312,17 +290,12 @@ protected: /* data */
|
||||
bool m_isRunning = false;
|
||||
shared_qobject_ptr<LaunchTask> m_launchProcess;
|
||||
QDateTime m_timeStarted;
|
||||
RuntimeContext m_runtimeContext;
|
||||
|
||||
private: /* data */
|
||||
Status m_status = Status::Present;
|
||||
bool m_crashed = false;
|
||||
bool m_hasUpdate = false;
|
||||
bool m_hasBrokenVersion = false;
|
||||
|
||||
SettingsObjectWeakPtr m_global_settings;
|
||||
bool m_specific_settings_loaded = false;
|
||||
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>)
|
||||
|
@ -25,7 +25,6 @@
|
||||
class BaseVersion
|
||||
{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<BaseVersion>;
|
||||
virtual ~BaseVersion() {}
|
||||
/*!
|
||||
* A string used to identify this version in config files.
|
||||
@ -55,4 +54,6 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(BaseVersion::Ptr)
|
||||
typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
|
||||
|
||||
Q_DECLARE_METATYPE(BaseVersionPtr)
|
||||
|
@ -40,20 +40,20 @@ BaseVersionList::BaseVersionList(QObject *parent) : QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
BaseVersion::Ptr BaseVersionList::findVersion(const QString &descriptor)
|
||||
BaseVersionPtr BaseVersionList::findVersion(const QString &descriptor)
|
||||
{
|
||||
for (int i = 0; i < count(); i++)
|
||||
{
|
||||
if (at(i)->descriptor() == descriptor)
|
||||
return at(i);
|
||||
}
|
||||
return nullptr;
|
||||
return BaseVersionPtr();
|
||||
}
|
||||
|
||||
BaseVersion::Ptr BaseVersionList::getRecommended() const
|
||||
BaseVersionPtr BaseVersionList::getRecommended() const
|
||||
{
|
||||
if (count() <= 0)
|
||||
return nullptr;
|
||||
return BaseVersionPtr();
|
||||
else
|
||||
return at(0);
|
||||
}
|
||||
@ -66,7 +66,7 @@ QVariant BaseVersionList::data(const QModelIndex &index, int role) const
|
||||
if (index.row() > count())
|
||||
return QVariant();
|
||||
|
||||
BaseVersion::Ptr version = at(index.row());
|
||||
BaseVersionPtr version = at(index.row());
|
||||
|
||||
switch (role)
|
||||
{
|
||||
|
@ -70,7 +70,7 @@ public:
|
||||
virtual bool isLoaded() = 0;
|
||||
|
||||
//! Gets the version at the given index.
|
||||
virtual const BaseVersion::Ptr at(int i) const = 0;
|
||||
virtual const BaseVersionPtr at(int i) const = 0;
|
||||
|
||||
//! Returns the number of versions in the list.
|
||||
virtual int count() const = 0;
|
||||
@ -90,13 +90,13 @@ public:
|
||||
* \return A const pointer to the version with the given descriptor. NULL if
|
||||
* one doesn't exist.
|
||||
*/
|
||||
virtual BaseVersion::Ptr findVersion(const QString &descriptor);
|
||||
virtual BaseVersionPtr findVersion(const QString &descriptor);
|
||||
|
||||
/*!
|
||||
* \brief Gets the recommended version from this list
|
||||
* If the list doesn't support recommended versions, this works exactly as getLatestStable
|
||||
*/
|
||||
virtual BaseVersion::Ptr getRecommended() const;
|
||||
virtual BaseVersionPtr getRecommended() const;
|
||||
|
||||
/*!
|
||||
* Sorts the version list.
|
||||
@ -117,5 +117,5 @@ slots:
|
||||
* then copies the versions and sets their parents correctly.
|
||||
* \param versions List of versions whose parents should be set.
|
||||
*/
|
||||
virtual void updateListData(QList<BaseVersion::Ptr> versions) = 0;
|
||||
virtual void updateListData(QList<BaseVersionPtr> versions) = 0;
|
||||
};
|
||||
|
@ -24,9 +24,8 @@ set(CORE_SOURCES
|
||||
NullInstance.h
|
||||
MMCZip.h
|
||||
MMCZip.cpp
|
||||
StringUtils.h
|
||||
StringUtils.cpp
|
||||
RuntimeContext.h
|
||||
MMCStrings.h
|
||||
MMCStrings.cpp
|
||||
|
||||
# Basic instance manipulation tasks (derived from InstanceTask)
|
||||
InstanceCreationTask.h
|
||||
@ -89,6 +88,12 @@ set(CORE_SOURCES
|
||||
MMCTime.cpp
|
||||
)
|
||||
|
||||
ecm_add_test(FileSystem_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME FileSystem) # TODO: needs testdata
|
||||
|
||||
ecm_add_test(GZip_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME GZip)
|
||||
|
||||
set(PATHMATCHER_SOURCES
|
||||
# Path matchers
|
||||
pathmatcher/FSTreeMatcher.h
|
||||
@ -289,6 +294,8 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/Rule.h
|
||||
minecraft/OneSixVersionFormat.cpp
|
||||
minecraft/OneSixVersionFormat.h
|
||||
minecraft/OpSys.cpp
|
||||
minecraft/OpSys.h
|
||||
minecraft/ParseUtils.cpp
|
||||
minecraft/ParseUtils.h
|
||||
minecraft/ProfileUtils.cpp
|
||||
@ -296,8 +303,6 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/Library.cpp
|
||||
minecraft/Library.h
|
||||
minecraft/MojangDownloadInfo.h
|
||||
minecraft/VanillaInstanceCreationTask.cpp
|
||||
minecraft/VanillaInstanceCreationTask.h
|
||||
minecraft/VersionFile.cpp
|
||||
minecraft/VersionFile.h
|
||||
minecraft/VersionFilterData.h
|
||||
@ -313,30 +318,16 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/mod/ModDetails.h
|
||||
minecraft/mod/ModFolderModel.h
|
||||
minecraft/mod/ModFolderModel.cpp
|
||||
minecraft/mod/Resource.h
|
||||
minecraft/mod/Resource.cpp
|
||||
minecraft/mod/ResourceFolderModel.h
|
||||
minecraft/mod/ResourceFolderModel.cpp
|
||||
minecraft/mod/ResourcePack.h
|
||||
minecraft/mod/ResourcePack.cpp
|
||||
minecraft/mod/ResourcePackFolderModel.h
|
||||
minecraft/mod/ResourcePackFolderModel.cpp
|
||||
minecraft/mod/TexturePack.h
|
||||
minecraft/mod/TexturePack.cpp
|
||||
minecraft/mod/TexturePackFolderModel.h
|
||||
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
|
||||
minecraft/AssetsUtils.h
|
||||
@ -354,6 +345,42 @@ set(MINECRAFT_SOURCES
|
||||
mojang/PackageManifest.cpp
|
||||
minecraft/Agent.h)
|
||||
|
||||
ecm_add_test(minecraft/GradleSpecifier_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME GradleSpecifier)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_executable(PackageManifest
|
||||
mojang/PackageManifest_test.cpp
|
||||
)
|
||||
target_link_libraries(PackageManifest
|
||||
Launcher_logic
|
||||
Qt${QT_VERSION_MAJOR}::Test
|
||||
)
|
||||
target_include_directories(PackageManifest
|
||||
PRIVATE ../cmake/UnitTest/
|
||||
)
|
||||
add_test(
|
||||
NAME PackageManifest
|
||||
COMMAND PackageManifest
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
endif()
|
||||
|
||||
# TODO: needs minecraft/testdata
|
||||
ecm_add_test(minecraft/MojangVersionFormat_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME MojangVersionFormat)
|
||||
|
||||
ecm_add_test(minecraft/Library_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME Library)
|
||||
|
||||
# FIXME: shares data with FileSystem test
|
||||
# TODO: needs testdata
|
||||
ecm_add_test(minecraft/mod/ModFolderModel_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME ModFolderModel)
|
||||
|
||||
ecm_add_test(minecraft/ParseUtils_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME ParseUtils)
|
||||
|
||||
# the screenshots feature
|
||||
set(SCREENSHOTS_SOURCES
|
||||
screenshots/Screenshot.h
|
||||
@ -375,6 +402,9 @@ set(TASKS_SOURCES
|
||||
tasks/MultipleOptionsTask.cpp
|
||||
)
|
||||
|
||||
ecm_add_test(tasks/Task_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME Task)
|
||||
|
||||
set(SETTINGS_SOURCES
|
||||
# Settings
|
||||
settings/INIFile.cpp
|
||||
@ -391,6 +421,9 @@ set(SETTINGS_SOURCES
|
||||
settings/SettingsObject.h
|
||||
)
|
||||
|
||||
ecm_add_test(settings/INIFile_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME INIFile)
|
||||
|
||||
set(JAVA_SOURCES
|
||||
java/JavaChecker.h
|
||||
java/JavaChecker.cpp
|
||||
@ -406,6 +439,9 @@ set(JAVA_SOURCES
|
||||
java/JavaVersion.cpp
|
||||
)
|
||||
|
||||
ecm_add_test(java/JavaVersion_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME JavaVersion)
|
||||
|
||||
set(TRANSLATIONS_SOURCES
|
||||
translations/TranslationsModel.h
|
||||
translations/TranslationsModel.cpp
|
||||
@ -460,8 +496,6 @@ set(API_SOURCES
|
||||
modplatform/helpers/NetworkModAPI.cpp
|
||||
modplatform/helpers/HashUtils.h
|
||||
modplatform/helpers/HashUtils.cpp
|
||||
modplatform/helpers/OverrideUtils.h
|
||||
modplatform/helpers/OverrideUtils.cpp
|
||||
)
|
||||
|
||||
set(FTB_SOURCES
|
||||
@ -487,8 +521,6 @@ set(FLAME_SOURCES
|
||||
modplatform/flame/FileResolvingTask.cpp
|
||||
modplatform/flame/FlameCheckUpdate.cpp
|
||||
modplatform/flame/FlameCheckUpdate.h
|
||||
modplatform/flame/FlameInstanceCreationTask.h
|
||||
modplatform/flame/FlameInstanceCreationTask.cpp
|
||||
)
|
||||
|
||||
set(MODRINTH_SOURCES
|
||||
@ -498,8 +530,6 @@ set(MODRINTH_SOURCES
|
||||
modplatform/modrinth/ModrinthPackManifest.h
|
||||
modplatform/modrinth/ModrinthCheckUpdate.cpp
|
||||
modplatform/modrinth/ModrinthCheckUpdate.h
|
||||
modplatform/modrinth/ModrinthInstanceCreationTask.cpp
|
||||
modplatform/modrinth/ModrinthInstanceCreationTask.h
|
||||
)
|
||||
|
||||
set(MODPACKSCH_SOURCES
|
||||
@ -514,6 +544,9 @@ set(PACKWIZ_SOURCES
|
||||
modplatform/packwiz/Packwiz.cpp
|
||||
)
|
||||
|
||||
# TODO: needs modplatform/packwiz/testdata
|
||||
ecm_add_test(modplatform/packwiz/Packwiz_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME Packwiz)
|
||||
|
||||
set(TECHNIC_SOURCES
|
||||
modplatform/technic/SingleZipPackInstallTask.h
|
||||
@ -537,6 +570,9 @@ set(ATLAUNCHER_SOURCES
|
||||
modplatform/atlauncher/ATLShareCode.h
|
||||
)
|
||||
|
||||
ecm_add_test(meta/Index_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME Index)
|
||||
|
||||
################################ COMPILE ################################
|
||||
|
||||
# we need zlib
|
||||
@ -730,8 +766,6 @@ SET(LAUNCHER_SOURCES
|
||||
ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h
|
||||
ui/pages/modplatform/atlauncher/AtlPage.cpp
|
||||
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.h
|
||||
@ -817,8 +851,6 @@ SET(LAUNCHER_SOURCES
|
||||
ui/dialogs/ModDownloadDialog.h
|
||||
ui/dialogs/ScrollMessageBox.cpp
|
||||
ui/dialogs/ScrollMessageBox.h
|
||||
ui/dialogs/BlockedModsDialog.cpp
|
||||
ui/dialogs/BlockedModsDialog.h
|
||||
ui/dialogs/ChooseProviderDialog.h
|
||||
ui/dialogs/ChooseProviderDialog.cpp
|
||||
ui/dialogs/ModUpdateDialog.cpp
|
||||
@ -845,8 +877,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/widgets/LineSeparator.h
|
||||
ui/widgets/LogView.cpp
|
||||
ui/widgets/LogView.h
|
||||
ui/widgets/InfoFrame.cpp
|
||||
ui/widgets/InfoFrame.h
|
||||
ui/widgets/MCModInfoFrame.cpp
|
||||
ui/widgets/MCModInfoFrame.h
|
||||
ui/widgets/ModFilterWidget.cpp
|
||||
ui/widgets/ModFilterWidget.h
|
||||
ui/widgets/ModListView.cpp
|
||||
@ -854,12 +886,6 @@ SET(LAUNCHER_SOURCES
|
||||
ui/widgets/PageContainer.cpp
|
||||
ui/widgets/PageContainer.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.h
|
||||
ui/widgets/VersionSelectWidget.cpp
|
||||
@ -883,16 +909,6 @@ SET(LAUNCHER_SOURCES
|
||||
ui/instanceview/VisualGroup.h
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
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/pages/global/AccountListPage.ui
|
||||
@ -924,7 +940,7 @@ qt_wrap_ui(LAUNCHER_UI
|
||||
ui/pages/modplatform/technic/TechnicPage.ui
|
||||
ui/widgets/InstanceCardWidget.ui
|
||||
ui/widgets/CustomCommands.ui
|
||||
ui/widgets/InfoFrame.ui
|
||||
ui/widgets/MCModInfoFrame.ui
|
||||
ui/widgets/ModFilterWidget.ui
|
||||
ui/dialogs/CopyInstanceDialog.ui
|
||||
ui/dialogs/ProfileSetupDialog.ui
|
||||
@ -944,7 +960,6 @@ qt_wrap_ui(LAUNCHER_UI
|
||||
ui/dialogs/EditAccountDialog.ui
|
||||
ui/dialogs/ReviewMessageBox.ui
|
||||
ui/dialogs/ScrollMessageBox.ui
|
||||
ui/dialogs/BlockedModsDialog.ui
|
||||
ui/dialogs/ChooseProviderDialog.ui
|
||||
)
|
||||
|
||||
@ -969,17 +984,16 @@ endif()
|
||||
|
||||
# Add executable
|
||||
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
|
||||
systeminfo
|
||||
Launcher_classparser
|
||||
Launcher_murmur2
|
||||
nbt++
|
||||
${ZLIB_LIBRARIES}
|
||||
tomlplusplus::tomlplusplus
|
||||
tomlc99
|
||||
BuildConfig
|
||||
Katabasis
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
ghcFilesystem::ghc_filesystem
|
||||
)
|
||||
|
||||
if (UNIX AND NOT CYGWIN AND NOT APPLE)
|
||||
|
@ -92,4 +92,412 @@ QStringList splitArgs(QString args)
|
||||
argv << current;
|
||||
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())
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QHash>
|
||||
#include <QStringList>
|
||||
|
||||
/**
|
||||
@ -34,4 +39,212 @@ namespace Commandline
|
||||
* @return a QStringList containing all arguments
|
||||
*/
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ bool openDirectory(const QString &path, bool ensureExists)
|
||||
return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
|
||||
};
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
if(!isFlatpak())
|
||||
if(!APPLICATION->isFlatpak())
|
||||
{
|
||||
return IndirectOpen(f);
|
||||
}
|
||||
@ -140,7 +140,7 @@ bool openFile(const QString &path)
|
||||
return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
};
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
if(!isFlatpak())
|
||||
if(!APPLICATION->isFlatpak())
|
||||
{
|
||||
return IndirectOpen(f);
|
||||
}
|
||||
@ -158,7 +158,7 @@ bool openFile(const QString &application, const QString &path, const QString &wo
|
||||
qDebug() << "Opening file" << path << "using" << application;
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
||||
if(!isFlatpak())
|
||||
if(!APPLICATION->isFlatpak())
|
||||
{
|
||||
return IndirectOpen([&]()
|
||||
{
|
||||
@ -178,7 +178,7 @@ bool run(const QString &application, const QStringList &args, const QString &wor
|
||||
{
|
||||
qDebug() << "Running" << application << "with args" << args.join(' ');
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
if(!isFlatpak())
|
||||
if(!APPLICATION->isFlatpak())
|
||||
{
|
||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
||||
return IndirectOpen([&]()
|
||||
@ -203,7 +203,7 @@ bool openUrl(const QUrl &url)
|
||||
return QDesktopServices::openUrl(url);
|
||||
};
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
if(!isFlatpak())
|
||||
if(!APPLICATION->isFlatpak())
|
||||
{
|
||||
return IndirectOpen(f);
|
||||
}
|
||||
@ -216,13 +216,4 @@ bool openUrl(const QUrl &url)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isFlatpak()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
return QFile::exists("/.flatpak-info");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -33,6 +33,4 @@ namespace DesktopServices
|
||||
* Open the URL, most likely in a browser. Maybe.
|
||||
*/
|
||||
bool openUrl(const QUrl &url);
|
||||
|
||||
bool isFlatpak();
|
||||
}
|
||||
|
@ -35,87 +35,76 @@
|
||||
|
||||
#include "FileSystem.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QSaveFile>
|
||||
#include <QFileInfo>
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
#include <QStandardPaths>
|
||||
#include <QTextStream>
|
||||
#include <QUrl>
|
||||
|
||||
#include "DesktopServices.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
#include <objbase.h>
|
||||
#include <objidl.h>
|
||||
#include <shlguid.h>
|
||||
#include <shlobj.h>
|
||||
#include <shobjidl.h>
|
||||
#include <sys/utime.h>
|
||||
#include <windows.h>
|
||||
#include <winnls.h>
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include <sys/utime.h>
|
||||
#include <winnls.h>
|
||||
#include <shobjidl.h>
|
||||
#include <objbase.h>
|
||||
#include <objidl.h>
|
||||
#include <shlguid.h>
|
||||
#include <shlobj.h>
|
||||
#else
|
||||
#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;
|
||||
#include <utime.h>
|
||||
#endif
|
||||
|
||||
namespace FS {
|
||||
|
||||
void ensureExists(const QDir& dir)
|
||||
void ensureExists(const QDir &dir)
|
||||
{
|
||||
if (!QDir().mkpath(dir.absolutePath())) {
|
||||
throw FileSystemException("Unable to create folder " + dir.dirName() + " (" + dir.absolutePath() + ")");
|
||||
if (!QDir().mkpath(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());
|
||||
QSaveFile file(filename);
|
||||
if (!file.open(QSaveFile::WriteOnly)) {
|
||||
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
|
||||
if (!file.open(QSaveFile::WriteOnly))
|
||||
{
|
||||
throw FileSystemException("Couldn't open " + filename + " for writing: " +
|
||||
file.errorString());
|
||||
}
|
||||
if (data.size() != file.write(data)) {
|
||||
throw FileSystemException("Error writing data to " + filename + ": " + file.errorString());
|
||||
if (data.size() != file.write(data))
|
||||
{
|
||||
throw FileSystemException("Error writing data to " + filename + ": " +
|
||||
file.errorString());
|
||||
}
|
||||
if (!file.commit()) {
|
||||
throw FileSystemException("Error while committing data to " + filename + ": " + file.errorString());
|
||||
if (!file.commit())
|
||||
{
|
||||
throw FileSystemException("Error while committing data to " + filename + ": " +
|
||||
file.errorString());
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray read(const QString& filename)
|
||||
QByteArray read(const QString &filename)
|
||||
{
|
||||
QFile file(filename);
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
throw FileSystemException("Unable to open " + filename + " for reading: " + file.errorString());
|
||||
if (!file.open(QFile::ReadOnly))
|
||||
{
|
||||
throw FileSystemException("Unable to open " + filename + " for reading: " +
|
||||
file.errorString());
|
||||
}
|
||||
const qint64 size = file.size();
|
||||
QByteArray data(int(size), 0);
|
||||
const qint64 ret = file.read(data.data(), size);
|
||||
if (ret == -1 || ret != size) {
|
||||
throw FileSystemException("Error reading data from " + filename + ": " + file.errorString());
|
||||
if (ret == -1 || ret != size)
|
||||
{
|
||||
throw FileSystemException("Error reading data from " + filename + ": " +
|
||||
file.errorString());
|
||||
}
|
||||
return data;
|
||||
}
|
||||
@ -149,102 +138,149 @@ bool ensureFolderPathExists(QString foldernamepath)
|
||||
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;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
auto src = PathCombine(m_src.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;
|
||||
|
||||
// The default behavior is to follow symlinks
|
||||
if (!m_followSymlinks)
|
||||
opt |= copy_opts::copy_symlinks;
|
||||
|
||||
// Function that'll do the actual copying
|
||||
auto copy_file = [&](QString src_path, QString relative_dst_path) {
|
||||
if (m_blacklist && m_blacklist->matches(relative_dst_path))
|
||||
return;
|
||||
|
||||
auto dst_path = PathCombine(dst, relative_dst_path);
|
||||
ensureFilePathExists(dst_path);
|
||||
|
||||
fs::copy(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), opt, err);
|
||||
if (err) {
|
||||
qWarning() << "Failed to copy files:" << QString::fromStdString(err.message());
|
||||
qDebug() << "Source file:" << src_path;
|
||||
qDebug() << "Destination file:" << dst_path;
|
||||
if(!m_followSymlinks && currentSrc.isSymLink())
|
||||
{
|
||||
qDebug() << "creating symlink" << src << " - " << dst;
|
||||
if (!ensureFilePathExists(dst))
|
||||
{
|
||||
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
|
||||
// match, we copy the file.
|
||||
QDir src_dir(src);
|
||||
QDirIterator source_it(src, QDir::Filter::Files | QDir::Filter::Hidden, QDirIterator::Subdirectories);
|
||||
|
||||
while (source_it.hasNext()) {
|
||||
auto src_path = source_it.next();
|
||||
auto relative_path = src_dir.relativeFilePath(src_path);
|
||||
|
||||
copy_file(src_path, relative_path);
|
||||
return QFile::link(currentSrc.symLinkTarget(), dst);
|
||||
}
|
||||
|
||||
// If the root src is not a directory, the previous iterator won't run.
|
||||
if (!fs::is_directory(StringUtils::toStdString(src)))
|
||||
copy_file(src, "");
|
||||
|
||||
return err.value() == 0;
|
||||
else if(currentSrc.isFile())
|
||||
{
|
||||
qDebug() << "copying file" << src << " - " << dst;
|
||||
if (!ensureFilePathExists(dst))
|
||||
{
|
||||
qWarning() << "Cannot create path!";
|
||||
return false;
|
||||
}
|
||||
return QFile::copy(src, dst);
|
||||
}
|
||||
else if(currentSrc.isDir())
|
||||
{
|
||||
qDebug() << "recursing" << offset;
|
||||
if (!ensureFolderPathExists(dst))
|
||||
{
|
||||
qWarning() << "Cannot create path!";
|
||||
return false;
|
||||
}
|
||||
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
|
||||
{
|
||||
qCritical() << "Copy ERROR: Unknown filesystem object:" << src;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool deletePath(QString path)
|
||||
{
|
||||
std::error_code err;
|
||||
|
||||
fs::remove_all(StringUtils::toStdString(path), err);
|
||||
|
||||
if (err) {
|
||||
qWarning() << "Failed to remove files:" << QString::fromStdString(err.message());
|
||||
bool OK = true;
|
||||
QFileInfo finfo(path);
|
||||
if(finfo.isFile()) {
|
||||
return QFile::remove(path);
|
||||
}
|
||||
|
||||
return err.value() == 0;
|
||||
}
|
||||
QDir dir(path);
|
||||
|
||||
bool trash(QString path, QString *pathInTrash = nullptr)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||
return false;
|
||||
if (!dir.exists())
|
||||
{
|
||||
return OK;
|
||||
}
|
||||
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
|
||||
// FIXME: Figure out trash in Flatpak. Qt seemingly doesn't use the Trash portal
|
||||
if (DesktopServices::isFlatpak())
|
||||
return false;
|
||||
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
|
||||
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;
|
||||
if (!path2.size())
|
||||
if(!path2.size())
|
||||
return path1;
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@ -256,14 +292,17 @@ QString AbsolutePath(QString path)
|
||||
|
||||
QString ResolveExecutable(QString path)
|
||||
{
|
||||
if (path.isEmpty()) {
|
||||
if (path.isEmpty())
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
if (!path.contains('/')) {
|
||||
if(!path.contains('/'))
|
||||
{
|
||||
path = QStandardPaths::findExecutable(path);
|
||||
}
|
||||
QFileInfo pathInfo(path);
|
||||
if (!pathInfo.exists() || !pathInfo.isExecutable()) {
|
||||
if(!pathInfo.exists() || !pathInfo.isExecutable())
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
return pathInfo.absoluteFilePath();
|
||||
@ -283,9 +322,12 @@ QString NormalizePath(QString path)
|
||||
QDir b(path);
|
||||
QString newAbsolute = b.absolutePath();
|
||||
|
||||
if (newAbsolute.startsWith(currentAbsolute)) {
|
||||
if (newAbsolute.startsWith(currentAbsolute))
|
||||
{
|
||||
return a.relativeFilePath(newAbsolute);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return newAbsolute;
|
||||
}
|
||||
}
|
||||
@ -294,8 +336,10 @@ QString badFilenameChars = "\"\\/?<>:;*|!+\r\n";
|
||||
|
||||
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
|
||||
{
|
||||
for (int i = 0; i < string.length(); i++) {
|
||||
if (badFilenameChars.contains(string[i])) {
|
||||
for (int i = 0; i < string.length(); i++)
|
||||
{
|
||||
if (badFilenameChars.contains(string[i]))
|
||||
{
|
||||
string[i] = replaceWith;
|
||||
}
|
||||
}
|
||||
@ -307,11 +351,15 @@ QString DirNameFromString(QString string, QString inDir)
|
||||
int num = 0;
|
||||
QString baseName = RemoveInvalidFilenameChars(string, '-');
|
||||
QString dirName;
|
||||
do {
|
||||
if (num == 0) {
|
||||
do
|
||||
{
|
||||
if(num == 0)
|
||||
{
|
||||
dirName = baseName;
|
||||
} else {
|
||||
dirName = baseName + "(" + QString::number(num) + ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
dirName = baseName + QString::number(num);;
|
||||
}
|
||||
|
||||
// If it's over 9000
|
||||
@ -330,13 +378,63 @@ bool checkProblemticPathJava(QDir folder)
|
||||
return pathfoldername.contains("!", Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
// Win32 crap
|
||||
#ifdef 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;
|
||||
}
|
||||
}
|
||||
|
||||
IShellLinkA *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()
|
||||
{
|
||||
return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
}
|
||||
|
||||
// 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)
|
||||
location = PathCombine(location, name + ".desktop");
|
||||
@ -361,7 +459,8 @@ bool createShortCut(QString location, QString dest, QStringList args, QString na
|
||||
stream.flush();
|
||||
f.close();
|
||||
|
||||
f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther);
|
||||
f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup |
|
||||
QFileDevice::ExeOther);
|
||||
|
||||
return true;
|
||||
#elif defined Q_OS_WIN
|
||||
@ -389,25 +488,47 @@ bool createShortCut(QString location, QString dest, QStringList args, QString na
|
||||
#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)
|
||||
{
|
||||
using copy_opts = fs::copy_options;
|
||||
|
||||
if (!FS::ensureFolderPathExists(overwritten_path))
|
||||
return false;
|
||||
|
||||
std::error_code err;
|
||||
fs::copy_options opt = copy_opts::recursive | copy_opts::overwrite_existing;
|
||||
QStringList paths_to_override;
|
||||
QDir root_override (override_path);
|
||||
for (auto file : listFolderPaths(root_override)) {
|
||||
QString destination = file;
|
||||
destination.replace(override_path, overwritten_path);
|
||||
ensureFilePathExists(destination);
|
||||
|
||||
// FIXME: hello traveller! Apparently std::copy does NOT overwrite existing files on GNU libstdc++ on Windows?
|
||||
fs::copy(StringUtils::toStdString(override_path), StringUtils::toStdString(overwritten_path), opt, err);
|
||||
qDebug() << QString("Applying override %1 in %2").arg(file, destination);
|
||||
|
||||
if (err) {
|
||||
qCritical() << QString("Failed to apply override from %1 to %2").arg(override_path, overwritten_path);
|
||||
qCritical() << "Reason:" << QString::fromStdString(err.message());
|
||||
if (QFile::exists(destination))
|
||||
QFile::remove(destination);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -41,27 +41,29 @@
|
||||
#include <QDir>
|
||||
#include <QFlags>
|
||||
|
||||
namespace FS {
|
||||
namespace FS
|
||||
{
|
||||
|
||||
class FileSystemException : public ::Exception {
|
||||
public:
|
||||
FileSystemException(const QString& message) : Exception(message) {}
|
||||
class FileSystemException : public ::Exception
|
||||
{
|
||||
public:
|
||||
FileSystemException(const QString &message) : Exception(message) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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\
|
||||
*/
|
||||
QByteArray read(const QString& filename);
|
||||
QByteArray read(const QString &filename);
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -75,31 +77,35 @@ bool ensureFilePathExists(QString filenamepath);
|
||||
*/
|
||||
bool ensureFolderPathExists(QString filenamepath);
|
||||
|
||||
class copy {
|
||||
public:
|
||||
copy(const QString& src, const QString& dst)
|
||||
class copy
|
||||
{
|
||||
public:
|
||||
copy(const QString & src, const QString & dst)
|
||||
{
|
||||
m_src.setPath(src);
|
||||
m_dst.setPath(dst);
|
||||
}
|
||||
copy& followSymlinks(const bool follow)
|
||||
copy & followSymlinks(const bool follow)
|
||||
{
|
||||
m_followSymlinks = follow;
|
||||
return *this;
|
||||
}
|
||||
copy& blacklist(const IPathMatcher* filter)
|
||||
copy & blacklist(const IPathMatcher * filter)
|
||||
{
|
||||
m_blacklist = filter;
|
||||
return *this;
|
||||
}
|
||||
bool operator()() { return operator()(QString()); }
|
||||
bool operator()()
|
||||
{
|
||||
return operator()(QString());
|
||||
}
|
||||
|
||||
private:
|
||||
bool operator()(const QString& offset);
|
||||
private:
|
||||
bool operator()(const QString &offset);
|
||||
|
||||
private:
|
||||
private:
|
||||
bool m_followSymlinks = true;
|
||||
const IPathMatcher* m_blacklist = nullptr;
|
||||
const IPathMatcher * m_blacklist = nullptr;
|
||||
QDir m_src;
|
||||
QDir m_dst;
|
||||
};
|
||||
@ -109,14 +115,9 @@ class copy {
|
||||
*/
|
||||
bool deletePath(QString path);
|
||||
|
||||
/**
|
||||
* Trash a folder / file
|
||||
*/
|
||||
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 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);
|
||||
|
||||
|
@ -2,9 +2,7 @@
|
||||
#include <QTemporaryDir>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include <FileSystem.h>
|
||||
|
||||
#include <pathmatcher/RegexpMatcher.h>
|
||||
#include "FileSystem.h"
|
||||
|
||||
class FileSystemTest : public QObject
|
||||
{
|
||||
@ -82,7 +80,7 @@ slots:
|
||||
|
||||
void test_copy()
|
||||
{
|
||||
QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder");
|
||||
QString folder = QFINDTESTDATA("testdata/test_folder");
|
||||
auto f = [&folder]()
|
||||
{
|
||||
QTemporaryDir tempDir;
|
||||
@ -113,102 +111,6 @@ slots:
|
||||
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_copy_single_file()
|
||||
{
|
||||
QTemporaryDir tempDir;
|
||||
tempDir.setAutoRemove(true);
|
||||
|
||||
{
|
||||
QString file = QFINDTESTDATA("testdata/FileSystem/test_folder/pack.mcmeta");
|
||||
|
||||
qDebug() << "From:" << file << "To:" << tempDir.path();
|
||||
|
||||
QDir target_dir(FS::PathCombine(tempDir.path(), "pack.mcmeta"));
|
||||
qDebug() << tempDir.path();
|
||||
qDebug() << target_dir.path();
|
||||
FS::copy c(file, target_dir.filePath("pack.mcmeta"));
|
||||
c();
|
||||
|
||||
auto filter = QDir::Filter::Files;
|
||||
|
||||
for (auto entry: target_dir.entryList(filter)) {
|
||||
qDebug() << entry;
|
||||
}
|
||||
|
||||
QVERIFY(target_dir.entryList(filter).contains("pack.mcmeta"));
|
||||
}
|
||||
}
|
||||
|
||||
void test_getDesktop()
|
||||
{
|
||||
QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
|
@ -72,7 +72,7 @@ bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedByte
|
||||
uncompLength *= 2;
|
||||
}
|
||||
|
||||
strm.next_out = reinterpret_cast<Bytef *>((uncompressedBytes.data() + strm.total_out));
|
||||
strm.next_out = (Bytef *)(uncompressedBytes.data() + strm.total_out);
|
||||
strm.avail_out = uncompLength - strm.total_out;
|
||||
|
||||
// Inflate another chunk.
|
||||
@ -129,7 +129,7 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
|
||||
{
|
||||
compressedBytes.resize(compressedBytes.size() * 2);
|
||||
}
|
||||
zs.next_out = reinterpret_cast<Bytef*>((compressedBytes.data() + offset));
|
||||
zs.next_out = (Bytef *) (compressedBytes.data() + offset);
|
||||
temp = zs.avail_out = compressedBytes.size() - offset;
|
||||
ret = deflate(&zs, Z_FINISH);
|
||||
offset += temp - zs.avail_out;
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include <QTest>
|
||||
|
||||
#include <GZip.h>
|
||||
#include "GZip.h"
|
||||
#include <random>
|
||||
|
||||
void fib(int &prev, int &cur)
|
@ -42,7 +42,7 @@ public:
|
||||
}
|
||||
void put(QByteArray input)
|
||||
{
|
||||
hoedown_buffer_put(buf, reinterpret_cast<uint8_t *>(input.data()), input.size());
|
||||
hoedown_buffer_put(buf, (uint8_t *) input.data(), input.size());
|
||||
}
|
||||
const uint8_t * data() const
|
||||
{
|
||||
|
@ -44,7 +44,7 @@ void InstanceCopyTask::copyFinished()
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
||||
|
||||
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
|
||||
inst->setName(name());
|
||||
inst->setName(m_instName);
|
||||
inst->setIconKey(m_instIcon);
|
||||
if(!m_keepPlaytime) {
|
||||
inst->resetTimePlayed();
|
||||
|
@ -1,56 +1,40 @@
|
||||
#include "InstanceCreationTask.h"
|
||||
#include "settings/INISettingsObject.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
//FIXME: remove this
|
||||
#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()
|
||||
{
|
||||
setAbortable(true);
|
||||
|
||||
if (updateInstance()) {
|
||||
emitSucceeded();
|
||||
return;
|
||||
setStatus(tr("Creating instance from version %1").arg(m_version->name()));
|
||||
{
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
||||
instanceSettings->suspendSave();
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
@ -1,46 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "tasks/Task.h"
|
||||
#include "net/NetJob.h"
|
||||
#include <QUrl>
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "BaseVersion.h"
|
||||
#include "InstanceTask.h"
|
||||
|
||||
class InstanceCreationTask : public InstanceTask {
|
||||
class InstanceCreationTask : public InstanceTask
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
InstanceCreationTask();
|
||||
virtual ~InstanceCreationTask() = default;
|
||||
public:
|
||||
explicit InstanceCreationTask(BaseVersionPtr version);
|
||||
explicit InstanceCreationTask(BaseVersionPtr version, QString loader, BaseVersionPtr loaderVersion);
|
||||
|
||||
protected:
|
||||
void executeTask() final override;
|
||||
protected:
|
||||
//! Entry point for tasks.
|
||||
virtual void executeTask() override;
|
||||
|
||||
/**
|
||||
* Tries to update an already existing instance.
|
||||
*
|
||||
* This can be implemented by subclasses to provide a way of updating an already existing
|
||||
* instance, according to that implementation's concept of 'identity' (i.e. instances that
|
||||
* 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;
|
||||
private: /* data */
|
||||
BaseVersionPtr m_version;
|
||||
bool m_usingLoader;
|
||||
QString m_loader;
|
||||
BaseVersionPtr m_loaderVersion;
|
||||
};
|
||||
|
@ -35,25 +35,34 @@
|
||||
*/
|
||||
|
||||
#include "InstanceImportTask.h"
|
||||
|
||||
#include <QtConcurrentRun>
|
||||
#include "Application.h"
|
||||
#include "BaseInstance.h"
|
||||
#include "FileSystem.h"
|
||||
#include "MMCZip.h"
|
||||
#include "NullInstance.h"
|
||||
|
||||
#include "icons/IconList.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 <QtConcurrentRun>
|
||||
#include <algorithm>
|
||||
|
||||
// FIXME: this does not belong here, it's Minecraft/Flame specific
|
||||
#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)
|
||||
{
|
||||
@ -63,41 +72,35 @@ InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent)
|
||||
|
||||
bool InstanceImportTask::abort()
|
||||
{
|
||||
if (!canAbort())
|
||||
return false;
|
||||
|
||||
if (m_filesNetJob)
|
||||
m_filesNetJob->abort();
|
||||
m_extractFuture.cancel();
|
||||
|
||||
return Task::abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
void InstanceImportTask::executeTask()
|
||||
{
|
||||
setAbortable(true);
|
||||
|
||||
if (m_sourceUrl.isLocalFile()) {
|
||||
if (m_sourceUrl.isLocalFile())
|
||||
{
|
||||
m_archivePath = m_sourceUrl.toLocalFile();
|
||||
processZipPack();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
||||
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);
|
||||
entry->setStale(true);
|
||||
m_archivePath = entry->getFullPath();
|
||||
|
||||
m_filesNetJob = new NetJob(tr("Modpack download"), APPLICATION->network());
|
||||
m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
|
||||
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
||||
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
||||
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
|
||||
|
||||
m_archivePath = entry->getFullPath();
|
||||
auto job = m_filesNetJob.get();
|
||||
connect(job, &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
||||
connect(job, &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
||||
connect(job, &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
||||
m_filesNetJob->start();
|
||||
}
|
||||
}
|
||||
@ -116,13 +119,7 @@ void InstanceImportTask::downloadFailed(QString reason)
|
||||
|
||||
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
|
||||
{
|
||||
setProgress(current, total);
|
||||
}
|
||||
|
||||
void InstanceImportTask::downloadAborted()
|
||||
{
|
||||
emitAborted();
|
||||
m_filesNetJob.reset();
|
||||
setProgress(current / 2, total);
|
||||
}
|
||||
|
||||
void InstanceImportTask::processZipPack()
|
||||
@ -258,31 +255,290 @@ void InstanceImportTask::extractFinished()
|
||||
|
||||
void InstanceImportTask::extractAborted()
|
||||
{
|
||||
emitAborted();
|
||||
emitFailed(tr("Instance import has been aborted."));
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
inst_creation_task->setIcon(m_instIcon);
|
||||
inst_creation_task->setGroup(m_instGroup);
|
||||
|
||||
connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] {
|
||||
setOverride(inst_creation_task->shouldOverride());
|
||||
emitSucceeded();
|
||||
QString forgeVersion;
|
||||
QString fabricVersion;
|
||||
// TODO: is Quilt relevant here?
|
||||
for(auto &loader: pack.minecraft.modLoaders)
|
||||
{
|
||||
auto id = loader.id;
|
||||
if(id.startsWith("forge-"))
|
||||
{
|
||||
id.remove("forge-");
|
||||
forgeVersion = id;
|
||||
continue;
|
||||
}
|
||||
if(id.startsWith("fabric-"))
|
||||
{
|
||||
id.remove("fabric-");
|
||||
fabricVersion = id;
|
||||
continue;
|
||||
}
|
||||
logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
|
||||
}
|
||||
|
||||
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(QRegularExpression("[.]+$"));
|
||||
logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack."));
|
||||
}
|
||||
auto components = instance.getPackProfile();
|
||||
components->buildingFromScratch();
|
||||
components->setComponentVersion("net.minecraft", mcVersion, true);
|
||||
if(!forgeVersion.isEmpty())
|
||||
{
|
||||
// FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata.
|
||||
if(forgeVersion == "recommended")
|
||||
{
|
||||
if(forgemap.contains(mcVersion))
|
||||
{
|
||||
forgeVersion = forgemap[mcVersion];
|
||||
}
|
||||
else
|
||||
{
|
||||
logWarning(tr("Could not map recommended Forge version for Minecraft %1").arg(mcVersion));
|
||||
}
|
||||
}
|
||||
components->setComponentVersion("net.minecraftforge", forgeVersion);
|
||||
}
|
||||
if(!fabricVersion.isEmpty())
|
||||
{
|
||||
components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion);
|
||||
}
|
||||
if (m_instIcon != "default")
|
||||
{
|
||||
instance.setIconKey(m_instIcon);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(pack.name.contains("Direwolf20"))
|
||||
{
|
||||
instance.setIconKey("steve");
|
||||
}
|
||||
else if(pack.name.contains("FTB") || pack.name.contains("Feed The Beast"))
|
||||
{
|
||||
instance.setIconKey("ftb_logo");
|
||||
}
|
||||
else
|
||||
{
|
||||
// default to something other than the MultiMC default to distinguish these
|
||||
instance.setIconKey("flame");
|
||||
}
|
||||
}
|
||||
QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods");
|
||||
QFileInfo jarmodsInfo(jarmodsPath);
|
||||
if(jarmodsInfo.isDir())
|
||||
{
|
||||
// install all the jar mods
|
||||
qDebug() << "Found jarmods:";
|
||||
QDir jarmodsDir(jarmodsPath);
|
||||
QStringList jarMods;
|
||||
for (auto info: jarmodsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
|
||||
{
|
||||
qDebug() << info.fileName();
|
||||
jarMods.push_back(info.absoluteFilePath());
|
||||
}
|
||||
auto profile = instance.getPackProfile();
|
||||
profile->installJarMods(jarMods);
|
||||
// nuke the original files
|
||||
FS::deletePath(jarmodsPath);
|
||||
}
|
||||
instance.setName(m_instName);
|
||||
m_modIdResolver = new Flame::FileResolvingTask(APPLICATION->network(), pack);
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]()
|
||||
{
|
||||
auto results = m_modIdResolver->getResults();
|
||||
//first check for blocked mods
|
||||
QString text;
|
||||
auto anyBlocked = false;
|
||||
for(const auto& result: results.files.values()) {
|
||||
if (!result.resolved || result.url.isEmpty()) {
|
||||
text += QString("%1: <a href='%2'>%2</a><br/>").arg(result.fileName, result.websiteUrl);
|
||||
anyBlocked = true;
|
||||
}
|
||||
}
|
||||
if(anyBlocked) {
|
||||
qWarning() << "Blocked mods found, displaying mod list";
|
||||
|
||||
auto message_dialog = new ScrollMessageBox(m_parent,
|
||||
tr("Blocked mods found"),
|
||||
tr("The following mods were blocked on third party launchers.<br/>"
|
||||
"You will need to manually download them and add them to the modpack"),
|
||||
text);
|
||||
message_dialog->setModal(true);
|
||||
|
||||
if (message_dialog->exec()) {
|
||||
m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
|
||||
for (const auto &result: m_modIdResolver->getResults().files) {
|
||||
QString filename = result.fileName;
|
||||
if (!result.required) {
|
||||
filename += ".disabled";
|
||||
}
|
||||
|
||||
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
|
||||
auto path = FS::PathCombine(m_stagingPath, relpath);
|
||||
|
||||
switch (result.type) {
|
||||
case Flame::File::Type::Folder: {
|
||||
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
|
||||
// fall-through intentional, we treat these as plain old mods and dump them wherever.
|
||||
}
|
||||
case Flame::File::Type::SingleFile:
|
||||
case Flame::File::Type::Mod: {
|
||||
if (!result.url.isEmpty()) {
|
||||
qDebug() << "Will download" << result.url << "to" << path;
|
||||
auto dl = Net::Download::makeFile(result.url, path);
|
||||
m_filesNetJob->addNetAction(dl);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Flame::File::Type::Modpack:
|
||||
logWarning(
|
||||
tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(
|
||||
relpath));
|
||||
break;
|
||||
case Flame::File::Type::Cmod2:
|
||||
case Flame::File::Type::Ctoc:
|
||||
case Flame::File::Type::Unknown:
|
||||
logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_modIdResolver.reset();
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() {
|
||||
m_filesNetJob.reset();
|
||||
emitSucceeded();
|
||||
}
|
||||
);
|
||||
connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason) {
|
||||
m_filesNetJob.reset();
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
|
||||
setProgress(current, total);
|
||||
});
|
||||
setStatus(tr("Downloading mods..."));
|
||||
m_filesNetJob->start();
|
||||
} else {
|
||||
m_modIdResolver.reset();
|
||||
emitFailed("Canceled");
|
||||
}
|
||||
} else {
|
||||
//TODO extract to function ?
|
||||
m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
|
||||
for (const auto &result: m_modIdResolver->getResults().files) {
|
||||
QString filename = result.fileName;
|
||||
if (!result.required) {
|
||||
filename += ".disabled";
|
||||
}
|
||||
|
||||
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
|
||||
auto path = FS::PathCombine(m_stagingPath, relpath);
|
||||
|
||||
switch (result.type) {
|
||||
case Flame::File::Type::Folder: {
|
||||
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
|
||||
// fall-through intentional, we treat these as plain old mods and dump them wherever.
|
||||
}
|
||||
case Flame::File::Type::SingleFile:
|
||||
case Flame::File::Type::Mod: {
|
||||
if (!result.url.isEmpty()) {
|
||||
qDebug() << "Will download" << result.url << "to" << path;
|
||||
auto dl = Net::Download::makeFile(result.url, path);
|
||||
m_filesNetJob->addNetAction(dl);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Flame::File::Type::Modpack:
|
||||
logWarning(
|
||||
tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(
|
||||
relpath));
|
||||
break;
|
||||
case Flame::File::Type::Cmod2:
|
||||
case Flame::File::Type::Ctoc:
|
||||
case Flame::File::Type::Unknown:
|
||||
logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_modIdResolver.reset();
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() {
|
||||
m_filesNetJob.reset();
|
||||
emitSucceeded();
|
||||
}
|
||||
);
|
||||
connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason) {
|
||||
m_filesNetJob.reset();
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
|
||||
setProgress(current, total);
|
||||
});
|
||||
setStatus(tr("Downloading mods..."));
|
||||
m_filesNetJob->start();
|
||||
}
|
||||
}
|
||||
);
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason)
|
||||
{
|
||||
m_modIdResolver.reset();
|
||||
emitFailed(tr("Unable to resolve mod IDs:\n") + reason);
|
||||
});
|
||||
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress);
|
||||
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus);
|
||||
connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater);
|
||||
|
||||
connect(this, &Task::aborted, inst_creation_task, &InstanceCreationTask::abort);
|
||||
connect(inst_creation_task, &Task::aborted, this, &Task::abort);
|
||||
connect(inst_creation_task, &Task::abortStatusChanged, this, &Task::setAbortable);
|
||||
|
||||
inst_creation_task->start();
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, [&](qint64 current, qint64 total)
|
||||
{
|
||||
setProgress(current, total);
|
||||
});
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, [&](QString status)
|
||||
{
|
||||
setStatus(status);
|
||||
});
|
||||
m_modIdResolver->start();
|
||||
}
|
||||
|
||||
void InstanceImportTask::processTechnic()
|
||||
@ -290,7 +546,7 @@ void InstanceImportTask::processTechnic()
|
||||
shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor = new Technic::TechnicPackProcessor();
|
||||
connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &InstanceImportTask::emitSucceeded);
|
||||
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()
|
||||
@ -304,7 +560,7 @@ void InstanceImportTask::processMultiMC()
|
||||
instance.resetTimePlayed();
|
||||
|
||||
// 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 (m_instIcon != "default") {
|
||||
@ -325,26 +581,198 @@ void InstanceImportTask::processMultiMC()
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
// https://docs.modrinth.com/docs/modpacks/format_definition/
|
||||
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);
|
||||
inst_creation_task->setIcon(m_instIcon);
|
||||
inst_creation_task->setGroup(m_instGroup);
|
||||
auto jsonFiles = Json::requireIsArrayOf<QJsonObject>(obj, "files", "modrinth.index.json");
|
||||
bool had_optional = false;
|
||||
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] {
|
||||
setOverride(inst_creation_task->shouldOverride());
|
||||
emitSucceeded();
|
||||
auto mcPath = FS::PathCombine(m_stagingPath, ".minecraft");
|
||||
|
||||
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);
|
||||
if (!quiltVersion.isEmpty())
|
||||
components->setComponentVersion("org.quiltmc.quilt-loader", quiltVersion);
|
||||
if (!forgeVersion.isEmpty())
|
||||
components->setComponentVersion("net.minecraftforge", forgeVersion);
|
||||
if (m_instIcon != "default")
|
||||
{
|
||||
instance.setIconKey(m_instIcon);
|
||||
}
|
||||
else
|
||||
{
|
||||
instance.setIconKey("modrinth");
|
||||
}
|
||||
instance.setName(m_instName);
|
||||
instance.saveNow();
|
||||
|
||||
m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
|
||||
for (auto file : files)
|
||||
{
|
||||
auto path = FS::PathCombine(m_stagingPath, ".minecraft", file.path);
|
||||
qDebug() << "Will try to download" << file.downloads.front() << "to" << path;
|
||||
auto dl = Net::Download::makeFile(file.downloads.dequeue(), path);
|
||||
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
||||
m_filesNetJob->addNetAction(dl);
|
||||
|
||||
if (file.downloads.size() > 0) {
|
||||
// FIXME: This really needs to be put into a ConcurrentTask of
|
||||
// MultipleOptionsTask's , once those exist :)
|
||||
connect(dl.get(), &NetAction::failed, [this, &file, path, dl]{
|
||||
auto dl = Net::Download::makeFile(file.downloads.dequeue(), path);
|
||||
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
||||
m_filesNetJob->addNetAction(dl);
|
||||
dl->succeeded();
|
||||
});
|
||||
}
|
||||
}
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
|
||||
{
|
||||
m_filesNetJob.reset();
|
||||
emitSucceeded();
|
||||
}
|
||||
);
|
||||
connect(m_filesNetJob.get(), &NetJob::failed, [&](const QString &reason)
|
||||
{
|
||||
m_filesNetJob.reset();
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress);
|
||||
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus);
|
||||
connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater);
|
||||
|
||||
connect(this, &Task::aborted, inst_creation_task, &InstanceCreationTask::abort);
|
||||
connect(inst_creation_task, &Task::aborted, this, &Task::abort);
|
||||
connect(inst_creation_task, &Task::abortStatusChanged, this, &Task::setAbortable);
|
||||
|
||||
inst_creation_task->start();
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
|
||||
{
|
||||
setProgress(current, total);
|
||||
});
|
||||
setStatus(tr("Downloading mods..."));
|
||||
m_filesNetJob->start();
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ class InstanceImportTask : public InstanceTask
|
||||
public:
|
||||
explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr);
|
||||
|
||||
bool canAbort() const override { return true; }
|
||||
bool abort() override;
|
||||
const QVector<Flame::File> &getBlockedFiles() const
|
||||
{
|
||||
@ -79,7 +80,6 @@ private slots:
|
||||
void downloadSucceeded();
|
||||
void downloadFailed(QString reason);
|
||||
void downloadProgressChanged(qint64 current, qint64 total);
|
||||
void downloadAborted();
|
||||
void extractFinished();
|
||||
void extractAborted();
|
||||
|
||||
|
@ -33,32 +33,30 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QSet>
|
||||
#include <QFile>
|
||||
#include <QThread>
|
||||
#include <QTextStream>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QUuid>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QMimeData>
|
||||
#include <QSet>
|
||||
#include <QStack>
|
||||
#include <QPair>
|
||||
#include <QTextStream>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include <QUuid>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
#include "BaseInstance.h"
|
||||
#include "ExponentialSeries.h"
|
||||
#include "FileSystem.h"
|
||||
#include "InstanceList.h"
|
||||
#include "BaseInstance.h"
|
||||
#include "InstanceTask.h"
|
||||
#include "NullInstance.h"
|
||||
#include "WatchLock.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "settings/INISettingsObject.h"
|
||||
#include "NullInstance.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "FileSystem.h"
|
||||
#include "ExponentialSeries.h"
|
||||
#include "WatchLock.h"
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
#include <Windows.h>
|
||||
@ -66,12 +64,13 @@
|
||||
|
||||
const static int GROUP_FILE_FORMAT_VERSION = 1;
|
||||
|
||||
InstanceList::InstanceList(SettingsObjectPtr settings, const QString& instDir, QObject* parent)
|
||||
InstanceList::InstanceList(SettingsObjectPtr settings, const QString & instDir, QObject *parent)
|
||||
: QAbstractListModel(parent), m_globalSettings(settings)
|
||||
{
|
||||
resumeWatch();
|
||||
// Create aand normalize path
|
||||
if (!QDir::current().exists(instDir)) {
|
||||
if (!QDir::current().exists(instDir))
|
||||
{
|
||||
QDir::current().mkpath(instDir);
|
||||
}
|
||||
|
||||
@ -84,7 +83,9 @@ InstanceList::InstanceList(SettingsObjectPtr settings, const QString& instDir, Q
|
||||
m_watcher->addPath(m_instDir);
|
||||
}
|
||||
|
||||
InstanceList::~InstanceList() {}
|
||||
InstanceList::~InstanceList()
|
||||
{
|
||||
}
|
||||
|
||||
Qt::DropActions InstanceList::supportedDragActions() const
|
||||
{
|
||||
@ -98,7 +99,7 @@ Qt::DropActions InstanceList::supportedDropActions() const
|
||||
|
||||
bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const
|
||||
{
|
||||
if (data && data->hasFormat("application/x-instanceid")) {
|
||||
if(data && data->hasFormat("application/x-instanceid")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -106,7 +107,7 @@ bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action,
|
||||
|
||||
bool InstanceList::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)
|
||||
{
|
||||
if (data && data->hasFormat("application/x-instanceid")) {
|
||||
if(data && data->hasFormat("application/x-instanceid")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -119,33 +120,35 @@ QStringList InstanceList::mimeTypes() const
|
||||
return types;
|
||||
}
|
||||
|
||||
QMimeData* InstanceList::mimeData(const QModelIndexList& indexes) const
|
||||
QMimeData * InstanceList::mimeData(const QModelIndexList& indexes) const
|
||||
{
|
||||
auto mimeData = QAbstractListModel::mimeData(indexes);
|
||||
if (indexes.size() == 1) {
|
||||
if(indexes.size() == 1) {
|
||||
auto instanceId = data(indexes[0], InstanceIDRole).toString();
|
||||
mimeData->setData("application/x-instanceid", instanceId.toUtf8());
|
||||
}
|
||||
return mimeData;
|
||||
}
|
||||
|
||||
int InstanceList::rowCount(const QModelIndex& parent) const
|
||||
|
||||
int InstanceList::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
return m_instances.count();
|
||||
}
|
||||
|
||||
QModelIndex InstanceList::index(int row, int column, const QModelIndex& parent) const
|
||||
QModelIndex InstanceList::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
if (row < 0 || row >= m_instances.size())
|
||||
return QModelIndex();
|
||||
return createIndex(row, column, (void*)m_instances.at(row).get());
|
||||
return createIndex(row, column, (void *)m_instances.at(row).get());
|
||||
}
|
||||
|
||||
QVariant InstanceList::data(const QModelIndex& index, int role) const
|
||||
QVariant InstanceList::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
if (!index.isValid())
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer());
|
||||
@ -190,25 +193,29 @@ QVariant InstanceList::data(const QModelIndex& index, int role) const
|
||||
|
||||
bool InstanceList::setData(const QModelIndex& index, const QVariant& value, int role)
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
if (!index.isValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (role != Qt::EditRole) {
|
||||
if(role != Qt::EditRole)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
BaseInstance* pdata = static_cast<BaseInstance*>(index.internalPointer());
|
||||
BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer());
|
||||
auto newName = value.toString();
|
||||
if (pdata->name() == newName) {
|
||||
if(pdata->name() == newName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
pdata->setName(newName);
|
||||
return true;
|
||||
}
|
||||
|
||||
Qt::ItemFlags InstanceList::flags(const QModelIndex& index) const
|
||||
Qt::ItemFlags InstanceList::flags(const QModelIndex &index) const
|
||||
{
|
||||
Qt::ItemFlags f;
|
||||
if (index.isValid()) {
|
||||
if (index.isValid())
|
||||
{
|
||||
f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
|
||||
}
|
||||
return f;
|
||||
@ -217,11 +224,13 @@ Qt::ItemFlags InstanceList::flags(const QModelIndex& index) const
|
||||
GroupId InstanceList::getInstanceGroup(const InstanceId& id) const
|
||||
{
|
||||
auto inst = getInstanceById(id);
|
||||
if (!inst) {
|
||||
if(!inst)
|
||||
{
|
||||
return GroupId();
|
||||
}
|
||||
auto iter = m_instanceGroupIndex.find(inst->id());
|
||||
if (iter != m_instanceGroupIndex.end()) {
|
||||
if(iter != m_instanceGroupIndex.end())
|
||||
{
|
||||
return *iter;
|
||||
}
|
||||
return GroupId();
|
||||
@ -230,27 +239,33 @@ GroupId InstanceList::getInstanceGroup(const InstanceId& id) const
|
||||
void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
|
||||
{
|
||||
auto inst = getInstanceById(id);
|
||||
if (!inst) {
|
||||
if(!inst)
|
||||
{
|
||||
qDebug() << "Attempt to set a null instance's group";
|
||||
return;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
auto iter = m_instanceGroupIndex.find(inst->id());
|
||||
if (iter != m_instanceGroupIndex.end()) {
|
||||
if (*iter != name) {
|
||||
if(iter != m_instanceGroupIndex.end())
|
||||
{
|
||||
if(*iter != name)
|
||||
{
|
||||
*iter = name;
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
changed = true;
|
||||
m_instanceGroupIndex[id] = name;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
if(changed)
|
||||
{
|
||||
m_groupNameCache.insert(name);
|
||||
auto idx = getInstIndex(inst.get());
|
||||
emit dataChanged(index(idx), index(idx), { GroupRole });
|
||||
emit dataChanged(index(idx), index(idx), {GroupRole});
|
||||
saveGroupList();
|
||||
}
|
||||
}
|
||||
@ -264,20 +279,24 @@ void InstanceList::deleteGroup(const QString& name)
|
||||
{
|
||||
bool removed = false;
|
||||
qDebug() << "Delete group" << name;
|
||||
for (auto& instance : m_instances) {
|
||||
const auto& instID = instance->id();
|
||||
for(auto & instance: m_instances)
|
||||
{
|
||||
const auto & instID = instance->id();
|
||||
auto instGroupName = getInstanceGroup(instID);
|
||||
if (instGroupName == name) {
|
||||
if(instGroupName == name)
|
||||
{
|
||||
m_instanceGroupIndex.remove(instID);
|
||||
qDebug() << "Remove" << instID << "from group" << name;
|
||||
removed = true;
|
||||
auto idx = getInstIndex(instance.get());
|
||||
if (idx > 0) {
|
||||
emit dataChanged(index(idx), index(idx), { GroupRole });
|
||||
if(idx > 0)
|
||||
{
|
||||
emit dataChanged(index(idx), index(idx), {GroupRole});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removed) {
|
||||
if(removed)
|
||||
{
|
||||
saveGroupList();
|
||||
}
|
||||
}
|
||||
@ -287,75 +306,23 @@ bool InstanceList::isGroupCollapsed(const QString& group)
|
||||
return m_collapsedGroups.contains(group);
|
||||
}
|
||||
|
||||
bool InstanceList::trashInstance(const InstanceId& id)
|
||||
{
|
||||
auto inst = getInstanceById(id);
|
||||
if (!inst) {
|
||||
qDebug() << "Cannot trash instance" << id << ". No such instance is present (deleted externally?).";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto cachedGroupId = m_instanceGroupIndex[id];
|
||||
|
||||
qDebug() << "Will trash instance" << id;
|
||||
QString trashedLoc;
|
||||
|
||||
if (m_instanceGroupIndex.remove(id)) {
|
||||
saveGroupList();
|
||||
}
|
||||
|
||||
if (!FS::trash(inst->instanceRoot(), &trashedLoc)) {
|
||||
qDebug() << "Trash of instance" << id << "has not been completely successfully...";
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << "Instance" << id << "has been trashed by the launcher.";
|
||||
m_trashHistory.push({id, inst->instanceRoot(), trashedLoc, cachedGroupId});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InstanceList::trashedSomething() {
|
||||
return !m_trashHistory.empty();
|
||||
}
|
||||
|
||||
void InstanceList::undoTrashInstance() {
|
||||
if (m_trashHistory.empty()) {
|
||||
qWarning() << "Nothing to recover from trash.";
|
||||
return;
|
||||
}
|
||||
|
||||
auto top = m_trashHistory.pop();
|
||||
|
||||
while (QDir(top.polyPath).exists()) {
|
||||
top.id += "1";
|
||||
top.polyPath += "1";
|
||||
}
|
||||
|
||||
qDebug() << "Moving" << top.trashPath << "back to" << top.polyPath;
|
||||
QFile(top.trashPath).rename(top.polyPath);
|
||||
|
||||
m_instanceGroupIndex[top.id] = top.groupName;
|
||||
m_groupNameCache.insert(top.groupName);
|
||||
|
||||
saveGroupList();
|
||||
emit instancesChanged();
|
||||
}
|
||||
|
||||
void InstanceList::deleteInstance(const InstanceId& id)
|
||||
{
|
||||
auto inst = getInstanceById(id);
|
||||
if (!inst) {
|
||||
if(!inst)
|
||||
{
|
||||
qDebug() << "Cannot delete instance" << id << ". No such instance is present (deleted externally?).";
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_instanceGroupIndex.remove(id)) {
|
||||
if(m_instanceGroupIndex.remove(id))
|
||||
{
|
||||
saveGroupList();
|
||||
}
|
||||
|
||||
qDebug() << "Will delete instance" << id;
|
||||
if (!FS::deletePath(inst->instanceRoot())) {
|
||||
if(!FS::deletePath(inst->instanceRoot()))
|
||||
{
|
||||
qWarning() << "Deletion of instance" << id << "has not been completely successful ...";
|
||||
return;
|
||||
}
|
||||
@ -363,13 +330,15 @@ void InstanceList::deleteInstance(const InstanceId& id)
|
||||
qDebug() << "Instance" << id << "has been deleted by the launcher.";
|
||||
}
|
||||
|
||||
static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr>& list)
|
||||
static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr> &list)
|
||||
{
|
||||
QMap<InstanceId, InstanceLocator> out;
|
||||
int i = 0;
|
||||
for (auto& item : list) {
|
||||
for(auto & item: list)
|
||||
{
|
||||
auto id = item->id();
|
||||
if (out.contains(id)) {
|
||||
if(out.contains(id))
|
||||
{
|
||||
qWarning() << "Duplicate ID" << id << "in instance list";
|
||||
}
|
||||
out[id] = std::make_pair(item, i);
|
||||
@ -378,21 +347,24 @@ static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr>&
|
||||
return out;
|
||||
}
|
||||
|
||||
QList<InstanceId> InstanceList::discoverInstances()
|
||||
QList< InstanceId > InstanceList::discoverInstances()
|
||||
{
|
||||
qDebug() << "Discovering instances in" << m_instDir;
|
||||
QList<InstanceId> out;
|
||||
QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden, QDirIterator::FollowSymlinks);
|
||||
while (iter.hasNext()) {
|
||||
while (iter.hasNext())
|
||||
{
|
||||
QString subDir = iter.next();
|
||||
QFileInfo dirInfo(subDir);
|
||||
if (!QFileInfo(FS::PathCombine(subDir, "instance.cfg")).exists())
|
||||
continue;
|
||||
// if it is a symlink, ignore it if it goes to the instance folder
|
||||
if (dirInfo.isSymLink()) {
|
||||
if(dirInfo.isSymLink())
|
||||
{
|
||||
QFileInfo targetInfo(dirInfo.symLinkTarget());
|
||||
QFileInfo instDirInfo(m_instDir);
|
||||
if (targetInfo.canonicalPath() == instDirInfo.canonicalFilePath()) {
|
||||
if(targetInfo.canonicalPath() == instDirInfo.canonicalFilePath())
|
||||
{
|
||||
qDebug() << "Ignoring symlink" << subDir << "that leads into the instances folder";
|
||||
continue;
|
||||
}
|
||||
@ -416,56 +388,74 @@ InstanceList::InstListError InstanceList::loadList()
|
||||
|
||||
QList<InstancePtr> newList;
|
||||
|
||||
for (auto& id : discoverInstances()) {
|
||||
if (existingIds.contains(id)) {
|
||||
for(auto & id: discoverInstances())
|
||||
{
|
||||
if(existingIds.contains(id))
|
||||
{
|
||||
auto instPair = existingIds[id];
|
||||
existingIds.remove(id);
|
||||
qDebug() << "Should keep and soft-reload" << id;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
InstancePtr instPtr = loadInstance(id);
|
||||
if (instPtr) {
|
||||
if(instPtr)
|
||||
{
|
||||
newList.append(instPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: looks like a general algorithm with a few specifics inserted. Do something about it.
|
||||
if (!existingIds.isEmpty()) {
|
||||
if(!existingIds.isEmpty())
|
||||
{
|
||||
// get the list of removed instances and sort it by their original index, from last to first
|
||||
auto deadList = existingIds.values();
|
||||
auto orderSortPredicate = [](const InstanceLocator& a, const InstanceLocator& b) -> bool { return a.second > b.second; };
|
||||
auto orderSortPredicate = [](const InstanceLocator & a, const InstanceLocator & b) -> bool
|
||||
{
|
||||
return a.second > b.second;
|
||||
};
|
||||
std::sort(deadList.begin(), deadList.end(), orderSortPredicate);
|
||||
// remove the contiguous ranges of rows
|
||||
int front_bookmark = -1;
|
||||
int back_bookmark = -1;
|
||||
int currentItem = -1;
|
||||
auto removeNow = [&]() {
|
||||
auto removeNow = [&]()
|
||||
{
|
||||
beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark);
|
||||
m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1);
|
||||
endRemoveRows();
|
||||
front_bookmark = -1;
|
||||
back_bookmark = currentItem;
|
||||
};
|
||||
for (auto& removedItem : deadList) {
|
||||
for(auto & removedItem: deadList)
|
||||
{
|
||||
auto instPtr = removedItem.first;
|
||||
instPtr->invalidate();
|
||||
currentItem = removedItem.second;
|
||||
if (back_bookmark == -1) {
|
||||
if(back_bookmark == -1)
|
||||
{
|
||||
// no bookmark yet
|
||||
back_bookmark = currentItem;
|
||||
} else if (currentItem == front_bookmark - 1) {
|
||||
}
|
||||
else if(currentItem == front_bookmark - 1)
|
||||
{
|
||||
// part of contiguous sequence, continue
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// seam between previous and current item
|
||||
removeNow();
|
||||
}
|
||||
front_bookmark = currentItem;
|
||||
}
|
||||
if (back_bookmark != -1) {
|
||||
if(back_bookmark != -1)
|
||||
{
|
||||
removeNow();
|
||||
}
|
||||
}
|
||||
if (newList.size()) {
|
||||
if(newList.size())
|
||||
{
|
||||
add(newList);
|
||||
}
|
||||
m_dirty = false;
|
||||
@ -476,23 +466,26 @@ InstanceList::InstListError InstanceList::loadList()
|
||||
void InstanceList::updateTotalPlayTime()
|
||||
{
|
||||
totalPlayTime = 0;
|
||||
for (auto const& itr : m_instances) {
|
||||
for(auto const& itr : m_instances)
|
||||
{
|
||||
totalPlayTime += itr.get()->totalTimePlayed();
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceList::saveNow()
|
||||
{
|
||||
for (auto& item : m_instances) {
|
||||
for(auto & item: m_instances)
|
||||
{
|
||||
item->saveNow();
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceList::add(const QList<InstancePtr>& t)
|
||||
void InstanceList::add(const QList<InstancePtr> &t)
|
||||
{
|
||||
beginInsertRows(QModelIndex(), m_instances.count(), m_instances.count() + t.size() - 1);
|
||||
m_instances.append(t);
|
||||
for (auto& ptr : t) {
|
||||
for(auto & ptr : t)
|
||||
{
|
||||
connect(ptr.get(), &BaseInstance::propertiesChanged, this, &InstanceList::propertiesChanged);
|
||||
}
|
||||
endInsertRows();
|
||||
@ -500,74 +493,69 @@ void InstanceList::add(const QList<InstancePtr>& t)
|
||||
|
||||
void InstanceList::resumeWatch()
|
||||
{
|
||||
if (m_watchLevel > 0) {
|
||||
if(m_watchLevel > 0)
|
||||
{
|
||||
qWarning() << "Bad suspend level resume in instance list";
|
||||
return;
|
||||
}
|
||||
m_watchLevel++;
|
||||
if (m_watchLevel > 0 && m_dirty) {
|
||||
if(m_watchLevel > 0 && m_dirty)
|
||||
{
|
||||
loadList();
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceList::suspendWatch()
|
||||
{
|
||||
m_watchLevel--;
|
||||
m_watchLevel --;
|
||||
}
|
||||
|
||||
void InstanceList::providerUpdated()
|
||||
{
|
||||
m_dirty = true;
|
||||
if (m_watchLevel == 1) {
|
||||
if(m_watchLevel == 1)
|
||||
{
|
||||
loadList();
|
||||
}
|
||||
}
|
||||
|
||||
InstancePtr InstanceList::getInstanceById(QString instId) const
|
||||
{
|
||||
if (instId.isEmpty())
|
||||
if(instId.isEmpty())
|
||||
return InstancePtr();
|
||||
for (auto& inst : m_instances) {
|
||||
if (inst->id() == instId) {
|
||||
for(auto & inst: m_instances)
|
||||
{
|
||||
if (inst->id() == instId)
|
||||
{
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
return InstancePtr();
|
||||
}
|
||||
|
||||
InstancePtr InstanceList::getInstanceByManagedName(const QString& managed_name) const
|
||||
{
|
||||
if (managed_name.isEmpty())
|
||||
return {};
|
||||
|
||||
for (auto instance : m_instances) {
|
||||
if (instance->getManagedPackName() == managed_name)
|
||||
return instance;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QModelIndex InstanceList::getInstanceIndexById(const QString &id) const
|
||||
{
|
||||
return index(getInstIndex(getInstanceById(id).get()));
|
||||
}
|
||||
|
||||
int InstanceList::getInstIndex(BaseInstance* inst) const
|
||||
int InstanceList::getInstIndex(BaseInstance *inst) const
|
||||
{
|
||||
int count = m_instances.count();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (inst == m_instances[i].get()) {
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (inst == m_instances[i].get())
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void InstanceList::propertiesChanged(BaseInstance* inst)
|
||||
void InstanceList::propertiesChanged(BaseInstance *inst)
|
||||
{
|
||||
int i = getInstIndex(inst);
|
||||
if (i != -1) {
|
||||
if (i != -1)
|
||||
{
|
||||
emit dataChanged(index(i), index(i));
|
||||
updateTotalPlayTime();
|
||||
}
|
||||
@ -575,7 +563,8 @@ void InstanceList::propertiesChanged(BaseInstance* inst)
|
||||
|
||||
InstancePtr InstanceList::loadInstance(const InstanceId& id)
|
||||
{
|
||||
if (!m_groupsLoaded) {
|
||||
if(!m_groupsLoaded)
|
||||
{
|
||||
loadGroupList();
|
||||
}
|
||||
|
||||
@ -603,42 +592,50 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id)
|
||||
void InstanceList::saveGroupList()
|
||||
{
|
||||
qDebug() << "Will save group list now.";
|
||||
if (!m_instancesProbed) {
|
||||
if(!m_instancesProbed)
|
||||
{
|
||||
qDebug() << "Group saving prevented because we don't know the full list of instances yet.";
|
||||
return;
|
||||
}
|
||||
WatchLock foo(m_watcher, m_instDir);
|
||||
QString groupFileName = m_instDir + "/instgroups.json";
|
||||
QMap<QString, QSet<QString>> reverseGroupMap;
|
||||
for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++) {
|
||||
for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++)
|
||||
{
|
||||
QString id = iter.key();
|
||||
QString group = iter.value();
|
||||
if (group.isEmpty())
|
||||
continue;
|
||||
if (!instanceSet.contains(id)) {
|
||||
if(!instanceSet.contains(id))
|
||||
{
|
||||
qDebug() << "Skipping saving missing instance" << id << "to groups list.";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!reverseGroupMap.count(group)) {
|
||||
if (!reverseGroupMap.count(group))
|
||||
{
|
||||
QSet<QString> set;
|
||||
set.insert(id);
|
||||
reverseGroupMap[group] = set;
|
||||
} else {
|
||||
QSet<QString>& set = reverseGroupMap[group];
|
||||
}
|
||||
else
|
||||
{
|
||||
QSet<QString> &set = reverseGroupMap[group];
|
||||
set.insert(id);
|
||||
}
|
||||
}
|
||||
QJsonObject toplevel;
|
||||
toplevel.insert("formatVersion", QJsonValue(QString("1")));
|
||||
QJsonObject groupsArr;
|
||||
for (auto iter = reverseGroupMap.begin(); iter != reverseGroupMap.end(); iter++) {
|
||||
for (auto iter = reverseGroupMap.begin(); iter != reverseGroupMap.end(); iter++)
|
||||
{
|
||||
auto list = iter.value();
|
||||
auto name = iter.key();
|
||||
QJsonObject groupObj;
|
||||
QJsonArray instanceArr;
|
||||
groupObj.insert("hidden", QJsonValue(m_collapsedGroups.contains(name)));
|
||||
for (auto item : list) {
|
||||
for (auto item : list)
|
||||
{
|
||||
instanceArr.append(QJsonValue(item));
|
||||
}
|
||||
groupObj.insert("instances", instanceArr);
|
||||
@ -646,10 +643,13 @@ void InstanceList::saveGroupList()
|
||||
}
|
||||
toplevel.insert("groups", groupsArr);
|
||||
QJsonDocument doc(toplevel);
|
||||
try {
|
||||
try
|
||||
{
|
||||
FS::write(groupFileName, doc.toJson());
|
||||
qDebug() << "Group list saved.";
|
||||
} catch (const FS::FileSystemException& e) {
|
||||
}
|
||||
catch (const FS::FileSystemException &e)
|
||||
{
|
||||
qCritical() << "Failed to write instance group file :" << e.cause();
|
||||
}
|
||||
}
|
||||
@ -665,9 +665,12 @@ void InstanceList::loadGroupList()
|
||||
return;
|
||||
|
||||
QByteArray jsonData;
|
||||
try {
|
||||
try
|
||||
{
|
||||
jsonData = FS::read(groupFileName);
|
||||
} catch (const FS::FileSystemException& e) {
|
||||
}
|
||||
catch (const FS::FileSystemException &e)
|
||||
{
|
||||
qCritical() << "Failed to read instance group file :" << e.cause();
|
||||
return;
|
||||
}
|
||||
@ -676,15 +679,17 @@ void InstanceList::loadGroupList()
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error);
|
||||
|
||||
// if the json was bad, fail
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
if (error.error != QJsonParseError::NoError)
|
||||
{
|
||||
qCritical() << QString("Failed to parse instance group file: %1 at offset %2")
|
||||
.arg(error.errorString(), QString::number(error.offset))
|
||||
.toUtf8();
|
||||
.arg(error.errorString(), QString::number(error.offset))
|
||||
.toUtf8();
|
||||
return;
|
||||
}
|
||||
|
||||
// if the root of the json wasn't an object, fail
|
||||
if (!jsonDoc.isObject()) {
|
||||
if (!jsonDoc.isObject())
|
||||
{
|
||||
qWarning() << "Invalid group file. Root entry should be an object.";
|
||||
return;
|
||||
}
|
||||
@ -696,7 +701,8 @@ void InstanceList::loadGroupList()
|
||||
return;
|
||||
|
||||
// Get the groups. if it's not an object, fail
|
||||
if (!rootObj.value("groups").isObject()) {
|
||||
if (!rootObj.value("groups").isObject())
|
||||
{
|
||||
qWarning() << "Invalid group list JSON: 'groups' should be an object.";
|
||||
return;
|
||||
}
|
||||
@ -706,20 +712,21 @@ void InstanceList::loadGroupList()
|
||||
|
||||
// Iterate through all the groups.
|
||||
QJsonObject groupMapping = rootObj.value("groups").toObject();
|
||||
for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++) {
|
||||
for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++)
|
||||
{
|
||||
QString groupName = iter.key();
|
||||
|
||||
// If not an object, complain and skip to the next one.
|
||||
if (!iter.value().isObject()) {
|
||||
if (!iter.value().isObject())
|
||||
{
|
||||
qWarning() << QString("Group '%1' in the group list should be an object.").arg(groupName).toUtf8();
|
||||
continue;
|
||||
}
|
||||
|
||||
QJsonObject groupObj = iter.value().toObject();
|
||||
if (!groupObj.value("instances").isArray()) {
|
||||
qWarning() << QString("Group '%1' in the group list is invalid. It should contain an array called 'instances'.")
|
||||
.arg(groupName)
|
||||
.toUtf8();
|
||||
if (!groupObj.value("instances").isArray())
|
||||
{
|
||||
qWarning() << QString("Group '%1' in the group list is invalid. It should contain an array called 'instances'.").arg(groupName).toUtf8();
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -727,14 +734,15 @@ void InstanceList::loadGroupList()
|
||||
groupSet.insert(groupName);
|
||||
|
||||
auto hidden = groupObj.value("hidden").toBool(false);
|
||||
if (hidden) {
|
||||
if(hidden) {
|
||||
m_collapsedGroups.insert(groupName);
|
||||
}
|
||||
|
||||
// Iterate through the list of instances in the group.
|
||||
QJsonArray instancesArray = groupObj.value("instances").toArray();
|
||||
|
||||
for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); iter2++) {
|
||||
for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); iter2++)
|
||||
{
|
||||
m_instanceGroupIndex[(*iter2).toString()] = groupName;
|
||||
}
|
||||
}
|
||||
@ -749,11 +757,13 @@ void InstanceList::instanceDirContentsChanged(const QString& path)
|
||||
emit instancesChanged();
|
||||
}
|
||||
|
||||
void InstanceList::on_InstFolderChanged(const Setting& setting, QVariant value)
|
||||
void InstanceList::on_InstFolderChanged(const Setting &setting, QVariant value)
|
||||
{
|
||||
QString newInstDir = QDir(value.toString()).canonicalPath();
|
||||
if (newInstDir != m_instDir) {
|
||||
if (m_groupsLoaded) {
|
||||
if(newInstDir != m_instDir)
|
||||
{
|
||||
if(m_groupsLoaded)
|
||||
{
|
||||
saveGroupList();
|
||||
}
|
||||
m_instDir = newInstDir;
|
||||
@ -765,7 +775,7 @@ void InstanceList::on_InstFolderChanged(const Setting& setting, QVariant value)
|
||||
void InstanceList::on_GroupStateChanged(const QString& group, bool collapsed)
|
||||
{
|
||||
qDebug() << "Group" << group << (collapsed ? "collapsed" : "expanded");
|
||||
if (collapsed) {
|
||||
if(collapsed) {
|
||||
m_collapsedGroups.insert(group);
|
||||
} else {
|
||||
m_collapsedGroups.remove(group);
|
||||
@ -773,75 +783,89 @@ void InstanceList::on_GroupStateChanged(const QString& group, bool collapsed)
|
||||
saveGroupList();
|
||||
}
|
||||
|
||||
class InstanceStaging : public Task {
|
||||
Q_OBJECT
|
||||
class InstanceStaging : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
const unsigned minBackoff = 1;
|
||||
const unsigned maxBackoff = 16;
|
||||
public:
|
||||
InstanceStaging(InstanceList* parent, InstanceTask* child, QString stagingPath, InstanceName const& instanceName, QString groupName)
|
||||
: m_parent(parent), backoff(minBackoff, maxBackoff), m_stagingPath(std::move(stagingPath)), m_instance_name(std::move(instanceName)), m_groupName(std::move(groupName))
|
||||
public:
|
||||
InstanceStaging (
|
||||
InstanceList * parent,
|
||||
Task * child,
|
||||
const QString & stagingPath,
|
||||
const QString& instanceName,
|
||||
const QString& groupName )
|
||||
: backoff(minBackoff, maxBackoff)
|
||||
{
|
||||
m_parent = parent;
|
||||
m_child.reset(child);
|
||||
connect(child, &Task::succeeded, this, &InstanceStaging::childSucceded);
|
||||
connect(child, &Task::failed, this, &InstanceStaging::childFailed);
|
||||
connect(child, &Task::aborted, this, &InstanceStaging::childAborted);
|
||||
connect(child, &Task::abortStatusChanged, this, &InstanceStaging::setAbortable);
|
||||
connect(child, &Task::status, this, &InstanceStaging::setStatus);
|
||||
connect(child, &Task::progress, this, &InstanceStaging::setProgress);
|
||||
m_instanceName = instanceName;
|
||||
m_groupName = groupName;
|
||||
m_stagingPath = stagingPath;
|
||||
m_backoffTimer.setSingleShot(true);
|
||||
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded);
|
||||
}
|
||||
|
||||
virtual ~InstanceStaging(){};
|
||||
virtual ~InstanceStaging() {};
|
||||
|
||||
|
||||
// FIXME/TODO: add ability to abort during instance commit retries
|
||||
bool abort() override
|
||||
{
|
||||
if (!canAbort())
|
||||
return false;
|
||||
|
||||
m_child->abort();
|
||||
|
||||
return Task::abort();
|
||||
if(m_child && m_child->canAbort())
|
||||
{
|
||||
return m_child->abort();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool canAbort() const override
|
||||
{
|
||||
return (m_child && m_child->canAbort());
|
||||
if(m_child && m_child->canAbort())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void executeTask() override { m_child->start(); }
|
||||
QStringList warnings() const override { return m_child->warnings(); }
|
||||
protected:
|
||||
virtual void executeTask() override
|
||||
{
|
||||
m_child->start();
|
||||
}
|
||||
QStringList warnings() const override
|
||||
{
|
||||
return m_child->warnings();
|
||||
}
|
||||
|
||||
private slots:
|
||||
private slots:
|
||||
void childSucceded()
|
||||
{
|
||||
unsigned sleepTime = backoff();
|
||||
if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, m_child->shouldOverride()))
|
||||
if(m_parent->commitStagedInstance(m_stagingPath, m_instanceName, m_groupName))
|
||||
{
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
// we actually failed, retry?
|
||||
if (sleepTime == maxBackoff) {
|
||||
if(sleepTime == maxBackoff)
|
||||
{
|
||||
emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something."));
|
||||
return;
|
||||
}
|
||||
qDebug() << "Failed to commit instance" << m_instance_name.name() << "Initiating backoff:" << sleepTime;
|
||||
qDebug() << "Failed to commit instance" << m_instanceName << "Initiating backoff:" << sleepTime;
|
||||
m_backoffTimer.start(sleepTime * 500);
|
||||
}
|
||||
void childFailed(const QString& reason)
|
||||
void childFailed(const QString & reason)
|
||||
{
|
||||
m_parent->destroyStagingPath(m_stagingPath);
|
||||
emitFailed(reason);
|
||||
}
|
||||
|
||||
void childAborted()
|
||||
{
|
||||
emitAborted();
|
||||
}
|
||||
|
||||
private:
|
||||
InstanceList * m_parent;
|
||||
/*
|
||||
* WHY: the whole reason why this uses an exponential backoff retry scheme is antivirus on Windows.
|
||||
* Basically, it starts messing things up while the launcher is extracting/creating instances
|
||||
@ -849,18 +873,19 @@ private:
|
||||
*/
|
||||
ExponentialSeries backoff;
|
||||
QString m_stagingPath;
|
||||
unique_qobject_ptr<InstanceTask> m_child;
|
||||
InstanceName m_instance_name;
|
||||
InstanceList * m_parent;
|
||||
unique_qobject_ptr<Task> m_child;
|
||||
QString m_instanceName;
|
||||
QString m_groupName;
|
||||
QTimer m_backoffTimer;
|
||||
};
|
||||
|
||||
Task* InstanceList::wrapInstanceTask(InstanceTask* task)
|
||||
Task * InstanceList::wrapInstanceTask(InstanceTask * task)
|
||||
{
|
||||
auto stagingPath = getStagedInstancePath();
|
||||
task->setStagingPath(stagingPath);
|
||||
task->setParentSettings(m_globalSettings);
|
||||
return new InstanceStaging(this, task, stagingPath, *task, task->group());
|
||||
return new InstanceStaging(this, task, stagingPath, task->name(), task->group());
|
||||
}
|
||||
|
||||
QString InstanceList::getStagedInstancePath()
|
||||
@ -870,7 +895,8 @@ QString InstanceList::getStagedInstancePath()
|
||||
QString relPath = FS::PathCombine(tempDir, key);
|
||||
QDir rootPath(m_instDir);
|
||||
auto path = FS::PathCombine(m_instDir, relPath);
|
||||
if (!rootPath.mkpath(relPath)) {
|
||||
if(!rootPath.mkpath(relPath))
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
#ifdef Q_OS_WIN32
|
||||
@ -880,50 +906,24 @@ QString InstanceList::getStagedInstancePath()
|
||||
return path;
|
||||
}
|
||||
|
||||
bool InstanceList::commitStagedInstance(const QString& path, InstanceName const& instanceName, const QString& groupName, bool should_override)
|
||||
bool InstanceList::commitStagedInstance(const QString& path, const QString& instanceName, const QString& groupName)
|
||||
{
|
||||
QDir dir;
|
||||
QString instID;
|
||||
InstancePtr inst;
|
||||
|
||||
if (should_override) {
|
||||
// This is to avoid problems when the instance folder gets manually renamed
|
||||
if ((inst = getInstanceByManagedName(instanceName.originalName()))) {
|
||||
instID = QFileInfo(inst->instanceRoot()).fileName();
|
||||
} else if ((inst = getInstanceByManagedName(instanceName.modifiedName()))) {
|
||||
instID = QFileInfo(inst->instanceRoot()).fileName();
|
||||
} else {
|
||||
instID = FS::RemoveInvalidFilenameChars(instanceName.modifiedName(), '-');
|
||||
}
|
||||
} else {
|
||||
instID = FS::DirNameFromString(instanceName.modifiedName(), m_instDir);
|
||||
}
|
||||
|
||||
QString instID = FS::DirNameFromString(instanceName, m_instDir);
|
||||
{
|
||||
WatchLock lock(m_watcher, m_instDir);
|
||||
QString destination = FS::PathCombine(m_instDir, instID);
|
||||
|
||||
if (should_override) {
|
||||
if (!FS::overrideFolder(destination, path)) {
|
||||
qWarning() << "Failed to override" << path << "to" << destination;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!dir.rename(path, destination)) {
|
||||
qWarning() << "Failed to move" << path << "to" << destination;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_instanceGroupIndex[instID] = groupName;
|
||||
m_groupNameCache.insert(groupName);
|
||||
if(!dir.rename(path, destination))
|
||||
{
|
||||
qWarning() << "Failed to move" << path << "to" << destination;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_instanceGroupIndex[instID] = groupName;
|
||||
instanceSet.insert(instID);
|
||||
|
||||
m_groupNameCache.insert(groupName);
|
||||
emit instancesChanged();
|
||||
emit instanceSelectRequest(instID);
|
||||
}
|
||||
|
||||
saveGroupList();
|
||||
return true;
|
||||
}
|
||||
@ -933,8 +933,7 @@ bool InstanceList::destroyStagingPath(const QString& keyPath)
|
||||
return FS::deletePath(keyPath);
|
||||
}
|
||||
|
||||
int InstanceList::getTotalPlayTime()
|
||||
{
|
||||
int InstanceList::getTotalPlayTime() {
|
||||
updateTotalPlayTime();
|
||||
return totalPlayTime;
|
||||
}
|
||||
|
@ -19,15 +19,13 @@
|
||||
#include <QAbstractListModel>
|
||||
#include <QSet>
|
||||
#include <QList>
|
||||
#include <QStack>
|
||||
#include <QPair>
|
||||
|
||||
#include "BaseInstance.h"
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
|
||||
class QFileSystemWatcher;
|
||||
class InstanceTask;
|
||||
struct InstanceName;
|
||||
|
||||
using InstanceId = QString;
|
||||
using GroupId = QString;
|
||||
using InstanceLocator = std::pair<InstancePtr, int>;
|
||||
@ -48,12 +46,6 @@ enum class GroupsState
|
||||
Dirty
|
||||
};
|
||||
|
||||
struct TrashHistoryItem {
|
||||
QString id;
|
||||
QString polyPath;
|
||||
QString trashPath;
|
||||
QString groupName;
|
||||
};
|
||||
|
||||
class InstanceList : public QAbstractListModel
|
||||
{
|
||||
@ -101,10 +93,7 @@ public:
|
||||
InstListError loadList();
|
||||
void saveNow();
|
||||
|
||||
/* O(n) */
|
||||
InstancePtr getInstanceById(QString id) const;
|
||||
/* O(n) */
|
||||
InstancePtr getInstanceByManagedName(const QString& managed_name) const;
|
||||
QModelIndex getInstanceIndexById(const QString &id) const;
|
||||
QStringList getGroups();
|
||||
bool isGroupCollapsed(const QString &groupName);
|
||||
@ -113,9 +102,6 @@ public:
|
||||
void setInstanceGroup(const InstanceId & id, const GroupId& name);
|
||||
|
||||
void deleteGroup(const GroupId & name);
|
||||
bool trashInstance(const InstanceId &id);
|
||||
bool trashedSomething();
|
||||
void undoTrashInstance();
|
||||
void deleteInstance(const InstanceId & id);
|
||||
|
||||
// Wrap an instance creation task in some more task machinery and make it ready to be used
|
||||
@ -130,10 +116,8 @@ public:
|
||||
/**
|
||||
* Commit the staging area given by @keyPath to the provider - used when creation succeeds.
|
||||
* 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.
|
||||
@ -196,6 +180,4 @@ private:
|
||||
QSet<InstanceId> instanceSet;
|
||||
bool m_groupsLoaded = false;
|
||||
bool m_instancesProbed = false;
|
||||
|
||||
QStack<TrashHistoryItem> m_trashHistory;
|
||||
};
|
||||
|
@ -37,9 +37,9 @@ public:
|
||||
modsPage->setFilter("%1 (*.zip *.jar *.litemod)");
|
||||
values.append(modsPage);
|
||||
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList()));
|
||||
values.append(new ResourcePackPage(onesix.get(), onesix->resourcePackList()));
|
||||
values.append(new TexturePackPage(onesix.get(), onesix->texturePackList()));
|
||||
values.append(new ShaderPackPage(onesix.get(), onesix->shaderPackList()));
|
||||
values.append(new ResourcePackPage(onesix.get()));
|
||||
values.append(new TexturePackPage(onesix.get()));
|
||||
values.append(new ShaderPackPage(onesix.get()));
|
||||
values.append(new NotesPage(onesix.get()));
|
||||
values.append(new WorldListPage(onesix.get(), onesix->worldList()));
|
||||
values.append(new ServersPage(onesix));
|
||||
|
@ -1,52 +1,9 @@
|
||||
#include "InstanceTask.h"
|
||||
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
|
||||
InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name)
|
||||
InstanceTask::InstanceTask()
|
||||
{
|
||||
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() {}
|
||||
|
@ -1,57 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "tasks/Task.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
|
||||
/* Helpers */
|
||||
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 {
|
||||
class InstanceTask : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
InstanceTask();
|
||||
~InstanceTask() override = default;
|
||||
public:
|
||||
explicit InstanceTask();
|
||||
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; }
|
||||
QString group() const { return m_instGroup; }
|
||||
void setIcon(const QString &icon)
|
||||
{
|
||||
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:
|
||||
void setOverride(bool override) { m_override_existing = override; }
|
||||
|
||||
protected: /* data */
|
||||
protected: /* data */
|
||||
SettingsObjectPtr m_globalSettings;
|
||||
QString m_instName;
|
||||
QString m_instIcon;
|
||||
QString m_instGroup;
|
||||
QString m_stagingPath;
|
||||
|
||||
bool m_override_existing = false;
|
||||
};
|
||||
|
@ -36,7 +36,7 @@
|
||||
#include "JavaCommon.h"
|
||||
#include "java/JavaUtils.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
|
||||
#include <MMCStrings.h>
|
||||
#include <QRegularExpression>
|
||||
|
||||
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
|
||||
|
@ -93,8 +93,8 @@ void LaunchController::decideAccount()
|
||||
auto reply = CustomMessageBox::selectable(
|
||||
m_parentWidget,
|
||||
tr("No Accounts"),
|
||||
tr("In order to play Minecraft, you must have at least one Microsoft or Mojang "
|
||||
"account logged in. Mojang accounts can only be used offline. "
|
||||
tr("In order to play Minecraft, you must have at least one Mojang or Microsoft "
|
||||
"account logged in. "
|
||||
"Would you like to open the account manager to add an account now?"),
|
||||
QMessageBox::Information,
|
||||
QMessageBox::Yes | QMessageBox::No
|
||||
@ -167,7 +167,6 @@ void LaunchController::login() {
|
||||
tries++;
|
||||
m_session = std::make_shared<AuthSession>();
|
||||
m_session->wants_online = m_online;
|
||||
m_session->demo = m_demo;
|
||||
m_accountToUse->fillSession(m_session);
|
||||
|
||||
// Launch immediately in true offline mode
|
||||
@ -185,18 +184,12 @@ void LaunchController::login() {
|
||||
if(!m_session->wants_online) {
|
||||
// we ask the user for a player name
|
||||
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 usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName;
|
||||
QString name = QInputDialog::getText(
|
||||
m_parentWidget,
|
||||
tr("Player name"),
|
||||
message,
|
||||
tr("Choose your offline mode player name."),
|
||||
QLineEdit::Normal,
|
||||
usedname,
|
||||
&ok
|
||||
@ -376,13 +369,13 @@ void LaunchController::launchInstance()
|
||||
}
|
||||
m_launcher->prependStep(new TextPrint(m_launcher.get(), resolved_servers, MessageLevel::Launcher));
|
||||
} 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));
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
@ -63,10 +63,6 @@ public:
|
||||
m_online = online;
|
||||
}
|
||||
|
||||
void setDemo(bool demo) {
|
||||
m_demo = demo;
|
||||
}
|
||||
|
||||
void setProfiler(BaseProfilerFactory *profiler) {
|
||||
m_profiler = profiler;
|
||||
}
|
||||
@ -105,7 +101,6 @@ private slots:
|
||||
private:
|
||||
BaseProfilerFactory *m_profiler = nullptr;
|
||||
bool m_online = true;
|
||||
bool m_demo = false;
|
||||
InstancePtr m_instance;
|
||||
QWidget * m_parentWidget = nullptr;
|
||||
InstanceWindow *m_console = nullptr;
|
||||
|
@ -1,28 +1,26 @@
|
||||
#include "StringUtils.h"
|
||||
|
||||
/// If you're wondering where these came from exactly, then know you're not the only one =D
|
||||
#include "MMCStrings.h"
|
||||
|
||||
/// TAKEN FROM Qt, because it doesn't expose it intelligently
|
||||
static inline QChar getNextChar(const QString& s, int location)
|
||||
static inline QChar getNextChar(const QString &s, int location)
|
||||
{
|
||||
return (location < s.length()) ? s.at(location) : QChar();
|
||||
}
|
||||
|
||||
/// TAKEN FROM Qt, because it doesn't expose it intelligently
|
||||
int StringUtils::naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs)
|
||||
int Strings::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs)
|
||||
{
|
||||
int l1 = 0, l2 = 0;
|
||||
while (l1 <= s1.count() && l2 <= s2.count()) {
|
||||
for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2)
|
||||
{
|
||||
// skip spaces, tabs and 0's
|
||||
QChar c1 = getNextChar(s1, l1);
|
||||
while (c1.isSpace())
|
||||
c1 = getNextChar(s1, ++l1);
|
||||
|
||||
QChar c2 = getNextChar(s2, l2);
|
||||
while (c2.isSpace())
|
||||
c2 = getNextChar(s2, ++l2);
|
||||
|
||||
if (c1.isDigit() && c2.isDigit()) {
|
||||
if (c1.isDigit() && c2.isDigit())
|
||||
{
|
||||
while (c1.digitValue() == 0)
|
||||
c1 = getNextChar(s1, ++l1);
|
||||
while (c2.digitValue() == 0)
|
||||
@ -32,8 +30,11 @@ int StringUtils::naturalCompare(const QString& s1, const QString& s2, Qt::CaseSe
|
||||
int lookAheadLocation2 = l2;
|
||||
int currentReturnValue = 0;
|
||||
// find the last digit, setting currentReturnValue as we go if it isn't equal
|
||||
for (QChar lookAhead1 = c1, lookAhead2 = c2; (lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length());
|
||||
lookAhead1 = getNextChar(s1, ++lookAheadLocation1), lookAhead2 = getNextChar(s2, ++lookAheadLocation2)) {
|
||||
for (QChar lookAhead1 = c1, lookAhead2 = c2;
|
||||
(lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length());
|
||||
lookAhead1 = getNextChar(s1, ++lookAheadLocation1),
|
||||
lookAhead2 = getNextChar(s2, ++lookAheadLocation2))
|
||||
{
|
||||
bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit();
|
||||
bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit();
|
||||
if (!is1ADigit && !is2ADigit)
|
||||
@ -42,10 +43,14 @@ int StringUtils::naturalCompare(const QString& s1, const QString& s2, Qt::CaseSe
|
||||
return -1;
|
||||
if (!is2ADigit)
|
||||
return 1;
|
||||
if (currentReturnValue == 0) {
|
||||
if (lookAhead1 < lookAhead2) {
|
||||
if (currentReturnValue == 0)
|
||||
{
|
||||
if (lookAhead1 < lookAhead2)
|
||||
{
|
||||
currentReturnValue = -1;
|
||||
} else if (lookAhead1 > lookAhead2) {
|
||||
}
|
||||
else if (lookAhead1 > lookAhead2)
|
||||
{
|
||||
currentReturnValue = 1;
|
||||
}
|
||||
}
|
||||
@ -53,24 +58,19 @@ int StringUtils::naturalCompare(const QString& s1, const QString& s2, Qt::CaseSe
|
||||
if (currentReturnValue != 0)
|
||||
return currentReturnValue;
|
||||
}
|
||||
|
||||
if (cs == Qt::CaseInsensitive) {
|
||||
if (cs == Qt::CaseInsensitive)
|
||||
{
|
||||
if (!c1.isLower())
|
||||
c1 = c1.toLower();
|
||||
if (!c2.isLower())
|
||||
c2 = c2.toLower();
|
||||
}
|
||||
|
||||
int r = QString::localeAwareCompare(c1, c2);
|
||||
if (r < 0)
|
||||
return -1;
|
||||
if (r > 0)
|
||||
return 1;
|
||||
|
||||
l1 += 1;
|
||||
l2 += 1;
|
||||
}
|
||||
|
||||
// The two strings are the same (02 == 2) so fall back to the normal sort
|
||||
return QString::compare(s1, s2, cs);
|
||||
}
|
8
launcher/MMCStrings.h
Normal file
8
launcher/MMCStrings.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace Strings
|
||||
{
|
||||
int naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs);
|
||||
}
|
@ -148,7 +148,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
||||
// do not merge disabled mods.
|
||||
if (!mod->enabled())
|
||||
continue;
|
||||
if (mod->type() == ResourceType::ZIPFILE)
|
||||
if (mod->type() == Mod::MOD_ZIPFILE)
|
||||
{
|
||||
if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles))
|
||||
{
|
||||
@ -158,7 +158,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (mod->type() == ResourceType::SINGLEFILE)
|
||||
else if (mod->type() == Mod::MOD_SINGLEFILE)
|
||||
{
|
||||
// FIXME: buggy - does not work with addedFiles
|
||||
auto filename = mod->fileinfo();
|
||||
@ -171,7 +171,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
||||
}
|
||||
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
|
||||
// FIXME: buggy - does not work with addedFiles
|
||||
|
@ -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
|
||||
#include "BaseInstance.h"
|
||||
#include "launch/LaunchTask.h"
|
||||
@ -50,10 +15,6 @@ public:
|
||||
void saveNow() override
|
||||
{
|
||||
}
|
||||
void loadSpecificSettings() override
|
||||
{
|
||||
setSpecificSettingsLoaded(true);
|
||||
}
|
||||
QString getStatusbarDescription() override
|
||||
{
|
||||
return tr("Unknown instance type");
|
||||
@ -82,7 +43,7 @@ public:
|
||||
{
|
||||
return QProcessEnvironment();
|
||||
}
|
||||
QMap<QString, QString> getVariables() override
|
||||
QMap<QString, QString> getVariables() const override
|
||||
{
|
||||
return QMap<QString, QString>();
|
||||
}
|
||||
@ -119,8 +80,4 @@ public:
|
||||
QString modsRoot() const override {
|
||||
return QString();
|
||||
}
|
||||
void updateRuntimeContext()
|
||||
{
|
||||
// NOOP
|
||||
}
|
||||
};
|
||||
|
@ -1,37 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include <functional>
|
||||
#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
|
||||
* Calls deleteLater() instead of destroying the contained object immediately
|
||||
*/
|
||||
template <typename T>
|
||||
using unique_qobject_ptr = QScopedPointer<T, QScopedPointerDeleteLater>;
|
||||
template<typename T> using unique_qobject_ptr = std::unique_ptr<T, details::DeleteQObjectLater>;
|
||||
|
||||
/**
|
||||
* A shared pointer class with shared pointer semantics intended for derivates of QObject
|
||||
* Calls deleteLater() instead of destroying the contained object immediately
|
||||
*/
|
||||
template <typename T>
|
||||
class shared_qobject_ptr : public QSharedPointer<T> {
|
||||
public:
|
||||
constexpr shared_qobject_ptr() : QSharedPointer<T>() {}
|
||||
constexpr shared_qobject_ptr(T* ptr) : QSharedPointer<T>(ptr, &QObject::deleteLater) {}
|
||||
constexpr shared_qobject_ptr(std::nullptr_t null_ptr) : QSharedPointer<T>(null_ptr, &QObject::deleteLater) {}
|
||||
|
||||
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)
|
||||
class shared_qobject_ptr
|
||||
{
|
||||
public:
|
||||
shared_qobject_ptr(){}
|
||||
shared_qobject_ptr(T * wrap)
|
||||
{
|
||||
shared_qobject_ptr<T> t(other);
|
||||
this->swap(t);
|
||||
reset(wrap);
|
||||
}
|
||||
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;
|
||||
}
|
||||
template<typename U>
|
||||
bool operator==(const shared_qobject_ptr<U>& other) const {
|
||||
return m_ptr == other.m_ptr;
|
||||
}
|
||||
template<typename U>
|
||||
bool operator!=(const shared_qobject_ptr<U>& other) const {
|
||||
return m_ptr != other.m_ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr <T> m_ptr;
|
||||
};
|
||||
|
@ -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
|
||||
}
|
||||
};
|
@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace StringUtils {
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
using string = std::wstring;
|
||||
|
||||
inline string toStdString(QString s)
|
||||
{
|
||||
return s.toStdWString();
|
||||
}
|
||||
inline QString fromStdString(string s)
|
||||
{
|
||||
return QString::fromStdWString(s);
|
||||
}
|
||||
#else
|
||||
using string = std::string;
|
||||
|
||||
inline string toStdString(QString s)
|
||||
{
|
||||
return s.toStdString();
|
||||
}
|
||||
inline QString fromStdString(string s)
|
||||
{
|
||||
return QString::fromStdString(s);
|
||||
}
|
||||
#endif
|
||||
|
||||
int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs);
|
||||
} // namespace StringUtils
|
@ -358,7 +358,7 @@ void UpdateController::fail()
|
||||
msg = QObject::tr(
|
||||
"Couldn't replace file %1. Changes will be reverted.\n"
|
||||
"See the %2 log file for details."
|
||||
).arg(m_failedFile, BuildConfig.LAUNCHER_DISPLAYNAME);
|
||||
).arg(m_failedFile, BuildConfig.LAUNCHER_NAME);
|
||||
doRollback = true;
|
||||
QMessageBox::critical(m_parent, failTitle, msg);
|
||||
break;
|
||||
@ -368,7 +368,7 @@ void UpdateController::fail()
|
||||
msg = QObject::tr(
|
||||
"Couldn't remove file %1. Changes will be reverted.\n"
|
||||
"See the %2 log file for details."
|
||||
).arg(m_failedFile, BuildConfig.LAUNCHER_DISPLAYNAME);
|
||||
).arg(m_failedFile, BuildConfig.LAUNCHER_NAME);
|
||||
doRollback = true;
|
||||
QMessageBox::critical(m_parent, failTitle, msg);
|
||||
break;
|
||||
@ -399,7 +399,7 @@ void UpdateController::fail()
|
||||
{
|
||||
msg = QObject::tr("The rollback failed too.\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);
|
||||
qApp->quit();
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <TestUtil.h>
|
||||
#include "TestUtil.h"
|
||||
#include <Version.h>
|
||||
|
||||
class ModUtilsTest : public QObject
|
@ -1,10 +1,9 @@
|
||||
#include "JavaInstall.h"
|
||||
|
||||
#include "StringUtils.h"
|
||||
#include <MMCStrings.h>
|
||||
|
||||
bool JavaInstall::operator<(const JavaInstall &rhs)
|
||||
{
|
||||
auto archCompare = StringUtils::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive);
|
||||
auto archCompare = Strings::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive);
|
||||
if(archCompare != 0)
|
||||
return archCompare < 0;
|
||||
if(id < rhs.id)
|
||||
@ -15,7 +14,7 @@ bool JavaInstall::operator<(const JavaInstall &rhs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return StringUtils::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0;
|
||||
return Strings::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0;
|
||||
}
|
||||
|
||||
bool JavaInstall::operator==(const JavaInstall &rhs)
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "java/JavaInstallList.h"
|
||||
#include "java/JavaCheckerJob.h"
|
||||
#include "java/JavaUtils.h"
|
||||
#include "MMCStrings.h"
|
||||
#include "minecraft/VersionFilterData.h"
|
||||
|
||||
JavaInstallList::JavaInstallList(QObject *parent) : BaseVersionList(parent)
|
||||
@ -72,7 +73,7 @@ void JavaInstallList::load()
|
||||
}
|
||||
}
|
||||
|
||||
const BaseVersion::Ptr JavaInstallList::at(int i) const
|
||||
const BaseVersionPtr JavaInstallList::at(int i) const
|
||||
{
|
||||
return m_vlist.at(i);
|
||||
}
|
||||
@ -121,7 +122,7 @@ BaseVersionList::RoleList JavaInstallList::providesRoles() const
|
||||
}
|
||||
|
||||
|
||||
void JavaInstallList::updateListData(QList<BaseVersion::Ptr> versions)
|
||||
void JavaInstallList::updateListData(QList<BaseVersionPtr> versions)
|
||||
{
|
||||
beginResetModel();
|
||||
m_vlist = versions;
|
||||
@ -136,7 +137,7 @@ void JavaInstallList::updateListData(QList<BaseVersion::Ptr> versions)
|
||||
m_loadTask.reset();
|
||||
}
|
||||
|
||||
bool sortJavas(BaseVersion::Ptr left, BaseVersion::Ptr right)
|
||||
bool sortJavas(BaseVersionPtr left, BaseVersionPtr right)
|
||||
{
|
||||
auto rleft = std::dynamic_pointer_cast<JavaInstall>(right);
|
||||
auto rright = std::dynamic_pointer_cast<JavaInstall>(left);
|
||||
@ -209,11 +210,11 @@ void JavaListLoadTask::javaCheckerFinished()
|
||||
}
|
||||
}
|
||||
|
||||
QList<BaseVersion::Ptr> javas_bvp;
|
||||
QList<BaseVersionPtr> javas_bvp;
|
||||
for (auto java : candidates)
|
||||
{
|
||||
//qDebug() << java->id << java->arch << " at " << java->path;
|
||||
BaseVersion::Ptr bp_java = std::dynamic_pointer_cast<BaseVersion>(java);
|
||||
BaseVersionPtr bp_java = std::dynamic_pointer_cast<BaseVersion>(java);
|
||||
|
||||
if (bp_java)
|
||||
{
|
||||
|
@ -42,7 +42,7 @@ public:
|
||||
|
||||
Task::Ptr getLoadTask() override;
|
||||
bool isLoaded() override;
|
||||
const BaseVersion::Ptr at(int i) const override;
|
||||
const BaseVersionPtr at(int i) const override;
|
||||
int count() const override;
|
||||
void sortVersions() override;
|
||||
|
||||
@ -50,7 +50,7 @@ public:
|
||||
RoleList providesRoles() const override;
|
||||
|
||||
public slots:
|
||||
void updateListData(QList<BaseVersion::Ptr> versions) override;
|
||||
void updateListData(QList<BaseVersionPtr> versions) override;
|
||||
|
||||
protected:
|
||||
void load();
|
||||
@ -59,7 +59,7 @@ protected:
|
||||
protected:
|
||||
Status m_status = Status::NotDone;
|
||||
shared_qobject_ptr<JavaListLoadTask> m_loadTask;
|
||||
QList<BaseVersion::Ptr> m_vlist;
|
||||
QList<BaseVersionPtr> m_vlist;
|
||||
};
|
||||
|
||||
class JavaListLoadTask : public Task
|
||||
|
@ -174,17 +174,11 @@ JavaInstallPtr JavaUtils::GetDefaultJava()
|
||||
|
||||
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)
|
||||
QList<QString> javaPaths = env.replace("\\", "/").split(QLatin1String(";"));
|
||||
|
||||
auto envPath = qEnvironmentVariable("PATH");
|
||||
QList<QString> javaPathsfromPath = envPath.replace("\\", "/").split(QLatin1String(";"));
|
||||
for (QString string : javaPathsfromPath) {
|
||||
javaPaths.append(string + "/javaw.exe");
|
||||
}
|
||||
QList<QString> javaPaths = QString::fromLocal8Bit(env).replace("\\", "/").split(QLatin1String(";"));
|
||||
#else
|
||||
QList<QString> javaPaths = env.split(QLatin1String(":"));
|
||||
QList<QString> javaPaths = QString::fromLocal8Bit(env).split(QLatin1String(":"));
|
||||
#endif
|
||||
for(QString i : javaPaths)
|
||||
{
|
||||
@ -379,9 +373,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
}
|
||||
}
|
||||
|
||||
candidates = addJavasFromEnv(candidates);
|
||||
candidates.removeDuplicates();
|
||||
return candidates;
|
||||
return addJavasFromEnv(candidates);
|
||||
}
|
||||
|
||||
#elif defined(Q_OS_MAC)
|
||||
@ -404,9 +396,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
|
||||
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java");
|
||||
}
|
||||
javas = addJavasFromEnv(javas);
|
||||
javas.removeDuplicates();
|
||||
return javas;
|
||||
return addJavasFromEnv(javas);
|
||||
}
|
||||
|
||||
#elif defined(Q_OS_LINUX)
|
||||
@ -445,16 +435,14 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
scanJavaDir("/usr/lib/jvm");
|
||||
scanJavaDir("/usr/lib64/jvm");
|
||||
scanJavaDir("/usr/lib32/jvm");
|
||||
// javas stored in Prism Launcher's folder
|
||||
// javas stored in PolyMC's folder
|
||||
scanJavaDir("java");
|
||||
// manually installed JDKs in /opt
|
||||
scanJavaDir("/opt/jdk");
|
||||
scanJavaDir("/opt/jdks");
|
||||
// flatpak
|
||||
scanJavaDir("/app/jdk");
|
||||
javas = addJavasFromEnv(javas);
|
||||
javas.removeDuplicates();
|
||||
return javas;
|
||||
return addJavasFromEnv(javas);
|
||||
}
|
||||
#else
|
||||
QList<QString> JavaUtils::FindJavaPaths()
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "JavaVersion.h"
|
||||
|
||||
#include "StringUtils.h"
|
||||
#include <MMCStrings.h>
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QString>
|
||||
@ -99,12 +98,12 @@ bool JavaVersion::operator<(const JavaVersion &rhs)
|
||||
else if(thisPre && rhsPre)
|
||||
{
|
||||
// both are prereleases - use natural compare...
|
||||
return StringUtils::naturalCompare(m_prerelease, rhs.m_prerelease, Qt::CaseSensitive) < 0;
|
||||
return Strings::naturalCompare(m_prerelease, rhs.m_prerelease, Qt::CaseSensitive) < 0;
|
||||
}
|
||||
// neither is prerelease, so they are the same -> this cannot be less than rhs
|
||||
return false;
|
||||
}
|
||||
else return StringUtils::naturalCompare(m_string, rhs.m_string, Qt::CaseSensitive) < 0;
|
||||
else return Strings::naturalCompare(m_string, rhs.m_string, Qt::CaseSensitive) < 0;
|
||||
}
|
||||
|
||||
bool JavaVersion::operator==(const JavaVersion &rhs)
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include <QTest>
|
||||
|
||||
#include <java/JavaVersion.h>
|
||||
#include "java/JavaVersion.h"
|
||||
|
||||
class JavaVersionTest : public QObject
|
||||
{
|
@ -37,6 +37,7 @@
|
||||
|
||||
#include "launch/LaunchTask.h"
|
||||
#include "MessageLevel.h"
|
||||
#include "MMCStrings.h"
|
||||
#include "java/JavaChecker.h"
|
||||
#include "tasks/Task.h"
|
||||
#include <QDebug>
|
||||
|
@ -121,6 +121,7 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
|
||||
emit logLine(QString("Could not start java:"), MessageLevel::Error);
|
||||
emit logLines(result.errorLog.split('\n'), MessageLevel::Error);
|
||||
emit logLine(QString("\nCheck your Java settings."), MessageLevel::Launcher);
|
||||
printSystemInfo(false, false);
|
||||
emitFailed(QString("Could not start java!"));
|
||||
return;
|
||||
}
|
||||
@ -129,6 +130,7 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
|
||||
emit logLine(QString("Java checker returned some invalid data we don't understand:"), MessageLevel::Error);
|
||||
emit logLines(result.outLog.split('\n'), MessageLevel::Warning);
|
||||
emit logLine("\nMinecraft might not start properly.", MessageLevel::Launcher);
|
||||
printSystemInfo(false, false);
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
@ -136,6 +138,7 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
|
||||
{
|
||||
auto instance = m_parent->instance();
|
||||
printJavaInfo(result.javaVersion.toString(), result.mojangPlatform, result.realPlatform, result.javaVendor);
|
||||
printSystemInfo(true, result.is_64bit);
|
||||
instance->settings()->set("JavaVersion", result.javaVersion.toString());
|
||||
instance->settings()->set("JavaArchitecture", result.mojangPlatform);
|
||||
instance->settings()->set("JavaRealArchitecture", result.realPlatform);
|
||||
@ -152,3 +155,20 @@ void CheckJava::printJavaInfo(const QString& version, const QString& architectur
|
||||
emit logLine(QString("Java is version %1, using %2 (%3) architecture, from %4.\n\n")
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ int main(int argc, char *argv[])
|
||||
Q_INIT_RESOURCE(multimc);
|
||||
Q_INIT_RESOURCE(backgrounds);
|
||||
Q_INIT_RESOURCE(documents);
|
||||
Q_INIT_RESOURCE(prismlauncher);
|
||||
Q_INIT_RESOURCE(polymc);
|
||||
|
||||
Q_INIT_RESOURCE(pe_dark);
|
||||
Q_INIT_RESOURCE(pe_light);
|
||||
@ -90,7 +90,5 @@ int main(int argc, char *argv[])
|
||||
return 1;
|
||||
case Application::Succeeded:
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ Index::Index(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
Index::Index(const QVector<VersionList::Ptr> &lists, QObject *parent)
|
||||
Index::Index(const QVector<VersionListPtr> &lists, QObject *parent)
|
||||
: QAbstractListModel(parent), m_lists(lists)
|
||||
{
|
||||
for (int i = 0; i < m_lists.size(); ++i)
|
||||
@ -41,7 +41,7 @@ QVariant Index::data(const QModelIndex &index, int role) const
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
VersionList::Ptr list = m_lists.at(index.row());
|
||||
VersionListPtr list = m_lists.at(index.row());
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
@ -81,9 +81,9 @@ bool Index::hasUid(const QString &uid) const
|
||||
return m_uids.contains(uid);
|
||||
}
|
||||
|
||||
VersionList::Ptr Index::get(const QString &uid)
|
||||
VersionListPtr Index::get(const QString &uid)
|
||||
{
|
||||
VersionList::Ptr out = m_uids.value(uid, nullptr);
|
||||
VersionListPtr out = m_uids.value(uid, nullptr);
|
||||
if(!out)
|
||||
{
|
||||
out = std::make_shared<VersionList>(uid);
|
||||
@ -92,7 +92,7 @@ VersionList::Ptr Index::get(const QString &uid)
|
||||
return out;
|
||||
}
|
||||
|
||||
Version::Ptr Index::get(const QString &uid, const QString &version)
|
||||
VersionPtr Index::get(const QString &uid, const QString &version)
|
||||
{
|
||||
auto list = get(uid);
|
||||
return list->getVersion(version);
|
||||
@ -105,7 +105,7 @@ void Index::parse(const QJsonObject& obj)
|
||||
|
||||
void Index::merge(const std::shared_ptr<Index> &other)
|
||||
{
|
||||
const QVector<VersionList::Ptr> lists = std::dynamic_pointer_cast<Index>(other)->m_lists;
|
||||
const QVector<VersionListPtr> lists = std::dynamic_pointer_cast<Index>(other)->m_lists;
|
||||
// initial load, no need to merge
|
||||
if (m_lists.isEmpty())
|
||||
{
|
||||
@ -120,7 +120,7 @@ void Index::merge(const std::shared_ptr<Index> &other)
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const VersionList::Ptr &list : lists)
|
||||
for (const VersionListPtr &list : lists)
|
||||
{
|
||||
if (m_uids.contains(list->uid()))
|
||||
{
|
||||
@ -138,7 +138,7 @@ void Index::merge(const std::shared_ptr<Index> &other)
|
||||
}
|
||||
}
|
||||
|
||||
void Index::connectVersionList(const int row, const VersionList::Ptr &list)
|
||||
void Index::connectVersionList(const int row, const VersionListPtr &list)
|
||||
{
|
||||
connect(list.get(), &VersionList::nameChanged, this, [this, row]()
|
||||
{
|
||||
|
@ -19,19 +19,20 @@
|
||||
#include <memory>
|
||||
|
||||
#include "BaseEntity.h"
|
||||
#include "meta/VersionList.h"
|
||||
|
||||
class Task;
|
||||
|
||||
namespace Meta
|
||||
{
|
||||
using VersionListPtr = std::shared_ptr<class VersionList>;
|
||||
using VersionPtr = std::shared_ptr<class Version>;
|
||||
|
||||
class Index : public QAbstractListModel, public BaseEntity
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Index(QObject *parent = nullptr);
|
||||
explicit Index(const QVector<VersionList::Ptr> &lists, QObject *parent = nullptr);
|
||||
explicit Index(const QVector<VersionListPtr> &lists, QObject *parent = nullptr);
|
||||
|
||||
enum
|
||||
{
|
||||
@ -48,21 +49,21 @@ public:
|
||||
QString localFilename() const override { return "index.json"; }
|
||||
|
||||
// queries
|
||||
VersionList::Ptr get(const QString &uid);
|
||||
Version::Ptr get(const QString &uid, const QString &version);
|
||||
VersionListPtr get(const QString &uid);
|
||||
VersionPtr get(const QString &uid, const QString &version);
|
||||
bool hasUid(const QString &uid) const;
|
||||
|
||||
QVector<VersionList::Ptr> lists() const { return m_lists; }
|
||||
QVector<VersionListPtr> lists() const { return m_lists; }
|
||||
|
||||
public: // for usage by parsers only
|
||||
void merge(const std::shared_ptr<Index> &other);
|
||||
void parse(const QJsonObject &obj) override;
|
||||
|
||||
private:
|
||||
QVector<VersionList::Ptr> m_lists;
|
||||
QHash<QString, VersionList::Ptr> m_uids;
|
||||
QVector<VersionListPtr> m_lists;
|
||||
QHash<QString, VersionListPtr> m_uids;
|
||||
|
||||
void connectVersionList(const int row, const VersionList::Ptr &list);
|
||||
void connectVersionList(const int row, const VersionListPtr &list);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include <QTest>
|
||||
|
||||
#include <meta/Index.h>
|
||||
#include <meta/VersionList.h>
|
||||
#include "meta/Index.h"
|
||||
#include "meta/VersionList.h"
|
||||
|
||||
class IndexTest : public QObject
|
||||
{
|
@ -37,11 +37,11 @@ MetadataVersion currentFormatVersion()
|
||||
static std::shared_ptr<Index> parseIndexInternal(const QJsonObject &obj)
|
||||
{
|
||||
const QVector<QJsonObject> objects = requireIsArrayOf<QJsonObject>(obj, "packages");
|
||||
QVector<VersionList::Ptr> lists;
|
||||
QVector<VersionListPtr> lists;
|
||||
lists.reserve(objects.size());
|
||||
std::transform(objects.begin(), objects.end(), std::back_inserter(lists), [](const QJsonObject &obj)
|
||||
{
|
||||
VersionList::Ptr list = std::make_shared<VersionList>(requireString(obj, "uid"));
|
||||
VersionListPtr list = std::make_shared<VersionList>(requireString(obj, "uid"));
|
||||
list->setName(ensureString(obj, "name", QString()));
|
||||
return list;
|
||||
});
|
||||
@ -49,9 +49,9 @@ static std::shared_ptr<Index> parseIndexInternal(const QJsonObject &obj)
|
||||
}
|
||||
|
||||
// Version
|
||||
static Version::Ptr parseCommonVersion(const QString &uid, const QJsonObject &obj)
|
||||
static VersionPtr parseCommonVersion(const QString &uid, const QJsonObject &obj)
|
||||
{
|
||||
Version::Ptr version = std::make_shared<Version>(uid, requireString(obj, "version"));
|
||||
VersionPtr version = std::make_shared<Version>(uid, requireString(obj, "version"));
|
||||
version->setTime(QDateTime::fromString(requireString(obj, "releaseTime"), Qt::ISODate).toMSecsSinceEpoch() / 1000);
|
||||
version->setType(ensureString(obj, "type", QString()));
|
||||
version->setRecommended(ensureBoolean(obj, QString("recommended"), false));
|
||||
@ -63,9 +63,9 @@ static Version::Ptr parseCommonVersion(const QString &uid, const QJsonObject &ob
|
||||
return version;
|
||||
}
|
||||
|
||||
static Version::Ptr parseVersionInternal(const QJsonObject &obj)
|
||||
static std::shared_ptr<Version> parseVersionInternal(const QJsonObject &obj)
|
||||
{
|
||||
Version::Ptr version = parseCommonVersion(requireString(obj, "uid"), obj);
|
||||
VersionPtr version = parseCommonVersion(requireString(obj, "uid"), obj);
|
||||
|
||||
version->setData(OneSixVersionFormat::versionFileFromJson(QJsonDocument(obj),
|
||||
QString("%1/%2.json").arg(version->uid(), version->version()),
|
||||
@ -74,12 +74,12 @@ static Version::Ptr parseVersionInternal(const QJsonObject &obj)
|
||||
}
|
||||
|
||||
// Version list / package
|
||||
static VersionList::Ptr parseVersionListInternal(const QJsonObject &obj)
|
||||
static std::shared_ptr<VersionList> parseVersionListInternal(const QJsonObject &obj)
|
||||
{
|
||||
const QString uid = requireString(obj, "uid");
|
||||
|
||||
const QVector<QJsonObject> versionsRaw = requireIsArrayOf<QJsonObject>(obj, "versions");
|
||||
QVector<Version::Ptr> versions;
|
||||
QVector<VersionPtr> versions;
|
||||
versions.reserve(versionsRaw.size());
|
||||
std::transform(versionsRaw.begin(), versionsRaw.end(), std::back_inserter(versions), [uid](const QJsonObject &vObj)
|
||||
{
|
||||
@ -88,7 +88,7 @@ static VersionList::Ptr parseVersionListInternal(const QJsonObject &obj)
|
||||
return version;
|
||||
});
|
||||
|
||||
VersionList::Ptr list = std::make_shared<VersionList>(uid);
|
||||
VersionListPtr list = std::make_shared<VersionList>(uid);
|
||||
list->setName(ensureString(obj, "name", QString()));
|
||||
list->setVersions(versions);
|
||||
return list;
|
||||
|
@ -54,7 +54,7 @@ void Meta::Version::parse(const QJsonObject& obj)
|
||||
parseVersion(obj, this);
|
||||
}
|
||||
|
||||
void Meta::Version::mergeFromList(const Meta::Version::Ptr& other)
|
||||
void Meta::Version::mergeFromList(const Meta::VersionPtr& other)
|
||||
{
|
||||
if(other->m_providesRecommendations)
|
||||
{
|
||||
@ -85,7 +85,7 @@ void Meta::Version::mergeFromList(const Meta::Version::Ptr& other)
|
||||
}
|
||||
}
|
||||
|
||||
void Meta::Version::merge(const Version::Ptr &other)
|
||||
void Meta::Version::merge(const VersionPtr &other)
|
||||
{
|
||||
mergeFromList(other);
|
||||
if(other->m_data)
|
||||
|
@ -30,14 +30,13 @@
|
||||
|
||||
namespace Meta
|
||||
{
|
||||
using VersionPtr = std::shared_ptr<class Version>;
|
||||
|
||||
class Version : public QObject, public BaseVersion, public BaseEntity
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using Ptr = std::shared_ptr<Version>;
|
||||
|
||||
public: /* con/des */
|
||||
explicit Version(const QString &uid, const QString &version);
|
||||
virtual ~Version();
|
||||
|
||||
@ -79,8 +78,8 @@ public:
|
||||
return m_data != nullptr;
|
||||
}
|
||||
|
||||
void merge(const Version::Ptr &other);
|
||||
void mergeFromList(const Version::Ptr &other);
|
||||
void merge(const VersionPtr &other);
|
||||
void mergeFromList(const VersionPtr &other);
|
||||
void parse(const QJsonObject &obj) override;
|
||||
|
||||
QString localFilename() const override;
|
||||
@ -114,4 +113,4 @@ private:
|
||||
};
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(Meta::Version::Ptr)
|
||||
Q_DECLARE_METATYPE(Meta::VersionPtr)
|
||||
|
@ -40,7 +40,7 @@ bool VersionList::isLoaded()
|
||||
return BaseEntity::isLoaded();
|
||||
}
|
||||
|
||||
const BaseVersion::Ptr VersionList::at(int i) const
|
||||
const BaseVersionPtr VersionList::at(int i) const
|
||||
{
|
||||
return m_versions.at(i);
|
||||
}
|
||||
@ -52,7 +52,7 @@ int VersionList::count() const
|
||||
void VersionList::sortVersions()
|
||||
{
|
||||
beginResetModel();
|
||||
std::sort(m_versions.begin(), m_versions.end(), [](const Version::Ptr &a, const Version::Ptr &b)
|
||||
std::sort(m_versions.begin(), m_versions.end(), [](const VersionPtr &a, const VersionPtr &b)
|
||||
{
|
||||
return *a.get() < *b.get();
|
||||
});
|
||||
@ -66,7 +66,7 @@ QVariant VersionList::data(const QModelIndex &index, int role) const
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
Version::Ptr version = m_versions.at(index.row());
|
||||
VersionPtr version = m_versions.at(index.row());
|
||||
|
||||
switch (role)
|
||||
{
|
||||
@ -129,9 +129,9 @@ QString VersionList::humanReadable() const
|
||||
return m_name.isEmpty() ? m_uid : m_name;
|
||||
}
|
||||
|
||||
Version::Ptr VersionList::getVersion(const QString &version)
|
||||
VersionPtr VersionList::getVersion(const QString &version)
|
||||
{
|
||||
Version::Ptr out = m_lookup.value(version, nullptr);
|
||||
VersionPtr out = m_lookup.value(version, nullptr);
|
||||
if(!out)
|
||||
{
|
||||
out = std::make_shared<Version>(m_uid, version);
|
||||
@ -143,7 +143,7 @@ Version::Ptr VersionList::getVersion(const QString &version)
|
||||
bool VersionList::hasVersion(QString version) const
|
||||
{
|
||||
auto ver = std::find_if(m_versions.constBegin(), m_versions.constEnd(),
|
||||
[&](Meta::Version::Ptr const& a){ return a->version() == version; });
|
||||
[&](Meta::VersionPtr const& a){ return a->version() == version; });
|
||||
return (ver != m_versions.constEnd());
|
||||
}
|
||||
|
||||
@ -153,11 +153,11 @@ void VersionList::setName(const QString &name)
|
||||
emit nameChanged(name);
|
||||
}
|
||||
|
||||
void VersionList::setVersions(const QVector<Version::Ptr> &versions)
|
||||
void VersionList::setVersions(const QVector<VersionPtr> &versions)
|
||||
{
|
||||
beginResetModel();
|
||||
m_versions = versions;
|
||||
std::sort(m_versions.begin(), m_versions.end(), [](const Version::Ptr &a, const Version::Ptr &b)
|
||||
std::sort(m_versions.begin(), m_versions.end(), [](const VersionPtr &a, const VersionPtr &b)
|
||||
{
|
||||
return a->rawTime() > b->rawTime();
|
||||
});
|
||||
@ -168,7 +168,7 @@ void VersionList::setVersions(const QVector<Version::Ptr> &versions)
|
||||
}
|
||||
|
||||
// FIXME: this is dumb, we have 'recommended' as part of the metadata already...
|
||||
auto recommendedIt = std::find_if(m_versions.constBegin(), m_versions.constEnd(), [](const Version::Ptr &ptr) { return ptr->type() == "release"; });
|
||||
auto recommendedIt = std::find_if(m_versions.constBegin(), m_versions.constEnd(), [](const VersionPtr &ptr) { return ptr->type() == "release"; });
|
||||
m_recommended = recommendedIt == m_versions.constEnd() ? nullptr : *recommendedIt;
|
||||
endResetModel();
|
||||
}
|
||||
@ -179,7 +179,7 @@ void VersionList::parse(const QJsonObject& obj)
|
||||
}
|
||||
|
||||
// FIXME: this is dumb, we have 'recommended' as part of the metadata already...
|
||||
static const Meta::Version::Ptr &getBetterVersion(const Meta::Version::Ptr &a, const Meta::Version::Ptr &b)
|
||||
static const Meta::VersionPtr &getBetterVersion(const Meta::VersionPtr &a, const Meta::VersionPtr &b)
|
||||
{
|
||||
if(!a)
|
||||
return b;
|
||||
@ -194,7 +194,7 @@ static const Meta::Version::Ptr &getBetterVersion(const Meta::Version::Ptr &a, c
|
||||
return (a->type() == "release" ? a : b);
|
||||
}
|
||||
|
||||
void VersionList::mergeFromIndex(const VersionList::Ptr &other)
|
||||
void VersionList::mergeFromIndex(const VersionListPtr &other)
|
||||
{
|
||||
if (m_name != other->m_name)
|
||||
{
|
||||
@ -202,7 +202,7 @@ void VersionList::mergeFromIndex(const VersionList::Ptr &other)
|
||||
}
|
||||
}
|
||||
|
||||
void VersionList::merge(const VersionList::Ptr &other)
|
||||
void VersionList::merge(const VersionListPtr &other)
|
||||
{
|
||||
if (m_name != other->m_name)
|
||||
{
|
||||
@ -216,7 +216,7 @@ void VersionList::merge(const VersionList::Ptr &other)
|
||||
{
|
||||
qWarning() << "Empty list loaded ...";
|
||||
}
|
||||
for (const Version::Ptr &version : other->m_versions)
|
||||
for (const VersionPtr &version : other->m_versions)
|
||||
{
|
||||
// we already have the version. merge the contents
|
||||
if (m_lookup.contains(version->version()))
|
||||
@ -235,7 +235,7 @@ void VersionList::merge(const VersionList::Ptr &other)
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void VersionList::setupAddedVersion(const int row, const Version::Ptr &version)
|
||||
void VersionList::setupAddedVersion(const int row, const VersionPtr &version)
|
||||
{
|
||||
// FIXME: do not disconnect from everythin, disconnect only the lambdas here
|
||||
version->disconnect();
|
||||
@ -244,7 +244,7 @@ void VersionList::setupAddedVersion(const int row, const Version::Ptr &version)
|
||||
connect(version.get(), &Version::typeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << TypeRole); });
|
||||
}
|
||||
|
||||
BaseVersion::Ptr VersionList::getRecommended() const
|
||||
BaseVersionPtr VersionList::getRecommended() const
|
||||
{
|
||||
return m_recommended;
|
||||
}
|
||||
|
@ -20,10 +20,10 @@
|
||||
#include <QJsonObject>
|
||||
#include <memory>
|
||||
|
||||
#include "meta/Version.h"
|
||||
|
||||
namespace Meta
|
||||
{
|
||||
using VersionPtr = std::shared_ptr<class Version>;
|
||||
using VersionListPtr = std::shared_ptr<class VersionList>;
|
||||
|
||||
class VersionList : public BaseVersionList, public BaseEntity
|
||||
{
|
||||
@ -33,8 +33,6 @@ class VersionList : public BaseVersionList, public BaseEntity
|
||||
public:
|
||||
explicit VersionList(const QString &uid, QObject *parent = nullptr);
|
||||
|
||||
using Ptr = std::shared_ptr<VersionList>;
|
||||
|
||||
enum Roles
|
||||
{
|
||||
UidRole = Qt::UserRole + 100,
|
||||
@ -45,11 +43,11 @@ public:
|
||||
|
||||
Task::Ptr getLoadTask() override;
|
||||
bool isLoaded() override;
|
||||
const BaseVersion::Ptr at(int i) const override;
|
||||
const BaseVersionPtr at(int i) const override;
|
||||
int count() const override;
|
||||
void sortVersions() override;
|
||||
|
||||
BaseVersion::Ptr getRecommended() const override;
|
||||
BaseVersionPtr getRecommended() const override;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
RoleList providesRoles() const override;
|
||||
@ -67,38 +65,38 @@ public:
|
||||
}
|
||||
QString humanReadable() const;
|
||||
|
||||
Version::Ptr getVersion(const QString &version);
|
||||
VersionPtr getVersion(const QString &version);
|
||||
bool hasVersion(QString version) const;
|
||||
|
||||
QVector<Version::Ptr> versions() const
|
||||
QVector<VersionPtr> versions() const
|
||||
{
|
||||
return m_versions;
|
||||
}
|
||||
|
||||
public: // for usage only by parsers
|
||||
void setName(const QString &name);
|
||||
void setVersions(const QVector<Version::Ptr> &versions);
|
||||
void merge(const VersionList::Ptr &other);
|
||||
void mergeFromIndex(const VersionList::Ptr &other);
|
||||
void setVersions(const QVector<VersionPtr> &versions);
|
||||
void merge(const VersionListPtr &other);
|
||||
void mergeFromIndex(const VersionListPtr &other);
|
||||
void parse(const QJsonObject &obj) override;
|
||||
|
||||
signals:
|
||||
void nameChanged(const QString &name);
|
||||
|
||||
protected slots:
|
||||
void updateListData(QList<BaseVersion::Ptr>) override
|
||||
void updateListData(QList<BaseVersionPtr>) override
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
QVector<Version::Ptr> m_versions;
|
||||
QHash<QString, Version::Ptr> m_lookup;
|
||||
QVector<VersionPtr> m_versions;
|
||||
QHash<QString, VersionPtr> m_lookup;
|
||||
QString m_uid;
|
||||
QString m_name;
|
||||
|
||||
Version::Ptr m_recommended;
|
||||
VersionPtr m_recommended;
|
||||
|
||||
void setupAddedVersion(const int row, const Version::Ptr &version);
|
||||
void setupAddedVersion(const int row, const VersionPtr &version);
|
||||
};
|
||||
}
|
||||
Q_DECLARE_METATYPE(Meta::VersionList::Ptr)
|
||||
Q_DECLARE_METATYPE(Meta::VersionListPtr)
|
||||
|
@ -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/Index.h>
|
||||
#include "Component.h"
|
||||
@ -95,7 +60,7 @@ void Component::applyTo(LaunchProfile* profile)
|
||||
auto vfile = getVersionFile();
|
||||
if(vfile)
|
||||
{
|
||||
vfile->applyTo(profile, m_parent->runtimeContext());
|
||||
vfile->applyTo(profile);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include <QTest>
|
||||
|
||||
#include <minecraft/GradleSpecifier.h>
|
||||
#include "minecraft/GradleSpecifier.h"
|
||||
|
||||
class GradleSpecifierTest : public QObject
|
||||
{
|
@ -173,9 +173,9 @@ void LaunchProfile::applyCompatibleJavaMajors(QList<int>& javaMajor)
|
||||
m_compatibleJavaMajors.append(javaMajor);
|
||||
}
|
||||
|
||||
void LaunchProfile::applyLibrary(LibraryPtr library, const RuntimeContext & runtimeContext)
|
||||
void LaunchProfile::applyLibrary(LibraryPtr library)
|
||||
{
|
||||
if(!library->isActive(runtimeContext))
|
||||
if(!library->isActive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -205,9 +205,9 @@ void LaunchProfile::applyLibrary(LibraryPtr library, const RuntimeContext & runt
|
||||
}
|
||||
}
|
||||
|
||||
void LaunchProfile::applyMavenFile(LibraryPtr mavenFile, const RuntimeContext & runtimeContext)
|
||||
void LaunchProfile::applyMavenFile(LibraryPtr mavenFile)
|
||||
{
|
||||
if(!mavenFile->isActive(runtimeContext))
|
||||
if(!mavenFile->isActive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -221,10 +221,10 @@ void LaunchProfile::applyMavenFile(LibraryPtr mavenFile, const RuntimeContext &
|
||||
m_mavenFiles.append(Library::limitedCopy(mavenFile));
|
||||
}
|
||||
|
||||
void LaunchProfile::applyAgent(AgentPtr agent, const RuntimeContext & runtimeContext)
|
||||
void LaunchProfile::applyAgent(AgentPtr agent)
|
||||
{
|
||||
auto lib = agent->library();
|
||||
if(!lib->isActive(runtimeContext))
|
||||
if(!lib->isActive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -354,7 +354,7 @@ const QList<int> & LaunchProfile::getCompatibleJavaMajors() const
|
||||
}
|
||||
|
||||
void LaunchProfile::getLibraryFiles(
|
||||
const RuntimeContext & runtimeContext,
|
||||
const QString& architecture,
|
||||
QStringList& jars,
|
||||
QStringList& nativeJars,
|
||||
const QString& overridePath,
|
||||
@ -366,7 +366,7 @@ void LaunchProfile::getLibraryFiles(
|
||||
nativeJars.clear();
|
||||
for (auto lib : getLibraries())
|
||||
{
|
||||
lib->getApplicableFiles(runtimeContext, jars, nativeJars, native32, native64, overridePath);
|
||||
lib->getApplicableFiles(currentSystem, jars, nativeJars, native32, native64, overridePath);
|
||||
}
|
||||
// NOTE: order is important here, add main jar last to the lists
|
||||
if(m_mainJar)
|
||||
@ -379,18 +379,18 @@ void LaunchProfile::getLibraryFiles(
|
||||
}
|
||||
else
|
||||
{
|
||||
m_mainJar->getApplicableFiles(runtimeContext, jars, nativeJars, native32, native64, overridePath);
|
||||
m_mainJar->getApplicableFiles(currentSystem, jars, nativeJars, native32, native64, overridePath);
|
||||
}
|
||||
}
|
||||
for (auto lib : getNativeLibraries())
|
||||
{
|
||||
lib->getApplicableFiles(runtimeContext, jars, nativeJars, native32, native64, overridePath);
|
||||
lib->getApplicableFiles(currentSystem, jars, nativeJars, native32, native64, overridePath);
|
||||
}
|
||||
if(runtimeContext.javaArchitecture == "32")
|
||||
if(architecture == "32")
|
||||
{
|
||||
nativeJars.append(native32);
|
||||
}
|
||||
else if(runtimeContext.javaArchitecture == "64")
|
||||
else if(architecture == "64")
|
||||
{
|
||||
nativeJars.append(native64);
|
||||
}
|
||||
|
@ -56,9 +56,9 @@ public: /* application of profile variables from patches */
|
||||
void applyTweakers(const QStringList &tweakers);
|
||||
void applyJarMods(const QList<LibraryPtr> &jarMods);
|
||||
void applyMods(const QList<LibraryPtr> &jarMods);
|
||||
void applyLibrary(LibraryPtr library, const RuntimeContext & runtimeContext);
|
||||
void applyMavenFile(LibraryPtr library, const RuntimeContext & runtimeContext);
|
||||
void applyAgent(AgentPtr agent, const RuntimeContext & runtimeContext);
|
||||
void applyLibrary(LibraryPtr library);
|
||||
void applyMavenFile(LibraryPtr library);
|
||||
void applyAgent(AgentPtr agent);
|
||||
void applyCompatibleJavaMajors(QList<int>& javaMajor);
|
||||
void applyMainJar(LibraryPtr jar);
|
||||
void applyProblemSeverity(ProblemSeverity severity);
|
||||
@ -83,7 +83,7 @@ public: /* getters for profile variables */
|
||||
const QList<int> & getCompatibleJavaMajors() const;
|
||||
const LibraryPtr getMainJar() const;
|
||||
void getLibraryFiles(
|
||||
const RuntimeContext & runtimeContext,
|
||||
const QString & architecture,
|
||||
QStringList & jars,
|
||||
QStringList & nativeJars,
|
||||
const QString & overridePath,
|
||||
|
@ -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 "Library.h"
|
||||
#include "MinecraftInstance.h"
|
||||
|
||||
@ -42,7 +7,7 @@
|
||||
#include <BuildConfig.h>
|
||||
|
||||
|
||||
void Library::getApplicableFiles(const RuntimeContext & runtimeContext, QStringList& jar, QStringList& native, QStringList& native32,
|
||||
void Library::getApplicableFiles(OpSys system, QStringList& jar, QStringList& native, QStringList& native32,
|
||||
QStringList& native64, const QString &overridePath) const
|
||||
{
|
||||
bool local = isLocal();
|
||||
@ -56,7 +21,7 @@ void Library::getApplicableFiles(const RuntimeContext & runtimeContext, QStringL
|
||||
}
|
||||
return out.absoluteFilePath();
|
||||
};
|
||||
QString raw_storage = storageSuffix(runtimeContext);
|
||||
QString raw_storage = storageSuffix(system);
|
||||
if(isNative())
|
||||
{
|
||||
if (raw_storage.contains("${arch}"))
|
||||
@ -80,7 +45,7 @@ void Library::getApplicableFiles(const RuntimeContext & runtimeContext, QStringL
|
||||
}
|
||||
|
||||
QList<NetAction::Ptr> Library::getDownloads(
|
||||
const RuntimeContext & runtimeContext,
|
||||
OpSys system,
|
||||
class HttpMetaCache* cache,
|
||||
QStringList& failedLocalFiles,
|
||||
const QString & overridePath
|
||||
@ -142,14 +107,14 @@ QList<NetAction::Ptr> Library::getDownloads(
|
||||
return true;
|
||||
};
|
||||
|
||||
QString raw_storage = storageSuffix(runtimeContext);
|
||||
QString raw_storage = storageSuffix(system);
|
||||
if(m_mojangDownloads)
|
||||
{
|
||||
if(isNative())
|
||||
{
|
||||
auto nativeClassifier = getCompatibleNative(runtimeContext);
|
||||
if(!nativeClassifier.isNull())
|
||||
if(m_nativeClassifiers.contains(system))
|
||||
{
|
||||
auto nativeClassifier = m_nativeClassifiers[system];
|
||||
if(nativeClassifier.contains("${arch}"))
|
||||
{
|
||||
auto nat32Classifier = nativeClassifier;
|
||||
@ -238,7 +203,7 @@ QList<NetAction::Ptr> Library::getDownloads(
|
||||
return out;
|
||||
}
|
||||
|
||||
bool Library::isActive(const RuntimeContext & runtimeContext) const
|
||||
bool Library::isActive() const
|
||||
{
|
||||
bool result = true;
|
||||
if (m_rules.empty())
|
||||
@ -250,7 +215,7 @@ bool Library::isActive(const RuntimeContext & runtimeContext) const
|
||||
RuleAction ruleResult = Disallow;
|
||||
for (auto rule : m_rules)
|
||||
{
|
||||
RuleAction temp = rule->apply(this, runtimeContext);
|
||||
RuleAction temp = rule->apply(this);
|
||||
if (temp != Defer)
|
||||
ruleResult = temp;
|
||||
}
|
||||
@ -258,7 +223,7 @@ bool Library::isActive(const RuntimeContext & runtimeContext) const
|
||||
}
|
||||
if (isNative())
|
||||
{
|
||||
result = result && !getCompatibleNative(runtimeContext).isNull();
|
||||
result = result && m_nativeClassifiers.contains(currentSystem);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -273,19 +238,6 @@ bool Library::isAlwaysStale() const
|
||||
return m_hint == "always-stale";
|
||||
}
|
||||
|
||||
QString Library::getCompatibleNative(const RuntimeContext & runtimeContext) const {
|
||||
// try to match precise classifier "[os]-[arch]"
|
||||
auto entry = m_nativeClassifiers.constFind(runtimeContext.getClassifier());
|
||||
// try to match imprecise classifier on legacy architectures "[os]"
|
||||
if (entry == m_nativeClassifiers.constEnd() && runtimeContext.isLegacyArch())
|
||||
entry = m_nativeClassifiers.constFind(runtimeContext.system);
|
||||
|
||||
if (entry == m_nativeClassifiers.constEnd())
|
||||
return QString();
|
||||
|
||||
return entry.value();
|
||||
}
|
||||
|
||||
void Library::setStoragePrefix(QString prefix)
|
||||
{
|
||||
m_storagePrefix = prefix;
|
||||
@ -305,7 +257,7 @@ QString Library::storagePrefix() const
|
||||
return m_storagePrefix;
|
||||
}
|
||||
|
||||
QString Library::filename(const RuntimeContext & runtimeContext) const
|
||||
QString Library::filename(OpSys system) const
|
||||
{
|
||||
if(!m_filename.isEmpty())
|
||||
{
|
||||
@ -319,10 +271,9 @@ QString Library::filename(const RuntimeContext & runtimeContext) const
|
||||
|
||||
// otherwise native, override classifiers. Mojang HACK!
|
||||
GradleSpecifier nativeSpec = m_name;
|
||||
QString nativeClassifier = getCompatibleNative(runtimeContext);
|
||||
if (!nativeClassifier.isNull())
|
||||
if (m_nativeClassifiers.contains(system))
|
||||
{
|
||||
nativeSpec.setClassifier(nativeClassifier);
|
||||
nativeSpec.setClassifier(m_nativeClassifiers[system]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -331,14 +282,14 @@ QString Library::filename(const RuntimeContext & runtimeContext) const
|
||||
return nativeSpec.getFileName();
|
||||
}
|
||||
|
||||
QString Library::displayName(const RuntimeContext & runtimeContext) const
|
||||
QString Library::displayName(OpSys system) const
|
||||
{
|
||||
if(!m_displayname.isEmpty())
|
||||
return m_displayname;
|
||||
return filename(runtimeContext);
|
||||
return filename(system);
|
||||
}
|
||||
|
||||
QString Library::storageSuffix(const RuntimeContext & runtimeContext) const
|
||||
QString Library::storageSuffix(OpSys system) const
|
||||
{
|
||||
// non-native? use only the gradle specifier
|
||||
if (!isNative())
|
||||
@ -348,10 +299,9 @@ QString Library::storageSuffix(const RuntimeContext & runtimeContext) const
|
||||
|
||||
// otherwise native, override classifiers. Mojang HACK!
|
||||
GradleSpecifier nativeSpec = m_name;
|
||||
QString nativeClassifier = getCompatibleNative(runtimeContext);
|
||||
if (!nativeClassifier.isNull())
|
||||
if (m_nativeClassifiers.contains(system))
|
||||
{
|
||||
nativeSpec.setClassifier(nativeClassifier);
|
||||
nativeSpec.setClassifier(m_nativeClassifiers[system]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1,44 +1,8 @@
|
||||
// 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
|
||||
#include <QString>
|
||||
#include <net/NetAction.h>
|
||||
#include <QPair>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QMap>
|
||||
#include <QDir>
|
||||
@ -46,9 +10,9 @@
|
||||
#include <memory>
|
||||
|
||||
#include "Rule.h"
|
||||
#include "minecraft/OpSys.h"
|
||||
#include "GradleSpecifier.h"
|
||||
#include "MojangDownloadInfo.h"
|
||||
#include "RuntimeContext.h"
|
||||
|
||||
class Library;
|
||||
class MinecraftInstance;
|
||||
@ -134,7 +98,7 @@ public: /* methods */
|
||||
m_repositoryURL = base_url;
|
||||
}
|
||||
|
||||
void getApplicableFiles(const RuntimeContext & runtimeContext, QStringList & jar, QStringList & native,
|
||||
void getApplicableFiles(OpSys system, QStringList & jar, QStringList & native,
|
||||
QStringList & native32, QStringList & native64, const QString & overridePath) const;
|
||||
|
||||
void setAbsoluteUrl(const QString &absolute_url)
|
||||
@ -148,7 +112,7 @@ public: /* methods */
|
||||
}
|
||||
|
||||
/// Get the file name of the library
|
||||
QString filename(const RuntimeContext & runtimeContext) const;
|
||||
QString filename(OpSys system) const;
|
||||
|
||||
// DEPRECATED: set a display name, used by jar mods only
|
||||
void setDisplayName(const QString & displayName)
|
||||
@ -157,7 +121,7 @@ public: /* methods */
|
||||
}
|
||||
|
||||
/// Get the file name of the library
|
||||
QString displayName(const RuntimeContext & runtimeContext) const;
|
||||
QString displayName(OpSys system) const;
|
||||
|
||||
void setMojangDownloadInfo(MojangLibraryDownloadInfo::Ptr info)
|
||||
{
|
||||
@ -176,7 +140,7 @@ public: /* methods */
|
||||
}
|
||||
|
||||
/// Returns true if the library should be loaded (or extracted, in case of natives)
|
||||
bool isActive(const RuntimeContext & runtimeContext) const;
|
||||
bool isActive() const;
|
||||
|
||||
/// Returns true if the library is contained in an instance and false if it is shared
|
||||
bool isLocal() const;
|
||||
@ -188,20 +152,18 @@ public: /* methods */
|
||||
bool isForge() const;
|
||||
|
||||
// Get a list of downloads for this library
|
||||
QList<NetAction::Ptr> getDownloads(const RuntimeContext & runtimeContext, class HttpMetaCache * cache,
|
||||
QList<NetAction::Ptr> getDownloads(OpSys system, class HttpMetaCache * cache,
|
||||
QStringList & failedLocalFiles, const QString & overridePath) const;
|
||||
|
||||
QString getCompatibleNative(const RuntimeContext & runtimeContext) const;
|
||||
|
||||
private: /* methods */
|
||||
/// the default storage prefix used by Prism Launcher
|
||||
/// the default storage prefix used by PolyMC
|
||||
static QString defaultStoragePrefix();
|
||||
|
||||
/// Get the prefix - root of the storage to be used
|
||||
QString storagePrefix() const;
|
||||
|
||||
/// Get the relative file path where the library should be saved
|
||||
QString storageSuffix(const RuntimeContext & runtimeContext) const;
|
||||
QString storageSuffix(OpSys system) const;
|
||||
|
||||
QString hint() const
|
||||
{
|
||||
@ -215,23 +177,23 @@ protected: /* data */
|
||||
/// DEPRECATED URL prefix of the maven repo where the file can be downloaded
|
||||
QString m_repositoryURL;
|
||||
|
||||
/// DEPRECATED: Prism Launcher-specific absolute URL. takes precedence over the implicit maven repo URL, if defined
|
||||
/// DEPRECATED: PolyMC-specific absolute URL. takes precedence over the implicit maven repo URL, if defined
|
||||
QString m_absoluteURL;
|
||||
|
||||
/// Prism Launcher extension - filename override
|
||||
/// PolyMC extension - filename override
|
||||
QString m_filename;
|
||||
|
||||
/// DEPRECATED Prism Launcher extension - display name
|
||||
/// DEPRECATED PolyMC extension - display name
|
||||
QString m_displayname;
|
||||
|
||||
/**
|
||||
* Prism Launcher-specific type hint - modifies how the library is treated
|
||||
* PolyMC-specific type hint - modifies how the library is treated
|
||||
*/
|
||||
QString m_hint;
|
||||
|
||||
/**
|
||||
* storage - by default the local libraries folder in Prism Launcher, but could be elsewhere
|
||||
* Prism Launcher specific, because of FTB.
|
||||
* storage - by default the local libraries folder in polymc, but could be elsewhere
|
||||
* PolyMC specific, because of FTB.
|
||||
*/
|
||||
QString m_storagePrefix;
|
||||
|
||||
@ -242,7 +204,7 @@ protected: /* data */
|
||||
QStringList m_extractExcludes;
|
||||
|
||||
/// native suffixes per OS
|
||||
QMap<QString, QString> m_nativeClassifiers;
|
||||
QMap<OpSys, QString> m_nativeClassifiers;
|
||||
|
||||
/// true if the library had a rules section (even empty)
|
||||
bool applyRules = false;
|
||||
|
@ -1,46 +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 <QTest>
|
||||
|
||||
#include <minecraft/MojangVersionFormat.h>
|
||||
#include <minecraft/OneSixVersionFormat.h>
|
||||
#include <minecraft/Library.h>
|
||||
#include <net/HttpMetaCache.h>
|
||||
#include <FileSystem.h>
|
||||
#include <RuntimeContext.h>
|
||||
#include "minecraft/MojangVersionFormat.h"
|
||||
#include "minecraft/OneSixVersionFormat.h"
|
||||
#include "minecraft/Library.h"
|
||||
#include "net/HttpMetaCache.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
class LibraryTest : public QObject
|
||||
{
|
||||
@ -60,31 +24,22 @@ private:
|
||||
{
|
||||
return {FS::PathCombine(cache->getBasePath("libraries"), relative)};
|
||||
}
|
||||
|
||||
RuntimeContext dummyContext(QString system = "linux", QString arch = "64", QString realArch = "amd64") {
|
||||
RuntimeContext r;
|
||||
r.javaArchitecture = arch;
|
||||
r.javaRealArchitecture = realArch;
|
||||
r.system = system;
|
||||
return r;
|
||||
}
|
||||
private
|
||||
slots:
|
||||
void initTestCase()
|
||||
{
|
||||
cache.reset(new HttpMetaCache());
|
||||
cache->addBase("libraries", QDir("libraries").absolutePath());
|
||||
dataDir = QDir(QFINDTESTDATA("testdata/Library")).absolutePath();
|
||||
dataDir = QDir(QFINDTESTDATA("testdata")).absolutePath();
|
||||
}
|
||||
void test_legacy()
|
||||
{
|
||||
RuntimeContext r = dummyContext();
|
||||
Library test("test.package:testname:testversion");
|
||||
QCOMPARE(test.artifactPrefix(), QString("test.package:testname"));
|
||||
QCOMPARE(test.isNative(), false);
|
||||
|
||||
QStringList jar, native, native32, native64;
|
||||
test.getApplicableFiles(r, jar, native, native32, native64, QString());
|
||||
test.getApplicableFiles(currentSystem, jar, native, native32, native64, QString());
|
||||
QCOMPARE(jar, getStorage("test/package/testname/testversion/testname-testversion.jar"));
|
||||
QCOMPARE(native, {});
|
||||
QCOMPARE(native32, {});
|
||||
@ -92,11 +47,10 @@ slots:
|
||||
}
|
||||
void test_legacy_url()
|
||||
{
|
||||
RuntimeContext r = dummyContext();
|
||||
QStringList failedFiles;
|
||||
Library test("test.package:testname:testversion");
|
||||
test.setRepositoryURL("file://foo/bar");
|
||||
auto downloads = test.getDownloads(r, cache.get(), failedFiles, QString());
|
||||
auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString());
|
||||
QCOMPARE(downloads.size(), 1);
|
||||
QCOMPARE(failedFiles, {});
|
||||
NetAction::Ptr dl = downloads[0];
|
||||
@ -104,50 +58,47 @@ slots:
|
||||
}
|
||||
void test_legacy_url_local_broken()
|
||||
{
|
||||
RuntimeContext r = dummyContext();
|
||||
Library test("test.package:testname:testversion");
|
||||
QCOMPARE(test.isNative(), false);
|
||||
QStringList failedFiles;
|
||||
test.setHint("local");
|
||||
auto downloads = test.getDownloads(r, cache.get(), failedFiles, QString());
|
||||
auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString());
|
||||
QCOMPARE(downloads.size(), 0);
|
||||
QCOMPARE(failedFiles, {"testname-testversion.jar"});
|
||||
}
|
||||
void test_legacy_url_local_override()
|
||||
{
|
||||
RuntimeContext r = dummyContext();
|
||||
Library test("com.paulscode:codecwav:20101023");
|
||||
QCOMPARE(test.isNative(), false);
|
||||
QStringList failedFiles;
|
||||
test.setHint("local");
|
||||
auto downloads = test.getDownloads(r, cache.get(), failedFiles, QFINDTESTDATA("testdata/Library"));
|
||||
auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QFINDTESTDATA("testdata"));
|
||||
QCOMPARE(downloads.size(), 0);
|
||||
qDebug() << failedFiles;
|
||||
QCOMPARE(failedFiles.size(), 0);
|
||||
|
||||
QStringList jar, native, native32, native64;
|
||||
test.getApplicableFiles(r, jar, native, native32, native64, QFINDTESTDATA("testdata/Library"));
|
||||
QCOMPARE(jar, {QFileInfo(QFINDTESTDATA("testdata/Library/codecwav-20101023.jar")).absoluteFilePath()});
|
||||
test.getApplicableFiles(currentSystem, jar, native, native32, native64, QFINDTESTDATA("testdata"));
|
||||
QCOMPARE(jar, {QFileInfo(QFINDTESTDATA("testdata/codecwav-20101023.jar")).absoluteFilePath()});
|
||||
QCOMPARE(native, {});
|
||||
QCOMPARE(native32, {});
|
||||
QCOMPARE(native64, {});
|
||||
}
|
||||
void test_legacy_native()
|
||||
{
|
||||
RuntimeContext r = dummyContext();
|
||||
Library test("test.package:testname:testversion");
|
||||
test.m_nativeClassifiers["linux"] = "linux";
|
||||
test.m_nativeClassifiers[OpSys::Os_Linux]="linux";
|
||||
QCOMPARE(test.isNative(), true);
|
||||
test.setRepositoryURL("file://foo/bar");
|
||||
{
|
||||
QStringList jar, native, native32, native64;
|
||||
test.getApplicableFiles(r, jar, native, native32, native64, QString());
|
||||
test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QString());
|
||||
QCOMPARE(jar, {});
|
||||
QCOMPARE(native, getStorage("test/package/testname/testversion/testname-testversion-linux.jar"));
|
||||
QCOMPARE(native32, {});
|
||||
QCOMPARE(native64, {});
|
||||
QStringList failedFiles;
|
||||
auto dls = test.getDownloads(r, cache.get(), failedFiles, QString());
|
||||
auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QString());
|
||||
QCOMPARE(dls.size(), 1);
|
||||
QCOMPARE(failedFiles, {});
|
||||
auto dl = dls[0];
|
||||
@ -156,52 +107,49 @@ slots:
|
||||
}
|
||||
void test_legacy_native_arch()
|
||||
{
|
||||
RuntimeContext r = dummyContext();
|
||||
Library test("test.package:testname:testversion");
|
||||
test.m_nativeClassifiers["linux"]="linux-${arch}";
|
||||
test.m_nativeClassifiers["osx"]="osx-${arch}";
|
||||
test.m_nativeClassifiers["windows"]="windows-${arch}";
|
||||
test.m_nativeClassifiers[OpSys::Os_Linux]="linux-${arch}";
|
||||
test.m_nativeClassifiers[OpSys::Os_OSX]="osx-${arch}";
|
||||
test.m_nativeClassifiers[OpSys::Os_Windows]="windows-${arch}";
|
||||
QCOMPARE(test.isNative(), true);
|
||||
test.setRepositoryURL("file://foo/bar");
|
||||
{
|
||||
QStringList jar, native, native32, native64;
|
||||
test.getApplicableFiles(r, jar, native, native32, native64, QString());
|
||||
test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QString());
|
||||
QCOMPARE(jar, {});
|
||||
QCOMPARE(native, {});
|
||||
QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-linux-32.jar"));
|
||||
QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-linux-64.jar"));
|
||||
QStringList failedFiles;
|
||||
auto dls = test.getDownloads(r, cache.get(), failedFiles, QString());
|
||||
auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QString());
|
||||
QCOMPARE(dls.size(), 2);
|
||||
QCOMPARE(failedFiles, {});
|
||||
QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux-32.jar"));
|
||||
QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux-64.jar"));
|
||||
}
|
||||
r.system = "windows";
|
||||
{
|
||||
QStringList jar, native, native32, native64;
|
||||
test.getApplicableFiles(r, jar, native, native32, native64, QString());
|
||||
test.getApplicableFiles(Os_Windows, jar, native, native32, native64, QString());
|
||||
QCOMPARE(jar, {});
|
||||
QCOMPARE(native, {});
|
||||
QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-windows-32.jar"));
|
||||
QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-windows-64.jar"));
|
||||
QStringList failedFiles;
|
||||
auto dls = test.getDownloads(r, cache.get(), failedFiles, QString());
|
||||
auto dls = test.getDownloads(Os_Windows, cache.get(), failedFiles, QString());
|
||||
QCOMPARE(dls.size(), 2);
|
||||
QCOMPARE(failedFiles, {});
|
||||
QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-windows-32.jar"));
|
||||
QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-windows-64.jar"));
|
||||
}
|
||||
r.system = "osx";
|
||||
{
|
||||
QStringList jar, native, native32, native64;
|
||||
test.getApplicableFiles(r, jar, native, native32, native64, QString());
|
||||
test.getApplicableFiles(Os_OSX, jar, native, native32, native64, QString());
|
||||
QCOMPARE(jar, {});
|
||||
QCOMPARE(native, {});
|
||||
QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-osx-32.jar"));
|
||||
QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-osx-64.jar"));
|
||||
QStringList failedFiles;
|
||||
auto dls = test.getDownloads(r, cache.get(), failedFiles, QString());
|
||||
auto dls = test.getDownloads(Os_OSX, cache.get(), failedFiles, QString());
|
||||
QCOMPARE(dls.size(), 2);
|
||||
QCOMPARE(failedFiles, {});
|
||||
QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-osx-32.jar"));
|
||||
@ -210,112 +158,103 @@ slots:
|
||||
}
|
||||
void test_legacy_native_arch_local_override()
|
||||
{
|
||||
RuntimeContext r = dummyContext();
|
||||
Library test("test.package:testname:testversion");
|
||||
test.m_nativeClassifiers["linux"]="linux-${arch}";
|
||||
test.m_nativeClassifiers[OpSys::Os_Linux]="linux-${arch}";
|
||||
test.setHint("local");
|
||||
QCOMPARE(test.isNative(), true);
|
||||
test.setRepositoryURL("file://foo/bar");
|
||||
{
|
||||
QStringList jar, native, native32, native64;
|
||||
test.getApplicableFiles(r, jar, native, native32, native64, QFINDTESTDATA("testdata/Library"));
|
||||
test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QFINDTESTDATA("testdata"));
|
||||
QCOMPARE(jar, {});
|
||||
QCOMPARE(native, {});
|
||||
QCOMPARE(native32, {QFileInfo(QFINDTESTDATA("testdata/Library/testname-testversion-linux-32.jar")).absoluteFilePath()});
|
||||
QCOMPARE(native64, {QFileInfo(QFINDTESTDATA("testdata/Library") + "/testname-testversion-linux-64.jar").absoluteFilePath()});
|
||||
QCOMPARE(native32, {QFileInfo(QFINDTESTDATA("testdata/testname-testversion-linux-32.jar")).absoluteFilePath()});
|
||||
QCOMPARE(native64, {QFileInfo(QFINDTESTDATA("testdata") + "/testname-testversion-linux-64.jar").absoluteFilePath()});
|
||||
QStringList failedFiles;
|
||||
auto dls = test.getDownloads(r, cache.get(), failedFiles, QFINDTESTDATA("testdata/Library"));
|
||||
auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QFINDTESTDATA("testdata"));
|
||||
QCOMPARE(dls.size(), 0);
|
||||
QCOMPARE(failedFiles, {QFileInfo(QFINDTESTDATA("testdata/Library") + "/testname-testversion-linux-64.jar").absoluteFilePath()});
|
||||
QCOMPARE(failedFiles, {QFileInfo(QFINDTESTDATA("testdata") + "/testname-testversion-linux-64.jar").absoluteFilePath()});
|
||||
}
|
||||
}
|
||||
void test_onenine()
|
||||
{
|
||||
RuntimeContext r = dummyContext("osx");
|
||||
auto test = readMojangJson(QFINDTESTDATA("testdata/Library/lib-simple.json"));
|
||||
auto test = readMojangJson(QFINDTESTDATA("testdata/lib-simple.json"));
|
||||
{
|
||||
QStringList jar, native, native32, native64;
|
||||
test->getApplicableFiles(r, jar, native, native32, native64, QString());
|
||||
test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString());
|
||||
QCOMPARE(jar, getStorage("com/paulscode/codecwav/20101023/codecwav-20101023.jar"));
|
||||
QCOMPARE(native, {});
|
||||
QCOMPARE(native32, {});
|
||||
QCOMPARE(native64, {});
|
||||
}
|
||||
r.system = "linux";
|
||||
{
|
||||
QStringList failedFiles;
|
||||
auto dls = test->getDownloads(r, cache.get(), failedFiles, QString());
|
||||
auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QString());
|
||||
QCOMPARE(dls.size(), 1);
|
||||
QCOMPARE(failedFiles, {});
|
||||
QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/com/paulscode/codecwav/20101023/codecwav-20101023.jar"));
|
||||
}
|
||||
r.system = "osx";
|
||||
test->setHint("local");
|
||||
{
|
||||
QStringList jar, native, native32, native64;
|
||||
test->getApplicableFiles(r, jar, native, native32, native64, QFINDTESTDATA("testdata/Library"));
|
||||
QCOMPARE(jar, {QFileInfo(QFINDTESTDATA("testdata/Library/codecwav-20101023.jar")).absoluteFilePath()});
|
||||
test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QFINDTESTDATA("testdata"));
|
||||
QCOMPARE(jar, {QFileInfo(QFINDTESTDATA("testdata/codecwav-20101023.jar")).absoluteFilePath()});
|
||||
QCOMPARE(native, {});
|
||||
QCOMPARE(native32, {});
|
||||
QCOMPARE(native64, {});
|
||||
}
|
||||
r.system = "linux";
|
||||
{
|
||||
QStringList failedFiles;
|
||||
auto dls = test->getDownloads(r, cache.get(), failedFiles, QFINDTESTDATA("testdata/Library"));
|
||||
auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QFINDTESTDATA("testdata"));
|
||||
QCOMPARE(dls.size(), 0);
|
||||
QCOMPARE(failedFiles, {});
|
||||
}
|
||||
}
|
||||
void test_onenine_local_override()
|
||||
{
|
||||
RuntimeContext r = dummyContext("osx");
|
||||
auto test = readMojangJson(QFINDTESTDATA("testdata/Library/lib-simple.json"));
|
||||
auto test = readMojangJson(QFINDTESTDATA("testdata/lib-simple.json"));
|
||||
test->setHint("local");
|
||||
{
|
||||
QStringList jar, native, native32, native64;
|
||||
test->getApplicableFiles(r, jar, native, native32, native64, QFINDTESTDATA("testdata/Library"));
|
||||
QCOMPARE(jar, {QFileInfo(QFINDTESTDATA("testdata/Library/codecwav-20101023.jar")).absoluteFilePath()});
|
||||
test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QFINDTESTDATA("testdata"));
|
||||
QCOMPARE(jar, {QFileInfo(QFINDTESTDATA("testdata/codecwav-20101023.jar")).absoluteFilePath()});
|
||||
QCOMPARE(native, {});
|
||||
QCOMPARE(native32, {});
|
||||
QCOMPARE(native64, {});
|
||||
}
|
||||
r.system = "linux";
|
||||
{
|
||||
QStringList failedFiles;
|
||||
auto dls = test->getDownloads(r, cache.get(), failedFiles, QFINDTESTDATA("testdata/Library"));
|
||||
auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QFINDTESTDATA("testdata"));
|
||||
QCOMPARE(dls.size(), 0);
|
||||
QCOMPARE(failedFiles, {});
|
||||
}
|
||||
}
|
||||
void test_onenine_native()
|
||||
{
|
||||
RuntimeContext r = dummyContext("osx");
|
||||
auto test = readMojangJson(QFINDTESTDATA("testdata/Library/lib-native.json"));
|
||||
auto test = readMojangJson(QFINDTESTDATA("testdata/lib-native.json"));
|
||||
QStringList jar, native, native32, native64;
|
||||
test->getApplicableFiles(r, jar, native, native32, native64, QString());
|
||||
test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString());
|
||||
QCOMPARE(jar, QStringList());
|
||||
QCOMPARE(native, getStorage("org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar"));
|
||||
QCOMPARE(native32, {});
|
||||
QCOMPARE(native64, {});
|
||||
QStringList failedFiles;
|
||||
auto dls = test->getDownloads(r, cache.get(), failedFiles, QString());
|
||||
auto dls = test->getDownloads(Os_OSX, cache.get(), failedFiles, QString());
|
||||
QCOMPARE(dls.size(), 1);
|
||||
QCOMPARE(failedFiles, {});
|
||||
QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar"));
|
||||
}
|
||||
void test_onenine_native_arch()
|
||||
{
|
||||
RuntimeContext r = dummyContext("windows");
|
||||
auto test = readMojangJson(QFINDTESTDATA("testdata/Library/lib-native-arch.json"));
|
||||
auto test = readMojangJson(QFINDTESTDATA("testdata/lib-native-arch.json"));
|
||||
QStringList jar, native, native32, native64;
|
||||
test->getApplicableFiles(r, jar, native, native32, native64, QString());
|
||||
test->getApplicableFiles(Os_Windows, jar, native, native32, native64, QString());
|
||||
QCOMPARE(jar, {});
|
||||
QCOMPARE(native, {});
|
||||
QCOMPARE(native32, getStorage("tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar"));
|
||||
QCOMPARE(native64, getStorage("tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar"));
|
||||
QStringList failedFiles;
|
||||
auto dls = test->getDownloads(r, cache.get(), failedFiles, QString());
|
||||
auto dls = test->getDownloads(Os_Windows, cache.get(), failedFiles, QString());
|
||||
QCOMPARE(dls.size(), 2);
|
||||
QCOMPARE(failedFiles, {});
|
||||
QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar"));
|
@ -43,6 +43,7 @@
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "Application.h"
|
||||
|
||||
#include "MMCStrings.h"
|
||||
#include "pathmatcher/RegexpMatcher.h"
|
||||
#include "pathmatcher/MultiMatcher.h"
|
||||
#include "FileSystem.h"
|
||||
@ -75,7 +76,6 @@
|
||||
|
||||
#include "mod/ModFolderModel.h"
|
||||
#include "mod/ResourcePackFolderModel.h"
|
||||
#include "mod/ShaderPackFolderModel.h"
|
||||
#include "mod/TexturePackFolderModel.h"
|
||||
|
||||
#include "WorldList.h"
|
||||
@ -115,19 +115,6 @@ private:
|
||||
MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir)
|
||||
: BaseInstance(globalSettings, settings, rootDir)
|
||||
{
|
||||
m_components.reset(new PackProfile(this));
|
||||
}
|
||||
|
||||
void MinecraftInstance::saveNow()
|
||||
{
|
||||
m_components->saveNow();
|
||||
}
|
||||
|
||||
void MinecraftInstance::loadSpecificSettings()
|
||||
{
|
||||
if (isSpecificSettingsLoaded())
|
||||
return;
|
||||
|
||||
// Java Settings
|
||||
auto javaOverride = m_settings->registerSetting("OverrideJava", false);
|
||||
auto locationOverride = m_settings->registerSetting("OverrideJavaLocation", false);
|
||||
@ -137,66 +124,64 @@ void MinecraftInstance::loadSpecificSettings()
|
||||
auto javaOrLocation = std::make_shared<OrSetting>("JavaOrLocationOverride", javaOverride, locationOverride);
|
||||
auto javaOrArgs = std::make_shared<OrSetting>("JavaOrArgsOverride", javaOverride, argsOverride);
|
||||
|
||||
if (auto global_settings = globalSettings()) {
|
||||
m_settings->registerOverride(global_settings->getSetting("JavaPath"), javaOrLocation);
|
||||
m_settings->registerOverride(global_settings->getSetting("JvmArgs"), javaOrArgs);
|
||||
m_settings->registerOverride(global_settings->getSetting("IgnoreJavaCompatibility"), javaOrLocation);
|
||||
m_settings->registerOverride(globalSettings->getSetting("JavaPath"), javaOrLocation);
|
||||
m_settings->registerOverride(globalSettings->getSetting("JvmArgs"), javaOrArgs);
|
||||
m_settings->registerOverride(globalSettings->getSetting("IgnoreJavaCompatibility"), javaOrLocation);
|
||||
|
||||
// special!
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaTimestamp"), javaOrLocation);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaVersion"), javaOrLocation);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaArchitecture"), javaOrLocation);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaRealArchitecture"), javaOrLocation);
|
||||
// special!
|
||||
m_settings->registerPassthrough(globalSettings->getSetting("JavaTimestamp"), javaOrLocation);
|
||||
m_settings->registerPassthrough(globalSettings->getSetting("JavaVersion"), javaOrLocation);
|
||||
m_settings->registerPassthrough(globalSettings->getSetting("JavaArchitecture"), javaOrLocation);
|
||||
|
||||
// Window Size
|
||||
auto windowSetting = m_settings->registerSetting("OverrideWindow", false);
|
||||
m_settings->registerOverride(global_settings->getSetting("LaunchMaximized"), windowSetting);
|
||||
m_settings->registerOverride(global_settings->getSetting("MinecraftWinWidth"), windowSetting);
|
||||
m_settings->registerOverride(global_settings->getSetting("MinecraftWinHeight"), windowSetting);
|
||||
// Window Size
|
||||
auto windowSetting = m_settings->registerSetting("OverrideWindow", false);
|
||||
m_settings->registerOverride(globalSettings->getSetting("LaunchMaximized"), windowSetting);
|
||||
m_settings->registerOverride(globalSettings->getSetting("MinecraftWinWidth"), windowSetting);
|
||||
m_settings->registerOverride(globalSettings->getSetting("MinecraftWinHeight"), windowSetting);
|
||||
|
||||
// Memory
|
||||
auto memorySetting = m_settings->registerSetting("OverrideMemory", false);
|
||||
m_settings->registerOverride(global_settings->getSetting("MinMemAlloc"), memorySetting);
|
||||
m_settings->registerOverride(global_settings->getSetting("MaxMemAlloc"), memorySetting);
|
||||
m_settings->registerOverride(global_settings->getSetting("PermGen"), memorySetting);
|
||||
// Memory
|
||||
auto memorySetting = m_settings->registerSetting("OverrideMemory", false);
|
||||
m_settings->registerOverride(globalSettings->getSetting("MinMemAlloc"), memorySetting);
|
||||
m_settings->registerOverride(globalSettings->getSetting("MaxMemAlloc"), memorySetting);
|
||||
m_settings->registerOverride(globalSettings->getSetting("PermGen"), memorySetting);
|
||||
|
||||
// Minecraft launch method
|
||||
auto launchMethodOverride = m_settings->registerSetting("OverrideMCLaunchMethod", false);
|
||||
m_settings->registerOverride(global_settings->getSetting("MCLaunchMethod"), launchMethodOverride);
|
||||
// Minecraft launch method
|
||||
auto launchMethodOverride = m_settings->registerSetting("OverrideMCLaunchMethod", false);
|
||||
m_settings->registerOverride(globalSettings->getSetting("MCLaunchMethod"), launchMethodOverride);
|
||||
|
||||
// Native library workarounds
|
||||
auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false);
|
||||
m_settings->registerOverride(global_settings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride);
|
||||
m_settings->registerOverride(global_settings->getSetting("UseNativeGLFW"), nativeLibraryWorkaroundsOverride);
|
||||
// Native library workarounds
|
||||
auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false);
|
||||
m_settings->registerOverride(globalSettings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride);
|
||||
m_settings->registerOverride(globalSettings->getSetting("UseNativeGLFW"), nativeLibraryWorkaroundsOverride);
|
||||
|
||||
// Peformance related options
|
||||
auto performanceOverride = m_settings->registerSetting("OverridePerformance", false);
|
||||
m_settings->registerOverride(global_settings->getSetting("EnableFeralGamemode"), performanceOverride);
|
||||
m_settings->registerOverride(global_settings->getSetting("EnableMangoHud"), performanceOverride);
|
||||
m_settings->registerOverride(global_settings->getSetting("UseDiscreteGpu"), performanceOverride);
|
||||
// Peformance related options
|
||||
auto performanceOverride = m_settings->registerSetting("OverridePerformance", false);
|
||||
m_settings->registerOverride(globalSettings->getSetting("EnableFeralGamemode"), performanceOverride);
|
||||
m_settings->registerOverride(globalSettings->getSetting("EnableMangoHud"), performanceOverride);
|
||||
m_settings->registerOverride(globalSettings->getSetting("UseDiscreteGpu"), performanceOverride);
|
||||
|
||||
// Miscellaneous
|
||||
auto miscellaneousOverride = m_settings->registerSetting("OverrideMiscellaneous", false);
|
||||
m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride);
|
||||
m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride);
|
||||
|
||||
m_settings->set("InstanceType", "OneSix");
|
||||
}
|
||||
// Game time
|
||||
auto gameTimeOverride = m_settings->registerSetting("OverrideGameTime", false);
|
||||
m_settings->registerOverride(globalSettings->getSetting("ShowGameTime"), gameTimeOverride);
|
||||
m_settings->registerOverride(globalSettings->getSetting("RecordGameTime"), gameTimeOverride);
|
||||
|
||||
// Join server on launch, this does not have a global override
|
||||
m_settings->registerSetting("JoinServerOnLaunch", false);
|
||||
m_settings->registerSetting("JoinServerOnLaunchAddress", "");
|
||||
|
||||
qDebug() << "Instance-type specific settings were loaded!";
|
||||
// Miscellaneous
|
||||
auto miscellaneousOverride = m_settings->registerSetting("OverrideMiscellaneous", false);
|
||||
m_settings->registerOverride(globalSettings->getSetting("CloseAfterLaunch"), miscellaneousOverride);
|
||||
m_settings->registerOverride(globalSettings->getSetting("QuitAfterGameStop"), miscellaneousOverride);
|
||||
|
||||
setSpecificSettingsLoaded(true);
|
||||
m_settings->set("InstanceType", "OneSix");
|
||||
|
||||
updateRuntimeContext();
|
||||
m_components.reset(new PackProfile(this));
|
||||
}
|
||||
|
||||
void MinecraftInstance::updateRuntimeContext()
|
||||
void MinecraftInstance::saveNow()
|
||||
{
|
||||
m_runtimeContext.updateFromInstanceSettings(m_settings);
|
||||
m_components->saveNow();
|
||||
}
|
||||
|
||||
QString MinecraftInstance::typeName() const
|
||||
@ -252,14 +237,6 @@ QString MinecraftInstance::getLocalLibraryPath() const
|
||||
return libraries_dir.absolutePath();
|
||||
}
|
||||
|
||||
bool MinecraftInstance::supportsDemo() const
|
||||
{
|
||||
Version instance_ver { getPackProfile()->getComponentVersion("net.minecraft") };
|
||||
// Demo mode was introduced in 1.3.1: https://minecraft.fandom.com/wiki/Demo_mode#History
|
||||
// FIXME: Due to Version constraints atm, this can't handle well non-release versions
|
||||
return instance_ver >= Version("1.3.1");
|
||||
}
|
||||
|
||||
QString MinecraftInstance::jarModsDir() const
|
||||
{
|
||||
QDir jarmods_dir(FS::PathCombine(instanceRoot(), "jarmods/"));
|
||||
@ -331,11 +308,12 @@ QDir MinecraftInstance::versionsPath() const
|
||||
return QDir::current().absoluteFilePath("versions");
|
||||
}
|
||||
|
||||
QStringList MinecraftInstance::getClassPath()
|
||||
QStringList MinecraftInstance::getClassPath() const
|
||||
{
|
||||
QStringList jars, nativeJars;
|
||||
auto javaArchitecture = settings()->get("JavaArchitecture").toString();
|
||||
auto profile = m_components->getProfile();
|
||||
profile->getLibraryFiles(runtimeContext(), jars, nativeJars, getLocalLibraryPath(), binRoot());
|
||||
profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot());
|
||||
return jars;
|
||||
}
|
||||
|
||||
@ -345,15 +323,16 @@ QString MinecraftInstance::getMainClass() const
|
||||
return profile->getMainClass();
|
||||
}
|
||||
|
||||
QStringList MinecraftInstance::getNativeJars()
|
||||
QStringList MinecraftInstance::getNativeJars() const
|
||||
{
|
||||
QStringList jars, nativeJars;
|
||||
auto javaArchitecture = settings()->get("JavaArchitecture").toString();
|
||||
auto profile = m_components->getProfile();
|
||||
profile->getLibraryFiles(runtimeContext(), jars, nativeJars, getLocalLibraryPath(), binRoot());
|
||||
profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot());
|
||||
return nativeJars;
|
||||
}
|
||||
|
||||
QStringList MinecraftInstance::extraArguments()
|
||||
QStringList MinecraftInstance::extraArguments() const
|
||||
{
|
||||
auto list = BaseInstance::extraArguments();
|
||||
auto version = getPackProfile();
|
||||
@ -373,13 +352,13 @@ QStringList MinecraftInstance::extraArguments()
|
||||
for (auto agent : agents)
|
||||
{
|
||||
QStringList jar, temp1, temp2, temp3;
|
||||
agent->library()->getApplicableFiles(runtimeContext(), jar, temp1, temp2, temp3, getLocalLibraryPath());
|
||||
agent->library()->getApplicableFiles(currentSystem, jar, temp1, temp2, temp3, getLocalLibraryPath());
|
||||
list.append("-javaagent:"+jar[0]+(agent->argument().isEmpty() ? "" : "="+agent->argument()));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
QStringList MinecraftInstance::javaArguments()
|
||||
QStringList MinecraftInstance::javaArguments() const
|
||||
{
|
||||
QStringList args;
|
||||
|
||||
@ -436,7 +415,7 @@ QStringList MinecraftInstance::javaArguments()
|
||||
return args;
|
||||
}
|
||||
|
||||
QMap<QString, QString> MinecraftInstance::getVariables()
|
||||
QMap<QString, QString> MinecraftInstance::getVariables() const
|
||||
{
|
||||
QMap<QString, QString> out;
|
||||
out.insert("INST_NAME", name());
|
||||
@ -468,11 +447,13 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
|
||||
QProcessEnvironment env = createEnvironment();
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
if (settings()->get("EnableMangoHud").toBool() && APPLICATION->capabilities() & Application::SupportsMangoHud)
|
||||
if (settings()->get("EnableMangoHud").toBool())
|
||||
{
|
||||
auto preload = env.value("LD_PRELOAD", "") + ":libMangoHud_dlsym.so:libMangoHud.so";
|
||||
auto lib_path = env.value("LD_LIBRARY_PATH", "") + ":/usr/local/$LIB/mangohud/:/usr/$LIB/mangohud/";
|
||||
|
||||
env.insert("LD_PRELOAD", preload);
|
||||
env.insert("LD_LIBRARY_PATH", lib_path);
|
||||
env.insert("MANGOHUD", "1");
|
||||
}
|
||||
|
||||
@ -630,7 +611,8 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS
|
||||
// libraries and class path.
|
||||
{
|
||||
QStringList jars, nativeJars;
|
||||
profile->getLibraryFiles(runtimeContext(), jars, nativeJars, getLocalLibraryPath(), binRoot());
|
||||
auto javaArchitecture = settings()->get("JavaArchitecture").toString();
|
||||
profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot());
|
||||
for(auto file: jars)
|
||||
{
|
||||
launchScript += "cp " + file + "\n";
|
||||
@ -686,7 +668,8 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr
|
||||
{
|
||||
out << "Libraries:";
|
||||
QStringList jars, nativeJars;
|
||||
profile->getLibraryFiles(runtimeContext(), jars, nativeJars, getLocalLibraryPath(), binRoot());
|
||||
auto javaArchitecture = settings->get("JavaArchitecture").toString();
|
||||
profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot());
|
||||
auto printLibFile = [&](const QString & path)
|
||||
{
|
||||
QFileInfo info(path);
|
||||
@ -717,14 +700,14 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr
|
||||
{
|
||||
out << QString("%1:").arg(label);
|
||||
auto modList = model.allMods();
|
||||
std::sort(modList.begin(), modList.end(), [](auto a, auto b) {
|
||||
std::sort(modList.begin(), modList.end(), [](Mod::Ptr a, Mod::Ptr b) {
|
||||
auto aName = a->fileinfo().completeBaseName();
|
||||
auto bName = b->fileinfo().completeBaseName();
|
||||
return aName.localeAwareCompare(bName) < 0;
|
||||
});
|
||||
for(auto mod: modList)
|
||||
{
|
||||
if(mod->type() == ResourceType::FOLDER)
|
||||
if(mod->type() == Mod::MOD_FOLDER)
|
||||
{
|
||||
out << u8" [🖿] " + mod->fileinfo().completeBaseName() + " (folder)";
|
||||
continue;
|
||||
@ -751,8 +734,8 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr
|
||||
out << "Jar Mods:";
|
||||
for(auto & jarmod: jarMods)
|
||||
{
|
||||
auto displayname = jarmod->displayName(runtimeContext());
|
||||
auto realname = jarmod->filename(runtimeContext());
|
||||
auto displayname = jarmod->displayName(currentSystem);
|
||||
auto realname = jarmod->filename(currentSystem);
|
||||
if(displayname != realname)
|
||||
{
|
||||
out << " " + displayname + " (" + realname + ")";
|
||||
@ -914,7 +897,6 @@ QString MinecraftInstance::getStatusbarDescription()
|
||||
|
||||
Task::Ptr MinecraftInstance::createUpdateTask(Net::Mode mode)
|
||||
{
|
||||
updateRuntimeContext();
|
||||
switch (mode)
|
||||
{
|
||||
case Net::Mode::Offline:
|
||||
@ -931,7 +913,6 @@ Task::Ptr MinecraftInstance::createUpdateTask(Net::Mode mode)
|
||||
|
||||
shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
|
||||
{
|
||||
updateRuntimeContext();
|
||||
// FIXME: get rid of shared_from_this ...
|
||||
auto process = LaunchTask::create(std::dynamic_pointer_cast<MinecraftInstance>(shared_from_this()));
|
||||
auto pptr = process.get();
|
||||
@ -962,9 +943,9 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
|
||||
process->appendStep(new CreateGameFolders(pptr));
|
||||
}
|
||||
|
||||
if (!serverToJoin && settings()->get("JoinServerOnLaunch").toBool())
|
||||
if (!serverToJoin && m_settings->get("JoinServerOnLaunch").toBool())
|
||||
{
|
||||
QString fullAddress = settings()->get("JoinServerOnLaunchAddress").toString();
|
||||
QString fullAddress = m_settings->get("JoinServerOnLaunchAddress").toString();
|
||||
serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(fullAddress)));
|
||||
}
|
||||
|
||||
@ -1072,10 +1053,10 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
|
||||
|
||||
QString MinecraftInstance::launchMethod()
|
||||
{
|
||||
return settings()->get("MCLaunchMethod").toString();
|
||||
return m_settings->get("MCLaunchMethod").toString();
|
||||
}
|
||||
|
||||
JavaVersion MinecraftInstance::getJavaVersion()
|
||||
JavaVersion MinecraftInstance::getJavaVersion() const
|
||||
{
|
||||
return JavaVersion(settings()->get("JavaVersion").toString());
|
||||
}
|
||||
@ -1104,18 +1085,18 @@ std::shared_ptr<ModFolderModel> MinecraftInstance::coreModList() const
|
||||
return m_core_mod_list;
|
||||
}
|
||||
|
||||
std::shared_ptr<ResourcePackFolderModel> MinecraftInstance::resourcePackList() const
|
||||
std::shared_ptr<ModFolderModel> MinecraftInstance::resourcePackList() const
|
||||
{
|
||||
if (!m_resource_pack_list)
|
||||
{
|
||||
m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir()));
|
||||
m_resource_pack_list->enableInteraction(!isRunning());
|
||||
connect(this, &BaseInstance::runningStatusChanged, m_resource_pack_list.get(), &ResourcePackFolderModel::disableInteraction);
|
||||
m_resource_pack_list->disableInteraction(isRunning());
|
||||
connect(this, &BaseInstance::runningStatusChanged, m_resource_pack_list.get(), &ModFolderModel::disableInteraction);
|
||||
}
|
||||
return m_resource_pack_list;
|
||||
}
|
||||
|
||||
std::shared_ptr<TexturePackFolderModel> MinecraftInstance::texturePackList() const
|
||||
std::shared_ptr<ModFolderModel> MinecraftInstance::texturePackList() const
|
||||
{
|
||||
if (!m_texture_pack_list)
|
||||
{
|
||||
@ -1126,11 +1107,11 @@ std::shared_ptr<TexturePackFolderModel> MinecraftInstance::texturePackList() con
|
||||
return m_texture_pack_list;
|
||||
}
|
||||
|
||||
std::shared_ptr<ShaderPackFolderModel> MinecraftInstance::shaderPackList() const
|
||||
std::shared_ptr<ModFolderModel> MinecraftInstance::shaderPackList() const
|
||||
{
|
||||
if (!m_shader_pack_list)
|
||||
{
|
||||
m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir()));
|
||||
m_shader_pack_list.reset(new ResourcePackFolderModel(shaderPacksDir()));
|
||||
m_shader_pack_list->disableInteraction(isRunning());
|
||||
connect(this, &BaseInstance::runningStatusChanged, m_shader_pack_list.get(), &ModFolderModel::disableInteraction);
|
||||
}
|
||||
@ -1162,7 +1143,7 @@ QList<Mod*> MinecraftInstance::getJarMods() const
|
||||
for (auto jarmod : profile->getJarMods())
|
||||
{
|
||||
QStringList jar, temp1, temp2, temp3;
|
||||
jarmod->getApplicableFiles(runtimeContext(), jar, temp1, temp2, temp3, jarmodsPath().absolutePath());
|
||||
jarmod->getApplicableFiles(currentSystem, jar, temp1, temp2, temp3, jarmodsPath().absolutePath());
|
||||
// QString filePath = jarmodsPath().absoluteFilePath(jarmod->filename(currentSystem));
|
||||
mods.push_back(new Mod(QFileInfo(jar[0])));
|
||||
}
|
||||
|
@ -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
|
||||
#include "BaseInstance.h"
|
||||
#include <java/JavaVersion.h>
|
||||
@ -42,10 +7,6 @@
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
|
||||
class ModFolderModel;
|
||||
class ResourceFolderModel;
|
||||
class ResourcePackFolderModel;
|
||||
class ShaderPackFolderModel;
|
||||
class TexturePackFolderModel;
|
||||
class WorldList;
|
||||
class GameOptions;
|
||||
class LaunchStep;
|
||||
@ -59,8 +20,6 @@ public:
|
||||
virtual ~MinecraftInstance() {};
|
||||
virtual void saveNow() override;
|
||||
|
||||
void loadSpecificSettings() override;
|
||||
|
||||
// FIXME: remove
|
||||
QString typeName() const override;
|
||||
// FIXME: remove
|
||||
@ -104,10 +63,6 @@ public:
|
||||
// where the instance-local libraries should be
|
||||
QString getLocalLibraryPath() const;
|
||||
|
||||
/** Returns whether the instance, with its version, has support for demo mode. */
|
||||
[[nodiscard]] bool supportsDemo() const;
|
||||
|
||||
void updateRuntimeContext();
|
||||
|
||||
////// Profile management //////
|
||||
std::shared_ptr<PackProfile> getPackProfile() const;
|
||||
@ -115,24 +70,24 @@ public:
|
||||
////// Mod Lists //////
|
||||
std::shared_ptr<ModFolderModel> loaderModList() const;
|
||||
std::shared_ptr<ModFolderModel> coreModList() const;
|
||||
std::shared_ptr<ResourcePackFolderModel> resourcePackList() const;
|
||||
std::shared_ptr<TexturePackFolderModel> texturePackList() const;
|
||||
std::shared_ptr<ShaderPackFolderModel> shaderPackList() const;
|
||||
std::shared_ptr<ModFolderModel> resourcePackList() const;
|
||||
std::shared_ptr<ModFolderModel> texturePackList() const;
|
||||
std::shared_ptr<ModFolderModel> shaderPackList() const;
|
||||
std::shared_ptr<WorldList> worldList() const;
|
||||
std::shared_ptr<GameOptions> gameOptionsModel() const;
|
||||
|
||||
////// Launch stuff //////
|
||||
Task::Ptr createUpdateTask(Net::Mode mode) override;
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override;
|
||||
QStringList extraArguments() override;
|
||||
QStringList extraArguments() const override;
|
||||
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override;
|
||||
QList<Mod*> getJarMods() const;
|
||||
QString createLaunchScript(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin);
|
||||
/// get arguments passed to java
|
||||
QStringList javaArguments();
|
||||
QStringList javaArguments() const;
|
||||
|
||||
/// get variables for launch command variable substitution/environment
|
||||
QMap<QString, QString> getVariables() override;
|
||||
QMap<QString, QString> getVariables() const override;
|
||||
|
||||
/// create an environment for launching processes
|
||||
QProcessEnvironment createEnvironment() override;
|
||||
@ -148,16 +103,16 @@ public:
|
||||
QString getStatusbarDescription() override;
|
||||
|
||||
// FIXME: remove
|
||||
virtual QStringList getClassPath();
|
||||
virtual QStringList getClassPath() const;
|
||||
// FIXME: remove
|
||||
virtual QStringList getNativeJars();
|
||||
virtual QStringList getNativeJars() const;
|
||||
// FIXME: remove
|
||||
virtual QString getMainClass() const;
|
||||
|
||||
// FIXME: remove
|
||||
virtual QStringList processMinecraftArgs(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) const;
|
||||
|
||||
virtual JavaVersion getJavaVersion();
|
||||
virtual JavaVersion getJavaVersion() const;
|
||||
|
||||
protected:
|
||||
QMap<QString, QString> createCensorFilterFromSession(AuthSessionPtr session);
|
||||
@ -168,9 +123,9 @@ protected: // data
|
||||
std::shared_ptr<PackProfile> m_components;
|
||||
mutable std::shared_ptr<ModFolderModel> m_loader_mod_list;
|
||||
mutable std::shared_ptr<ModFolderModel> m_core_mod_list;
|
||||
mutable std::shared_ptr<ResourcePackFolderModel> m_resource_pack_list;
|
||||
mutable std::shared_ptr<ShaderPackFolderModel> m_shader_pack_list;
|
||||
mutable std::shared_ptr<TexturePackFolderModel> m_texture_pack_list;
|
||||
mutable std::shared_ptr<ModFolderModel> m_resource_pack_list;
|
||||
mutable std::shared_ptr<ModFolderModel> m_shader_pack_list;
|
||||
mutable std::shared_ptr<ModFolderModel> m_texture_pack_list;
|
||||
mutable std::shared_ptr<WorldList> m_world_list;
|
||||
mutable std::shared_ptr<GameOptions> m_game_options;
|
||||
};
|
||||
|
@ -43,7 +43,7 @@ void MinecraftUpdate::executeTask()
|
||||
m_tasks.clear();
|
||||
// create folders
|
||||
{
|
||||
m_tasks.append(new FoldersTask(m_inst));
|
||||
m_tasks.append(std::make_shared<FoldersTask>(m_inst));
|
||||
}
|
||||
|
||||
// add metadata update task if necessary
|
||||
@ -53,23 +53,23 @@ void MinecraftUpdate::executeTask()
|
||||
auto task = components->getCurrentTask();
|
||||
if(task)
|
||||
{
|
||||
m_tasks.append(task);
|
||||
m_tasks.append(task.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
// libraries download
|
||||
{
|
||||
m_tasks.append(new LibrariesTask(m_inst));
|
||||
m_tasks.append(std::make_shared<LibrariesTask>(m_inst));
|
||||
}
|
||||
|
||||
// FML libraries download and copy into the instance
|
||||
{
|
||||
m_tasks.append(new FMLLibrariesTask(m_inst));
|
||||
m_tasks.append(std::make_shared<FMLLibrariesTask>(m_inst));
|
||||
}
|
||||
|
||||
// assets update
|
||||
{
|
||||
m_tasks.append(new AssetUpdateTask(m_inst));
|
||||
m_tasks.append(std::make_shared<AssetUpdateTask>(m_inst));
|
||||
}
|
||||
|
||||
if(!m_preFailure.isEmpty())
|
||||
|
@ -50,7 +50,7 @@ private:
|
||||
|
||||
private:
|
||||
MinecraftInstance *m_inst = nullptr;
|
||||
QList<Task::Ptr> m_tasks;
|
||||
QList<std::shared_ptr<Task>> m_tasks;
|
||||
QString m_preFailure;
|
||||
int m_currentTask = -1;
|
||||
bool m_abort = false;
|
||||
|
@ -214,7 +214,7 @@ void MojangVersionFormat::readVersionProperties(const QJsonObject &in, VersionFi
|
||||
QObject::tr("The 'minimumLauncherVersion' value of this version (%1) is higher than supported by %3 (%2). It might not work properly!")
|
||||
.arg(out->minimumLauncherVersion)
|
||||
.arg(CURRENT_MINIMUM_LAUNCHER_VERSION)
|
||||
.arg(BuildConfig.LAUNCHER_DISPLAYNAME)
|
||||
.arg(BuildConfig.LAUNCHER_NAME)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -362,8 +362,11 @@ LibraryPtr MojangVersionFormat::libraryFromJson(ProblemContainer & problems, con
|
||||
{
|
||||
qWarning() << filename << "contains an invalid native (skipping)";
|
||||
}
|
||||
// FIXME: Skip unknown platforms
|
||||
out->m_nativeClassifiers[it.key()] = it.value().toString();
|
||||
OpSys opSys = OpSys_fromString(it.key());
|
||||
if (opSys != Os_Other)
|
||||
{
|
||||
out->m_nativeClassifiers[opSys] = it.value().toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (libObj.contains("rules"))
|
||||
@ -392,7 +395,7 @@ QJsonObject MojangVersionFormat::libraryToJson(Library *library)
|
||||
auto iter = library->m_nativeClassifiers.begin();
|
||||
while (iter != library->m_nativeClassifiers.end())
|
||||
{
|
||||
nativeList.insert(iter.key(), iter.value());
|
||||
nativeList.insert(OpSys_toString(iter.key()), iter.value());
|
||||
iter++;
|
||||
}
|
||||
libRoot.insert("natives", nativeList);
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include <QTest>
|
||||
#include <QDebug>
|
||||
|
||||
#include <minecraft/MojangVersionFormat.h>
|
||||
#include "minecraft/MojangVersionFormat.h"
|
||||
|
||||
class MojangVersionFormatTest : public QObject
|
||||
{
|
||||
@ -29,7 +29,7 @@ private
|
||||
slots:
|
||||
void test_Through_Simple()
|
||||
{
|
||||
QJsonDocument doc = readJson(QFINDTESTDATA("testdata/MojangVersionFormat/1.9-simple.json"));
|
||||
QJsonDocument doc = readJson(QFINDTESTDATA("testdata/1.9-simple.json"));
|
||||
auto vfile = MojangVersionFormat::versionFileFromJson(doc, "1.9-simple.json");
|
||||
auto doc2 = MojangVersionFormat::versionFileToJson(vfile);
|
||||
writeJson("1.9-simple-passthorugh.json", doc2);
|
||||
@ -39,7 +39,7 @@ slots:
|
||||
|
||||
void test_Through()
|
||||
{
|
||||
QJsonDocument doc = readJson(QFINDTESTDATA("testdata/MojangVersionFormat/1.9.json"));
|
||||
QJsonDocument doc = readJson(QFINDTESTDATA("testdata/1.9.json"));
|
||||
auto vfile = MojangVersionFormat::versionFileFromJson(doc, "1.9.json");
|
||||
auto doc2 = MojangVersionFormat::versionFileToJson(vfile);
|
||||
writeJson("1.9-passthorugh.json", doc2);
|
46
launcher/minecraft/OpSys.cpp
Normal file
46
launcher/minecraft/OpSys.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
/* 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 "OpSys.h"
|
||||
|
||||
OpSys OpSys_fromString(QString name)
|
||||
{
|
||||
if (name == "freebsd")
|
||||
return Os_FreeBSD;
|
||||
if (name == "linux")
|
||||
return Os_Linux;
|
||||
if (name == "windows")
|
||||
return Os_Windows;
|
||||
if (name == "osx")
|
||||
return Os_OSX;
|
||||
return Os_Other;
|
||||
}
|
||||
|
||||
QString OpSys_toString(OpSys name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case Os_FreeBSD:
|
||||
return "freebsd";
|
||||
case Os_Linux:
|
||||
return "linux";
|
||||
case Os_OSX:
|
||||
return "osx";
|
||||
case Os_Windows:
|
||||
return "windows";
|
||||
default:
|
||||
return "other";
|
||||
}
|
||||
}
|
38
launcher/minecraft/OpSys.h
Normal file
38
launcher/minecraft/OpSys.h
Normal file
@ -0,0 +1,38 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <QString>
|
||||
enum OpSys
|
||||
{
|
||||
Os_Windows,
|
||||
Os_FreeBSD,
|
||||
Os_Linux,
|
||||
Os_OSX,
|
||||
Os_Other
|
||||
};
|
||||
|
||||
OpSys OpSys_fromString(QString);
|
||||
QString OpSys_toString(OpSys);
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
#define currentSystem Os_Windows
|
||||
#elif defined Q_OS_MAC
|
||||
#define currentSystem Os_OSX
|
||||
#elif defined Q_OS_FREEBSD
|
||||
#define currentSystem Os_FreeBSD
|
||||
#else
|
||||
#define currentSystem Os_Linux
|
||||
#endif
|
@ -273,11 +273,6 @@ void PackProfile::scheduleSave()
|
||||
d->m_saveTimer.start();
|
||||
}
|
||||
|
||||
RuntimeContext PackProfile::runtimeContext()
|
||||
{
|
||||
return d->m_instance->runtimeContext();
|
||||
}
|
||||
|
||||
QString PackProfile::componentsFilePath() const
|
||||
{
|
||||
return FS::PathCombine(d->m_instance->instanceRoot(), "mmc-pack.json");
|
||||
@ -789,7 +784,7 @@ bool PackProfile::removeComponent_internal(ComponentPtr patch)
|
||||
return true;
|
||||
}
|
||||
QStringList jar, temp1, temp2, temp3;
|
||||
jarMod->getApplicableFiles(d->m_instance->runtimeContext(), jar, temp1, temp2, temp3, d->m_instance->jarmodsPath().absolutePath());
|
||||
jarMod->getApplicableFiles(currentSystem, jar, temp1, temp2, temp3, d->m_instance->jarmodsPath().absolutePath());
|
||||
QFileInfo finfo (jar[0]);
|
||||
if(finfo.exists())
|
||||
{
|
||||
|
@ -1,36 +1,16 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.
|
||||
* 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
|
||||
@ -124,9 +104,6 @@ public:
|
||||
/// if there is a save scheduled, do it now.
|
||||
void saveNow();
|
||||
|
||||
/// helper method, returns RuntimeContext of instance
|
||||
RuntimeContext runtimeContext();
|
||||
|
||||
signals:
|
||||
void minecraftChanged();
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include <QTest>
|
||||
|
||||
#include <minecraft/ParseUtils.h>
|
||||
#include "minecraft/ParseUtils.h"
|
||||
|
||||
class ParseUtilsTest : public QObject
|
||||
{
|
@ -1,36 +1,16 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.
|
||||
* 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 <QJsonObject>
|
||||
@ -80,10 +60,10 @@ QList<std::shared_ptr<Rule>> rulesFromJsonV4(const QJsonObject &objectWithRules)
|
||||
auto osNameVal = osObj.value("name");
|
||||
if (!osNameVal.isString())
|
||||
continue;
|
||||
QString osName = osNameVal.toString();
|
||||
OpSys requiredOs = OpSys_fromString(osNameVal.toString());
|
||||
QString versionRegex = osObj.value("version").toString();
|
||||
// add a new OS rule
|
||||
rules.append(OsRule::create(action, osName, versionRegex));
|
||||
rules.append(OsRule::create(action, requiredOs, versionRegex));
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
@ -101,7 +81,7 @@ QJsonObject OsRule::toJson()
|
||||
ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow"));
|
||||
QJsonObject osObj;
|
||||
{
|
||||
osObj.insert("name", m_system);
|
||||
osObj.insert("name", OpSys_toString(m_system));
|
||||
if(!m_version_regexp.isEmpty())
|
||||
{
|
||||
osObj.insert("version", m_version_regexp);
|
||||
|
@ -1,36 +1,16 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.
|
||||
* 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
|
||||
@ -39,7 +19,7 @@
|
||||
#include <QList>
|
||||
#include <QJsonObject>
|
||||
#include <memory>
|
||||
#include "RuntimeContext.h"
|
||||
#include "OpSys.h"
|
||||
|
||||
class Library;
|
||||
class Rule;
|
||||
@ -57,7 +37,7 @@ class Rule
|
||||
{
|
||||
protected:
|
||||
RuleAction m_result;
|
||||
virtual bool applies(const Library *parent, const RuntimeContext & runtimeContext) = 0;
|
||||
virtual bool applies(const Library *parent) = 0;
|
||||
|
||||
public:
|
||||
Rule(RuleAction result) : m_result(result)
|
||||
@ -65,9 +45,9 @@ public:
|
||||
}
|
||||
virtual ~Rule() {};
|
||||
virtual QJsonObject toJson() = 0;
|
||||
RuleAction apply(const Library *parent, const RuntimeContext & runtimeContext)
|
||||
RuleAction apply(const Library *parent)
|
||||
{
|
||||
if (applies(parent, runtimeContext))
|
||||
if (applies(parent))
|
||||
return m_result;
|
||||
else
|
||||
return Defer;
|
||||
@ -78,23 +58,23 @@ class OsRule : public Rule
|
||||
{
|
||||
private:
|
||||
// the OS
|
||||
QString m_system;
|
||||
OpSys m_system;
|
||||
// the OS version regexp
|
||||
QString m_version_regexp;
|
||||
|
||||
protected:
|
||||
virtual bool applies(const Library *, const RuntimeContext & runtimeContext)
|
||||
virtual bool applies(const Library *)
|
||||
{
|
||||
return runtimeContext.classifierMatches(m_system);
|
||||
return (m_system == currentSystem);
|
||||
}
|
||||
OsRule(RuleAction result, QString system, QString version_regexp)
|
||||
OsRule(RuleAction result, OpSys system, QString version_regexp)
|
||||
: Rule(result), m_system(system), m_version_regexp(version_regexp)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
virtual QJsonObject toJson();
|
||||
static std::shared_ptr<OsRule> create(RuleAction result, QString system,
|
||||
static std::shared_ptr<OsRule> create(RuleAction result, OpSys system,
|
||||
QString version_regexp)
|
||||
{
|
||||
return std::shared_ptr<OsRule>(new OsRule(result, system, version_regexp));
|
||||
@ -104,7 +84,7 @@ public:
|
||||
class ImplicitRule : public Rule
|
||||
{
|
||||
protected:
|
||||
virtual bool applies(const Library *, const RuntimeContext & runtimeContext)
|
||||
virtual bool applies(const Library *)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -1,34 +0,0 @@
|
||||
#include "VanillaInstanceCreationTask.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "settings/INISettingsObject.h"
|
||||
|
||||
VanillaCreationTask::VanillaCreationTask(BaseVersion::Ptr version, QString loader, BaseVersion::Ptr loader_version)
|
||||
: InstanceCreationTask(), m_version(std::move(version)), m_using_loader(true), m_loader(std::move(loader)), m_loader_version(std::move(loader_version))
|
||||
{}
|
||||
|
||||
bool VanillaCreationTask::createInstance()
|
||||
{
|
||||
setStatus(tr("Creating instance from version %1").arg(m_version->name()));
|
||||
|
||||
auto instance_settings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
||||
instance_settings->suspendSave();
|
||||
{
|
||||
MinecraftInstance inst(m_globalSettings, instance_settings, m_stagingPath);
|
||||
auto components = inst.getPackProfile();
|
||||
components->buildingFromScratch();
|
||||
components->setComponentVersion("net.minecraft", m_version->descriptor(), true);
|
||||
if(m_using_loader)
|
||||
components->setComponentVersion(m_loader, m_loader_version->descriptor());
|
||||
|
||||
inst.setName(name());
|
||||
inst.setIconKey(m_instIcon);
|
||||
}
|
||||
instance_settings->resumeSave();
|
||||
|
||||
return true;
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "InstanceCreationTask.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
class VanillaCreationTask final : public InstanceCreationTask {
|
||||
Q_OBJECT
|
||||
public:
|
||||
VanillaCreationTask(BaseVersion::Ptr version) : InstanceCreationTask(), m_version(std::move(version)) {}
|
||||
VanillaCreationTask(BaseVersion::Ptr version, QString loader, BaseVersion::Ptr loader_version);
|
||||
|
||||
bool createInstance() override;
|
||||
|
||||
private:
|
||||
// Version to update to / create of the instance.
|
||||
BaseVersion::Ptr m_version;
|
||||
|
||||
bool m_using_loader = false;
|
||||
QString m_loader;
|
||||
BaseVersion::Ptr m_loader_version;
|
||||
};
|
@ -51,7 +51,7 @@ static bool isMinecraftVersion(const QString &uid)
|
||||
return uid == "net.minecraft";
|
||||
}
|
||||
|
||||
void VersionFile::applyTo(LaunchProfile *profile, const RuntimeContext & runtimeContext)
|
||||
void VersionFile::applyTo(LaunchProfile *profile)
|
||||
{
|
||||
// Only real Minecraft can set those. Don't let anything override them.
|
||||
if (isMinecraftVersion(uid))
|
||||
@ -77,15 +77,15 @@ void VersionFile::applyTo(LaunchProfile *profile, const RuntimeContext & runtime
|
||||
|
||||
for (auto library : libraries)
|
||||
{
|
||||
profile->applyLibrary(library, runtimeContext);
|
||||
profile->applyLibrary(library);
|
||||
}
|
||||
for (auto mavenFile : mavenFiles)
|
||||
{
|
||||
profile->applyMavenFile(mavenFile, runtimeContext);
|
||||
profile->applyMavenFile(mavenFile);
|
||||
}
|
||||
for (auto agent : agents)
|
||||
{
|
||||
profile->applyAgent(agent, runtimeContext);
|
||||
profile->applyAgent(agent);
|
||||
}
|
||||
profile->applyProblemSeverity(getProblemSeverity());
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user