Compare commits
273 Commits
Author | SHA1 | Date | |
---|---|---|---|
56ce7f5dcd | |||
0ccbc801cf | |||
4c52cc414f | |||
6b45386252 | |||
3acc761419 | |||
dd4c67b654 | |||
0c581cfb62 | |||
703bf9bb7a | |||
a40dee2230 | |||
cab9afa45f | |||
ba5946dc60 | |||
9bad83a551 | |||
a549828655 | |||
c1398a6a1a | |||
e11d1b5202 | |||
abdb846c3f | |||
b0b6dd8f87 | |||
4a971226e4 | |||
b10d4d3b8f | |||
6ed130fc16 | |||
90d4acd1a1 | |||
a42d2afcee | |||
ba020fbd21 | |||
af167e8e67 | |||
be82f4db9e | |||
6a97ac603a | |||
e59d3a339f | |||
db7cb12551 | |||
1049507b3f | |||
1303771b58 | |||
f6605bc3f8 | |||
80ec178d5f | |||
ef76bd355a | |||
b0a8bd7dfe | |||
5d8d7740ba | |||
3e64935844 | |||
7577115c3c | |||
2cb242e9b3 | |||
7aeccbb6b0 | |||
8406c7f431 | |||
8c98cc9458 | |||
715d7d4424 | |||
8e9eca6a97 | |||
5f15f51610 | |||
277de41200 | |||
63bce04648 | |||
76dfb7825a | |||
5cb0e75093 | |||
c730fd6e5f | |||
c2b97c3e3f | |||
e0ab8207ed | |||
ecad388846 | |||
9a120f43c8 | |||
4ff1306e0c | |||
1dd663af6e | |||
06d9821b2c | |||
abb20c65e3 | |||
9fb5674233 | |||
18ac109e5a | |||
14a0e85862 | |||
fa2b3bcc63 | |||
620555d210 | |||
ea3ceb382a | |||
37a30fbc3f | |||
99193a2d7b | |||
abfb99ba3f | |||
fcb311eecd | |||
54e4f88ada | |||
89125fde22 | |||
9f3eed6ca2 | |||
ab82358dcb | |||
75fddd0052 | |||
8a2c5f5b0d | |||
66caac0bbc | |||
ab8d897bd7 | |||
3024dbcf2c | |||
eeae3eca67 | |||
d0cda6d605 | |||
167e32a69f | |||
be2512bb4b | |||
c3f1c13a31 | |||
35cfb41a9c | |||
74cdf5350d | |||
9349232bd4 | |||
1811302deb | |||
e6564aa69f | |||
566a83b245 | |||
9eb9ddc668 | |||
cc5261051f | |||
fa870bc026 | |||
99d569ed0e | |||
a1a7b9c151 | |||
dc6340bf38 | |||
8732bea99b | |||
bbc6b71138 | |||
5fb096d7b9 | |||
cf8680f1ab | |||
115d8b5945 | |||
bd8b61651a | |||
d2ffaee9f8 | |||
8f61633551 | |||
d33d5b847d | |||
c8879df621 | |||
c367769781 | |||
02b44256b2 | |||
b6e722a048 | |||
7f2615b2a5 | |||
a232c2d509 | |||
c8205fda9f | |||
8df88e7fbb | |||
c8092269ba | |||
9d88f07955 | |||
f267375ac2 | |||
d44fa416ca | |||
41d7b27d43 | |||
5f461374b8 | |||
d5576779b7 | |||
c098be40ab | |||
333f7cc320 | |||
9180c751d8 | |||
131a04653f | |||
6e6d495bc7 | |||
2c07f758a0 | |||
251f0efec2 | |||
e8697068fb | |||
269c1bbf58 | |||
9b8493c304 | |||
c389a711ed | |||
382548e0a7 | |||
a90fc3d7fe | |||
48a6380e31 | |||
64ca96f470 | |||
59b3e30821 | |||
92e5e0e95b | |||
9202996c62 | |||
e22d54abd3 | |||
b53ba12fa2 | |||
335115041c | |||
bac67800be | |||
306df9e17f | |||
5f2e768376 | |||
575c92ec47 | |||
4d8bf0b621 | |||
954074942e | |||
341eb16a4c | |||
3a7eeff135 | |||
659f93b1de | |||
ea60e48d9d | |||
6054abaffb | |||
3a1feed723 | |||
85f3fc9944 | |||
87cf38a377 | |||
5e77b548b1 | |||
104de9e795 | |||
424f4a72ff | |||
ec6409914d | |||
6a25cacc3e | |||
b1af689546 | |||
0a5dfeb3d7 | |||
6a180f495f | |||
3672dbc5af | |||
81b50c0387 | |||
87acaa0926 | |||
54d2c91320 | |||
94e7961df0 | |||
0d46ea5c71 | |||
6bd345b1ad | |||
cb384261b8 | |||
75301bec4b | |||
d00c320c00 | |||
e13ca94061 | |||
e02369ba6b | |||
82c35f2746 | |||
92f3154e8f | |||
f66910d054 | |||
1b47132ebb | |||
a89cbf116d | |||
471ea680a5 | |||
51de84407f | |||
d252917792 | |||
dfa5f614aa | |||
f3a244e90a | |||
c7c83a35fa | |||
c7fdfb8116 | |||
c6b1a776dc | |||
8accb6f04e | |||
d1d055564c | |||
2741c58a01 | |||
64399dd8d6 | |||
f2ca11688e | |||
3c0c57359b | |||
062fc79286 | |||
2da565f5d4 | |||
e8373bbf65 | |||
26acc836d9 | |||
9c22af9685 | |||
75d0078a38 | |||
536b1a23fc | |||
cafff5e504 | |||
dd5c4b6864 | |||
a2c85a8531 | |||
de4d757650 | |||
f22cd0e8c7 | |||
702a1da0ac | |||
768007d980 | |||
2e40ab6244 | |||
95182ed74b | |||
8bc6cdf55c | |||
6c0b101fed | |||
9841c0a63d | |||
17d200dc88 | |||
c8fec556c0 | |||
b7f2959353 | |||
4899d3c458 | |||
c311dba465 | |||
75ec4256e2 | |||
bb5a91c179 | |||
8225f1ac92 | |||
06d16c6b13 | |||
90780818ca | |||
a160bd0062 | |||
7e0312493b | |||
ccfd06ad21 | |||
48c2146a42 | |||
abb9fa8cbd | |||
f99245b917 | |||
e35db82c27 | |||
6202525372 | |||
ec66c8fd3d | |||
19804c5718 | |||
fa5fa53592 | |||
da43ed8ce1 | |||
2d1f99b765 | |||
f01b8f29c6 | |||
440e9731e2 | |||
acdb54b88e | |||
00c3336ec8 | |||
a268ac7141 | |||
aedb513c9e | |||
ac3a7acc45 | |||
8409aa2571 | |||
199740cc61 | |||
a0f76cba62 | |||
5b8003cbe5 | |||
c0719102a0 | |||
9d8bbf34e6 | |||
ec9d0e70fb | |||
f1e44291cc | |||
b3b613d8b4 | |||
177b685557 | |||
71a4333f55 | |||
f1c21a912a | |||
84a142096f | |||
bb2b344d33 | |||
b96572774f | |||
641a96e4a9 | |||
4fe13c64a1 | |||
b3c2a56ece | |||
9c57b54a81 | |||
b131d3b2ec | |||
16bfafa29e | |||
f714adf6d2 | |||
39bd04f06f | |||
d755174bee | |||
1229e90817 | |||
5a638fa977 | |||
5e9d49a910 | |||
9a8599e4ba | |||
2d68308d49 | |||
0dd1c26cf3 | |||
881b2f2b38 | |||
6d1f9d4d02 | |||
da70122d9c |
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -1,6 +1,6 @@
|
||||
name: Bug Report
|
||||
description: File a bug report
|
||||
labels: [bug, needs-triage]
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@ -8,7 +8,7 @@ body:
|
||||
If you need help with running Minecraft, please visit us on our Discord before making a bug report.
|
||||
|
||||
Before submitting a bug report, please make sure you have read this *entire* form, and that:
|
||||
* You have read the [FAQ](https://github.com/PolyMC/PolyMC/wiki/FAQ) and it has not answered your question
|
||||
* You have read the [PolyMC wiki](https://polymc.org/wiki/) and it has not answered your question.
|
||||
* Your bug is not caused by Minecraft or any mods you have installed.
|
||||
* Your issue has not been reported before, [make sure to use the search function!](https://github.com/PolyMC/PolyMC/issues)
|
||||
|
||||
|
5
.github/ISSUE_TEMPLATE/rfc.yml
vendored
5
.github/ISSUE_TEMPLATE/rfc.yml
vendored
@ -1,7 +1,7 @@
|
||||
# Template based on https://gitlab.archlinux.org/archlinux/rfcs/-/blob/0ba3b61e987e197f8d1901709409b8564958f78a/rfcs/0000-template.rst
|
||||
name: Request for Comment (RFC)
|
||||
description: Propose a larger change and start a discussion.
|
||||
labels: [RFC]
|
||||
labels: [rfc]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@ -21,8 +21,7 @@ body:
|
||||
Introduce the topic. If this is a not-well-known section of PolyMC, a detailed explanation of the background is recommended.
|
||||
Some example points of discussion:
|
||||
- What specific problems are you facing right now that you're trying to address?
|
||||
- Are there any previous discussions? Link to them and summarize them (don't
|
||||
- force your readers to read them though!).
|
||||
- Are there any previous discussions? Link to them and summarize them (don't force your readers to read them though!).
|
||||
- Is there any precedent set by other software? If so, link to resources.
|
||||
placeholder: I don't like cats. I think many users also don't like cats.
|
||||
validations:
|
||||
|
2
.github/ISSUE_TEMPLATE/suggestion.yml
vendored
2
.github/ISSUE_TEMPLATE/suggestion.yml
vendored
@ -1,6 +1,6 @@
|
||||
name: Suggestion
|
||||
description: Make a suggestion
|
||||
labels: [idea, needs-triage]
|
||||
labels: [enhancement]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
19
.github/workflows/backport.yml
vendored
Normal file
19
.github/workflows/backport.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
name: Backport PR to stable
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ develop ]
|
||||
types: [ closed ]
|
||||
jobs:
|
||||
release_pull_request:
|
||||
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'backport')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Backport PR by cherry-pick-ing
|
||||
uses: Nathanmalnoury/gh-backport-action@master
|
||||
with:
|
||||
pr_branch: 'stable'
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
245
.github/workflows/build.yml
vendored
245
.github/workflows/build.yml
vendored
@ -16,47 +16,31 @@ jobs:
|
||||
include:
|
||||
|
||||
- os: ubuntu-20.04
|
||||
qt_version: 5.12.8
|
||||
qt_host: linux
|
||||
|
||||
- os: ubuntu-20.04
|
||||
qt_version: 5.15.2
|
||||
qt_host: linux
|
||||
app_image: true
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-i686"
|
||||
msystem: mingw32
|
||||
portable: false
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-x86_64"
|
||||
msystem: mingw64
|
||||
portable: false
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-i686-portable"
|
||||
msystem: mingw32
|
||||
portable: true
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-x86_64-portable"
|
||||
msystem: mingw64
|
||||
portable: true
|
||||
|
||||
- os: macos-11
|
||||
qt_version: 5.12.12
|
||||
qt_host: mac
|
||||
macosx_deployment_target: 10.12
|
||||
macosx_deployment_target: 10.13
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }}
|
||||
INSTALL_DIR: "install"
|
||||
INSTALL_PORTABLE_DIR: "install-portable"
|
||||
INSTALL_APPIMAGE_DIR: "install-appdir"
|
||||
BUILD_DIR: "build"
|
||||
|
||||
steps:
|
||||
##
|
||||
# PREPARE
|
||||
##
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
@ -88,26 +72,14 @@ jobs:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
|
||||
- name: Cache Qt
|
||||
if: runner.os != 'Windows'
|
||||
id: cache-qt
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: "${{ github.workspace }}/Qt/"
|
||||
key: ${{ runner.os }}-${{ matrix.qt_version }}-${{ matrix.qt_arch }}-qt_cache
|
||||
- name: Install Qt (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew update
|
||||
brew install qt@5
|
||||
|
||||
- name: Install Qt
|
||||
if: runner.os != 'Linux' && runner.os != 'Windows' || matrix.app_image == true
|
||||
uses: jurplel/install-qt-action@v2
|
||||
with:
|
||||
version: ${{ matrix.qt_version }}
|
||||
host: ${{ matrix.qt_host }}
|
||||
arch: ${{ matrix.qt_arch }}
|
||||
cached: ${{ steps.cache-qt.outputs.cache-hit }}
|
||||
dir: "${{ github.workspace }}/Qt/"
|
||||
|
||||
- name: Install System Qt on Linux
|
||||
if: runner.os == 'Linux' && matrix.app_image != true
|
||||
- name: Install Qt (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
|
||||
@ -116,152 +88,169 @@ jobs:
|
||||
if: runner.os != 'Windows'
|
||||
uses: urkle/action-get-ninja@v1
|
||||
|
||||
- name: Download linuxdeploy family for AppImage on Linux
|
||||
if: matrix.app_image == true
|
||||
- name: Prepare AppImage (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
wget "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage"
|
||||
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage"
|
||||
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage"
|
||||
|
||||
- name: Download JREs for AppImage on Linux
|
||||
if: matrix.app_image == true
|
||||
shell: bash
|
||||
run: |
|
||||
${{ github.workspace }}/.github/scripts/prepare_JREs.sh
|
||||
|
||||
- name: Configure CMake
|
||||
if: runner.os != 'Linux' && runner.os != 'Windows'
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -G Ninja
|
||||
##
|
||||
# CONFIGURE
|
||||
##
|
||||
|
||||
- name: Configure CMake on Windows
|
||||
if: runner.os == 'Windows' && matrix.portable != true
|
||||
- name: Configure CMake (macOS)
|
||||
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 -DQt5_DIR=/usr/local/opt/qt@5 -DCMAKE_PREFIX_PATH=/usr/local/opt/qt@5 -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 }} -DLauncher_PORTABLE=OFF -G Ninja
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -G Ninja
|
||||
|
||||
|
||||
- name: Configure CMake on Windows portable
|
||||
if: runner.os == 'Windows' && matrix.portable == true
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -G Ninja
|
||||
|
||||
- name: Configure CMake on Linux
|
||||
- name: Configure CMake (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DLauncher_PORTABLE=OFF -G Ninja
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -G Ninja
|
||||
|
||||
##
|
||||
# BUILD
|
||||
##
|
||||
|
||||
- name: Build
|
||||
if: runner.os != 'Windows'
|
||||
run: |
|
||||
cmake --build ${{ env.BUILD_DIR }}
|
||||
|
||||
- name: Build on Windows
|
||||
- name: Build (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
cmake --build ${{ env.BUILD_DIR }}
|
||||
|
||||
- name: Install
|
||||
if: runner.os != 'Linux' && runner.os != 'Windows'
|
||||
##
|
||||
# PACKAGE BUILDS
|
||||
##
|
||||
|
||||
- name: Package (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }}
|
||||
|
||||
- name: Install on Windows
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
chmod +x "PolyMC.app/Contents/MacOS/polymc"
|
||||
tar -czf ../PolyMC.tar.gz *
|
||||
|
||||
- name: Package (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }}
|
||||
|
||||
- name: Install on Linux
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
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)
|
||||
if: runner.os == 'Windows'
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||
|
||||
- name: Package (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
DESTDIR=${{ env.INSTALL_DIR }} cmake --install ${{ env.BUILD_DIR }}
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }}
|
||||
|
||||
- name: Bundle AppImage
|
||||
if: matrix.app_image == true
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
tar -czf ../PolyMC.tar.gz *
|
||||
|
||||
- name: Package (Linux, portable)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||
|
||||
cd ${{ env.INSTALL_PORTABLE_DIR }}
|
||||
tar -czf ../PolyMC-portable.tar.gz *
|
||||
|
||||
- name: Package AppImage (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
shell: bash
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
|
||||
|
||||
export OUTPUT="PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
|
||||
|
||||
chmod +x linuxdeploy-*.AppImage
|
||||
|
||||
mkdir -p ${{ env.INSTALL_DIR }}/usr/lib/jvm/java-{8,17}-openjdk
|
||||
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-{8,17}-openjdk
|
||||
|
||||
cp -r ${{ github.workspace }}/JREs/jre8/* ${{ env.INSTALL_DIR }}/usr/lib/jvm/java-8-openjdk
|
||||
cp -r ${{ github.workspace }}/JREs/jre8/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk
|
||||
|
||||
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_DIR }}/usr/lib/jvm/java-17-openjdk
|
||||
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk
|
||||
|
||||
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_DIR }}/usr/lib"
|
||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server"
|
||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64"
|
||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_DIR }}/usr/lib/jvm/java-17-openjdk/lib/server"
|
||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
|
||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
|
||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server"
|
||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64"
|
||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib/server"
|
||||
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_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_DIR }}/usr/share/icons/hicolor/scalable/apps/org.polymc.PolyMC.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
|
||||
|
||||
- name: Run macdeployqt
|
||||
##
|
||||
# UPLOAD BUILDS
|
||||
##
|
||||
|
||||
- name: Upload binary tarball (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
macdeployqt "PolyMC.app" -executable="PolyMC.app/Contents/MacOS/polymc" -always-overwrite
|
||||
|
||||
- name: chmod binary on macOS
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
chmod +x "${{ github.workspace }}/${{ env.INSTALL_DIR }}/PolyMC.app/Contents/MacOS/polymc"
|
||||
|
||||
- name: tar bundle on macOS
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
tar -czf ../PolyMC.tar.gz *
|
||||
|
||||
- name: tar on Linux
|
||||
if: runner.os == 'Linux' && matrix.app_image != true
|
||||
run: |
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
tar -czf ../PolyMC.tar.gz *
|
||||
|
||||
- name: Upload Linux tar.gz
|
||||
if: runner.os == 'Linux' && matrix.app_image != true
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PolyMC.tar.gz
|
||||
|
||||
- name: Upload AppImage for Linux
|
||||
if: matrix.app_image == true
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||
path: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||
|
||||
- name: Copy OpenSSL libs on Windows x86
|
||||
if: runner.os == 'Windows' && matrix.msystem == 'mingw32'
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
cp /mingw32/bin/libcrypto-1_1.dll ${{ env.INSTALL_DIR }}/
|
||||
cp /mingw32/bin/libssl-1_1.dll ${{ env.INSTALL_DIR }}/
|
||||
|
||||
- name: Copy OpenSSL libs on Windows x86_64
|
||||
if: runner.os == 'Windows' && matrix.msystem == 'mingw64'
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
cp /mingw64/bin/libcrypto-1_1-x64.dll ${{ env.INSTALL_DIR }}/
|
||||
cp /mingw64/bin/libssl-1_1-x64.dll ${{ env.INSTALL_DIR }}/
|
||||
|
||||
- name: Upload package for Windows
|
||||
- name: Upload binary zip (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PolyMC-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: ${{ env.INSTALL_DIR }}/**
|
||||
|
||||
- name: Upload package for macOS
|
||||
if: runner.os == 'macOS'
|
||||
- name: Upload binary zip (Windows, portable)
|
||||
if: runner.os == 'Windows'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PolyMC-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: ${{ env.INSTALL_PORTABLE_DIR }}/**
|
||||
|
||||
- name: Upload binary tarball (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PolyMC.tar.gz
|
||||
|
||||
- name: Upload binary tarball (Linux, portable)
|
||||
if: runner.os == 'Linux'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PolyMC-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PolyMC-portable.tar.gz
|
||||
|
||||
- name: Upload AppImage (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||
path: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||
|
||||
|
||||
|
61
.github/workflows/pr-comment.yml
vendored
Normal file
61
.github/workflows/pr-comment.yml
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
name: Comment on pull request
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ['Test workflow with upload']
|
||||
types: [completed]
|
||||
jobs:
|
||||
pr_comment:
|
||||
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/github-script@v5
|
||||
with:
|
||||
# This snippet is public-domain, taken from
|
||||
# https://github.com/oprypin/nightly.link/blob/master/.github/workflows/pr-comment.yml
|
||||
script: |
|
||||
async function upsertComment(owner, repo, issue_number, purpose, body) {
|
||||
const {data: comments} = await github.rest.issues.listComments(
|
||||
{owner, repo, issue_number});
|
||||
|
||||
const marker = `<!-- bot: ${purpose} -->`;
|
||||
body = marker + "\n" + body;
|
||||
|
||||
const existing = comments.filter((c) => c.body.includes(marker));
|
||||
if (existing.length > 0) {
|
||||
const last = existing[existing.length - 1];
|
||||
core.info(`Updating comment ${last.id}`);
|
||||
await github.rest.issues.updateComment({
|
||||
owner, repo,
|
||||
body,
|
||||
comment_id: last.id,
|
||||
});
|
||||
} else {
|
||||
core.info(`Creating a comment in issue / PR #${issue_number}`);
|
||||
await github.rest.issues.createComment({issue_number, body, owner, repo});
|
||||
}
|
||||
}
|
||||
|
||||
const {owner, repo} = context.repo;
|
||||
const run_id = ${{github.event.workflow_run.id}};
|
||||
|
||||
const pull_requests = ${{ toJSON(github.event.workflow_run.pull_requests) }};
|
||||
if (!pull_requests.length) {
|
||||
return core.error("This workflow doesn't match any pull requests!");
|
||||
}
|
||||
|
||||
const artifacts = await github.paginate(
|
||||
github.rest.actions.listWorkflowRunArtifacts, {owner, repo, run_id});
|
||||
if (!artifacts.length) {
|
||||
return core.error(`No artifacts found`);
|
||||
}
|
||||
let body = `Download the artifacts for this pull request:\n`;
|
||||
for (const art of artifacts) {
|
||||
body += `\n* [${art.name}.zip](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||
}
|
||||
|
||||
core.info("Review thread message body:", body);
|
||||
|
||||
for (const pr of pull_requests) {
|
||||
await upsertComment(owner, repo, pr.number,
|
||||
"nightly-link", body);
|
||||
}
|
10
.github/workflows/trigger_builds.yml
vendored
10
.github/workflows/trigger_builds.yml
vendored
@ -9,12 +9,16 @@ on:
|
||||
- '**/LICENSE'
|
||||
- 'flake.lock'
|
||||
- '**.nix'
|
||||
- 'packages/**'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**/LICENSE'
|
||||
- 'flake.lock'
|
||||
- '**.nix'
|
||||
- 'packages/**'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@ -24,9 +28,3 @@ jobs:
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
build_type: Debug
|
||||
|
||||
build_release:
|
||||
name: Build Release
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
build_type: Release
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -42,3 +42,7 @@ run/
|
||||
|
||||
# Nix/NixOS
|
||||
result/
|
||||
|
||||
# Flatpak
|
||||
.flatpak-builder
|
||||
flatbuild
|
||||
|
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -1,7 +1,7 @@
|
||||
[submodule "depends/libnbtplusplus"]
|
||||
path = libraries/libnbtplusplus
|
||||
url = https://github.com/MultiMC/libnbtplusplus.git
|
||||
pushurl = git@github.com:MultiMC/libnbtplusplus.git
|
||||
url = https://github.com/PolyMC/libnbtplusplus.git
|
||||
pushurl = git@github.com:PolyMC/libnbtplusplus.git
|
||||
|
||||
[submodule "libraries/quazip"]
|
||||
path = libraries/quazip
|
||||
|
@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
cmake_minimum_required(VERSION 3.9.4)
|
||||
|
||||
if(WIN32)
|
||||
# In Qt 5.1+ we have our own main() function, don't autolink to qtmain on Windows
|
||||
@ -6,7 +6,7 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
project(Launcher)
|
||||
enable_testing()
|
||||
include(CTest)
|
||||
|
||||
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
|
||||
if(IS_IN_SOURCE_BUILD)
|
||||
@ -43,30 +43,43 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type")
|
||||
# Fix build with Qt 5.13
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y")
|
||||
|
||||
option(ENABLE_LTO "Enable Link Time Optimization" off)
|
||||
|
||||
if(ENABLE_LTO)
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT ipo_supported OUTPUT ipo_error)
|
||||
|
||||
if(ipo_supported AND (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel"))
|
||||
message(STATUS "IPO / LTO enabled")
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
elseif(ipo_supported)
|
||||
message(STATUS "Not enabling IPO / LTO on debug builds")
|
||||
else()
|
||||
message(STATUS "IPO / LTO not supported: <${ipo_error}>")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
##################################### Set Application options #####################################
|
||||
|
||||
######## Set URLs ########
|
||||
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/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
||||
set(Launcher_HELP_URL "https://polymc.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
||||
|
||||
######## Set version numbers ########
|
||||
set(Launcher_VERSION_MAJOR 1)
|
||||
set(Launcher_VERSION_MINOR 1)
|
||||
set(Launcher_VERSION_HOTFIX 1)
|
||||
set(Launcher_VERSION_MINOR 2)
|
||||
set(Launcher_VERSION_HOTFIX 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 by the notification system and to display in the about dialog.")
|
||||
set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.")
|
||||
|
||||
# Channel list URL
|
||||
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
|
||||
|
||||
# Notification URL
|
||||
set(Launcher_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.")
|
||||
|
||||
# The metadata server
|
||||
set(Launcher_META_URL "https://meta.polymc.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
|
||||
|
||||
@ -83,7 +96,7 @@ set(Launcher_BUG_TRACKER_URL "https://github.com/PolyMC/PolyMC/issues" CACHE STR
|
||||
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/#/#polymc:polymc.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/Z52pwxWCHP" CACHE STRING "URL for the Discord guild.")
|
||||
@ -91,11 +104,10 @@ set(Launcher_DISCORD_URL "https://discord.gg/Z52pwxWCHP" CACHE STRING "URL for t
|
||||
|
||||
|
||||
# Subreddit URL
|
||||
set(Launcher_SUBREDDIT_URL "" CACHE STRING "URL for the subreddit.")
|
||||
set(Launcher_SUBREDDIT_URL "https://www.reddit.com/r/PolyMCLauncher/" CACHE STRING "URL for the subreddit.")
|
||||
|
||||
# Builds
|
||||
# TODO: Launcher_FORCE_BUNDLED_LIBS should be off in the future, but as of QuaZip 1.2, we can't do that yet.
|
||||
set(Launcher_FORCE_BUNDLED_LIBS ON CACHE BOOL "Prevent using system libraries, if they are available as submodules")
|
||||
set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
|
||||
set(Launcher_QT_VERSION_MAJOR "5" CACHE STRING "Major Qt version to build against")
|
||||
|
||||
|
||||
@ -122,7 +134,7 @@ if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
||||
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml)
|
||||
|
||||
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
||||
find_package(QuaZip-Qt5 REQUIRED)
|
||||
find_package(QuaZip-Qt5 1.3)
|
||||
endif()
|
||||
if (NOT QuaZip-Qt5_FOUND)
|
||||
set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE)
|
||||
@ -152,12 +164,9 @@ add_subdirectory(program_info)
|
||||
|
||||
####################################### Install layout #######################################
|
||||
|
||||
# Install the build results according to platform
|
||||
set(Launcher_PORTABLE 1 CACHE BOOL "The type of installation (Portable or System)")
|
||||
|
||||
if (Launcher_PORTABLE)
|
||||
# launcher/Application.cpp will use this value
|
||||
set(Launcher_APP_BINARY_DEFS "-DLAUNCHER_PORTABLE")
|
||||
if(NOT (UNIX AND APPLE))
|
||||
# Install "portable.txt" if selected component is "portable"
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_Portable_File}" DESTINATION "." COMPONENT portable EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
if(UNIX AND APPLE)
|
||||
@ -167,8 +176,6 @@ if(UNIX AND APPLE)
|
||||
set(RESOURCES_DEST_DIR "${Launcher_Name}.app/Contents/Resources")
|
||||
set(JARS_DEST_DIR "${Launcher_Name}.app/Contents/MacOS/jars")
|
||||
|
||||
set(BUNDLE_DEST_DIR ".")
|
||||
|
||||
# Apps to bundle
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.app")
|
||||
|
||||
@ -193,28 +200,12 @@ if(UNIX AND APPLE)
|
||||
|
||||
elseif(UNIX)
|
||||
set(BINARY_DEST_DIR "bin")
|
||||
if(Launcher_PORTABLE)
|
||||
set(LIBRARY_DEST_DIR "bin")
|
||||
set(BUNDLE_DEST_DIR ".")
|
||||
set(JARS_DEST_DIR "bin/jars")
|
||||
|
||||
# Install basic runner script
|
||||
configure_file(launcher/Launcher.in "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" @ONLY)
|
||||
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" DESTINATION ${BUNDLE_DEST_DIR} RENAME ${Launcher_Name})
|
||||
else()
|
||||
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
|
||||
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")
|
||||
|
||||
# jars path is determined on runtime, relative to "Application root path", generally /usr for Launcher_PORTABLE=0
|
||||
set(Launcher_APP_BINARY_DEFS "-DLAUNCHER_JARS_LOCATION=${JARS_DEST_DIR}")
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${LAUNCHER_DESKTOP_DEST_DIR})
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${LAUNCHER_METAINFO_DEST_DIR})
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION ${LAUNCHER_ICON_DEST_DIR})
|
||||
endif()
|
||||
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
|
||||
set(JARS_DEST_DIR "share/jars")
|
||||
set(LAUNCHER_DESKTOP_DEST_DIR "share/applications" CACHE STRING "Path to the desktop file directory")
|
||||
set(LAUNCHER_METAINFO_DEST_DIR "share/metainfo" CACHE STRING "Path to the metainfo directory")
|
||||
set(LAUNCHER_ICON_DEST_DIR "share/icons/hicolor/scalable/apps" CACHE STRING "Path to the scalable icon directory")
|
||||
set(LAUNCHER_MAN_DEST_DIR "share/man/man6" CACHE STRING "Path to the man page directory")
|
||||
|
||||
# install as bundle with no dependencies included
|
||||
set(INSTALL_BUNDLE "nodeps")
|
||||
@ -222,11 +213,22 @@ elseif(UNIX)
|
||||
# Set RPATH
|
||||
SET(Launcher_BINARY_RPATH "$ORIGIN/")
|
||||
|
||||
# jars path is determined on runtime, relative to "Application root path", generally /usr or the root of the portable bundle
|
||||
set(Launcher_APP_BINARY_DEFS "-DLAUNCHER_JARS_LOCATION=${JARS_DEST_DIR}")
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${LAUNCHER_DESKTOP_DEST_DIR})
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${LAUNCHER_METAINFO_DEST_DIR})
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION ${LAUNCHER_ICON_DEST_DIR})
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_ManPage} DESTINATION ${LAUNCHER_MAN_DEST_DIR} RENAME "${Launcher_APP_BINARY_NAME}.6")
|
||||
|
||||
# Install basic runner script if component "portable" is selected
|
||||
configure_file(launcher/Launcher.in "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" @ONLY)
|
||||
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" DESTINATION "." RENAME ${Launcher_Name} COMPONENT portable EXCLUDE_FROM_ALL)
|
||||
|
||||
elseif(WIN32)
|
||||
set(BINARY_DEST_DIR ".")
|
||||
set(LIBRARY_DEST_DIR ".")
|
||||
set(PLUGIN_DEST_DIR ".")
|
||||
set(BUNDLE_DEST_DIR ".")
|
||||
set(RESOURCES_DEST_DIR ".")
|
||||
set(JARS_DEST_DIR "jars")
|
||||
|
||||
@ -262,6 +264,8 @@ if (FORCE_BUNDLED_QUAZIP)
|
||||
set(BUILD_SHARED_LIBS 0) # link statically to avoid conflicts.
|
||||
set(QUAZIP_INSTALL 0)
|
||||
add_subdirectory(libraries/quazip) # zip manipulation library
|
||||
else()
|
||||
message(STATUS "Using system QuaZip")
|
||||
endif()
|
||||
add_subdirectory(libraries/rainbow) # Qt extension for colors
|
||||
add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader
|
||||
|
30
README.md
30
README.md
@ -33,14 +33,22 @@ Feel free to create an issue if you need help. However, you might find it easier
|
||||
|
||||
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:polymc.org)
|
||||
[](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/#/#support:polymc.org)
|
||||
[](https://matrix.to/#/#discussion:polymc.org)
|
||||
[](https://matrix.to/#/#development:polymc.org)
|
||||
[](https://matrix.to/#/#news:polymc.org)
|
||||
[](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
|
||||
|
||||
@ -48,7 +56,7 @@ If you want to contribute to PolyMC you might find it useful to join our Discord
|
||||
|
||||
## Building
|
||||
|
||||
If you want to build PolyMC yourself, check [BUILD.md](BUILD.md) for build instructions.
|
||||
If you want to build PolyMC yourself, check [Build Instructions](https://polymc.org/wiki/development/build-instructions/) for build instructions.
|
||||
|
||||
## Code formatting
|
||||
|
||||
@ -66,9 +74,15 @@ In general, in order of importance:
|
||||
|
||||
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 infomation
|
||||
To modify download infomation or change packaging infomation send a pull request or issue to the website [Here](https://github.com/PolyMC/polymc.github.io/blob/master/src/download.md)
|
||||
## Download information
|
||||
To modify download information or change packaging information send a pull request or issue to the website [Here](https://github.com/PolyMC/polymc.github.io/blob/master/src/download.md)
|
||||
|
||||
## Forking/Redistributing/Custom builds policy
|
||||
|
||||
Do whatever you want, we don't care. Just follow the license. If you have any questions about this feel free to ask in an issue.
|
||||
|
||||
All launcher code is available under the GPL-3 license.
|
||||
|
||||
[Source for the website](https://github.com/PolyMC/polymc.github.io) is hosted under the AGPL-3 License.
|
||||
|
||||
The logo and related assets are under the CC BY-SA 4.0 license.
|
||||
|
@ -60,8 +60,6 @@ Config::Config()
|
||||
|
||||
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
|
||||
UPDATER_BASE = "@Launcher_UPDATER_BASE@";
|
||||
NOTIFICATION_URL = "@Launcher_NOTIFICATION_URL@";
|
||||
FULL_VERSION_STR = "@Launcher_VERSION_MAJOR@.@Launcher_VERSION_MINOR@.@Launcher_VERSION_BUILD@";
|
||||
|
||||
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
||||
GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";
|
||||
|
@ -81,13 +81,6 @@ public:
|
||||
/// User-Agent to use for uncached requests.
|
||||
QString USER_AGENT_UNCACHED;
|
||||
|
||||
|
||||
/// URL for notifications
|
||||
QString NOTIFICATION_URL;
|
||||
|
||||
/// Used for matching notifications
|
||||
QString FULL_VERSION_STR;
|
||||
|
||||
/// The git commit hash of this build
|
||||
QString GIT_COMMIT;
|
||||
|
||||
@ -147,6 +140,12 @@ public:
|
||||
|
||||
QString ATL_DOWNLOAD_SERVER_URL = "https://download.nodecdn.net/containers/atl/";
|
||||
|
||||
QString TECHNIC_API_BASE_URL = "https://api.technicpack.net/";
|
||||
/**
|
||||
* The build that is reported to the Technic API.
|
||||
*/
|
||||
QString TECHNIC_API_BUILD = "multimc";
|
||||
|
||||
/**
|
||||
* \brief Converts the Version to a string.
|
||||
* \return The version number in string format (major.minor.revision.build).
|
||||
|
@ -5,44 +5,46 @@ set(TEST_RESOURCE_PATH ${CMAKE_CURRENT_LIST_DIR})
|
||||
message(${TEST_RESOURCE_PATH})
|
||||
|
||||
function(add_unit_test name)
|
||||
set(options "")
|
||||
set(oneValueArgs DATA)
|
||||
set(multiValueArgs SOURCES LIBS)
|
||||
if(BUILD_TESTING)
|
||||
set(options "")
|
||||
set(oneValueArgs DATA)
|
||||
set(multiValueArgs SOURCES LIBS)
|
||||
|
||||
cmake_parse_arguments(OPT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
|
||||
cmake_parse_arguments(OPT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
|
||||
|
||||
if(WIN32)
|
||||
add_executable(${name}_test ${OPT_SOURCES} ${TEST_RESOURCE_PATH}/UnitTest/test.rc)
|
||||
else()
|
||||
add_executable(${name}_test ${OPT_SOURCES})
|
||||
endif()
|
||||
|
||||
if(NOT "${OPT_DATA}" STREQUAL "")
|
||||
set(TEST_DATA_PATH "${CMAKE_CURRENT_BINARY_DIR}/data")
|
||||
set(TEST_DATA_PATH_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${OPT_DATA}")
|
||||
message("From ${TEST_DATA_PATH_SRC} to ${TEST_DATA_PATH}")
|
||||
string(REGEX REPLACE "[/\\:]" "_" DATA_TARGET_NAME "${TEST_DATA_PATH_SRC}")
|
||||
if(UNIX)
|
||||
# on unix we get the third / from the filename
|
||||
set(TEST_DATA_URL "file://${TEST_DATA_PATH}")
|
||||
if(WIN32)
|
||||
add_executable(${name}_test ${OPT_SOURCES} ${TEST_RESOURCE_PATH}/UnitTest/test.rc)
|
||||
else()
|
||||
# we don't on windows, so we have to add it ourselves
|
||||
set(TEST_DATA_URL "file:///${TEST_DATA_PATH}")
|
||||
add_executable(${name}_test ${OPT_SOURCES})
|
||||
endif()
|
||||
if(NOT TARGET "${DATA_TARGET_NAME}")
|
||||
add_custom_target(${DATA_TARGET_NAME})
|
||||
add_dependencies(${name}_test ${DATA_TARGET_NAME})
|
||||
add_custom_command(
|
||||
TARGET ${DATA_TARGET_NAME}
|
||||
COMMAND ${CMAKE_COMMAND} "-DTEST_DATA_URL=${TEST_DATA_URL}" -DSOURCE=${TEST_DATA_PATH_SRC} -DDESTINATION=${TEST_DATA_PATH} -P ${TEST_RESOURCE_PATH}/UnitTest/generate_test_data.cmake
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
if(NOT "${OPT_DATA}" STREQUAL "")
|
||||
set(TEST_DATA_PATH "${CMAKE_CURRENT_BINARY_DIR}/data")
|
||||
set(TEST_DATA_PATH_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${OPT_DATA}")
|
||||
message("From ${TEST_DATA_PATH_SRC} to ${TEST_DATA_PATH}")
|
||||
string(REGEX REPLACE "[/\\:]" "_" DATA_TARGET_NAME "${TEST_DATA_PATH_SRC}")
|
||||
if(UNIX)
|
||||
# on unix we get the third / from the filename
|
||||
set(TEST_DATA_URL "file://${TEST_DATA_PATH}")
|
||||
else()
|
||||
# we don't on windows, so we have to add it ourselves
|
||||
set(TEST_DATA_URL "file:///${TEST_DATA_PATH}")
|
||||
endif()
|
||||
if(NOT TARGET "${DATA_TARGET_NAME}")
|
||||
add_custom_target(${DATA_TARGET_NAME})
|
||||
add_dependencies(${name}_test ${DATA_TARGET_NAME})
|
||||
add_custom_command(
|
||||
TARGET ${DATA_TARGET_NAME}
|
||||
COMMAND ${CMAKE_COMMAND} "-DTEST_DATA_URL=${TEST_DATA_URL}" -DSOURCE=${TEST_DATA_PATH_SRC} -DDESTINATION=${TEST_DATA_PATH} -P ${TEST_RESOURCE_PATH}/UnitTest/generate_test_data.cmake
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_link_libraries(${name}_test Qt5::Test ${OPT_LIBS})
|
||||
|
||||
target_include_directories(${name}_test PRIVATE "${TEST_RESOURCE_PATH}/UnitTest/")
|
||||
|
||||
add_test(NAME ${name} COMMAND ${name}_test WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
target_link_libraries(${name}_test Qt5::Test ${OPT_LIBS})
|
||||
|
||||
target_include_directories(${name}_test PRIVATE "${TEST_RESOURCE_PATH}/UnitTest/")
|
||||
|
||||
add_test(NAME ${name} COMMAND ${name}_test WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endfunction()
|
||||
|
30
flake.lock
generated
30
flake.lock
generated
@ -3,11 +3,11 @@
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1641205782,
|
||||
"narHash": "sha256-4jY7RCWUoZ9cKD8co0/4tFARpWB+57+r1bLLvXNJliY=",
|
||||
"lastModified": 1648199409,
|
||||
"narHash": "sha256-JwPKdC2PoVBkG6E+eWw3j6BMR6sL3COpYWfif7RVb8Y=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "b7547d3eed6f32d06102ead8991ec52ab0a4f1a7",
|
||||
"rev": "64a525ee38886ab9028e6f61790de0832aa3ef03",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -16,21 +16,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"locked": {
|
||||
"lastModified": 1642700792,
|
||||
"narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "846b2ae0fc4cc943637d3d1def4454213e203cba",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"libnbtplusplus": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
@ -49,16 +34,16 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1643169865,
|
||||
"narHash": "sha256-+KIpNRazbc8Gac9jdWCKQkFv9bjceaLaLhlwqUEYu8c=",
|
||||
"lastModified": 1648219316,
|
||||
"narHash": "sha256-Ctij+dOi0ZZIfX5eMhgwugfvB+WZSrvVNAyAuANOsnQ=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "945ec499041db73043f745fad3b2a3a01e826081",
|
||||
"rev": "30d3d79b7d3607d56546dd2a6b49e156ba0ec634",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
@ -82,7 +67,6 @@
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-utils": "flake-utils",
|
||||
"libnbtplusplus": "libnbtplusplus",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"quazip": "quazip"
|
||||
|
74
flake.nix
74
flake.nix
@ -1,50 +1,34 @@
|
||||
{
|
||||
description = "PolyMC flake";
|
||||
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
inputs.flake-compat = {
|
||||
url = "github:edolstra/flake-compat";
|
||||
flake = false;
|
||||
};
|
||||
inputs.libnbtplusplus = {
|
||||
url = "github:multimc/libnbtplusplus";
|
||||
flake = false;
|
||||
};
|
||||
inputs.quazip = {
|
||||
url = "github:stachenov/quazip";
|
||||
flake = false;
|
||||
description = "A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once (Fork of MultiMC)";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
|
||||
libnbtplusplus = { url = "github:multimc/libnbtplusplus"; flake = false; };
|
||||
quazip = { url = "github:stachenov/quazip"; flake = false; };
|
||||
};
|
||||
|
||||
outputs = args@{ self, nixpkgs, flake-utils, libnbtplusplus, quazip, ... }:
|
||||
outputs = { self, nixpkgs, libnbtplusplus, quazip, ... }:
|
||||
let
|
||||
systems = [
|
||||
"aarch64-linux"
|
||||
# "aarch64-darwin" # qtbase is currently broken
|
||||
"i686-linux"
|
||||
"x86_64-darwin"
|
||||
"x86_64-linux"
|
||||
];
|
||||
in {
|
||||
overlay = final: prev: {
|
||||
inherit (self.packages.${final.system}) polymc;
|
||||
};
|
||||
} // flake-utils.lib.eachSystem systems (system:
|
||||
let pkgs = import nixpkgs { inherit system; };
|
||||
in {
|
||||
packages = {
|
||||
polymc = pkgs.libsForQt5.callPackage ./packages/nix/polymc {
|
||||
inherit self;
|
||||
submoduleQuazip = quazip;
|
||||
submoduleNbt = libnbtplusplus;
|
||||
};
|
||||
};
|
||||
apps = {
|
||||
polymc = flake-utils.lib.mkApp {
|
||||
name = "polymc";
|
||||
drv = self.packages.${system}.polymc;
|
||||
};
|
||||
};
|
||||
defaultPackage = self.packages.${system}.polymc;
|
||||
defaultApp = self.apps.${system}.polymc;
|
||||
});
|
||||
# Generate a user-friendly version number.
|
||||
version = builtins.substring 0 8 self.lastModifiedDate;
|
||||
|
||||
# System types to support (qtbase is currently broken for "aarch64-darwin")
|
||||
supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" ];
|
||||
|
||||
# Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'.
|
||||
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||
|
||||
# Nixpkgs instantiated for supported system types.
|
||||
pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system});
|
||||
in
|
||||
{
|
||||
packages = forAllSystems (system: { polymc = pkgs.${system}.libsForQt5.callPackage ./packages/nix/polymc { inherit version self quazip libnbtplusplus; }; });
|
||||
defaultPackage = forAllSystems (system: self.packages.${system}.polymc);
|
||||
|
||||
apps = forAllSystems (system: { polymc = { type = "app"; program = "${self.defaultPackage.${system}}/bin/polymc"; }; });
|
||||
defaultApp = forAllSystems (system: self.apps.${system}.polymc);
|
||||
|
||||
overlay = final: prev: { polymc = self.defaultPackage.${final.system}; };
|
||||
};
|
||||
}
|
||||
|
@ -80,6 +80,7 @@
|
||||
#include <QStringList>
|
||||
#include <QDebug>
|
||||
#include <QStyleFactory>
|
||||
#include <QWindow>
|
||||
|
||||
#include "InstanceList.h"
|
||||
|
||||
@ -316,6 +317,26 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
|
||||
QString origcwdPath = QDir::currentPath();
|
||||
QString binPath = applicationDirPath();
|
||||
|
||||
{
|
||||
// Root path is used for updates and portable data
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
QDir foo(FS::PathCombine(binPath, "..")); // typically portable-root or /usr
|
||||
m_rootPath = foo.absolutePath();
|
||||
#elif defined(Q_OS_WIN32)
|
||||
m_rootPath = binPath;
|
||||
#elif defined(Q_OS_MAC)
|
||||
QDir foo(FS::PathCombine(binPath, "../.."));
|
||||
m_rootPath = foo.absolutePath();
|
||||
// on macOS, touch the root to force Finder to reload the .app metadata (and fix any icon change issues)
|
||||
FS::updateTimestamp(m_rootPath);
|
||||
#endif
|
||||
|
||||
#ifdef LAUNCHER_JARS_LOCATION
|
||||
m_jarsPath = TOSTRING(LAUNCHER_JARS_LOCATION);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString adjustedBy;
|
||||
QString dataPath;
|
||||
// change folder
|
||||
@ -324,15 +345,14 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
{
|
||||
// the dir param. it makes multimc data path point to whatever the user specified
|
||||
// on command line
|
||||
adjustedBy += "Command line " + dirParam;
|
||||
adjustedBy = "Command line";
|
||||
dataPath = dirParam;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !defined(LAUNCHER_PORTABLE) || defined(Q_OS_MAC)
|
||||
QDir foo(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), ".."));
|
||||
dataPath = foo.absolutePath();
|
||||
adjustedBy += dataPath;
|
||||
adjustedBy = "Persistent data path";
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
// TODO: this should be removed in a future version
|
||||
@ -340,12 +360,15 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
QDir bar(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation), "polymc"));
|
||||
if (bar.exists()) {
|
||||
dataPath = bar.absolutePath();
|
||||
adjustedBy += "Legacy data path " + dataPath;
|
||||
adjustedBy = "Legacy data path";
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
dataPath = applicationDirPath();
|
||||
adjustedBy += "Fallback to binary path " + dataPath;
|
||||
|
||||
#ifndef Q_OS_MACOS
|
||||
if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) {
|
||||
dataPath = m_rootPath;
|
||||
adjustedBy = "Portable data path";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -535,24 +558,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
qDebug() << "<> Log initialized.";
|
||||
}
|
||||
|
||||
// Set up paths
|
||||
{
|
||||
// Root path is used for updates.
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
QDir foo(FS::PathCombine(binPath, ".."));
|
||||
m_rootPath = foo.absolutePath();
|
||||
#elif defined(Q_OS_WIN32)
|
||||
m_rootPath = binPath;
|
||||
#elif defined(Q_OS_MAC)
|
||||
QDir foo(FS::PathCombine(binPath, "../.."));
|
||||
m_rootPath = foo.absolutePath();
|
||||
// on macOS, touch the root to force Finder to reload the .app metadata (and fix any icon change issues)
|
||||
FS::updateTimestamp(m_rootPath);
|
||||
#endif
|
||||
|
||||
#ifdef LAUNCHER_JARS_LOCATION
|
||||
m_jarsPath = TOSTRING(LAUNCHER_JARS_LOCATION);
|
||||
#endif
|
||||
|
||||
qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT;
|
||||
qDebug() << "Version : " << BuildConfig.printableVersionString();
|
||||
@ -610,12 +616,11 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
m_settings->registerSetting("IconTheme", QString("pe_colored"));
|
||||
m_settings->registerSetting("ApplicationTheme", QString("system"));
|
||||
|
||||
// Notifications
|
||||
m_settings->registerSetting("ShownNotifications", QString());
|
||||
|
||||
// Remembered state
|
||||
m_settings->registerSetting("LastUsedGroupForNewInstance", QString());
|
||||
|
||||
m_settings->registerSetting("MenuBarInsteadOfToolBar", false);
|
||||
|
||||
QString defaultMonospace;
|
||||
int defaultSize = 11;
|
||||
#ifdef Q_OS_WIN32
|
||||
@ -685,6 +690,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
m_settings->registerSetting("JavaVendor", "");
|
||||
m_settings->registerSetting("LastHostname", "");
|
||||
m_settings->registerSetting("JvmArgs", "");
|
||||
m_settings->registerSetting("IgnoreJavaCompatibility", false);
|
||||
|
||||
// Native library workarounds
|
||||
m_settings->registerSetting("UseNativeOpenAL", false);
|
||||
@ -698,6 +704,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
// Minecraft launch method
|
||||
m_settings->registerSetting("MCLaunchMethod", "LauncherPart");
|
||||
|
||||
// Minecraft offline player name
|
||||
m_settings->registerSetting("LastOfflinePlayerName", "");
|
||||
|
||||
// Wrapper command for launch
|
||||
m_settings->registerSetting("WrapperCommand", "");
|
||||
|
||||
@ -730,6 +739,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
m_settings->registerSetting("PastebinURL", "https://0x0.st");
|
||||
|
||||
m_settings->registerSetting("CloseAfterLaunch", false);
|
||||
m_settings->registerSetting("QuitAfterGameStop", false);
|
||||
|
||||
// Custom MSA credentials
|
||||
m_settings->registerSetting("MSAClientIDOverride", "");
|
||||
@ -1142,6 +1152,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();
|
||||
@ -1257,6 +1276,12 @@ bool Application::kill(InstancePtr instance)
|
||||
return true;
|
||||
}
|
||||
|
||||
void Application::closeCurrentWindow()
|
||||
{
|
||||
if (focusWindow())
|
||||
focusWindow()->close();
|
||||
}
|
||||
|
||||
void Application::addRunningInstance()
|
||||
{
|
||||
m_runningInstances ++;
|
||||
@ -1532,7 +1557,7 @@ QString Application::getJarsPath()
|
||||
return FS::PathCombine(m_rootPath, m_jarsPath);
|
||||
}
|
||||
|
||||
QString Application::getMSAClientID()
|
||||
QString Application::getMSAClientID()
|
||||
{
|
||||
QString clientIDOverride = m_settings->get("MSAClientIDOverride").toString();
|
||||
if (!clientIDOverride.isEmpty()) {
|
||||
|
@ -104,6 +104,8 @@ public:
|
||||
|
||||
QIcon getThemedIcon(const QString& name);
|
||||
|
||||
bool isFlatpak();
|
||||
|
||||
void setIconTheme(const QString& name);
|
||||
|
||||
std::vector<ITheme *> getValidApplicationThemes();
|
||||
@ -187,6 +189,7 @@ public slots:
|
||||
MinecraftAccountPtr accountToUse = nullptr
|
||||
);
|
||||
bool kill(InstancePtr instance);
|
||||
void closeCurrentWindow();
|
||||
|
||||
private slots:
|
||||
void on_windowClose();
|
||||
|
@ -144,6 +144,8 @@ set(LAUNCH_SOURCES
|
||||
launch/steps/TextPrint.h
|
||||
launch/steps/Update.cpp
|
||||
launch/steps/Update.h
|
||||
launch/steps/QuitAfterGameStop.cpp
|
||||
launch/steps/QuitAfterGameStop.h
|
||||
launch/LaunchStep.cpp
|
||||
launch/LaunchStep.h
|
||||
launch/LaunchTask.cpp
|
||||
@ -174,13 +176,6 @@ add_unit_test(DownloadTask
|
||||
DATA updater/testdata
|
||||
)
|
||||
|
||||
# Rarely used notifications
|
||||
set(NOTIFICATIONS_SOURCES
|
||||
# Notifications - short warning messages
|
||||
notifications/NotificationChecker.h
|
||||
notifications/NotificationChecker.cpp
|
||||
)
|
||||
|
||||
# Backend for the news bar... there's usually no news.
|
||||
set(NEWS_SOURCES
|
||||
# News System
|
||||
@ -353,28 +348,30 @@ set(MINECRAFT_SOURCES
|
||||
|
||||
mojang/PackageManifest.h
|
||||
mojang/PackageManifest.cpp
|
||||
)
|
||||
minecraft/Agent.h)
|
||||
|
||||
add_unit_test(GradleSpecifier
|
||||
SOURCES minecraft/GradleSpecifier_test.cpp
|
||||
LIBS Launcher_logic
|
||||
)
|
||||
|
||||
add_executable(PackageManifest
|
||||
mojang/PackageManifest_test.cpp
|
||||
)
|
||||
target_link_libraries(PackageManifest
|
||||
Launcher_logic
|
||||
Qt5::Test
|
||||
)
|
||||
target_include_directories(PackageManifest
|
||||
PRIVATE ../cmake/UnitTest/
|
||||
)
|
||||
add_test(
|
||||
NAME PackageManifest
|
||||
COMMAND PackageManifest
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
if(BUILD_TESTING)
|
||||
add_executable(PackageManifest
|
||||
mojang/PackageManifest_test.cpp
|
||||
)
|
||||
target_link_libraries(PackageManifest
|
||||
Launcher_logic
|
||||
Qt5::Test
|
||||
)
|
||||
target_include_directories(PackageManifest
|
||||
PRIVATE ../cmake/UnitTest/
|
||||
)
|
||||
add_test(
|
||||
NAME PackageManifest
|
||||
COMMAND PackageManifest
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
endif()
|
||||
|
||||
add_unit_test(MojangVersionFormat
|
||||
SOURCES minecraft/MojangVersionFormat_test.cpp
|
||||
@ -416,6 +413,11 @@ set(TASKS_SOURCES
|
||||
tasks/SequentialTask.cpp
|
||||
)
|
||||
|
||||
add_unit_test(Task
|
||||
SOURCES tasks/Task_test.cpp
|
||||
LIBS Launcher_logic
|
||||
)
|
||||
|
||||
set(SETTINGS_SOURCES
|
||||
# Settings
|
||||
settings/INIFile.cpp
|
||||
@ -492,6 +494,16 @@ set(META_SOURCES
|
||||
meta/Index.h
|
||||
)
|
||||
|
||||
set(API_SOURCES
|
||||
modplatform/ModAPI.h
|
||||
|
||||
modplatform/flame/FlameAPI.h
|
||||
modplatform/modrinth/ModrinthAPI.h
|
||||
|
||||
modplatform/helpers/NetworkModAPI.h
|
||||
modplatform/helpers/NetworkModAPI.cpp
|
||||
)
|
||||
|
||||
set(FTB_SOURCES
|
||||
modplatform/legacy_ftb/PackFetchTask.h
|
||||
modplatform/legacy_ftb/PackFetchTask.cpp
|
||||
@ -532,6 +544,8 @@ set(TECHNIC_SOURCES
|
||||
modplatform/technic/SingleZipPackInstallTask.cpp
|
||||
modplatform/technic/SolderPackInstallTask.h
|
||||
modplatform/technic/SolderPackInstallTask.cpp
|
||||
modplatform/technic/SolderPackManifest.h
|
||||
modplatform/technic/SolderPackManifest.cpp
|
||||
modplatform/technic/TechnicPackProcessor.h
|
||||
modplatform/technic/TechnicPackProcessor.cpp
|
||||
)
|
||||
@ -561,7 +575,6 @@ set(LOGIC_SOURCES
|
||||
${NET_SOURCES}
|
||||
${LAUNCH_SOURCES}
|
||||
${UPDATE_SOURCES}
|
||||
${NOTIFICATIONS_SOURCES}
|
||||
${NEWS_SOURCES}
|
||||
${MINECRAFT_SOURCES}
|
||||
${SCREENSHOTS_SOURCES}
|
||||
@ -572,6 +585,7 @@ set(LOGIC_SOURCES
|
||||
${TOOLS_SOURCES}
|
||||
${META_SOURCES}
|
||||
${ICONS_SOURCES}
|
||||
${API_SOURCES}
|
||||
${FTB_SOURCES}
|
||||
${FLAME_SOURCES}
|
||||
${MODRINTH_SOURCES}
|
||||
@ -721,6 +735,11 @@ SET(LAUNCHER_SOURCES
|
||||
ui/pages/modplatform/VanillaPage.cpp
|
||||
ui/pages/modplatform/VanillaPage.h
|
||||
|
||||
ui/pages/modplatform/ModPage.cpp
|
||||
ui/pages/modplatform/ModPage.h
|
||||
ui/pages/modplatform/ModModel.cpp
|
||||
ui/pages/modplatform/ModModel.h
|
||||
|
||||
ui/pages/modplatform/atlauncher/AtlFilterModel.cpp
|
||||
ui/pages/modplatform/atlauncher/AtlFilterModel.h
|
||||
ui/pages/modplatform/atlauncher/AtlListModel.cpp
|
||||
@ -791,8 +810,6 @@ SET(LAUNCHER_SOURCES
|
||||
ui/dialogs/NewComponentDialog.h
|
||||
ui/dialogs/NewInstanceDialog.cpp
|
||||
ui/dialogs/NewInstanceDialog.h
|
||||
ui/dialogs/NotificationDialog.cpp
|
||||
ui/dialogs/NotificationDialog.h
|
||||
ui/pagedialog/PageDialog.cpp
|
||||
ui/pagedialog/PageDialog.h
|
||||
ui/dialogs/ProgressDialog.cpp
|
||||
@ -808,7 +825,6 @@ SET(LAUNCHER_SOURCES
|
||||
ui/dialogs/ModDownloadDialog.cpp
|
||||
ui/dialogs/ModDownloadDialog.h
|
||||
|
||||
|
||||
# GUI - widgets
|
||||
ui/widgets/Common.cpp
|
||||
ui/widgets/Common.h
|
||||
@ -832,6 +848,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/widgets/LogView.h
|
||||
ui/widgets/MCModInfoFrame.cpp
|
||||
ui/widgets/MCModInfoFrame.h
|
||||
ui/widgets/ModFilterWidget.cpp
|
||||
ui/widgets/ModFilterWidget.h
|
||||
ui/widgets/ModListView.cpp
|
||||
ui/widgets/ModListView.h
|
||||
ui/widgets/PageContainer.cpp
|
||||
@ -881,21 +899,20 @@ qt5_wrap_ui(LAUNCHER_UI
|
||||
ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui
|
||||
ui/pages/modplatform/atlauncher/AtlPage.ui
|
||||
ui/pages/modplatform/VanillaPage.ui
|
||||
ui/pages/modplatform/ModPage.ui
|
||||
ui/pages/modplatform/flame/FlamePage.ui
|
||||
ui/pages/modplatform/flame/FlameModPage.ui
|
||||
ui/pages/modplatform/legacy_ftb/Page.ui
|
||||
ui/pages/modplatform/ImportPage.ui
|
||||
ui/pages/modplatform/ftb/FtbPage.ui
|
||||
ui/pages/modplatform/technic/TechnicPage.ui
|
||||
ui/pages/modplatform/modrinth/ModrinthPage.ui
|
||||
ui/widgets/InstanceCardWidget.ui
|
||||
ui/widgets/CustomCommands.ui
|
||||
ui/widgets/MCModInfoFrame.ui
|
||||
ui/widgets/ModFilterWidget.ui
|
||||
ui/dialogs/CopyInstanceDialog.ui
|
||||
ui/dialogs/ProfileSetupDialog.ui
|
||||
ui/dialogs/ProgressDialog.ui
|
||||
ui/dialogs/NewInstanceDialog.ui
|
||||
ui/dialogs/NotificationDialog.ui
|
||||
ui/dialogs/UpdateDialog.ui
|
||||
ui/dialogs/NewComponentDialog.ui
|
||||
ui/dialogs/ProfileSelectDialog.ui
|
||||
@ -974,7 +991,7 @@ if(DEFINED Launcher_APP_BINARY_DEFS)
|
||||
endif()
|
||||
|
||||
install(TARGETS ${Launcher_Name}
|
||||
BUNDLE DESTINATION ${BUNDLE_DEST_DIR} COMPONENT Runtime
|
||||
BUNDLE DESTINATION "." COMPONENT Runtime
|
||||
LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime
|
||||
RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime
|
||||
)
|
||||
|
@ -1,8 +1,43 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (C) 2022 dada513 <dada513@protonmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2022 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 "DesktopServices.h"
|
||||
#include <QDir>
|
||||
#include <QDesktopServices>
|
||||
#include <QProcess>
|
||||
#include <QDebug>
|
||||
#include "Application.h"
|
||||
|
||||
/**
|
||||
* This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing.
|
||||
@ -84,7 +119,14 @@ bool openDirectory(const QString &path, bool ensureExists)
|
||||
return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
|
||||
};
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
return IndirectOpen(f);
|
||||
if(!APPLICATION->isFlatpak())
|
||||
{
|
||||
return IndirectOpen(f);
|
||||
}
|
||||
else
|
||||
{
|
||||
return f();
|
||||
}
|
||||
#else
|
||||
return f();
|
||||
#endif
|
||||
@ -98,7 +140,14 @@ bool openFile(const QString &path)
|
||||
return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
};
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
return IndirectOpen(f);
|
||||
if(!APPLICATION->isFlatpak())
|
||||
{
|
||||
return IndirectOpen(f);
|
||||
}
|
||||
else
|
||||
{
|
||||
return f();
|
||||
}
|
||||
#else
|
||||
return f();
|
||||
#endif
|
||||
@ -109,10 +158,17 @@ 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
|
||||
return IndirectOpen([&]()
|
||||
if(!APPLICATION->isFlatpak())
|
||||
{
|
||||
return QProcess::startDetached(application, QStringList() << path, workingDirectory);
|
||||
}, pid);
|
||||
return IndirectOpen([&]()
|
||||
{
|
||||
return QProcess::startDetached(application, QStringList() << path, workingDirectory);
|
||||
}, pid);
|
||||
}
|
||||
else
|
||||
{
|
||||
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
|
||||
}
|
||||
#else
|
||||
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
|
||||
#endif
|
||||
@ -122,11 +178,18 @@ 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(!APPLICATION->isFlatpak())
|
||||
{
|
||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
||||
return IndirectOpen([&]()
|
||||
{
|
||||
return QProcess::startDetached(application, args, workingDirectory);
|
||||
}, pid);
|
||||
}
|
||||
else
|
||||
{
|
||||
return QProcess::startDetached(application, args, workingDirectory, pid);
|
||||
}
|
||||
#else
|
||||
return QProcess::startDetached(application, args, workingDirectory, pid);
|
||||
#endif
|
||||
@ -140,7 +203,14 @@ bool openUrl(const QUrl &url)
|
||||
return QDesktopServices::openUrl(url);
|
||||
};
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
return IndirectOpen(f);
|
||||
if(!APPLICATION->isFlatpak())
|
||||
{
|
||||
return IndirectOpen(f);
|
||||
}
|
||||
else
|
||||
{
|
||||
return f();
|
||||
}
|
||||
#else
|
||||
return f();
|
||||
#endif
|
||||
|
@ -9,6 +9,15 @@
|
||||
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()
|
||||
@ -21,6 +30,8 @@ void InstanceCreationTask::executeTask()
|
||||
auto components = inst.getPackProfile();
|
||||
components->buildingFromScratch();
|
||||
components->setComponentVersion("net.minecraft", m_version->descriptor(), true);
|
||||
if(m_usingLoader)
|
||||
components->setComponentVersion(m_loader, m_loaderVersion->descriptor(), true);
|
||||
inst.setName(m_instName);
|
||||
inst.setIconKey(m_instIcon);
|
||||
instanceSettings->resumeSave();
|
||||
|
@ -12,6 +12,7 @@ class InstanceCreationTask : public InstanceTask
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit InstanceCreationTask(BaseVersionPtr version);
|
||||
explicit InstanceCreationTask(BaseVersionPtr version, QString loader, BaseVersionPtr loaderVersion);
|
||||
|
||||
protected:
|
||||
//! Entry point for tasks.
|
||||
@ -19,4 +20,7 @@ protected:
|
||||
|
||||
private: /* data */
|
||||
BaseVersionPtr m_version;
|
||||
bool m_usingLoader;
|
||||
QString m_loader;
|
||||
BaseVersionPtr m_loaderVersion;
|
||||
};
|
||||
|
@ -40,6 +40,14 @@ InstanceImportTask::InstanceImportTask(const QUrl sourceUrl)
|
||||
m_sourceUrl = sourceUrl;
|
||||
}
|
||||
|
||||
bool InstanceImportTask::abort()
|
||||
{
|
||||
m_filesNetJob->abort();
|
||||
m_extractFuture.cancel();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InstanceImportTask::executeTask()
|
||||
{
|
||||
if (m_sourceUrl.isLocalFile())
|
||||
@ -241,6 +249,7 @@ void InstanceImportTask::processFlame()
|
||||
|
||||
QString forgeVersion;
|
||||
QString fabricVersion;
|
||||
// TODO: is Quilt relevant here?
|
||||
for(auto &loader: pack.minecraft.modLoaders)
|
||||
{
|
||||
auto id = loader.id;
|
||||
|
@ -37,6 +37,9 @@ class InstanceImportTask : public InstanceTask
|
||||
public:
|
||||
explicit InstanceImportTask(const QUrl sourceUrl);
|
||||
|
||||
bool canAbort() const override { return true; }
|
||||
bool abort() override;
|
||||
|
||||
protected:
|
||||
//! Entry point for tasks.
|
||||
virtual void executeTask() override;
|
||||
|
@ -17,6 +17,17 @@ bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
|
||||
QMessageBox::Warning)->exec();
|
||||
return false;
|
||||
}
|
||||
// block lunacy with passing required version to the JVM
|
||||
if (jvmargs.contains(QRegExp("-version:.*"))) {
|
||||
auto warnStr = QObject::tr(
|
||||
"You tried to pass required Java version argument to the JVM (using \"-version:xxx\"). This is not safe and will not be allowed.\n"
|
||||
"This message will be displayed until you remove this from the JVM arguments.");
|
||||
CustomMessageBox::selectable(
|
||||
parent, QObject::tr("JVM arguments warning"),
|
||||
warnStr,
|
||||
QMessageBox::Warning)->exec();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,10 @@ void LaunchController::executeTask()
|
||||
return;
|
||||
}
|
||||
|
||||
JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget);
|
||||
if(!JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget)) {
|
||||
emitFailed(tr("Invalid Java arguments specified. Please fix this first."));
|
||||
return;
|
||||
}
|
||||
|
||||
login();
|
||||
}
|
||||
@ -166,13 +169,14 @@ void LaunchController::login() {
|
||||
if(!m_session->wants_online) {
|
||||
// we ask the user for a player name
|
||||
bool ok = false;
|
||||
QString usedname = m_session->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"),
|
||||
tr("Choose your offline mode player name."),
|
||||
QLineEdit::Normal,
|
||||
m_session->player_name,
|
||||
usedname,
|
||||
&ok
|
||||
);
|
||||
if (!ok)
|
||||
@ -183,6 +187,7 @@ void LaunchController::login() {
|
||||
if (name.length())
|
||||
{
|
||||
usedname = name;
|
||||
APPLICATION->settings()->set("LastOfflinePlayerName", usedname);
|
||||
}
|
||||
m_session->MakeOffline(usedname);
|
||||
// offline flavored game from here :3
|
||||
|
@ -21,7 +21,7 @@ echo "Launcher Dir: ${LAUNCHER_DIR}"
|
||||
# Set up env - filter out input LD_ variables but pass them in under different names
|
||||
export GAME_LIBRARY_PATH=${GAME_LIBRARY_PATH-${LD_LIBRARY_PATH}}
|
||||
export GAME_PRELOAD=${GAME_PRELOAD-${LD_PRELOAD}}
|
||||
export LD_LIBRARY_PATH="${LAUNCHER_DIR}/bin":$LAUNCHER_LIBRARY_PATH
|
||||
export LD_LIBRARY_PATH="${LAUNCHER_DIR}/lib@LIB_SUFFIX@":$LAUNCHER_LIBRARY_PATH
|
||||
export LD_PRELOAD=$LAUNCHER_PRELOAD
|
||||
export QT_PLUGIN_PATH="${LAUNCHER_DIR}/plugins"
|
||||
export QT_FONTPATH="${LAUNCHER_DIR}/fonts"
|
||||
|
@ -129,7 +129,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||
auto os_arch = results["os.arch"];
|
||||
auto java_version = results["java.version"];
|
||||
auto java_vendor = results["java.vendor"];
|
||||
bool is_64 = os_arch == "x86_64" || os_arch == "amd64";
|
||||
bool is_64 = os_arch == "x86_64" || os_arch == "amd64" || os_arch == "aarch64" || os_arch == "arm64";
|
||||
|
||||
|
||||
result.validity = JavaCheckResult::Validity::Valid;
|
||||
|
@ -183,7 +183,7 @@ void JavaListLoadTask::javaCheckerFinished()
|
||||
JavaInstallPtr javaVersion(new JavaInstall());
|
||||
|
||||
javaVersion->id = result.javaVersion;
|
||||
javaVersion->arch = result.mojangPlatform;
|
||||
javaVersion->arch = result.realPlatform;
|
||||
javaVersion->path = result.path;
|
||||
candidates.append(javaVersion);
|
||||
|
||||
|
@ -153,7 +153,7 @@ QStringList addJavasFromEnv(QList<QString> javas)
|
||||
{
|
||||
QByteArray env = qgetenv("POLYMC_JAVA_PATHS");
|
||||
#if defined(Q_OS_WIN32)
|
||||
QList<QString> javaPaths = QString::fromLocal8Bit(env).split(QLatin1String(";"));
|
||||
QList<QString> javaPaths = QString::fromLocal8Bit(env).replace("\\", "/").split(QLatin1String(";"));
|
||||
#else
|
||||
QList<QString> javaPaths = QString::fromLocal8Bit(env).split(QLatin1String(":"));
|
||||
#endif
|
||||
@ -355,7 +355,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
}
|
||||
}
|
||||
|
||||
return candidates;
|
||||
return addJavasFromEnv(candidates);
|
||||
}
|
||||
|
||||
#elif defined(Q_OS_MAC)
|
||||
|
@ -1,18 +1,38 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
* 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.
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "launch/LaunchTask.h"
|
||||
@ -212,7 +232,7 @@ shared_qobject_ptr<LogModel> LaunchTask::getLogModel()
|
||||
m_logModel->setMaxLines(m_instance->getConsoleMaxLines());
|
||||
m_logModel->setStopOnOverflow(m_instance->shouldStopOnConsoleOverflow());
|
||||
// FIXME: should this really be here?
|
||||
m_logModel->setOverflowMessage(tr("PolyMC stopped watching the game log because the log length surpassed %1 lines.\n"
|
||||
m_logModel->setOverflowMessage(tr("Stopped watching the game log because the log length surpassed %1 lines.\n"
|
||||
"You may have to fix your mods because the game is still logging to files and"
|
||||
" likely wasting harddrive space at an alarming rate!").arg(m_logModel->getMaxLines()));
|
||||
}
|
||||
|
@ -1,16 +1,36 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* 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 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.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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 "CheckJava.h"
|
||||
@ -87,14 +107,14 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
|
||||
// Error message displayed if java can't start
|
||||
emit logLine(QString("Could not start java:"), MessageLevel::Error);
|
||||
emit logLines(result.errorLog.split('\n'), MessageLevel::Error);
|
||||
emit logLine("\nCheck your PolyMC Java settings.", MessageLevel::Launcher);
|
||||
emit logLine(QString("\nCheck your Java settings."), MessageLevel::Launcher);
|
||||
printSystemInfo(false, false);
|
||||
emitFailed(QString("Could not start java!"));
|
||||
return;
|
||||
}
|
||||
case JavaCheckResult::Validity::ReturnedInvalidData:
|
||||
{
|
||||
emit logLine(QString("Java checker returned some invalid data PolyMC doesn't understand:"), MessageLevel::Error);
|
||||
emit logLine(QString("Java checker returned some invalid data we don't understand:"), MessageLevel::Error);
|
||||
emit logLines(result.outLog.split('\n'), MessageLevel::Warning);
|
||||
emit logLine("\nMinecraft might not start properly.", MessageLevel::Launcher);
|
||||
printSystemInfo(false, false);
|
||||
@ -104,7 +124,8 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
|
||||
case JavaCheckResult::Validity::Valid:
|
||||
{
|
||||
auto instance = m_parent->instance();
|
||||
printJavaInfo(result.javaVersion.toString(), result.mojangPlatform, result.javaVendor);
|
||||
printJavaInfo(result.javaVersion.toString(), result.realPlatform, result.javaVendor);
|
||||
printSystemInfo(true, result.is_64bit);
|
||||
instance->settings()->set("JavaVersion", result.javaVersion.toString());
|
||||
instance->settings()->set("JavaArchitecture", result.mojangPlatform);
|
||||
instance->settings()->set("JavaVendor", result.javaVendor);
|
||||
@ -117,8 +138,7 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
|
||||
|
||||
void CheckJava::printJavaInfo(const QString& version, const QString& architecture, const QString & vendor)
|
||||
{
|
||||
emit logLine(QString("Java is version %1, using %2-bit architecture, from %3.\n\n").arg(version, architecture, vendor), MessageLevel::Launcher);
|
||||
printSystemInfo(true, architecture == "64");
|
||||
emit logLine(QString("Java is version %1, using %2 architecture, from %3.\n\n").arg(version, architecture, vendor), MessageLevel::Launcher);
|
||||
}
|
||||
|
||||
void CheckJava::printSystemInfo(bool javaIsKnown, bool javaIs64bit)
|
||||
|
26
launcher/launch/steps/QuitAfterGameStop.cpp
Normal file
26
launcher/launch/steps/QuitAfterGameStop.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (C) 2022 dada513 <dada513@protonmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "QuitAfterGameStop.h"
|
||||
#include <launch/LaunchTask.h>
|
||||
#include "Application.h"
|
||||
|
||||
void QuitAfterGameStop::executeTask()
|
||||
{
|
||||
APPLICATION->quit();
|
||||
}
|
35
launcher/launch/steps/QuitAfterGameStop.h
Normal file
35
launcher/launch/steps/QuitAfterGameStop.h
Normal file
@ -0,0 +1,35 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (C) 2022 dada513 <dada513@protonmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <launch/LaunchStep.h>
|
||||
|
||||
class QuitAfterGameStop: public LaunchStep
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QuitAfterGameStop(LaunchTask *parent) :LaunchStep(parent){};
|
||||
virtual ~QuitAfterGameStop() {};
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool canAbort() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
36
launcher/minecraft/Agent.h
Normal file
36
launcher/minecraft/Agent.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "Library.h"
|
||||
|
||||
class Agent;
|
||||
|
||||
typedef std::shared_ptr<Agent> AgentPtr;
|
||||
|
||||
class Agent {
|
||||
public:
|
||||
Agent(LibraryPtr library, QString &argument)
|
||||
{
|
||||
m_library = library;
|
||||
m_argument = argument;
|
||||
}
|
||||
|
||||
public: /* methods */
|
||||
|
||||
LibraryPtr library() {
|
||||
return m_library;
|
||||
}
|
||||
QString argument() {
|
||||
return m_argument;
|
||||
}
|
||||
|
||||
protected: /* data */
|
||||
|
||||
/// The library pointing to the jar this Java agent is contained within
|
||||
LibraryPtr m_library;
|
||||
|
||||
/// The argument to the Java agent, passed after an = if present
|
||||
QString m_argument;
|
||||
|
||||
};
|
@ -591,7 +591,7 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly)
|
||||
{
|
||||
component->m_version = "3.1.2";
|
||||
}
|
||||
else if (add.uid == "net.fabricmc.intermediary")
|
||||
else if (add.uid == "net.fabricmc.intermediary" || add.uid == "org.quiltmc.hashed")
|
||||
{
|
||||
auto minecraft = std::find_if(components.begin(), components.end(), [](ComponentPtr & cmp){
|
||||
return cmp->getID() == "net.minecraft";
|
||||
|
@ -1,3 +1,38 @@
|
||||
// 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 "LaunchProfile.h"
|
||||
#include <Version.h>
|
||||
|
||||
@ -7,11 +42,13 @@ void LaunchProfile::clear()
|
||||
m_minecraftVersionType.clear();
|
||||
m_minecraftAssets.reset();
|
||||
m_minecraftArguments.clear();
|
||||
m_addnJvmArguments.clear();
|
||||
m_tweakers.clear();
|
||||
m_mainClass.clear();
|
||||
m_appletClass.clear();
|
||||
m_libraries.clear();
|
||||
m_mavenFiles.clear();
|
||||
m_agents.clear();
|
||||
m_traits.clear();
|
||||
m_jarMods.clear();
|
||||
m_mainJar.reset();
|
||||
@ -45,6 +82,11 @@ void LaunchProfile::applyMinecraftArguments(const QString& minecraftArguments)
|
||||
applyString(minecraftArguments, this->m_minecraftArguments);
|
||||
}
|
||||
|
||||
void LaunchProfile::applyAddnJvmArguments(const QStringList& addnJvmArguments)
|
||||
{
|
||||
this->m_addnJvmArguments.append(addnJvmArguments);
|
||||
}
|
||||
|
||||
void LaunchProfile::applyMinecraftVersionType(const QString& type)
|
||||
{
|
||||
applyString(type, this->m_minecraftVersionType);
|
||||
@ -126,6 +168,11 @@ void LaunchProfile::applyMods(const QList<LibraryPtr>& mods)
|
||||
}
|
||||
}
|
||||
|
||||
void LaunchProfile::applyCompatibleJavaMajors(QList<int>& javaMajor)
|
||||
{
|
||||
m_compatibleJavaMajors.append(javaMajor);
|
||||
}
|
||||
|
||||
void LaunchProfile::applyLibrary(LibraryPtr library)
|
||||
{
|
||||
if(!library->isActive())
|
||||
@ -174,6 +221,22 @@ void LaunchProfile::applyMavenFile(LibraryPtr mavenFile)
|
||||
m_mavenFiles.append(Library::limitedCopy(mavenFile));
|
||||
}
|
||||
|
||||
void LaunchProfile::applyAgent(AgentPtr agent)
|
||||
{
|
||||
auto lib = agent->library();
|
||||
if(!lib->isActive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(lib->isNative())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_agents.append(agent);
|
||||
}
|
||||
|
||||
const LibraryPtr LaunchProfile::getMainJar() const
|
||||
{
|
||||
return m_mainJar;
|
||||
@ -255,6 +318,11 @@ QString LaunchProfile::getMinecraftArguments() const
|
||||
return m_minecraftArguments;
|
||||
}
|
||||
|
||||
const QStringList & LaunchProfile::getAddnJvmArguments() const
|
||||
{
|
||||
return m_addnJvmArguments;
|
||||
}
|
||||
|
||||
const QList<LibraryPtr> & LaunchProfile::getJarMods() const
|
||||
{
|
||||
return m_jarMods;
|
||||
@ -275,6 +343,16 @@ const QList<LibraryPtr> & LaunchProfile::getMavenFiles() const
|
||||
return m_mavenFiles;
|
||||
}
|
||||
|
||||
const QList<AgentPtr> & LaunchProfile::getAgents() const
|
||||
{
|
||||
return m_agents;
|
||||
}
|
||||
|
||||
const QList<int> & LaunchProfile::getCompatibleJavaMajors() const
|
||||
{
|
||||
return m_compatibleJavaMajors;
|
||||
}
|
||||
|
||||
void LaunchProfile::getLibraryFiles(
|
||||
const QString& architecture,
|
||||
QStringList& jars,
|
||||
|
@ -1,6 +1,42 @@
|
||||
// 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 "Library.h"
|
||||
#include "Agent.h"
|
||||
#include <ProblemProvider.h>
|
||||
|
||||
class LaunchProfile: public ProblemProvider
|
||||
@ -13,6 +49,7 @@ public: /* application of profile variables from patches */
|
||||
void applyMainClass(const QString& mainClass);
|
||||
void applyAppletClass(const QString& appletClass);
|
||||
void applyMinecraftArguments(const QString& minecraftArguments);
|
||||
void applyAddnJvmArguments(const QStringList& minecraftArguments);
|
||||
void applyMinecraftVersionType(const QString& type);
|
||||
void applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets);
|
||||
void applyTraits(const QSet<QString> &traits);
|
||||
@ -21,6 +58,8 @@ public: /* application of profile variables from patches */
|
||||
void applyMods(const QList<LibraryPtr> &jarMods);
|
||||
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);
|
||||
/// clear the profile
|
||||
@ -33,12 +72,15 @@ public: /* getters for profile variables */
|
||||
QString getMinecraftVersionType() const;
|
||||
MojangAssetIndexInfo::Ptr getMinecraftAssets() const;
|
||||
QString getMinecraftArguments() const;
|
||||
const QStringList & getAddnJvmArguments() const;
|
||||
const QSet<QString> & getTraits() const;
|
||||
const QStringList & getTweakers() const;
|
||||
const QList<LibraryPtr> & getJarMods() const;
|
||||
const QList<LibraryPtr> & getLibraries() const;
|
||||
const QList<LibraryPtr> & getNativeLibraries() const;
|
||||
const QList<LibraryPtr> & getMavenFiles() const;
|
||||
const QList<AgentPtr> & getAgents() const;
|
||||
const QList<int> & getCompatibleJavaMajors() const;
|
||||
const LibraryPtr getMainJar() const;
|
||||
void getLibraryFiles(
|
||||
const QString & architecture,
|
||||
@ -69,6 +111,12 @@ private:
|
||||
*/
|
||||
QString m_minecraftArguments;
|
||||
|
||||
/**
|
||||
* Additional arguments to pass to the JVM in addition to those the user has configured,
|
||||
* memory settings, etc.
|
||||
*/
|
||||
QStringList m_addnJvmArguments;
|
||||
|
||||
/// A list of all tweaker classes
|
||||
QStringList m_tweakers;
|
||||
|
||||
@ -84,6 +132,9 @@ private:
|
||||
/// the list of maven files to be placed in the libraries folder, but not acted upon
|
||||
QList<LibraryPtr> m_mavenFiles;
|
||||
|
||||
/// the list of java agents to add to JVM arguments
|
||||
QList<AgentPtr> m_agents;
|
||||
|
||||
/// the main jar
|
||||
LibraryPtr m_mainJar;
|
||||
|
||||
@ -99,6 +150,9 @@ private:
|
||||
/// the list of mods
|
||||
QList<LibraryPtr> m_mods;
|
||||
|
||||
/// compatible java major versions
|
||||
QList<int> m_compatibleJavaMajors;
|
||||
|
||||
ProblemSeverity m_problemSeverity = ProblemSeverity::None;
|
||||
|
||||
};
|
||||
|
@ -1,4 +1,40 @@
|
||||
// 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 "MinecraftInstance.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "minecraft/launch/CreateGameFolders.h"
|
||||
#include "minecraft/launch/ExtractNatives.h"
|
||||
#include "minecraft/launch/PrintInstanceInfo.h"
|
||||
@ -20,6 +56,7 @@
|
||||
#include "launch/steps/PreLaunchCommand.h"
|
||||
#include "launch/steps/TextPrint.h"
|
||||
#include "launch/steps/CheckJava.h"
|
||||
#include "launch/steps/QuitAfterGameStop.h"
|
||||
|
||||
#include "minecraft/launch/LauncherPartLaunch.h"
|
||||
#include "minecraft/launch/DirectJavaLaunch.h"
|
||||
@ -88,6 +125,7 @@ MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsO
|
||||
|
||||
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(globalSettings->getSetting("JavaTimestamp"), javaOrLocation);
|
||||
@ -175,8 +213,12 @@ QString MinecraftInstance::binRoot() const
|
||||
|
||||
QString MinecraftInstance::getNativePath() const
|
||||
{
|
||||
#ifdef Q_OS_FREEBSD
|
||||
QDir natives_dir("/usr/local/lib/lwjgl/");
|
||||
#else
|
||||
QDir natives_dir(FS::PathCombine(instanceRoot(), "natives/"));
|
||||
return natives_dir.absolutePath();
|
||||
#endif
|
||||
}
|
||||
|
||||
QString MinecraftInstance::getLocalLibraryPath() const
|
||||
@ -292,6 +334,17 @@ QStringList MinecraftInstance::extraArguments() const
|
||||
list.append({"-Dfml.ignoreInvalidMinecraftCertificates=true",
|
||||
"-Dfml.ignorePatchDiscrepancies=true"});
|
||||
}
|
||||
auto addn = m_components->getProfile()->getAddnJvmArguments();
|
||||
if (!addn.isEmpty()) {
|
||||
list.append(addn);
|
||||
}
|
||||
auto agents = m_components->getProfile()->getAgents();
|
||||
for (auto agent : agents)
|
||||
{
|
||||
QStringList jar, temp1, temp2, temp3;
|
||||
agent->library()->getApplicableFiles(currentSystem, jar, temp1, temp2, temp3, getLocalLibraryPath());
|
||||
list.append("-javaagent:"+jar[0]+(agent->argument().isEmpty() ? "" : "="+agent->argument()));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@ -434,7 +487,7 @@ QStringList MinecraftInstance::processMinecraftArgs(
|
||||
}
|
||||
|
||||
// blatant self-promotion.
|
||||
token_mapping["profile_name"] = token_mapping["version_name"] = "PolyMC";
|
||||
token_mapping["profile_name"] = token_mapping["version_name"] = BuildConfig.LAUNCHER_NAME;
|
||||
|
||||
token_mapping["version_type"] = profile->getMinecraftVersionType();
|
||||
|
||||
@ -935,6 +988,11 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
|
||||
{
|
||||
process->setCensorFilter(createCensorFilterFromSession(session));
|
||||
}
|
||||
if(APPLICATION->settings()->get("QuitAfterGameStop").toBool())
|
||||
{
|
||||
auto step = new QuitAfterGameStop(pptr);
|
||||
process->appendStep(step);
|
||||
}
|
||||
m_launchProcess = process;
|
||||
emit launchTaskChanged(m_launchProcess);
|
||||
return m_launchProcess;
|
||||
|
@ -1,3 +1,38 @@
|
||||
// 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 "MojangVersionFormat.h"
|
||||
#include "OneSixVersionFormat.h"
|
||||
#include "MojangDownloadInfo.h"
|
||||
@ -183,6 +218,15 @@ void MojangVersionFormat::readVersionProperties(const QJsonObject &in, VersionFi
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (in.contains("compatibleJavaMajors"))
|
||||
{
|
||||
for (auto compatible : requireArray(in.value("compatibleJavaMajors")))
|
||||
{
|
||||
out->compatibleJavaMajors.append(requireInteger(compatible));
|
||||
}
|
||||
}
|
||||
|
||||
if(in.contains("downloads"))
|
||||
{
|
||||
auto downloadsObj = requireObject(in, "downloads");
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "OneSixVersionFormat.h"
|
||||
#include <Json.h>
|
||||
#include "minecraft/Agent.h"
|
||||
#include "minecraft/ParseUtils.h"
|
||||
#include <minecraft/MojangVersionFormat.h>
|
||||
|
||||
@ -108,6 +109,14 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc
|
||||
}
|
||||
}
|
||||
|
||||
if (root.contains("+jvmArgs"))
|
||||
{
|
||||
for (auto arg : requireArray(root.value("+jvmArgs")))
|
||||
{
|
||||
out->addnJvmArguments.append(requireString(arg));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (root.contains("jarMods"))
|
||||
{
|
||||
@ -176,6 +185,21 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc
|
||||
readLibs("mavenFiles", out->mavenFiles);
|
||||
}
|
||||
|
||||
if(root.contains("+agents")) {
|
||||
for (auto agentVal : requireArray(root.value("+agents")))
|
||||
{
|
||||
QJsonObject agentObj = requireObject(agentVal);
|
||||
auto lib = libraryFromJson(*out, agentObj, filename);
|
||||
QString arg = "";
|
||||
if (agentObj.contains("argument"))
|
||||
{
|
||||
readString(agentObj, "argument", arg);
|
||||
}
|
||||
AgentPtr agent(new Agent(lib, arg));
|
||||
out->agents.append(agent);
|
||||
}
|
||||
}
|
||||
|
||||
// if we have mainJar, just use it
|
||||
if(root.contains("mainJar"))
|
||||
{
|
||||
|
@ -970,3 +970,20 @@ void PackProfile::disableInteraction(bool disable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModAPI::ModLoaderType PackProfile::getModLoader()
|
||||
{
|
||||
if (!getComponentVersion("net.minecraftforge").isEmpty())
|
||||
{
|
||||
return ModAPI::Forge;
|
||||
}
|
||||
else if (!getComponentVersion("net.fabricmc.fabric-loader").isEmpty())
|
||||
{
|
||||
return ModAPI::Fabric;
|
||||
}
|
||||
else if (!getComponentVersion("org.quiltmc.quilt-loader").isEmpty())
|
||||
{
|
||||
return ModAPI::Quilt;
|
||||
}
|
||||
return ModAPI::Unspecified;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "BaseVersion.h"
|
||||
#include "MojangDownloadInfo.h"
|
||||
#include "net/Mode.h"
|
||||
#include "modplatform/ModAPI.h"
|
||||
|
||||
class MinecraftInstance;
|
||||
struct PackProfileData;
|
||||
@ -117,6 +118,8 @@ public:
|
||||
// todo(merged): is this the best approach
|
||||
void appendComponent(ComponentPtr component);
|
||||
|
||||
ModAPI::ModLoaderType getModLoader();
|
||||
|
||||
private:
|
||||
void scheduleSave();
|
||||
bool saveIsScheduled() const;
|
||||
|
@ -1,3 +1,38 @@
|
||||
// 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 <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
||||
@ -32,10 +67,12 @@ void VersionFile::applyTo(LaunchProfile *profile)
|
||||
profile->applyMainClass(mainClass);
|
||||
profile->applyAppletClass(appletClass);
|
||||
profile->applyMinecraftArguments(minecraftArguments);
|
||||
profile->applyAddnJvmArguments(addnJvmArguments);
|
||||
profile->applyTweakers(addTweakers);
|
||||
profile->applyJarMods(jarMods);
|
||||
profile->applyMods(mods);
|
||||
profile->applyTraits(traits);
|
||||
profile->applyCompatibleJavaMajors(compatibleJavaMajors);
|
||||
|
||||
for (auto library : libraries)
|
||||
{
|
||||
@ -45,6 +82,10 @@ void VersionFile::applyTo(LaunchProfile *profile)
|
||||
{
|
||||
profile->applyMavenFile(mavenFile);
|
||||
}
|
||||
for (auto agent : agents)
|
||||
{
|
||||
profile->applyAgent(agent);
|
||||
}
|
||||
profile->applyProblemSeverity(getProblemSeverity());
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,38 @@
|
||||
// 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>
|
||||
@ -10,6 +45,7 @@
|
||||
#include "minecraft/Rule.h"
|
||||
#include "ProblemProvider.h"
|
||||
#include "Library.h"
|
||||
#include "Agent.h"
|
||||
#include <meta/JsonFormat.h>
|
||||
|
||||
class PackProfile;
|
||||
@ -57,6 +93,12 @@ public: /* data */
|
||||
/// Mojang: Minecraft launch arguments (may contain placeholders for variable substitution)
|
||||
QString minecraftArguments;
|
||||
|
||||
/// PolyMC: Additional JVM launch arguments
|
||||
QStringList addnJvmArguments;
|
||||
|
||||
/// Mojang: list of compatible java majors
|
||||
QList<int> compatibleJavaMajors;
|
||||
|
||||
/// Mojang: type of the Minecraft version
|
||||
QString type;
|
||||
|
||||
@ -78,6 +120,9 @@ public: /* data */
|
||||
/// PolyMC: list of maven files to put in the libraries folder, but not in classpath
|
||||
QList<LibraryPtr> mavenFiles;
|
||||
|
||||
/// PolyMC: list of agents to add to JVM arguments
|
||||
QList<AgentPtr> agents;
|
||||
|
||||
/// The main jar (Minecraft version library, normally)
|
||||
LibraryPtr mainJar;
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <QString>
|
||||
#include <QDebug>
|
||||
#include <QSaveFile>
|
||||
#include <QDirIterator>
|
||||
#include "World.h"
|
||||
|
||||
#include "GZip.h"
|
||||
@ -187,6 +188,26 @@ bool putLevelDatDataToFS(const QFileInfo &file, QByteArray & data)
|
||||
return f.commit();
|
||||
}
|
||||
|
||||
int64_t calculateWorldSize(const QFileInfo &file)
|
||||
{
|
||||
if (file.isFile() && file.suffix() == "zip")
|
||||
{
|
||||
return file.size();
|
||||
}
|
||||
else if(file.isDir())
|
||||
{
|
||||
QDirIterator it(file.absoluteFilePath(), QDir::Files, QDirIterator::Subdirectories);
|
||||
int64_t total = 0;
|
||||
while (it.hasNext())
|
||||
{
|
||||
total += it.fileInfo().size();
|
||||
it.next();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
World::World(const QFileInfo &file)
|
||||
{
|
||||
repath(file);
|
||||
@ -196,6 +217,7 @@ void World::repath(const QFileInfo &file)
|
||||
{
|
||||
m_containerFile = file;
|
||||
m_folderName = file.fileName();
|
||||
m_size = calculateWorldSize(file);
|
||||
if(file.isFile() && file.suffix() == "zip")
|
||||
{
|
||||
m_iconFile = QString();
|
||||
@ -482,6 +504,7 @@ void World::loadFromLevelDat(QByteArray data)
|
||||
if(randomSeed) {
|
||||
qDebug() << "Seed:" << *randomSeed;
|
||||
}
|
||||
qDebug() << "Size:" << m_size;
|
||||
qDebug() << "GameType:" << m_gameType.toLogString();
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,10 @@ public:
|
||||
{
|
||||
return m_iconFile;
|
||||
}
|
||||
int64_t bytes() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
QDateTime lastPlayed() const
|
||||
{
|
||||
return m_lastPlayed;
|
||||
@ -105,6 +109,7 @@ protected:
|
||||
QString m_iconFile;
|
||||
QDateTime levelDatTime;
|
||||
QDateTime m_lastPlayed;
|
||||
int64_t m_size;
|
||||
int64_t m_randomSeed = 0;
|
||||
GameType m_gameType;
|
||||
bool is_valid = false;
|
||||
|
@ -14,6 +14,8 @@
|
||||
*/
|
||||
|
||||
#include "WorldList.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include <FileSystem.h>
|
||||
#include <QMimeData>
|
||||
#include <QUrl>
|
||||
@ -150,7 +152,7 @@ bool WorldList::resetIcon(int row)
|
||||
|
||||
int WorldList::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
return 3;
|
||||
return 4;
|
||||
}
|
||||
|
||||
QVariant WorldList::data(const QModelIndex &index, int role) const
|
||||
@ -164,6 +166,8 @@ QVariant WorldList::data(const QModelIndex &index, int role) const
|
||||
if (row < 0 || row >= worlds.size())
|
||||
return QVariant();
|
||||
|
||||
QLocale locale;
|
||||
|
||||
auto & world = worlds[row];
|
||||
switch (role)
|
||||
{
|
||||
@ -179,6 +183,9 @@ QVariant WorldList::data(const QModelIndex &index, int role) const
|
||||
case LastPlayedColumn:
|
||||
return world.lastPlayed();
|
||||
|
||||
case SizeColumn:
|
||||
return locale.formattedDataSize(world.bytes());
|
||||
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
@ -207,6 +214,10 @@ QVariant WorldList::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
return world.lastPlayed();
|
||||
}
|
||||
case SizeRole:
|
||||
{
|
||||
return locale.formattedDataSize(world.bytes());
|
||||
}
|
||||
case IconFileRole:
|
||||
{
|
||||
return world.iconFile();
|
||||
@ -229,6 +240,9 @@ QVariant WorldList::headerData(int section, Qt::Orientation orientation, int rol
|
||||
return tr("Game Mode");
|
||||
case LastPlayedColumn:
|
||||
return tr("Last Played");
|
||||
case SizeColumn:
|
||||
//: World size on disk
|
||||
return tr("Size");
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
@ -242,6 +256,8 @@ QVariant WorldList::headerData(int section, Qt::Orientation orientation, int rol
|
||||
return tr("Game mode of the world.");
|
||||
case LastPlayedColumn:
|
||||
return tr("Date and time the world was last played.");
|
||||
case SizeColumn:
|
||||
return tr("Size of the world on disk.");
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
|
@ -32,7 +32,8 @@ public:
|
||||
{
|
||||
NameColumn,
|
||||
GameModeColumn,
|
||||
LastPlayedColumn
|
||||
LastPlayedColumn,
|
||||
SizeColumn
|
||||
};
|
||||
|
||||
enum Roles
|
||||
@ -43,6 +44,7 @@ public:
|
||||
NameRole,
|
||||
GameModeRole,
|
||||
LastPlayedRole,
|
||||
SizeRole,
|
||||
IconFileRole
|
||||
};
|
||||
|
||||
|
@ -65,7 +65,7 @@ void XboxAuthorizationStep::onRequestDone(
|
||||
if(!processSTSError(error, data, headers)) {
|
||||
emit finished(
|
||||
AccountTaskState::STATE_FAILED_SOFT,
|
||||
tr("Failed to get authorization for %1 services. Error %1.").arg(m_authorizationKind, error)
|
||||
tr("Failed to get authorization for %1 services. Error %2.").arg(m_authorizationKind, error)
|
||||
);
|
||||
}
|
||||
return;
|
||||
|
@ -170,6 +170,7 @@ void LauncherPartLaunch::on_state(LoggedProcess::State state)
|
||||
{
|
||||
if (APPLICATION->settings()->get("CloseAfterLaunch").toBool())
|
||||
APPLICATION->showMainWindow();
|
||||
|
||||
m_parent->setPid(-1);
|
||||
// if the exit code wasn't 0, report this as a crash
|
||||
auto exitCode = m_process.exitCode();
|
||||
|
@ -1,50 +1,75 @@
|
||||
// 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 "VerifyJavaInstall.h"
|
||||
|
||||
#include <launch/LaunchTask.h>
|
||||
#include <minecraft/MinecraftInstance.h>
|
||||
#include <minecraft/PackProfile.h>
|
||||
#include <minecraft/VersionFilterData.h>
|
||||
|
||||
#ifdef major
|
||||
#undef major
|
||||
#endif
|
||||
#ifdef minor
|
||||
#undef minor
|
||||
#endif
|
||||
#include "java/JavaVersion.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
|
||||
void VerifyJavaInstall::executeTask() {
|
||||
auto m_inst = std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
|
||||
auto instance = std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
|
||||
auto packProfile = instance->getPackProfile();
|
||||
auto settings = instance->settings();
|
||||
auto storedVersion = settings->get("JavaVersion").toString();
|
||||
auto ignoreCompatibility = settings->get("IgnoreJavaCompatibility").toBool();
|
||||
|
||||
auto javaVersion = m_inst->getJavaVersion();
|
||||
auto minecraftComponent = m_inst->getPackProfile()->getComponent("net.minecraft");
|
||||
auto compatibleMajors = packProfile->getProfile()->getCompatibleJavaMajors();
|
||||
|
||||
// Java 17 requirement
|
||||
if (minecraftComponent->getReleaseDateTime() >= g_VersionFilterData.java17BeginsDate) {
|
||||
if (javaVersion.major() < 17) {
|
||||
emit logLine("Minecraft 1.18 Pre Release 2 and above require the use of Java 17",
|
||||
MessageLevel::Fatal);
|
||||
emitFailed(tr("Minecraft 1.18 Pre Release 2 and above require the use of Java 17"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Java 16 requirement
|
||||
else if (minecraftComponent->getReleaseDateTime() >= g_VersionFilterData.java16BeginsDate) {
|
||||
if (javaVersion.major() < 16) {
|
||||
emit logLine("Minecraft 21w19a and above require the use of Java 16",
|
||||
MessageLevel::Fatal);
|
||||
emitFailed(tr("Minecraft 21w19a and above require the use of Java 16"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Java 8 requirement
|
||||
else if (minecraftComponent->getReleaseDateTime() >= g_VersionFilterData.java8BeginsDate) {
|
||||
if (javaVersion.major() < 8) {
|
||||
emit logLine("Minecraft 17w13a and above require the use of Java 8",
|
||||
MessageLevel::Fatal);
|
||||
emitFailed(tr("Minecraft 17w13a and above require the use of Java 8"));
|
||||
return;
|
||||
}
|
||||
JavaVersion javaVersion(storedVersion);
|
||||
|
||||
if (compatibleMajors.isEmpty() || compatibleMajors.contains(javaVersion.major()))
|
||||
{
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
|
||||
emitSucceeded();
|
||||
|
||||
if (ignoreCompatibility)
|
||||
{
|
||||
emit logLine(tr("Java major version is incompatible. Things might break."), MessageLevel::Warning);
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
|
||||
emit logLine(tr("This instance is not compatible with Java version %1.\n"
|
||||
"Please switch to one of the following Java versions for this instance:").arg(javaVersion.major()),
|
||||
MessageLevel::Error);
|
||||
for (auto major : compatibleMajors)
|
||||
{
|
||||
emit logLine(tr("Java version %1").arg(major), MessageLevel::Error);
|
||||
}
|
||||
emitFailed(QString("Incompatible Java major version"));
|
||||
}
|
||||
|
@ -1,6 +1,42 @@
|
||||
// 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 <launch/LaunchStep.h>
|
||||
#include <launch/LaunchTask.h>
|
||||
|
||||
class VerifyJavaInstall : public LaunchStep {
|
||||
Q_OBJECT
|
||||
|
@ -391,7 +391,7 @@ void LocalModParseTask::processAsZip()
|
||||
zip.close();
|
||||
return;
|
||||
}
|
||||
else if (zip.setCurrentFile("fabric.mod.json"))
|
||||
else if (zip.setCurrentFile("fabric.mod.json")) // TODO: Support quilt.mod.json
|
||||
{
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
|
@ -48,6 +48,10 @@ void LibrariesTask::executeTask()
|
||||
libArtifactPool.append(profile->getLibraries());
|
||||
libArtifactPool.append(profile->getNativeLibraries());
|
||||
libArtifactPool.append(profile->getMavenFiles());
|
||||
for (auto agent : profile->getAgents())
|
||||
{
|
||||
libArtifactPool.append(agent->library());
|
||||
}
|
||||
libArtifactPool.append(profile->getMainJar());
|
||||
processArtifactPool(libArtifactPool, failedLocalLibraries, inst->getLocalLibraryPath());
|
||||
|
||||
|
69
launcher/modplatform/ModAPI.h
Normal file
69
launcher/modplatform/ModAPI.h
Normal file
@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
|
||||
#include "Version.h"
|
||||
|
||||
namespace ModPlatform {
|
||||
class ListModel;
|
||||
}
|
||||
|
||||
class ModAPI {
|
||||
protected:
|
||||
using CallerType = ModPlatform::ListModel;
|
||||
|
||||
public:
|
||||
virtual ~ModAPI() = default;
|
||||
|
||||
// https://docs.curseforge.com/?http#tocS_ModLoaderType
|
||||
enum ModLoaderType { Unspecified = 0, Forge = 1, Cauldron = 2, LiteLoader = 3, Fabric = 4, Quilt = 5 };
|
||||
|
||||
struct SearchArgs {
|
||||
int offset;
|
||||
QString search;
|
||||
QString sorting;
|
||||
ModLoaderType mod_loader;
|
||||
std::list<Version> versions;
|
||||
};
|
||||
|
||||
virtual void searchMods(CallerType* caller, SearchArgs&& args) const = 0;
|
||||
|
||||
|
||||
struct VersionSearchArgs {
|
||||
QString addonId;
|
||||
std::list<Version> mcVersions;
|
||||
ModLoaderType loader;
|
||||
};
|
||||
|
||||
virtual void getVersions(CallerType* caller, VersionSearchArgs&& args) const = 0;
|
||||
|
||||
static auto getModLoaderString(ModLoaderType type) -> const QString {
|
||||
switch (type) {
|
||||
case Unspecified:
|
||||
break;
|
||||
case Forge:
|
||||
return "forge";
|
||||
case Cauldron:
|
||||
return "cauldron";
|
||||
case LiteLoader:
|
||||
return "liteloader";
|
||||
case Fabric:
|
||||
return "fabric";
|
||||
case Quilt:
|
||||
return "quilt";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
protected:
|
||||
inline auto getGameVersionsString(std::list<Version> mcVersions) const -> QString
|
||||
{
|
||||
QString s;
|
||||
for(auto& ver : mcVersions){
|
||||
s += QString("%1,").arg(ver.toString());
|
||||
}
|
||||
s.remove(s.length() - 1, 1); //remove last comma
|
||||
return s;
|
||||
}
|
||||
};
|
42
launcher/modplatform/ModIndex.h
Normal file
42
launcher/modplatform/ModIndex.h
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <QList>
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QVector>
|
||||
|
||||
namespace ModPlatform {
|
||||
|
||||
struct ModpackAuthor {
|
||||
QString name;
|
||||
QString url;
|
||||
};
|
||||
|
||||
struct IndexedVersion {
|
||||
QVariant addonId;
|
||||
QVariant fileId;
|
||||
QString version;
|
||||
QVector<QString> mcVersion;
|
||||
QString downloadUrl;
|
||||
QString date;
|
||||
QString fileName;
|
||||
QVector<QString> loaders = {};
|
||||
};
|
||||
|
||||
struct IndexedPack {
|
||||
QVariant addonId;
|
||||
QString name;
|
||||
QString description;
|
||||
QList<ModpackAuthor> authors;
|
||||
QString logoName;
|
||||
QString logoUrl;
|
||||
QString websiteUrl;
|
||||
|
||||
bool versionsLoaded = false;
|
||||
QVector<IndexedVersion> versions;
|
||||
};
|
||||
|
||||
} // namespace ModPlatform
|
||||
|
||||
Q_DECLARE_METATYPE(ModPlatform::IndexedPack)
|
43
launcher/modplatform/flame/FlameAPI.h
Normal file
43
launcher/modplatform/flame/FlameAPI.h
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "modplatform/helpers/NetworkModAPI.h"
|
||||
|
||||
class FlameAPI : public NetworkModAPI {
|
||||
private:
|
||||
inline auto getModSearchURL(SearchArgs& args) const -> QString override
|
||||
{
|
||||
auto gameVersionStr = args.versions.size() != 0 ? QString("gameVersion=%1").arg(args.versions.front().toString()) : QString();
|
||||
|
||||
return QString(
|
||||
"https://addons-ecs.forgesvc.net/api/v2/addon/search?"
|
||||
"gameId=432&"
|
||||
"categoryId=0&"
|
||||
"sectionId=6&"
|
||||
|
||||
"index=%1&"
|
||||
"pageSize=25&"
|
||||
"searchFilter=%2&"
|
||||
"sort=%3&"
|
||||
"modLoaderType=%4&"
|
||||
"%5")
|
||||
.arg(args.offset)
|
||||
.arg(args.search)
|
||||
.arg(args.sorting)
|
||||
.arg(getMappedModLoader(args.mod_loader))
|
||||
.arg(gameVersionStr);
|
||||
};
|
||||
|
||||
inline auto getVersionsURL(VersionSearchArgs& args) const -> QString override
|
||||
{
|
||||
return QString("https://addons-ecs.forgesvc.net/api/v2/addon/%1/files").arg(args.addonId);
|
||||
};
|
||||
|
||||
public:
|
||||
static auto getMappedModLoader(const ModLoaderType type) -> const ModLoaderType
|
||||
{
|
||||
// TODO: remove this once Quilt drops official Fabric support
|
||||
if (type == Quilt) // NOTE: Most if not all Fabric mods should work *currently*
|
||||
return Fabric;
|
||||
return type;
|
||||
}
|
||||
};
|
@ -1,13 +1,12 @@
|
||||
#include <QObject>
|
||||
#include "FlameModIndex.h"
|
||||
|
||||
#include "Json.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "BaseInstance.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
|
||||
void FlameMod::loadIndexedPack(FlameMod::IndexedPack & pack, QJsonObject & obj)
|
||||
void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||
{
|
||||
pack.addonId = Json::requireInteger(obj, "id");
|
||||
pack.name = Json::requireString(obj, "name");
|
||||
@ -16,10 +15,10 @@ void FlameMod::loadIndexedPack(FlameMod::IndexedPack & pack, QJsonObject & obj)
|
||||
|
||||
bool thumbnailFound = false;
|
||||
auto attachments = Json::requireArray(obj, "attachments");
|
||||
for(auto attachmentRaw: attachments) {
|
||||
for (auto attachmentRaw : attachments) {
|
||||
auto attachmentObj = Json::requireObject(attachmentRaw);
|
||||
bool isDefault = attachmentObj.value("isDefault").toBool(false);
|
||||
if(isDefault) {
|
||||
if (isDefault) {
|
||||
thumbnailFound = true;
|
||||
pack.logoName = Json::requireString(attachmentObj, "title");
|
||||
pack.logoUrl = Json::requireString(attachmentObj, "thumbnailUrl");
|
||||
@ -27,37 +26,36 @@ void FlameMod::loadIndexedPack(FlameMod::IndexedPack & pack, QJsonObject & obj)
|
||||
}
|
||||
}
|
||||
|
||||
if(!thumbnailFound) {
|
||||
throw JSONValidationError(QString("Pack without an icon, skipping: %1").arg(pack.name));
|
||||
}
|
||||
|
||||
if (!thumbnailFound) { throw JSONValidationError(QString("Pack without an icon, skipping: %1").arg(pack.name)); }
|
||||
|
||||
auto authors = Json::requireArray(obj, "authors");
|
||||
for(auto authorIter: authors) {
|
||||
for (auto authorIter : authors) {
|
||||
auto author = Json::requireObject(authorIter);
|
||||
FlameMod::ModpackAuthor packAuthor;
|
||||
ModPlatform::ModpackAuthor packAuthor;
|
||||
packAuthor.name = Json::requireString(author, "name");
|
||||
packAuthor.url = Json::requireString(author, "url");
|
||||
pack.authors.append(packAuthor);
|
||||
}
|
||||
}
|
||||
|
||||
void FlameMod::loadIndexedPackVersions(FlameMod::IndexedPack & pack, QJsonArray & arr, const shared_qobject_ptr<QNetworkAccessManager>& network, BaseInstance * inst)
|
||||
void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
||||
QJsonArray& arr,
|
||||
const shared_qobject_ptr<QNetworkAccessManager>& network,
|
||||
BaseInstance* inst)
|
||||
{
|
||||
QVector<FlameMod::IndexedVersion> unsortedVersions;
|
||||
bool hasFabric = !((MinecraftInstance *)inst)->getPackProfile()->getComponentVersion("net.fabricmc.fabric-loader").isEmpty();
|
||||
QString mcVersion = ((MinecraftInstance *)inst)->getPackProfile()->getComponentVersion("net.minecraft");
|
||||
QVector<ModPlatform::IndexedVersion> unsortedVersions;
|
||||
auto profile = (dynamic_cast<MinecraftInstance*>(inst))->getPackProfile();
|
||||
bool hasFabric = FlameAPI::getMappedModLoader(profile->getModLoader()) == ModAPI::Fabric;
|
||||
QString mcVersion = profile->getComponentVersion("net.minecraft");
|
||||
|
||||
for(auto versionIter: arr) {
|
||||
for (auto versionIter : arr) {
|
||||
auto obj = versionIter.toObject();
|
||||
|
||||
auto versionArray = Json::requireArray(obj, "gameVersion");
|
||||
if (versionArray.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (versionArray.isEmpty()) { continue; }
|
||||
|
||||
FlameMod::IndexedVersion file;
|
||||
for(auto mcVer : versionArray){
|
||||
ModPlatform::IndexedVersion file;
|
||||
for (auto mcVer : versionArray) {
|
||||
file.mcVersion.append(mcVer.toString());
|
||||
}
|
||||
|
||||
@ -70,29 +68,28 @@ void FlameMod::loadIndexedPackVersions(FlameMod::IndexedPack & pack, QJsonArray
|
||||
|
||||
auto modules = Json::requireArray(obj, "modules");
|
||||
bool is_valid_fabric_version = false;
|
||||
for(auto m : modules){
|
||||
auto fname = Json::requireString(m.toObject(),"foldername");
|
||||
for (auto m : modules) {
|
||||
auto fname = Json::requireString(m.toObject(), "foldername");
|
||||
// FIXME: This does not work properly when a mod supports more than one mod loader, since
|
||||
// FIXME: This also doesn't deal with Quilt mods at the moment
|
||||
// they bundle the meta files for all of them in the same arquive, even when that version
|
||||
// doesn't support the given mod loader.
|
||||
if(hasFabric){
|
||||
if(fname == "fabric.mod.json"){
|
||||
if (hasFabric) {
|
||||
if (fname == "fabric.mod.json") {
|
||||
is_valid_fabric_version = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else break;
|
||||
} else
|
||||
break;
|
||||
// NOTE: Since we're not validating forge versions, we can just skip this loop.
|
||||
}
|
||||
|
||||
if(hasFabric && !is_valid_fabric_version)
|
||||
continue;
|
||||
if (hasFabric && !is_valid_fabric_version) continue;
|
||||
|
||||
unsortedVersions.append(file);
|
||||
}
|
||||
auto orderSortPredicate = [](const IndexedVersion & a, const IndexedVersion & b) -> bool
|
||||
{
|
||||
//dates are in RFC 3339 format
|
||||
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
||||
// dates are in RFC 3339 format
|
||||
return a.date > b.date;
|
||||
};
|
||||
std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate);
|
||||
|
@ -3,48 +3,18 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <QList>
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QObjectPtr.h>
|
||||
#include "net/NetJob.h"
|
||||
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
#include "BaseInstance.h"
|
||||
#include <QNetworkAccessManager>
|
||||
|
||||
namespace FlameMod {
|
||||
struct ModpackAuthor {
|
||||
QString name;
|
||||
QString url;
|
||||
};
|
||||
|
||||
struct IndexedVersion {
|
||||
int addonId;
|
||||
int fileId;
|
||||
QString version;
|
||||
QVector<QString> mcVersion;
|
||||
QString downloadUrl;
|
||||
QString date;
|
||||
QString fileName;
|
||||
};
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj);
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
||||
QJsonArray& arr,
|
||||
const shared_qobject_ptr<QNetworkAccessManager>& network,
|
||||
BaseInstance* inst);
|
||||
|
||||
struct IndexedPack
|
||||
{
|
||||
int addonId;
|
||||
QString name;
|
||||
QString description;
|
||||
QList<ModpackAuthor> authors;
|
||||
QString logoName;
|
||||
QString logoUrl;
|
||||
QString websiteUrl;
|
||||
|
||||
bool versionsLoaded = false;
|
||||
QVector<IndexedVersion> versions;
|
||||
};
|
||||
|
||||
void loadIndexedPack(IndexedPack & m, QJsonObject & obj);
|
||||
void loadIndexedPackVersions(IndexedPack &pack, QJsonArray &arr, const shared_qobject_ptr<QNetworkAccessManager> &network, BaseInstance *inst);
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(FlameMod::IndexedPack)
|
||||
} // namespace FlameMod
|
||||
|
60
launcher/modplatform/helpers/NetworkModAPI.cpp
Normal file
60
launcher/modplatform/helpers/NetworkModAPI.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include "NetworkModAPI.h"
|
||||
|
||||
#include "ui/pages/modplatform/ModModel.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
void NetworkModAPI::searchMods(CallerType* caller, SearchArgs&& args) const
|
||||
{
|
||||
auto netJob = new NetJob(QString("%1::Search").arg(caller->debugName()), APPLICATION->network());
|
||||
auto searchUrl = getModSearchURL(args);
|
||||
|
||||
auto response = new QByteArray();
|
||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response));
|
||||
|
||||
QObject::connect(netJob, &NetJob::started, caller, [caller, netJob] { caller->setActiveJob(netJob); });
|
||||
QObject::connect(netJob, &NetJob::failed, caller, &CallerType::searchRequestFailed);
|
||||
QObject::connect(netJob, &NetJob::succeeded, caller, [caller, response] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from " << caller->debugName() << " at " << parse_error.offset
|
||||
<< " reason: " << parse_error.errorString();
|
||||
qWarning() << *response;
|
||||
return;
|
||||
}
|
||||
|
||||
caller->searchRequestFinished(doc);
|
||||
});
|
||||
|
||||
netJob->start();
|
||||
}
|
||||
|
||||
void NetworkModAPI::getVersions(CallerType* caller, VersionSearchArgs&& args) const
|
||||
{
|
||||
auto netJob = new NetJob(QString("%1::ModVersions(%2)").arg(caller->debugName()).arg(args.addonId), APPLICATION->network());
|
||||
auto response = new QByteArray();
|
||||
|
||||
netJob->addNetAction(Net::Download::makeByteArray(getVersionsURL(args), response));
|
||||
|
||||
QObject::connect(netJob, &NetJob::succeeded, caller, [response, caller, args] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from " << caller->debugName() << " at " << parse_error.offset
|
||||
<< " reason: " << parse_error.errorString();
|
||||
qWarning() << *response;
|
||||
return;
|
||||
}
|
||||
|
||||
caller->versionRequestSucceeded(doc, args.addonId);
|
||||
});
|
||||
|
||||
QObject::connect(netJob, &NetJob::finished, caller, [response, netJob] {
|
||||
netJob->deleteLater();
|
||||
delete response;
|
||||
});
|
||||
|
||||
netJob->start();
|
||||
}
|
13
launcher/modplatform/helpers/NetworkModAPI.h
Normal file
13
launcher/modplatform/helpers/NetworkModAPI.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "modplatform/ModAPI.h"
|
||||
|
||||
class NetworkModAPI : public ModAPI {
|
||||
public:
|
||||
void searchMods(CallerType* caller, SearchArgs&& args) const override;
|
||||
void getVersions(CallerType* caller, VersionSearchArgs&& args) const override;
|
||||
|
||||
protected:
|
||||
virtual auto getModSearchURL(SearchArgs& args) const -> QString = 0;
|
||||
virtual auto getVersionsURL(VersionSearchArgs& args) const -> QString = 0;
|
||||
};
|
68
launcher/modplatform/modrinth/ModrinthAPI.h
Normal file
68
launcher/modplatform/modrinth/ModrinthAPI.h
Normal file
@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include "modplatform/helpers/NetworkModAPI.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
class ModrinthAPI : public NetworkModAPI {
|
||||
public:
|
||||
inline auto getAuthorURL(const QString& name) const -> QString { return "https://modrinth.com/user/" + name; };
|
||||
|
||||
private:
|
||||
inline auto getModSearchURL(SearchArgs& args) const -> QString override
|
||||
{
|
||||
if (!validateModLoader(args.mod_loader)) {
|
||||
qWarning() << "Modrinth only have Forge and Fabric-compatible mods!";
|
||||
return "";
|
||||
}
|
||||
|
||||
return QString(
|
||||
"https://api.modrinth.com/v2/search?"
|
||||
"offset=%1&"
|
||||
"limit=25&"
|
||||
"query=%2&"
|
||||
"index=%3&"
|
||||
"facets=[[\"categories:%4\"],%5[\"project_type:mod\"]]")
|
||||
.arg(args.offset)
|
||||
.arg(args.search)
|
||||
.arg(args.sorting)
|
||||
.arg(getModLoaderString(args.mod_loader))
|
||||
.arg(getGameVersionsArray(args.versions));
|
||||
};
|
||||
|
||||
inline auto getVersionsURL(VersionSearchArgs& args) const -> QString override
|
||||
{
|
||||
return QString("https://api.modrinth.com/v2/project/%1/version?"
|
||||
"game_versions=[%2]"
|
||||
"loaders=[%3]")
|
||||
.arg(args.addonId)
|
||||
.arg(getGameVersionsString(args.mcVersions))
|
||||
.arg(getModLoaderString(args.loader));
|
||||
};
|
||||
|
||||
auto getGameVersionsArray(std::list<Version> mcVersions) const -> QString
|
||||
{
|
||||
QString s;
|
||||
for(auto& ver : mcVersions){
|
||||
s += QString("\"versions:%1\",").arg(ver.toString());
|
||||
}
|
||||
s.remove(s.length() - 1, 1); //remove last comma
|
||||
return s.isEmpty() ? QString() : QString("[%1],").arg(s);
|
||||
}
|
||||
|
||||
static auto getModLoaderString(ModLoaderType type) -> const QString
|
||||
{
|
||||
if (type == Unspecified)
|
||||
return "fabric, forge, quilt";
|
||||
// TODO: remove this once Quilt drops official Fabric support
|
||||
if (type == Quilt) // NOTE: Most if not all Fabric mods should work *currently*
|
||||
return "fabric, quilt";
|
||||
return ModAPI::getModLoaderString(type);
|
||||
}
|
||||
|
||||
inline auto validateModLoader(ModLoaderType modLoader) const -> bool
|
||||
{
|
||||
return modLoader == Unspecified || modLoader == Forge || modLoader == Fabric || modLoader == Quilt;
|
||||
}
|
||||
|
||||
};
|
@ -1,50 +1,56 @@
|
||||
#include <QObject>
|
||||
#include "ModrinthPackIndex.h"
|
||||
#include "ModrinthAPI.h"
|
||||
|
||||
#include "Json.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "BaseInstance.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
static ModrinthAPI api;
|
||||
|
||||
void Modrinth::loadIndexedPack(Modrinth::IndexedPack & pack, QJsonObject & obj)
|
||||
void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||
{
|
||||
pack.addonId = Json::requireString(obj, "project_id");
|
||||
pack.name = Json::requireString(obj, "title");
|
||||
pack.websiteUrl = Json::ensureString(obj, "page_url", "");
|
||||
|
||||
QString slug = Json::ensureString(obj, "slug", "");
|
||||
if (!slug.isEmpty())
|
||||
pack.websiteUrl = "https://modrinth.com/mod/" + Json::ensureString(obj, "slug", "");
|
||||
else
|
||||
pack.websiteUrl = "";
|
||||
|
||||
pack.description = Json::ensureString(obj, "description", "");
|
||||
|
||||
pack.logoUrl = Json::requireString(obj, "icon_url");
|
||||
pack.logoName = pack.addonId;
|
||||
pack.logoName = pack.addonId.toString();
|
||||
|
||||
Modrinth::ModpackAuthor modAuthor;
|
||||
ModPlatform::ModpackAuthor modAuthor;
|
||||
modAuthor.name = Json::requireString(obj, "author");
|
||||
modAuthor.url = "https://modrinth.com/user/"+modAuthor.name;
|
||||
pack.author = modAuthor;
|
||||
modAuthor.url = api.getAuthorURL(modAuthor.name);
|
||||
pack.authors.append(modAuthor);
|
||||
}
|
||||
|
||||
void Modrinth::loadIndexedPackVersions(Modrinth::IndexedPack & pack, QJsonArray & arr, const shared_qobject_ptr<QNetworkAccessManager>& network, BaseInstance * inst)
|
||||
void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
||||
QJsonArray& arr,
|
||||
const shared_qobject_ptr<QNetworkAccessManager>& network,
|
||||
BaseInstance* inst)
|
||||
{
|
||||
QVector<Modrinth::IndexedVersion> unsortedVersions;
|
||||
bool hasFabric = !((MinecraftInstance *)inst)->getPackProfile()->getComponentVersion("net.fabricmc.fabric-loader").isEmpty();
|
||||
QString mcVersion = ((MinecraftInstance *)inst)->getPackProfile()->getComponentVersion("net.minecraft");
|
||||
QVector<ModPlatform::IndexedVersion> unsortedVersions;
|
||||
QString mcVersion = (static_cast<MinecraftInstance*>(inst))->getPackProfile()->getComponentVersion("net.minecraft");
|
||||
|
||||
for(auto versionIter: arr) {
|
||||
for (auto versionIter : arr) {
|
||||
auto obj = versionIter.toObject();
|
||||
Modrinth::IndexedVersion file;
|
||||
file.addonId = Json::requireString(obj,"project_id") ;
|
||||
ModPlatform::IndexedVersion file;
|
||||
file.addonId = Json::requireString(obj, "project_id");
|
||||
file.fileId = Json::requireString(obj, "id");
|
||||
file.date = Json::requireString(obj, "date_published");
|
||||
auto versionArray = Json::requireArray(obj, "game_versions");
|
||||
if (versionArray.empty()) {
|
||||
continue;
|
||||
}
|
||||
for(auto mcVer : versionArray){
|
||||
if (versionArray.empty()) { continue; }
|
||||
for (auto mcVer : versionArray) {
|
||||
file.mcVersion.append(mcVer.toString());
|
||||
}
|
||||
auto loaders = Json::requireArray(obj,"loaders");
|
||||
for(auto loader : loaders){
|
||||
auto loaders = Json::requireArray(obj, "loaders");
|
||||
for (auto loader : loaders) {
|
||||
file.loaders.append(loader.toString());
|
||||
}
|
||||
file.version = Json::requireString(obj, "name");
|
||||
@ -60,17 +66,6 @@ void Modrinth::loadIndexedPackVersions(Modrinth::IndexedPack & pack, QJsonArray
|
||||
auto parent = files[i].toObject();
|
||||
auto fileName = Json::requireString(parent, "filename");
|
||||
|
||||
// Grab the correct mod loader
|
||||
if(hasFabric){
|
||||
if(fileName.contains("forge",Qt::CaseInsensitive)){
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
} else if(fileName.contains("fabric", Qt::CaseInsensitive)){
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Grab the primary file, if available
|
||||
if(Json::requireBoolean(parent, "primary"))
|
||||
break;
|
||||
@ -78,18 +73,16 @@ void Modrinth::loadIndexedPackVersions(Modrinth::IndexedPack & pack, QJsonArray
|
||||
i++;
|
||||
}
|
||||
|
||||
|
||||
auto parent = files[i].toObject();
|
||||
if(parent.contains("url")) {
|
||||
if (parent.contains("url")) {
|
||||
file.downloadUrl = Json::requireString(parent, "url");
|
||||
file.fileName = Json::requireString(parent, "filename");
|
||||
|
||||
unsortedVersions.append(file);
|
||||
}
|
||||
}
|
||||
auto orderSortPredicate = [](const IndexedVersion & a, const IndexedVersion & b) -> bool
|
||||
{
|
||||
//dates are in RFC 3339 format
|
||||
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
||||
// dates are in RFC 3339 format
|
||||
return a.date > b.date;
|
||||
};
|
||||
std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate);
|
||||
|
@ -1,48 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <QList>
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QObjectPtr.h>
|
||||
#include "net/NetJob.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
#include "BaseInstance.h"
|
||||
#include <QNetworkAccessManager>
|
||||
|
||||
namespace Modrinth {
|
||||
|
||||
struct ModpackAuthor {
|
||||
QString name;
|
||||
QString url;
|
||||
};
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj);
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
||||
QJsonArray& arr,
|
||||
const shared_qobject_ptr<QNetworkAccessManager>& network,
|
||||
BaseInstance* inst);
|
||||
|
||||
struct IndexedVersion {
|
||||
QString addonId;
|
||||
QString fileId;
|
||||
QString version;
|
||||
QVector<QString> mcVersion;
|
||||
QString downloadUrl;
|
||||
QString date;
|
||||
QString fileName;
|
||||
QVector<QString> loaders;
|
||||
};
|
||||
|
||||
struct IndexedPack
|
||||
{
|
||||
QString addonId;
|
||||
QString name;
|
||||
QString description;
|
||||
ModpackAuthor author;
|
||||
QString logoName;
|
||||
QString logoUrl;
|
||||
QString websiteUrl;
|
||||
|
||||
bool versionsLoaded = false;
|
||||
QVector<IndexedVersion> versions;
|
||||
};
|
||||
|
||||
void loadIndexedPack(IndexedPack & m, QJsonObject & obj);
|
||||
void loadIndexedPackVersions(IndexedPack &pack, QJsonArray &arr, const shared_qobject_ptr<QNetworkAccessManager> &network, BaseInstance *inst);
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(Modrinth::IndexedPack)
|
||||
} // namespace Modrinth
|
||||
|
@ -1,16 +1,36 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (c) 2021-2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* 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 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.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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 "SolderPackInstallTask.h"
|
||||
@ -19,16 +39,23 @@
|
||||
#include <Json.h>
|
||||
#include <QtConcurrentRun>
|
||||
#include <MMCZip.h>
|
||||
|
||||
#include "TechnicPackProcessor.h"
|
||||
#include "SolderPackManifest.h"
|
||||
#include "net/ChecksumValidator.h"
|
||||
|
||||
Technic::SolderPackInstallTask::SolderPackInstallTask(
|
||||
shared_qobject_ptr<QNetworkAccessManager> network,
|
||||
const QUrl &sourceUrl,
|
||||
const QUrl &solderUrl,
|
||||
const QString &pack,
|
||||
const QString &version,
|
||||
const QString &minecraftVersion
|
||||
) {
|
||||
m_sourceUrl = sourceUrl;
|
||||
m_minecraftVersion = minecraftVersion;
|
||||
m_solderUrl = solderUrl;
|
||||
m_pack = pack;
|
||||
m_version = version;
|
||||
m_network = network;
|
||||
m_minecraftVersion = minecraftVersion;
|
||||
}
|
||||
|
||||
bool Technic::SolderPackInstallTask::abort() {
|
||||
@ -41,34 +68,12 @@ bool Technic::SolderPackInstallTask::abort() {
|
||||
|
||||
void Technic::SolderPackInstallTask::executeTask()
|
||||
{
|
||||
setStatus(tr("Finding recommended version:\n%1").arg(m_sourceUrl.toString()));
|
||||
m_filesNetJob = new NetJob(tr("Finding recommended version"), m_network);
|
||||
m_filesNetJob->addNetAction(Net::Download::makeByteArray(m_sourceUrl, &m_response));
|
||||
auto job = m_filesNetJob.get();
|
||||
connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::versionSucceeded);
|
||||
connect(job, &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);
|
||||
m_filesNetJob->start();
|
||||
}
|
||||
setStatus(tr("Resolving modpack files"));
|
||||
|
||||
void Technic::SolderPackInstallTask::versionSucceeded()
|
||||
{
|
||||
try
|
||||
{
|
||||
QJsonDocument doc = Json::requireDocument(m_response);
|
||||
QJsonObject obj = Json::requireObject(doc);
|
||||
QString version = Json::requireString(obj, "recommended", "__placeholder__");
|
||||
m_sourceUrl = m_sourceUrl.toString() + '/' + version;
|
||||
}
|
||||
catch (const JSONValidationError &e)
|
||||
{
|
||||
emitFailed(e.cause());
|
||||
m_filesNetJob.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
setStatus(tr("Resolving modpack files:\n%1").arg(m_sourceUrl.toString()));
|
||||
m_filesNetJob = new NetJob(tr("Resolving modpack files"), m_network);
|
||||
m_filesNetJob->addNetAction(Net::Download::makeByteArray(m_sourceUrl, &m_response));
|
||||
auto sourceUrl = QString("%1/modpack/%2/%3").arg(m_solderUrl.toString(), m_pack, m_version);
|
||||
m_filesNetJob->addNetAction(Net::Download::makeByteArray(sourceUrl, &m_response));
|
||||
|
||||
auto job = m_filesNetJob.get();
|
||||
connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::fileListSucceeded);
|
||||
connect(job, &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);
|
||||
@ -77,38 +82,47 @@ void Technic::SolderPackInstallTask::versionSucceeded()
|
||||
|
||||
void Technic::SolderPackInstallTask::fileListSucceeded()
|
||||
{
|
||||
setStatus(tr("Downloading modpack:"));
|
||||
QStringList modUrls;
|
||||
try
|
||||
{
|
||||
QJsonDocument doc = Json::requireDocument(m_response);
|
||||
QJsonObject obj = Json::requireObject(doc);
|
||||
QString minecraftVersion = Json::ensureString(obj, "minecraft", QString(), "__placeholder__");
|
||||
if (!minecraftVersion.isEmpty())
|
||||
m_minecraftVersion = minecraftVersion;
|
||||
QJsonArray mods = Json::requireArray(obj, "mods", "'mods'");
|
||||
for (auto mod: mods)
|
||||
{
|
||||
QJsonObject modObject = Json::requireObject(mod);
|
||||
modUrls.append(Json::requireString(modObject, "url", "'url'"));
|
||||
}
|
||||
setStatus(tr("Downloading modpack"));
|
||||
|
||||
QJsonParseError parse_error {};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(m_response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Solder at " << parse_error.offset << " reason: " << parse_error.errorString();
|
||||
qWarning() << m_response;
|
||||
return;
|
||||
}
|
||||
catch (const JSONValidationError &e)
|
||||
{
|
||||
emitFailed(e.cause());
|
||||
auto obj = doc.object();
|
||||
|
||||
TechnicSolder::PackBuild build;
|
||||
try {
|
||||
TechnicSolder::loadPackBuild(build, obj);
|
||||
}
|
||||
catch (const JSONValidationError& e) {
|
||||
emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
|
||||
m_filesNetJob.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!build.minecraft.isEmpty())
|
||||
m_minecraftVersion = build.minecraft;
|
||||
|
||||
m_filesNetJob = new NetJob(tr("Downloading modpack"), m_network);
|
||||
|
||||
int i = 0;
|
||||
for (auto &modUrl: modUrls)
|
||||
{
|
||||
for (const auto &mod : build.mods) {
|
||||
auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i));
|
||||
m_filesNetJob->addNetAction(Net::Download::makeFile(modUrl, path));
|
||||
|
||||
auto dl = Net::Download::makeFile(mod.url, path);
|
||||
if (!mod.md5.isEmpty()) {
|
||||
auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1());
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
|
||||
}
|
||||
m_filesNetJob->addNetAction(dl);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
m_modCount = modUrls.size();
|
||||
m_modCount = build.mods.size();
|
||||
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &Technic::SolderPackInstallTask::downloadSucceeded);
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &Technic::SolderPackInstallTask::downloadProgressChanged);
|
||||
@ -206,6 +220,5 @@ void Technic::SolderPackInstallTask::extractFinished()
|
||||
void Technic::SolderPackInstallTask::extractAborted()
|
||||
{
|
||||
emitFailed(tr("Instance import has been aborted."));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,36 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (c) 2021-2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* 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 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.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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
|
||||
@ -27,7 +47,7 @@ namespace Technic
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, const QUrl &sourceUrl, const QString &minecraftVersion);
|
||||
explicit SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, const QUrl &solderUrl, const QString& pack, const QString& version, const QString &minecraftVersion);
|
||||
|
||||
bool canAbort() const override { return true; }
|
||||
bool abort() override;
|
||||
@ -37,7 +57,6 @@ namespace Technic
|
||||
virtual void executeTask() override;
|
||||
|
||||
private slots:
|
||||
void versionSucceeded();
|
||||
void fileListSucceeded();
|
||||
void downloadSucceeded();
|
||||
void downloadFailed(QString reason);
|
||||
@ -51,7 +70,9 @@ namespace Technic
|
||||
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
||||
|
||||
NetJob::Ptr m_filesNetJob;
|
||||
QUrl m_sourceUrl;
|
||||
QUrl m_solderUrl;
|
||||
QString m_pack;
|
||||
QString m_version;
|
||||
QString m_minecraftVersion;
|
||||
QByteArray m_response;
|
||||
QTemporaryDir m_outputDir;
|
||||
|
58
launcher/modplatform/technic/SolderPackManifest.cpp
Normal file
58
launcher/modplatform/technic/SolderPackManifest.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SolderPackManifest.h"
|
||||
|
||||
#include "Json.h"
|
||||
|
||||
namespace TechnicSolder {
|
||||
|
||||
void loadPack(Pack& v, QJsonObject& obj)
|
||||
{
|
||||
v.recommended = Json::requireString(obj, "recommended");
|
||||
v.latest = Json::requireString(obj, "latest");
|
||||
|
||||
auto builds = Json::requireArray(obj, "builds");
|
||||
for (const auto buildRaw : builds) {
|
||||
auto build = Json::requireString(buildRaw);
|
||||
v.builds.append(build);
|
||||
}
|
||||
}
|
||||
|
||||
static void loadPackBuildMod(PackBuildMod& b, QJsonObject& obj)
|
||||
{
|
||||
b.name = Json::requireString(obj, "name");
|
||||
b.version = Json::requireString(obj, "version");
|
||||
b.md5 = Json::requireString(obj, "md5");
|
||||
b.url = Json::requireString(obj, "url");
|
||||
}
|
||||
|
||||
void loadPackBuild(PackBuild& v, QJsonObject& obj)
|
||||
{
|
||||
v.minecraft = Json::requireString(obj, "minecraft");
|
||||
|
||||
auto mods = Json::requireArray(obj, "mods");
|
||||
for (const auto modRaw : mods) {
|
||||
auto modObj = Json::requireObject(modRaw);
|
||||
PackBuildMod mod;
|
||||
loadPackBuildMod(mod, modObj);
|
||||
v.mods.append(mod);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
49
launcher/modplatform/technic/SolderPackManifest.h
Normal file
49
launcher/modplatform/technic/SolderPackManifest.h
Normal file
@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QJsonObject>
|
||||
|
||||
namespace TechnicSolder {
|
||||
|
||||
struct Pack {
|
||||
QString recommended;
|
||||
QString latest;
|
||||
QVector<QString> builds;
|
||||
};
|
||||
|
||||
void loadPack(Pack& v, QJsonObject& obj);
|
||||
|
||||
struct PackBuildMod {
|
||||
QString name;
|
||||
QString version;
|
||||
QString md5;
|
||||
QString url;
|
||||
};
|
||||
|
||||
struct PackBuild {
|
||||
QString minecraft;
|
||||
QVector<PackBuildMod> mods;
|
||||
};
|
||||
|
||||
void loadPackBuild(PackBuild& v, QJsonObject& obj);
|
||||
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
#include "NotificationChecker.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QDebug>
|
||||
|
||||
#include "net/Download.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
NotificationChecker::NotificationChecker(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void NotificationChecker::setNotificationsUrl(const QUrl ¬ificationsUrl)
|
||||
{
|
||||
m_notificationsUrl = notificationsUrl;
|
||||
}
|
||||
|
||||
void NotificationChecker::setApplicationChannel(QString channel)
|
||||
{
|
||||
m_appVersionChannel = channel;
|
||||
}
|
||||
|
||||
void NotificationChecker::setApplicationFullVersion(QString version)
|
||||
{
|
||||
m_appFullVersion = version;
|
||||
}
|
||||
|
||||
void NotificationChecker::setApplicationPlatform(QString platform)
|
||||
{
|
||||
m_appPlatform = platform;
|
||||
}
|
||||
|
||||
QList<NotificationChecker::NotificationEntry> NotificationChecker::notificationEntries() const
|
||||
{
|
||||
return m_entries;
|
||||
}
|
||||
|
||||
void NotificationChecker::checkForNotifications()
|
||||
{
|
||||
if (!m_notificationsUrl.isValid())
|
||||
{
|
||||
qCritical() << "Failed to check for notifications. No notifications URL set."
|
||||
<< "If you'd like to use PolyMC's notification system, please pass the "
|
||||
"URL to CMake at compile time.";
|
||||
return;
|
||||
}
|
||||
if (m_checkJob)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_checkJob = new NetJob("Checking for notifications", APPLICATION->network());
|
||||
auto entry = APPLICATION->metacache()->resolveEntry("root", "notifications.json");
|
||||
entry->setStale(true);
|
||||
m_checkJob->addNetAction(m_download = Net::Download::makeCached(m_notificationsUrl, entry));
|
||||
connect(m_download.get(), &Net::Download::succeeded, this, &NotificationChecker::downloadSucceeded);
|
||||
m_checkJob->start();
|
||||
}
|
||||
|
||||
void NotificationChecker::downloadSucceeded(int)
|
||||
{
|
||||
m_entries.clear();
|
||||
|
||||
QFile file(m_download->getTargetFilepath());
|
||||
if (file.open(QFile::ReadOnly))
|
||||
{
|
||||
QJsonArray root = QJsonDocument::fromJson(file.readAll()).array();
|
||||
for (auto it = root.begin(); it != root.end(); ++it)
|
||||
{
|
||||
QJsonObject obj = (*it).toObject();
|
||||
NotificationEntry entry;
|
||||
entry.id = obj.value("id").toDouble();
|
||||
entry.message = obj.value("message").toString();
|
||||
entry.channel = obj.value("channel").toString();
|
||||
entry.platform = obj.value("platform").toString();
|
||||
entry.from = obj.value("from").toString();
|
||||
entry.to = obj.value("to").toString();
|
||||
const QString type = obj.value("type").toString("critical");
|
||||
if (type == "critical")
|
||||
{
|
||||
entry.type = NotificationEntry::Critical;
|
||||
}
|
||||
else if (type == "warning")
|
||||
{
|
||||
entry.type = NotificationEntry::Warning;
|
||||
}
|
||||
else if (type == "information")
|
||||
{
|
||||
entry.type = NotificationEntry::Information;
|
||||
}
|
||||
if(entryApplies(entry))
|
||||
m_entries.append(entry);
|
||||
}
|
||||
}
|
||||
|
||||
m_checkJob.reset();
|
||||
|
||||
emit notificationCheckFinished();
|
||||
}
|
||||
|
||||
bool versionLessThan(const QString &v1, const QString &v2)
|
||||
{
|
||||
QStringList l1 = v1.split('.');
|
||||
QStringList l2 = v2.split('.');
|
||||
while (!l1.isEmpty() && !l2.isEmpty())
|
||||
{
|
||||
int one = l1.isEmpty() ? 0 : l1.takeFirst().toInt();
|
||||
int two = l2.isEmpty() ? 0 : l2.takeFirst().toInt();
|
||||
if (one != two)
|
||||
{
|
||||
return one < two;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NotificationChecker::entryApplies(const NotificationChecker::NotificationEntry& entry) const
|
||||
{
|
||||
bool channelApplies = entry.channel.isEmpty() || entry.channel == m_appVersionChannel;
|
||||
bool platformApplies = entry.platform.isEmpty() || entry.platform == m_appPlatform;
|
||||
bool fromApplies =
|
||||
entry.from.isEmpty() || entry.from == m_appFullVersion || !versionLessThan(m_appFullVersion, entry.from);
|
||||
bool toApplies =
|
||||
entry.to.isEmpty() || entry.to == m_appFullVersion || !versionLessThan(entry.to, m_appFullVersion);
|
||||
return channelApplies && platformApplies && fromApplies && toApplies;
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "net/NetJob.h"
|
||||
#include "net/Download.h"
|
||||
|
||||
class NotificationChecker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NotificationChecker(QObject *parent = 0);
|
||||
|
||||
void setNotificationsUrl(const QUrl ¬ificationsUrl);
|
||||
void setApplicationPlatform(QString platform);
|
||||
void setApplicationChannel(QString channel);
|
||||
void setApplicationFullVersion(QString version);
|
||||
|
||||
struct NotificationEntry
|
||||
{
|
||||
int id;
|
||||
QString message;
|
||||
enum
|
||||
{
|
||||
Critical,
|
||||
Warning,
|
||||
Information
|
||||
} type;
|
||||
QString channel;
|
||||
QString platform;
|
||||
QString from;
|
||||
QString to;
|
||||
};
|
||||
|
||||
QList<NotificationEntry> notificationEntries() const;
|
||||
|
||||
public
|
||||
slots:
|
||||
void checkForNotifications();
|
||||
|
||||
private
|
||||
slots:
|
||||
void downloadSucceeded(int);
|
||||
|
||||
signals:
|
||||
void notificationCheckFinished();
|
||||
|
||||
private:
|
||||
bool entryApplies(const NotificationEntry &entry) const;
|
||||
|
||||
private:
|
||||
QList<NotificationEntry> m_entries;
|
||||
QUrl m_notificationsUrl;
|
||||
NetJob::Ptr m_checkJob;
|
||||
Net::Download::Ptr m_download;
|
||||
|
||||
QString m_appVersionChannel;
|
||||
QString m_appPlatform;
|
||||
QString m_appFullVersion;
|
||||
};
|
@ -1,108 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="128mm"
|
||||
height="128mm"
|
||||
viewBox="0 0 453.54331 453.54331"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="discord.svg">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4166">
|
||||
<stop
|
||||
style="stop-color:#7593d7;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4168" />
|
||||
<stop
|
||||
style="stop-color:#4f6aa3;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop4170" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4166"
|
||||
id="linearGradient4174"
|
||||
x1="351.42856"
|
||||
y1="513.79077"
|
||||
x2="351.42856"
|
||||
y2="943.79077"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(24.999996,-5.8267714e-6)" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.98994949"
|
||||
inkscape:cx="249.4082"
|
||||
inkscape:cy="153.28604"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:snap-nodes="true"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="false"
|
||||
inkscape:snap-bbox-edge-midpoints="false"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-midpoints="false"
|
||||
inkscape:snap-page="true"
|
||||
inkscape:window-width="1911"
|
||||
inkscape:window-height="2120"
|
||||
inkscape:window-x="2970"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4155" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-1.4285583,-500.24745)">
|
||||
<path
|
||||
style="fill:url(#linearGradient4174);fill-opacity:1;fill-rule:evenodd"
|
||||
d="m 73.928594,513.79076 a 37.5,37.5 0 0 0 -37.36914,34.88476 l -0.13086,0.11524 0,2.5 0,302.5 0.11914,0.11914 a 37.5,37.5 0 0 0 -0.11914,2.38086 37.5,37.5 0 0 0 37.5,37.5 37.5,37.5 0 0 0 2.41406,-0.0859 l 0.0859,0.0859 250.000006,0 -10,-40 100,90 0,-392.5 0,-2.5 -0.0918,-0.0918 a 37.5,37.5 0 0 0 -34.77734,-34.77734 l -0.13086,-0.13086 -2.5,0 -302.500006,0 -0.13477,0.11914 a 37.5,37.5 0 0 0 -2.36523,-0.11914 z"
|
||||
id="path4157"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-rule:evenodd"
|
||||
d="m 262.51367,111.60742 -3.53515,4.79688 c 20.92905,7.8755 41.53825,15.82838 52.90625,26.01172 -54.51023,-27.29925 -111.94069,-29.57156 -173.11524,0.25195 13.83558,-12.04023 34.12183,-19.9521 56.31641,-26.64258 l -2.39844,-3.78711 c -19.87573,2.47189 -39.74673,4.97961 -57.48273,19.75608 -1.26426,1.0533 -2.43875,1.7112 -3.75946,3.35134 -1.04157,1.68882 -1.47654,2.94289 -2.20384,4.4259 -16.51968,33.6846 -29.13904,71.47893 -29.23561,122.47449 13.7929,19.97074 35.85963,31.20604 66.79492,33.08203 l 14.01563,-19.0664 c -16.32939,-4.839 -28.63498,-12.99042 -36.86914,-24.4961 68.24127,41.30702 122.04171,25.27927 169.32617,-0.50585 -10.12692,12.60017 -23.08016,21.50538 -39.39649,26.01171 l 14.52149,17.80469 c 31.64413,-2.6726 53.44205,-13.71547 66.03906,-32.57812 -1.25854,-42.6757 -9.40904,-79.47015 -23.71494,-111.01207 -2.51316,-5.54105 -4.27674,-11.49413 -8.10537,-16.14028 -3.45571,-4.1936 -7.96544,-7.13252 -12.90109,-9.9657 -12.42806,-7.13399 -28.6789,-11.32721 -47.20243,-13.77258 z m -76.00195,82.42383 c 12.33462,8e-5 22.33381,10.55717 22.33398,23.58008 -1.7e-4,13.02291 -9.99936,23.58 -22.33398,23.58008 -12.33462,-7e-5 -22.33382,-10.55716 -22.33399,-23.58008 1.7e-4,-13.02292 9.99937,-23.58001 22.33399,-23.58008 z m 79.54883,0 c 12.33462,8e-5 22.33381,10.55717 22.33398,23.58008 -1.7e-4,13.02291 -9.99936,23.58 -22.33398,23.58008 -12.33462,-7e-5 -22.33382,-10.55716 -22.33399,-23.58008 1.7e-4,-13.02292 9.99937,-23.58001 22.33399,-23.58008 z"
|
||||
transform="translate(1.4285583,500.24745)"
|
||||
id="path4176"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccscsccccccccsasccccccccccc" />
|
||||
</g>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg fill="none" version="1.1" viewBox="0 0 71 71" xmlns="http://www.w3.org/2000/svg">
|
||||
<g transform="translate(0 8.0294)" clip-path="url(#clip0)">
|
||||
<path d="m60.104 4.8978c-4.5253-2.0764-9.378-3.6062-14.452-4.4824-0.0924-0.01691-0.1847 0.025349-0.2323 0.10987-0.6241 1.11-1.3154 2.5581-1.7995 3.6963-5.4572-0.817-10.886-0.817-16.232 0-0.4842-1.1635-1.2006-2.5863-1.8275-3.6963-0.0476-0.0817-0.1399-0.12396-0.2323-0.10987-5.071 0.87338-9.9237 2.4032-14.452 4.4824-0.0392 0.0169-0.0728 0.0451-0.0951 0.0817-9.2046 13.751-11.726 27.165-10.489 40.412 0.005597 0.0648 0.041978 0.1268 0.092353 0.1662 6.0729 4.4598 11.956 7.1673 17.729 8.9619 0.0924 0.0282 0.1903-0.0056 0.2491-0.0817 1.3657-1.865 2.5831-3.8315 3.6269-5.8995 0.0616-0.1211 0.0028-0.2648-0.1231-0.3127-1.931-0.7325-3.7697-1.6256-5.5384-2.6398-0.1399-0.0817-0.1511-0.2818-0.0224-0.3776 0.3722-0.2789 0.7445-0.5691 1.0999-0.8621 0.0643-0.0535 0.1539-0.0648 0.2295-0.031 11.62 5.3051 24.199 5.3051 35.682 0 0.0756-0.0366 0.1652-0.0253 0.2323 0.0282 0.3555 0.293 0.7277 0.586 1.1027 0.8649 0.1287 0.0958 0.1203 0.2959-0.0196 0.3776-1.7687 1.0339-3.6074 1.9073-5.5412 2.637-0.1259 0.0479-0.1819 0.1944-0.1203 0.3155 1.0662 2.0651 2.2836 4.0316 3.6241 5.8967 0.056 0.0789 0.1567 0.1127 0.2491 0.0845 5.8014-1.7946 11.684-4.5021 17.757-8.9619 0.0532-0.0394 0.0868-0.0986 0.0924-0.1634 1.4804-15.315-2.4796-28.618-10.498-40.412-0.0196-0.0394-0.0531-0.0676-0.0923-0.0845zm-36.379 32.428c-3.4983 0-6.3808-3.2117-6.3808-7.156s2.8266-7.156 6.3808-7.156c3.5821 0 6.4367 3.2399 6.3807 7.156 0 3.9443-2.8266 7.156-6.3807 7.156zm23.592 0c-3.4982 0-6.3807-3.2117-6.3807-7.156s2.8265-7.156 6.3807-7.156c3.5822 0 6.4367 3.2399 6.3808 7.156 0 3.9443-2.7986 7.156-6.3808 7.156z" fill="#5865f2"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="71" height="55" fill="#fff"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 1.8 KiB |
@ -3,18 +3,18 @@
|
||||
<svg width="64" height="64" version="1.1" viewBox="0 0 16.933 16.933" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="linearGradient84726" x1="4.4979" x2="12.435" y1="3.8011" y2="9.5681" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#88b858" offset="0"/>
|
||||
<stop stop-color="#72b147" offset=".5"/>
|
||||
<stop stop-color="#5a9a30" offset="1"/>
|
||||
<stop stop-color="#dedede" offset="0"/>
|
||||
<stop stop-color="#d2d2d2" offset=".5"/>
|
||||
<stop stop-color="#c0c0c0" offset="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g>
|
||||
<path d="m3.561 16.016s0-3.5642 4.9056-3.5642c4.9069 0 4.9056 3.5642 4.9056 3.5642z" fill="#765338"/>
|
||||
<path d="m8.4667 12.452-4.9056 3.5642-3.0319-9.3311z" fill="#b7835a"/>
|
||||
<path d="m8.4667 12.452 7.9375-5.7669-3.0319 9.3311z" fill="#5b422d"/>
|
||||
<path d="m8.8308 12.716-0.36417 0.26458-0.36417-0.26458c0-0.26458 0.36417-0.26458 0.36417-0.26458s0.36417 0 0.36417 0.26458z" fill="#72b147"/>
|
||||
<path d="m8.4667 12.452s-2e-7 -5.7669 7.9375-5.7669l-0.22507 0.69269-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819z" fill="#5a9a30"/>
|
||||
<path d="m8.1025 12.716-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.22507-0.69269c7.9375 1e-7 7.9375 5.7669 7.9375 5.7669z" fill="#88b858"/>
|
||||
<path d="m3.561 16.016s0-3.5642 4.9056-3.5642c4.9069 0 4.9056 3.5642 4.9056 3.5642z" fill="#8f8f8f"/>
|
||||
<path d="m8.4667 12.452-4.9056 3.5642-3.0319-9.3311z" fill="#c2c2c2"/>
|
||||
<path d="m8.4667 12.452 7.9375-5.7669-3.0319 9.3311z" fill="#7c7c7c"/>
|
||||
<path d="m8.8308 12.716-0.36417 0.26458-0.36417-0.26458c0-0.26458 0.36417-0.26458 0.36417-0.26458s0.36417 0 0.36417 0.26458z" fill="#d3d3d3"/>
|
||||
<path d="m8.4667 12.452s-2e-7 -5.7669 7.9375-5.7669l-0.22507 0.69269-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819z" fill="#bcbcbc"/>
|
||||
<path d="m8.1025 12.716-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.22507-0.69269c7.9375 1e-7 7.9375 5.7669 7.9375 5.7669z" fill="#dedede"/>
|
||||
<path d="m0.52917 6.6846 7.9375 5.7669 7.9375-5.7669-7.9375-5.7669z" fill="url(#linearGradient84726)"/>
|
||||
</g>
|
||||
<path d="m0.75424 7.3773-0.22507-0.69269 7.9375 5.7669 7.9375-5.7669-0.22507 0.69269-7.7124 5.6034z" fill-opacity="0"/>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
@ -1,7 +1,23 @@
|
||||
#include "SequentialTask.h"
|
||||
|
||||
SequentialTask::SequentialTask(QObject *parent) : Task(parent), m_currentIndex(-1)
|
||||
SequentialTask::SequentialTask(QObject* parent, const QString& task_name) : Task(parent), m_name(task_name), m_currentIndex(-1) {}
|
||||
|
||||
SequentialTask::~SequentialTask()
|
||||
{
|
||||
for(auto task : m_queue){
|
||||
if(task)
|
||||
task->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
auto SequentialTask::getStepProgress() const -> qint64
|
||||
{
|
||||
return m_stepProgress;
|
||||
}
|
||||
|
||||
auto SequentialTask::getStepTotalProgress() const -> qint64
|
||||
{
|
||||
return m_stepTotalProgress;
|
||||
}
|
||||
|
||||
void SequentialTask::addTask(Task::Ptr task)
|
||||
@ -15,16 +31,24 @@ void SequentialTask::executeTask()
|
||||
startNext();
|
||||
}
|
||||
|
||||
bool SequentialTask::abort()
|
||||
{
|
||||
bool succeeded = true;
|
||||
for (auto& task : m_queue) {
|
||||
if (!task->abort()) succeeded = false;
|
||||
}
|
||||
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
void SequentialTask::startNext()
|
||||
{
|
||||
if (m_currentIndex != -1)
|
||||
{
|
||||
if (m_currentIndex != -1) {
|
||||
Task::Ptr previous = m_queue[m_currentIndex];
|
||||
disconnect(previous.get(), 0, this, 0);
|
||||
}
|
||||
m_currentIndex++;
|
||||
if (m_queue.isEmpty() || m_currentIndex >= m_queue.size())
|
||||
{
|
||||
if (m_queue.isEmpty() || m_currentIndex >= m_queue.size()) {
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
@ -33,23 +57,27 @@ void SequentialTask::startNext()
|
||||
connect(next.get(), SIGNAL(status(QString)), this, SLOT(subTaskStatus(QString)));
|
||||
connect(next.get(), SIGNAL(progress(qint64, qint64)), this, SLOT(subTaskProgress(qint64, qint64)));
|
||||
connect(next.get(), SIGNAL(succeeded()), this, SLOT(startNext()));
|
||||
|
||||
setStatus(tr("Executing task %1 out of %2").arg(m_currentIndex + 1).arg(m_queue.size()));
|
||||
next->start();
|
||||
}
|
||||
|
||||
void SequentialTask::subTaskFailed(const QString &msg)
|
||||
void SequentialTask::subTaskFailed(const QString& msg)
|
||||
{
|
||||
emitFailed(msg);
|
||||
}
|
||||
void SequentialTask::subTaskStatus(const QString &msg)
|
||||
void SequentialTask::subTaskStatus(const QString& msg)
|
||||
{
|
||||
setStatus(msg);
|
||||
setStepStatus(m_queue[m_currentIndex]->getStatus());
|
||||
}
|
||||
void SequentialTask::subTaskProgress(qint64 current, qint64 total)
|
||||
{
|
||||
if(total == 0)
|
||||
{
|
||||
if (total == 0) {
|
||||
setProgress(0, 100);
|
||||
return;
|
||||
}
|
||||
setProgress(current, total);
|
||||
setProgress(m_currentIndex, m_queue.count());
|
||||
|
||||
m_stepProgress = current;
|
||||
m_stepTotalProgress = total;
|
||||
}
|
||||
|
@ -9,13 +9,21 @@ class SequentialTask : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SequentialTask(QObject *parent = 0);
|
||||
virtual ~SequentialTask() {};
|
||||
explicit SequentialTask(QObject *parent = nullptr, const QString& task_name = "");
|
||||
virtual ~SequentialTask();
|
||||
|
||||
inline auto isMultiStep() const -> bool override { return m_queue.size() > 1; };
|
||||
auto getStepProgress() const -> qint64 override;
|
||||
auto getStepTotalProgress() const -> qint64 override;
|
||||
|
||||
inline auto getStepStatus() const -> QString override { return m_step_status; }
|
||||
|
||||
void addTask(Task::Ptr task);
|
||||
|
||||
protected:
|
||||
void executeTask();
|
||||
protected slots:
|
||||
void executeTask() override;
|
||||
public slots:
|
||||
bool abort() override;
|
||||
|
||||
private
|
||||
slots:
|
||||
@ -24,7 +32,19 @@ slots:
|
||||
void subTaskStatus(const QString &msg);
|
||||
void subTaskProgress(qint64 current, qint64 total);
|
||||
|
||||
signals:
|
||||
void stepStatus(QString status);
|
||||
|
||||
private:
|
||||
void setStepStatus(QString status) { m_step_status = status; };
|
||||
|
||||
private:
|
||||
QString m_name;
|
||||
QString m_step_status;
|
||||
|
||||
QQueue<Task::Ptr > m_queue;
|
||||
int m_currentIndex;
|
||||
|
||||
qint64 m_stepProgress = 0;
|
||||
qint64 m_stepTotalProgress = 100;
|
||||
};
|
||||
|
@ -21,29 +21,27 @@
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
|
||||
class Task : public QObject
|
||||
{
|
||||
class Task : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
public:
|
||||
using Ptr = shared_qobject_ptr<Task>;
|
||||
|
||||
enum class State
|
||||
{
|
||||
Inactive,
|
||||
Running,
|
||||
Succeeded,
|
||||
Failed,
|
||||
AbortedByUser
|
||||
};
|
||||
enum class State { Inactive, Running, Succeeded, Failed, AbortedByUser };
|
||||
|
||||
public:
|
||||
explicit Task(QObject *parent = 0);
|
||||
virtual ~Task() {};
|
||||
public:
|
||||
explicit Task(QObject* parent = 0);
|
||||
virtual ~Task() = default;
|
||||
|
||||
bool isRunning() const;
|
||||
bool isFinished() const;
|
||||
bool wasSuccessful() const;
|
||||
|
||||
/*!
|
||||
* MultiStep tasks are combinations of multiple tasks into a single logical task.
|
||||
* The main usage of this is in SequencialTask.
|
||||
*/
|
||||
virtual auto isMultiStep() const -> bool { return false; }
|
||||
|
||||
/*!
|
||||
* Returns the string that was passed to emitFailed as the error message when the task failed.
|
||||
* If the task hasn't failed, returns an empty string.
|
||||
@ -54,52 +52,45 @@ public:
|
||||
|
||||
virtual bool canAbort() const { return false; }
|
||||
|
||||
QString getStatus()
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
QString getStatus() { return m_status; }
|
||||
virtual auto getStepStatus() const -> QString { return m_status; }
|
||||
|
||||
qint64 getProgress()
|
||||
{
|
||||
return m_progress;
|
||||
}
|
||||
qint64 getProgress() { return m_progress; }
|
||||
qint64 getTotalProgress() { return m_progressTotal; }
|
||||
virtual auto getStepProgress() const -> qint64 { return 0; }
|
||||
virtual auto getStepTotalProgress() const -> qint64 { return 100; }
|
||||
|
||||
qint64 getTotalProgress()
|
||||
{
|
||||
return m_progressTotal;
|
||||
}
|
||||
protected:
|
||||
void logWarning(const QString& line);
|
||||
|
||||
protected:
|
||||
void logWarning(const QString & line);
|
||||
|
||||
private:
|
||||
private:
|
||||
QString describe();
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void started();
|
||||
void progress(qint64 current, qint64 total);
|
||||
virtual void progress(qint64 current, qint64 total);
|
||||
void finished();
|
||||
void succeeded();
|
||||
void failed(QString reason);
|
||||
void status(QString status);
|
||||
|
||||
public slots:
|
||||
public slots:
|
||||
virtual void start();
|
||||
virtual bool abort() { return false; };
|
||||
|
||||
protected:
|
||||
protected:
|
||||
virtual void executeTask() = 0;
|
||||
|
||||
protected slots:
|
||||
protected slots:
|
||||
virtual void emitSucceeded();
|
||||
virtual void emitAborted();
|
||||
virtual void emitFailed(QString reason);
|
||||
|
||||
public slots:
|
||||
void setStatus(const QString &status);
|
||||
public slots:
|
||||
void setStatus(const QString& status);
|
||||
void setProgress(qint64 current, qint64 total);
|
||||
|
||||
private:
|
||||
private:
|
||||
State m_state = State::Inactive;
|
||||
QStringList m_Warnings;
|
||||
QString m_failReason = "";
|
||||
@ -107,4 +98,3 @@ private:
|
||||
int m_progress = 0;
|
||||
int m_progressTotal = 100;
|
||||
};
|
||||
|
||||
|
68
launcher/tasks/Task_test.cpp
Normal file
68
launcher/tasks/Task_test.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
#include <QTest>
|
||||
#include "TestUtil.h"
|
||||
|
||||
#include "Task.h"
|
||||
|
||||
/* Does nothing. Only used for testing. */
|
||||
class BasicTask : public Task {
|
||||
Q_OBJECT
|
||||
|
||||
friend class TaskTest;
|
||||
|
||||
private:
|
||||
void executeTask() override {};
|
||||
};
|
||||
|
||||
/* Does nothing. Only used for testing. */
|
||||
class BasicTask_MultiStep : public Task {
|
||||
Q_OBJECT
|
||||
|
||||
friend class TaskTest;
|
||||
|
||||
private:
|
||||
auto isMultiStep() const -> bool override { return true; }
|
||||
|
||||
void executeTask() override {};
|
||||
};
|
||||
|
||||
class TaskTest : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void test_SetStatus_NoMultiStep(){
|
||||
BasicTask t;
|
||||
QString status {"test status"};
|
||||
|
||||
t.setStatus(status);
|
||||
|
||||
QCOMPARE(t.getStatus(), status);
|
||||
QCOMPARE(t.getStepStatus(), status);
|
||||
}
|
||||
|
||||
void test_SetStatus_MultiStep(){
|
||||
BasicTask_MultiStep t;
|
||||
QString status {"test status"};
|
||||
|
||||
t.setStatus(status);
|
||||
|
||||
QCOMPARE(t.getStatus(), status);
|
||||
// Even though it is multi step, it does not override the getStepStatus method,
|
||||
// so it should remain the same.
|
||||
QCOMPARE(t.getStepStatus(), status);
|
||||
}
|
||||
|
||||
void test_SetProgress(){
|
||||
BasicTask t;
|
||||
int current = 42;
|
||||
int total = 207;
|
||||
|
||||
t.setProgress(current, total);
|
||||
|
||||
QCOMPARE(t.getProgress(), current);
|
||||
QCOMPARE(t.getTotalProgress(), total);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(TaskTest)
|
||||
|
||||
#include "Task_test.moc"
|
@ -38,6 +38,7 @@
|
||||
#include <QtWidgets/QToolBar>
|
||||
#include <QtWidgets/QWidget>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QMenuBar>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QInputDialog>
|
||||
#include <QtWidgets/QLabel>
|
||||
@ -59,7 +60,6 @@
|
||||
#include <net/NetJob.h>
|
||||
#include <net/Download.h>
|
||||
#include <news/NewsChecker.h>
|
||||
#include <notifications/NotificationChecker.h>
|
||||
#include <tools/BaseProfiler.h>
|
||||
#include <updater/DownloadTask.h>
|
||||
#include <updater/UpdateChecker.h>
|
||||
@ -82,7 +82,6 @@
|
||||
#include "ui/dialogs/CopyInstanceDialog.h"
|
||||
#include "ui/dialogs/UpdateDialog.h"
|
||||
#include "ui/dialogs/EditAccountDialog.h"
|
||||
#include "ui/dialogs/NotificationDialog.h"
|
||||
#include "ui/dialogs/ExportInstanceDialog.h"
|
||||
|
||||
#include "UpdateController.h"
|
||||
@ -245,6 +244,16 @@ public:
|
||||
QHBoxLayout *horizontalLayout = nullptr;
|
||||
QStatusBar *statusBar = nullptr;
|
||||
|
||||
QMenuBar *menuBar = nullptr;
|
||||
QMenu *fileMenu;
|
||||
QMenu *viewMenu;
|
||||
QMenu *profileMenu;
|
||||
|
||||
TranslatedAction actionCloseWindow;
|
||||
|
||||
TranslatedAction actionOpenWiki;
|
||||
TranslatedAction actionNewsMenuBar;
|
||||
|
||||
TranslatedToolbar mainToolBar;
|
||||
TranslatedToolbar instanceToolBar;
|
||||
TranslatedToolbar newsToolBar;
|
||||
@ -255,12 +264,12 @@ public:
|
||||
{
|
||||
if(m_kill)
|
||||
{
|
||||
actionLaunchInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Kill"));
|
||||
actionLaunchInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Kill"));
|
||||
actionLaunchInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Kill the running instance"));
|
||||
}
|
||||
else
|
||||
{
|
||||
actionLaunchInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Launch"));
|
||||
actionLaunchInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Launch"));
|
||||
actionLaunchInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Launch the selected instance."));
|
||||
}
|
||||
actionLaunchInstance.retranslate();
|
||||
@ -271,28 +280,16 @@ public:
|
||||
updateLaunchAction();
|
||||
}
|
||||
|
||||
void createMainToolbar(QMainWindow *MainWindow)
|
||||
void createMainToolbarActions(QMainWindow *MainWindow)
|
||||
{
|
||||
mainToolBar = TranslatedToolbar(MainWindow);
|
||||
mainToolBar->setObjectName(QStringLiteral("mainToolBar"));
|
||||
mainToolBar->setMovable(false);
|
||||
mainToolBar->setAllowedAreas(Qt::TopToolBarArea);
|
||||
mainToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
mainToolBar->setFloatable(false);
|
||||
mainToolBar.setWindowTitleId(QT_TRANSLATE_NOOP("MainWindow", "Main Toolbar"));
|
||||
|
||||
actionAddInstance = TranslatedAction(MainWindow);
|
||||
actionAddInstance->setObjectName(QStringLiteral("actionAddInstance"));
|
||||
actionAddInstance->setIcon(APPLICATION->getThemedIcon("new"));
|
||||
actionAddInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Add Instance"));
|
||||
actionAddInstance->setIconVisibleInMenu(false);
|
||||
actionAddInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Add Instanc&e..."));
|
||||
actionAddInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Add a new instance."));
|
||||
actionAddInstance->setShortcut(QKeySequence::New);
|
||||
all_actions.append(&actionAddInstance);
|
||||
mainToolBar->addAction(actionAddInstance);
|
||||
|
||||
mainToolBar->addSeparator();
|
||||
|
||||
foldersMenu = new QMenu(MainWindow);
|
||||
foldersMenu->setToolTipsVisible(true);
|
||||
|
||||
actionViewInstanceFolder = TranslatedAction(MainWindow);
|
||||
actionViewInstanceFolder->setObjectName(QStringLiteral("actionViewInstanceFolder"));
|
||||
@ -300,7 +297,6 @@ public:
|
||||
actionViewInstanceFolder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "View Instance Folder"));
|
||||
actionViewInstanceFolder.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the instance folder in a file browser."));
|
||||
all_actions.append(&actionViewInstanceFolder);
|
||||
foldersMenu->addAction(actionViewInstanceFolder);
|
||||
|
||||
actionViewCentralModsFolder = TranslatedAction(MainWindow);
|
||||
actionViewCentralModsFolder->setObjectName(QStringLiteral("actionViewCentralModsFolder"));
|
||||
@ -308,10 +304,16 @@ public:
|
||||
actionViewCentralModsFolder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "View Central Mods Folder"));
|
||||
actionViewCentralModsFolder.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the central mods folder in a file browser."));
|
||||
all_actions.append(&actionViewCentralModsFolder);
|
||||
|
||||
foldersMenu = new QMenu(MainWindow);
|
||||
foldersMenu->setTitle(tr("F&olders"));
|
||||
foldersMenu->setToolTipsVisible(true);
|
||||
|
||||
foldersMenu->addAction(actionViewInstanceFolder);
|
||||
foldersMenu->addAction(actionViewCentralModsFolder);
|
||||
|
||||
foldersMenuButton = TranslatedToolButton(MainWindow);
|
||||
foldersMenuButton.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Folders"));
|
||||
foldersMenuButton.setTextId(QT_TRANSLATE_NOOP("MainWindow", "F&olders"));
|
||||
foldersMenuButton.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open one of the folders shared between instances."));
|
||||
foldersMenuButton->setMenu(foldersMenu);
|
||||
foldersMenuButton->setPopupMode(QToolButton::InstantPopup);
|
||||
@ -319,69 +321,130 @@ public:
|
||||
foldersMenuButton->setIcon(APPLICATION->getThemedIcon("viewfolder"));
|
||||
foldersMenuButton->setFocusPolicy(Qt::NoFocus);
|
||||
all_toolbuttons.append(&foldersMenuButton);
|
||||
QWidgetAction* foldersButtonAction = new QWidgetAction(MainWindow);
|
||||
foldersButtonAction->setDefaultWidget(foldersMenuButton);
|
||||
mainToolBar->addAction(foldersButtonAction);
|
||||
|
||||
actionSettings = TranslatedAction(MainWindow);
|
||||
actionSettings->setObjectName(QStringLiteral("actionSettings"));
|
||||
actionSettings->setIcon(APPLICATION->getThemedIcon("settings"));
|
||||
actionSettings->setMenuRole(QAction::PreferencesRole);
|
||||
actionSettings.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Settings"));
|
||||
actionSettings.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Settings..."));
|
||||
actionSettings.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Change settings."));
|
||||
actionSettings->setShortcut(QKeySequence::Preferences);
|
||||
all_actions.append(&actionSettings);
|
||||
mainToolBar->addAction(actionSettings);
|
||||
|
||||
helpMenu = new QMenu(MainWindow);
|
||||
helpMenu->setToolTipsVisible(true);
|
||||
|
||||
if (!BuildConfig.BUG_TRACKER_URL.isEmpty()) {
|
||||
actionReportBug = TranslatedAction(MainWindow);
|
||||
actionReportBug->setObjectName(QStringLiteral("actionReportBug"));
|
||||
actionReportBug->setIcon(APPLICATION->getThemedIcon("bug"));
|
||||
actionReportBug.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Report a Bug"));
|
||||
actionReportBug.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Report a &Bug..."));
|
||||
actionReportBug.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the bug tracker to report a bug with %1."));
|
||||
all_actions.append(&actionReportBug);
|
||||
helpMenu->addAction(actionReportBug);
|
||||
}
|
||||
|
||||
|
||||
if(!BuildConfig.MATRIX_URL.isEmpty()) {
|
||||
actionMATRIX = TranslatedAction(MainWindow);
|
||||
actionMATRIX->setObjectName(QStringLiteral("actionMATRIX"));
|
||||
actionMATRIX->setIcon(APPLICATION->getThemedIcon("matrix"));
|
||||
actionMATRIX.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Matrix space"));
|
||||
actionMATRIX.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Matrix Space"));
|
||||
actionMATRIX.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open %1 Matrix space"));
|
||||
all_actions.append(&actionMATRIX);
|
||||
helpMenu->addAction(actionMATRIX);
|
||||
}
|
||||
|
||||
if (!BuildConfig.DISCORD_URL.isEmpty()) {
|
||||
actionDISCORD = TranslatedAction(MainWindow);
|
||||
actionDISCORD->setObjectName(QStringLiteral("actionDISCORD"));
|
||||
actionDISCORD->setIcon(APPLICATION->getThemedIcon("discord"));
|
||||
actionDISCORD.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Discord guild"));
|
||||
actionDISCORD.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Discord Guild"));
|
||||
actionDISCORD.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open %1 Discord guild."));
|
||||
all_actions.append(&actionDISCORD);
|
||||
helpMenu->addAction(actionDISCORD);
|
||||
}
|
||||
|
||||
if (!BuildConfig.SUBREDDIT_URL.isEmpty()) {
|
||||
actionREDDIT = TranslatedAction(MainWindow);
|
||||
actionREDDIT->setObjectName(QStringLiteral("actionREDDIT"));
|
||||
actionREDDIT->setIcon(APPLICATION->getThemedIcon("reddit-alien"));
|
||||
actionREDDIT.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Subreddit"));
|
||||
actionREDDIT.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Sub&reddit"));
|
||||
actionREDDIT.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open %1 subreddit."));
|
||||
all_actions.append(&actionREDDIT);
|
||||
helpMenu->addAction(actionREDDIT);
|
||||
}
|
||||
|
||||
actionAbout = TranslatedAction(MainWindow);
|
||||
actionAbout->setObjectName(QStringLiteral("actionAbout"));
|
||||
actionAbout->setIcon(APPLICATION->getThemedIcon("about"));
|
||||
actionAbout->setMenuRole(QAction::AboutRole);
|
||||
actionAbout.setTextId(QT_TRANSLATE_NOOP("MainWindow", "About %1"));
|
||||
actionAbout.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&About %1"));
|
||||
actionAbout.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "View information about %1."));
|
||||
all_actions.append(&actionAbout);
|
||||
|
||||
if(BuildConfig.UPDATER_ENABLED)
|
||||
{
|
||||
actionCheckUpdate = TranslatedAction(MainWindow);
|
||||
actionCheckUpdate->setObjectName(QStringLiteral("actionCheckUpdate"));
|
||||
actionCheckUpdate->setIcon(APPLICATION->getThemedIcon("checkupdate"));
|
||||
actionCheckUpdate.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Update..."));
|
||||
actionCheckUpdate.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Check for new updates for %1."));
|
||||
actionCheckUpdate->setMenuRole(QAction::ApplicationSpecificRole);
|
||||
all_actions.append(&actionCheckUpdate);
|
||||
}
|
||||
|
||||
actionCAT = TranslatedAction(MainWindow);
|
||||
actionCAT->setObjectName(QStringLiteral("actionCAT"));
|
||||
actionCAT->setCheckable(true);
|
||||
actionCAT->setIcon(APPLICATION->getThemedIcon("cat"));
|
||||
actionCAT.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Meow"));
|
||||
actionCAT.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "It's a fluffy kitty :3"));
|
||||
actionCAT->setPriority(QAction::LowPriority);
|
||||
all_actions.append(&actionCAT);
|
||||
|
||||
// profile menu and its actions
|
||||
actionManageAccounts = TranslatedAction(MainWindow);
|
||||
actionManageAccounts->setObjectName(QStringLiteral("actionManageAccounts"));
|
||||
actionManageAccounts.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Manage Accounts..."));
|
||||
// FIXME: no tooltip!
|
||||
actionManageAccounts->setCheckable(false);
|
||||
actionManageAccounts->setIcon(APPLICATION->getThemedIcon("accounts"));
|
||||
all_actions.append(&actionManageAccounts);
|
||||
}
|
||||
|
||||
void createMainToolbar(QMainWindow *MainWindow)
|
||||
{
|
||||
mainToolBar = TranslatedToolbar(MainWindow);
|
||||
mainToolBar->setVisible(menuBar->isNativeMenuBar() || !APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool());
|
||||
mainToolBar->setObjectName(QStringLiteral("mainToolBar"));
|
||||
mainToolBar->setMovable(true);
|
||||
mainToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
|
||||
mainToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
mainToolBar->setFloatable(false);
|
||||
mainToolBar.setWindowTitleId(QT_TRANSLATE_NOOP("MainWindow", "Main Toolbar"));
|
||||
|
||||
mainToolBar->addAction(actionAddInstance);
|
||||
|
||||
mainToolBar->addSeparator();
|
||||
|
||||
QWidgetAction* foldersButtonAction = new QWidgetAction(MainWindow);
|
||||
foldersButtonAction->setDefaultWidget(foldersMenuButton);
|
||||
mainToolBar->addAction(foldersButtonAction);
|
||||
|
||||
mainToolBar->addAction(actionSettings);
|
||||
|
||||
helpMenu = new QMenu(MainWindow);
|
||||
helpMenu->setToolTipsVisible(true);
|
||||
|
||||
if (!BuildConfig.BUG_TRACKER_URL.isEmpty()) {
|
||||
helpMenu->addAction(actionReportBug);
|
||||
}
|
||||
|
||||
if(!BuildConfig.MATRIX_URL.isEmpty()) {
|
||||
helpMenu->addAction(actionMATRIX);
|
||||
}
|
||||
|
||||
if (!BuildConfig.DISCORD_URL.isEmpty()) {
|
||||
helpMenu->addAction(actionDISCORD);
|
||||
}
|
||||
|
||||
if (!BuildConfig.SUBREDDIT_URL.isEmpty()) {
|
||||
helpMenu->addAction(actionREDDIT);
|
||||
}
|
||||
|
||||
helpMenu->addAction(actionAbout);
|
||||
|
||||
helpMenuButton = TranslatedToolButton(MainWindow);
|
||||
@ -399,40 +462,118 @@ public:
|
||||
|
||||
if(BuildConfig.UPDATER_ENABLED)
|
||||
{
|
||||
actionCheckUpdate = TranslatedAction(MainWindow);
|
||||
actionCheckUpdate->setObjectName(QStringLiteral("actionCheckUpdate"));
|
||||
actionCheckUpdate->setIcon(APPLICATION->getThemedIcon("checkupdate"));
|
||||
actionCheckUpdate.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Update"));
|
||||
actionCheckUpdate.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Check for new updates for %1."));
|
||||
all_actions.append(&actionCheckUpdate);
|
||||
mainToolBar->addAction(actionCheckUpdate);
|
||||
}
|
||||
|
||||
mainToolBar->addSeparator();
|
||||
|
||||
actionCAT = TranslatedAction(MainWindow);
|
||||
actionCAT->setObjectName(QStringLiteral("actionCAT"));
|
||||
actionCAT->setCheckable(true);
|
||||
actionCAT->setIcon(APPLICATION->getThemedIcon("cat"));
|
||||
actionCAT.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Meow"));
|
||||
actionCAT.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "It's a fluffy kitty :3"));
|
||||
actionCAT->setPriority(QAction::LowPriority);
|
||||
all_actions.append(&actionCAT);
|
||||
mainToolBar->addAction(actionCAT);
|
||||
|
||||
// profile menu and its actions
|
||||
actionManageAccounts = TranslatedAction(MainWindow);
|
||||
actionManageAccounts->setObjectName(QStringLiteral("actionManageAccounts"));
|
||||
actionManageAccounts.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Manage Accounts"));
|
||||
// FIXME: no tooltip!
|
||||
actionManageAccounts->setCheckable(false);
|
||||
actionManageAccounts->setIcon(APPLICATION->getThemedIcon("accounts"));
|
||||
all_actions.append(&actionManageAccounts);
|
||||
|
||||
all_toolbars.append(&mainToolBar);
|
||||
MainWindow->addToolBar(Qt::TopToolBarArea, mainToolBar);
|
||||
}
|
||||
|
||||
void createMenuBar(QMainWindow *MainWindow)
|
||||
{
|
||||
menuBar = new QMenuBar(MainWindow);
|
||||
menuBar->setVisible(APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool());
|
||||
|
||||
fileMenu = menuBar->addMenu(tr("&File"));
|
||||
fileMenu->addAction(actionAddInstance);
|
||||
fileMenu->addAction(actionLaunchInstance);
|
||||
fileMenu->addAction(actionLaunchInstanceOffline);
|
||||
fileMenu->addAction(actionCloseWindow);
|
||||
fileMenu->addSeparator();
|
||||
fileMenu->addAction(actionEditInstance);
|
||||
fileMenu->addAction(actionEditInstNotes);
|
||||
fileMenu->addAction(actionMods);
|
||||
fileMenu->addAction(actionWorlds);
|
||||
fileMenu->addAction(actionScreenshots);
|
||||
fileMenu->addAction(actionChangeInstGroup);
|
||||
fileMenu->addSeparator();
|
||||
fileMenu->addAction(actionViewSelectedMCFolder);
|
||||
fileMenu->addAction(actionConfig_Folder);
|
||||
fileMenu->addAction(actionViewSelectedInstFolder);
|
||||
fileMenu->addSeparator();
|
||||
fileMenu->addAction(actionExportInstance);
|
||||
fileMenu->addAction(actionDeleteInstance);
|
||||
fileMenu->addAction(actionCopyInstance);
|
||||
fileMenu->addSeparator();
|
||||
fileMenu->addAction(actionSettings);
|
||||
|
||||
viewMenu = menuBar->addMenu(tr("&View"));
|
||||
viewMenu->addAction(actionCAT);
|
||||
viewMenu->addSeparator();
|
||||
|
||||
menuBar->addMenu(foldersMenu);
|
||||
|
||||
profileMenu = menuBar->addMenu(tr("&Profiles"));
|
||||
profileMenu->addAction(actionManageAccounts);
|
||||
|
||||
helpMenu = menuBar->addMenu(tr("&Help"));
|
||||
helpMenu->addAction(actionAbout);
|
||||
helpMenu->addAction(actionOpenWiki);
|
||||
helpMenu->addAction(actionNewsMenuBar);
|
||||
helpMenu->addSeparator();
|
||||
if (!BuildConfig.BUG_TRACKER_URL.isEmpty())
|
||||
helpMenu->addAction(actionReportBug);
|
||||
if (!BuildConfig.MATRIX_URL.isEmpty())
|
||||
helpMenu->addAction(actionMATRIX);
|
||||
if (!BuildConfig.DISCORD_URL.isEmpty())
|
||||
helpMenu->addAction(actionDISCORD);
|
||||
if (!BuildConfig.SUBREDDIT_URL.isEmpty())
|
||||
helpMenu->addAction(actionREDDIT);
|
||||
helpMenu->addSeparator();
|
||||
if(BuildConfig.UPDATER_ENABLED)
|
||||
helpMenu->addAction(actionCheckUpdate);
|
||||
|
||||
MainWindow->setMenuBar(menuBar);
|
||||
}
|
||||
|
||||
void createMenuActions(MainWindow *MainWindow)
|
||||
{
|
||||
actionCloseWindow = TranslatedAction(MainWindow);
|
||||
actionCloseWindow->setObjectName(QStringLiteral("actionCloseWindow"));
|
||||
actionCloseWindow.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Close &Window"));
|
||||
actionCloseWindow.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Close the current window"));
|
||||
actionCloseWindow->setShortcut(QKeySequence::Close);
|
||||
connect(actionCloseWindow, &QAction::triggered, APPLICATION, &Application::closeCurrentWindow);
|
||||
all_actions.append(&actionCloseWindow);
|
||||
|
||||
actionOpenWiki = TranslatedAction(MainWindow);
|
||||
actionOpenWiki->setObjectName(QStringLiteral("actionOpenWiki"));
|
||||
actionOpenWiki.setTextId(QT_TRANSLATE_NOOP("MainWindow", "%1 He&lp"));
|
||||
actionOpenWiki.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the %1 wiki"));
|
||||
connect(actionOpenWiki, &QAction::triggered, MainWindow, &MainWindow::on_actionOpenWiki_triggered);
|
||||
all_actions.append(&actionOpenWiki);
|
||||
|
||||
actionNewsMenuBar = TranslatedAction(MainWindow);
|
||||
actionNewsMenuBar->setObjectName(QStringLiteral("actionNewsMenuBar"));
|
||||
actionNewsMenuBar.setTextId(QT_TRANSLATE_NOOP("MainWindow", "%1 &News"));
|
||||
actionNewsMenuBar.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the %1 wiki"));
|
||||
connect(actionNewsMenuBar, &QAction::triggered, MainWindow, &MainWindow::on_actionMoreNews_triggered);
|
||||
all_actions.append(&actionNewsMenuBar);
|
||||
}
|
||||
|
||||
// "Instance actions" are actions that require an instance to be selected (i.e. "new instance" is not here)
|
||||
void setInstanceActionsEnabled(bool enabled)
|
||||
{
|
||||
actionLaunchInstance->setEnabled(enabled);
|
||||
actionLaunchInstanceOffline->setEnabled(enabled);
|
||||
actionEditInstance->setEnabled(enabled);
|
||||
actionEditInstNotes->setEnabled(enabled);
|
||||
actionMods->setEnabled(enabled);
|
||||
actionWorlds->setEnabled(enabled);
|
||||
actionScreenshots->setEnabled(enabled);
|
||||
actionChangeInstGroup->setEnabled(enabled);
|
||||
actionViewSelectedMCFolder->setEnabled(enabled);
|
||||
actionConfig_Folder->setEnabled(enabled);
|
||||
actionViewSelectedInstFolder->setEnabled(enabled);
|
||||
actionExportInstance->setEnabled(enabled);
|
||||
actionDeleteInstance->setEnabled(enabled);
|
||||
actionCopyInstance->setEnabled(enabled);
|
||||
}
|
||||
|
||||
void createStatusBar(QMainWindow *MainWindow)
|
||||
{
|
||||
statusBar = new QStatusBar(MainWindow);
|
||||
@ -444,8 +585,8 @@ public:
|
||||
{
|
||||
newsToolBar = TranslatedToolbar(MainWindow);
|
||||
newsToolBar->setObjectName(QStringLiteral("newsToolBar"));
|
||||
newsToolBar->setMovable(false);
|
||||
newsToolBar->setAllowedAreas(Qt::BottomToolBarArea);
|
||||
newsToolBar->setMovable(true);
|
||||
newsToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
|
||||
newsToolBar->setIconSize(QSize(16, 16));
|
||||
newsToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
newsToolBar->setFloatable(false);
|
||||
@ -463,17 +604,8 @@ public:
|
||||
MainWindow->addToolBar(Qt::BottomToolBarArea, newsToolBar);
|
||||
}
|
||||
|
||||
void createInstanceToolbar(QMainWindow *MainWindow)
|
||||
void createInstanceActions(QMainWindow *MainWindow)
|
||||
{
|
||||
instanceToolBar = TranslatedToolbar(MainWindow);
|
||||
instanceToolBar->setObjectName(QStringLiteral("instanceToolBar"));
|
||||
// disabled until we have an instance selected
|
||||
instanceToolBar->setEnabled(false);
|
||||
instanceToolBar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);
|
||||
instanceToolBar->setToolButtonStyle(Qt::ToolButtonTextOnly);
|
||||
instanceToolBar->setFloatable(false);
|
||||
instanceToolBar->setWindowTitle(QT_TRANSLATE_NOOP("MainWindow", "Instance Toolbar"));
|
||||
|
||||
// NOTE: not added to toolbar, but used for instance context menu (right click)
|
||||
actionChangeInstIcon = TranslatedAction(MainWindow);
|
||||
actionChangeInstIcon->setObjectName(QStringLiteral("actionChangeInstIcon"));
|
||||
@ -488,7 +620,6 @@ public:
|
||||
changeIconButton->setIcon(APPLICATION->getThemedIcon("news"));
|
||||
changeIconButton->setToolTip(actionChangeInstIcon->toolTip());
|
||||
changeIconButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
instanceToolBar->addWidget(changeIconButton);
|
||||
|
||||
// NOTE: not added to toolbar, but used for instance context menu (right click)
|
||||
actionRenameInstance = TranslatedAction(MainWindow);
|
||||
@ -502,74 +633,63 @@ public:
|
||||
renameButton->setObjectName(QStringLiteral("renameButton"));
|
||||
renameButton->setToolTip(actionRenameInstance->toolTip());
|
||||
renameButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
instanceToolBar->addWidget(renameButton);
|
||||
|
||||
instanceToolBar->addSeparator();
|
||||
|
||||
actionLaunchInstance = TranslatedAction(MainWindow);
|
||||
actionLaunchInstance->setObjectName(QStringLiteral("actionLaunchInstance"));
|
||||
actionLaunchInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Launch"));
|
||||
actionLaunchInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Launch the selected instance."));
|
||||
all_actions.append(&actionLaunchInstance);
|
||||
instanceToolBar->addAction(actionLaunchInstance);
|
||||
|
||||
actionLaunchInstanceOffline = TranslatedAction(MainWindow);
|
||||
actionLaunchInstanceOffline->setObjectName(QStringLiteral("actionLaunchInstanceOffline"));
|
||||
actionLaunchInstanceOffline.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Launch Offline"));
|
||||
actionLaunchInstanceOffline.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Launch &Offline"));
|
||||
actionLaunchInstanceOffline.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Launch the selected instance in offline mode."));
|
||||
all_actions.append(&actionLaunchInstanceOffline);
|
||||
instanceToolBar->addAction(actionLaunchInstanceOffline);
|
||||
|
||||
instanceToolBar->addSeparator();
|
||||
|
||||
actionEditInstance = TranslatedAction(MainWindow);
|
||||
actionEditInstance->setObjectName(QStringLiteral("actionEditInstance"));
|
||||
actionEditInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Edit Instance"));
|
||||
actionEditInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Edit Inst&ance..."));
|
||||
actionEditInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Change the instance settings, mods and versions."));
|
||||
actionEditInstance->setShortcut(QKeySequence(tr("Ctrl+I")));
|
||||
all_actions.append(&actionEditInstance);
|
||||
instanceToolBar->addAction(actionEditInstance);
|
||||
|
||||
actionEditInstNotes = TranslatedAction(MainWindow);
|
||||
actionEditInstNotes->setObjectName(QStringLiteral("actionEditInstNotes"));
|
||||
actionEditInstNotes.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Edit Notes"));
|
||||
actionEditInstNotes.setTextId(QT_TRANSLATE_NOOP("MainWindow", "E&dit Notes..."));
|
||||
actionEditInstNotes.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Edit the notes for the selected instance."));
|
||||
all_actions.append(&actionEditInstNotes);
|
||||
instanceToolBar->addAction(actionEditInstNotes);
|
||||
|
||||
actionMods = TranslatedAction(MainWindow);
|
||||
actionMods->setObjectName(QStringLiteral("actionMods"));
|
||||
actionMods.setTextId(QT_TRANSLATE_NOOP("MainWindow", "View Mods"));
|
||||
actionMods.setTextId(QT_TRANSLATE_NOOP("MainWindow", "View &Mods"));
|
||||
actionMods.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "View the mods of this instance."));
|
||||
all_actions.append(&actionMods);
|
||||
instanceToolBar->addAction(actionMods);
|
||||
|
||||
actionWorlds = TranslatedAction(MainWindow);
|
||||
actionWorlds->setObjectName(QStringLiteral("actionWorlds"));
|
||||
actionWorlds.setTextId(QT_TRANSLATE_NOOP("MainWindow", "View Worlds"));
|
||||
actionWorlds.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&View Worlds"));
|
||||
actionWorlds.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "View the worlds of this instance."));
|
||||
all_actions.append(&actionWorlds);
|
||||
instanceToolBar->addAction(actionWorlds);
|
||||
|
||||
actionScreenshots = TranslatedAction(MainWindow);
|
||||
actionScreenshots->setObjectName(QStringLiteral("actionScreenshots"));
|
||||
actionScreenshots.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Manage Screenshots"));
|
||||
actionScreenshots.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Manage &Screenshots"));
|
||||
actionScreenshots.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "View and upload screenshots for this instance."));
|
||||
all_actions.append(&actionScreenshots);
|
||||
instanceToolBar->addAction(actionScreenshots);
|
||||
|
||||
actionChangeInstGroup = TranslatedAction(MainWindow);
|
||||
actionChangeInstGroup->setObjectName(QStringLiteral("actionChangeInstGroup"));
|
||||
actionChangeInstGroup.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Change Group"));
|
||||
actionChangeInstGroup.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Change Group..."));
|
||||
actionChangeInstGroup.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Change the selected instance's group."));
|
||||
actionChangeInstGroup->setShortcut(QKeySequence(tr("Ctrl+G")));
|
||||
all_actions.append(&actionChangeInstGroup);
|
||||
instanceToolBar->addAction(actionChangeInstGroup);
|
||||
|
||||
instanceToolBar->addSeparator();
|
||||
|
||||
actionViewSelectedMCFolder = TranslatedAction(MainWindow);
|
||||
actionViewSelectedMCFolder->setObjectName(QStringLiteral("actionViewSelectedMCFolder"));
|
||||
actionViewSelectedMCFolder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Minecraft Folder"));
|
||||
actionViewSelectedMCFolder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Minec&raft Folder"));
|
||||
actionViewSelectedMCFolder.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the selected instance's Minecraft folder in a file browser."));
|
||||
actionViewSelectedMCFolder->setShortcut(QKeySequence(tr("Ctrl+M")));
|
||||
all_actions.append(&actionViewSelectedMCFolder);
|
||||
instanceToolBar->addAction(actionViewSelectedMCFolder);
|
||||
|
||||
/*
|
||||
actionViewSelectedModsFolder = TranslatedAction(MainWindow);
|
||||
@ -577,52 +697,97 @@ public:
|
||||
actionViewSelectedModsFolder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Mods Folder"));
|
||||
actionViewSelectedModsFolder.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the selected instance's mods folder in a file browser."));
|
||||
all_actions.append(&actionViewSelectedModsFolder);
|
||||
instanceToolBar->addAction(actionViewSelectedModsFolder);
|
||||
*/
|
||||
|
||||
actionConfig_Folder = TranslatedAction(MainWindow);
|
||||
actionConfig_Folder->setObjectName(QStringLiteral("actionConfig_Folder"));
|
||||
actionConfig_Folder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Config Folder"));
|
||||
actionConfig_Folder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Confi&g Folder"));
|
||||
actionConfig_Folder.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the instance's config folder."));
|
||||
// Qt on macOS is "smart" and will eat up this action when added to the menu bar because it starts with the word "config"...
|
||||
// Docs: https://doc.qt.io/qt-5/qmenubar.html#qmenubar-as-a-global-menu-bar
|
||||
actionConfig_Folder->setMenuRole(QAction::NoRole);
|
||||
all_actions.append(&actionConfig_Folder);
|
||||
instanceToolBar->addAction(actionConfig_Folder);
|
||||
|
||||
actionViewSelectedInstFolder = TranslatedAction(MainWindow);
|
||||
actionViewSelectedInstFolder->setObjectName(QStringLiteral("actionViewSelectedInstFolder"));
|
||||
actionViewSelectedInstFolder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Instance Folder"));
|
||||
actionViewSelectedInstFolder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Instance Folder"));
|
||||
actionViewSelectedInstFolder.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the selected instance's root folder in a file browser."));
|
||||
all_actions.append(&actionViewSelectedInstFolder);
|
||||
instanceToolBar->addAction(actionViewSelectedInstFolder);
|
||||
|
||||
instanceToolBar->addSeparator();
|
||||
|
||||
actionExportInstance = TranslatedAction(MainWindow);
|
||||
actionExportInstance->setObjectName(QStringLiteral("actionExportInstance"));
|
||||
actionExportInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Export Instance"));
|
||||
actionExportInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "E&xport Instance..."));
|
||||
actionExportInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Export the selected instance as a zip file."));
|
||||
actionExportInstance->setShortcut(QKeySequence(tr("Ctrl+E")));
|
||||
all_actions.append(&actionExportInstance);
|
||||
instanceToolBar->addAction(actionExportInstance);
|
||||
|
||||
actionDeleteInstance = TranslatedAction(MainWindow);
|
||||
actionDeleteInstance->setObjectName(QStringLiteral("actionDeleteInstance"));
|
||||
actionDeleteInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Delete Instance"));
|
||||
actionDeleteInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Dele&te Instance..."));
|
||||
actionDeleteInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Delete the selected instance."));
|
||||
actionDeleteInstance->setShortcuts({QKeySequence(tr("Backspace")), QKeySequence::Delete});
|
||||
all_actions.append(&actionDeleteInstance);
|
||||
instanceToolBar->addAction(actionDeleteInstance);
|
||||
|
||||
actionCopyInstance = TranslatedAction(MainWindow);
|
||||
actionCopyInstance->setObjectName(QStringLiteral("actionCopyInstance"));
|
||||
actionCopyInstance->setIcon(APPLICATION->getThemedIcon("copy"));
|
||||
actionCopyInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Copy Instance"));
|
||||
actionCopyInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Cop&y Instance..."));
|
||||
actionCopyInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Copy the selected instance."));
|
||||
actionCopyInstance->setShortcut(QKeySequence(tr("Ctrl+D")));
|
||||
all_actions.append(&actionCopyInstance);
|
||||
|
||||
setInstanceActionsEnabled(false);
|
||||
}
|
||||
|
||||
void createInstanceToolbar(QMainWindow *MainWindow)
|
||||
{
|
||||
instanceToolBar = TranslatedToolbar(MainWindow);
|
||||
instanceToolBar->setObjectName(QStringLiteral("instanceToolBar"));
|
||||
// disabled until we have an instance selected
|
||||
instanceToolBar->setEnabled(false);
|
||||
instanceToolBar->setMovable(true);
|
||||
instanceToolBar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);
|
||||
instanceToolBar->setToolButtonStyle(Qt::ToolButtonTextOnly);
|
||||
instanceToolBar->setFloatable(false);
|
||||
instanceToolBar->setWindowTitle(QT_TRANSLATE_NOOP("MainWindow", "Instance Toolbar"));
|
||||
|
||||
instanceToolBar->addWidget(changeIconButton);
|
||||
instanceToolBar->addWidget(renameButton);
|
||||
|
||||
instanceToolBar->addSeparator();
|
||||
|
||||
instanceToolBar->addAction(actionLaunchInstance);
|
||||
instanceToolBar->addAction(actionLaunchInstanceOffline);
|
||||
|
||||
instanceToolBar->addSeparator();
|
||||
|
||||
instanceToolBar->addAction(actionEditInstance);
|
||||
instanceToolBar->addAction(actionEditInstNotes);
|
||||
instanceToolBar->addAction(actionMods);
|
||||
instanceToolBar->addAction(actionWorlds);
|
||||
instanceToolBar->addAction(actionScreenshots);
|
||||
instanceToolBar->addAction(actionChangeInstGroup);
|
||||
|
||||
instanceToolBar->addSeparator();
|
||||
|
||||
instanceToolBar->addAction(actionViewSelectedMCFolder);
|
||||
/*
|
||||
instanceToolBar->addAction(actionViewSelectedModsFolder);
|
||||
*/
|
||||
instanceToolBar->addAction(actionConfig_Folder);
|
||||
instanceToolBar->addAction(actionViewSelectedInstFolder);
|
||||
|
||||
instanceToolBar->addSeparator();
|
||||
|
||||
instanceToolBar->addAction(actionExportInstance);
|
||||
instanceToolBar->addAction(actionDeleteInstance);
|
||||
instanceToolBar->addAction(actionCopyInstance);
|
||||
|
||||
all_toolbars.append(&instanceToolBar);
|
||||
MainWindow->addToolBar(Qt::RightToolBarArea, instanceToolBar);
|
||||
}
|
||||
|
||||
void setupUi(QMainWindow *MainWindow)
|
||||
void setupUi(MainWindow *MainWindow)
|
||||
{
|
||||
if (MainWindow->objectName().isEmpty())
|
||||
{
|
||||
@ -635,6 +800,12 @@ public:
|
||||
MainWindow->setAccessibleName(BuildConfig.LAUNCHER_NAME);
|
||||
#endif
|
||||
|
||||
createMainToolbarActions(MainWindow);
|
||||
createMenuActions(MainWindow);
|
||||
createInstanceActions(MainWindow);
|
||||
|
||||
createMenuBar(MainWindow);
|
||||
|
||||
createMainToolbar(MainWindow);
|
||||
|
||||
centralWidget = new QWidget(MainWindow);
|
||||
@ -650,6 +821,8 @@ public:
|
||||
createNewsToolbar(MainWindow);
|
||||
createInstanceToolbar(MainWindow);
|
||||
|
||||
MainWindow->updateToolsMenu();
|
||||
|
||||
retranslateUi(MainWindow);
|
||||
|
||||
QMetaObject::connectSlotsByName(MainWindow);
|
||||
@ -846,17 +1019,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new MainWindow
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto checker = new NotificationChecker();
|
||||
checker->setNotificationsUrl(QUrl(BuildConfig.NOTIFICATION_URL));
|
||||
checker->setApplicationChannel(BuildConfig.VERSION_CHANNEL);
|
||||
checker->setApplicationPlatform(BuildConfig.BUILD_PLATFORM);
|
||||
checker->setApplicationFullVersion(BuildConfig.FULL_VERSION_STR);
|
||||
m_notificationChecker.reset(checker);
|
||||
connect(m_notificationChecker.get(), &NotificationChecker::notificationCheckFinished, this, &MainWindow::notificationsChanged);
|
||||
checker->checkForNotifications();
|
||||
}
|
||||
|
||||
setSelectedInstanceById(APPLICATION->settings()->get("SelectedInstance").toString());
|
||||
|
||||
// removing this looks stupid
|
||||
@ -865,6 +1027,18 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new MainWindow
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
// macOS always has a native menu bar, so these fixes are not applicable
|
||||
// Other systems may or may not have a native menu bar (most do not - it seems like only Ubuntu Unity does)
|
||||
#ifndef Q_OS_MAC
|
||||
void MainWindow::keyReleaseEvent(QKeyEvent *event)
|
||||
{
|
||||
if(event->key()==Qt::Key_Alt && !APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool())
|
||||
ui->menuBar->setVisible(!ui->menuBar->isVisible());
|
||||
else
|
||||
QMainWindow::keyReleaseEvent(event);
|
||||
}
|
||||
#endif
|
||||
|
||||
void MainWindow::retranslateUi()
|
||||
{
|
||||
auto accounts = APPLICATION->accounts();
|
||||
@ -966,12 +1140,18 @@ void MainWindow::showInstanceContextMenu(const QPoint &pos)
|
||||
myMenu.exec(view->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void MainWindow::updateMainToolBar()
|
||||
{
|
||||
ui->menuBar->setVisible(APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool());
|
||||
ui->mainToolBar->setVisible(ui->menuBar->isNativeMenuBar() || !APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool());
|
||||
}
|
||||
|
||||
void MainWindow::updateToolsMenu()
|
||||
{
|
||||
QToolButton *launchButton = dynamic_cast<QToolButton*>(ui->instanceToolBar->widgetForAction(ui->actionLaunchInstance));
|
||||
QToolButton *launchOfflineButton = dynamic_cast<QToolButton*>(ui->instanceToolBar->widgetForAction(ui->actionLaunchInstanceOffline));
|
||||
|
||||
if(!m_selectedInstance || m_selectedInstance->isRunning())
|
||||
if(m_selectedInstance && m_selectedInstance->isRunning())
|
||||
{
|
||||
ui->actionLaunchInstance->setMenu(nullptr);
|
||||
ui->actionLaunchInstanceOffline->setMenu(nullptr);
|
||||
@ -1001,15 +1181,23 @@ void MainWindow::updateToolsMenu()
|
||||
}
|
||||
|
||||
QAction *normalLaunch = launchMenu->addAction(tr("Launch"));
|
||||
normalLaunch->setShortcut(QKeySequence::Open);
|
||||
QAction *normalLaunchOffline = launchOfflineMenu->addAction(tr("Launch Offline"));
|
||||
connect(normalLaunch, &QAction::triggered, [this]()
|
||||
{
|
||||
APPLICATION->launch(m_selectedInstance, true);
|
||||
});
|
||||
connect(normalLaunchOffline, &QAction::triggered, [this]()
|
||||
{
|
||||
APPLICATION->launch(m_selectedInstance, false);
|
||||
});
|
||||
normalLaunchOffline->setShortcut(QKeySequence(tr("Ctrl+Shift+O")));
|
||||
if (m_selectedInstance)
|
||||
{
|
||||
connect(normalLaunch, &QAction::triggered, [this]() {
|
||||
APPLICATION->launch(m_selectedInstance, true);
|
||||
});
|
||||
connect(normalLaunchOffline, &QAction::triggered, [this]() {
|
||||
APPLICATION->launch(m_selectedInstance, false);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
normalLaunch->setDisabled(true);
|
||||
normalLaunchOffline->setDisabled(true);
|
||||
}
|
||||
QString profilersTitle = tr("Profilers");
|
||||
launchMenu->addSeparator()->setText(profilersTitle);
|
||||
launchOfflineMenu->addSeparator()->setText(profilersTitle);
|
||||
@ -1026,7 +1214,7 @@ void MainWindow::updateToolsMenu()
|
||||
profilerAction->setToolTip(profilerToolTip);
|
||||
profilerOfflineAction->setToolTip(profilerToolTip);
|
||||
}
|
||||
else
|
||||
else if (m_selectedInstance)
|
||||
{
|
||||
connect(profilerAction, &QAction::triggered, [this, profiler]()
|
||||
{
|
||||
@ -1037,6 +1225,11 @@ void MainWindow::updateToolsMenu()
|
||||
APPLICATION->launch(m_selectedInstance, false, profiler.get());
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
profilerAction->setDisabled(true);
|
||||
profilerOfflineAction->setDisabled(true);
|
||||
}
|
||||
}
|
||||
ui->actionLaunchInstance->setMenu(launchMenu);
|
||||
ui->actionLaunchInstanceOffline->setMenu(launchOfflineMenu);
|
||||
@ -1045,6 +1238,7 @@ void MainWindow::updateToolsMenu()
|
||||
void MainWindow::repopulateAccountsMenu()
|
||||
{
|
||||
accountMenu->clear();
|
||||
ui->profileMenu->clear();
|
||||
|
||||
auto accounts = APPLICATION->accounts();
|
||||
MinecraftAccountPtr defaultAccount = accounts->defaultAccount();
|
||||
@ -1065,6 +1259,7 @@ void MainWindow::repopulateAccountsMenu()
|
||||
QAction *action = new QAction(tr("No accounts added!"), this);
|
||||
action->setEnabled(false);
|
||||
accountMenu->addAction(action);
|
||||
ui->profileMenu->addAction(action);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1088,26 +1283,39 @@ void MainWindow::repopulateAccountsMenu()
|
||||
else {
|
||||
action->setIcon(APPLICATION->getThemedIcon("noaccount"));
|
||||
}
|
||||
|
||||
const int highestNumberKey = 9;
|
||||
if(i<highestNumberKey)
|
||||
{
|
||||
action->setShortcut(QKeySequence(tr("Ctrl+%1").arg(i + 1)));
|
||||
}
|
||||
|
||||
accountMenu->addAction(action);
|
||||
ui->profileMenu->addAction(action);
|
||||
connect(action, SIGNAL(triggered(bool)), SLOT(changeActiveAccount()));
|
||||
}
|
||||
}
|
||||
|
||||
accountMenu->addSeparator();
|
||||
ui->profileMenu->addSeparator();
|
||||
|
||||
QAction *action = new QAction(tr("No Default Account"), this);
|
||||
action->setCheckable(true);
|
||||
action->setIcon(APPLICATION->getThemedIcon("noaccount"));
|
||||
action->setData(-1);
|
||||
action->setShortcut(QKeySequence(tr("Ctrl+0")));
|
||||
if (!defaultAccount) {
|
||||
action->setChecked(true);
|
||||
}
|
||||
|
||||
accountMenu->addAction(action);
|
||||
ui->profileMenu->addAction(action);
|
||||
connect(action, SIGNAL(triggered(bool)), SLOT(changeActiveAccount()));
|
||||
|
||||
accountMenu->addSeparator();
|
||||
ui->profileMenu->addSeparator();
|
||||
accountMenu->addAction(ui->actionManageAccounts);
|
||||
ui->profileMenu->addAction(ui->actionManageAccounts);
|
||||
}
|
||||
|
||||
void MainWindow::updatesAllowedChanged(bool allowed)
|
||||
@ -1268,24 +1476,6 @@ QString intListToString(const QList<int> &list)
|
||||
}
|
||||
return slist.join(',');
|
||||
}
|
||||
void MainWindow::notificationsChanged()
|
||||
{
|
||||
QList<NotificationChecker::NotificationEntry> entries = m_notificationChecker->notificationEntries();
|
||||
QList<int> shownNotifications = stringToIntList(APPLICATION->settings()->get("ShownNotifications").toString());
|
||||
for (auto it = entries.begin(); it != entries.end(); ++it)
|
||||
{
|
||||
NotificationChecker::NotificationEntry entry = *it;
|
||||
if (!shownNotifications.contains(entry.id))
|
||||
{
|
||||
NotificationDialog dialog(entry, this);
|
||||
if (dialog.exec() == NotificationDialog::DontShowAgain)
|
||||
{
|
||||
shownNotifications.append(entry.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
APPLICATION->settings()->set("ShownNotifications", intListToString(shownNotifications));
|
||||
}
|
||||
|
||||
void MainWindow::downloadUpdates(GoUpdate::Status status)
|
||||
{
|
||||
@ -1656,6 +1846,7 @@ void MainWindow::globalSettingsClosed()
|
||||
APPLICATION->instances()->loadList();
|
||||
proxymodel->invalidate();
|
||||
proxymodel->sort(0);
|
||||
updateMainToolBar();
|
||||
updateToolsMenu();
|
||||
updateStatusCenter();
|
||||
update();
|
||||
@ -1701,6 +1892,11 @@ void MainWindow::on_actionReportBug_triggered()
|
||||
DesktopServices::openUrl(QUrl(BuildConfig.BUG_TRACKER_URL));
|
||||
}
|
||||
|
||||
void MainWindow::on_actionOpenWiki_triggered()
|
||||
{
|
||||
DesktopServices::openUrl(QUrl(BuildConfig.HELP_URL.arg("")));
|
||||
}
|
||||
|
||||
void MainWindow::on_actionMoreNews_triggered()
|
||||
{
|
||||
DesktopServices::openUrl(QUrl(BuildConfig.NEWS_OPEN_URL));
|
||||
@ -1888,6 +2084,7 @@ void MainWindow::instanceChanged(const QModelIndex ¤t, const QModelIndex &
|
||||
if (m_selectedInstance)
|
||||
{
|
||||
ui->instanceToolBar->setEnabled(true);
|
||||
ui->setInstanceActionsEnabled(true);
|
||||
if(m_selectedInstance->isRunning())
|
||||
{
|
||||
ui->actionLaunchInstance->setEnabled(true);
|
||||
@ -1912,6 +2109,7 @@ void MainWindow::instanceChanged(const QModelIndex ¤t, const QModelIndex &
|
||||
else
|
||||
{
|
||||
ui->instanceToolBar->setEnabled(false);
|
||||
ui->setInstanceActionsEnabled(false);
|
||||
APPLICATION->settings()->set("SelectedInstance", QString());
|
||||
selectionBad();
|
||||
return;
|
||||
@ -1940,6 +2138,7 @@ void MainWindow::selectionBad()
|
||||
|
||||
statusBar()->clearMessage();
|
||||
ui->instanceToolBar->setEnabled(false);
|
||||
ui->setInstanceActionsEnabled(false);
|
||||
ui->renameButton->setText(tr("Rename Instance"));
|
||||
updateInstanceToolIcon("grass");
|
||||
|
||||
|
@ -28,7 +28,6 @@
|
||||
|
||||
class LaunchController;
|
||||
class NewsChecker;
|
||||
class NotificationChecker;
|
||||
class QToolButton;
|
||||
class InstanceProxyModel;
|
||||
class LabeledToolButton;
|
||||
@ -111,6 +110,8 @@ private slots:
|
||||
|
||||
void on_actionReportBug_triggered();
|
||||
|
||||
void on_actionOpenWiki_triggered();
|
||||
|
||||
void on_actionMoreNews_triggered();
|
||||
|
||||
void newsButtonClicked();
|
||||
@ -150,6 +151,8 @@ private slots:
|
||||
|
||||
void showInstanceContextMenu(const QPoint &);
|
||||
|
||||
void updateMainToolBar();
|
||||
|
||||
void updateToolsMenu();
|
||||
|
||||
void instanceActivated(QModelIndex);
|
||||
@ -168,8 +171,6 @@ private slots:
|
||||
|
||||
void updateNotAvailable();
|
||||
|
||||
void notificationsChanged();
|
||||
|
||||
void defaultAccountChanged();
|
||||
|
||||
void changeActiveAccount();
|
||||
@ -187,6 +188,10 @@ private slots:
|
||||
|
||||
void globalSettingsClosed();
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
void keyReleaseEvent(QKeyEvent *event) override;
|
||||
#endif
|
||||
|
||||
private:
|
||||
void retranslateUi();
|
||||
|
||||
@ -215,7 +220,6 @@ private:
|
||||
KonamiCode * secretEventFilter = nullptr;
|
||||
|
||||
unique_qobject_ptr<NewsChecker> m_newsChecker;
|
||||
unique_qobject_ptr<NotificationChecker> m_notificationChecker;
|
||||
|
||||
InstancePtr m_selectedInstance;
|
||||
QString m_currentInstIcon;
|
||||
|
@ -1,29 +1,63 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* 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 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.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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 "AboutDialog.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "ui_AboutDialog.h"
|
||||
#include <QIcon>
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
|
||||
#include <net/NetJob.h>
|
||||
#include <qobject.h>
|
||||
|
||||
#include "HoeDown.h"
|
||||
|
||||
namespace {
|
||||
QString getLink(QString link, QString name) {
|
||||
return QString("<<a href='%1'>%2</a>>").arg(link).arg(name);
|
||||
}
|
||||
|
||||
QString getWebsite(QString link) {
|
||||
return getLink(link, QObject::tr("Website"));
|
||||
}
|
||||
|
||||
QString getGitHub(QString username) {
|
||||
return getLink("https://github.com/" + username, "GitHub");
|
||||
}
|
||||
|
||||
// Credits
|
||||
// This is a hack, but I can't think of a better way to do this easily without screwing with QTextDocument...
|
||||
QString getCreditsHtml()
|
||||
@ -33,15 +67,29 @@ QString getCreditsHtml()
|
||||
stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
stream << "<center>\n";
|
||||
|
||||
stream << "<h3>" << QObject::tr("PolyMC Developers", "About Credits") << "</h3>\n";
|
||||
stream << "<p>swirl <<a href='mailto:swurl@swurl.xyz'>swurl@swurl.xyz </a>></p>\n";
|
||||
stream << "<p>LennyMcLennington <<a href='mailto:lenny@sneed.church'>lenny@sneed.church</a>></p>\n";
|
||||
//: %1 is the name of the launcher, determined at build time, e.g. "PolyMC Developers"
|
||||
stream << "<h3>" << QObject::tr("%1 Developers", "About Credits").arg(BuildConfig.LAUNCHER_NAME) << "</h3>\n";
|
||||
stream << QString("<p>LennyMcLennington %1</p>\n") .arg(getGitHub("LennyMcLennington"));
|
||||
stream << QString("<p>Sefa Eyeoglu (Scrumplex) %1</p>\n") .arg(getWebsite("https://scrumplex.net"));
|
||||
stream << QString("<p>dada513 %1</p>\n") .arg(getGitHub("dada513"));
|
||||
stream << QString("<p>txtsd %1</p>\n") .arg(getGitHub("txtsd"));
|
||||
stream << QString("<p>timoreo %1</p>\n") .arg(getGitHub("timoreo22"));
|
||||
stream << QString("<p>Ezekiel Smith (ZekeSmith) %1</p>\n") .arg(getGitHub("ZekeSmith"));
|
||||
stream << QString("<p>cozyGalvinism %1</p>\n") .arg(getGitHub("cozyGalvinism"));
|
||||
stream << "<br />\n";
|
||||
|
||||
//: %1 is the name of the launcher, determined at build time, e.g. "PolyMC Contributors"
|
||||
stream << "<h3>" << QObject::tr("%1 Contributors", "About Credits").arg(BuildConfig.LAUNCHER_NAME) << "</h3>\n";
|
||||
stream << QString("<p>DioEgizio %1</p>\n") .arg(getGitHub("DioEgizio"));
|
||||
stream << QString("<p>flowln %1</p>\n") .arg(getGitHub("flowln"));
|
||||
stream << QString("<p>swirl %1</p>\n") .arg(getWebsite("https://swurl.xyz/"));
|
||||
stream << "<br />\n";
|
||||
|
||||
// TODO: possibly retrieve from git history at build time?
|
||||
stream << "<h3>" << QObject::tr("MultiMC Developers", "About Credits") << "</h3>\n";
|
||||
//: %1 is the name of the launcher, determined at build time, e.g. "PolyMC Developers"
|
||||
stream << "<h3>" << QObject::tr("%1 Developers", "About Credits").arg("MultiMC") << "</h3>\n";
|
||||
stream << "<p>Andrew Okin <<a href='mailto:forkk@forkk.net'>forkk@forkk.net</a>></p>\n";
|
||||
stream << "<p>Petr Mrázek <<a href='mailto:peterix@gmail.com'>peterix@gmail.com</a>></p>\n";
|
||||
stream << QString("<p>Petr Mrázek <<a href='mailto:peterix@gmail.com'>peterix@gmail.com</a>></p>\n");
|
||||
stream << "<p>Sky Welch <<a href='mailto:multimc@bunnies.io'>multimc@bunnies.io</a>></p>\n";
|
||||
stream << "<p>Jan (02JanDal) <<a href='mailto:02jandal@gmail.com'>02jandal@gmail.com</a>></p>\n";
|
||||
stream << "<p>RoboSky <<a href='https://twitter.com/RoboSky_'>@RoboSky_</a>></p>\n";
|
||||
|
@ -80,14 +80,14 @@
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">PolyMC</string>
|
||||
<string notr="true">Launcher</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item>
|
||||
<widget class="QLabel" name="versionLabel">
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
@ -209,13 +209,10 @@
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QTextEdit" name="creditsText">
|
||||
<property name="readOnly">
|
||||
<widget class="QTextBrowser" name="creditsText">
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextBrowserInteraction</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -22,7 +22,9 @@ ModDownloadDialog::ModDownloadDialog(const std::shared_ptr<ModFolderModel> &mods
|
||||
: QDialog(parent), mods(mods), m_instance(instance)
|
||||
{
|
||||
setObjectName(QStringLiteral("ModDownloadDialog"));
|
||||
resize(400, 347);
|
||||
|
||||
resize(std::max(0.5*parent->width(), 400.0), std::max(0.75*parent->height(), 400.0));
|
||||
|
||||
m_verticalLayout = new QVBoxLayout(this);
|
||||
m_verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
|
||||
|
||||
@ -80,7 +82,7 @@ void ModDownloadDialog::confirm()
|
||||
tr("Confirm mods to download")
|
||||
);
|
||||
|
||||
for(auto task : keys){
|
||||
for(auto& task : keys){
|
||||
confirm_dialog->appendMod(task, modTask.find(task).value()->getFilename());
|
||||
}
|
||||
|
||||
|
@ -1,86 +0,0 @@
|
||||
#include "NotificationDialog.h"
|
||||
#include "ui_NotificationDialog.h"
|
||||
|
||||
#include <QTimerEvent>
|
||||
#include <QStyle>
|
||||
|
||||
NotificationDialog::NotificationDialog(const NotificationChecker::NotificationEntry &entry, QWidget *parent) :
|
||||
QDialog(parent, Qt::MSWindowsFixedSizeDialogHint | Qt::WindowTitleHint | Qt::CustomizeWindowHint),
|
||||
ui(new Ui::NotificationDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
QStyle::StandardPixmap icon;
|
||||
switch (entry.type)
|
||||
{
|
||||
case NotificationChecker::NotificationEntry::Critical:
|
||||
icon = QStyle::SP_MessageBoxCritical;
|
||||
break;
|
||||
case NotificationChecker::NotificationEntry::Warning:
|
||||
icon = QStyle::SP_MessageBoxWarning;
|
||||
break;
|
||||
default:
|
||||
case NotificationChecker::NotificationEntry::Information:
|
||||
icon = QStyle::SP_MessageBoxInformation;
|
||||
break;
|
||||
}
|
||||
ui->iconLabel->setPixmap(style()->standardPixmap(icon, 0, this));
|
||||
ui->messageLabel->setText(entry.message);
|
||||
|
||||
m_dontShowAgainText = tr("Don't show again");
|
||||
m_closeText = tr("Close");
|
||||
|
||||
ui->dontShowAgainBtn->setText(m_dontShowAgainText + QString(" (%1)").arg(m_dontShowAgainTime));
|
||||
ui->closeBtn->setText(m_closeText + QString(" (%1)").arg(m_closeTime));
|
||||
|
||||
startTimer(1000);
|
||||
}
|
||||
|
||||
NotificationDialog::~NotificationDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void NotificationDialog::timerEvent(QTimerEvent *event)
|
||||
{
|
||||
if (m_dontShowAgainTime > 0)
|
||||
{
|
||||
m_dontShowAgainTime--;
|
||||
if (m_dontShowAgainTime == 0)
|
||||
{
|
||||
ui->dontShowAgainBtn->setText(m_dontShowAgainText);
|
||||
ui->dontShowAgainBtn->setEnabled(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->dontShowAgainBtn->setText(m_dontShowAgainText + QString(" (%1)").arg(m_dontShowAgainTime));
|
||||
}
|
||||
}
|
||||
if (m_closeTime > 0)
|
||||
{
|
||||
m_closeTime--;
|
||||
if (m_closeTime == 0)
|
||||
{
|
||||
ui->closeBtn->setText(m_closeText);
|
||||
ui->closeBtn->setEnabled(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->closeBtn->setText(m_closeText + QString(" (%1)").arg(m_closeTime));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_closeTime == 0 && m_dontShowAgainTime == 0)
|
||||
{
|
||||
killTimer(event->timerId());
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationDialog::on_dontShowAgainBtn_clicked()
|
||||
{
|
||||
done(DontShowAgain);
|
||||
}
|
||||
void NotificationDialog::on_closeBtn_clicked()
|
||||
{
|
||||
done(Normal);
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
#ifndef NOTIFICATIONDIALOG_H
|
||||
#define NOTIFICATIONDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "notifications/NotificationChecker.h"
|
||||
|
||||
namespace Ui {
|
||||
class NotificationDialog;
|
||||
}
|
||||
|
||||
class NotificationDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NotificationDialog(const NotificationChecker::NotificationEntry &entry, QWidget *parent = 0);
|
||||
~NotificationDialog();
|
||||
|
||||
enum ExitCode
|
||||
{
|
||||
Normal,
|
||||
DontShowAgain
|
||||
};
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *event);
|
||||
|
||||
private:
|
||||
Ui::NotificationDialog *ui;
|
||||
|
||||
int m_dontShowAgainTime = 10;
|
||||
int m_closeTime = 5;
|
||||
|
||||
QString m_dontShowAgainText;
|
||||
QString m_closeText;
|
||||
|
||||
private
|
||||
slots:
|
||||
void on_dontShowAgainBtn_clicked();
|
||||
void on_closeBtn_clicked();
|
||||
};
|
||||
|
||||
#endif // NOTIFICATIONDIALOG_H
|
@ -1,85 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>NotificationDialog</class>
|
||||
<widget class="QDialog" name="NotificationDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>320</width>
|
||||
<height>240</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Notification</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,0">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1">
|
||||
<item>
|
||||
<widget class="QLabel" name="iconLabel">
|
||||
<property name="text">
|
||||
<string notr="true">TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="messageLabel">
|
||||
<property name="text">
|
||||
<string notr="true">TextLabel</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextBrowserInteraction</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="dontShowAgainBtn">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Don't show again</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeBtn">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -81,6 +81,12 @@ int ProgressDialog::execWithTask(Task *task)
|
||||
connect(task, SIGNAL(status(QString)), SLOT(changeStatus(const QString &)));
|
||||
connect(task, SIGNAL(progress(qint64, qint64)), SLOT(changeProgress(qint64, qint64)));
|
||||
|
||||
m_is_multi_step = task->isMultiStep();
|
||||
if(!m_is_multi_step){
|
||||
ui->globalStatusLabel->setHidden(true);
|
||||
ui->globalProgressBar->setHidden(true);
|
||||
}
|
||||
|
||||
// if this didn't connect to an already running task, invoke start
|
||||
if(!task->isRunning())
|
||||
{
|
||||
@ -152,14 +158,24 @@ void ProgressDialog::onTaskSucceeded()
|
||||
|
||||
void ProgressDialog::changeStatus(const QString &status)
|
||||
{
|
||||
ui->statusLabel->setText(status);
|
||||
ui->statusLabel->setText(task->getStepStatus());
|
||||
ui->globalStatusLabel->setText(status);
|
||||
updateSize();
|
||||
}
|
||||
|
||||
void ProgressDialog::changeProgress(qint64 current, qint64 total)
|
||||
{
|
||||
ui->taskProgressBar->setMaximum(total);
|
||||
ui->taskProgressBar->setValue(current);
|
||||
ui->globalProgressBar->setMaximum(total);
|
||||
ui->globalProgressBar->setValue(current);
|
||||
|
||||
if(!m_is_multi_step){
|
||||
ui->taskProgressBar->setMaximum(total);
|
||||
ui->taskProgressBar->setValue(current);
|
||||
}
|
||||
else{
|
||||
ui->taskProgressBar->setMaximum(task->getStepProgress());
|
||||
ui->taskProgressBar->setValue(task->getStepTotalProgress());
|
||||
}
|
||||
}
|
||||
|
||||
void ProgressDialog::keyPressEvent(QKeyEvent *e)
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <memory>
|
||||
|
||||
class Task;
|
||||
class SequentialTask;
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
@ -35,7 +36,7 @@ public:
|
||||
|
||||
void updateSize();
|
||||
|
||||
int execWithTask(Task *task);
|
||||
int execWithTask(Task* task);
|
||||
int execWithTask(std::unique_ptr<Task> &&task);
|
||||
int execWithTask(std::unique_ptr<Task> &task);
|
||||
|
||||
@ -68,4 +69,6 @@ private:
|
||||
Ui::ProgressDialog *ui;
|
||||
|
||||
Task *task;
|
||||
|
||||
bool m_is_multi_step = false;
|
||||
};
|
||||
|
@ -2,14 +2,6 @@
|
||||
<ui version="4.0">
|
||||
<class>ProgressDialog</class>
|
||||
<widget class="QDialog" name="ProgressDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>400</width>
|
||||
@ -26,27 +18,7 @@
|
||||
<string>Please wait...</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="statusLabel">
|
||||
<property name="text">
|
||||
<string>Task Status...</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QProgressBar" name="taskProgressBar">
|
||||
<property name="value">
|
||||
<number>24</number>
|
||||
</property>
|
||||
<property name="textVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="4" column="0">
|
||||
<widget class="QPushButton" name="skipButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
@ -59,6 +31,43 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="globalStatusLabel">
|
||||
<property name="text">
|
||||
<string>Global Task Status...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="statusLabel">
|
||||
<property name="text">
|
||||
<string>Task Status...</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QProgressBar" name="taskProgressBar">
|
||||
<property name="value">
|
||||
<number>24</number>
|
||||
</property>
|
||||
<property name="textVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QProgressBar" name="globalProgressBar">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>24</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>PolyMC Update</string>
|
||||
<string>Launcher Update</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset>
|
||||
|
@ -161,10 +161,11 @@ void AccountListPage::on_actionAddMicrosoft_triggered()
|
||||
CustomMessageBox::selectable(
|
||||
this,
|
||||
tr("Microsoft Accounts not available"),
|
||||
//: %1 refers to the launcher itself
|
||||
tr(
|
||||
"Microsoft accounts are only usable on macOS 10.13 or newer, with fully updated PolyMC.\n\n"
|
||||
"Please update both your operating system and PolyMC."
|
||||
),
|
||||
"Microsoft accounts are only usable on macOS 10.13 or newer, with fully updated %1.\n\n"
|
||||
"Please update both your operating system and %1."
|
||||
).arg(BuildConfig.LAUNCHER_NAME),
|
||||
QMessageBox::Warning
|
||||
)->exec();
|
||||
return;
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
* 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
|
||||
@ -95,6 +96,7 @@ void JavaPage::applySettings()
|
||||
// Java Settings
|
||||
s->set("JavaPath", ui->javaPathTextBox->text());
|
||||
s->set("JvmArgs", ui->jvmArgsTextBox->text());
|
||||
s->set("IgnoreJavaCompatibility", ui->skipCompatibilityCheckbox->isChecked());
|
||||
JavaCommon::checkJVMArgs(s->get("JvmArgs").toString(), this->parentWidget());
|
||||
}
|
||||
void JavaPage::loadSettings()
|
||||
@ -118,6 +120,7 @@ void JavaPage::loadSettings()
|
||||
// Java Settings
|
||||
ui->javaPathTextBox->setText(s->get("JavaPath").toString());
|
||||
ui->jvmArgsTextBox->setText(s->get("JvmArgs").toString());
|
||||
ui->skipCompatibilityCheckbox->setChecked(s->get("IgnoreJavaCompatibility").toBool());
|
||||
}
|
||||
|
||||
void JavaPage::on_javaDetectBtn_clicked()
|
||||
|
@ -222,6 +222,22 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="skipCompatibilityCheckbox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>If enabled, the launcher will not check if an instance is compatible with the selected Java version.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Skip Java compatibility checks</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
* Copyright (c) 2022 dada513 <dada513@protonmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -40,6 +41,7 @@
|
||||
#include <QMessageBox>
|
||||
#include <QDir>
|
||||
#include <QTextCharFormat>
|
||||
#include <QMenuBar>
|
||||
|
||||
#include "updater/UpdateChecker.h"
|
||||
|
||||
@ -134,13 +136,31 @@ void LauncherPage::on_instDirBrowseBtn_clicked()
|
||||
warning.setInformativeText(
|
||||
tr("Do you really want to use this path? "
|
||||
"Selecting \"No\" will close this and not alter your instance path."));
|
||||
warning.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
warning.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
|
||||
int result = warning.exec();
|
||||
if (result == QMessageBox::Yes)
|
||||
if (result == QMessageBox::Ok)
|
||||
{
|
||||
ui->instDirTextBox->setText(cooked_dir);
|
||||
}
|
||||
}
|
||||
else if(APPLICATION->isFlatpak() && raw_dir.startsWith("/run/user"))
|
||||
{
|
||||
QMessageBox warning;
|
||||
warning.setText(tr("You're trying to specify an instance folder "
|
||||
"which was granted temporaily via Flatpak.\n"
|
||||
"This is known to cause problems. "
|
||||
"After a restart the launcher might break, "
|
||||
"because it will no longer have access to that directory.\n\n"
|
||||
"Granting PolyMC access to it via Flatseal is recommended."));
|
||||
warning.setInformativeText(
|
||||
tr("Do you want to proceed anyway?"));
|
||||
warning.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
|
||||
int result = warning.exec();
|
||||
if (result == QMessageBox::Ok)
|
||||
{
|
||||
ui->instDirTextBox->setText(cooked_dir);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->instDirTextBox->setText(cooked_dir);
|
||||
@ -254,11 +274,6 @@ void LauncherPage::applySettings()
|
||||
{
|
||||
auto s = APPLICATION->settings();
|
||||
|
||||
if (ui->resetNotificationsBtn->isChecked())
|
||||
{
|
||||
s->set("ShownNotifications", QString());
|
||||
}
|
||||
|
||||
// Updates
|
||||
s->set("AutoUpdate", ui->autoUpdateCheckBox->isChecked());
|
||||
s->set("UpdateChannel", m_currentUpdateChannel);
|
||||
@ -308,6 +323,8 @@ void LauncherPage::applySettings()
|
||||
APPLICATION->setApplicationTheme(newAppTheme, false);
|
||||
}
|
||||
|
||||
s->set("MenuBarInsteadOfToolBar", ui->preferMenuBarCheckBox->isChecked());
|
||||
|
||||
// Console settings
|
||||
s->set("ShowConsole", ui->showConsoleCheck->isChecked());
|
||||
s->set("AutoCloseConsole", ui->autoCloseConsoleCheck->isChecked());
|
||||
@ -396,6 +413,13 @@ void LauncherPage::loadSettings()
|
||||
}
|
||||
}
|
||||
|
||||
// Toolbar/menu bar settings (not applicable if native menu bar is present)
|
||||
ui->toolsBox->setEnabled(!QMenuBar().isNativeMenuBar());
|
||||
#ifdef Q_OS_MACOS
|
||||
ui->toolsBox->setVisible(!QMenuBar().isNativeMenuBar());
|
||||
#endif
|
||||
ui->preferMenuBarCheckBox->setChecked(s->get("MenuBarInsteadOfToolBar").toBool());
|
||||
|
||||
// Console settings
|
||||
ui->showConsoleCheck->setChecked(s->get("ShowConsole").toBool());
|
||||
ui->autoCloseConsoleCheck->setChecked(s->get("AutoCloseConsole").toBool());
|
||||
|
@ -184,25 +184,6 @@
|
||||
<string>User Interface</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="title">
|
||||
<string>Launcher notifications</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QPushButton" name="resetNotificationsBtn">
|
||||
<property name="text">
|
||||
<string>Reset hidden notifications</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="sortingModeBox">
|
||||
<property name="enabled">
|
||||
@ -309,6 +290,16 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Colors</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>themeComboBoxColors</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="themeComboBoxColors">
|
||||
<property name="sizePolicy">
|
||||
@ -322,13 +313,28 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Colors</string>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="toolsBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Tools</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="preferMenuBarCheckBox">
|
||||
<property name="toolTip">
|
||||
<string>The menubar is more friendly for keyboard-driven interaction.</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>themeComboBoxColors</cstring>
|
||||
<property name="text">
|
||||
<string>Replace toolbar with menubar</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -499,7 +505,6 @@
|
||||
<tabstop>modsDirBrowseBtn</tabstop>
|
||||
<tabstop>iconsDirTextBox</tabstop>
|
||||
<tabstop>iconsDirBrowseBtn</tabstop>
|
||||
<tabstop>resetNotificationsBtn</tabstop>
|
||||
<tabstop>sortLastLaunchedBtn</tabstop>
|
||||
<tabstop>sortByNameBtn</tabstop>
|
||||
<tabstop>themeComboBox</tabstop>
|
||||
|
@ -94,6 +94,7 @@ void MinecraftPage::applySettings()
|
||||
|
||||
// Miscellaneous
|
||||
s->set("CloseAfterLaunch", ui->closeAfterLaunchCheck->isChecked());
|
||||
s->set("QuitAfterGameStop", ui->quitAfterGameStopCheck->isChecked());
|
||||
}
|
||||
|
||||
void MinecraftPage::loadSettings()
|
||||
@ -113,6 +114,7 @@ void MinecraftPage::loadSettings()
|
||||
ui->recordGameTime->setChecked(s->get("RecordGameTime").toBool());
|
||||
|
||||
ui->closeAfterLaunchCheck->setChecked(s->get("CloseAfterLaunch").toBool());
|
||||
ui->quitAfterGameStopCheck->setChecked(s->get("QuitAfterGameStop").toBool());
|
||||
}
|
||||
|
||||
void MinecraftPage::retranslate()
|
||||
|
@ -173,13 +173,23 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="closeAfterLaunchCheck">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>PolyMC will automatically reopen when the game crashes or exits.</p></body></html></string>
|
||||
<string><html><head/><body><p>The launcher will automatically reopen when the game crashes or exits.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close PolyMC after game window opens</string>
|
||||
<string>Close the launcher after game window opens</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="quitAfterGameStopCheck">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>The launcher will automatically quit after the game exits or crashes.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Quit the launcher after game window closes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -165,10 +165,12 @@ void InstanceSettingsPage::applySettings()
|
||||
if (javaInstall)
|
||||
{
|
||||
m_settings->set("JavaPath", ui->javaPathTextBox->text());
|
||||
m_settings->set("IgnoreJavaCompatibility", ui->skipCompatibilityCheckbox->isChecked());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_settings->reset("JavaPath");
|
||||
m_settings->reset("IgnoreJavaCompatibility");
|
||||
}
|
||||
|
||||
// Java arguments
|
||||
@ -177,7 +179,6 @@ void InstanceSettingsPage::applySettings()
|
||||
if(javaArgs)
|
||||
{
|
||||
m_settings->set("JvmArgs", ui->jvmArgsTextBox->toPlainText().replace("\n", " "));
|
||||
JavaCommon::checkJVMArgs(m_settings->get("JvmArgs").toString(), this->parentWidget());
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -286,6 +287,7 @@ void InstanceSettingsPage::loadSettings()
|
||||
|
||||
ui->javaSettingsGroupBox->setChecked(overrideLocation);
|
||||
ui->javaPathTextBox->setText(m_settings->get("JavaPath").toString());
|
||||
ui->skipCompatibilityCheckbox->setChecked(m_settings->get("IgnoreJavaCompatibility").toBool());
|
||||
|
||||
ui->javaArgumentsGroupBox->setChecked(overrideArgs);
|
||||
ui->jvmArgsTextBox->setPlainText(m_settings->get("JvmArgs").toString());
|
||||
|
@ -85,6 +85,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="skipCompatibilityCheckbox">
|
||||
<property name="toolTip">
|
||||
<string>If enabled, the launcher will not check if an instance is compatible with the selected Java version.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Skip Java compatibility checks</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -56,8 +56,11 @@
|
||||
#include "minecraft/VersionFilterData.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
|
||||
#include "modplatform/ModAPI.h"
|
||||
|
||||
#include "Version.h"
|
||||
#include "ui/dialogs/ProgressDialog.h"
|
||||
#include "tasks/SequentialTask.h"
|
||||
|
||||
namespace {
|
||||
// FIXME: wasteful
|
||||
@ -387,32 +390,31 @@ void ModFolderPage::on_actionInstall_mods_triggered()
|
||||
if(m_inst->typeName() != "Minecraft"){
|
||||
return; //this is a null instance or a legacy instance
|
||||
}
|
||||
bool hasFabric = !((MinecraftInstance *)m_inst)->getPackProfile()->getComponentVersion("net.fabricmc.fabric-loader").isEmpty();
|
||||
bool hasForge = !((MinecraftInstance *)m_inst)->getPackProfile()->getComponentVersion("net.minecraftforge").isEmpty();
|
||||
if (!hasFabric && !hasForge) {
|
||||
auto profile = ((MinecraftInstance *)m_inst)->getPackProfile();
|
||||
if (profile->getModLoader() == ModAPI::Unspecified) {
|
||||
QMessageBox::critical(this,tr("Error"),tr("Please install a mod loader first!"));
|
||||
return;
|
||||
}
|
||||
ModDownloadDialog mdownload(m_mods, this, m_inst);
|
||||
if(mdownload.exec()) {
|
||||
for(auto task : mdownload.getTasks()){
|
||||
connect(task, &Task::failed, [this, task](QString reason) {
|
||||
task->deleteLater();
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show();
|
||||
});
|
||||
connect(task, &Task::succeeded, [this, task]() {
|
||||
QStringList warnings = task->warnings();
|
||||
if (warnings.count()) {
|
||||
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'),
|
||||
QMessageBox::Warning)->show();
|
||||
}
|
||||
task->deleteLater();
|
||||
});
|
||||
ProgressDialog loadDialog(this);
|
||||
loadDialog.setSkipButton(true, tr("Abort"));
|
||||
loadDialog.execWithTask(task);
|
||||
m_mods->update();
|
||||
if (mdownload.exec()) {
|
||||
SequentialTask* tasks = new SequentialTask(this);
|
||||
connect(tasks, &Task::failed, [this, tasks](QString reason) {
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show();
|
||||
tasks->deleteLater();
|
||||
});
|
||||
connect(tasks, &Task::succeeded, [this, tasks]() {
|
||||
QStringList warnings = tasks->warnings();
|
||||
if (warnings.count()) { CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); }
|
||||
tasks->deleteLater();
|
||||
});
|
||||
|
||||
for (auto task : mdownload.getTasks()) {
|
||||
tasks->addTask(task);
|
||||
}
|
||||
ProgressDialog loadDialog(this);
|
||||
loadDialog.setSkipButton(true, tr("Abort"));
|
||||
loadDialog.execWithTask(tasks);
|
||||
m_mods->update();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
* 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
|
||||
@ -242,6 +243,9 @@ void VersionPage::updateVersionControls()
|
||||
bool supportsFabric = minecraftVersion >= Version("1.14");
|
||||
ui->actionInstall_Fabric->setEnabled(controlsEnabled && supportsFabric);
|
||||
|
||||
bool supportsQuilt = minecraftVersion >= Version("1.14");
|
||||
ui->actionInstall_Quilt->setEnabled(controlsEnabled && supportsQuilt);
|
||||
|
||||
bool supportsLiteLoader = minecraftVersion <= Version("1.12.2");
|
||||
ui->actionInstall_LiteLoader->setEnabled(controlsEnabled && supportsLiteLoader);
|
||||
|
||||
@ -391,7 +395,7 @@ void VersionPage::on_actionChange_version_triggered()
|
||||
return;
|
||||
}
|
||||
VersionSelectDialog vselect(list.get(), tr("Change %1 version").arg(name), this);
|
||||
if (uid == "net.fabricmc.intermediary")
|
||||
if (uid == "net.fabricmc.intermediary" || uid == "org.quiltmc.hashed")
|
||||
{
|
||||
vselect.setEmptyString(tr("No intermediary mappings versions are currently available."));
|
||||
vselect.setEmptyErrorString(tr("Couldn't load or download the intermediary mappings version lists!"));
|
||||
@ -422,7 +426,7 @@ void VersionPage::on_actionDownload_All_triggered()
|
||||
{
|
||||
CustomMessageBox::selectable(
|
||||
this, tr("Error"),
|
||||
tr("PolyMC cannot download Minecraft or update instances unless you have at least "
|
||||
tr("Cannot download Minecraft or update instances unless you have at least "
|
||||
"one account added.\nPlease add your Mojang or Minecraft account."),
|
||||
QMessageBox::Warning)->show();
|
||||
return;
|
||||
@ -497,6 +501,33 @@ void VersionPage::on_actionInstall_Fabric_triggered()
|
||||
}
|
||||
}
|
||||
|
||||
void VersionPage::on_actionInstall_Quilt_triggered()
|
||||
{
|
||||
auto vlist = APPLICATION->metadataIndex()->get("org.quiltmc.quilt-loader");
|
||||
if(!vlist)
|
||||
{
|
||||
return;
|
||||
}
|
||||
VersionSelectDialog vselect(vlist.get(), tr("Select Quilt Loader version"), this);
|
||||
vselect.setEmptyString(tr("No Quilt Loader versions are currently available."));
|
||||
vselect.setEmptyErrorString(tr("Couldn't load or download the Quilt Loader version lists!"));
|
||||
|
||||
auto currentVersion = m_profile->getComponentVersion("org.quiltmc.quilt-loader");
|
||||
if(!currentVersion.isEmpty())
|
||||
{
|
||||
vselect.setCurrentVersion(currentVersion);
|
||||
}
|
||||
|
||||
if (vselect.exec() && vselect.selectedVersion())
|
||||
{
|
||||
auto vsn = vselect.selectedVersion();
|
||||
m_profile->setComponentVersion("org.quiltmc.quilt-loader", vsn->descriptor());
|
||||
m_profile->resolve(Net::Mode::Online);
|
||||
preselect(m_profile->rowCount(QModelIndex())-1);
|
||||
m_container->refreshContainer();
|
||||
}
|
||||
}
|
||||
|
||||
void VersionPage::on_actionAdd_Empty_triggered()
|
||||
{
|
||||
NewComponentDialog compdialog(QString(), QString(), this);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user