Compare commits
751 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c95dfe89e1 | ||
|
|
6c53068f32 | ||
|
|
6cad5f358b | ||
|
|
0b9bfc95dd | ||
|
|
eff51d6511 | ||
|
|
e537265ade | ||
|
|
f16a8fd234 | ||
|
|
65e8bfba6c | ||
|
|
52833d9921 | ||
|
|
bb04cb09a3 | ||
|
|
e17a648c2b | ||
|
|
d4247bf3ae | ||
|
|
07811bddd7 | ||
|
|
f2d1f98cae | ||
|
|
056faf3307 | ||
|
|
45ec0b690d | ||
|
|
2bd0d5d1a9 | ||
|
|
0912669610 | ||
|
|
1b77965225 | ||
|
|
e425abf8d0 | ||
|
|
51fff1f02c | ||
|
|
60aead3255 | ||
|
|
294147b859 | ||
|
|
e5d10913d4 | ||
|
|
d22707c7fc | ||
|
|
7747f08952 | ||
|
|
14981321d4 | ||
|
|
3993d1227e | ||
|
|
26d041ec21 | ||
|
|
334812f74d | ||
|
|
0b4b9a1783 | ||
|
|
17a06d8eab | ||
|
|
82df3ac9ac | ||
|
|
c57d58f6f8 | ||
|
|
f9fb2443ab | ||
|
|
fb323d8a0b | ||
|
|
eba8c1e33f | ||
|
|
026070b836 | ||
|
|
0d01261956 | ||
|
|
2c9e7b75f4 | ||
|
|
87852db4ae | ||
|
|
0b91a983e2 | ||
|
|
4fe664204e | ||
|
|
bb8e02a2f2 | ||
|
|
b69ab34f4b | ||
|
|
98099c8ec5 | ||
|
|
1bb7e3d0f9 | ||
|
|
7450faaf13 | ||
|
|
17865bfd7d | ||
|
|
da4973dc94 | ||
|
|
f458204a52 | ||
|
|
9ec1d43565 | ||
|
|
18b6792cc7 | ||
|
|
33a8b916a1 | ||
|
|
35783d9dd9 | ||
|
|
30f8f5c65f | ||
|
|
574571b51b | ||
|
|
39780c7238 | ||
|
|
72e62058c0 | ||
|
|
d6dabfa67d | ||
|
|
29c78b56fc | ||
|
|
e1b7630677 | ||
|
|
44a4712e61 | ||
|
|
bd95c3ea33 | ||
|
|
d05a9a6a5f | ||
|
|
d2deb1821f | ||
|
|
a003b8e867 | ||
|
|
ea262c45ed | ||
|
|
b0844db1df | ||
|
|
89ac58256c | ||
|
|
5ff03584a5 | ||
|
|
1ad3e4417f | ||
|
|
b8811039f2 | ||
|
|
55edaa2039 | ||
|
|
1014141608 | ||
|
|
f756b25993 | ||
|
|
724eb76dc4 | ||
|
|
c6c56e56bd | ||
|
|
71cf4f8d18 | ||
|
|
22f82c34bf | ||
|
|
c1ed09e747 | ||
|
|
f57ac946fd | ||
|
|
4c216753ba | ||
|
|
d98022d3ae | ||
|
|
413397c3c1 | ||
|
|
34f8de4682 | ||
|
|
4f95e4618f | ||
|
|
f9c2f818fe | ||
|
|
496440cbc8 | ||
|
|
67c140ce5c | ||
|
|
bea3251d9c | ||
|
|
3840684a54 | ||
|
|
c7b0d15393 | ||
|
|
e50cb5caa4 | ||
|
|
e463edb185 | ||
|
|
82dde9f426 | ||
|
|
aae2f23eb6 | ||
|
|
a452b7ee96 | ||
|
|
01f3511e88 | ||
|
|
a1f256a745 | ||
|
|
a6dff61ff7 | ||
|
|
74addfb78b | ||
|
|
16cf56b7a4 | ||
|
|
867d240a2f | ||
|
|
458944ad91 | ||
|
|
75568ed04b | ||
|
|
f624754f0c | ||
|
|
1f1cdbe0a8 | ||
|
|
cb81cb9835 | ||
|
|
aba63c8f00 | ||
|
|
73e1d15ab9 | ||
|
|
e5b7517d2f | ||
|
|
0fd546a11c | ||
|
|
e044744faf | ||
|
|
8bced9da83 | ||
|
|
149adbd1d6 | ||
|
|
30312ea701 | ||
|
|
858487521e | ||
|
|
08dd08afc1 | ||
|
|
6307689cf1 | ||
|
|
86f68389c9 | ||
|
|
7354c578fd | ||
|
|
90b16fb903 | ||
|
|
8335432543 | ||
|
|
319ba3c8f9 | ||
|
|
9e5883c173 | ||
|
|
597da783d7 | ||
|
|
1849db93ec | ||
|
|
b82d667859 | ||
|
|
9bdfa5c8de | ||
|
|
fb0970b496 | ||
|
|
b6290ac254 | ||
|
|
b8a736c673 | ||
|
|
18f790953a | ||
|
|
2334a44221 | ||
|
|
65a4f8919a | ||
|
|
ddc094b76b | ||
|
|
e35f2b6c2c | ||
|
|
064c0febd3 | ||
|
|
79204e5df0 | ||
|
|
79cd37be94 | ||
|
|
dbe7d9ea2e | ||
|
|
b433882ac1 | ||
|
|
301b44d1c4 | ||
|
|
c24a89f3af | ||
|
|
41f728b22f | ||
|
|
af36e5c43f | ||
|
|
c37b7f771e | ||
|
|
518568b803 | ||
|
|
b069478f6e | ||
|
|
8d100ba97c | ||
|
|
6bfa07e728 | ||
|
|
5488ab11d4 | ||
|
|
b5e81bbb0d | ||
|
|
ec498074c1 | ||
|
|
2517671396 | ||
|
|
e5c962b7b9 | ||
|
|
b88296029b | ||
|
|
844ed61aa4 | ||
|
|
bec8293f28 | ||
|
|
cf49f171b3 | ||
|
|
05e3910fbd | ||
|
|
81b7e5f769 | ||
|
|
ffec1e1930 | ||
|
|
ec897aee95 | ||
|
|
00589b247a | ||
|
|
ff64d2ad59 | ||
|
|
346496c3fd | ||
|
|
518b19e667 | ||
|
|
0f41bea58a | ||
|
|
3621594213 | ||
|
|
79910e3542 | ||
|
|
0a2ad17f06 | ||
|
|
7df413db1a | ||
|
|
6faa0ef711 | ||
|
|
363588789e | ||
|
|
2dc44b3ff5 | ||
|
|
7938585abb | ||
|
|
bbdbe47e72 | ||
|
|
75b8765604 | ||
|
|
7217e3991a | ||
|
|
bf5be5568e | ||
|
|
018e6229ca | ||
|
|
21413b964a | ||
|
|
6a3ff58c8c | ||
|
|
645bc3f445 | ||
|
|
a0c44f7062 | ||
|
|
6c31125f02 | ||
|
|
b326fef61b | ||
|
|
5d14dede50 | ||
|
|
bf80bd1143 | ||
|
|
0917706b22 | ||
|
|
7e63ddf7d4 | ||
|
|
583b63e90e | ||
|
|
c6b60969bb | ||
|
|
c7256744c6 | ||
|
|
8235752dec | ||
|
|
dd277cba08 | ||
|
|
fdedf7078e | ||
|
|
fe75a7f09e | ||
|
|
305157746a | ||
|
|
214d615d18 | ||
|
|
66c6e6e05d | ||
|
|
f59822fd65 | ||
|
|
43ebd02dcf | ||
|
|
8a6f673567 | ||
|
|
efe181bd28 | ||
|
|
41e5b3b628 | ||
|
|
399247a018 | ||
|
|
0327a3cdbf | ||
|
|
c88f147ae4 | ||
|
|
f9924af2a0 | ||
|
|
edc351c0d0 | ||
|
|
3c815b8b8d | ||
|
|
66e165f4b8 | ||
|
|
aebd9aa745 | ||
|
|
cbe6d0dbfd | ||
|
|
0a827ba70e | ||
|
|
3304798aa7 | ||
|
|
78003c5eed | ||
|
|
585c93fd7f | ||
|
|
cfb6ac8a8f | ||
|
|
a28adf7b62 | ||
|
|
3e1939de8e | ||
|
|
ceaea5db2f | ||
|
|
40e67d2bc6 | ||
|
|
f7acde4389 | ||
|
|
8c1d95b484 | ||
|
|
863cf3807e | ||
|
|
cad522fe41 | ||
|
|
9d255901bc | ||
|
|
f948fd82ab | ||
|
|
9c4fcb826d | ||
|
|
5c1026bd12 | ||
|
|
acc637cfcb | ||
|
|
831b76bb78 | ||
|
|
6ce93de8c0 | ||
|
|
f45f831736 | ||
|
|
a8b3d470da | ||
|
|
b39410a2c2 | ||
|
|
208c83e1df | ||
|
|
ef1bf57b58 | ||
|
|
6afff3e31f | ||
|
|
e4d8ae582d | ||
|
|
1f6a1c7fee | ||
|
|
40d77bc265 | ||
|
|
2a2b18ddff | ||
|
|
edeffef51d | ||
|
|
533cd09057 | ||
|
|
b2e18253a1 | ||
|
|
ed99c6a0dc | ||
|
|
ae8667726e | ||
|
|
cbcd822e44 | ||
|
|
0fa6c7fbbf | ||
|
|
68fbbf0156 | ||
|
|
da36a5f8de | ||
|
|
918090e02a | ||
|
|
022aee7729 | ||
|
|
a2f0cc29de | ||
|
|
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 | ||
|
|
464b05f6bf | ||
|
|
4b02b3f84a | ||
|
|
b1047d839e | ||
|
|
613afce705 | ||
|
|
af5b1beb64 | ||
|
|
84c673c5ba | ||
|
|
0c98589a7f | ||
|
|
10e7f48347 | ||
|
|
0a869fc9ed | ||
|
|
9c69b0cdde | ||
|
|
9d91cd496f | ||
|
|
384680ca13 | ||
|
|
1edcd9b86e | ||
|
|
280903e52b | ||
|
|
152d476f20 | ||
|
|
40bdb0ffd6 | ||
|
|
1e59dafd75 | ||
|
|
6eabd336ee | ||
|
|
003e019048 | ||
|
|
6b3b7ded2d | ||
|
|
485f123362 | ||
|
|
7265abf763 | ||
|
|
7ca5cf0e70 | ||
|
|
f3205ebf25 | ||
|
|
e8b9176736 | ||
|
|
13a7f8d3b7 | ||
|
|
434369ca7c | ||
|
|
c54d922f26 | ||
|
|
85d5e9ff23 | ||
|
|
ab19b86341 | ||
|
|
5e980ceef2 | ||
|
|
1cf60a01d5 | ||
|
|
ba4a1a68bd | ||
|
|
11f7a432c4 | ||
|
|
047546c59e | ||
|
|
75c14990e5 | ||
|
|
71bb50f64c | ||
|
|
1868e0ccf1 | ||
|
|
f8ca96a335 | ||
|
|
5a7c1f9edf | ||
|
|
359905a8ef | ||
|
|
8eee4e3e6c | ||
|
|
df2b9adc1e | ||
|
|
61004a25ec | ||
|
|
0de064eb35 | ||
|
|
1cb9382282 | ||
|
|
02887536f7 | ||
|
|
9100373af2 | ||
|
|
dc45d46e9c | ||
|
|
e9abf8a2d4 | ||
|
|
a1a3f0d492 | ||
|
|
331f6df9e5 | ||
|
|
e9b779aa77 | ||
|
|
42d124ed2b | ||
|
|
a9a78c74db | ||
|
|
d55030367e | ||
|
|
65990a1bf1 | ||
|
|
399109728f | ||
|
|
c1161e2377 | ||
|
|
66b0ed2669 | ||
|
|
f4d58e17ee | ||
|
|
6aa126be30 | ||
|
|
58d168bda2 | ||
|
|
5eace10d51 | ||
|
|
b8c6a42f49 | ||
|
|
ed11d33054 | ||
|
|
11a21b57e4 | ||
|
|
7321a4fd3d | ||
|
|
6249a54ba2 | ||
|
|
58c2228247 | ||
|
|
88d6b6ea3f | ||
|
|
66c0999901 | ||
|
|
edfea9894e | ||
|
|
fd1a8f039a | ||
|
|
49126fa8e2 | ||
|
|
7da2fd5b8b | ||
|
|
eb3e6e4c6a | ||
|
|
4197ae0128 | ||
|
|
a7216ecca1 | ||
|
|
4689571c24 | ||
|
|
4c62776044 | ||
|
|
8021fb25d0 | ||
|
|
762ddaea65 | ||
|
|
05ffcf706b | ||
|
|
dded11004f | ||
|
|
f1284ab96b | ||
|
|
3aca4cbe20 | ||
|
|
015c34bb38 | ||
|
|
a49472349d | ||
|
|
0946c7c138 | ||
|
|
5180536cc3 | ||
|
|
27e43d037e | ||
|
|
feae420450 | ||
|
|
6995a2e1ba | ||
|
|
0f2757f000 | ||
|
|
8a0027c73a | ||
|
|
c6c9feb3a2 | ||
|
|
b0f5f4cb13 | ||
|
|
f6097ef2b7 | ||
|
|
bb88697354 | ||
|
|
762a019a49 | ||
|
|
f44bab0e0c | ||
|
|
883dbe2933 | ||
|
|
6753e217a9 | ||
|
|
e7f373496e | ||
|
|
3158082b16 | ||
|
|
814407ccec | ||
|
|
155f4f7471 | ||
|
|
90d45d2abf | ||
|
|
cd57e354fe | ||
|
|
a0ef20a264 | ||
|
|
4ca62916f5 | ||
|
|
41590f2e50 | ||
|
|
03dc8ddf81 | ||
|
|
79208a7383 | ||
|
|
3680f1e599 | ||
|
|
c4779e5a35 | ||
|
|
aad34f3679 | ||
|
|
0047ca454f | ||
|
|
07a1052c69 | ||
|
|
ba6a97557a | ||
|
|
e7f79c9076 | ||
|
|
9eaa636908 | ||
|
|
997c25620a | ||
|
|
bbcacec6ec | ||
|
|
313a6574c1 | ||
|
|
5ca5661c23 | ||
|
|
cb0887ffa8 | ||
|
|
1f9378af9f | ||
|
|
5c921589f1 | ||
|
|
130e1263b6 | ||
|
|
296ff6de96 | ||
|
|
5e951299b2 | ||
|
|
3ff93a4216 | ||
|
|
21ac860e27 | ||
|
|
164bb6c78c | ||
|
|
381c12547f | ||
|
|
060d6955e1 | ||
|
|
a6085db0ae | ||
|
|
bc04d89c32 | ||
|
|
e6b5d9656a |
51
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,51 +0,0 @@
|
||||
<!--
|
||||
Before submitting this issue, please make sure you have:
|
||||
|
||||
1. Filled out this form completely, the only optional field is "additional info".
|
||||
- Use as many details as possible and state the problem clearly.
|
||||
2. Proof-read your ENTIRE issue report.
|
||||
- Grammar and spelling mistakes make issue reports harder to understand.
|
||||
3. Made sure your problem is not caused by an issue in your own modpack.
|
||||
- We provide support for MultiMC, not your modpack. Problems with your modpack will be ignored.
|
||||
4. Given the issue a descriptive title.
|
||||
- A good title includes a brief summary of the issue and avoids things such as "Help" and "What?!".
|
||||
Use of UPPERCASE is discouraged, as it reads like someone is screaming.
|
||||
5. Place all information below the ---- of lines.
|
||||
- It makes the issue look pretty
|
||||
|
||||
If you believe your issue to be a bug, please make sure you check the wiki page: https://github.com/MultiMC/MultiMC5/wiki/Report-a-Bug
|
||||
-->
|
||||
|
||||
System Information
|
||||
-----------------------------
|
||||
MultiMC version:
|
||||
|
||||
Operating System:
|
||||
|
||||
Summary of the issue or suggestion:
|
||||
----------------------------------------------
|
||||
|
||||
|
||||
What should happen:
|
||||
------------------------------
|
||||
|
||||
|
||||
Steps to reproduce the issue (Add more if needed):
|
||||
-------------------------------------------------------------
|
||||
1.
|
||||
|
||||
2.
|
||||
|
||||
3.
|
||||
|
||||
Suspected cause:
|
||||
---------------------------
|
||||
|
||||
|
||||
Logs/Screenshots:
|
||||
----------------------------
|
||||
[//]: # (Please refer to https://github.com/MultiMC/MultiMC5/wiki/Log-Upload for instructions on how to attach your logs.)
|
||||
|
||||
|
||||
Additional Info:
|
||||
---------------------------
|
||||
51
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Bug Report
|
||||
description: File a bug report
|
||||
labels: [bug, needs-triage]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
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/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/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
|
||||
attributes:
|
||||
label: Operating System
|
||||
description: If you know this bug occurs on multiple operating systems, select all you have tested.
|
||||
multiple: true
|
||||
options:
|
||||
- Windows
|
||||
- macOS
|
||||
- Linux
|
||||
- Other
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description of bug
|
||||
description: What did you expect to happen, what happened, and why is it incorrect?
|
||||
placeholder: The cat button should show a cat, but it showed a dog instead!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: A bulleted list, or an exported instance if relevant.
|
||||
placeholder: "* Press the cat button"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Suspected cause
|
||||
description: If you know what could be causing this bug, describe it here.
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: This issue is unique
|
||||
options:
|
||||
- label: I have searched the issue tracker and did not find an issue describing my bug.
|
||||
required: true
|
||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
blank_issues_enabled: true
|
||||
38
.github/ISSUE_TEMPLATE/suggestion.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: Suggestion
|
||||
description: Make a suggestion
|
||||
labels: [idea, needs-triage]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### Use this form to suggest a feature for MultiMC.
|
||||
- type: input
|
||||
attributes:
|
||||
label: Role
|
||||
description: In what way do you use MultiMC that needs this feature?
|
||||
placeholder: I play modded Minecraft.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Suggestion
|
||||
description: What do you want MultiMC to do?
|
||||
placeholder: I want the cat button to meow.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Benefit
|
||||
description: Why do you need MultiMC to do this?
|
||||
placeholder: so that I can always hear a cat when I need to.
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: This suggestion is unique
|
||||
options:
|
||||
- label: I have searched the issue tracker and did not find an issue describing my suggestion, especially not one that has been rejected.
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: You may use the editor below to elaborate further.
|
||||
20
.github/workflows/dispatch.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Dispatcher
|
||||
on:
|
||||
push:
|
||||
branches: ['6']
|
||||
jobs:
|
||||
dispatch:
|
||||
name: Dispatch
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Extract branch name
|
||||
shell: bash
|
||||
run: echo "branch=$(echo ${GITHUB_REF#refs/heads/})" >>$GITHUB_OUTPUT
|
||||
id: extract_branch
|
||||
- name: Dispatch to workflows
|
||||
run: |
|
||||
curl -H "Accept: application/vnd.github.everest-preview+json" \
|
||||
-H "Authorization: token ${{ secrets.DISPATCH_TOKEN }}" \
|
||||
--request POST \
|
||||
--data '{"event_type": "push_to_main_repo", "client_payload": { "branch": "${{ steps.extract_branch.outputs.branch }}" }}' https://api.github.com/repos/MultiMC/Build/dispatches
|
||||
286
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,286 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ develop ]
|
||||
pull_request:
|
||||
branches: [ develop ]
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 0 1 * *"
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
name: build-linux
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32
|
||||
sudo add-apt-repository 'deb http://dk.archive.ubuntu.com/ubuntu/ bionic main'
|
||||
sudo add-apt-repository 'deb http://dk.archive.ubuntu.com/ubuntu/ bionic universe'
|
||||
sudo apt update
|
||||
sudo apt install libgl1-mesa-dev qttools5-dev g++-5 gcc-5
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
export JAVA_HOME=$JAVA_HOME_8_X64
|
||||
mkdir build
|
||||
cd build
|
||||
cmake \
|
||||
-DCMAKE_C_COMPILER=/usr/bin/gcc-5 \
|
||||
-DCMAKE_CXX_COMPILER=/usr/bin/g++-5 \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DLauncher_NOTIFICATION_URL:STRING=https://files.multimc.org/notifications.json \
|
||||
-DCMAKE_INSTALL_PREFIX:PATH=/home/runner/UltimMC/UltimMC \
|
||||
-DLauncher_UPDATER_BASE=https://files.multimc.org/update/ \
|
||||
-DLauncher_PASTE_EE_API_KEY:STRING=utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ \
|
||||
-DLauncher_ANALYTICS_ID:STRING=UA-87731965-2 \
|
||||
-DLauncher_LAYOUT=lin-nodeps \
|
||||
-DLauncher_BUILD_PLATFORM=lin64 \
|
||||
-DLauncher_BUG_TRACKER_URL=https://github.com/UltimMC/Launcher/issues \
|
||||
-DLauncher_EMBED_SECRETS=On \
|
||||
$GITHUB_WORKSPACE
|
||||
|
||||
- name: Compile
|
||||
run: |
|
||||
cd build
|
||||
make -j$(nproc)
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
cd build
|
||||
make test
|
||||
cmake -E remove_directory "/home/runner/UltimMC/UltimMC"
|
||||
|
||||
- name: Install
|
||||
run: |
|
||||
cd build
|
||||
make install
|
||||
chmod +x /home/runner/UltimMC/UltimMC/UltimMC
|
||||
chmod +x /home/runner/UltimMC/UltimMC/bin/UltimMC
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@main
|
||||
with:
|
||||
name: mmc-cracked-lin64
|
||||
path: /home/runner/UltimMC
|
||||
|
||||
build-windows:
|
||||
name: build-windows
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Cache Qt
|
||||
uses: actions/cache@main
|
||||
id: qt-cached
|
||||
with:
|
||||
path: "D:/Qt"
|
||||
key: ${{ runner.os }}-qt56-installed-d
|
||||
|
||||
- name: Cache Qt Installer
|
||||
uses: actions/cache@main
|
||||
if: steps.qt-cached.outputs.cache-hit != 'true'
|
||||
id: installer-cached
|
||||
with:
|
||||
path: "installer.exe"
|
||||
key: ${{ runner.os }}-qt56-installer
|
||||
|
||||
- name: Create QtAccount File
|
||||
if: steps.qt-cached.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
mkdir C:/Users/runneradmin/AppData/Roaming/Qt/
|
||||
curl https://gist.github.com/Neptune650/1086e0a3126be6a66580b71afcf8bd99/raw/797d8b90edf07ce88f265b38a573cc6b1fb45bfb/qtaccount.txt --output C:/Users/runneradmin/AppData/Roaming/Qt/qtaccount.ini
|
||||
|
||||
- name: Download Qt Installer
|
||||
if: steps.installer-cached.outputs.cache-hit != 'true' && steps.qt-cached.outputs.cache-hit != 'true'
|
||||
run: curl https://download.qt.io/new_archive/qt/5.6/5.6.3/qt-opensource-windows-x86-mingw492-5.6.3.exe --output installer.exe
|
||||
|
||||
- name: Download Qt non-Interactive Script
|
||||
if: steps.qt-cached.outputs.cache-hit != 'true'
|
||||
run: curl https://gist.githubusercontent.com/Neptune650/aa6c051abc17e7d9d609add7f6dfd16a/raw/074dedb7525c0ffc010b39871615b008c2efbcd6/qt-installer-noninteractive.qs --output nonInteractive.qs
|
||||
|
||||
- name: Install Qt
|
||||
if: steps.qt-cached.outputs.cache-hit != 'true'
|
||||
shell: cmd
|
||||
run: installer.exe -v --script nonInteractive.qs --silent
|
||||
|
||||
- name: Setup CMake
|
||||
run: |
|
||||
curl -L https://github.com/Kitware/CMake/releases/download/v3.30.4/cmake-3.30.4-windows-i386.zip -o cmake.zip
|
||||
unzip cmake.zip
|
||||
|
||||
- name: Setup JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '8'
|
||||
architecture: x86
|
||||
|
||||
- name: Setup zlib
|
||||
run: |
|
||||
mkdir zlib
|
||||
cd zlib
|
||||
C:\msys64\usr\bin\wget.exe -O zlib.zip https://downloads.sourceforge.net/project/gnuwin32/zlib/1.2.3/zlib-1.2.3-bin.zip
|
||||
C:\msys64\usr\bin\wget.exe -O zliblibs.zip https://downloads.sourceforge.net/project/gnuwin32/zlib/1.2.3/zlib-1.2.3-lib.zip
|
||||
unzip zlib.zip
|
||||
unzip zliblibs.zip
|
||||
|
||||
- name: Setup OpenSSL
|
||||
run: |
|
||||
mkdir OpenSSL
|
||||
cd OpenSSL
|
||||
curl -L https://files.catbox.moe/ctwswu.dll -o libeay32.dll
|
||||
curl -L https://files.catbox.moe/ie9e77.dll -o ssleay32.dll
|
||||
|
||||
- name: Build
|
||||
shell: cmd
|
||||
if: steps.build-cached.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
for /f "tokens=*" %%n in ('powershell -NoLogo -Command "$(ls $pwd\cmake-*-windows-i386\bin).Fullname"') do @(set PATHCM=%%n)
|
||||
set PATH=D:\Qt\5.6.3\mingw49_32\bin;D:\Qt\Tools\mingw492_32\bin;
|
||||
set PATH=%CD%\zlib;%CD%\zlib\bin;%CD%\zlib\lib;%CD%\zlib\include;%PATH%
|
||||
set PATH=%CD%\OpenSSL;%PATH%
|
||||
set PATH=%PATHCM%;%PATH%
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ^
|
||||
-DCMAKE_C_COMPILER=gcc ^
|
||||
-DCMAKE_CXX_COMPILER=g++ ^
|
||||
-DCMAKE_BUILD_TYPE=Release ^
|
||||
-DLauncher_NOTIFICATION_URL:STRING=https://files.multimc.org/notifications.json ^
|
||||
-DCMAKE_INSTALL_PREFIX:PATH="D:/UltimMC/UltimMC" ^
|
||||
-DCMAKE_PREFIX_PATH="D:\Qt\5.6.3\mingw49_32" ^
|
||||
-DQt5_DIR="D:\Qt\5.6.3\mingw49_32" ^
|
||||
-DLauncher_UPDATER_BASE=https://files.multimc.org/update/ ^
|
||||
-DLauncher_PASTE_EE_API_KEY:STRING=utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ ^
|
||||
-DLauncher_ANALYTICS_ID:STRING=UA-87731965-2 ^
|
||||
-DLauncher_LAYOUT=win-bundle ^
|
||||
-DLauncher_BUILD_PLATFORM=win32 ^
|
||||
-DLauncher_BUG_TRACKER_URL=https://github.com/UltimMC/Launcher/issues ^
|
||||
-DLauncher_EMBED_SECRETS=On ^
|
||||
-G "MinGW Makefiles" ^
|
||||
..
|
||||
|
||||
- name: Compile
|
||||
shell: cmd
|
||||
run: |
|
||||
for /f "tokens=*" %%n in ('powershell -NoLogo -Command "$(ls $pwd\cmake-*-windows-i386\bin).Fullname"') do @(set PATHCM=%%n)
|
||||
set PATH=D:\Qt\5.6.3\mingw49_32\bin;D:\Qt\Tools\mingw492_32\bin;
|
||||
set PATH=%CD%\zlib;%CD%\zlib\bin;%PATH%
|
||||
set PATH=%CD%\OpenSSL;%PATH%
|
||||
set PATH=%PATHCM%;%PATH%
|
||||
set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;%PATH%
|
||||
cd build
|
||||
mingw32-make -j%NUMBER_OF_PROCESSORS%
|
||||
|
||||
- name: Test
|
||||
shell: cmd
|
||||
run: |
|
||||
for /f "tokens=*" %%n in ('powershell -NoLogo -Command "$(ls $pwd\cmake-*-windows-i386\bin).Fullname"') do @(set PATHCM=%%n)
|
||||
set PATH=D:\Qt\5.6.3\mingw49_32\bin;D:\Qt\Tools\mingw492_32\bin;
|
||||
set PATH=%CD%\zlib;%CD%\zlib\bin;%PATH%
|
||||
set PATH=%CD%\OpenSSL;%PATH%
|
||||
set PATH=%PATHCM%;%PATH%
|
||||
set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;%PATH%
|
||||
cd build
|
||||
mingw32-make test
|
||||
cmake -E remove_directory "D:/UltimMC/UltimMC"
|
||||
|
||||
- name: Install
|
||||
shell: cmd
|
||||
run: |
|
||||
for /f "tokens=*" %%n in ('powershell -NoLogo -Command "$(ls $pwd\cmake-*-windows-i386\bin).Fullname"') do @(set PATHCM=%%n)
|
||||
set PATH=D:\Qt\5.6.3\mingw49_32\bin;D:\Qt\Tools\mingw492_32\bin;
|
||||
set PATH=%CD%\zlib;%CD%\zlib\bin;%PATH%
|
||||
set PATH=%CD%\OpenSSL;%PATH%
|
||||
set PATH=%PATHCM%;%PATH%
|
||||
set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;%PATH%
|
||||
cd build
|
||||
mingw32-make install
|
||||
|
||||
- name: Copy OpenSSL
|
||||
shell: cmd
|
||||
run: |
|
||||
cp OpenSSL/ssleay32.dll D:/UltimMC/UltimMC/ssleay32.dll
|
||||
cp OpenSSL/libeay32.dll D:/UltimMC/UltimMC/libeay32.dll
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@main
|
||||
with:
|
||||
name: mmc-cracked-win32
|
||||
path: "D:/UltimMC"
|
||||
|
||||
build-mac:
|
||||
name: build-mac
|
||||
runs-on: macos-13
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Cache Dependencies
|
||||
uses: actions/cache@main
|
||||
with:
|
||||
path: /Users/runner/Library/Caches/Homebrew
|
||||
key: ${{ runner.os }}-deps-cache
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
brew cleanup
|
||||
brew install qt@5
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake \
|
||||
-DCMAKE_C_COMPILER=/usr/bin/clang \
|
||||
-DCMAKE_CXX_COMPILER=/usr/bin/clang++ \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DLauncher_NOTIFICATION_URL:STRING=https://files.multimc.org/notifications.json \
|
||||
-DCMAKE_INSTALL_PREFIX:PATH="/Users/runner/work/UltimMC/build/dist" \
|
||||
-DCMAKE_PREFIX_PATH="$(brew --prefix qt@5)/lib/cmake" \
|
||||
-DQt5_DIR="$(brew --prefix qt@5)" \
|
||||
-DLauncher_UPDATER_BASE=https://files.multimc.org/update/ \
|
||||
-DLauncher_PASTE_EE_API_KEY:STRING=utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ \
|
||||
-DLauncher_ANALYTICS_ID:STRING=UA-87731965-2 \
|
||||
-DLauncher_LAYOUT=mac-bundle \
|
||||
-DLauncher_BUILD_PLATFORM=osx64-5.15.2 \
|
||||
-DLauncher_BUG_TRACKER_URL=https://github.com/UltimMC/Launcher/issues \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 \
|
||||
-DLauncher_EMBED_SECRETS=On \
|
||||
$GITHUB_WORKSPACE
|
||||
|
||||
- name: Compile
|
||||
run: |
|
||||
cd build
|
||||
make -j$(sysctl -n hw.logicalcpu)
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
cd build
|
||||
make test
|
||||
cmake -E remove_directory "/Users/runner/work/UltimMC/build/dist"
|
||||
|
||||
- name: Install
|
||||
run: |
|
||||
cd build
|
||||
make install
|
||||
chmod +x /Users/runner/work/UltimMC/build/dist/UltimMC.app/Contents/MacOS/UltimMC
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@main
|
||||
with:
|
||||
name: mmc-cracked-osx64
|
||||
path: /Users/runner/work/UltimMC/build/dist
|
||||
10
.gitignore
vendored
@@ -3,7 +3,6 @@ Thumbs.db
|
||||
.user
|
||||
.directory
|
||||
resources/CMakeFiles
|
||||
resources/MultiMCLauncher.jar
|
||||
*~
|
||||
*.swp
|
||||
html/
|
||||
@@ -16,11 +15,17 @@ CMakeLists.txt.user.*
|
||||
/.settings
|
||||
/.idea
|
||||
cmake-build-*/
|
||||
Debug
|
||||
.cache
|
||||
|
||||
# Build dirs
|
||||
build
|
||||
/build-*
|
||||
|
||||
# Install dirs
|
||||
install
|
||||
/install-*
|
||||
|
||||
# Ctags File
|
||||
tags
|
||||
|
||||
@@ -29,3 +34,6 @@ tags
|
||||
|
||||
#OSX Stuff
|
||||
.DS_Store
|
||||
|
||||
branding/
|
||||
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
|
||||
|
||||
185
BUILD.md
@@ -1,185 +0,0 @@
|
||||
Build Instructions
|
||||
==================
|
||||
|
||||
# Contents
|
||||
|
||||
* [Note](#note)
|
||||
* [Getting the source](#source)
|
||||
* [Linux](#linux)
|
||||
* [Windows](#windows)
|
||||
* [OS X](#os-x)
|
||||
|
||||
# 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
|
||||
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.
|
||||
|
||||
|
||||
# Getting the source
|
||||
|
||||
Clone the source code using git and grab all the submodules:
|
||||
|
||||
```
|
||||
git clone git@github.com:MultiMC/MultiMC5.git
|
||||
git submodule init
|
||||
git submodule update
|
||||
```
|
||||
|
||||
# Linux
|
||||
|
||||
Getting the project to build and run on Linux is easy if you use any modern and up-to-date linux distribution.
|
||||
|
||||
## Build dependencies
|
||||
* A C++ compiler capable of building C++11 code.
|
||||
* Qt 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.
|
||||
* cmake 3.1 or newer
|
||||
* zlib (for example, `zlib1g-dev`)
|
||||
* Java JDK 8 (for example, `openjdk-8-jdk`)
|
||||
* GL headers (for example, `libgl1-mesa-dev`)
|
||||
|
||||
### Building from command line
|
||||
You need a source folder, a build folder and an install folder.
|
||||
|
||||
Let's say you want everything in `~/MultiMC/`:
|
||||
|
||||
```
|
||||
# make all the folders
|
||||
mkdir ~/MultiMC && cd ~/MultiMC
|
||||
mkdir build
|
||||
mkdir install
|
||||
# clone the complete source
|
||||
git clone --recursive git@github.com:MultiMC/MultiMC5.git src
|
||||
# configure the project
|
||||
cd build
|
||||
cmake -DCMAKE_INSTALL_PREFIX=../install ../src
|
||||
# build & install (use -j with the number of cores your CPU has)
|
||||
make -j8 install
|
||||
```
|
||||
|
||||
You can use IDEs like KDevelop or QtCreator to open the CMake project if you want to work on the code.
|
||||
|
||||
### Installing Qt using the installer (optional)
|
||||
1. Run the Qt installer.
|
||||
2. Choose a place to install Qt.
|
||||
3. Choose the components you want to install.
|
||||
- You need Qt 5.6.x 64-bit ticked.
|
||||
- You need Tools/Qt Creator ticked.
|
||||
- Other components are selected by default, you can untick them if you don't need them.
|
||||
4. Accept the license agreements.
|
||||
5. Double check the install details and then click "Install".
|
||||
- Installation can take a very long time, go grab a cup of tea or something and let it work.
|
||||
|
||||
### Loading the project in Qt Creator (optional)
|
||||
1. Open Qt Creator.
|
||||
2. Choose `File->Open File or Project`.
|
||||
3. Navigate to the MultiMC5 source folder you cloned and choose CMakeLists.txt.
|
||||
4. Read the instructions that just popped up about a build location and choose one.
|
||||
5. You should see "Run CMake" in the window.
|
||||
- Make sure that Generator is set to "Unix Generator (Desktop Qt 5.6.x GCC 64bit)".
|
||||
- Hit the "Run CMake" button.
|
||||
- You'll see warnings and it might not be clear that it succeeded until you scroll to the bottom of the window.
|
||||
- Hit "Finish" if CMake ran successfully.
|
||||
6. Cross your fingers and press the Run button (bottom left of Qt Creator).
|
||||
- If the project builds successfully it will run and the MultiMC5 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))!**
|
||||
|
||||
# Windows
|
||||
|
||||
Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt Creator. The project will simply not compile using Microsoft build tools, because that's not something we do. If it does compile, it is by chance only.
|
||||
|
||||
## Dependencies
|
||||
* [Qt 5.6+ Development tools](http://qt-project.org/downloads) -- Qt Online Installer for Windows
|
||||
* [OpenSSL](http://slproweb.com/products/Win32OpenSSL.html) -- Newest Win32 OpenSSL Light
|
||||
- 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)
|
||||
* [CMake](http://www.cmake.org/cmake/resources/software.html) -- Windows (Win32 Installer)
|
||||
|
||||
Put it somewhere on the `PATH`, so that it is accessible from the console.
|
||||
|
||||
## Getting set up
|
||||
|
||||
### Installing Qt
|
||||
1. Run the Qt installer
|
||||
2. Choose a place to install Qt (C:\Qt is the default),
|
||||
3. Choose the components you want to install
|
||||
- You need Qt 5.6 (32 bit) ticked,
|
||||
- You need Tools/Qt Creator ticked,
|
||||
- Other components are selected by default, you can untick them if you don't need them.
|
||||
4. Accept the license agreements,
|
||||
5. Double check the install details and then click "Install"
|
||||
- Installation can take a very long time, go grab a cup of tea or something and let it work.
|
||||
|
||||
### Installing OpenSSL
|
||||
1. Run the OpenSSL installer,
|
||||
2. It's best to choose the option to copy OpenSSL DLLs to the `/bin` directory
|
||||
- If you do this you'll need to add that directory (the default being `C:\OpenSSL-Win32\bin`) to your PATH system variable (Google how to do this, or use this guide for Java: http://www.java.com/en/download/help/path.xml).
|
||||
|
||||
### Installing CMake
|
||||
1. Run the CMake installer,
|
||||
2. It's easiest if you choose to add CMake to the PATH for all users,
|
||||
- If you don't choose to do this, remember where you installed CMake.
|
||||
|
||||
### Loading the project
|
||||
1. Open Qt Creator,
|
||||
2. Choose File->Open File or Project,
|
||||
3. Navigate to the MultiMC5 source folder you cloned and choose CMakeLists.txt,
|
||||
4. Read the instructions that just popped up about a build location and choose one,
|
||||
5. If you chose not to add CMake to the system PATH, tell Qt Creator where you installed it,
|
||||
- Otherwise you can skip this step.
|
||||
6. You should see "Run CMake" in the window,
|
||||
- Make sure that Generator is set to "MinGW Generator (Desktop Qt 5.6.x MinGW 32bit)",
|
||||
- Hit the "Run CMake" button,
|
||||
- You'll see warnings and it might not be clear that it succeeded until you scroll to the bottom of the window.
|
||||
- Hit "Finish" if CMake ran successfully.
|
||||
7. Cross your fingers and press the Run button (bottom left of Qt Creator)!
|
||||
- If the project builds successfully it will run and the MultiMC5 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.
|
||||
|
||||
**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.
|
||||
3. Make a build directory, and change directory to the directory and do `cmake -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=C:\Path\that\makes\sense\for\you`. By default, it will install to C:\Program Files (x86), which you might not want, if you want a local installation. If you want to install it to that directory, make sure to run the command window as administrator.
|
||||
3. Do `mingw32-make -jX`, where X is the number of cores your CPU has plus one.
|
||||
4. Now to wait for it to compile. This could take some time. Hopefully it compiles properly.
|
||||
5. Run the command `mingw32-make install`, and it should install MultiMC, to whatever the `-DCMAKE_INSTALL_PREFIX` was.
|
||||
6. In most cases, whenever compiling, the OpenSSL dll's aren't put into the directory to where MultiMC installs, meaning you cannot log in. The best way to fix this is just to do `copy C:\OpenSSL-Win32\*.dll C:\Where\you\installed\MultiMC\to`. This should copy the required OpenSSL dll's to log in.
|
||||
|
||||
# OS X
|
||||
|
||||
### Install prerequisites:
|
||||
* install homebrew
|
||||
* then:
|
||||
|
||||
```
|
||||
brew install qt5
|
||||
brew tap homebrew/versions
|
||||
brew install gcc48
|
||||
brew install cmake
|
||||
```
|
||||
|
||||
### 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 https://github.com/MultiMC/MultiMC5.git
|
||||
git submodule init
|
||||
git submodule update
|
||||
cd MultiMC5
|
||||
mkdir build
|
||||
cd build
|
||||
export CMAKE_PREFIX_PATH=/usr/local/opt/qt5
|
||||
export CC=/usr/local/bin/gcc-4.8
|
||||
export CXX=/usr/local/bin/g++-4.8
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/Users/YOU/some/path/that/makes/sense/
|
||||
make
|
||||
make install
|
||||
```
|
||||
|
||||
**These build instructions were taken and adapted from https://gist.github.com/number5/7250865 If they don't work for you, let us know on IRC ([Esper/#MultiMC](http://webchat.esper.net/?nick=&channels=MultiMC))!**
|
||||
219
CMakeLists.txt
@@ -1,18 +1,41 @@
|
||||
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(AUTHOR_WARNING "You are building MultiMC in-source. This is NOT recommended!")
|
||||
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()
|
||||
|
||||
option(Launcher_RUN_CLANG_TIDY "Run clang-tidy with the compiler." OFF)
|
||||
if(Launcher_RUN_CLANG_TIDY)
|
||||
find_program(CLANG_TIDY_COMMAND NAMES clang-tidy)
|
||||
if(NOT CLANG_TIDY_COMMAND)
|
||||
message(WARNING "CMake_RUN_CLANG_TIDY is ON but clang-tidy is not found!")
|
||||
set(DO_CLANG_TIDY "" CACHE STRING "" FORCE)
|
||||
else()
|
||||
set(CLANG_TIDY_CHECKS "-modernize-use-trailing-return-type,-readability-magic-numbers,-modernize-avoid-c-arrays")
|
||||
set(DO_CLANG_TIDY "${CLANG_TIDY_COMMAND};-checks=${CLANG_TIDY_CHECKS};-header-filter='${CMAKE_SOURCE_DIR}/src/*'")
|
||||
endif()
|
||||
# TODO: make this per-target, differentiate between libraries and main codebase
|
||||
set(CMAKE_CXX_CLANG_TIDY ${DO_CLANG_TIDY})
|
||||
endif()
|
||||
|
||||
|
||||
##################################### Set CMake options #####################################
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
@@ -32,52 +55,85 @@ set(CMAKE_C_STANDARD_REQUIRED true)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
include(GenerateExportHeader)
|
||||
set(CMAKE_CXX_FLAGS " -Wall -pedantic -Werror -Wno-deprecated-declarations -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 -O3 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS " -Wall -pedantic -Wno-deprecated-declarations -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 ${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE " -O3 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
if(UNIX AND APPLE)
|
||||
set(CMAKE_CXX_FLAGS " -stdlib=libc++ ${CMAKE_CXX_FLAGS}")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror -Werror=return-type -O0")
|
||||
|
||||
# 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 11)
|
||||
set(Launcher_VERSION_MAJOR 0)
|
||||
set(Launcher_VERSION_MINOR 7)
|
||||
set(Launcher_VERSION_HOTFIX 0)
|
||||
|
||||
# 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(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)
|
||||
|
||||
# API Keys
|
||||
# NOTE: These API keys are here for convenience. If you rebrand this software or intend to break the terms of service
|
||||
# of these platforms, please change these API keys beforehand.
|
||||
# Be aware that if you were to use these API keys for malicious purposes they might get revoked, which might cause
|
||||
# breakage to thousands of users.
|
||||
# If you don't plan to use these features of this software, you can just remove these values.
|
||||
|
||||
# By using this key in your builds you accept the terms of use laid down in
|
||||
# https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use
|
||||
set(Launcher_MSA_CLIENT_ID "f4404707-7bbe-4e40-80ba-85fb2bb825a1" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
|
||||
|
||||
#### 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 ################################
|
||||
|
||||
@@ -102,47 +158,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-2019 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-2023 ${Launcher_Copyright}")
|
||||
|
||||
# directories to look for dependencies
|
||||
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
||||
@@ -151,32 +215,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")
|
||||
@@ -188,28 +229,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 ".")
|
||||
@@ -218,7 +244,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})
|
||||
@@ -237,7 +263,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)
|
||||
|
||||
@@ -248,16 +274,17 @@ add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
|
||||
add_subdirectory(libraries/javacheck) # java compatibility checker
|
||||
add_subdirectory(libraries/xz-embedded) # xz compression
|
||||
add_subdirectory(libraries/quazip) # zip manipulation library
|
||||
add_subdirectory(libraries/pack200) # java pack200 compression
|
||||
add_subdirectory(libraries/rainbow) # Qt extension for colors
|
||||
add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader
|
||||
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
|
||||
add_subdirectory(libraries/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(api/logic)
|
||||
add_subdirectory(api/gui)
|
||||
add_subdirectory(buildconfig)
|
||||
|
||||
# NOTE: this must always be last to appease the CMake deity of quirky install command evaluation order.
|
||||
add_subdirectory(application)
|
||||
add_subdirectory(launcher)
|
||||
|
||||
173
COPYING.md
@@ -1,6 +1,8 @@
|
||||
# MultiMC
|
||||
|
||||
Copyright 2012-2019 MultiMC Contributors
|
||||
Portions are licensed under Apache 2.0 License:
|
||||
|
||||
Copyright 2012-2021 MultiMC Contributors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
@@ -13,6 +15,67 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Portions are licensed under MS-PL:
|
||||
|
||||
This license governs use of the accompanying software. If you use the
|
||||
software, you accept this license. If you do not accept the license,
|
||||
do not use the software.
|
||||
|
||||
1. Definitions
|
||||
The terms "reproduce," "reproduction," "derivative works," and
|
||||
"distribution" have the same meaning here as under U.S. copyright law.
|
||||
|
||||
A "contribution" is the original software, or any additions or
|
||||
changes to the software.
|
||||
|
||||
A "contributor" is any person that distributes its contribution
|
||||
under this license.
|
||||
|
||||
"Licensed patents" are a contributor's patent claims that read
|
||||
directly on its contribution.
|
||||
|
||||
2. Grant of Rights
|
||||
|
||||
(A) Copyright Grant- Subject to the terms of this license,
|
||||
including the license conditions and limitations in section 3,
|
||||
each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
copyright license to reproduce its contribution, prepare derivative
|
||||
works of its contribution, and distribute its contribution or any
|
||||
derivative works that you create.
|
||||
|
||||
(B) Patent Grant- Subject to the terms of this license, including
|
||||
the license conditions and limitations in section 3, each contributor
|
||||
grants you a non-exclusive, worldwide, royalty-free license under its
|
||||
licensed patents to make, have made, use, sell, offer for sale, import,
|
||||
and/or otherwise dispose of its contribution in the software or derivative
|
||||
works of the contribution in the software.
|
||||
|
||||
3. Conditions and Limitations
|
||||
|
||||
(A) No Trademark License- This license does not grant you rights to
|
||||
use any contributors' name, logo, or trademarks.
|
||||
|
||||
(B) If you bring a patent claim against any contributor over patents
|
||||
that you claim are infringed by the software, your patent license
|
||||
from such contributor to the software ends automatically.
|
||||
|
||||
(C) If you distribute any portion of the software, you must retain all
|
||||
copyright, patent, trademark, and attribution notices that are present
|
||||
in the software.
|
||||
|
||||
(D) If you distribute any portion of the software in source code form,
|
||||
you may do so only under this license by including a complete copy of
|
||||
this license with your distribution. If you distribute any portion of
|
||||
the software in compiled or object code form, you may only do so under
|
||||
a license that complies with this license.
|
||||
|
||||
(E) The software is licensed "as-is." You bear the risk of using it.
|
||||
The contributors give no express warranties, guarantees or conditions.
|
||||
You may have additional consumer rights under your local laws which
|
||||
this license cannot change. To the extent permitted under your local
|
||||
laws, the contributors exclude the implied warranties of merchantability,
|
||||
fitness for a particular purpose and non-infringement.
|
||||
|
||||
# MinGW runtime (Windows)
|
||||
|
||||
Copyright (c) 2012 MinGW.org project
|
||||
@@ -128,35 +191,6 @@
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
# Pack200
|
||||
|
||||
The GNU General Public License (GPL)
|
||||
|
||||
Version 2, June 1991
|
||||
|
||||
+ "CLASSPATH" EXCEPTION TO THE GPL
|
||||
|
||||
Certain source files distributed by Oracle America and/or its affiliates are
|
||||
subject to the following clarification and special exception to the GPL, but
|
||||
only where Oracle has expressly included in the particular source file's header
|
||||
the words "Oracle designates this particular file as subject to the "Classpath"
|
||||
exception as provided by Oracle in the LICENSE file that accompanied this code."
|
||||
|
||||
Linking this library statically or dynamically with other modules is making
|
||||
a combined work based on this library. Thus, the terms and conditions of
|
||||
the GNU General Public License cover the whole combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent modules,
|
||||
and to copy and distribute the resulting executable under terms of your
|
||||
choice, provided that you also meet, for each linked independent module,
|
||||
the terms and conditions of the license of that module. An independent
|
||||
module is a module which is not derived from or based on this library. If
|
||||
you modify this library, you may extend this exception to your version of
|
||||
the library, but you are not obligated to do so. If you do not wish to do
|
||||
so, delete this exception statement from your version.
|
||||
|
||||
# Quazip
|
||||
|
||||
Copyright (C) 2005-2011 Sergey A. Tachenov
|
||||
@@ -252,3 +286,82 @@
|
||||
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.
|
||||
|
||||
# optional-bare
|
||||
|
||||
Code from https://github.com/martinmoene/optional-bare/
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
# tomlc99
|
||||
|
||||
MIT License
|
||||
|
||||
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.
|
||||
|
||||
96
README.md
@@ -1,79 +1,61 @@
|
||||
<p align="center">
|
||||
<img src="https://avatars2.githubusercontent.com/u/5411890" alt="MultiMC logo"/>
|
||||
|
||||
<img width="150" src="https://github.com/UltimMC/Launcher/assets/62727185/48d60c15-23ae-4d48-84b8-813f72db6cdd">
|
||||
|
||||
<p align="center">UltimMC is a custom launcher for Minecraft which allows you to manage multiple instances and use offline ("cracked") accounts while keeping as close as possible to the original.</p>
|
||||
|
||||
</p>
|
||||
|
||||
MultiMC 5
|
||||
=========
|
||||
> [!IMPORTANT]
|
||||
> This project is a **fork** of MultiMC. </br> </br>
|
||||
> This software is provided without any warranty, so please don't contact the main
|
||||
> MultiMC developers in case anything goes wrong using this launcher. </br> </br>
|
||||
> Nonetheless, feel free to create an issue within this repository
|
||||
> in case you face an issue specific to UltimMC.
|
||||
|
||||
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.
|
||||
## Downloading
|
||||
|
||||
- All the available downloads can be found [here](https://nightly.link/UltimMC/Launcher/workflows/main/develop). These builds are directly taken from our [GitHub Actions](https://github.com/UltimMC/Launcher/actions).
|
||||
|
||||
## 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.
|
||||
Direct downloads for specific platforms can be found below.
|
||||
|
||||
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.
|
||||
- *[Windows \(32-bit and 64-bit\)](https://nightly.link/UltimMC/Launcher/workflows/main/develop/mmc-cracked-win32.zip)*.
|
||||
|
||||
If you want to contribute, either talk to us on [Discord](https://discord.gg/0k2zsXGNHs0fE4Wm), [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.
|
||||
- *[Linux (64-bit)](https://nightly.link/UltimMC/Launcher/workflows/main/develop/mmc-cracked-lin64.zip)*.
|
||||
|
||||
### Building
|
||||
If you want to build MultiMC yourself, check [BUILD.md](BUILD.md) for build instructions.
|
||||
- *[macOS (10.14 and newer)](https://nightly.link/UltimMC/Launcher/workflows/main/develop/mmc-cracked-osx64.zip)*.
|
||||
|
||||
### Code formatting
|
||||
Just follow the existing formatting.
|
||||
> [!NOTE]
|
||||
> In the case you're using macOS then another additional step you might need to do
|
||||
> is to make `UltimMC` an executable by running the command `chmod +x UltimMC.app/Contents/MacOS/UltimMC` in the terminal.
|
||||
|
||||
In general:
|
||||
* Indent with 4 space unless it's in a submodule
|
||||
* Keep lists (of arguments, parameters, initializators...) as lists, not paragraphs.
|
||||
* Prefer readability over dogma.
|
||||
There's additionally a [.deb package](https://nightly.link/UltimMC/ultimmc-deb/workflows/ci/master/UltimMC.zip) for Debian/Ubuntu distributions.
|
||||
|
||||
And an AUR package as [ultimmc-bin](https://aur.archlinux.org/packages/ultimmc-bin). [](https://aur.archlinux.org/packages/ultimmc-bin)
|
||||
|
||||
## Translations
|
||||
Translations can be done [on crowdin](https://translate.multimc.org).
|
||||
## Installing and Using
|
||||
|
||||
## 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.
|
||||
1. Pick the correct download for your system.
|
||||
2. Uncompress it in your desired directory.
|
||||
3. Launch `UltimMC`.
|
||||
4. Go to account settings.
|
||||
6. A. Pick "Add Local" and you will be requested to use the username you desire, this can be anything.
|
||||
7. B. Pick "Add Ely.by" and add your Ely.by account by putting your email and password.
|
||||
8. Save it.
|
||||
9. Now enjoy the Launcher.
|
||||
|
||||
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).
|
||||
## Updating
|
||||
|
||||
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.
|
||||
To update the launcher replace all replaceable files and folders with the newer ones from any of the links listed above.
|
||||
|
||||
A better update system is in the works.
|
||||
|
||||
## License
|
||||
Copyright © 2013-2019 MultiMC Contributors
|
||||
## Forking
|
||||
|
||||
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).
|
||||
This project now includes our MSA API key in order to have functional Microsoft authentication within the launcher.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
This means you're accepting the:
|
||||
|
||||
## 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>
|
||||
- [Microsoft Identity Platform Terms of Use](https://learn.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
|
||||
|
||||
### 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>
|
||||
|
||||
### 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>
|
||||
|
||||
### 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>
|
||||
We humbly ask that in case you wish to fork UltimMC, please either remove the key by setting it empty (`""`) or by setting your own.
|
||||
|
||||
@@ -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,509 +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/URLConstants.cpp
|
||||
net/URLConstants.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/CreateServerResourcePacksFolder.cpp
|
||||
minecraft/launch/CreateServerResourcePacksFolder.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/ComponentList.cpp
|
||||
minecraft/ComponentList.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
|
||||
|
||||
# Forge and all things forge related
|
||||
minecraft/forge/ForgeXzDownload.h
|
||||
minecraft/forge/ForgeXzDownload.cpp
|
||||
|
||||
# Skin upload utilities
|
||||
minecraft/SkinUpload.cpp
|
||||
minecraft/SkinUpload.h
|
||||
)
|
||||
|
||||
add_unit_test(GradleSpecifier
|
||||
SOURCES minecraft/GradleSpecifier_test.cpp
|
||||
LIBS MultiMC_logic
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
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}
|
||||
)
|
||||
|
||||
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 xz-embedded MultiMC_unpack200 systeminfo MultiMC_quazip MultiMC_classparser ${NBT_NAME} ${ZLIB_LIBRARIES})
|
||||
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,209 +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("FTBPacks", QDir("cache/FTBPacks").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("%3@%1:%2 pass %4")
|
||||
.arg(proxy.hostName())
|
||||
.arg(proxy.port())
|
||||
.arg(proxy.user())
|
||||
.arg(proxy.password());
|
||||
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,411 +0,0 @@
|
||||
#include "InstanceImportTask.h"
|
||||
#include "BaseInstance.h"
|
||||
#include "FileSystem.h"
|
||||
#include "Env.h"
|
||||
#include "MMCZip.h"
|
||||
#include "NullInstance.h"
|
||||
#include "settings/INISettingsObject.h"
|
||||
#include "icons/IIconList.h"
|
||||
#include "icons/IconUtils.h"
|
||||
#include <QtConcurrentRun>
|
||||
|
||||
// FIXME: this does not belong here, it's Minecraft/Flame specific
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/ComponentList.h"
|
||||
#include "modplatform/flame/FileResolvingTask.h"
|
||||
#include "modplatform/flame/PackManifest.h"
|
||||
#include "Json.h"
|
||||
|
||||
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl)
|
||||
{
|
||||
m_sourceUrl = sourceUrl;
|
||||
}
|
||||
|
||||
void InstanceImportTask::executeTask()
|
||||
{
|
||||
InstancePtr newInstance;
|
||||
|
||||
if (m_sourceUrl.isLocalFile())
|
||||
{
|
||||
m_archivePath = m_sourceUrl.toLocalFile();
|
||||
processZipPack();
|
||||
}
|
||||
else
|
||||
{
|
||||
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
||||
m_downloadRequired = true;
|
||||
|
||||
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
|
||||
auto entry = ENV.metacache()->resolveEntry("general", path);
|
||||
entry->setStale(true);
|
||||
m_filesNetJob.reset(new NetJob(tr("Modpack download")));
|
||||
m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
|
||||
m_archivePath = entry->getFullPath();
|
||||
auto job = m_filesNetJob.get();
|
||||
connect(job, &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
||||
connect(job, &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
||||
connect(job, &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
||||
m_filesNetJob->start();
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceImportTask::downloadSucceeded()
|
||||
{
|
||||
processZipPack();
|
||||
m_filesNetJob.reset();
|
||||
}
|
||||
|
||||
void InstanceImportTask::downloadFailed(QString reason)
|
||||
{
|
||||
emitFailed(reason);
|
||||
m_filesNetJob.reset();
|
||||
}
|
||||
|
||||
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
|
||||
{
|
||||
setProgress(current / 2, total);
|
||||
}
|
||||
|
||||
void InstanceImportTask::processZipPack()
|
||||
{
|
||||
setStatus(tr("Extracting modpack"));
|
||||
QDir extractDir(m_stagingPath);
|
||||
qDebug() << "Attempting to create instance from" << m_archivePath;
|
||||
|
||||
// open the zip and find relevant files in it
|
||||
m_packZip.reset(new QuaZip(m_archivePath));
|
||||
if (!m_packZip->open(QuaZip::mdUnzip))
|
||||
{
|
||||
emitFailed(tr("Unable to open supplied modpack zip file."));
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList blacklist = {"instance.cfg", "manifest.json"};
|
||||
QString mmcFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
|
||||
QString flameFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json");
|
||||
QString root;
|
||||
if(!mmcFound.isNull())
|
||||
{
|
||||
// process as MultiMC instance/pack
|
||||
qDebug() << "MultiMC:" << mmcFound;
|
||||
root = mmcFound;
|
||||
m_modpackType = ModpackType::MultiMC;
|
||||
}
|
||||
else if(!flameFound.isNull())
|
||||
{
|
||||
// process as Flame pack
|
||||
qDebug() << "Flame:" << flameFound;
|
||||
root = flameFound;
|
||||
m_modpackType = ModpackType::Flame;
|
||||
}
|
||||
|
||||
if(m_modpackType == ModpackType::Unknown)
|
||||
{
|
||||
emitFailed(tr("Archive does not contain a recognized modpack type."));
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure we extract just the pack
|
||||
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
|
||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
|
||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &InstanceImportTask::extractAborted);
|
||||
m_extractFutureWatcher.setFuture(m_extractFuture);
|
||||
}
|
||||
|
||||
void InstanceImportTask::extractFinished()
|
||||
{
|
||||
m_packZip.reset();
|
||||
if (m_extractFuture.result().isEmpty())
|
||||
{
|
||||
emitFailed(tr("Failed to extract modpack"));
|
||||
return;
|
||||
}
|
||||
QDir extractDir(m_stagingPath);
|
||||
|
||||
qDebug() << "Fixing permissions for extracted pack files...";
|
||||
QDirIterator it(extractDir, QDirIterator::Subdirectories);
|
||||
while (it.hasNext())
|
||||
{
|
||||
auto filepath = it.next();
|
||||
QFileInfo file(filepath);
|
||||
auto permissions = QFile::permissions(filepath);
|
||||
auto origPermissions = permissions;
|
||||
if(file.isDir())
|
||||
{
|
||||
// Folder +rwx for current user
|
||||
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser;
|
||||
}
|
||||
else
|
||||
{
|
||||
// File +rw for current user
|
||||
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
|
||||
}
|
||||
if(origPermissions != permissions)
|
||||
{
|
||||
if(!QFile::setPermissions(filepath, permissions))
|
||||
{
|
||||
logWarning(tr("Could not fix permissions for %1").arg(filepath));
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Fixed" << filepath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(m_modpackType)
|
||||
{
|
||||
case ModpackType::Flame:
|
||||
processFlame();
|
||||
return;
|
||||
case ModpackType::MultiMC:
|
||||
processMultiMC();
|
||||
return;
|
||||
case ModpackType::Unknown:
|
||||
emitFailed(tr("Archive does not contain a recognized modpack type."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceImportTask::extractAborted()
|
||||
{
|
||||
emitFailed(tr("Instance import has been aborted."));
|
||||
return;
|
||||
}
|
||||
|
||||
void InstanceImportTask::processFlame()
|
||||
{
|
||||
const static QMap<QString,QString> forgemap = {
|
||||
{"1.2.5", "3.4.9.171"},
|
||||
{"1.4.2", "6.0.1.355"},
|
||||
{"1.4.7", "6.6.2.534"},
|
||||
{"1.5.2", "7.8.1.737"}
|
||||
};
|
||||
Flame::Manifest pack;
|
||||
try
|
||||
{
|
||||
QString configPath = FS::PathCombine(m_stagingPath, "manifest.json");
|
||||
Flame::loadManifest(pack, configPath);
|
||||
QFile::remove(configPath);
|
||||
}
|
||||
catch (const JSONValidationError &e)
|
||||
{
|
||||
emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
|
||||
return;
|
||||
}
|
||||
if(!pack.overrides.isEmpty())
|
||||
{
|
||||
QString overridePath = FS::PathCombine(m_stagingPath, pack.overrides);
|
||||
if (QFile::exists(overridePath))
|
||||
{
|
||||
QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
|
||||
if (!QFile::rename(overridePath, mcPath))
|
||||
{
|
||||
emitFailed(tr("Could not rename the overrides folder:\n") + pack.overrides);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logWarning(tr("The specified overrides folder (%1) is missing. Maybe the modpack was already used before?").arg(pack.overrides));
|
||||
}
|
||||
}
|
||||
|
||||
QString forgeVersion;
|
||||
for(auto &loader: pack.minecraft.modLoaders)
|
||||
{
|
||||
auto id = loader.id;
|
||||
if(id.startsWith("forge-"))
|
||||
{
|
||||
id.remove("forge-");
|
||||
forgeVersion = id;
|
||||
continue;
|
||||
}
|
||||
logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
|
||||
}
|
||||
|
||||
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||
instanceSettings->set("InstanceType", "OneSix");
|
||||
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
||||
auto mcVersion = pack.minecraft.version;
|
||||
// Hack to correct some 'special sauce'...
|
||||
if(mcVersion.endsWith('.'))
|
||||
{
|
||||
mcVersion.remove(QRegExp("[.]+$"));
|
||||
logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack."));
|
||||
}
|
||||
auto components = instance.getComponentList();
|
||||
components->buildingFromScratch();
|
||||
components->setComponentVersion("net.minecraft", mcVersion, true);
|
||||
if(!forgeVersion.isEmpty())
|
||||
{
|
||||
// FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata.
|
||||
if(forgeVersion == "recommended")
|
||||
{
|
||||
if(forgemap.contains(mcVersion))
|
||||
{
|
||||
forgeVersion = forgemap[mcVersion];
|
||||
}
|
||||
else
|
||||
{
|
||||
logWarning(tr("Could not map recommended forge version for Minecraft %1").arg(mcVersion));
|
||||
}
|
||||
}
|
||||
components->setComponentVersion("net.minecraftforge", forgeVersion);
|
||||
}
|
||||
if (m_instIcon != "default")
|
||||
{
|
||||
instance.setIconKey(m_instIcon);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(pack.name.contains("Direwolf20"))
|
||||
{
|
||||
instance.setIconKey("steve");
|
||||
}
|
||||
else if(pack.name.contains("FTB") || pack.name.contains("Feed The Beast"))
|
||||
{
|
||||
instance.setIconKey("ftb_logo");
|
||||
}
|
||||
else
|
||||
{
|
||||
// default to something other than the MultiMC default to distinguish these
|
||||
instance.setIconKey("flame");
|
||||
}
|
||||
}
|
||||
QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods");
|
||||
QFileInfo jarmodsInfo(jarmodsPath);
|
||||
if(jarmodsInfo.isDir())
|
||||
{
|
||||
// install all the jar mods
|
||||
qDebug() << "Found jarmods:";
|
||||
QDir jarmodsDir(jarmodsPath);
|
||||
QStringList jarMods;
|
||||
for (auto info: jarmodsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
|
||||
{
|
||||
qDebug() << info.fileName();
|
||||
jarMods.push_back(info.absoluteFilePath());
|
||||
}
|
||||
auto profile = instance.getComponentList();
|
||||
profile->installJarMods(jarMods);
|
||||
// nuke the original files
|
||||
FS::deletePath(jarmodsPath);
|
||||
}
|
||||
instance.setName(m_instName);
|
||||
m_modIdResolver.reset(new Flame::FileResolvingTask(pack));
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]()
|
||||
{
|
||||
auto results = m_modIdResolver->getResults();
|
||||
m_filesNetJob.reset(new NetJob(tr("Mod download")));
|
||||
for(auto result: results.files)
|
||||
{
|
||||
QString filename = result.fileName;
|
||||
if(!result.required)
|
||||
{
|
||||
filename += ".disabled";
|
||||
}
|
||||
|
||||
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
|
||||
auto path = FS::PathCombine(m_stagingPath , relpath);
|
||||
|
||||
switch(result.type)
|
||||
{
|
||||
case Flame::File::Type::Folder:
|
||||
{
|
||||
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
|
||||
// fall-through intentional, we treat these as plain old mods and dump them wherever.
|
||||
}
|
||||
case Flame::File::Type::SingleFile:
|
||||
case Flame::File::Type::Mod:
|
||||
{
|
||||
qDebug() << "Will download" << result.url << "to" << path;
|
||||
auto dl = Net::Download::makeFile(result.url, path);
|
||||
m_filesNetJob->addNetAction(dl);
|
||||
break;
|
||||
}
|
||||
case Flame::File::Type::Modpack:
|
||||
logWarning(tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(relpath));
|
||||
break;
|
||||
case Flame::File::Type::Cmod2:
|
||||
case Flame::File::Type::Ctoc:
|
||||
case Flame::File::Type::Unknown:
|
||||
logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_modIdResolver.reset();
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
|
||||
{
|
||||
m_filesNetJob.reset();
|
||||
emitSucceeded();
|
||||
}
|
||||
);
|
||||
connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason)
|
||||
{
|
||||
m_filesNetJob.reset();
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
|
||||
{
|
||||
setProgress(current, total);
|
||||
});
|
||||
setStatus(tr("Downloading mods..."));
|
||||
m_filesNetJob->start();
|
||||
}
|
||||
);
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason)
|
||||
{
|
||||
m_modIdResolver.reset();
|
||||
emitFailed(tr("Unable to resolve mod IDs:\n") + reason);
|
||||
});
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, [&](qint64 current, qint64 total)
|
||||
{
|
||||
setProgress(current, total);
|
||||
});
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, [&](QString status)
|
||||
{
|
||||
setStatus(status);
|
||||
});
|
||||
m_modIdResolver->start();
|
||||
}
|
||||
|
||||
void InstanceImportTask::processMultiMC()
|
||||
{
|
||||
// FIXME: copy from FolderInstanceProvider!!! FIX IT!!!
|
||||
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||
|
||||
NullInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
||||
|
||||
// reset time played on import... because packs.
|
||||
instance.resetTimePlayed();
|
||||
|
||||
// set a new nice name
|
||||
instance.setName(m_instName);
|
||||
|
||||
// if the icon was specified by user, use that. otherwise pull icon from the pack
|
||||
if (m_instIcon != "default")
|
||||
{
|
||||
instance.setIconKey(m_instIcon);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_instIcon = instance.iconKey();
|
||||
|
||||
auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon);
|
||||
if (!importIconPath.isNull() && QFile::exists(importIconPath))
|
||||
{
|
||||
// import icon
|
||||
auto iconList = ENV.icons();
|
||||
if (iconList->iconFileExists(m_instIcon))
|
||||
{
|
||||
iconList->deleteIcon(m_instIcon);
|
||||
}
|
||||
iconList->installIcons({importIconPath});
|
||||
}
|
||||
}
|
||||
emitSucceeded();
|
||||
}
|
||||
@@ -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,28 +0,0 @@
|
||||
#pragma once
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
#include <QSet>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
struct FMLlib
|
||||
{
|
||||
QString filename;
|
||||
QString checksum;
|
||||
bool ours;
|
||||
};
|
||||
|
||||
struct VersionFilterData
|
||||
{
|
||||
VersionFilterData();
|
||||
// mapping between minecraft versions and FML libraries required
|
||||
QMap<QString, QList<FMLlib>> fmlLibsMapping;
|
||||
// set of minecraft versions for which using forge installers is blacklisted
|
||||
QSet<QString> forgeInstallerBlacklist;
|
||||
// no new versions below this date will be accepted from Mojang servers
|
||||
QDateTime legacyCutoffDate;
|
||||
// Libraries that belong to LWJGL
|
||||
QSet<QString> lwjglWhitelist;
|
||||
};
|
||||
extern VersionFilterData MULTIMC_LOGIC_EXPORT g_VersionFilterData;
|
||||
@@ -1,315 +0,0 @@
|
||||
/* Copyright 2013-2019 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-2019 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-2019 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-2019 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-2019 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 <net/URLConstants.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(URLConstants::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-2019 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-2019 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-2019 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,44 +0,0 @@
|
||||
/* Copyright 2013-2019 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 a possibly timed-out access token
|
||||
* and attempts to authenticate with Mojang's servers.
|
||||
* If successful, it will set the new access token. The token is considered validated.
|
||||
*/
|
||||
class RefreshTask : public YggdrasilTask
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RefreshTask(MojangAccount * account);
|
||||
|
||||
protected:
|
||||
virtual QJsonObject getRequestContent() const override;
|
||||
|
||||
virtual QString getEndpoint() const override;
|
||||
|
||||
virtual void processResponse(QJsonObject responseData) override;
|
||||
|
||||
virtual QString getStateMessage() const override;
|
||||
};
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
|
||||
/* Copyright 2013-2019 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,47 +0,0 @@
|
||||
/* Copyright 2013-2019 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* :FIXME: DEAD CODE, DEAD CODE, DEAD CODE! :FIXME:
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../YggdrasilTask.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QJsonObject>
|
||||
|
||||
/**
|
||||
* The validate task takes a MojangAccount and checks to make sure its access token is valid.
|
||||
*/
|
||||
class ValidateTask : public YggdrasilTask
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ValidateTask(MojangAccount *account, 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:
|
||||
};
|
||||
@@ -1,393 +0,0 @@
|
||||
/* Copyright 2013-2019 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 "Env.h"
|
||||
#include "ForgeXzDownload.h"
|
||||
#include <FileSystem.h>
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QFileInfo>
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
|
||||
ForgeXzDownload::ForgeXzDownload(QString url, QString relative_path, MetaEntryPtr entry) : NetAction()
|
||||
{
|
||||
m_entry = entry;
|
||||
m_target_path = entry->getFullPath();
|
||||
m_pack200_xz_file.setFileTemplate("./dl_temp.XXXXXX");
|
||||
m_status = Job_NotStarted;
|
||||
m_url_path = relative_path;
|
||||
m_url = url + ".pack.xz";
|
||||
}
|
||||
|
||||
void ForgeXzDownload::start()
|
||||
{
|
||||
if(m_status == Job_Aborted)
|
||||
{
|
||||
qWarning() << "Attempt to start an aborted Download:" << m_url.toString();
|
||||
emit aborted(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
m_status = Job_InProgress;
|
||||
if (!m_entry->isStale())
|
||||
{
|
||||
m_status = Job_Finished;
|
||||
emit succeeded(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
// can we actually create the real, final file?
|
||||
if (!FS::ensureFilePathExists(m_target_path))
|
||||
{
|
||||
m_status = Job_Failed;
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "Downloading " << m_url.toString();
|
||||
QNetworkRequest request(m_url);
|
||||
request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->getETag().toLatin1());
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)");
|
||||
|
||||
QNetworkReply *rep = ENV.qnam().get(request);
|
||||
|
||||
m_reply.reset(rep);
|
||||
connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SLOT(downloadProgress(qint64, qint64)));
|
||||
connect(rep, SIGNAL(finished()), SLOT(downloadFinished()));
|
||||
connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError)));
|
||||
connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead()));
|
||||
}
|
||||
|
||||
void ForgeXzDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
||||
{
|
||||
m_total_progress = bytesTotal;
|
||||
m_progress = bytesReceived;
|
||||
emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal);
|
||||
}
|
||||
|
||||
void ForgeXzDownload::downloadError(QNetworkReply::NetworkError error)
|
||||
{
|
||||
if(error == QNetworkReply::OperationCanceledError)
|
||||
{
|
||||
qCritical() << "Aborted " << m_url.toString();
|
||||
m_status = Job_Aborted;
|
||||
}
|
||||
else
|
||||
{
|
||||
// error happened during download.
|
||||
qCritical() << "Failed " << m_url.toString() << " with reason " << error;
|
||||
m_status = Job_Failed;
|
||||
}
|
||||
}
|
||||
|
||||
void ForgeXzDownload::failAndTryNextMirror()
|
||||
{
|
||||
m_status = Job_Failed;
|
||||
emit failed(m_index_within_job);
|
||||
}
|
||||
|
||||
void ForgeXzDownload::downloadFinished()
|
||||
{
|
||||
// if the download succeeded
|
||||
if (m_status != Job_Failed && m_status != Job_Aborted)
|
||||
{
|
||||
// nothing went wrong...
|
||||
m_status = Job_Finished;
|
||||
if (m_pack200_xz_file.isOpen())
|
||||
{
|
||||
// we actually downloaded something! process and isntall it
|
||||
decompressAndInstall();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// something bad happened -- on the local machine!
|
||||
m_status = Job_Failed;
|
||||
m_pack200_xz_file.remove();
|
||||
m_reply.reset();
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(m_status == Job_Aborted)
|
||||
{
|
||||
m_pack200_xz_file.remove();
|
||||
m_reply.reset();
|
||||
emit failed(m_index_within_job);
|
||||
emit aborted(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
// else the download failed
|
||||
else
|
||||
{
|
||||
m_status = Job_Failed;
|
||||
m_pack200_xz_file.close();
|
||||
m_pack200_xz_file.remove();
|
||||
m_reply.reset();
|
||||
failAndTryNextMirror();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ForgeXzDownload::downloadReadyRead()
|
||||
{
|
||||
|
||||
if (!m_pack200_xz_file.isOpen())
|
||||
{
|
||||
if (!m_pack200_xz_file.open())
|
||||
{
|
||||
/*
|
||||
* Can't open the file... the job failed
|
||||
*/
|
||||
m_reply->abort();
|
||||
emit failed(m_index_within_job);
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_pack200_xz_file.write(m_reply->readAll());
|
||||
}
|
||||
|
||||
#include "xz.h"
|
||||
#include "unpack200.h"
|
||||
#include <stdexcept>
|
||||
#include <unistd.h>
|
||||
|
||||
const size_t buffer_size = 8196;
|
||||
|
||||
// NOTE: once this gets here, it can't be aborted anymore. we don't care.
|
||||
void ForgeXzDownload::decompressAndInstall()
|
||||
{
|
||||
// rewind the downloaded temp file
|
||||
m_pack200_xz_file.seek(0);
|
||||
// de-xz'd file
|
||||
QTemporaryFile pack200_file("./dl_temp.XXXXXX");
|
||||
pack200_file.open();
|
||||
|
||||
bool xz_success = false;
|
||||
// first, de-xz
|
||||
{
|
||||
uint8_t in[buffer_size];
|
||||
uint8_t out[buffer_size];
|
||||
struct xz_buf b;
|
||||
struct xz_dec *s;
|
||||
enum xz_ret ret;
|
||||
xz_crc32_init();
|
||||
xz_crc64_init();
|
||||
s = xz_dec_init(XZ_DYNALLOC, 1 << 26);
|
||||
if (s == nullptr)
|
||||
{
|
||||
xz_dec_end(s);
|
||||
failAndTryNextMirror();
|
||||
return;
|
||||
}
|
||||
b.in = in;
|
||||
b.in_pos = 0;
|
||||
b.in_size = 0;
|
||||
b.out = out;
|
||||
b.out_pos = 0;
|
||||
b.out_size = buffer_size;
|
||||
while (!xz_success)
|
||||
{
|
||||
if (b.in_pos == b.in_size)
|
||||
{
|
||||
b.in_size = m_pack200_xz_file.read((char *)in, sizeof(in));
|
||||
b.in_pos = 0;
|
||||
}
|
||||
|
||||
ret = xz_dec_run(s, &b);
|
||||
|
||||
if (b.out_pos == sizeof(out))
|
||||
{
|
||||
auto wresult = pack200_file.write((char *)out, b.out_pos);
|
||||
if (wresult < 0 || size_t(wresult) != b.out_pos)
|
||||
{
|
||||
// msg = "Write error\n";
|
||||
xz_dec_end(s);
|
||||
failAndTryNextMirror();
|
||||
return;
|
||||
}
|
||||
|
||||
b.out_pos = 0;
|
||||
}
|
||||
|
||||
if (ret == XZ_OK)
|
||||
continue;
|
||||
|
||||
if (ret == XZ_UNSUPPORTED_CHECK)
|
||||
{
|
||||
// unsupported check. this is OK, but we should log this
|
||||
continue;
|
||||
}
|
||||
|
||||
auto wresult = pack200_file.write((char *)out, b.out_pos);
|
||||
if (wresult < 0 || size_t(wresult) != b.out_pos)
|
||||
{
|
||||
// write error
|
||||
pack200_file.close();
|
||||
xz_dec_end(s);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ret)
|
||||
{
|
||||
case XZ_STREAM_END:
|
||||
xz_dec_end(s);
|
||||
xz_success = true;
|
||||
break;
|
||||
|
||||
case XZ_MEM_ERROR:
|
||||
qCritical() << "Memory allocation failed\n";
|
||||
xz_dec_end(s);
|
||||
failAndTryNextMirror();
|
||||
return;
|
||||
|
||||
case XZ_MEMLIMIT_ERROR:
|
||||
qCritical() << "Memory usage limit reached\n";
|
||||
xz_dec_end(s);
|
||||
failAndTryNextMirror();
|
||||
return;
|
||||
|
||||
case XZ_FORMAT_ERROR:
|
||||
qCritical() << "Not a .xz file\n";
|
||||
xz_dec_end(s);
|
||||
failAndTryNextMirror();
|
||||
return;
|
||||
|
||||
case XZ_OPTIONS_ERROR:
|
||||
qCritical() << "Unsupported options in the .xz headers\n";
|
||||
xz_dec_end(s);
|
||||
failAndTryNextMirror();
|
||||
return;
|
||||
|
||||
case XZ_DATA_ERROR:
|
||||
case XZ_BUF_ERROR:
|
||||
qCritical() << "File is corrupt\n";
|
||||
xz_dec_end(s);
|
||||
failAndTryNextMirror();
|
||||
return;
|
||||
|
||||
default:
|
||||
qCritical() << "Bug!\n";
|
||||
xz_dec_end(s);
|
||||
failAndTryNextMirror();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_pack200_xz_file.remove();
|
||||
|
||||
// revert pack200
|
||||
pack200_file.seek(0);
|
||||
int handle_in = pack200_file.handle();
|
||||
// FIXME: dispose of file handles, pointers and the like. Ideally wrap in objects.
|
||||
if(handle_in == -1)
|
||||
{
|
||||
qCritical() << "Error reopening " << pack200_file.fileName();
|
||||
failAndTryNextMirror();
|
||||
return;
|
||||
}
|
||||
int handle_in_dup = dup (handle_in);
|
||||
if(handle_in_dup == -1)
|
||||
{
|
||||
qCritical() << "Error reopening " << pack200_file.fileName();
|
||||
failAndTryNextMirror();
|
||||
return;
|
||||
}
|
||||
FILE *file_in = fdopen (handle_in_dup, "rb");
|
||||
if(!file_in)
|
||||
{
|
||||
qCritical() << "Error reopening " << pack200_file.fileName();
|
||||
failAndTryNextMirror();
|
||||
return;
|
||||
}
|
||||
QFile qfile_out(m_target_path);
|
||||
if(!qfile_out.open(QIODevice::WriteOnly))
|
||||
{
|
||||
qCritical() << "Error opening " << qfile_out.fileName();
|
||||
failAndTryNextMirror();
|
||||
return;
|
||||
}
|
||||
int handle_out = qfile_out.handle();
|
||||
if(handle_out == -1)
|
||||
{
|
||||
qCritical() << "Error opening " << qfile_out.fileName();
|
||||
failAndTryNextMirror();
|
||||
return;
|
||||
}
|
||||
int handle_out_dup = dup (handle_out);
|
||||
if(handle_out_dup == -1)
|
||||
{
|
||||
qCritical() << "Error reopening " << qfile_out.fileName();
|
||||
failAndTryNextMirror();
|
||||
return;
|
||||
}
|
||||
FILE *file_out = fdopen (handle_out_dup, "wb");
|
||||
if(!file_out)
|
||||
{
|
||||
qCritical() << "Error opening " << qfile_out.fileName();
|
||||
failAndTryNextMirror();
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
// NOTE: this takes ownership of both FILE pointers. That's why we duplicate them above.
|
||||
unpack_200(file_in, file_out);
|
||||
}
|
||||
catch (const std::runtime_error &err)
|
||||
{
|
||||
m_status = Job_Failed;
|
||||
qCritical() << "Error unpacking " << pack200_file.fileName() << " : " << err.what();
|
||||
QFile f(m_target_path);
|
||||
if (f.exists())
|
||||
f.remove();
|
||||
failAndTryNextMirror();
|
||||
return;
|
||||
}
|
||||
pack200_file.remove();
|
||||
|
||||
QFile jar_file(m_target_path);
|
||||
|
||||
if (!jar_file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
jar_file.remove();
|
||||
failAndTryNextMirror();
|
||||
return;
|
||||
}
|
||||
auto hash = QCryptographicHash::hash(jar_file.readAll(), QCryptographicHash::Md5);
|
||||
m_entry->setMD5Sum(hash.toHex().constData());
|
||||
jar_file.close();
|
||||
|
||||
QFileInfo output_file_info(m_target_path);
|
||||
m_entry->setETag(m_reply->rawHeader("ETag").constData());
|
||||
m_entry->setLocalChangedTimestamp(output_file_info.lastModified().toUTC().toMSecsSinceEpoch());
|
||||
m_entry->setStale(false);
|
||||
ENV.metacache()->updateEntry(m_entry);
|
||||
|
||||
m_reply.reset();
|
||||
emit succeeded(m_index_within_job);
|
||||
}
|
||||
|
||||
bool ForgeXzDownload::abort()
|
||||
{
|
||||
if(m_reply)
|
||||
m_reply->abort();
|
||||
m_status = Job_Aborted;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ForgeXzDownload::canAbort()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/* Copyright 2013-2019 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 "net/NetAction.h"
|
||||
#include "net/HttpMetaCache.h"
|
||||
#include <QFile>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
typedef std::shared_ptr<class ForgeXzDownload> ForgeXzDownloadPtr;
|
||||
|
||||
class ForgeXzDownload : public NetAction
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MetaEntryPtr m_entry;
|
||||
/// if saving to file, use the one specified in this string
|
||||
QString m_target_path;
|
||||
/// this is the output file, if any
|
||||
QTemporaryFile m_pack200_xz_file;
|
||||
/// path relative to the mirror base
|
||||
QString m_url_path;
|
||||
|
||||
public:
|
||||
explicit ForgeXzDownload(QString url, QString relative_path, MetaEntryPtr entry);
|
||||
static ForgeXzDownloadPtr make(QString url, QString relative_path, MetaEntryPtr entry)
|
||||
{
|
||||
return ForgeXzDownloadPtr(new ForgeXzDownload(url, relative_path, entry));
|
||||
}
|
||||
virtual ~ForgeXzDownload(){};
|
||||
bool canAbort() override;
|
||||
|
||||
protected
|
||||
slots:
|
||||
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;
|
||||
void downloadError(QNetworkReply::NetworkError error) override;
|
||||
void downloadFinished() override;
|
||||
void downloadReadyRead() override;
|
||||
|
||||
public
|
||||
slots:
|
||||
void start() override;
|
||||
bool abort() override;
|
||||
|
||||
private:
|
||||
void decompressAndInstall();
|
||||
void failAndTryNextMirror();
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
#include "CreateServerResourcePacksFolder.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "launch/LaunchTask.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
CreateServerResourcePacksFolder::CreateServerResourcePacksFolder(LaunchTask* parent): LaunchStep(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void CreateServerResourcePacksFolder::executeTask()
|
||||
{
|
||||
auto instance = m_parent->instance();
|
||||
std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
|
||||
if(!FS::ensureFolderPathExists(FS::PathCombine(minecraftInstance->gameRoot(), "server-resource-packs")))
|
||||
{
|
||||
emit logLine(tr("Couldn't create the 'server-resource-packs' folder"), MessageLevel::Error);
|
||||
}
|
||||
emitSucceeded();
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
#include "FileResolvingTask.h"
|
||||
#include "Json.h"
|
||||
|
||||
namespace {
|
||||
const char * metabase = "https://cursemeta.dries007.net";
|
||||
}
|
||||
|
||||
Flame::FileResolvingTask::FileResolvingTask(Flame::Manifest& toProcess)
|
||||
: m_toProcess(toProcess)
|
||||
{
|
||||
}
|
||||
|
||||
void Flame::FileResolvingTask::executeTask()
|
||||
{
|
||||
setStatus(tr("Resolving mod IDs..."));
|
||||
setProgress(0, m_toProcess.files.size());
|
||||
m_dljob.reset(new NetJob("Mod id resolver"));
|
||||
results.resize(m_toProcess.files.size());
|
||||
int index = 0;
|
||||
for(auto & file: m_toProcess.files)
|
||||
{
|
||||
auto projectIdStr = QString::number(file.projectId);
|
||||
auto fileIdStr = QString::number(file.fileId);
|
||||
QString metaurl = QString("%1/%2/%3.json").arg(metabase, projectIdStr, fileIdStr);
|
||||
auto dl = Net::Download::makeByteArray(QUrl(metaurl), &results[index]);
|
||||
m_dljob->addNetAction(dl);
|
||||
index ++;
|
||||
}
|
||||
connect(m_dljob.get(), &NetJob::finished, this, &Flame::FileResolvingTask::netJobFinished);
|
||||
m_dljob->start();
|
||||
}
|
||||
|
||||
void Flame::FileResolvingTask::netJobFinished()
|
||||
{
|
||||
bool failed = false;
|
||||
int index = 0;
|
||||
for(auto & bytes: results)
|
||||
{
|
||||
auto & out = m_toProcess.files[index];
|
||||
try
|
||||
{
|
||||
failed &= (!out.parseFromBytes(bytes));
|
||||
}
|
||||
catch (const JSONValidationError &e)
|
||||
{
|
||||
|
||||
qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of a parsing error:";
|
||||
qCritical() << e.cause();
|
||||
qCritical() << "JSON:";
|
||||
qCritical() << bytes;
|
||||
failed = true;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
if(!failed)
|
||||
{
|
||||
emitSucceeded();
|
||||
}
|
||||
else
|
||||
{
|
||||
emitFailed(tr("Some mod ID resolving tasks failed."));
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "tasks/Task.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "PackManifest.h"
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
namespace Flame
|
||||
{
|
||||
class MULTIMC_LOGIC_EXPORT FileResolvingTask : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FileResolvingTask(Flame::Manifest &toProcess);
|
||||
virtual ~FileResolvingTask() {};
|
||||
|
||||
const Flame::Manifest &getResults() const
|
||||
{
|
||||
return m_toProcess;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void executeTask() override;
|
||||
|
||||
protected slots:
|
||||
void netJobFinished();
|
||||
|
||||
private: /* data */
|
||||
Flame::Manifest m_toProcess;
|
||||
QVector<QByteArray> results;
|
||||
NetJobPtr m_dljob;
|
||||
};
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
#include "PackManifest.h"
|
||||
#include "Json.h"
|
||||
|
||||
static void loadFileV1(Flame::File & f, QJsonObject & file)
|
||||
{
|
||||
f.projectId = Json::requireInteger(file, "projectID");
|
||||
f.fileId = Json::requireInteger(file, "fileID");
|
||||
f.required = Json::ensureBoolean(file, QString("required"), true);
|
||||
}
|
||||
|
||||
static void loadModloaderV1(Flame::Modloader & m, QJsonObject & modLoader)
|
||||
{
|
||||
m.id = Json::requireString(modLoader, "id");
|
||||
m.primary = Json::ensureBoolean(modLoader, QString("primary"), false);
|
||||
}
|
||||
|
||||
static void loadMinecraftV1(Flame::Minecraft & m, QJsonObject & minecraft)
|
||||
{
|
||||
m.version = Json::requireString(minecraft, "version");
|
||||
// extra libraries... apparently only used for a custom Minecraft launcher in the 1.2.5 FTB retro pack
|
||||
// intended use is likely hardcoded in the 'Flame' client, the manifest says nothing
|
||||
m.libraries = Json::ensureString(minecraft, QString("libraries"), QString());
|
||||
auto arr = Json::ensureArray(minecraft, "modLoaders", QJsonArray());
|
||||
for (const auto & item : arr)
|
||||
{
|
||||
auto obj = Json::requireObject(item);
|
||||
Flame::Modloader loader;
|
||||
loadModloaderV1(loader, obj);
|
||||
m.modLoaders.append(loader);
|
||||
}
|
||||
}
|
||||
|
||||
static void loadManifestV1(Flame::Manifest & m, QJsonObject & manifest)
|
||||
{
|
||||
auto mc = Json::requireObject(manifest, "minecraft");
|
||||
loadMinecraftV1(m.minecraft, mc);
|
||||
m.name = Json::ensureString(manifest, QString("name"), "Unnamed");
|
||||
m.version = Json::ensureString(manifest, QString("version"), QString());
|
||||
m.author = Json::ensureString(manifest, QString("author"), "Anonymous Coward");
|
||||
auto arr = Json::ensureArray(manifest, "files", QJsonArray());
|
||||
for (const auto & item : arr)
|
||||
{
|
||||
auto obj = Json::requireObject(item);
|
||||
Flame::File file;
|
||||
loadFileV1(file, obj);
|
||||
m.files.append(file);
|
||||
}
|
||||
m.overrides = Json::ensureString(manifest, "overrides", "overrides");
|
||||
}
|
||||
|
||||
void Flame::loadManifest(Flame::Manifest & m, const QString &filepath)
|
||||
{
|
||||
auto doc = Json::requireDocument(filepath);
|
||||
auto obj = Json::requireObject(doc);
|
||||
m.manifestType = Json::requireString(obj, "manifestType");
|
||||
if(m.manifestType != "minecraftModpack")
|
||||
{
|
||||
throw JSONValidationError("Not a modpack manifest!");
|
||||
}
|
||||
m.manifestVersion = Json::requireInteger(obj, "manifestVersion");
|
||||
if(m.manifestVersion != 1)
|
||||
{
|
||||
throw JSONValidationError(QString("Unknown manifest version (%1)").arg(m.manifestVersion));
|
||||
}
|
||||
loadManifestV1(m, obj);
|
||||
}
|
||||
|
||||
bool Flame::File::parseFromBytes(const QByteArray& bytes)
|
||||
{
|
||||
auto doc = Json::requireDocument(bytes);
|
||||
auto obj = Json::requireObject(doc);
|
||||
// result code signifies true failure.
|
||||
if(obj.contains("code"))
|
||||
{
|
||||
qCritical() << "Resolving of" << projectId << fileId << "failed because of a negative result:";
|
||||
qCritical() << bytes;
|
||||
return false;
|
||||
}
|
||||
fileName = Json::requireString(obj, "FileNameOnDisk");
|
||||
QString rawUrl = Json::requireString(obj, "DownloadURL");
|
||||
url = QUrl(rawUrl, QUrl::TolerantMode);
|
||||
if(!url.isValid())
|
||||
{
|
||||
throw JSONValidationError(QString("Invalid URL: %1").arg(rawUrl));
|
||||
}
|
||||
// This is a piece of a Flame project JSON pulled out into the file metadata (here) for convenience
|
||||
// It is also optional
|
||||
QJsonObject projObj = Json::ensureObject(obj, "_Project", {});
|
||||
if(!projObj.isEmpty())
|
||||
{
|
||||
QString strType = Json::ensureString(projObj, "PackageType", "mod").toLower();
|
||||
if(strType == "singlefile")
|
||||
{
|
||||
type = File::Type::SingleFile;
|
||||
}
|
||||
else if(strType == "ctoc")
|
||||
{
|
||||
type = File::Type::Ctoc;
|
||||
}
|
||||
else if(strType == "cmod2")
|
||||
{
|
||||
type = File::Type::Cmod2;
|
||||
}
|
||||
else if(strType == "mod")
|
||||
{
|
||||
type = File::Type::Mod;
|
||||
}
|
||||
else if(strType == "folder")
|
||||
{
|
||||
type = File::Type::Folder;
|
||||
}
|
||||
else if(strType == "modpack")
|
||||
{
|
||||
type = File::Type::Modpack;
|
||||
}
|
||||
else
|
||||
{
|
||||
qCritical() << "Resolving of" << projectId << fileId << "failed because of unknown file type:" << strType;
|
||||
type = File::Type::Unknown;
|
||||
return false;
|
||||
}
|
||||
targetFolder = Json::ensureString(projObj, "Path", "mods");
|
||||
}
|
||||
resolved = true;
|
||||
return true;
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QUrl>
|
||||
|
||||
namespace Flame
|
||||
{
|
||||
struct File
|
||||
{
|
||||
// NOTE: throws JSONValidationError
|
||||
bool parseFromBytes(const QByteArray &bytes);
|
||||
|
||||
int projectId = 0;
|
||||
int fileId = 0;
|
||||
// NOTE: the opposite to 'optional'. This is at the time of writing unused.
|
||||
bool required = true;
|
||||
|
||||
// our
|
||||
bool resolved = false;
|
||||
QString fileName;
|
||||
QUrl url;
|
||||
QString targetFolder = QLatin1Literal("mods");
|
||||
enum class Type
|
||||
{
|
||||
Unknown,
|
||||
Folder,
|
||||
Ctoc,
|
||||
SingleFile,
|
||||
Cmod2,
|
||||
Modpack,
|
||||
Mod
|
||||
} type = Type::Mod;
|
||||
};
|
||||
|
||||
struct Modloader
|
||||
{
|
||||
QString id;
|
||||
bool primary = false;
|
||||
};
|
||||
|
||||
struct Minecraft
|
||||
{
|
||||
QString version;
|
||||
QString libraries;
|
||||
QVector<Flame::Modloader> modLoaders;
|
||||
};
|
||||
|
||||
struct Manifest
|
||||
{
|
||||
QString manifestType;
|
||||
int manifestVersion = 0;
|
||||
Flame::Minecraft minecraft;
|
||||
QString name;
|
||||
QString version;
|
||||
QString author;
|
||||
QVector<Flame::File> files;
|
||||
QString overrides;
|
||||
};
|
||||
|
||||
void loadManifest(Flame::Manifest & m, const QString &filepath);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#include "URLConstants.h"
|
||||
|
||||
namespace URLConstants {
|
||||
|
||||
QString getLegacyJarUrl(QString version)
|
||||
{
|
||||
return AWS_DOWNLOAD_VERSIONS + getJarPath(version);
|
||||
}
|
||||
|
||||
QString getJarPath(QString version)
|
||||
{
|
||||
return version + "/" + version + ".jar";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/* Copyright 2013-2019 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace URLConstants
|
||||
{
|
||||
const QString AWS_DOWNLOAD_VERSIONS("https://s3.amazonaws.com/Minecraft.Download/versions/");
|
||||
const QString RESOURCE_BASE("https://resources.download.minecraft.net/");
|
||||
const QString LIBRARY_BASE("https://libraries.minecraft.net/");
|
||||
const QString SKINS_BASE("https://crafatar.com/skins/");
|
||||
const QString AUTH_BASE("https://authserver.mojang.com/");
|
||||
const QString MOJANG_STATUS_URL("https://status.mojang.com/check");
|
||||
const QString IMGUR_BASE_URL("https://api.imgur.com/3/");
|
||||
const QString FMLLIBS_OUR_BASE_URL("https://files.multimc.org/fmllibs/");
|
||||
const QString FMLLIBS_FORGE_BASE_URL("https://files.minecraftforge.net/fmllibs/");
|
||||
const QString TRANSLATIONS_BASE_URL("https://files.multimc.org/translations/");
|
||||
|
||||
const QString LEGACY_FTB_CDN_BASE_URL("https://dist.creeper.host/FTB2/");
|
||||
|
||||
QString getJarPath(QString version);
|
||||
QString getLegacyJarUrl(QString 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-2019 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 <net/URLConstants.h>
|
||||
|
||||
#include <QByteArray>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
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(URLConstants::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-2019 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,54 +0,0 @@
|
||||
#include "BuildConfig.h"
|
||||
#include <QObject>
|
||||
|
||||
Config BuildConfig;
|
||||
|
||||
Config::Config()
|
||||
{
|
||||
// Version information
|
||||
VERSION_MAJOR = @MultiMC_VERSION_MAJOR@;
|
||||
VERSION_MINOR = @MultiMC_VERSION_MINOR@;
|
||||
VERSION_HOTFIX = @MultiMC_VERSION_HOTFIX@;
|
||||
VERSION_BUILD = @MultiMC_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@";
|
||||
|
||||
GIT_COMMIT = "@MultiMC_GIT_COMMIT@";
|
||||
GIT_REFSPEC = "@MultiMC_GIT_REFSPEC@";
|
||||
if(GIT_REFSPEC.startsWith("refs/heads/") && !CHANLIST_URL.isEmpty() && VERSION_BUILD >= 0)
|
||||
{
|
||||
VERSION_CHANNEL = GIT_REFSPEC;
|
||||
VERSION_CHANNEL.remove("refs/heads/");
|
||||
UPDATER_ENABLED = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
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@";
|
||||
}
|
||||
|
||||
QString Config::printableVersionString() const
|
||||
{
|
||||
QString vstr = QString("%1.%2.%3").arg(QString::number(VERSION_MAJOR), QString::number(VERSION_MINOR), QString::number(VERSION_HOTFIX));
|
||||
|
||||
// If the build is not a main release, append the channel
|
||||
if(VERSION_CHANNEL != "stable")
|
||||
{
|
||||
vstr += "-" + VERSION_CHANNEL;
|
||||
}
|
||||
|
||||
// if a build number is set, also add it to the end
|
||||
if(VERSION_BUILD >= 0)
|
||||
{
|
||||
vstr += "-" + QString::number(VERSION_BUILD);
|
||||
}
|
||||
return vstr;
|
||||
}
|
||||
@@ -1,387 +0,0 @@
|
||||
project(application)
|
||||
|
||||
######## Configure the file with build properties ########
|
||||
configure_file("${PROJECT_SOURCE_DIR}/BuildConfig.cpp.in" "${PROJECT_BINARY_DIR}/BuildConfig.cpp")
|
||||
|
||||
################################ FILES ################################
|
||||
|
||||
######## Sources and headers ########
|
||||
SET(MULTIMC_SOURCES
|
||||
# Application base
|
||||
main.cpp
|
||||
MultiMC.h
|
||||
MultiMC.cpp
|
||||
BuildConfig.h
|
||||
${PROJECT_BINARY_DIR}/BuildConfig.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/legacy_ftb/Page.cpp
|
||||
pages/modplatform/legacy_ftb/Page.h
|
||||
pages/modplatform/legacy_ftb/ListModel.h
|
||||
pages/modplatform/legacy_ftb/ListModel.cpp
|
||||
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/legacy_ftb/Page.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-2019 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,12 +0,0 @@
|
||||
# What is this?
|
||||
A simple ubuntu package for MultiMC that wraps the contains a script that downloads and installs real MultiMC on ubuntu based systems.
|
||||
|
||||
It contains a `.desktop` file, an icon, and a simple script that does the heavy lifting.
|
||||
|
||||
# How to build this?
|
||||
You need dpkg utils. Rename the `multimc` folder to `multimc_1.3-1` and then run:
|
||||
```
|
||||
fakeroot dpkg-deb --build multimc_1.3-1
|
||||
```
|
||||
|
||||
Replace the version with whatever is appropriate.
|
||||
@@ -1,192 +0,0 @@
|
||||
/* Copyright 2013-2019 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 "AccountListPage.h"
|
||||
#include "ui_AccountListPage.h"
|
||||
|
||||
#include <QItemSelectionModel>
|
||||
#include <QMenu>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "net/NetJob.h"
|
||||
#include "net/URLConstants.h"
|
||||
#include "Env.h"
|
||||
|
||||
#include "dialogs/ProgressDialog.h"
|
||||
#include "dialogs/LoginDialog.h"
|
||||
#include "dialogs/CustomMessageBox.h"
|
||||
#include "dialogs/SkinUploadDialog.h"
|
||||
#include "tasks/Task.h"
|
||||
#include "minecraft/auth/YggdrasilTask.h"
|
||||
|
||||
#include "MultiMC.h"
|
||||
|
||||
AccountListPage::AccountListPage(QWidget *parent)
|
||||
: QMainWindow(parent), ui(new Ui::AccountListPage)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->listView->setEmptyString(tr(
|
||||
"Welcome!\n"
|
||||
"If you're new here, you can click the \"Add\" button to add your Mojang or Minecraft account."
|
||||
));
|
||||
ui->listView->setEmptyMode(VersionListView::String);
|
||||
ui->listView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
m_accounts = MMC->accounts();
|
||||
|
||||
ui->listView->setModel(m_accounts.get());
|
||||
ui->listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
ui->listView->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
|
||||
// Expand the account column
|
||||
ui->listView->header()->setSectionResizeMode(1, QHeaderView::Stretch);
|
||||
|
||||
QItemSelectionModel *selectionModel = ui->listView->selectionModel();
|
||||
|
||||
connect(selectionModel, &QItemSelectionModel::selectionChanged, [this](const QItemSelection &sel, const QItemSelection &dsel) {
|
||||
updateButtonStates();
|
||||
});
|
||||
connect(ui->listView, &VersionListView::customContextMenuRequested, this, &AccountListPage::ShowContextMenu);
|
||||
|
||||
connect(m_accounts.get(), SIGNAL(listChanged()), SLOT(listChanged()));
|
||||
connect(m_accounts.get(), SIGNAL(activeAccountChanged()), SLOT(listChanged()));
|
||||
|
||||
updateButtonStates();
|
||||
}
|
||||
|
||||
AccountListPage::~AccountListPage()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void AccountListPage::ShowContextMenu(const QPoint& pos)
|
||||
{
|
||||
auto menu = ui->toolBar->createContextMenu(this, tr("Context menu"));
|
||||
menu->exec(ui->listView->mapToGlobal(pos));
|
||||
delete menu;
|
||||
}
|
||||
|
||||
void AccountListPage::changeEvent(QEvent* event)
|
||||
{
|
||||
if (event->type() == QEvent::LanguageChange)
|
||||
{
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
QMainWindow::changeEvent(event);
|
||||
}
|
||||
|
||||
QMenu * AccountListPage::createPopupMenu()
|
||||
{
|
||||
QMenu* filteredMenu = QMainWindow::createPopupMenu();
|
||||
filteredMenu->removeAction(ui->toolBar->toggleViewAction() );
|
||||
return filteredMenu;
|
||||
}
|
||||
|
||||
|
||||
void AccountListPage::listChanged()
|
||||
{
|
||||
updateButtonStates();
|
||||
}
|
||||
|
||||
void AccountListPage::on_actionAdd_triggered()
|
||||
{
|
||||
addAccount(tr("Please enter your Minecraft account email and password to add your account."));
|
||||
}
|
||||
|
||||
void AccountListPage::on_actionRemove_triggered()
|
||||
{
|
||||
QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes();
|
||||
if (selection.size() > 0)
|
||||
{
|
||||
QModelIndex selected = selection.first();
|
||||
m_accounts->removeAccount(selected);
|
||||
}
|
||||
}
|
||||
|
||||
void AccountListPage::on_actionSetDefault_triggered()
|
||||
{
|
||||
QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes();
|
||||
if (selection.size() > 0)
|
||||
{
|
||||
QModelIndex selected = selection.first();
|
||||
MojangAccountPtr account =
|
||||
selected.data(MojangAccountList::PointerRole).value<MojangAccountPtr>();
|
||||
m_accounts->setActiveAccount(account->username());
|
||||
}
|
||||
}
|
||||
|
||||
void AccountListPage::on_actionNoDefault_triggered()
|
||||
{
|
||||
m_accounts->setActiveAccount("");
|
||||
}
|
||||
|
||||
void AccountListPage::updateButtonStates()
|
||||
{
|
||||
// If there is no selection, disable buttons that require something selected.
|
||||
QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes();
|
||||
|
||||
ui->actionRemove->setEnabled(selection.size() > 0);
|
||||
ui->actionSetDefault->setEnabled(selection.size() > 0);
|
||||
ui->actionUploadSkin->setEnabled(selection.size() > 0);
|
||||
|
||||
if(m_accounts->activeAccount().get() == nullptr) {
|
||||
ui->actionNoDefault->setEnabled(false);
|
||||
ui->actionNoDefault->setChecked(true);
|
||||
}
|
||||
else {
|
||||
ui->actionNoDefault->setEnabled(true);
|
||||
ui->actionNoDefault->setChecked(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AccountListPage::addAccount(const QString &errMsg)
|
||||
{
|
||||
// TODO: The login dialog isn't quite done yet
|
||||
MojangAccountPtr account = LoginDialog::newAccount(this, errMsg);
|
||||
|
||||
if (account != nullptr)
|
||||
{
|
||||
m_accounts->addAccount(account);
|
||||
if (m_accounts->count() == 1)
|
||||
m_accounts->setActiveAccount(account->username());
|
||||
|
||||
// Grab associated player skins
|
||||
auto job = new NetJob("Player skins: " + account->username());
|
||||
|
||||
for (AccountProfile profile : account->profiles())
|
||||
{
|
||||
auto meta = Env::getInstance().metacache()->resolveEntry("skins", profile.id + ".png");
|
||||
auto action = Net::Download::makeCached(QUrl(URLConstants::SKINS_BASE + profile.id + ".png"), meta);
|
||||
job->addNetAction(action);
|
||||
meta->setStale(true);
|
||||
}
|
||||
|
||||
job->start();
|
||||
}
|
||||
}
|
||||
|
||||
void AccountListPage::on_actionUploadSkin_triggered()
|
||||
{
|
||||
QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes();
|
||||
if (selection.size() > 0)
|
||||
{
|
||||
QModelIndex selected = selection.first();
|
||||
MojangAccountPtr account = selected.data(MojangAccountList::PointerRole).value<MojangAccountPtr>();
|
||||
SkinUploadDialog dialog(account, this);
|
||||
dialog.exec();
|
||||
}
|
||||
}
|
||||
|
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 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="2500" height="2479" viewBox="0 0 1464.248 1452.156"><path fill="#1389D2" d="M644.592 5.192c159.049-19.094 324.188 14.764 461.941 96.756 143.299 84.188 256.41 218.769 313.509 374.945 62.353 168.133 58.63 359.689-10.432 525.205-63.691 154.529-182.775 285.021-330.227 363.417-149.71 80.709-327.849 105.845-494.333 71.979-172.465-34.279-331.022-133.533-437.232-273.729C51.059 1037.725-2.015 878.924.058 719.941c.548-167.949 63.08-334.559 172.343-461.998C290.451 118.177 462.794 25.934 644.592 5.192m-120.67 181.494c-131.408 49.538-243.479 148.488-308.511 273.003-60.641 114.509-80.038 249.94-54.906 377.018 25.076 130.193 97.609 250.192 200.162 334.012 101.76 84.247 232.924 131.896 365.063 132.754-.795-37.831-.366-75.591-1.04-113.416-.792-31.415-30.866-53.744-59.722-58.563-.917 28.305.119 62.895-24.771 82.113-23.731 16.288-55.638 15.919-82.479 8.782-27.149-7.626-42.216-35.808-43.011-62.532-1.647-40.565-.244-81.196-.729-121.828-10.86-.059-21.781-.059-32.702-.059-.06-35.442-.06-70.892 0-106.273 10.92-.065 21.84-.125 32.762-.125 0-12.076 0-24.093-.06-36.115-14.033-4.635-30.263-10.188-35.934-25.379-14.033-33.186 3.417-70.462-10.188-103.891-13.911-35.203-27.759-74.125-14.825-111.888 7.992-26.598 34.042-43.56 61.007-45.694-.06-76.808 0-153.553-.06-230.358-.429-40.754 16.288-82.725 48.924-108.164 37.215-29.525 86.57-37.092 132.812-36.786 102.921.061 203.701 48.864 270.2 126.893-.124 18.362 0 36.727-.124 55.149-92.185.061-184.359-.063-276.54 0-10.006-1.828-19.523 7.505-17.754 17.569.304 58.812-.549 117.682-.308 176.492 17.021.061 34.104.304 51.125-.488 2.866-11.774 6.038-23.425 9.454-35.019 74.982 18.424 149.893 36.789 224.809 55.335-2.502 8.6-4.938 17.266-7.382 25.927-6.588 2.5-13.177 5.001-19.767 7.504-2.621 203.154-4.331 406.363-6.648 609.514 176.251-60.091 319.795-208.828 369.755-388.73 39.415-137.021 25.262-288.622-39.527-415.699-63.628-126.646-175.579-227.98-307.72-279.104-133.783-52.65-287.092-53.321-421.365-1.954m140.741 710.665c0 11.401 0 22.817.063 34.221 10.125.06 20.253.06 30.381.06.061 35.448.061 70.896 0 106.34-10.128 0-20.315 0-30.442.059v32.333c19.949.06 39.961.06 59.909-.061-.917-57.646-.365-115.305-1.283-172.95a9391.232 9391.232 0 0 0-58.628-.002z"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.2 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("sessionserver.mojang.com", 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;
|
||||
};
|
||||
72
buildconfig/BuildConfig.cpp.in
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "BuildConfig.h"
|
||||
#include <QObject>
|
||||
|
||||
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 = @Launcher_VERSION_MAJOR@;
|
||||
VERSION_MINOR = @Launcher_VERSION_MINOR@;
|
||||
VERSION_HOTFIX = @Launcher_VERSION_HOTFIX@;
|
||||
VERSION_BUILD = @Launcher_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 = "@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/");
|
||||
UPDATER_ENABLED = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
VERSION_CHANNEL = QObject::tr("custom");
|
||||
}
|
||||
|
||||
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@";
|
||||
MSA_CLIENT_ID = "@Launcher_MSA_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
|
||||
{
|
||||
QString vstr = QString("%1.%2.%3").arg(QString::number(VERSION_MAJOR), QString::number(VERSION_MINOR), QString::number(VERSION_HOTFIX));
|
||||
|
||||
// If the build is not a main release, append the channel
|
||||
if(VERSION_CHANNEL != "develop")
|
||||
{
|
||||
vstr += "-" + VERSION_CHANNEL;
|
||||
}
|
||||
|
||||
// if a build number is set, also add it to the end
|
||||
if(VERSION_BUILD >= 0)
|
||||
{
|
||||
vstr += "-" + QString::number(VERSION_BUILD);
|
||||
}
|
||||
return vstr;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -60,6 +75,42 @@ public:
|
||||
*/
|
||||
QString PASTE_EE_KEY;
|
||||
|
||||
/**
|
||||
* Client ID you can get from Imgur when you register an application
|
||||
*/
|
||||
QString IMGUR_CLIENT_ID;
|
||||
|
||||
/**
|
||||
* Client ID you can get from Microsoft Identity Platform when you register an application
|
||||
*/
|
||||
QString MSA_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 AUTH_BASE = "https://authserver.mojang.com/";
|
||||
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
|
||||
QString FMLLIBS_BASE_URL = "https://files.multimc.org/fmllibs/";
|
||||
QString TRANSLATIONS_BASE_URL = "https://files.multimc.org/translations/";
|
||||
|
||||
QString LEGACY_FTB_CDN_BASE_URL = "https://dist.creeper.host/FTB2/";
|
||||
|
||||
QString ATL_DOWNLOAD_SERVER_URL = "https://download.nodecdn.net/containers/atl/";
|
||||
|
||||
QString TECHNIC_API_BASE_URL = "https://api.technicpack.net/";
|
||||
/**
|
||||
* The build that is reported to the Technic API.
|
||||
*/
|
||||
QString TECHNIC_API_BUILD = "multimc";
|
||||
|
||||
/**
|
||||
* \brief Converts the Version to a string.
|
||||
* \return The version number in string format (major.minor.revision.build).
|
||||
@@ -67,4 +118,4 @@ public:
|
||||
QString printableVersionString() const;
|
||||
};
|
||||
|
||||
extern Config BuildConfig;
|
||||
extern const Config BuildConfig;
|
||||
11
buildconfig/CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
######## Configure the file with build properties ########
|
||||
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/BuildConfig.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/BuildConfig.cpp")
|
||||
|
||||
add_library(BuildConfig STATIC
|
||||
BuildConfig.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/BuildConfig.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(BuildConfig Qt5::Core)
|
||||
target_include_directories(BuildConfig PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
1285
changelog.md
@@ -36,5 +36,7 @@
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>MultiMC does not need access to your microphone for itself. Likely it's requesting it on behalf of a Minecraft mod you installed, which needs to access your microphone in order to function properly.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -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/QuickPlayTarget.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;
|
||||
@@ -32,20 +33,24 @@ class BaseDetachedToolFactory;
|
||||
class TranslationsModel;
|
||||
class ITheme;
|
||||
class MCEditTool;
|
||||
class AuthServer;
|
||||
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 +58,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 +81,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 +89,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 +150,19 @@ 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,
|
||||
QuickPlayTargetPtr quickPlayTarget = nullptr,
|
||||
MinecraftAccountPtr accountToUse = nullptr,
|
||||
const QString &offlineName = QString()
|
||||
);
|
||||
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 +183,30 @@ 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;
|
||||
std::shared_ptr<AuthServer> m_authserver;
|
||||
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 +214,7 @@ private:
|
||||
#endif
|
||||
|
||||
// FIXME: attach to instances instead.
|
||||
struct InstanceXtras
|
||||
{
|
||||
struct InstanceXtras {
|
||||
InstanceWindow * window = nullptr;
|
||||
shared_qobject_ptr<LaunchController> controller;
|
||||
};
|
||||
@@ -214,13 +228,18 @@ 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_worldToJoin;
|
||||
QString m_profileToUse;
|
||||
bool m_offline = false;
|
||||
QString m_offlineName;
|
||||
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);
|
||||
};
|
||||
188
launcher/AuthServer.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
#include "AuthServer.h"
|
||||
|
||||
#include <QThread>
|
||||
#include <QDebug>
|
||||
#include <QTcpSocket>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
struct Request
|
||||
{
|
||||
QString method;
|
||||
QString url;
|
||||
QMap<QString, QString> headers;
|
||||
QByteArray body;
|
||||
|
||||
QJsonDocument json() {
|
||||
return QJsonDocument::fromJson(body.data());
|
||||
}
|
||||
};
|
||||
|
||||
struct Response
|
||||
{
|
||||
int statusCode;
|
||||
QMap<QString, QString> headers;
|
||||
QString body;
|
||||
};
|
||||
|
||||
enum ConnectionState
|
||||
{
|
||||
CREATING,
|
||||
READING_HEAD,
|
||||
READING_BODY,
|
||||
PROCESS_REQUEST
|
||||
};
|
||||
|
||||
struct Connection
|
||||
{
|
||||
ConnectionState state;
|
||||
Request *request;
|
||||
Response *response;
|
||||
int leftToRead;
|
||||
QByteArray buffer;
|
||||
};
|
||||
|
||||
AuthServer::AuthServer(QObject *parent) : QObject(parent)
|
||||
{
|
||||
m_tcpServer.reset(new QTcpServer(this));
|
||||
|
||||
connect(m_tcpServer.get(), &QTcpServer::newConnection, this, &AuthServer::newConnection);
|
||||
|
||||
if (!m_tcpServer->listen(QHostAddress::LocalHost))
|
||||
{
|
||||
// TODO: think about stop launching when server start fails
|
||||
qCritical() << "Auth server start failed";
|
||||
}
|
||||
}
|
||||
|
||||
quint16 AuthServer::port()
|
||||
{
|
||||
return m_tcpServer->serverPort();
|
||||
}
|
||||
|
||||
void processRequest(Request *request, Response *response)
|
||||
{
|
||||
qDebug() << "Processing request";
|
||||
if (request->url == "/")
|
||||
{
|
||||
response->body = "{\"Status\":\"OK\",\"Runtime-Mode\":\"productionMode\",\"Application-Author\":\"Mojang Web Force\",\"Application-Description\":\"Mojang Public API.\",\"Specification-Version\":\"6.0.0\",\"Application-Name\":\"yggdrasil.accounts.restlet.server.public\",\"Implementation-Version\":\"6.0.0\",\"Application-Owner\":\"Mojang\"}";
|
||||
response->statusCode = 200;
|
||||
response->headers["Content-Type"] = "application/json; charset=utf-8";
|
||||
return;
|
||||
}
|
||||
|
||||
if (request->url == "/sessionserver/session/minecraft/join" || request->url == "/sessionserver/session/minecraft/hasJoined")
|
||||
{
|
||||
response->statusCode = 204;
|
||||
return;
|
||||
}
|
||||
|
||||
if (request->url == "/auth/authenticate" || request->url == "/auth/refresh")
|
||||
{
|
||||
auto json = request->json().object();
|
||||
QString clientToken = json.value("clientToken").toString();
|
||||
QString username = json.value(request->url == "/auth/authenticate" ? "username" : "accessToken").toString();
|
||||
|
||||
QString profile = ((QString) "{\"id\":\"%1\",\"name\":\"%2\"}").arg(clientToken, username);
|
||||
|
||||
response->statusCode = 200;
|
||||
response->body = ((QString) "{\"accessToken\":\"%1\",\"clientToken\":\"%2\",\"availableProfiles\":[%3], \"selectedProfile\": %3}").arg(username, clientToken, profile);
|
||||
return;
|
||||
}
|
||||
|
||||
response->body = "Not found";
|
||||
response->statusCode = 404;
|
||||
}
|
||||
|
||||
void AuthServer::newConnection()
|
||||
{
|
||||
|
||||
QTcpSocket *tcpSocket = m_tcpServer->nextPendingConnection();
|
||||
Connection *connection = new Connection();
|
||||
|
||||
connect(tcpSocket, &QTcpSocket::readyRead, this, [tcpSocket, connection]()
|
||||
{
|
||||
// Not the best way to process queries, but it just works
|
||||
QByteArray curBuf = tcpSocket->readAll().data();
|
||||
qDebug() << "Read " << curBuf.size() << " bytes";
|
||||
|
||||
if (connection->state == CREATING)
|
||||
{
|
||||
connection->response = new Response();
|
||||
connection->request = new Request();
|
||||
connection->buffer = ((QString)"").toUtf8();
|
||||
connection->state = READING_HEAD;
|
||||
}
|
||||
|
||||
if (connection->state == READING_HEAD)
|
||||
{
|
||||
connection->buffer.append(curBuf);
|
||||
if (connection->buffer.contains("\r\n\r\n"))
|
||||
{
|
||||
QByteArray head = connection->buffer.left(connection->buffer.indexOf("\r\n\r\n"));
|
||||
QString headStr = head.data();
|
||||
QStringList headList = headStr.split("\r\n");
|
||||
QStringList firstLine = headList.at(0).split(" ");
|
||||
connection->request->method = firstLine.at(0);
|
||||
connection->request->url = firstLine.at(1);
|
||||
|
||||
for (int i = 1; i < headList.size(); i++)
|
||||
{
|
||||
QStringList header = headList.at(i).split(":");
|
||||
connection->request->headers.insert(header.at(0), header.at(1));
|
||||
}
|
||||
|
||||
if (connection->request->headers.contains("Content-Length"))
|
||||
{
|
||||
connection->leftToRead = connection->request->headers["Content-Length"].toInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
connection->leftToRead = 0;
|
||||
}
|
||||
|
||||
curBuf = connection->buffer.mid(connection->buffer.indexOf("\r\n\r\n") + 4);
|
||||
connection->state = READING_BODY;
|
||||
}
|
||||
}
|
||||
|
||||
if (connection->state == READING_BODY)
|
||||
{
|
||||
connection->request->body.append(curBuf);
|
||||
if (connection->request->body.size() >= connection->leftToRead)
|
||||
{
|
||||
connection->state = PROCESS_REQUEST;
|
||||
}
|
||||
}
|
||||
|
||||
if(connection->state == PROCESS_REQUEST){
|
||||
processRequest(connection->request, connection->response);
|
||||
|
||||
if(connection->response->body.size() > 0){
|
||||
connection->response->headers["Content-Length"] = QString::number(connection->response->body.size());
|
||||
}
|
||||
connection->response->headers["Connection"] = "Keep-Alive";
|
||||
|
||||
QString responseStatusText = "Internal Server Error";
|
||||
if (connection->response->statusCode == 200)
|
||||
responseStatusText = "OK";
|
||||
else if (connection->response->statusCode == 204)
|
||||
responseStatusText = "No Content";
|
||||
else if (connection->response->statusCode == 404)
|
||||
responseStatusText = "Not Found";
|
||||
|
||||
|
||||
QString responseHead = ((QString)"HTTP/1.1 %1 %2\r\n").arg(connection->response->statusCode).arg(responseStatusText);
|
||||
for (auto h: connection->response->headers.keys())
|
||||
{
|
||||
responseHead += ((QString)"%1: %2\r\n").arg(h, connection->response->headers[h]);
|
||||
}
|
||||
responseHead += "\r\n";
|
||||
tcpSocket->write(responseHead.toUtf8());
|
||||
tcpSocket->write(connection->response->body.toUtf8());
|
||||
connection->state = CREATING;
|
||||
}
|
||||
});
|
||||
connect(tcpSocket, &QTcpSocket::disconnected, this, [tcpSocket]()
|
||||
{ tcpSocket->close(); });
|
||||
}
|
||||
19
launcher/AuthServer.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QTcpServer>
|
||||
#include "settings/SettingsObject.h"
|
||||
|
||||
class AuthServer: public QObject
|
||||
{
|
||||
public:
|
||||
explicit AuthServer(QObject *parent = 0);
|
||||
|
||||
quint16 port();
|
||||
|
||||
private:
|
||||
void newConnection();
|
||||
|
||||
private:
|
||||
std::shared_ptr<QTcpServer> m_tcpServer;
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013-2019 MultiMC Contributors
|
||||
/* 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.
|
||||