Compare commits
375 Commits
Author | SHA1 | Date | |
---|---|---|---|
9f8983b570 | |||
e42c5b00a4 | |||
a3d7ad731d | |||
dc98609ef8 | |||
c799faaca6 | |||
07e4c4d189 | |||
c4a8fd353c | |||
91524d6d6e | |||
c1201997a3 | |||
d814e21f0d | |||
49d122a2c4 | |||
0813eba367 | |||
dc9e250868 | |||
b6cf0359fa | |||
ae39d16c11 | |||
b93daf1fb7 | |||
6545d250e8 | |||
b162351ff4 | |||
53468ea474 | |||
3697d70b48 | |||
e0c025b162 | |||
9e443faba3 | |||
f95cebaf06 | |||
ca211558b5 | |||
3a03f90831 | |||
9020f632b2 | |||
8bdd2befe9 | |||
ebececf8c6 | |||
e1f28be151 | |||
10231aa404 | |||
139ff82432 | |||
4e8f075ff3 | |||
148775b3e9 | |||
075d900d45 | |||
5d1ca33b84 | |||
ccc493cb2b | |||
2745325ae0 | |||
84e9ce71b0 | |||
88fc1e32ee | |||
4f975bfb04 | |||
692b9cf0e0 | |||
ae354688c9 | |||
c9bf7f9896 | |||
e8929599a5 | |||
27f37315f8 | |||
c4f4e9e620 | |||
f2b850ad20 | |||
ccc27d1b7c | |||
4835ec3f6d | |||
40a9828fba | |||
04840d0638 | |||
38f12c50f7 | |||
ca8b62291f | |||
69d01204e0 | |||
1e3b896fda | |||
9e35560554 | |||
f9d4751ec0 | |||
a66e6a413f | |||
2c911a5475 | |||
e2c2a38005 | |||
aa4a6bb3be | |||
4af020161d | |||
f8b0d6453a | |||
0102e91940 | |||
1004211a66 | |||
f5cf4eb45f | |||
512395e3f1 | |||
9c6727e27f | |||
624ab25cd4 | |||
a70d1f1a91 | |||
3059f13011 | |||
d3e7d30ee0 | |||
5bbb4f31dc | |||
8556ff5eac | |||
adacab3349 | |||
613b351f13 | |||
80a29af497 | |||
7e1fad55d9 | |||
9b7cd029a7 | |||
c5d9944993 | |||
14717396eb | |||
9c71f364d2 | |||
be910374dc | |||
10de75623e | |||
7fc55b58fe | |||
12c8a04458 | |||
796e58b6f2 | |||
a7d37fa69a | |||
7b4b997a34 | |||
6b4469c6cc | |||
107a0ea852 | |||
6d7676202f | |||
0305b7a1fd | |||
02384f81c7 | |||
4536b19512 | |||
45f89c6255 | |||
b1cf77e847 | |||
037b0d7190 | |||
ff17202b43 | |||
a3d8313dcb | |||
36841eaf63 | |||
e24a183dad | |||
8596753a63 | |||
51183bef33 | |||
2cd837896d | |||
c75ae71190 | |||
48c20f5aaa | |||
929a035f96 | |||
5779f20fa1 | |||
384e0df9e1 | |||
a5b06514c6 | |||
a309f4e721 | |||
159d868b77 | |||
0854e83ce4 | |||
bb02226870 | |||
9ddbaaf7e8 | |||
3b7cc4391a | |||
70a055bc27 | |||
61db1c46be | |||
a60b2feb5f | |||
6ba031f048 | |||
812f00eb81 | |||
2f6973e08b | |||
fb14796ed7 | |||
7d912726d0 | |||
e3c8eb062c | |||
3cf81faabc | |||
0df605e559 | |||
e97a6ef957 | |||
c4dd8d9c72 | |||
bcfa3246cb | |||
f67871e79e | |||
11f892380e | |||
fd72cb034c | |||
b9be8d08d2 | |||
33d369d78c | |||
0e6e6a7521 | |||
eb692c2ee9 | |||
3eee38fedd | |||
0ba61bb590 | |||
9497485103 | |||
551d9c86ba | |||
e61e827eb9 | |||
92a5b12bd9 | |||
35d1330fe2 | |||
b4e0b7584a | |||
2341212337 | |||
53ea261350 | |||
a17e5d0a4d | |||
5d4a66ed8a | |||
8d2e7db178 | |||
383c93ff01 | |||
13d41bde7f | |||
d42d6fe25a | |||
beaac54dc9 | |||
70f8cb81b8 | |||
f8ca6b4867 | |||
49f5f67467 | |||
35d2ae3ef7 | |||
22ca572ae0 | |||
619fcbfabf | |||
292869141f | |||
ca9929214d | |||
121ad4e4bf | |||
2f87a4477e | |||
d72c511131 | |||
8b68c06547 | |||
b911151786 | |||
7531a2894b | |||
5b507a8944 | |||
d2f86cbf32 | |||
08dff6613b | |||
901ec15dc8 | |||
5284d604ef | |||
b2b4ab3f0c | |||
b646fc5a13 | |||
d35cbfd9c4 | |||
f25152e068 | |||
dd8946b15a | |||
305350fdc8 | |||
f31d5372e7 | |||
ca1a2bbe2c | |||
dba9199e58 | |||
e806903d7e | |||
41d8e3cbd7 | |||
5e32783c4e | |||
fdc3a4104b | |||
8199941a81 | |||
651d237200 | |||
6f5b380199 | |||
c746d77a06 | |||
9ab3841570 | |||
3a28741cc3 | |||
22f7a85cf2 | |||
86935068f5 | |||
00e12b776b | |||
3b96a9a8fa | |||
5ac5c767e0 | |||
f1d3d4a366 | |||
1160066f0d | |||
8d603d6162 | |||
076efc4cb2 | |||
2c62a34c2f | |||
2177aa2a6b | |||
e8a4902a3d | |||
e2952061af | |||
fcc4420cfe | |||
f5358aa1ca | |||
3d3f9a8609 | |||
1f176fcb7b | |||
3ca661127f | |||
407f9d9ef0 | |||
bff683e6d4 | |||
770da6317e | |||
cf3c2482c9 | |||
2001c5dec7 | |||
80cf716c98 | |||
37e1962845 | |||
f8c5d80c66 | |||
11841c47e6 | |||
71b1ac9f34 | |||
eda06df878 | |||
c4cb7ddc4f | |||
a8089b76c0 | |||
71516e6c72 | |||
88686ef065 | |||
0442b80a2c | |||
81c72c2038 | |||
3aa809b8c0 | |||
efa414c442 | |||
c39da093bf | |||
aa2c27bf69 | |||
1f92125a7f | |||
5c48b7dfab | |||
d664361b15 | |||
813de1c703 | |||
02f24117f0 | |||
7a7a937f1e | |||
0211ee3ef1 | |||
c569bfbe6d | |||
b925338688 | |||
2cf04d034a | |||
049aafd0a1 | |||
e0a04c5031 | |||
9d23ac562f | |||
0065a29901 | |||
8ea1ebaf1b | |||
5c2d3e430d | |||
b710b719a8 | |||
7df5091fdc | |||
5abb97362f | |||
b34239ebc6 | |||
7ef6c586c5 | |||
9d36cf4b5a | |||
04be2404ce | |||
7f4fd04cfe | |||
b2bcdb9d9b | |||
d81e2bb0b0 | |||
177c10bb0f | |||
304775952f | |||
ca00103ee2 | |||
3aa9f5c376 | |||
efc44c56a6 | |||
8b790a6dd9 | |||
f36930d812 | |||
232295bda7 | |||
451768ccf9 | |||
c1aaf89baa | |||
5ac528f141 | |||
204e3dca22 | |||
33aac2985a | |||
361ce7818e | |||
ad6e3a0868 | |||
fd2269ac15 | |||
14a8ead6b4 | |||
bbf0508846 | |||
79314ee67b | |||
86b0637ef7 | |||
1c982b0182 | |||
cd5faee7d7 | |||
ec1e27031a | |||
02889b7a11 | |||
0eff21a4f1 | |||
0680945839 | |||
631e670775 | |||
4edd2cff9f | |||
0235eb5c28 | |||
8804b035b2 | |||
54e3438e37 | |||
ddfed7bb87 | |||
70c04745ee | |||
63d4486855 | |||
1d0e6bf453 | |||
a2d88f6df4 | |||
019c77f9f7 | |||
5b5c5afc3a | |||
af20b5ee0e | |||
a1b779da15 | |||
8b31c638f3 | |||
3da69d8e53 | |||
dd76fb0ec7 | |||
caeab926bc | |||
35caa3c21a | |||
7022d3d401 | |||
6d22794cf9 | |||
b50e584369 | |||
affc2521aa | |||
63098b6f19 | |||
83e1dd285a | |||
b9beb3c7d2 | |||
2dd2555a63 | |||
126e6d13aa | |||
236c0166f6 | |||
9a33fb1f49 | |||
2c2c22ccf8 | |||
55597b458c | |||
f55297eca9 | |||
0bc8baf117 | |||
5f9270ed4b | |||
81fe41a038 | |||
0316cf88aa | |||
917f8a31e3 | |||
aa770b63fb | |||
c1bf31cb27 | |||
86d99f80c3 | |||
975f77756d | |||
8172dcd2d5 | |||
b95c27ceef | |||
f78bb90ed9 | |||
dc129fd886 | |||
621e0ba4a8 | |||
f6de472da2 | |||
4b37c46889 | |||
c371558883 | |||
0bbd0ac0b9 | |||
ac93c64cd4 | |||
6cd4375aff | |||
a606b47a22 | |||
a62155c1c9 | |||
b4f750e7db | |||
b19e315615 | |||
41dba376a8 | |||
2896f70cd8 | |||
1a8c972aef | |||
cdaa397dcf | |||
9e6fa8f29a | |||
4d599eb118 | |||
4e9039be2d | |||
395e265564 | |||
b07853c9ef | |||
b635a7e693 | |||
f84fc783b6 | |||
2301a934b0 | |||
60e9d2754a | |||
d06c2cacb1 | |||
49c2bd477c | |||
46a3b4de6e | |||
6ecc8c5496 | |||
a1ff3b1ee3 | |||
d4b522b6cb | |||
3b524e99cc | |||
3329d94c9b | |||
a12118f1a0 | |||
5fe33a98a2 | |||
1d8196e11a | |||
c7f6f94930 | |||
131615811b | |||
3fa6e22430 | |||
c86ac772c8 | |||
52420963cf | |||
addf5f4e52 | |||
03d7300732 | |||
a97d0a36f4 | |||
e9c52ec696 | |||
75f2dab3c8 |
69
.github/ISSUE_TEMPLATE/rfc.yml
vendored
Normal file
69
.github/ISSUE_TEMPLATE/rfc.yml
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
# 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]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### Use this form to suggest a larger change for PolyMC.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Goal
|
||||
description: Short description, 1-2 sentences.
|
||||
placeholder: Remove the cat from the launcher.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Motivation
|
||||
description: |
|
||||
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!).
|
||||
- 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:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Specification
|
||||
description: A concrete, thorough explanation of what is being planned.
|
||||
placeholder: Remove the cat button and all references to the cat from the codebase. Including resource files.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Drawbacks
|
||||
description: Carefully consider every possible objection and issue with your proposal. This section should be updated as feedback comes in from discussion.
|
||||
placeholder: Some users might like cats.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Unresolved Questions
|
||||
description: |
|
||||
Are there any portions of your proposal which need to be discussed with the community before the RFC can proceed?
|
||||
Be careful here -- an RFC with a lot of remaining questions is likely to be stalled.
|
||||
If your RFC is mostly unresolved questions and not too much substance, it may not be ready.
|
||||
placeholder: Do a lot of users care about the cat?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Alternatives Considered
|
||||
description: A list of alternatives, that have been considered and offer equal or similar features to the proposed change.
|
||||
placeholder: Maybe the cat could be replaced with an axolotl?
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: This suggestion is unique
|
||||
options:
|
||||
- label: I have searched the issue tracker and did not find an issue describing my suggestion, especially not one that has been rejected.
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: You may use the editor below to elaborate further.
|
41
.github/scripts/prepare_JREs.sh
vendored
Executable file
41
.github/scripts/prepare_JREs.sh
vendored
Executable file
@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
URL_JDK8="https://api.adoptium.net/v3/binary/latest/8/ga/linux/x64/jre/hotspot/normal/eclipse"
|
||||
URL_JDK17="https://api.adoptium.net/v3/binary/latest/17/ga/linux/x64/jre/hotspot/normal/eclipse"
|
||||
|
||||
mkdir -p JREs
|
||||
pushd JREs
|
||||
|
||||
wget --content-disposition "$URL_JDK8"
|
||||
wget --content-disposition "$URL_JDK17"
|
||||
|
||||
for file in *;
|
||||
do
|
||||
mkdir temp
|
||||
|
||||
re='(OpenJDK([[:digit:]]+)U-jre_x64_linux_hotspot_([[:digit:]]+)(.*).tar.gz)'
|
||||
if [[ $file =~ $re ]];
|
||||
then
|
||||
version_major=${BASH_REMATCH[2]}
|
||||
version_trailing=${BASH_REMATCH[4]}
|
||||
|
||||
if [ $version_major = 17 ];
|
||||
then
|
||||
hyphen='-'
|
||||
else
|
||||
hyphen=''
|
||||
fi
|
||||
|
||||
version_edit=$(echo $version_trailing | sed -e 's/_/+/g' | sed -e 's/b/-b/g')
|
||||
dir_name=jdk$hyphen$version_major$version_edit-jre
|
||||
mkdir jre$version_major
|
||||
tar -xzf $file -C temp
|
||||
pushd temp/$dir_name
|
||||
cp -r . ../../jre$version_major
|
||||
popd
|
||||
fi
|
||||
|
||||
rm -rf temp
|
||||
done
|
||||
|
||||
popd
|
222
.github/workflows/build.yml
vendored
Normal file
222
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,222 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
build_type:
|
||||
description: Type of build (Debug, Release, RelWithDebInfo, MinSizeRel)
|
||||
type: string
|
||||
default: Debug
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
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
|
||||
qt_version: 5.15.2
|
||||
qt_host: windows
|
||||
qt_arch: win32_mingw81
|
||||
|
||||
- os: macos-11
|
||||
qt_version: 5.12.12
|
||||
qt_host: mac
|
||||
macosx_deployment_target: 10.12
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }}
|
||||
INSTALL_DIR: "install"
|
||||
BUILD_DIR: "build"
|
||||
|
||||
steps:
|
||||
- name: Install 32bit mingw on Windows
|
||||
if: runner.os == 'Windows'
|
||||
uses: egor-tensin/setup-mingw@v2
|
||||
with:
|
||||
platform: x86
|
||||
|
||||
- name: Install 32bit zlib via Strawberry on Windows
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
choco install strawberryperl -y --force --x86
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
# We need to do this here because it inexplicably fails if we split the step
|
||||
- name: Download and install OpenSSL libs on Windows
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install aqtinstall==2.0.5
|
||||
python -m aqt install-tool -O "${{ github.workspace }}\Qt\" windows desktop tools_openssl_x86
|
||||
mkdir ${{ env.INSTALL_DIR }}
|
||||
copy "${{ github.workspace }}\Qt\Tools\OpenSSL\Win_x86\bin\libssl-1_1.dll" "${{ github.workspace }}\${{ env.INSTALL_DIR }}\"
|
||||
copy "${{ github.workspace }}\Qt\Tools\OpenSSL\Win_x86\bin\libcrypto-1_1.dll" "${{ github.workspace }}\${{ env.INSTALL_DIR }}\"
|
||||
|
||||
- name: Set short version
|
||||
shell: bash
|
||||
run: |
|
||||
ver_short=`git rev-parse --short HEAD`
|
||||
echo "VERSION=$ver_short" >> $GITHUB_ENV
|
||||
|
||||
- name: Install OpenJDK
|
||||
uses: AdoptOpenJDK/install-jdk@v1
|
||||
with:
|
||||
version: '17'
|
||||
|
||||
- name: Cache Qt
|
||||
id: cache-qt
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "${{ github.workspace }}/Qt/"
|
||||
key: ${{ runner.os }}-${{ matrix.qt_version }}-${{ matrix.qt_arch }}-qt_cache
|
||||
|
||||
- name: Install Qt
|
||||
if: runner.os != 'Linux' || 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
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
|
||||
|
||||
- name: Install Ninja
|
||||
uses: urkle/action-get-ninja@v1
|
||||
|
||||
- name: Download linuxdeploy family for AppImage on Linux
|
||||
if: matrix.app_image == true
|
||||
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'
|
||||
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
|
||||
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
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cmake --build ${{ env.BUILD_DIR }}
|
||||
|
||||
- name: Install
|
||||
if: runner.os != 'Linux'
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }}
|
||||
|
||||
- name: Install on Linux
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
DESTDIR=${{ env.INSTALL_DIR }} cmake --install ${{ env.BUILD_DIR }}
|
||||
|
||||
- name: Bundle AppImage
|
||||
if: matrix.app_image == true
|
||||
shell: bash
|
||||
run: |
|
||||
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
|
||||
|
||||
cp -r ${{ github.workspace }}/JREs/jre8/* ${{ env.INSTALL_DIR }}/usr/lib/jvm/java-8-openjdk
|
||||
|
||||
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_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"
|
||||
|
||||
./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
|
||||
|
||||
- name: Run windeployqt
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
windeployqt --no-translations --no-system-d3d-compiler --no-opengl-sw "${{ env.INSTALL_DIR }}/polymc.exe"
|
||||
|
||||
- name: Run macdeployqt
|
||||
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@v2
|
||||
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@v2
|
||||
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: Upload package for Windows
|
||||
if: runner.os == 'Windows'
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: ${{ env.INSTALL_DIR }}/**
|
||||
|
||||
- name: Upload package for macOS
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PolyMC.tar.gz
|
32
.github/workflows/trigger_builds.yml
vendored
Normal file
32
.github/workflows/trigger_builds.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: Build Application
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'stable'
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**/LICENSE'
|
||||
- 'flake.lock'
|
||||
- '**.nix'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**/LICENSE'
|
||||
- 'flake.lock'
|
||||
- '**.nix'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
build_debug:
|
||||
name: Build Debug
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
build_type: Debug
|
||||
|
||||
build_release:
|
||||
name: Build Release
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
build_type: Release
|
99
.github/workflows/trigger_release.yml
vendored
Normal file
99
.github/workflows/trigger_release.yml
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
name: Build Application and Make Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
|
||||
build_release:
|
||||
name: Build Release
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
build_type: Release
|
||||
|
||||
create_release:
|
||||
needs: build_release
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
steps:
|
||||
- name: Grab and store version
|
||||
run: |
|
||||
tag_name=$(echo ${{ github.ref }} | grep -oE "[^/]+$")
|
||||
echo "VERSION=$tag_name" >> $GITHUB_ENV
|
||||
- name: Create release
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
name: PolyMC ${{ env.VERSION }}
|
||||
draft: true
|
||||
prerelease: false
|
||||
|
||||
upload_release:
|
||||
needs: create_release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
|
||||
- name: Grab and store version
|
||||
run: |
|
||||
tag_name=$(echo ${{ github.ref }} | grep -oE "[^/]+$")
|
||||
echo "VERSION=$tag_name" >> $GITHUB_ENV
|
||||
|
||||
- name: Package artifacts properly
|
||||
run: |
|
||||
mv PolyMC-Linux*/PolyMC.tar.gz PolyMC-Linux-${{ env.VERSION }}.tar.gz
|
||||
mv PolyMC-*.AppImage/PolyMC-*.AppImage PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||
mv PolyMC-Windows* PolyMC-Windows-${{ env.VERSION }}
|
||||
mv PolyMC-macOS*/PolyMC.tar.gz PolyMC-macOS-${{ env.VERSION }}.tar.gz
|
||||
|
||||
cd PolyMC-Windows-${{ env.VERSION }}
|
||||
zip -r -9 ../PolyMC-Windows-${{ env.VERSION }}.zip *
|
||||
cd ..
|
||||
|
||||
- name: Upload Linux asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create_release.outputs.upload_url }}
|
||||
asset_name: PolyMC-Linux-${{ env.VERSION }}.tar.gz
|
||||
asset_path: PolyMC-Linux-${{ env.VERSION }}.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
|
||||
- name: Upload Linux AppImage asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create_release.outputs.upload_url }}
|
||||
asset_name: PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||
asset_path: PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||
asset_content_type: application/x-executable
|
||||
|
||||
- name: Upload Windows asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create_release.outputs.upload_url }}
|
||||
asset_name: PolyMC-Windows-${{ env.VERSION }}.zip
|
||||
asset_path: PolyMC-Windows-${{ env.VERSION }}.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: Upload macOS asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create_release.outputs.upload_url }}
|
||||
asset_name: PolyMC-macOS-${{ env.VERSION }}.tar.gz
|
||||
asset_path: PolyMC-macOS-${{ env.VERSION }}.tar.gz
|
||||
asset_content_type: application/gzip
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -40,13 +40,5 @@ run/
|
||||
|
||||
.cache/
|
||||
|
||||
# Flatpak builds
|
||||
.flatpak-builder
|
||||
flatbuild
|
||||
builddir
|
||||
# Deb
|
||||
packages/debian/polymc/usr/
|
||||
packages/debian/polymc.deb
|
||||
packages/debian/polymc/DEBIAN/control
|
||||
# Nix/NixOS
|
||||
result/
|
||||
|
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -2,7 +2,7 @@
|
||||
path = libraries/libnbtplusplus
|
||||
url = https://github.com/MultiMC/libnbtplusplus.git
|
||||
pushurl = git@github.com:MultiMC/libnbtplusplus.git
|
||||
|
||||
[submodule "libraries/quazip"]
|
||||
path = libraries/quazip
|
||||
url = https://github.com/PolyMC/quazip.git
|
||||
pushurl = git@github.com:PolyMC/quazip.git
|
||||
path = libraries/quazip
|
||||
url = https://github.com/stachenov/quazip.git
|
||||
|
245
BUILD.md
245
BUILD.md
@ -1,244 +1,5 @@
|
||||
Build Instructions
|
||||
==================
|
||||
# Build Instructions
|
||||
|
||||
# Contents
|
||||
Build instructions are available on [the website](https://polymc.org/wiki/development/build-instructions/).
|
||||
|
||||
* [Note](#note)
|
||||
* [Getting the source](#source)
|
||||
* [Linux](#linux)
|
||||
* [Windows](#windows)
|
||||
* [macOS](#macos)
|
||||
|
||||
|
||||
# Getting the source
|
||||
|
||||
Clone the source code using git and grab all the submodules:
|
||||
|
||||
```
|
||||
git clone https://github.com/PolyMC/PolyMC.git
|
||||
git submodule init
|
||||
git submodule update
|
||||
```
|
||||
|
||||
# Linux
|
||||
|
||||
Getting the project to build and run on Linux is easy if you use any modern and up-to-date linux distribution.
|
||||
|
||||
## Build dependencies
|
||||
* A C++ compiler capable of building C++11 code.
|
||||
* Qt Development tools 5.6 or newer (`qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5` on Debian-based system)
|
||||
* cmake 3.1 or newer (`cmake` on Debian-based system)
|
||||
* zlib (`zlib1g-dev` on Debian-based system)
|
||||
* Java JDK (`openjdk-17-jdk`on Debian-based system)
|
||||
* GL headers (`libgl1-mesa-dev` on Debian-based system)
|
||||
|
||||
### Building a .deb
|
||||
|
||||
You need to install the build dependencies first
|
||||
|
||||
```
|
||||
git clone https://github.com/PolyMC/PolyMC.git
|
||||
git submodule init && git submodule update
|
||||
cd packages/debian
|
||||
./makedeb.sh
|
||||
```
|
||||
|
||||
If everything works correctly, the .deb will be next to the build script, in `PolyMC/packages/debian`
|
||||
|
||||
### Building a .rpm
|
||||
|
||||
You don't need to install the build dependencies, as the script will use `dnf` to install them for you.
|
||||
|
||||
```
|
||||
git clone https://github.com/PolyMC/PolyMC.git
|
||||
cd packages/rpm
|
||||
./makerpm.sh
|
||||
```
|
||||
|
||||
If everything works correctly, the .rpm will be next to the build script, in `PolyMC/packages/rpm`
|
||||
|
||||
### Building from command line
|
||||
You need a source folder, a build folder and an install folder.
|
||||
|
||||
```
|
||||
# make all the folders
|
||||
mkdir ~/PolyMC && cd ~/PolyMC
|
||||
mkdir build
|
||||
mkdir install
|
||||
# clone the complete source
|
||||
git clone --recursive https://github.com/PolyMC/PolyMC.git src
|
||||
# configure the project
|
||||
cd build
|
||||
cmake -DCMAKE_INSTALL_PREFIX=../install ../src
|
||||
make -j$(nproc) install
|
||||
```
|
||||
|
||||
You can use IDEs like KDevelop or QtCreator to open the CMake project if you want to work on the code.
|
||||
|
||||
### Building & Installing to the System
|
||||
This is the preferred method for installation, and is suitable for packages.
|
||||
|
||||
```
|
||||
git clone --recursive https://github.com/PolyMC/PolyMC.git && cd PolyMC
|
||||
|
||||
# configure everything
|
||||
cmake -S . -B build \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \ # Use "/usr" for packages, otherwise, leave it at the default "/usr/local".
|
||||
-DLauncher_LAYOUT=lin-system
|
||||
|
||||
make -j$(nproc) install # Optionally specify DESTDIR for packages (i.e. DESTDIR=${pkgdir})
|
||||
```
|
||||
|
||||
### Installing Qt using the installer (optional)
|
||||
1. Run the Qt installer.
|
||||
2. Choose a place to install Qt.
|
||||
3. Choose the components you want to install.
|
||||
- You need Qt 5.6.x 64-bit ticked.
|
||||
- You need Tools/Qt Creator ticked.
|
||||
- Other components are selected by default, you can untick them if you don't need them.
|
||||
4. Accept the license agreements.
|
||||
5. Double check the install details and then click "Install".
|
||||
- Installation can take a very long time, go grab a cup of tea or something and let it work.
|
||||
|
||||
### Loading the project in Qt Creator (optional)
|
||||
1. Open Qt Creator.
|
||||
2. Choose `File->Open File or Project`.
|
||||
3. Navigate to the Launcher source folder you cloned and choose CMakeLists.txt.
|
||||
4. Read the instructions that just popped up about a build location and choose one.
|
||||
5. You should see "Run CMake" in the window.
|
||||
- Make sure that Generator is set to "Unix Generator (Desktop Qt 5.6.x GCC 64bit)".
|
||||
- Hit the "Run CMake" button.
|
||||
- You'll see warnings and it might not be clear that it succeeded until you scroll to the bottom of the window.
|
||||
- Hit "Finish" if CMake ran successfully.
|
||||
6. Cross your fingers and press the Run button (bottom left of Qt Creator).
|
||||
- If the project builds successfully it will run and the Launcher window will pop up.
|
||||
|
||||
**If this doesn't work for you, let us know on our Discord.**
|
||||
|
||||
# Windows
|
||||
|
||||
Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt Creator. The project will simply not compile using Microsoft build tools, because that's not something we do. If it does compile, it is by chance only.
|
||||
|
||||
## Dependencies
|
||||
* [Qt 5.6+ Development tools](http://qt-project.org/downloads) -- Qt Online Installer for Windows
|
||||
- http://download.qt.io/new_archive/qt/5.6/5.6.0/qt-opensource-windows-x86-mingw492-5.6.0.exe
|
||||
- Download the MinGW version (MSVC version does not work).
|
||||
* [OpenSSL](https://github.com/IndySockets/OpenSSL-Binaries/tree/master/Archive/) -- Win32 OpenSSL, version 1.0.2g (from 2016)
|
||||
- https://github.com/IndySockets/OpenSSL-Binaries/raw/master/Archive/openssl-1.0.2g-i386-win32.zip
|
||||
- the usual OpenSSL for Windows (http://slproweb.com/products/Win32OpenSSL.html) only provides the newest version of OpenSSL, and we need the 1.0.2g version
|
||||
- **Download the 32-bit version, not 64-bit.**
|
||||
- Microsoft Visual C++ 2008 Redist is required for this, there's a link on the OpenSSL download page above next to the main download.
|
||||
- We use a custom build of OpenSSL that doesn't have this dependency. For normal development, the custom build is not necessary though.
|
||||
* [zlib 1.2+](http://gnuwin32.sourceforge.net/packages/zlib.htm) - the Setup is fine
|
||||
* [Java JDK 8](https://adoptium.net/releases.html?variant=openjdk8) - Use the MSI installer.
|
||||
* [CMake](http://www.cmake.org/cmake/resources/software.html) -- Windows (Win32 Installer)
|
||||
|
||||
Ensure that OpenSSL, zlib, Java and CMake are on `PATH`.
|
||||
|
||||
## Getting set up
|
||||
|
||||
### Installing Qt
|
||||
1. Run the Qt installer
|
||||
2. Choose a place to install Qt (C:\Qt is the default),
|
||||
3. Choose the components you want to install
|
||||
- You need Qt 5.6 (32 bit) ticked,
|
||||
- You need Tools/Qt Creator ticked,
|
||||
- Other components are selected by default, you can untick them if you don't need them.
|
||||
4. Accept the license agreements,
|
||||
5. Double check the install details and then click "Install"
|
||||
- Installation can take a very long time, go grab a cup of tea or something and let it work.
|
||||
|
||||
### Installing OpenSSL
|
||||
1. Download .zip file from the link above.
|
||||
2. Unzip and add the directory to PATH, so CMake can find it.
|
||||
|
||||
### Installing CMake
|
||||
1. Run the CMake installer,
|
||||
2. It's easiest if you choose to add CMake to the PATH for all users,
|
||||
- If you don't choose to do this, remember where you installed CMake.
|
||||
|
||||
### Loading the project
|
||||
1. Open Qt Creator,
|
||||
2. Choose File->Open File or Project,
|
||||
3. Navigate to the Launcher source folder you cloned and choose CMakeLists.txt,
|
||||
4. Read the instructions that just popped up about a build location and choose one,
|
||||
5. If you chose not to add CMake to the system PATH, tell Qt Creator where you installed it,
|
||||
- Otherwise you can skip this step.
|
||||
6. You should see "Run CMake" in the window,
|
||||
- Make sure that Generator is set to "MinGW Generator (Desktop Qt 5.6.x MinGW 32bit)",
|
||||
- Hit the "Run CMake" button,
|
||||
- You'll see warnings and it might not be clear that it succeeded until you scroll to the bottom of the window.
|
||||
- Hit "Finish" if CMake ran successfully.
|
||||
7. Cross your fingers and press the Run button (bottom left of Qt Creator)!
|
||||
- If the project builds successfully it will run and the Launcher window will pop up,
|
||||
- Test OpenSSL by making an instance and trying to log in. If Qt Creator couldn't find OpenSSL during the CMake stage, login will fail and you'll get an error.
|
||||
|
||||
The following .dlls are needed for the app to run (copy them to build directory if you want to be able to move the build to another pc):
|
||||
```
|
||||
platforms/qwindows.dll
|
||||
libeay32.dll
|
||||
libgcc_s_dw2-1.dll
|
||||
libssp-0.dll
|
||||
libstdc++-6.dll
|
||||
libwinpthread-1.dll
|
||||
Qt5Core.dll
|
||||
Qt5Gui.dll
|
||||
Qt5Network.dll
|
||||
Qt5Svg.dll
|
||||
Qt5Widgets.dll
|
||||
Qt5Xml.dll
|
||||
ssleay32.dll
|
||||
zlib1.dll
|
||||
```
|
||||
|
||||
**These build instructions worked for me (Drayshak) on a fresh Windows 8 x64 Professional install. If they don't work for you, let us know on our Discord.**
|
||||
### Compile from command line on Windows
|
||||
1. If you installed Qt with the web installer, there should be a shortcut called `Qt 5.4 for Desktop (MinGW 4.9 32-bit)` in the Start menu on Windows 7 and 10. Best way to find it is to search for it. Do note you cannot just use cmd.exe, you have to use the shortcut, otherwise the proper MinGW software will not be on the PATH.
|
||||
2. Once that is open, change into your user directory, and clone PolyMC by doing `git clone --recursive https://github.com/PolyMC/PolyMC.git`, and change directory to the folder you cloned to.
|
||||
3. Make a build directory, and change directory to the directory and do `cmake -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=C:\Path\that\makes\sense\for\you`. By default, it will install to C:\Program Files (x86), which you might not want, if you want a local installation. If you want to install it to that directory, make sure to run the command window as administrator.
|
||||
3. Do `mingw32-make -jX`, where X is the number of cores your CPU has plus one.
|
||||
4. Now to wait for it to compile. This could take some time. Hopefully it compiles properly.
|
||||
5. Run the command `mingw32-make install`, and it should install PolyMC, to whatever the `-DCMAKE_INSTALL_PREFIX` was.
|
||||
6. In most cases, whenever compiling, the OpenSSL dll's aren't put into the directory to where PolyMC installs, meaning you cannot log in. The best way to fix this is just to do `copy C:\OpenSSL-Win32\*.dll C:\Where\you\installed\PolyMC\to`. This should copy the required OpenSSL dll's to log in.
|
||||
|
||||
# macOS
|
||||
|
||||
### Install prerequisites:
|
||||
- Install XCode Command Line tools
|
||||
- Install the official build of CMake (https://cmake.org/download/)
|
||||
- Install JDK 8 (https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html)
|
||||
- Get Qt 5.6 and install it (https://download.qt.io/new_archive/qt/5.6/5.6.3/)
|
||||
|
||||
### XCode Command Line tools
|
||||
|
||||
If you don't have XCode CommandLine tools installed, you can install them by using this command in the Terminal App
|
||||
|
||||
```bash
|
||||
xcode-select --install
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
Pick an installation path - this is where the final `.app` will be constructed when you run `make install`. Supply it as the `CMAKE_INSTALL_PREFIX` argument during CMake configuration.
|
||||
|
||||
```
|
||||
git clone --recursive https://github.com/PolyMC/PolyMC.git
|
||||
cd Launcher
|
||||
mkdir build
|
||||
cd build
|
||||
cmake \
|
||||
-DCMAKE_C_COMPILER=/usr/bin/clang \
|
||||
-DCMAKE_CXX_COMPILER=/usr/bin/clang++ \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX:PATH="$(dirname $PWD)/dist/" \
|
||||
-DCMAKE_PREFIX_PATH="/path/to/Qt5.6/" \
|
||||
-DQt5_DIR="/path/to/Qt5.6/" \
|
||||
-DLauncher_LAYOUT=mac-bundle \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.7 \
|
||||
..
|
||||
make install
|
||||
```
|
||||
|
||||
**Note:** The final app bundle may not run due to code signing issues, which
|
||||
need to be fixed with `codesign -fs -`.
|
||||
If you would like to contribute or fix an issue with the Build instructions you can do so [here](https://github.com/PolyMC/polymc.github.io/blob/master/src/wiki/development/build-instructions.md).
|
||||
|
145
CMakeLists.txt
145
CMakeLists.txt
@ -46,12 +46,14 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y")
|
||||
##################################### Set Application options #####################################
|
||||
|
||||
######## Set URLs ########
|
||||
set(Launcher_NEWS_RSS_URL "https://multimc.org/rss.xml" CACHE STRING "URL to fetch Launcher's news RSS feed from.")
|
||||
set(Launcher_NEWS_RSS_URL "https://polymc.org/feed/feed.xml" CACHE STRING "URL to fetch PolyMC's news RSS feed from.")
|
||||
set(Launcher_NEWS_OPEN_URL "https://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 version numbers ########
|
||||
set(Launcher_VERSION_MAJOR 1)
|
||||
set(Launcher_VERSION_MINOR 0)
|
||||
set(Launcher_VERSION_HOTFIX 5)
|
||||
set(Launcher_VERSION_MINOR 1)
|
||||
set(Launcher_VERSION_HOTFIX 0)
|
||||
|
||||
# Build number
|
||||
set(Launcher_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
|
||||
@ -66,26 +68,37 @@ set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
|
||||
set(Launcher_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.")
|
||||
|
||||
# The metadata server
|
||||
set(Launcher_META_URL "https://meta.multimc.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
|
||||
|
||||
# paste.ee API key
|
||||
set(Launcher_PASTE_EE_API_KEY "utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ" CACHE STRING "API key you can get from paste.ee when you register an account")
|
||||
set(Launcher_META_URL "https://meta.polymc.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
|
||||
|
||||
# Imgur API Client ID
|
||||
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
|
||||
|
||||
# MSA Client ID
|
||||
set(Launcher_MSA_CLIENT_ID "17b47edd-c884-4997-926d-9e7f9a6b4647" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
|
||||
set(Launcher_MSA_CLIENT_ID "549033b2-1532-4d4e-ae77-1bbaa46f9d74" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
|
||||
|
||||
# Bug tracker URL
|
||||
set(Launcher_BUG_TRACKER_URL "https://github.com/PolyMC/PolyMC/issues" CACHE STRING "URL for the bug tracker.")
|
||||
|
||||
# Translations Platform URL
|
||||
set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/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")
|
||||
|
||||
# Discord URL
|
||||
set(Launcher_DISCORD_URL "https://discord.gg/Z52pwxWCHP" CACHE STRING "URL for the Discord guild.")
|
||||
|
||||
|
||||
|
||||
# Subreddit URL
|
||||
set(Launcher_SUBREDDIT_URL "" 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_QT_VERSION_MAJOR "5" CACHE STRING "Major Qt version to build against")
|
||||
|
||||
|
||||
#### Check the current Git commit and branch
|
||||
include(GetGitRevisionDescription)
|
||||
get_git_head_revision(Launcher_GIT_REFSPEC Launcher_GIT_COMMIT)
|
||||
@ -94,6 +107,8 @@ message(STATUS "Git commit: ${Launcher_GIT_COMMIT}")
|
||||
message(STATUS "Git refspec: ${Launcher_GIT_REFSPEC}")
|
||||
|
||||
set(Launcher_RELEASE_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
||||
string(TIMESTAMP TODAY "%Y-%m-%d")
|
||||
set(Launcher_RELEASE_TIMESTAMP "${TODAY}")
|
||||
|
||||
#### Custom target to just print the version.
|
||||
add_custom_target(version echo "Version: ${Launcher_RELEASE_VERSION_NAME}")
|
||||
@ -102,12 +117,20 @@ add_custom_target(tcversion echo "\\#\\#teamcity[setParameter name=\\'env.LAUNCH
|
||||
################################ 3rd Party Libs ################################
|
||||
|
||||
# Find the required Qt parts
|
||||
find_package(Qt5Core REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
find_package(Qt5Concurrent REQUIRED)
|
||||
find_package(Qt5Network REQUIRED)
|
||||
find_package(Qt5Test REQUIRED)
|
||||
find_package(Qt5Xml REQUIRED)
|
||||
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
||||
set(QT_VERSION_MAJOR 5)
|
||||
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml)
|
||||
|
||||
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
||||
find_package(QuaZip-Qt5 REQUIRED)
|
||||
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)
|
||||
set(FORCE_BUNDLED_QUAZIP 1)
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported")
|
||||
endif()
|
||||
|
||||
# The Qt5 cmake files don't provide its install paths, so ask qmake.
|
||||
include(QMakeQuery)
|
||||
@ -129,25 +152,11 @@ add_subdirectory(program_info)
|
||||
|
||||
####################################### Install layout #######################################
|
||||
|
||||
# How to install the build results
|
||||
set(Launcher_LAYOUT "auto" CACHE STRING "The layout for the launcher installation (auto, win-bundle, lin-nodeps, lin-system, mac-bundle)")
|
||||
set_property(CACHE Launcher_LAYOUT PROPERTY STRINGS auto win-bundle lin-nodeps lin-system mac-bundle)
|
||||
# Install the build results according to platform
|
||||
set(Launcher_PORTABLE 1 CACHE BOOL "The type of installation (Portable or System)")
|
||||
|
||||
if(Launcher_LAYOUT STREQUAL "auto")
|
||||
if(UNIX AND APPLE)
|
||||
set(Launcher_LAYOUT_REAL "mac-bundle")
|
||||
elseif(UNIX)
|
||||
set(Launcher_LAYOUT_REAL "lin-nodeps")
|
||||
elseif(WIN32)
|
||||
set(Launcher_LAYOUT_REAL "win-bundle")
|
||||
else()
|
||||
message(FATAL_ERROR "Cannot choose a sensible install layout for your platform.")
|
||||
endif()
|
||||
else()
|
||||
set(Launcher_LAYOUT_REAL ${Launcher_LAYOUT})
|
||||
endif()
|
||||
|
||||
if(Launcher_LAYOUT_REAL STREQUAL "mac-bundle")
|
||||
if(UNIX AND APPLE)
|
||||
set(BINARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
|
||||
set(LIBRARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
|
||||
set(PLUGIN_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
|
||||
@ -162,7 +171,7 @@ if(Launcher_LAYOUT_REAL STREQUAL "mac-bundle")
|
||||
# Mac bundle settings
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_Name}")
|
||||
set(MACOSX_BUNDLE_INFO_STRING "${Launcher_Name}: Minecraft launcher and management utility.")
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.multimc.${Launcher_Name}")
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.polymc.${Launcher_Name}")
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}.${Launcher_VERSION_BUILD}")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}.${Launcher_VERSION_BUILD}")
|
||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}.${Launcher_VERSION_BUILD}")
|
||||
@ -178,13 +187,32 @@ if(Launcher_LAYOUT_REAL STREQUAL "mac-bundle")
|
||||
# Add the icon
|
||||
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
|
||||
|
||||
elseif(Launcher_LAYOUT_REAL STREQUAL "lin-nodeps")
|
||||
elseif(UNIX)
|
||||
set(BINARY_DEST_DIR "bin")
|
||||
set(LIBRARY_DEST_DIR "bin")
|
||||
set(PLUGIN_DEST_DIR "plugins")
|
||||
set(BUNDLE_DEST_DIR ".")
|
||||
set(RESOURCES_DEST_DIR ".")
|
||||
set(JARS_DEST_DIR "bin/jars")
|
||||
if(Launcher_PORTABLE)
|
||||
set(LIBRARY_DEST_DIR "bin")
|
||||
set(BUNDLE_DEST_DIR ".")
|
||||
set(JARS_DEST_DIR "bin/jars")
|
||||
|
||||
# launcher/Application.cpp will use this value
|
||||
set(Launcher_APP_BINARY_DEFS "-DLAUNCHER_PORTABLE")
|
||||
|
||||
# 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")
|
||||
|
||||
set(Launcher_APP_BINARY_DEFS "-DMULTIMC_JARS_LOCATION=${CMAKE_INSTALL_PREFIX}/${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()
|
||||
|
||||
# install as bundle with no dependencies included
|
||||
set(INSTALL_BUNDLE "nodeps")
|
||||
@ -192,33 +220,7 @@ elseif(Launcher_LAYOUT_REAL STREQUAL "lin-nodeps")
|
||||
# Set RPATH
|
||||
SET(Launcher_BINARY_RPATH "$ORIGIN/")
|
||||
|
||||
# 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})
|
||||
|
||||
elseif(Launcher_LAYOUT_REAL STREQUAL "lin-system")
|
||||
set(Launcher_BINARY_DEST_DIR "bin" CACHE STRING "Path to the binary directory")
|
||||
set(Launcher_LIBRARY_DEST_DIR "lib${LIB_SUFFIX}" CACHE STRING "Path to the library directory")
|
||||
set(Launcher_SHARE_DEST_DIR "share/polymc" CACHE STRING "Path to the shared data directory")
|
||||
set(JARS_DEST_DIR "${Launcher_SHARE_DEST_DIR}/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(BINARY_DEST_DIR ${Launcher_BINARY_DEST_DIR})
|
||||
set(LIBRARY_DEST_DIR ${Launcher_LIBRARY_DEST_DIR})
|
||||
|
||||
MESSAGE(STATUS "Compiling for linux system with ${Launcher_SHARE_DEST_DIR} and LAUNCHER_LINUX_DATADIR")
|
||||
SET(Launcher_APP_BINARY_DEFS "-DMULTIMC_JARS_LOCATION=${CMAKE_INSTALL_PREFIX}/${JARS_DEST_DIR}" "-DLAUNCHER_LINUX_DATADIR")
|
||||
|
||||
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 as bundle with no dependencies included
|
||||
set(INSTALL_BUNDLE "nodeps")
|
||||
|
||||
elseif(Launcher_LAYOUT_REAL STREQUAL "win-bundle")
|
||||
elseif(WIN32)
|
||||
set(BINARY_DEST_DIR ".")
|
||||
set(LIBRARY_DEST_DIR ".")
|
||||
set(PLUGIN_DEST_DIR ".")
|
||||
@ -235,7 +237,7 @@ elseif(Launcher_LAYOUT_REAL STREQUAL "win-bundle")
|
||||
# install as bundle
|
||||
set(INSTALL_BUNDLE "full")
|
||||
else()
|
||||
message(FATAL_ERROR "No sensible install layout set.")
|
||||
message(FATAL_ERROR "Platform not supported")
|
||||
endif()
|
||||
|
||||
################################ Included Libs ################################
|
||||
@ -243,11 +245,9 @@ endif()
|
||||
include(ExternalProject)
|
||||
set_directory_properties(PROPERTIES EP_BASE External)
|
||||
|
||||
option(NBT_BUILD_SHARED "Build NBT shared library" ON)
|
||||
option(NBT_BUILD_SHARED "Build NBT shared library" OFF)
|
||||
option(NBT_USE_ZLIB "Build NBT library with zlib support" OFF)
|
||||
option(NBT_BUILD_TESTS "Build NBT library tests" OFF) #FIXME: fix unit tests.
|
||||
set(NBT_NAME Launcher_nbt++)
|
||||
set(NBT_DEST_DIR ${LIBRARY_DEST_DIR})
|
||||
add_subdirectory(libraries/libnbtplusplus)
|
||||
|
||||
add_subdirectory(libraries/systeminfo) # system information library
|
||||
@ -255,7 +255,12 @@ add_subdirectory(libraries/hoedown) # markdown parser
|
||||
add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
|
||||
add_subdirectory(libraries/javacheck) # java compatibility checker
|
||||
add_subdirectory(libraries/xz-embedded) # xz compression
|
||||
add_subdirectory(libraries/quazip) # zip manipulation library
|
||||
if (FORCE_BUNDLED_QUAZIP)
|
||||
message(STATUS "Using bundled QuaZip")
|
||||
set(BUILD_SHARED_LIBS 0) # link statically to avoid conflicts.
|
||||
set(QUAZIP_INSTALL 0)
|
||||
add_subdirectory(libraries/quazip) # zip manipulation library
|
||||
endif()
|
||||
add_subdirectory(libraries/rainbow) # Qt extension for colors
|
||||
add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader
|
||||
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
|
||||
|
136
CODE_OF_CONDUCT.md
Normal file
136
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,136 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
This is a modified version of the Contributor Covenant.
|
||||
See commit history to see our changes.
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
* Trolling (antagonistic, inflammatory, insincere behaviour), insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement via email at
|
||||
[polymc-enforcement@scrumplex.net](mailto:polymc-enforcement@scrumplex.net) (Email
|
||||
address subject to change).
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of
|
||||
actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.1, available at
|
||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||
[https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
|
16
COPYING.md
16
COPYING.md
@ -1,7 +1,7 @@
|
||||
# PolyMC
|
||||
|
||||
Copyright (C) 2012-2021 MultiMC Contributors
|
||||
Copyright (C) 2021 PolyMC Contributors
|
||||
Copyright (C) 2021-2022 PolyMC Contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -16,6 +16,20 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# Launcher (https://github.com/MultiMC/Launcher)
|
||||
Copyright 2012-2021 MultiMC Contributors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
# MinGW runtime (Windows)
|
||||
|
||||
Copyright (c) 2012 MinGW.org project
|
||||
|
84
README.md
84
README.md
@ -1,55 +1,32 @@
|
||||
<p align="center">
|
||||
<img src="/program_info/polymc-light.png#gh-light-mode-only" alt="PolyMC logo"/>
|
||||
<img src="/program_info/polymc-dark.png#gh-dark-mode-only" alt="PolyMC logo"/>
|
||||
<img src="./program_info/polymc-header-black.svg#gh-light-mode-only" alt="PolyMC logo"/>
|
||||
<img src="./program_info/polymc-header.svg#gh-dark-mode-only" alt="PolyMC logo"/>
|
||||
</p>
|
||||
<br>
|
||||
|
||||
PolyMC is a custom launcher for Minecraft that focuses on predictability, long term stability and simplicity.
|
||||
|
||||
This is a **fork** of the MultiMC Launcher and not endorsed by MultiMC. The PolyMC community felt that the maintainer was not acting in the spirit of Free Software so this fork was made. Read "[Why was this fork made?](https://github.com/PolyMC/PolyMC/wiki/FAQ)" on the wiki for more details.
|
||||
|
||||
## Packages
|
||||
Several source build packages are available, along with experimental pre-built generic packages.
|
||||
|
||||
<a href='https://flathub.org/apps/details/org.polymc.PolyMC'><img width='240' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/></a>
|
||||
This is a **fork** of the MultiMC Launcher and not endorsed by MultiMC. The PolyMC community felt that the maintainer was not acting in the spirit of Free Software so this fork was made.
|
||||
<br>
|
||||
[](https://aur.archlinux.org/packages/polymc-git/)
|
||||
- A [Nix](packages/nix/NIX.md) derivation is available in repo.
|
||||
- A Gentoo ebuild is available in the [swirl](https://git.swurl.xyz/swirl/ebuilds) overlay, named `games-action/polymc`. Check the README for instructions on how to add the overlay.
|
||||
- The Flatpak can be built using [this source](https://github.com/flathub/org.polymc.PolyMC).
|
||||
- An RPM package is available on [COPR](https://copr.fedorainfracloud.org/coprs/sentry/polymc/), or can be built by going to the `packages/rpm` directory and running `rpmbuild -bb polymc.spec`.
|
||||
- Generic, prebuilt packages (archived by version) can be found [here](https://packages.polymc.org/) ([latest](https://packages.polymc.org/latest)).
|
||||
- Last build status: https://jenkins.polymc.org/job/PolyMC/lastBuild/
|
||||
- [Linux (AMD64) System](https://packages.polymc.org/latest/lin64-system/lin64-system.tar.zst) ([SHA256](https://packages.polymc.org/latest/lin64-system/lin64-system.tar.zst.sha256)) - this is a generic system package intended to be used as a base for making distro-specific packages.
|
||||
- [Windows (32-bit)](https://packages.polymc.org/latest/win32/win32.zip) ([SHA256](https://packages.polymc.org/latest/win32/win32.zip.sha256)) - this is a portable package, you can extract it anywhere and run it. This package needs testing.
|
||||
- [Debian (AMD64)](https://packages.polymc.org/latest/deb/polymc-amd64.deb) ([SHA256](https://packages.polymc.org/latest/deb/polymc-amd64.deb.sha256)) - this is intended to be installed with `dpkg -i`. Alternatively, you may build the `.deb` yourself, by going to `packages/debian` and running `./makedeb.sh`.
|
||||
- [AppImage (AMD64)](https://packages.polymc.org/latest/appimage/PolyMC-latest-x86_64.AppImage) ([SHA256](https://packages.polymc.org/latest/appimage/PolyMC-latest-x86_64.AppImage.sha256)) - `chmod +x` must be run on this file before usage. This should work on any distribution.
|
||||
- MacOS currently does not have any packages. We are still working on setting up MacOS packaging.
|
||||
|
||||
## Development
|
||||
If you want to contribute to PolyMC you might find it useful to join [#development:polymc.org on Matrix](https://matrix.to/#/#development:polymc.org) or join [our Discord server](https://discord.gg/xq7fxrgtMP), which is bridged with the PolyMC Matrix rooms. Thank you!
|
||||
# Installation
|
||||
|
||||
### Building
|
||||
If you want to build PolyMC yourself, check [BUILD.md](BUILD.md) for build instructions.
|
||||
- All downloads and instructions for PolyMC can be found [here](https://polymc.org/download/)
|
||||
- Last build status: https://github.com/PolyMC/PolyMC/actions
|
||||
|
||||
You can build the flatpak using [this source](https://github.com/flathub/org.polymc.PolyMC).
|
||||
|
||||
### Code formatting
|
||||
Just follow the existing formatting.
|
||||
## Development Builds
|
||||
|
||||
In general, in order of importance:
|
||||
* Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
|
||||
* Prefer readability over dogma.
|
||||
* Keep to the existing formatting.
|
||||
* Indent with 4 space unless it's in a submodule.
|
||||
* Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
|
||||
There are per-commit development builds available [here](https://github.com/PolyMC/PolyMC/actions). These have debug information in the binaries, so their file sizes are relatively larger.
|
||||
Portable builds are provided for AppImage on Linux, Windows, and macOS.
|
||||
|
||||
## Translations
|
||||
TODO
|
||||
For Debian and Arch, you can use these packages for the latest development versions:
|
||||
[](https://aur.archlinux.org/packages/polymc-git/)
|
||||
[](https://mpr.makedeb.org/packages/polymc-git)
|
||||
For flatpak, you can use [flathub-beta](https://discourse.flathub.org/t/how-to-use-flathub-beta/2111)
|
||||
|
||||
## 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.
|
||||
# Help & Support
|
||||
|
||||
## Help & Support
|
||||
Feel free to create an issue if you need help. However, you might find it easier to ask in the Discord server.
|
||||
|
||||
[](https://discord.gg/xq7fxrgtMP)
|
||||
@ -64,3 +41,34 @@ If there are any issues with the space or you are using a client that does not s
|
||||
[](https://matrix.to/#/#discussion:polymc.org)
|
||||
[](https://matrix.to/#/#development:polymc.org)
|
||||
[](https://matrix.to/#/#news:polymc.org)
|
||||
|
||||
# Development
|
||||
|
||||
If you want to contribute to PolyMC you might find it useful to join our Discord Server or Matrix Space.
|
||||
|
||||
## Building
|
||||
|
||||
If you want to build PolyMC yourself, check [BUILD.md](BUILD.md) for build instructions.
|
||||
|
||||
## Code formatting
|
||||
|
||||
Just follow the existing formatting.
|
||||
|
||||
In general, in order of importance:
|
||||
|
||||
- Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
|
||||
- Prefer readability over dogma.
|
||||
- Keep to the existing formatting.
|
||||
- Indent with 4 space unless it's in a submodule.
|
||||
- Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
|
||||
|
||||
## Translations
|
||||
|
||||
The translation effort for PolyMC is hosted on [Weblate](https://hosted.weblate.org/projects/polymc/polymc/) and information about translating PolyMC is available at https://github.com/PolyMC/Translations
|
||||
|
||||
## Download 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)
|
||||
|
||||
## 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.
|
||||
|
@ -12,6 +12,7 @@ Config::Config()
|
||||
LAUNCHER_DOMAIN = "@Launcher_Domain@";
|
||||
LAUNCHER_CONFIGFILE = "@Launcher_ConfigFile@";
|
||||
LAUNCHER_GIT = "@Launcher_Git@";
|
||||
LAUNCHER_DESKTOPFILENAME = "@Launcher_DesktopFileName@";
|
||||
|
||||
USER_AGENT = "@Launcher_UserAgent@";
|
||||
USER_AGENT_UNCACHED = USER_AGENT + " (Uncached)";
|
||||
@ -29,25 +30,34 @@ Config::Config()
|
||||
|
||||
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
||||
GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";
|
||||
if(GIT_REFSPEC.startsWith("refs/heads/") && !UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty() && VERSION_BUILD >= 0)
|
||||
if(GIT_REFSPEC.startsWith("refs/heads/"))
|
||||
{
|
||||
VERSION_CHANNEL = GIT_REFSPEC;
|
||||
VERSION_CHANNEL.remove("refs/heads/");
|
||||
UPDATER_ENABLED = true;
|
||||
if(!UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty() && VERSION_BUILD >= 0) {
|
||||
UPDATER_ENABLED = true;
|
||||
}
|
||||
}
|
||||
else if (!GIT_COMMIT.isEmpty())
|
||||
{
|
||||
VERSION_CHANNEL = GIT_COMMIT.mid(0, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
VERSION_CHANNEL = QObject::tr("custom");
|
||||
VERSION_CHANNEL = QObject::tr("unknown");
|
||||
}
|
||||
|
||||
VERSION_STR = "@Launcher_VERSION_STRING@";
|
||||
NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
|
||||
PASTE_EE_KEY = "@Launcher_PASTE_EE_API_KEY@";
|
||||
NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@";
|
||||
HELP_URL = "@Launcher_HELP_URL@";
|
||||
IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
|
||||
MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@";
|
||||
META_URL = "@Launcher_META_URL@";
|
||||
|
||||
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
|
||||
TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@";
|
||||
MATRIX_URL = "@Launcher_MATRIX_URL@";
|
||||
DISCORD_URL = "@Launcher_DISCORD_URL@";
|
||||
SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@";
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ public:
|
||||
QString LAUNCHER_DOMAIN;
|
||||
QString LAUNCHER_CONFIGFILE;
|
||||
QString LAUNCHER_GIT;
|
||||
QString LAUNCHER_DESKTOPFILENAME;
|
||||
|
||||
/// The major version number.
|
||||
int VERSION_MAJOR;
|
||||
@ -68,9 +69,14 @@ public:
|
||||
QString NEWS_RSS_URL;
|
||||
|
||||
/**
|
||||
* API key you can get from paste.ee when you register an account
|
||||
* URL that gets opened when the user clicks "More News"
|
||||
*/
|
||||
QString PASTE_EE_KEY;
|
||||
QString NEWS_OPEN_URL;
|
||||
|
||||
/**
|
||||
* URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help
|
||||
*/
|
||||
QString HELP_URL;
|
||||
|
||||
/**
|
||||
* Client ID you can get from Imgur when you register an application
|
||||
@ -88,6 +94,8 @@ public:
|
||||
QString META_URL;
|
||||
|
||||
QString BUG_TRACKER_URL;
|
||||
QString TRANSLATIONS_URL;
|
||||
QString MATRIX_URL;
|
||||
QString DISCORD_URL;
|
||||
QString SUBREDDIT_URL;
|
||||
|
||||
@ -95,8 +103,8 @@ public:
|
||||
QString LIBRARY_BASE = "https://libraries.minecraft.net/";
|
||||
QString AUTH_BASE = "https://authserver.mojang.com/";
|
||||
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
|
||||
QString FMLLIBS_BASE_URL = "https://files.multimc.org/fmllibs/";
|
||||
QString TRANSLATIONS_BASE_URL = "https://files.multimc.org/translations/";
|
||||
QString FMLLIBS_BASE_URL = "https://files.polymc.org/fmllibs/";
|
||||
QString TRANSLATIONS_BASE_URL = "https://i18n.polymc.org/";
|
||||
|
||||
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
|
||||
|
||||
|
1532
changelog.md
1532
changelog.md
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,7 @@ BEGIN
|
||||
BEGIN
|
||||
BLOCK "000004b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "MultiMC Contributors"
|
||||
VALUE "CompanyName", "MultiMC & PolyMC Contributors"
|
||||
VALUE "FileDescription", "Testcase"
|
||||
VALUE "FileVersion", "1.0.0.0"
|
||||
VALUE "ProductName", "Launcher Testcase"
|
||||
|
22
flake.lock
generated
22
flake.lock
generated
@ -18,11 +18,11 @@
|
||||
},
|
||||
"flake-utils": {
|
||||
"locked": {
|
||||
"lastModified": 1638122382,
|
||||
"narHash": "sha256-sQzZzAbvKEqN9s0bzWuYmRaA03v40gaJ4+iL1LXjaeI=",
|
||||
"lastModified": 1642700792,
|
||||
"narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "74f7e4319258e287b0f9cb95426c9853b282730b",
|
||||
"rev": "846b2ae0fc4cc943637d3d1def4454213e203cba",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -49,11 +49,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1641528457,
|
||||
"narHash": "sha256-FyU9E63n1W7Ql4pMnhW2/rO9OftWZ37pLppn/c1aisY=",
|
||||
"lastModified": 1643169865,
|
||||
"narHash": "sha256-+KIpNRazbc8Gac9jdWCKQkFv9bjceaLaLhlwqUEYu8c=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "ff377a78794d412a35245e05428c8f95fef3951f",
|
||||
"rev": "945ec499041db73043f745fad3b2a3a01e826081",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -66,15 +66,15 @@
|
||||
"quazip": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1633895098,
|
||||
"narHash": "sha256-+Of0M2IAoTf1CyC0teCpsyurv6xfqiBo84V49dSeNTA=",
|
||||
"owner": "multimc",
|
||||
"lastModified": 1643049383,
|
||||
"narHash": "sha256-LcJY6yd6GyeL7X5MP4L94diceM1TYespWByliBsjK98=",
|
||||
"owner": "stachenov",
|
||||
"repo": "quazip",
|
||||
"rev": "b1a72ac0bb5a732bf887a535ab75c6f9bedb6b6b",
|
||||
"rev": "09ec1d10c6d627f895109b21728dda000cbfa7d1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "multimc",
|
||||
"owner": "stachenov",
|
||||
"repo": "quazip",
|
||||
"type": "github"
|
||||
}
|
||||
|
49
flake.nix
49
flake.nix
@ -2,7 +2,7 @@
|
||||
description = "PolyMC flake";
|
||||
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
inputs.flake-compat = {
|
||||
inputs.flake-compat = {
|
||||
url = "github:edolstra/flake-compat";
|
||||
flake = false;
|
||||
};
|
||||
@ -11,17 +11,26 @@
|
||||
flake = false;
|
||||
};
|
||||
inputs.quazip = {
|
||||
url = "github:multimc/quazip";
|
||||
url = "github:stachenov/quazip";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
outputs = inputs@{ self, nixpkgs, flake-utils, libnbtplusplus, quazip, ... }:
|
||||
flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" ] (system:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
};
|
||||
|
||||
outputs = args@{ self, nixpkgs, flake-utils, 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;
|
||||
@ -29,27 +38,13 @@
|
||||
submoduleNbt = libnbtplusplus;
|
||||
};
|
||||
};
|
||||
|
||||
# 'nix flake check' fails
|
||||
overlay = (final: prev: rec {
|
||||
polymc = prev.libsForQt5.callPackage ./packages/nix/polymc {
|
||||
inherit self;
|
||||
submoduleQuazip = quazip;
|
||||
submoduleNbt = libnbtplusplus;
|
||||
};
|
||||
});
|
||||
|
||||
apps = {
|
||||
polymc = flake-utils.lib.mkApp {
|
||||
name = "polymc";
|
||||
drv = packages.polymc;
|
||||
drv = self.packages.${system}.polymc;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
inherit packages overlay apps;
|
||||
defaultPackage = packages.polymc;
|
||||
defaultApp = apps.polymc;
|
||||
}
|
||||
);
|
||||
defaultPackage = self.packages.${system}.polymc;
|
||||
defaultApp = self.apps.${system}.polymc;
|
||||
});
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "ui/pages/global/ProxyPage.h"
|
||||
#include "ui/pages/global/ExternalToolsPage.h"
|
||||
#include "ui/pages/global/AccountListPage.h"
|
||||
#include "ui/pages/global/PasteEEPage.h"
|
||||
#include "ui/pages/global/APIPage.h"
|
||||
#include "ui/pages/global/CustomCommandsPage.h"
|
||||
|
||||
#include "ui/themes/ITheme.h"
|
||||
@ -187,30 +187,11 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
setApplicationName(BuildConfig.LAUNCHER_NAME);
|
||||
setApplicationDisplayName(BuildConfig.LAUNCHER_DISPLAYNAME);
|
||||
setApplicationVersion(BuildConfig.printableVersionString());
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5,7,0))
|
||||
setDesktopFileName(BuildConfig.LAUNCHER_DESKTOPFILENAME);
|
||||
#endif
|
||||
startTime = QDateTime::currentDateTime();
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
{
|
||||
QFile osrelease("/proc/sys/kernel/osrelease");
|
||||
if (osrelease.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QTextStream in(&osrelease);
|
||||
auto contents = in.readAll();
|
||||
if(
|
||||
contents.contains("WSL", Qt::CaseInsensitive) ||
|
||||
contents.contains("Microsoft", Qt::CaseInsensitive)
|
||||
) {
|
||||
showFatalErrorMessage(
|
||||
"Unsupported system detected!",
|
||||
"Linux-on-Windows distributions are not supported.\n\n"
|
||||
"Please use the Windows binary when playing on Windows."
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Don't quit on hiding the last window
|
||||
this->setQuitOnLastWindowClosed(false);
|
||||
|
||||
@ -283,12 +264,21 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_instanceIdToLaunch = args["launch"].toString();
|
||||
m_serverToJoin = args["server"].toString();
|
||||
m_profileToUse = args["profile"].toString();
|
||||
m_liveCheck = args["alive"].toBool();
|
||||
m_zipToImport = args["import"].toUrl();
|
||||
|
||||
// error if --launch is missing with --server or --profile
|
||||
if((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty())
|
||||
{
|
||||
std::cerr << "--server and --profile can only be used in combination with --launch!" << std::endl;
|
||||
m_status = Application::Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
QString origcwdPath = QDir::currentPath();
|
||||
QString binPath = applicationDirPath();
|
||||
QString adjustedBy;
|
||||
@ -304,16 +294,20 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef LAUNCHER_LINUX_DATADIR
|
||||
QString xdgDataHome = QFile::decodeName(qgetenv("XDG_DATA_HOME"));
|
||||
if (xdgDataHome.isEmpty())
|
||||
xdgDataHome = QDir::homePath() + QLatin1String("/.local/share");
|
||||
dataPath = xdgDataHome + "/polymc";
|
||||
adjustedBy += "XDG standard " + dataPath;
|
||||
#elif defined(Q_OS_MAC)
|
||||
QDir foo(FS::PathCombine(applicationDirPath(), "../../Data"));
|
||||
#if !defined(LAUNCHER_PORTABLE) || defined(Q_OS_MAC)
|
||||
QDir foo(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), ".."));
|
||||
dataPath = foo.absolutePath();
|
||||
adjustedBy += "Fallback to special Mac location " + dataPath;
|
||||
adjustedBy += dataPath;
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
// TODO: this should be removed in a future version
|
||||
// TODO: provide a migration path similar to macOS migration
|
||||
QDir bar(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation), "polymc"));
|
||||
if (bar.exists()) {
|
||||
dataPath = bar.absolutePath();
|
||||
adjustedBy += "Legacy data path " + dataPath;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
dataPath = applicationDirPath();
|
||||
adjustedBy += "Fallback to binary path " + dataPath;
|
||||
@ -357,20 +351,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_instanceIdToLaunch.isEmpty() && !m_serverToJoin.isEmpty())
|
||||
{
|
||||
std::cerr << "--server can only be used in combination with --launch!" << std::endl;
|
||||
m_status = Application::Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_instanceIdToLaunch.isEmpty() && !m_profileToUse.isEmpty())
|
||||
{
|
||||
std::cerr << "--account can only be used in combination with --launch!" << std::endl;
|
||||
m_status = Application::Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
// move user data to new location if on macOS and it still exists in Contents/MacOS
|
||||
QDir fi(applicationDirPath());
|
||||
@ -435,7 +415,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Establish the mechanism for communication with an already running MultiMC that uses the same data path.
|
||||
* Establish the mechanism for communication with an already running PolyMC that uses the same data path.
|
||||
* If there is one, tell it what the user actually wanted to do and exit.
|
||||
* We want to initialize this before logging to avoid messing with the log of a potential already running copy.
|
||||
*/
|
||||
@ -566,26 +546,23 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
qDebug() << "<> Paths set.";
|
||||
}
|
||||
|
||||
do // once
|
||||
if(m_liveCheck)
|
||||
{
|
||||
if(m_liveCheck)
|
||||
QFile check(liveCheckFile);
|
||||
if(check.open(QIODevice::WriteOnly | QIODevice::Truncate))
|
||||
{
|
||||
QFile check(liveCheckFile);
|
||||
if(!check.open(QIODevice::WriteOnly | QIODevice::Truncate))
|
||||
{
|
||||
qWarning() << "Could not open" << liveCheckFile << "for writing!";
|
||||
break;
|
||||
}
|
||||
auto payload = appID.toString().toUtf8();
|
||||
if(check.write(payload) != payload.size())
|
||||
if(check.write(payload) == payload.size())
|
||||
{
|
||||
check.close();
|
||||
} else {
|
||||
qWarning() << "Could not write into" << liveCheckFile << "!";
|
||||
check.remove();
|
||||
break;
|
||||
check.remove(); // also closes file!
|
||||
}
|
||||
check.close();
|
||||
} else {
|
||||
qWarning() << "Could not open" << liveCheckFile << "for writing!";
|
||||
}
|
||||
} while(false);
|
||||
}
|
||||
|
||||
// Initialize application settings
|
||||
{
|
||||
@ -595,7 +572,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
m_settings->registerSetting("AutoUpdate", true);
|
||||
|
||||
// Theming
|
||||
m_settings->registerSetting("IconTheme", QString("multimc"));
|
||||
m_settings->registerSetting("IconTheme", QString("pe_colored"));
|
||||
m_settings->registerSetting("ApplicationTheme", QString("system"));
|
||||
|
||||
// Notifications
|
||||
@ -662,7 +639,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
|
||||
// Memory
|
||||
m_settings->registerSetting({"MinMemAlloc", "MinMemoryAlloc"}, 512);
|
||||
m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, 1024);
|
||||
m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, 4096);
|
||||
m_settings->registerSetting("PermGen", 128);
|
||||
|
||||
// Java Settings
|
||||
@ -714,8 +691,13 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
|
||||
m_settings->registerSetting("UpdateDialogGeometry", "");
|
||||
|
||||
// paste.ee API key
|
||||
m_settings->registerSetting("PasteEEAPIKey", "multimc");
|
||||
// pastebin URL
|
||||
m_settings->registerSetting("PastebinURL", "https://0x0.st");
|
||||
|
||||
m_settings->registerSetting("CloseAfterLaunch", false);
|
||||
|
||||
// Custom MSA credentials
|
||||
m_settings->registerSetting("MSAClientIDOverride", "");
|
||||
|
||||
// Init page provider
|
||||
{
|
||||
@ -728,7 +710,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
m_globalSettingsProvider->addPage<ProxyPage>();
|
||||
m_globalSettingsProvider->addPage<ExternalToolsPage>();
|
||||
m_globalSettingsProvider->addPage<AccountListPage>();
|
||||
m_globalSettingsProvider->addPage<PasteEEPage>();
|
||||
m_globalSettingsProvider->addPage<APIPage>();
|
||||
}
|
||||
qDebug() << "<> Settings loaded.";
|
||||
}
|
||||
@ -1514,3 +1496,13 @@ QString Application::getJarsPath()
|
||||
}
|
||||
return m_jarsPath;
|
||||
}
|
||||
|
||||
QString Application::getMSAClientID()
|
||||
{
|
||||
QString clientIDOverride = m_settings->get("MSAClientIDOverride").toString();
|
||||
if (!clientIDOverride.isEmpty()) {
|
||||
return clientIDOverride;
|
||||
}
|
||||
|
||||
return BuildConfig.MSA_CLIENT_ID;
|
||||
}
|
||||
|
@ -117,6 +117,8 @@ public:
|
||||
|
||||
QString getJarsPath();
|
||||
|
||||
QString getMSAClientID();
|
||||
|
||||
/// this is the root of the 'installation'. Used for automatic updates
|
||||
const QString &root() {
|
||||
return m_rootPath;
|
||||
|
@ -39,6 +39,7 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
|
||||
m_settings->registerSetting("lastLaunchTime", 0);
|
||||
m_settings->registerSetting("totalTimePlayed", 0);
|
||||
m_settings->registerSetting("lastTimePlayed", 0);
|
||||
m_settings->registerSetting("InstanceType", "OneSix");
|
||||
|
||||
// Custom Commands
|
||||
auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
|
||||
|
@ -37,6 +37,10 @@ set(CORE_SOURCES
|
||||
InstanceImportTask.h
|
||||
InstanceImportTask.cpp
|
||||
|
||||
# Mod downloading task
|
||||
ModDownloadTask.h
|
||||
ModDownloadTask.cpp
|
||||
|
||||
# Use tracking separate from memory management
|
||||
Usable.h
|
||||
|
||||
@ -221,7 +225,11 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/auth/flows/Mojang.h
|
||||
minecraft/auth/flows/MSA.cpp
|
||||
minecraft/auth/flows/MSA.h
|
||||
minecraft/auth/flows/Offline.cpp
|
||||
minecraft/auth/flows/Offline.h
|
||||
|
||||
minecraft/auth/steps/OfflineStep.cpp
|
||||
minecraft/auth/steps/OfflineStep.h
|
||||
minecraft/auth/steps/EntitlementsStep.cpp
|
||||
minecraft/auth/steps/EntitlementsStep.h
|
||||
minecraft/auth/steps/GetSkinStep.cpp
|
||||
@ -278,13 +286,6 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/launch/VerifyJavaInstall.cpp
|
||||
minecraft/launch/VerifyJavaInstall.h
|
||||
|
||||
minecraft/legacy/LegacyModList.h
|
||||
minecraft/legacy/LegacyModList.cpp
|
||||
minecraft/legacy/LegacyInstance.h
|
||||
minecraft/legacy/LegacyInstance.cpp
|
||||
minecraft/legacy/LegacyUpgradeTask.h
|
||||
minecraft/legacy/LegacyUpgradeTask.cpp
|
||||
|
||||
minecraft/GradleSpecifier.h
|
||||
minecraft/MinecraftInstance.cpp
|
||||
minecraft/MinecraftInstance.h
|
||||
@ -506,12 +507,19 @@ set(FLAME_SOURCES
|
||||
# Flame
|
||||
modplatform/flame/FlamePackIndex.cpp
|
||||
modplatform/flame/FlamePackIndex.h
|
||||
modplatform/flame/FlameModIndex.cpp
|
||||
modplatform/flame/FlameModIndex.h
|
||||
modplatform/flame/PackManifest.h
|
||||
modplatform/flame/PackManifest.cpp
|
||||
modplatform/flame/FileResolvingTask.h
|
||||
modplatform/flame/FileResolvingTask.cpp
|
||||
)
|
||||
|
||||
set(MODRINTH_SOURCES
|
||||
modplatform/modrinth/ModrinthPackIndex.cpp
|
||||
modplatform/modrinth/ModrinthPackIndex.h
|
||||
)
|
||||
|
||||
set(MODPACKSCH_SOURCES
|
||||
modplatform/modpacksch/FTBPackInstallTask.h
|
||||
modplatform/modpacksch/FTBPackInstallTask.cpp
|
||||
@ -566,6 +574,7 @@ set(LOGIC_SOURCES
|
||||
${ICONS_SOURCES}
|
||||
${FTB_SOURCES}
|
||||
${FLAME_SOURCES}
|
||||
${MODRINTH_SOURCES}
|
||||
${MODPACKSCH_SOURCES}
|
||||
${TECHNIC_SOURCES}
|
||||
${ATLAUNCHER_SOURCES}
|
||||
@ -685,8 +694,6 @@ SET(LAUNCHER_SOURCES
|
||||
ui/pages/instance/OtherLogsPage.h
|
||||
ui/pages/instance/ServersPage.cpp
|
||||
ui/pages/instance/ServersPage.h
|
||||
ui/pages/instance/LegacyUpgradePage.cpp
|
||||
ui/pages/instance/LegacyUpgradePage.h
|
||||
ui/pages/instance/WorldListPage.cpp
|
||||
ui/pages/instance/WorldListPage.h
|
||||
|
||||
@ -707,8 +714,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/pages/global/LauncherPage.h
|
||||
ui/pages/global/ProxyPage.cpp
|
||||
ui/pages/global/ProxyPage.h
|
||||
ui/pages/global/PasteEEPage.cpp
|
||||
ui/pages/global/PasteEEPage.h
|
||||
ui/pages/global/APIPage.cpp
|
||||
ui/pages/global/APIPage.h
|
||||
|
||||
# GUI - platform pages
|
||||
ui/pages/modplatform/VanillaPage.cpp
|
||||
@ -739,6 +746,10 @@ SET(LAUNCHER_SOURCES
|
||||
ui/pages/modplatform/flame/FlameModel.h
|
||||
ui/pages/modplatform/flame/FlamePage.cpp
|
||||
ui/pages/modplatform/flame/FlamePage.h
|
||||
ui/pages/modplatform/flame/FlameModModel.cpp
|
||||
ui/pages/modplatform/flame/FlameModModel.h
|
||||
ui/pages/modplatform/flame/FlameModPage.cpp
|
||||
ui/pages/modplatform/flame/FlameModPage.h
|
||||
|
||||
ui/pages/modplatform/technic/TechnicModel.cpp
|
||||
ui/pages/modplatform/technic/TechnicModel.h
|
||||
@ -748,6 +759,11 @@ SET(LAUNCHER_SOURCES
|
||||
ui/pages/modplatform/ImportPage.cpp
|
||||
ui/pages/modplatform/ImportPage.h
|
||||
|
||||
ui/pages/modplatform/modrinth/ModrinthModel.cpp
|
||||
ui/pages/modplatform/modrinth/ModrinthModel.h
|
||||
ui/pages/modplatform/modrinth/ModrinthPage.cpp
|
||||
ui/pages/modplatform/modrinth/ModrinthPage.h
|
||||
|
||||
# GUI - dialogs
|
||||
ui/dialogs/AboutDialog.cpp
|
||||
ui/dialogs/AboutDialog.h
|
||||
@ -769,6 +785,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/dialogs/LoginDialog.h
|
||||
ui/dialogs/MSALoginDialog.cpp
|
||||
ui/dialogs/MSALoginDialog.h
|
||||
ui/dialogs/OfflineLoginDialog.cpp
|
||||
ui/dialogs/OfflineLoginDialog.h
|
||||
ui/dialogs/NewComponentDialog.cpp
|
||||
ui/dialogs/NewComponentDialog.h
|
||||
ui/dialogs/NewInstanceDialog.cpp
|
||||
@ -785,6 +803,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/dialogs/VersionSelectDialog.h
|
||||
ui/dialogs/SkinUploadDialog.cpp
|
||||
ui/dialogs/SkinUploadDialog.h
|
||||
ui/dialogs/ModDownloadDialog.cpp
|
||||
ui/dialogs/ModDownloadDialog.h
|
||||
|
||||
|
||||
# GUI - widgets
|
||||
@ -842,7 +862,7 @@ qt5_wrap_ui(LAUNCHER_UI
|
||||
ui/pages/global/AccountListPage.ui
|
||||
ui/pages/global/JavaPage.ui
|
||||
ui/pages/global/LauncherPage.ui
|
||||
ui/pages/global/PasteEEPage.ui
|
||||
ui/pages/global/APIPage.ui
|
||||
ui/pages/global/ProxyPage.ui
|
||||
ui/pages/global/MinecraftPage.ui
|
||||
ui/pages/global/ExternalToolsPage.ui
|
||||
@ -855,16 +875,17 @@ qt5_wrap_ui(LAUNCHER_UI
|
||||
ui/pages/instance/InstanceSettingsPage.ui
|
||||
ui/pages/instance/VersionPage.ui
|
||||
ui/pages/instance/WorldListPage.ui
|
||||
ui/pages/instance/LegacyUpgradePage.ui
|
||||
ui/pages/instance/ScreenshotsPage.ui
|
||||
ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui
|
||||
ui/pages/modplatform/atlauncher/AtlPage.ui
|
||||
ui/pages/modplatform/VanillaPage.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
|
||||
@ -880,6 +901,7 @@ qt5_wrap_ui(LAUNCHER_UI
|
||||
ui/dialogs/ExportInstanceDialog.ui
|
||||
ui/dialogs/IconPickerDialog.ui
|
||||
ui/dialogs/MSALoginDialog.ui
|
||||
ui/dialogs/OfflineLoginDialog.ui
|
||||
ui/dialogs/AboutDialog.ui
|
||||
ui/dialogs/LoginDialog.ui
|
||||
ui/dialogs/EditAccountDialog.ui
|
||||
@ -908,9 +930,8 @@ endif()
|
||||
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES})
|
||||
target_link_libraries(Launcher_logic
|
||||
systeminfo
|
||||
Launcher_quazip
|
||||
Launcher_classparser
|
||||
${NBT_NAME}
|
||||
nbt++
|
||||
${ZLIB_LIBRARIES}
|
||||
optional-bare
|
||||
tomlc99
|
||||
@ -926,9 +947,9 @@ target_link_libraries(Launcher_logic
|
||||
)
|
||||
target_link_libraries(Launcher_logic
|
||||
Launcher_iconfix
|
||||
${QUAZIP_LIBRARIES}
|
||||
QuaZip::QuaZip
|
||||
hoedown
|
||||
Launcher_rainbow
|
||||
PolyMC_rainbow
|
||||
LocalPeer
|
||||
)
|
||||
|
||||
|
@ -42,7 +42,6 @@ void InstanceCopyTask::copyFinished()
|
||||
}
|
||||
// FIXME: shouldn't this be able to report errors?
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||
|
||||
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
|
||||
inst->setName(m_instName);
|
||||
|
@ -17,8 +17,6 @@ void InstanceCreationTask::executeTask()
|
||||
{
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
||||
instanceSettings->suspendSave();
|
||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||
instanceSettings->set("InstanceType", "OneSix");
|
||||
MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
|
||||
auto components = inst.getPackProfile();
|
||||
components->buildingFromScratch();
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include "modplatform/flame/FileResolvingTask.h"
|
||||
#include "modplatform/flame/PackManifest.h"
|
||||
#include "Json.h"
|
||||
#include <quazipdir.h>
|
||||
#include <quazip/quazipdir.h>
|
||||
#include "modplatform/technic/TechnicPackProcessor.h"
|
||||
|
||||
#include "icons/IconList.h"
|
||||
@ -261,8 +261,6 @@ void InstanceImportTask::processFlame()
|
||||
|
||||
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||
instanceSettings->set("InstanceType", "OneSix");
|
||||
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
||||
auto mcVersion = pack.minecraft.version;
|
||||
// Hack to correct some 'special sauce'...
|
||||
@ -422,7 +420,6 @@ void InstanceImportTask::processMultiMC()
|
||||
{
|
||||
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||
|
||||
NullInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
||||
|
||||
|
@ -32,7 +32,6 @@
|
||||
#include "BaseInstance.h"
|
||||
#include "InstanceTask.h"
|
||||
#include "settings/INISettingsObject.h"
|
||||
#include "minecraft/legacy/LegacyInstance.h"
|
||||
#include "NullInstance.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "FileSystem.h"
|
||||
@ -544,23 +543,8 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id)
|
||||
auto instanceRoot = FS::PathCombine(m_instDir, id);
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg"));
|
||||
InstancePtr inst;
|
||||
|
||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||
|
||||
QString inst_type = instanceSettings->get("InstanceType").toString();
|
||||
|
||||
if (inst_type == "OneSix" || inst_type == "Nostalgia")
|
||||
{
|
||||
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
|
||||
}
|
||||
else if (inst_type == "Legacy")
|
||||
{
|
||||
inst.reset(new LegacyInstance(m_globalSettings, instanceSettings, instanceRoot));
|
||||
}
|
||||
else
|
||||
{
|
||||
inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot));
|
||||
}
|
||||
// TODO: Handle incompatible instances
|
||||
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
|
||||
qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot();
|
||||
return inst;
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/legacy/LegacyInstance.h"
|
||||
#include <FileSystem.h>
|
||||
#include "ui/pages/BasePage.h"
|
||||
#include "ui/pages/BasePageProvider.h"
|
||||
@ -14,7 +13,6 @@
|
||||
#include "ui/pages/instance/ScreenshotsPage.h"
|
||||
#include "ui/pages/instance/InstanceSettingsPage.h"
|
||||
#include "ui/pages/instance/OtherLogsPage.h"
|
||||
#include "ui/pages/instance/LegacyUpgradePage.h"
|
||||
#include "ui/pages/instance/WorldListPage.h"
|
||||
#include "ui/pages/instance/ServersPage.h"
|
||||
#include "ui/pages/instance/GameOptionsPage.h"
|
||||
@ -34,31 +32,20 @@ public:
|
||||
QList<BasePage *> values;
|
||||
values.append(new LogPage(inst));
|
||||
std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst);
|
||||
if(onesix)
|
||||
{
|
||||
values.append(new VersionPage(onesix.get()));
|
||||
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList(), "mods", "loadermods", tr("Loader mods"), "Loader-mods");
|
||||
modsPage->setFilter("%1 (*.zip *.jar *.litemod)");
|
||||
values.append(modsPage);
|
||||
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList(), "coremods", "coremods", tr("Core mods"), "Core-mods"));
|
||||
values.append(new ResourcePackPage(onesix.get()));
|
||||
values.append(new TexturePackPage(onesix.get()));
|
||||
values.append(new ShaderPackPage(onesix.get()));
|
||||
values.append(new NotesPage(onesix.get()));
|
||||
values.append(new WorldListPage(onesix.get(), onesix->worldList()));
|
||||
values.append(new ServersPage(onesix));
|
||||
// values.append(new GameOptionsPage(onesix.get()));
|
||||
values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots")));
|
||||
values.append(new InstanceSettingsPage(onesix.get()));
|
||||
}
|
||||
std::shared_ptr<LegacyInstance> legacy = std::dynamic_pointer_cast<LegacyInstance>(inst);
|
||||
if(legacy)
|
||||
{
|
||||
values.append(new LegacyUpgradePage(legacy));
|
||||
values.append(new NotesPage(legacy.get()));
|
||||
values.append(new WorldListPage(legacy.get(), legacy->worldList()));
|
||||
values.append(new ScreenshotsPage(FS::PathCombine(legacy->gameRoot(), "screenshots")));
|
||||
}
|
||||
values.append(new VersionPage(onesix.get()));
|
||||
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList(), "mods", "loadermods", tr("Mods"), "Loader-mods");
|
||||
modsPage->setFilter("%1 (*.zip *.jar *.litemod)");
|
||||
values.append(modsPage);
|
||||
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList(), "coremods", "coremods", tr("Core mods"), "Core-mods"));
|
||||
values.append(new ResourcePackPage(onesix.get()));
|
||||
values.append(new TexturePackPage(onesix.get()));
|
||||
values.append(new ShaderPackPage(onesix.get()));
|
||||
values.append(new NotesPage(onesix.get()));
|
||||
values.append(new WorldListPage(onesix.get(), onesix->worldList()));
|
||||
values.append(new ServersPage(onesix));
|
||||
// values.append(new GameOptionsPage(onesix.get()));
|
||||
values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots")));
|
||||
values.append(new InstanceSettingsPage(onesix.get()));
|
||||
auto logMatcher = inst->getLogFileMatcher();
|
||||
if(logMatcher)
|
||||
{
|
||||
@ -74,3 +61,4 @@ public:
|
||||
protected:
|
||||
InstancePtr inst;
|
||||
};
|
||||
|
||||
|
@ -56,7 +56,7 @@ void LaunchController::decideAccount()
|
||||
m_parentWidget,
|
||||
tr("No Accounts"),
|
||||
tr("In order to play Minecraft, you must have at least one Mojang or Minecraft "
|
||||
"account logged in."
|
||||
"account logged in. "
|
||||
"Would you like to open the account manager to add an account now?"),
|
||||
QMessageBox::Information,
|
||||
QMessageBox::Yes | QMessageBox::No
|
||||
@ -116,6 +116,12 @@ void LaunchController::login() {
|
||||
m_session->wants_online = m_online;
|
||||
m_accountToUse->fillSession(m_session);
|
||||
|
||||
// Launch immediately in true offline mode
|
||||
if(m_accountToUse->isOffline()) {
|
||||
launchInstance();
|
||||
return;
|
||||
}
|
||||
|
||||
switch(m_accountToUse->accountState()) {
|
||||
case AccountState::Offline: {
|
||||
m_session->wants_online = false;
|
||||
@ -222,6 +228,18 @@ void LaunchController::login() {
|
||||
emitFailed(errorString);
|
||||
return;
|
||||
}
|
||||
case AccountState::Disabled: {
|
||||
auto errorString = tr("The launcher's client identification has changed. Please remove this account and add it again.");
|
||||
QMessageBox::warning(
|
||||
m_parentWidget,
|
||||
tr("Client identification changed"),
|
||||
errorString,
|
||||
QMessageBox::StandardButton::Ok,
|
||||
QMessageBox::StandardButton::Ok
|
||||
);
|
||||
emitFailed(errorString);
|
||||
return;
|
||||
}
|
||||
case AccountState::Gone: {
|
||||
auto errorString = tr("The account no longer exists on the servers. It may have been migrated, in which case please add the new account you migrated this one to.");
|
||||
QMessageBox::warning(
|
||||
|
@ -14,7 +14,7 @@ if [[ $EUID -eq 0 ]]; then
|
||||
fi
|
||||
|
||||
|
||||
LAUNCHER_NAME=@Launcher_Name@
|
||||
LAUNCHER_NAME=@Launcher_APP_BINARY_NAME@
|
||||
LAUNCHER_DIR="$(dirname "$(readlink -f "$0")")"
|
||||
echo "Launcher Dir: ${LAUNCHER_DIR}"
|
||||
|
||||
|
@ -13,17 +13,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <quazip.h>
|
||||
#include <quazipdir.h>
|
||||
#include <quazipfile.h>
|
||||
#include <JlCompress.h>
|
||||
#include <quazip/quazip.h>
|
||||
#include <quazip/quazipdir.h>
|
||||
#include <quazip/quazipfile.h>
|
||||
#include "MMCZip.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
// ours
|
||||
bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained, const JlCompress::FilterFunction filter)
|
||||
bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained, const FilterFunction filter)
|
||||
{
|
||||
QuaZip modZip(from.filePath());
|
||||
modZip.open(QuaZip::mdUnzip);
|
||||
@ -74,6 +73,39 @@ bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &containe
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files)
|
||||
{
|
||||
QDir directory(dir);
|
||||
if (!directory.exists()) return false;
|
||||
|
||||
for (auto e : files) {
|
||||
auto filePath = directory.relativeFilePath(e.absoluteFilePath());
|
||||
if( !JlCompress::compressFile(zip, e.absoluteFilePath(), filePath)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files)
|
||||
{
|
||||
QuaZip zip(fileCompressed);
|
||||
QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
|
||||
if(!zip.open(QuaZip::mdCreate)) {
|
||||
QFile::remove(fileCompressed);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto result = compressDirFiles(&zip, dir, files);
|
||||
|
||||
zip.close();
|
||||
if(zip.getZipError()!=0) {
|
||||
QFile::remove(fileCompressed);
|
||||
return false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ours
|
||||
bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods)
|
||||
{
|
||||
@ -122,13 +154,22 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
||||
}
|
||||
else if (mod.type() == Mod::MOD_FOLDER)
|
||||
{
|
||||
// untested, but seems to be unused / not possible to reach
|
||||
// FIXME: buggy - does not work with addedFiles
|
||||
auto filename = mod.filename();
|
||||
QString what_to_zip = filename.absoluteFilePath();
|
||||
QDir dir(what_to_zip);
|
||||
dir.cdUp();
|
||||
QString parent_dir = dir.absolutePath();
|
||||
if (!JlCompress::compressSubDir(&zipOut, what_to_zip, parent_dir, addedFiles))
|
||||
auto files = QFileInfoList();
|
||||
MMCZip::collectFileListRecursively(what_to_zip, nullptr, &files, nullptr);
|
||||
|
||||
for (auto e : files) {
|
||||
if (addedFiles.contains(e.filePath()))
|
||||
files.removeAll(e);
|
||||
}
|
||||
|
||||
if (!MMCZip::compressDirFiles(&zipOut, parent_dir, files))
|
||||
{
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
@ -136,7 +177,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
||||
return false;
|
||||
}
|
||||
qDebug() << "Adding folder " << filename.fileName() << " from "
|
||||
<< filename.absoluteFilePath();
|
||||
<< filename.absoluteFilePath();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -310,3 +351,37 @@ bool MMCZip::extractFile(QString fileCompressed, QString file, QString target)
|
||||
}
|
||||
return MMCZip::extractRelFile(&zip, file, target);
|
||||
}
|
||||
|
||||
bool MMCZip::collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList *files,
|
||||
MMCZip::FilterFunction excludeFilter) {
|
||||
QDir rootDirectory(rootDir);
|
||||
if (!rootDirectory.exists()) return false;
|
||||
|
||||
QDir directory;
|
||||
if (subDir == nullptr)
|
||||
directory = rootDirectory;
|
||||
else
|
||||
directory = QDir(subDir);
|
||||
|
||||
if (!directory.exists()) return false; // shouldn't ever happen
|
||||
|
||||
// recurse directories
|
||||
QFileInfoList entries = directory.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden);
|
||||
for (const auto& e: entries) {
|
||||
if (!collectFileListRecursively(rootDir, e.filePath(), files, excludeFilter))
|
||||
return false;
|
||||
}
|
||||
|
||||
// collect files
|
||||
entries = directory.entryInfoList(QDir::Files);
|
||||
for (const auto& e: entries) {
|
||||
QString relativeFilePath = rootDirectory.relativeFilePath(e.absoluteFilePath());
|
||||
if (excludeFilter && excludeFilter(relativeFilePath)) {
|
||||
qDebug() << "Skipping file " << relativeFilePath;
|
||||
continue;
|
||||
}
|
||||
|
||||
files->append(e.filePath()); // we want the original paths for MMCZip::compressDirFiles
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -21,17 +21,36 @@
|
||||
#include "minecraft/mod/Mod.h"
|
||||
#include <functional>
|
||||
|
||||
#include <JlCompress.h>
|
||||
#include <quazip/JlCompress.h>
|
||||
#include <nonstd/optional>
|
||||
|
||||
namespace MMCZip
|
||||
{
|
||||
using FilterFunction = std::function<bool(const QString &)>;
|
||||
|
||||
/**
|
||||
* Merge two zip files, using a filter function
|
||||
*/
|
||||
bool mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
|
||||
const JlCompress::FilterFunction filter = nullptr);
|
||||
const FilterFunction filter = nullptr);
|
||||
|
||||
/**
|
||||
* Compress directory, by providing a list of files to compress
|
||||
* \param zip target archive
|
||||
* \param dir directory that will be compressed (to compress with relative paths)
|
||||
* \param files list of files to compress
|
||||
* \return true for success or false for failure
|
||||
*/
|
||||
bool compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files);
|
||||
|
||||
/**
|
||||
* Compress directory, by providing a list of files to compress
|
||||
* \param fileCompressed target archive file
|
||||
* \param dir directory that will be compressed (to compress with relative paths)
|
||||
* \param files list of files to compress
|
||||
* \return true for success or false for failure
|
||||
*/
|
||||
bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files);
|
||||
|
||||
/**
|
||||
* take a source jar, add mods to it, resulting in target jar
|
||||
@ -89,4 +108,13 @@ namespace MMCZip
|
||||
*/
|
||||
bool extractFile(QString fileCompressed, QString file, QString dir);
|
||||
|
||||
/**
|
||||
* Populate a QFileInfoList with a directory tree recursively, while allowing to excludeFilter what shouldn't be included.
|
||||
* \param rootDir directory to start off
|
||||
* \param subDir subdirectory, should be nullptr for first invocation
|
||||
* \param files resulting list of QFileInfo
|
||||
* \param excludeFilter function to excludeFilter which files shouldn't be included (returning true means to excude)
|
||||
* \return true for success or false for failure
|
||||
*/
|
||||
bool collectFileListRecursively(const QString &rootDir, const QString &subDir, QFileInfoList *files, FilterFunction excludeFilter);
|
||||
}
|
||||
|
39
launcher/ModDownloadTask.cpp
Normal file
39
launcher/ModDownloadTask.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
#include "ModDownloadTask.h"
|
||||
#include "Application.h"
|
||||
|
||||
ModDownloadTask::ModDownloadTask(const QUrl sourceUrl,const QString filename, const std::shared_ptr<ModFolderModel> mods)
|
||||
: m_sourceUrl(sourceUrl), mods(mods), filename(filename) {
|
||||
}
|
||||
|
||||
void ModDownloadTask::executeTask() {
|
||||
setStatus(tr("Downloading mod:\n%1").arg(m_sourceUrl.toString()));
|
||||
|
||||
m_filesNetJob.reset(new NetJob(tr("Mod download"), APPLICATION->network()));
|
||||
m_filesNetJob->addNetAction(Net::Download::makeFile(m_sourceUrl, mods->dir().absoluteFilePath(filename)));
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ModDownloadTask::downloadSucceeded);
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &ModDownloadTask::downloadProgressChanged);
|
||||
connect(m_filesNetJob.get(), &NetJob::failed, this, &ModDownloadTask::downloadFailed);
|
||||
m_filesNetJob->start();
|
||||
}
|
||||
|
||||
void ModDownloadTask::downloadSucceeded()
|
||||
{
|
||||
emitSucceeded();
|
||||
m_filesNetJob.reset();
|
||||
}
|
||||
|
||||
void ModDownloadTask::downloadFailed(QString reason)
|
||||
{
|
||||
emitFailed(reason);
|
||||
m_filesNetJob.reset();
|
||||
}
|
||||
|
||||
void ModDownloadTask::downloadProgressChanged(qint64 current, qint64 total)
|
||||
{
|
||||
emit progress(current, total);
|
||||
}
|
||||
|
||||
bool ModDownloadTask::abort() {
|
||||
return m_filesNetJob->abort();
|
||||
}
|
||||
|
35
launcher/ModDownloadTask.h
Normal file
35
launcher/ModDownloadTask.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include "QObjectPtr.h"
|
||||
#include "tasks/Task.h"
|
||||
#include "minecraft/mod/ModFolderModel.h"
|
||||
#include "net/NetJob.h"
|
||||
#include <QUrl>
|
||||
|
||||
|
||||
class ModDownloadTask : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ModDownloadTask(const QUrl sourceUrl, const QString filename, const std::shared_ptr<ModFolderModel> mods);
|
||||
const QString& getFilename() const { return filename; }
|
||||
|
||||
public slots:
|
||||
bool abort() override;
|
||||
protected:
|
||||
//! Entry point for tasks.
|
||||
void executeTask() override;
|
||||
|
||||
private:
|
||||
QUrl m_sourceUrl;
|
||||
NetJob::Ptr m_filesNetJob;
|
||||
const std::shared_ptr<ModFolderModel> mods;
|
||||
const QString filename;
|
||||
|
||||
void downloadProgressChanged(qint64 current, qint64 total);
|
||||
|
||||
void downloadFailed(QString reason);
|
||||
|
||||
void downloadSucceeded();
|
||||
};
|
||||
|
||||
|
||||
|
@ -93,7 +93,7 @@ void UpdateController::installUpdates()
|
||||
qDebug() << "Installing updates.";
|
||||
#ifdef Q_OS_WIN
|
||||
QString finishCmd = QApplication::applicationFilePath();
|
||||
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined (Q_OS_OPENBSD)
|
||||
QString finishCmd = FS::PathCombine(m_root, BuildConfig.LAUNCHER_NAME);
|
||||
#elif defined Q_OS_MAC
|
||||
QString finishCmd = QApplication::applicationFilePath();
|
||||
|
@ -103,11 +103,15 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||
for(QString line : lines)
|
||||
{
|
||||
line = line.trimmed();
|
||||
// NOTE: workaround for GH-4125, where garbage is getting printed into stdout on bedrock linux
|
||||
if (line.contains("/bedrock/strata")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto parts = line.split('=', QString::SkipEmptyParts);
|
||||
if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty())
|
||||
{
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -120,8 +120,8 @@ void JavaInstallList::updateListData(QList<BaseVersionPtr> versions)
|
||||
|
||||
bool sortJavas(BaseVersionPtr left, BaseVersionPtr right)
|
||||
{
|
||||
auto rleft = std::dynamic_pointer_cast<JavaInstall>(left);
|
||||
auto rright = std::dynamic_pointer_cast<JavaInstall>(right);
|
||||
auto rleft = std::dynamic_pointer_cast<JavaInstall>(right);
|
||||
auto rright = std::dynamic_pointer_cast<JavaInstall>(left);
|
||||
return (*rleft) > (*rright);
|
||||
}
|
||||
|
||||
|
@ -77,14 +77,14 @@ QProcessEnvironment CleanEnviroment()
|
||||
qDebug() << "Env: ignoring" << key << value;
|
||||
continue;
|
||||
}
|
||||
// filter MultiMC-related things
|
||||
// filter PolyMC-related things
|
||||
if(key.startsWith("QT_"))
|
||||
{
|
||||
qDebug() << "Env: ignoring" << key << value;
|
||||
continue;
|
||||
}
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
// Do not pass LD_* variables to java. They were intended for MultiMC
|
||||
// Do not pass LD_* variables to java. They were intended for PolyMC
|
||||
if(key.startsWith("LD_"))
|
||||
{
|
||||
qDebug() << "Env: ignoring" << key << value;
|
||||
@ -149,6 +149,21 @@ JavaInstallPtr JavaUtils::GetDefaultJava()
|
||||
return javaVersion;
|
||||
}
|
||||
|
||||
QStringList addJavasFromEnv(QList<QString> javas)
|
||||
{
|
||||
QByteArray env = qgetenv("POLYMC_JAVA_PATHS");
|
||||
#if defined(Q_OS_WIN32)
|
||||
QList<QString> javaPaths = QString::fromLocal8Bit(env).split(QLatin1String(";"));
|
||||
#else
|
||||
QList<QString> javaPaths = QString::fromLocal8Bit(env).split(QLatin1String(":"));
|
||||
#endif
|
||||
for(QString i : javaPaths)
|
||||
{
|
||||
javas.append(i);
|
||||
};
|
||||
return javas;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_WIN32)
|
||||
QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString keyName, QString keyJavaDir, QString subkeySuffix)
|
||||
{
|
||||
@ -290,7 +305,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
KEY_WOW64_64KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath");
|
||||
QList<JavaInstallPtr> ZULU32s = this->FindJavaFromRegistryKey(
|
||||
KEY_WOW64_32KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath");
|
||||
|
||||
|
||||
// BellSoft Liberica
|
||||
QList<JavaInstallPtr> LIBERICA64s = this->FindJavaFromRegistryKey(
|
||||
KEY_WOW64_64KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath");
|
||||
@ -328,7 +343,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
java_candidates.append(ADOPTIUMJDK32s);
|
||||
java_candidates.append(ZULU32s);
|
||||
java_candidates.append(LIBERICA32s);
|
||||
|
||||
|
||||
java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path));
|
||||
|
||||
QList<QString> candidates;
|
||||
@ -363,7 +378,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
|
||||
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java");
|
||||
}
|
||||
return javas;
|
||||
return addJavasFromEnv(javas);
|
||||
}
|
||||
|
||||
#elif defined(Q_OS_LINUX)
|
||||
@ -402,14 +417,14 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
scanJavaDir("/usr/lib/jvm");
|
||||
scanJavaDir("/usr/lib64/jvm");
|
||||
scanJavaDir("/usr/lib32/jvm");
|
||||
// javas stored in MultiMC's folder
|
||||
// javas stored in PolyMC's folder
|
||||
scanJavaDir("java");
|
||||
// manually installed JDKs in /opt
|
||||
scanJavaDir("/opt/jdk");
|
||||
scanJavaDir("/opt/jdks");
|
||||
// flatpak
|
||||
scanJavaDir("/app/jdk");
|
||||
return javas;
|
||||
return addJavasFromEnv(javas);
|
||||
}
|
||||
#else
|
||||
QList<QString> JavaUtils::FindJavaPaths()
|
||||
@ -419,6 +434,6 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
QList<QString> javas;
|
||||
javas.append(this->GetDefaultJava()->path);
|
||||
|
||||
return javas;
|
||||
return addJavasFromEnv(javas);
|
||||
}
|
||||
#endif
|
||||
|
@ -212,7 +212,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("MultiMC stopped watching the game log because the log length surpassed %1 lines.\n"
|
||||
m_logModel->setOverflowMessage(tr("PolyMC 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()));
|
||||
}
|
||||
@ -277,4 +277,3 @@ QString LaunchTask::substituteVariables(const QString &cmd) const
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -87,14 +87,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 MultiMC Java settings.", MessageLevel::Launcher);
|
||||
emit logLine("\nCheck your PolyMC 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 MultiMC doesn't understand:"), MessageLevel::Error);
|
||||
emit logLine(QString("Java checker returned some invalid data PolyMC doesn't understand:"), MessageLevel::Error);
|
||||
emit logLines(result.outLog.split('\n'), MessageLevel::Warning);
|
||||
emit logLine("\nMinecraft might not start properly.", MessageLevel::Launcher);
|
||||
printSystemInfo(false, false);
|
||||
|
@ -156,7 +156,7 @@ public: /* methods */
|
||||
QStringList & failedLocalFiles, const QString & overridePath) const;
|
||||
|
||||
private: /* methods */
|
||||
/// the default storage prefix used by MultiMC
|
||||
/// the default storage prefix used by PolyMC
|
||||
static QString defaultStoragePrefix();
|
||||
|
||||
/// Get the prefix - root of the storage to be used
|
||||
@ -177,23 +177,23 @@ protected: /* data */
|
||||
/// DEPRECATED URL prefix of the maven repo where the file can be downloaded
|
||||
QString m_repositoryURL;
|
||||
|
||||
/// DEPRECATED: MultiMC-specific absolute URL. takes precedence over the implicit maven repo URL, if defined
|
||||
/// DEPRECATED: PolyMC-specific absolute URL. takes precedence over the implicit maven repo URL, if defined
|
||||
QString m_absoluteURL;
|
||||
|
||||
/// MultiMC extension - filename override
|
||||
/// PolyMC extension - filename override
|
||||
QString m_filename;
|
||||
|
||||
/// DEPRECATED MultiMC extension - display name
|
||||
/// DEPRECATED PolyMC extension - display name
|
||||
QString m_displayname;
|
||||
|
||||
/**
|
||||
* MultiMC-specific type hint - modifies how the library is treated
|
||||
* PolyMC-specific type hint - modifies how the library is treated
|
||||
*/
|
||||
QString m_hint;
|
||||
|
||||
/**
|
||||
* storage - by default the local libraries folder in multimc, but could be elsewhere
|
||||
* MultiMC specific, because of FTB.
|
||||
* storage - by default the local libraries folder in polymc, but could be elsewhere
|
||||
* PolyMC specific, because of FTB.
|
||||
*/
|
||||
QString m_storagePrefix;
|
||||
|
||||
@ -215,3 +215,4 @@ protected: /* data */
|
||||
/// MOJANG: container with Mojang style download info
|
||||
MojangLibraryDownloadInfo::Ptr m_mojangDownloads;
|
||||
};
|
||||
|
||||
|
@ -124,18 +124,7 @@ MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsO
|
||||
m_settings->registerSetting("JoinServerOnLaunch", false);
|
||||
m_settings->registerSetting("JoinServerOnLaunchAddress", "");
|
||||
|
||||
// DEPRECATED: Read what versions the user configuration thinks should be used
|
||||
m_settings->registerSetting({"IntendedVersion", "MinecraftVersion"}, "");
|
||||
m_settings->registerSetting("LWJGLVersion", "");
|
||||
m_settings->registerSetting("ForgeVersion", "");
|
||||
m_settings->registerSetting("LiteloaderVersion", "");
|
||||
|
||||
m_components.reset(new PackProfile(this));
|
||||
m_components->setOldConfigVersion("net.minecraft", m_settings->get("IntendedVersion").toString());
|
||||
auto setting = m_settings->getSetting("LWJGLVersion");
|
||||
m_components->setOldConfigVersion("org.lwjgl", m_settings->get("LWJGLVersion").toString());
|
||||
m_components->setOldConfigVersion("net.minecraftforge", m_settings->get("ForgeVersion").toString());
|
||||
m_components->setOldConfigVersion("com.mumfrey.liteloader", m_settings->get("LiteloaderVersion").toString());
|
||||
}
|
||||
|
||||
void MinecraftInstance::saveNow()
|
||||
@ -445,7 +434,7 @@ QStringList MinecraftInstance::processMinecraftArgs(
|
||||
}
|
||||
|
||||
// blatant self-promotion.
|
||||
token_mapping["profile_name"] = token_mapping["version_name"] = "MultiMC5";
|
||||
token_mapping["profile_name"] = token_mapping["version_name"] = "PolyMC";
|
||||
|
||||
token_mapping["version_type"] = profile->getMinecraftVersionType();
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include <QUrl>
|
||||
|
||||
#include "tasks/Task.h"
|
||||
#include <quazip.h>
|
||||
#include <quazip/quazip.h>
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "net/NetJob.h"
|
||||
#include "tasks/Task.h"
|
||||
#include "minecraft/VersionFilterData.h"
|
||||
#include <quazip.h>
|
||||
#include <quazip/quazip.h>
|
||||
|
||||
class MinecraftVersion;
|
||||
class MinecraftInstance;
|
||||
|
@ -272,18 +272,6 @@ void PackProfile::save_internal()
|
||||
bool PackProfile::load()
|
||||
{
|
||||
auto filename = componentsFilePath();
|
||||
QFile componentsFile(filename);
|
||||
|
||||
// migrate old config to new one, if needed
|
||||
if(!componentsFile.exists())
|
||||
{
|
||||
if(!migratePreComponentConfig())
|
||||
{
|
||||
// FIXME: the user should be notified...
|
||||
qCritical() << "Failed to convert old pre-component config for instance" << d->m_instance->name();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// load the new component list and swap it with the current one...
|
||||
ComponentContainer newComponents;
|
||||
@ -369,239 +357,6 @@ void PackProfile::updateFailed(const QString& error)
|
||||
invalidateLaunchProfile();
|
||||
}
|
||||
|
||||
// NOTE this is really old stuff, and only needs to be used when loading the old hardcoded component-unaware format (loadPreComponentConfig).
|
||||
static void upgradeDeprecatedFiles(QString root, QString instanceName)
|
||||
{
|
||||
auto versionJsonPath = FS::PathCombine(root, "version.json");
|
||||
auto customJsonPath = FS::PathCombine(root, "custom.json");
|
||||
auto mcJson = FS::PathCombine(root, "patches" , "net.minecraft.json");
|
||||
|
||||
QString sourceFile;
|
||||
QString renameFile;
|
||||
|
||||
// convert old crap.
|
||||
if(QFile::exists(customJsonPath))
|
||||
{
|
||||
sourceFile = customJsonPath;
|
||||
renameFile = versionJsonPath;
|
||||
}
|
||||
else if(QFile::exists(versionJsonPath))
|
||||
{
|
||||
sourceFile = versionJsonPath;
|
||||
}
|
||||
if(!sourceFile.isEmpty() && !QFile::exists(mcJson))
|
||||
{
|
||||
if(!FS::ensureFilePathExists(mcJson))
|
||||
{
|
||||
qWarning() << "Couldn't create patches folder for" << instanceName;
|
||||
return;
|
||||
}
|
||||
if(!renameFile.isEmpty() && QFile::exists(renameFile))
|
||||
{
|
||||
if(!QFile::rename(renameFile, renameFile + ".old"))
|
||||
{
|
||||
qWarning() << "Couldn't rename" << renameFile << "to" << renameFile + ".old" << "in" << instanceName;
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto file = ProfileUtils::parseJsonFile(QFileInfo(sourceFile), false);
|
||||
ProfileUtils::removeLwjglFromPatch(file);
|
||||
file->uid = "net.minecraft";
|
||||
file->version = file->minecraftVersion;
|
||||
file->name = "Minecraft";
|
||||
|
||||
Meta::Require needsLwjgl;
|
||||
needsLwjgl.uid = "org.lwjgl";
|
||||
file->requires.insert(needsLwjgl);
|
||||
|
||||
if(!ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), mcJson))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(!QFile::rename(sourceFile, sourceFile + ".old"))
|
||||
{
|
||||
qWarning() << "Couldn't rename" << sourceFile << "to" << sourceFile + ".old" << "in" << instanceName;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Migrate old layout to the component based one...
|
||||
* - Part of the version information is taken from `instance.cfg` (fed to this class from outside).
|
||||
* - Part is taken from the old order.json file.
|
||||
* - Part is loaded from loose json files in the instance's `patches` directory.
|
||||
*/
|
||||
bool PackProfile::migratePreComponentConfig()
|
||||
{
|
||||
// upgrade the very old files from the beginnings of MultiMC 5
|
||||
upgradeDeprecatedFiles(d->m_instance->instanceRoot(), d->m_instance->name());
|
||||
|
||||
QList<ComponentPtr> components;
|
||||
QSet<QString> loaded;
|
||||
|
||||
auto addBuiltinPatch = [&](const QString &uid, bool asDependency, const QString & emptyVersion, const Meta::Require & req, const Meta::Require & conflict)
|
||||
{
|
||||
auto jsonFilePath = FS::PathCombine(d->m_instance->instanceRoot(), "patches" , uid + ".json");
|
||||
auto intendedVersion = d->getOldConfigVersion(uid);
|
||||
// load up the base minecraft patch
|
||||
ComponentPtr component;
|
||||
if(QFile::exists(jsonFilePath))
|
||||
{
|
||||
if(intendedVersion.isEmpty())
|
||||
{
|
||||
intendedVersion = emptyVersion;
|
||||
}
|
||||
auto file = ProfileUtils::parseJsonFile(QFileInfo(jsonFilePath), false);
|
||||
// fix uid
|
||||
file->uid = uid;
|
||||
// if version is missing, add it from the outside.
|
||||
if(file->version.isEmpty())
|
||||
{
|
||||
file->version = intendedVersion;
|
||||
}
|
||||
// if this is a dependency (LWJGL), mark it also as volatile
|
||||
if(asDependency)
|
||||
{
|
||||
file->m_volatile = true;
|
||||
}
|
||||
// insert requirements if needed
|
||||
if(!req.uid.isEmpty())
|
||||
{
|
||||
file->requires.insert(req);
|
||||
}
|
||||
// insert conflicts if needed
|
||||
if(!conflict.uid.isEmpty())
|
||||
{
|
||||
file->conflicts.insert(conflict);
|
||||
}
|
||||
// FIXME: @QUALITY do not ignore return value
|
||||
ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), jsonFilePath);
|
||||
component = new Component(this, uid, file);
|
||||
component->m_version = intendedVersion;
|
||||
}
|
||||
else if(!intendedVersion.isEmpty())
|
||||
{
|
||||
auto metaVersion = APPLICATION->metadataIndex()->get(uid, intendedVersion);
|
||||
component = new Component(this, metaVersion);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
component->m_dependencyOnly = asDependency;
|
||||
component->m_important = !asDependency;
|
||||
components.append(component);
|
||||
};
|
||||
// TODO: insert depends and conflicts here if these are customized files...
|
||||
Meta::Require reqLwjgl;
|
||||
reqLwjgl.uid = "org.lwjgl";
|
||||
reqLwjgl.suggests = "2.9.1";
|
||||
Meta::Require conflictLwjgl3;
|
||||
conflictLwjgl3.uid = "org.lwjgl3";
|
||||
Meta::Require nullReq;
|
||||
addBuiltinPatch("org.lwjgl", true, "2.9.1", nullReq, conflictLwjgl3);
|
||||
addBuiltinPatch("net.minecraft", false, QString(), reqLwjgl, nullReq);
|
||||
|
||||
// first, collect all other file-based patches and load them
|
||||
QMap<QString, ComponentPtr> loadedComponents;
|
||||
QDir patchesDir(FS::PathCombine(d->m_instance->instanceRoot(),"patches"));
|
||||
for (auto info : patchesDir.entryInfoList(QStringList() << "*.json", QDir::Files))
|
||||
{
|
||||
// parse the file
|
||||
qDebug() << "Reading" << info.fileName();
|
||||
auto file = ProfileUtils::parseJsonFile(info, true);
|
||||
|
||||
// correct missing or wrong uid based on the file name
|
||||
QString uid = info.completeBaseName();
|
||||
|
||||
// ignore builtins, they've been handled already
|
||||
if (uid == "net.minecraft")
|
||||
continue;
|
||||
if (uid == "org.lwjgl")
|
||||
continue;
|
||||
|
||||
// handle horrible corner cases
|
||||
if(uid.isEmpty())
|
||||
{
|
||||
// if you have a file named '.json', make it just go away.
|
||||
// FIXME: @QUALITY do not ignore return value
|
||||
QFile::remove(info.absoluteFilePath());
|
||||
continue;
|
||||
}
|
||||
file->uid = uid;
|
||||
// FIXME: @QUALITY do not ignore return value
|
||||
ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), info.absoluteFilePath());
|
||||
|
||||
auto component = new Component(this, file->uid, file);
|
||||
auto version = d->getOldConfigVersion(file->uid);
|
||||
if(!version.isEmpty())
|
||||
{
|
||||
component->m_version = version;
|
||||
}
|
||||
loadedComponents[file->uid] = component;
|
||||
}
|
||||
// try to load the other 'hardcoded' patches (forge, liteloader), if they weren't loaded from files
|
||||
auto loadSpecial = [&](const QString & uid, int order)
|
||||
{
|
||||
auto patchVersion = d->getOldConfigVersion(uid);
|
||||
if(!patchVersion.isEmpty() && !loadedComponents.contains(uid))
|
||||
{
|
||||
auto patch = new Component(this, APPLICATION->metadataIndex()->get(uid, patchVersion));
|
||||
patch->setOrder(order);
|
||||
loadedComponents[uid] = patch;
|
||||
}
|
||||
};
|
||||
loadSpecial("net.minecraftforge", 5);
|
||||
loadSpecial("com.mumfrey.liteloader", 10);
|
||||
|
||||
// load the old order.json file, if present
|
||||
ProfileUtils::PatchOrder userOrder;
|
||||
ProfileUtils::readOverrideOrders(FS::PathCombine(d->m_instance->instanceRoot(), "order.json"), userOrder);
|
||||
|
||||
// now add all the patches by user sort order
|
||||
for (auto uid : userOrder)
|
||||
{
|
||||
// ignore builtins
|
||||
if (uid == "net.minecraft")
|
||||
continue;
|
||||
if (uid == "org.lwjgl")
|
||||
continue;
|
||||
// ordering has a patch that is gone?
|
||||
if(!loadedComponents.contains(uid))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
components.append(loadedComponents.take(uid));
|
||||
}
|
||||
|
||||
// is there anything left to sort? - this is used when there are leftover components that aren't part of the order.json
|
||||
if(!loadedComponents.isEmpty())
|
||||
{
|
||||
// inserting into multimap by order number as key sorts the patches and detects duplicates
|
||||
QMultiMap<int, ComponentPtr> files;
|
||||
auto iter = loadedComponents.begin();
|
||||
while(iter != loadedComponents.end())
|
||||
{
|
||||
files.insert((*iter)->getOrder(), *iter);
|
||||
iter++;
|
||||
}
|
||||
|
||||
// then just extract the patches and put them in the list
|
||||
for (auto order : files.keys())
|
||||
{
|
||||
const auto &values = files.values(order);
|
||||
for(auto &value: values)
|
||||
{
|
||||
// TODO: put back the insertion of problem messages here, so the user knows about the id duplication
|
||||
components.append(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
// new we have a complete list of components...
|
||||
return savePackProfile(componentsFilePath(), components);
|
||||
}
|
||||
|
||||
// END: save/load
|
||||
|
||||
void PackProfile::appendComponent(ComponentPtr component)
|
||||
@ -1169,15 +924,6 @@ std::shared_ptr<LaunchProfile> PackProfile::getProfile() const
|
||||
return d->m_profile;
|
||||
}
|
||||
|
||||
void PackProfile::setOldConfigVersion(const QString& uid, const QString& version)
|
||||
{
|
||||
if(version.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
d->m_oldConfigVersions[uid] = version;
|
||||
}
|
||||
|
||||
bool PackProfile::setComponentVersion(const QString& uid, const QString& version, bool important)
|
||||
{
|
||||
auto iter = d->componentIndex.find(uid);
|
||||
|
@ -143,8 +143,6 @@ private:
|
||||
bool installCustomJar_internal(QString filepath);
|
||||
bool removeComponent_internal(ComponentPtr patch);
|
||||
|
||||
bool migratePreComponentConfig();
|
||||
|
||||
private: /* data */
|
||||
|
||||
std::unique_ptr<PackProfileData> d;
|
||||
|
@ -18,18 +18,6 @@ struct PackProfileData
|
||||
// the launch profile (volatile, temporary thing created on demand)
|
||||
std::shared_ptr<LaunchProfile> m_profile;
|
||||
|
||||
// version information migrated from instance.cfg file. Single use on migration!
|
||||
std::map<QString, QString> m_oldConfigVersions;
|
||||
QString getOldConfigVersion(const QString& uid) const
|
||||
{
|
||||
const auto iter = m_oldConfigVersions.find(uid);
|
||||
if(iter != m_oldConfigVersions.cend())
|
||||
{
|
||||
return (*iter).second;
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
// persistent list of components and related machinery
|
||||
ComponentContainer components;
|
||||
ComponentIndex componentIndex;
|
||||
|
@ -27,19 +27,19 @@ public: /* methods */
|
||||
void applyTo(LaunchProfile* profile);
|
||||
|
||||
public: /* data */
|
||||
/// MultiMC: order hint for this version file if no explicit order is set
|
||||
/// PolyMC: order hint for this version file if no explicit order is set
|
||||
int order = 0;
|
||||
|
||||
/// MultiMC: human readable name of this package
|
||||
/// PolyMC: human readable name of this package
|
||||
QString name;
|
||||
|
||||
/// MultiMC: package ID of this package
|
||||
/// PolyMC: package ID of this package
|
||||
QString uid;
|
||||
|
||||
/// MultiMC: version of this package
|
||||
/// PolyMC: version of this package
|
||||
QString version;
|
||||
|
||||
/// MultiMC: DEPRECATED dependency on a Minecraft version
|
||||
/// PolyMC: DEPRECATED dependency on a Minecraft version
|
||||
QString dependsOnMinecraftVersion;
|
||||
|
||||
/// Mojang: DEPRECATED used to version the Mojang version format
|
||||
@ -51,7 +51,7 @@ public: /* data */
|
||||
/// Mojang: class to launch Minecraft with
|
||||
QString mainClass;
|
||||
|
||||
/// MultiMC: class to launch legacy Minecraft with (embed in a custom window)
|
||||
/// PolyMC: class to launch legacy Minecraft with (embed in a custom window)
|
||||
QString appletClass;
|
||||
|
||||
/// Mojang: Minecraft launch arguments (may contain placeholders for variable substitution)
|
||||
@ -69,35 +69,35 @@ public: /* data */
|
||||
/// Mojang: DEPRECATED asset group to be used with Minecraft
|
||||
QString assets;
|
||||
|
||||
/// MultiMC: list of tweaker mod arguments for launchwrapper
|
||||
/// PolyMC: list of tweaker mod arguments for launchwrapper
|
||||
QStringList addTweakers;
|
||||
|
||||
/// Mojang: list of libraries to add to the version
|
||||
QList<LibraryPtr> libraries;
|
||||
|
||||
/// MultiMC: list of maven files to put in the libraries folder, but not in classpath
|
||||
/// PolyMC: list of maven files to put in the libraries folder, but not in classpath
|
||||
QList<LibraryPtr> mavenFiles;
|
||||
|
||||
/// The main jar (Minecraft version library, normally)
|
||||
LibraryPtr mainJar;
|
||||
|
||||
/// MultiMC: list of attached traits of this version file - used to enable features
|
||||
/// PolyMC: list of attached traits of this version file - used to enable features
|
||||
QSet<QString> traits;
|
||||
|
||||
/// MultiMC: list of jar mods added to this version
|
||||
/// PolyMC: list of jar mods added to this version
|
||||
QList<LibraryPtr> jarMods;
|
||||
|
||||
/// MultiMC: list of mods added to this version
|
||||
/// PolyMC: list of mods added to this version
|
||||
QList<LibraryPtr> mods;
|
||||
|
||||
/**
|
||||
* MultiMC: set of packages this depends on
|
||||
* PolyMC: set of packages this depends on
|
||||
* NOTE: this is shared with the meta format!!!
|
||||
*/
|
||||
Meta::RequireSet requires;
|
||||
|
||||
/**
|
||||
* MultiMC: set of packages this conflicts with
|
||||
* PolyMC: set of packages this conflicts with
|
||||
* NOTE: this is shared with the meta format!!!
|
||||
*/
|
||||
Meta::RequireSet conflicts;
|
||||
@ -112,3 +112,4 @@ public:
|
||||
// Mojang: extended asset index download information
|
||||
std::shared_ptr<MojangAssetIndexInfo> mojangAssetIndex;
|
||||
};
|
||||
|
||||
|
@ -26,9 +26,9 @@
|
||||
#include <io/stream_reader.h>
|
||||
#include <tag_string.h>
|
||||
#include <tag_primitive.h>
|
||||
#include <quazip.h>
|
||||
#include <quazipfile.h>
|
||||
#include <quazipdir.h>
|
||||
#include <quazip/quazip.h>
|
||||
#include <quazip/quazipfile.h>
|
||||
#include <quazip/quazipdir.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
|
@ -314,6 +314,8 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
|
||||
type = AccountType::MSA;
|
||||
} else if (typeS == "Mojang") {
|
||||
type = AccountType::Mojang;
|
||||
} else if (typeS == "Offline") {
|
||||
type = AccountType::Offline;
|
||||
} else {
|
||||
qWarning() << "Failed to parse account data: type is not recognized.";
|
||||
return false;
|
||||
@ -325,6 +327,10 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
|
||||
}
|
||||
|
||||
if(type == AccountType::MSA) {
|
||||
auto clientIDV = data.value("msa-client-id");
|
||||
if (clientIDV.isString()) {
|
||||
msaClientID = clientIDV.toString();
|
||||
} // leave msaClientID empty if it doesn't exist or isn't a string
|
||||
msaToken = tokenFromJSONV3(data, "msa");
|
||||
userToken = tokenFromJSONV3(data, "utoken");
|
||||
xboxApiToken = tokenFromJSONV3(data, "xrp-main");
|
||||
@ -358,11 +364,15 @@ QJsonObject AccountData::saveState() const {
|
||||
}
|
||||
else if (type == AccountType::MSA) {
|
||||
output["type"] = "MSA";
|
||||
output["msa-client-id"] = msaClientID;
|
||||
tokenToJSONV3(output, msaToken, "msa");
|
||||
tokenToJSONV3(output, userToken, "utoken");
|
||||
tokenToJSONV3(output, xboxApiToken, "xrp-main");
|
||||
tokenToJSONV3(output, mojangservicesToken, "xrp-mc");
|
||||
}
|
||||
else if (type == AccountType::Offline) {
|
||||
output["type"] = "Offline";
|
||||
}
|
||||
|
||||
tokenToJSONV3(output, yggdrasilToken, "ygg");
|
||||
profileToJSONV3(output, minecraftProfile, "profile");
|
||||
@ -371,7 +381,7 @@ QJsonObject AccountData::saveState() const {
|
||||
}
|
||||
|
||||
QString AccountData::userName() const {
|
||||
if(type != AccountType::Mojang) {
|
||||
if(type == AccountType::MSA) {
|
||||
return QString();
|
||||
}
|
||||
return yggdrasilToken.extra["userName"].toString();
|
||||
@ -427,6 +437,9 @@ QString AccountData::accountDisplayString() const {
|
||||
case AccountType::Mojang: {
|
||||
return userName();
|
||||
}
|
||||
case AccountType::Offline: {
|
||||
return userName();
|
||||
}
|
||||
case AccountType::MSA: {
|
||||
if(xboxApiToken.extra.contains("gtg")) {
|
||||
return xboxApiToken.extra["gtg"].toString();
|
||||
|
@ -38,7 +38,8 @@ struct MinecraftProfile {
|
||||
|
||||
enum class AccountType {
|
||||
MSA,
|
||||
Mojang
|
||||
Mojang,
|
||||
Offline
|
||||
};
|
||||
|
||||
enum class AccountState {
|
||||
@ -46,6 +47,7 @@ enum class AccountState {
|
||||
Offline,
|
||||
Working,
|
||||
Online,
|
||||
Disabled,
|
||||
Errored,
|
||||
Expired,
|
||||
Gone
|
||||
@ -80,6 +82,7 @@ struct AccountData {
|
||||
bool legacy = false;
|
||||
bool canMigrateToMSA = false;
|
||||
|
||||
QString msaClientID;
|
||||
Katabasis::Token msaToken;
|
||||
Katabasis::Token userToken;
|
||||
Katabasis::Token xboxApiToken;
|
||||
|
@ -291,6 +291,9 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
|
||||
case AccountState::Expired: {
|
||||
return tr("Expired", "Account status");
|
||||
}
|
||||
case AccountState::Disabled: {
|
||||
return tr("Disabled", "Account status");
|
||||
}
|
||||
case AccountState::Gone: {
|
||||
return tr("Gone", "Account status");
|
||||
}
|
||||
@ -302,7 +305,7 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
|
||||
case MigrationColumn: {
|
||||
if(account->isMSA()) {
|
||||
if(account->isMSA() || account->isOffline()) {
|
||||
return tr("N/A", "Can Migrate?");
|
||||
}
|
||||
if (account->canMigrate()) {
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
/*!
|
||||
* List of available Mojang accounts.
|
||||
* This should be loaded in the background by MultiMC on startup.
|
||||
* This should be loaded in the background by PolyMC on startup.
|
||||
*/
|
||||
class AccountList : public QAbstractListModel
|
||||
{
|
||||
@ -158,3 +158,4 @@ protected:
|
||||
*/
|
||||
bool m_autosave = false;
|
||||
};
|
||||
|
||||
|
@ -43,6 +43,8 @@ QString AccountTask::getStateMessage() const
|
||||
return tr("Authentication task succeeded.");
|
||||
case AccountTaskState::STATE_OFFLINE:
|
||||
return tr("Failed to contact the authentication server.");
|
||||
case AccountTaskState::STATE_DISABLED:
|
||||
return tr("Client ID has changed. New session needs to be created.");
|
||||
case AccountTaskState::STATE_FAILED_SOFT:
|
||||
return tr("Encountered an error during authentication.");
|
||||
case AccountTaskState::STATE_FAILED_HARD:
|
||||
@ -78,6 +80,12 @@ bool AccountTask::changeState(AccountTaskState newState, QString reason)
|
||||
emitFailed(reason);
|
||||
return false;
|
||||
}
|
||||
case AccountTaskState::STATE_DISABLED: {
|
||||
m_data->errorString = reason;
|
||||
m_data->accountState = AccountState::Disabled;
|
||||
emitFailed(reason);
|
||||
return false;
|
||||
}
|
||||
case AccountTaskState::STATE_FAILED_SOFT: {
|
||||
m_data->errorString = reason;
|
||||
m_data->accountState = AccountState::Errored;
|
||||
|
@ -35,6 +35,7 @@ enum class AccountTaskState
|
||||
STATE_CREATED,
|
||||
STATE_WORKING,
|
||||
STATE_SUCCEEDED,
|
||||
STATE_DISABLED, //!< MSA Client ID has changed. Tell user to reloginn
|
||||
STATE_FAILED_SOFT, //!< soft failure. authentication went through partially
|
||||
STATE_FAILED_HARD, //!< hard failure. main tokens are invalid
|
||||
STATE_FAILED_GONE, //!< hard failure. main tokens are invalid, and the account no longer exists
|
||||
|
@ -44,7 +44,7 @@ void AuthRequest::onRequestFinished() {
|
||||
if (reply_ != qobject_cast<QNetworkReply *>(sender())) {
|
||||
return;
|
||||
}
|
||||
httpStatus_ = 200;
|
||||
httpStatus_ = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
finish();
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#include "flows/MSA.h"
|
||||
#include "flows/Mojang.h"
|
||||
#include "flows/Offline.h"
|
||||
|
||||
MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) {
|
||||
data.internalId = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
|
||||
@ -68,6 +69,23 @@ MinecraftAccountPtr MinecraftAccount::createBlankMSA()
|
||||
return account;
|
||||
}
|
||||
|
||||
MinecraftAccountPtr MinecraftAccount::createOffline(const QString &username)
|
||||
{
|
||||
MinecraftAccountPtr account = new MinecraftAccount();
|
||||
account->data.type = AccountType::Offline;
|
||||
account->data.yggdrasilToken.token = "offline";
|
||||
account->data.yggdrasilToken.validity = Katabasis::Validity::Certain;
|
||||
account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc();
|
||||
account->data.yggdrasilToken.extra["userName"] = username;
|
||||
account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
|
||||
account->data.minecraftEntitlement.ownsMinecraft = true;
|
||||
account->data.minecraftEntitlement.canPlayMinecraft = true;
|
||||
account->data.minecraftProfile.id = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
|
||||
account->data.minecraftProfile.name = username;
|
||||
account->data.minecraftProfile.validity = Katabasis::Validity::Certain;
|
||||
return account;
|
||||
}
|
||||
|
||||
|
||||
QJsonObject MinecraftAccount::saveToJson() const
|
||||
{
|
||||
@ -111,6 +129,16 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::loginMSA() {
|
||||
return m_currentTask;
|
||||
}
|
||||
|
||||
shared_qobject_ptr<AccountTask> MinecraftAccount::loginOffline() {
|
||||
Q_ASSERT(m_currentTask.get() == nullptr);
|
||||
|
||||
m_currentTask.reset(new OfflineLogin(&data));
|
||||
connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
|
||||
connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
|
||||
emit activityChanged(true);
|
||||
return m_currentTask;
|
||||
}
|
||||
|
||||
shared_qobject_ptr<AccountTask> MinecraftAccount::refresh() {
|
||||
if(m_currentTask) {
|
||||
return m_currentTask;
|
||||
@ -119,6 +147,9 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::refresh() {
|
||||
if(data.type == AccountType::MSA) {
|
||||
m_currentTask.reset(new MSASilent(&data));
|
||||
}
|
||||
else if(data.type == AccountType::Offline) {
|
||||
m_currentTask.reset(new OfflineRefresh(&data));
|
||||
}
|
||||
else {
|
||||
m_currentTask.reset(new MojangRefresh(&data));
|
||||
}
|
||||
@ -145,6 +176,9 @@ void MinecraftAccount::authFailed(QString reason)
|
||||
{
|
||||
switch (m_currentTask->taskState()) {
|
||||
case AccountTaskState::STATE_OFFLINE:
|
||||
case AccountTaskState::STATE_DISABLED: {
|
||||
// NOTE: user will need to fix this themselves.
|
||||
}
|
||||
case AccountTaskState::STATE_FAILED_SOFT: {
|
||||
// NOTE: this doesn't do much. There was an error of some sort.
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ Q_DECLARE_METATYPE(MinecraftAccountPtr)
|
||||
* A profile within someone's Mojang account.
|
||||
*
|
||||
* Currently, the profile system has not been implemented by Mojang yet,
|
||||
* but we might as well add some things for it in MultiMC right now so
|
||||
* but we might as well add some things for it in PolyMC right now so
|
||||
* we don't have to rip the code to pieces to add it later.
|
||||
*/
|
||||
struct AccountProfile
|
||||
@ -73,6 +73,8 @@ public: /* construction */
|
||||
|
||||
static MinecraftAccountPtr createBlankMSA();
|
||||
|
||||
static MinecraftAccountPtr createOffline(const QString &username);
|
||||
|
||||
static MinecraftAccountPtr loadFromJsonV2(const QJsonObject &json);
|
||||
static MinecraftAccountPtr loadFromJsonV3(const QJsonObject &json);
|
||||
|
||||
@ -89,6 +91,8 @@ public: /* manipulation */
|
||||
|
||||
shared_qobject_ptr<AccountTask> loginMSA();
|
||||
|
||||
shared_qobject_ptr<AccountTask> loginOffline();
|
||||
|
||||
shared_qobject_ptr<AccountTask> refresh();
|
||||
|
||||
shared_qobject_ptr<AccountTask> currentTask();
|
||||
@ -128,6 +132,10 @@ public: /* queries */
|
||||
return data.type == AccountType::MSA;
|
||||
}
|
||||
|
||||
bool isOffline() const {
|
||||
return data.type == AccountType::Offline;
|
||||
}
|
||||
|
||||
bool ownsMinecraft() const {
|
||||
return data.minecraftEntitlement.ownsMinecraft;
|
||||
}
|
||||
@ -149,6 +157,10 @@ public: /* queries */
|
||||
return "msa";
|
||||
}
|
||||
break;
|
||||
case AccountType::Offline: {
|
||||
return "offline";
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
return "unknown";
|
||||
}
|
||||
@ -198,3 +210,4 @@ slots:
|
||||
void authSucceeded();
|
||||
void authFailed(QString reason);
|
||||
};
|
||||
|
||||
|
@ -94,7 +94,7 @@ bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output, QString na
|
||||
return false;
|
||||
}
|
||||
if(!getString(obj.value("Token"), output.token)) {
|
||||
qWarning() << "User Token is not a timestamp";
|
||||
qWarning() << "User Token is not a string";
|
||||
return false;
|
||||
}
|
||||
auto arrayVal = obj.value("DisplayClaims").toObject().value("xui");
|
||||
|
17
launcher/minecraft/auth/flows/Offline.cpp
Normal file
17
launcher/minecraft/auth/flows/Offline.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "Offline.h"
|
||||
|
||||
#include "minecraft/auth/steps/OfflineStep.h"
|
||||
|
||||
OfflineRefresh::OfflineRefresh(
|
||||
AccountData *data,
|
||||
QObject *parent
|
||||
) : AuthFlow(data, parent) {
|
||||
m_steps.append(new OfflineStep(m_data));
|
||||
}
|
||||
|
||||
OfflineLogin::OfflineLogin(
|
||||
AccountData *data,
|
||||
QObject *parent
|
||||
) : AuthFlow(data, parent) {
|
||||
m_steps.append(new OfflineStep(m_data));
|
||||
}
|
22
launcher/minecraft/auth/flows/Offline.h
Normal file
22
launcher/minecraft/auth/flows/Offline.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "AuthFlow.h"
|
||||
|
||||
class OfflineRefresh : public AuthFlow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit OfflineRefresh(
|
||||
AccountData *data,
|
||||
QObject *parent = 0
|
||||
);
|
||||
};
|
||||
|
||||
class OfflineLogin : public AuthFlow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit OfflineLogin(
|
||||
AccountData *data,
|
||||
QObject *parent = 0
|
||||
);
|
||||
};
|
@ -12,9 +12,10 @@ using OAuth2 = Katabasis::DeviceFlow;
|
||||
using Activity = Katabasis::Activity;
|
||||
|
||||
MSAStep::MSAStep(AccountData* data, Action action) : AuthStep(data), m_action(action) {
|
||||
m_clientId = APPLICATION->getMSAClientID();
|
||||
OAuth2::Options opts;
|
||||
opts.scope = "XboxLive.signin offline_access";
|
||||
opts.clientIdentifier = BuildConfig.MSA_CLIENT_ID;
|
||||
opts.clientIdentifier = m_clientId;
|
||||
opts.authorizationUrl = "https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode";
|
||||
opts.accessTokenUrl = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token";
|
||||
|
||||
@ -48,6 +49,10 @@ void MSAStep::rehydrate() {
|
||||
void MSAStep::perform() {
|
||||
switch(m_action) {
|
||||
case Refresh: {
|
||||
if (m_data->msaClientID != m_clientId) {
|
||||
emit hideVerificationUriAndCode();
|
||||
emit finished(AccountTaskState::STATE_DISABLED, tr("Microsoft user authentication failed - client identification has changed."));
|
||||
}
|
||||
m_oauth2->refresh();
|
||||
return;
|
||||
}
|
||||
@ -57,6 +62,7 @@ void MSAStep::perform() {
|
||||
m_oauth2->setExtraRequestParams(extraOpts);
|
||||
|
||||
*m_data = AccountData();
|
||||
m_data->msaClientID = m_clientId;
|
||||
m_oauth2->login();
|
||||
return;
|
||||
}
|
||||
|
@ -29,4 +29,5 @@ private slots:
|
||||
private:
|
||||
Katabasis::DeviceFlow *m_oauth2 = nullptr;
|
||||
Action m_action;
|
||||
QString m_clientId;
|
||||
};
|
||||
|
@ -56,6 +56,14 @@ void MinecraftProfileStep::onRequestDone(
|
||||
return;
|
||||
}
|
||||
if (error != QNetworkReply::NoError) {
|
||||
qWarning() << "Error getting profile:";
|
||||
qWarning() << " HTTP Status: " << requestor->httpStatus_;
|
||||
qWarning() << " Internal error no.: " << error;
|
||||
qWarning() << " Error string: " << requestor->errorString_;
|
||||
|
||||
qWarning() << " Response:";
|
||||
qWarning() << QString::fromUtf8(data);
|
||||
|
||||
emit finished(
|
||||
AccountTaskState::STATE_FAILED_SOFT,
|
||||
tr("Minecraft Java profile acquisition failed.")
|
||||
|
18
launcher/minecraft/auth/steps/OfflineStep.cpp
Normal file
18
launcher/minecraft/auth/steps/OfflineStep.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
#include "OfflineStep.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
OfflineStep::OfflineStep(AccountData* data) : AuthStep(data) {}
|
||||
OfflineStep::~OfflineStep() noexcept = default;
|
||||
|
||||
QString OfflineStep::describe() {
|
||||
return tr("Creating offline account.");
|
||||
}
|
||||
|
||||
void OfflineStep::rehydrate() {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
void OfflineStep::perform() {
|
||||
emit finished(AccountTaskState::STATE_WORKING, tr("Created offline account."));
|
||||
}
|
19
launcher/minecraft/auth/steps/OfflineStep.h
Normal file
19
launcher/minecraft/auth/steps/OfflineStep.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include <QObject>
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
#include "minecraft/auth/AuthStep.h"
|
||||
|
||||
#include <katabasis/DeviceFlow.h>
|
||||
|
||||
class OfflineStep : public AuthStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit OfflineStep(AccountData *data);
|
||||
virtual ~OfflineStep() noexcept;
|
||||
|
||||
void perform() override;
|
||||
void rehydrate() override;
|
||||
|
||||
QString describe() override;
|
||||
};
|
@ -17,8 +17,8 @@
|
||||
#include <minecraft/MinecraftInstance.h>
|
||||
#include <launch/LaunchTask.h>
|
||||
|
||||
#include <quazip.h>
|
||||
#include <quazipdir.h>
|
||||
#include <quazip/quazip.h>
|
||||
#include <quazip/quazipdir.h>
|
||||
#include "MMCZip.h"
|
||||
#include "FileSystem.h"
|
||||
#include <QDir>
|
||||
|
@ -25,6 +25,19 @@
|
||||
|
||||
LauncherPartLaunch::LauncherPartLaunch(LaunchTask *parent) : LaunchStep(parent)
|
||||
{
|
||||
if (APPLICATION->settings()->get("CloseAfterLaunch").toBool())
|
||||
{
|
||||
std::shared_ptr<QMetaObject::Connection> connection{new QMetaObject::Connection};
|
||||
*connection = connect(&m_process, &LoggedProcess::log, this, [=](QStringList lines, MessageLevel::Enum level) {
|
||||
qDebug() << lines;
|
||||
if (lines.filter(QRegularExpression(".*Setting user.+", QRegularExpression::CaseInsensitiveOption)).length() != 0)
|
||||
{
|
||||
APPLICATION->closeAllWindows();
|
||||
disconnect(*connection);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
connect(&m_process, &LoggedProcess::log, this, &LauncherPartLaunch::logLines);
|
||||
connect(&m_process, &LoggedProcess::stateChanged, this, &LauncherPartLaunch::on_state);
|
||||
}
|
||||
@ -155,6 +168,8 @@ void LauncherPartLaunch::on_state(LoggedProcess::State state)
|
||||
}
|
||||
case LoggedProcess::Finished:
|
||||
{
|
||||
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();
|
||||
|
@ -23,7 +23,7 @@ MinecraftServerTarget MinecraftServerTarget::parse(const QString &fullAddress) {
|
||||
|
||||
// The logic below replicates the exact logic minecraft uses for parsing server addresses.
|
||||
// While the conversion is not lossless and eats errors, it ensures the same behavior
|
||||
// within Minecraft and MultiMC when entering server addresses.
|
||||
// within Minecraft and PolyMC when entering server addresses.
|
||||
if (fullAddress.startsWith("["))
|
||||
{
|
||||
int bracket = fullAddress.indexOf("]");
|
||||
|
@ -1,256 +0,0 @@
|
||||
/* 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 <QFileInfo>
|
||||
#include <minecraft/launch/LauncherPartLaunch.h>
|
||||
#include <QDir>
|
||||
#include <settings/Setting.h>
|
||||
|
||||
#include "LegacyInstance.h"
|
||||
|
||||
#include "minecraft/legacy/LegacyModList.h"
|
||||
#include "minecraft/WorldList.h"
|
||||
#include <MMCZip.h>
|
||||
#include <FileSystem.h>
|
||||
|
||||
LegacyInstance::LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir)
|
||||
: BaseInstance(globalSettings, settings, rootDir)
|
||||
{
|
||||
settings->registerSetting("NeedsRebuild", true);
|
||||
settings->registerSetting("ShouldUpdate", false);
|
||||
settings->registerSetting("JarVersion", QString());
|
||||
settings->registerSetting("IntendedJarVersion", QString());
|
||||
/*
|
||||
* custom base jar has no default. it is determined in code... see the accessor methods for
|
||||
*it
|
||||
*
|
||||
* for instances that DO NOT have the CustomBaseJar setting (legacy instances),
|
||||
* [.]minecraft/bin/mcbackup.jar is the default base jar
|
||||
*/
|
||||
settings->registerSetting("UseCustomBaseJar", true);
|
||||
settings->registerSetting("CustomBaseJar", "");
|
||||
}
|
||||
|
||||
QString LegacyInstance::mainJarToPreserve() const
|
||||
{
|
||||
bool customJar = m_settings->get("UseCustomBaseJar").toBool();
|
||||
if(customJar)
|
||||
{
|
||||
auto base = baseJar();
|
||||
if(QFile::exists(base))
|
||||
{
|
||||
return base;
|
||||
}
|
||||
}
|
||||
auto runnable = runnableJar();
|
||||
if(QFile::exists(runnable))
|
||||
{
|
||||
return runnable;
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
|
||||
QString LegacyInstance::baseJar() const
|
||||
{
|
||||
bool customJar = m_settings->get("UseCustomBaseJar").toBool();
|
||||
if (customJar)
|
||||
{
|
||||
return customBaseJar();
|
||||
}
|
||||
else
|
||||
return defaultBaseJar();
|
||||
}
|
||||
|
||||
QString LegacyInstance::customBaseJar() const
|
||||
{
|
||||
QString value = m_settings->get("CustomBaseJar").toString();
|
||||
if (value.isNull() || value.isEmpty())
|
||||
{
|
||||
return defaultCustomBaseJar();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
bool LegacyInstance::shouldUseCustomBaseJar() const
|
||||
{
|
||||
return m_settings->get("UseCustomBaseJar").toBool();
|
||||
}
|
||||
|
||||
|
||||
Task::Ptr LegacyInstance::createUpdateTask(Net::Mode)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<LegacyModList> LegacyInstance::jarModList() const
|
||||
{
|
||||
if (!jar_mod_list)
|
||||
{
|
||||
auto list = new LegacyModList(jarModsDir(), modListFile());
|
||||
jar_mod_list.reset(list);
|
||||
}
|
||||
jar_mod_list->update();
|
||||
return jar_mod_list;
|
||||
}
|
||||
|
||||
QString LegacyInstance::gameRoot() const
|
||||
{
|
||||
QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
|
||||
QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft"));
|
||||
|
||||
if (mcDir.exists() && !dotMCDir.exists())
|
||||
return mcDir.filePath();
|
||||
else
|
||||
return dotMCDir.filePath();
|
||||
}
|
||||
|
||||
QString LegacyInstance::binRoot() const
|
||||
{
|
||||
return FS::PathCombine(gameRoot(), "bin");
|
||||
}
|
||||
|
||||
QString LegacyInstance::modsRoot() const {
|
||||
return FS::PathCombine(gameRoot(), "mods");
|
||||
}
|
||||
|
||||
|
||||
QString LegacyInstance::jarModsDir() const
|
||||
{
|
||||
return FS::PathCombine(instanceRoot(), "instMods");
|
||||
}
|
||||
|
||||
QString LegacyInstance::libDir() const
|
||||
{
|
||||
return FS::PathCombine(gameRoot(), "lib");
|
||||
}
|
||||
|
||||
QString LegacyInstance::savesDir() const
|
||||
{
|
||||
return FS::PathCombine(gameRoot(), "saves");
|
||||
}
|
||||
|
||||
QString LegacyInstance::coreModsDir() const
|
||||
{
|
||||
return FS::PathCombine(gameRoot(), "coremods");
|
||||
}
|
||||
|
||||
QString LegacyInstance::resourceDir() const
|
||||
{
|
||||
return FS::PathCombine(gameRoot(), "resources");
|
||||
}
|
||||
QString LegacyInstance::texturePacksDir() const
|
||||
{
|
||||
return FS::PathCombine(gameRoot(), "texturepacks");
|
||||
}
|
||||
|
||||
QString LegacyInstance::runnableJar() const
|
||||
{
|
||||
return FS::PathCombine(binRoot(), "minecraft.jar");
|
||||
}
|
||||
|
||||
QString LegacyInstance::modListFile() const
|
||||
{
|
||||
return FS::PathCombine(instanceRoot(), "modlist");
|
||||
}
|
||||
|
||||
QString LegacyInstance::instanceConfigFolder() const
|
||||
{
|
||||
return FS::PathCombine(gameRoot(), "config");
|
||||
}
|
||||
|
||||
bool LegacyInstance::shouldRebuild() const
|
||||
{
|
||||
return m_settings->get("NeedsRebuild").toBool();
|
||||
}
|
||||
|
||||
QString LegacyInstance::currentVersionId() const
|
||||
{
|
||||
return m_settings->get("JarVersion").toString();
|
||||
}
|
||||
|
||||
QString LegacyInstance::intendedVersionId() const
|
||||
{
|
||||
return m_settings->get("IntendedJarVersion").toString();
|
||||
}
|
||||
|
||||
bool LegacyInstance::shouldUpdate() const
|
||||
{
|
||||
QVariant var = settings()->get("ShouldUpdate");
|
||||
if (!var.isValid() || var.toBool() == false)
|
||||
{
|
||||
return intendedVersionId() != currentVersionId();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QString LegacyInstance::defaultBaseJar() const
|
||||
{
|
||||
return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar";
|
||||
}
|
||||
|
||||
QString LegacyInstance::defaultCustomBaseJar() const
|
||||
{
|
||||
return FS::PathCombine(binRoot(), "mcbackup.jar");
|
||||
}
|
||||
|
||||
std::shared_ptr<WorldList> LegacyInstance::worldList() const
|
||||
{
|
||||
if (!m_world_list)
|
||||
{
|
||||
m_world_list.reset(new WorldList(savesDir()));
|
||||
}
|
||||
return m_world_list;
|
||||
}
|
||||
|
||||
QString LegacyInstance::typeName() const
|
||||
{
|
||||
return tr("Legacy");
|
||||
}
|
||||
|
||||
QString LegacyInstance::getStatusbarDescription()
|
||||
{
|
||||
return tr("Instance from previous versions.");
|
||||
}
|
||||
|
||||
QStringList LegacyInstance::verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
|
||||
{
|
||||
QStringList out;
|
||||
|
||||
auto alltraits = traits();
|
||||
if(alltraits.size())
|
||||
{
|
||||
out << "Traits:";
|
||||
for (auto trait : alltraits)
|
||||
{
|
||||
out << " " + trait;
|
||||
}
|
||||
out << "";
|
||||
}
|
||||
|
||||
QString windowParams;
|
||||
if (settings()->get("LaunchMaximized").toBool())
|
||||
{
|
||||
out << "Window size: max (if available)";
|
||||
}
|
||||
else
|
||||
{
|
||||
auto width = settings()->get("MinecraftWinWidth").toInt();
|
||||
auto height = settings()->get("MinecraftWinHeight").toInt();
|
||||
out << "Window size: " + QString::number(width) + " x " + QString::number(height);
|
||||
}
|
||||
out << "";
|
||||
return out;
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BaseInstance.h"
|
||||
#include "launch/LaunchTask.h"
|
||||
|
||||
class ModFolderModel;
|
||||
class LegacyModList;
|
||||
class WorldList;
|
||||
class Task;
|
||||
/*
|
||||
* WHY: Legacy instances - from MultiMC 3 and 4 - are here only to provide a way to upgrade them to the current format.
|
||||
*/
|
||||
class LegacyInstance : public BaseInstance
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
explicit LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
|
||||
|
||||
virtual void saveNow() override {}
|
||||
|
||||
/// Path to the instance's minecraft.jar
|
||||
QString runnableJar() const;
|
||||
|
||||
//! Path to the instance's modlist file.
|
||||
QString modListFile() const;
|
||||
|
||||
////// Directories //////
|
||||
QString libDir() const;
|
||||
QString savesDir() const;
|
||||
QString texturePacksDir() const;
|
||||
QString jarModsDir() const;
|
||||
QString coreModsDir() const;
|
||||
QString resourceDir() const;
|
||||
|
||||
QString instanceConfigFolder() const override;
|
||||
|
||||
QString gameRoot() const override; // Path to the instance's minecraft directory.
|
||||
QString modsRoot() const override; // Path to the instance's minecraft directory.
|
||||
QString binRoot() const; // Path to the instance's minecraft bin directory.
|
||||
|
||||
/// Get the curent base jar of this instance. By default, it's the
|
||||
/// versions/$version/$version.jar
|
||||
QString baseJar() const;
|
||||
|
||||
/// the default base jar of this instance
|
||||
QString defaultBaseJar() const;
|
||||
/// the default custom base jar of this instance
|
||||
QString defaultCustomBaseJar() const;
|
||||
|
||||
// the main jar that we actually want to keep when migrating the instance
|
||||
QString mainJarToPreserve() const;
|
||||
|
||||
/*!
|
||||
* Whether or not custom base jar is used
|
||||
*/
|
||||
bool shouldUseCustomBaseJar() const;
|
||||
|
||||
/*!
|
||||
* The value of the custom base jar
|
||||
*/
|
||||
QString customBaseJar() const;
|
||||
|
||||
std::shared_ptr<LegacyModList> jarModList() const;
|
||||
std::shared_ptr<WorldList> worldList() const;
|
||||
|
||||
/*!
|
||||
* Whether or not the instance's minecraft.jar needs to be rebuilt.
|
||||
* If this is true, when the instance launches, its jar mods will be
|
||||
* re-added to a fresh minecraft.jar file.
|
||||
*/
|
||||
bool shouldRebuild() const;
|
||||
|
||||
QString currentVersionId() const;
|
||||
QString intendedVersionId() const;
|
||||
|
||||
QSet<QString> traits() const override
|
||||
{
|
||||
return {"legacy-instance", "texturepacks"};
|
||||
};
|
||||
|
||||
virtual bool shouldUpdate() const;
|
||||
virtual Task::Ptr createUpdateTask(Net::Mode mode) override;
|
||||
|
||||
virtual QString typeName() const override;
|
||||
|
||||
bool canLaunch() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool canEdit() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool canExport() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(
|
||||
AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
IPathMatcher::Ptr getLogFileMatcher() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
QString getLogFileRoot() override
|
||||
{
|
||||
return gameRoot();
|
||||
}
|
||||
|
||||
QString getStatusbarDescription() override;
|
||||
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override;
|
||||
|
||||
QProcessEnvironment createEnvironment() override
|
||||
{
|
||||
return QProcessEnvironment();
|
||||
}
|
||||
QMap<QString, QString> getVariables() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
protected:
|
||||
mutable std::shared_ptr<LegacyModList> jar_mod_list;
|
||||
mutable std::shared_ptr<WorldList> m_world_list;
|
||||
};
|
@ -1,136 +0,0 @@
|
||||
/* 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 "LegacyModList.h"
|
||||
#include <FileSystem.h>
|
||||
#include <QString>
|
||||
#include <QDebug>
|
||||
|
||||
LegacyModList::LegacyModList(const QString &dir, const QString &list_file)
|
||||
: m_dir(dir), m_list_file(list_file)
|
||||
{
|
||||
FS::ensureFolderPathExists(m_dir.absolutePath());
|
||||
m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs);
|
||||
m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
|
||||
}
|
||||
|
||||
struct OrderItem
|
||||
{
|
||||
QString id;
|
||||
bool enabled = false;
|
||||
};
|
||||
typedef QList<OrderItem> OrderList;
|
||||
|
||||
static void internalSort(QList<LegacyModList::Mod> &what)
|
||||
{
|
||||
auto predicate = [](const LegacyModList::Mod &left, const LegacyModList::Mod &right)
|
||||
{
|
||||
return left.fileName().localeAwareCompare(right.fileName()) < 0;
|
||||
};
|
||||
std::sort(what.begin(), what.end(), predicate);
|
||||
}
|
||||
|
||||
static OrderList readListFile(const QString &m_list_file)
|
||||
{
|
||||
OrderList itemList;
|
||||
if (m_list_file.isNull() || m_list_file.isEmpty())
|
||||
return itemList;
|
||||
|
||||
QFile textFile(m_list_file);
|
||||
if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
return OrderList();
|
||||
|
||||
QTextStream textStream;
|
||||
textStream.setAutoDetectUnicode(true);
|
||||
textStream.setDevice(&textFile);
|
||||
while (true)
|
||||
{
|
||||
QString line = textStream.readLine();
|
||||
if (line.isNull() || line.isEmpty())
|
||||
break;
|
||||
else
|
||||
{
|
||||
OrderItem it;
|
||||
it.enabled = !line.endsWith(".disabled");
|
||||
if (!it.enabled)
|
||||
{
|
||||
line.chop(9);
|
||||
}
|
||||
it.id = line;
|
||||
itemList.append(it);
|
||||
}
|
||||
}
|
||||
textFile.close();
|
||||
return itemList;
|
||||
}
|
||||
|
||||
bool LegacyModList::update()
|
||||
{
|
||||
if (!m_dir.exists() || !m_dir.isReadable())
|
||||
return false;
|
||||
|
||||
QList<Mod> orderedMods;
|
||||
QList<Mod> newMods;
|
||||
m_dir.refresh();
|
||||
auto folderContents = m_dir.entryInfoList();
|
||||
|
||||
// first, process the ordered items (if any)
|
||||
OrderList listOrder = readListFile(m_list_file);
|
||||
for (auto item : listOrder)
|
||||
{
|
||||
QFileInfo infoEnabled(m_dir.filePath(item.id));
|
||||
QFileInfo infoDisabled(m_dir.filePath(item.id + ".disabled"));
|
||||
int idxEnabled = folderContents.indexOf(infoEnabled);
|
||||
int idxDisabled = folderContents.indexOf(infoDisabled);
|
||||
bool isEnabled;
|
||||
// if both enabled and disabled versions are present, it's a special case...
|
||||
if (idxEnabled >= 0 && idxDisabled >= 0)
|
||||
{
|
||||
// we only process the one we actually have in the order file.
|
||||
// and exactly as we have it.
|
||||
// THIS IS A CORNER CASE
|
||||
isEnabled = item.enabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
// only one is present.
|
||||
// we pick the one that we found.
|
||||
// we assume the mod was enabled/disabled by external means
|
||||
isEnabled = idxEnabled >= 0;
|
||||
}
|
||||
int idx = isEnabled ? idxEnabled : idxDisabled;
|
||||
QFileInfo &info = isEnabled ? infoEnabled : infoDisabled;
|
||||
// if the file from the index file exists
|
||||
if (idx != -1)
|
||||
{
|
||||
// remove from the actual folder contents list
|
||||
folderContents.takeAt(idx);
|
||||
// append the new mod
|
||||
orderedMods.append(info);
|
||||
}
|
||||
}
|
||||
// if there are any untracked files... append them sorted at the end
|
||||
if (folderContents.size())
|
||||
{
|
||||
for (auto entry : folderContents)
|
||||
{
|
||||
newMods.append(entry);
|
||||
}
|
||||
internalSort(newMods);
|
||||
orderedMods.append(newMods);
|
||||
}
|
||||
mods.swap(orderedMods);
|
||||
return true;
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
/* 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 <QList>
|
||||
#include <QString>
|
||||
#include <QDir>
|
||||
|
||||
class LegacyModList
|
||||
{
|
||||
public:
|
||||
|
||||
using Mod = QFileInfo;
|
||||
|
||||
LegacyModList(const QString &dir, const QString &list_file = QString());
|
||||
|
||||
/// Reloads the mod list and returns true if the list changed.
|
||||
bool update();
|
||||
|
||||
QDir dir()
|
||||
{
|
||||
return m_dir;
|
||||
}
|
||||
|
||||
const QList<Mod> & allMods()
|
||||
{
|
||||
return mods;
|
||||
}
|
||||
|
||||
protected:
|
||||
QDir m_dir;
|
||||
QString m_list_file;
|
||||
QList<Mod> mods;
|
||||
};
|
@ -1,138 +0,0 @@
|
||||
#include "LegacyUpgradeTask.h"
|
||||
#include "settings/INISettingsObject.h"
|
||||
#include "FileSystem.h"
|
||||
#include "NullInstance.h"
|
||||
#include "pathmatcher/RegexpMatcher.h"
|
||||
#include <QtConcurrentRun>
|
||||
#include "LegacyInstance.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "LegacyModList.h"
|
||||
#include "classparser.h"
|
||||
|
||||
LegacyUpgradeTask::LegacyUpgradeTask(InstancePtr origInstance)
|
||||
{
|
||||
m_origInstance = origInstance;
|
||||
}
|
||||
|
||||
void LegacyUpgradeTask::executeTask()
|
||||
{
|
||||
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
|
||||
|
||||
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
|
||||
folderCopy.followSymlinks(true);
|
||||
|
||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy);
|
||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &LegacyUpgradeTask::copyFinished);
|
||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &LegacyUpgradeTask::copyAborted);
|
||||
m_copyFutureWatcher.setFuture(m_copyFuture);
|
||||
}
|
||||
|
||||
static QString decideVersion(const QString& currentVersion, const QString& intendedVersion)
|
||||
{
|
||||
if(intendedVersion != currentVersion)
|
||||
{
|
||||
if(!intendedVersion.isEmpty())
|
||||
{
|
||||
return intendedVersion;
|
||||
}
|
||||
else if(!currentVersion.isEmpty())
|
||||
{
|
||||
return currentVersion;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!intendedVersion.isEmpty())
|
||||
{
|
||||
return intendedVersion;
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
void LegacyUpgradeTask::copyFinished()
|
||||
{
|
||||
auto successful = m_copyFuture.result();
|
||||
if(!successful)
|
||||
{
|
||||
emitFailed(tr("Instance folder copy failed."));
|
||||
return;
|
||||
}
|
||||
auto legacyInst = std::dynamic_pointer_cast<LegacyInstance>(m_origInstance);
|
||||
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||
instanceSettings->set("InstanceType", "OneSix");
|
||||
// NOTE: this scope ensures the instance is fully saved before we emitSucceeded
|
||||
{
|
||||
MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
|
||||
inst.setName(m_instName);
|
||||
|
||||
QString preferredVersionNumber = decideVersion(legacyInst->currentVersionId(), legacyInst->intendedVersionId());
|
||||
if(preferredVersionNumber.isNull())
|
||||
{
|
||||
// try to decide version based on the jar(s?)
|
||||
preferredVersionNumber = classparser::GetMinecraftJarVersion(legacyInst->baseJar());
|
||||
if(preferredVersionNumber.isNull())
|
||||
{
|
||||
preferredVersionNumber = classparser::GetMinecraftJarVersion(legacyInst->runnableJar());
|
||||
if(preferredVersionNumber.isNull())
|
||||
{
|
||||
emitFailed(tr("Could not decide Minecraft version."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto components = inst.getPackProfile();
|
||||
components->buildingFromScratch();
|
||||
components->setComponentVersion("net.minecraft", preferredVersionNumber, true);
|
||||
|
||||
QString jarPath = legacyInst->mainJarToPreserve();
|
||||
if(!jarPath.isNull())
|
||||
{
|
||||
qDebug() << "Preserving base jar! : " << jarPath;
|
||||
// FIXME: handle case when the jar is unreadable?
|
||||
// TODO: check the hash, if it's the same as the upstream jar, do not do this
|
||||
components->installCustomJar(jarPath);
|
||||
}
|
||||
|
||||
auto jarMods = legacyInst->jarModList()->allMods();
|
||||
for(auto & jarMod: jarMods)
|
||||
{
|
||||
QString modPath = jarMod.absoluteFilePath();
|
||||
qDebug() << "jarMod: " << modPath;
|
||||
components->installJarMods({modPath});
|
||||
}
|
||||
|
||||
// remove all the extra garbage we no longer need
|
||||
auto removeAll = [&](const QString &root, const QStringList &things)
|
||||
{
|
||||
for(auto &thing : things)
|
||||
{
|
||||
auto removePath = FS::PathCombine(root, thing);
|
||||
QFileInfo stat(removePath);
|
||||
if(stat.isDir())
|
||||
{
|
||||
FS::deletePath(removePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
QFile::remove(removePath);
|
||||
}
|
||||
}
|
||||
};
|
||||
QStringList rootRemovables = {"modlist", "version", "instMods"};
|
||||
QStringList mcRemovables = {"bin", "MultiMCLauncher.jar", "icon.png"};
|
||||
removeAll(inst.instanceRoot(), rootRemovables);
|
||||
removeAll(inst.gameRoot(), mcRemovables);
|
||||
}
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
void LegacyUpgradeTask::copyAborted()
|
||||
{
|
||||
emitFailed(tr("Instance folder copy has been aborted."));
|
||||
return;
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "InstanceTask.h"
|
||||
#include "net/NetJob.h"
|
||||
#include <QUrl>
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "BaseVersion.h"
|
||||
#include "BaseInstance.h"
|
||||
|
||||
|
||||
class LegacyUpgradeTask : public InstanceTask
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LegacyUpgradeTask(InstancePtr origInstance);
|
||||
|
||||
protected:
|
||||
//! Entry point for tasks.
|
||||
virtual void executeTask() override;
|
||||
void copyFinished();
|
||||
void copyAborted();
|
||||
|
||||
private: /* data */
|
||||
InstancePtr m_origInstance;
|
||||
QFuture<bool> m_copyFuture;
|
||||
QFutureWatcher<bool> m_copyFutureWatcher;
|
||||
};
|
@ -4,8 +4,8 @@
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonValue>
|
||||
#include <quazip.h>
|
||||
#include <quazipfile.h>
|
||||
#include <quazip/quazip.h>
|
||||
#include <quazip/quazipfile.h>
|
||||
#include <toml.h>
|
||||
|
||||
#include "settings/INIFile.h"
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
#include <quazip.h>
|
||||
#include <quazip/quazip.h>
|
||||
|
||||
#include "MMCZip.h"
|
||||
#include "minecraft/OneSixVersionFormat.h"
|
||||
@ -720,8 +720,6 @@ void PackInstallTask::install()
|
||||
auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
|
||||
instanceSettings->suspendSave();
|
||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||
instanceSettings->set("InstanceType", "OneSix");
|
||||
|
||||
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
||||
auto components = instance.getPackProfile();
|
||||
|
101
launcher/modplatform/flame/FlameModIndex.cpp
Normal file
101
launcher/modplatform/flame/FlameModIndex.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
#include <QObject>
|
||||
#include "FlameModIndex.h"
|
||||
#include "Json.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "BaseInstance.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
|
||||
|
||||
void FlameMod::loadIndexedPack(FlameMod::IndexedPack & pack, QJsonObject & obj)
|
||||
{
|
||||
pack.addonId = Json::requireInteger(obj, "id");
|
||||
pack.name = Json::requireString(obj, "name");
|
||||
pack.websiteUrl = Json::ensureString(obj, "websiteUrl", "");
|
||||
pack.description = Json::ensureString(obj, "summary", "");
|
||||
|
||||
bool thumbnailFound = false;
|
||||
auto attachments = Json::requireArray(obj, "attachments");
|
||||
for(auto attachmentRaw: attachments) {
|
||||
auto attachmentObj = Json::requireObject(attachmentRaw);
|
||||
bool isDefault = attachmentObj.value("isDefault").toBool(false);
|
||||
if(isDefault) {
|
||||
thumbnailFound = true;
|
||||
pack.logoName = Json::requireString(attachmentObj, "title");
|
||||
pack.logoUrl = Json::requireString(attachmentObj, "thumbnailUrl");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!thumbnailFound) {
|
||||
throw JSONValidationError(QString("Pack without an icon, skipping: %1").arg(pack.name));
|
||||
}
|
||||
|
||||
|
||||
auto authors = Json::requireArray(obj, "authors");
|
||||
for(auto authorIter: authors) {
|
||||
auto author = Json::requireObject(authorIter);
|
||||
FlameMod::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)
|
||||
{
|
||||
QVector<FlameMod::IndexedVersion> unsortedVersions;
|
||||
bool hasFabric = !((MinecraftInstance *)inst)->getPackProfile()->getComponentVersion("net.fabricmc.fabric-loader").isEmpty();
|
||||
QString mcVersion = ((MinecraftInstance *)inst)->getPackProfile()->getComponentVersion("net.minecraft");
|
||||
|
||||
for(auto versionIter: arr) {
|
||||
auto obj = versionIter.toObject();
|
||||
|
||||
auto versionArray = Json::requireArray(obj, "gameVersion");
|
||||
if (versionArray.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FlameMod::IndexedVersion file;
|
||||
for(auto mcVer : versionArray){
|
||||
file.mcVersion.append(mcVer.toString());
|
||||
}
|
||||
|
||||
file.addonId = pack.addonId;
|
||||
file.fileId = Json::requireInteger(obj, "id");
|
||||
file.date = Json::requireString(obj, "fileDate");
|
||||
file.version = Json::requireString(obj, "displayName");
|
||||
file.downloadUrl = Json::requireString(obj, "downloadUrl");
|
||||
file.fileName = Json::requireString(obj, "fileName");
|
||||
|
||||
auto modules = Json::requireArray(obj, "modules");
|
||||
bool is_valid_fabric_version = false;
|
||||
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
|
||||
// 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"){
|
||||
is_valid_fabric_version = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else break;
|
||||
// NOTE: Since we're not validating forge versions, we can just skip this loop.
|
||||
}
|
||||
|
||||
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
|
||||
return a.date > b.date;
|
||||
};
|
||||
std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate);
|
||||
pack.versions = unsortedVersions;
|
||||
pack.versionsLoaded = true;
|
||||
}
|
50
launcher/modplatform/flame/FlameModIndex.h
Normal file
50
launcher/modplatform/flame/FlameModIndex.h
Normal file
@ -0,0 +1,50 @@
|
||||
//
|
||||
// Created by timoreo on 16/01/2022.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <QList>
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QObjectPtr.h>
|
||||
#include "net/NetJob.h"
|
||||
#include "BaseInstance.h"
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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)
|
@ -122,8 +122,6 @@ void PackInstallTask::install()
|
||||
QString instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
|
||||
instanceSettings->suspendSave();
|
||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||
instanceSettings->set("InstanceType", "OneSix");
|
||||
|
||||
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
||||
auto components = instance.getPackProfile();
|
||||
|
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
#include "InstanceTask.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "quazip.h"
|
||||
#include "quazipdir.h"
|
||||
#include <quazip/quazip.h>
|
||||
#include <quazip/quazipdir.h>
|
||||
#include "meta/Index.h"
|
||||
#include "meta/Version.h"
|
||||
#include "meta/VersionList.h"
|
||||
|
@ -181,8 +181,6 @@ void PackInstallTask::install()
|
||||
auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
|
||||
instanceSettings->suspendSave();
|
||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||
instanceSettings->set("InstanceType", "OneSix");
|
||||
|
||||
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
||||
auto components = instance.getPackProfile();
|
||||
|
95
launcher/modplatform/modrinth/ModrinthPackIndex.cpp
Normal file
95
launcher/modplatform/modrinth/ModrinthPackIndex.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
#include <QObject>
|
||||
#include "ModrinthPackIndex.h"
|
||||
|
||||
#include "Json.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "BaseInstance.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
|
||||
|
||||
void Modrinth::loadIndexedPack(Modrinth::IndexedPack & pack, QJsonObject & obj)
|
||||
{
|
||||
pack.addonId = Json::requireString(obj, "project_id");
|
||||
pack.name = Json::requireString(obj, "title");
|
||||
pack.websiteUrl = Json::ensureString(obj, "page_url", "");
|
||||
pack.description = Json::ensureString(obj, "description", "");
|
||||
|
||||
pack.logoUrl = Json::requireString(obj, "icon_url");
|
||||
pack.logoName = pack.addonId;
|
||||
|
||||
Modrinth::ModpackAuthor modAuthor;
|
||||
modAuthor.name = Json::requireString(obj, "author");
|
||||
modAuthor.url = "https://modrinth.com/user/"+modAuthor.name;
|
||||
pack.author = modAuthor;
|
||||
}
|
||||
|
||||
void Modrinth::loadIndexedPackVersions(Modrinth::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");
|
||||
|
||||
for(auto versionIter: arr) {
|
||||
auto obj = versionIter.toObject();
|
||||
Modrinth::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){
|
||||
file.mcVersion.append(mcVer.toString());
|
||||
}
|
||||
auto loaders = Json::requireArray(obj,"loaders");
|
||||
for(auto loader : loaders){
|
||||
file.loaders.append(loader.toString());
|
||||
}
|
||||
file.version = Json::requireString(obj, "name");
|
||||
|
||||
auto files = Json::requireArray(obj, "files");
|
||||
int i = 0;
|
||||
while (files.count() > 1 && i < files.count()){
|
||||
//try to resolve the correct file
|
||||
auto parent = files[i].toObject();
|
||||
auto fileName = Json::requireString(parent, "filename");
|
||||
//avoid grabbing "dev" files
|
||||
if(fileName.contains("javadocs",Qt::CaseInsensitive) || fileName.contains("sources",Qt::CaseInsensitive)){
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
//grab the correct mod loader
|
||||
if(fileName.contains("forge",Qt::CaseInsensitive) || fileName.contains("fabric",Qt::CaseInsensitive) ){
|
||||
if(hasFabric){
|
||||
if(fileName.contains("forge",Qt::CaseInsensitive)){
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}else{
|
||||
if(fileName.contains("fabric",Qt::CaseInsensitive)){
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
auto parent = files[i].toObject();
|
||||
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
|
||||
return a.date > b.date;
|
||||
};
|
||||
std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate);
|
||||
pack.versions = unsortedVersions;
|
||||
pack.versionsLoaded = true;
|
||||
}
|
48
launcher/modplatform/modrinth/ModrinthPackIndex.h
Normal file
48
launcher/modplatform/modrinth/ModrinthPackIndex.h
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <QList>
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QObjectPtr.h>
|
||||
#include "net/NetJob.h"
|
||||
#include "BaseInstance.h"
|
||||
|
||||
namespace Modrinth {
|
||||
|
||||
struct ModpackAuthor {
|
||||
QString name;
|
||||
QString url;
|
||||
};
|
||||
|
||||
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)
|
@ -18,7 +18,7 @@
|
||||
#include "InstanceTask.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
#include "quazip.h"
|
||||
#include <quazip/quazip.h>
|
||||
|
||||
#include <QFutureWatcher>
|
||||
#include <QStringList>
|
||||
|
@ -19,9 +19,9 @@
|
||||
#include <Json.h>
|
||||
#include <minecraft/MinecraftInstance.h>
|
||||
#include <minecraft/PackProfile.h>
|
||||
#include <quazip.h>
|
||||
#include <quazipdir.h>
|
||||
#include <quazipfile.h>
|
||||
#include <quazip/quazip.h>
|
||||
#include <quazip/quazipdir.h>
|
||||
#include <quazip/quazipfile.h>
|
||||
#include <settings/INISettingsObject.h>
|
||||
|
||||
#include <memory>
|
||||
@ -31,8 +31,6 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const
|
||||
QString minecraftPath = FS::PathCombine(stagingPath, ".minecraft");
|
||||
QString configPath = FS::PathCombine(stagingPath, "instance.cfg");
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||
instanceSettings->set("InstanceType", "OneSix");
|
||||
MinecraftInstance instance(globalSettings, instanceSettings, stagingPath);
|
||||
|
||||
instance.setName(instName);
|
||||
|
@ -8,44 +8,34 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QFile>
|
||||
|
||||
PasteUpload::PasteUpload(QWidget *window, QString text, QString key) : m_window(window)
|
||||
PasteUpload::PasteUpload(QWidget *window, QString text, QString url) : m_window(window), m_uploadUrl(url), m_text(text.toUtf8())
|
||||
{
|
||||
m_key = key;
|
||||
QByteArray temp;
|
||||
QJsonObject topLevelObj;
|
||||
QJsonObject sectionObject;
|
||||
sectionObject.insert("contents", text);
|
||||
QJsonArray sectionArray;
|
||||
sectionArray.append(sectionObject);
|
||||
topLevelObj.insert("description", "Log Upload");
|
||||
topLevelObj.insert("sections", sectionArray);
|
||||
QJsonDocument docOut;
|
||||
docOut.setObject(topLevelObj);
|
||||
m_jsonContent = docOut.toJson();
|
||||
}
|
||||
|
||||
PasteUpload::~PasteUpload()
|
||||
{
|
||||
}
|
||||
|
||||
bool PasteUpload::validateText()
|
||||
{
|
||||
return m_jsonContent.size() <= maxSize();
|
||||
}
|
||||
|
||||
void PasteUpload::executeTask()
|
||||
{
|
||||
QNetworkRequest request(QUrl("https://api.paste.ee/v1/pastes"));
|
||||
QNetworkRequest request{QUrl(m_uploadUrl)};
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, BuildConfig.USER_AGENT_UNCACHED);
|
||||
|
||||
request.setRawHeader("Content-Type", "application/json");
|
||||
request.setRawHeader("Content-Length", QByteArray::number(m_jsonContent.size()));
|
||||
request.setRawHeader("X-Auth-Token", m_key.toStdString().c_str());
|
||||
QHttpMultiPart *multiPart = new QHttpMultiPart{QHttpMultiPart::FormDataType};
|
||||
|
||||
QNetworkReply *rep = APPLICATION->network()->post(request, m_jsonContent);
|
||||
QHttpPart filePart;
|
||||
filePart.setBody(m_text);
|
||||
filePart.setHeader(QNetworkRequest::ContentTypeHeader, "text/plain");
|
||||
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"file\"; filename=\"log.txt\"");
|
||||
|
||||
multiPart->append(filePart);
|
||||
|
||||
QNetworkReply *rep = APPLICATION->network()->post(request, multiPart);
|
||||
multiPart->setParent(rep);
|
||||
|
||||
m_reply = std::shared_ptr<QNetworkReply>(rep);
|
||||
setStatus(tr("Uploading to paste.ee"));
|
||||
setStatus(tr("Uploading to %1").arg(m_uploadUrl));
|
||||
|
||||
connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress);
|
||||
connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
|
||||
connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished()));
|
||||
@ -61,45 +51,23 @@ void PasteUpload::downloadError(QNetworkReply::NetworkError error)
|
||||
void PasteUpload::downloadFinished()
|
||||
{
|
||||
QByteArray data = m_reply->readAll();
|
||||
// if the download succeeded
|
||||
if (m_reply->error() == QNetworkReply::NetworkError::NoError)
|
||||
int statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
|
||||
if (m_reply->error() != QNetworkReply::NetworkError::NoError)
|
||||
{
|
||||
m_reply.reset();
|
||||
QJsonParseError jsonError;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
|
||||
if (jsonError.error != QJsonParseError::NoError)
|
||||
{
|
||||
emitFailed(jsonError.errorString());
|
||||
return;
|
||||
}
|
||||
if (!parseResult(doc))
|
||||
{
|
||||
emitFailed(tr("paste.ee returned an error. Please consult the logs for more information"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
// else the download failed
|
||||
else
|
||||
{
|
||||
emitFailed(QString("Network error: %1").arg(m_reply->errorString()));
|
||||
emitFailed(tr("Network error: %1").arg(m_reply->errorString()));
|
||||
m_reply.reset();
|
||||
return;
|
||||
}
|
||||
else if (statusCode != 200 && statusCode != 201)
|
||||
{
|
||||
QString reasonPhrase = m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
|
||||
emitFailed(tr("Error: %1 returned unexpected status code %2 %3").arg(m_uploadUrl).arg(statusCode).arg(reasonPhrase));
|
||||
qCritical() << m_uploadUrl << " returned unexpected status code " << statusCode << " with body: " << data;
|
||||
m_reply.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
m_pasteLink = QString::fromUtf8(data).trimmed();
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
bool PasteUpload::parseResult(QJsonDocument doc)
|
||||
{
|
||||
auto object = doc.object();
|
||||
auto status = object.value("success").toBool();
|
||||
if (!status)
|
||||
{
|
||||
qCritical() << "paste.ee reported error:" << QString(object.value("error").toString());
|
||||
return false;
|
||||
}
|
||||
m_pasteLink = object.value("link").toString();
|
||||
m_pasteID = object.value("id").toString();
|
||||
qDebug() << m_pasteLink;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -8,37 +8,21 @@ class PasteUpload : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PasteUpload(QWidget *window, QString text, QString key = "public");
|
||||
PasteUpload(QWidget *window, QString text, QString url);
|
||||
virtual ~PasteUpload();
|
||||
|
||||
QString pasteLink()
|
||||
{
|
||||
return m_pasteLink;
|
||||
}
|
||||
QString pasteID()
|
||||
{
|
||||
return m_pasteID;
|
||||
}
|
||||
int maxSize()
|
||||
{
|
||||
// 2MB for paste.ee - public
|
||||
if(m_key == "public")
|
||||
return 1024*1024*2;
|
||||
// 12MB for paste.ee - with actual key
|
||||
return 1024*1024*12;
|
||||
}
|
||||
bool validateText();
|
||||
protected:
|
||||
virtual void executeTask();
|
||||
|
||||
private:
|
||||
bool parseResult(QJsonDocument doc);
|
||||
QString m_error;
|
||||
QWidget *m_window;
|
||||
QString m_pasteID;
|
||||
QString m_pasteLink;
|
||||
QString m_key;
|
||||
QByteArray m_jsonContent;
|
||||
QString m_uploadUrl;
|
||||
QByteArray m_text;
|
||||
std::shared_ptr<QNetworkReply> m_reply;
|
||||
public
|
||||
slots:
|
||||
|
@ -70,7 +70,7 @@ void NewsChecker::rssDownloadFinished()
|
||||
}
|
||||
|
||||
// If the parsing succeeded, read it.
|
||||
QDomNodeList items = doc.elementsByTagName("item");
|
||||
QDomNodeList items = doc.elementsByTagName("entry");
|
||||
m_newsEntries.clear();
|
||||
for (int i = 0; i < items.length(); i++)
|
||||
{
|
||||
|
@ -24,18 +24,14 @@ NewsEntry::NewsEntry(QObject* parent) :
|
||||
this->title = tr("Untitled");
|
||||
this->content = tr("No content.");
|
||||
this->link = "";
|
||||
this->author = tr("Unknown Author");
|
||||
this->pubDate = QDateTime::currentDateTime();
|
||||
}
|
||||
|
||||
NewsEntry::NewsEntry(const QString& title, const QString& content, const QString& link, const QString& author, const QDateTime& pubDate, QObject* parent) :
|
||||
NewsEntry::NewsEntry(const QString& title, const QString& content, const QString& link, QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
this->title = title;
|
||||
this->content = content;
|
||||
this->link = link;
|
||||
this->author = author;
|
||||
this->pubDate = pubDate;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -59,19 +55,11 @@ bool NewsEntry::fromXmlElement(const QDomElement& element, NewsEntry* entry, QSt
|
||||
{
|
||||
QString title = childValue(element, "title", tr("Untitled"));
|
||||
QString content = childValue(element, "description", tr("No content."));
|
||||
QString link = childValue(element, "link");
|
||||
QString author = childValue(element, "dc:creator", tr("Unknown Author"));
|
||||
QString pubDateStr = childValue(element, "pubDate");
|
||||
|
||||
// FIXME: For now, we're just ignoring timezones. We assume that all time zones in the RSS feed are the same.
|
||||
QString dateFormat("ddd, dd MMM yyyy hh:mm:ss");
|
||||
QDateTime pubDate = QDateTime::fromString(pubDateStr, dateFormat);
|
||||
QString link = childValue(element, "id");
|
||||
|
||||
entry->title = title;
|
||||
entry->content = content;
|
||||
entry->link = link;
|
||||
entry->author = author;
|
||||
entry->pubDate = pubDate;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -18,8 +18,6 @@
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QDomElement>
|
||||
#include <QDateTime>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class NewsEntry : public QObject
|
||||
@ -36,7 +34,7 @@ public:
|
||||
* Constructs a new news entry.
|
||||
* Note that content may contain HTML.
|
||||
*/
|
||||
NewsEntry(const QString& title, const QString& content, const QString& link, const QString& author, const QDateTime& pubDate, QObject* parent=0);
|
||||
NewsEntry(const QString& title, const QString& content, const QString& link, QObject* parent=0);
|
||||
|
||||
/*!
|
||||
* Attempts to load information from the given XML element into the given news entry pointer.
|
||||
@ -53,12 +51,6 @@ public:
|
||||
|
||||
//! URL to the post.
|
||||
QString link;
|
||||
|
||||
//! The post's author.
|
||||
QString author;
|
||||
|
||||
//! The date and time that this post was published.
|
||||
QDateTime pubDate;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<NewsEntry> NewsEntryPtr;
|
||||
|
@ -44,7 +44,7 @@ void NotificationChecker::checkForNotifications()
|
||||
if (!m_notificationsUrl.isValid())
|
||||
{
|
||||
qCritical() << "Failed to check for notifications. No notifications URL set."
|
||||
<< "If you'd like to use MultiMC's notification system, please pass the "
|
||||
<< "If you'd like to use PolyMC's notification system, please pass the "
|
||||
"URL to CMake at compile time.";
|
||||
return;
|
||||
}
|
||||
|
BIN
launcher/resources/multimc/128x128/instances/modrinth.png
Normal file
BIN
launcher/resources/multimc/128x128/instances/modrinth.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
launcher/resources/multimc/32x32/instances/modrinth.png
Normal file
BIN
launcher/resources/multimc/32x32/instances/modrinth.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
@ -246,9 +246,13 @@
|
||||
<!-- placeholder when loading screenshot images -->
|
||||
<file>scalable/screenshot-placeholder.svg</file>
|
||||
|
||||
<!-- matrix logo -->
|
||||
<file>scalable/matrix.svg</file>
|
||||
|
||||
<!-- discord logo icon thing. from discord. traced from bitmap -->
|
||||
<file>scalable/discord.svg</file>
|
||||
|
||||
|
||||
<!-- instance icons -->
|
||||
<file>32x32/instances/chicken.png</file>
|
||||
<file>128x128/instances/chicken.png</file>
|
||||
@ -268,6 +272,9 @@
|
||||
<file>32x32/instances/flame.png</file>
|
||||
<file>128x128/instances/flame.png</file>
|
||||
|
||||
<file>32x32/instances/modrinth.png</file>
|
||||
<file>128x128/instances/modrinth.png</file>
|
||||
|
||||
<file>32x32/instances/gear.png</file>
|
||||
<file>128x128/instances/gear.png</file>
|
||||
|
||||
|
7
launcher/resources/multimc/scalable/matrix.svg
Normal file
7
launcher/resources/multimc/scalable/matrix.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64">
|
||||
<g fill="#fff">
|
||||
<path d="M49.46 42.2h-5.32c-.178 0-.323-.145-.323-.323V30.91l-.074-1.808c-.047-.53-.173-.992-.376-1.376-.194-.367-.487-.664-.868-.883s-.93-.332-1.62-.332-1.238.13-1.647.382-.743.597-.976 1.01a4.21 4.21 0 0 0-.486 1.462c-.085.567-.128 1.15-.128 1.732v10.79c0 .178-.145.323-.323.323H32c-.178 0-.323-.145-.323-.323V31.02l-.037-1.69c-.024-.524-.124-1.013-.297-1.45-.164-.415-.43-.74-.814-.992s-.972-.378-1.752-.378c-.22 0-.527.053-.908.157-.368.1-.732.294-1.08.577s-.65.694-.904 1.235-.382 1.27-.382 2.167v11.24c0 .178-.144.323-.323.323h-5.32c-.178 0-.323-.145-.323-.323V22.515c0-.178.145-.322.323-.322h5.02c.178 0 .323.145.323.322V24.3c.618-.726 1.33-1.315 2.125-1.757 1.032-.574 2.225-.865 3.548-.865 1.265 0 2.44.25 3.5.743.934.44 1.68 1.17 2.224 2.18.556-.703 1.263-1.34 2.108-1.895 1.036-.682 2.274-1.028 3.68-1.028 1.048 0 2.036.13 2.937.387.917.263 1.715.69 2.373 1.267s1.18 1.348 1.548 2.278c.363.922.547 2.04.547 3.323v12.964c0 .178-.145.323-.323.323z" opacity=".5" />
|
||||
<path d="M24.88 22.515v2.623h.075c.7-.998 1.542-1.774 2.53-2.323s2.117-.824 3.39-.824c1.224 0 2.342.238 3.353.712s1.78 1.31 2.305 2.51c.574-.85 1.355-1.6 2.342-2.248s2.154-.974 3.504-.974c1.024 0 1.973.125 2.848.375s1.623.65 2.248 1.2 1.11 1.268 1.462 2.154.525 1.955.525 3.204v12.964h-5.32V30.91l-.075-1.836c-.05-.574-.187-1.073-.412-1.5s-.556-.762-.993-1.012-1.03-.374-1.78-.374-1.355.145-1.817.43-.824.663-1.087 1.124-.437.987-.524 1.574a12 12 0 0 0-.131 1.78v10.79H32V31.022l-.037-1.705c-.025-.562-.13-1.08-.32-1.556s-.5-.855-.937-1.143-1.08-.43-1.93-.43c-.25 0-.58.056-.993.17a3.3 3.3 0 0 0-1.199.637c-.388.313-.718.762-.993 1.35s-.412 1.355-.412 2.304v11.24h-5.32V22.515z" opacity=".5" />
|
||||
</g>
|
||||
<path d="M1.432 6.084v51.833h3.73v1.244H0V4.84h5.162v1.243zm20.788 16.43v2.623h.075c.7-.998 1.542-1.774 2.53-2.323s2.117-.824 3.4-.824c1.224 0 2.342.238 3.353.712s1.78 1.3 2.305 2.5c.574-.85 1.355-1.6 2.342-2.248s2.154-.974 3.504-.974c1.024 0 1.973.125 2.848.375s1.623.65 2.248 1.2 1.1 1.268 1.462 2.154.525 1.955.525 3.204v12.964h-5.32V30.9l-.075-1.836c-.05-.574-.187-1.073-.412-1.5s-.556-.762-.993-1.012-1.03-.374-1.78-.374-1.355.145-1.817.43a3.12 3.12 0 0 0-1.087 1.124c-.263.46-.437.987-.524 1.574a12 12 0 0 0-.131 1.78v10.8h-5.32V31.022l-.037-1.705c-.025-.562-.13-1.08-.32-1.556s-.5-.855-.937-1.143-1.08-.43-1.93-.43c-.25 0-.58.056-.993.17a3.3 3.3 0 0 0-1.199.637c-.388.313-.718.762-.993 1.35s-.412 1.355-.412 2.304v11.24H17.2V22.515zm40.348 35.402V6.084h-3.73V4.84H64v54.32h-5.162v-1.244z" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
@ -33,7 +33,7 @@ public:
|
||||
* Construct a Setting
|
||||
*
|
||||
* Synonyms are all the possible names used in the settings object, in order of preference.
|
||||
* First synonym is the ID, which identifies the setting in MultiMC.
|
||||
* First synonym is the ID, which identifies the setting in PolyMC.
|
||||
*
|
||||
* defVal is the default value that will be returned when the settings object
|
||||
* doesn't have any value for this setting.
|
||||
@ -115,3 +115,4 @@ protected:
|
||||
QStringList m_synonyms;
|
||||
QVariant m_defVal;
|
||||
};
|
||||
|
||||
|
@ -52,7 +52,7 @@ QString MCEditTool::getProgramPath()
|
||||
#else
|
||||
const QString mceditPath = path();
|
||||
QDir mceditDir(mceditPath);
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||
if (mceditDir.exists("mcedit.sh"))
|
||||
{
|
||||
return mceditDir.absoluteFilePath("mcedit.sh");
|
||||
|
@ -143,6 +143,11 @@ struct TranslationsModel::Private
|
||||
|
||||
std::unique_ptr<POTranslator> m_po_translator;
|
||||
QFileSystemWatcher *watcher;
|
||||
|
||||
const QString m_system_locale = QLocale::system().name();
|
||||
const QString m_system_language = m_system_locale.split('_').front();
|
||||
|
||||
bool no_language_set = false;
|
||||
};
|
||||
|
||||
TranslationsModel::TranslationsModel(QString path, QObject* parent): QAbstractListModel(parent)
|
||||
@ -164,7 +169,10 @@ TranslationsModel::~TranslationsModel()
|
||||
void TranslationsModel::translationDirChanged(const QString& path)
|
||||
{
|
||||
qDebug() << "Dir changed:" << path;
|
||||
reloadLocalFiles();
|
||||
if (!d->no_language_set)
|
||||
{
|
||||
reloadLocalFiles();
|
||||
}
|
||||
selectLanguage(selectedLanguage());
|
||||
}
|
||||
|
||||
@ -172,7 +180,26 @@ void TranslationsModel::indexReceived()
|
||||
{
|
||||
qDebug() << "Got translations index!";
|
||||
d->m_index_job.reset();
|
||||
if(d->m_selectedLanguage != defaultLangCode)
|
||||
|
||||
if (d->no_language_set)
|
||||
{
|
||||
reloadLocalFiles();
|
||||
|
||||
auto language = d->m_system_locale;
|
||||
if (!findLanguage(language))
|
||||
{
|
||||
language = d->m_system_language;
|
||||
}
|
||||
selectLanguage(language);
|
||||
if (selectedLanguage() != defaultLangCode)
|
||||
{
|
||||
updateLanguage(selectedLanguage());
|
||||
}
|
||||
APPLICATION->settings()->set("Language", selectedLanguage());
|
||||
d->no_language_set = false;
|
||||
}
|
||||
|
||||
else if(d->m_selectedLanguage != defaultLangCode)
|
||||
{
|
||||
downloadTranslation(d->m_selectedLanguage);
|
||||
}
|
||||
@ -319,8 +346,19 @@ void TranslationsModel::reloadLocalFiles()
|
||||
{
|
||||
d->m_languages.append(language);
|
||||
}
|
||||
std::sort(d->m_languages.begin(), d->m_languages.end(), [](const Language& a, const Language& b) {
|
||||
return a.key.compare(b.key) < 0;
|
||||
std::sort(d->m_languages.begin(), d->m_languages.end(), [this](const Language& a, const Language& b) {
|
||||
if (a.key != b.key)
|
||||
{
|
||||
if (a.key == d->m_system_locale || a.key == d->m_system_language)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (b.key == d->m_system_locale || b.key == d->m_system_language)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return a.key < b.key;
|
||||
});
|
||||
endInsertRows();
|
||||
}
|
||||
@ -439,6 +477,12 @@ bool TranslationsModel::selectLanguage(QString key)
|
||||
{
|
||||
QString &langCode = key;
|
||||
auto langPtr = findLanguage(key);
|
||||
|
||||
if (langCode.isEmpty())
|
||||
{
|
||||
d->no_language_set = true;
|
||||
}
|
||||
|
||||
if(!langPtr)
|
||||
{
|
||||
qWarning() << "Selected invalid language" << key << ", defaulting to" << defaultLangCode;
|
||||
@ -576,7 +620,7 @@ void TranslationsModel::downloadIndex()
|
||||
d->m_index_job = new NetJob("Translations Index", APPLICATION->network());
|
||||
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("translations", "index_v2.json");
|
||||
entry->setStale(true);
|
||||
d->m_index_task = Net::Download::makeCached(QUrl("https://files.multimc.org/translations/index_v2.json"), entry);
|
||||
d->m_index_task = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATIONS_BASE_URL + "index_v2.json"), entry);
|
||||
d->m_index_job->addNetAction(d->m_index_task);
|
||||
connect(d->m_index_job.get(), &NetJob::failed, this, &TranslationsModel::indexFailed);
|
||||
connect(d->m_index_job.get(), &NetJob::succeeded, this, &TranslationsModel::indexReceived);
|
||||
|
@ -16,21 +16,8 @@
|
||||
QString GuiUtil::uploadPaste(const QString &text, QWidget *parentWidget)
|
||||
{
|
||||
ProgressDialog dialog(parentWidget);
|
||||
auto APIKeySetting = APPLICATION->settings()->get("PasteEEAPIKey").toString();
|
||||
if(APIKeySetting == "multimc")
|
||||
{
|
||||
APIKeySetting = BuildConfig.PASTE_EE_KEY;
|
||||
}
|
||||
std::unique_ptr<PasteUpload> paste(new PasteUpload(parentWidget, text, APIKeySetting));
|
||||
|
||||
if (!paste->validateText())
|
||||
{
|
||||
CustomMessageBox::selectable(
|
||||
parentWidget, QObject::tr("Upload failed"),
|
||||
QObject::tr("The log file is too big. You'll have to upload it manually."),
|
||||
QMessageBox::Warning)->exec();
|
||||
return QString();
|
||||
}
|
||||
auto pasteUrlSetting = APPLICATION->settings()->get("PastebinURL").toString();
|
||||
std::unique_ptr<PasteUpload> paste(new PasteUpload(parentWidget, text, pasteUrlSetting));
|
||||
|
||||
dialog.execWithTask(paste.get());
|
||||
if (!paste->wasSuccessful())
|
||||
|
@ -235,6 +235,7 @@ public:
|
||||
TranslatedToolButton helpMenuButton;
|
||||
TranslatedAction actionReportBug;
|
||||
TranslatedAction actionDISCORD;
|
||||
TranslatedAction actionMATRIX;
|
||||
TranslatedAction actionREDDIT;
|
||||
TranslatedAction actionAbout;
|
||||
|
||||
@ -343,13 +344,23 @@ public:
|
||||
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"));
|
||||
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"));
|
||||
actionDISCORD.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open %1 discord voice chat."));
|
||||
actionDISCORD.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open %1 Discord guild."));
|
||||
all_actions.append(&actionDISCORD);
|
||||
helpMenu->addAction(actionDISCORD);
|
||||
}
|
||||
@ -588,7 +599,7 @@ public:
|
||||
actionExportInstance = TranslatedAction(MainWindow);
|
||||
actionExportInstance->setObjectName(QStringLiteral("actionExportInstance"));
|
||||
actionExportInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Export Instance"));
|
||||
// FIXME: missing tooltip
|
||||
actionExportInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Export the selected instance as a zip file."));
|
||||
all_actions.append(&actionExportInstance);
|
||||
instanceToolBar->addAction(actionExportInstance);
|
||||
|
||||
@ -1500,6 +1511,11 @@ void MainWindow::on_actionDISCORD_triggered()
|
||||
DesktopServices::openUrl(QUrl(BuildConfig.DISCORD_URL));
|
||||
}
|
||||
|
||||
void MainWindow::on_actionMATRIX_triggered()
|
||||
{
|
||||
DesktopServices::openUrl(QUrl(BuildConfig.MATRIX_URL));
|
||||
}
|
||||
|
||||
void MainWindow::on_actionChangeInstIcon_triggered()
|
||||
{
|
||||
if (!m_selectedInstance)
|
||||
@ -1687,7 +1703,7 @@ void MainWindow::on_actionReportBug_triggered()
|
||||
|
||||
void MainWindow::on_actionMoreNews_triggered()
|
||||
{
|
||||
DesktopServices::openUrl(QUrl("https://multimc.org/posts.html"));
|
||||
DesktopServices::openUrl(QUrl(BuildConfig.NEWS_OPEN_URL));
|
||||
}
|
||||
|
||||
void MainWindow::newsButtonClicked()
|
||||
@ -1699,7 +1715,7 @@ void MainWindow::newsButtonClicked()
|
||||
}
|
||||
else
|
||||
{
|
||||
DesktopServices::openUrl(QUrl("https://multimc.org/posts.html"));
|
||||
DesktopServices::openUrl(QUrl(BuildConfig.NEWS_OPEN_URL));
|
||||
}
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user