Compare commits
366 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a2b18ddff | ||
|
|
edeffef51d | ||
|
|
ed99c6a0dc | ||
|
|
ae8667726e | ||
|
|
cbcd822e44 | ||
|
|
0fa6c7fbbf | ||
|
|
68fbbf0156 | ||
|
|
da36a5f8de | ||
|
|
3ca661127f | ||
|
|
f8c5d80c66 | ||
|
|
eda06df878 | ||
|
|
2cf04d034a | ||
|
|
049aafd0a1 | ||
|
|
3aa9f5c376 | ||
|
|
63d4486855 | ||
|
|
8b31c638f3 | ||
|
|
63098b6f19 | ||
|
|
9a33fb1f49 | ||
|
|
917f8a31e3 | ||
|
|
aa770b63fb | ||
|
|
c1bf31cb27 | ||
|
|
86d99f80c3 | ||
|
|
52420963cf | ||
|
|
addf5f4e52 | ||
|
|
03d7300732 | ||
|
|
9579231ccc | ||
|
|
9cc168c526 | ||
|
|
94fdf13f4a | ||
|
|
3efcccf334 | ||
|
|
5e909a4e85 | ||
|
|
e7b90a9a3c | ||
|
|
a6e59cb4f4 | ||
|
|
1f21d84fdc | ||
|
|
be029ab360 | ||
|
|
6fe07561fe | ||
|
|
f42c3a953c | ||
|
|
4063a496d7 | ||
|
|
01e4e62de3 | ||
|
|
b1b615e17f | ||
|
|
95c9a6d8f4 | ||
|
|
3dd6cea8da | ||
|
|
6d3eace2a8 | ||
|
|
6f6fa6955a | ||
|
|
5961c69019 | ||
|
|
526b322a4a | ||
|
|
aaa12e1ddc | ||
|
|
2f7fd221af | ||
|
|
1f4e3e83e3 | ||
|
|
fe12b58d06 | ||
|
|
d19ad15aec | ||
|
|
8bc3293ad0 | ||
|
|
06bedee835 | ||
|
|
7e1eb02ee6 | ||
|
|
cb67fc1d15 | ||
|
|
7d047f9223 | ||
|
|
3bc450a6d7 | ||
|
|
fce98f5e16 | ||
|
|
7179e75e70 | ||
|
|
431d773eec | ||
|
|
b1910642bf | ||
|
|
298564ed71 | ||
|
|
871ceb21dd | ||
|
|
c29b616497 | ||
|
|
80beccb2c4 | ||
|
|
dba4c452e0 | ||
|
|
90a62c429a | ||
|
|
a5581b479e | ||
|
|
825ef52dd5 | ||
|
|
3a940ffb52 | ||
|
|
70d400f205 | ||
|
|
cd513c02c4 | ||
|
|
c8ca6acc15 | ||
|
|
d37003b1de | ||
|
|
db6431d9e0 | ||
|
|
3c46d8a412 | ||
|
|
a97d0a36f4 | ||
|
|
e9c52ec696 | ||
|
|
75f2dab3c8 | ||
|
|
eb1091a5f4 | ||
|
|
ffcef673de | ||
|
|
241086883e | ||
|
|
20eada7bbe | ||
|
|
859d710581 | ||
|
|
285188ea53 | ||
|
|
0e31f77468 | ||
|
|
024f5952ce | ||
|
|
a522cad6d6 | ||
|
|
b49987e876 | ||
|
|
27e328c044 | ||
|
|
b258eac215 | ||
|
|
5040231f8d | ||
|
|
9fc677c2a4 | ||
|
|
69213b1206 | ||
|
|
c2c56a2f6c | ||
|
|
0c861db7a2 | ||
|
|
eafeb64dec | ||
|
|
0022aed8bb | ||
|
|
014e65220e | ||
|
|
6c82883206 | ||
|
|
25fbeb265a | ||
|
|
30d5a7ab48 | ||
|
|
475d949a1e | ||
|
|
32f9c61c6e | ||
|
|
86f6a3e751 | ||
|
|
9ccce62f50 | ||
|
|
0660768478 | ||
|
|
b1beeee11f | ||
|
|
30602363d7 | ||
|
|
0423464b88 | ||
|
|
2576a28f73 | ||
|
|
7b4c52e1e3 | ||
|
|
27f276ef13 | ||
|
|
393d17b8d3 | ||
|
|
ae4939e0d2 | ||
|
|
85ecbad467 | ||
|
|
040af58070 | ||
|
|
f11d68e2a2 | ||
|
|
73995106d3 | ||
|
|
6f6c9c6f68 | ||
|
|
7f28f0bf01 | ||
|
|
45520129d9 | ||
|
|
41d855fd11 | ||
|
|
4b13577a59 | ||
|
|
c377ad6025 | ||
|
|
a6df7d709f | ||
|
|
cbe1b3353c | ||
|
|
2e60413f7f | ||
|
|
98887911c1 | ||
|
|
5bc6dd8f97 | ||
|
|
7cbca6ab20 | ||
|
|
ddf98c59f5 | ||
|
|
110c73edf2 | ||
|
|
e6cb7b7710 | ||
|
|
aed8d5f8f5 | ||
|
|
8bc6560b5e | ||
|
|
7c86732a47 | ||
|
|
a8458e36ff | ||
|
|
8bc5b5a01f | ||
|
|
264d3017aa | ||
|
|
d9b46289a1 | ||
|
|
5b3dffce62 | ||
|
|
297d4b4196 | ||
|
|
e12a769800 | ||
|
|
f39c313c5f | ||
|
|
462a44b4be | ||
|
|
441ab7eedc | ||
|
|
6a4130c914 | ||
|
|
3f0e729815 | ||
|
|
b93997501d | ||
|
|
1869dd0de3 | ||
|
|
175132539b | ||
|
|
6cc7788b4a | ||
|
|
0c798b8fc7 | ||
|
|
fa7a7d52d0 | ||
|
|
8c4fb86ba0 | ||
|
|
668d31b79e | ||
|
|
3138e58c75 | ||
|
|
0b312956db | ||
|
|
04b2c7ee0d | ||
|
|
3f3fac8462 | ||
|
|
e46dc03eb1 | ||
|
|
0bb0578f2b | ||
|
|
cdf8d20c95 | ||
|
|
560f7fa61a | ||
|
|
0b0e7aa633 | ||
|
|
1752d47035 | ||
|
|
0f3cf0595b | ||
|
|
433dd2d161 | ||
|
|
a5956194df | ||
|
|
d2a44effb5 | ||
|
|
0dcd24a53b | ||
|
|
28d7c5d309 | ||
|
|
7229ebbb08 | ||
|
|
29f304f070 | ||
|
|
e2355eb276 | ||
|
|
d1b1113798 | ||
|
|
7185fd9a8f | ||
|
|
06661bff35 | ||
|
|
6c9dc4c86a | ||
|
|
426135b76a | ||
|
|
46468c8f14 | ||
|
|
878c4fb810 | ||
|
|
d644fb2094 | ||
|
|
823e7d22c7 | ||
|
|
c17b359d03 | ||
|
|
938f896bfa | ||
|
|
cd87029e6f | ||
|
|
4f7aad0f8d | ||
|
|
b47d986f22 | ||
|
|
51cdb8c790 | ||
|
|
acbca16013 | ||
|
|
62ecb3e58d | ||
|
|
92895f11d1 | ||
|
|
23442442d8 | ||
|
|
3171014301 | ||
|
|
7239502675 | ||
|
|
1e1655bc4b | ||
|
|
d4a3fc5195 | ||
|
|
93c527ed3d | ||
|
|
b2c1100b1c | ||
|
|
34a5459dce | ||
|
|
5c0e70e237 | ||
|
|
e2be2ada05 | ||
|
|
eae65da110 | ||
|
|
50b92c1af2 | ||
|
|
1b68d51da6 | ||
|
|
94fd9a3535 | ||
|
|
345641f7d2 | ||
|
|
4a283fe4c1 | ||
|
|
f1a5f7bc4d | ||
|
|
2a21e28ffd | ||
|
|
4ea52f4758 | ||
|
|
44d634f564 | ||
|
|
3a53349e33 | ||
|
|
fca2e9e44c | ||
|
|
c2ec2a4af5 | ||
|
|
17af6d70b4 | ||
|
|
d2de849c86 | ||
|
|
7921f47ec4 | ||
|
|
20b9f2b42a | ||
|
|
dd13368085 | ||
|
|
2568752af5 | ||
|
|
fbd93a47a3 | ||
|
|
0c466bc530 | ||
|
|
b902c5cd78 | ||
|
|
78124f6fba | ||
|
|
34f74fff0a | ||
|
|
211cfb4af7 | ||
|
|
c5d0348181 | ||
|
|
1762d2fc7d | ||
|
|
8ea500de68 | ||
|
|
9e9281f06e | ||
|
|
6f12b31ead | ||
|
|
3974f12643 | ||
|
|
66fc707105 | ||
|
|
4fc37f5a35 | ||
|
|
295bcbe3a7 | ||
|
|
4b3305afbe | ||
|
|
74f5255eef | ||
|
|
d63ef939be | ||
|
|
66fde9e6b7 | ||
|
|
4401b9e137 | ||
|
|
20c393321c | ||
|
|
e883cf2359 | ||
|
|
df1d3dbae2 | ||
|
|
220971fadd | ||
|
|
d5c4489313 | ||
|
|
db392b4994 | ||
|
|
d1a142f040 | ||
|
|
417994735a | ||
|
|
2e78b64058 | ||
|
|
c15bd655f1 | ||
|
|
f51efc9109 | ||
|
|
665b9213c6 | ||
|
|
dc3a4cebce | ||
|
|
d92733feae | ||
|
|
a20a7e987f | ||
|
|
7c0fdaa730 | ||
|
|
c77f5285e3 | ||
|
|
d8598d6901 | ||
|
|
27d3ae145a | ||
|
|
5479fbec92 | ||
|
|
bc2c6cf030 | ||
|
|
ba8af797a9 | ||
|
|
4ba0c9c298 | ||
|
|
74311a54cf | ||
|
|
a87c64d7d1 | ||
|
|
8179a89103 | ||
|
|
e439ce6e0b | ||
|
|
f6d6e4c1c4 | ||
|
|
bace6fec1b | ||
|
|
a487234968 | ||
|
|
f7c144c393 | ||
|
|
6c0ff0b46f | ||
|
|
81d4dc09cc | ||
|
|
5f8d07c009 | ||
|
|
25955c0817 | ||
|
|
6db6ebe37f | ||
|
|
b246fc171e | ||
|
|
c92b44e6d6 | ||
|
|
e148cfbbfd | ||
|
|
c0f72488d0 | ||
|
|
cb22d5fb09 | ||
|
|
d08a2f00a2 | ||
|
|
0c147fb5ee | ||
|
|
09ce3d8f40 | ||
|
|
00820df656 | ||
|
|
c2c288a956 | ||
|
|
7ac6c4f3d9 | ||
|
|
fd04ff2b08 | ||
|
|
434adc4cd7 | ||
|
|
40f41e5fbe | ||
|
|
663a1a5a83 | ||
|
|
9fafe3ffe6 | ||
|
|
3390367d93 | ||
|
|
6dd1fdbaf9 | ||
|
|
efa3bb33f5 | ||
|
|
60b686f014 | ||
|
|
3a8068e75f | ||
|
|
52c1150522 | ||
|
|
58ab005f7e | ||
|
|
ea6c42a93c | ||
|
|
f33fe05e5f | ||
|
|
d97f13b4aa | ||
|
|
0ccd7223fd | ||
|
|
23a706bbae | ||
|
|
cc6cd0648a | ||
|
|
f78152d725 | ||
|
|
f0eb5b4a0c | ||
|
|
2e2a5d0943 | ||
|
|
911074e966 | ||
|
|
b8cd13bb21 | ||
|
|
deac64e0a2 | ||
|
|
2f1e8e82a3 | ||
|
|
de089195cd | ||
|
|
df7873eb9a | ||
|
|
3d11f9a7e9 | ||
|
|
c2fd714f8d | ||
|
|
a09d03d71d | ||
|
|
e2e5294fb9 | ||
|
|
e668aa0f95 | ||
|
|
13afad80fb | ||
|
|
7156e086f6 | ||
|
|
860706caec | ||
|
|
42253150e4 | ||
|
|
438ddfb88d | ||
|
|
73788f5d2f | ||
|
|
87dbe82474 | ||
|
|
88ce42bc0a | ||
|
|
8b926d29d7 | ||
|
|
4ac38991ad | ||
|
|
4ca481b2b3 | ||
|
|
524fc5b6ec | ||
|
|
64617201b0 | ||
|
|
d6dc22d57c | ||
|
|
3a1abb555b | ||
|
|
1f8408c793 | ||
|
|
e5804b1279 | ||
|
|
7246d8a779 | ||
|
|
fbe9d15875 | ||
|
|
094ed0bc81 | ||
|
|
43cf647642 | ||
|
|
5400d4e613 | ||
|
|
adf2301b2a | ||
|
|
9d76219434 | ||
|
|
8e6400e8d8 | ||
|
|
34bf688479 | ||
|
|
ba13e33ccc | ||
|
|
73af0f271a | ||
|
|
b8ee9a2a8e | ||
|
|
369a243f1f | ||
|
|
1db6985be2 | ||
|
|
cc8ac7bdbe | ||
|
|
8f4062b5e6 | ||
|
|
a0cb1a0d42 | ||
|
|
7acad35c3f | ||
|
|
9d7ba275ab | ||
|
|
4f3328e71c | ||
|
|
cbc973a5af | ||
|
|
f6097ef2b7 | ||
|
|
bb88697354 | ||
|
|
762a019a49 | ||
|
|
f44bab0e0c | ||
|
|
883dbe2933 | ||
|
|
6753e217a9 | ||
|
|
a6085db0ae |
5
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,7 +1,6 @@
|
||||
name: Bug Report
|
||||
description: File a bug report
|
||||
labels: [bug, needs-triage]
|
||||
issue_body: false
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -9,9 +8,9 @@ body:
|
||||
If you need help with running Minecraft, please visit us [on our Discord](https://discord.gg/multimc) before making a bug report.
|
||||
|
||||
Before submitting a bug report, please make sure you have read this *entire* form, and that:
|
||||
* You have read the [FAQ](https://github.com/MultiMC/MultiMC5/wiki/FAQ) and it has not answered your question
|
||||
* You have read the [FAQ](https://github.com/MultiMC/Launcher/wiki/FAQ) and it has not answered your question
|
||||
* Your bug is not caused by Minecraft or any mods you have installed.
|
||||
* Your issue has not been reported before, [make sure to use the search function!](https://github.com/MultiMC/MultiMC5/issues)
|
||||
* Your issue has not been reported before, [make sure to use the search function!](https://github.com/MultiMC/Launcher/issues)
|
||||
|
||||
**Do not forget to give your issue a descriptive title.** "Bug in the instance screen" makes it hard to distinguish issues at a glance.
|
||||
- type: dropdown
|
||||
|
||||
7
.github/ISSUE_TEMPLATE/suggestion.yml
vendored
@@ -1,7 +1,6 @@
|
||||
name: Suggestion
|
||||
description: Make a suggestion
|
||||
labels: [idea, needs-triage]
|
||||
issue_body: true
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -34,8 +33,6 @@ body:
|
||||
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: markdown
|
||||
- type: textarea
|
||||
attributes:
|
||||
value: |
|
||||
### You may use the editor below to elaborate further.
|
||||
# The issue_body: true up there makes the standard WYSIWYG editor for issues show up down here.
|
||||
label: You may use the editor below to elaborate further.
|
||||
|
||||
10
.gitignore
vendored
@@ -3,7 +3,6 @@ Thumbs.db
|
||||
.user
|
||||
.directory
|
||||
resources/CMakeFiles
|
||||
resources/MultiMCLauncher.jar
|
||||
*~
|
||||
*.swp
|
||||
html/
|
||||
@@ -16,11 +15,16 @@ CMakeLists.txt.user.*
|
||||
/.settings
|
||||
/.idea
|
||||
cmake-build-*/
|
||||
Debug
|
||||
|
||||
# Build dirs
|
||||
build
|
||||
/build-*
|
||||
|
||||
# Install dirs
|
||||
install
|
||||
/install-*
|
||||
|
||||
# Ctags File
|
||||
tags
|
||||
|
||||
@@ -29,3 +33,7 @@ tags
|
||||
|
||||
#OSX Stuff
|
||||
.DS_Store
|
||||
|
||||
branding/
|
||||
secrets/
|
||||
run/
|
||||
|
||||
2
.gitmodules
vendored
@@ -1,6 +1,8 @@
|
||||
[submodule "depends/libnbtplusplus"]
|
||||
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/MultiMC/quazip.git
|
||||
pushurl = git@github.com:MultiMC/quazip.git
|
||||
|
||||
48
BUILD.md
@@ -12,17 +12,24 @@ Build Instructions
|
||||
# Note
|
||||
|
||||
MultiMC is a portable application and is not supposed to be installed into any system folders.
|
||||
That would be anything outside your home folder. Before runing `make install`, make sure
|
||||
That would be anything outside your home folder. Before running `make install`, make sure
|
||||
you set the install path to something you have write access to. Never build this under
|
||||
an administrator/root level account. Don't use `sudo`. It won't work and it's not supposed to work.
|
||||
Also note that this guide is for development purposes only.
|
||||
**No support is given for building your own fork or special build for any reason whatsoever**.
|
||||
|
||||
# Branding, identifying marks and API keys
|
||||
|
||||
The logo and related assets are All Rights Reserved and may only be used in official builds of MultiMC hosted on multimc.org, and as such, are not, and will not be included in this repository. The source is only provided for the purpose of collaboration.
|
||||
|
||||
API keys are necessary for Microsoft account functionality. More info in [(Not) Secrets](https://github.com/MultiMC/Launcher/tree/develop/notsecrets)
|
||||
|
||||
# Getting the source
|
||||
|
||||
Clone the source code using git and grab all the submodules:
|
||||
|
||||
```
|
||||
git clone git@github.com:MultiMC/MultiMC5.git
|
||||
git clone https://github.com/MultiMC/Launcher.git
|
||||
git submodule init
|
||||
git submodule update
|
||||
```
|
||||
@@ -33,7 +40,7 @@ Getting the project to build and run on Linux is easy if you use any modern and
|
||||
|
||||
## Build dependencies
|
||||
* A C++ compiler capable of building C++11 code.
|
||||
* Qt 5.6+ Development tools (http://qt-project.org/downloads) ("Qt Online Installer for Linux (64 bit)") or the equivalent from your package manager. It is always better to use the Qt from your distribution, as long as it has a new enough version.
|
||||
* Qt 5.6+ Development tools (http://qt-project.org/downloads) ("Qt Online Installer for Linux (64 bit)") or the equivalent from your package manager. It is always better to use the Qt from your distribution, as long as it has a new enough version. (for example, `qttools5-dev`)
|
||||
* cmake 3.1 or newer
|
||||
* zlib (for example, `zlib1g-dev`)
|
||||
* Java JDK 8 (for example, `openjdk-8-jdk`)
|
||||
@@ -50,7 +57,7 @@ mkdir ~/MultiMC && cd ~/MultiMC
|
||||
mkdir build
|
||||
mkdir install
|
||||
# clone the complete source
|
||||
git clone --recursive https://github.com/MultiMC/MultiMC5.git src
|
||||
git clone --recursive https://github.com/MultiMC/Launcher.git src
|
||||
# configure the project
|
||||
cd build
|
||||
cmake -DCMAKE_INSTALL_PREFIX=../install ../src
|
||||
@@ -74,7 +81,7 @@ You can use IDEs like KDevelop or QtCreator to open the CMake project if you wan
|
||||
### Loading the project in Qt Creator (optional)
|
||||
1. Open Qt Creator.
|
||||
2. Choose `File->Open File or Project`.
|
||||
3. Navigate to the MultiMC5 source folder you cloned and choose CMakeLists.txt.
|
||||
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)".
|
||||
@@ -82,7 +89,7 @@ You can use IDEs like KDevelop or QtCreator to open the CMake project if you wan
|
||||
- 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 MultiMC5 window will pop up.
|
||||
- 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 IRC ([Esper/#MultiMC](http://webchat.esper.net/?nick=&channels=MultiMC))!**
|
||||
|
||||
@@ -101,7 +108,7 @@ Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt
|
||||
- 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](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html)
|
||||
* [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`.
|
||||
@@ -131,7 +138,7 @@ Ensure that OpenSSL, zlib, Java and CMake are on `PATH`.
|
||||
### Loading the project
|
||||
1. Open Qt Creator,
|
||||
2. Choose File->Open File or Project,
|
||||
3. Navigate to the MultiMC5 source folder you cloned and choose CMakeLists.txt,
|
||||
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.
|
||||
@@ -141,7 +148,7 @@ Ensure that OpenSSL, zlib, Java and CMake are on `PATH`.
|
||||
- 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 MultiMC5 window will pop up,
|
||||
- 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):
|
||||
@@ -165,7 +172,7 @@ 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 IRC ([Esper/#MultiMC](http://webchat.esper.net/?nick=&channels=MultiMC))!**
|
||||
### 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 MultiMC by doing `git clone --recursive https://github.com/MultiMC/MultiMC5.git`, and change directory to the folder you cloned to.
|
||||
2. Once that is open, change into your user directory, and clone MultiMC by doing `git clone --recursive https://github.com/MultiMC/Launcher.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.
|
||||
@@ -175,29 +182,40 @@ zlib1.dll
|
||||
# macOS
|
||||
|
||||
### Install prerequisites:
|
||||
- Install XCode and set it up to the point where you can build things from a terminal
|
||||
- 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/MultiMC/MultiMC5.git
|
||||
cd MultiMC5
|
||||
git clone --recursive https://github.com/MultiMC/Launcher.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="../dist/" \
|
||||
-DCMAKE_INSTALL_PREFIX:PATH="$(dirname $PWD)/dist/" \
|
||||
-DCMAKE_PREFIX_PATH="/path/to/Qt5.6/" \
|
||||
-DQt5_DIR="/path/to/Qt5.6/" \
|
||||
-DMultiMC_LAYOUT=mac-bundle \
|
||||
-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 -`.
|
||||
|
||||
192
CMakeLists.txt
@@ -1,26 +1,26 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
|
||||
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
|
||||
if(IS_IN_SOURCE_BUILD)
|
||||
message(FATAL_ERROR "You are building MultiMC in-source. Please separate the build tree from the source tree.")
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
if(CMAKE_HOST_SYSTEM_VERSION MATCHES ".*[Mm]icrosoft.*" OR
|
||||
CMAKE_HOST_SYSTEM_VERSION MATCHES ".*WSL.*"
|
||||
)
|
||||
message(FATAL_ERROR "Building MultiMC is not supported in Linux-on-Windows distributions.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
# In Qt 5.1+ we have our own main() function, don't autolink to qtmain on Windows
|
||||
cmake_policy(SET CMP0020 OLD)
|
||||
endif()
|
||||
|
||||
project(MultiMC)
|
||||
project(Launcher)
|
||||
enable_testing()
|
||||
|
||||
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
|
||||
if(IS_IN_SOURCE_BUILD)
|
||||
message(FATAL_ERROR "You are building the Launcher in-source. Please separate the build tree from the source tree.")
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
if(CMAKE_HOST_SYSTEM_VERSION MATCHES ".*[Mm]icrosoft.*" OR
|
||||
CMAKE_HOST_SYSTEM_VERSION MATCHES ".*WSL.*"
|
||||
)
|
||||
message(FATAL_ERROR "Building the Launcher is not supported in Linux-on-Windows distributions.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
##################################### Set CMake options #####################################
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
@@ -47,49 +47,67 @@ if(UNIX AND APPLE)
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type")
|
||||
|
||||
# Fix build with Qt 5.13
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y")
|
||||
|
||||
##################################### Set Application options #####################################
|
||||
|
||||
######## Set URLs ########
|
||||
set(MultiMC_NEWS_RSS_URL "https://multimc.org/rss.xml" CACHE STRING "URL to fetch MultiMC's news RSS feed from.")
|
||||
set(Launcher_NEWS_RSS_URL "https://multimc.org/rss.xml" CACHE STRING "URL to fetch Launcher's news RSS feed from.")
|
||||
|
||||
######## Set version numbers ########
|
||||
set(MultiMC_VERSION_MAJOR 0)
|
||||
set(MultiMC_VERSION_MINOR 6)
|
||||
set(MultiMC_VERSION_HOTFIX 12)
|
||||
set(Launcher_VERSION_MAJOR 0)
|
||||
set(Launcher_VERSION_MINOR 6)
|
||||
set(Launcher_VERSION_HOTFIX 15)
|
||||
|
||||
# Build number
|
||||
set(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
|
||||
set(Launcher_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
|
||||
|
||||
# Build platform.
|
||||
set(MultiMC_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used by the notification system and to display in the about dialog.")
|
||||
set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used by the notification system and to display in the about dialog.")
|
||||
|
||||
# Channel list URL
|
||||
set(MultiMC_CHANLIST_URL "" CACHE STRING "URL for the channel list.")
|
||||
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
|
||||
|
||||
# Notification URL
|
||||
set(MultiMC_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.")
|
||||
set(Launcher_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.")
|
||||
|
||||
# The metadata server
|
||||
set(MultiMC_META_URL "https://meta.multimc.org/v1/" CACHE STRING "URL to fetch MultiMC's meta files from.")
|
||||
set(Launcher_META_URL "https://meta.multimc.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
|
||||
|
||||
# paste.ee API key
|
||||
set(MultiMC_PASTE_EE_API_KEY "utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ" CACHE STRING "API key you can get from paste.ee when you register an account")
|
||||
set(Launcher_PASTE_EE_API_KEY "utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ" CACHE STRING "API key you can get from paste.ee when you register an account")
|
||||
|
||||
# Imgur API Client ID
|
||||
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
|
||||
|
||||
# Google analytics ID
|
||||
set(MultiMC_ANALYTICS_ID "UA-87731965-2" CACHE STRING "ID you can get from Google analytics")
|
||||
set(Launcher_ANALYTICS_ID "UA-87731965-2" CACHE STRING "ID you can get from Google analytics")
|
||||
|
||||
# Bug tracker URL
|
||||
set(Launcher_BUG_TRACKER_URL "" CACHE STRING "URL for the bug tracker.")
|
||||
|
||||
# Discord URL
|
||||
set(Launcher_DISCORD_URL "" CACHE STRING "URL for the Discord guild.")
|
||||
|
||||
# Subreddit URL
|
||||
set(Launcher_SUBREDDIT_URL "" CACHE STRING "URL for the subreddit.")
|
||||
|
||||
# Use the secrets library or a public stub?
|
||||
option(Launcher_EMBED_SECRETS "Determines whether to embed secrets. Secrets are separate and non-public." OFF)
|
||||
|
||||
#### Check the current Git commit and branch
|
||||
include(GetGitRevisionDescription)
|
||||
get_git_head_revision(MultiMC_GIT_REFSPEC MultiMC_GIT_COMMIT)
|
||||
get_git_head_revision(Launcher_GIT_REFSPEC Launcher_GIT_COMMIT)
|
||||
|
||||
message(STATUS "Git commit: ${MultiMC_GIT_COMMIT}")
|
||||
message(STATUS "Git refspec: ${MultiMC_GIT_REFSPEC}")
|
||||
message(STATUS "Git commit: ${Launcher_GIT_COMMIT}")
|
||||
message(STATUS "Git refspec: ${Launcher_GIT_REFSPEC}")
|
||||
|
||||
set(MultiMC_RELEASE_VERSION_NAME "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}")
|
||||
set(Launcher_RELEASE_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
||||
|
||||
#### Custom target to just print the version.
|
||||
add_custom_target(version echo "Version: ${MultiMC_RELEASE_VERSION_NAME}")
|
||||
add_custom_target(tcversion echo "\\#\\#teamcity[setParameter name=\\'env.MULTIMC_VERSION\\' value=\\'${MultiMC_RELEASE_VERSION_NAME}\\']")
|
||||
add_custom_target(version echo "Version: ${Launcher_RELEASE_VERSION_NAME}")
|
||||
add_custom_target(tcversion echo "\\#\\#teamcity[setParameter name=\\'env.LAUNCHER_VERSION\\' value=\\'${Launcher_RELEASE_VERSION_NAME}\\']")
|
||||
|
||||
################################ 3rd Party Libs ################################
|
||||
|
||||
@@ -114,47 +132,55 @@ if (Qt5_POSITION_INDEPENDENT_CODE)
|
||||
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
endif()
|
||||
|
||||
####################################### Secrets #######################################
|
||||
|
||||
if(Launcher_EMBED_SECRETS)
|
||||
add_subdirectory(secrets)
|
||||
else()
|
||||
add_subdirectory(notsecrets)
|
||||
endif()
|
||||
|
||||
####################################### Install layout #######################################
|
||||
|
||||
# How to install the build results
|
||||
set(MultiMC_LAYOUT "auto" CACHE STRING "The layout for MultiMC installation (auto, win-bundle, lin-bundle, lin-nodeps, lin-system, mac-bundle)")
|
||||
set_property(CACHE MultiMC_LAYOUT PROPERTY STRINGS auto win-bundle lin-bundle lin-nodeps lin-system mac-bundle)
|
||||
set(Launcher_LAYOUT "auto" CACHE STRING "The layout for the launcher installation (auto, win-bundle, lin-nodeps, mac-bundle)")
|
||||
set_property(CACHE Launcher_LAYOUT PROPERTY STRINGS auto win-bundle lin-nodeps mac-bundle)
|
||||
|
||||
if(MultiMC_LAYOUT STREQUAL "auto")
|
||||
if(Launcher_LAYOUT STREQUAL "auto")
|
||||
if(UNIX AND APPLE)
|
||||
set(MultiMC_LAYOUT_REAL "mac-bundle")
|
||||
set(Launcher_LAYOUT_REAL "mac-bundle")
|
||||
elseif(UNIX)
|
||||
set(MultiMC_LAYOUT_REAL "lin-nodeps")
|
||||
set(Launcher_LAYOUT_REAL "lin-nodeps")
|
||||
elseif(WIN32)
|
||||
set(MultiMC_LAYOUT_REAL "win-bundle")
|
||||
set(Launcher_LAYOUT_REAL "win-bundle")
|
||||
else()
|
||||
message(FATAL_ERROR "Cannot choose a sensible install layout for your platform.")
|
||||
endif()
|
||||
else()
|
||||
set(MultiMC_LAYOUT_REAL ${MultiMC_LAYOUT})
|
||||
set(Launcher_LAYOUT_REAL ${Launcher_LAYOUT})
|
||||
endif()
|
||||
|
||||
if(MultiMC_LAYOUT_REAL STREQUAL "mac-bundle")
|
||||
set(BINARY_DEST_DIR "MultiMC.app/Contents/MacOS")
|
||||
set(LIBRARY_DEST_DIR "MultiMC.app/Contents/MacOS")
|
||||
set(PLUGIN_DEST_DIR "MultiMC.app/Contents/MacOS")
|
||||
set(RESOURCES_DEST_DIR "MultiMC.app/Contents/Resources")
|
||||
set(JARS_DEST_DIR "MultiMC.app/Contents/MacOS/jars")
|
||||
if(Launcher_LAYOUT_REAL STREQUAL "mac-bundle")
|
||||
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")
|
||||
set(RESOURCES_DEST_DIR "${Launcher_Name}.app/Contents/Resources")
|
||||
set(JARS_DEST_DIR "${Launcher_Name}.app/Contents/MacOS/jars")
|
||||
|
||||
set(BUNDLE_DEST_DIR ".")
|
||||
|
||||
# Apps to bundle
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.app")
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.app")
|
||||
|
||||
# Mac bundle settings
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "MultiMC")
|
||||
set(MACOSX_BUNDLE_INFO_STRING "MultiMC Minecraft launcher and management utility.")
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.multimc.MultiMC5")
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}")
|
||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}")
|
||||
set(MACOSX_BUNDLE_ICON_FILE MultiMC.icns)
|
||||
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2015-2021 MultiMC Contributors")
|
||||
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_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}")
|
||||
set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns)
|
||||
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2015-2021 ${Launcher_Copyright}")
|
||||
|
||||
# directories to look for dependencies
|
||||
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
||||
@@ -163,32 +189,9 @@ if(MultiMC_LAYOUT_REAL STREQUAL "mac-bundle")
|
||||
set(INSTALL_BUNDLE "full")
|
||||
|
||||
# Add the icon
|
||||
install(FILES application/resources/MultiMC.icns DESTINATION ${RESOURCES_DEST_DIR})
|
||||
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
|
||||
|
||||
elseif(MultiMC_LAYOUT_REAL STREQUAL "lin-bundle")
|
||||
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")
|
||||
|
||||
# Apps to bundle
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/MultiMC")
|
||||
|
||||
# directories to look for dependencies
|
||||
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
||||
|
||||
# install as bundle
|
||||
set(INSTALL_BUNDLE "full")
|
||||
|
||||
# Set RPATH
|
||||
SET(MultiMC_BINARY_RPATH "$ORIGIN/")
|
||||
|
||||
# Install basic runner script
|
||||
install(PROGRAMS application/package/linux/MultiMC DESTINATION ${BUNDLE_DEST_DIR})
|
||||
|
||||
elseif(MultiMC_LAYOUT_REAL STREQUAL "lin-nodeps")
|
||||
elseif(Launcher_LAYOUT_REAL STREQUAL "lin-nodeps")
|
||||
set(BINARY_DEST_DIR "bin")
|
||||
set(LIBRARY_DEST_DIR "bin")
|
||||
set(PLUGIN_DEST_DIR "plugins")
|
||||
@@ -200,28 +203,13 @@ elseif(MultiMC_LAYOUT_REAL STREQUAL "lin-nodeps")
|
||||
set(INSTALL_BUNDLE "nodeps")
|
||||
|
||||
# Set RPATH
|
||||
SET(MultiMC_BINARY_RPATH "$ORIGIN/")
|
||||
SET(Launcher_BINARY_RPATH "$ORIGIN/")
|
||||
|
||||
# Install basic runner script
|
||||
install(PROGRAMS application/package/linux/MultiMC DESTINATION ${BUNDLE_DEST_DIR})
|
||||
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(MultiMC_LAYOUT_REAL STREQUAL "lin-system")
|
||||
set(MultiMC_APP_BINARY_NAME "multimc" CACHE STRING "Name of the MultiMC binary")
|
||||
set(MultiMC_BINARY_DEST_DIR "bin" CACHE STRING "Path to the binary directory")
|
||||
set(MultiMC_LIBRARY_DEST_DIR "lib${LIB_SUFFIX}" CACHE STRING "Path to the library directory")
|
||||
set(MultiMC_SHARE_DEST_DIR "share/multimc" CACHE STRING "Path to the shared data directory")
|
||||
set(JARS_DEST_DIR "${MultiMC_SHARE_DEST_DIR}/jars")
|
||||
|
||||
set(BINARY_DEST_DIR ${MultiMC_BINARY_DEST_DIR})
|
||||
set(LIBRARY_DEST_DIR ${MultiMC_LIBRARY_DEST_DIR})
|
||||
|
||||
MESSAGE(STATUS "Compiling for linux system with ${MultiMC_SHARE_DEST_DIR} and MULTIMC_LINUX_DATADIR")
|
||||
SET(MultiMC_APP_BINARY_DEFS "-DMULTIMC_JARS_LOCATION=${CMAKE_INSTALL_PREFIX}/${JARS_DEST_DIR}" "-DMULTIMC_LINUX_DATADIR")
|
||||
|
||||
# install as bundle with no dependencies included
|
||||
set(INSTALL_BUNDLE "nodeps")
|
||||
|
||||
elseif(MultiMC_LAYOUT_REAL STREQUAL "win-bundle")
|
||||
elseif(Launcher_LAYOUT_REAL STREQUAL "win-bundle")
|
||||
set(BINARY_DEST_DIR ".")
|
||||
set(LIBRARY_DEST_DIR ".")
|
||||
set(PLUGIN_DEST_DIR ".")
|
||||
@@ -230,7 +218,7 @@ elseif(MultiMC_LAYOUT_REAL STREQUAL "win-bundle")
|
||||
set(JARS_DEST_DIR "jars")
|
||||
|
||||
# Apps to bundle
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.exe")
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.exe")
|
||||
|
||||
# directories to look for dependencies
|
||||
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
||||
@@ -249,7 +237,7 @@ set_directory_properties(PROPERTIES EP_BASE External)
|
||||
option(NBT_BUILD_SHARED "Build NBT shared library" ON)
|
||||
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 MultiMC_nbt++)
|
||||
set(NBT_NAME Launcher_nbt++)
|
||||
set(NBT_DEST_DIR ${LIBRARY_DEST_DIR})
|
||||
add_subdirectory(libraries/libnbtplusplus)
|
||||
|
||||
@@ -265,12 +253,12 @@ add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader
|
||||
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
|
||||
add_subdirectory(libraries/classparser) # google analytics library
|
||||
add_subdirectory(libraries/optional-bare)
|
||||
add_subdirectory(libraries/tomlc99) # toml parser
|
||||
add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much
|
||||
|
||||
############################### Built Artifacts ###############################
|
||||
|
||||
add_subdirectory(buildconfig)
|
||||
add_subdirectory(api/logic)
|
||||
add_subdirectory(api/gui)
|
||||
|
||||
# NOTE: this must always be last to appease the CMake deity of quirky install command evaluation order.
|
||||
add_subdirectory(application)
|
||||
add_subdirectory(launcher)
|
||||
|
||||
51
COPYING.md
@@ -251,3 +251,54 @@
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
# tomlc99
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 CK Tan
|
||||
https://github.com/cktan/tomlc99
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
# O2 (Katabasis fork)
|
||||
|
||||
Copyright (c) 2012, Akos Polster
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
76
README.md
@@ -2,78 +2,52 @@
|
||||
<img src="https://avatars2.githubusercontent.com/u/5411890" alt="MultiMC logo"/>
|
||||
</p>
|
||||
|
||||
MultiMC 5
|
||||
=========
|
||||
|
||||
MultiMC is a custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once. It also allows you to easily install and remove mods by simply dragging and dropping. Here are the current [features](https://github.com/MultiMC/MultiMC5/wiki#features) of MultiMC.
|
||||
MultiMC
|
||||
=======
|
||||
|
||||
MultiMC is a custom launcher for Minecraft that focuses on predictability, long term stability and simplicity.
|
||||
|
||||
## Development
|
||||
The project uses C++ and Qt5 as the language and base framework. This might seem odd in the Minecraft community, but allows using 25MB of RAM, where other tools use an excessive amount of resources for no reason.
|
||||
If you want to contribute, talk to us on [Discord](https://discord.gg/multimc) first.
|
||||
|
||||
We can do more, with less, on worse hardware and leave more resources for the game while keeping the launcher running and providing extra features.
|
||||
While blindly submitting PRs is definitely possible, they're not necessarily going to get accepted.
|
||||
|
||||
If you want to contribute, either talk to us on [Discord](https://discord.gg/multimc), [IRC](http://webchat.esper.net/?nick=&channels=MultiMC)(esper.net/#MultiMC) or pick up some item from the github issues [workflowy](https://github.com/MultiMC/MultiMC5/issues) - there is always plenty of ideas around.
|
||||
We aren't looking for flashy features, but expanding upon the existing feature set without distruption or endangering future viability of the project is OK.
|
||||
|
||||
### Building
|
||||
If you want to build MultiMC yourself, check [BUILD.md](BUILD.md) for build instructions.
|
||||
If you want to build the launcher yourself, check [BUILD.md](BUILD.md) for build instructions.
|
||||
|
||||
### Code formatting
|
||||
Just follow the existing formatting.
|
||||
|
||||
In general:
|
||||
* Indent with 4 space unless it's in a submodule
|
||||
* Keep lists (of arguments, parameters, initializators...) as lists, not paragraphs.
|
||||
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
|
||||
Translations can be done [on crowdin](https://translate.multimc.org).
|
||||
|
||||
## Forking/Redistributing
|
||||
We keep MultiMC open source because we think it's important to be able to see the source code for a project like this, and we do so using the Apache license.
|
||||
|
||||
Part of the reason for using the Apache license is we don't want people using the "MultiMC" name when redistributing the project. This means people must take the time to go through the source code and remove all references to "MultiMC", including but not limited to the project icon and the title of windows, (no *MultiMC-fork* in the title).
|
||||
|
||||
Apache covers reasonable use for the name - a mention of the project's origins in the About dialog and the license is acceptable. However, it should be abundantly clear that the project is a fork *without* implying that you have our blessing.
|
||||
|
||||
Translations can be done [on crowdin](https://translate.multimc.org). Please avoid making direct pull requests to the translations repository.
|
||||
|
||||
## License
|
||||
Copyright © 2013-2021 MultiMC Contributors
|
||||
Copyright © 2013-2022 MultiMC Contributors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this program except in compliance with the License. You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](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.
|
||||
|
||||
## Build status
|
||||
### Linux (Intel32)
|
||||
<a href="https://teamcity.multimc.org/viewType.html?buildTypeId=MultiMC_Launcher_Linux32_Build&guest=1">
|
||||
Build: <img src="https://teamcity.multimc.org/app/rest/builds/buildType:(id:MultiMC_Launcher_Linux32_Build)/statusIcon"/>
|
||||
</a>
|
||||
<a href="https://teamcity.multimc.org/viewType.html?buildTypeId=MultiMC_Launcher_Linux32_Deploy&guest=1">
|
||||
Deploy: <img src="https://teamcity.multimc.org/app/rest/builds/buildType:(id:MultiMC_Launcher_Linux32_Deploy)/statusIcon"/>
|
||||
</a>
|
||||
## Forking/Redistributing/Custom builds policy
|
||||
We keep Launcher open source because we think it's important to be able to see the source code for a project like this, and we do so using the Apache license.
|
||||
|
||||
### Linux (AMD64)
|
||||
<a href="https://teamcity.multimc.org/viewType.html?buildTypeId=MultiMC_Launcher_Linux64_Build&guest=1">
|
||||
Build: <img src="https://teamcity.multimc.org/app/rest/builds/buildType:(id:MultiMC_Launcher_Linux64_Build)/statusIcon"/>
|
||||
</a>
|
||||
<a href="https://teamcity.multimc.org/viewType.html?buildTypeId=MultiMC_Launcher_Linux64_Deploy&guest=1">
|
||||
Deploy: <img src="https://teamcity.multimc.org/app/rest/builds/buildType:(id:MultiMC_Launcher_Linux64_Deploy)/statusIcon"/>
|
||||
</a>
|
||||
The license gives you access to the source MultiMC is build from, but:
|
||||
- Not the name, logo and other branding.
|
||||
- Not the API tokens required to talk to services the launcher depends on.
|
||||
|
||||
### macOS (AMD64)
|
||||
<a href="https://teamcity.multimc.org/viewType.html?buildTypeId=MultiMC_Launcher_MacOS_Build&guest=1">
|
||||
Build: <img src="https://teamcity.multimc.org/app/rest/builds/buildType:(id:MultiMC_Launcher_MacOS_Build)/statusIcon"/>
|
||||
</a>
|
||||
<a href="https://teamcity.multimc.org/viewType.html?buildTypeId=MultiMC_Launcher_MacOS_Deploy&guest=1">
|
||||
Deploy: <img src="https://teamcity.multimc.org/app/rest/builds/buildType:(id:MultiMC_Launcher_MacOS_Deploy)/statusIcon"/>
|
||||
</a>
|
||||
Because of the nature of the agreements required to interact with the Microsoft identity platform, it's impossible for us to continue allowing everyone to build the code as 'MultiMC'. The source code has been debranded and now builds as `DevLauncher` by default.
|
||||
|
||||
### Windows (Intel32)
|
||||
<a href="https://teamcity.multimc.org/viewType.html?buildTypeId=MultiMC_Launcher_Windows_Build&guest=1">
|
||||
Build: <img src="https://teamcity.multimc.org/app/rest/builds/buildType:(id:MultiMC_Launcher_Windows_Build)/statusIcon"/>
|
||||
</a>
|
||||
<a href="https://teamcity.multimc.org/viewType.html?buildTypeId=MultiMC_Launcher_Windows_Deploy&guest=1">
|
||||
Deploy: <img src="https://teamcity.multimc.org/app/rest/builds/buildType:(id:MultiMC_Launcher_Windows_Deploy)/statusIcon"/>
|
||||
</a>
|
||||
You must provide your own branding if you want to distribute your own builds.
|
||||
|
||||
You will also have to register your own app on Azure to be able to handle Microsoft account logins.
|
||||
|
||||
If you decide to fork the project, a mention of its origins in the About dialog and the license is acceptable. However, it should be abundantly clear that the project is a fork *without* implying that you have our blessing.
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
project(MultiMC_gui LANGUAGES CXX)
|
||||
|
||||
set(GUI_SOURCES
|
||||
DesktopServices.h
|
||||
DesktopServices.cpp
|
||||
|
||||
# Icons
|
||||
icons/MMCIcon.h
|
||||
icons/MMCIcon.cpp
|
||||
icons/IconList.h
|
||||
icons/IconList.cpp
|
||||
|
||||
SkinUtils.cpp
|
||||
SkinUtils.h
|
||||
)
|
||||
################################ COMPILE ################################
|
||||
|
||||
add_library(MultiMC_gui SHARED ${GUI_SOURCES})
|
||||
set_target_properties(MultiMC_gui PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1)
|
||||
|
||||
generate_export_header(MultiMC_gui)
|
||||
|
||||
# Link
|
||||
target_link_libraries(MultiMC_gui MultiMC_iconfix MultiMC_logic Qt5::Gui)
|
||||
|
||||
# Mark and export headers
|
||||
target_include_directories(MultiMC_gui PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
# Install it
|
||||
install(
|
||||
TARGETS MultiMC_gui
|
||||
RUNTIME DESTINATION ${LIBRARY_DEST_DIR}
|
||||
LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
|
||||
)
|
||||
@@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QUrl>
|
||||
#include <QString>
|
||||
#include "multimc_gui_export.h"
|
||||
|
||||
/**
|
||||
* This wraps around QDesktopServices and adds workarounds where needed
|
||||
* Use this instead of QDesktopServices!
|
||||
*/
|
||||
namespace DesktopServices
|
||||
{
|
||||
/**
|
||||
* Open a file in whatever application is applicable
|
||||
*/
|
||||
MULTIMC_GUI_EXPORT bool openFile(const QString &path);
|
||||
|
||||
/**
|
||||
* Open a file in the specified application
|
||||
*/
|
||||
MULTIMC_GUI_EXPORT bool openFile(const QString &application, const QString &path, const QString & workingDirectory = QString(), qint64 *pid = 0);
|
||||
|
||||
/**
|
||||
* Run an application
|
||||
*/
|
||||
MULTIMC_GUI_EXPORT bool run(const QString &application,const QStringList &args, const QString & workingDirectory = QString(), qint64 *pid = 0);
|
||||
|
||||
/**
|
||||
* Open a directory
|
||||
*/
|
||||
MULTIMC_GUI_EXPORT bool openDirectory(const QString &path, bool ensureExists = false);
|
||||
|
||||
/**
|
||||
* Open the URL, most likely in a browser. Maybe.
|
||||
*/
|
||||
MULTIMC_GUI_EXPORT bool openUrl(const QUrl &url);
|
||||
}
|
||||
@@ -1,552 +0,0 @@
|
||||
project(MultiMC_logic)
|
||||
|
||||
include (UnitTest)
|
||||
|
||||
set(CORE_SOURCES
|
||||
# LOGIC - Base classes and infrastructure
|
||||
BaseInstaller.h
|
||||
BaseInstaller.cpp
|
||||
BaseVersionList.h
|
||||
BaseVersionList.cpp
|
||||
InstanceList.h
|
||||
InstanceList.cpp
|
||||
InstanceTask.h
|
||||
InstanceTask.cpp
|
||||
LoggedProcess.h
|
||||
LoggedProcess.cpp
|
||||
MessageLevel.cpp
|
||||
MessageLevel.h
|
||||
BaseVersion.h
|
||||
BaseInstance.h
|
||||
BaseInstance.cpp
|
||||
NullInstance.h
|
||||
MMCZip.h
|
||||
MMCZip.cpp
|
||||
MMCStrings.h
|
||||
MMCStrings.cpp
|
||||
|
||||
# Basic instance manipulation tasks (derived from InstanceTask)
|
||||
InstanceCreationTask.h
|
||||
InstanceCreationTask.cpp
|
||||
InstanceCopyTask.h
|
||||
InstanceCopyTask.cpp
|
||||
InstanceImportTask.h
|
||||
InstanceImportTask.cpp
|
||||
|
||||
# Use tracking separate from memory management
|
||||
Usable.h
|
||||
|
||||
# Prefix tree where node names are strings between separators
|
||||
SeparatorPrefixTree.h
|
||||
|
||||
# WARNING: globals live here
|
||||
Env.h
|
||||
Env.cpp
|
||||
|
||||
# String filters
|
||||
Filter.h
|
||||
Filter.cpp
|
||||
|
||||
# JSON parsing helpers
|
||||
Json.h
|
||||
Json.cpp
|
||||
|
||||
FileSystem.h
|
||||
FileSystem.cpp
|
||||
|
||||
Exception.h
|
||||
|
||||
# RW lock protected map
|
||||
RWStorage.h
|
||||
|
||||
# A variable that has an implicit default value and keeps track of changes
|
||||
DefaultVariable.h
|
||||
|
||||
# a smart pointer wrapper intended for safer use with Qt signal/slot mechanisms
|
||||
QObjectPtr.h
|
||||
|
||||
# Compression support
|
||||
GZip.h
|
||||
GZip.cpp
|
||||
|
||||
# Command line parameter parsing
|
||||
Commandline.h
|
||||
Commandline.cpp
|
||||
|
||||
# Version number string support
|
||||
Version.h
|
||||
Version.cpp
|
||||
|
||||
# A Recursive file system watcher
|
||||
RecursiveFileSystemWatcher.h
|
||||
RecursiveFileSystemWatcher.cpp
|
||||
)
|
||||
|
||||
add_unit_test(FileSystem
|
||||
SOURCES FileSystem_test.cpp
|
||||
LIBS MultiMC_logic
|
||||
DATA testdata
|
||||
)
|
||||
|
||||
add_unit_test(GZip
|
||||
SOURCES GZip_test.cpp
|
||||
LIBS MultiMC_logic
|
||||
)
|
||||
|
||||
set(PATHMATCHER_SOURCES
|
||||
# Path matchers
|
||||
pathmatcher/FSTreeMatcher.h
|
||||
pathmatcher/IPathMatcher.h
|
||||
pathmatcher/MultiMatcher.h
|
||||
pathmatcher/RegexpMatcher.h
|
||||
)
|
||||
|
||||
set(NET_SOURCES
|
||||
# network stuffs
|
||||
net/ByteArraySink.h
|
||||
net/ChecksumValidator.h
|
||||
net/Download.cpp
|
||||
net/Download.h
|
||||
net/FileSink.cpp
|
||||
net/FileSink.h
|
||||
net/HttpMetaCache.cpp
|
||||
net/HttpMetaCache.h
|
||||
net/MetaCacheSink.cpp
|
||||
net/MetaCacheSink.h
|
||||
net/NetAction.h
|
||||
net/NetJob.cpp
|
||||
net/NetJob.h
|
||||
net/PasteUpload.cpp
|
||||
net/PasteUpload.h
|
||||
net/Sink.h
|
||||
net/Validator.h
|
||||
)
|
||||
|
||||
# Game launch logic
|
||||
set(LAUNCH_SOURCES
|
||||
launch/steps/PostLaunchCommand.cpp
|
||||
launch/steps/PostLaunchCommand.h
|
||||
launch/steps/PreLaunchCommand.cpp
|
||||
launch/steps/PreLaunchCommand.h
|
||||
launch/steps/TextPrint.cpp
|
||||
launch/steps/TextPrint.h
|
||||
launch/steps/Update.cpp
|
||||
launch/steps/Update.h
|
||||
launch/LaunchStep.cpp
|
||||
launch/LaunchStep.h
|
||||
launch/LaunchTask.cpp
|
||||
launch/LaunchTask.h
|
||||
launch/LogModel.cpp
|
||||
launch/LogModel.h
|
||||
)
|
||||
|
||||
# Old update system
|
||||
set(UPDATE_SOURCES
|
||||
updater/GoUpdate.h
|
||||
updater/GoUpdate.cpp
|
||||
updater/UpdateChecker.h
|
||||
updater/UpdateChecker.cpp
|
||||
updater/DownloadTask.h
|
||||
updater/DownloadTask.cpp
|
||||
)
|
||||
|
||||
add_unit_test(UpdateChecker
|
||||
SOURCES updater/UpdateChecker_test.cpp
|
||||
LIBS MultiMC_logic
|
||||
DATA updater/testdata
|
||||
)
|
||||
|
||||
add_unit_test(DownloadTask
|
||||
SOURCES updater/DownloadTask_test.cpp
|
||||
LIBS MultiMC_logic
|
||||
DATA updater/testdata
|
||||
)
|
||||
|
||||
# Rarely used notifications
|
||||
set(NOTIFICATIONS_SOURCES
|
||||
# Notifications - short warning messages
|
||||
notifications/NotificationChecker.h
|
||||
notifications/NotificationChecker.cpp
|
||||
)
|
||||
|
||||
# Backend for the news bar... there's usually no news.
|
||||
set(NEWS_SOURCES
|
||||
# News System
|
||||
news/NewsChecker.h
|
||||
news/NewsChecker.cpp
|
||||
news/NewsEntry.h
|
||||
news/NewsEntry.cpp
|
||||
)
|
||||
|
||||
# Icon interface
|
||||
set(ICONS_SOURCES
|
||||
# Icons System and related code
|
||||
icons/IIconList.h
|
||||
icons/IIconList.cpp
|
||||
icons/IconUtils.h
|
||||
icons/IconUtils.cpp
|
||||
)
|
||||
|
||||
# Minecraft services status checker
|
||||
set(STATUS_SOURCES
|
||||
# Status system
|
||||
status/StatusChecker.h
|
||||
status/StatusChecker.cpp
|
||||
)
|
||||
|
||||
# Support for Minecraft instances and launch
|
||||
set(MINECRAFT_SOURCES
|
||||
# Minecraft support
|
||||
minecraft/auth/AuthSession.h
|
||||
minecraft/auth/AuthSession.cpp
|
||||
minecraft/auth/MojangAccountList.h
|
||||
minecraft/auth/MojangAccountList.cpp
|
||||
minecraft/auth/MojangAccount.h
|
||||
minecraft/auth/MojangAccount.cpp
|
||||
minecraft/auth/YggdrasilTask.h
|
||||
minecraft/auth/YggdrasilTask.cpp
|
||||
minecraft/auth/flows/AuthenticateTask.h
|
||||
minecraft/auth/flows/AuthenticateTask.cpp
|
||||
minecraft/auth/flows/RefreshTask.cpp
|
||||
minecraft/auth/flows/RefreshTask.cpp
|
||||
minecraft/auth/flows/ValidateTask.h
|
||||
minecraft/auth/flows/ValidateTask.cpp
|
||||
|
||||
minecraft/gameoptions/GameOptions.h
|
||||
minecraft/gameoptions/GameOptions.cpp
|
||||
|
||||
minecraft/update/AssetUpdateTask.h
|
||||
minecraft/update/AssetUpdateTask.cpp
|
||||
minecraft/update/FMLLibrariesTask.cpp
|
||||
minecraft/update/FMLLibrariesTask.h
|
||||
minecraft/update/FoldersTask.cpp
|
||||
minecraft/update/FoldersTask.h
|
||||
minecraft/update/LibrariesTask.cpp
|
||||
minecraft/update/LibrariesTask.h
|
||||
|
||||
minecraft/launch/ClaimAccount.cpp
|
||||
minecraft/launch/ClaimAccount.h
|
||||
minecraft/launch/CreateGameFolders.cpp
|
||||
minecraft/launch/CreateGameFolders.h
|
||||
minecraft/launch/ModMinecraftJar.cpp
|
||||
minecraft/launch/ModMinecraftJar.h
|
||||
minecraft/launch/DirectJavaLaunch.cpp
|
||||
minecraft/launch/DirectJavaLaunch.h
|
||||
minecraft/launch/ExtractNatives.cpp
|
||||
minecraft/launch/ExtractNatives.h
|
||||
minecraft/launch/LauncherPartLaunch.cpp
|
||||
minecraft/launch/LauncherPartLaunch.h
|
||||
minecraft/launch/PrintInstanceInfo.cpp
|
||||
minecraft/launch/PrintInstanceInfo.h
|
||||
minecraft/launch/ReconstructAssets.cpp
|
||||
minecraft/launch/ReconstructAssets.h
|
||||
minecraft/launch/ScanModFolders.cpp
|
||||
minecraft/launch/ScanModFolders.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
|
||||
minecraft/LaunchProfile.cpp
|
||||
minecraft/LaunchProfile.h
|
||||
minecraft/Component.cpp
|
||||
minecraft/Component.h
|
||||
minecraft/PackProfile.cpp
|
||||
minecraft/PackProfile.h
|
||||
minecraft/ComponentUpdateTask.cpp
|
||||
minecraft/ComponentUpdateTask.h
|
||||
minecraft/MinecraftLoadAndCheck.h
|
||||
minecraft/MinecraftLoadAndCheck.cpp
|
||||
minecraft/MinecraftUpdate.h
|
||||
minecraft/MinecraftUpdate.cpp
|
||||
minecraft/MojangVersionFormat.cpp
|
||||
minecraft/MojangVersionFormat.h
|
||||
minecraft/Rule.cpp
|
||||
minecraft/Rule.h
|
||||
minecraft/OneSixVersionFormat.cpp
|
||||
minecraft/OneSixVersionFormat.h
|
||||
minecraft/OpSys.cpp
|
||||
minecraft/OpSys.h
|
||||
minecraft/ParseUtils.cpp
|
||||
minecraft/ParseUtils.h
|
||||
minecraft/ProfileUtils.cpp
|
||||
minecraft/ProfileUtils.h
|
||||
minecraft/Library.cpp
|
||||
minecraft/Library.h
|
||||
minecraft/MojangDownloadInfo.h
|
||||
minecraft/VersionFile.cpp
|
||||
minecraft/VersionFile.h
|
||||
minecraft/VersionFilterData.h
|
||||
minecraft/VersionFilterData.cpp
|
||||
minecraft/World.h
|
||||
minecraft/World.cpp
|
||||
minecraft/WorldList.h
|
||||
minecraft/WorldList.cpp
|
||||
|
||||
minecraft/mod/Mod.h
|
||||
minecraft/mod/Mod.cpp
|
||||
minecraft/mod/ModDetails.h
|
||||
minecraft/mod/ModFolderModel.h
|
||||
minecraft/mod/ModFolderModel.cpp
|
||||
minecraft/mod/ModFolderLoadTask.h
|
||||
minecraft/mod/ModFolderLoadTask.cpp
|
||||
minecraft/mod/LocalModParseTask.h
|
||||
minecraft/mod/LocalModParseTask.cpp
|
||||
|
||||
# Assets
|
||||
minecraft/AssetsUtils.h
|
||||
minecraft/AssetsUtils.cpp
|
||||
|
||||
# Minecraft services
|
||||
minecraft/services/SkinUpload.cpp
|
||||
minecraft/services/SkinUpload.h
|
||||
minecraft/services/SkinDelete.cpp
|
||||
minecraft/services/SkinDelete.h
|
||||
|
||||
mojang/PackageManifest.h
|
||||
mojang/PackageManifest.cpp
|
||||
)
|
||||
|
||||
add_unit_test(GradleSpecifier
|
||||
SOURCES minecraft/GradleSpecifier_test.cpp
|
||||
LIBS MultiMC_logic
|
||||
)
|
||||
|
||||
add_executable(PackageManifest
|
||||
mojang/PackageManifest_test.cpp
|
||||
)
|
||||
target_link_libraries(PackageManifest
|
||||
MultiMC_logic
|
||||
Qt5::Test
|
||||
)
|
||||
target_include_directories(PackageManifest
|
||||
PRIVATE ../../cmake/UnitTest/
|
||||
)
|
||||
add_test(
|
||||
NAME PackageManifest
|
||||
COMMAND PackageManifest
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
add_unit_test(MojangVersionFormat
|
||||
SOURCES minecraft/MojangVersionFormat_test.cpp
|
||||
LIBS MultiMC_logic
|
||||
DATA minecraft/testdata
|
||||
)
|
||||
|
||||
add_unit_test(Library
|
||||
SOURCES minecraft/Library_test.cpp
|
||||
LIBS MultiMC_logic
|
||||
)
|
||||
|
||||
# FIXME: shares data with FileSystem test
|
||||
add_unit_test(ModFolderModel
|
||||
SOURCES minecraft/mod/ModFolderModel_test.cpp
|
||||
DATA testdata
|
||||
LIBS MultiMC_logic
|
||||
)
|
||||
|
||||
add_unit_test(ParseUtils
|
||||
SOURCES minecraft/ParseUtils_test.cpp
|
||||
LIBS MultiMC_logic
|
||||
)
|
||||
|
||||
# the screenshots feature
|
||||
set(SCREENSHOTS_SOURCES
|
||||
screenshots/Screenshot.h
|
||||
screenshots/ImgurUpload.h
|
||||
screenshots/ImgurUpload.cpp
|
||||
screenshots/ImgurAlbumCreation.h
|
||||
screenshots/ImgurAlbumCreation.cpp
|
||||
)
|
||||
|
||||
set(TASKS_SOURCES
|
||||
# Tasks
|
||||
tasks/Task.h
|
||||
tasks/Task.cpp
|
||||
tasks/SequentialTask.h
|
||||
tasks/SequentialTask.cpp
|
||||
)
|
||||
|
||||
set(SETTINGS_SOURCES
|
||||
# Settings
|
||||
settings/INIFile.cpp
|
||||
settings/INIFile.h
|
||||
settings/INISettingsObject.cpp
|
||||
settings/INISettingsObject.h
|
||||
settings/OverrideSetting.cpp
|
||||
settings/OverrideSetting.h
|
||||
settings/PassthroughSetting.cpp
|
||||
settings/PassthroughSetting.h
|
||||
settings/Setting.cpp
|
||||
settings/Setting.h
|
||||
settings/SettingsObject.cpp
|
||||
settings/SettingsObject.h
|
||||
)
|
||||
|
||||
add_unit_test(INIFile
|
||||
SOURCES settings/INIFile_test.cpp
|
||||
LIBS MultiMC_logic
|
||||
)
|
||||
|
||||
set(JAVA_SOURCES
|
||||
# Java related code
|
||||
java/launch/CheckJava.cpp
|
||||
java/launch/CheckJava.h
|
||||
java/JavaChecker.h
|
||||
java/JavaChecker.cpp
|
||||
java/JavaCheckerJob.h
|
||||
java/JavaCheckerJob.cpp
|
||||
java/JavaInstall.h
|
||||
java/JavaInstall.cpp
|
||||
java/JavaInstallList.h
|
||||
java/JavaInstallList.cpp
|
||||
java/JavaUtils.h
|
||||
java/JavaUtils.cpp
|
||||
java/JavaVersion.h
|
||||
java/JavaVersion.cpp
|
||||
)
|
||||
|
||||
add_unit_test(JavaVersion
|
||||
SOURCES java/JavaVersion_test.cpp
|
||||
LIBS MultiMC_logic
|
||||
)
|
||||
|
||||
set(TRANSLATIONS_SOURCES
|
||||
translations/TranslationsModel.h
|
||||
translations/TranslationsModel.cpp
|
||||
translations/POTranslator.h
|
||||
translations/POTranslator.cpp
|
||||
)
|
||||
|
||||
set(TOOLS_SOURCES
|
||||
# Tools
|
||||
tools/BaseExternalTool.cpp
|
||||
tools/BaseExternalTool.h
|
||||
tools/BaseProfiler.cpp
|
||||
tools/BaseProfiler.h
|
||||
tools/JProfiler.cpp
|
||||
tools/JProfiler.h
|
||||
tools/JVisualVM.cpp
|
||||
tools/JVisualVM.h
|
||||
tools/MCEditTool.cpp
|
||||
tools/MCEditTool.h
|
||||
)
|
||||
|
||||
set(META_SOURCES
|
||||
# Metadata sources
|
||||
meta/JsonFormat.cpp
|
||||
meta/JsonFormat.h
|
||||
meta/BaseEntity.cpp
|
||||
meta/BaseEntity.h
|
||||
meta/VersionList.cpp
|
||||
meta/VersionList.h
|
||||
meta/Version.cpp
|
||||
meta/Version.h
|
||||
meta/Index.cpp
|
||||
meta/Index.h
|
||||
)
|
||||
|
||||
set(FTB_SOURCES
|
||||
modplatform/legacy_ftb/PackFetchTask.h
|
||||
modplatform/legacy_ftb/PackFetchTask.cpp
|
||||
modplatform/legacy_ftb/PackInstallTask.h
|
||||
modplatform/legacy_ftb/PackInstallTask.cpp
|
||||
modplatform/legacy_ftb/PrivatePackManager.h
|
||||
modplatform/legacy_ftb/PrivatePackManager.cpp
|
||||
|
||||
modplatform/legacy_ftb/PackHelpers.h
|
||||
)
|
||||
|
||||
set(FLAME_SOURCES
|
||||
# Flame
|
||||
modplatform/flame/PackManifest.h
|
||||
modplatform/flame/PackManifest.cpp
|
||||
modplatform/flame/FileResolvingTask.h
|
||||
modplatform/flame/FileResolvingTask.cpp
|
||||
)
|
||||
|
||||
set(MODPACKSCH_SOURCES
|
||||
modplatform/modpacksch/FTBPackInstallTask.h
|
||||
modplatform/modpacksch/FTBPackInstallTask.cpp
|
||||
modplatform/modpacksch/FTBPackManifest.h
|
||||
modplatform/modpacksch/FTBPackManifest.cpp
|
||||
)
|
||||
|
||||
set(TECHNIC_SOURCES
|
||||
modplatform/technic/SingleZipPackInstallTask.h
|
||||
modplatform/technic/SingleZipPackInstallTask.cpp
|
||||
modplatform/technic/SolderPackInstallTask.h
|
||||
modplatform/technic/SolderPackInstallTask.cpp
|
||||
modplatform/technic/TechnicPackProcessor.h
|
||||
modplatform/technic/TechnicPackProcessor.cpp
|
||||
)
|
||||
|
||||
set(ATLAUNCHER_SOURCES
|
||||
modplatform/atlauncher/ATLPackIndex.cpp
|
||||
modplatform/atlauncher/ATLPackIndex.h
|
||||
modplatform/atlauncher/ATLPackInstallTask.cpp
|
||||
modplatform/atlauncher/ATLPackInstallTask.h
|
||||
modplatform/atlauncher/ATLPackManifest.cpp
|
||||
modplatform/atlauncher/ATLPackManifest.h
|
||||
)
|
||||
|
||||
add_unit_test(Index
|
||||
SOURCES meta/Index_test.cpp
|
||||
LIBS MultiMC_logic
|
||||
)
|
||||
|
||||
################################ COMPILE ################################
|
||||
|
||||
# we need zlib
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
set(LOGIC_SOURCES
|
||||
${CORE_SOURCES}
|
||||
${PATHMATCHER_SOURCES}
|
||||
${NET_SOURCES}
|
||||
${LAUNCH_SOURCES}
|
||||
${UPDATE_SOURCES}
|
||||
${NOTIFICATIONS_SOURCES}
|
||||
${NEWS_SOURCES}
|
||||
${STATUS_SOURCES}
|
||||
${MINECRAFT_SOURCES}
|
||||
${SCREENSHOTS_SOURCES}
|
||||
${TASKS_SOURCES}
|
||||
${SETTINGS_SOURCES}
|
||||
${JAVA_SOURCES}
|
||||
${TRANSLATIONS_SOURCES}
|
||||
${TOOLS_SOURCES}
|
||||
${META_SOURCES}
|
||||
${ICONS_SOURCES}
|
||||
${FTB_SOURCES}
|
||||
${FLAME_SOURCES}
|
||||
${MODPACKSCH_SOURCES}
|
||||
${TECHNIC_SOURCES}
|
||||
${ATLAUNCHER_SOURCES}
|
||||
)
|
||||
|
||||
add_library(MultiMC_logic SHARED ${LOGIC_SOURCES})
|
||||
set_target_properties(MultiMC_logic PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1)
|
||||
|
||||
generate_export_header(MultiMC_logic)
|
||||
|
||||
# Link
|
||||
target_link_libraries(MultiMC_logic systeminfo MultiMC_quazip MultiMC_classparser ${NBT_NAME} ${ZLIB_LIBRARIES} optional-bare BuildConfig)
|
||||
target_link_libraries(MultiMC_logic Qt5::Core Qt5::Xml Qt5::Network Qt5::Concurrent)
|
||||
|
||||
# Mark and export headers
|
||||
target_include_directories(MultiMC_logic PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" PRIVATE "${ZLIB_INCLUDE_DIRS}")
|
||||
|
||||
# Install it
|
||||
install(
|
||||
TARGETS MultiMC_logic
|
||||
RUNTIME DESTINATION ${LIBRARY_DEST_DIR}
|
||||
LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
|
||||
)
|
||||
@@ -1,211 +0,0 @@
|
||||
#include "Env.h"
|
||||
#include "net/HttpMetaCache.h"
|
||||
#include "BaseVersion.h"
|
||||
#include "BaseVersionList.h"
|
||||
#include <QDir>
|
||||
#include <QCoreApplication>
|
||||
#include <QNetworkProxy>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QDebug>
|
||||
#include "tasks/Task.h"
|
||||
#include "meta/Index.h"
|
||||
#include "FileSystem.h"
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
struct Env::Private
|
||||
{
|
||||
QNetworkAccessManager m_qnam;
|
||||
shared_qobject_ptr<HttpMetaCache> m_metacache;
|
||||
std::shared_ptr<IIconList> m_iconlist;
|
||||
shared_qobject_ptr<Meta::Index> m_metadataIndex;
|
||||
QString m_jarsPath;
|
||||
QSet<QString> m_features;
|
||||
};
|
||||
|
||||
static Env * instance;
|
||||
|
||||
/*
|
||||
* The *NEW* global rat nest of an object. Handle with care.
|
||||
*/
|
||||
|
||||
Env::Env()
|
||||
{
|
||||
d = new Private();
|
||||
}
|
||||
|
||||
Env::~Env()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
Env& Env::Env::getInstance()
|
||||
{
|
||||
if(!instance)
|
||||
{
|
||||
instance = new Env();
|
||||
}
|
||||
return *instance;
|
||||
}
|
||||
|
||||
void Env::dispose()
|
||||
{
|
||||
delete instance;
|
||||
instance = nullptr;
|
||||
}
|
||||
|
||||
shared_qobject_ptr< HttpMetaCache > Env::metacache()
|
||||
{
|
||||
return d->m_metacache;
|
||||
}
|
||||
|
||||
QNetworkAccessManager& Env::qnam() const
|
||||
{
|
||||
return d->m_qnam;
|
||||
}
|
||||
|
||||
std::shared_ptr<IIconList> Env::icons()
|
||||
{
|
||||
return d->m_iconlist;
|
||||
}
|
||||
|
||||
void Env::registerIconList(std::shared_ptr<IIconList> iconlist)
|
||||
{
|
||||
d->m_iconlist = iconlist;
|
||||
}
|
||||
|
||||
shared_qobject_ptr<Meta::Index> Env::metadataIndex()
|
||||
{
|
||||
if (!d->m_metadataIndex)
|
||||
{
|
||||
d->m_metadataIndex.reset(new Meta::Index());
|
||||
}
|
||||
return d->m_metadataIndex;
|
||||
}
|
||||
|
||||
|
||||
void Env::initHttpMetaCache()
|
||||
{
|
||||
auto &m_metacache = d->m_metacache;
|
||||
m_metacache.reset(new HttpMetaCache("metacache"));
|
||||
m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath());
|
||||
m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath());
|
||||
m_metacache->addBase("versions", QDir("versions").absolutePath());
|
||||
m_metacache->addBase("libraries", QDir("libraries").absolutePath());
|
||||
m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
|
||||
m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath());
|
||||
m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
|
||||
m_metacache->addBase("general", QDir("cache").absolutePath());
|
||||
m_metacache->addBase("ATLauncherPacks", QDir("cache/ATLauncherPacks").absolutePath());
|
||||
m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath());
|
||||
m_metacache->addBase("ModpacksCHPacks", QDir("cache/ModpacksCHPacks").absolutePath());
|
||||
m_metacache->addBase("TechnicPacks", QDir("cache/TechnicPacks").absolutePath());
|
||||
m_metacache->addBase("TwitchPacks", QDir("cache/TwitchPacks").absolutePath());
|
||||
m_metacache->addBase("skins", QDir("accounts/skins").absolutePath());
|
||||
m_metacache->addBase("root", QDir::currentPath());
|
||||
m_metacache->addBase("translations", QDir("translations").absolutePath());
|
||||
m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
|
||||
m_metacache->addBase("meta", QDir("meta").absolutePath());
|
||||
m_metacache->Load();
|
||||
}
|
||||
|
||||
void Env::updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password)
|
||||
{
|
||||
// Set the application proxy settings.
|
||||
if (proxyTypeStr == "SOCKS5")
|
||||
{
|
||||
QNetworkProxy::setApplicationProxy(
|
||||
QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, password));
|
||||
}
|
||||
else if (proxyTypeStr == "HTTP")
|
||||
{
|
||||
QNetworkProxy::setApplicationProxy(
|
||||
QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, password));
|
||||
}
|
||||
else if (proxyTypeStr == "None")
|
||||
{
|
||||
// If we have no proxy set, set no proxy and return.
|
||||
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we have "Default" selected, set Qt to use the system proxy settings.
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||
}
|
||||
|
||||
qDebug() << "Detecting proxy settings...";
|
||||
QNetworkProxy proxy = QNetworkProxy::applicationProxy();
|
||||
d->m_qnam.setProxy(proxy);
|
||||
QString proxyDesc;
|
||||
if (proxy.type() == QNetworkProxy::NoProxy)
|
||||
{
|
||||
qDebug() << "Using no proxy is an option!";
|
||||
return;
|
||||
}
|
||||
switch (proxy.type())
|
||||
{
|
||||
case QNetworkProxy::DefaultProxy:
|
||||
proxyDesc = "Default proxy: ";
|
||||
break;
|
||||
case QNetworkProxy::Socks5Proxy:
|
||||
proxyDesc = "Socks5 proxy: ";
|
||||
break;
|
||||
case QNetworkProxy::HttpProxy:
|
||||
proxyDesc = "HTTP proxy: ";
|
||||
break;
|
||||
case QNetworkProxy::HttpCachingProxy:
|
||||
proxyDesc = "HTTP caching: ";
|
||||
break;
|
||||
case QNetworkProxy::FtpCachingProxy:
|
||||
proxyDesc = "FTP caching: ";
|
||||
break;
|
||||
default:
|
||||
proxyDesc = "DERP proxy: ";
|
||||
break;
|
||||
}
|
||||
proxyDesc += QString("%1:%2")
|
||||
.arg(proxy.hostName())
|
||||
.arg(proxy.port());
|
||||
qDebug() << proxyDesc;
|
||||
}
|
||||
|
||||
QString Env::getJarsPath()
|
||||
{
|
||||
if(d->m_jarsPath.isEmpty())
|
||||
{
|
||||
return FS::PathCombine(QCoreApplication::applicationDirPath(), "jars");
|
||||
}
|
||||
return d->m_jarsPath;
|
||||
}
|
||||
|
||||
void Env::setJarsPath(const QString& path)
|
||||
{
|
||||
d->m_jarsPath = path;
|
||||
}
|
||||
|
||||
void Env::enableFeature(const QString& featureName, bool state)
|
||||
{
|
||||
if(state)
|
||||
{
|
||||
d->m_features.insert(featureName);
|
||||
}
|
||||
else
|
||||
{
|
||||
d->m_features.remove(featureName);
|
||||
}
|
||||
}
|
||||
|
||||
bool Env::isFeatureEnabled(const QString& featureName) const
|
||||
{
|
||||
return d->m_features.contains(featureName);
|
||||
}
|
||||
|
||||
void Env::getEnabledFeatures(QSet<QString>& features) const
|
||||
{
|
||||
features = d->m_features;
|
||||
}
|
||||
|
||||
void Env::setEnabledFeatures(const QSet<QString>& features) const
|
||||
{
|
||||
d->m_features = features;
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "icons/IIconList.h"
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class HttpMetaCache;
|
||||
class BaseVersionList;
|
||||
class BaseVersion;
|
||||
|
||||
namespace Meta
|
||||
{
|
||||
class Index;
|
||||
}
|
||||
|
||||
#if defined(ENV)
|
||||
#undef ENV
|
||||
#endif
|
||||
#define ENV (Env::getInstance())
|
||||
|
||||
|
||||
class MULTIMC_LOGIC_EXPORT Env
|
||||
{
|
||||
friend class MultiMC;
|
||||
private:
|
||||
struct Private;
|
||||
Env();
|
||||
~Env();
|
||||
static void dispose();
|
||||
public:
|
||||
static Env& getInstance();
|
||||
|
||||
QNetworkAccessManager &qnam() const;
|
||||
|
||||
shared_qobject_ptr<HttpMetaCache> metacache();
|
||||
|
||||
std::shared_ptr<IIconList> icons();
|
||||
|
||||
/// init the cache. FIXME: possible future hook point
|
||||
void initHttpMetaCache();
|
||||
|
||||
/// Updates the application proxy settings from the settings object.
|
||||
void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
|
||||
|
||||
void registerIconList(std::shared_ptr<IIconList> iconlist);
|
||||
|
||||
shared_qobject_ptr<Meta::Index> metadataIndex();
|
||||
|
||||
QString getJarsPath();
|
||||
void setJarsPath(const QString & path);
|
||||
|
||||
bool isFeatureEnabled(const QString & featureName) const;
|
||||
void enableFeature(const QString & featureName, bool state = true);
|
||||
void getEnabledFeatures(QSet<QString> & features) const;
|
||||
void setEnabledFeatures(const QSet<QString> & features) const;
|
||||
|
||||
protected:
|
||||
Private * d;
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
namespace Strings
|
||||
{
|
||||
int MULTIMC_LOGIC_EXPORT naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
#include "IIconList.h"
|
||||
|
||||
// blargh
|
||||
IIconList::~IIconList()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
enum IconType : unsigned
|
||||
{
|
||||
Builtin,
|
||||
Transient,
|
||||
FileBased,
|
||||
ICONS_TOTAL,
|
||||
ToBeDeleted
|
||||
};
|
||||
|
||||
class MULTIMC_LOGIC_EXPORT IIconList
|
||||
{
|
||||
public:
|
||||
virtual ~IIconList();
|
||||
virtual bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type) = 0;
|
||||
virtual bool deleteIcon(const QString &key) = 0;
|
||||
virtual void saveIcon(const QString &key, const QString &path, const char * format) const = 0;
|
||||
virtual bool iconFileExists(const QString &key) const = 0;
|
||||
virtual void installIcons(const QStringList &iconFiles) = 0;
|
||||
virtual void installIcon(const QString &file, const QString &name) = 0;
|
||||
};
|
||||
@@ -1,315 +0,0 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "MojangAccount.h"
|
||||
#include "flows/RefreshTask.h"
|
||||
#include "flows/AuthenticateTask.h"
|
||||
|
||||
#include <QUuid>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QRegExp>
|
||||
#include <QStringList>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object)
|
||||
{
|
||||
// The JSON object must at least have a username for it to be valid.
|
||||
if (!object.value("username").isString())
|
||||
{
|
||||
qCritical() << "Can't load Mojang account info from JSON object. Username field is "
|
||||
"missing or of the wrong type.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QString username = object.value("username").toString("");
|
||||
QString clientToken = object.value("clientToken").toString("");
|
||||
QString accessToken = object.value("accessToken").toString("");
|
||||
|
||||
QJsonArray profileArray = object.value("profiles").toArray();
|
||||
if (profileArray.size() < 1)
|
||||
{
|
||||
qCritical() << "Can't load Mojang account with username \"" << username
|
||||
<< "\". No profiles found.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QList<AccountProfile> profiles;
|
||||
for (QJsonValue profileVal : profileArray)
|
||||
{
|
||||
QJsonObject profileObject = profileVal.toObject();
|
||||
QString id = profileObject.value("id").toString("");
|
||||
QString name = profileObject.value("name").toString("");
|
||||
bool legacy = profileObject.value("legacy").toBool(false);
|
||||
if (id.isEmpty() || name.isEmpty())
|
||||
{
|
||||
qWarning() << "Unable to load a profile because it was missing an ID or a name.";
|
||||
continue;
|
||||
}
|
||||
profiles.append({id, name, legacy});
|
||||
}
|
||||
|
||||
MojangAccountPtr account(new MojangAccount());
|
||||
if (object.value("user").isObject())
|
||||
{
|
||||
User u;
|
||||
QJsonObject userStructure = object.value("user").toObject();
|
||||
u.id = userStructure.value("id").toString();
|
||||
/*
|
||||
QJsonObject propMap = userStructure.value("properties").toObject();
|
||||
for(auto key: propMap.keys())
|
||||
{
|
||||
auto values = propMap.operator[](key).toArray();
|
||||
for(auto value: values)
|
||||
u.properties.insert(key, value.toString());
|
||||
}
|
||||
*/
|
||||
account->m_user = u;
|
||||
}
|
||||
account->m_username = username;
|
||||
account->m_clientToken = clientToken;
|
||||
account->m_accessToken = accessToken;
|
||||
account->m_profiles = profiles;
|
||||
|
||||
// Get the currently selected profile.
|
||||
QString currentProfile = object.value("activeProfile").toString("");
|
||||
if (!currentProfile.isEmpty())
|
||||
account->setCurrentProfile(currentProfile);
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
MojangAccountPtr MojangAccount::createFromUsername(const QString &username)
|
||||
{
|
||||
MojangAccountPtr account(new MojangAccount());
|
||||
account->m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
|
||||
account->m_username = username;
|
||||
return account;
|
||||
}
|
||||
|
||||
QJsonObject MojangAccount::saveToJson() const
|
||||
{
|
||||
QJsonObject json;
|
||||
json.insert("username", m_username);
|
||||
json.insert("clientToken", m_clientToken);
|
||||
json.insert("accessToken", m_accessToken);
|
||||
|
||||
QJsonArray profileArray;
|
||||
for (AccountProfile profile : m_profiles)
|
||||
{
|
||||
QJsonObject profileObj;
|
||||
profileObj.insert("id", profile.id);
|
||||
profileObj.insert("name", profile.name);
|
||||
profileObj.insert("legacy", profile.legacy);
|
||||
profileArray.append(profileObj);
|
||||
}
|
||||
json.insert("profiles", profileArray);
|
||||
|
||||
QJsonObject userStructure;
|
||||
{
|
||||
userStructure.insert("id", m_user.id);
|
||||
/*
|
||||
QJsonObject userAttrs;
|
||||
for(auto key: m_user.properties.keys())
|
||||
{
|
||||
auto array = QJsonArray::fromStringList(m_user.properties.values(key));
|
||||
userAttrs.insert(key, array);
|
||||
}
|
||||
userStructure.insert("properties", userAttrs);
|
||||
*/
|
||||
}
|
||||
json.insert("user", userStructure);
|
||||
|
||||
if (m_currentProfile != -1)
|
||||
json.insert("activeProfile", currentProfile()->id);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
bool MojangAccount::setCurrentProfile(const QString &profileId)
|
||||
{
|
||||
for (int i = 0; i < m_profiles.length(); i++)
|
||||
{
|
||||
if (m_profiles[i].id == profileId)
|
||||
{
|
||||
m_currentProfile = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const AccountProfile *MojangAccount::currentProfile() const
|
||||
{
|
||||
if (m_currentProfile == -1)
|
||||
return nullptr;
|
||||
return &m_profiles[m_currentProfile];
|
||||
}
|
||||
|
||||
AccountStatus MojangAccount::accountStatus() const
|
||||
{
|
||||
if (m_accessToken.isEmpty())
|
||||
return NotVerified;
|
||||
else
|
||||
return Verified;
|
||||
}
|
||||
|
||||
std::shared_ptr<YggdrasilTask> MojangAccount::login(AuthSessionPtr session, QString password)
|
||||
{
|
||||
Q_ASSERT(m_currentTask.get() == nullptr);
|
||||
|
||||
// take care of the true offline status
|
||||
if (accountStatus() == NotVerified && password.isEmpty())
|
||||
{
|
||||
if (session)
|
||||
{
|
||||
session->status = AuthSession::RequiresPassword;
|
||||
fillSession(session);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(accountStatus() == Verified && !session->wants_online)
|
||||
{
|
||||
session->status = AuthSession::PlayableOffline;
|
||||
session->auth_server_online = false;
|
||||
fillSession(session);
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (password.isEmpty())
|
||||
{
|
||||
m_currentTask.reset(new RefreshTask(this));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentTask.reset(new AuthenticateTask(this, password));
|
||||
}
|
||||
m_currentTask->assignSession(session);
|
||||
|
||||
connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
|
||||
connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
|
||||
}
|
||||
return m_currentTask;
|
||||
}
|
||||
|
||||
void MojangAccount::authSucceeded()
|
||||
{
|
||||
auto session = m_currentTask->getAssignedSession();
|
||||
if (session)
|
||||
{
|
||||
session->status =
|
||||
session->wants_online ? AuthSession::PlayableOnline : AuthSession::PlayableOffline;
|
||||
fillSession(session);
|
||||
session->auth_server_online = true;
|
||||
}
|
||||
m_currentTask.reset();
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void MojangAccount::authFailed(QString reason)
|
||||
{
|
||||
auto session = m_currentTask->getAssignedSession();
|
||||
// This is emitted when the yggdrasil tasks time out or are cancelled.
|
||||
// -> we treat the error as no-op
|
||||
if (m_currentTask->state() == YggdrasilTask::STATE_FAILED_SOFT)
|
||||
{
|
||||
if (session)
|
||||
{
|
||||
session->status = accountStatus() == Verified ? AuthSession::PlayableOffline
|
||||
: AuthSession::RequiresPassword;
|
||||
session->auth_server_online = false;
|
||||
fillSession(session);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_accessToken = QString();
|
||||
emit changed();
|
||||
if (session)
|
||||
{
|
||||
session->status = AuthSession::RequiresPassword;
|
||||
session->auth_server_online = true;
|
||||
fillSession(session);
|
||||
}
|
||||
}
|
||||
m_currentTask.reset();
|
||||
}
|
||||
|
||||
void MojangAccount::fillSession(AuthSessionPtr session)
|
||||
{
|
||||
// the user name. you have to have an user name
|
||||
session->username = m_username;
|
||||
// volatile auth token
|
||||
session->access_token = m_accessToken;
|
||||
// the semi-permanent client token
|
||||
session->client_token = m_clientToken;
|
||||
if (currentProfile())
|
||||
{
|
||||
// profile name
|
||||
session->player_name = currentProfile()->name;
|
||||
// profile ID
|
||||
session->uuid = currentProfile()->id;
|
||||
// 'legacy' or 'mojang', depending on account type
|
||||
session->user_type = currentProfile()->legacy ? "legacy" : "mojang";
|
||||
if (!session->access_token.isEmpty())
|
||||
{
|
||||
session->session = "token:" + m_accessToken + ":" + m_profiles[m_currentProfile].id;
|
||||
}
|
||||
else
|
||||
{
|
||||
session->session = "-";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
session->player_name = "Player";
|
||||
session->session = "-";
|
||||
}
|
||||
session->u = user();
|
||||
session->m_accountPtr = shared_from_this();
|
||||
}
|
||||
|
||||
void MojangAccount::decrementUses()
|
||||
{
|
||||
Usable::decrementUses();
|
||||
if(!isInUse())
|
||||
{
|
||||
emit changed();
|
||||
qWarning() << "Account" << m_username << "is no longer in use.";
|
||||
}
|
||||
}
|
||||
|
||||
void MojangAccount::incrementUses()
|
||||
{
|
||||
bool wasInUse = isInUse();
|
||||
Usable::incrementUses();
|
||||
if(!wasInUse)
|
||||
{
|
||||
emit changed();
|
||||
qWarning() << "Account" << m_username << "is now in use.";
|
||||
}
|
||||
}
|
||||
|
||||
void MojangAccount::invalidateClientToken()
|
||||
{
|
||||
m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
|
||||
emit changed();
|
||||
}
|
||||
@@ -1,182 +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 <QObject>
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QJsonObject>
|
||||
#include <QPair>
|
||||
#include <QMap>
|
||||
|
||||
#include <memory>
|
||||
#include "AuthSession.h"
|
||||
#include "Usable.h"
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
class Task;
|
||||
class YggdrasilTask;
|
||||
class MojangAccount;
|
||||
|
||||
typedef std::shared_ptr<MojangAccount> MojangAccountPtr;
|
||||
Q_DECLARE_METATYPE(MojangAccountPtr)
|
||||
|
||||
/**
|
||||
* 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
|
||||
* we don't have to rip the code to pieces to add it later.
|
||||
*/
|
||||
struct AccountProfile
|
||||
{
|
||||
QString id;
|
||||
QString name;
|
||||
bool legacy;
|
||||
};
|
||||
|
||||
enum AccountStatus
|
||||
{
|
||||
NotVerified,
|
||||
Verified
|
||||
};
|
||||
|
||||
/**
|
||||
* Object that stores information about a certain Mojang account.
|
||||
*
|
||||
* Said information may include things such as that account's username, client token, and access
|
||||
* token if the user chose to stay logged in.
|
||||
*/
|
||||
class MULTIMC_LOGIC_EXPORT MojangAccount :
|
||||
public QObject,
|
||||
public Usable,
|
||||
public std::enable_shared_from_this<MojangAccount>
|
||||
{
|
||||
Q_OBJECT
|
||||
public: /* construction */
|
||||
//! Do not copy accounts. ever.
|
||||
explicit MojangAccount(const MojangAccount &other, QObject *parent) = delete;
|
||||
|
||||
//! Default constructor
|
||||
explicit MojangAccount(QObject *parent = 0) : QObject(parent) {};
|
||||
|
||||
//! Creates an empty account for the specified user name.
|
||||
static MojangAccountPtr createFromUsername(const QString &username);
|
||||
|
||||
//! Loads a MojangAccount from the given JSON object.
|
||||
static MojangAccountPtr loadFromJson(const QJsonObject &json);
|
||||
|
||||
//! Saves a MojangAccount to a JSON object and returns it.
|
||||
QJsonObject saveToJson() const;
|
||||
|
||||
public: /* manipulation */
|
||||
/**
|
||||
* Sets the currently selected profile to the profile with the given ID string.
|
||||
* If profileId is not in the list of available profiles, the function will simply return
|
||||
* false.
|
||||
*/
|
||||
bool setCurrentProfile(const QString &profileId);
|
||||
|
||||
/**
|
||||
* Attempt to login. Empty password means we use the token.
|
||||
* If the attempt fails because we already are performing some task, it returns false.
|
||||
*/
|
||||
std::shared_ptr<YggdrasilTask> login(AuthSessionPtr session, QString password = QString());
|
||||
void invalidateClientToken();
|
||||
|
||||
public: /* queries */
|
||||
const QString &username() const
|
||||
{
|
||||
return m_username;
|
||||
}
|
||||
|
||||
const QString &clientToken() const
|
||||
{
|
||||
return m_clientToken;
|
||||
}
|
||||
|
||||
const QString &accessToken() const
|
||||
{
|
||||
return m_accessToken;
|
||||
}
|
||||
|
||||
const QList<AccountProfile> &profiles() const
|
||||
{
|
||||
return m_profiles;
|
||||
}
|
||||
|
||||
const User &user()
|
||||
{
|
||||
return m_user;
|
||||
}
|
||||
|
||||
//! Returns the currently selected profile (if none, returns nullptr)
|
||||
const AccountProfile *currentProfile() const;
|
||||
|
||||
//! Returns whether the account is NotVerified, Verified or Online
|
||||
AccountStatus accountStatus() const;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This signal is emitted when the account changes
|
||||
*/
|
||||
void changed();
|
||||
|
||||
// TODO: better signalling for the various possible state changes - especially errors
|
||||
|
||||
protected: /* variables */
|
||||
QString m_username;
|
||||
|
||||
// Used to identify the client - the user can have multiple clients for the same account
|
||||
// Think: different launchers, all connecting to the same account/profile
|
||||
QString m_clientToken;
|
||||
|
||||
// Blank if not logged in.
|
||||
QString m_accessToken;
|
||||
|
||||
// Index of the selected profile within the list of available
|
||||
// profiles. -1 if nothing is selected.
|
||||
int m_currentProfile = -1;
|
||||
|
||||
// List of available profiles.
|
||||
QList<AccountProfile> m_profiles;
|
||||
|
||||
// the user structure, whatever it is.
|
||||
User m_user;
|
||||
|
||||
// current task we are executing here
|
||||
std::shared_ptr<YggdrasilTask> m_currentTask;
|
||||
|
||||
protected: /* methods */
|
||||
|
||||
void incrementUses() override;
|
||||
void decrementUses() override;
|
||||
|
||||
private
|
||||
slots:
|
||||
void authSucceeded();
|
||||
void authFailed(QString reason);
|
||||
|
||||
private:
|
||||
void fillSession(AuthSessionPtr session);
|
||||
|
||||
public:
|
||||
friend class YggdrasilTask;
|
||||
friend class AuthenticateTask;
|
||||
friend class ValidateTask;
|
||||
friend class RefreshTask;
|
||||
};
|
||||
@@ -1,468 +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 "MojangAccountList.h"
|
||||
#include "MojangAccount.h"
|
||||
|
||||
#include <QIODevice>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonParseError>
|
||||
#include <QDir>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <FileSystem.h>
|
||||
|
||||
#define ACCOUNT_LIST_FORMAT_VERSION 2
|
||||
|
||||
MojangAccountList::MojangAccountList(QObject *parent) : QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
MojangAccountPtr MojangAccountList::findAccount(const QString &username) const
|
||||
{
|
||||
for (int i = 0; i < count(); i++)
|
||||
{
|
||||
MojangAccountPtr account = at(i);
|
||||
if (account->username() == username)
|
||||
return account;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const MojangAccountPtr MojangAccountList::at(int i) const
|
||||
{
|
||||
return MojangAccountPtr(m_accounts.at(i));
|
||||
}
|
||||
|
||||
void MojangAccountList::addAccount(const MojangAccountPtr account)
|
||||
{
|
||||
int row = m_accounts.count();
|
||||
beginInsertRows(QModelIndex(), row, row);
|
||||
connect(account.get(), SIGNAL(changed()), SLOT(accountChanged()));
|
||||
m_accounts.append(account);
|
||||
endInsertRows();
|
||||
onListChanged();
|
||||
}
|
||||
|
||||
void MojangAccountList::removeAccount(const QString &username)
|
||||
{
|
||||
int idx = 0;
|
||||
for (auto account : m_accounts)
|
||||
{
|
||||
if (account->username() == username)
|
||||
{
|
||||
beginRemoveRows(QModelIndex(), idx, idx);
|
||||
m_accounts.removeOne(account);
|
||||
endRemoveRows();
|
||||
return;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
onListChanged();
|
||||
}
|
||||
|
||||
void MojangAccountList::removeAccount(QModelIndex index)
|
||||
{
|
||||
int row = index.row();
|
||||
if(index.isValid() && row >= 0 && row < m_accounts.size())
|
||||
{
|
||||
auto & account = m_accounts[row];
|
||||
if(account == m_activeAccount)
|
||||
{
|
||||
m_activeAccount = nullptr;
|
||||
onActiveChanged();
|
||||
}
|
||||
beginRemoveRows(QModelIndex(), row, row);
|
||||
m_accounts.removeAt(index.row());
|
||||
endRemoveRows();
|
||||
onListChanged();
|
||||
}
|
||||
}
|
||||
|
||||
MojangAccountPtr MojangAccountList::activeAccount() const
|
||||
{
|
||||
return m_activeAccount;
|
||||
}
|
||||
|
||||
void MojangAccountList::setActiveAccount(const QString &username)
|
||||
{
|
||||
if (username.isEmpty() && m_activeAccount)
|
||||
{
|
||||
int idx = 0;
|
||||
auto prevActiveAcc = m_activeAccount;
|
||||
m_activeAccount = nullptr;
|
||||
for (MojangAccountPtr account : m_accounts)
|
||||
{
|
||||
if (account == prevActiveAcc)
|
||||
{
|
||||
emit dataChanged(index(idx), index(idx));
|
||||
}
|
||||
idx ++;
|
||||
}
|
||||
onActiveChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto currentActiveAccount = m_activeAccount;
|
||||
int currentActiveAccountIdx = -1;
|
||||
auto newActiveAccount = m_activeAccount;
|
||||
int newActiveAccountIdx = -1;
|
||||
int idx = 0;
|
||||
for (MojangAccountPtr account : m_accounts)
|
||||
{
|
||||
if (account->username() == username)
|
||||
{
|
||||
newActiveAccount = account;
|
||||
newActiveAccountIdx = idx;
|
||||
}
|
||||
if(currentActiveAccount == account)
|
||||
{
|
||||
currentActiveAccountIdx = idx;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
if(currentActiveAccount != newActiveAccount)
|
||||
{
|
||||
emit dataChanged(index(currentActiveAccountIdx), index(currentActiveAccountIdx));
|
||||
emit dataChanged(index(newActiveAccountIdx), index(newActiveAccountIdx));
|
||||
m_activeAccount = newActiveAccount;
|
||||
onActiveChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MojangAccountList::accountChanged()
|
||||
{
|
||||
// the list changed. there is no doubt.
|
||||
onListChanged();
|
||||
}
|
||||
|
||||
void MojangAccountList::onListChanged()
|
||||
{
|
||||
if (m_autosave)
|
||||
// TODO: Alert the user if this fails.
|
||||
saveList();
|
||||
|
||||
emit listChanged();
|
||||
}
|
||||
|
||||
void MojangAccountList::onActiveChanged()
|
||||
{
|
||||
if (m_autosave)
|
||||
saveList();
|
||||
|
||||
emit activeAccountChanged();
|
||||
}
|
||||
|
||||
int MojangAccountList::count() const
|
||||
{
|
||||
return m_accounts.count();
|
||||
}
|
||||
|
||||
QVariant MojangAccountList::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
if (index.row() > count())
|
||||
return QVariant();
|
||||
|
||||
MojangAccountPtr account = at(index.row());
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
switch (index.column())
|
||||
{
|
||||
case NameColumn:
|
||||
return account->username();
|
||||
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
case Qt::ToolTipRole:
|
||||
return account->username();
|
||||
|
||||
case PointerRole:
|
||||
return qVariantFromValue(account);
|
||||
|
||||
case Qt::CheckStateRole:
|
||||
switch (index.column())
|
||||
{
|
||||
case ActiveColumn:
|
||||
return account == m_activeAccount ? Qt::Checked : Qt::Unchecked;
|
||||
}
|
||||
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant MojangAccountList::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
switch (section)
|
||||
{
|
||||
case ActiveColumn:
|
||||
return tr("Active?");
|
||||
|
||||
case NameColumn:
|
||||
return tr("Name");
|
||||
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
case Qt::ToolTipRole:
|
||||
switch (section)
|
||||
{
|
||||
case NameColumn:
|
||||
return tr("The name of the version.");
|
||||
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
int MojangAccountList::rowCount(const QModelIndex &) const
|
||||
{
|
||||
// Return count
|
||||
return count();
|
||||
}
|
||||
|
||||
int MojangAccountList::columnCount(const QModelIndex &) const
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
Qt::ItemFlags MojangAccountList::flags(const QModelIndex &index) const
|
||||
{
|
||||
if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
|
||||
{
|
||||
return Qt::NoItemFlags;
|
||||
}
|
||||
|
||||
return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||
}
|
||||
|
||||
bool MojangAccountList::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(role == Qt::CheckStateRole)
|
||||
{
|
||||
if(value == Qt::Checked)
|
||||
{
|
||||
MojangAccountPtr account = this->at(index.row());
|
||||
this->setActiveAccount(account->username());
|
||||
}
|
||||
}
|
||||
|
||||
emit dataChanged(index, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MojangAccountList::updateListData(QList<MojangAccountPtr> versions)
|
||||
{
|
||||
beginResetModel();
|
||||
m_accounts = versions;
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
bool MojangAccountList::loadList(const QString &filePath)
|
||||
{
|
||||
QString path = filePath;
|
||||
if (path.isEmpty())
|
||||
path = m_listFilePath;
|
||||
if (path.isEmpty())
|
||||
{
|
||||
qCritical() << "Can't load Mojang account list. No file path given and no default set.";
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile file(path);
|
||||
|
||||
// Try to open the file and fail if we can't.
|
||||
// TODO: We should probably report this error to the user.
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
qCritical() << QString("Failed to read the account list file (%1).").arg(path).toUtf8();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the file and close it.
|
||||
QByteArray jsonData = file.readAll();
|
||||
file.close();
|
||||
|
||||
QJsonParseError parseError;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);
|
||||
|
||||
// Fail if the JSON is invalid.
|
||||
if (parseError.error != QJsonParseError::NoError)
|
||||
{
|
||||
qCritical() << QString("Failed to parse account list file: %1 at offset %2")
|
||||
.arg(parseError.errorString(), QString::number(parseError.offset))
|
||||
.toUtf8();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the root is an object.
|
||||
if (!jsonDoc.isObject())
|
||||
{
|
||||
qCritical() << "Invalid account list JSON: Root should be an array.";
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonObject root = jsonDoc.object();
|
||||
|
||||
// Make sure the format version matches.
|
||||
if (root.value("formatVersion").toVariant().toInt() != ACCOUNT_LIST_FORMAT_VERSION)
|
||||
{
|
||||
QString newName = "accounts-old.json";
|
||||
qWarning() << "Format version mismatch when loading account list. Existing one will be renamed to"
|
||||
<< newName;
|
||||
|
||||
// Attempt to rename the old version.
|
||||
file.rename(newName);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now, load the accounts array.
|
||||
beginResetModel();
|
||||
QJsonArray accounts = root.value("accounts").toArray();
|
||||
for (QJsonValue accountVal : accounts)
|
||||
{
|
||||
QJsonObject accountObj = accountVal.toObject();
|
||||
MojangAccountPtr account = MojangAccount::loadFromJson(accountObj);
|
||||
if (account.get() != nullptr)
|
||||
{
|
||||
connect(account.get(), SIGNAL(changed()), SLOT(accountChanged()));
|
||||
m_accounts.append(account);
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "Failed to load an account.";
|
||||
}
|
||||
}
|
||||
// Load the active account.
|
||||
m_activeAccount = findAccount(root.value("activeAccount").toString(""));
|
||||
endResetModel();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MojangAccountList::saveList(const QString &filePath)
|
||||
{
|
||||
QString path(filePath);
|
||||
if (path.isEmpty())
|
||||
path = m_listFilePath;
|
||||
if (path.isEmpty())
|
||||
{
|
||||
qCritical() << "Can't save Mojang account list. No file path given and no default set.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure the parent folder exists
|
||||
if(!FS::ensureFilePathExists(path))
|
||||
return false;
|
||||
|
||||
// make sure the file wasn't overwritten with a folder before (fixes a bug)
|
||||
QFileInfo finfo(path);
|
||||
if(finfo.isDir())
|
||||
{
|
||||
QDir badDir(path);
|
||||
badDir.removeRecursively();
|
||||
}
|
||||
|
||||
qDebug() << "Writing account list to" << path;
|
||||
|
||||
qDebug() << "Building JSON data structure.";
|
||||
// Build the JSON document to write to the list file.
|
||||
QJsonObject root;
|
||||
|
||||
root.insert("formatVersion", ACCOUNT_LIST_FORMAT_VERSION);
|
||||
|
||||
// Build a list of accounts.
|
||||
qDebug() << "Building account array.";
|
||||
QJsonArray accounts;
|
||||
for (MojangAccountPtr account : m_accounts)
|
||||
{
|
||||
QJsonObject accountObj = account->saveToJson();
|
||||
accounts.append(accountObj);
|
||||
}
|
||||
|
||||
// Insert the account list into the root object.
|
||||
root.insert("accounts", accounts);
|
||||
|
||||
if(m_activeAccount)
|
||||
{
|
||||
// Save the active account.
|
||||
root.insert("activeAccount", m_activeAccount->username());
|
||||
}
|
||||
|
||||
// Create a JSON document object to convert our JSON to bytes.
|
||||
QJsonDocument doc(root);
|
||||
|
||||
// Now that we're done building the JSON object, we can write it to the file.
|
||||
qDebug() << "Writing account list to file.";
|
||||
QFile file(path);
|
||||
|
||||
// Try to open the file and fail if we can't.
|
||||
// TODO: We should probably report this error to the user.
|
||||
if (!file.open(QIODevice::WriteOnly))
|
||||
{
|
||||
qCritical() << QString("Failed to read the account list file (%1).").arg(path).toUtf8();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the JSON to the file.
|
||||
file.write(doc.toJson());
|
||||
file.setPermissions(QFile::ReadOwner|QFile::WriteOwner|QFile::ReadUser|QFile::WriteUser);
|
||||
file.close();
|
||||
|
||||
qDebug() << "Saved account list to" << path;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MojangAccountList::setListFilePath(QString path, bool autosave)
|
||||
{
|
||||
m_listFilePath = path;
|
||||
m_autosave = autosave;
|
||||
}
|
||||
|
||||
bool MojangAccountList::anyAccountIsValid()
|
||||
{
|
||||
for(auto account:m_accounts)
|
||||
{
|
||||
if(account->accountStatus() != NotVerified)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1,201 +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 "MojangAccount.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
#include <QAbstractListModel>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
/*!
|
||||
* \brief List of available Mojang accounts.
|
||||
* This should be loaded in the background by MultiMC on startup.
|
||||
*
|
||||
* This class also inherits from QAbstractListModel. Methods from that
|
||||
* class determine how this list shows up in a list view. Said methods
|
||||
* all have a default implementation, but they can be overridden by subclasses to
|
||||
* change the behavior of the list.
|
||||
*/
|
||||
class MULTIMC_LOGIC_EXPORT MojangAccountList : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum ModelRoles
|
||||
{
|
||||
PointerRole = 0x34B1CB48
|
||||
};
|
||||
|
||||
enum VListColumns
|
||||
{
|
||||
// TODO: Add icon column.
|
||||
|
||||
// First column - Active?
|
||||
ActiveColumn = 0,
|
||||
|
||||
// Second column - Name
|
||||
NameColumn,
|
||||
};
|
||||
|
||||
explicit MojangAccountList(QObject *parent = 0);
|
||||
|
||||
//! Gets the account at the given index.
|
||||
virtual const MojangAccountPtr at(int i) const;
|
||||
|
||||
//! Returns the number of accounts in the list.
|
||||
virtual int count() const;
|
||||
|
||||
//////// List Model Functions ////////
|
||||
virtual QVariant data(const QModelIndex &index, int role) const;
|
||||
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
||||
virtual int rowCount(const QModelIndex &parent) const;
|
||||
virtual int columnCount(const QModelIndex &parent) const;
|
||||
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
|
||||
|
||||
/*!
|
||||
* Adds a the given Mojang account to the account list.
|
||||
*/
|
||||
virtual void addAccount(const MojangAccountPtr account);
|
||||
|
||||
/*!
|
||||
* Removes the mojang account with the given username from the account list.
|
||||
*/
|
||||
virtual void removeAccount(const QString &username);
|
||||
|
||||
/*!
|
||||
* Removes the account at the given QModelIndex.
|
||||
*/
|
||||
virtual void removeAccount(QModelIndex index);
|
||||
|
||||
/*!
|
||||
* \brief Finds an account by its username.
|
||||
* \param The username of the account to find.
|
||||
* \return A const pointer to the account with the given username. NULL if
|
||||
* one doesn't exist.
|
||||
*/
|
||||
virtual MojangAccountPtr findAccount(const QString &username) const;
|
||||
|
||||
/*!
|
||||
* Sets the default path to save the list file to.
|
||||
* If autosave is true, this list will automatically save to the given path whenever it changes.
|
||||
* THIS FUNCTION DOES NOT LOAD THE LIST. If you set autosave, be sure to call loadList() immediately
|
||||
* after calling this function to ensure an autosaved change doesn't overwrite the list you intended
|
||||
* to load.
|
||||
*/
|
||||
virtual void setListFilePath(QString path, bool autosave = false);
|
||||
|
||||
/*!
|
||||
* \brief Loads the account list from the given file path.
|
||||
* If the given file is an empty string (default), will load from the default account list file.
|
||||
* \return True if successful, otherwise false.
|
||||
*/
|
||||
virtual bool loadList(const QString &file = "");
|
||||
|
||||
/*!
|
||||
* \brief Saves the account list to the given file.
|
||||
* If the given file is an empty string (default), will save from the default account list file.
|
||||
* \return True if successful, otherwise false.
|
||||
*/
|
||||
virtual bool saveList(const QString &file = "");
|
||||
|
||||
/*!
|
||||
* \brief Gets a pointer to the account that the user has selected as their "active" account.
|
||||
* Which account is active can be overridden on a per-instance basis, but this will return the one that
|
||||
* is set as active globally.
|
||||
* \return The currently active MojangAccount. If there isn't an active account, returns a null pointer.
|
||||
*/
|
||||
virtual MojangAccountPtr activeAccount() const;
|
||||
|
||||
/*!
|
||||
* Sets the given account as the current active account.
|
||||
* If the username given is an empty string, sets the active account to nothing.
|
||||
*/
|
||||
virtual void setActiveAccount(const QString &username);
|
||||
|
||||
/*!
|
||||
* Returns true if any of the account is at least Validated
|
||||
*/
|
||||
bool anyAccountIsValid();
|
||||
|
||||
signals:
|
||||
/*!
|
||||
* Signal emitted to indicate that the account list has changed.
|
||||
* This will also fire if the value of an element in the list changes (will be implemented
|
||||
* later).
|
||||
*/
|
||||
void listChanged();
|
||||
|
||||
/*!
|
||||
* Signal emitted to indicate that the active account has changed.
|
||||
*/
|
||||
void activeAccountChanged();
|
||||
|
||||
public
|
||||
slots:
|
||||
/**
|
||||
* This is called when one of the accounts changes and the list needs to be updated
|
||||
*/
|
||||
void accountChanged();
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Called whenever the list changes.
|
||||
* This emits the listChanged() signal and autosaves the list (if autosave is enabled).
|
||||
*/
|
||||
void onListChanged();
|
||||
|
||||
/*!
|
||||
* Called whenever the active account changes.
|
||||
* Emits the activeAccountChanged() signal and autosaves the list if enabled.
|
||||
*/
|
||||
void onActiveChanged();
|
||||
|
||||
QList<MojangAccountPtr> m_accounts;
|
||||
|
||||
/*!
|
||||
* Account that is currently active.
|
||||
*/
|
||||
MojangAccountPtr m_activeAccount;
|
||||
|
||||
//! Path to the account list file. Empty string if there isn't one.
|
||||
QString m_listFilePath;
|
||||
|
||||
/*!
|
||||
* If true, the account list will automatically save to the account list path when it changes.
|
||||
* Ignored if m_listFilePath is blank.
|
||||
*/
|
||||
bool m_autosave = false;
|
||||
|
||||
protected
|
||||
slots:
|
||||
/*!
|
||||
* Updates this list with the given list of accounts.
|
||||
* This is done by copying each account in the given list and inserting it
|
||||
* into this one.
|
||||
* We need to do this so that we can set the parents of the accounts are set to this
|
||||
* account list. This can't be done in the load task, because the accounts the load
|
||||
* task creates are on the load task's thread and Qt won't allow their parents
|
||||
* to be set to something created on another thread.
|
||||
* To get around that problem, we invoke this method on the GUI thread, which
|
||||
* then copies the accounts and sets their parents correctly.
|
||||
* \param accounts List of accounts whose parents should be set.
|
||||
*/
|
||||
virtual void updateListData(QList<MojangAccountPtr> versions);
|
||||
};
|
||||
@@ -1,255 +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 "YggdrasilTask.h"
|
||||
#include "MojangAccount.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonDocument>
|
||||
#include <QNetworkReply>
|
||||
#include <QByteArray>
|
||||
|
||||
#include <Env.h>
|
||||
|
||||
#include <BuildConfig.h>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
YggdrasilTask::YggdrasilTask(MojangAccount *account, QObject *parent)
|
||||
: Task(parent), m_account(account)
|
||||
{
|
||||
changeState(STATE_CREATED);
|
||||
}
|
||||
|
||||
void YggdrasilTask::executeTask()
|
||||
{
|
||||
changeState(STATE_SENDING_REQUEST);
|
||||
|
||||
// Get the content of the request we're going to send to the server.
|
||||
QJsonDocument doc(getRequestContent());
|
||||
|
||||
QUrl reqUrl(BuildConfig.AUTH_BASE + getEndpoint());
|
||||
QNetworkRequest netRequest(reqUrl);
|
||||
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
QByteArray requestData = doc.toJson();
|
||||
m_netReply = ENV.qnam().post(netRequest, requestData);
|
||||
connect(m_netReply, &QNetworkReply::finished, this, &YggdrasilTask::processReply);
|
||||
connect(m_netReply, &QNetworkReply::uploadProgress, this, &YggdrasilTask::refreshTimers);
|
||||
connect(m_netReply, &QNetworkReply::downloadProgress, this, &YggdrasilTask::refreshTimers);
|
||||
connect(m_netReply, &QNetworkReply::sslErrors, this, &YggdrasilTask::sslErrors);
|
||||
timeout_keeper.setSingleShot(true);
|
||||
timeout_keeper.start(timeout_max);
|
||||
counter.setSingleShot(false);
|
||||
counter.start(time_step);
|
||||
progress(0, timeout_max);
|
||||
connect(&timeout_keeper, &QTimer::timeout, this, &YggdrasilTask::abortByTimeout);
|
||||
connect(&counter, &QTimer::timeout, this, &YggdrasilTask::heartbeat);
|
||||
}
|
||||
|
||||
void YggdrasilTask::refreshTimers(qint64, qint64)
|
||||
{
|
||||
timeout_keeper.stop();
|
||||
timeout_keeper.start(timeout_max);
|
||||
progress(count = 0, timeout_max);
|
||||
}
|
||||
void YggdrasilTask::heartbeat()
|
||||
{
|
||||
count += time_step;
|
||||
progress(count, timeout_max);
|
||||
}
|
||||
|
||||
bool YggdrasilTask::abort()
|
||||
{
|
||||
progress(timeout_max, timeout_max);
|
||||
// TODO: actually use this in a meaningful way
|
||||
m_aborted = YggdrasilTask::BY_USER;
|
||||
m_netReply->abort();
|
||||
return true;
|
||||
}
|
||||
|
||||
void YggdrasilTask::abortByTimeout()
|
||||
{
|
||||
progress(timeout_max, timeout_max);
|
||||
// TODO: actually use this in a meaningful way
|
||||
m_aborted = YggdrasilTask::BY_TIMEOUT;
|
||||
m_netReply->abort();
|
||||
}
|
||||
|
||||
void YggdrasilTask::sslErrors(QList<QSslError> errors)
|
||||
{
|
||||
int i = 1;
|
||||
for (auto error : errors)
|
||||
{
|
||||
qCritical() << "LOGIN SSL Error #" << i << " : " << error.errorString();
|
||||
auto cert = error.certificate();
|
||||
qCritical() << "Certificate in question:\n" << cert.toText();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void YggdrasilTask::processReply()
|
||||
{
|
||||
changeState(STATE_PROCESSING_RESPONSE);
|
||||
|
||||
switch (m_netReply->error())
|
||||
{
|
||||
case QNetworkReply::NoError:
|
||||
break;
|
||||
case QNetworkReply::TimeoutError:
|
||||
changeState(STATE_FAILED_SOFT, tr("Authentication operation timed out."));
|
||||
return;
|
||||
case QNetworkReply::OperationCanceledError:
|
||||
changeState(STATE_FAILED_SOFT, tr("Authentication operation cancelled."));
|
||||
return;
|
||||
case QNetworkReply::SslHandshakeFailedError:
|
||||
changeState(
|
||||
STATE_FAILED_SOFT,
|
||||
tr("<b>SSL Handshake failed.</b><br/>There might be a few causes for it:<br/>"
|
||||
"<ul>"
|
||||
"<li>You use Windows XP and need to <a "
|
||||
"href=\"https://www.microsoft.com/en-us/download/details.aspx?id=38918\">update "
|
||||
"your root certificates</a></li>"
|
||||
"<li>Some device on your network is interfering with SSL traffic. In that case, "
|
||||
"you have bigger worries than Minecraft not starting.</li>"
|
||||
"<li>Possibly something else. Check the MultiMC log file for details</li>"
|
||||
"</ul>"));
|
||||
return;
|
||||
// used for invalid credentials and similar errors. Fall through.
|
||||
case QNetworkReply::ContentAccessDenied:
|
||||
case QNetworkReply::ContentOperationNotPermittedError:
|
||||
break;
|
||||
default:
|
||||
changeState(STATE_FAILED_SOFT,
|
||||
tr("Authentication operation failed due to a network error: %1 (%2)")
|
||||
.arg(m_netReply->errorString()).arg(m_netReply->error()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to parse the response regardless of the response code.
|
||||
// Sometimes the auth server will give more information and an error code.
|
||||
QJsonParseError jsonError;
|
||||
QByteArray replyData = m_netReply->readAll();
|
||||
QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError);
|
||||
// Check the response code.
|
||||
int responseCode = m_netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
|
||||
if (responseCode == 200)
|
||||
{
|
||||
// If the response code was 200, then there shouldn't be an error. Make sure
|
||||
// anyways.
|
||||
// Also, sometimes an empty reply indicates success. If there was no data received,
|
||||
// pass an empty json object to the processResponse function.
|
||||
if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0)
|
||||
{
|
||||
processResponse(replyData.size() > 0 ? doc.object() : QJsonObject());
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
changeState(STATE_FAILED_SOFT, tr("Failed to parse authentication server response "
|
||||
"JSON response: %1 at offset %2.")
|
||||
.arg(jsonError.errorString())
|
||||
.arg(jsonError.offset));
|
||||
qCritical() << replyData;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If the response code was not 200, then Yggdrasil may have given us information
|
||||
// about the error.
|
||||
// If we can parse the response, then get information from it. Otherwise just say
|
||||
// there was an unknown error.
|
||||
if (jsonError.error == QJsonParseError::NoError)
|
||||
{
|
||||
// We were able to parse the server's response. Woo!
|
||||
// Call processError. If a subclass has overridden it then they'll handle their
|
||||
// stuff there.
|
||||
qDebug() << "The request failed, but the server gave us an error message. "
|
||||
"Processing error.";
|
||||
processError(doc.object());
|
||||
}
|
||||
else
|
||||
{
|
||||
// The server didn't say anything regarding the error. Give the user an unknown
|
||||
// error.
|
||||
qDebug()
|
||||
<< "The request failed and the server gave no error message. Unknown error.";
|
||||
changeState(STATE_FAILED_SOFT,
|
||||
tr("An unknown error occurred when trying to communicate with the "
|
||||
"authentication server: %1").arg(m_netReply->errorString()));
|
||||
}
|
||||
}
|
||||
|
||||
void YggdrasilTask::processError(QJsonObject responseData)
|
||||
{
|
||||
QJsonValue errorVal = responseData.value("error");
|
||||
QJsonValue errorMessageValue = responseData.value("errorMessage");
|
||||
QJsonValue causeVal = responseData.value("cause");
|
||||
|
||||
if (errorVal.isString() && errorMessageValue.isString())
|
||||
{
|
||||
m_error = std::shared_ptr<Error>(new Error{
|
||||
errorVal.toString(""), errorMessageValue.toString(""), causeVal.toString("")});
|
||||
changeState(STATE_FAILED_HARD, m_error->m_errorMessageVerbose);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Error is not in standard format. Don't set m_error and return unknown error.
|
||||
changeState(STATE_FAILED_HARD, tr("An unknown Yggdrasil error occurred."));
|
||||
}
|
||||
}
|
||||
|
||||
QString YggdrasilTask::getStateMessage() const
|
||||
{
|
||||
switch (m_state)
|
||||
{
|
||||
case STATE_CREATED:
|
||||
return "Waiting...";
|
||||
case STATE_SENDING_REQUEST:
|
||||
return tr("Sending request to auth servers...");
|
||||
case STATE_PROCESSING_RESPONSE:
|
||||
return tr("Processing response from servers...");
|
||||
case STATE_SUCCEEDED:
|
||||
return tr("Authentication task succeeded.");
|
||||
case STATE_FAILED_SOFT:
|
||||
return tr("Failed to contact the authentication server.");
|
||||
case STATE_FAILED_HARD:
|
||||
return tr("Failed to authenticate.");
|
||||
default:
|
||||
return tr("...");
|
||||
}
|
||||
}
|
||||
|
||||
void YggdrasilTask::changeState(YggdrasilTask::State newState, QString reason)
|
||||
{
|
||||
m_state = newState;
|
||||
setStatus(getStateMessage());
|
||||
if (newState == STATE_SUCCEEDED)
|
||||
{
|
||||
emitSucceeded();
|
||||
}
|
||||
else if (newState == STATE_FAILED_HARD || newState == STATE_FAILED_SOFT)
|
||||
{
|
||||
emitFailed(reason);
|
||||
}
|
||||
}
|
||||
|
||||
YggdrasilTask::State YggdrasilTask::state()
|
||||
{
|
||||
return m_state;
|
||||
}
|
||||
@@ -1,202 +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 "AuthenticateTask.h"
|
||||
#include "../MojangAccount.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QVariant>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QUuid>
|
||||
|
||||
AuthenticateTask::AuthenticateTask(MojangAccount * account, const QString &password,
|
||||
QObject *parent)
|
||||
: YggdrasilTask(account, parent), m_password(password)
|
||||
{
|
||||
}
|
||||
|
||||
QJsonObject AuthenticateTask::getRequestContent() const
|
||||
{
|
||||
/*
|
||||
* {
|
||||
* "agent": { // optional
|
||||
* "name": "Minecraft", // So far this is the only encountered value
|
||||
* "version": 1 // This number might be increased
|
||||
* // by the vanilla client in the future
|
||||
* },
|
||||
* "username": "mojang account name", // Can be an email address or player name for
|
||||
// unmigrated accounts
|
||||
* "password": "mojang account password",
|
||||
* "clientToken": "client identifier" // optional
|
||||
* "requestUser": true/false // request the user structure
|
||||
* }
|
||||
*/
|
||||
QJsonObject req;
|
||||
|
||||
{
|
||||
QJsonObject agent;
|
||||
// C++ makes string literals void* for some stupid reason, so we have to tell it
|
||||
// QString... Thanks Obama.
|
||||
agent.insert("name", QString("Minecraft"));
|
||||
agent.insert("version", 1);
|
||||
req.insert("agent", agent);
|
||||
}
|
||||
|
||||
req.insert("username", m_account->username());
|
||||
req.insert("password", m_password);
|
||||
req.insert("requestUser", true);
|
||||
|
||||
// If we already have a client token, give it to the server.
|
||||
// Otherwise, let the server give us one.
|
||||
|
||||
if(m_account->m_clientToken.isEmpty())
|
||||
{
|
||||
auto uuid = QUuid::createUuid();
|
||||
auto uuidString = uuid.toString().remove('{').remove('-').remove('}');
|
||||
m_account->m_clientToken = uuidString;
|
||||
}
|
||||
req.insert("clientToken", m_account->m_clientToken);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
void AuthenticateTask::processResponse(QJsonObject responseData)
|
||||
{
|
||||
// Read the response data. We need to get the client token, access token, and the selected
|
||||
// profile.
|
||||
qDebug() << "Processing authentication response.";
|
||||
// qDebug() << responseData;
|
||||
// If we already have a client token, make sure the one the server gave us matches our
|
||||
// existing one.
|
||||
qDebug() << "Getting client token.";
|
||||
QString clientToken = responseData.value("clientToken").toString("");
|
||||
if (clientToken.isEmpty())
|
||||
{
|
||||
// Fail if the server gave us an empty client token
|
||||
changeState(STATE_FAILED_HARD, tr("Authentication server didn't send a client token."));
|
||||
return;
|
||||
}
|
||||
if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken)
|
||||
{
|
||||
changeState(STATE_FAILED_HARD, tr("Authentication server attempted to change the client token. This isn't supported."));
|
||||
return;
|
||||
}
|
||||
// Set the client token.
|
||||
m_account->m_clientToken = clientToken;
|
||||
|
||||
// Now, we set the access token.
|
||||
qDebug() << "Getting access token.";
|
||||
QString accessToken = responseData.value("accessToken").toString("");
|
||||
if (accessToken.isEmpty())
|
||||
{
|
||||
// Fail if the server didn't give us an access token.
|
||||
changeState(STATE_FAILED_HARD, tr("Authentication server didn't send an access token."));
|
||||
return;
|
||||
}
|
||||
// Set the access token.
|
||||
m_account->m_accessToken = accessToken;
|
||||
|
||||
// Now we load the list of available profiles.
|
||||
// Mojang hasn't yet implemented the profile system,
|
||||
// but we might as well support what's there so we
|
||||
// don't have trouble implementing it later.
|
||||
qDebug() << "Loading profile list.";
|
||||
QJsonArray availableProfiles = responseData.value("availableProfiles").toArray();
|
||||
QList<AccountProfile> loadedProfiles;
|
||||
for (auto iter : availableProfiles)
|
||||
{
|
||||
QJsonObject profile = iter.toObject();
|
||||
// Profiles are easy, we just need their ID and name.
|
||||
QString id = profile.value("id").toString("");
|
||||
QString name = profile.value("name").toString("");
|
||||
bool legacy = profile.value("legacy").toBool(false);
|
||||
|
||||
if (id.isEmpty() || name.isEmpty())
|
||||
{
|
||||
// This should never happen, but we might as well
|
||||
// warn about it if it does so we can debug it easily.
|
||||
// You never know when Mojang might do something truly derpy.
|
||||
qWarning() << "Found entry in available profiles list with missing ID or name "
|
||||
"field. Ignoring it.";
|
||||
}
|
||||
|
||||
// Now, add a new AccountProfile entry to the list.
|
||||
loadedProfiles.append({id, name, legacy});
|
||||
}
|
||||
// Put the list of profiles we loaded into the MojangAccount object.
|
||||
m_account->m_profiles = loadedProfiles;
|
||||
|
||||
// Finally, we set the current profile to the correct value. This is pretty simple.
|
||||
// We do need to make sure that the current profile that the server gave us
|
||||
// is actually in the available profiles list.
|
||||
// If it isn't, we'll just fail horribly (*shouldn't* ever happen, but you never know).
|
||||
qDebug() << "Setting current profile.";
|
||||
QJsonObject currentProfile = responseData.value("selectedProfile").toObject();
|
||||
QString currentProfileId = currentProfile.value("id").toString("");
|
||||
if (currentProfileId.isEmpty())
|
||||
{
|
||||
changeState(STATE_FAILED_HARD, tr("Authentication server didn't specify a currently selected profile. The account exists, but likely isn't premium."));
|
||||
return;
|
||||
}
|
||||
if (!m_account->setCurrentProfile(currentProfileId))
|
||||
{
|
||||
changeState(STATE_FAILED_HARD, tr("Authentication server specified a selected profile that wasn't in the available profiles list."));
|
||||
return;
|
||||
}
|
||||
|
||||
// this is what the vanilla launcher passes to the userProperties launch param
|
||||
if (responseData.contains("user"))
|
||||
{
|
||||
User u;
|
||||
auto obj = responseData.value("user").toObject();
|
||||
u.id = obj.value("id").toString();
|
||||
auto propArray = obj.value("properties").toArray();
|
||||
for (auto prop : propArray)
|
||||
{
|
||||
auto propTuple = prop.toObject();
|
||||
auto name = propTuple.value("name").toString();
|
||||
auto value = propTuple.value("value").toString();
|
||||
u.properties.insert(name, value);
|
||||
}
|
||||
m_account->m_user = u;
|
||||
}
|
||||
|
||||
// We've made it through the minefield of possible errors. Return true to indicate that
|
||||
// we've succeeded.
|
||||
qDebug() << "Finished reading authentication response.";
|
||||
changeState(STATE_SUCCEEDED);
|
||||
}
|
||||
|
||||
QString AuthenticateTask::getEndpoint() const
|
||||
{
|
||||
return "authenticate";
|
||||
}
|
||||
|
||||
QString AuthenticateTask::getStateMessage() const
|
||||
{
|
||||
switch (m_state)
|
||||
{
|
||||
case STATE_SENDING_REQUEST:
|
||||
return tr("Authenticating: Sending request...");
|
||||
case STATE_PROCESSING_RESPONSE:
|
||||
return tr("Authenticating: Processing response...");
|
||||
default:
|
||||
return YggdrasilTask::getStateMessage();
|
||||
}
|
||||
}
|
||||
@@ -1,46 +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 "../YggdrasilTask.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QJsonObject>
|
||||
|
||||
/**
|
||||
* The authenticate task takes a MojangAccount with no access token and password and attempts to
|
||||
* authenticate with Mojang's servers.
|
||||
* If successful, it will set the MojangAccount's access token.
|
||||
*/
|
||||
class AuthenticateTask : public YggdrasilTask
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AuthenticateTask(MojangAccount *account, const QString &password, QObject *parent = 0);
|
||||
|
||||
protected:
|
||||
virtual QJsonObject getRequestContent() const override;
|
||||
|
||||
virtual QString getEndpoint() const override;
|
||||
|
||||
virtual void processResponse(QJsonObject responseData) override;
|
||||
|
||||
virtual QString getStateMessage() const override;
|
||||
|
||||
private:
|
||||
QString m_password;
|
||||
};
|
||||
@@ -1,144 +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 "RefreshTask.h"
|
||||
#include "../MojangAccount.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QVariant>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
RefreshTask::RefreshTask(MojangAccount *account) : YggdrasilTask(account)
|
||||
{
|
||||
}
|
||||
|
||||
QJsonObject RefreshTask::getRequestContent() const
|
||||
{
|
||||
/*
|
||||
* {
|
||||
* "clientToken": "client identifier"
|
||||
* "accessToken": "current access token to be refreshed"
|
||||
* "selectedProfile": // specifying this causes errors
|
||||
* {
|
||||
* "id": "profile ID"
|
||||
* "name": "profile name"
|
||||
* }
|
||||
* "requestUser": true/false // request the user structure
|
||||
* }
|
||||
*/
|
||||
QJsonObject req;
|
||||
req.insert("clientToken", m_account->m_clientToken);
|
||||
req.insert("accessToken", m_account->m_accessToken);
|
||||
/*
|
||||
{
|
||||
auto currentProfile = m_account->currentProfile();
|
||||
QJsonObject profile;
|
||||
profile.insert("id", currentProfile->id());
|
||||
profile.insert("name", currentProfile->name());
|
||||
req.insert("selectedProfile", profile);
|
||||
}
|
||||
*/
|
||||
req.insert("requestUser", true);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
void RefreshTask::processResponse(QJsonObject responseData)
|
||||
{
|
||||
// Read the response data. We need to get the client token, access token, and the selected
|
||||
// profile.
|
||||
qDebug() << "Processing authentication response.";
|
||||
|
||||
// qDebug() << responseData;
|
||||
// If we already have a client token, make sure the one the server gave us matches our
|
||||
// existing one.
|
||||
QString clientToken = responseData.value("clientToken").toString("");
|
||||
if (clientToken.isEmpty())
|
||||
{
|
||||
// Fail if the server gave us an empty client token
|
||||
changeState(STATE_FAILED_HARD, tr("Authentication server didn't send a client token."));
|
||||
return;
|
||||
}
|
||||
if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken)
|
||||
{
|
||||
changeState(STATE_FAILED_HARD, tr("Authentication server attempted to change the client token. This isn't supported."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Now, we set the access token.
|
||||
qDebug() << "Getting new access token.";
|
||||
QString accessToken = responseData.value("accessToken").toString("");
|
||||
if (accessToken.isEmpty())
|
||||
{
|
||||
// Fail if the server didn't give us an access token.
|
||||
changeState(STATE_FAILED_HARD, tr("Authentication server didn't send an access token."));
|
||||
return;
|
||||
}
|
||||
|
||||
// we validate that the server responded right. (our current profile = returned current
|
||||
// profile)
|
||||
QJsonObject currentProfile = responseData.value("selectedProfile").toObject();
|
||||
QString currentProfileId = currentProfile.value("id").toString("");
|
||||
if (m_account->currentProfile()->id != currentProfileId)
|
||||
{
|
||||
changeState(STATE_FAILED_HARD, tr("Authentication server didn't specify the same prefile as expected."));
|
||||
return;
|
||||
}
|
||||
|
||||
// this is what the vanilla launcher passes to the userProperties launch param
|
||||
if (responseData.contains("user"))
|
||||
{
|
||||
User u;
|
||||
auto obj = responseData.value("user").toObject();
|
||||
u.id = obj.value("id").toString();
|
||||
auto propArray = obj.value("properties").toArray();
|
||||
for (auto prop : propArray)
|
||||
{
|
||||
auto propTuple = prop.toObject();
|
||||
auto name = propTuple.value("name").toString();
|
||||
auto value = propTuple.value("value").toString();
|
||||
u.properties.insert(name, value);
|
||||
}
|
||||
m_account->m_user = u;
|
||||
}
|
||||
|
||||
// We've made it through the minefield of possible errors. Return true to indicate that
|
||||
// we've succeeded.
|
||||
qDebug() << "Finished reading refresh response.";
|
||||
// Reset the access token.
|
||||
m_account->m_accessToken = accessToken;
|
||||
changeState(STATE_SUCCEEDED);
|
||||
}
|
||||
|
||||
QString RefreshTask::getEndpoint() const
|
||||
{
|
||||
return "refresh";
|
||||
}
|
||||
|
||||
QString RefreshTask::getStateMessage() const
|
||||
{
|
||||
switch (m_state)
|
||||
{
|
||||
case STATE_SENDING_REQUEST:
|
||||
return tr("Refreshing login token...");
|
||||
case STATE_PROCESSING_RESPONSE:
|
||||
return tr("Refreshing login token: Processing response...");
|
||||
default:
|
||||
return YggdrasilTask::getStateMessage();
|
||||
}
|
||||
}
|
||||
@@ -1,61 +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 "ValidateTask.h"
|
||||
#include "../MojangAccount.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QVariant>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
ValidateTask::ValidateTask(MojangAccount * account, QObject *parent)
|
||||
: YggdrasilTask(account, parent)
|
||||
{
|
||||
}
|
||||
|
||||
QJsonObject ValidateTask::getRequestContent() const
|
||||
{
|
||||
QJsonObject req;
|
||||
req.insert("accessToken", m_account->m_accessToken);
|
||||
return req;
|
||||
}
|
||||
|
||||
void ValidateTask::processResponse(QJsonObject responseData)
|
||||
{
|
||||
// Assume that if processError wasn't called, then the request was successful.
|
||||
changeState(YggdrasilTask::STATE_SUCCEEDED);
|
||||
}
|
||||
|
||||
QString ValidateTask::getEndpoint() const
|
||||
{
|
||||
return "validate";
|
||||
}
|
||||
|
||||
QString ValidateTask::getStateMessage() const
|
||||
{
|
||||
switch (m_state)
|
||||
{
|
||||
case YggdrasilTask::STATE_SENDING_REQUEST:
|
||||
return tr("Validating access token: Sending request...");
|
||||
case YggdrasilTask::STATE_PROCESSING_RESPONSE:
|
||||
return tr("Validating access token: Processing response...");
|
||||
default:
|
||||
return YggdrasilTask::getStateMessage();
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QFile>
|
||||
#include <QtNetwork/QtNetwork>
|
||||
#include <memory>
|
||||
#include <minecraft/auth/AuthSession.h>
|
||||
#include "tasks/Task.h"
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
typedef std::shared_ptr<class SkinDelete> SkinDeletePtr;
|
||||
|
||||
class MULTIMC_LOGIC_EXPORT SkinDelete : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SkinDelete(QObject *parent, AuthSessionPtr session);
|
||||
virtual ~SkinDelete() = default;
|
||||
|
||||
private:
|
||||
AuthSessionPtr m_session;
|
||||
std::shared_ptr<QNetworkReply> m_reply;
|
||||
|
||||
protected:
|
||||
virtual void executeTask();
|
||||
|
||||
public slots:
|
||||
void downloadError(QNetworkReply::NetworkError);
|
||||
void downloadFinished();
|
||||
};
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ATLPackManifest.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QMetaType>
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
namespace ATLauncher
|
||||
{
|
||||
|
||||
struct IndexedVersion
|
||||
{
|
||||
QString version;
|
||||
QString minecraft;
|
||||
};
|
||||
|
||||
struct IndexedPack
|
||||
{
|
||||
int id;
|
||||
int position;
|
||||
QString name;
|
||||
PackType type;
|
||||
QVector<IndexedVersion> versions;
|
||||
bool system;
|
||||
QString description;
|
||||
|
||||
QString safeName;
|
||||
};
|
||||
|
||||
MULTIMC_LOGIC_EXPORT void loadIndexedPack(IndexedPack & m, QJsonObject & obj);
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(ATLauncher::IndexedPack)
|
||||
@@ -1,42 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "FTBPackManifest.h"
|
||||
|
||||
#include "InstanceTask.h"
|
||||
#include "multimc_logic_export.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
namespace ModpacksCH {
|
||||
|
||||
class MULTIMC_LOGIC_EXPORT PackInstallTask : public InstanceTask
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PackInstallTask(Modpack pack, QString version);
|
||||
virtual ~PackInstallTask(){}
|
||||
|
||||
bool abort() override;
|
||||
|
||||
protected:
|
||||
virtual void executeTask() override;
|
||||
|
||||
private slots:
|
||||
void onDownloadSucceeded();
|
||||
void onDownloadFailed(QString reason);
|
||||
|
||||
private:
|
||||
void downloadPack();
|
||||
void install();
|
||||
|
||||
private:
|
||||
NetJobPtr jobPtr;
|
||||
QByteArray response;
|
||||
|
||||
Modpack m_pack;
|
||||
QString m_version_name;
|
||||
Version m_version;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
#include "net/NetAction.h"
|
||||
#include "Screenshot.h"
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
typedef std::shared_ptr<class ImgurUpload> ImgurUploadPtr;
|
||||
class MULTIMC_LOGIC_EXPORT ImgurUpload : public NetAction
|
||||
{
|
||||
public:
|
||||
explicit ImgurUpload(ScreenshotPtr shot);
|
||||
static ImgurUploadPtr make(ScreenshotPtr shot)
|
||||
{
|
||||
return ImgurUploadPtr(new ImgurUpload(shot));
|
||||
}
|
||||
|
||||
protected
|
||||
slots:
|
||||
virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||
virtual void downloadError(QNetworkReply::NetworkError error);
|
||||
virtual void downloadFinished();
|
||||
virtual void downloadReadyRead()
|
||||
{
|
||||
}
|
||||
|
||||
public
|
||||
slots:
|
||||
virtual void start();
|
||||
|
||||
private:
|
||||
ScreenshotPtr m_shot;
|
||||
bool finished = true;
|
||||
};
|
||||
@@ -1,148 +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 "StatusChecker.h"
|
||||
|
||||
#include <QByteArray>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <BuildConfig.h>
|
||||
|
||||
StatusChecker::StatusChecker()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void StatusChecker::timerEvent(QTimerEvent *e)
|
||||
{
|
||||
QObject::timerEvent(e);
|
||||
reloadStatus();
|
||||
}
|
||||
|
||||
void StatusChecker::reloadStatus()
|
||||
{
|
||||
if (isLoadingStatus())
|
||||
{
|
||||
// qDebug() << "Ignored request to reload status. Currently reloading already.";
|
||||
return;
|
||||
}
|
||||
|
||||
// qDebug() << "Reloading status.";
|
||||
|
||||
NetJob* job = new NetJob("Status JSON");
|
||||
job->addNetAction(Net::Download::makeByteArray(BuildConfig.MOJANG_STATUS_URL, &dataSink));
|
||||
QObject::connect(job, &NetJob::succeeded, this, &StatusChecker::statusDownloadFinished);
|
||||
QObject::connect(job, &NetJob::failed, this, &StatusChecker::statusDownloadFailed);
|
||||
m_statusNetJob.reset(job);
|
||||
emit statusLoading(true);
|
||||
job->start();
|
||||
}
|
||||
|
||||
void StatusChecker::statusDownloadFinished()
|
||||
{
|
||||
qDebug() << "Finished loading status JSON.";
|
||||
m_statusEntries.clear();
|
||||
m_statusNetJob.reset();
|
||||
|
||||
QJsonParseError jsonError;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(dataSink, &jsonError);
|
||||
|
||||
if (jsonError.error != QJsonParseError::NoError)
|
||||
{
|
||||
fail("Error parsing status JSON:" + jsonError.errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!jsonDoc.isArray())
|
||||
{
|
||||
fail("Error parsing status JSON: JSON root is not an array");
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonArray root = jsonDoc.array();
|
||||
|
||||
for(auto status = root.begin(); status != root.end(); ++status)
|
||||
{
|
||||
QVariantMap map = (*status).toObject().toVariantMap();
|
||||
|
||||
for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter)
|
||||
{
|
||||
QString key = iter.key();
|
||||
QVariant value = iter.value();
|
||||
|
||||
if(value.type() == QVariant::Type::String)
|
||||
{
|
||||
m_statusEntries.insert(key, value.toString());
|
||||
//qDebug() << "Status JSON object: " << key << m_statusEntries[key];
|
||||
}
|
||||
else
|
||||
{
|
||||
fail("Malformed status JSON: expected status type to be a string.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
succeed();
|
||||
}
|
||||
|
||||
void StatusChecker::statusDownloadFailed(QString reason)
|
||||
{
|
||||
fail(tr("Failed to load status JSON:\n%1").arg(reason));
|
||||
}
|
||||
|
||||
|
||||
QMap<QString, QString> StatusChecker::getStatusEntries() const
|
||||
{
|
||||
return m_statusEntries;
|
||||
}
|
||||
|
||||
bool StatusChecker::isLoadingStatus() const
|
||||
{
|
||||
return m_statusNetJob.get() != nullptr;
|
||||
}
|
||||
|
||||
QString StatusChecker::getLastLoadErrorMsg() const
|
||||
{
|
||||
return m_lastLoadError;
|
||||
}
|
||||
|
||||
void StatusChecker::succeed()
|
||||
{
|
||||
if(m_prevEntries != m_statusEntries)
|
||||
{
|
||||
emit statusChanged(m_statusEntries);
|
||||
m_prevEntries = m_statusEntries;
|
||||
}
|
||||
m_lastLoadError = "";
|
||||
qDebug() << "Status loading succeeded.";
|
||||
m_statusNetJob.reset();
|
||||
emit statusLoading(false);
|
||||
}
|
||||
|
||||
void StatusChecker::fail(const QString& errorMsg)
|
||||
{
|
||||
if(m_prevEntries != m_statusEntries)
|
||||
{
|
||||
emit statusChanged(m_statusEntries);
|
||||
m_prevEntries = m_statusEntries;
|
||||
}
|
||||
m_lastLoadError = errorMsg;
|
||||
qDebug() << "Failed to load status:" << errorMsg;
|
||||
m_statusNetJob.reset();
|
||||
emit statusLoading(false);
|
||||
}
|
||||
|
||||
@@ -1,60 +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 <QObject>
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
|
||||
#include <net/NetJob.h>
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
class MULTIMC_LOGIC_EXPORT StatusChecker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public: /* con/des */
|
||||
StatusChecker();
|
||||
|
||||
public: /* methods */
|
||||
QString getLastLoadErrorMsg() const;
|
||||
bool isLoadingStatus() const;
|
||||
QMap<QString, QString> getStatusEntries() const;
|
||||
|
||||
signals:
|
||||
void statusLoading(bool loading);
|
||||
void statusChanged(QMap<QString, QString> newStatus);
|
||||
|
||||
public slots:
|
||||
void reloadStatus();
|
||||
|
||||
protected: /* methods */
|
||||
virtual void timerEvent(QTimerEvent *);
|
||||
|
||||
protected slots:
|
||||
void statusDownloadFinished();
|
||||
void statusDownloadFailed(QString reason);
|
||||
void succeed();
|
||||
void fail(const QString& errorMsg);
|
||||
|
||||
protected: /* data */
|
||||
QMap<QString, QString> m_prevEntries;
|
||||
QMap<QString, QString> m_statusEntries;
|
||||
NetJobPtr m_statusNetJob;
|
||||
QString m_lastLoadError;
|
||||
QByteArray dataSink;
|
||||
};
|
||||
|
||||
@@ -1,414 +0,0 @@
|
||||
project(application)
|
||||
|
||||
################################ FILES ################################
|
||||
|
||||
######## Sources and headers ########
|
||||
SET(MULTIMC_SOURCES
|
||||
# Application base
|
||||
main.cpp
|
||||
MultiMC.h
|
||||
MultiMC.cpp
|
||||
UpdateController.cpp
|
||||
UpdateController.h
|
||||
|
||||
# GUI - general utilities
|
||||
GuiUtil.h
|
||||
GuiUtil.cpp
|
||||
ColumnResizer.h
|
||||
ColumnResizer.cpp
|
||||
InstanceProxyModel.h
|
||||
InstanceProxyModel.cpp
|
||||
VersionProxyModel.h
|
||||
VersionProxyModel.cpp
|
||||
ColorCache.h
|
||||
ColorCache.cpp
|
||||
HoeDown.h
|
||||
|
||||
# Super secret!
|
||||
KonamiCode.h
|
||||
KonamiCode.cpp
|
||||
|
||||
# GUI - windows
|
||||
MainWindow.h
|
||||
MainWindow.cpp
|
||||
InstanceWindow.h
|
||||
InstanceWindow.cpp
|
||||
|
||||
# GUI - setup wizard
|
||||
setupwizard/SetupWizard.h
|
||||
setupwizard/SetupWizard.cpp
|
||||
setupwizard/AnalyticsWizardPage.cpp
|
||||
setupwizard/AnalyticsWizardPage.h
|
||||
setupwizard/BaseWizardPage.h
|
||||
setupwizard/JavaWizardPage.cpp
|
||||
setupwizard/JavaWizardPage.h
|
||||
setupwizard/LanguageWizardPage.cpp
|
||||
setupwizard/LanguageWizardPage.h
|
||||
|
||||
# GUI - themes
|
||||
themes/FusionTheme.cpp
|
||||
themes/FusionTheme.h
|
||||
themes/BrightTheme.cpp
|
||||
themes/BrightTheme.h
|
||||
themes/CustomTheme.cpp
|
||||
themes/CustomTheme.h
|
||||
themes/DarkTheme.cpp
|
||||
themes/DarkTheme.h
|
||||
themes/ITheme.cpp
|
||||
themes/ITheme.h
|
||||
themes/SystemTheme.cpp
|
||||
themes/SystemTheme.h
|
||||
|
||||
# Processes
|
||||
LaunchController.h
|
||||
LaunchController.cpp
|
||||
|
||||
# page provider for instances
|
||||
InstancePageProvider.h
|
||||
|
||||
# Common java checking UI
|
||||
JavaCommon.h
|
||||
JavaCommon.cpp
|
||||
|
||||
# GUI - paged dialog base
|
||||
pages/BasePage.h
|
||||
pages/BasePageContainer.h
|
||||
pages/BasePageProvider.h
|
||||
|
||||
# GUI - instance pages
|
||||
pages/instance/GameOptionsPage.cpp
|
||||
pages/instance/GameOptionsPage.h
|
||||
pages/instance/VersionPage.cpp
|
||||
pages/instance/VersionPage.h
|
||||
pages/instance/TexturePackPage.h
|
||||
pages/instance/ResourcePackPage.h
|
||||
pages/instance/ModFolderPage.cpp
|
||||
pages/instance/ModFolderPage.h
|
||||
pages/instance/NotesPage.cpp
|
||||
pages/instance/NotesPage.h
|
||||
pages/instance/LogPage.cpp
|
||||
pages/instance/LogPage.h
|
||||
pages/instance/InstanceSettingsPage.cpp
|
||||
pages/instance/InstanceSettingsPage.h
|
||||
pages/instance/ScreenshotsPage.cpp
|
||||
pages/instance/ScreenshotsPage.h
|
||||
pages/instance/OtherLogsPage.cpp
|
||||
pages/instance/OtherLogsPage.h
|
||||
pages/instance/ServersPage.cpp
|
||||
pages/instance/ServersPage.h
|
||||
pages/instance/LegacyUpgradePage.cpp
|
||||
pages/instance/LegacyUpgradePage.h
|
||||
pages/instance/WorldListPage.cpp
|
||||
pages/instance/WorldListPage.h
|
||||
|
||||
# GUI - global settings pages
|
||||
pages/global/AccountListPage.cpp
|
||||
pages/global/AccountListPage.h
|
||||
pages/global/CustomCommandsPage.cpp
|
||||
pages/global/CustomCommandsPage.h
|
||||
pages/global/ExternalToolsPage.cpp
|
||||
pages/global/ExternalToolsPage.h
|
||||
pages/global/JavaPage.cpp
|
||||
pages/global/JavaPage.h
|
||||
pages/global/LanguagePage.cpp
|
||||
pages/global/LanguagePage.h
|
||||
pages/global/MinecraftPage.cpp
|
||||
pages/global/MinecraftPage.h
|
||||
pages/global/MultiMCPage.cpp
|
||||
pages/global/MultiMCPage.h
|
||||
pages/global/ProxyPage.cpp
|
||||
pages/global/ProxyPage.h
|
||||
pages/global/PasteEEPage.cpp
|
||||
pages/global/PasteEEPage.h
|
||||
|
||||
# GUI - platform pages
|
||||
pages/modplatform/VanillaPage.cpp
|
||||
pages/modplatform/VanillaPage.h
|
||||
|
||||
pages/modplatform/atlauncher/AtlModel.cpp
|
||||
pages/modplatform/atlauncher/AtlModel.h
|
||||
pages/modplatform/atlauncher/AtlFilterModel.cpp
|
||||
pages/modplatform/atlauncher/AtlFilterModel.h
|
||||
pages/modplatform/atlauncher/AtlPage.cpp
|
||||
pages/modplatform/atlauncher/AtlPage.h
|
||||
pages/modplatform/atlauncher/AtlPage.h
|
||||
|
||||
pages/modplatform/ftb/FtbFilterModel.cpp
|
||||
pages/modplatform/ftb/FtbFilterModel.h
|
||||
pages/modplatform/ftb/FtbListModel.cpp
|
||||
pages/modplatform/ftb/FtbListModel.h
|
||||
pages/modplatform/ftb/FtbPage.cpp
|
||||
pages/modplatform/ftb/FtbPage.h
|
||||
|
||||
pages/modplatform/legacy_ftb/Page.cpp
|
||||
pages/modplatform/legacy_ftb/Page.h
|
||||
pages/modplatform/legacy_ftb/ListModel.h
|
||||
pages/modplatform/legacy_ftb/ListModel.cpp
|
||||
|
||||
pages/modplatform/twitch/TwitchData.h
|
||||
pages/modplatform/twitch/TwitchModel.cpp
|
||||
pages/modplatform/twitch/TwitchModel.h
|
||||
pages/modplatform/twitch/TwitchPage.cpp
|
||||
pages/modplatform/twitch/TwitchPage.h
|
||||
|
||||
pages/modplatform/technic/TechnicModel.cpp
|
||||
pages/modplatform/technic/TechnicModel.h
|
||||
pages/modplatform/technic/TechnicPage.cpp
|
||||
pages/modplatform/technic/TechnicPage.h
|
||||
|
||||
pages/modplatform/ImportPage.cpp
|
||||
pages/modplatform/ImportPage.h
|
||||
|
||||
# GUI - dialogs
|
||||
dialogs/AboutDialog.cpp
|
||||
dialogs/AboutDialog.h
|
||||
dialogs/ProfileSelectDialog.cpp
|
||||
dialogs/ProfileSelectDialog.h
|
||||
dialogs/CopyInstanceDialog.cpp
|
||||
dialogs/CopyInstanceDialog.h
|
||||
dialogs/CustomMessageBox.cpp
|
||||
dialogs/CustomMessageBox.h
|
||||
dialogs/EditAccountDialog.cpp
|
||||
dialogs/EditAccountDialog.h
|
||||
dialogs/ExportInstanceDialog.cpp
|
||||
dialogs/ExportInstanceDialog.h
|
||||
dialogs/IconPickerDialog.cpp
|
||||
dialogs/IconPickerDialog.h
|
||||
dialogs/LoginDialog.cpp
|
||||
dialogs/LoginDialog.h
|
||||
dialogs/NewComponentDialog.cpp
|
||||
dialogs/NewComponentDialog.h
|
||||
dialogs/NewInstanceDialog.cpp
|
||||
dialogs/NewInstanceDialog.h
|
||||
dialogs/NotificationDialog.cpp
|
||||
dialogs/NotificationDialog.h
|
||||
pagedialog/PageDialog.cpp
|
||||
pagedialog/PageDialog.h
|
||||
dialogs/ProgressDialog.cpp
|
||||
dialogs/ProgressDialog.h
|
||||
dialogs/UpdateDialog.cpp
|
||||
dialogs/UpdateDialog.h
|
||||
dialogs/VersionSelectDialog.cpp
|
||||
dialogs/VersionSelectDialog.h
|
||||
dialogs/SkinUploadDialog.cpp
|
||||
dialogs/SkinUploadDialog.h
|
||||
|
||||
|
||||
# GUI - widgets
|
||||
widgets/Common.cpp
|
||||
widgets/Common.h
|
||||
widgets/CustomCommands.cpp
|
||||
widgets/CustomCommands.h
|
||||
widgets/DropLabel.cpp
|
||||
widgets/DropLabel.h
|
||||
widgets/FocusLineEdit.cpp
|
||||
widgets/FocusLineEdit.h
|
||||
widgets/IconLabel.cpp
|
||||
widgets/IconLabel.h
|
||||
widgets/JavaSettingsWidget.cpp
|
||||
widgets/JavaSettingsWidget.h
|
||||
widgets/LabeledToolButton.cpp
|
||||
widgets/LabeledToolButton.h
|
||||
widgets/LanguageSelectionWidget.cpp
|
||||
widgets/LanguageSelectionWidget.h
|
||||
widgets/LineSeparator.cpp
|
||||
widgets/LineSeparator.h
|
||||
widgets/LogView.cpp
|
||||
widgets/LogView.h
|
||||
widgets/MCModInfoFrame.cpp
|
||||
widgets/MCModInfoFrame.h
|
||||
widgets/ModListView.cpp
|
||||
widgets/ModListView.h
|
||||
widgets/PageContainer.cpp
|
||||
widgets/PageContainer.h
|
||||
widgets/PageContainer_p.h
|
||||
widgets/ServerStatus.cpp
|
||||
widgets/ServerStatus.h
|
||||
widgets/VersionListView.cpp
|
||||
widgets/VersionListView.h
|
||||
widgets/VersionSelectWidget.cpp
|
||||
widgets/VersionSelectWidget.h
|
||||
widgets/ProgressWidget.h
|
||||
widgets/ProgressWidget.cpp
|
||||
widgets/WideBar.h
|
||||
widgets/WideBar.cpp
|
||||
|
||||
# GUI - instance group view
|
||||
groupview/GroupedProxyModel.cpp
|
||||
groupview/GroupedProxyModel.h
|
||||
groupview/AccessibleGroupView.cpp
|
||||
groupview/AccessibleGroupView.h
|
||||
groupview/AccessibleGroupView_p.h
|
||||
groupview/GroupView.cpp
|
||||
groupview/GroupView.h
|
||||
groupview/InstanceDelegate.cpp
|
||||
groupview/InstanceDelegate.h
|
||||
groupview/VisualGroup.cpp
|
||||
groupview/VisualGroup.h
|
||||
)
|
||||
|
||||
######## UIs ########
|
||||
SET(MULTIMC_UIS
|
||||
# Instance pages
|
||||
pages/instance/GameOptionsPage.ui
|
||||
pages/instance/VersionPage.ui
|
||||
pages/instance/ModFolderPage.ui
|
||||
pages/instance/LogPage.ui
|
||||
pages/instance/InstanceSettingsPage.ui
|
||||
pages/instance/NotesPage.ui
|
||||
pages/instance/ScreenshotsPage.ui
|
||||
pages/instance/OtherLogsPage.ui
|
||||
pages/instance/LegacyUpgradePage.ui
|
||||
pages/instance/ServersPage.ui
|
||||
pages/instance/WorldListPage.ui
|
||||
|
||||
# Global settings pages
|
||||
pages/global/AccountListPage.ui
|
||||
pages/global/ExternalToolsPage.ui
|
||||
pages/global/JavaPage.ui
|
||||
pages/global/MinecraftPage.ui
|
||||
pages/global/MultiMCPage.ui
|
||||
pages/global/ProxyPage.ui
|
||||
pages/global/PasteEEPage.ui
|
||||
|
||||
# Platform pages
|
||||
pages/modplatform/VanillaPage.ui
|
||||
pages/modplatform/atlauncher/AtlPage.ui
|
||||
pages/modplatform/ftb/FtbPage.ui
|
||||
pages/modplatform/legacy_ftb/Page.ui
|
||||
pages/modplatform/twitch/TwitchPage.ui
|
||||
pages/modplatform/technic/TechnicPage.ui
|
||||
pages/modplatform/ImportPage.ui
|
||||
|
||||
# Dialogs
|
||||
dialogs/CopyInstanceDialog.ui
|
||||
dialogs/NewComponentDialog.ui
|
||||
dialogs/NewInstanceDialog.ui
|
||||
dialogs/AboutDialog.ui
|
||||
dialogs/ProgressDialog.ui
|
||||
dialogs/IconPickerDialog.ui
|
||||
dialogs/ProfileSelectDialog.ui
|
||||
dialogs/EditAccountDialog.ui
|
||||
dialogs/ExportInstanceDialog.ui
|
||||
dialogs/LoginDialog.ui
|
||||
dialogs/UpdateDialog.ui
|
||||
dialogs/NotificationDialog.ui
|
||||
dialogs/SkinUploadDialog.ui
|
||||
|
||||
# Widgets/other
|
||||
widgets/CustomCommands.ui
|
||||
widgets/MCModInfoFrame.ui
|
||||
)
|
||||
|
||||
set(MULTIMC_QRCS
|
||||
resources/backgrounds/backgrounds.qrc
|
||||
resources/multimc/multimc.qrc
|
||||
resources/pe_dark/pe_dark.qrc
|
||||
resources/pe_light/pe_light.qrc
|
||||
resources/pe_colored/pe_colored.qrc
|
||||
resources/pe_blue/pe_blue.qrc
|
||||
resources/OSX/OSX.qrc
|
||||
resources/iOS/iOS.qrc
|
||||
resources/flat/flat.qrc
|
||||
resources/documents/documents.qrc
|
||||
)
|
||||
|
||||
######## Windows resource files ########
|
||||
if(WIN32)
|
||||
set(MULTIMC_RCS resources/multimc.rc)
|
||||
endif()
|
||||
|
||||
# Qt 5 stuff
|
||||
qt5_wrap_ui(MULTIMC_UI ${MULTIMC_UIS})
|
||||
qt5_add_resources(MULTIMC_RESOURCES ${MULTIMC_QRCS})
|
||||
|
||||
# Add executable
|
||||
add_executable(MultiMC MACOSX_BUNDLE WIN32 ${MULTIMC_SOURCES} ${MULTIMC_UI} ${MULTIMC_RESOURCES} ${MULTIMC_RCS})
|
||||
target_link_libraries(MultiMC MultiMC_gui ${QUAZIP_LIBRARIES} hoedown MultiMC_rainbow LocalPeer ganalytics)
|
||||
if(DEFINED MultiMC_APP_BINARY_NAME)
|
||||
set_target_properties(MultiMC PROPERTIES OUTPUT_NAME "${MultiMC_APP_BINARY_NAME}")
|
||||
endif()
|
||||
if(DEFINED MultiMC_BINARY_RPATH)
|
||||
SET_TARGET_PROPERTIES(MultiMC PROPERTIES INSTALL_RPATH "${MultiMC_BINARY_RPATH}")
|
||||
endif()
|
||||
if(DEFINED MultiMC_APP_BINARY_DEFS)
|
||||
target_compile_definitions(MultiMC PRIVATE ${MultiMC_APP_BINARY_DEFS})
|
||||
endif()
|
||||
|
||||
install(TARGETS MultiMC
|
||||
BUNDLE DESTINATION ${BUNDLE_DEST_DIR} COMPONENT Runtime
|
||||
LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime
|
||||
RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime
|
||||
)
|
||||
|
||||
#### The MultiMC bundle mess! ####
|
||||
# Bundle utilities are used to complete the portable packages - they add all the libraries that would otherwise be missing on the target system.
|
||||
# NOTE: it seems that this absolutely has to be here, and nowhere else.
|
||||
if(INSTALL_BUNDLE STREQUAL "full")
|
||||
# Add qt.conf - this makes Qt stop looking for things outside the bundle
|
||||
install(
|
||||
CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${RESOURCES_DEST_DIR}/qt.conf\" \" \")"
|
||||
COMPONENT Runtime
|
||||
)
|
||||
# Bundle plugins
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
# Image formats
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "tga|tiff|mng|webp" EXCLUDE
|
||||
)
|
||||
# Icon engines
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "fontawesome" EXCLUDE
|
||||
)
|
||||
# Platform plugins
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "minimal|linuxfb|offscreen" EXCLUDE
|
||||
)
|
||||
else()
|
||||
# Image formats
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "tga|tiff|mng|webp" EXCLUDE
|
||||
REGEX "d\\." EXCLUDE
|
||||
REGEX "_debug\\." EXCLUDE
|
||||
REGEX "\\.dSYM" EXCLUDE
|
||||
)
|
||||
# Icon engines
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "fontawesome" EXCLUDE
|
||||
REGEX "d\\." EXCLUDE
|
||||
REGEX "_debug\\." EXCLUDE
|
||||
REGEX "\\.dSYM" EXCLUDE
|
||||
)
|
||||
# Platform plugins
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "minimal|linuxfb|offscreen" EXCLUDE
|
||||
REGEX "d\\." EXCLUDE
|
||||
REGEX "_debug\\." EXCLUDE
|
||||
REGEX "\\.dSYM" EXCLUDE
|
||||
)
|
||||
endif()
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake"
|
||||
@ONLY
|
||||
)
|
||||
install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake" COMPONENT Runtime)
|
||||
endif()
|
||||
@@ -1,199 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Aurélien Gâteau <agateau@kde.org>
|
||||
* License: BSD-3-Clause
|
||||
*/
|
||||
#include <ColumnResizer.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QEvent>
|
||||
#include <QFormLayout>
|
||||
#include <QGridLayout>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
class FormLayoutWidgetItem : public QWidgetItem
|
||||
{
|
||||
public:
|
||||
FormLayoutWidgetItem(QWidget* widget, QFormLayout* formLayout, QFormLayout::ItemRole itemRole)
|
||||
: QWidgetItem(widget)
|
||||
, m_width(-1)
|
||||
, m_formLayout(formLayout)
|
||||
, m_itemRole(itemRole)
|
||||
{}
|
||||
|
||||
QSize sizeHint() const
|
||||
{
|
||||
QSize size = QWidgetItem::sizeHint();
|
||||
if (m_width != -1) {
|
||||
size.setWidth(m_width);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
QSize minimumSize() const
|
||||
{
|
||||
QSize size = QWidgetItem::minimumSize();
|
||||
if (m_width != -1) {
|
||||
size.setWidth(m_width);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
QSize maximumSize() const
|
||||
{
|
||||
QSize size = QWidgetItem::maximumSize();
|
||||
if (m_width != -1) {
|
||||
size.setWidth(m_width);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void setWidth(int width)
|
||||
{
|
||||
if (width != m_width) {
|
||||
m_width = width;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void setGeometry(const QRect& _rect)
|
||||
{
|
||||
QRect rect = _rect;
|
||||
int width = widget()->sizeHint().width();
|
||||
if (m_itemRole == QFormLayout::LabelRole && m_formLayout->labelAlignment() & Qt::AlignRight) {
|
||||
rect.setLeft(rect.right() - width);
|
||||
}
|
||||
QWidgetItem::setGeometry(rect);
|
||||
}
|
||||
|
||||
QFormLayout* formLayout() const
|
||||
{
|
||||
return m_formLayout;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_width;
|
||||
QFormLayout* m_formLayout;
|
||||
QFormLayout::ItemRole m_itemRole;
|
||||
};
|
||||
|
||||
typedef QPair<QGridLayout*, int> GridColumnInfo;
|
||||
|
||||
class ColumnResizerPrivate
|
||||
{
|
||||
public:
|
||||
ColumnResizerPrivate(ColumnResizer* q_ptr)
|
||||
: q(q_ptr)
|
||||
, m_updateTimer(new QTimer(q))
|
||||
{
|
||||
m_updateTimer->setSingleShot(true);
|
||||
m_updateTimer->setInterval(0);
|
||||
QObject::connect(m_updateTimer, SIGNAL(timeout()), q, SLOT(updateWidth()));
|
||||
}
|
||||
|
||||
void scheduleWidthUpdate()
|
||||
{
|
||||
m_updateTimer->start();
|
||||
}
|
||||
|
||||
ColumnResizer* q;
|
||||
QTimer* m_updateTimer;
|
||||
QList<QWidget*> m_widgets;
|
||||
QList<FormLayoutWidgetItem*> m_wrWidgetItemList;
|
||||
QList<GridColumnInfo> m_gridColumnInfoList;
|
||||
};
|
||||
|
||||
ColumnResizer::ColumnResizer(QObject* parent)
|
||||
: QObject(parent)
|
||||
, d(new ColumnResizerPrivate(this))
|
||||
{}
|
||||
|
||||
ColumnResizer::~ColumnResizer()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void ColumnResizer::addWidget(QWidget* widget)
|
||||
{
|
||||
d->m_widgets.append(widget);
|
||||
widget->installEventFilter(this);
|
||||
d->scheduleWidthUpdate();
|
||||
}
|
||||
|
||||
void ColumnResizer::updateWidth()
|
||||
{
|
||||
int width = 0;
|
||||
Q_FOREACH(QWidget* widget, d->m_widgets) {
|
||||
width = qMax(widget->sizeHint().width(), width);
|
||||
}
|
||||
Q_FOREACH(FormLayoutWidgetItem* item, d->m_wrWidgetItemList) {
|
||||
item->setWidth(width);
|
||||
item->formLayout()->update();
|
||||
}
|
||||
Q_FOREACH(GridColumnInfo info, d->m_gridColumnInfoList) {
|
||||
info.first->setColumnMinimumWidth(info.second, width);
|
||||
}
|
||||
}
|
||||
|
||||
bool ColumnResizer::eventFilter(QObject*, QEvent* event)
|
||||
{
|
||||
if (event->type() == QEvent::Resize) {
|
||||
d->scheduleWidthUpdate();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ColumnResizer::addWidgetsFromLayout(QLayout* layout, int column)
|
||||
{
|
||||
Q_ASSERT(column >= 0);
|
||||
QGridLayout* gridLayout = qobject_cast<QGridLayout*>(layout);
|
||||
QFormLayout* formLayout = qobject_cast<QFormLayout*>(layout);
|
||||
if (gridLayout) {
|
||||
addWidgetsFromGridLayout(gridLayout, column);
|
||||
} else if (formLayout) {
|
||||
if (column > QFormLayout::SpanningRole) {
|
||||
qCritical() << "column should not be more than" << QFormLayout::SpanningRole << "for QFormLayout";
|
||||
return;
|
||||
}
|
||||
QFormLayout::ItemRole role = static_cast<QFormLayout::ItemRole>(column);
|
||||
addWidgetsFromFormLayout(formLayout, role);
|
||||
} else {
|
||||
qCritical() << "Don't know how to handle layout" << layout;
|
||||
}
|
||||
}
|
||||
|
||||
void ColumnResizer::addWidgetsFromGridLayout(QGridLayout* layout, int column)
|
||||
{
|
||||
for (int row = 0; row < layout->rowCount(); ++row) {
|
||||
QLayoutItem* item = layout->itemAtPosition(row, column);
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
QWidget* widget = item->widget();
|
||||
if (!widget) {
|
||||
continue;
|
||||
}
|
||||
addWidget(widget);
|
||||
}
|
||||
d->m_gridColumnInfoList << GridColumnInfo(layout, column);
|
||||
}
|
||||
|
||||
void ColumnResizer::addWidgetsFromFormLayout(QFormLayout* layout, QFormLayout::ItemRole role)
|
||||
{
|
||||
for (int row = 0; row < layout->rowCount(); ++row) {
|
||||
QLayoutItem* item = layout->itemAt(row, role);
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
QWidget* widget = item->widget();
|
||||
if (!widget) {
|
||||
continue;
|
||||
}
|
||||
layout->removeItem(item);
|
||||
delete item;
|
||||
FormLayoutWidgetItem* newItem = new FormLayoutWidgetItem(widget, layout, role);
|
||||
layout->setItem(row, role, newItem);
|
||||
addWidget(widget);
|
||||
d->m_wrWidgetItemList << newItem;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Aurélien Gâteau <agateau@kde.org>
|
||||
* License: BSD-3-Clause
|
||||
*/
|
||||
#ifndef COLUMNRESIZER_H
|
||||
#define COLUMNRESIZER_H
|
||||
|
||||
#include <QFormLayout>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QList>
|
||||
|
||||
class QEvent;
|
||||
class QGridLayout;
|
||||
class QLayout;
|
||||
class QWidget;
|
||||
|
||||
class ColumnResizerPrivate;
|
||||
class ColumnResizer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ColumnResizer(QObject* parent = 0);
|
||||
~ColumnResizer();
|
||||
|
||||
void addWidget(QWidget* widget);
|
||||
void addWidgetsFromLayout(QLayout*, int column);
|
||||
void addWidgetsFromGridLayout(QGridLayout*, int column);
|
||||
void addWidgetsFromFormLayout(QFormLayout*, QFormLayout::ItemRole role);
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateWidth();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject*, QEvent* event);
|
||||
|
||||
private:
|
||||
ColumnResizerPrivate* const d;
|
||||
};
|
||||
|
||||
#endif /* COLUMNRESIZER_H */
|
||||
@@ -1,34 +0,0 @@
|
||||
#include "InstanceProxyModel.h"
|
||||
#include "MultiMC.h"
|
||||
#include <BaseInstance.h>
|
||||
#include <icons/IconList.h>
|
||||
|
||||
InstanceProxyModel::InstanceProxyModel(QObject *parent) : GroupedProxyModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QVariant InstanceProxyModel::data(const QModelIndex & index, int role) const
|
||||
{
|
||||
QVariant data = QSortFilterProxyModel::data(index, role);
|
||||
if(role == Qt::DecorationRole)
|
||||
{
|
||||
return QVariant(MMC->icons()->getIcon(data.toString()));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
bool InstanceProxyModel::subSortLessThan(const QModelIndex &left,
|
||||
const QModelIndex &right) const
|
||||
{
|
||||
BaseInstance *pdataLeft = static_cast<BaseInstance *>(left.internalPointer());
|
||||
BaseInstance *pdataRight = static_cast<BaseInstance *>(right.internalPointer());
|
||||
QString sortMode = MMC->settings()->get("InstSortMode").toString();
|
||||
if (sortMode == "LastLaunch")
|
||||
{
|
||||
return pdataLeft->lastLaunch() > pdataRight->lastLaunch();
|
||||
}
|
||||
else
|
||||
{
|
||||
return QString::localeAwareCompare(pdataLeft->name(), pdataRight->name()) < 0;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "groupview/GroupedProxyModel.h"
|
||||
|
||||
/**
|
||||
* A proxy model that is responsible for sorting instances into groups
|
||||
*/
|
||||
class InstanceProxyModel : public GroupedProxyModel
|
||||
{
|
||||
public:
|
||||
explicit InstanceProxyModel(QObject *parent = 0);
|
||||
QVariant data(const QModelIndex & index, int role) const override;
|
||||
|
||||
protected:
|
||||
virtual bool subSortLessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
||||
};
|
||||
@@ -1,311 +0,0 @@
|
||||
#include "LaunchController.h"
|
||||
#include "MainWindow.h"
|
||||
#include <minecraft/auth/MojangAccountList.h>
|
||||
#include "MultiMC.h"
|
||||
#include "dialogs/CustomMessageBox.h"
|
||||
#include "dialogs/ProfileSelectDialog.h"
|
||||
#include "dialogs/ProgressDialog.h"
|
||||
#include "dialogs/EditAccountDialog.h"
|
||||
#include "InstanceWindow.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "JavaCommon.h"
|
||||
#include <QLineEdit>
|
||||
#include <QInputDialog>
|
||||
#include <tasks/Task.h>
|
||||
#include <minecraft/auth/YggdrasilTask.h>
|
||||
#include <launch/steps/TextPrint.h>
|
||||
#include <QStringList>
|
||||
|
||||
LaunchController::LaunchController(QObject *parent) : Task(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void LaunchController::executeTask()
|
||||
{
|
||||
if (!m_instance)
|
||||
{
|
||||
emitFailed(tr("No instance specified!"));
|
||||
return;
|
||||
}
|
||||
|
||||
login();
|
||||
}
|
||||
|
||||
// FIXME: minecraft specific
|
||||
void LaunchController::login()
|
||||
{
|
||||
JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget);
|
||||
|
||||
// Find an account to use.
|
||||
std::shared_ptr<MojangAccountList> accounts = MMC->accounts();
|
||||
MojangAccountPtr account = accounts->activeAccount();
|
||||
if (accounts->count() <= 0)
|
||||
{
|
||||
// Tell the user they need to log in at least one account in order to play.
|
||||
auto reply = CustomMessageBox::selectable(
|
||||
m_parentWidget, tr("No Accounts"),
|
||||
tr("In order to play Minecraft, you must have at least one Mojang or Minecraft "
|
||||
"account logged in to MultiMC."
|
||||
"Would you like to open the account manager to add an account now?"),
|
||||
QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)->exec();
|
||||
|
||||
if (reply == QMessageBox::Yes)
|
||||
{
|
||||
// Open the account manager.
|
||||
MMC->ShowGlobalSettings(m_parentWidget, "accounts");
|
||||
}
|
||||
}
|
||||
else if (account.get() == nullptr)
|
||||
{
|
||||
// If no default account is set, ask the user which one to use.
|
||||
ProfileSelectDialog selectDialog(tr("Which profile would you like to use?"),
|
||||
ProfileSelectDialog::GlobalDefaultCheckbox, m_parentWidget);
|
||||
|
||||
selectDialog.exec();
|
||||
|
||||
// Launch the instance with the selected account.
|
||||
account = selectDialog.selectedAccount();
|
||||
|
||||
// If the user said to use the account as default, do that.
|
||||
if (selectDialog.useAsGlobalDefault() && account.get() != nullptr)
|
||||
accounts->setActiveAccount(account->username());
|
||||
}
|
||||
|
||||
// if no account is selected, we bail
|
||||
if (!account.get())
|
||||
{
|
||||
emitFailed(tr("No account selected for launch."));
|
||||
return;
|
||||
}
|
||||
|
||||
// we try empty password first :)
|
||||
QString password;
|
||||
// we loop until the user succeeds in logging in or gives up
|
||||
bool tryagain = true;
|
||||
// the failure. the default failure.
|
||||
const QString needLoginAgain = tr("Your account is currently not logged in. Please enter your password to log in again. <br /> <br /> This could be caused by a password change.");
|
||||
QString failReason = needLoginAgain;
|
||||
|
||||
while (tryagain)
|
||||
{
|
||||
m_session = std::make_shared<AuthSession>();
|
||||
m_session->wants_online = m_online;
|
||||
auto task = account->login(m_session, password);
|
||||
if (task)
|
||||
{
|
||||
// We'll need to validate the access token to make sure the account
|
||||
// is still logged in.
|
||||
ProgressDialog progDialog(m_parentWidget);
|
||||
if (m_online)
|
||||
{
|
||||
progDialog.setSkipButton(true, tr("Play Offline"));
|
||||
}
|
||||
progDialog.execWithTask(task.get());
|
||||
if (!task->wasSuccessful())
|
||||
{
|
||||
auto failReasonNew = task->failReason();
|
||||
if(failReasonNew == "Invalid token.")
|
||||
{
|
||||
account->invalidateClientToken();
|
||||
failReason = needLoginAgain;
|
||||
}
|
||||
else failReason = failReasonNew;
|
||||
}
|
||||
}
|
||||
switch (m_session->status)
|
||||
{
|
||||
case AuthSession::Undetermined:
|
||||
{
|
||||
qCritical() << "Received undetermined session status during login. Bye.";
|
||||
tryagain = false;
|
||||
emitFailed(tr("Received undetermined session status during login."));
|
||||
break;
|
||||
}
|
||||
case AuthSession::RequiresPassword:
|
||||
{
|
||||
EditAccountDialog passDialog(failReason, m_parentWidget, EditAccountDialog::PasswordField);
|
||||
auto username = m_session->username;
|
||||
auto chopN = [](QString toChop, int N) -> QString
|
||||
{
|
||||
if(toChop.size() > N)
|
||||
{
|
||||
auto left = toChop.left(N);
|
||||
left += QString("\u25CF").repeated(toChop.size() - N);
|
||||
return left;
|
||||
}
|
||||
return toChop;
|
||||
};
|
||||
|
||||
if(username.contains('@'))
|
||||
{
|
||||
auto parts = username.split('@');
|
||||
auto mailbox = chopN(parts[0],3);
|
||||
QString domain = chopN(parts[1], 3);
|
||||
username = mailbox + '@' + domain;
|
||||
}
|
||||
passDialog.setUsername(username);
|
||||
if (passDialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
password = passDialog.password();
|
||||
}
|
||||
else
|
||||
{
|
||||
tryagain = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AuthSession::PlayableOffline:
|
||||
{
|
||||
// we ask the user for a player name
|
||||
bool ok = false;
|
||||
QString usedname = m_session->player_name;
|
||||
QString name = QInputDialog::getText(m_parentWidget, tr("Player name"),
|
||||
tr("Choose your offline mode player name."),
|
||||
QLineEdit::Normal, m_session->player_name, &ok);
|
||||
if (!ok)
|
||||
{
|
||||
tryagain = false;
|
||||
break;
|
||||
}
|
||||
if (name.length())
|
||||
{
|
||||
usedname = name;
|
||||
}
|
||||
m_session->MakeOffline(usedname);
|
||||
// offline flavored game from here :3
|
||||
}
|
||||
case AuthSession::PlayableOnline:
|
||||
{
|
||||
launchInstance();
|
||||
tryagain = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
emitFailed(tr("Failed to launch."));
|
||||
}
|
||||
|
||||
void LaunchController::launchInstance()
|
||||
{
|
||||
Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL");
|
||||
Q_ASSERT_X(m_session.get() != nullptr, "launchInstance", "session is NULL");
|
||||
|
||||
if(!m_instance->reloadSettings())
|
||||
{
|
||||
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't load the instance profile."));
|
||||
emitFailed(tr("Couldn't load the instance profile."));
|
||||
return;
|
||||
}
|
||||
|
||||
m_launcher = m_instance->createLaunchTask(m_session);
|
||||
if (!m_launcher)
|
||||
{
|
||||
emitFailed(tr("Couldn't instantiate a launcher."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto console = qobject_cast<InstanceWindow *>(m_parentWidget);
|
||||
auto showConsole = m_instance->settings()->get("ShowConsole").toBool();
|
||||
if(!console && showConsole)
|
||||
{
|
||||
MMC->showInstanceWindow(m_instance);
|
||||
}
|
||||
connect(m_launcher.get(), &LaunchTask::readyForLaunch, this, &LaunchController::readyForLaunch);
|
||||
connect(m_launcher.get(), &LaunchTask::succeeded, this, &LaunchController::onSucceeded);
|
||||
connect(m_launcher.get(), &LaunchTask::failed, this, &LaunchController::onFailed);
|
||||
connect(m_launcher.get(), &LaunchTask::requestProgress, this, &LaunchController::onProgressRequested);
|
||||
|
||||
|
||||
m_launcher->prependStep(new TextPrint(m_launcher.get(), "MultiMC version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::MultiMC));
|
||||
m_launcher->start();
|
||||
}
|
||||
|
||||
void LaunchController::readyForLaunch()
|
||||
{
|
||||
if (!m_profiler)
|
||||
{
|
||||
m_launcher->proceed();
|
||||
return;
|
||||
}
|
||||
|
||||
QString error;
|
||||
if (!m_profiler->check(&error))
|
||||
{
|
||||
m_launcher->abort();
|
||||
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't start profiler: %1").arg(error));
|
||||
emitFailed("Profiler startup failed!");
|
||||
return;
|
||||
}
|
||||
BaseProfiler *profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this);
|
||||
|
||||
connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString & message)
|
||||
{
|
||||
QMessageBox msg;
|
||||
msg.setText(tr("The game launch is delayed until you press the "
|
||||
"button. This is the right time to setup the profiler, as the "
|
||||
"profiler server is running now.\n\n%1").arg(message));
|
||||
msg.setWindowTitle(tr("Waiting."));
|
||||
msg.setIcon(QMessageBox::Information);
|
||||
msg.addButton(tr("Launch"), QMessageBox::AcceptRole);
|
||||
msg.setModal(true);
|
||||
msg.exec();
|
||||
m_launcher->proceed();
|
||||
});
|
||||
connect(profilerInstance, &BaseProfiler::abortLaunch, [this](const QString & message)
|
||||
{
|
||||
QMessageBox msg;
|
||||
msg.setText(tr("Couldn't start the profiler: %1").arg(message));
|
||||
msg.setWindowTitle(tr("Error"));
|
||||
msg.setIcon(QMessageBox::Critical);
|
||||
msg.addButton(QMessageBox::Ok);
|
||||
msg.setModal(true);
|
||||
msg.exec();
|
||||
m_launcher->abort();
|
||||
emitFailed("Profiler startup failed!");
|
||||
});
|
||||
profilerInstance->beginProfiling(m_launcher);
|
||||
}
|
||||
|
||||
void LaunchController::onSucceeded()
|
||||
{
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
void LaunchController::onFailed(QString reason)
|
||||
{
|
||||
if(m_instance->settings()->get("ShowConsoleOnError").toBool())
|
||||
{
|
||||
MMC->showInstanceWindow(m_instance, "console");
|
||||
}
|
||||
emitFailed(reason);
|
||||
}
|
||||
|
||||
void LaunchController::onProgressRequested(Task* task)
|
||||
{
|
||||
ProgressDialog progDialog(m_parentWidget);
|
||||
progDialog.setSkipButton(true, tr("Abort"));
|
||||
m_launcher->proceed();
|
||||
progDialog.execWithTask(task);
|
||||
}
|
||||
|
||||
bool LaunchController::abort()
|
||||
{
|
||||
if(!m_launcher)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if(!m_launcher->canAbort())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto response = CustomMessageBox::selectable(
|
||||
m_parentWidget, tr("Kill Minecraft?"),
|
||||
tr("This can cause the instance to get corrupted and should only be used if Minecraft "
|
||||
"is frozen for some reason"),
|
||||
QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)->exec();
|
||||
if (response == QMessageBox::Yes)
|
||||
{
|
||||
return m_launcher->abort();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SkinUploadDialog</class>
|
||||
<widget class="QDialog" name="SkinUploadDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>413</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Skin Upload</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="fileBox">
|
||||
<property name="title">
|
||||
<string>Skin File</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="skinPathTextBox"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="skinBrowseBtn">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>28</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="modelBox">
|
||||
<property name="title">
|
||||
<string>Player Model</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_1">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="steveBtn">
|
||||
<property name="text">
|
||||
<string>Steve Model</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="alexBtn">
|
||||
<property name="text">
|
||||
<string>Alex Model</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -1,48 +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 "GroupedProxyModel.h"
|
||||
|
||||
#include "GroupView.h"
|
||||
#include <QDebug>
|
||||
|
||||
GroupedProxyModel::GroupedProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool GroupedProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||
{
|
||||
const QString leftCategory = left.data(GroupViewRoles::GroupRole).toString();
|
||||
const QString rightCategory = right.data(GroupViewRoles::GroupRole).toString();
|
||||
if (leftCategory == rightCategory)
|
||||
{
|
||||
return subSortLessThan(left, right);
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME: real group sorting happens in GroupView::updateGeometries(), see LocaleString
|
||||
auto result = leftCategory.localeAwareCompare(rightCategory);
|
||||
if(result == 0)
|
||||
{
|
||||
return subSortLessThan(left, right);
|
||||
}
|
||||
return result < 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool GroupedProxyModel::subSortLessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||
{
|
||||
return left.row() < right.row();
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=MultiMC
|
||||
GenericName=Minecraft Launcher
|
||||
Comment=Free, open source launcher and instance manager for Minecraft.
|
||||
Type=Application
|
||||
Terminal=false
|
||||
Exec=multimc
|
||||
Icon=multimc
|
||||
Categories=Game
|
||||
Keywords=game;minecraft;
|
||||
@@ -1,45 +0,0 @@
|
||||
Name: MultiMC5
|
||||
Version: 1.4
|
||||
Release: 1%{?dist}
|
||||
Summary: A local install wrapper for MultiMC
|
||||
|
||||
License: ASL 2.0
|
||||
URL: https://multimc.org
|
||||
BuildArch: x86_64
|
||||
|
||||
Requires: zenity qt5-qtbase wget
|
||||
Provides: multimc MultiMC multimc5
|
||||
|
||||
%description
|
||||
A local install wrapper for MultiMC
|
||||
|
||||
%prep
|
||||
|
||||
|
||||
%build
|
||||
|
||||
|
||||
%install
|
||||
mkdir -p %{buildroot}/opt/multimc
|
||||
install -m 0644 ../ubuntu/multimc/opt/multimc/icon.svg %{buildroot}/opt/multimc/icon.svg
|
||||
install -m 0755 ../ubuntu/multimc/opt/multimc/run.sh %{buildroot}/opt/multimc/run.sh
|
||||
mkdir -p %{buildroot}/%{_datadir}/applications
|
||||
install -m 0644 ../ubuntu/multimc/usr/share/applications/multimc.desktop %{buildroot}/%{_datadir}/applications/multimc.desktop
|
||||
mkdir -p %{buildroot}/%{_metainfodir}
|
||||
install -m 0644 ../ubuntu/multimc/usr/share/metainfo/multimc.metainfo.xml %{buildroot}/%{_metainfodir}/multimc.metainfo.xml
|
||||
|
||||
%files
|
||||
%dir /opt/multimc
|
||||
/opt/multimc/icon.svg
|
||||
/opt/multimc/run.sh
|
||||
%{_datadir}/applications/multimc.desktop
|
||||
%{_metainfodir}/multimc.metainfo.xml
|
||||
|
||||
|
||||
%changelog
|
||||
|
||||
* Tue Dec 08 00:34:35 CET 2020 joshua-stone <joshua.gage.stone@gmail.com>
|
||||
- Add metainfo.xml for improving package metadata
|
||||
|
||||
* Wed Nov 25 22:53:59 CET 2020 kb1000 <fedora@kb1000.de>
|
||||
- Initial version of the RPM package, based on the Ubuntu package
|
||||
@@ -1,114 +0,0 @@
|
||||
#include "AtlPage.h"
|
||||
#include "ui_AtlPage.h"
|
||||
|
||||
#include "dialogs/NewInstanceDialog.h"
|
||||
#include <modplatform/atlauncher/ATLPackInstallTask.h>
|
||||
#include <BuildConfig.h>
|
||||
|
||||
AtlPage::AtlPage(NewInstanceDialog* dialog, QWidget *parent)
|
||||
: QWidget(parent), ui(new Ui::AtlPage), dialog(dialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
filterModel = new Atl::FilterModel(this);
|
||||
listModel = new Atl::ListModel(this);
|
||||
filterModel->setSourceModel(listModel);
|
||||
ui->packView->setModel(filterModel);
|
||||
ui->packView->setSortingEnabled(true);
|
||||
|
||||
ui->packView->header()->hide();
|
||||
ui->packView->setIndentation(0);
|
||||
|
||||
for(int i = 0; i < filterModel->getAvailableSortings().size(); i++)
|
||||
{
|
||||
ui->sortByBox->addItem(filterModel->getAvailableSortings().keys().at(i));
|
||||
}
|
||||
ui->sortByBox->setCurrentText(filterModel->translateCurrentSorting());
|
||||
|
||||
connect(ui->searchEdit, &QLineEdit::textChanged, this, &AtlPage::triggerSearch);
|
||||
connect(ui->resetButton, &QPushButton::clicked, this, &AtlPage::resetSearch);
|
||||
connect(ui->sortByBox, &QComboBox::currentTextChanged, this, &AtlPage::onSortingSelectionChanged);
|
||||
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &AtlPage::onSelectionChanged);
|
||||
connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &AtlPage::onVersionSelectionChanged);
|
||||
}
|
||||
|
||||
AtlPage::~AtlPage()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
bool AtlPage::shouldDisplay() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void AtlPage::openedImpl()
|
||||
{
|
||||
listModel->request();
|
||||
}
|
||||
|
||||
void AtlPage::suggestCurrent()
|
||||
{
|
||||
if(isOpened) {
|
||||
dialog->setSuggestedPack(selected.name, new ATLauncher::PackInstallTask(selected.safeName, selectedVersion));
|
||||
}
|
||||
|
||||
auto editedLogoName = selected.safeName;
|
||||
auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1.png").arg(selected.safeName.toLower());
|
||||
listModel->getLogo(selected.safeName, url, [this, editedLogoName](QString logo)
|
||||
{
|
||||
dialog->setSuggestedIconFromFile(logo, editedLogoName);
|
||||
});
|
||||
}
|
||||
|
||||
void AtlPage::triggerSearch()
|
||||
{
|
||||
filterModel->setSearchTerm(ui->searchEdit->text());
|
||||
}
|
||||
|
||||
void AtlPage::resetSearch()
|
||||
{
|
||||
ui->searchEdit->setText("");
|
||||
}
|
||||
|
||||
void AtlPage::onSortingSelectionChanged(QString data)
|
||||
{
|
||||
auto toSet = filterModel->getAvailableSortings().value(data);
|
||||
filterModel->setSorting(toSet);
|
||||
}
|
||||
|
||||
void AtlPage::onSelectionChanged(QModelIndex first, QModelIndex second)
|
||||
{
|
||||
ui->versionSelectionBox->clear();
|
||||
|
||||
if(!first.isValid())
|
||||
{
|
||||
if(isOpened)
|
||||
{
|
||||
dialog->setSuggestedPack();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
selected = filterModel->data(first, Qt::UserRole).value<ATLauncher::IndexedPack>();
|
||||
|
||||
ui->packDescription->setHtml(selected.description.replace("\n", "<br>"));
|
||||
|
||||
for(const auto& version : selected.versions) {
|
||||
ui->versionSelectionBox->addItem(version.version);
|
||||
}
|
||||
|
||||
suggestCurrent();
|
||||
}
|
||||
|
||||
void AtlPage::onVersionSelectionChanged(QString data)
|
||||
{
|
||||
if(data.isNull() || data.isEmpty())
|
||||
{
|
||||
selectedVersion = "";
|
||||
return;
|
||||
}
|
||||
|
||||
selectedVersion = data;
|
||||
suggestCurrent();
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QSortFilterProxyModel>
|
||||
|
||||
namespace Ftb {
|
||||
|
||||
class FilterModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FilterModel(QObject* parent = Q_NULLPTR);
|
||||
enum Sorting {
|
||||
ByPlays,
|
||||
ByInstalls,
|
||||
ByName,
|
||||
};
|
||||
const QMap<QString, Sorting> getAvailableSortings();
|
||||
QString translateCurrentSorting();
|
||||
void setSorting(Sorting sorting);
|
||||
Sorting getCurrentSorting();
|
||||
|
||||
protected:
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
||||
|
||||
private:
|
||||
QMap<QString, Sorting> sortings;
|
||||
Sorting currentSorting;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
#include "TwitchPage.h"
|
||||
#include "ui_TwitchPage.h"
|
||||
|
||||
#include "MultiMC.h"
|
||||
#include "dialogs/NewInstanceDialog.h"
|
||||
#include <InstanceImportTask.h>
|
||||
#include "TwitchModel.h"
|
||||
#include <QKeyEvent>
|
||||
|
||||
TwitchPage::TwitchPage(NewInstanceDialog* dialog, QWidget *parent)
|
||||
: QWidget(parent), ui(new Ui::TwitchPage), dialog(dialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
connect(ui->searchButton, &QPushButton::clicked, this, &TwitchPage::triggerSearch);
|
||||
ui->searchEdit->installEventFilter(this);
|
||||
model = new Twitch::ListModel(this);
|
||||
ui->packView->setModel(model);
|
||||
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &TwitchPage::onSelectionChanged);
|
||||
}
|
||||
|
||||
TwitchPage::~TwitchPage()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
bool TwitchPage::eventFilter(QObject* watched, QEvent* event)
|
||||
{
|
||||
if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||
if (keyEvent->key() == Qt::Key_Return) {
|
||||
triggerSearch();
|
||||
keyEvent->accept();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return QWidget::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
bool TwitchPage::shouldDisplay() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void TwitchPage::openedImpl()
|
||||
{
|
||||
suggestCurrent();
|
||||
}
|
||||
|
||||
void TwitchPage::triggerSearch()
|
||||
{
|
||||
model->searchWithTerm(ui->searchEdit->text());
|
||||
}
|
||||
|
||||
void TwitchPage::onSelectionChanged(QModelIndex first, QModelIndex second)
|
||||
{
|
||||
if(!first.isValid())
|
||||
{
|
||||
if(isOpened)
|
||||
{
|
||||
dialog->setSuggestedPack();
|
||||
}
|
||||
ui->frame->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
current = model->data(first, Qt::UserRole).value<Twitch::Modpack>();
|
||||
QString text = "";
|
||||
QString name = current.name;
|
||||
|
||||
if (current.websiteUrl.isEmpty())
|
||||
text = name;
|
||||
else
|
||||
text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>";
|
||||
if (!current.authors.empty()) {
|
||||
auto authorToStr = [](Twitch::ModpackAuthor & author) {
|
||||
if(author.url.isEmpty()) {
|
||||
return author.name;
|
||||
}
|
||||
return QString("<a href=\"%1\">%2</a>").arg(author.url, author.name);
|
||||
};
|
||||
QStringList authorStrs;
|
||||
for(auto & author: current.authors) {
|
||||
authorStrs.push_back(authorToStr(author));
|
||||
}
|
||||
text += tr(" by ") + authorStrs.join(", ");
|
||||
}
|
||||
|
||||
ui->frame->setModText(text);
|
||||
ui->frame->setModDescription(current.description);
|
||||
suggestCurrent();
|
||||
}
|
||||
|
||||
void TwitchPage::suggestCurrent()
|
||||
{
|
||||
if(!isOpened)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(current.broken)
|
||||
{
|
||||
dialog->setSuggestedPack();
|
||||
}
|
||||
|
||||
dialog->setSuggestedPack(current.name, new InstanceImportTask(current.latestFile.downloadUrl));
|
||||
QString editedLogoName;
|
||||
editedLogoName = "twitch_" + current.logoName.section(".", 0, 0);
|
||||
model->getLogo(current.logoName, current.logoUrl, [this, editedLogoName](QString logo)
|
||||
{
|
||||
dialog->setSuggestedIconFromFile(logo, editedLogoName);
|
||||
});
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TwitchPage</class>
|
||||
<widget class="QWidget" name="TwitchPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>875</width>
|
||||
<height>745</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLineEdit" name="searchEdit"/>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="searchButton">
|
||||
<property name="text">
|
||||
<string>Search</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="MCModInfoFrame" name="frame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QListView" name="packView">
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>48</width>
|
||||
<height>48</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>MCModInfoFrame</class>
|
||||
<extends>QFrame</extends>
|
||||
<header>widgets/MCModInfoFrame.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>searchEdit</tabstop>
|
||||
<tabstop>searchButton</tabstop>
|
||||
<tabstop>packView</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
Before Width: | Height: | Size: 83 KiB |
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<path fill="#585858" d="M6.9,4.4v16h16v-16H6.9z M21.9,5.4l0,14h-14v-14H21.9z"/>
|
||||
<rect fill="none" width="24" height="24"/>
|
||||
<rect x="7.9" y="5.4" fill="#F2F2F2" width="14" height="14"/>
|
||||
<polygon fill="#999999" points="7.9,7.4 7.9,6.9 8.9,6.9 8.9,7.9 9.9,7.9 9.9,7.4 10.9,7.4 10.9,8.9 11.9,8.9 11.9,6.4 12.9,6.4
|
||||
12.9,7.4 13.9,7.4 13.9,6.9 14.9,6.9 14.9,8.9 15.9,8.9 15.9,7.4 16.9,7.4 16.9,8.4 17.9,8.4 17.9,7.4 18.9,7.4 18.9,6.9 19.9,6.9
|
||||
19.9,7.4 21.4,7.4 21.4,6.9 21.9,6.9 21.9,7.4 22.9,7.4 22.9,4.4 6.9,4.4 6.9,7.4 "/>
|
||||
<g>
|
||||
<path fill="#585858" d="M14.9,13.6c-0.9,0.9-1.9,2-3.4,2c-1.5,0-2.6-1.1-2.6-2.7c0-1.4,1.2-2.7,2.8-2.7c1.3,0,2.4,1.1,3.2,2
|
||||
c0.9-0.9,1.9-2,3.4-2c1.6,0,2.6,1.1,2.6,2.7c0,1.4-1.2,2.7-2.8,2.7C16.7,15.6,15.7,14.5,14.9,13.6z M14.1,12.9
|
||||
c-0.7-0.7-1.5-1.6-2.5-1.6c-0.9,0-1.6,0.7-1.6,1.6c0,0.9,0.7,1.6,1.6,1.6C12.6,14.5,13.4,13.6,14.1,12.9z M19.7,12.9
|
||||
c0-0.9-0.7-1.6-1.5-1.6c-1,0-1.9,0.9-2.6,1.6c0.7,0.7,1.5,1.6,2.5,1.6C19,14.5,19.7,13.7,19.7,12.9z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 682 B |
|
Before Width: | Height: | Size: 976 B |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 713 B |
|
Before Width: | Height: | Size: 708 B |
|
Before Width: | Height: | Size: 978 B |
|
Before Width: | Height: | Size: 532 B |
|
Before Width: | Height: | Size: 461 B |
|
Before Width: | Height: | Size: 438 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,353 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="68.26667"
|
||||
height="68.26667"
|
||||
id="svg4427"
|
||||
version="1.1"
|
||||
inkscape:version="0.92.1 r"
|
||||
sodipodi:docname="multimc-smooth-biginfinity.svg"
|
||||
inkscape:export-filename="/home/peterix/playground/MultiMC-icons/multimc-smooth-biginfinity.png"
|
||||
inkscape:export-xdpi="180"
|
||||
inkscape:export-ydpi="180">
|
||||
<defs
|
||||
id="defs4429">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4809">
|
||||
<stop
|
||||
style="stop-color:#98c867;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4805" />
|
||||
<stop
|
||||
style="stop-color:#5c9a33;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop4807" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5668"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop5670"
|
||||
offset="0"
|
||||
style="stop-color:#75b54b;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop5672"
|
||||
offset="1"
|
||||
style="stop-color:#75b54b;stop-opacity:0.6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5084"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop5086"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:0.8" />
|
||||
<stop
|
||||
id="stop5088"
|
||||
offset="1"
|
||||
style="stop-color:#000000;stop-opacity:0.35" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5668"
|
||||
id="linearGradient5072"
|
||||
x1="6.7342591"
|
||||
y1="28.510933"
|
||||
x2="50.506943"
|
||||
y2="61.773685"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-0.01532073,-0.00938002)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5084"
|
||||
id="linearGradient5082"
|
||||
x1="14.312115"
|
||||
y1="9.7948904"
|
||||
x2="44.097023"
|
||||
y2="82.973114"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5668"
|
||||
id="linearGradient3281"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-0.01532073,-0.00938002)"
|
||||
x1="6.7342591"
|
||||
y1="28.510933"
|
||||
x2="50.506943"
|
||||
y2="61.773685" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5668"
|
||||
id="linearGradient3283"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-0.01532073,-0.00938002)"
|
||||
x1="6.7342591"
|
||||
y1="28.510933"
|
||||
x2="50.506943"
|
||||
y2="61.773685" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5668"
|
||||
id="linearGradient3286"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.2671525,0,0,0.89790119,-0.01941371,-0.00842234)"
|
||||
x1="6.7342591"
|
||||
y1="28.510933"
|
||||
x2="50.506943"
|
||||
y2="61.773685" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5084"
|
||||
id="linearGradient3288"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="14.312115"
|
||||
y1="9.7948904"
|
||||
x2="44.097023"
|
||||
y2="82.973114" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5084"
|
||||
id="linearGradient3290"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="14.312115"
|
||||
y1="9.7948904"
|
||||
x2="44.097023"
|
||||
y2="82.973114" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5084"
|
||||
id="linearGradient3293"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="14.312115"
|
||||
y1="9.7948904"
|
||||
x2="44.097023"
|
||||
y2="82.973114"
|
||||
gradientTransform="scale(1.2671525,0.89790119)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient5580">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0.0627451"
|
||||
offset="0"
|
||||
id="stop5576" />
|
||||
<stop
|
||||
style="stop-color:#322217;stop-opacity:0.58823532"
|
||||
offset="1"
|
||||
id="stop5578" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3999"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop3995"
|
||||
offset="0"
|
||||
style="stop-color:#a3704b;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop3997"
|
||||
offset="1"
|
||||
style="stop-color:#6a4a33;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2727"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop2723"
|
||||
offset="0"
|
||||
style="stop-color:#966c4a;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop2725"
|
||||
offset="1"
|
||||
style="stop-color:#593d29;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2727"
|
||||
id="linearGradient2050"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="36.546478"
|
||||
y1="33.80484"
|
||||
x2="86.415741"
|
||||
y2="97.065842" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3999"
|
||||
id="radialGradient2052"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-9.105292e-4,-0.00104444)"
|
||||
cx="34.133331"
|
||||
cy="34.133335"
|
||||
fx="34.133331"
|
||||
fy="34.133335"
|
||||
r="29.866665" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5580"
|
||||
id="linearGradient2140"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-0.0010513,-9.083059e-4)"
|
||||
x1="29.866674"
|
||||
y1="29.867579"
|
||||
x2="38.400005"
|
||||
y2="38.400913" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5084"
|
||||
id="linearGradient4790"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.2671525,0,0,0.89790119,-0.82864077,-1.0012743)"
|
||||
x1="14.312115"
|
||||
y1="9.7948904"
|
||||
x2="44.097023"
|
||||
y2="82.973114" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4809"
|
||||
id="radialGradient4803"
|
||||
cx="-42.66758"
|
||||
cy="-34.134373"
|
||||
fx="-42.66758"
|
||||
fy="-34.134373"
|
||||
r="34.132812"
|
||||
gradientTransform="matrix(1.7500268,0.1250019,-0.01781176,0.24936465,95.393964,18.110151)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="3.6203867"
|
||||
inkscape:cx="52.171166"
|
||||
inkscape:cy="11.292073"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:window-width="1368"
|
||||
inkscape:window-height="905"
|
||||
inkscape:window-x="2452"
|
||||
inkscape:window-y="723"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="false"
|
||||
inkscape:snap-bbox-edge-midpoints="false"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-midpoints="false"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-midpoints="false"
|
||||
inkscape:snap-intersection-paths="true"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:snap-object-midpoints="true"
|
||||
inkscape:snap-text-baseline="true"
|
||||
inkscape:snap-center="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4446"
|
||||
empspacing="16"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true"
|
||||
spacingx="4.2666667"
|
||||
spacingy="4.2666667"
|
||||
originx="0"
|
||||
originy="0" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata4432">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<g
|
||||
id="g2048"
|
||||
transform="translate(9.113e-4,0.00104183)">
|
||||
<rect
|
||||
rx="8.5333338"
|
||||
ry="8.5333338"
|
||||
style="fill:url(#linearGradient2050);fill-opacity:1;stroke:none;stroke-width:17.06666756"
|
||||
id="rect2026"
|
||||
width="68.26667"
|
||||
height="68.26667"
|
||||
x="-1.3322676e-15"
|
||||
y="3.0270508e-06" />
|
||||
<rect
|
||||
rx="4.2666626"
|
||||
y="4.2656283"
|
||||
x="4.2657552"
|
||||
height="59.733334"
|
||||
width="59.73333"
|
||||
id="rect2028"
|
||||
style="fill:url(#radialGradient2052);fill-opacity:1;stroke:none;stroke-width:14.93333435"
|
||||
ry="4.2666669" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4811"
|
||||
d="m 4.2669272,4.2645856 -9.11e-4,8.5333334 h 4.267577 v 4.267579 h 8.5332038 v 4.265625 h 4.265625 V 8.5322946 H 25.6 v 8.5332034 h 4.265625 v -4.267579 h 4.267578 v 8.533204 h 4.265625 v -4.265625 h 4.267578 v 4.265625 h 4.267579 v -4.265625 h 4.265624 v -4.267579 h 4.267579 v 4.267579 h 8.533203 l -1.3e-4,-12.8009124 z"
|
||||
style="opacity:0.6;fill:#593d29;fill-opacity:1;stroke:none;stroke-width:17.06666756"
|
||||
sodipodi:nodetypes="ccccccccccccccccccccccccccc" />
|
||||
<path
|
||||
style="fill:url(#radialGradient4803);fill-opacity:1;stroke:none;stroke-width:17.06666756"
|
||||
d="m 8.5329442,-0.0018207 c -4.7274675,0 -8.5332035,3.805736 -8.5332035,8.533203 v 4.2675787 h 4.265625 V 8.5313823 c 0,-0.521698 0.105433,-1.01339 0.27539,-1.47461 -0.169616,0.460814 -0.27539,0.953462 -0.27539,1.47461 h 4.2675785 v 4.2675787 h 4.2656248 4.267578 v 4.265625 h 4.265625 V 12.798961 8.5313823 4.2657573 h 4.267578 v 4.265625 4.2675787 h 4.265625 V 8.5313823 h 4.267578 v 4.2675787 4.265625 h 4.265625 v -4.265625 h 4.267578 v 4.265625 h 4.267579 v -4.265625 h 4.265624 V 8.5313823 h 4.267579 v 4.2675787 h 4.265625 4.267578 V 8.5313823 h 4.265625 c 0,-4.727467 -3.805737,-8.533203 -8.533203,-8.533203 z m -3.019531,5.513671 c -0.318089,0.317888 -0.570428,0.695824 -0.7753915,1.101563 0.2048795,-0.405231 0.4576385,-0.784012 0.7753915,-1.101563 z"
|
||||
id="path4794"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="opacity:1;fill:url(#linearGradient2140);fill-opacity:1;stroke:none;stroke-width:17.06666756"
|
||||
d="m 8.5322887,-9.083059e-4 c -4.72747,0 -8.5332,3.8057359059 -8.5332,8.5332029059 V 59.731515 c 0,4.727467 3.80573,8.535156 8.5332,8.535156 H 59.731502 c 4.72747,0 8.5332,-3.807689 8.5332,-8.535156 V 8.5322946 c 0,-4.727467 -3.80573,-8.5332029059 -8.5332,-8.5332029059 z m 0,4.2675779059 H 59.731502 c 2.36373,0 4.26758,1.901892 4.26758,4.265625 V 59.731515 c 0,2.363733 -1.90385,4.267578 -4.26758,4.267578 H 8.5322887 c -2.36373,0 -4.26758,-1.903845 -4.26758,-4.267578 V 8.5322946 c 0,-2.363733 1.90385,-4.265625 4.26758,-4.265625 z"
|
||||
id="path2046"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
id="g1092">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4786"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:76.18933868px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient4790);fill-opacity:1;stroke:none;stroke-width:1.06666672;opacity:0.5"
|
||||
d="m 38.886673,44.940882 c -0.974277,-0.801673 -2.231353,-2.137814 -3.771231,-4.008427 -2.105641,2.672298 -4.085536,4.598569 -5.939688,5.778816 -2.325625,1.425227 -5.295467,2.137836 -8.909534,2.137828 -4.242656,8e-6 -7.762467,-1.124578 -10.5594458,-3.37376 C 6.7526311,43.114834 5.275567,39.986037 5.2755773,36.088937 5.275567,32.347763 6.7526311,29.207831 9.7067742,26.669132 12.346618,24.419991 15.897857,23.295407 20.360501,23.295373 c 2.294138,3.4e-5 4.289747,0.334069 5.986829,1.002107 1.979863,0.73491 3.645488,1.737016 4.996881,3.00632 1.257039,1.135751 2.514115,2.471891 3.771231,4.008428 2.105563,-2.672257 4.085457,-4.598527 5.939689,-5.778816 2.325544,-1.425186 5.295385,-2.137794 8.909533,-2.137828 4.242577,3.4e-5 7.762388,1.124618 10.559447,3.37376 2.954063,2.360546 4.431127,5.489343 4.431197,9.386401 -7e-5,3.741216 -1.477134,6.881147 -4.431197,9.419806 -2.639925,2.24918 -6.191163,3.373767 -10.653727,3.373758 -2.294219,9e-6 -4.289826,-0.334026 -5.98683,-1.002106 -1.697101,-0.601255 -3.362726,-1.603361 -4.996881,-3.006321 M 19.747676,44.473233 c 5.185412,1.1e-5 9.333763,-2.672271 12.445062,-8.016856 -3.991253,-5.834464 -8.139602,-8.751705 -12.445062,-8.751733 -3.142715,2.8e-5 -5.515446,0.801713 -7.118198,2.405057 -1.728498,1.71474 -2.592737,3.707818 -2.592722,5.979236 -1.5e-5,2.494152 0.864224,4.509499 2.592722,6.046046 1.759887,1.558846 4.132618,2.338261 7.118198,2.33825 M 50.483209,27.77145 c -4.682663,2.9e-5 -8.831013,2.672312 -12.445062,8.016856 3.959745,5.834503 8.108095,8.751746 12.445062,8.751733 3.142633,1.3e-5 5.515364,-0.801671 7.118198,-2.405056 1.728416,-1.714701 2.592656,-3.707778 2.592722,-5.979238 -6.6e-5,-2.49411 -0.864306,-4.509456 -2.592722,-6.046044 -1.759968,-1.558805 -4.132699,-2.338222 -7.118198,-2.338251" />
|
||||
<path
|
||||
d="m 39.715314,45.942156 c -0.974277,-0.801673 -2.231353,-2.137814 -3.771231,-4.008427 -2.105641,2.672298 -4.085536,4.598569 -5.939688,5.778816 -2.325625,1.425227 -5.295467,2.137836 -8.909534,2.137828 -4.242656,8e-6 -7.762467,-1.124578 -10.559446,-3.37376 -2.9541431,-2.360505 -4.4312072,-5.489302 -4.4311969,-9.386402 -1.03e-5,-3.741174 1.4770538,-6.881106 4.4311969,-9.419805 2.639844,-2.249141 6.191083,-3.373725 10.653727,-3.373759 2.294138,3.4e-5 4.289747,0.334069 5.986829,1.002107 1.979863,0.73491 3.645488,1.737016 4.996881,3.00632 1.257039,1.135751 2.514115,2.471891 3.771231,4.008428 2.105563,-2.672257 4.085457,-4.598527 5.939689,-5.778816 2.325544,-1.425186 5.295385,-2.137794 8.909533,-2.137828 4.242577,3.4e-5 7.762388,1.124618 10.559447,3.37376 2.954063,2.360546 4.431127,5.489343 4.431197,9.386401 -7e-5,3.741216 -1.477134,6.881147 -4.431197,9.419806 -2.639925,2.24918 -6.191163,3.373767 -10.653727,3.373758 -2.294219,9e-6 -4.289826,-0.334026 -5.98683,-1.002106 -1.697101,-0.601255 -3.362726,-1.603361 -4.996881,-3.006321 M 20.576317,45.474507 c 5.185412,1.1e-5 9.333763,-2.672271 12.445062,-8.016856 -3.991253,-5.834464 -8.139602,-8.751705 -12.445062,-8.751733 -3.142715,2.8e-5 -5.515446,0.801713 -7.118198,2.405057 -1.728498,1.71474 -2.592737,3.707818 -2.592722,5.979236 -1.5e-5,2.494152 0.864224,4.509499 2.592722,6.046046 1.759887,1.558846 4.132618,2.338261 7.118198,2.33825 M 51.31185,28.772724 c -4.682663,2.9e-5 -8.831013,2.672312 -12.445062,8.016856 3.959745,5.834503 8.108095,8.751746 12.445062,8.751733 3.142633,1.3e-5 5.515364,-0.801671 7.118198,-2.405056 1.728416,-1.714701 2.592656,-3.707778 2.592722,-5.979238 -6.6e-5,-2.49411 -0.864306,-4.509456 -2.592722,-6.046044 C 56.67008,29.55217 54.297349,28.772753 51.31185,28.772724"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:76.18933868px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient3293);fill-opacity:1;stroke:none;stroke-width:1.06666672;opacity:0.5"
|
||||
id="path3279"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 37.904564,42.951873 c -0.974278,-0.801672 -2.231352,-2.137814 -3.771231,-4.008428 -2.105642,2.672298 -4.085537,4.598568 -5.939688,5.778817 -2.325625,1.425227 -5.295466,2.137836 -8.909534,2.137828 -4.242656,8e-6 -7.762467,-1.124577 -10.5594464,-3.37376 -2.9541428,-2.360505 -4.4312068,-5.489302 -4.4311963,-9.386401 -1.05e-5,-3.741175 1.4770535,-6.881107 4.4311963,-9.419805 2.6398444,-2.249142 6.1910824,-3.373727 10.6537284,-3.37376 2.294137,3.3e-5 4.289745,0.334068 5.986829,1.002107 1.979863,0.734909 3.645487,1.737016 4.99688,3.00632 1.257039,1.13575 2.514116,2.471891 3.771231,4.008428 2.105562,-2.672257 4.085456,-4.598528 5.939689,-5.778817 2.325544,-1.425185 5.295387,-2.137795 8.909534,-2.137828 4.242576,3.3e-5 7.762387,1.12462 10.559446,3.373761 2.954062,2.360545 4.431127,5.489343 4.431197,9.386401 -7e-5,3.741216 -1.477135,6.881148 -4.431197,9.419805 -2.639924,2.249182 -6.191164,3.373767 -10.653728,3.37376 -2.294217,7e-6 -4.289826,-0.334028 -5.986828,-1.002107 -1.697101,-0.601254 -3.362727,-1.603361 -4.996882,-3.006321 m -19.138997,-0.46765 c 5.185412,1.3e-5 9.333762,-2.67227 12.445062,-8.016856 -3.991252,-5.834462 -8.139602,-8.751704 -12.445062,-8.751733 -3.142714,2.9e-5 -5.515444,0.801714 -7.118198,2.405056 -1.7284972,1.714743 -2.5927368,3.707819 -2.5927216,5.979239 -1.52e-5,2.49415 0.8642244,4.509496 2.5927216,6.046045 1.759888,1.558845 4.132618,2.338262 7.118198,2.338249 M 49.5011,25.782442 c -4.682663,2.8e-5 -8.831014,2.672311 -12.445063,8.016855 3.959745,5.834504 8.108096,8.751745 12.445063,8.751733 3.142634,1.2e-5 5.515365,-0.801673 7.118198,-2.405056 1.728417,-1.7147 2.592657,-3.707778 2.592721,-5.979238 -6.4e-5,-2.49411 -0.864304,-4.509456 -2.592721,-6.046046 C 54.85933,26.561886 52.486599,25.78247 49.5011,25.782442"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:76.18933868px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient3286);fill-opacity:1;stroke:none;stroke-width:1.06666672"
|
||||
id="path3272"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccscsccccccccccccccccccccscscccccsccscccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="text5100"
|
||||
d="m 19.4,21.166667 c -4.462644,3.3e-5 -8.026822,1.150858 -10.6666667,3.4 -2.9541428,2.538698 -4.4333436,5.658825 -4.4333333,9.4 -1.03e-5,3.897098 1.4791905,7.039495 4.4333333,9.4 -1.622701,-2.044271 -2.433341,-4.51168 -2.4333333,-7.4 -1.03e-5,-3.741175 1.4791905,-6.861302 4.433333,-9.4 2.639845,-2.249142 6.204023,-3.399967 10.666667,-3.4 2.294138,3.3e-5 4.302916,0.365295 6,1.033333 1.979862,0.73491 3.615274,1.730695 4.966667,3 0.06836,0.06177 0.131637,0.137049 0.2,0.2 -0.731813,-0.797005 -1.468213,-1.538822 -2.2,-2.2 -1.351393,-1.269305 -2.986805,-2.26509 -4.966667,-3 -1.697084,-0.668038 -3.705862,-1.0333 -6,-1.033333 z m 29.6,0.1 c -3.614148,3.3e-5 -6.574457,0.74148 -8.9,2.166666 -1.818222,1.157367 -3.923451,3.291388 -5.983333,5.883334 0.618278,0.658774 1.248369,1.377605 1.866666,2.133333 2.105562,-2.672257 4.262434,-4.836378 6.116667,-6.016667 2.325543,-1.425186 5.285852,-2.166633 8.9,-2.166666 4.242576,3.3e-5 7.769607,1.150858 10.566667,3.4 -0.570388,-0.722129 -1.227721,-1.382884 -2,-2 C 56.769607,22.417525 53.242576,21.2667 49,21.266667 Z m 8.866667,8.1 c 0.9092,1.305235 1.366619,2.857751 1.366666,4.666666 -6.5e-5,2.271461 -0.871584,4.285301 -2.6,6 -1.602834,1.603384 -3.957366,2.400012 -7.1,2.4 -2.653707,8e-6 -5.320858,-1.032242 -7.833333,-3.216666 3.136636,3.509305 6.469807,5.216676 9.833333,5.216666 3.142634,1.2e-5 5.497166,-0.796616 7.1,-2.4 1.728416,-1.714699 2.599935,-3.728539 2.6,-6 -6.5e-5,-2.49411 -0.871584,-4.496744 -2.6,-6.033333 -0.24943,-0.220921 -0.49262,-0.443723 -0.766666,-0.633333 z m -26.633334,4.966666 c -3.1113,5.344585 -7.247921,8.033345 -12.433333,8.033334 -2.58055,1e-5 -4.543473,-0.352086 -6.208333,-1.516667 0.348871,0.50642 0.590094,0.752276 1.075,1.183333 1.759888,1.558846 4.147753,2.333345 7.133333,2.333334 5.185412,1.1e-5 9.322033,-2.688749 12.433333,-8.033334 z m 4.933334,6.5 c -0.04103,0.05207 -0.09239,0.08182 -0.133334,0.133334 0.687326,0.744419 1.306949,1.359747 1.833334,1.8 -0.529404,-0.580895 -1.078447,-1.178283 -1.7,-1.933334 z"
|
||||
style="font-style:normal;font-weight:normal;font-size:76.18933868px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;opacity:0.3;fill:#ccff00;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccsccscccccccccccccccccccscscccccsccsccccccc"
|
||||
id="text5058-0"
|
||||
d="m 19.730474,21.54714 c -4.462645,3.3e-5 -8.026823,1.150859 -10.6666669,3.4 -2.9541429,2.538699 -4.433344,5.658826 -4.4333333,9.4 -1.07e-5,3.897099 1.4791904,7.039495 4.4333333,9.4 0.042837,0.03444 0.090155,0.06608 0.1333334,0.1 -2.2392086,-2.228193 -3.3666752,-5.040417 -3.3666667,-8.433333 -1.07e-5,-3.741174 1.4791904,-6.861301 4.4333332,-9.4 2.639844,-2.249141 6.204022,-3.399967 10.666667,-3.4 2.294137,3.3e-5 4.302916,0.365295 6,1.033333 1.870874,0.694455 3.42364,1.628367 4.733333,2.8 -0.314265,-0.308986 -0.652406,-0.582729 -0.966667,-0.866666 -1.351393,-1.269305 -2.986804,-2.265091 -4.966666,-3 -1.697084,-0.668039 -3.705863,-1.033301 -6,-1.033334 z m 29.6,0.1 c -3.614149,3.3e-5 -6.574457,0.741481 -8.9,2.166667 -1.813279,1.154221 -3.963039,3.235656 -6.016667,5.816667 0.355649,0.402628 0.711011,0.798625 1.066667,1.233333 2.105561,-2.672257 4.295767,-4.803044 6.15,-5.983333 2.325543,-1.425187 5.285851,-2.166634 8.9,-2.166667 4.22442,3.3e-5 7.742084,1.136734 10.533333,3.366667 -0.36096,-0.367566 -0.745726,-0.696967 -1.166667,-1.033334 -2.797059,-2.249141 -6.32409,-3.399967 -10.566666,-3.4 z m 8.233333,7.333334 c 1.323326,1.449243 1.999942,3.250987 2,5.433333 -6.5e-5,2.27146 -0.871584,4.2853 -2.6,6 -1.602834,1.603383 -3.957366,2.400012 -7.1,2.4 -2.406328,6e-6 -4.776468,-0.90386 -7.066667,-2.7 2.669147,2.483838 5.436929,3.766674 8.266667,3.766667 3.142634,1.1e-5 5.497166,-0.796617 7.1,-2.4 1.728416,-1.7147 2.599935,-3.72854 2.6,-6 -6.5e-5,-2.49411 -0.871584,-4.496745 -2.6,-6.033334 -0.185641,-0.164422 -0.400724,-0.319587 -0.6,-0.466666 z m -26,5.733333 c -3.1113,5.344584 -7.247921,8.033345 -12.433333,8.033333 -2.612382,1.1e-5 -4.759372,-0.60651 -6.433334,-1.8 0.166027,0.176488 0.313947,0.367942 0.5,0.533334 1.759888,1.558845 4.147754,2.333345 7.133334,2.333333 5.185412,1.2e-5 9.322033,-2.688749 12.433333,-8.033333 z m 4.133333,5.566667 c -0.04657,0.05909 -0.08689,0.108298 -0.133333,0.166666 1.038571,1.18897 1.9748,2.169945 2.7,2.766667 0.06249,0.05364 0.137426,0.08086 0.2,0.133333 -0.792178,-0.781249 -1.706288,-1.778539 -2.766667,-3.066666 z"
|
||||
style="font-style:normal;font-weight:normal;font-size:76.18933868px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;opacity:0.6;fill:#ccff00;fill-opacity:1;stroke:none;stroke-width:1.06666672"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 22 KiB |
@@ -1,353 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="68.26667"
|
||||
height="68.26667"
|
||||
id="svg4427"
|
||||
version="1.1"
|
||||
inkscape:version="0.92.1 r"
|
||||
sodipodi:docname="multimc-smooth-biginfinity.svg"
|
||||
inkscape:export-filename="/home/peterix/playground/MultiMC-icons/multimc-smooth-biginfinity.png"
|
||||
inkscape:export-xdpi="180"
|
||||
inkscape:export-ydpi="180">
|
||||
<defs
|
||||
id="defs4429">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4809">
|
||||
<stop
|
||||
style="stop-color:#98c867;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4805" />
|
||||
<stop
|
||||
style="stop-color:#5c9a33;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop4807" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5668"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop5670"
|
||||
offset="0"
|
||||
style="stop-color:#75b54b;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop5672"
|
||||
offset="1"
|
||||
style="stop-color:#75b54b;stop-opacity:0.6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5084"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop5086"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:0.8" />
|
||||
<stop
|
||||
id="stop5088"
|
||||
offset="1"
|
||||
style="stop-color:#000000;stop-opacity:0.35" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5668"
|
||||
id="linearGradient5072"
|
||||
x1="6.7342591"
|
||||
y1="28.510933"
|
||||
x2="50.506943"
|
||||
y2="61.773685"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-0.01532073,-0.00938002)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5084"
|
||||
id="linearGradient5082"
|
||||
x1="14.312115"
|
||||
y1="9.7948904"
|
||||
x2="44.097023"
|
||||
y2="82.973114"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5668"
|
||||
id="linearGradient3281"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-0.01532073,-0.00938002)"
|
||||
x1="6.7342591"
|
||||
y1="28.510933"
|
||||
x2="50.506943"
|
||||
y2="61.773685" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5668"
|
||||
id="linearGradient3283"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-0.01532073,-0.00938002)"
|
||||
x1="6.7342591"
|
||||
y1="28.510933"
|
||||
x2="50.506943"
|
||||
y2="61.773685" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5668"
|
||||
id="linearGradient3286"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.2671525,0,0,0.89790119,-0.01941371,-0.00842234)"
|
||||
x1="6.7342591"
|
||||
y1="28.510933"
|
||||
x2="50.506943"
|
||||
y2="61.773685" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5084"
|
||||
id="linearGradient3288"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="14.312115"
|
||||
y1="9.7948904"
|
||||
x2="44.097023"
|
||||
y2="82.973114" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5084"
|
||||
id="linearGradient3290"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="14.312115"
|
||||
y1="9.7948904"
|
||||
x2="44.097023"
|
||||
y2="82.973114" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5084"
|
||||
id="linearGradient3293"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="14.312115"
|
||||
y1="9.7948904"
|
||||
x2="44.097023"
|
||||
y2="82.973114"
|
||||
gradientTransform="scale(1.2671525,0.89790119)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient5580">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0.0627451"
|
||||
offset="0"
|
||||
id="stop5576" />
|
||||
<stop
|
||||
style="stop-color:#322217;stop-opacity:0.58823532"
|
||||
offset="1"
|
||||
id="stop5578" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3999"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop3995"
|
||||
offset="0"
|
||||
style="stop-color:#a3704b;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop3997"
|
||||
offset="1"
|
||||
style="stop-color:#6a4a33;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2727"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop2723"
|
||||
offset="0"
|
||||
style="stop-color:#966c4a;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop2725"
|
||||
offset="1"
|
||||
style="stop-color:#593d29;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2727"
|
||||
id="linearGradient2050"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="36.546478"
|
||||
y1="33.80484"
|
||||
x2="86.415741"
|
||||
y2="97.065842" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3999"
|
||||
id="radialGradient2052"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-9.105292e-4,-0.00104444)"
|
||||
cx="34.133331"
|
||||
cy="34.133335"
|
||||
fx="34.133331"
|
||||
fy="34.133335"
|
||||
r="29.866665" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5580"
|
||||
id="linearGradient2140"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-0.0010513,-9.083059e-4)"
|
||||
x1="29.866674"
|
||||
y1="29.867579"
|
||||
x2="38.400005"
|
||||
y2="38.400913" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5084"
|
||||
id="linearGradient4790"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.2671525,0,0,0.89790119,-0.82864077,-1.0012743)"
|
||||
x1="14.312115"
|
||||
y1="9.7948904"
|
||||
x2="44.097023"
|
||||
y2="82.973114" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4809"
|
||||
id="radialGradient4803"
|
||||
cx="-42.66758"
|
||||
cy="-34.134373"
|
||||
fx="-42.66758"
|
||||
fy="-34.134373"
|
||||
r="34.132812"
|
||||
gradientTransform="matrix(1.7500268,0.1250019,-0.01781176,0.24936465,95.393964,18.110151)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="3.6203867"
|
||||
inkscape:cx="52.171166"
|
||||
inkscape:cy="11.292073"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:window-width="1368"
|
||||
inkscape:window-height="905"
|
||||
inkscape:window-x="2452"
|
||||
inkscape:window-y="723"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="false"
|
||||
inkscape:snap-bbox-edge-midpoints="false"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-midpoints="false"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-midpoints="false"
|
||||
inkscape:snap-intersection-paths="true"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:snap-object-midpoints="true"
|
||||
inkscape:snap-text-baseline="true"
|
||||
inkscape:snap-center="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4446"
|
||||
empspacing="16"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true"
|
||||
spacingx="4.2666667"
|
||||
spacingy="4.2666667"
|
||||
originx="0"
|
||||
originy="0" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata4432">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<g
|
||||
id="g2048"
|
||||
transform="translate(9.113e-4,0.00104183)">
|
||||
<rect
|
||||
rx="8.5333338"
|
||||
ry="8.5333338"
|
||||
style="fill:url(#linearGradient2050);fill-opacity:1;stroke:none;stroke-width:17.06666756"
|
||||
id="rect2026"
|
||||
width="68.26667"
|
||||
height="68.26667"
|
||||
x="-1.3322676e-15"
|
||||
y="3.0270508e-06" />
|
||||
<rect
|
||||
rx="4.2666626"
|
||||
y="4.2656283"
|
||||
x="4.2657552"
|
||||
height="59.733334"
|
||||
width="59.73333"
|
||||
id="rect2028"
|
||||
style="fill:url(#radialGradient2052);fill-opacity:1;stroke:none;stroke-width:14.93333435"
|
||||
ry="4.2666669" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4811"
|
||||
d="m 4.2669272,4.2645856 -9.11e-4,8.5333334 h 4.267577 v 4.267579 h 8.5332038 v 4.265625 h 4.265625 V 8.5322946 H 25.6 v 8.5332034 h 4.265625 v -4.267579 h 4.267578 v 8.533204 h 4.265625 v -4.265625 h 4.267578 v 4.265625 h 4.267579 v -4.265625 h 4.265624 v -4.267579 h 4.267579 v 4.267579 h 8.533203 l -1.3e-4,-12.8009124 z"
|
||||
style="opacity:0.6;fill:#593d29;fill-opacity:1;stroke:none;stroke-width:17.06666756"
|
||||
sodipodi:nodetypes="ccccccccccccccccccccccccccc" />
|
||||
<path
|
||||
style="fill:url(#radialGradient4803);fill-opacity:1;stroke:none;stroke-width:17.06666756"
|
||||
d="m 8.5329442,-0.0018207 c -4.7274675,0 -8.5332035,3.805736 -8.5332035,8.533203 v 4.2675787 h 4.265625 V 8.5313823 c 0,-0.521698 0.105433,-1.01339 0.27539,-1.47461 -0.169616,0.460814 -0.27539,0.953462 -0.27539,1.47461 h 4.2675785 v 4.2675787 h 4.2656248 4.267578 v 4.265625 h 4.265625 V 12.798961 8.5313823 4.2657573 h 4.267578 v 4.265625 4.2675787 h 4.265625 V 8.5313823 h 4.267578 v 4.2675787 4.265625 h 4.265625 v -4.265625 h 4.267578 v 4.265625 h 4.267579 v -4.265625 h 4.265624 V 8.5313823 h 4.267579 v 4.2675787 h 4.265625 4.267578 V 8.5313823 h 4.265625 c 0,-4.727467 -3.805737,-8.533203 -8.533203,-8.533203 z m -3.019531,5.513671 c -0.318089,0.317888 -0.570428,0.695824 -0.7753915,1.101563 0.2048795,-0.405231 0.4576385,-0.784012 0.7753915,-1.101563 z"
|
||||
id="path4794"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="opacity:1;fill:url(#linearGradient2140);fill-opacity:1;stroke:none;stroke-width:17.06666756"
|
||||
d="m 8.5322887,-9.083059e-4 c -4.72747,0 -8.5332,3.8057359059 -8.5332,8.5332029059 V 59.731515 c 0,4.727467 3.80573,8.535156 8.5332,8.535156 H 59.731502 c 4.72747,0 8.5332,-3.807689 8.5332,-8.535156 V 8.5322946 c 0,-4.727467 -3.80573,-8.5332029059 -8.5332,-8.5332029059 z m 0,4.2675779059 H 59.731502 c 2.36373,0 4.26758,1.901892 4.26758,4.265625 V 59.731515 c 0,2.363733 -1.90385,4.267578 -4.26758,4.267578 H 8.5322887 c -2.36373,0 -4.26758,-1.903845 -4.26758,-4.267578 V 8.5322946 c 0,-2.363733 1.90385,-4.265625 4.26758,-4.265625 z"
|
||||
id="path2046"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
id="g1092">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4786"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:76.18933868px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient4790);fill-opacity:1;stroke:none;stroke-width:1.06666672;opacity:0.5"
|
||||
d="m 38.886673,44.940882 c -0.974277,-0.801673 -2.231353,-2.137814 -3.771231,-4.008427 -2.105641,2.672298 -4.085536,4.598569 -5.939688,5.778816 -2.325625,1.425227 -5.295467,2.137836 -8.909534,2.137828 -4.242656,8e-6 -7.762467,-1.124578 -10.5594458,-3.37376 C 6.7526311,43.114834 5.275567,39.986037 5.2755773,36.088937 5.275567,32.347763 6.7526311,29.207831 9.7067742,26.669132 12.346618,24.419991 15.897857,23.295407 20.360501,23.295373 c 2.294138,3.4e-5 4.289747,0.334069 5.986829,1.002107 1.979863,0.73491 3.645488,1.737016 4.996881,3.00632 1.257039,1.135751 2.514115,2.471891 3.771231,4.008428 2.105563,-2.672257 4.085457,-4.598527 5.939689,-5.778816 2.325544,-1.425186 5.295385,-2.137794 8.909533,-2.137828 4.242577,3.4e-5 7.762388,1.124618 10.559447,3.37376 2.954063,2.360546 4.431127,5.489343 4.431197,9.386401 -7e-5,3.741216 -1.477134,6.881147 -4.431197,9.419806 -2.639925,2.24918 -6.191163,3.373767 -10.653727,3.373758 -2.294219,9e-6 -4.289826,-0.334026 -5.98683,-1.002106 -1.697101,-0.601255 -3.362726,-1.603361 -4.996881,-3.006321 M 19.747676,44.473233 c 5.185412,1.1e-5 9.333763,-2.672271 12.445062,-8.016856 -3.991253,-5.834464 -8.139602,-8.751705 -12.445062,-8.751733 -3.142715,2.8e-5 -5.515446,0.801713 -7.118198,2.405057 -1.728498,1.71474 -2.592737,3.707818 -2.592722,5.979236 -1.5e-5,2.494152 0.864224,4.509499 2.592722,6.046046 1.759887,1.558846 4.132618,2.338261 7.118198,2.33825 M 50.483209,27.77145 c -4.682663,2.9e-5 -8.831013,2.672312 -12.445062,8.016856 3.959745,5.834503 8.108095,8.751746 12.445062,8.751733 3.142633,1.3e-5 5.515364,-0.801671 7.118198,-2.405056 1.728416,-1.714701 2.592656,-3.707778 2.592722,-5.979238 -6.6e-5,-2.49411 -0.864306,-4.509456 -2.592722,-6.046044 -1.759968,-1.558805 -4.132699,-2.338222 -7.118198,-2.338251" />
|
||||
<path
|
||||
d="m 39.715314,45.942156 c -0.974277,-0.801673 -2.231353,-2.137814 -3.771231,-4.008427 -2.105641,2.672298 -4.085536,4.598569 -5.939688,5.778816 -2.325625,1.425227 -5.295467,2.137836 -8.909534,2.137828 -4.242656,8e-6 -7.762467,-1.124578 -10.559446,-3.37376 -2.9541431,-2.360505 -4.4312072,-5.489302 -4.4311969,-9.386402 -1.03e-5,-3.741174 1.4770538,-6.881106 4.4311969,-9.419805 2.639844,-2.249141 6.191083,-3.373725 10.653727,-3.373759 2.294138,3.4e-5 4.289747,0.334069 5.986829,1.002107 1.979863,0.73491 3.645488,1.737016 4.996881,3.00632 1.257039,1.135751 2.514115,2.471891 3.771231,4.008428 2.105563,-2.672257 4.085457,-4.598527 5.939689,-5.778816 2.325544,-1.425186 5.295385,-2.137794 8.909533,-2.137828 4.242577,3.4e-5 7.762388,1.124618 10.559447,3.37376 2.954063,2.360546 4.431127,5.489343 4.431197,9.386401 -7e-5,3.741216 -1.477134,6.881147 -4.431197,9.419806 -2.639925,2.24918 -6.191163,3.373767 -10.653727,3.373758 -2.294219,9e-6 -4.289826,-0.334026 -5.98683,-1.002106 -1.697101,-0.601255 -3.362726,-1.603361 -4.996881,-3.006321 M 20.576317,45.474507 c 5.185412,1.1e-5 9.333763,-2.672271 12.445062,-8.016856 -3.991253,-5.834464 -8.139602,-8.751705 -12.445062,-8.751733 -3.142715,2.8e-5 -5.515446,0.801713 -7.118198,2.405057 -1.728498,1.71474 -2.592737,3.707818 -2.592722,5.979236 -1.5e-5,2.494152 0.864224,4.509499 2.592722,6.046046 1.759887,1.558846 4.132618,2.338261 7.118198,2.33825 M 51.31185,28.772724 c -4.682663,2.9e-5 -8.831013,2.672312 -12.445062,8.016856 3.959745,5.834503 8.108095,8.751746 12.445062,8.751733 3.142633,1.3e-5 5.515364,-0.801671 7.118198,-2.405056 1.728416,-1.714701 2.592656,-3.707778 2.592722,-5.979238 -6.6e-5,-2.49411 -0.864306,-4.509456 -2.592722,-6.046044 C 56.67008,29.55217 54.297349,28.772753 51.31185,28.772724"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:76.18933868px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient3293);fill-opacity:1;stroke:none;stroke-width:1.06666672;opacity:0.5"
|
||||
id="path3279"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 37.904564,42.951873 c -0.974278,-0.801672 -2.231352,-2.137814 -3.771231,-4.008428 -2.105642,2.672298 -4.085537,4.598568 -5.939688,5.778817 -2.325625,1.425227 -5.295466,2.137836 -8.909534,2.137828 -4.242656,8e-6 -7.762467,-1.124577 -10.5594464,-3.37376 -2.9541428,-2.360505 -4.4312068,-5.489302 -4.4311963,-9.386401 -1.05e-5,-3.741175 1.4770535,-6.881107 4.4311963,-9.419805 2.6398444,-2.249142 6.1910824,-3.373727 10.6537284,-3.37376 2.294137,3.3e-5 4.289745,0.334068 5.986829,1.002107 1.979863,0.734909 3.645487,1.737016 4.99688,3.00632 1.257039,1.13575 2.514116,2.471891 3.771231,4.008428 2.105562,-2.672257 4.085456,-4.598528 5.939689,-5.778817 2.325544,-1.425185 5.295387,-2.137795 8.909534,-2.137828 4.242576,3.3e-5 7.762387,1.12462 10.559446,3.373761 2.954062,2.360545 4.431127,5.489343 4.431197,9.386401 -7e-5,3.741216 -1.477135,6.881148 -4.431197,9.419805 -2.639924,2.249182 -6.191164,3.373767 -10.653728,3.37376 -2.294217,7e-6 -4.289826,-0.334028 -5.986828,-1.002107 -1.697101,-0.601254 -3.362727,-1.603361 -4.996882,-3.006321 m -19.138997,-0.46765 c 5.185412,1.3e-5 9.333762,-2.67227 12.445062,-8.016856 -3.991252,-5.834462 -8.139602,-8.751704 -12.445062,-8.751733 -3.142714,2.9e-5 -5.515444,0.801714 -7.118198,2.405056 -1.7284972,1.714743 -2.5927368,3.707819 -2.5927216,5.979239 -1.52e-5,2.49415 0.8642244,4.509496 2.5927216,6.046045 1.759888,1.558845 4.132618,2.338262 7.118198,2.338249 M 49.5011,25.782442 c -4.682663,2.8e-5 -8.831014,2.672311 -12.445063,8.016855 3.959745,5.834504 8.108096,8.751745 12.445063,8.751733 3.142634,1.2e-5 5.515365,-0.801673 7.118198,-2.405056 1.728417,-1.7147 2.592657,-3.707778 2.592721,-5.979238 -6.4e-5,-2.49411 -0.864304,-4.509456 -2.592721,-6.046046 C 54.85933,26.561886 52.486599,25.78247 49.5011,25.782442"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:76.18933868px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient3286);fill-opacity:1;stroke:none;stroke-width:1.06666672"
|
||||
id="path3272"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccscsccccccccccccccccccccscscccccsccscccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="text5100"
|
||||
d="m 19.4,21.166667 c -4.462644,3.3e-5 -8.026822,1.150858 -10.6666667,3.4 -2.9541428,2.538698 -4.4333436,5.658825 -4.4333333,9.4 -1.03e-5,3.897098 1.4791905,7.039495 4.4333333,9.4 -1.622701,-2.044271 -2.433341,-4.51168 -2.4333333,-7.4 -1.03e-5,-3.741175 1.4791905,-6.861302 4.433333,-9.4 2.639845,-2.249142 6.204023,-3.399967 10.666667,-3.4 2.294138,3.3e-5 4.302916,0.365295 6,1.033333 1.979862,0.73491 3.615274,1.730695 4.966667,3 0.06836,0.06177 0.131637,0.137049 0.2,0.2 -0.731813,-0.797005 -1.468213,-1.538822 -2.2,-2.2 -1.351393,-1.269305 -2.986805,-2.26509 -4.966667,-3 -1.697084,-0.668038 -3.705862,-1.0333 -6,-1.033333 z m 29.6,0.1 c -3.614148,3.3e-5 -6.574457,0.74148 -8.9,2.166666 -1.818222,1.157367 -3.923451,3.291388 -5.983333,5.883334 0.618278,0.658774 1.248369,1.377605 1.866666,2.133333 2.105562,-2.672257 4.262434,-4.836378 6.116667,-6.016667 2.325543,-1.425186 5.285852,-2.166633 8.9,-2.166666 4.242576,3.3e-5 7.769607,1.150858 10.566667,3.4 -0.570388,-0.722129 -1.227721,-1.382884 -2,-2 C 56.769607,22.417525 53.242576,21.2667 49,21.266667 Z m 8.866667,8.1 c 0.9092,1.305235 1.366619,2.857751 1.366666,4.666666 -6.5e-5,2.271461 -0.871584,4.285301 -2.6,6 -1.602834,1.603384 -3.957366,2.400012 -7.1,2.4 -2.653707,8e-6 -5.320858,-1.032242 -7.833333,-3.216666 3.136636,3.509305 6.469807,5.216676 9.833333,5.216666 3.142634,1.2e-5 5.497166,-0.796616 7.1,-2.4 1.728416,-1.714699 2.599935,-3.728539 2.6,-6 -6.5e-5,-2.49411 -0.871584,-4.496744 -2.6,-6.033333 -0.24943,-0.220921 -0.49262,-0.443723 -0.766666,-0.633333 z m -26.633334,4.966666 c -3.1113,5.344585 -7.247921,8.033345 -12.433333,8.033334 -2.58055,1e-5 -4.543473,-0.352086 -6.208333,-1.516667 0.348871,0.50642 0.590094,0.752276 1.075,1.183333 1.759888,1.558846 4.147753,2.333345 7.133333,2.333334 5.185412,1.1e-5 9.322033,-2.688749 12.433333,-8.033334 z m 4.933334,6.5 c -0.04103,0.05207 -0.09239,0.08182 -0.133334,0.133334 0.687326,0.744419 1.306949,1.359747 1.833334,1.8 -0.529404,-0.580895 -1.078447,-1.178283 -1.7,-1.933334 z"
|
||||
style="font-style:normal;font-weight:normal;font-size:76.18933868px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;opacity:0.3;fill:#ccff00;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccsccscccccccccccccccccccscscccccsccsccccccc"
|
||||
id="text5058-0"
|
||||
d="m 19.730474,21.54714 c -4.462645,3.3e-5 -8.026823,1.150859 -10.6666669,3.4 -2.9541429,2.538699 -4.433344,5.658826 -4.4333333,9.4 -1.07e-5,3.897099 1.4791904,7.039495 4.4333333,9.4 0.042837,0.03444 0.090155,0.06608 0.1333334,0.1 -2.2392086,-2.228193 -3.3666752,-5.040417 -3.3666667,-8.433333 -1.07e-5,-3.741174 1.4791904,-6.861301 4.4333332,-9.4 2.639844,-2.249141 6.204022,-3.399967 10.666667,-3.4 2.294137,3.3e-5 4.302916,0.365295 6,1.033333 1.870874,0.694455 3.42364,1.628367 4.733333,2.8 -0.314265,-0.308986 -0.652406,-0.582729 -0.966667,-0.866666 -1.351393,-1.269305 -2.986804,-2.265091 -4.966666,-3 -1.697084,-0.668039 -3.705863,-1.033301 -6,-1.033334 z m 29.6,0.1 c -3.614149,3.3e-5 -6.574457,0.741481 -8.9,2.166667 -1.813279,1.154221 -3.963039,3.235656 -6.016667,5.816667 0.355649,0.402628 0.711011,0.798625 1.066667,1.233333 2.105561,-2.672257 4.295767,-4.803044 6.15,-5.983333 2.325543,-1.425187 5.285851,-2.166634 8.9,-2.166667 4.22442,3.3e-5 7.742084,1.136734 10.533333,3.366667 -0.36096,-0.367566 -0.745726,-0.696967 -1.166667,-1.033334 -2.797059,-2.249141 -6.32409,-3.399967 -10.566666,-3.4 z m 8.233333,7.333334 c 1.323326,1.449243 1.999942,3.250987 2,5.433333 -6.5e-5,2.27146 -0.871584,4.2853 -2.6,6 -1.602834,1.603383 -3.957366,2.400012 -7.1,2.4 -2.406328,6e-6 -4.776468,-0.90386 -7.066667,-2.7 2.669147,2.483838 5.436929,3.766674 8.266667,3.766667 3.142634,1.1e-5 5.497166,-0.796617 7.1,-2.4 1.728416,-1.7147 2.599935,-3.72854 2.6,-6 -6.5e-5,-2.49411 -0.871584,-4.496745 -2.6,-6.033334 -0.185641,-0.164422 -0.400724,-0.319587 -0.6,-0.466666 z m -26,5.733333 c -3.1113,5.344584 -7.247921,8.033345 -12.433333,8.033333 -2.612382,1.1e-5 -4.759372,-0.60651 -6.433334,-1.8 0.166027,0.176488 0.313947,0.367942 0.5,0.533334 1.759888,1.558845 4.147754,2.333345 7.133334,2.333333 5.185412,1.2e-5 9.322033,-2.688749 12.433333,-8.033333 z m 4.133333,5.566667 c -0.04657,0.05909 -0.08689,0.108298 -0.133333,0.166666 1.038571,1.18897 1.9748,2.169945 2.7,2.766667 0.06249,0.05364 0.137426,0.08086 0.2,0.133333 -0.792178,-0.781249 -1.706288,-1.778539 -2.766667,-3.066666 z"
|
||||
style="font-style:normal;font-weight:normal;font-size:76.18933868px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;opacity:0.6;fill:#ccff00;fill-opacity:1;stroke:none;stroke-width:1.06666672"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 22 KiB |
@@ -1,63 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="Layer_1"
|
||||
data-name="Layer 1"
|
||||
viewBox="0 0 134 134"
|
||||
version="1.1"
|
||||
sodipodi:docname="twitch.svg"
|
||||
inkscape:version="0.92.2 2405546, 2018-03-11"
|
||||
width="134"
|
||||
height="134">
|
||||
<metadata
|
||||
id="metadata13">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>Glitch</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1414"
|
||||
inkscape:window-height="944"
|
||||
id="namedview11"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.761194"
|
||||
inkscape:cx="-109.17797"
|
||||
inkscape:cy="66.999998"
|
||||
inkscape:window-x="2640"
|
||||
inkscape:window-y="554"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="Layer_1" />
|
||||
<defs
|
||||
id="defs4">
|
||||
<style
|
||||
id="style2">.cls-1{fill:#6441a4;fill-rule:evenodd;}</style>
|
||||
</defs>
|
||||
<title
|
||||
id="title6">Glitch</title>
|
||||
<path
|
||||
class="cls-1"
|
||||
d="M 9,0 0,23 v 94 h 32 v 17 H 50 L 67,117 H 93 L 128,82 V 0 Z M 116,76 96,96 H 64 L 47,113 V 96 H 20 V 12 h 96 z M 96,35 V 70 H 84 V 35 Z M 64,35 V 70 H 52 V 35 Z"
|
||||
id="path8"
|
||||
style="fill:#6441a4;fill-opacity:1;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -1,61 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
|
||||
<rect fill="#754C24" width="32" height="32"/>
|
||||
<polygon fill="#39B54A" points="0,5 1,5 1,4 4,4 4,5 8,5 8,8 9,8 9,2 12,2 12,5 13,5 13,4 16,4 16,8 17,8 17,6 18,6 18,5 20,5 20,8
|
||||
21,8 21,6 22,6 22,5 24,5 24,4 26,4 26,5 29,5 29,4 32,4 32,0 0,0 "/>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-963.8778" y1="47.5718" x2="-963.0319" y2="49.6501" gradientTransform="matrix(20.79 0 0 14.7315 20048.0879 -696.8257)">
|
||||
<stop offset="0" style="stop-color:#000000;stop-opacity:0.8"/>
|
||||
<stop offset="1" style="stop-color:#000000;stop-opacity:0.35"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_1_)" d="M18.5,21.5c-0.5-0.4-1-1-1.8-1.9c-1,1.2-1.9,2.1-2.8,2.7c-1.1,0.7-2.5,1-4.1,1c-2,0-3.6-0.5-4.9-1.6
|
||||
c-1.4-1.1-2.1-2.6-2.1-4.4c0-1.7,0.7-3.2,2.1-4.4c1.2-1,2.9-1.6,5-1.6c1.1,0,2,0.2,2.8,0.5c0.9,0.3,1.7,0.8,2.3,1.4
|
||||
c0.6,0.5,1.2,1.2,1.8,1.9c1-1.2,1.9-2.1,2.8-2.7c1.1-0.7,2.5-1,4.1-1c2,0,3.6,0.5,4.9,1.6c1.4,1.1,2.1,2.6,2.1,4.4
|
||||
c0,1.7-0.7,3.2-2.1,4.4c-1.2,1-2.9,1.6-5,1.6c-1.1,0-2-0.2-2.8-0.5S19.2,22.1,18.5,21.5 M9.6,21.3c2.4,0,4.3-1.2,5.8-3.7
|
||||
c-1.9-2.7-3.8-4.1-5.8-4.1c-1.5,0-2.6,0.4-3.3,1.1c-0.8,0.8-1.2,1.7-1.2,2.8c0,1.2,0.4,2.1,1.2,2.8C7.1,20.9,8.2,21.3,9.6,21.3
|
||||
M23.9,13.5c-2.2,0-4.1,1.2-5.8,3.7c1.8,2.7,3.8,4.1,5.8,4.1c1.5,0,2.6-0.4,3.3-1.1c0.8-0.8,1.2-1.7,1.2-2.8c0-1.2-0.4-2.1-1.2-2.8
|
||||
C26.3,13.8,25.2,13.5,23.9,13.5"/>
|
||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="-964.0289" y1="48.0195" x2="-962.7877" y2="48.9627" gradientTransform="matrix(20.79 0 0 14.7315 20047.7695 -696.964)">
|
||||
<stop offset="0" style="stop-color:#75B54B"/>
|
||||
<stop offset="1" style="stop-color:#75B54B;stop-opacity:0.6"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_2_)" d="M17.6,20.1c-0.5-0.4-1-1-1.8-1.9c-1,1.2-1.9,2.1-2.8,2.7c-1.1,0.7-2.5,1-4.1,1c-2,0-3.6-0.5-4.9-1.6
|
||||
c-1.4-1.1-2.1-2.6-2.1-4.4c0-1.7,0.7-3.2,2.1-4.4c1.2-1,2.9-1.6,5-1.6c1.1,0,2,0.2,2.8,0.5c0.9,0.3,1.7,0.8,2.3,1.4
|
||||
c0.6,0.5,1.2,1.2,1.8,1.9c1-1.2,1.9-2.1,2.8-2.7c1.1-0.7,2.5-1,4.1-1c2,0,3.6,0.5,4.9,1.6c1.4,1.1,2.1,2.6,2.1,4.4
|
||||
c0,1.7-0.7,3.2-2.1,4.4c-1.2,1-2.9,1.6-5,1.6c-1.1,0-2-0.2-2.8-0.5C19.2,21.2,18.4,20.8,17.6,20.1 M8.7,19.9c2.4,0,4.3-1.2,5.8-3.7
|
||||
c-1.9-2.7-3.8-4.1-5.8-4.1c-1.5,0-2.6,0.4-3.3,1.1C4.6,14,4.2,14.9,4.2,16c0,1.2,0.4,2.1,1.2,2.8C6.2,19.5,7.3,19.9,8.7,19.9
|
||||
M23,12.1c-2.2,0-4.1,1.2-5.8,3.7c1.8,2.7,3.8,4.1,5.8,4.1c1.5,0,2.6-0.4,3.3-1.1c0.8-0.8,1.2-1.7,1.2-2.8c0-1.2-0.4-2.1-1.2-2.8
|
||||
C25.5,12.5,24.4,12.1,23,12.1"/>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.9 KiB |
@@ -1,32 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="-463 265 32 32" enable-background="new -463 265 32 32" xml:space="preserve">
|
||||
<rect x="-463" y="265" fill="#754C24" width="32" height="32"/>
|
||||
<polygon fill="#39B54A" points="-463,270 -462,270 -462,269 -459,269 -459,270 -455,270 -455,273 -454,273 -454,267 -451,267
|
||||
-451,270 -450,270 -450,269 -447,269 -447,273 -446,273 -446,271 -445,271 -445,270 -443,270 -443,273 -442,273 -442,271 -441,271
|
||||
-441,270 -439,270 -439,269 -437,269 -437,270 -434,270 -434,269 -431,269 -431,265 -463,265 "/>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-1426.8778" y1="312.5718" x2="-1426.032" y2="314.6501" gradientTransform="matrix(20.79 0 0 14.7315 29210.8574 -4335.6729)">
|
||||
<stop offset="0" style="stop-color:#000000;stop-opacity:0.8"/>
|
||||
<stop offset="1" style="stop-color:#000000;stop-opacity:0.35"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_1_)" d="M-444.5,286.5c-0.5-0.4-1-1-1.8-1.9c-1,1.2-1.9,2.1-2.8,2.7c-1.1,0.7-2.5,1-4.1,1
|
||||
c-2,0-3.6-0.5-4.9-1.6c-1.4-1.1-2.1-2.6-2.1-4.4c0-1.7,0.7-3.2,2.1-4.4c1.2-1,2.9-1.6,5-1.6c1.1,0,2,0.2,2.8,0.5
|
||||
c0.9,0.3,1.7,0.8,2.3,1.4c0.6,0.5,1.2,1.2,1.8,1.9c1-1.2,1.9-2.1,2.8-2.7c1.1-0.7,2.5-1,4.1-1c2,0,3.6,0.5,4.9,1.6
|
||||
c1.4,1.1,2.1,2.6,2.1,4.4c0,1.7-0.7,3.2-2.1,4.4c-1.2,1-2.9,1.6-5,1.6c-1.1,0-2-0.2-2.8-0.5S-443.8,287.1-444.5,286.5 M-453.4,286.3
|
||||
c2.4,0,4.3-1.2,5.8-3.7c-1.9-2.7-3.8-4.1-5.8-4.1c-1.5,0-2.6,0.4-3.3,1.1c-0.8,0.8-1.2,1.7-1.2,2.8c0,1.2,0.4,2.1,1.2,2.8
|
||||
C-455.9,285.9-454.8,286.3-453.4,286.3 M-439.1,278.5c-2.2,0-4.1,1.2-5.8,3.7c1.8,2.7,3.8,4.1,5.8,4.1c1.5,0,2.6-0.4,3.3-1.1
|
||||
c0.8-0.8,1.2-1.7,1.2-2.8c0-1.2-0.4-2.1-1.2-2.8C-436.7,278.8-437.8,278.5-439.1,278.5"/>
|
||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="-1427.0289" y1="313.0195" x2="-1425.7877" y2="313.9627" gradientTransform="matrix(20.79 0 0 14.7315 29210.5391 -4335.8115)">
|
||||
<stop offset="0" style="stop-color:#75B54B"/>
|
||||
<stop offset="1" style="stop-color:#75B54B;stop-opacity:0.6"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_2_)" d="M-445.4,285.1c-0.5-0.4-1-1-1.8-1.9c-1,1.2-1.9,2.1-2.8,2.7c-1.1,0.7-2.5,1-4.1,1
|
||||
c-2,0-3.6-0.5-4.9-1.6c-1.4-1.1-2.1-2.6-2.1-4.4c0-1.7,0.7-3.2,2.1-4.4c1.2-1,2.9-1.6,5-1.6c1.1,0,2,0.2,2.8,0.5
|
||||
c0.9,0.3,1.7,0.8,2.3,1.4c0.6,0.5,1.2,1.2,1.8,1.9c1-1.2,1.9-2.1,2.8-2.7c1.1-0.7,2.5-1,4.1-1c2,0,3.6,0.5,4.9,1.6
|
||||
c1.4,1.1,2.1,2.6,2.1,4.4c0,1.7-0.7,3.2-2.1,4.4c-1.2,1-2.9,1.6-5,1.6c-1.1,0-2-0.2-2.8-0.5C-443.8,286.2-444.6,285.8-445.4,285.1
|
||||
M-454.3,284.9c2.4,0,4.3-1.2,5.8-3.7c-1.9-2.7-3.8-4.1-5.8-4.1c-1.5,0-2.6,0.4-3.3,1.1c-0.8,0.8-1.2,1.7-1.2,2.8
|
||||
c0,1.2,0.4,2.1,1.2,2.8C-456.8,284.5-455.7,284.9-454.3,284.9 M-440,277.1c-2.2,0-4.1,1.2-5.8,3.7c1.8,2.7,3.8,4.1,5.8,4.1
|
||||
c1.5,0,2.6-0.4,3.3-1.1c0.8-0.8,1.2-1.7,1.2-2.8c0-1.2-0.4-2.1-1.2-2.8C-437.5,277.5-438.6,277.1-440,277.1"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.0 KiB |
@@ -1,61 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
|
||||
<rect fill="#666666" width="32" height="32"/>
|
||||
<polygon fill="#999999" points="0,5 1,5 1,4 4,4 4,5 8,5 8,8 9,8 9,2 12,2 12,5 13,5 13,4 16,4 16,8 17,8 17,6 18,6 18,5 20,5 20,8
|
||||
21,8 21,6 22,6 22,5 24,5 24,4 26,4 26,5 29,5 29,4 32,4 32,0 0,0 "/>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-963.8778" y1="47.5718" x2="-963.0319" y2="49.6501" gradientTransform="matrix(20.79 0 0 14.7315 20048.0879 -696.8257)">
|
||||
<stop offset="0" style="stop-color:#000000;stop-opacity:0.8"/>
|
||||
<stop offset="1" style="stop-color:#000000;stop-opacity:0.35"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_1_)" d="M18.5,21.5c-0.5-0.4-1-1-1.8-1.9c-1,1.2-1.9,2.1-2.8,2.7c-1.1,0.7-2.5,1-4.1,1c-2,0-3.6-0.5-4.9-1.6
|
||||
c-1.4-1.1-2.1-2.6-2.1-4.4c0-1.7,0.7-3.2,2.1-4.4c1.2-1,2.9-1.6,5-1.6c1.1,0,2,0.2,2.8,0.5c0.9,0.3,1.7,0.8,2.3,1.4
|
||||
c0.6,0.5,1.2,1.2,1.8,1.9c1-1.2,1.9-2.1,2.8-2.7c1.1-0.7,2.5-1,4.1-1c2,0,3.6,0.5,4.9,1.6c1.4,1.1,2.1,2.6,2.1,4.4
|
||||
c0,1.7-0.7,3.2-2.1,4.4c-1.2,1-2.9,1.6-5,1.6c-1.1,0-2-0.2-2.8-0.5S19.2,22.1,18.5,21.5 M9.6,21.3c2.4,0,4.3-1.2,5.8-3.7
|
||||
c-1.9-2.7-3.8-4.1-5.8-4.1c-1.5,0-2.6,0.4-3.3,1.1c-0.8,0.8-1.2,1.7-1.2,2.8c0,1.2,0.4,2.1,1.2,2.8C7.1,20.9,8.2,21.3,9.6,21.3
|
||||
M23.9,13.5c-2.2,0-4.1,1.2-5.8,3.7c1.8,2.7,3.8,4.1,5.8,4.1c1.5,0,2.6-0.4,3.3-1.1c0.8-0.8,1.2-1.7,1.2-2.8c0-1.2-0.4-2.1-1.2-2.8
|
||||
C26.3,13.8,25.2,13.5,23.9,13.5"/>
|
||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="-964.0289" y1="48.0195" x2="-962.7877" y2="48.9627" gradientTransform="matrix(20.79 0 0 14.7315 20047.7695 -696.964)">
|
||||
<stop offset="0" style="stop-color:#999999"/>
|
||||
<stop offset="1" style="stop-color:#999999;stop-opacity:0.6"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_2_)" d="M17.6,20.1c-0.5-0.4-1-1-1.8-1.9c-1,1.2-1.9,2.1-2.8,2.7c-1.1,0.7-2.5,1-4.1,1c-2,0-3.6-0.5-4.9-1.6
|
||||
c-1.4-1.1-2.1-2.6-2.1-4.4c0-1.7,0.7-3.2,2.1-4.4c1.2-1,2.9-1.6,5-1.6c1.1,0,2,0.2,2.8,0.5c0.9,0.3,1.7,0.8,2.3,1.4
|
||||
c0.6,0.5,1.2,1.2,1.8,1.9c1-1.2,1.9-2.1,2.8-2.7c1.1-0.7,2.5-1,4.1-1c2,0,3.6,0.5,4.9,1.6c1.4,1.1,2.1,2.6,2.1,4.4
|
||||
c0,1.7-0.7,3.2-2.1,4.4c-1.2,1-2.9,1.6-5,1.6c-1.1,0-2-0.2-2.8-0.5C19.2,21.2,18.4,20.8,17.6,20.1 M8.7,19.9c2.4,0,4.3-1.2,5.8-3.7
|
||||
c-1.9-2.7-3.8-4.1-5.8-4.1c-1.5,0-2.6,0.4-3.3,1.1C4.6,14,4.2,14.9,4.2,16c0,1.2,0.4,2.1,1.2,2.8C6.2,19.5,7.3,19.9,8.7,19.9
|
||||
M23,12.1c-2.2,0-4.1,1.2-5.8,3.7c1.8,2.7,3.8,4.1,5.8,4.1c1.5,0,2.6-0.4,3.3-1.1c0.8-0.8,1.2-1.7,1.2-2.8c0-1.2-0.4-2.1-1.2-2.8
|
||||
C25.5,12.5,24.4,12.1,23,12.1"/>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.9 KiB |
@@ -1,61 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
|
||||
<rect fill="#999999" width="32" height="32"/>
|
||||
<polygon fill="#F2F2F2" points="0,5 1,5 1,4 4,4 4,5 8,5 8,8 9,8 9,2 12,2 12,5 13,5 13,4 16,4 16,8 17,8 17,6 18,6 18,5 20,5 20,8
|
||||
21,8 21,6 22,6 22,5 24,5 24,4 26,4 26,5 29,5 29,4 32,4 32,0 0,0 "/>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="2.8" y1="17.35" x2="30.7" y2="17.35">
|
||||
<stop offset="0" style="stop-color:#000000;stop-opacity:0.8"/>
|
||||
<stop offset="1" style="stop-color:#000000;stop-opacity:0.35"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_1_)" d="M18.5,21.5c-0.5-0.4-1-1-1.8-1.9c-1,1.2-1.9,2.1-2.8,2.7c-1.1,0.7-2.5,1-4.1,1c-2,0-3.6-0.5-4.9-1.6
|
||||
c-1.4-1.1-2.1-2.6-2.1-4.4c0-1.7,0.7-3.2,2.1-4.4c1.2-1,2.9-1.6,5-1.6c1.1,0,2,0.2,2.8,0.5c0.9,0.3,1.7,0.8,2.3,1.4
|
||||
c0.6,0.5,1.2,1.2,1.8,1.9c1-1.2,1.9-2.1,2.8-2.7c1.1-0.7,2.5-1,4.1-1c2,0,3.6,0.5,4.9,1.6c1.4,1.1,2.1,2.6,2.1,4.4
|
||||
c0,1.7-0.7,3.2-2.1,4.4c-1.2,1-2.9,1.6-5,1.6c-1.1,0-2-0.2-2.8-0.5S19.2,22.1,18.5,21.5 M9.6,21.3c2.4,0,4.3-1.2,5.8-3.7
|
||||
c-1.9-2.7-3.8-4.1-5.8-4.1c-1.5,0-2.6,0.4-3.3,1.1c-0.8,0.8-1.2,1.7-1.2,2.8c0,1.2,0.4,2.1,1.2,2.8C7.1,20.9,8.2,21.3,9.6,21.3
|
||||
M23.9,13.5c-2.2,0-4.1,1.2-5.8,3.7c1.8,2.7,3.8,4.1,5.8,4.1c1.5,0,2.6-0.4,3.3-1.1c0.8-0.8,1.2-1.7,1.2-2.8c0-1.2-0.4-2.1-1.2-2.8
|
||||
C26.3,13.8,25.2,13.5,23.9,13.5"/>
|
||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="-964.0289" y1="48.0195" x2="-962.7877" y2="48.9627" gradientTransform="matrix(20.79 0 0 14.7315 20047.7695 -696.964)">
|
||||
<stop offset="0" style="stop-color:#CCCCCC"/>
|
||||
<stop offset="1" style="stop-color:#F2F2F2;stop-opacity:0.6"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_2_)" d="M17.6,20.1c-0.5-0.4-1-1-1.8-1.9c-1,1.2-1.9,2.1-2.8,2.7c-1.1,0.7-2.5,1-4.1,1c-2,0-3.6-0.5-4.9-1.6
|
||||
c-1.4-1.1-2.1-2.6-2.1-4.4c0-1.7,0.7-3.2,2.1-4.4c1.2-1,2.9-1.6,5-1.6c1.1,0,2,0.2,2.8,0.5c0.9,0.3,1.7,0.8,2.3,1.4
|
||||
c0.6,0.5,1.2,1.2,1.8,1.9c1-1.2,1.9-2.1,2.8-2.7c1.1-0.7,2.5-1,4.1-1c2,0,3.6,0.5,4.9,1.6c1.4,1.1,2.1,2.6,2.1,4.4
|
||||
c0,1.7-0.7,3.2-2.1,4.4c-1.2,1-2.9,1.6-5,1.6c-1.1,0-2-0.2-2.8-0.5C19.2,21.2,18.4,20.8,17.6,20.1 M8.7,19.9c2.4,0,4.3-1.2,5.8-3.7
|
||||
c-1.9-2.7-3.8-4.1-5.8-4.1c-1.5,0-2.6,0.4-3.3,1.1C4.6,14,4.2,14.9,4.2,16c0,1.2,0.4,2.1,1.2,2.8C6.2,19.5,7.3,19.9,8.7,19.9
|
||||
M23,12.1c-2.2,0-4.1,1.2-5.8,3.7c1.8,2.7,3.8,4.1,5.8,4.1c1.5,0,2.6-0.4,3.3-1.1c0.8-0.8,1.2-1.7,1.2-2.8c0-1.2-0.4-2.1-1.2-2.8
|
||||
C25.5,12.5,24.4,12.1,23,12.1"/>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.8 KiB |
@@ -1,265 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
version="1.1"
|
||||
id="svg4427"
|
||||
height="68.26667"
|
||||
width="68.26667">
|
||||
<defs
|
||||
id="defs4429">
|
||||
<linearGradient
|
||||
id="linearGradient4809">
|
||||
<stop
|
||||
id="stop4805"
|
||||
offset="0"
|
||||
style="stop-color:#98c867;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop4807"
|
||||
offset="1"
|
||||
style="stop-color:#5c9a33;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5668">
|
||||
<stop
|
||||
style="stop-color:#75b54b;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5670" />
|
||||
<stop
|
||||
style="stop-color:#75b54b;stop-opacity:0.6"
|
||||
offset="1"
|
||||
id="stop5672" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5084">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0.8"
|
||||
offset="0"
|
||||
id="stop5086" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0.35"
|
||||
offset="1"
|
||||
id="stop5088" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="translate(-0.01532073,-0.00938002)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="61.773685"
|
||||
x2="50.506943"
|
||||
y1="28.510933"
|
||||
x1="6.7342591"
|
||||
id="linearGradient5072"
|
||||
xlink:href="#linearGradient5668" />
|
||||
<linearGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="82.973114"
|
||||
x2="44.097023"
|
||||
y1="9.7948904"
|
||||
x1="14.312115"
|
||||
id="linearGradient5082"
|
||||
xlink:href="#linearGradient5084" />
|
||||
<linearGradient
|
||||
y2="61.773685"
|
||||
x2="50.506943"
|
||||
y1="28.510933"
|
||||
x1="6.7342591"
|
||||
gradientTransform="translate(-0.01532073,-0.00938002)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3281"
|
||||
xlink:href="#linearGradient5668" />
|
||||
<linearGradient
|
||||
y2="61.773685"
|
||||
x2="50.506943"
|
||||
y1="28.510933"
|
||||
x1="6.7342591"
|
||||
gradientTransform="translate(-0.01532073,-0.00938002)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3283"
|
||||
xlink:href="#linearGradient5668" />
|
||||
<linearGradient
|
||||
y2="61.773685"
|
||||
x2="50.506943"
|
||||
y1="28.510933"
|
||||
x1="6.7342591"
|
||||
gradientTransform="matrix(1.2671525,0,0,0.89790119,-0.01941371,-0.00842234)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3286"
|
||||
xlink:href="#linearGradient5668" />
|
||||
<linearGradient
|
||||
y2="82.973114"
|
||||
x2="44.097023"
|
||||
y1="9.7948904"
|
||||
x1="14.312115"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3288"
|
||||
xlink:href="#linearGradient5084" />
|
||||
<linearGradient
|
||||
y2="82.973114"
|
||||
x2="44.097023"
|
||||
y1="9.7948904"
|
||||
x1="14.312115"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3290"
|
||||
xlink:href="#linearGradient5084" />
|
||||
<linearGradient
|
||||
gradientTransform="scale(1.2671525,0.89790119)"
|
||||
y2="82.973114"
|
||||
x2="44.097023"
|
||||
y1="9.7948904"
|
||||
x1="14.312115"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3293"
|
||||
xlink:href="#linearGradient5084" />
|
||||
<linearGradient
|
||||
id="linearGradient5580">
|
||||
<stop
|
||||
id="stop5576"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:0.0627451" />
|
||||
<stop
|
||||
id="stop5578"
|
||||
offset="1"
|
||||
style="stop-color:#322217;stop-opacity:0.58823532" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3999">
|
||||
<stop
|
||||
style="stop-color:#a3704b;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3995" />
|
||||
<stop
|
||||
style="stop-color:#6a4a33;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3997" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2727">
|
||||
<stop
|
||||
style="stop-color:#966c4a;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop2723" />
|
||||
<stop
|
||||
style="stop-color:#593d29;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop2725" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="97.065842"
|
||||
x2="86.415741"
|
||||
y1="33.80484"
|
||||
x1="36.546478"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient2050"
|
||||
xlink:href="#linearGradient2727" />
|
||||
<radialGradient
|
||||
r="29.866665"
|
||||
fy="34.133335"
|
||||
fx="34.133331"
|
||||
cy="34.133335"
|
||||
cx="34.133331"
|
||||
gradientTransform="matrix(1.1428572,0,0,1.1428572,-4.8771039,-4.8772393)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient2052"
|
||||
xlink:href="#linearGradient3999" />
|
||||
<linearGradient
|
||||
y2="38.400913"
|
||||
x2="38.400005"
|
||||
y1="29.867579"
|
||||
x1="29.866674"
|
||||
gradientTransform="translate(77.635668,-7.276299)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient2140"
|
||||
xlink:href="#linearGradient5580" />
|
||||
<linearGradient
|
||||
y2="82.973114"
|
||||
x2="44.097023"
|
||||
y1="9.7948904"
|
||||
x1="14.312115"
|
||||
gradientTransform="matrix(1.2671525,0,0,0.89790119,-0.82864077,-1.0012743)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient4790"
|
||||
xlink:href="#linearGradient5084" />
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.7500268,0.1250019,-0.01781176,0.24936465,95.393964,18.110151)"
|
||||
r="34.132812"
|
||||
fy="-34.134373"
|
||||
fx="-42.66758"
|
||||
cy="-34.134373"
|
||||
cx="-42.66758"
|
||||
id="radialGradient4803"
|
||||
xlink:href="#linearGradient4809" />
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata4432">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1">
|
||||
<g
|
||||
transform="translate(9.113e-4,0.00104183)"
|
||||
id="g2048">
|
||||
<rect
|
||||
y="3.0270508e-06"
|
||||
x="-1.3322676e-15"
|
||||
height="68.26667"
|
||||
width="68.26667"
|
||||
id="rect2026"
|
||||
style="fill:url(#linearGradient2050);fill-opacity:1;stroke:none;stroke-width:17.06666756"
|
||||
ry="8.5333338"
|
||||
rx="8.5333338" />
|
||||
<rect
|
||||
ry="0"
|
||||
style="fill:url(#radialGradient2052);fill-opacity:1;stroke:none;stroke-width:17.06666946"
|
||||
id="rect2028"
|
||||
width="68.26667"
|
||||
height="68.26667"
|
||||
x="-0.00091432704"
|
||||
y="-0.0010418301"
|
||||
rx="0" />
|
||||
<path
|
||||
style="opacity:0.6;fill:#593d29;fill-opacity:1;stroke:none;stroke-width:17.06666756"
|
||||
d="m 4.2669272,4.2645856 -9.11e-4,8.5333334 h 4.267577 v 4.267579 h 8.5332038 v 4.265625 h 4.265625 V 8.5322946 H 25.6 v 8.5332034 h 4.265625 v -4.267579 h 4.267578 v 8.533204 h 4.265625 v -4.265625 h 4.267578 v 4.265625 h 4.267579 v -4.265625 h 4.265624 v -4.267579 h 4.267579 v 4.267579 h 8.533203 l -1.3e-4,-12.8009124 z"
|
||||
id="path4811" />
|
||||
<path
|
||||
id="path4794"
|
||||
d="m -9.113e-4,-0.0010388 6.52e-4,8.5324211 v 4.2675787 h 4.265625 V 8.5313823 c 0,-0.521698 0.105433,-1.01339 0.27539,-1.47461 -0.169616,0.460814 -0.27539,0.953462 -0.27539,1.47461 h 4.2675785 v 4.2675787 h 4.2656248 4.267578 v 4.265625 h 4.265625 V 12.798961 8.5313823 4.2657573 h 4.267578 v 4.265625 4.2675787 h 4.265625 V 8.5313823 h 4.267578 v 4.2675787 4.265625 h 4.265625 v -4.265625 h 4.267578 v 4.265625 h 4.267579 v -4.265625 h 4.265624 V 8.5313823 h 4.267579 v 4.2675787 h 4.265625 4.267578 V 8.5313823 h 4.265625 l 3.9e-4,-8.5324211 z m 5.5143245,5.5128891 c -0.318089,0.317888 -0.570428,0.695824 -0.7753915,1.101563 0.2048795,-0.405231 0.4576385,-0.784012 0.7753915,-1.101563 z"
|
||||
style="fill:url(#radialGradient4803);fill-opacity:1;stroke:none;stroke-width:17.06666756" />
|
||||
</g>
|
||||
<g
|
||||
id="g1092">
|
||||
<path
|
||||
d="m 38.886673,44.940882 c -0.974277,-0.801673 -2.231353,-2.137814 -3.771231,-4.008427 -2.105641,2.672298 -4.085536,4.598569 -5.939688,5.778816 -2.325625,1.425227 -5.295467,2.137836 -8.909534,2.137828 -4.242656,8e-6 -7.762467,-1.124578 -10.5594458,-3.37376 C 6.7526311,43.114834 5.275567,39.986037 5.2755773,36.088937 5.275567,32.347763 6.7526311,29.207831 9.7067742,26.669132 12.346618,24.419991 15.897857,23.295407 20.360501,23.295373 c 2.294138,3.4e-5 4.289747,0.334069 5.986829,1.002107 1.979863,0.73491 3.645488,1.737016 4.996881,3.00632 1.257039,1.135751 2.514115,2.471891 3.771231,4.008428 2.105563,-2.672257 4.085457,-4.598527 5.939689,-5.778816 2.325544,-1.425186 5.295385,-2.137794 8.909533,-2.137828 4.242577,3.4e-5 7.762388,1.124618 10.559447,3.37376 2.954063,2.360546 4.431127,5.489343 4.431197,9.386401 -7e-5,3.741216 -1.477134,6.881147 -4.431197,9.419806 -2.639925,2.24918 -6.191163,3.373767 -10.653727,3.373758 -2.294219,9e-6 -4.289826,-0.334026 -5.98683,-1.002106 -1.697101,-0.601255 -3.362726,-1.603361 -4.996881,-3.006321 M 19.747676,44.473233 c 5.185412,1.1e-5 9.333763,-2.672271 12.445062,-8.016856 -3.991253,-5.834464 -8.139602,-8.751705 -12.445062,-8.751733 -3.142715,2.8e-5 -5.515446,0.801713 -7.118198,2.405057 -1.728498,1.71474 -2.592737,3.707818 -2.592722,5.979236 -1.5e-5,2.494152 0.864224,4.509499 2.592722,6.046046 1.759887,1.558846 4.132618,2.338261 7.118198,2.33825 M 50.483209,27.77145 c -4.682663,2.9e-5 -8.831013,2.672312 -12.445062,8.016856 3.959745,5.834503 8.108095,8.751746 12.445062,8.751733 3.142633,1.3e-5 5.515364,-0.801671 7.118198,-2.405056 1.728416,-1.714701 2.592656,-3.707778 2.592722,-5.979238 -6.6e-5,-2.49411 -0.864306,-4.509456 -2.592722,-6.046044 -1.759968,-1.558805 -4.132699,-2.338222 -7.118198,-2.338251"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:76.18933868px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient4790);fill-opacity:1;stroke:none;stroke-width:1.06666672;opacity:0.5"
|
||||
id="path4786" />
|
||||
<path
|
||||
id="path3279"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:76.18933868px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient3293);fill-opacity:1;stroke:none;stroke-width:1.06666672;opacity:0.5"
|
||||
d="m 39.715314,45.942156 c -0.974277,-0.801673 -2.231353,-2.137814 -3.771231,-4.008427 -2.105641,2.672298 -4.085536,4.598569 -5.939688,5.778816 -2.325625,1.425227 -5.295467,2.137836 -8.909534,2.137828 -4.242656,8e-6 -7.762467,-1.124578 -10.559446,-3.37376 -2.9541431,-2.360505 -4.4312072,-5.489302 -4.4311969,-9.386402 -1.03e-5,-3.741174 1.4770538,-6.881106 4.4311969,-9.419805 2.639844,-2.249141 6.191083,-3.373725 10.653727,-3.373759 2.294138,3.4e-5 4.289747,0.334069 5.986829,1.002107 1.979863,0.73491 3.645488,1.737016 4.996881,3.00632 1.257039,1.135751 2.514115,2.471891 3.771231,4.008428 2.105563,-2.672257 4.085457,-4.598527 5.939689,-5.778816 2.325544,-1.425186 5.295385,-2.137794 8.909533,-2.137828 4.242577,3.4e-5 7.762388,1.124618 10.559447,3.37376 2.954063,2.360546 4.431127,5.489343 4.431197,9.386401 -7e-5,3.741216 -1.477134,6.881147 -4.431197,9.419806 -2.639925,2.24918 -6.191163,3.373767 -10.653727,3.373758 -2.294219,9e-6 -4.289826,-0.334026 -5.98683,-1.002106 -1.697101,-0.601255 -3.362726,-1.603361 -4.996881,-3.006321 M 20.576317,45.474507 c 5.185412,1.1e-5 9.333763,-2.672271 12.445062,-8.016856 -3.991253,-5.834464 -8.139602,-8.751705 -12.445062,-8.751733 -3.142715,2.8e-5 -5.515446,0.801713 -7.118198,2.405057 -1.728498,1.71474 -2.592737,3.707818 -2.592722,5.979236 -1.5e-5,2.494152 0.864224,4.509499 2.592722,6.046046 1.759887,1.558846 4.132618,2.338261 7.118198,2.33825 M 51.31185,28.772724 c -4.682663,2.9e-5 -8.831013,2.672312 -12.445062,8.016856 3.959745,5.834503 8.108095,8.751746 12.445062,8.751733 3.142633,1.3e-5 5.515364,-0.801671 7.118198,-2.405056 1.728416,-1.714701 2.592656,-3.707778 2.592722,-5.979238 -6.6e-5,-2.49411 -0.864306,-4.509456 -2.592722,-6.046044 C 56.67008,29.55217 54.297349,28.772753 51.31185,28.772724" />
|
||||
<path
|
||||
id="path3272"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:76.18933868px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient3286);fill-opacity:1;stroke:none;stroke-width:1.06666672"
|
||||
d="m 37.904564,42.951873 c -0.974278,-0.801672 -2.231352,-2.137814 -3.771231,-4.008428 -2.105642,2.672298 -4.085537,4.598568 -5.939688,5.778817 -2.325625,1.425227 -5.295466,2.137836 -8.909534,2.137828 -4.242656,8e-6 -7.762467,-1.124577 -10.5594464,-3.37376 -2.9541428,-2.360505 -4.4312068,-5.489302 -4.4311963,-9.386401 -1.05e-5,-3.741175 1.4770535,-6.881107 4.4311963,-9.419805 2.6398444,-2.249142 6.1910824,-3.373727 10.6537284,-3.37376 2.294137,3.3e-5 4.289745,0.334068 5.986829,1.002107 1.979863,0.734909 3.645487,1.737016 4.99688,3.00632 1.257039,1.13575 2.514116,2.471891 3.771231,4.008428 2.105562,-2.672257 4.085456,-4.598528 5.939689,-5.778817 2.325544,-1.425185 5.295387,-2.137795 8.909534,-2.137828 4.242576,3.3e-5 7.762387,1.12462 10.559446,3.373761 2.954062,2.360545 4.431127,5.489343 4.431197,9.386401 -7e-5,3.741216 -1.477135,6.881148 -4.431197,9.419805 -2.639924,2.249182 -6.191164,3.373767 -10.653728,3.37376 -2.294217,7e-6 -4.289826,-0.334028 -5.986828,-1.002107 -1.697101,-0.601254 -3.362727,-1.603361 -4.996882,-3.006321 m -19.138997,-0.46765 c 5.185412,1.3e-5 9.333762,-2.67227 12.445062,-8.016856 -3.991252,-5.834462 -8.139602,-8.751704 -12.445062,-8.751733 -3.142714,2.9e-5 -5.515444,0.801714 -7.118198,2.405056 -1.7284972,1.714743 -2.5927368,3.707819 -2.5927216,5.979239 -1.52e-5,2.49415 0.8642244,4.509496 2.5927216,6.046045 1.759888,1.558845 4.132618,2.338262 7.118198,2.338249 M 49.5011,25.782442 c -4.682663,2.8e-5 -8.831014,2.672311 -12.445063,8.016855 3.959745,5.834504 8.108096,8.751745 12.445063,8.751733 3.142634,1.2e-5 5.515365,-0.801673 7.118198,-2.405056 1.728417,-1.7147 2.592657,-3.707778 2.592721,-5.979238 -6.4e-5,-2.49411 -0.864304,-4.509456 -2.592721,-6.046046 C 54.85933,26.561886 52.486599,25.78247 49.5011,25.782442" />
|
||||
<path
|
||||
style="font-style:normal;font-weight:normal;font-size:76.18933868px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;opacity:0.3;fill:#ccff00;fill-opacity:1;stroke:none;stroke-width:1.06666672"
|
||||
d="m 19.4,21.166667 c -4.462644,3.3e-5 -8.026822,1.150858 -10.6666667,3.4 -2.9541428,2.538698 -4.4333436,5.658825 -4.4333333,9.4 -1.03e-5,3.897098 1.4791905,7.039495 4.4333333,9.4 -1.622701,-2.044271 -2.433341,-4.51168 -2.4333333,-7.4 -1.03e-5,-3.741175 1.4791905,-6.861302 4.433333,-9.4 2.639845,-2.249142 6.204023,-3.399967 10.666667,-3.4 2.294138,3.3e-5 4.302916,0.365295 6,1.033333 1.979862,0.73491 3.615274,1.730695 4.966667,3 0.06836,0.06177 0.131637,0.137049 0.2,0.2 -0.731813,-0.797005 -1.468213,-1.538822 -2.2,-2.2 -1.351393,-1.269305 -2.986805,-2.26509 -4.966667,-3 -1.697084,-0.668038 -3.705862,-1.0333 -6,-1.033333 z m 29.6,0.1 c -3.614148,3.3e-5 -6.574457,0.74148 -8.9,2.166666 -1.818222,1.157367 -3.923451,3.291388 -5.983333,5.883334 0.618278,0.658774 1.248369,1.377605 1.866666,2.133333 2.105562,-2.672257 4.262434,-4.836378 6.116667,-6.016667 2.325543,-1.425186 5.285852,-2.166633 8.9,-2.166666 4.242576,3.3e-5 7.769607,1.150858 10.566667,3.4 -0.570388,-0.722129 -1.227721,-1.382884 -2,-2 C 56.769607,22.417525 53.242576,21.2667 49,21.266667 Z m 8.866667,8.1 c 0.9092,1.305235 1.366619,2.857751 1.366666,4.666666 -6.5e-5,2.271461 -0.871584,4.285301 -2.6,6 -1.602834,1.603384 -3.957366,2.400012 -7.1,2.4 -2.653707,8e-6 -5.320858,-1.032242 -7.833333,-3.216666 3.136636,3.509305 6.469807,5.216676 9.833333,5.216666 3.142634,1.2e-5 5.497166,-0.796616 7.1,-2.4 1.728416,-1.714699 2.599935,-3.728539 2.6,-6 -6.5e-5,-2.49411 -0.871584,-4.496744 -2.6,-6.033333 -0.24943,-0.220921 -0.49262,-0.443723 -0.766666,-0.633333 z m -26.633334,4.966666 c -3.1113,5.344585 -7.247921,8.033345 -12.433333,8.033334 -2.58055,1e-5 -4.543473,-0.352086 -6.208333,-1.516667 0.348871,0.50642 0.590094,0.752276 1.075,1.183333 1.759888,1.558846 4.147753,2.333345 7.133333,2.333334 5.185412,1.1e-5 9.322033,-2.688749 12.433333,-8.033334 z m 4.933334,6.5 c -0.04103,0.05207 -0.09239,0.08182 -0.133334,0.133334 0.687326,0.744419 1.306949,1.359747 1.833334,1.8 -0.529404,-0.580895 -1.078447,-1.178283 -1.7,-1.933334 z"
|
||||
id="text5100" />
|
||||
<path
|
||||
style="font-style:normal;font-weight:normal;font-size:76.18933868px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;opacity:0.6;fill:#ccff00;fill-opacity:1;stroke:none;stroke-width:1.06666672"
|
||||
d="m 19.730474,21.54714 c -4.462645,3.3e-5 -8.026823,1.150859 -10.6666669,3.4 -2.9541429,2.538699 -4.433344,5.658826 -4.4333333,9.4 -1.07e-5,3.897099 1.4791904,7.039495 4.4333333,9.4 0.042837,0.03444 0.090155,0.06608 0.1333334,0.1 -2.2392086,-2.228193 -3.3666752,-5.040417 -3.3666667,-8.433333 -1.07e-5,-3.741174 1.4791904,-6.861301 4.4333332,-9.4 2.639844,-2.249141 6.204022,-3.399967 10.666667,-3.4 2.294137,3.3e-5 4.302916,0.365295 6,1.033333 1.870874,0.694455 3.42364,1.628367 4.733333,2.8 -0.314265,-0.308986 -0.652406,-0.582729 -0.966667,-0.866666 -1.351393,-1.269305 -2.986804,-2.265091 -4.966666,-3 -1.697084,-0.668039 -3.705863,-1.033301 -6,-1.033334 z m 29.6,0.1 c -3.614149,3.3e-5 -6.574457,0.741481 -8.9,2.166667 -1.813279,1.154221 -3.963039,3.235656 -6.016667,5.816667 0.355649,0.402628 0.711011,0.798625 1.066667,1.233333 2.105561,-2.672257 4.295767,-4.803044 6.15,-5.983333 2.325543,-1.425187 5.285851,-2.166634 8.9,-2.166667 4.22442,3.3e-5 7.742084,1.136734 10.533333,3.366667 -0.36096,-0.367566 -0.745726,-0.696967 -1.166667,-1.033334 -2.797059,-2.249141 -6.32409,-3.399967 -10.566666,-3.4 z m 8.233333,7.333334 c 1.323326,1.449243 1.999942,3.250987 2,5.433333 -6.5e-5,2.27146 -0.871584,4.2853 -2.6,6 -1.602834,1.603383 -3.957366,2.400012 -7.1,2.4 -2.406328,6e-6 -4.776468,-0.90386 -7.066667,-2.7 2.669147,2.483838 5.436929,3.766674 8.266667,3.766667 3.142634,1.1e-5 5.497166,-0.796617 7.1,-2.4 1.728416,-1.7147 2.599935,-3.72854 2.6,-6 -6.5e-5,-2.49411 -0.871584,-4.496745 -2.6,-6.033334 -0.185641,-0.164422 -0.400724,-0.319587 -0.6,-0.466666 z m -26,5.733333 c -3.1113,5.344584 -7.247921,8.033345 -12.433333,8.033333 -2.612382,1.1e-5 -4.759372,-0.60651 -6.433334,-1.8 0.166027,0.176488 0.313947,0.367942 0.5,0.533334 1.759888,1.558845 4.147754,2.333345 7.133334,2.333333 5.185412,1.2e-5 9.322033,-2.688749 12.433333,-8.033333 z m 4.133333,5.566667 c -0.04657,0.05909 -0.08689,0.108298 -0.133333,0.166666 1.038571,1.18897 1.9748,2.169945 2.7,2.766667 0.06249,0.05364 0.137426,0.08086 0.2,0.133333 -0.792178,-0.781249 -1.706288,-1.778539 -2.766667,-3.066666 z"
|
||||
id="text5058-0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 19 KiB |
@@ -1,179 +0,0 @@
|
||||
#include "ServerStatus.h"
|
||||
#include "LineSeparator.h"
|
||||
#include "IconLabel.h"
|
||||
#include "status/StatusChecker.h"
|
||||
#include <DesktopServices.h>
|
||||
|
||||
#include "MultiMC.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QFrame>
|
||||
#include <QLabel>
|
||||
#include <QMap>
|
||||
#include <QToolButton>
|
||||
#include <QAction>
|
||||
|
||||
class ClickableLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ClickableLabel(QWidget *parent) : QLabel(parent)
|
||||
{
|
||||
setCursor(Qt::PointingHandCursor);
|
||||
}
|
||||
|
||||
~ClickableLabel(){};
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
emit clicked();
|
||||
}
|
||||
};
|
||||
|
||||
class ClickableIconLabel : public IconLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ClickableIconLabel(QWidget *parent, QIcon icon, QSize size) : IconLabel(parent, icon, size)
|
||||
{
|
||||
setCursor(Qt::PointingHandCursor);
|
||||
}
|
||||
|
||||
~ClickableIconLabel(){};
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
emit clicked();
|
||||
}
|
||||
};
|
||||
|
||||
ServerStatus::ServerStatus(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f)
|
||||
{
|
||||
layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
goodIcon = MMC->getThemedIcon("status-good");
|
||||
yellowIcon = MMC->getThemedIcon("status-yellow");
|
||||
badIcon = MMC->getThemedIcon("status-bad");
|
||||
|
||||
addStatus("authserver.mojang.com", tr("Auth"));
|
||||
addLine();
|
||||
addStatus("session.minecraft.net", tr("Session"));
|
||||
addLine();
|
||||
addStatus("textures.minecraft.net", tr("Skins"));
|
||||
addLine();
|
||||
addStatus("api.mojang.com", tr("API"));
|
||||
|
||||
m_statusRefresh = new QToolButton(this);
|
||||
m_statusRefresh->setCheckable(true);
|
||||
m_statusRefresh->setToolButtonStyle(Qt::ToolButtonIconOnly);
|
||||
m_statusRefresh->setIcon(MMC->getThemedIcon("refresh"));
|
||||
layout->addWidget(m_statusRefresh);
|
||||
|
||||
setLayout(layout);
|
||||
|
||||
// Start status checker
|
||||
m_statusChecker.reset(new StatusChecker());
|
||||
{
|
||||
auto reloader = m_statusChecker.get();
|
||||
connect(reloader, &StatusChecker::statusChanged, this, &ServerStatus::StatusChanged);
|
||||
connect(reloader, &StatusChecker::statusLoading, this, &ServerStatus::StatusReloading);
|
||||
connect(m_statusRefresh, &QAbstractButton::clicked, this, &ServerStatus::reloadStatus);
|
||||
m_statusChecker->startTimer(60000);
|
||||
reloadStatus();
|
||||
}
|
||||
}
|
||||
|
||||
ServerStatus::~ServerStatus()
|
||||
{
|
||||
}
|
||||
|
||||
void ServerStatus::reloadStatus()
|
||||
{
|
||||
m_statusChecker->reloadStatus();
|
||||
}
|
||||
|
||||
void ServerStatus::addLine()
|
||||
{
|
||||
layout->addWidget(new LineSeparator(this, Qt::Vertical));
|
||||
}
|
||||
|
||||
void ServerStatus::addStatus(QString key, QString name)
|
||||
{
|
||||
{
|
||||
auto label = new ClickableIconLabel(this, badIcon, QSize(16, 16));
|
||||
label->setToolTip(key);
|
||||
serverLabels[key] = label;
|
||||
layout->addWidget(label);
|
||||
connect(label,SIGNAL(clicked()),SLOT(clicked()));
|
||||
}
|
||||
{
|
||||
auto label = new ClickableLabel(this);
|
||||
label->setText(name);
|
||||
label->setToolTip(key);
|
||||
layout->addWidget(label);
|
||||
connect(label,SIGNAL(clicked()),SLOT(clicked()));
|
||||
}
|
||||
}
|
||||
|
||||
void ServerStatus::clicked()
|
||||
{
|
||||
DesktopServices::openUrl(QUrl("https://github.com/MultiMC/MultiMC5/wiki/Mojang-Services-Status"));
|
||||
}
|
||||
|
||||
void ServerStatus::setStatus(QString key, int value)
|
||||
{
|
||||
if (!serverLabels.contains(key))
|
||||
return;
|
||||
IconLabel *label = serverLabels[key];
|
||||
switch(value)
|
||||
{
|
||||
case 0:
|
||||
label->setIcon(goodIcon);
|
||||
break;
|
||||
case 1:
|
||||
label->setIcon(yellowIcon);
|
||||
break;
|
||||
default:
|
||||
case 2:
|
||||
label->setIcon(badIcon);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ServerStatus::StatusChanged(const QMap<QString, QString> statusEntries)
|
||||
{
|
||||
auto convertStatus = [&](QString status)->int
|
||||
{
|
||||
if (status == "green")
|
||||
return 0;
|
||||
else if (status == "yellow")
|
||||
return 1;
|
||||
else if (status == "red")
|
||||
return 2;
|
||||
return 2;
|
||||
}
|
||||
;
|
||||
auto iter = statusEntries.begin();
|
||||
while (iter != statusEntries.end())
|
||||
{
|
||||
QString key = iter.key();
|
||||
auto value = convertStatus(iter.value());
|
||||
setStatus(key, value);
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
void ServerStatus::StatusReloading(bool is_reloading)
|
||||
{
|
||||
m_statusRefresh->setChecked(is_reloading);
|
||||
}
|
||||
|
||||
#include "ServerStatus.moc"
|
||||
@@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
#include <QMap>
|
||||
#include <QIcon>
|
||||
#include <memory>
|
||||
|
||||
class IconLabel;
|
||||
class QToolButton;
|
||||
class QHBoxLayout;
|
||||
class StatusChecker;
|
||||
|
||||
class ServerStatus: public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ServerStatus(QWidget *parent = nullptr, Qt::WindowFlags f = 0);
|
||||
virtual ~ServerStatus();
|
||||
|
||||
public slots:
|
||||
void reloadStatus();
|
||||
void StatusChanged(const QMap<QString, QString> statuses);
|
||||
void StatusReloading(bool is_reloading);
|
||||
|
||||
private slots:
|
||||
void clicked();
|
||||
|
||||
private: /* methods */
|
||||
void addLine();
|
||||
void addStatus(QString key, QString name);
|
||||
void setStatus(QString key, int value);
|
||||
private: /* data */
|
||||
QHBoxLayout * layout = nullptr;
|
||||
QToolButton *m_statusRefresh = nullptr;
|
||||
QMap<QString, IconLabel *> serverLabels;
|
||||
QIcon goodIcon;
|
||||
QIcon yellowIcon;
|
||||
QIcon badIcon;
|
||||
std::shared_ptr<StatusChecker> m_statusChecker;
|
||||
};
|
||||
@@ -5,21 +5,32 @@ const Config BuildConfig;
|
||||
|
||||
Config::Config()
|
||||
{
|
||||
// Name and copyright
|
||||
LAUNCHER_NAME = "@Launcher_Name@";
|
||||
LAUNCHER_DISPLAYNAME = "@Launcher_DisplayName@";
|
||||
LAUNCHER_COPYRIGHT = "@Launcher_Copyright@";
|
||||
LAUNCHER_DOMAIN = "@Launcher_Domain@";
|
||||
LAUNCHER_CONFIGFILE = "@Launcher_ConfigFile@";
|
||||
LAUNCHER_GIT = "@Launcher_Git@";
|
||||
|
||||
USER_AGENT = "@Launcher_UserAgent@";
|
||||
USER_AGENT_UNCACHED = USER_AGENT + " (Uncached)";
|
||||
|
||||
// Version information
|
||||
VERSION_MAJOR = @MultiMC_VERSION_MAJOR@;
|
||||
VERSION_MINOR = @MultiMC_VERSION_MINOR@;
|
||||
VERSION_HOTFIX = @MultiMC_VERSION_HOTFIX@;
|
||||
VERSION_BUILD = @MultiMC_VERSION_BUILD@;
|
||||
VERSION_MAJOR = @Launcher_VERSION_MAJOR@;
|
||||
VERSION_MINOR = @Launcher_VERSION_MINOR@;
|
||||
VERSION_HOTFIX = @Launcher_VERSION_HOTFIX@;
|
||||
VERSION_BUILD = @Launcher_VERSION_BUILD@;
|
||||
|
||||
BUILD_PLATFORM = "@MultiMC_BUILD_PLATFORM@";
|
||||
CHANLIST_URL = "@MultiMC_CHANLIST_URL@";
|
||||
ANALYTICS_ID = "@MultiMC_ANALYTICS_ID@";
|
||||
NOTIFICATION_URL = "@MultiMC_NOTIFICATION_URL@";
|
||||
FULL_VERSION_STR = "@MultiMC_VERSION_MAJOR@.@MultiMC_VERSION_MINOR@.@MultiMC_VERSION_BUILD@";
|
||||
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
|
||||
UPDATER_BASE = "@Launcher_UPDATER_BASE@";
|
||||
ANALYTICS_ID = "@Launcher_ANALYTICS_ID@";
|
||||
NOTIFICATION_URL = "@Launcher_NOTIFICATION_URL@";
|
||||
FULL_VERSION_STR = "@Launcher_VERSION_MAJOR@.@Launcher_VERSION_MINOR@.@Launcher_VERSION_BUILD@";
|
||||
|
||||
GIT_COMMIT = "@MultiMC_GIT_COMMIT@";
|
||||
GIT_REFSPEC = "@MultiMC_GIT_REFSPEC@";
|
||||
if(GIT_REFSPEC.startsWith("refs/heads/") && !CHANLIST_URL.isEmpty() && VERSION_BUILD >= 0)
|
||||
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)
|
||||
{
|
||||
VERSION_CHANNEL = GIT_REFSPEC;
|
||||
VERSION_CHANNEL.remove("refs/heads/");
|
||||
@@ -30,10 +41,15 @@ Config::Config()
|
||||
VERSION_CHANNEL = QObject::tr("custom");
|
||||
}
|
||||
|
||||
VERSION_STR = "@MultiMC_VERSION_STRING@";
|
||||
NEWS_RSS_URL = "@MultiMC_NEWS_RSS_URL@";
|
||||
PASTE_EE_KEY = "@MultiMC_PASTE_EE_API_KEY@";
|
||||
META_URL = "@MultiMC_META_URL@";
|
||||
VERSION_STR = "@Launcher_VERSION_STRING@";
|
||||
NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
|
||||
PASTE_EE_KEY = "@Launcher_PASTE_EE_API_KEY@";
|
||||
IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
|
||||
META_URL = "@Launcher_META_URL@";
|
||||
|
||||
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
|
||||
DISCORD_URL = "@Launcher_DISCORD_URL@";
|
||||
SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@";
|
||||
}
|
||||
|
||||
QString Config::printableVersionString() const
|
||||
|
||||
@@ -8,6 +8,13 @@ class Config
|
||||
{
|
||||
public:
|
||||
Config();
|
||||
QString LAUNCHER_NAME;
|
||||
QString LAUNCHER_DISPLAYNAME;
|
||||
QString LAUNCHER_COPYRIGHT;
|
||||
QString LAUNCHER_DOMAIN;
|
||||
QString LAUNCHER_CONFIGFILE;
|
||||
QString LAUNCHER_GIT;
|
||||
|
||||
/// The major version number.
|
||||
int VERSION_MAJOR;
|
||||
/// The minor version number.
|
||||
@@ -29,7 +36,15 @@ public:
|
||||
QString BUILD_PLATFORM;
|
||||
|
||||
/// URL for the updater's channel
|
||||
QString CHANLIST_URL;
|
||||
QString UPDATER_BASE;
|
||||
|
||||
|
||||
/// User-Agent to use.
|
||||
QString USER_AGENT;
|
||||
|
||||
/// User-Agent to use for uncached requests.
|
||||
QString USER_AGENT_UNCACHED;
|
||||
|
||||
|
||||
/// Google analytics ID
|
||||
QString ANALYTICS_ID;
|
||||
@@ -61,18 +76,24 @@ public:
|
||||
QString PASTE_EE_KEY;
|
||||
|
||||
/**
|
||||
* MultiMC Metadata repository URL prefix
|
||||
* Client ID you can get from Imgur when you register an application
|
||||
*/
|
||||
QString IMGUR_CLIENT_ID;
|
||||
|
||||
/**
|
||||
* Metadata repository URL prefix
|
||||
*/
|
||||
QString META_URL;
|
||||
|
||||
QString BUG_TRACKER_URL;
|
||||
QString DISCORD_URL;
|
||||
QString SUBREDDIT_URL;
|
||||
|
||||
QString RESOURCE_BASE = "https://resources.download.minecraft.net/";
|
||||
QString LIBRARY_BASE = "https://libraries.minecraft.net/";
|
||||
QString SKINS_BASE = "https://crafatar.com/skins/";
|
||||
QString AUTH_BASE = "https://authserver.mojang.com/";
|
||||
QString MOJANG_STATUS_URL = "https://status.mojang.com/check";
|
||||
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
|
||||
QString FMLLIBS_OUR_BASE_URL = "https://files.multimc.org/fmllibs/";
|
||||
QString FMLLIBS_FORGE_BASE_URL = "https://files.minecraftforge.net/fmllibs/";
|
||||
QString FMLLIBS_BASE_URL = "https://files.multimc.org/fmllibs/";
|
||||
QString TRANSLATIONS_BASE_URL = "https://files.multimc.org/translations/";
|
||||
|
||||
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
|
||||
|
||||
243
changelog.md
@@ -1,19 +1,228 @@
|
||||
# MultiMC 0.6.12
|
||||
# MultiMC 0.6.15
|
||||
|
||||
This is mostly a bugfix release.
|
||||
|
||||
#### Accounts
|
||||
|
||||
- Scan for changes to Minecraft entitlements on account refresh (to make sure we detect changes to entitlements).
|
||||
- Add some more logging to interaction with Minecraft services and authentication (profile fetching, etc.).
|
||||
- Add support for demo mode for accounts that don't have a Minecraft profile.
|
||||
|
||||
#### UI
|
||||
|
||||
- Fixed how 'español de Latinoamérica' appears in the language selector.
|
||||
- Added a way to open loader mods for an instance from the main window.
|
||||
- Improved error messages when the data folder cannot be accessed correctly.
|
||||
- Fixed window title of the Java checker failure dialog.
|
||||
- Updated layout of various modpack import pages to be more consistent and better in general.
|
||||
|
||||
#### Java
|
||||
|
||||
- GH-4000: add detection for Adoptium Java runtimes.
|
||||
- Changed minimum Java version to 7 - it was too hard to maintain compatibility with 6 in development environments.
|
||||
- GH-4125: Add a workaround for Java interacting badly with some features of Bedrock Linux.
|
||||
|
||||
#### Screenshots
|
||||
|
||||
- GH-4299: Fix crash when uploading screenshots.
|
||||
- Added the capability to copypaste screenshots as raw images (to image editors) and files (to file explorers).
|
||||
|
||||
# Previous releases
|
||||
|
||||
## MultiMC 0.6.14
|
||||
|
||||
This further refines Microsoft account support, along with small fixes related to modpack platforms and Java runtime detection.
|
||||
|
||||
It's also been 10 years since the first release of MultiMC. All background cats are now ready to party!
|
||||
|
||||
#### Microsoft accounts
|
||||
|
||||
The account system now refreshes accounts in the background while the application is running.
|
||||
|
||||
- GH-4071: Errors encountered while refreshing account tokens no longer always result in the tokens expiring:
|
||||
- Network errors encountered when refreshing the main account tokens result in the account being **Offline**.
|
||||
- **Hard** errors are produced by the main tokens becoming provably invalid.
|
||||
- Errors encountered later are treated as **Soft** - they do make the account unusable, but still recoverable by trying again.
|
||||
- **Soft** errors are treated as **Hard** errors when adding the account initially.
|
||||
|
||||
In general, this should make MultiMC much more forgiving towards various temporary and non-fatal errors.
|
||||
|
||||
- GH-4217: Added support for GamePass accounts and Minecraft profile setup:
|
||||
- The new endpoint for logging in with Microsoft is now used (`/launcher/login`), enabling compatibility with GamePass.
|
||||
- Game ownership is checked instead of only relying on Minecraft profile presence.
|
||||
- Accounts can now be added even when they do not have a profile.
|
||||
- The launcher should guide you through selecting a Minecraft name if you don't have one yet.
|
||||
|
||||
#### Modpack platform changes
|
||||
|
||||
- GH-4055: MultiMC now tries to avoid downloading multiple files to the same path for FTB modpacks.
|
||||
|
||||
- Search as you type is now used for FTB.
|
||||
|
||||
- GH-4185: Version of the modpack is now included in the name of the instance by default.
|
||||
|
||||
- The modpack platform UIs now include text field clear buttons.
|
||||
|
||||
#### Other changes
|
||||
|
||||
- Adjusted warnings about Java runtime required for Minecraft 1.18 (it's not Java 16, it's Java 17).
|
||||
|
||||
- GH-3490: Instance sorting is now aware of numbers (and sorts 99 before 100).
|
||||
|
||||
- GH-4164: Reimplemented assigning instances to groups using drag & drop.
|
||||
|
||||
- GH-1795: Added terminal launch option to use a specific Minecraft profile (in-game player name).
|
||||
|
||||
Used like this:
|
||||
```
|
||||
./MultiMC --launch 1.17.1 --profile MultiMCTest --server mc.hypixel.net
|
||||
```
|
||||
|
||||
- GH-4227: Fix crash related to invalid Forge mod metadata.
|
||||
|
||||
- GH-4200: Search for the *Eclipse Foundation* and *Adoptium* Java runtimes in the Windows Registry.
|
||||
|
||||
- Added shader packs page to instances.
|
||||
|
||||
- Removed Mojang services status information from the main window - the status is no longer provided by Mojang.
|
||||
|
||||
- It is now possible to turn of global tracking of play time.
|
||||
|
||||
#### Technical changes
|
||||
|
||||
- Debranding is mostly finished. You may see some changes in the logo being used in less places.
|
||||
|
||||
## MultiMC 0.6.13
|
||||
|
||||
This release brings initial support for Microsoft accounts, along with a nice pile of modpack platform support changes and improved Java runtime detection.
|
||||
|
||||
Java runtimes still need an overhaul, so we're staying on the 0.6 version for a little longer.
|
||||
|
||||
Next release should also tackle the current Forge 1.17.x issues in a systematic way.
|
||||
|
||||
#### Microsoft accounts
|
||||
|
||||
This is the first release with Microsoft accounts in.
|
||||
|
||||
Implementation is loosely based on documentation available from [wiki.vg](https://wiki.vg/Microsoft_Authentication_Scheme) with some notable changes:
|
||||
|
||||
- More complete implementation including getting and displaying GamerTags [(see XR-046)](https://docs.microsoft.com/en-us/gaming/gdk/_content/gc/policies/pc/live-policies-pc#xr-046-display-name-and-gamerpic-).
|
||||
|
||||
- Using the OAuth Device Flow instead of closely integrating with a browser engine.
|
||||
|
||||
MultiMC asks you to open a Microsoft login web page and put in a code that lets MultiMC authenticate.
|
||||
|
||||
This lets you authenticate on a completely separate device like your phone, leaving code we ship and the computer you may not even trust out of the picture.
|
||||
|
||||
As part of this, the skin fetching no longer uses a third party service and instead gets skins directly from Mojang.
|
||||
|
||||
Capes can also be selected in MultiMC now. With how many people will now get one for migrating their accounts, it only makes sense.
|
||||
|
||||
#### macOS update
|
||||
|
||||
Because of issues with the Microsoft accounts, we now have two builds on macOS:
|
||||
|
||||
- The old build with Qt 5.6 that does not work with Microsoft accounts, but can run on macOS older than 10.13.
|
||||
|
||||
- A new build with Qt 5.15.2 that does work with Microsoft accounts, can use the new macOS dark theme and highlight colors, but requires at least macOS 10.13.
|
||||
|
||||
MultiMC will update to the 5.15.2 builds when it detects that this is possible. **It may look like it is updating twice, just let it do its thing.**
|
||||
|
||||
Similar approach got attempted on Windows, aiming to fix various display scaling and theming issues, but it ran into too many problems and will be attempted later, with more caution.
|
||||
|
||||
#### Modpack platforms
|
||||
|
||||
In general, the modpack platform pages have been made more consistent with each other (GH-3118, GH-3720, GH-3731).
|
||||
|
||||
- FTB improvements:
|
||||
|
||||
- Modpack file downloads are now checked with checksums and cached.
|
||||
|
||||
- GH-1949: Allow Legacy FTB and FTB pack downloads to be aborted.
|
||||
|
||||
- CurseForge improvements:
|
||||
|
||||
- CurseForge modpack platform is now presented as CurseForge, not Twitch.
|
||||
|
||||
- UI has been updated to match other platforms
|
||||
|
||||
- Added sorting
|
||||
|
||||
- GH-3667: Added version selection
|
||||
|
||||
- GH-3611: Added ability to install beta versions
|
||||
|
||||
- GH-3633: When a CurseForge pack is available for multiple Minecraft versions, we assume the latest one.
|
||||
|
||||
- ATLauncher improvements:
|
||||
|
||||
- Handling latest/custom/recommended mod loader versions.
|
||||
|
||||
- Fabric loader packs should now work.
|
||||
|
||||
- GH-3764: Only client mods are installed now for ATL packs.
|
||||
|
||||
- Improved error handling
|
||||
|
||||
- Optional mods are supported.
|
||||
|
||||
- GH-1949: Allow ATLauncher pack downloads to be aborted
|
||||
|
||||
|
||||
- Fixed bugs in FTB platform search.
|
||||
|
||||
#### Other changes
|
||||
|
||||
- Forge installation is disabled on Minecraft 1.17+ because of incompatible/unresolved changes on the Forge side.
|
||||
|
||||
We're going to aim for fixing it in time for 1.18. Thankfully, 1.17 is more of a in-between release, so go play some 1.16.x packs!
|
||||
|
||||
- GH-2529: On macOS, MultiMC will ask to move all the instance data to a new `Data` folder in order to fix long load times caused by macOS checking all files.
|
||||
|
||||
- Detection of a large amount of various Java runtime flavors have been added.
|
||||
|
||||
- It is now possible to join servers when starting an instance:
|
||||
|
||||
- From command line via the `--launch` and `--server` arguments.
|
||||
|
||||
- Or by setting this up in the instance settings page.
|
||||
|
||||
This may not work correctly in some cases, because it is a rarely used feature and modders do not test with it.
|
||||
|
||||
- MultiMC now prints resolved IP addresses of Minecraft services into the game log for diagnostic purposes.
|
||||
|
||||
- Updated instance icons based on Minecraft textures.
|
||||
|
||||
- Forge `mods.toml` files are now used for displaying mods in the UI.
|
||||
|
||||
- Datapack button is now disabled when no world is selected.
|
||||
|
||||
- MultiMC warns about GLFW and OpenAL workarounds being enabled in the game log.
|
||||
|
||||
- Languages in the translations list are now sorted by their two/three letter key
|
||||
|
||||
- GH-3450: Displaying and recording gameplay time is now optional and can be turned off.
|
||||
|
||||
- GH-3930: MultiMC can now track the gameplay time of the last session.
|
||||
|
||||
- GH-3033: The version pages of instances now have a filter bar.
|
||||
|
||||
- GH-2971: UI descriptions of texture and resource packs no longer mention mods.
|
||||
|
||||
- Quick and dirty minimum Java runtime versions checks have been added. This needs to be expanded in the future.
|
||||
|
||||
#### Technical changes
|
||||
|
||||
- The codebase continues to move towards being debranded and harder to build as 'MultiMC' for third parties.
|
||||
|
||||
|
||||
## MultiMC 0.6.12
|
||||
|
||||
After roughly one year of maintenance and development work by various contributors, we're just calling it a good time to release.
|
||||
|
||||
What got added since the last time? Quite a bit! But in general, this is more of a spring cleaning before the major changes that we need to make come in.
|
||||
|
||||
What comes next after this:
|
||||
|
||||
- Rework of the account system to add support for Microsoft accounts
|
||||
- Rework of the Mojang accounts to allow them to co-exist with Microsoft accounts
|
||||
- General fixes to dealing with account state and skins -- currently, we use a third party service to fetch skins and we only do it on application start.
|
||||
- Complete rework of Java runtime management. The game will soon require different versions of the JRE and our approach will no longer work in that environment.
|
||||
|
||||
We'll probably call it 0.7 once all these are in place.
|
||||
|
||||
### Modpack platforms
|
||||
#### Modpack platforms
|
||||
|
||||
We've added a whole bunch of new modpack platforms to pick from right into the new instance dialog. If you run into any unusual issues with the imported packs, report them on the bug tracker.
|
||||
|
||||
@@ -27,7 +236,7 @@ We've added a whole bunch of new modpack platforms to pick from right into the n
|
||||
|
||||
- GH-405: Added a ATLauncher pack browser
|
||||
|
||||
### Other changes
|
||||
#### Other changes
|
||||
|
||||
- Added the option to not use OpenAL and/or GLFW libraries bundled with the game.
|
||||
|
||||
@@ -55,7 +264,7 @@ We've added a whole bunch of new modpack platforms to pick from right into the n
|
||||
|
||||
- GH-3602: Pre-launch commands could fail on first launch of the instance because the .minecraft folder has not been created yet.
|
||||
|
||||
### Technical changes
|
||||
#### Technical changes
|
||||
|
||||
- GH-3234: At build time, the meta URL can be changed.
|
||||
|
||||
@@ -67,8 +276,6 @@ We've added a whole bunch of new modpack platforms to pick from right into the n
|
||||
|
||||
- Compatibility with unusual build environments has been increased
|
||||
|
||||
# Previous releases
|
||||
|
||||
## MultiMC 0.6.11
|
||||
|
||||
This adds Forge 1.13+ support using [ForgeWrapper](https://github.com/ZekerZhayard/ForgeWrapper) by ZekerZhayard.
|
||||
@@ -1007,7 +1214,7 @@ Fluffy and functional!
|
||||
- GH-1275: Server resource pack folder is created on launch.
|
||||
- This is a workaround for Minecraft bug MCL-3732.
|
||||
- GH-1320: Improve compatibility with non-Oracle Java.
|
||||
- GH-1355: MMC environment will no longer leak into Minecraft after MultiMC updates itself.
|
||||
- GH-1355: LAUNCHER environment will no longer leak into Minecraft after MultiMC updates itself.
|
||||
|
||||
- Minecraft log:
|
||||
- Java exception detection in Minecraft logs has been improved.
|
||||
@@ -1052,7 +1259,7 @@ Fluffy and functional!
|
||||
- GH-1069, GH-1100: `LD_LIBRARY_PATH` and `LD_PRELOAD` environment variables supplied to MultiMC now don't affect MultiMC but affect the launched game.
|
||||
|
||||
This means you can use something like the Steam overlay in MultiMC instances on Linux.
|
||||
If you need to use these variables for MultiMC itself, you can use `MMC_LIBRARY_PATH` and `MMC_PRELOAD` instead.
|
||||
If you need to use these variables for MultiMC itself, you can use `LAUNCHER_LIBRARY_PATH` and `LAUNCHER_PRELOAD` instead.
|
||||
|
||||
- GH-1389: External processes (like folder views, editors, etc.) are now started in a way that prevents the MultiMC environment to be reused by them.
|
||||
- GH-1355: If `LD_LIBRARY_PATH` contains any of MultiMC's internal folders, this will not propagate to started processes.
|
||||
@@ -1339,7 +1546,7 @@ Long time coming, this release brought a lot of incremental improvements and fix
|
||||
- Update to the German translation.
|
||||
|
||||
## MultiMC 0.1.1
|
||||
- Hotfix - Changed the issue tracker URL to [GitHub issues](https://github.com/MultiMC/MultiMC5/issues).
|
||||
- Hotfix - Changed the issue tracker URL to [GitHub issues](https://github.com/MultiMC/Launcher/issues).
|
||||
|
||||
## MultiMC 0.1
|
||||
- Reworked the version numbering system to support our [new Git workflow](http://nvie.com/posts/a-successful-git-branching-model/).
|
||||
|
||||
@@ -23,6 +23,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
#define MULTIMC_GET_TEST_FILE(file) TestsInternal::readFile(QFINDTESTDATA(file))
|
||||
#define MULTIMC_GET_TEST_FILE_UTF8(file) TestsInternal::readFileUtf8(QFINDTESTDATA(file))
|
||||
#define GET_TEST_FILE(file) TestsInternal::readFile(QFINDTESTDATA(file))
|
||||
#define GET_TEST_FILE_UTF8(file) TestsInternal::readFileUtf8(QFINDTESTDATA(file))
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<assemblyIdentity name="MultiMC.Test.0" type="win32" version="5.0.0.0" />
|
||||
<assemblyIdentity name="Launcher.Test.0" type="win32" version="5.0.0.0" />
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
@@ -24,4 +24,4 @@
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
||||
</assembly>
|
||||
|
||||
@@ -17,7 +17,7 @@ BEGIN
|
||||
VALUE "CompanyName", "MultiMC Contributors"
|
||||
VALUE "FileDescription", "Testcase"
|
||||
VALUE "FileVersion", "1.0.0.0"
|
||||
VALUE "ProductName", "MultiMC Testcase"
|
||||
VALUE "ProductName", "Launcher Testcase"
|
||||
VALUE "ProductVersion", "5"
|
||||
END
|
||||
END
|
||||
|
||||
64
doc/multimc.1.txt
Normal file
@@ -0,0 +1,64 @@
|
||||
MULTIMC(1)
|
||||
==========
|
||||
:doctype: manpage
|
||||
|
||||
|
||||
NAME
|
||||
----
|
||||
multimc - a launcher and instance manager for Minecraft.
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
*multimc* ['OPTIONS']
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
MultiMC is a custom launcher for Minecraft that allows you to easily manage
|
||||
multiple installations of Minecraft at once. It also allows you to easily
|
||||
install and remove mods by simply dragging and dropping.
|
||||
Here are the current features of MultiMC.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
*-d, --dir*='DIRECTORY'::
|
||||
Use 'DIRECTORY' as the MultiMC root.
|
||||
|
||||
*-l, --launch*='INSTANCE_ID'::
|
||||
Launch the instance specified by 'INSTANCE_ID'.
|
||||
|
||||
*--alive*::
|
||||
Write a small 'live.check' file after MultiMC starts.
|
||||
|
||||
*-h, --help*::
|
||||
Display help text and exit.
|
||||
|
||||
*-v, --version*::
|
||||
Display program version and exit.
|
||||
*-a, --profile*='PROFILE'::
|
||||
Use the account specified by 'PROFILE' (only valid in combination with --launch).
|
||||
|
||||
EXIT STATUS
|
||||
-----------
|
||||
*0*::
|
||||
Success
|
||||
|
||||
*1*::
|
||||
Failure (syntax or usage error; configuration error; unexpected error).
|
||||
|
||||
BUGS
|
||||
----
|
||||
<https://github.com/MultiMC/Launcher/issues>
|
||||
|
||||
RESOURCES
|
||||
---------
|
||||
GitHub: <https://github.com/MultiMC/Launcher>
|
||||
|
||||
Main website: <https://multimc.org>
|
||||
|
||||
AUTHORS
|
||||
-------
|
||||
peterix <peterix@gmail.com>
|
||||
|
||||
// vim: syntax=asciidoc
|
||||
@@ -11,18 +11,19 @@
|
||||
|
||||
#include <BaseInstance.h>
|
||||
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
|
||||
class LaunchController;
|
||||
class LocalPeer;
|
||||
class InstanceWindow;
|
||||
class MainWindow;
|
||||
class SetupWizard;
|
||||
class FolderInstanceProvider;
|
||||
class GenericPageProvider;
|
||||
class QFile;
|
||||
class HttpMetaCache;
|
||||
class SettingsObject;
|
||||
class InstanceList;
|
||||
class MojangAccountList;
|
||||
class AccountList;
|
||||
class IconList;
|
||||
class QNetworkAccessManager;
|
||||
class JavaInstallList;
|
||||
@@ -34,18 +35,21 @@ class ITheme;
|
||||
class MCEditTool;
|
||||
class GAnalytics;
|
||||
|
||||
#if defined(MMC)
|
||||
#undef MMC
|
||||
#endif
|
||||
#define MMC (static_cast<MultiMC *>(QCoreApplication::instance()))
|
||||
namespace Meta {
|
||||
class Index;
|
||||
}
|
||||
|
||||
class MultiMC : public QApplication
|
||||
#if defined(APPLICATION)
|
||||
#undef APPLICATION
|
||||
#endif
|
||||
#define APPLICATION (static_cast<Application *>(QCoreApplication::instance()))
|
||||
|
||||
class Application : public QApplication
|
||||
{
|
||||
// friends for the purpose of limiting access to deprecated stuff
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Status
|
||||
{
|
||||
enum Status {
|
||||
StartingUp,
|
||||
Failed,
|
||||
Succeeded,
|
||||
@@ -53,21 +57,18 @@ public:
|
||||
};
|
||||
|
||||
public:
|
||||
MultiMC(int &argc, char **argv);
|
||||
virtual ~MultiMC();
|
||||
Application(int &argc, char **argv);
|
||||
virtual ~Application();
|
||||
|
||||
GAnalytics *analytics() const
|
||||
{
|
||||
GAnalytics *analytics() const {
|
||||
return m_analytics;
|
||||
}
|
||||
|
||||
std::shared_ptr<SettingsObject> settings() const
|
||||
{
|
||||
std::shared_ptr<SettingsObject> settings() const {
|
||||
return m_settings;
|
||||
}
|
||||
|
||||
qint64 timeSinceStart() const
|
||||
{
|
||||
qint64 timeSinceStart() const {
|
||||
return startTime.msecsTo(QDateTime::currentDateTime());
|
||||
}
|
||||
|
||||
@@ -79,9 +80,7 @@ public:
|
||||
|
||||
void setApplicationTheme(const QString& name, bool initial);
|
||||
|
||||
// DownloadUpdateTask
|
||||
std::shared_ptr<UpdateChecker> updateChecker()
|
||||
{
|
||||
shared_qobject_ptr<UpdateChecker> updateChecker() {
|
||||
return m_updateChecker;
|
||||
}
|
||||
|
||||
@@ -89,44 +88,44 @@ public:
|
||||
|
||||
std::shared_ptr<JavaInstallList> javalist();
|
||||
|
||||
std::shared_ptr<InstanceList> instances() const
|
||||
{
|
||||
std::shared_ptr<InstanceList> instances() const {
|
||||
return m_instances;
|
||||
}
|
||||
|
||||
FolderInstanceProvider * folderProvider() const
|
||||
{
|
||||
return m_instanceFolder;
|
||||
}
|
||||
|
||||
std::shared_ptr<IconList> icons() const
|
||||
{
|
||||
std::shared_ptr<IconList> icons() const {
|
||||
return m_icons;
|
||||
}
|
||||
|
||||
MCEditTool *mcedit() const
|
||||
{
|
||||
MCEditTool *mcedit() const {
|
||||
return m_mcedit.get();
|
||||
}
|
||||
|
||||
std::shared_ptr<MojangAccountList> accounts() const
|
||||
{
|
||||
shared_qobject_ptr<AccountList> accounts() const {
|
||||
return m_accounts;
|
||||
}
|
||||
|
||||
Status status() const
|
||||
{
|
||||
QString msaClientId() const;
|
||||
|
||||
Status status() const {
|
||||
return m_status;
|
||||
}
|
||||
|
||||
const QMap<QString, std::shared_ptr<BaseProfilerFactory>> &profilers() const
|
||||
{
|
||||
const QMap<QString, std::shared_ptr<BaseProfilerFactory>> &profilers() const {
|
||||
return m_profilers;
|
||||
}
|
||||
|
||||
void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
|
||||
|
||||
shared_qobject_ptr<QNetworkAccessManager> network();
|
||||
|
||||
shared_qobject_ptr<HttpMetaCache> metacache();
|
||||
|
||||
shared_qobject_ptr<Meta::Index> metadataIndex();
|
||||
|
||||
QString getJarsPath();
|
||||
|
||||
/// this is the root of the 'installation'. Used for automatic updates
|
||||
const QString &root()
|
||||
{
|
||||
const QString &root() {
|
||||
return m_rootPath;
|
||||
}
|
||||
|
||||
@@ -150,12 +149,18 @@ signals:
|
||||
void globalSettingsClosed();
|
||||
|
||||
public slots:
|
||||
bool launch(InstancePtr instance, bool online = true, BaseProfilerFactory *profiler = nullptr);
|
||||
bool launch(
|
||||
InstancePtr instance,
|
||||
bool online = true,
|
||||
BaseProfilerFactory *profiler = nullptr,
|
||||
MinecraftServerTargetPtr serverToJoin = nullptr,
|
||||
MinecraftAccountPtr accountToUse = nullptr
|
||||
);
|
||||
bool kill(InstancePtr instance);
|
||||
|
||||
private slots:
|
||||
void on_windowClose();
|
||||
void messageReceived(const QString & message);
|
||||
void messageReceived(const QByteArray & message);
|
||||
void controllerSucceeded();
|
||||
void controllerFailed(const QString & error);
|
||||
void analyticsSettingChanged(const Setting &setting, QVariant value);
|
||||
@@ -176,22 +181,29 @@ private:
|
||||
private:
|
||||
QDateTime startTime;
|
||||
|
||||
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
||||
|
||||
shared_qobject_ptr<UpdateChecker> m_updateChecker;
|
||||
shared_qobject_ptr<AccountList> m_accounts;
|
||||
|
||||
shared_qobject_ptr<HttpMetaCache> m_metacache;
|
||||
shared_qobject_ptr<Meta::Index> m_metadataIndex;
|
||||
|
||||
std::shared_ptr<SettingsObject> m_settings;
|
||||
std::shared_ptr<InstanceList> m_instances;
|
||||
FolderInstanceProvider * m_instanceFolder = nullptr;
|
||||
std::shared_ptr<IconList> m_icons;
|
||||
std::shared_ptr<UpdateChecker> m_updateChecker;
|
||||
std::shared_ptr<MojangAccountList> m_accounts;
|
||||
std::shared_ptr<JavaInstallList> m_javalist;
|
||||
std::shared_ptr<TranslationsModel> m_translations;
|
||||
std::shared_ptr<GenericPageProvider> m_globalSettingsProvider;
|
||||
std::map<QString, std::unique_ptr<ITheme>> m_themes;
|
||||
std::unique_ptr<MCEditTool> m_mcedit;
|
||||
QString m_jarsPath;
|
||||
QSet<QString> m_features;
|
||||
|
||||
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
|
||||
|
||||
QString m_rootPath;
|
||||
Status m_status = MultiMC::StartingUp;
|
||||
Status m_status = Application::StartingUp;
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
// used on Windows to attach the standard IO streams
|
||||
@@ -199,8 +211,7 @@ private:
|
||||
#endif
|
||||
|
||||
// FIXME: attach to instances instead.
|
||||
struct InstanceXtras
|
||||
{
|
||||
struct InstanceXtras {
|
||||
InstanceWindow * window = nullptr;
|
||||
shared_qobject_ptr<LaunchController> controller;
|
||||
};
|
||||
@@ -214,13 +225,15 @@ private:
|
||||
// main window, if any
|
||||
MainWindow * m_mainWindow = nullptr;
|
||||
|
||||
// peer MultiMC instance connector - used to implement single instance MultiMC and signalling
|
||||
// peer launcher instance connector - used to implement single instance launcher and signalling
|
||||
LocalPeer * m_peerInstance = nullptr;
|
||||
|
||||
GAnalytics * m_analytics = nullptr;
|
||||
SetupWizard * m_setupWizard = nullptr;
|
||||
public:
|
||||
QString m_instanceIdToLaunch;
|
||||
QString m_serverToJoin;
|
||||
QString m_profileToUse;
|
||||
bool m_liveCheck = false;
|
||||
QUrl m_zipToImport;
|
||||
std::unique_ptr<QFile> logFile;
|
||||
31
launcher/ApplicationMessage.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "ApplicationMessage.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
void ApplicationMessage::parse(const QByteArray & input) {
|
||||
auto doc = QJsonDocument::fromBinaryData(input);
|
||||
auto root = doc.object();
|
||||
|
||||
command = root.value("command").toString();
|
||||
args.clear();
|
||||
|
||||
auto parsedArgs = root.value("args").toObject();
|
||||
for(auto iter = parsedArgs.begin(); iter != parsedArgs.end(); iter++) {
|
||||
args[iter.key()] = iter.value().toString();
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray ApplicationMessage::serialize() {
|
||||
QJsonObject root;
|
||||
root.insert("command", command);
|
||||
QJsonObject outArgs;
|
||||
for (auto iter = args.begin(); iter != args.end(); iter++) {
|
||||
outArgs[iter.key()] = iter.value();
|
||||
}
|
||||
root.insert("args", outArgs);
|
||||
|
||||
QJsonDocument out;
|
||||
out.setObject(root);
|
||||
return out.toBinaryData();
|
||||
}
|
||||
13
launcher/ApplicationMessage.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
#include <QByteArray>
|
||||
|
||||
struct ApplicationMessage {
|
||||
QString command;
|
||||
QMap<QString, QString> args;
|
||||
|
||||
QByteArray serialize();
|
||||
void parse(const QByteArray & input);
|
||||
};
|
||||
@@ -17,8 +17,6 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
class MinecraftInstance;
|
||||
class QDir;
|
||||
class QString;
|
||||
@@ -27,7 +25,7 @@ class Task;
|
||||
class BaseVersion;
|
||||
typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
|
||||
|
||||
class MULTIMC_LOGIC_EXPORT BaseInstaller
|
||||
class BaseInstaller
|
||||
{
|
||||
public:
|
||||
BaseInstaller();
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "Commandline.h"
|
||||
#include "BuildConfig.h"
|
||||
|
||||
BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir)
|
||||
: QObject()
|
||||
@@ -37,6 +38,7 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
|
||||
m_settings->registerSetting("notes", "");
|
||||
m_settings->registerSetting("lastLaunchTime", 0);
|
||||
m_settings->registerSetting("totalTimePlayed", 0);
|
||||
m_settings->registerSetting("lastTimePlayed", 0);
|
||||
|
||||
// Custom Commands
|
||||
auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
|
||||
@@ -134,15 +136,24 @@ void BaseInstance::setRunning(bool running)
|
||||
|
||||
m_isRunning = running;
|
||||
|
||||
if(!m_settings->get("RecordGameTime").toBool())
|
||||
{
|
||||
emit runningStatusChanged(running);
|
||||
return;
|
||||
}
|
||||
|
||||
if(running)
|
||||
{
|
||||
m_timeStarted = QDateTime::currentDateTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
qint64 current = settings()->get("totalTimePlayed").toLongLong();
|
||||
QDateTime timeEnded = QDateTime::currentDateTime();
|
||||
|
||||
qint64 current = settings()->get("totalTimePlayed").toLongLong();
|
||||
settings()->set("totalTimePlayed", current + m_timeStarted.secsTo(timeEnded));
|
||||
settings()->set("lastTimePlayed", m_timeStarted.secsTo(timeEnded));
|
||||
|
||||
emit propertiesChanged(this);
|
||||
}
|
||||
|
||||
@@ -160,9 +171,20 @@ int64_t BaseInstance::totalTimePlayed() const
|
||||
return current;
|
||||
}
|
||||
|
||||
int64_t BaseInstance::lastTimePlayed() const
|
||||
{
|
||||
if(m_isRunning)
|
||||
{
|
||||
QDateTime timeNow = QDateTime::currentDateTime();
|
||||
return m_timeStarted.secsTo(timeNow);
|
||||
}
|
||||
return settings()->get("lastTimePlayed").toLongLong();
|
||||
}
|
||||
|
||||
void BaseInstance::resetTimePlayed()
|
||||
{
|
||||
settings()->reset("totalTimePlayed");
|
||||
settings()->reset("lastTimePlayed");
|
||||
}
|
||||
|
||||
QString BaseInstance::instanceType() const
|
||||
@@ -239,7 +261,7 @@ QString BaseInstance::name() const
|
||||
|
||||
QString BaseInstance::windowTitle() const
|
||||
{
|
||||
return "MultiMC: " + name().replace(QRegExp("[ \n\r\t]+"), " ");
|
||||
return BuildConfig.LAUNCHER_NAME + ": " + name().replace(QRegExp("[ \n\r\t]+"), " ");
|
||||
}
|
||||
|
||||
// FIXME: why is this here? move it to MinecraftInstance!!!
|
||||
@@ -26,13 +26,13 @@
|
||||
|
||||
#include "settings/INIFile.h"
|
||||
#include "BaseVersionList.h"
|
||||
#include "minecraft/auth/MojangAccount.h"
|
||||
#include "minecraft/auth/MinecraftAccount.h"
|
||||
#include "MessageLevel.h"
|
||||
#include "pathmatcher/IPathMatcher.h"
|
||||
|
||||
#include "net/Mode.h"
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
|
||||
class QDir;
|
||||
class Task;
|
||||
@@ -50,7 +50,7 @@ typedef std::shared_ptr<BaseInstance> InstancePtr;
|
||||
* To create a new instance type, create a new class inheriting from this class
|
||||
* and implement the pure virtual functions.
|
||||
*/
|
||||
class MULTIMC_LOGIC_EXPORT BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance>
|
||||
class BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance>
|
||||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
@@ -71,20 +71,21 @@ public:
|
||||
virtual void saveNow() = 0;
|
||||
|
||||
/***
|
||||
* the instance has been invalidated - it is no longer tracked by MultiMC for some reason,
|
||||
* the instance has been invalidated - it is no longer tracked by the launcher for some reason,
|
||||
* but it has not necessarily been deleted.
|
||||
*
|
||||
* Happens when the instance folder changes to some other location, or the instance is removed by external means.
|
||||
*/
|
||||
void invalidate();
|
||||
|
||||
/// The instance's ID. The ID SHALL be determined by MMC internally. The ID IS guaranteed to
|
||||
/// The instance's ID. The ID SHALL be determined by LAUNCHER internally. The ID IS guaranteed to
|
||||
/// be unique.
|
||||
virtual QString id() const;
|
||||
|
||||
void setRunning(bool running);
|
||||
bool isRunning() const;
|
||||
int64_t totalTimePlayed() const;
|
||||
int64_t lastTimePlayed() const;
|
||||
void resetTimePlayed();
|
||||
|
||||
/// get the type of this instance
|
||||
@@ -99,6 +100,9 @@ public:
|
||||
return instanceRoot();
|
||||
}
|
||||
|
||||
/// Path to the instance's mods directory.
|
||||
virtual QString modsRoot() const = 0;
|
||||
|
||||
QString name() const;
|
||||
void setName(QString val);
|
||||
|
||||
@@ -142,10 +146,11 @@ public:
|
||||
virtual SettingsObjectPtr settings() const;
|
||||
|
||||
/// returns a valid update task
|
||||
virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) = 0;
|
||||
virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0;
|
||||
|
||||
/// returns a valid launcher (task container)
|
||||
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) = 0;
|
||||
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(
|
||||
AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0;
|
||||
|
||||
/// returns the current launch task (if any)
|
||||
shared_qobject_ptr<LaunchTask> getLaunchTask();
|
||||
@@ -221,9 +226,9 @@ public:
|
||||
bool reloadSettings();
|
||||
|
||||
/**
|
||||
* 'print' a verbose desription of the instance into a QStringList
|
||||
* 'print' a verbose description of the instance into a QStringList
|
||||
*/
|
||||
virtual QStringList verboseDescription(AuthSessionPtr session) = 0;
|
||||
virtual QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) = 0;
|
||||
|
||||
Status currentStatus() const;
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
#include "BaseVersion.h"
|
||||
#include "tasks/Task.h"
|
||||
#include "multimc_logic_export.h"
|
||||
#include "QObjectPtr.h"
|
||||
|
||||
/*!
|
||||
@@ -36,7 +35,7 @@
|
||||
* all have a default implementation, but they can be overridden by plugins to
|
||||
* change the behavior of the list.
|
||||
*/
|
||||
class MULTIMC_LOGIC_EXPORT BaseVersionList : public QAbstractListModel
|
||||
class BaseVersionList : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -64,7 +63,7 @@ public:
|
||||
* The task returned by this function should reset the model when it's done.
|
||||
* \return A pointer to a task that reloads the version list.
|
||||
*/
|
||||
virtual shared_qobject_ptr<Task> getLoadTask() = 0;
|
||||
virtual Task::Ptr getLoadTask() = 0;
|
||||
|
||||
//! Checks whether or not the list is loaded. If this returns false, the list should be
|
||||
//loaded.
|
||||
1050
launcher/CMakeLists.txt
Normal file
@@ -25,8 +25,6 @@
|
||||
#include <QHash>
|
||||
#include <QStringList>
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
/**
|
||||
* @file libutil/include/cmdutils.h
|
||||
* @brief commandline parsing and processing utilities
|
||||
@@ -40,7 +38,7 @@ namespace Commandline
|
||||
* @param args the argument string
|
||||
* @return a QStringList containing all arguments
|
||||
*/
|
||||
MULTIMC_LOGIC_EXPORT QStringList splitArgs(QString args);
|
||||
QStringList splitArgs(QString args);
|
||||
|
||||
/**
|
||||
* @brief The FlagStyle enum
|
||||
@@ -83,7 +81,7 @@ enum Enum
|
||||
/**
|
||||
* @brief The ParsingError class
|
||||
*/
|
||||
class MULTIMC_LOGIC_EXPORT ParsingError : public std::runtime_error
|
||||
class ParsingError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
ParsingError(const QString &what);
|
||||
@@ -92,7 +90,7 @@ public:
|
||||
/**
|
||||
* @brief The Parser class
|
||||
*/
|
||||
class MULTIMC_LOGIC_EXPORT Parser
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
@@ -7,7 +7,7 @@
|
||||
/**
|
||||
* This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing.
|
||||
*/
|
||||
#if defined(Q_OS_LINUX)
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
@@ -83,7 +83,7 @@ bool openDirectory(const QString &path, bool ensureExists)
|
||||
{
|
||||
return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
|
||||
};
|
||||
#if defined(Q_OS_LINUX)
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
return IndirectOpen(f);
|
||||
#else
|
||||
return f();
|
||||
@@ -97,7 +97,7 @@ bool openFile(const QString &path)
|
||||
{
|
||||
return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
};
|
||||
#if defined(Q_OS_LINUX)
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
return IndirectOpen(f);
|
||||
#else
|
||||
return f();
|
||||
@@ -107,7 +107,7 @@ bool openFile(const QString &path)
|
||||
bool openFile(const QString &application, const QString &path, const QString &workingDirectory, qint64 *pid)
|
||||
{
|
||||
qDebug() << "Opening file" << path << "using" << application;
|
||||
#if defined(Q_OS_LINUX)
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
||||
return IndirectOpen([&]()
|
||||
{
|
||||
@@ -121,7 +121,7 @@ bool openFile(const QString &application, const QString &path, const QString &wo
|
||||
bool run(const QString &application, const QStringList &args, const QString &workingDirectory, qint64 *pid)
|
||||
{
|
||||
qDebug() << "Running" << application << "with args" << args.join(' ');
|
||||
#if defined(Q_OS_LINUX)
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
||||
return IndirectOpen([&]()
|
||||
{
|
||||
@@ -139,7 +139,7 @@ bool openUrl(const QUrl &url)
|
||||
{
|
||||
return QDesktopServices::openUrl(url);
|
||||
};
|
||||
#if defined(Q_OS_LINUX)
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
return IndirectOpen(f);
|
||||
#else
|
||||
return f();
|
||||
36
launcher/DesktopServices.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <QUrl>
|
||||
#include <QString>
|
||||
|
||||
/**
|
||||
* This wraps around QDesktopServices and adds workarounds where needed
|
||||
* Use this instead of QDesktopServices!
|
||||
*/
|
||||
namespace DesktopServices
|
||||
{
|
||||
/**
|
||||
* Open a file in whatever application is applicable
|
||||
*/
|
||||
bool openFile(const QString &path);
|
||||
|
||||
/**
|
||||
* Open a file in the specified application
|
||||
*/
|
||||
bool openFile(const QString &application, const QString &path, const QString & workingDirectory = QString(), qint64 *pid = 0);
|
||||
|
||||
/**
|
||||
* Run an application
|
||||
*/
|
||||
bool run(const QString &application,const QStringList &args, const QString & workingDirectory = QString(), qint64 *pid = 0);
|
||||
|
||||
/**
|
||||
* Open a directory
|
||||
*/
|
||||
bool openDirectory(const QString &path, bool ensureExists = false);
|
||||
|
||||
/**
|
||||
* Open the URL, most likely in a browser. Maybe.
|
||||
*/
|
||||
bool openUrl(const QUrl &url);
|
||||
}
|
||||
@@ -6,9 +6,7 @@
|
||||
#include <QDebug>
|
||||
#include <exception>
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
class MULTIMC_LOGIC_EXPORT Exception : public std::exception
|
||||
class Exception : public std::exception
|
||||
{
|
||||
public:
|
||||
Exception(const QString &message) : std::exception(), m_message(message)
|
||||
@@ -403,7 +403,7 @@ QString getDesktopDir()
|
||||
bool createShortCut(QString location, QString dest, QStringList args, QString name,
|
||||
QString icon)
|
||||
{
|
||||
#if defined Q_OS_LINUX
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
location = PathCombine(location, name + ".desktop");
|
||||
|
||||
QFile f(location);
|
||||
@@ -5,14 +5,13 @@
|
||||
#include "Exception.h"
|
||||
#include "pathmatcher/IPathMatcher.h"
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
#include <QDir>
|
||||
#include <QFlags>
|
||||
|
||||
namespace FS
|
||||
{
|
||||
|
||||
class MULTIMC_LOGIC_EXPORT FileSystemException : public ::Exception
|
||||
class FileSystemException : public ::Exception
|
||||
{
|
||||
public:
|
||||
FileSystemException(const QString &message) : Exception(message) {}
|
||||
@@ -21,31 +20,31 @@ public:
|
||||
/**
|
||||
* write data to a file safely
|
||||
*/
|
||||
MULTIMC_LOGIC_EXPORT void write(const QString &filename, const QByteArray &data);
|
||||
void write(const QString &filename, const QByteArray &data);
|
||||
|
||||
/**
|
||||
* read data from a file safely\
|
||||
*/
|
||||
MULTIMC_LOGIC_EXPORT QByteArray read(const QString &filename);
|
||||
QByteArray read(const QString &filename);
|
||||
|
||||
/**
|
||||
* Update the last changed timestamp of an existing file
|
||||
*/
|
||||
MULTIMC_LOGIC_EXPORT bool updateTimestamp(const QString & filename);
|
||||
bool updateTimestamp(const QString & filename);
|
||||
|
||||
/**
|
||||
* Creates all the folders in a path for the specified path
|
||||
* last segment of the path is treated as a file name and is ignored!
|
||||
*/
|
||||
MULTIMC_LOGIC_EXPORT bool ensureFilePathExists(QString filenamepath);
|
||||
bool ensureFilePathExists(QString filenamepath);
|
||||
|
||||
/**
|
||||
* Creates all the folders in a path for the specified path
|
||||
* last segment of the path is treated as a folder name and is created!
|
||||
*/
|
||||
MULTIMC_LOGIC_EXPORT bool ensureFolderPathExists(QString filenamepath);
|
||||
bool ensureFolderPathExists(QString filenamepath);
|
||||
|
||||
class MULTIMC_LOGIC_EXPORT copy
|
||||
class copy
|
||||
{
|
||||
public:
|
||||
copy(const QString & src, const QString & dst)
|
||||
@@ -81,13 +80,13 @@ private:
|
||||
/**
|
||||
* Delete a folder recursively
|
||||
*/
|
||||
MULTIMC_LOGIC_EXPORT bool deletePath(QString path);
|
||||
bool deletePath(QString path);
|
||||
|
||||
MULTIMC_LOGIC_EXPORT QString PathCombine(const QString &path1, const QString &path2);
|
||||
MULTIMC_LOGIC_EXPORT QString PathCombine(const QString &path1, const QString &path2, const QString &path3);
|
||||
MULTIMC_LOGIC_EXPORT QString PathCombine(const QString &path1, const QString &path2, const QString &path3, const QString &path4);
|
||||
QString PathCombine(const QString &path1, const QString &path2);
|
||||
QString PathCombine(const QString &path1, const QString &path2, const QString &path3);
|
||||
QString PathCombine(const QString &path1, const QString &path2, const QString &path3, const QString &path4);
|
||||
|
||||
MULTIMC_LOGIC_EXPORT QString AbsolutePath(QString path);
|
||||
QString AbsolutePath(QString path);
|
||||
|
||||
/**
|
||||
* Resolve an executable
|
||||
@@ -99,7 +98,7 @@ MULTIMC_LOGIC_EXPORT QString AbsolutePath(QString path);
|
||||
*
|
||||
* @return absolute path to executable or null string
|
||||
*/
|
||||
MULTIMC_LOGIC_EXPORT QString ResolveExecutable(QString path);
|
||||
QString ResolveExecutable(QString path);
|
||||
|
||||
/**
|
||||
* Normalize path
|
||||
@@ -109,20 +108,20 @@ MULTIMC_LOGIC_EXPORT QString ResolveExecutable(QString path);
|
||||
*
|
||||
* Returns false if the path logic somehow filed (and normalizedPath in invalid)
|
||||
*/
|
||||
MULTIMC_LOGIC_EXPORT QString NormalizePath(QString path);
|
||||
QString NormalizePath(QString path);
|
||||
|
||||
MULTIMC_LOGIC_EXPORT QString RemoveInvalidFilenameChars(QString string, QChar replaceWith = '-');
|
||||
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith = '-');
|
||||
|
||||
MULTIMC_LOGIC_EXPORT QString DirNameFromString(QString string, QString inDir = ".");
|
||||
QString DirNameFromString(QString string, QString inDir = ".");
|
||||
|
||||
/// Checks if the a given Path contains "!"
|
||||
MULTIMC_LOGIC_EXPORT bool checkProblemticPathJava(QDir folder);
|
||||
bool checkProblemticPathJava(QDir folder);
|
||||
|
||||
// Get the Directory representing the User's Desktop
|
||||
MULTIMC_LOGIC_EXPORT QString getDesktopDir();
|
||||
QString getDesktopDir();
|
||||
|
||||
// Create a shortcut at *location*, pointing to *dest* called with the arguments *args*
|
||||
// call it *name* and assign it the icon *icon*
|
||||
// return true if operation succeeded
|
||||
MULTIMC_LOGIC_EXPORT bool createShortCut(QString location, QString dest, QStringList args, QString name, QString iconLocation);
|
||||
bool createShortCut(QString location, QString dest, QStringList args, QString name, QString iconLocation);
|
||||
}
|
||||
@@ -135,7 +135,7 @@ slots:
|
||||
<< "asdf"
|
||||
<< QString()
|
||||
#if defined(Q_OS_LINUX)
|
||||
<< MULTIMC_GET_TEST_FILE("data/FileSystem-test_createShortcut-unix")
|
||||
<< GET_TEST_FILE("data/FileSystem-test_createShortcut-unix")
|
||||
#elif defined(Q_OS_WIN)
|
||||
<< QByteArray()
|
||||
#endif
|
||||