forked from Github/PrismLauncher
Compare commits
1620 commits
release-8.
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6799b772de | ||
|
|
3b87452699 | ||
|
|
0af021fef2 | ||
|
|
d24066c965 | ||
|
|
46385e671d | ||
|
|
73bb740bbb | ||
|
|
50ab1b2c40 | ||
|
|
d472b966dc | ||
|
|
beee93e199 | ||
|
|
8f1750df51 | ||
|
|
bdf87fa02f | ||
|
|
3295b0717a | ||
|
|
7b97863685 | ||
|
|
3e4fc2ff3b | ||
|
|
d22a20ffa6 | ||
|
|
de86e63611 | ||
|
|
e41a60e57e | ||
|
|
174cddcf42 | ||
|
|
9e954548dd | ||
|
|
14b653e08d | ||
|
|
79180db663 | ||
|
|
22b59e760c | ||
|
|
1d1a4f1b30 | ||
|
|
bf1084d7fa | ||
|
|
ef825a7bc5 | ||
|
|
8d143c7b95 | ||
|
|
9ea33e0651 | ||
|
|
f1209c1279 | ||
|
|
17e95c0d07 | ||
|
|
cb83f8e52d | ||
|
|
7d202e348d | ||
|
|
364b5be856 | ||
|
|
e1896d5b6b | ||
|
|
6e3dd04a1f | ||
|
|
f7a401ab22 | ||
|
|
9ed6801b15 | ||
|
|
06b50dfd33 | ||
|
|
5a76d9b7e1 | ||
|
|
34438824c1 | ||
|
|
ac598740b3 | ||
|
|
85c2710e33 | ||
|
|
afe6d4836d | ||
|
|
9c26ba5932 | ||
|
|
2a987b5a4a | ||
|
|
1a375dc243 | ||
|
|
ab3792b080 | ||
|
|
f9dcc31843 | ||
|
|
989e6c6cb6 | ||
|
|
138684a84f | ||
|
|
faf7e6b06e | ||
|
|
f1671e3ded | ||
|
|
6404d81ada | ||
|
|
d6eb96df5c | ||
|
|
c5efe081b4 | ||
|
|
a639bc019e | ||
|
|
588e0d6305 | ||
|
|
d7eddedf41 | ||
|
|
6ae9fa4df4 | ||
|
|
86cc6d3c5e | ||
|
|
61d7f08883 | ||
|
|
0deb5e20ae | ||
|
|
fcb3dd5e9d | ||
|
|
169f5728b1 | ||
|
|
8816be1668 | ||
|
|
bd752c025c | ||
|
|
49d734f314 | ||
|
|
417688089d | ||
|
|
de21b77ae1 | ||
|
|
68bf409a98 | ||
|
|
5d179671bf | ||
|
|
dc6f536843 | ||
|
|
cc504f4a6c | ||
|
|
a92c68adbd | ||
|
|
7aaa9fa93c | ||
|
|
81769b9047 | ||
|
|
147b315c14 | ||
|
|
9d0918a064 | ||
|
|
98ab686348 | ||
|
|
39ae2fbd4a | ||
|
|
36a35d7925 | ||
|
|
1b4b36df33 | ||
|
|
3d9e073721 | ||
|
|
fdb4159f2a | ||
|
|
98be91c9d5 | ||
|
|
705375c0df | ||
|
|
8d3684c484 | ||
|
|
0d4eeb9965 | ||
|
|
caccaefb39 | ||
|
|
532fec7748 | ||
|
|
4ba3fd559d | ||
|
|
2a403b3bfe | ||
|
|
f8a504e254 | ||
|
|
e66f447ce5 | ||
|
|
67b218b1f3 | ||
|
|
db766574a4 | ||
|
|
18da01c9a6 | ||
|
|
c79ad73311 | ||
|
|
fb3926a85d | ||
|
|
8d4f279539 | ||
|
|
1b5d3c2bf9 | ||
|
|
be3eca8c97 | ||
|
|
66f0397087 | ||
|
|
8e8538b506 | ||
|
|
dc00c47f2e | ||
|
|
7d3238d234 | ||
|
|
f17599c8b8 | ||
|
|
8e5a7c6e33 | ||
|
|
1d8bf1d5a7 | ||
|
|
c2f4389e5f | ||
|
|
b7d117cc77 | ||
|
|
1cce582d56 | ||
|
|
867aaf881e | ||
|
|
14b348ffe7 | ||
|
|
181610a8c1 | ||
|
|
b7f2175718 | ||
|
|
7a1dc0e9c9 | ||
|
|
9317a675ed | ||
|
|
7cb8deeb90 | ||
|
|
bca517b8d3 | ||
|
|
e4ad4051c8 | ||
|
|
3ee73916ca | ||
|
|
03834e1455 | ||
|
|
54da41c625 | ||
|
|
f4f1d5f0d7 | ||
|
|
fbd886e9db | ||
|
|
0fb99d837f | ||
|
|
cb8320ca41 | ||
|
|
33981b9870 | ||
|
|
07a0a15457 | ||
|
|
0f9ea5c949 | ||
|
|
d40c8b704c | ||
|
|
82a500f4e8 | ||
|
|
6ebfcb91cf | ||
|
|
e01df73deb | ||
|
|
7dde35fef2 | ||
|
|
0b6ce5525d | ||
|
|
f17faadf8a | ||
|
|
c7b6977692 | ||
|
|
423d72a6e8 | ||
|
|
2a2ed9f8bd | ||
|
|
0412ed3f78 | ||
|
|
f4d93a08ba | ||
|
|
8d53242952 | ||
|
|
706e263050 | ||
|
|
507faf9f00 | ||
|
|
c0803365ef | ||
|
|
02e527c15b | ||
|
|
5659dad23d | ||
|
|
647bb04a0d | ||
|
|
b6cd46ad27 | ||
|
|
c3712ba648 | ||
|
|
c3e44554ab | ||
|
|
a1c1c0b769 | ||
|
|
15e58589d5 | ||
|
|
eb8c375ec5 | ||
|
|
1dd0728a58 | ||
|
|
cf2dcbd431 | ||
|
|
dedb7a2343 | ||
|
|
614574f15c | ||
|
|
28a471777a | ||
|
|
93526637cb | ||
|
|
94c893bd86 | ||
|
|
c99876f3d7 | ||
|
|
a3b2f1a931 | ||
|
|
488dc7815c | ||
|
|
dbee4bee14 | ||
|
|
2cfd116947 | ||
|
|
3f67ef968b | ||
|
|
fd9c80db62 | ||
|
|
7e76638f2c | ||
|
|
884f15da1e | ||
|
|
616aa002e1 | ||
|
|
e0faee7f26 | ||
|
|
f6770a847a | ||
|
|
4a50e94967 | ||
|
|
514ec99a03 | ||
|
|
6c062c48b4 | ||
|
|
b40a1973bf | ||
|
|
a09af619ce | ||
|
|
835944387b | ||
|
|
d927c539b5 | ||
|
|
14454faac8 | ||
|
|
2351531e84 | ||
|
|
33ff3b4f36 | ||
|
|
11e7b218d4 | ||
|
|
fbbd64ddb7 | ||
|
|
41db87c8cf | ||
|
|
79dd912126 | ||
|
|
00fa65c18b | ||
|
|
59db80cb0e | ||
|
|
e5a4dfdf16 | ||
|
|
6ca18c6266 | ||
|
|
e74592fa09 | ||
|
|
abbebff400 | ||
|
|
3c88eccf17 | ||
|
|
48235f5b71 | ||
|
|
736ecf266d | ||
|
|
b2a37452be | ||
|
|
54f2e75b9d | ||
|
|
f7620a43aa | ||
|
|
bcbe5da87f | ||
|
|
e735ea7e63 | ||
|
|
ce61ed2f86 | ||
|
|
172e43cd3f | ||
|
|
9f0b2bafd8 | ||
|
|
03b34e39f8 | ||
|
|
885fc66c14 | ||
|
|
f41380d91c | ||
|
|
66883db645 | ||
|
|
df45e5518f | ||
|
|
4c8c6e543e | ||
|
|
1d9508c268 | ||
|
|
7d03cb114d | ||
|
|
2fcf03ead3 | ||
|
|
012bbca197 | ||
|
|
8132828ad7 | ||
|
|
9601fbf2d5 | ||
|
|
6e55239cec | ||
|
|
900235642c | ||
|
|
f667f56fb7 | ||
|
|
e06b3e7a7a | ||
|
|
223b6b3b2b | ||
|
|
71e55f8829 | ||
|
|
e6d4e5d78d | ||
|
|
8ecc21c093 | ||
|
|
0a80da28fa | ||
|
|
b67d400f60 | ||
|
|
1ab10bdd84 | ||
|
|
ba6743d134 | ||
|
|
d63a336411 | ||
|
|
4a92573235 | ||
|
|
db3d796275 | ||
|
|
6f2f3c2d3b | ||
|
|
f77f0207f7 | ||
|
|
99cfb62370 | ||
|
|
2b590f24ed | ||
|
|
670c932f32 | ||
|
|
ba07a136dc | ||
|
|
9ae16921bc | ||
|
|
887e4a964a | ||
|
|
8faff9ac6a | ||
|
|
0e80aae1b8 | ||
|
|
c142d38f08 | ||
|
|
82bffabb69 | ||
|
|
14c95b9d37 | ||
|
|
b7598c6a41 | ||
|
|
ced2f9d1a0 | ||
|
|
5661444414 | ||
|
|
afa1e0599a | ||
|
|
43e4481b70 | ||
|
|
65b0fdf08d | ||
|
|
f6511c601e | ||
|
|
fd109c4740 | ||
|
|
2368521a9a | ||
|
|
d49e6187cd | ||
|
|
c1c198b84b | ||
|
|
e7ff3146fe | ||
|
|
8e7d84d05b | ||
|
|
d157159106 | ||
|
|
bd8b30c606 | ||
|
|
f56a8feade | ||
|
|
f3cae427f1 | ||
|
|
51b7abec24 | ||
|
|
d90ed89a30 | ||
|
|
ad1d5ddcdf | ||
|
|
2dee936246 | ||
|
|
a0a74612e1 | ||
|
|
fbd3aa36c4 | ||
|
|
94f65f8727 | ||
|
|
2b22a1aebe | ||
|
|
354a0ed46e | ||
|
|
9cdda4377b | ||
|
|
49044e23de | ||
|
|
f57ca1e79e | ||
|
|
0f42f3cc5c | ||
|
|
81c5866fa9 | ||
|
|
1be29076ea | ||
|
|
3c17f606ed | ||
|
|
09a118e85e | ||
|
|
c12beb43a0 | ||
|
|
08f5148a9a | ||
|
|
14ad720c58 | ||
|
|
0ee5665c95 | ||
|
|
bd82737efb | ||
|
|
f75e049675 | ||
|
|
1f8ce9eb92 | ||
|
|
d4bf61da50 | ||
|
|
33754db9b9 | ||
|
|
1809858fc7 | ||
|
|
0cafac84ac | ||
|
|
9eb1ce3ad7 | ||
|
|
6a6fe60a5b | ||
|
|
6338e2b362 | ||
|
|
b1113f4293 | ||
|
|
c4cd7cf6c1 | ||
|
|
11d4d948aa | ||
|
|
61448a61ea | ||
|
|
f373a5cea0 | ||
|
|
935b2e8fb2 | ||
|
|
b1c8ab9350 | ||
|
|
b593ff09e9 | ||
|
|
44894a29b1 | ||
|
|
685d3c79ac | ||
|
|
b66d6b2812 | ||
|
|
5b8ea89092 | ||
|
|
13e13ea8fc | ||
|
|
63b10738b2 | ||
|
|
a5c554cf6e | ||
|
|
6a2ed4a631 | ||
|
|
b7152e7010 | ||
|
|
d766de5fbf | ||
|
|
8daa1219a5 | ||
|
|
3bc40120cd | ||
|
|
4ce025c0a2 | ||
|
|
d8702e1357 | ||
|
|
9fe81ff564 | ||
|
|
5eff9b0934 | ||
|
|
8e586c883c | ||
|
|
7bd04ae928 | ||
|
|
4a7d624e9b | ||
|
|
92a906e0d4 | ||
|
|
24cecf7b63 | ||
|
|
89a327d363 | ||
|
|
e7cd8fe078 | ||
|
|
3d82bc37af | ||
|
|
f13e875f0d | ||
|
|
79439a0217 | ||
|
|
210d0d8aa2 | ||
|
|
836aebc0d9 | ||
|
|
73d33f93b3 | ||
|
|
562c301326 | ||
|
|
a43ebefc97 | ||
|
|
69028969f1 | ||
|
|
671d5270eb | ||
|
|
9a5b773e69 | ||
|
|
f3f4c44620 | ||
|
|
b049e92697 | ||
|
|
ea703ba26e | ||
|
|
78e24962f9 | ||
|
|
fcadbbb739 | ||
|
|
a8c3a49cb9 | ||
|
|
0586d38e03 | ||
|
|
3ac080806d | ||
|
|
38e0e0cb73 | ||
|
|
0501c1e17a | ||
|
|
417bb3564d | ||
|
|
e43836a271 | ||
|
|
9a161a0335 | ||
|
|
2d234b127b | ||
|
|
464c7c1d4d | ||
|
|
2030041fcc | ||
|
|
01721b593b | ||
|
|
0bae14999f | ||
|
|
522d105dbe | ||
|
|
7519c63f2e | ||
|
|
f60d6e4d48 | ||
|
|
43eb27b695 | ||
|
|
ac13579b99 | ||
|
|
a108b5b420 | ||
|
|
f239f4c17c | ||
|
|
5685bc3407 | ||
|
|
bd01c47dc9 | ||
|
|
3c836ece71 | ||
|
|
3a1c06de02 | ||
|
|
ef0cb88dd3 | ||
|
|
0b624a43d6 | ||
|
|
97f4ead0fb | ||
|
|
78ceae9c17 | ||
|
|
72cfad8fee | ||
|
|
09c9da268f | ||
|
|
2bc6ae9756 | ||
|
|
0c4c86c41f | ||
|
|
0beaa94311 | ||
|
|
b39098dbc5 | ||
|
|
5b6d551650 | ||
|
|
98adcc60a3 | ||
|
|
909114bf2a | ||
|
|
2abbcdfa6f | ||
|
|
093313e591 | ||
|
|
c72cce6572 | ||
|
|
4b66320f63 | ||
|
|
a76f37760c | ||
|
|
d31c947b36 | ||
|
|
e2b346fc53 | ||
|
|
7c45f88769 | ||
|
|
acd4dceafa | ||
|
|
df9f54454a | ||
|
|
538815e033 | ||
|
|
3fae659b30 | ||
|
|
5bd5627a96 | ||
|
|
a200fca45c | ||
|
|
dc1f56e108 | ||
|
|
6e5f616ced | ||
|
|
1eee5c924b | ||
|
|
999dec18dd | ||
|
|
d5990cca43 | ||
|
|
03294f0000 | ||
|
|
f7bd76c7d4 | ||
|
|
cc0c9d208d | ||
|
|
859fac604b | ||
|
|
a61c93bede | ||
|
|
023aa18e85 | ||
|
|
63172540fb | ||
|
|
bcb1d456d4 | ||
|
|
010678da53 | ||
|
|
dfe3cd849d | ||
|
|
44b4262f3c | ||
|
|
2539aa42da | ||
|
|
62f19a6ac9 | ||
|
|
c57e26cf57 | ||
|
|
1b58645c52 | ||
|
|
946e791d39 | ||
|
|
5ea2dece54 | ||
|
|
0eefa52dbc | ||
|
|
b676a67b3c | ||
|
|
fa92e6bd29 | ||
|
|
79be2fc062 | ||
|
|
974c89b9c3 | ||
|
|
38fa2e0cd4 | ||
|
|
988ef32019 | ||
|
|
bf432b5514 | ||
|
|
68bf500f7e | ||
|
|
5adca58337 | ||
|
|
2663ac5405 | ||
|
|
732008db20 | ||
|
|
a63451212f | ||
|
|
c899043816 | ||
|
|
ed15a28eb3 | ||
|
|
a897b012d9 | ||
|
|
00f4221a83 | ||
|
|
b9c19fd3f7 | ||
|
|
8fa4467a36 | ||
|
|
c5daca3b01 | ||
|
|
37d3b2a4f6 | ||
|
|
2dc6f7504b | ||
|
|
b9a6c13d47 | ||
|
|
6475dc5786 | ||
|
|
1b2e9b95e2 | ||
|
|
3e60713257 | ||
|
|
48904c0d66 | ||
|
|
f641f3acda | ||
|
|
b675406b1a | ||
|
|
a58e81d744 | ||
|
|
4a40b53e5f | ||
|
|
4a5749f4d9 | ||
|
|
9567d763c1 | ||
|
|
3e1cf6b2a7 | ||
|
|
1243328876 | ||
|
|
77463c6d33 | ||
|
|
dc67807894 | ||
|
|
926a6bc72a | ||
|
|
d06f0529a0 | ||
|
|
53c3d99bdd | ||
|
|
c57b1d4227 | ||
|
|
1b00d61587 | ||
|
|
3ca67cc388 | ||
|
|
2fd8685894 | ||
|
|
0af639c20b | ||
|
|
7438c71fdd | ||
|
|
b4ecb38ae5 | ||
|
|
f36cfaf878 | ||
|
|
aab82705cd | ||
|
|
914ae6ead6 | ||
|
|
e53a5b3eca | ||
|
|
2e537e55de | ||
|
|
7eac06cad9 | ||
|
|
8d14060ad1 | ||
|
|
8faebe320e | ||
|
|
580fc32518 | ||
|
|
db3c7d01fd | ||
|
|
160d2962e6 | ||
|
|
75756b49c3 | ||
|
|
81f30f0360 | ||
|
|
ca6b62c773 | ||
|
|
2c2defb266 | ||
|
|
d2f0d1d8d5 | ||
|
|
d38e7fa142 | ||
|
|
e35faa5522 | ||
|
|
bee59c904a | ||
|
|
8932a42644 | ||
|
|
71dda06276 | ||
|
|
1c6bd74ed8 | ||
|
|
0afdce8a31 | ||
|
|
6492ad7076 | ||
|
|
ad786fb983 | ||
|
|
281621cfc0 | ||
|
|
312009513d | ||
|
|
5073fc19d4 | ||
|
|
e59f90eca8 | ||
|
|
22ebf7e961 | ||
|
|
6706317f20 | ||
|
|
e18e9b0509 | ||
|
|
f46b73acb5 | ||
|
|
40b10ecf30 | ||
|
|
1db122f1a2 | ||
|
|
e34ebc1a0d | ||
|
|
d20f24d96f | ||
|
|
ef76894587 | ||
|
|
f1ef243617 | ||
|
|
973b43ca7a | ||
|
|
c85294af9d | ||
|
|
7b49fd6508 | ||
|
|
f7f7c4b43e | ||
|
|
506ec64273 | ||
|
|
0a576a0f67 | ||
|
|
40c3866f63 | ||
|
|
6b0007291b | ||
|
|
fa68428a90 | ||
|
|
b17a688237 | ||
|
|
e3f78e0205 | ||
|
|
7e49fa0426 | ||
|
|
e2ef280da9 | ||
|
|
7fbf0d3bf3 | ||
|
|
2622d3daa9 | ||
|
|
01015a7345 | ||
|
|
14fb54afd9 | ||
|
|
42eedd1afa | ||
|
|
c2a2e95183 | ||
|
|
c5592a446a | ||
|
|
166207753c | ||
|
|
4ea7132bdf | ||
|
|
828acef7e9 | ||
|
|
685519d12f | ||
|
|
74acb483af | ||
|
|
cf12365358 | ||
|
|
630df29010 | ||
|
|
034e2a44ad | ||
|
|
c64b7cd693 | ||
|
|
430013737a | ||
|
|
d55ebefac3 | ||
|
|
6a12c43c78 | ||
|
|
43fc9ba932 | ||
|
|
23c69e7f94 | ||
|
|
70a0a71f10 | ||
|
|
ff9fa6aa4f | ||
|
|
faf9086705 | ||
|
|
465c9498d4 | ||
|
|
44f589817f | ||
|
|
aa206f4c89 | ||
|
|
1bb7f35ddb | ||
|
|
5f874330d5 | ||
|
|
96414d7153 | ||
|
|
cbdacf81ee | ||
|
|
d2304b6698 | ||
|
|
fac28a81ae | ||
|
|
2a86ceb5d6 | ||
|
|
575cdb043b | ||
|
|
eae5d70385 | ||
|
|
92495494ca | ||
|
|
e4be8f97cd | ||
|
|
fd14d3e9b0 | ||
|
|
11927e9bf2 | ||
|
|
762d24bd02 | ||
|
|
a910337e9d | ||
|
|
89ed85182c | ||
|
|
5f64d7dc47 | ||
|
|
0b05c7cb07 | ||
|
|
7d9e47ac18 | ||
|
|
aa3a4585ee | ||
|
|
2ad34c724e | ||
|
|
6d017b5f0b | ||
|
|
65f8526152 | ||
|
|
7472233033 | ||
|
|
99bd4a8937 | ||
|
|
cf914526bf | ||
|
|
26ae608685 | ||
|
|
da7cc529f0 | ||
|
|
99ecab01fc | ||
|
|
a49a58bc45 | ||
|
|
13eedbb7de | ||
|
|
e29ea4efb0 | ||
|
|
0ae421c265 | ||
|
|
e85b364748 | ||
|
|
271c38bc56 | ||
|
|
7a83d0cea0 | ||
|
|
4c81d8c53d | ||
|
|
7dff77ca35 | ||
|
|
1edfbeec8d | ||
|
|
858f6aa9b8 | ||
|
|
e6f30c0ebe | ||
|
|
014fc14291 | ||
|
|
7f594a342e | ||
|
|
002fc71b8d | ||
|
|
ca298d1ed5 | ||
|
|
7a66276838 | ||
|
|
4d24213aa3 | ||
|
|
23c06f23c3 | ||
|
|
1b756b99bb | ||
|
|
38b5ee198a | ||
|
|
6bd8b72f68 | ||
|
|
efac063797 | ||
|
|
e9791d6bc1 | ||
|
|
91d6465d45 | ||
|
|
b5bbe93775 | ||
|
|
b0cd412926 | ||
|
|
e6bc61d6d0 | ||
|
|
6de026bfcf | ||
|
|
d6a825dd47 | ||
|
|
3d4be7ac8c | ||
|
|
af83fc84a7 | ||
|
|
6b07e8e72d | ||
|
|
d2daacf736 | ||
|
|
96dad63173 | ||
|
|
c2192cfa98 | ||
|
|
dcca175b36 | ||
|
|
a9c7e95c66 | ||
|
|
c9809fff6d | ||
|
|
b41e730707 | ||
|
|
659fa7fbfb | ||
|
|
0215af828d | ||
|
|
7a5e4a4293 | ||
|
|
128827cef5 | ||
|
|
ba7391308e | ||
|
|
0308710211 | ||
|
|
fe23e76714 | ||
|
|
af65eb17cf | ||
|
|
9d684dcb82 | ||
|
|
e4967f8d08 | ||
|
|
5702b8e3c2 | ||
|
|
110efb7259 | ||
|
|
b77dc47c6c | ||
|
|
ca5df3ea60 | ||
|
|
61e62dce1b | ||
|
|
d5c5f5d4fc | ||
|
|
2c0b17d107 | ||
|
|
0ac62f9ddf | ||
|
|
4928706d8c | ||
|
|
63a380a039 | ||
|
|
33bf370d17 | ||
|
|
5d3549a5e9 | ||
|
|
29a6db171f | ||
|
|
ff31e88cf8 | ||
|
|
91d03b34ac | ||
|
|
6352362907 | ||
|
|
fc2d013402 | ||
|
|
c666d060fc | ||
|
|
cae1fbc171 | ||
|
|
0041d2a126 | ||
|
|
69c55d90ce | ||
|
|
ddcaf25149 | ||
|
|
fcdedc795c | ||
|
|
fb10cb5fdf | ||
|
|
dcc58c8b75 | ||
|
|
cb65f6dfb5 | ||
|
|
d7579319e0 | ||
|
|
e6019ab396 | ||
|
|
6d425717e9 | ||
|
|
84263cd94c | ||
|
|
303f3303f6 | ||
|
|
2df5d4012c | ||
|
|
d3ca8864c2 | ||
|
|
0a543cb66b | ||
|
|
b989c5a36c | ||
|
|
3076e5b961 | ||
|
|
e5b1fa1e3a | ||
|
|
061fdc1ada | ||
|
|
5f345cedea | ||
|
|
39b4293a62 | ||
|
|
ebd7974fcc | ||
|
|
71c7dd1c5d | ||
|
|
ca2dda5547 | ||
|
|
aeb7f29a5b | ||
|
|
fdcd1068f3 | ||
|
|
08cab11cfe | ||
|
|
e49e2622fc | ||
|
|
c07260e836 | ||
|
|
330c95e55b | ||
|
|
a6ae078e7e | ||
|
|
c886111e18 | ||
|
|
a09742a406 | ||
|
|
5973c1c45e | ||
|
|
9596e6fb01 | ||
|
|
5d8d1c4b90 | ||
|
|
1dd21a5e4a | ||
|
|
83a5fe1984 | ||
|
|
219de66783 | ||
|
|
9b172278f8 | ||
|
|
dbcfefca1b | ||
|
|
d70be5121c | ||
|
|
4f795eb6ef | ||
|
|
0f311453e1 | ||
|
|
7104f8909d | ||
|
|
a184b26a00 | ||
|
|
4b4da8e6f6 | ||
|
|
f0c9b52055 | ||
|
|
046e3588af | ||
|
|
fc445078cd | ||
|
|
7ff173d72a | ||
|
|
aa24d70a24 | ||
|
|
80243ab554 | ||
|
|
5d9f4f9ee7 | ||
|
|
de9df31efa | ||
|
|
8488d11c69 | ||
|
|
492c78a570 | ||
|
|
c4cc1cfe4f | ||
|
|
4ed92a95c2 | ||
|
|
a85d6cb1f2 | ||
|
|
474effe7c7 | ||
|
|
cf319649f9 | ||
|
|
a94a081b9c | ||
|
|
ce280c2d04 | ||
|
|
a791e22853 | ||
|
|
44bf0315ad | ||
|
|
25bae20db4 | ||
|
|
7a0b299be9 | ||
|
|
ae3e4ed528 | ||
|
|
b4f5c3d241 | ||
|
|
9d1fffff27 | ||
|
|
41bcfb3c07 | ||
|
|
e6234b5086 | ||
|
|
eab53c6e42 | ||
|
|
f1a4a1e8ee | ||
|
|
63a458ca0c | ||
|
|
196b2b5a3e | ||
|
|
ebad11d34f | ||
|
|
06add2b0a8 | ||
|
|
9d903175ab | ||
|
|
cf220356b1 | ||
|
|
715c99636d | ||
|
|
0a95b57c0a | ||
|
|
06e1cab41f | ||
|
|
2af6902b42 | ||
|
|
4f8b20437f | ||
|
|
515baffb8e | ||
|
|
4ad19cded5 | ||
|
|
0b79abcdcd | ||
|
|
90e756c041 | ||
|
|
ddf1ea014c | ||
|
|
4aa2e5b85d | ||
|
|
55f740453c | ||
|
|
72dd3dfba5 | ||
|
|
1021222c95 | ||
|
|
2cd0223cff | ||
|
|
09d258670a | ||
|
|
2c02d02214 | ||
|
|
bac678a7fc | ||
|
|
147741e616 | ||
|
|
66e6fc04d3 | ||
|
|
9d7c36c2fc | ||
|
|
5928bc30e1 | ||
|
|
68292aff6c | ||
|
|
3f3351f89e | ||
|
|
37388e1d48 | ||
|
|
3b473b24d8 | ||
|
|
b1a86bec34 | ||
|
|
3aaa36a2bc | ||
|
|
8e2bb21e23 | ||
|
|
2958691857 | ||
|
|
7c38edc93d | ||
|
|
555c4a7c59 | ||
|
|
4e9e7212a6 | ||
|
|
52839774e6 | ||
|
|
1a66db78a9 | ||
|
|
14b09549e1 | ||
|
|
2f88da9de2 | ||
|
|
12db1efd20 | ||
|
|
6b1c16b01a | ||
|
|
1fcad8d427 | ||
|
|
096266c141 | ||
|
|
73db320bff | ||
|
|
00b83efb9a | ||
|
|
e57c2180da | ||
|
|
bb48d1ec9d | ||
|
|
c04031ff00 | ||
|
|
98d68bafeb | ||
|
|
92b9f1a3e3 | ||
|
|
e0063b1ee7 | ||
|
|
d8b47f5c8a | ||
|
|
369d7d83f5 | ||
|
|
5f1d86d91a | ||
|
|
2ac89c2378 | ||
|
|
b0386a4c2f | ||
|
|
24aa337a64 | ||
|
|
27ee8374e7 | ||
|
|
178bd4a9c3 | ||
|
|
c1d0560524 | ||
|
|
8014283bf4 | ||
|
|
888b22b4c7 | ||
|
|
8607dd46b5 | ||
|
|
97147f2d24 | ||
|
|
5f3d3c3294 | ||
|
|
a63842c727 | ||
|
|
ac8c7baff2 | ||
|
|
9cbdbddb08 | ||
|
|
766ddc80e3 | ||
|
|
3f68b68691 | ||
|
|
c07e791d7f | ||
|
|
35e195b397 | ||
|
|
6af52c97c2 | ||
|
|
c9827f8085 | ||
|
|
a587de8553 | ||
|
|
2fb3e12492 | ||
|
|
8d3e6667cd | ||
|
|
94fd02a34f | ||
|
|
fdd302bc11 | ||
|
|
8dde773486 | ||
|
|
bbf52e3549 | ||
|
|
d0584684f2 | ||
|
|
2108ddcc1d | ||
|
|
5fbbdb9707 | ||
|
|
c01f3107a4 | ||
|
|
978e96916d | ||
|
|
f1d1c9e22c | ||
|
|
a121bfbb15 | ||
|
|
98f8bc526d | ||
|
|
d5fb9f0935 | ||
|
|
8cdaf5d05f | ||
|
|
cfdf4a9e1f | ||
|
|
1a84dc9bca | ||
|
|
b872f88617 | ||
|
|
d2510b851b | ||
|
|
2d51008304 | ||
|
|
3c4eed13a3 | ||
|
|
9d16557328 | ||
|
|
863ebe421c | ||
|
|
52490743b4 | ||
|
|
164d31dcb1 | ||
|
|
3db4f4dc2b | ||
|
|
8db0b5f421 | ||
|
|
f60d0135f7 | ||
|
|
74a86df53c | ||
|
|
1bd4e60e99 | ||
|
|
c35461b314 | ||
|
|
014721ca10 | ||
|
|
712fbc3a4b | ||
|
|
e9d681a088 | ||
|
|
52338f0e88 | ||
|
|
6e8dc6ccc0 | ||
|
|
3fc5f56ed6 | ||
|
|
ce5b2c54c5 | ||
|
|
f7c3e08ac8 | ||
|
|
7fdc65eda8 | ||
|
|
2b229d7326 | ||
|
|
80ea67f7cf | ||
|
|
a74bebb358 | ||
|
|
125fb2eaf2 | ||
|
|
5f5296ef5e | ||
|
|
bbbe7c879e | ||
|
|
df142550fe | ||
|
|
c4a65dd861 | ||
|
|
6b1c4981e7 | ||
|
|
c1c182fd0c | ||
|
|
1c1e296960 | ||
|
|
7d418a32e7 | ||
|
|
93a0277479 | ||
|
|
2ffabbdc6d | ||
|
|
11ae169087 | ||
|
|
95e7f36732 | ||
|
|
6607c3bfa4 | ||
|
|
3f0fa54fe8 | ||
|
|
e63f25c9e4 | ||
|
|
dc39698c48 | ||
|
|
12e04c412b | ||
|
|
154f4811c0 | ||
|
|
29d32f0d9a | ||
|
|
242ddbb7e1 | ||
|
|
34e1a5e660 | ||
|
|
1e0db46728 | ||
|
|
6c76ba5163 | ||
|
|
04b2ac2efa | ||
|
|
689b76885c | ||
|
|
6a99312df5 | ||
|
|
ca369ac2b0 | ||
|
|
9560f9ed98 | ||
|
|
d6a03d3f2c | ||
|
|
3af4c94612 | ||
|
|
55b6490530 | ||
|
|
7e5db651fd | ||
|
|
6f6ddc00c8 | ||
|
|
0e996270b7 | ||
|
|
24cf671370 | ||
|
|
3ef5cd20f4 | ||
|
|
052ee0d71c | ||
|
|
39b8451130 | ||
|
|
8ff53e47e8 | ||
|
|
6e5c41fc8e | ||
|
|
e961cab352 | ||
|
|
b096ee6a0c | ||
|
|
aeb174fc55 | ||
|
|
71b2c4b1fd | ||
|
|
448ded2387 | ||
|
|
b7008d2880 | ||
|
|
45463dcd75 | ||
|
|
3263a706b4 | ||
|
|
080e64faaf | ||
|
|
a6788a92cf | ||
|
|
bbff31bcc0 | ||
|
|
a70aeeea2b | ||
|
|
83883cadf9 | ||
|
|
f350918186 | ||
|
|
c9c39b8203 | ||
|
|
f7120a4f6f | ||
|
|
f4f0a61bf2 | ||
|
|
817ff1ecbd | ||
|
|
2145f9f0ac | ||
|
|
7ab8f8ef54 | ||
|
|
da76b72800 | ||
|
|
2635d7c3be | ||
|
|
d1286bbe90 | ||
|
|
eaccdca02d | ||
|
|
75fbdc2df5 | ||
|
|
44a16c1ca1 | ||
|
|
a7a1b28765 | ||
|
|
8704cd8402 | ||
|
|
e94243f99b | ||
|
|
a289e0c849 | ||
|
|
7fed5e9173 | ||
|
|
5e36def18b | ||
|
|
469f051e30 | ||
|
|
6d60a6f4e3 | ||
|
|
b8fe93efa3 | ||
|
|
29444cfe0f | ||
|
|
660772d4d7 | ||
|
|
b256d325f7 | ||
|
|
f332503088 | ||
|
|
f34c263964 | ||
|
|
4917287292 | ||
|
|
5012178a98 | ||
|
|
3f7847ad34 | ||
|
|
4b52c72629 | ||
|
|
59a8b91274 | ||
|
|
694ea65457 | ||
|
|
7a200a337f | ||
|
|
764463ada7 | ||
|
|
f5f32e2c6e | ||
|
|
a2b5ac73ff | ||
|
|
a8712f7126 | ||
|
|
eacd7758e9 | ||
|
|
2b949a8059 | ||
|
|
399071c717 | ||
|
|
23095b70a4 | ||
|
|
dbe4adc034 | ||
|
|
f6a7fc215a | ||
|
|
af157c1613 | ||
|
|
08918be11e | ||
|
|
7a42fb284a | ||
|
|
0e239dfb2a | ||
|
|
abb628edb3 | ||
|
|
0ecdceccd2 | ||
|
|
201c4d2cc6 | ||
|
|
529dc7c6b7 | ||
|
|
428889a68d | ||
|
|
1b0e8260ac | ||
|
|
be66a91295 | ||
|
|
425a6e0919 | ||
|
|
d9e2badf71 | ||
|
|
d21597cf89 | ||
|
|
76a656a017 | ||
|
|
6078a771c1 | ||
|
|
5cf7466e4c | ||
|
|
74c2f2599f | ||
|
|
5a598db608 | ||
|
|
5bd28e2cc8 | ||
|
|
e42dfbc391 | ||
|
|
477d1408ad | ||
|
|
83fdfe8091 | ||
|
|
4d21f71ccf | ||
|
|
31d0cad377 | ||
|
|
c9c67ef79e | ||
|
|
d77d1f3be3 | ||
|
|
b23b53d98d | ||
|
|
7a5348a179 | ||
|
|
3ba84cb844 | ||
|
|
633489884d | ||
|
|
3887cfe5df | ||
|
|
7b39ec5048 | ||
|
|
ba947993c2 | ||
|
|
acd23ff163 | ||
|
|
c8ff768d64 | ||
|
|
b6d87fc090 | ||
|
|
6aaf3a3ed4 | ||
|
|
2ac6efaa4a | ||
|
|
23134d4b72 | ||
|
|
318819afc6 | ||
|
|
eb0d0450e3 | ||
|
|
0f2736306e | ||
|
|
639c6e7f80 | ||
|
|
78804b26ee | ||
|
|
75a457ebfa | ||
|
|
ce09f06bda | ||
|
|
09c0c11033 | ||
|
|
ab5f628453 | ||
|
|
051e788683 | ||
|
|
2a58fb0ac5 | ||
|
|
c15f6fcd2a | ||
|
|
898ee67a07 | ||
|
|
1d384544d3 | ||
|
|
8b051b97fd | ||
|
|
abedc6a23c | ||
|
|
849c3faeb4 | ||
|
|
80d8e3ee06 | ||
|
|
c2ed50627d | ||
|
|
e285a85fe8 | ||
|
|
3336f8107c | ||
|
|
93da066aa2 | ||
|
|
d74926e60d | ||
|
|
d795ba25e7 | ||
|
|
f15981d786 | ||
|
|
c703babda9 | ||
|
|
72e9d7b148 | ||
|
|
7bcb1777a1 | ||
|
|
a4142573ac | ||
|
|
a95c1768a7 | ||
|
|
e7c95c9ccb | ||
|
|
4787141698 | ||
|
|
adf0cfdebf | ||
|
|
8e0af16de9 | ||
|
|
5ce8cd6b84 | ||
|
|
5d36ed9cf9 | ||
|
|
0c76e7ab20 | ||
|
|
ce873e4a0d | ||
|
|
814f84efab | ||
|
|
51f4ede797 | ||
|
|
b9c010ffb2 | ||
|
|
e3d64608b9 | ||
|
|
cec27fd014 | ||
|
|
30ce8ecb5f | ||
|
|
5400c24c73 | ||
|
|
e068120375 | ||
|
|
48f3ca56ba | ||
|
|
bfac12604e | ||
|
|
4dddf62e5e | ||
|
|
603d196dac | ||
|
|
d2568333ed | ||
|
|
9b70606d7e | ||
|
|
cb9a83320c | ||
|
|
be8ac0b5bb | ||
|
|
a5b059ebad | ||
|
|
23a38b19f2 | ||
|
|
890fd0b09c | ||
|
|
c14e6a67a8 | ||
|
|
ccf0b01de7 | ||
|
|
46b291d02e | ||
|
|
5348a90a15 | ||
|
|
e3f55f6865 | ||
|
|
a988415028 | ||
|
|
6ecd2c7f31 | ||
|
|
3c9a207192 | ||
|
|
60bd8104c4 | ||
|
|
9ce10231ed | ||
|
|
803e26a401 | ||
|
|
e1ed317c13 | ||
|
|
2cfe482298 | ||
|
|
dedea2c05d | ||
|
|
a30c7193a2 | ||
|
|
1961e2081f | ||
|
|
e07e182e95 | ||
|
|
e33ddbadd2 | ||
|
|
be15abda90 | ||
|
|
c902da4461 | ||
|
|
e777201187 | ||
|
|
5ae98f4f90 | ||
|
|
3a4f82ffd1 | ||
|
|
f6f0f3b08d | ||
|
|
10d1720da5 | ||
|
|
73c3794c1b | ||
|
|
dac7027846 | ||
|
|
dbfd535b38 | ||
|
|
5eeb3b7768 | ||
|
|
8e8877cf8c | ||
|
|
1c8d333bfd | ||
|
|
a96f957b02 | ||
|
|
9f4654ede9 | ||
|
|
de5de6f88f | ||
|
|
210841606c | ||
|
|
89230c4f8a | ||
|
|
d88fb266a5 | ||
|
|
d185f6b856 | ||
|
|
252406ba3e | ||
|
|
a8a234b666 | ||
|
|
0cae155380 | ||
|
|
77aead6470 | ||
|
|
ef230b7ae4 | ||
|
|
cb8936ed68 | ||
|
|
8121a6536b | ||
|
|
9f48694eb2 | ||
|
|
1a994e8aed | ||
|
|
a877f9146d | ||
|
|
f91f97e56d | ||
|
|
9cc58fe62c | ||
|
|
1c3c9d2457 | ||
|
|
70f7e0878e | ||
|
|
74fcfbae32 | ||
|
|
5bc415d512 | ||
|
|
a88cc2133c | ||
|
|
9323dcb9dc | ||
|
|
9986fe4c45 | ||
|
|
363273a17b | ||
|
|
8056a2fa46 | ||
|
|
44ea8cd885 | ||
|
|
df209c7b44 | ||
|
|
e704fbbcec | ||
|
|
3ddcadcdd1 | ||
|
|
8ecab305ac | ||
|
|
0d372d2fbf | ||
|
|
450b73328e | ||
|
|
90097f1309 | ||
|
|
5f11b2e77f | ||
|
|
186a6d0c52 | ||
|
|
71904b52c1 | ||
|
|
71998cb7d9 | ||
|
|
a0eba1ace6 | ||
|
|
a2d28f6872 | ||
|
|
f1351a143e | ||
|
|
b7835036d4 | ||
|
|
e6a9badb73 | ||
|
|
28cfd31269 | ||
|
|
1930689a27 | ||
|
|
81874f22f7 | ||
|
|
45028ddc61 | ||
|
|
2012b2c243 | ||
|
|
b3abc052b5 | ||
|
|
6b967d3f76 | ||
|
|
e4742fdb3d | ||
|
|
0f178270be | ||
|
|
93f6315b16 | ||
|
|
0e5727315b | ||
|
|
f6572c1244 | ||
|
|
e212c019a0 | ||
|
|
ccf3a1582d | ||
|
|
5117b21262 | ||
|
|
5d72ee1c4f | ||
|
|
765bdf67c9 | ||
|
|
23bdccc91d | ||
|
|
df0280f52b | ||
|
|
3a833b659a | ||
|
|
3c12e27226 | ||
|
|
c0754b80a2 | ||
|
|
0b87e4c03b | ||
|
|
a7029a9e53 | ||
|
|
3046822272 | ||
|
|
1a6dfd04d6 | ||
|
|
37d348e60e | ||
|
|
794a60b684 | ||
|
|
ab7fc2e46c | ||
|
|
93f19bda6e | ||
|
|
1c809f0fb1 | ||
|
|
703470e57d | ||
|
|
5f8269f5e1 | ||
|
|
09c2c6793b | ||
|
|
d72bd70c05 | ||
|
|
7ffebcad72 | ||
|
|
ef4e5eb3cf | ||
|
|
1322277ae6 | ||
|
|
05487152fe | ||
|
|
46616d1c5d | ||
|
|
95c3daca59 | ||
|
|
e288ee6ddd | ||
|
|
853e4a13ab | ||
|
|
3b0564f098 | ||
|
|
75628bf81e | ||
|
|
92113e233a | ||
|
|
51443fce8b | ||
|
|
03def913ad | ||
|
|
634ab279b0 | ||
|
|
6591379a8a | ||
|
|
9120848278 | ||
|
|
a0311780c1 | ||
|
|
27780cc7ae | ||
|
|
fd9a4eed9e | ||
|
|
d6abcb73c5 | ||
|
|
1627f3c63b | ||
|
|
0dc2bf2681 | ||
|
|
ad8c3ca54b | ||
|
|
4101fbb634 | ||
|
|
138df66355 | ||
|
|
e1146455a3 | ||
|
|
850e48ec67 | ||
|
|
322a8899ed | ||
|
|
41d52dc347 | ||
|
|
27e76d0dcb | ||
|
|
24fd07861b | ||
|
|
da96172b0b | ||
|
|
344398f238 | ||
|
|
fba2ce3c9f | ||
|
|
1b89c63ad4 | ||
|
|
c0eb80947d | ||
|
|
fccf857d8e | ||
|
|
bd0cd82826 | ||
|
|
df60f5cc96 | ||
|
|
9574ebe480 | ||
|
|
6a9f5540d3 | ||
|
|
aac6fea724 | ||
|
|
726b0ffb3b | ||
|
|
1cdb9bd5af | ||
|
|
02340f19a5 | ||
|
|
f747ae949c | ||
|
|
bff8d7220d | ||
|
|
4421746c0e | ||
|
|
01c4e6a65c | ||
|
|
9aa81cf005 | ||
|
|
1c96ae5807 | ||
|
|
889f604a41 | ||
|
|
a288779790 | ||
|
|
038d3e4596 | ||
|
|
44327980db | ||
|
|
b4f3a969b3 | ||
|
|
99cbb5d0b2 | ||
|
|
dbdd96edc3 | ||
|
|
4aafa98852 | ||
|
|
2f489d1aec | ||
|
|
976f5f10cf | ||
|
|
4c76f7afe0 | ||
|
|
a7dad96a70 | ||
|
|
54bfbe562e | ||
|
|
5232b3cd89 | ||
|
|
ea2adf909d | ||
|
|
599a08d309 | ||
|
|
2941307581 | ||
|
|
25b69b4c0d | ||
|
|
94b48e9702 | ||
|
|
5d36067c74 | ||
|
|
01180c20e1 | ||
|
|
e738b78044 | ||
|
|
b02eef9358 | ||
|
|
8cf2a04f31 | ||
|
|
7f4c74ffbe | ||
|
|
fdaaee64d1 | ||
|
|
ff989b4305 | ||
|
|
ded77e6183 | ||
|
|
b0b77c2178 | ||
|
|
0f254c8425 | ||
|
|
29bf6cc68c | ||
|
|
830ce246e1 | ||
|
|
f3600972a0 | ||
|
|
031a9f4738 | ||
|
|
5099061a5c | ||
|
|
c0fb053ccc | ||
|
|
27d662e642 | ||
|
|
0a3303bcbd | ||
|
|
379f1a70e9 | ||
|
|
b3fc07d444 | ||
|
|
3d29da916d | ||
|
|
ba990e075b | ||
|
|
7cac8ad2b4 | ||
|
|
6935c0dc3b | ||
|
|
15670cc27b | ||
|
|
82b15268bc | ||
|
|
0384e652fb | ||
|
|
3c58fb0677 | ||
|
|
5d223f6f93 | ||
|
|
6c5bb3817b | ||
|
|
5afe75e821 | ||
|
|
b0a473a5ce | ||
|
|
901cc4e4dd | ||
|
|
7317105e4d | ||
|
|
b54410b48c | ||
|
|
ad98f600d5 | ||
|
|
cff2f4b6ff | ||
|
|
ab4974629c | ||
|
|
82d0f204e2 | ||
|
|
97ee0a19b5 | ||
|
|
4cd236ed80 | ||
|
|
81282bf7e0 | ||
|
|
f36be3f0e3 | ||
|
|
e897032383 | ||
|
|
2c18d0f1a5 | ||
|
|
25bca28707 | ||
|
|
a0e7729aa6 | ||
|
|
1c3d63981b | ||
|
|
8de1060bfb | ||
|
|
1075a94034 | ||
|
|
35b5c8097c | ||
|
|
8312713dc2 | ||
|
|
7bce88e83e | ||
|
|
72e6218df9 | ||
|
|
e60235cb88 | ||
|
|
913f6d8563 | ||
|
|
6b637a8797 | ||
|
|
9bbeb5ef3a | ||
|
|
12bf913764 | ||
|
|
0dc4a10f48 | ||
|
|
f77749e448 | ||
|
|
78482d0d3e | ||
|
|
e4306d5cb6 | ||
|
|
f54ac25614 | ||
|
|
c7cd8b7877 | ||
|
|
30913fdb2b | ||
|
|
0303b1cc7f | ||
|
|
4cda04271f | ||
|
|
bc75bcdcca | ||
|
|
a62eaba702 | ||
|
|
2c0274159f | ||
|
|
b2c2bf1460 | ||
|
|
bfd1262c14 | ||
|
|
0b3e91ac62 | ||
|
|
de9232783e | ||
|
|
27c52eff8b | ||
|
|
baebef982e | ||
|
|
67d088dc53 | ||
|
|
5a0240e709 | ||
|
|
96c714de6e | ||
|
|
f931890e95 | ||
|
|
9c610b616a | ||
|
|
c38f584fe9 | ||
|
|
b0f65476d9 | ||
|
|
7549bf7da6 | ||
|
|
839adfc90b | ||
|
|
26474d1374 | ||
|
|
dbdec15ddb | ||
|
|
a7d293b3f2 | ||
|
|
a0ebd0714d | ||
|
|
57913f1943 | ||
|
|
d94e641900 | ||
|
|
43dd929214 | ||
|
|
2cb07d3e38 | ||
|
|
35ad4448d9 | ||
|
|
f285c3fe13 | ||
|
|
219e2862f9 | ||
|
|
d56d21f928 | ||
|
|
7783ef9285 | ||
|
|
b76c413936 | ||
|
|
048d34dc9d | ||
|
|
786e03571b | ||
|
|
57a589367f | ||
|
|
9523a2d77b | ||
|
|
e5b608447a | ||
|
|
f454333cc5 | ||
|
|
345cdf5c9d | ||
|
|
05e4533096 | ||
|
|
1130fe8510 | ||
|
|
0cf9b6537b | ||
|
|
73f956c897 | ||
|
|
b280a6d36d | ||
|
|
ad01102bad | ||
|
|
0ce0769842 | ||
|
|
eca5d88576 | ||
|
|
b0a4af7412 | ||
|
|
dbbbf5aa93 | ||
|
|
ea38f8168a | ||
|
|
c29af83dc3 | ||
|
|
1e48bf838d | ||
|
|
1bf386603d | ||
|
|
9a33bdcf50 | ||
|
|
526e580fbb | ||
|
|
13d29ac6f4 | ||
|
|
358cf5b348 | ||
|
|
43306350e0 | ||
|
|
e39a03421a | ||
|
|
a1e3901b3f | ||
|
|
2993306226 | ||
|
|
74e2e49134 | ||
|
|
e227826a90 | ||
|
|
211e62cedb | ||
|
|
7382d33430 | ||
|
|
1fcf405c88 | ||
|
|
8f29cdf195 | ||
|
|
a8220cd296 | ||
|
|
213963257c | ||
|
|
eb8e150fa8 | ||
|
|
56397446db | ||
|
|
8cbdecc454 | ||
|
|
317d52b44a | ||
|
|
d0fe700b53 | ||
|
|
0ccdcd23e3 | ||
|
|
f33110ee0c | ||
|
|
9580f16c33 | ||
|
|
400b518bc2 | ||
|
|
32a9c37ecd | ||
|
|
3d50bef10c | ||
|
|
608a09d211 | ||
|
|
469aaf6051 | ||
|
|
6a61900328 | ||
|
|
763eab6b96 | ||
|
|
4498d32cc0 | ||
|
|
d10b075677 | ||
|
|
a382754b1e | ||
|
|
2391ad200f | ||
|
|
648aa18e6f | ||
|
|
7c33a8e448 | ||
|
|
201c9783d5 | ||
|
|
836f74cd46 | ||
|
|
3567369d44 | ||
|
|
4612607803 | ||
|
|
a39e2a1145 | ||
|
|
79218cf13b | ||
|
|
f8a6a099c7 | ||
|
|
9467b11897 | ||
|
|
bcaeab1ddc | ||
|
|
f8db991727 | ||
|
|
ad6ca1c6ea | ||
|
|
d7cb139235 | ||
|
|
4db00897c6 | ||
|
|
e408a047ed | ||
|
|
877eb4172a | ||
|
|
faedd0fd65 | ||
|
|
132344df1c | ||
|
|
657416fe30 | ||
|
|
577c737d68 | ||
|
|
c721fa795d | ||
|
|
8b4b0f2f01 | ||
|
|
eb011e6729 | ||
|
|
463ff4c6ae | ||
|
|
6f6b6312a5 | ||
|
|
2d91bd09d4 | ||
|
|
f4e098f7f5 | ||
|
|
1a0fd5f993 | ||
|
|
20e0cb43b9 | ||
|
|
2db5677281 | ||
|
|
ff5a968de0 | ||
|
|
1650237c8f | ||
|
|
e0d776e551 | ||
|
|
403a7f5d46 | ||
|
|
af628be648 | ||
|
|
3c75d761a2 | ||
|
|
b47ec7f2dc | ||
|
|
3234bb5a8c | ||
|
|
bb9df1684b | ||
|
|
0d44c0df1d | ||
|
|
c5cdebb42f | ||
|
|
2318a83881 | ||
|
|
be67067fa2 | ||
|
|
9c13504b06 | ||
|
|
ac223a29ef | ||
|
|
afcdb7595c | ||
|
|
f71e622f71 | ||
|
|
4f49a56e4d | ||
|
|
944ed48a61 | ||
|
|
e503d44c54 | ||
|
|
908072710a | ||
|
|
e43a557a0b | ||
|
|
7d4778ffd6 | ||
|
|
80383d237a | ||
|
|
1095d0698a | ||
|
|
c2fc2d330b | ||
|
|
f16f0ed93e | ||
|
|
9345416361 | ||
|
|
2268e46b91 | ||
|
|
ceb88a170e | ||
|
|
d8c5ed55c2 | ||
|
|
6169af70db | ||
|
|
e11b7e3770 | ||
|
|
670a0c1442 | ||
|
|
d5d8b7be8d | ||
|
|
721a08462c | ||
|
|
f0cff15d56 | ||
|
|
a7be21733f | ||
|
|
66decbb180 | ||
|
|
4ef626dab3 | ||
|
|
e3af4f9a78 | ||
|
|
85b4e1f24f | ||
|
|
140a55810d | ||
|
|
54c308e5b5 | ||
|
|
26931475ae | ||
|
|
80c85aef99 | ||
|
|
76216335b1 | ||
|
|
f7e478c755 | ||
|
|
9f39e45b28 | ||
|
|
4e3132e8b2 | ||
|
|
6b777653a5 | ||
|
|
2bfb5fc138 | ||
|
|
53c6493b9a | ||
|
|
a531f32b69 | ||
|
|
215465e833 | ||
|
|
4d93f4adb1 | ||
|
|
bedb4da869 | ||
|
|
6804e2ba59 | ||
|
|
99fbcbee4c | ||
|
|
f4940e26c6 | ||
|
|
17916dbd24 | ||
|
|
50a4d61ded | ||
|
|
463608b289 | ||
|
|
5e2eb7bbbb | ||
|
|
b4bfc03e8b | ||
|
|
7532a41043 | ||
|
|
7a0b2a316a | ||
|
|
1d67fc6646 | ||
|
|
5afe6600ee | ||
|
|
f828ea8929 | ||
|
|
79e299123c | ||
|
|
a4978946d5 | ||
|
|
2b6d8b8a2d | ||
|
|
cbb453a0ed | ||
|
|
1b2654bfa6 | ||
|
|
4081c51573 | ||
|
|
3cbc63bb9f | ||
|
|
2863a691ef | ||
|
|
60fd7f0ae6 | ||
|
|
7166d68736 | ||
|
|
e42a2404a9 | ||
|
|
1490193402 | ||
|
|
33cf7066b4 | ||
|
|
2256850cb6 | ||
|
|
60a7628dbb | ||
|
|
3d84635b24 | ||
|
|
321bbf1fa8 | ||
|
|
dba54332fb | ||
|
|
04acd27dab | ||
|
|
8b8ea2d270 | ||
|
|
44cdf3f697 | ||
|
|
263dc5af67 | ||
|
|
a5938a7de9 | ||
|
|
6a19f2dae8 | ||
|
|
694840937b | ||
|
|
2aeb829176 | ||
|
|
0ca3873f1c | ||
|
|
a748f49362 | ||
|
|
932cd9e262 | ||
|
|
10ae808da9 | ||
|
|
d151e97e3a | ||
|
|
41c9ca4f8a | ||
|
|
35d62cc5f2 | ||
|
|
9aac8e389f | ||
|
|
9e85297f7a | ||
|
|
4850434c67 | ||
|
|
a9d7af9096 | ||
|
|
db4a7ce239 | ||
|
|
35a8ab2393 | ||
|
|
6883c19579 | ||
|
|
55946c8923 | ||
|
|
1ac9757a86 | ||
|
|
03a27d5b5a | ||
|
|
8d4f508cc6 | ||
|
|
30f73a0696 | ||
|
|
fea4c4eba8 | ||
|
|
b54376062e | ||
|
|
9001d9eb75 | ||
|
|
cc66350286 | ||
|
|
9400879242 | ||
|
|
b6f48f6fe0 | ||
|
|
869f654a10 | ||
|
|
0ff98c5876 | ||
|
|
fe14e48d72 | ||
|
|
52c94eb568 | ||
|
|
b075711be0 | ||
|
|
749975e8ef | ||
|
|
ab725eeb18 | ||
|
|
5d56ac7cf2 | ||
|
|
5f7513d61c | ||
|
|
0b6373e4df | ||
|
|
8dd640819d | ||
|
|
47dbb09115 | ||
|
|
87f34f07d7 | ||
|
|
2e4fcd6aa3 | ||
|
|
09a10a916b | ||
|
|
0e41ceffc4 | ||
|
|
01e98a6ce8 | ||
|
|
4053229544 | ||
|
|
07645984b5 | ||
|
|
c81689d393 | ||
|
|
e96d0b6caf | ||
|
|
ee7016fa54 | ||
|
|
e1dda6c005 | ||
|
|
ddf0c28b1b | ||
|
|
a4e6530513 | ||
|
|
df88ccd419 | ||
|
|
b16085f66c | ||
|
|
fbe4043651 | ||
|
|
58bd6d929f | ||
|
|
7a7c3015f4 | ||
|
|
1261908ef7 | ||
|
|
ef1dc2afac | ||
|
|
05f4214cc5 | ||
|
|
04aa0155bf | ||
|
|
093d09efe3 | ||
|
|
d2e662ddbb | ||
|
|
6ec1cf6e49 | ||
|
|
8c8e4329d7 | ||
|
|
8bad255a91 | ||
|
|
ab648e58ce | ||
|
|
c86b8b0f70 | ||
|
|
179abfa03e | ||
|
|
9ad029e028 | ||
|
|
abbac5c26c | ||
|
|
609eaa67ab | ||
|
|
ad16d61208 | ||
|
|
6aecbfc38f | ||
|
|
ee48766996 | ||
|
|
93876e27f8 | ||
|
|
bf810053b7 | ||
|
|
907c2fd19c | ||
|
|
42b06674bb | ||
|
|
71fd02e50e | ||
|
|
8fbc665acb | ||
|
|
f3baa420b2 | ||
|
|
8778313170 | ||
|
|
c41c39b5d8 | ||
|
|
fa164aa081 | ||
|
|
f7f7bc6865 | ||
|
|
c28d9161cf | ||
|
|
748a644e2c | ||
|
|
38d92bbcf1 | ||
|
|
42a6c670c7 | ||
|
|
983c943eef | ||
|
|
57692d6ce6 | ||
|
|
24b47e4ec6 | ||
|
|
5981e7815a | ||
|
|
5538c1d0af | ||
|
|
d4ea9c2717 | ||
|
|
ce6a36c8b5 | ||
|
|
6768501db1 | ||
|
|
43cc04433d | ||
|
|
5f37e339fb | ||
|
|
f42514a1a3 | ||
|
|
6cf7c0a647 | ||
|
|
a7e0c9db96 | ||
|
|
0880d4d37b | ||
|
|
b54e093313 | ||
|
|
68b5fdf748 | ||
|
|
d3dc00dcab | ||
|
|
7f4665e3cc | ||
|
|
aaab95ba55 | ||
|
|
6a5db12c6a | ||
|
|
dcbd6cc1af | ||
|
|
a65f20a789 | ||
|
|
86e3518430 | ||
|
|
89f1b60538 | ||
|
|
dc84a61999 | ||
|
|
a902f29ccf | ||
|
|
24dc154856 | ||
|
|
68446c2a54 | ||
|
|
f946964490 | ||
|
|
53ddba8077 | ||
|
|
54ad91c3b7 | ||
|
|
89ce80b279 | ||
|
|
98a82cd484 | ||
|
|
a97387b692 | ||
|
|
9a4a92de4f | ||
|
|
3433c102b7 | ||
|
|
f95856d062 |
891 changed files with 22456 additions and 14731 deletions
20
.clang-tidy
20
.clang-tidy
|
|
@ -1,5 +1,23 @@
|
||||||
Checks:
|
Checks:
|
||||||
- modernize-use-using
|
- modernize-use-using
|
||||||
- readability-avoid-const-params-in-decls
|
- readability-avoid-const-params-in-decls
|
||||||
|
- misc-unused-parameters,
|
||||||
|
- readability-identifier-naming
|
||||||
|
|
||||||
SystemHeaders: false
|
# ^ Without unused-parameters the readability-identifier-naming check doesn't cause any warnings.
|
||||||
|
|
||||||
|
CheckOptions:
|
||||||
|
- { key: readability-identifier-naming.ClassCase, value: PascalCase }
|
||||||
|
- { key: readability-identifier-naming.EnumCase, value: PascalCase }
|
||||||
|
- { key: readability-identifier-naming.FunctionCase, value: camelCase }
|
||||||
|
- { key: readability-identifier-naming.GlobalVariableCase, value: camelCase }
|
||||||
|
- { key: readability-identifier-naming.GlobalFunctionCase, value: camelCase }
|
||||||
|
- { key: readability-identifier-naming.GlobalConstantCase, value: SCREAMING_SNAKE_CASE }
|
||||||
|
- { key: readability-identifier-naming.MacroDefinitionCase, value: SCREAMING_SNAKE_CASE }
|
||||||
|
- { key: readability-identifier-naming.ClassMemberCase, value: camelCase }
|
||||||
|
- { key: readability-identifier-naming.PrivateMemberPrefix, value: m_ }
|
||||||
|
- { key: readability-identifier-naming.ProtectedMemberPrefix, value: m_ }
|
||||||
|
- { key: readability-identifier-naming.PrivateStaticMemberPrefix, value: s_ }
|
||||||
|
- { key: readability-identifier-naming.ProtectedStaticMemberPrefix, value: s_ }
|
||||||
|
- { key: readability-identifier-naming.PublicStaticConstantCase, value: SCREAMING_SNAKE_CASE }
|
||||||
|
- { key: readability-identifier-naming.EnumConstantCase, value: SCREAMING_SNAKE_CASE }
|
||||||
|
|
@ -2,3 +2,6 @@
|
||||||
|
|
||||||
# tabs -> spaces
|
# tabs -> spaces
|
||||||
bbb3b3e6f6e3c0f95873f22e6d0a4aaf350f49d9
|
bbb3b3e6f6e3c0f95873f22e6d0a4aaf350f49d9
|
||||||
|
|
||||||
|
# (nix) alejandra -> nixfmt
|
||||||
|
4c81d8c53d09196426568c4a31a4e752ed05397a
|
||||||
|
|
|
||||||
41
.github/scripts/prepare_JREs.sh
vendored
41
.github/scripts/prepare_JREs.sh
vendored
|
|
@ -1,41 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
URL_JDK8="https://api.adoptium.net/v3/binary/version/jdk8u312-b07/linux/x64/jre/hotspot/normal/eclipse"
|
|
||||||
URL_JDK17="https://api.adoptium.net/v3/binary/latest/17/ga/linux/x64/jre/hotspot/normal/eclipse"
|
|
||||||
|
|
||||||
mkdir -p JREs
|
|
||||||
pushd JREs
|
|
||||||
|
|
||||||
wget --content-disposition "$URL_JDK8"
|
|
||||||
wget --content-disposition "$URL_JDK17"
|
|
||||||
|
|
||||||
for file in *;
|
|
||||||
do
|
|
||||||
mkdir temp
|
|
||||||
|
|
||||||
re='(OpenJDK([[:digit:]]+)U-jre_x64_linux_hotspot_([[:digit:]]+)(.*).tar.gz)'
|
|
||||||
if [[ $file =~ $re ]];
|
|
||||||
then
|
|
||||||
version_major=${BASH_REMATCH[2]}
|
|
||||||
version_trailing=${BASH_REMATCH[4]}
|
|
||||||
|
|
||||||
if [ $version_major = 17 ];
|
|
||||||
then
|
|
||||||
hyphen='-'
|
|
||||||
else
|
|
||||||
hyphen=''
|
|
||||||
fi
|
|
||||||
|
|
||||||
version_edit=$(echo $version_trailing | sed -e 's/_/+/g' | sed -e 's/b/-b/g')
|
|
||||||
dir_name=jdk$hyphen$version_major$version_edit-jre
|
|
||||||
mkdir jre$version_major
|
|
||||||
tar -xzf $file -C temp
|
|
||||||
pushd temp/$dir_name
|
|
||||||
cp -r . ../../jre$version_major
|
|
||||||
popd
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -rf temp
|
|
||||||
done
|
|
||||||
|
|
||||||
popd
|
|
||||||
3
.github/workflows/backport.yml
vendored
3
.github/workflows/backport.yml
vendored
|
|
@ -16,6 +16,7 @@ jobs:
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # for korthout/backport-action to create branch
|
contents: write # for korthout/backport-action to create branch
|
||||||
pull-requests: write # for korthout/backport-action to create PR to backport
|
pull-requests: write # for korthout/backport-action to create PR to backport
|
||||||
|
actions: write # for korthout/backport-action to create PR with workflow changes
|
||||||
name: Backport Pull Request
|
name: Backport Pull Request
|
||||||
if: github.repository_owner == 'PrismLauncher' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
|
if: github.repository_owner == 'PrismLauncher' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
@ -24,7 +25,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
- name: Create backport PRs
|
- name: Create backport PRs
|
||||||
uses: korthout/backport-action@v2.1.1
|
uses: korthout/backport-action@v3.1.0
|
||||||
with:
|
with:
|
||||||
# Config README: https://github.com/korthout/backport-action#backport-action
|
# Config README: https://github.com/korthout/backport-action#backport-action
|
||||||
pull_description: |-
|
pull_description: |-
|
||||||
|
|
|
||||||
278
.github/workflows/build.yml
vendored
278
.github/workflows/build.yml
vendored
|
|
@ -21,8 +21,23 @@ on:
|
||||||
WINDOWS_CODESIGN_PASSWORD:
|
WINDOWS_CODESIGN_PASSWORD:
|
||||||
description: Password for signing Windows builds
|
description: Password for signing Windows builds
|
||||||
required: false
|
required: false
|
||||||
CACHIX_AUTH_TOKEN:
|
APPLE_CODESIGN_CERT:
|
||||||
description: Private token for authenticating against Cachix cache
|
description: Certificate for signing macOS builds
|
||||||
|
required: false
|
||||||
|
APPLE_CODESIGN_PASSWORD:
|
||||||
|
description: Password for signing macOS builds
|
||||||
|
required: false
|
||||||
|
APPLE_CODESIGN_ID:
|
||||||
|
description: Certificate ID for signing macOS builds
|
||||||
|
required: false
|
||||||
|
APPLE_NOTARIZE_APPLE_ID:
|
||||||
|
description: Apple ID used for notarizing macOS builds
|
||||||
|
required: false
|
||||||
|
APPLE_NOTARIZE_TEAM_ID:
|
||||||
|
description: Team ID used for notarizing macOS builds
|
||||||
|
required: false
|
||||||
|
APPLE_NOTARIZE_PASSWORD:
|
||||||
|
description: Password used for notarizing macOS builds
|
||||||
required: false
|
required: false
|
||||||
GPG_PRIVATE_KEY:
|
GPG_PRIVATE_KEY:
|
||||||
description: Private key for AppImage signing
|
description: Private key for AppImage signing
|
||||||
|
|
@ -39,14 +54,17 @@ jobs:
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
qt_ver: 5
|
qt_ver: 5
|
||||||
|
qt_host: linux
|
||||||
|
qt_arch: ""
|
||||||
|
qt_version: "5.15.2"
|
||||||
|
qt_modules: "qtnetworkauth"
|
||||||
|
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-22.04
|
||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: linux
|
qt_host: linux
|
||||||
qt_arch: ""
|
qt_arch: ""
|
||||||
qt_version: "6.2.4"
|
qt_version: "6.5.3"
|
||||||
qt_modules: "qt5compat qtimageformats"
|
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||||
qt_tools: ""
|
|
||||||
|
|
||||||
- os: windows-2022
|
- os: windows-2022
|
||||||
name: "Windows-MinGW-w64"
|
name: "Windows-MinGW-w64"
|
||||||
|
|
@ -59,11 +77,12 @@ jobs:
|
||||||
architecture: "x64"
|
architecture: "x64"
|
||||||
vcvars_arch: "amd64"
|
vcvars_arch: "amd64"
|
||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: windows
|
qt_host: "windows"
|
||||||
qt_arch: ''
|
qt_arch: "win64_msvc2022_64"
|
||||||
qt_version: '6.6.0'
|
qt_version: "6.8.1"
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||||
qt_tools: ''
|
nscurl_tag: "v24.9.26.122"
|
||||||
|
nscurl_sha256: "AEE6C4BE3CB6455858E9C1EE4B3AFE0DB9960FA03FE99CCDEDC28390D57CCBB0"
|
||||||
|
|
||||||
- os: windows-2022
|
- os: windows-2022
|
||||||
name: "Windows-MSVC-arm64"
|
name: "Windows-MSVC-arm64"
|
||||||
|
|
@ -71,30 +90,29 @@ jobs:
|
||||||
architecture: "arm64"
|
architecture: "arm64"
|
||||||
vcvars_arch: "amd64_arm64"
|
vcvars_arch: "amd64_arm64"
|
||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: windows
|
qt_host: "windows"
|
||||||
qt_arch: 'win64_msvc2019_arm64'
|
qt_arch: "win64_msvc2022_arm64_cross_compiled"
|
||||||
qt_version: '6.6.0'
|
qt_version: "6.8.1"
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||||
qt_tools: ''
|
nscurl_tag: "v24.9.26.122"
|
||||||
|
nscurl_sha256: "AEE6C4BE3CB6455858E9C1EE4B3AFE0DB9960FA03FE99CCDEDC28390D57CCBB0"
|
||||||
|
|
||||||
- os: macos-12
|
- os: macos-14
|
||||||
name: macOS
|
name: macOS
|
||||||
macosx_deployment_target: 11.0
|
macosx_deployment_target: 11.0
|
||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: mac
|
qt_host: mac
|
||||||
qt_arch: ''
|
qt_arch: ""
|
||||||
qt_version: '6.6.0'
|
qt_version: "6.8.1"
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||||
qt_tools: ''
|
|
||||||
|
|
||||||
- os: macos-12
|
- os: macos-14
|
||||||
name: macOS-Legacy
|
name: macOS-Legacy
|
||||||
macosx_deployment_target: 10.13
|
macosx_deployment_target: 10.13
|
||||||
qt_ver: 5
|
qt_ver: 5
|
||||||
qt_host: mac
|
qt_host: mac
|
||||||
qt_version: "5.15.2"
|
qt_version: "5.15.2"
|
||||||
qt_modules: ""
|
qt_modules: "qtnetworkauth"
|
||||||
qt_tools: ""
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
|
@ -136,6 +154,7 @@ jobs:
|
||||||
quazip-qt6:p
|
quazip-qt6:p
|
||||||
ccache:p
|
ccache:p
|
||||||
qt6-5compat:p
|
qt6-5compat:p
|
||||||
|
qt6-networkauth:p
|
||||||
cmark:p
|
cmark:p
|
||||||
|
|
||||||
- name: Force newer ccache
|
- name: Force newer ccache
|
||||||
|
|
@ -145,13 +164,13 @@ jobs:
|
||||||
|
|
||||||
- name: Setup ccache
|
- name: Setup ccache
|
||||||
if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug'
|
if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug'
|
||||||
uses: hendrikmuhs/ccache-action@v1.2.10
|
uses: hendrikmuhs/ccache-action@v1.2.17
|
||||||
with:
|
with:
|
||||||
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }}
|
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }}
|
||||||
|
|
||||||
- name: Retrieve ccache cache (Windows MinGW-w64)
|
- name: Retrieve ccache cache (Windows MinGW-w64)
|
||||||
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
||||||
uses: actions/cache@v3.3.2
|
uses: actions/cache@v4.2.1
|
||||||
with:
|
with:
|
||||||
path: '${{ github.workspace }}\.ccache'
|
path: '${{ github.workspace }}\.ccache'
|
||||||
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
|
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
|
||||||
|
|
@ -184,7 +203,7 @@ jobs:
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get -y update
|
sudo apt-get -y update
|
||||||
sudo apt-get -y install ninja-build extra-cmake-modules scdoc appstream
|
sudo apt-get -y install ninja-build extra-cmake-modules scdoc appstream libxcb-cursor-dev
|
||||||
|
|
||||||
- name: Install Dependencies (macOS)
|
- name: Install Dependencies (macOS)
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
|
|
@ -192,36 +211,29 @@ jobs:
|
||||||
brew update
|
brew update
|
||||||
brew install ninja extra-cmake-modules
|
brew install ninja extra-cmake-modules
|
||||||
|
|
||||||
- name: Install Qt (Linux)
|
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
|
||||||
run: |
|
|
||||||
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
|
|
||||||
|
|
||||||
- name: Install host Qt (Windows MSVC arm64)
|
- name: Install host Qt (Windows MSVC arm64)
|
||||||
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v4
|
||||||
with:
|
with:
|
||||||
aqtversion: "==3.1.*"
|
aqtversion: "==3.1.*"
|
||||||
py7zrversion: ">=0.20.2"
|
py7zrversion: ">=0.20.2"
|
||||||
version: ${{ matrix.qt_version }}
|
version: ${{ matrix.qt_version }}
|
||||||
host: "windows"
|
host: "windows"
|
||||||
target: "desktop"
|
target: "desktop"
|
||||||
arch: ""
|
arch: ${{ matrix.qt_arch }}
|
||||||
modules: ${{ matrix.qt_modules }}
|
modules: ${{ matrix.qt_modules }}
|
||||||
tools: ${{ matrix.qt_tools }}
|
|
||||||
cache: ${{ inputs.is_qt_cached }}
|
cache: ${{ inputs.is_qt_cached }}
|
||||||
cache-key-prefix: host-qt-arm64-windows
|
cache-key-prefix: host-qt-arm64-windows
|
||||||
dir: ${{ github.workspace }}\HostQt
|
dir: ${{ github.workspace }}\HostQt
|
||||||
set-env: false
|
set-env: false
|
||||||
|
|
||||||
- name: Install Qt (macOS, Linux, Qt 6 & Windows MSVC)
|
- name: Install Qt (macOS, Linux & Windows MSVC)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS' || (runner.os == 'Windows' && matrix.msystem == '')
|
if: matrix.msystem == ''
|
||||||
uses: jurplel/install-qt-action@v3
|
uses: jurplel/install-qt-action@v4
|
||||||
with:
|
with:
|
||||||
aqtversion: "==3.1.*"
|
aqtversion: "==3.1.*"
|
||||||
py7zrversion: ">=0.20.2"
|
py7zrversion: ">=0.20.2"
|
||||||
version: ${{ matrix.qt_version }}
|
version: ${{ matrix.qt_version }}
|
||||||
host: ${{ matrix.qt_host }}
|
|
||||||
target: "desktop"
|
target: "desktop"
|
||||||
arch: ${{ matrix.qt_arch }}
|
arch: ${{ matrix.qt_arch }}
|
||||||
modules: ${{ matrix.qt_modules }}
|
modules: ${{ matrix.qt_modules }}
|
||||||
|
|
@ -244,14 +256,19 @@ jobs:
|
||||||
|
|
||||||
wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage"
|
wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage"
|
||||||
|
|
||||||
${{ github.workspace }}/.github/scripts/prepare_JREs.sh
|
sudo apt install libopengl0 libfuse2
|
||||||
sudo apt install libopengl0
|
|
||||||
|
|
||||||
- name: Add QT_HOST_PATH var (Windows MSVC arm64)
|
- name: Add QT_HOST_PATH var (Windows MSVC arm64)
|
||||||
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||||
run: |
|
run: |
|
||||||
echo "QT_HOST_PATH=${{ github.workspace }}\HostQt\Qt\${{ matrix.qt_version }}\msvc2019_64" >> $env:GITHUB_ENV
|
echo "QT_HOST_PATH=${{ github.workspace }}\HostQt\Qt\${{ matrix.qt_version }}\msvc2022_64" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Setup java (macOS)
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: "temurin"
|
||||||
|
java-version: "17"
|
||||||
##
|
##
|
||||||
# CONFIGURE
|
# CONFIGURE
|
||||||
##
|
##
|
||||||
|
|
@ -259,23 +276,23 @@ jobs:
|
||||||
- name: Configure CMake (macOS)
|
- name: Configure CMake (macOS)
|
||||||
if: runner.os == 'macOS' && matrix.qt_ver == 6
|
if: runner.os == 'macOS' && matrix.qt_ver == 6
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_ENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja
|
||||||
|
|
||||||
- name: Configure CMake (macOS-Legacy)
|
- name: Configure CMake (macOS-Legacy)
|
||||||
if: runner.os == 'macOS' && matrix.qt_ver == 5
|
if: runner.os == 'macOS' && matrix.qt_ver == 5
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_ENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -DCMAKE_OSX_ARCHITECTURES="x86_64" -G Ninja
|
||||||
|
|
||||||
- name: Configure CMake (Windows MinGW-w64)
|
- name: Configure CMake (Windows MinGW-w64)
|
||||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} -G Ninja
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_ENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} -G Ninja
|
||||||
|
|
||||||
- name: Configure CMake (Windows MSVC)
|
- name: Configure CMake (Windows MSVC)
|
||||||
if: runner.os == 'Windows' && matrix.msystem == ''
|
if: runner.os == 'Windows' && matrix.msystem == ''
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }}
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_ENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }}
|
||||||
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
|
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
|
||||||
if ("${{ env.CCACHE_VAR }}")
|
if ("${{ env.CCACHE_VAR }}")
|
||||||
{
|
{
|
||||||
|
|
@ -290,7 +307,7 @@ jobs:
|
||||||
- name: Configure CMake (Linux)
|
- name: Configure CMake (Linux)
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -G Ninja
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_ENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -G Ninja
|
||||||
|
|
||||||
##
|
##
|
||||||
# BUILD
|
# BUILD
|
||||||
|
|
@ -336,6 +353,20 @@ jobs:
|
||||||
# PACKAGE BUILDS
|
# PACKAGE BUILDS
|
||||||
##
|
##
|
||||||
|
|
||||||
|
- name: Fetch codesign certificate (macOS)
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
run: |
|
||||||
|
echo '${{ secrets.APPLE_CODESIGN_CERT }}' | base64 --decode > codesign.p12
|
||||||
|
if [ -n '${{ secrets.APPLE_CODESIGN_ID }}' ]; then
|
||||||
|
security create-keychain -p '${{ secrets.APPLE_CODESIGN_PASSWORD }}' build.keychain
|
||||||
|
security default-keychain -s build.keychain
|
||||||
|
security unlock-keychain -p '${{ secrets.APPLE_CODESIGN_PASSWORD }}' build.keychain
|
||||||
|
security import codesign.p12 -k build.keychain -P '${{ secrets.APPLE_CODESIGN_PASSWORD }}' -T /usr/bin/codesign
|
||||||
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k '${{ secrets.APPLE_CODESIGN_PASSWORD }}' build.keychain
|
||||||
|
else
|
||||||
|
echo ":warning: Using ad-hoc code signing for macOS, as certificate was not present." >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Package (macOS)
|
- name: Package (macOS)
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -343,17 +374,43 @@ jobs:
|
||||||
|
|
||||||
cd ${{ env.INSTALL_DIR }}
|
cd ${{ env.INSTALL_DIR }}
|
||||||
chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
||||||
sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
|
||||||
|
if [ -n '${{ secrets.APPLE_CODESIGN_ID }}' ]; then
|
||||||
|
APPLE_CODESIGN_ID='${{ secrets.APPLE_CODESIGN_ID }}'
|
||||||
|
ENTITLEMENTS_FILE='../program_info/App.entitlements'
|
||||||
|
else
|
||||||
|
APPLE_CODESIGN_ID='-'
|
||||||
|
ENTITLEMENTS_FILE='../program_info/AdhocSignedApp.entitlements'
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo codesign --sign "$APPLE_CODESIGN_ID" --deep --force --entitlements "$ENTITLEMENTS_FILE" --options runtime "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
||||||
mv "PrismLauncher.app" "Prism Launcher.app"
|
mv "PrismLauncher.app" "Prism Launcher.app"
|
||||||
tar -czf ../PrismLauncher.tar.gz *
|
|
||||||
|
- name: Notarize (macOS)
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
run: |
|
||||||
|
cd ${{ env.INSTALL_DIR }}
|
||||||
|
|
||||||
|
if [ -n '${{ secrets.APPLE_NOTARIZE_PASSWORD }}' ]; then
|
||||||
|
ditto -c -k --sequesterRsrc --keepParent "Prism Launcher.app" ../PrismLauncher.zip
|
||||||
|
xcrun notarytool submit ../PrismLauncher.zip \
|
||||||
|
--wait --progress \
|
||||||
|
--apple-id '${{ secrets.APPLE_NOTARIZE_APPLE_ID }}' \
|
||||||
|
--team-id '${{ secrets.APPLE_NOTARIZE_TEAM_ID }}' \
|
||||||
|
--password '${{ secrets.APPLE_NOTARIZE_PASSWORD }}'
|
||||||
|
|
||||||
|
xcrun stapler staple "Prism Launcher.app"
|
||||||
|
else
|
||||||
|
echo ":warning: Skipping notarization as credentials are not present." >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
ditto -c -k --sequesterRsrc --keepParent "Prism Launcher.app" ../PrismLauncher.zip
|
||||||
|
|
||||||
- name: Make Sparkle signature (macOS)
|
- name: Make Sparkle signature (macOS)
|
||||||
if: matrix.name == 'macOS'
|
if: matrix.name == 'macOS'
|
||||||
run: |
|
run: |
|
||||||
if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then
|
if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then
|
||||||
brew install openssl@3
|
|
||||||
echo '${{ secrets.SPARKLE_ED25519_KEY }}' > ed25519-priv.pem
|
echo '${{ secrets.SPARKLE_ED25519_KEY }}' > ed25519-priv.pem
|
||||||
signature=$(/usr/local/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.tar.gz -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
|
signature=$(/opt/homebrew/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.zip -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
|
||||||
rm ed25519-priv.pem
|
rm ed25519-priv.pem
|
||||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||||
### Artifact Information :information_source:
|
### Artifact Information :information_source:
|
||||||
|
|
@ -379,12 +436,6 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }}
|
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }}
|
||||||
|
|
||||||
cd ${{ env.INSTALL_DIR }}
|
|
||||||
if ("${{ matrix.qt_ver }}" -eq "5")
|
|
||||||
{
|
|
||||||
Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll
|
|
||||||
Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll
|
|
||||||
}
|
|
||||||
cd ${{ github.workspace }}
|
cd ${{ github.workspace }}
|
||||||
|
|
||||||
Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
|
Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
|
||||||
|
|
@ -425,6 +476,16 @@ jobs:
|
||||||
- name: Package (Windows, installer)
|
- name: Package (Windows, installer)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
run: |
|
run: |
|
||||||
|
if ('${{ matrix.nscurl_tag }}') {
|
||||||
|
New-Item -Name NSISPlugins -ItemType Directory
|
||||||
|
Invoke-Webrequest https://github.com/negrutiu/nsis-nscurl/releases/download/${{ matrix.nscurl_tag }}/NScurl.zip -OutFile NSISPlugins\NScurl.zip
|
||||||
|
$nscurl_hash = Get-FileHash NSISPlugins\NScurl.zip -Algorithm Sha256 | Select-Object -ExpandProperty Hash
|
||||||
|
if ( $nscurl_hash -ne "${{ matrix.nscurl_sha256 }}") {
|
||||||
|
echo "::error:: NSCurl.zip sha256 mismatch"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
Expand-Archive -Path NSISPlugins\NScurl.zip -DestinationPath NSISPlugins\NScurl
|
||||||
|
}
|
||||||
cd ${{ env.INSTALL_DIR }}
|
cd ${{ env.INSTALL_DIR }}
|
||||||
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
|
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
|
||||||
|
|
||||||
|
|
@ -437,26 +498,6 @@ jobs:
|
||||||
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||||
}
|
}
|
||||||
|
|
||||||
- name: Package (Linux)
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
run: |
|
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }}
|
|
||||||
for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_DIR }}/manifest.txt
|
|
||||||
|
|
||||||
cd ${{ env.INSTALL_DIR }}
|
|
||||||
tar --owner root --group root -czf ../PrismLauncher.tar.gz *
|
|
||||||
|
|
||||||
- name: Package (Linux, portable)
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
run: |
|
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
|
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
|
||||||
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
|
|
||||||
|
|
||||||
|
|
||||||
cd ${{ env.INSTALL_PORTABLE_DIR }}
|
|
||||||
tar -czf ../PrismLauncher-portable.tar.gz *
|
|
||||||
|
|
||||||
- name: Package AppImage (Linux)
|
- name: Package AppImage (Linux)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
@ -472,24 +513,16 @@ jobs:
|
||||||
|
|
||||||
chmod +x linuxdeploy-*.AppImage
|
chmod +x linuxdeploy-*.AppImage
|
||||||
|
|
||||||
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-{8,17}-openjdk
|
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib
|
||||||
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
||||||
|
|
||||||
cp -r ${{ github.workspace }}/JREs/jre8/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk
|
|
||||||
|
|
||||||
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk
|
|
||||||
|
|
||||||
cp -r ${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
cp -r ${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
||||||
|
|
||||||
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||||
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
cp /usr/lib/x86_64-linux-gnu/libssl.so.* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||||
cp /usr/lib/x86_64-linux-gnu/libOpenGL.so.0* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
cp /usr/lib/x86_64-linux-gnu/libOpenGL.so.0* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||||
|
|
||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
|
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
|
||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server"
|
|
||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64"
|
|
||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib/server"
|
|
||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
|
|
||||||
export LD_LIBRARY_PATH
|
export LD_LIBRARY_PATH
|
||||||
|
|
||||||
chmod +x AppImageUpdate-x86_64.AppImage
|
chmod +x AppImageUpdate-x86_64.AppImage
|
||||||
|
|
@ -511,76 +544,81 @@ jobs:
|
||||||
|
|
||||||
mv "PrismLauncher-Linux-x86_64.AppImage" "PrismLauncher-Linux-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
|
mv "PrismLauncher-Linux-x86_64.AppImage" "PrismLauncher-Linux-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
|
||||||
|
|
||||||
|
- name: Package (Linux, portable)
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
run: |
|
||||||
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_PORTABLE_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -DINSTALL_BUNDLE=full -G Ninja
|
||||||
|
cmake --install ${{ env.BUILD_DIR }}
|
||||||
|
cmake --install ${{ env.BUILD_DIR }} --component portable
|
||||||
|
|
||||||
|
mkdir ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||||
|
cp /lib/x86_64-linux-gnu/libbz2.so.1.0 ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||||
|
cp /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0 ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||||
|
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.* ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||||
|
cp /usr/lib/x86_64-linux-gnu/libssl.so.* ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||||
|
cp /usr/lib/x86_64-linux-gnu/libffi.so.*.* ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||||
|
mv ${{ env.INSTALL_PORTABLE_DIR }}/bin/*.so* ${{ env.INSTALL_PORTABLE_DIR }}/lib
|
||||||
|
|
||||||
|
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
|
||||||
|
cd ${{ env.INSTALL_PORTABLE_DIR }}
|
||||||
|
tar -czf ../PrismLauncher-portable.tar.gz *
|
||||||
|
|
||||||
##
|
##
|
||||||
# UPLOAD BUILDS
|
# UPLOAD BUILDS
|
||||||
##
|
##
|
||||||
|
|
||||||
- name: Upload binary tarball (macOS)
|
- name: Upload binary tarball (macOS)
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PrismLauncher.tar.gz
|
path: PrismLauncher.zip
|
||||||
|
|
||||||
- name: Upload binary zip (Windows)
|
- name: Upload binary zip (Windows)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: ${{ env.INSTALL_DIR }}/**
|
path: ${{ env.INSTALL_DIR }}/**
|
||||||
|
|
||||||
- name: Upload binary zip (Windows, portable)
|
- name: Upload binary zip (Windows, portable)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: ${{ env.INSTALL_PORTABLE_DIR }}/**
|
path: ${{ env.INSTALL_PORTABLE_DIR }}/**
|
||||||
|
|
||||||
- name: Upload installer (Windows)
|
- name: Upload installer (Windows)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PrismLauncher-Setup.exe
|
path: PrismLauncher-Setup.exe
|
||||||
|
|
||||||
- name: Upload binary tarball (Linux, Qt 5)
|
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: PrismLauncher-${{ runner.os }}-Qt5-${{ env.VERSION }}-${{ inputs.build_type }}
|
|
||||||
path: PrismLauncher.tar.gz
|
|
||||||
|
|
||||||
- name: Upload binary tarball (Linux, portable, Qt 5)
|
- name: Upload binary tarball (Linux, portable, Qt 5)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ runner.os }}-Qt5-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ runner.os }}-Qt5-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PrismLauncher-portable.tar.gz
|
path: PrismLauncher-portable.tar.gz
|
||||||
|
|
||||||
- name: Upload binary tarball (Linux, Qt 6)
|
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver !=5
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: PrismLauncher-${{ runner.os }}-Qt6-${{ env.VERSION }}-${{ inputs.build_type }}
|
|
||||||
path: PrismLauncher.tar.gz
|
|
||||||
|
|
||||||
- name: Upload binary tarball (Linux, portable, Qt 6)
|
- name: Upload binary tarball (Linux, portable, Qt 6)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PrismLauncher-portable.tar.gz
|
path: PrismLauncher-portable.tar.gz
|
||||||
|
|
||||||
- name: Upload AppImage (Linux)
|
- name: Upload AppImage (Linux)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||||
path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||||
|
|
||||||
- name: Upload AppImage Zsync (Linux)
|
- name: Upload AppImage Zsync (Linux)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync
|
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync
|
||||||
path: PrismLauncher-Linux-x86_64.AppImage.zsync
|
path: PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||||
|
|
@ -590,21 +628,3 @@ jobs:
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
ccache -s
|
ccache -s
|
||||||
|
|
||||||
flatpak:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container:
|
|
||||||
image: bilelmoussaoui/flatpak-github-actions:kde-5.15-23.08
|
|
||||||
options: --privileged
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
if: inputs.build_type == 'Debug'
|
|
||||||
with:
|
|
||||||
submodules: "true"
|
|
||||||
- name: Build Flatpak (Linux)
|
|
||||||
if: inputs.build_type == 'Debug'
|
|
||||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
|
||||||
with:
|
|
||||||
bundle: "Prism Launcher.flatpak"
|
|
||||||
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
|
|
||||||
|
|
|
||||||
6
.github/workflows/codeql.yml
vendored
6
.github/workflows/codeql.yml
vendored
|
|
@ -13,7 +13,7 @@ jobs:
|
||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v3
|
||||||
with:
|
with:
|
||||||
config-file: ./.github/codeql/codeql-config.yml
|
config-file: ./.github/codeql/codeql-config.yml
|
||||||
queries: security-and-quality
|
queries: security-and-quality
|
||||||
|
|
@ -23,7 +23,7 @@ jobs:
|
||||||
run:
|
run:
|
||||||
sudo apt-get -y update
|
sudo apt-get -y update
|
||||||
|
|
||||||
sudo apt-get -y install ninja-build extra-cmake-modules scdoc qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
|
sudo apt-get -y install ninja-build extra-cmake-modules scdoc qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 libqt5networkauth5 libqt5networkauth5-dev
|
||||||
|
|
||||||
- name: Configure and Build
|
- name: Configure and Build
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -32,4 +32,4 @@ jobs:
|
||||||
cmake --build build
|
cmake --build build
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v3
|
||||||
|
|
|
||||||
62
.github/workflows/flatpak.yml
vendored
Normal file
62
.github/workflows/flatpak.yml
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
name: Flatpak
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
- "**/LICENSE"
|
||||||
|
- ".github/ISSUE_TEMPLATE/**"
|
||||||
|
- ".markdownlint**"
|
||||||
|
- "nix/**"
|
||||||
|
# We don't do anything with these artifacts on releases. They go to Flathub
|
||||||
|
tags-ignore:
|
||||||
|
- "*"
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
- "**/LICENSE"
|
||||||
|
- ".github/ISSUE_TEMPLATE/**"
|
||||||
|
- ".markdownlint**"
|
||||||
|
- "nix/**"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build (${{ matrix.arch }})
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
arch: x86_64
|
||||||
|
|
||||||
|
- os: ubuntu-22.04-arm
|
||||||
|
arch: aarch64
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
container:
|
||||||
|
image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.8
|
||||||
|
options: --privileged
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Set short version
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "VERSION=${GITHUB_SHA::7}" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
- name: Build Flatpak
|
||||||
|
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
||||||
|
with:
|
||||||
|
bundle: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-Flatpak.flatpak
|
||||||
|
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
|
||||||
|
arch: ${{ matrix.arch }}
|
||||||
90
.github/workflows/nix.yml
vendored
Normal file
90
.github/workflows/nix.yml
vendored
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
name: Nix
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
- "**/LICENSE"
|
||||||
|
- ".github/ISSUE_TEMPLATE/**"
|
||||||
|
- ".markdownlint**"
|
||||||
|
- "flatpak/**"
|
||||||
|
pull_request_target:
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
- "**/LICENSE"
|
||||||
|
- ".github/ISSUE_TEMPLATE/**"
|
||||||
|
- ".markdownlint**"
|
||||||
|
- "flatpak/**"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
env:
|
||||||
|
DEBUG: ${{ github.ref_type != 'tag' }}
|
||||||
|
USE_DETERMINATE: ${{ github.event_name == 'pull_request' }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build (${{ matrix.system }})
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
system: x86_64-linux
|
||||||
|
|
||||||
|
- os: ubuntu-22.04-arm
|
||||||
|
system: aarch64-linux
|
||||||
|
|
||||||
|
- os: macos-13
|
||||||
|
system: x86_64-darwin
|
||||||
|
|
||||||
|
- os: macos-14
|
||||||
|
system: aarch64-darwin
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Nix
|
||||||
|
uses: DeterminateSystems/nix-installer-action@v16
|
||||||
|
with:
|
||||||
|
determinate: ${{ env.USE_DETERMINATE }}
|
||||||
|
|
||||||
|
# For PRs
|
||||||
|
- name: Setup Nix Magic Cache
|
||||||
|
if: ${{ env.USE_DETERMINATE }}
|
||||||
|
uses: DeterminateSystems/flakehub-cache-action@v1
|
||||||
|
|
||||||
|
# For in-tree builds
|
||||||
|
- name: Setup Cachix
|
||||||
|
if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}
|
||||||
|
uses: cachix/cachix-action@v15
|
||||||
|
with:
|
||||||
|
name: prismlauncher
|
||||||
|
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||||
|
|
||||||
|
- name: Run Flake checks
|
||||||
|
run: |
|
||||||
|
nix flake check --print-build-logs --show-trace
|
||||||
|
|
||||||
|
- name: Build debug package
|
||||||
|
if: ${{ env.DEBUG }}
|
||||||
|
run: |
|
||||||
|
nix build \
|
||||||
|
--no-link --print-build-logs --print-out-paths \
|
||||||
|
.#prismlauncher-debug >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
|
||||||
|
- name: Build release package
|
||||||
|
if: ${{ !env.DEBUG }}
|
||||||
|
run: |
|
||||||
|
nix build \
|
||||||
|
--no-link --print-build-logs --print-out-paths \
|
||||||
|
.#prismlauncher >> "$GITHUB_STEP_SUMMARY"
|
||||||
45
.github/workflows/publish.yml
vendored
Normal file
45
.github/workflows/publish.yml
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
name: Publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [ released ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
flakehub:
|
||||||
|
name: FlakeHub
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.ref }}
|
||||||
|
|
||||||
|
- name: Install Nix
|
||||||
|
uses: cachix/install-nix-action@v30
|
||||||
|
|
||||||
|
- name: Publish on FlakeHub
|
||||||
|
uses: determinatesystems/flakehub-push@v5
|
||||||
|
with:
|
||||||
|
visibility: "public"
|
||||||
|
|
||||||
|
winget:
|
||||||
|
name: Winget
|
||||||
|
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Publish on Winget
|
||||||
|
uses: vedantmgoyal2009/winget-releaser@v2
|
||||||
|
with:
|
||||||
|
identifier: PrismLauncher.PrismLauncher
|
||||||
|
version: ${{ github.event.release.tag_name }}
|
||||||
|
installers-regex: 'PrismLauncher-Windows-MSVC(:?-arm64|-Legacy)?-Setup-.+\.exe$'
|
||||||
|
token: ${{ secrets.WINGET_TOKEN }}
|
||||||
29
.github/workflows/stale.yml
vendored
Normal file
29
.github/workflows/stale.yml
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
name: Stale
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# run weekly on sunday
|
||||||
|
- cron: "0 0 * * 0"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
label:
|
||||||
|
name: Label issues and PRs
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/stale@v9
|
||||||
|
with:
|
||||||
|
days-before-stale: 60
|
||||||
|
days-before-close: -1 # Don't close anything
|
||||||
|
exempt-issue-labels: rfc,nostale,help wanted
|
||||||
|
exempt-all-milestones: true
|
||||||
|
exempt-all-assignees: true
|
||||||
|
operations-per-run: 1000
|
||||||
|
stale-issue-label: inactive
|
||||||
|
stale-pr-label: inactive
|
||||||
7
.github/workflows/trigger_builds.yml
vendored
7
.github/workflows/trigger_builds.yml
vendored
|
|
@ -32,6 +32,11 @@ jobs:
|
||||||
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
||||||
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
||||||
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
APPLE_CODESIGN_CERT: ${{ secrets.APPLE_CODESIGN_CERT }}
|
||||||
|
APPLE_CODESIGN_PASSWORD: ${{ secrets.APPLE_CODESIGN_PASSWORD }}
|
||||||
|
APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }}
|
||||||
|
APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
|
||||||
|
APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
|
||||||
|
APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
|
||||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||||
|
|
|
||||||
23
.github/workflows/trigger_release.yml
vendored
23
.github/workflows/trigger_release.yml
vendored
|
|
@ -16,7 +16,12 @@ jobs:
|
||||||
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
||||||
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
||||||
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
APPLE_CODESIGN_CERT: ${{ secrets.APPLE_CODESIGN_CERT }}
|
||||||
|
APPLE_CODESIGN_PASSWORD: ${{ secrets.APPLE_CODESIGN_PASSWORD }}
|
||||||
|
APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }}
|
||||||
|
APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
|
||||||
|
APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
|
||||||
|
APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
|
||||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||||
|
|
||||||
|
|
@ -32,7 +37,7 @@ jobs:
|
||||||
submodules: "true"
|
submodules: "true"
|
||||||
path: "PrismLauncher-source"
|
path: "PrismLauncher-source"
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
- name: Grab and store version
|
- name: Grab and store version
|
||||||
run: |
|
run: |
|
||||||
tag_name=$(echo ${{ github.ref }} | grep -oE "[^/]+$")
|
tag_name=$(echo ${{ github.ref }} | grep -oE "[^/]+$")
|
||||||
|
|
@ -41,13 +46,11 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
|
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
|
||||||
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||||
mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
|
||||||
mv PrismLauncher-Linux-Qt5-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
|
mv PrismLauncher-Linux-Qt5-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
|
||||||
mv PrismLauncher-Linux-Qt5*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz
|
|
||||||
mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage
|
mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage
|
||||||
mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
|
mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||||
mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
mv PrismLauncher-macOS-Legacy*/PrismLauncher.zip PrismLauncher-macOS-Legacy-${{ env.VERSION }}.zip
|
||||||
mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
|
mv PrismLauncher-macOS*/PrismLauncher.zip PrismLauncher-macOS-${{ env.VERSION }}.zip
|
||||||
|
|
||||||
tar --exclude='.git' -czf PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }}
|
tar --exclude='.git' -czf PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }}
|
||||||
|
|
||||||
|
|
@ -79,7 +82,7 @@ jobs:
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
id: create_release
|
id: create_release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
tag_name: ${{ github.ref }}
|
tag_name: ${{ github.ref }}
|
||||||
|
|
@ -87,11 +90,9 @@ jobs:
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: false
|
prerelease: false
|
||||||
files: |
|
files: |
|
||||||
PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz
|
|
||||||
PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
|
PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
|
||||||
PrismLauncher-Linux-x86_64.AppImage
|
PrismLauncher-Linux-x86_64.AppImage
|
||||||
PrismLauncher-Linux-x86_64.AppImage.zsync
|
PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||||
PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
|
||||||
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||||
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip
|
||||||
|
|
@ -102,6 +103,6 @@ jobs:
|
||||||
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MSVC-Portable-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MSVC-Portable-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MSVC-Setup-${{ env.VERSION }}.exe
|
PrismLauncher-Windows-MSVC-Setup-${{ env.VERSION }}.exe
|
||||||
PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
|
PrismLauncher-macOS-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
PrismLauncher-macOS-Legacy-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-${{ env.VERSION }}.tar.gz
|
PrismLauncher-${{ env.VERSION }}.tar.gz
|
||||||
|
|
|
||||||
4
.github/workflows/update-flake.yml
vendored
4
.github/workflows/update-flake.yml
vendored
|
|
@ -17,9 +17,9 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: cachix/install-nix-action@7ac1ec25491415c381d9b62f0657c7a028df52a7 # v24
|
- uses: cachix/install-nix-action@08dcb3a5e62fa31e2da3d490afc4176ef55ecd72 # v30
|
||||||
|
|
||||||
- uses: DeterminateSystems/update-flake-lock@v20
|
- uses: DeterminateSystems/update-flake-lock@v24
|
||||||
with:
|
with:
|
||||||
commit-msg: "chore(nix): update lockfile"
|
commit-msg: "chore(nix): update lockfile"
|
||||||
pr-title: "chore(nix): update lockfile"
|
pr-title: "chore(nix): update lockfile"
|
||||||
|
|
|
||||||
15
.github/workflows/winget.yml
vendored
15
.github/workflows/winget.yml
vendored
|
|
@ -1,15 +0,0 @@
|
||||||
name: Publish to WinGet
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [released]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish:
|
|
||||||
runs-on: windows-latest
|
|
||||||
steps:
|
|
||||||
- uses: vedantmgoyal2009/winget-releaser@v2
|
|
||||||
with:
|
|
||||||
identifier: PrismLauncher.PrismLauncher
|
|
||||||
version: ${{ github.event.release.tag_name }}
|
|
||||||
installers-regex: 'PrismLauncher-Windows-MSVC(:?-arm64|-Legacy)?-Setup-.+\.exe$'
|
|
||||||
token: ${{ secrets.WINGET_TOKEN }}
|
|
||||||
3
BUILD.md
3
BUILD.md
|
|
@ -1,3 +0,0 @@
|
||||||
# Build Instructions
|
|
||||||
|
|
||||||
Full build instructions are available on [the website](https://prismlauncher.org/wiki/development/build-instructions/).
|
|
||||||
|
|
@ -78,6 +78,13 @@ else()
|
||||||
# ATL's pack list needs more than the default 1 Mib stack on windows
|
# ATL's pack list needs more than the default 1 Mib stack on windows
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,8388608 ${CMAKE_EXE_LINKER_FLAGS}")
|
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,8388608 ${CMAKE_EXE_LINKER_FLAGS}")
|
||||||
|
|
||||||
|
# -ffunction-sections and -fdata-sections help reduce binary size
|
||||||
|
# -mguard=cf enables Control Flow Guard
|
||||||
|
# TODO: Look into -gc-sections to further reduce binary size
|
||||||
|
foreach(lang C CXX)
|
||||||
|
set("CMAKE_${lang}_FLAGS_RELEASE" "-ffunction-sections -fdata-sections -mguard=cf")
|
||||||
|
endforeach()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
@ -99,21 +106,21 @@ if ((CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebI
|
||||||
message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off")
|
message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off")
|
||||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||||
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||||
# using clang with clang-cl front end
|
# using clang with clang-cl front end
|
||||||
message(STATUS "Address Sanitizer available on Clang MSVC frontend")
|
message(STATUS "Address Sanitizer available on Clang MSVC frontend")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
|
||||||
else()
|
else()
|
||||||
# AppleClang and Clang
|
# AppleClang and Clang
|
||||||
message(STATUS "Address Sanitizer available on Clang")
|
message(STATUS "Address Sanitizer available on Clang")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=null")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=null")
|
||||||
endif()
|
endif()
|
||||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||||
# GCC
|
# GCC
|
||||||
message(STATUS "Address Sanitizer available on GCC")
|
message(STATUS "Address Sanitizer available on GCC")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover")
|
||||||
link_libraries("asan")
|
link_libraries("asan")
|
||||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||||
message(STATUS "Address Sanitizer available on MSVC")
|
message(STATUS "Address Sanitizer available on MSVC")
|
||||||
|
|
@ -176,9 +183,11 @@ endif()
|
||||||
set(Launcher_NEWS_RSS_URL "https://prismlauncher.org/feed/feed.xml" CACHE STRING "URL to fetch Prism Launcher's news RSS feed from.")
|
set(Launcher_NEWS_RSS_URL "https://prismlauncher.org/feed/feed.xml" CACHE STRING "URL to fetch Prism Launcher's news RSS feed from.")
|
||||||
set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'")
|
set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'")
|
||||||
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
||||||
|
set(Launcher_LOGIN_CALLBACK_URL "https://prismlauncher.org/successful-login" CACHE STRING "URL that gets opened when the user successfully logins.")
|
||||||
|
set(Launcher_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" CACHE STRING "URL for FML Libraries.")
|
||||||
|
|
||||||
######## Set version numbers ########
|
######## Set version numbers ########
|
||||||
set(Launcher_VERSION_MAJOR 8)
|
set(Launcher_VERSION_MAJOR 10)
|
||||||
set(Launcher_VERSION_MINOR 0)
|
set(Launcher_VERSION_MINOR 0)
|
||||||
|
|
||||||
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
||||||
|
|
@ -205,6 +214,7 @@ set(Launcher_BUG_TRACKER_URL "https://github.com/PrismLauncher/PrismLauncher/iss
|
||||||
|
|
||||||
# Translations Platform URL
|
# Translations Platform URL
|
||||||
set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/prismlauncher/launcher/" CACHE STRING "URL for the translations platform.")
|
set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/prismlauncher/launcher/" CACHE STRING "URL for the translations platform.")
|
||||||
|
set(Launcher_TRANSLATION_FILES_URL "https://i18n.prismlauncher.org/" CACHE STRING "URL for the translations files.")
|
||||||
|
|
||||||
# Matrix Space
|
# Matrix Space
|
||||||
set(Launcher_MATRIX_URL "https://prismlauncher.org/matrix" CACHE STRING "URL to the Matrix Space")
|
set(Launcher_MATRIX_URL "https://prismlauncher.org/matrix" CACHE STRING "URL to the Matrix Space")
|
||||||
|
|
@ -219,6 +229,19 @@ set(Launcher_SUBREDDIT_URL "https://prismlauncher.org/reddit" CACHE STRING "URL
|
||||||
set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
|
set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
|
||||||
set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against")
|
set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against")
|
||||||
|
|
||||||
|
# Java downloader
|
||||||
|
set(Launcher_ENABLE_JAVA_DOWNLOADER_DEFAULT ON)
|
||||||
|
|
||||||
|
# Although we recommend enabling this, we cannot guarantee binary compatibility on
|
||||||
|
# differing Linux/BSD/etc distributions. Downstream packagers should be explicitly opt-ing into this
|
||||||
|
# feature if they know it will work with their distribution.
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
set(Launcher_ENABLE_JAVA_DOWNLOADER_DEFAULT OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Java downloader
|
||||||
|
option(Launcher_ENABLE_JAVA_DOWNLOADER "Build the java downloader feature" ${Launcher_ENABLE_JAVA_DOWNLOADER_DEFAULT})
|
||||||
|
|
||||||
# Native libraries
|
# Native libraries
|
||||||
if(UNIX AND APPLE)
|
if(UNIX AND APPLE)
|
||||||
set(Launcher_GLFW_LIBRARY_NAME "libglfw.dylib" CACHE STRING "Name of native glfw library")
|
set(Launcher_GLFW_LIBRARY_NAME "libglfw.dylib" CACHE STRING "Name of native glfw library")
|
||||||
|
|
@ -282,7 +305,9 @@ endif()
|
||||||
include(QtVersionlessBackport)
|
include(QtVersionlessBackport)
|
||||||
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
||||||
set(QT_VERSION_MAJOR 5)
|
set(QT_VERSION_MAJOR 5)
|
||||||
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml)
|
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml NetworkAuth)
|
||||||
|
find_package(Qt5 COMPONENTS DBus)
|
||||||
|
list(APPEND Launcher_QT_DBUS Qt5::DBus)
|
||||||
|
|
||||||
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
||||||
find_package(QuaZip-Qt5 1.3 QUIET)
|
find_package(QuaZip-Qt5 1.3 QUIET)
|
||||||
|
|
@ -296,7 +321,9 @@ if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUNICODE -D_UNICODE")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUNICODE -D_UNICODE")
|
||||||
elseif(Launcher_QT_VERSION_MAJOR EQUAL 6)
|
elseif(Launcher_QT_VERSION_MAJOR EQUAL 6)
|
||||||
set(QT_VERSION_MAJOR 6)
|
set(QT_VERSION_MAJOR 6)
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml Core5Compat)
|
find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml Core5Compat NetworkAuth)
|
||||||
|
find_package(Qt6 COMPONENTS DBus)
|
||||||
|
list(APPEND Launcher_QT_DBUS Qt6::DBus)
|
||||||
list(APPEND Launcher_QT_LIBS Qt6::Core5Compat)
|
list(APPEND Launcher_QT_LIBS Qt6::Core5Compat)
|
||||||
|
|
||||||
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
||||||
|
|
@ -377,12 +404,12 @@ if(UNIX AND APPLE)
|
||||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}")
|
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}")
|
||||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}")
|
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}")
|
||||||
set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns)
|
set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns)
|
||||||
set(MACOSX_BUNDLE_COPYRIGHT "© 2022-2023 ${Launcher_Copyright_Mac}")
|
set(MACOSX_BUNDLE_COPYRIGHT "${Launcher_Copyright_Mac}")
|
||||||
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=" CACHE STRING "Public key for Sparkle update feed")
|
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=" CACHE STRING "Public key for Sparkle update feed")
|
||||||
set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml" CACHE STRING "URL for Sparkle update feed")
|
set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml" CACHE STRING "URL for Sparkle update feed")
|
||||||
|
|
||||||
set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0/Sparkle-2.1.0.tar.xz" CACHE STRING "URL to Sparkle release archive")
|
set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.6.4/Sparkle-2.6.4.tar.xz" CACHE STRING "URL to Sparkle release archive")
|
||||||
set(MACOSX_SPARKLE_SHA256 "bf6ac1caa9f8d321d5784859c88da874f28412f37fb327bc21b7b14c5d61ef94" CACHE STRING "SHA256 checksum for Sparkle release archive")
|
set(MACOSX_SPARKLE_SHA256 "50612a06038abc931f16011d7903b8326a362c1074dabccb718404ce8e585f0b" CACHE STRING "SHA256 checksum for Sparkle release archive")
|
||||||
set(MACOSX_SPARKLE_DIR "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
|
set(MACOSX_SPARKLE_DIR "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
|
||||||
|
|
||||||
# directories to look for dependencies
|
# directories to look for dependencies
|
||||||
|
|
@ -417,7 +444,19 @@ elseif(UNIX)
|
||||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
|
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
|
||||||
|
|
||||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "share/${Launcher_Name}")
|
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "share/${Launcher_Name}")
|
||||||
|
|
||||||
|
if (INSTALL_BUNDLE STREQUAL full)
|
||||||
|
set(PLUGIN_DEST_DIR "plugins")
|
||||||
|
set(BUNDLE_DEST_DIR ".")
|
||||||
|
set(RESOURCES_DEST_DIR ".")
|
||||||
|
|
||||||
|
# Apps to bundle
|
||||||
|
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${Launcher_APP_BINARY_NAME}")
|
||||||
|
|
||||||
|
# directories to look for dependencies
|
||||||
|
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
||||||
|
endif()
|
||||||
|
|
||||||
if(Launcher_ManPage)
|
if(Launcher_ManPage)
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -467,7 +506,7 @@ if(FORCE_BUNDLED_ZLIB)
|
||||||
set(SKIP_INSTALL_ALL ON)
|
set(SKIP_INSTALL_ALL ON)
|
||||||
add_subdirectory(libraries/zlib EXCLUDE_FROM_ALL)
|
add_subdirectory(libraries/zlib EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
# On OS where unistd.h exists, zlib's generated header defines `Z_HAVE_UNISTD_H`, while the included header does not.
|
# On OS where unistd.h exists, zlib's generated header defines `Z_HAVE_UNISTD_H`, while the included header does not.
|
||||||
# We cannot safely undo the rename on those systems, and they generally have packages for zlib anyway.
|
# We cannot safely undo the rename on those systems, and they generally have packages for zlib anyway.
|
||||||
check_include_file(unistd.h NEED_GENERATED_ZCONF)
|
check_include_file(unistd.h NEED_GENERATED_ZCONF)
|
||||||
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h.included" AND NOT NEED_GENERATED_ZCONF)
|
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h.included" AND NOT NEED_GENERATED_ZCONF)
|
||||||
|
|
@ -504,15 +543,15 @@ else()
|
||||||
endif()
|
endif()
|
||||||
if(NOT cmark_FOUND)
|
if(NOT cmark_FOUND)
|
||||||
message(STATUS "Using bundled cmark")
|
message(STATUS "Using bundled cmark")
|
||||||
set(CMARK_STATIC ON CACHE BOOL "Build static libcmark library" FORCE)
|
set(ORIGINAL_BUILD_TESTING ${BUILD_TESTING})
|
||||||
set(CMARK_SHARED OFF CACHE BOOL "Build shared libcmark library" FORCE)
|
set(BUILD_TESTING 0)
|
||||||
set(CMARK_TESTS OFF CACHE BOOL "Build cmark tests and enable testing" FORCE)
|
set(BUILD_SHARED_LIBS 0)
|
||||||
add_subdirectory(libraries/cmark EXCLUDE_FROM_ALL) # Markdown parser
|
add_subdirectory(libraries/cmark EXCLUDE_FROM_ALL) # Markdown parser
|
||||||
add_library(cmark::cmark ALIAS cmark_static)
|
add_library(cmark::cmark ALIAS cmark)
|
||||||
|
set(BUILD_TESTING ${ORIGINAL_BUILD_TESTING})
|
||||||
else()
|
else()
|
||||||
message(STATUS "Using system cmark")
|
message(STATUS "Using system cmark")
|
||||||
endif()
|
endif()
|
||||||
add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much
|
|
||||||
add_subdirectory(libraries/gamemode)
|
add_subdirectory(libraries/gamemode)
|
||||||
add_subdirectory(libraries/murmur2) # Hash for usage with the CurseForge API
|
add_subdirectory(libraries/murmur2) # Hash for usage with the CurseForge API
|
||||||
if (NOT ghc_filesystem_FOUND)
|
if (NOT ghc_filesystem_FOUND)
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,59 @@
|
||||||
|
|
||||||
## Code formatting
|
## Code formatting
|
||||||
|
|
||||||
Try to follow the existing formatting.
|
All files are formatted with `clang-format` using the configuration in `.clang-format`. Ensure it is run on changed files before committing!
|
||||||
If there is no existing formatting, you may use `clang-format` with our included `.clang-format` configuration.
|
|
||||||
|
|
||||||
In general, in order of importance:
|
Please also follow the project's conventions for C++:
|
||||||
|
|
||||||
- Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
|
- Class and type names should be formatted as `PascalCase`: `MyClass`.
|
||||||
- Prefer readability over dogma.
|
- Private or protected class data members should be formatted as `camelCase` prefixed with `m_`: `m_myCounter`.
|
||||||
- Keep to the existing formatting.
|
- Private or protected `static` class data members should be formatted as `camelCase` prefixed with `s_`: `s_instance`.
|
||||||
- Indent with 4 space unless it's in a submodule.
|
- Public class data members should be formatted as `camelCase` without the prefix: `dateOfBirth`.
|
||||||
- Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
|
- Public, private or protected `static const` class data members should be formatted as `SCREAMING_SNAKE_CASE`: `MAX_VALUE`.
|
||||||
|
- Class function members should be formatted as `camelCase` without a prefix: `incrementCounter`.
|
||||||
|
- Global functions and non-`const` global variables should be formatted as `camelCase` without a prefix: `globalData`.
|
||||||
|
- `const` global variables, macros, and enum constants should be formatted as `SCREAMING_SNAKE_CASE`: `LIGHT_GRAY`.
|
||||||
|
- Avoid inventing acronyms or abbreviations especially for a name of multiple words - like `tp` for `texturePack`.
|
||||||
|
|
||||||
|
Most of these rules are included in the `.clang-tidy` file, so you can run `clang-tidy` to check for any violations.
|
||||||
|
|
||||||
|
Here is what these conventions with the formatting configuration look like:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#define AWESOMENESS 10
|
||||||
|
|
||||||
|
constexpr double PI = 3.14159;
|
||||||
|
|
||||||
|
enum class PizzaToppings { HAM_AND_PINEAPPLE, OREO_AND_KETCHUP };
|
||||||
|
|
||||||
|
struct Person {
|
||||||
|
QString name;
|
||||||
|
QDateTime dateOfBirth;
|
||||||
|
|
||||||
|
long daysOld() const { return dateOfBirth.daysTo(QDateTime::currentDateTime()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class ImportantClass {
|
||||||
|
public:
|
||||||
|
void incrementCounter()
|
||||||
|
{
|
||||||
|
if (m_counter + 1 > MAX_COUNTER_VALUE)
|
||||||
|
throw std::runtime_error("Counter has reached limit!");
|
||||||
|
|
||||||
|
++m_counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
int counter() const { return m_counter; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr int MAX_COUNTER_VALUE = 100;
|
||||||
|
int m_counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
ImportantClass importantClassInstance;
|
||||||
|
```
|
||||||
|
|
||||||
|
If you see any names which do not follow these conventions, it is preferred that you leave them be - renames increase the number of changes therefore make reviewing harder and make your PR more prone to conflicts. However, if you're refactoring a whole class anyway, it's fine.
|
||||||
|
|
||||||
## Signing your work
|
## Signing your work
|
||||||
|
|
||||||
|
|
|
||||||
30
COPYING.md
30
COPYING.md
|
|
@ -1,7 +1,7 @@
|
||||||
## Prism Launcher
|
## Prism Launcher
|
||||||
|
|
||||||
Prism Launcher - Minecraft Launcher
|
Prism Launcher - Minecraft Launcher
|
||||||
Copyright (C) 2022-2023 Prism Launcher Contributors
|
Copyright (C) 2022-2024 Prism Launcher Contributors
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -333,32 +333,6 @@
|
||||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 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.
|
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.
|
|
||||||
|
|
||||||
## Gamemode
|
## Gamemode
|
||||||
|
|
||||||
Copyright (c) 2017-2022, Feral Interactive
|
Copyright (c) 2017-2022, Feral Interactive
|
||||||
|
|
@ -436,7 +410,7 @@
|
||||||
Copyright (C) 2007 Johann Ollivier Lapeyre <johann@oxygen-icons.org>
|
Copyright (C) 2007 Johann Ollivier Lapeyre <johann@oxygen-icons.org>
|
||||||
Copyright (C) 2007 Kenneth Wimer <kwwii@bootsplash.org>
|
Copyright (C) 2007 Kenneth Wimer <kwwii@bootsplash.org>
|
||||||
Copyright (C) 2007 Riccardo Iaconelli <riccardo@oxygen-icons.org>
|
Copyright (C) 2007 Riccardo Iaconelli <riccardo@oxygen-icons.org>
|
||||||
|
|
||||||
and others
|
and others
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
This library is free software; you can redistribute it and/or
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
<p align="center"><b><i>This is a fork of PrismLauncher, unlocked to be able to use offline accounts without owning the game.</b></i></p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<picture>
|
<picture>
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="/program_info/org.prismlauncher.PrismLauncher.logo-darkmode.svg">
|
<source media="(prefers-color-scheme: dark)" srcset="/program_info/org.prismlauncher.PrismLauncher.logo-darkmode.svg">
|
||||||
|
|
@ -61,7 +63,12 @@ The translation effort for Prism Launcher is hosted on [Weblate](https://hosted.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
If you want to build Prism Launcher yourself, check the [Build Instructions](https://prismlauncher.org/wiki/development/build-instructions/).
|
If you want to build Prism Launcher yourself, check the build instructions:
|
||||||
|
|
||||||
|
- [Windows](https://prismlauncher.org/wiki/development/build-instructions/windows/)
|
||||||
|
- [Linux](https://prismlauncher.org/wiki/development/build-instructions/linux/)
|
||||||
|
- [MacOS](https://prismlauncher.org/wiki/development/build-instructions/macos/)
|
||||||
|
- [OpenBSD](https://prismlauncher.org/wiki/development/build-instructions/openbsd/)
|
||||||
|
|
||||||
## Sponsors & Partners
|
## Sponsors & Partners
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,9 @@ Config::Config()
|
||||||
UPDATER_ENABLED = true;
|
UPDATER_ENABLED = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#cmakedefine01 Launcher_ENABLE_JAVA_DOWNLOADER
|
||||||
|
JAVA_DOWNLOADER_ENABLED = Launcher_ENABLE_JAVA_DOWNLOADER;
|
||||||
|
|
||||||
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
||||||
GIT_TAG = "@Launcher_GIT_TAG@";
|
GIT_TAG = "@Launcher_GIT_TAG@";
|
||||||
GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";
|
GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";
|
||||||
|
|
@ -113,16 +116,19 @@ Config::Config()
|
||||||
NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
|
NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
|
||||||
NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@";
|
NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@";
|
||||||
HELP_URL = "@Launcher_HELP_URL@";
|
HELP_URL = "@Launcher_HELP_URL@";
|
||||||
|
LOGIN_CALLBACK_URL = "@Launcher_LOGIN_CALLBACK_URL@";
|
||||||
IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
|
IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
|
||||||
MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@";
|
MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@";
|
||||||
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
|
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
|
||||||
META_URL = "@Launcher_META_URL@";
|
META_URL = "@Launcher_META_URL@";
|
||||||
|
FMLLIBS_BASE_URL = "@Launcher_FMLLIBS_BASE_URL@";
|
||||||
|
|
||||||
GLFW_LIBRARY_NAME = "@Launcher_GLFW_LIBRARY_NAME@";
|
GLFW_LIBRARY_NAME = "@Launcher_GLFW_LIBRARY_NAME@";
|
||||||
OPENAL_LIBRARY_NAME = "@Launcher_OPENAL_LIBRARY_NAME@";
|
OPENAL_LIBRARY_NAME = "@Launcher_OPENAL_LIBRARY_NAME@";
|
||||||
|
|
||||||
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
|
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
|
||||||
TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@";
|
TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@";
|
||||||
|
TRANSLATION_FILES_URL = "@Launcher_TRANSLATION_FILES_URL@";
|
||||||
MATRIX_URL = "@Launcher_MATRIX_URL@";
|
MATRIX_URL = "@Launcher_MATRIX_URL@";
|
||||||
DISCORD_URL = "@Launcher_DISCORD_URL@";
|
DISCORD_URL = "@Launcher_DISCORD_URL@";
|
||||||
SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@";
|
SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@";
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ class Config {
|
||||||
QString VERSION_CHANNEL;
|
QString VERSION_CHANNEL;
|
||||||
|
|
||||||
bool UPDATER_ENABLED = false;
|
bool UPDATER_ENABLED = false;
|
||||||
|
bool JAVA_DOWNLOADER_ENABLED = false;
|
||||||
|
|
||||||
/// A short string identifying this build's platform or distribution.
|
/// A short string identifying this build's platform or distribution.
|
||||||
QString BUILD_PLATFORM;
|
QString BUILD_PLATFORM;
|
||||||
|
|
@ -132,6 +133,11 @@ class Config {
|
||||||
*/
|
*/
|
||||||
QString HELP_URL;
|
QString HELP_URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL that gets opened when the user succesfully logins.
|
||||||
|
*/
|
||||||
|
QString LOGIN_CALLBACK_URL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client ID you can get from Imgur when you register an application
|
* Client ID you can get from Imgur when you register an application
|
||||||
*/
|
*/
|
||||||
|
|
@ -163,10 +169,9 @@ class Config {
|
||||||
|
|
||||||
QString RESOURCE_BASE = "https://resources.download.minecraft.net/";
|
QString RESOURCE_BASE = "https://resources.download.minecraft.net/";
|
||||||
QString LIBRARY_BASE = "https://libraries.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 IMGUR_BASE_URL = "https://api.imgur.com/3/";
|
||||||
QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists
|
QString FMLLIBS_BASE_URL;
|
||||||
QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists
|
QString TRANSLATION_FILES_URL;
|
||||||
|
|
||||||
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
|
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,10 @@
|
||||||
<string>A Minecraft mod wants to access your camera.</string>
|
<string>A Minecraft mod wants to access your camera.</string>
|
||||||
<key>NSMicrophoneUsageDescription</key>
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
<string>A Minecraft mod wants to access your microphone.</string>
|
<string>A Minecraft mod wants to access your microphone.</string>
|
||||||
|
<key>NSDownloadsFolderUsageDescription</key>
|
||||||
|
<string>Prism uses access to your Downloads folder to help you more quickly add mods that can't be automatically downloaded to your instance. You can change where Prism scans for downloaded mods in Settings or the prompt that appears.</string>
|
||||||
|
<key>NSLocalNetworkUsageDescription</key>
|
||||||
|
<string>Minecraft uses the local network to find and connect to LAN servers.</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
<string>NSApplication</string>
|
<string>NSApplication</string>
|
||||||
<key>NSHighResolutionCapable</key>
|
<key>NSHighResolutionCapable</key>
|
||||||
|
|
@ -77,6 +81,14 @@
|
||||||
<string>curseforge</string>
|
<string>curseforge</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>Prismlauncher</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>prismlauncher</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
23
default.nix
23
default.nix
|
|
@ -1,14 +1,9 @@
|
||||||
(
|
(import (
|
||||||
import
|
let
|
||||||
(
|
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
|
||||||
let
|
in
|
||||||
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
|
fetchTarball {
|
||||||
in
|
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
|
||||||
fetchTarball {
|
sha256 = lock.nodes.flake-compat.locked.narHash;
|
||||||
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
|
}
|
||||||
sha256 = lock.nodes.flake-compat.locked.narHash;
|
) { src = ./.; }).defaultNix
|
||||||
}
|
|
||||||
)
|
|
||||||
{src = ./.;}
|
|
||||||
)
|
|
||||||
.defaultNix
|
|
||||||
|
|
|
||||||
140
flake.lock
generated
140
flake.lock
generated
|
|
@ -3,11 +3,11 @@
|
||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1696426674,
|
"lastModified": 1733328505,
|
||||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
|
||||||
"owner": "edolstra",
|
"owner": "edolstra",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -16,63 +16,6 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-parts": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs-lib": "nixpkgs-lib"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1698882062,
|
|
||||||
"narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=",
|
|
||||||
"owner": "hercules-ci",
|
|
||||||
"repo": "flake-parts",
|
|
||||||
"rev": "8c9fa2545007b49a5db5f650ae91f227672c3877",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "hercules-ci",
|
|
||||||
"repo": "flake-parts",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-utils": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1685518550,
|
|
||||||
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gitignore": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"pre-commit-hooks",
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1660459072,
|
|
||||||
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
|
|
||||||
"owner": "hercules-ci",
|
|
||||||
"repo": "gitignore.nix",
|
|
||||||
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "hercules-ci",
|
|
||||||
"repo": "gitignore.nix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"libnbtplusplus": {
|
"libnbtplusplus": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
|
|
@ -91,11 +34,11 @@
|
||||||
},
|
},
|
||||||
"nix-filter": {
|
"nix-filter": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1694857738,
|
"lastModified": 1731533336,
|
||||||
"narHash": "sha256-bxxNyLHjhu0N8T3REINXQ2ZkJco0ABFPn6PIe2QUfqo=",
|
"narHash": "sha256-oRam5PS1vcrr5UPgALW0eo1m/5/pls27Z/pabHNy2Ms=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "nix-filter",
|
"repo": "nix-filter",
|
||||||
"rev": "41fd48e00c22b4ced525af521ead8792402de0ea",
|
"rev": "f7653272fd234696ae94229839a99b73c9ab7de0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -106,89 +49,26 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1700856099,
|
"lastModified": 1739446958,
|
||||||
"narHash": "sha256-RnEA7iJ36Ay9jI0WwP+/y4zjEhmeN6Cjs9VOFBH7eVQ=",
|
"narHash": "sha256-+/bYK3DbPxMIvSL4zArkMX0LQvS7rzBKXnDXLfKyRVc=",
|
||||||
"owner": "nixos",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "0bd59c54ef06bc34eca01e37d689f5e46b3fe2f1",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nixos",
|
|
||||||
"ref": "nixpkgs-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs-lib": {
|
|
||||||
"locked": {
|
|
||||||
"dir": "lib",
|
|
||||||
"lastModified": 1698611440,
|
|
||||||
"narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=",
|
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735",
|
"rev": "2ff53fe64443980e139eaa286017f53f88336dd0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"dir": "lib",
|
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-unstable",
|
"ref": "nixos-unstable",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"pre-commit-hooks": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-compat": [
|
|
||||||
"flake-compat"
|
|
||||||
],
|
|
||||||
"flake-utils": "flake-utils",
|
|
||||||
"gitignore": "gitignore",
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"nixpkgs-stable": [
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1700922917,
|
|
||||||
"narHash": "sha256-ej2fch/T584b5K9sk1UhmZF7W6wEfDHuoUYpFN8dtvM=",
|
|
||||||
"owner": "cachix",
|
|
||||||
"repo": "pre-commit-hooks.nix",
|
|
||||||
"rev": "e5ee5c5f3844550c01d2131096c7271cec5e9b78",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "cachix",
|
|
||||||
"repo": "pre-commit-hooks.nix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
"flake-parts": "flake-parts",
|
|
||||||
"libnbtplusplus": "libnbtplusplus",
|
"libnbtplusplus": "libnbtplusplus",
|
||||||
"nix-filter": "nix-filter",
|
"nix-filter": "nix-filter",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs"
|
||||||
"pre-commit-hooks": "pre-commit-hooks"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
153
flake.nix
153
flake.nix
|
|
@ -1,44 +1,137 @@
|
||||||
{
|
{
|
||||||
description = "A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once (Fork of MultiMC)";
|
description = "A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once (Fork of MultiMC)";
|
||||||
|
|
||||||
|
nixConfig = {
|
||||||
|
extra-substituters = [ "https://prismlauncher.cachix.org" ];
|
||||||
|
extra-trusted-public-keys = [
|
||||||
|
"prismlauncher.cachix.org-1:9/n/FGyABA2jLUVfY+DEp4hKds/rwO+SCOtbOkDzd+c="
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
|
||||||
nix-filter.url = "github:numtide/nix-filter";
|
|
||||||
pre-commit-hooks = {
|
|
||||||
url = "github:cachix/pre-commit-hooks.nix";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
inputs.nixpkgs-stable.follows = "nixpkgs";
|
|
||||||
inputs.flake-compat.follows = "flake-compat";
|
|
||||||
};
|
|
||||||
flake-compat = {
|
|
||||||
url = "github:edolstra/flake-compat";
|
|
||||||
flake = false;
|
|
||||||
};
|
|
||||||
libnbtplusplus = {
|
libnbtplusplus = {
|
||||||
url = "github:PrismLauncher/libnbtplusplus";
|
url = "github:PrismLauncher/libnbtplusplus";
|
||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nix-filter.url = "github:numtide/nix-filter";
|
||||||
|
|
||||||
|
/*
|
||||||
|
Inputs below this are optional and can be removed
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
inputs.prismlauncher = {
|
||||||
|
url = "github:PrismLauncher/PrismLauncher";
|
||||||
|
inputs = {
|
||||||
|
flake-compat.follows = "";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
|
||||||
|
flake-compat = {
|
||||||
|
url = "github:edolstra/flake-compat";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs =
|
||||||
flake-parts,
|
{
|
||||||
pre-commit-hooks,
|
self,
|
||||||
...
|
nixpkgs,
|
||||||
} @ inputs:
|
libnbtplusplus,
|
||||||
flake-parts.lib.mkFlake {inherit inputs;} {
|
nix-filter,
|
||||||
imports = [
|
...
|
||||||
pre-commit-hooks.flakeModule
|
}:
|
||||||
|
let
|
||||||
|
inherit (nixpkgs) lib;
|
||||||
|
|
||||||
./nix/dev.nix
|
# While we only officially support aarch and x86_64 on Linux and MacOS,
|
||||||
./nix/distribution.nix
|
# we expose a reasonable amount of other systems for users who want to
|
||||||
];
|
# build for most exotic platforms
|
||||||
|
systems = lib.systems.flakeExposed;
|
||||||
|
|
||||||
systems = [
|
forAllSystems = lib.genAttrs systems;
|
||||||
"x86_64-linux"
|
nixpkgsFor = forAllSystems (system: nixpkgs.legacyPackages.${system});
|
||||||
"aarch64-linux"
|
in
|
||||||
"x86_64-darwin"
|
{
|
||||||
"aarch64-darwin"
|
checks = forAllSystems (
|
||||||
];
|
system:
|
||||||
|
let
|
||||||
|
checks' = nixpkgsFor.${system}.callPackage ./nix/checks.nix { inherit self; };
|
||||||
|
in
|
||||||
|
lib.filterAttrs (_: lib.isDerivation) checks'
|
||||||
|
);
|
||||||
|
|
||||||
|
devShells = forAllSystems (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
pkgs = nixpkgsFor.${system};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
default = pkgs.mkShell {
|
||||||
|
inputsFrom = [ self.packages.${system}.prismlauncher-unwrapped ];
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
ccache
|
||||||
|
ninja
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
formatter = forAllSystems (system: nixpkgsFor.${system}.nixfmt-rfc-style);
|
||||||
|
|
||||||
|
overlays.default = final: prev: {
|
||||||
|
prismlauncher-unwrapped = prev.callPackage ./nix/unwrapped.nix {
|
||||||
|
inherit
|
||||||
|
libnbtplusplus
|
||||||
|
nix-filter
|
||||||
|
self
|
||||||
|
;
|
||||||
|
};
|
||||||
|
|
||||||
|
prismlauncher = final.callPackage ./nix/wrapper.nix { };
|
||||||
|
};
|
||||||
|
|
||||||
|
packages = forAllSystems (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
pkgs = nixpkgsFor.${system};
|
||||||
|
|
||||||
|
# Build a scope from our overlay
|
||||||
|
prismPackages = lib.makeScope pkgs.newScope (final: self.overlays.default final pkgs);
|
||||||
|
|
||||||
|
# Grab our packages from it and set the default
|
||||||
|
packages = {
|
||||||
|
inherit (prismPackages) prismlauncher-unwrapped prismlauncher;
|
||||||
|
default = prismPackages.prismlauncher;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
# Only output them if they're available on the current system
|
||||||
|
lib.filterAttrs (_: lib.meta.availableOn pkgs.stdenv.hostPlatform) packages
|
||||||
|
);
|
||||||
|
|
||||||
|
# We put these under legacyPackages as they are meant for CI, not end user consumption
|
||||||
|
legacyPackages = forAllSystems (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
prismPackages = self.packages.${system};
|
||||||
|
legacyPackages = self.legacyPackages.${system};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
prismlauncher-debug = prismPackages.prismlauncher.override {
|
||||||
|
prismlauncher-unwrapped = legacyPackages.prismlauncher-unwrapped-debug;
|
||||||
|
};
|
||||||
|
|
||||||
|
prismlauncher-unwrapped-debug = prismPackages.prismlauncher-unwrapped.overrideAttrs {
|
||||||
|
cmakeBuildType = "Debug";
|
||||||
|
dontStrip = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
20
flatpak/flite.json
Normal file
20
flatpak/flite.json
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "flite",
|
||||||
|
"config-opts": [
|
||||||
|
"--enable-shared",
|
||||||
|
"--with-audio=pulseaudio"
|
||||||
|
],
|
||||||
|
"no-parallel-make": true,
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/festvox/flite.git",
|
||||||
|
"tag": "v2.2",
|
||||||
|
"commit": "e9e2e37c329dbe98bfeb27a1828ef9a71fa84f88",
|
||||||
|
"x-checker-data": {
|
||||||
|
"type": "git",
|
||||||
|
"tag-pattern": "^v([\\d.]+)$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -1,22 +1,18 @@
|
||||||
{
|
{
|
||||||
"name": "libdecor",
|
"name": "libdecor",
|
||||||
"buildsystem": "meson",
|
"buildsystem": "meson",
|
||||||
"config-opts": [
|
"config-opts": [
|
||||||
"-Ddemo=false"
|
"-Ddemo=false"
|
||||||
],
|
],
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://gitlab.freedesktop.org/libdecor/libdecor.git",
|
"url": "https://gitlab.freedesktop.org/libdecor/libdecor.git",
|
||||||
"commit": "73260393a97291c887e1074ab7f318e031be0ac6"
|
"commit": "c2bd8ad6fa42c0cb17553ce77ad8a87d1f543b1f"
|
||||||
},
|
}
|
||||||
{
|
],
|
||||||
"type": "patch",
|
"cleanup": [
|
||||||
"path": "patches/weird_libdecor.patch"
|
"/include",
|
||||||
}
|
"/lib/pkgconfig"
|
||||||
],
|
]
|
||||||
"cleanup": [
|
|
||||||
"/include",
|
|
||||||
"/lib/pkgconfig"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
id: org.prismlauncher.PrismLauncher
|
id: org.prismlauncher.PrismLauncher
|
||||||
runtime: org.kde.Platform
|
runtime: org.kde.Platform
|
||||||
runtime-version: "5.15-23.08"
|
runtime-version: '6.8'
|
||||||
sdk: org.kde.Sdk
|
sdk: org.kde.Sdk
|
||||||
sdk-extensions:
|
sdk-extensions:
|
||||||
- org.freedesktop.Sdk.Extension.openjdk17
|
- org.freedesktop.Sdk.Extension.openjdk17
|
||||||
- org.freedesktop.Sdk.Extension.openjdk8
|
|
||||||
|
|
||||||
command: prismlauncher
|
command: prismlauncher
|
||||||
finish-args:
|
finish-args:
|
||||||
|
|
@ -20,9 +19,12 @@ finish-args:
|
||||||
- --filesystem=xdg-download:ro
|
- --filesystem=xdg-download:ro
|
||||||
# FTBApp import
|
# FTBApp import
|
||||||
- --filesystem=~/.ftba:ro
|
- --filesystem=~/.ftba:ro
|
||||||
|
# Userspace visibility for manual hugepages configuration
|
||||||
cleanup:
|
# Required for -XX:+UseLargePages
|
||||||
- /lib/libGLU*
|
- --filesystem=/sys/kernel/mm/hugepages:ro
|
||||||
|
# Userspace visibility for transparent hugepages configuration
|
||||||
|
# Required for -XX:+UseTransparentHugePages
|
||||||
|
- --filesystem=/sys/kernel/mm/transparent_hugepage:ro
|
||||||
|
|
||||||
modules:
|
modules:
|
||||||
# Might be needed by some Controller mods (see https://github.com/isXander/Controlify/issues/31)
|
# Might be needed by some Controller mods (see https://github.com/isXander/Controlify/issues/31)
|
||||||
|
|
@ -31,48 +33,39 @@ modules:
|
||||||
# Needed for proper Wayland support
|
# Needed for proper Wayland support
|
||||||
- libdecor.json
|
- libdecor.json
|
||||||
|
|
||||||
|
# Text to Speech in the game
|
||||||
|
- flite.json
|
||||||
|
|
||||||
- name: prismlauncher
|
- name: prismlauncher
|
||||||
buildsystem: cmake-ninja
|
buildsystem: cmake-ninja
|
||||||
builddir: true
|
builddir: true
|
||||||
config-opts:
|
config-opts:
|
||||||
- -DLauncher_BUILD_PLATFORM=flatpak
|
- -DLauncher_BUILD_PLATFORM=flatpak
|
||||||
|
# This allows us to manage and update Java independently of this Flatpak
|
||||||
|
- -DLauncher_ENABLE_JAVA_DOWNLOADER=ON
|
||||||
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||||
- -DLauncher_QT_VERSION_MAJOR=5
|
|
||||||
build-options:
|
build-options:
|
||||||
env:
|
env:
|
||||||
JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17
|
JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17
|
||||||
JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac
|
JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac
|
||||||
|
run-tests: true
|
||||||
sources:
|
sources:
|
||||||
- type: dir
|
- type: dir
|
||||||
path: ../
|
path: ../
|
||||||
|
|
||||||
- name: openjdk
|
|
||||||
buildsystem: simple
|
|
||||||
build-commands:
|
|
||||||
- mkdir -p /app/jdk/
|
|
||||||
- /usr/lib/sdk/openjdk17/install.sh
|
|
||||||
- mv /app/jre /app/jdk/17
|
|
||||||
- /usr/lib/sdk/openjdk8/install.sh
|
|
||||||
- mv /app/jre /app/jdk/8
|
|
||||||
cleanup:
|
|
||||||
- /jre
|
|
||||||
|
|
||||||
- name: glfw
|
- name: glfw
|
||||||
buildsystem: cmake-ninja
|
buildsystem: cmake-ninja
|
||||||
config-opts:
|
config-opts:
|
||||||
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||||
- -DBUILD_SHARED_LIBS:BOOL=ON
|
- -DBUILD_SHARED_LIBS:BOOL=ON
|
||||||
- -DGLFW_USE_WAYLAND=ON
|
- -DGLFW_BUILD_WAYLAND:BOOL=ON
|
||||||
|
- -DGLFW_BUILD_DOCS:BOOL=OFF
|
||||||
sources:
|
sources:
|
||||||
- type: git
|
- type: git
|
||||||
url: https://github.com/glfw/glfw.git
|
url: https://github.com/glfw/glfw.git
|
||||||
commit: 3fa2360720eeba1964df3c0ecf4b5df8648a8e52
|
commit: 7b6aead9fb88b3623e3b3725ebb42670cbe4c579 # 3.4
|
||||||
- type: patch
|
- type: patch
|
||||||
path: patches/0003-Don-t-crash-on-calls-to-focus-or-icon.patch
|
path: patches/0009-Defer-setting-cursor-position-until-the-cursor-is-lo.patch
|
||||||
- type: patch
|
|
||||||
path: patches/0005-Add-warning-about-being-an-unofficial-patch.patch
|
|
||||||
- type: patch
|
|
||||||
path: patches/0007-Platform-Prefer-Wayland-over-X11.patch
|
|
||||||
cleanup:
|
cleanup:
|
||||||
- /include
|
- /include
|
||||||
- /lib/cmake
|
- /lib/cmake
|
||||||
|
|
@ -82,8 +75,8 @@ modules:
|
||||||
buildsystem: autotools
|
buildsystem: autotools
|
||||||
sources:
|
sources:
|
||||||
- type: archive
|
- type: archive
|
||||||
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.2.tar.xz
|
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.3.tar.xz
|
||||||
sha256: c8bee4790d9058bacc4b6246456c58021db58a87ddda1a9d0139bf5f18f1f240
|
sha256: f8dd7566adb74147fab9964680b6bbadee87cf406a7fcff51718a5e6949b841c
|
||||||
x-checker-data:
|
x-checker-data:
|
||||||
type: anitya
|
type: anitya
|
||||||
project-id: 14957
|
project-id: 14957
|
||||||
|
|
@ -104,18 +97,15 @@ modules:
|
||||||
- install -Dm755 ../data/gamemoderun -t /app/bin
|
- install -Dm755 ../data/gamemoderun -t /app/bin
|
||||||
sources:
|
sources:
|
||||||
- type: archive
|
- type: archive
|
||||||
archive-type: tar-gzip
|
dest-filename: gamemode.tar.gz
|
||||||
url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.7
|
url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.8.2
|
||||||
sha256: 57ce73ba605d1cf12f8d13725006a895182308d93eba0f69f285648449641803
|
sha256: 2886d4ce543c78bd2a364316d5e7fd59ef06b71de63f896b37c6d3dc97658f60
|
||||||
x-checker-data:
|
x-checker-data:
|
||||||
type: json
|
type: json
|
||||||
url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest
|
url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest
|
||||||
version-query: .tag_name
|
version-query: .tag_name
|
||||||
url-query: .tarball_url
|
url-query: .tarball_url
|
||||||
timestamp-query: .published_at
|
timestamp-query: .published_at
|
||||||
# from https://github.com/flathub/net.gaijin.WarThunder/blob/7ea6f7a9f84b9c77150c003a7059dc03f8dcbc7f/gamemode.patch
|
|
||||||
- type: patch
|
|
||||||
path: patches/gamemode.patch
|
|
||||||
cleanup:
|
cleanup:
|
||||||
- /include
|
- /include
|
||||||
- /lib/pkgconfig
|
- /lib/pkgconfig
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
diff --git a/src/wl_window.c b/src/wl_window.c
|
|
||||||
index 52d3b9eb..4ac4eb5d 100644
|
|
||||||
--- a/src/wl_window.c
|
|
||||||
+++ b/src/wl_window.c
|
|
||||||
@@ -2117,8 +2117,7 @@ void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title)
|
|
||||||
void _glfwSetWindowIconWayland(_GLFWwindow* window,
|
|
||||||
int count, const GLFWimage* images)
|
|
||||||
{
|
|
||||||
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
|
||||||
- "Wayland: The platform does not support setting the window icon");
|
|
||||||
+ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the window icon\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos)
|
|
||||||
@@ -2361,8 +2360,7 @@ void _glfwRequestWindowAttentionWayland(_GLFWwindow* window)
|
|
||||||
|
|
||||||
void _glfwFocusWindowWayland(_GLFWwindow* window)
|
|
||||||
{
|
|
||||||
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
|
||||||
- "Wayland: The platform does not support setting the input focus");
|
|
||||||
+ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the input focus\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void _glfwSetWindowMonitorWayland(_GLFWwindow* window,
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
diff --git a/src/init.c b/src/init.c
|
|
||||||
index 06dbb3f2..a7c6da86 100644
|
|
||||||
--- a/src/init.c
|
|
||||||
+++ b/src/init.c
|
|
||||||
@@ -449,6 +449,12 @@ GLFWAPI int glfwInit(void)
|
|
||||||
_glfw.initialized = GLFW_TRUE;
|
|
||||||
|
|
||||||
glfwDefaultWindowHints();
|
|
||||||
+
|
|
||||||
+ fprintf(stderr, "!!! Patched GLFW from https://github.com/Admicos/minecraft-wayland\n"
|
|
||||||
+ "!!! If any issues with the window, or some issues with rendering, occur, "
|
|
||||||
+ "first try with the built-in GLFW, and if that solves the issue, report there first.\n"
|
|
||||||
+ "!!! Use outside Minecraft is untested, and things might break.\n");
|
|
||||||
+
|
|
||||||
return GLFW_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
diff --git a/src/platform.c b/src/platform.c
|
|
||||||
index c5966ae7..3e7442f9 100644
|
|
||||||
--- a/src/platform.c
|
|
||||||
+++ b/src/platform.c
|
|
||||||
@@ -49,12 +49,12 @@ static const struct
|
|
||||||
#if defined(_GLFW_COCOA)
|
|
||||||
{ GLFW_PLATFORM_COCOA, _glfwConnectCocoa },
|
|
||||||
#endif
|
|
||||||
-#if defined(_GLFW_X11)
|
|
||||||
- { GLFW_PLATFORM_X11, _glfwConnectX11 },
|
|
||||||
-#endif
|
|
||||||
#if defined(_GLFW_WAYLAND)
|
|
||||||
{ GLFW_PLATFORM_WAYLAND, _glfwConnectWayland },
|
|
||||||
#endif
|
|
||||||
+#if defined(_GLFW_X11)
|
|
||||||
+ { GLFW_PLATFORM_X11, _glfwConnectX11 },
|
|
||||||
+#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
GLFWbool _glfwSelectPlatform(int desiredID, _GLFWplatform* platform)
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
From 9997ae55a47de469ea26f8437c30b51483abda5f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Dan Klishch <danilklishch@gmail.com>
|
||||||
|
Date: Sat, 30 Sep 2023 23:38:05 -0400
|
||||||
|
Subject: Defer setting cursor position until the cursor is locked
|
||||||
|
|
||||||
|
---
|
||||||
|
src/wl_platform.h | 3 +++
|
||||||
|
src/wl_window.c | 14 ++++++++++++--
|
||||||
|
2 files changed, 15 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/wl_platform.h b/src/wl_platform.h
|
||||||
|
index ca34f66e..cd1f227f 100644
|
||||||
|
--- a/src/wl_platform.h
|
||||||
|
+++ b/src/wl_platform.h
|
||||||
|
@@ -403,6 +403,9 @@ typedef struct _GLFWwindowWayland
|
||||||
|
int scaleSize;
|
||||||
|
int compositorPreferredScale;
|
||||||
|
|
||||||
|
+ double askedCursorPosX, askedCursorPosY;
|
||||||
|
+ GLFWbool didAskForSetCursorPos;
|
||||||
|
+
|
||||||
|
struct zwp_relative_pointer_v1* relativePointer;
|
||||||
|
struct zwp_locked_pointer_v1* lockedPointer;
|
||||||
|
struct zwp_confined_pointer_v1* confinedPointer;
|
||||||
|
diff --git a/src/wl_window.c b/src/wl_window.c
|
||||||
|
index 1de26558..0df16747 100644
|
||||||
|
--- a/src/wl_window.c
|
||||||
|
+++ b/src/wl_window.c
|
||||||
|
@@ -2586,8 +2586,9 @@ void _glfwGetCursorPosWayland(_GLFWwindow* window, double* xpos, double* ypos)
|
||||||
|
|
||||||
|
void _glfwSetCursorPosWayland(_GLFWwindow* window, double x, double y)
|
||||||
|
{
|
||||||
|
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||||
|
- "Wayland: The platform does not support setting the cursor position");
|
||||||
|
+ window->wl.didAskForSetCursorPos = true;
|
||||||
|
+ window->wl.askedCursorPosX = x;
|
||||||
|
+ window->wl.askedCursorPosY = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode)
|
||||||
|
@@ -2819,6 +2820,15 @@ static const struct zwp_relative_pointer_v1_listener relativePointerListener =
|
||||||
|
static void lockedPointerHandleLocked(void* userData,
|
||||||
|
struct zwp_locked_pointer_v1* lockedPointer)
|
||||||
|
{
|
||||||
|
+ _GLFWwindow* window = userData;
|
||||||
|
+
|
||||||
|
+ if (window->wl.didAskForSetCursorPos)
|
||||||
|
+ {
|
||||||
|
+ window->wl.didAskForSetCursorPos = false;
|
||||||
|
+ zwp_locked_pointer_v1_set_cursor_position_hint(window->wl.lockedPointer,
|
||||||
|
+ wl_fixed_from_double(window->wl.askedCursorPosX),
|
||||||
|
+ wl_fixed_from_double(window->wl.askedCursorPosY));
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lockedPointerHandleUnlocked(void* userData,
|
||||||
|
--
|
||||||
|
2.42.0
|
||||||
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
diff -ruN a/common/common-pidfds.c b/common/common-pidfds.c
|
|
||||||
--- a/common/common-pidfds.c 2021-02-18 20:00:12.000000000 +0100
|
|
||||||
+++ b/common/common-pidfds.c 2023-09-07 08:57:42.954362763 +0200
|
|
||||||
@@ -58,6 +58,8 @@
|
|
||||||
{
|
|
||||||
return (int)syscall(__NR_pidfd_open, pid, flags);
|
|
||||||
}
|
|
||||||
+#else
|
|
||||||
+#include <sys/pidfd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* pidfd functions */
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
diff --git a/src/libdecor.c b/src/libdecor.c
|
|
||||||
index a9c1106..1aa38b3 100644
|
|
||||||
--- a/src/libdecor.c
|
|
||||||
+++ b/src/libdecor.c
|
|
||||||
@@ -1391,22 +1391,32 @@ calculate_priority(const struct libdecor_plugin_description *plugin_description)
|
|
||||||
static bool
|
|
||||||
check_symbol_conflicts(const struct libdecor_plugin_description *plugin_description)
|
|
||||||
{
|
|
||||||
+ bool ret = true;
|
|
||||||
char * const *symbol;
|
|
||||||
+ void* main_prog = dlopen(NULL, RTLD_LAZY);
|
|
||||||
+ if (!main_prog) {
|
|
||||||
+ fprintf(stderr, "Plugin \"%s\" couldn't check conflicting symbols: \"%s\".\n",
|
|
||||||
+ plugin_description->description, dlerror());
|
|
||||||
+ return false;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
|
|
||||||
symbol = plugin_description->conflicting_symbols;
|
|
||||||
while (*symbol) {
|
|
||||||
dlerror();
|
|
||||||
- dlsym (RTLD_DEFAULT, *symbol);
|
|
||||||
+ dlsym (main_prog, *symbol);
|
|
||||||
if (!dlerror()) {
|
|
||||||
fprintf(stderr, "Plugin \"%s\" uses conflicting symbol \"%s\".\n",
|
|
||||||
plugin_description->description, *symbol);
|
|
||||||
- return false;
|
|
||||||
+ ret = false;
|
|
||||||
+ break;
|
|
||||||
}
|
|
||||||
|
|
||||||
symbol++;
|
|
||||||
}
|
|
||||||
|
|
||||||
- return true;
|
|
||||||
+ dlclose(main_prog);
|
|
||||||
+ return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct plugin_loader *
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 45094ca570be383d06df729b6972830ec63bd3df
|
Subproject commit f5d368a31d6ef046eb2955c74ec6f54f32ed5c4e
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
builds:
|
|
||||||
exclude:
|
|
||||||
- "*.x86_64-darwin.*"
|
|
||||||
include:
|
|
||||||
- "checks.x86_64-linux.*"
|
|
||||||
- "devShells.*.*"
|
|
||||||
- "packages.*.*"
|
|
||||||
|
|
@ -44,10 +44,12 @@
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
|
|
||||||
#include "DataMigrationTask.h"
|
#include "DataMigrationTask.h"
|
||||||
|
#include "java/JavaInstallList.h"
|
||||||
#include "net/PasteUpload.h"
|
#include "net/PasteUpload.h"
|
||||||
#include "pathmatcher/MultiMatcher.h"
|
#include "pathmatcher/MultiMatcher.h"
|
||||||
#include "pathmatcher/SimplePrefixMatcher.h"
|
#include "pathmatcher/SimplePrefixMatcher.h"
|
||||||
#include "settings/INIFile.h"
|
#include "tasks/Task.h"
|
||||||
|
#include "tools/GenericProfiler.h"
|
||||||
#include "ui/InstanceWindow.h"
|
#include "ui/InstanceWindow.h"
|
||||||
#include "ui/MainWindow.h"
|
#include "ui/MainWindow.h"
|
||||||
|
|
||||||
|
|
@ -57,8 +59,6 @@
|
||||||
#include "ui/pages/BasePageProvider.h"
|
#include "ui/pages/BasePageProvider.h"
|
||||||
#include "ui/pages/global/APIPage.h"
|
#include "ui/pages/global/APIPage.h"
|
||||||
#include "ui/pages/global/AccountListPage.h"
|
#include "ui/pages/global/AccountListPage.h"
|
||||||
#include "ui/pages/global/CustomCommandsPage.h"
|
|
||||||
#include "ui/pages/global/EnvironmentVariablesPage.h"
|
|
||||||
#include "ui/pages/global/ExternalToolsPage.h"
|
#include "ui/pages/global/ExternalToolsPage.h"
|
||||||
#include "ui/pages/global/JavaPage.h"
|
#include "ui/pages/global/JavaPage.h"
|
||||||
#include "ui/pages/global/LanguagePage.h"
|
#include "ui/pages/global/LanguagePage.h"
|
||||||
|
|
@ -66,8 +66,10 @@
|
||||||
#include "ui/pages/global/MinecraftPage.h"
|
#include "ui/pages/global/MinecraftPage.h"
|
||||||
#include "ui/pages/global/ProxyPage.h"
|
#include "ui/pages/global/ProxyPage.h"
|
||||||
|
|
||||||
|
#include "ui/setupwizard/AutoJavaWizardPage.h"
|
||||||
#include "ui/setupwizard/JavaWizardPage.h"
|
#include "ui/setupwizard/JavaWizardPage.h"
|
||||||
#include "ui/setupwizard/LanguageWizardPage.h"
|
#include "ui/setupwizard/LanguageWizardPage.h"
|
||||||
|
#include "ui/setupwizard/LoginWizardPage.h"
|
||||||
#include "ui/setupwizard/PasteWizardPage.h"
|
#include "ui/setupwizard/PasteWizardPage.h"
|
||||||
#include "ui/setupwizard/SetupWizard.h"
|
#include "ui/setupwizard/SetupWizard.h"
|
||||||
#include "ui/setupwizard/ThemeWizardPage.h"
|
#include "ui/setupwizard/ThemeWizardPage.h"
|
||||||
|
|
@ -105,7 +107,7 @@
|
||||||
#include "icons/IconList.h"
|
#include "icons/IconList.h"
|
||||||
#include "net/HttpMetaCache.h"
|
#include "net/HttpMetaCache.h"
|
||||||
|
|
||||||
#include "java/JavaUtils.h"
|
#include "java/JavaInstallList.h"
|
||||||
|
|
||||||
#include "updater/ExternalUpdater.h"
|
#include "updater/ExternalUpdater.h"
|
||||||
|
|
||||||
|
|
@ -125,6 +127,7 @@
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys.h>
|
#include <sys.h>
|
||||||
|
#include "SysInfo.h"
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
|
@ -150,6 +153,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
|
#include <windows.h>
|
||||||
#include "WindowsConsole.h"
|
#include "WindowsConsole.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -225,6 +229,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
|
|
||||||
// Don't quit on hiding the last window
|
// Don't quit on hiding the last window
|
||||||
this->setQuitOnLastWindowClosed(false);
|
this->setQuitOnLastWindowClosed(false);
|
||||||
|
this->setQuitLockEnabled(false);
|
||||||
|
|
||||||
// Commandline parsing
|
// Commandline parsing
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
|
|
@ -234,7 +239,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
{ { { "d", "dir" }, "Use a custom path as application root (use '.' for current directory)", "directory" },
|
{ { { "d", "dir" }, "Use a custom path as application root (use '.' for current directory)", "directory" },
|
||||||
{ { "l", "launch" }, "Launch the specified instance (by instance ID)", "instance" },
|
{ { "l", "launch" }, "Launch the specified instance (by instance ID)", "instance" },
|
||||||
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
|
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
|
||||||
|
{ { "w", "world" }, "Join the specified world on launch (only valid in combination with --launch)", "world" },
|
||||||
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" },
|
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" },
|
||||||
|
{ { "o", "offline" }, "Launch offline, with given player name (only valid in combination with --launch)", "offline" },
|
||||||
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
|
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
|
||||||
{ { "I", "import" }, "Import instance or resource from specified local path or URL", "url" },
|
{ { "I", "import" }, "Import instance or resource from specified local path or URL", "url" },
|
||||||
{ "show", "Opens the window for the specified instance (by instance ID)", "show" } });
|
{ "show", "Opens the window for the specified instance (by instance ID)", "show" } });
|
||||||
|
|
@ -248,7 +255,12 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
|
|
||||||
m_instanceIdToLaunch = parser.value("launch");
|
m_instanceIdToLaunch = parser.value("launch");
|
||||||
m_serverToJoin = parser.value("server");
|
m_serverToJoin = parser.value("server");
|
||||||
|
m_worldToJoin = parser.value("world");
|
||||||
m_profileToUse = parser.value("profile");
|
m_profileToUse = parser.value("profile");
|
||||||
|
if (parser.isSet("offline")) {
|
||||||
|
m_offline = true;
|
||||||
|
m_offlineName = parser.value("offline");
|
||||||
|
}
|
||||||
m_liveCheck = parser.isSet("alive");
|
m_liveCheck = parser.isSet("alive");
|
||||||
|
|
||||||
m_instanceIdToShowWindowOf = parser.value("show");
|
m_instanceIdToShowWindowOf = parser.value("show");
|
||||||
|
|
@ -263,8 +275,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// error if --launch is missing with --server or --profile
|
// error if --launch is missing with --server or --profile
|
||||||
if ((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) {
|
if ((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty() || !m_profileToUse.isEmpty() || m_offline) &&
|
||||||
std::cerr << "--server and --profile can only be used in combination with --launch!" << std::endl;
|
m_instanceIdToLaunch.isEmpty()) {
|
||||||
|
std::cerr << "--server, --profile and --offline can only be used in combination with --launch!" << std::endl;
|
||||||
m_status = Application::Failed;
|
m_status = Application::Failed;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -290,12 +303,17 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
QString adjustedBy;
|
QString adjustedBy;
|
||||||
QString dataPath;
|
QString dataPath;
|
||||||
// change folder
|
// change folder
|
||||||
|
QString dataDirEnv;
|
||||||
QString dirParam = parser.value("dir");
|
QString dirParam = parser.value("dir");
|
||||||
if (!dirParam.isEmpty()) {
|
if (!dirParam.isEmpty()) {
|
||||||
// the dir param. it makes multimc data path point to whatever the user specified
|
// the dir param. it makes multimc data path point to whatever the user specified
|
||||||
// on command line
|
// on command line
|
||||||
adjustedBy = "Command line";
|
adjustedBy = "Command line";
|
||||||
dataPath = dirParam;
|
dataPath = dirParam;
|
||||||
|
} else if (dataDirEnv = QProcessEnvironment::systemEnvironment().value(QString("%1_DATA_DIR").arg(BuildConfig.LAUNCHER_NAME.toUpper()));
|
||||||
|
!dataDirEnv.isEmpty()) {
|
||||||
|
adjustedBy = "System environment";
|
||||||
|
dataPath = dataDirEnv;
|
||||||
} else {
|
} else {
|
||||||
QDir foo;
|
QDir foo;
|
||||||
if (DesktopServices::isSnap()) {
|
if (DesktopServices::isSnap()) {
|
||||||
|
|
@ -308,7 +326,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
adjustedBy = "Persistent data path";
|
adjustedBy = "Persistent data path";
|
||||||
|
|
||||||
#ifndef Q_OS_MACOS
|
#ifndef Q_OS_MACOS
|
||||||
if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) {
|
if (auto portableUserData = FS::PathCombine(m_rootPath, "UserData"); QDir(portableUserData).exists()) {
|
||||||
|
dataPath = portableUserData;
|
||||||
|
adjustedBy = "Portable user data path";
|
||||||
|
m_portable = true;
|
||||||
|
} else if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) {
|
||||||
dataPath = m_rootPath;
|
dataPath = m_rootPath;
|
||||||
adjustedBy = "Portable data path";
|
adjustedBy = "Portable data path";
|
||||||
m_portable = true;
|
m_portable = true;
|
||||||
|
|
@ -374,10 +396,16 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
|
|
||||||
if (!m_serverToJoin.isEmpty()) {
|
if (!m_serverToJoin.isEmpty()) {
|
||||||
launch.args["server"] = m_serverToJoin;
|
launch.args["server"] = m_serverToJoin;
|
||||||
|
} else if (!m_worldToJoin.isEmpty()) {
|
||||||
|
launch.args["world"] = m_worldToJoin;
|
||||||
}
|
}
|
||||||
if (!m_profileToUse.isEmpty()) {
|
if (!m_profileToUse.isEmpty()) {
|
||||||
launch.args["profile"] = m_profileToUse;
|
launch.args["profile"] = m_profileToUse;
|
||||||
}
|
}
|
||||||
|
if (m_offline) {
|
||||||
|
launch.args["offline_enabled"] = "true";
|
||||||
|
launch.args["offline_name"] = m_offlineName;
|
||||||
|
}
|
||||||
m_peerInstance->sendMessage(launch.serialize(), timeout);
|
m_peerInstance->sendMessage(launch.serialize(), timeout);
|
||||||
}
|
}
|
||||||
m_status = Application::Succeeded;
|
m_status = Application::Succeeded;
|
||||||
|
|
@ -389,20 +417,15 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
{
|
{
|
||||||
static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "-%0.log";
|
static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "-%0.log";
|
||||||
static const QString logBase = FS::PathCombine("logs", baseLogFile);
|
static const QString logBase = FS::PathCombine("logs", baseLogFile);
|
||||||
auto moveFile = [](const QString& oldName, const QString& newName) {
|
|
||||||
QFile::remove(newName);
|
|
||||||
QFile::copy(oldName, newName);
|
|
||||||
QFile::remove(oldName);
|
|
||||||
};
|
|
||||||
if (FS::ensureFolderPathExists("logs")) { // if this did not fail
|
if (FS::ensureFolderPathExists("logs")) { // if this did not fail
|
||||||
for (auto i = 0; i <= 4; i++)
|
for (auto i = 0; i <= 4; i++)
|
||||||
if (auto oldName = baseLogFile.arg(i);
|
if (auto oldName = baseLogFile.arg(i);
|
||||||
QFile::exists(oldName)) // do not pointlessly delete new files if the old ones are not there
|
QFile::exists(oldName)) // do not pointlessly delete new files if the old ones are not there
|
||||||
moveFile(oldName, logBase.arg(i));
|
FS::move(oldName, logBase.arg(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto i = 4; i > 0; i--)
|
for (auto i = 4; i > 0; i--)
|
||||||
moveFile(logBase.arg(i - 1), logBase.arg(i));
|
FS::move(logBase.arg(i - 1), logBase.arg(i));
|
||||||
|
|
||||||
logFile = std::unique_ptr<QFile>(new QFile(logBase.arg(0)));
|
logFile = std::unique_ptr<QFile>(new QFile(logBase.arg(0)));
|
||||||
if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
|
if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
|
||||||
|
|
@ -442,7 +465,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
|
|
||||||
// search the dataPath()
|
// search the dataPath()
|
||||||
// seach app data standard path
|
// seach app data standard path
|
||||||
if (!foundLoggingRules && !isPortable() && dirParam.isEmpty()) {
|
if (!foundLoggingRules && !isPortable() && dirParam.isEmpty() && dataDirEnv.isEmpty()) {
|
||||||
logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile));
|
logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile));
|
||||||
if (!logRulesPath.isEmpty()) {
|
if (!logRulesPath.isEmpty()) {
|
||||||
qDebug() << "Found" << logRulesPath << "...";
|
qDebug() << "Found" << logRulesPath << "...";
|
||||||
|
|
@ -494,8 +517,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << ", (c) 2022-2023 "
|
qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME + ", " + QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", "));
|
||||||
<< qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", "));
|
|
||||||
qDebug() << "Version : " << BuildConfig.printableVersionString();
|
qDebug() << "Version : " << BuildConfig.printableVersionString();
|
||||||
qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM;
|
qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM;
|
||||||
qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
|
qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
|
||||||
|
|
@ -518,6 +540,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
}
|
}
|
||||||
if (!m_serverToJoin.isEmpty()) {
|
if (!m_serverToJoin.isEmpty()) {
|
||||||
qDebug() << "Address of server to join :" << m_serverToJoin;
|
qDebug() << "Address of server to join :" << m_serverToJoin;
|
||||||
|
} else if (!m_worldToJoin.isEmpty()) {
|
||||||
|
qDebug() << "Name of the world to join :" << m_worldToJoin;
|
||||||
}
|
}
|
||||||
qDebug() << "<> Paths set.";
|
qDebug() << "<> Paths set.";
|
||||||
}
|
}
|
||||||
|
|
@ -554,6 +578,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
|
|
||||||
m_settings->registerSetting("NumberOfConcurrentTasks", 10);
|
m_settings->registerSetting("NumberOfConcurrentTasks", 10);
|
||||||
m_settings->registerSetting("NumberOfConcurrentDownloads", 6);
|
m_settings->registerSetting("NumberOfConcurrentDownloads", 6);
|
||||||
|
m_settings->registerSetting("NumberOfManualRetries", 1);
|
||||||
|
m_settings->registerSetting("RequestTimeout", 60);
|
||||||
|
|
||||||
QString defaultMonospace;
|
QString defaultMonospace;
|
||||||
int defaultSize = 11;
|
int defaultSize = 11;
|
||||||
|
|
@ -588,6 +614,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
m_settings->registerSetting("IconsDir", "icons");
|
m_settings->registerSetting("IconsDir", "icons");
|
||||||
m_settings->registerSetting("DownloadsDir", QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
|
m_settings->registerSetting("DownloadsDir", QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
|
||||||
m_settings->registerSetting("DownloadsDirWatchRecursive", false);
|
m_settings->registerSetting("DownloadsDirWatchRecursive", false);
|
||||||
|
m_settings->registerSetting("MoveModsFromDownloadsDir", false);
|
||||||
|
m_settings->registerSetting("SkinsDir", "skins");
|
||||||
|
m_settings->registerSetting("JavaDir", "java");
|
||||||
|
|
||||||
// Editors
|
// Editors
|
||||||
m_settings->registerSetting("JsonEditor", QString());
|
m_settings->registerSetting("JsonEditor", QString());
|
||||||
|
|
@ -616,7 +645,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
|
|
||||||
// Memory
|
// Memory
|
||||||
m_settings->registerSetting({ "MinMemAlloc", "MinMemoryAlloc" }, 512);
|
m_settings->registerSetting({ "MinMemAlloc", "MinMemoryAlloc" }, 512);
|
||||||
m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, suitableMaxMem());
|
m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, SysInfo::suitableMaxMem());
|
||||||
m_settings->registerSetting("PermGen", 128);
|
m_settings->registerSetting("PermGen", 128);
|
||||||
|
|
||||||
// Java Settings
|
// Java Settings
|
||||||
|
|
@ -630,6 +659,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
m_settings->registerSetting("JvmArgs", "");
|
m_settings->registerSetting("JvmArgs", "");
|
||||||
m_settings->registerSetting("IgnoreJavaCompatibility", false);
|
m_settings->registerSetting("IgnoreJavaCompatibility", false);
|
||||||
m_settings->registerSetting("IgnoreJavaWizard", false);
|
m_settings->registerSetting("IgnoreJavaWizard", false);
|
||||||
|
auto defaultEnableAutoJava = m_settings->get("JavaPath").toString().isEmpty();
|
||||||
|
m_settings->registerSetting("AutomaticJavaSwitch", defaultEnableAutoJava);
|
||||||
|
m_settings->registerSetting("AutomaticJavaDownload", defaultEnableAutoJava);
|
||||||
|
m_settings->registerSetting("UserAskedAboutAutomaticJavaDownload", false);
|
||||||
|
|
||||||
// Legacy settings
|
// Legacy settings
|
||||||
m_settings->registerSetting("OnlineFixes", false);
|
m_settings->registerSetting("OnlineFixes", false);
|
||||||
|
|
@ -640,10 +673,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
m_settings->registerSetting("UseNativeGLFW", false);
|
m_settings->registerSetting("UseNativeGLFW", false);
|
||||||
m_settings->registerSetting("CustomGLFWPath", "");
|
m_settings->registerSetting("CustomGLFWPath", "");
|
||||||
|
|
||||||
// Peformance related options
|
// Performance related options
|
||||||
m_settings->registerSetting("EnableFeralGamemode", false);
|
m_settings->registerSetting("EnableFeralGamemode", false);
|
||||||
m_settings->registerSetting("EnableMangoHud", false);
|
m_settings->registerSetting("EnableMangoHud", false);
|
||||||
m_settings->registerSetting("UseDiscreteGpu", false);
|
m_settings->registerSetting("UseDiscreteGpu", false);
|
||||||
|
m_settings->registerSetting("UseZink", false);
|
||||||
|
|
||||||
// Game time
|
// Game time
|
||||||
m_settings->registerSetting("ShowGameTime", true);
|
m_settings->registerSetting("ShowGameTime", true);
|
||||||
|
|
@ -654,6 +688,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
// Minecraft mods
|
// Minecraft mods
|
||||||
m_settings->registerSetting("ModMetadataDisabled", false);
|
m_settings->registerSetting("ModMetadataDisabled", false);
|
||||||
m_settings->registerSetting("ModDependenciesDisabled", false);
|
m_settings->registerSetting("ModDependenciesDisabled", false);
|
||||||
|
m_settings->registerSetting("SkipModpackUpdatePrompt", false);
|
||||||
|
|
||||||
// Minecraft offline player name
|
// Minecraft offline player name
|
||||||
m_settings->registerSetting("LastOfflinePlayerName", "");
|
m_settings->registerSetting("LastOfflinePlayerName", "");
|
||||||
|
|
@ -667,6 +702,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
|
|
||||||
// The cat
|
// The cat
|
||||||
m_settings->registerSetting("TheCat", false);
|
m_settings->registerSetting("TheCat", false);
|
||||||
|
m_settings->registerSetting("CatOpacity", 100);
|
||||||
|
|
||||||
|
m_settings->registerSetting("StatusBarVisible", true);
|
||||||
|
|
||||||
m_settings->registerSetting("ToolbarsLocked", false);
|
m_settings->registerSetting("ToolbarsLocked", false);
|
||||||
|
|
||||||
|
|
@ -749,6 +787,12 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
m_settings->registerSetting("ModrinthToken", "");
|
m_settings->registerSetting("ModrinthToken", "");
|
||||||
m_settings->registerSetting("UserAgentOverride", "");
|
m_settings->registerSetting("UserAgentOverride", "");
|
||||||
|
|
||||||
|
// FTBApp instances
|
||||||
|
m_settings->registerSetting("FTBAppInstancesPath", "");
|
||||||
|
|
||||||
|
// Custom Technic Client ID
|
||||||
|
m_settings->registerSetting("TechnicClientID", "");
|
||||||
|
|
||||||
// Init page provider
|
// Init page provider
|
||||||
{
|
{
|
||||||
m_globalSettingsProvider = std::make_shared<GenericPageProvider>(tr("Settings"));
|
m_globalSettingsProvider = std::make_shared<GenericPageProvider>(tr("Settings"));
|
||||||
|
|
@ -756,8 +800,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
m_globalSettingsProvider->addPage<MinecraftPage>();
|
m_globalSettingsProvider->addPage<MinecraftPage>();
|
||||||
m_globalSettingsProvider->addPage<JavaPage>();
|
m_globalSettingsProvider->addPage<JavaPage>();
|
||||||
m_globalSettingsProvider->addPage<LanguagePage>();
|
m_globalSettingsProvider->addPage<LanguagePage>();
|
||||||
m_globalSettingsProvider->addPage<CustomCommandsPage>();
|
|
||||||
m_globalSettingsProvider->addPage<EnvironmentVariablesPage>();
|
|
||||||
m_globalSettingsProvider->addPage<ProxyPage>();
|
m_globalSettingsProvider->addPage<ProxyPage>();
|
||||||
m_globalSettingsProvider->addPage<ExternalToolsPage>();
|
m_globalSettingsProvider->addPage<ExternalToolsPage>();
|
||||||
m_globalSettingsProvider->addPage<AccountListPage>();
|
m_globalSettingsProvider->addPage<AccountListPage>();
|
||||||
|
|
@ -801,8 +843,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
":/icons/multimc/128x128/instances/", ":/icons/multimc/scalable/instances/" };
|
":/icons/multimc/128x128/instances/", ":/icons/multimc/scalable/instances/" };
|
||||||
m_icons.reset(new IconList(instFolders, setting->get().toString()));
|
m_icons.reset(new IconList(instFolders, setting->get().toString()));
|
||||||
connect(setting.get(), &Setting::SettingChanged,
|
connect(setting.get(), &Setting::SettingChanged,
|
||||||
[&](const Setting&, QVariant value) { m_icons->directoryChanged(value.toString()); });
|
[this](const Setting&, QVariant value) { m_icons->directoryChanged(value.toString()); });
|
||||||
qDebug() << "<> Instance icons intialized.";
|
qDebug() << "<> Instance icons initialized.";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Themes
|
// Themes
|
||||||
|
|
@ -839,25 +881,19 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
{
|
{
|
||||||
m_metacache.reset(new HttpMetaCache("metacache"));
|
m_metacache.reset(new HttpMetaCache("metacache"));
|
||||||
m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath());
|
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("libraries", QDir("libraries").absolutePath());
|
||||||
m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
|
|
||||||
m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").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("general", QDir("cache").absolutePath());
|
||||||
m_metacache->addBase("ATLauncherPacks", QDir("cache/ATLauncherPacks").absolutePath());
|
m_metacache->addBase("ATLauncherPacks", QDir("cache/ATLauncherPacks").absolutePath());
|
||||||
m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath());
|
m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath());
|
||||||
m_metacache->addBase("ModpacksCHPacks", QDir("cache/ModpacksCHPacks").absolutePath());
|
|
||||||
m_metacache->addBase("TechnicPacks", QDir("cache/TechnicPacks").absolutePath());
|
m_metacache->addBase("TechnicPacks", QDir("cache/TechnicPacks").absolutePath());
|
||||||
m_metacache->addBase("FlamePacks", QDir("cache/FlamePacks").absolutePath());
|
m_metacache->addBase("FlamePacks", QDir("cache/FlamePacks").absolutePath());
|
||||||
m_metacache->addBase("FlameMods", QDir("cache/FlameMods").absolutePath());
|
m_metacache->addBase("FlameMods", QDir("cache/FlameMods").absolutePath());
|
||||||
m_metacache->addBase("ModrinthPacks", QDir("cache/ModrinthPacks").absolutePath());
|
m_metacache->addBase("ModrinthPacks", QDir("cache/ModrinthPacks").absolutePath());
|
||||||
m_metacache->addBase("ModrinthModpacks", QDir("cache/ModrinthModpacks").absolutePath());
|
m_metacache->addBase("ModrinthModpacks", QDir("cache/ModrinthModpacks").absolutePath());
|
||||||
m_metacache->addBase("root", QDir::currentPath());
|
|
||||||
m_metacache->addBase("translations", QDir("translations").absolutePath());
|
m_metacache->addBase("translations", QDir("translations").absolutePath());
|
||||||
m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
|
|
||||||
m_metacache->addBase("meta", QDir("meta").absolutePath());
|
m_metacache->addBase("meta", QDir("meta").absolutePath());
|
||||||
|
m_metacache->addBase("java", QDir("cache/java").absolutePath());
|
||||||
m_metacache->Load();
|
m_metacache->Load();
|
||||||
qDebug() << "<> Cache initialized.";
|
qDebug() << "<> Cache initialized.";
|
||||||
}
|
}
|
||||||
|
|
@ -868,6 +904,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
// FIXME: what to do with these?
|
// FIXME: what to do with these?
|
||||||
m_profilers.insert("jprofiler", std::shared_ptr<BaseProfilerFactory>(new JProfilerFactory()));
|
m_profilers.insert("jprofiler", std::shared_ptr<BaseProfilerFactory>(new JProfilerFactory()));
|
||||||
m_profilers.insert("jvisualvm", std::shared_ptr<BaseProfilerFactory>(new JVisualVMFactory()));
|
m_profilers.insert("jvisualvm", std::shared_ptr<BaseProfilerFactory>(new JVisualVMFactory()));
|
||||||
|
m_profilers.insert("generic", std::shared_ptr<BaseProfilerFactory>(new GenericProfilerFactory()));
|
||||||
for (auto profiler : m_profilers.values()) {
|
for (auto profiler : m_profilers.values()) {
|
||||||
profiler->registerSettings(m_settings);
|
profiler->registerSettings(m_settings);
|
||||||
}
|
}
|
||||||
|
|
@ -936,8 +973,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
default: {
|
default: {
|
||||||
qDebug() << "Exiting because update lockfile is present";
|
qDebug() << "Exiting because update lockfile is present";
|
||||||
QMetaObject::invokeMethod(
|
QMetaObject::invokeMethod(this, []() { exit(1); }, Qt::QueuedConnection);
|
||||||
this, []() { exit(1); }, Qt::QueuedConnection);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -969,8 +1005,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
default: {
|
default: {
|
||||||
qDebug() << "Exiting because update lockfile is present";
|
qDebug() << "Exiting because update lockfile is present";
|
||||||
QMetaObject::invokeMethod(
|
QMetaObject::invokeMethod(this, []() { exit(1); }, Qt::QueuedConnection);
|
||||||
this, []() { exit(1); }, Qt::QueuedConnection);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -982,7 +1017,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
"\n"
|
"\n"
|
||||||
"You are now running %1 .\n"
|
"You are now running %1 .\n"
|
||||||
"Check the Prism Launcher updater log at: \n"
|
"Check the Prism Launcher updater log at: \n"
|
||||||
"%1\n"
|
"%2\n"
|
||||||
"for details.")
|
"for details.")
|
||||||
.arg(BuildConfig.printableVersionString())
|
.arg(BuildConfig.printableVersionString())
|
||||||
.arg(update_log_path);
|
.arg(update_log_path);
|
||||||
|
|
@ -998,7 +1033,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// notify user if /tmp is mounted with `noexec` (#1693)
|
// notify user if /tmp is mounted with `noexec` (#1693)
|
||||||
{
|
QString jvmArgs = m_settings->get("JvmArgs").toString();
|
||||||
|
if (jvmArgs.indexOf("java.io.tmpdir") == -1) { /* java.io.tmpdir is a valid workaround, so don't annoy */
|
||||||
bool is_tmp_noexec = false;
|
bool is_tmp_noexec = false;
|
||||||
|
|
||||||
#if defined(Q_OS_LINUX)
|
#if defined(Q_OS_LINUX)
|
||||||
|
|
@ -1018,7 +1054,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
if (is_tmp_noexec) {
|
if (is_tmp_noexec) {
|
||||||
auto infoMsg =
|
auto infoMsg =
|
||||||
tr("Your /tmp directory is currently mounted with the 'noexec' flag enabled.\n"
|
tr("Your /tmp directory is currently mounted with the 'noexec' flag enabled.\n"
|
||||||
"Some versions of Minecraft may not launch.\n");
|
"Some versions of Minecraft may not launch.\n"
|
||||||
|
"\n"
|
||||||
|
"You may solve this issue by remounting /tmp as 'exec' or setting "
|
||||||
|
"the java.io.tmpdir JVM argument to a writeable directory in a "
|
||||||
|
"filesystem where the 'exec' flag is set (e.g., /home/user/.local/tmp)\n");
|
||||||
auto msgBox = new QMessageBox(QMessageBox::Information, tr("Incompatible system configuration"), infoMsg, QMessageBox::Ok);
|
auto msgBox = new QMessageBox(QMessageBox::Information, tr("Incompatible system configuration"), infoMsg, QMessageBox::Ok);
|
||||||
msgBox->setDefaultButton(QMessageBox::Ok);
|
msgBox->setDefaultButton(QMessageBox::Ok);
|
||||||
msgBox->setAttribute(Qt::WA_DeleteOnClose);
|
msgBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
|
@ -1038,8 +1078,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
|
|
||||||
bool Application::createSetupWizard()
|
bool Application::createSetupWizard()
|
||||||
{
|
{
|
||||||
bool javaRequired = [&]() {
|
bool javaRequired = [this]() {
|
||||||
bool ignoreJavaWizard = m_settings->get("IgnoreJavaWizard").toBool();
|
if (BuildConfig.JAVA_DOWNLOADER_ENABLED && settings()->get("AutomaticJavaDownload").toBool()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool ignoreJavaWizard = settings()->get("IgnoreJavaWizard").toBool();
|
||||||
if (ignoreJavaWizard) {
|
if (ignoreJavaWizard) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -1051,18 +1094,17 @@ bool Application::createSetupWizard()
|
||||||
}
|
}
|
||||||
QString currentJavaPath = settings()->get("JavaPath").toString();
|
QString currentJavaPath = settings()->get("JavaPath").toString();
|
||||||
QString actualPath = FS::ResolveExecutable(currentJavaPath);
|
QString actualPath = FS::ResolveExecutable(currentJavaPath);
|
||||||
if (actualPath.isNull()) {
|
return actualPath.isNull();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}();
|
}();
|
||||||
|
bool askjava = BuildConfig.JAVA_DOWNLOADER_ENABLED && !javaRequired && !settings()->get("AutomaticJavaDownload").toBool() &&
|
||||||
|
!settings()->get("AutomaticJavaSwitch").toBool() && !settings()->get("UserAskedAboutAutomaticJavaDownload").toBool();
|
||||||
bool languageRequired = settings()->get("Language").toString().isEmpty();
|
bool languageRequired = settings()->get("Language").toString().isEmpty();
|
||||||
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
|
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
|
||||||
bool validWidgets = m_themeManager->isValidApplicationTheme(settings()->get("ApplicationTheme").toString());
|
bool validWidgets = m_themeManager->isValidApplicationTheme(settings()->get("ApplicationTheme").toString());
|
||||||
bool validIcons = m_themeManager->isValidIconTheme(settings()->get("IconTheme").toString());
|
bool validIcons = m_themeManager->isValidIconTheme(settings()->get("IconTheme").toString());
|
||||||
|
bool login = !m_accounts->anyAccountIsValid() && capabilities() & Application::SupportsMSA;
|
||||||
bool themeInterventionRequired = !validWidgets || !validIcons;
|
bool themeInterventionRequired = !validWidgets || !validIcons;
|
||||||
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
|
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired || askjava || login;
|
||||||
|
|
||||||
if (wizardRequired) {
|
if (wizardRequired) {
|
||||||
// set default theme after going into theme wizard
|
// set default theme after going into theme wizard
|
||||||
if (!validIcons)
|
if (!validIcons)
|
||||||
|
|
@ -1079,6 +1121,8 @@ bool Application::createSetupWizard()
|
||||||
|
|
||||||
if (javaRequired) {
|
if (javaRequired) {
|
||||||
m_setupWizard->addPage(new JavaWizardPage(m_setupWizard));
|
m_setupWizard->addPage(new JavaWizardPage(m_setupWizard));
|
||||||
|
} else if (askjava) {
|
||||||
|
m_setupWizard->addPage(new AutoJavaWizardPage(m_setupWizard));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pasteInterventionRequired) {
|
if (pasteInterventionRequired) {
|
||||||
|
|
@ -1089,11 +1133,14 @@ bool Application::createSetupWizard()
|
||||||
m_setupWizard->addPage(new ThemeWizardPage(m_setupWizard));
|
m_setupWizard->addPage(new ThemeWizardPage(m_setupWizard));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (login) {
|
||||||
|
m_setupWizard->addPage(new LoginWizardPage(m_setupWizard));
|
||||||
|
}
|
||||||
connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished);
|
connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished);
|
||||||
m_setupWizard->show();
|
m_setupWizard->show();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
return wizardRequired || login;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::updaterEnabled()
|
bool Application::updaterEnabled()
|
||||||
|
|
@ -1149,14 +1196,17 @@ void Application::performMainStartupAction()
|
||||||
if (!m_instanceIdToLaunch.isEmpty()) {
|
if (!m_instanceIdToLaunch.isEmpty()) {
|
||||||
auto inst = instances()->getInstanceById(m_instanceIdToLaunch);
|
auto inst = instances()->getInstanceById(m_instanceIdToLaunch);
|
||||||
if (inst) {
|
if (inst) {
|
||||||
MinecraftServerTargetPtr serverToJoin = nullptr;
|
MinecraftTarget::Ptr targetToJoin = nullptr;
|
||||||
MinecraftAccountPtr accountToUse = nullptr;
|
MinecraftAccountPtr accountToUse = nullptr;
|
||||||
|
|
||||||
qDebug() << "<> Instance" << m_instanceIdToLaunch << "launching";
|
qDebug() << "<> Instance" << m_instanceIdToLaunch << "launching";
|
||||||
if (!m_serverToJoin.isEmpty()) {
|
if (!m_serverToJoin.isEmpty()) {
|
||||||
// FIXME: validate the server string
|
// FIXME: validate the server string
|
||||||
serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(m_serverToJoin)));
|
targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(m_serverToJoin, false)));
|
||||||
qDebug() << " Launching with server" << m_serverToJoin;
|
qDebug() << " Launching with server" << m_serverToJoin;
|
||||||
|
} else if (!m_worldToJoin.isEmpty()) {
|
||||||
|
targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(m_worldToJoin, true)));
|
||||||
|
qDebug() << " Launching with world" << m_worldToJoin;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_profileToUse.isEmpty()) {
|
if (!m_profileToUse.isEmpty()) {
|
||||||
|
|
@ -1167,7 +1217,7 @@ void Application::performMainStartupAction()
|
||||||
qDebug() << " Launching with account" << m_profileToUse;
|
qDebug() << " Launching with account" << m_profileToUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
launch(inst, true, false, serverToJoin, accountToUse);
|
launch(inst, !m_offline, false, targetToJoin, accountToUse, m_offlineName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1198,6 +1248,12 @@ void Application::performMainStartupAction()
|
||||||
qDebug() << "<> Updater started.";
|
qDebug() << "<> Updater started.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{ // delete instances tmp dirctory
|
||||||
|
auto instDir = m_settings->get("InstanceDir").toString();
|
||||||
|
const QString tempRoot = FS::PathCombine(instDir, ".tmp");
|
||||||
|
FS::deletePath(tempRoot);
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_urlsToImport.isEmpty()) {
|
if (!m_urlsToImport.isEmpty()) {
|
||||||
qDebug() << "<> Importing from url:" << m_urlsToImport;
|
qDebug() << "<> Importing from url:" << m_urlsToImport;
|
||||||
m_mainWindow->processURLs(m_urlsToImport);
|
m_mainWindow->processURLs(m_urlsToImport);
|
||||||
|
|
@ -1229,16 +1285,23 @@ Application::~Application()
|
||||||
|
|
||||||
void Application::messageReceived(const QByteArray& message)
|
void Application::messageReceived(const QByteArray& message)
|
||||||
{
|
{
|
||||||
if (status() != Initialized) {
|
|
||||||
qDebug() << "Received message" << message << "while still initializing. It will be ignored.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ApplicationMessage received;
|
ApplicationMessage received;
|
||||||
received.parse(message);
|
received.parse(message);
|
||||||
|
|
||||||
auto& command = received.command;
|
auto& command = received.command;
|
||||||
|
|
||||||
|
if (status() != Initialized) {
|
||||||
|
bool isLoginAtempt = false;
|
||||||
|
if (command == "import") {
|
||||||
|
QString url = received.args["url"];
|
||||||
|
isLoginAtempt = !url.isEmpty() && normalizeImportUrl(url).scheme() == BuildConfig.LAUNCHER_APP_BINARY_NAME;
|
||||||
|
}
|
||||||
|
if (!isLoginAtempt) {
|
||||||
|
qDebug() << "Received message" << message << "while still initializing. It will be ignored.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (command == "activate") {
|
if (command == "activate") {
|
||||||
showMainWindow();
|
showMainWindow();
|
||||||
} else if (command == "import") {
|
} else if (command == "import") {
|
||||||
|
|
@ -1251,7 +1314,10 @@ void Application::messageReceived(const QByteArray& message)
|
||||||
} else if (command == "launch") {
|
} else if (command == "launch") {
|
||||||
QString id = received.args["id"];
|
QString id = received.args["id"];
|
||||||
QString server = received.args["server"];
|
QString server = received.args["server"];
|
||||||
|
QString world = received.args["world"];
|
||||||
QString profile = received.args["profile"];
|
QString profile = received.args["profile"];
|
||||||
|
bool offline = received.args["offline_enabled"] == "true";
|
||||||
|
QString offlineName = received.args["offline_name"];
|
||||||
|
|
||||||
InstancePtr instance;
|
InstancePtr instance;
|
||||||
if (!id.isEmpty()) {
|
if (!id.isEmpty()) {
|
||||||
|
|
@ -1265,11 +1331,12 @@ void Application::messageReceived(const QByteArray& message)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MinecraftServerTargetPtr serverObject = nullptr;
|
MinecraftTarget::Ptr serverObject = nullptr;
|
||||||
if (!server.isEmpty()) {
|
if (!server.isEmpty()) {
|
||||||
serverObject = std::make_shared<MinecraftServerTarget>(MinecraftServerTarget::parse(server));
|
serverObject = std::make_shared<MinecraftTarget>(MinecraftTarget::parse(server, false));
|
||||||
|
} else if (!world.isEmpty()) {
|
||||||
|
serverObject = std::make_shared<MinecraftTarget>(MinecraftTarget::parse(world, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
MinecraftAccountPtr accountObject;
|
MinecraftAccountPtr accountObject;
|
||||||
if (!profile.isEmpty()) {
|
if (!profile.isEmpty()) {
|
||||||
accountObject = accounts()->getAccountByProfileName(profile);
|
accountObject = accounts()->getAccountByProfileName(profile);
|
||||||
|
|
@ -1280,7 +1347,7 @@ void Application::messageReceived(const QByteArray& message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
launch(instance, true, false, serverObject, accountObject);
|
launch(instance, !offline, false, serverObject, accountObject, offlineName);
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Received invalid message" << message;
|
qWarning() << "Received invalid message" << message;
|
||||||
}
|
}
|
||||||
|
|
@ -1321,12 +1388,14 @@ bool Application::openJsonEditor(const QString& filename)
|
||||||
bool Application::launch(InstancePtr instance,
|
bool Application::launch(InstancePtr instance,
|
||||||
bool online,
|
bool online,
|
||||||
bool demo,
|
bool demo,
|
||||||
MinecraftServerTargetPtr serverToJoin,
|
MinecraftTarget::Ptr targetToJoin,
|
||||||
MinecraftAccountPtr accountToUse)
|
MinecraftAccountPtr accountToUse,
|
||||||
|
const QString& offlineName)
|
||||||
{
|
{
|
||||||
if (m_updateRunning) {
|
if (m_updateRunning) {
|
||||||
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
|
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
|
||||||
} else if (instance->canLaunch()) {
|
} else if (instance->canLaunch()) {
|
||||||
|
QMutexLocker locker(&m_instanceExtrasMutex);
|
||||||
auto& extras = m_instanceExtras[instance->id()];
|
auto& extras = m_instanceExtras[instance->id()];
|
||||||
auto window = extras.window;
|
auto window = extras.window;
|
||||||
if (window) {
|
if (window) {
|
||||||
|
|
@ -1340,8 +1409,9 @@ bool Application::launch(InstancePtr instance,
|
||||||
controller->setOnline(online);
|
controller->setOnline(online);
|
||||||
controller->setDemo(demo);
|
controller->setDemo(demo);
|
||||||
controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get());
|
controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get());
|
||||||
controller->setServerToJoin(serverToJoin);
|
controller->setTargetToJoin(targetToJoin);
|
||||||
controller->setAccountToUse(accountToUse);
|
controller->setAccountToUse(accountToUse);
|
||||||
|
controller->setOfflineName(offlineName);
|
||||||
if (window) {
|
if (window) {
|
||||||
controller->setParentWidget(window);
|
controller->setParentWidget(window);
|
||||||
} else if (m_mainWindow) {
|
} else if (m_mainWindow) {
|
||||||
|
|
@ -1351,7 +1421,7 @@ bool Application::launch(InstancePtr instance,
|
||||||
connect(controller.get(), &LaunchController::failed, this, &Application::controllerFailed);
|
connect(controller.get(), &LaunchController::failed, this, &Application::controllerFailed);
|
||||||
connect(controller.get(), &LaunchController::aborted, this, [this] { controllerFailed(tr("Aborted")); });
|
connect(controller.get(), &LaunchController::aborted, this, [this] { controllerFailed(tr("Aborted")); });
|
||||||
addRunningInstance();
|
addRunningInstance();
|
||||||
controller->start();
|
QMetaObject::invokeMethod(controller.get(), &Task::start, Qt::QueuedConnection);
|
||||||
return true;
|
return true;
|
||||||
} else if (instance->isRunning()) {
|
} else if (instance->isRunning()) {
|
||||||
showInstanceWindow(instance, "console");
|
showInstanceWindow(instance, "console");
|
||||||
|
|
@ -1369,9 +1439,11 @@ bool Application::kill(InstancePtr instance)
|
||||||
qWarning() << "Attempted to kill instance" << instance->id() << ", which isn't running.";
|
qWarning() << "Attempted to kill instance" << instance->id() << ", which isn't running.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
QMutexLocker locker(&m_instanceExtrasMutex);
|
||||||
auto& extras = m_instanceExtras[instance->id()];
|
auto& extras = m_instanceExtras[instance->id()];
|
||||||
// NOTE: copy of the shared pointer keeps it alive
|
// NOTE: copy of the shared pointer keeps it alive
|
||||||
auto controller = extras.controller;
|
auto controller = extras.controller;
|
||||||
|
locker.unlock();
|
||||||
if (controller) {
|
if (controller) {
|
||||||
return controller->abort();
|
return controller->abort();
|
||||||
}
|
}
|
||||||
|
|
@ -1425,12 +1497,14 @@ void Application::controllerSucceeded()
|
||||||
if (!controller)
|
if (!controller)
|
||||||
return;
|
return;
|
||||||
auto id = controller->id();
|
auto id = controller->id();
|
||||||
|
|
||||||
|
QMutexLocker locker(&m_instanceExtrasMutex);
|
||||||
auto& extras = m_instanceExtras[id];
|
auto& extras = m_instanceExtras[id];
|
||||||
|
|
||||||
// on success, do...
|
// on success, do...
|
||||||
if (controller->instance()->settings()->get("AutoCloseConsole").toBool()) {
|
if (controller->instance()->settings()->get("AutoCloseConsole").toBool()) {
|
||||||
if (extras.window) {
|
if (extras.window) {
|
||||||
extras.window->close();
|
QMetaObject::invokeMethod(extras.window, &QWidget::close, Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
extras.controller.reset();
|
extras.controller.reset();
|
||||||
|
|
@ -1450,6 +1524,7 @@ void Application::controllerFailed(const QString& error)
|
||||||
if (!controller)
|
if (!controller)
|
||||||
return;
|
return;
|
||||||
auto id = controller->id();
|
auto id = controller->id();
|
||||||
|
QMutexLocker locker(&m_instanceExtrasMutex);
|
||||||
auto& extras = m_instanceExtras[id];
|
auto& extras = m_instanceExtras[id];
|
||||||
|
|
||||||
// on failure, do... nothing
|
// on failure, do... nothing
|
||||||
|
|
@ -1507,10 +1582,22 @@ InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString pa
|
||||||
if (!instance)
|
if (!instance)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
auto id = instance->id();
|
auto id = instance->id();
|
||||||
|
QMutexLocker locker(&m_instanceExtrasMutex);
|
||||||
auto& extras = m_instanceExtras[id];
|
auto& extras = m_instanceExtras[id];
|
||||||
auto& window = extras.window;
|
auto& window = extras.window;
|
||||||
|
|
||||||
if (window) {
|
if (window) {
|
||||||
|
// If the window is minimized on macOS or Windows, activate and bring it up
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
if (window->isMinimized()) {
|
||||||
|
window->setWindowState(window->windowState() & ~Qt::WindowMinimized);
|
||||||
|
}
|
||||||
|
#elif defined(Q_OS_WIN)
|
||||||
|
if (window->isMinimized()) {
|
||||||
|
window->showNormal();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
window->raise();
|
window->raise();
|
||||||
window->activateWindow();
|
window->activateWindow();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1518,6 +1605,7 @@ InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString pa
|
||||||
m_openWindows++;
|
m_openWindows++;
|
||||||
connect(window, &InstanceWindow::isClosing, this, &Application::on_windowClose);
|
connect(window, &InstanceWindow::isClosing, this, &Application::on_windowClose);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!page.isEmpty()) {
|
if (!page.isEmpty()) {
|
||||||
window->selectPage(page);
|
window->selectPage(page);
|
||||||
}
|
}
|
||||||
|
|
@ -1532,6 +1620,7 @@ void Application::on_windowClose()
|
||||||
m_openWindows--;
|
m_openWindows--;
|
||||||
auto instWindow = qobject_cast<InstanceWindow*>(QObject::sender());
|
auto instWindow = qobject_cast<InstanceWindow*>(QObject::sender());
|
||||||
if (instWindow) {
|
if (instWindow) {
|
||||||
|
QMutexLocker locker(&m_instanceExtrasMutex);
|
||||||
auto& extras = m_instanceExtras[instWindow->instanceId()];
|
auto& extras = m_instanceExtras[instWindow->instanceId()];
|
||||||
extras.window = nullptr;
|
extras.window = nullptr;
|
||||||
if (extras.controller) {
|
if (extras.controller) {
|
||||||
|
|
@ -1646,8 +1735,7 @@ QString Application::getJarPath(QString jarFile)
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||||
FS::PathCombine(m_rootPath, "share", BuildConfig.LAUNCHER_NAME),
|
FS::PathCombine(m_rootPath, "share", BuildConfig.LAUNCHER_NAME),
|
||||||
#endif
|
#endif
|
||||||
FS::PathCombine(m_rootPath, "jars"),
|
FS::PathCombine(m_rootPath, "jars"), FS::PathCombine(applicationDirPath(), "jars"),
|
||||||
FS::PathCombine(applicationDirPath(), "jars"),
|
|
||||||
FS::PathCombine(applicationDirPath(), "..", "jars") // from inside build dir, for debuging
|
FS::PathCombine(applicationDirPath(), "..", "jars") // from inside build dir, for debuging
|
||||||
};
|
};
|
||||||
for (QString p : potentialPaths) {
|
for (QString p : potentialPaths) {
|
||||||
|
|
@ -1708,20 +1796,6 @@ QString Application::getUserAgentUncached()
|
||||||
return BuildConfig.USER_AGENT_UNCACHED;
|
return BuildConfig.USER_AGENT_UNCACHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Application::suitableMaxMem()
|
|
||||||
{
|
|
||||||
float totalRAM = (float)Sys::getSystemRam() / (float)Sys::mebibyte;
|
|
||||||
int maxMemoryAlloc;
|
|
||||||
|
|
||||||
// If totalRAM < 6GB, use (totalRAM / 1.5), else 4GB
|
|
||||||
if (totalRAM < (4096 * 1.5))
|
|
||||||
maxMemoryAlloc = (int)(totalRAM / 1.5);
|
|
||||||
else
|
|
||||||
maxMemoryAlloc = 4096;
|
|
||||||
|
|
||||||
return maxMemoryAlloc;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Application::handleDataMigration(const QString& currentData,
|
bool Application::handleDataMigration(const QString& currentData,
|
||||||
const QString& oldData,
|
const QString& oldData,
|
||||||
const QString& name,
|
const QString& name,
|
||||||
|
|
@ -1794,7 +1868,7 @@ bool Application::handleDataMigration(const QString& currentData,
|
||||||
matcher->add(std::make_shared<SimplePrefixMatcher>("themes/"));
|
matcher->add(std::make_shared<SimplePrefixMatcher>("themes/"));
|
||||||
|
|
||||||
ProgressDialog diag;
|
ProgressDialog diag;
|
||||||
DataMigrationTask task(nullptr, oldData, currentData, matcher);
|
DataMigrationTask task(oldData, currentData, matcher);
|
||||||
if (diag.execWithTask(&task)) {
|
if (diag.execWithTask(&task)) {
|
||||||
qDebug() << "<> Migration succeeded";
|
qDebug() << "<> Migration succeeded";
|
||||||
setDoNotMigrate();
|
setDoNotMigrate();
|
||||||
|
|
@ -1828,3 +1902,36 @@ QUrl Application::normalizeImportUrl(QString const& url)
|
||||||
return QUrl::fromUserInput(url);
|
return QUrl::fromUserInput(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QString Application::javaPath()
|
||||||
|
{
|
||||||
|
return m_settings->get("JavaDir").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::addQSavePath(QString path)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_qsaveResourcesMutex);
|
||||||
|
m_qsaveResources[path] = m_qsaveResources.value(path, 0) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::removeQSavePath(QString path)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_qsaveResourcesMutex);
|
||||||
|
auto count = m_qsaveResources.value(path, 0) - 1;
|
||||||
|
if (count <= 0) {
|
||||||
|
m_qsaveResources.remove(path);
|
||||||
|
} else {
|
||||||
|
m_qsaveResources[path] = count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Application::checkQSavePath(QString path)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_qsaveResourcesMutex);
|
||||||
|
for (auto partialPath : m_qsaveResources.keys()) {
|
||||||
|
if (path.startsWith(partialPath) && m_qsaveResources.value(partialPath, 0) > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
@ -42,13 +42,13 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFlag>
|
#include <QFlag>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
#include <QMutex>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <BaseInstance.h>
|
#include <BaseInstance.h>
|
||||||
|
|
||||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
#include "minecraft/launch/MinecraftTarget.h"
|
||||||
#include "ui/themes/CatPack.h"
|
|
||||||
|
|
||||||
class LaunchController;
|
class LaunchController;
|
||||||
class LocalPeer;
|
class LocalPeer;
|
||||||
|
|
@ -82,6 +82,12 @@ class Index;
|
||||||
#endif
|
#endif
|
||||||
#define APPLICATION (static_cast<Application*>(QCoreApplication::instance()))
|
#define APPLICATION (static_cast<Application*>(QCoreApplication::instance()))
|
||||||
|
|
||||||
|
// Used for checking if is a test
|
||||||
|
#if defined(APPLICATION_DYN)
|
||||||
|
#undef APPLICATION_DYN
|
||||||
|
#endif
|
||||||
|
#define APPLICATION_DYN (dynamic_cast<Application*>(QCoreApplication::instance()))
|
||||||
|
|
||||||
class Application : public QApplication {
|
class Application : public QApplication {
|
||||||
// friends for the purpose of limiting access to deprecated stuff
|
// friends for the purpose of limiting access to deprecated stuff
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
@ -162,6 +168,9 @@ class Application : public QApplication {
|
||||||
/// the data path the application is using
|
/// the data path the application is using
|
||||||
const QString& dataRoot() { return m_dataPath; }
|
const QString& dataRoot() { return m_dataPath; }
|
||||||
|
|
||||||
|
/// the java installed path the application is using
|
||||||
|
const QString javaPath();
|
||||||
|
|
||||||
bool isPortable() { return m_portable; }
|
bool isPortable() { return m_portable; }
|
||||||
|
|
||||||
const Capabilities capabilities() { return m_capabilities; }
|
const Capabilities capabilities() { return m_capabilities; }
|
||||||
|
|
@ -180,8 +189,6 @@ class Application : public QApplication {
|
||||||
|
|
||||||
void ShowGlobalSettings(class QWidget* parent, QString open_page = QString());
|
void ShowGlobalSettings(class QWidget* parent, QString open_page = QString());
|
||||||
|
|
||||||
int suitableMaxMem();
|
|
||||||
|
|
||||||
bool updaterEnabled();
|
bool updaterEnabled();
|
||||||
QString updaterBinaryName();
|
QString updaterBinaryName();
|
||||||
|
|
||||||
|
|
@ -193,6 +200,8 @@ class Application : public QApplication {
|
||||||
void globalSettingsClosed();
|
void globalSettingsClosed();
|
||||||
int currentCatChanged(int index);
|
int currentCatChanged(int index);
|
||||||
|
|
||||||
|
void oauthReplyRecieved(QVariantMap);
|
||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
void clickedOnDock();
|
void clickedOnDock();
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -201,8 +210,9 @@ class Application : public QApplication {
|
||||||
bool launch(InstancePtr instance,
|
bool launch(InstancePtr instance,
|
||||||
bool online = true,
|
bool online = true,
|
||||||
bool demo = false,
|
bool demo = false,
|
||||||
MinecraftServerTargetPtr serverToJoin = nullptr,
|
MinecraftTarget::Ptr targetToJoin = nullptr,
|
||||||
MinecraftAccountPtr accountToUse = nullptr);
|
MinecraftAccountPtr accountToUse = nullptr,
|
||||||
|
const QString& offlineName = QString());
|
||||||
bool kill(InstancePtr instance);
|
bool kill(InstancePtr instance);
|
||||||
void closeCurrentWindow();
|
void closeCurrentWindow();
|
||||||
|
|
||||||
|
|
@ -270,6 +280,7 @@ class Application : public QApplication {
|
||||||
shared_qobject_ptr<LaunchController> controller;
|
shared_qobject_ptr<LaunchController> controller;
|
||||||
};
|
};
|
||||||
std::map<QString, InstanceXtras> m_instanceExtras;
|
std::map<QString, InstanceXtras> m_instanceExtras;
|
||||||
|
mutable QMutex m_instanceExtrasMutex;
|
||||||
|
|
||||||
// main state variables
|
// main state variables
|
||||||
size_t m_openWindows = 0;
|
size_t m_openWindows = 0;
|
||||||
|
|
@ -289,9 +300,21 @@ class Application : public QApplication {
|
||||||
QString m_detectedOpenALPath;
|
QString m_detectedOpenALPath;
|
||||||
QString m_instanceIdToLaunch;
|
QString m_instanceIdToLaunch;
|
||||||
QString m_serverToJoin;
|
QString m_serverToJoin;
|
||||||
|
QString m_worldToJoin;
|
||||||
QString m_profileToUse;
|
QString m_profileToUse;
|
||||||
|
bool m_offline = false;
|
||||||
|
QString m_offlineName;
|
||||||
bool m_liveCheck = false;
|
bool m_liveCheck = false;
|
||||||
QList<QUrl> m_urlsToImport;
|
QList<QUrl> m_urlsToImport;
|
||||||
QString m_instanceIdToShowWindowOf;
|
QString m_instanceIdToShowWindowOf;
|
||||||
std::unique_ptr<QFile> logFile;
|
std::unique_ptr<QFile> logFile;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void addQSavePath(QString);
|
||||||
|
void removeQSavePath(QString);
|
||||||
|
bool checkQSavePath(QString);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QHash<QString, int> m_qsaveResources;
|
||||||
|
mutable QMutex m_qsaveResourcesMutex;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
||||||
#include "BaseInstaller.h"
|
#include "BaseInstaller.h"
|
||||||
|
#include "FileSystem.h"
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include "minecraft/MinecraftInstance.h"
|
||||||
|
|
||||||
BaseInstaller::BaseInstaller() {}
|
BaseInstaller::BaseInstaller() {}
|
||||||
|
|
@ -42,7 +43,7 @@ bool BaseInstaller::add(MinecraftInstance* to)
|
||||||
|
|
||||||
bool BaseInstaller::remove(MinecraftInstance* from)
|
bool BaseInstaller::remove(MinecraftInstance* from)
|
||||||
{
|
{
|
||||||
return QFile::remove(filename(from->instanceRoot()));
|
return FS::deletePath(filename(from->instanceRoot()));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BaseInstaller::filename(const QString& root) const
|
QString BaseInstaller::filename(const QString& root) const
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ class BaseVersion;
|
||||||
class BaseInstaller {
|
class BaseInstaller {
|
||||||
public:
|
public:
|
||||||
BaseInstaller();
|
BaseInstaller();
|
||||||
virtual ~BaseInstaller(){};
|
virtual ~BaseInstaller() {};
|
||||||
bool isApplied(MinecraftInstance* on);
|
bool isApplied(MinecraftInstance* on);
|
||||||
|
|
||||||
virtual bool add(MinecraftInstance* to);
|
virtual bool add(MinecraftInstance* to);
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,8 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
|
||||||
|
|
||||||
m_settings->registerSetting("lastLaunchTime", 0);
|
m_settings->registerSetting("lastLaunchTime", 0);
|
||||||
m_settings->registerSetting("totalTimePlayed", 0);
|
m_settings->registerSetting("totalTimePlayed", 0);
|
||||||
|
if (m_settings->get("totalTimePlayed").toLongLong() < 0)
|
||||||
|
m_settings->reset("totalTimePlayed");
|
||||||
m_settings->registerSetting("lastTimePlayed", 0);
|
m_settings->registerSetting("lastTimePlayed", 0);
|
||||||
|
|
||||||
m_settings->registerSetting("linkedInstances", "[]");
|
m_settings->registerSetting("linkedInstances", "[]");
|
||||||
|
|
@ -267,13 +269,18 @@ void BaseInstance::setRunning(bool running)
|
||||||
|
|
||||||
m_isRunning = running;
|
m_isRunning = running;
|
||||||
|
|
||||||
if (!m_settings->get("RecordGameTime").toBool()) {
|
emit runningStatusChanged(running);
|
||||||
emit runningStatusChanged(running);
|
}
|
||||||
|
|
||||||
|
void BaseInstance::setMinecraftRunning(bool running)
|
||||||
|
{
|
||||||
|
if (!settings()->get("RecordGameTime").toBool()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (running) {
|
if (running) {
|
||||||
m_timeStarted = QDateTime::currentDateTime();
|
m_timeStarted = QDateTime::currentDateTime();
|
||||||
|
setLastLaunch(m_timeStarted.toMSecsSinceEpoch());
|
||||||
} else {
|
} else {
|
||||||
QDateTime timeEnded = QDateTime::currentDateTime();
|
QDateTime timeEnded = QDateTime::currentDateTime();
|
||||||
|
|
||||||
|
|
@ -283,8 +290,6 @@ void BaseInstance::setRunning(bool running)
|
||||||
|
|
||||||
emit propertiesChanged(this);
|
emit propertiesChanged(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit runningStatusChanged(running);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t BaseInstance::totalTimePlayed() const
|
int64_t BaseInstance::totalTimePlayed() const
|
||||||
|
|
@ -406,3 +411,8 @@ void BaseInstance::updateRuntimeContext()
|
||||||
{
|
{
|
||||||
// NOOP
|
// NOOP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BaseInstance::isLegacy()
|
||||||
|
{
|
||||||
|
return traits().contains("legacyLaunch") || traits().contains("alphaLaunch");
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
#include "net/Mode.h"
|
#include "net/Mode.h"
|
||||||
|
|
||||||
#include "RuntimeContext.h"
|
#include "RuntimeContext.h"
|
||||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
#include "minecraft/launch/MinecraftTarget.h"
|
||||||
|
|
||||||
class QDir;
|
class QDir;
|
||||||
class Task;
|
class Task;
|
||||||
|
|
@ -104,6 +104,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||||
/// be unique.
|
/// be unique.
|
||||||
virtual QString id() const;
|
virtual QString id() const;
|
||||||
|
|
||||||
|
void setMinecraftRunning(bool running);
|
||||||
void setRunning(bool running);
|
void setRunning(bool running);
|
||||||
bool isRunning() const;
|
bool isRunning() const;
|
||||||
int64_t totalTimePlayed() const;
|
int64_t totalTimePlayed() const;
|
||||||
|
|
@ -180,10 +181,10 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||||
virtual void loadSpecificSettings() = 0;
|
virtual void loadSpecificSettings() = 0;
|
||||||
|
|
||||||
/// returns a valid update task
|
/// returns a valid update task
|
||||||
virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0;
|
virtual QList<Task::Ptr> createUpdateTask() = 0;
|
||||||
|
|
||||||
/// returns a valid launcher (task container)
|
/// returns a valid launcher (task container)
|
||||||
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0;
|
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) = 0;
|
||||||
|
|
||||||
/// returns the current launch task (if any)
|
/// returns the current launch task (if any)
|
||||||
shared_qobject_ptr<LaunchTask> getLaunchTask();
|
shared_qobject_ptr<LaunchTask> getLaunchTask();
|
||||||
|
|
@ -214,7 +215,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||||
|
|
||||||
virtual QString typeName() const = 0;
|
virtual QString typeName() const = 0;
|
||||||
|
|
||||||
void updateRuntimeContext();
|
virtual void updateRuntimeContext();
|
||||||
RuntimeContext runtimeContext() const { return m_runtimeContext; }
|
RuntimeContext runtimeContext() const { return m_runtimeContext; }
|
||||||
|
|
||||||
bool hasVersionBroken() const { return m_hasBrokenVersion; }
|
bool hasVersionBroken() const { return m_hasBrokenVersion; }
|
||||||
|
|
@ -255,7 +256,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||||
/**
|
/**
|
||||||
* 'print' a verbose description of the instance into a QStringList
|
* 'print' a verbose description of the instance into a QStringList
|
||||||
*/
|
*/
|
||||||
virtual QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) = 0;
|
virtual QStringList verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) = 0;
|
||||||
|
|
||||||
Status currentStatus() const;
|
Status currentStatus() const;
|
||||||
|
|
||||||
|
|
@ -268,6 +269,8 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||||
bool removeLinkedInstanceId(const QString& id);
|
bool removeLinkedInstanceId(const QString& id);
|
||||||
bool isLinkedToInstanceId(const QString& id) const;
|
bool isLinkedToInstanceId(const QString& id) const;
|
||||||
|
|
||||||
|
bool isLegacy();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void changeStatus(Status newStatus);
|
void changeStatus(Status newStatus);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,14 @@ QVariant BaseVersionList::data(const QModelIndex& index, int role) const
|
||||||
case TypeRole:
|
case TypeRole:
|
||||||
return version->typeString();
|
return version->typeString();
|
||||||
|
|
||||||
|
case JavaMajorRole: {
|
||||||
|
auto major = version->name();
|
||||||
|
if (major.startsWith("java")) {
|
||||||
|
major = "Java " + major.mid(4);
|
||||||
|
}
|
||||||
|
return major;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
@ -110,6 +118,8 @@ QHash<int, QByteArray> BaseVersionList::roleNames() const
|
||||||
roles.insert(TypeRole, "type");
|
roles.insert(TypeRole, "type");
|
||||||
roles.insert(BranchRole, "branch");
|
roles.insert(BranchRole, "branch");
|
||||||
roles.insert(PathRole, "path");
|
roles.insert(PathRole, "path");
|
||||||
roles.insert(ArchitectureRole, "architecture");
|
roles.insert(JavaNameRole, "javaName");
|
||||||
|
roles.insert(CPUArchitectureRole, "architecture");
|
||||||
|
roles.insert(JavaMajorRole, "javaMajor");
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,9 @@ class BaseVersionList : public QAbstractListModel {
|
||||||
TypeRole,
|
TypeRole,
|
||||||
BranchRole,
|
BranchRole,
|
||||||
PathRole,
|
PathRole,
|
||||||
ArchitectureRole,
|
JavaNameRole,
|
||||||
|
JavaMajorRole,
|
||||||
|
CPUArchitectureRole,
|
||||||
SortRole
|
SortRole
|
||||||
};
|
};
|
||||||
using RoleList = QList<int>;
|
using RoleList = QList<int>;
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,13 @@ set(CORE_SOURCES
|
||||||
NullInstance.h
|
NullInstance.h
|
||||||
MMCZip.h
|
MMCZip.h
|
||||||
MMCZip.cpp
|
MMCZip.cpp
|
||||||
|
Untar.h
|
||||||
|
Untar.cpp
|
||||||
StringUtils.h
|
StringUtils.h
|
||||||
StringUtils.cpp
|
StringUtils.cpp
|
||||||
QVariantUtils.h
|
QVariantUtils.h
|
||||||
RuntimeContext.h
|
RuntimeContext.h
|
||||||
|
PSaveFile.h
|
||||||
|
|
||||||
# Basic instance manipulation tasks (derived from InstanceTask)
|
# Basic instance manipulation tasks (derived from InstanceTask)
|
||||||
InstanceCreationTask.h
|
InstanceCreationTask.h
|
||||||
|
|
@ -126,7 +129,6 @@ set(NET_SOURCES
|
||||||
net/MetaCacheSink.h
|
net/MetaCacheSink.h
|
||||||
net/Logging.h
|
net/Logging.h
|
||||||
net/Logging.cpp
|
net/Logging.cpp
|
||||||
net/NetAction.h
|
|
||||||
net/NetJob.cpp
|
net/NetJob.cpp
|
||||||
net/NetJob.h
|
net/NetJob.h
|
||||||
net/NetUtils.h
|
net/NetUtils.h
|
||||||
|
|
@ -139,7 +141,6 @@ set(NET_SOURCES
|
||||||
net/HeaderProxy.h
|
net/HeaderProxy.h
|
||||||
net/RawHeaderProxy.h
|
net/RawHeaderProxy.h
|
||||||
net/ApiHeaderProxy.h
|
net/ApiHeaderProxy.h
|
||||||
net/StaticHeaderProxy.h
|
|
||||||
net/ApiDownload.h
|
net/ApiDownload.h
|
||||||
net/ApiDownload.cpp
|
net/ApiDownload.cpp
|
||||||
net/ApiUpload.cpp
|
net/ApiUpload.cpp
|
||||||
|
|
@ -160,16 +161,18 @@ set(LAUNCH_SOURCES
|
||||||
launch/steps/PreLaunchCommand.h
|
launch/steps/PreLaunchCommand.h
|
||||||
launch/steps/TextPrint.cpp
|
launch/steps/TextPrint.cpp
|
||||||
launch/steps/TextPrint.h
|
launch/steps/TextPrint.h
|
||||||
launch/steps/Update.cpp
|
|
||||||
launch/steps/Update.h
|
|
||||||
launch/steps/QuitAfterGameStop.cpp
|
launch/steps/QuitAfterGameStop.cpp
|
||||||
launch/steps/QuitAfterGameStop.h
|
launch/steps/QuitAfterGameStop.h
|
||||||
|
launch/steps/PrintServers.cpp
|
||||||
|
launch/steps/PrintServers.h
|
||||||
launch/LaunchStep.cpp
|
launch/LaunchStep.cpp
|
||||||
launch/LaunchStep.h
|
launch/LaunchStep.h
|
||||||
launch/LaunchTask.cpp
|
launch/LaunchTask.cpp
|
||||||
launch/LaunchTask.h
|
launch/LaunchTask.h
|
||||||
launch/LogModel.cpp
|
launch/LogModel.cpp
|
||||||
launch/LogModel.h
|
launch/LogModel.h
|
||||||
|
launch/TaskStepWrapper.cpp
|
||||||
|
launch/TaskStepWrapper.h
|
||||||
)
|
)
|
||||||
|
|
||||||
# Old update system
|
# Old update system
|
||||||
|
|
@ -205,33 +208,27 @@ set(ICONS_SOURCES
|
||||||
|
|
||||||
# Support for Minecraft instances and launch
|
# Support for Minecraft instances and launch
|
||||||
set(MINECRAFT_SOURCES
|
set(MINECRAFT_SOURCES
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
minecraft/Logging.h
|
||||||
|
minecraft/Logging.cpp
|
||||||
|
|
||||||
# Minecraft support
|
# Minecraft support
|
||||||
minecraft/auth/AccountData.cpp
|
minecraft/auth/AccountData.cpp
|
||||||
minecraft/auth/AccountData.h
|
minecraft/auth/AccountData.h
|
||||||
minecraft/auth/AccountList.cpp
|
minecraft/auth/AccountList.cpp
|
||||||
minecraft/auth/AccountList.h
|
minecraft/auth/AccountList.h
|
||||||
minecraft/auth/AccountTask.cpp
|
|
||||||
minecraft/auth/AccountTask.h
|
|
||||||
minecraft/auth/AuthRequest.cpp
|
|
||||||
minecraft/auth/AuthRequest.h
|
|
||||||
minecraft/auth/AuthSession.cpp
|
minecraft/auth/AuthSession.cpp
|
||||||
minecraft/auth/AuthSession.h
|
minecraft/auth/AuthSession.h
|
||||||
minecraft/auth/AuthStep.cpp
|
|
||||||
minecraft/auth/AuthStep.h
|
minecraft/auth/AuthStep.h
|
||||||
minecraft/auth/MinecraftAccount.cpp
|
minecraft/auth/MinecraftAccount.cpp
|
||||||
minecraft/auth/MinecraftAccount.h
|
minecraft/auth/MinecraftAccount.h
|
||||||
minecraft/auth/Parsers.cpp
|
minecraft/auth/Parsers.cpp
|
||||||
minecraft/auth/Parsers.h
|
minecraft/auth/Parsers.h
|
||||||
|
|
||||||
minecraft/auth/flows/AuthFlow.cpp
|
minecraft/auth/AuthFlow.cpp
|
||||||
minecraft/auth/flows/AuthFlow.h
|
minecraft/auth/AuthFlow.h
|
||||||
minecraft/auth/flows/MSA.cpp
|
|
||||||
minecraft/auth/flows/MSA.h
|
|
||||||
minecraft/auth/flows/Offline.cpp
|
|
||||||
minecraft/auth/flows/Offline.h
|
|
||||||
|
|
||||||
minecraft/auth/steps/OfflineStep.cpp
|
|
||||||
minecraft/auth/steps/OfflineStep.h
|
|
||||||
minecraft/auth/steps/EntitlementsStep.cpp
|
minecraft/auth/steps/EntitlementsStep.cpp
|
||||||
minecraft/auth/steps/EntitlementsStep.h
|
minecraft/auth/steps/EntitlementsStep.h
|
||||||
minecraft/auth/steps/GetSkinStep.cpp
|
minecraft/auth/steps/GetSkinStep.cpp
|
||||||
|
|
@ -240,6 +237,8 @@ set(MINECRAFT_SOURCES
|
||||||
minecraft/auth/steps/LauncherLoginStep.h
|
minecraft/auth/steps/LauncherLoginStep.h
|
||||||
minecraft/auth/steps/MinecraftProfileStep.cpp
|
minecraft/auth/steps/MinecraftProfileStep.cpp
|
||||||
minecraft/auth/steps/MinecraftProfileStep.h
|
minecraft/auth/steps/MinecraftProfileStep.h
|
||||||
|
minecraft/auth/steps/MSADeviceCodeStep.cpp
|
||||||
|
minecraft/auth/steps/MSADeviceCodeStep.h
|
||||||
minecraft/auth/steps/MSAStep.cpp
|
minecraft/auth/steps/MSAStep.cpp
|
||||||
minecraft/auth/steps/MSAStep.h
|
minecraft/auth/steps/MSAStep.h
|
||||||
minecraft/auth/steps/XboxAuthorizationStep.cpp
|
minecraft/auth/steps/XboxAuthorizationStep.cpp
|
||||||
|
|
@ -271,8 +270,8 @@ set(MINECRAFT_SOURCES
|
||||||
minecraft/launch/ExtractNatives.h
|
minecraft/launch/ExtractNatives.h
|
||||||
minecraft/launch/LauncherPartLaunch.cpp
|
minecraft/launch/LauncherPartLaunch.cpp
|
||||||
minecraft/launch/LauncherPartLaunch.h
|
minecraft/launch/LauncherPartLaunch.h
|
||||||
minecraft/launch/MinecraftServerTarget.cpp
|
minecraft/launch/MinecraftTarget.cpp
|
||||||
minecraft/launch/MinecraftServerTarget.h
|
minecraft/launch/MinecraftTarget.h
|
||||||
minecraft/launch/PrintInstanceInfo.cpp
|
minecraft/launch/PrintInstanceInfo.cpp
|
||||||
minecraft/launch/PrintInstanceInfo.h
|
minecraft/launch/PrintInstanceInfo.h
|
||||||
minecraft/launch/ReconstructAssets.cpp
|
minecraft/launch/ReconstructAssets.cpp
|
||||||
|
|
@ -281,6 +280,8 @@ set(MINECRAFT_SOURCES
|
||||||
minecraft/launch/ScanModFolders.h
|
minecraft/launch/ScanModFolders.h
|
||||||
minecraft/launch/VerifyJavaInstall.cpp
|
minecraft/launch/VerifyJavaInstall.cpp
|
||||||
minecraft/launch/VerifyJavaInstall.h
|
minecraft/launch/VerifyJavaInstall.h
|
||||||
|
minecraft/launch/AutoInstallJava.cpp
|
||||||
|
minecraft/launch/AutoInstallJava.h
|
||||||
|
|
||||||
minecraft/GradleSpecifier.h
|
minecraft/GradleSpecifier.h
|
||||||
minecraft/MinecraftInstance.cpp
|
minecraft/MinecraftInstance.cpp
|
||||||
|
|
@ -295,8 +296,6 @@ set(MINECRAFT_SOURCES
|
||||||
minecraft/ComponentUpdateTask.h
|
minecraft/ComponentUpdateTask.h
|
||||||
minecraft/MinecraftLoadAndCheck.h
|
minecraft/MinecraftLoadAndCheck.h
|
||||||
minecraft/MinecraftLoadAndCheck.cpp
|
minecraft/MinecraftLoadAndCheck.cpp
|
||||||
minecraft/MinecraftUpdate.h
|
|
||||||
minecraft/MinecraftUpdate.cpp
|
|
||||||
minecraft/MojangVersionFormat.cpp
|
minecraft/MojangVersionFormat.cpp
|
||||||
minecraft/MojangVersionFormat.h
|
minecraft/MojangVersionFormat.h
|
||||||
minecraft/Rule.cpp
|
minecraft/Rule.cpp
|
||||||
|
|
@ -346,13 +345,12 @@ set(MINECRAFT_SOURCES
|
||||||
minecraft/mod/TexturePackFolderModel.h
|
minecraft/mod/TexturePackFolderModel.h
|
||||||
minecraft/mod/TexturePackFolderModel.cpp
|
minecraft/mod/TexturePackFolderModel.cpp
|
||||||
minecraft/mod/ShaderPackFolderModel.h
|
minecraft/mod/ShaderPackFolderModel.h
|
||||||
minecraft/mod/tasks/BasicFolderLoadTask.h
|
minecraft/mod/tasks/ResourceFolderLoadTask.h
|
||||||
minecraft/mod/tasks/ModFolderLoadTask.h
|
minecraft/mod/tasks/ResourceFolderLoadTask.cpp
|
||||||
minecraft/mod/tasks/ModFolderLoadTask.cpp
|
|
||||||
minecraft/mod/tasks/LocalModParseTask.h
|
minecraft/mod/tasks/LocalModParseTask.h
|
||||||
minecraft/mod/tasks/LocalModParseTask.cpp
|
minecraft/mod/tasks/LocalModParseTask.cpp
|
||||||
minecraft/mod/tasks/LocalModUpdateTask.h
|
minecraft/mod/tasks/LocalResourceUpdateTask.h
|
||||||
minecraft/mod/tasks/LocalModUpdateTask.cpp
|
minecraft/mod/tasks/LocalResourceUpdateTask.cpp
|
||||||
minecraft/mod/tasks/LocalDataPackParseTask.h
|
minecraft/mod/tasks/LocalDataPackParseTask.h
|
||||||
minecraft/mod/tasks/LocalDataPackParseTask.cpp
|
minecraft/mod/tasks/LocalDataPackParseTask.cpp
|
||||||
minecraft/mod/tasks/LocalResourcePackParseTask.h
|
minecraft/mod/tasks/LocalResourcePackParseTask.h
|
||||||
|
|
@ -372,13 +370,17 @@ set(MINECRAFT_SOURCES
|
||||||
minecraft/AssetsUtils.h
|
minecraft/AssetsUtils.h
|
||||||
minecraft/AssetsUtils.cpp
|
minecraft/AssetsUtils.cpp
|
||||||
|
|
||||||
# Minecraft services
|
# Minecraft skins
|
||||||
minecraft/services/CapeChange.cpp
|
minecraft/skins/CapeChange.cpp
|
||||||
minecraft/services/CapeChange.h
|
minecraft/skins/CapeChange.h
|
||||||
minecraft/services/SkinUpload.cpp
|
minecraft/skins/SkinUpload.cpp
|
||||||
minecraft/services/SkinUpload.h
|
minecraft/skins/SkinUpload.h
|
||||||
minecraft/services/SkinDelete.cpp
|
minecraft/skins/SkinDelete.cpp
|
||||||
minecraft/services/SkinDelete.h
|
minecraft/skins/SkinDelete.h
|
||||||
|
minecraft/skins/SkinModel.cpp
|
||||||
|
minecraft/skins/SkinModel.h
|
||||||
|
minecraft/skins/SkinList.cpp
|
||||||
|
minecraft/skins/SkinList.h
|
||||||
|
|
||||||
minecraft/Agent.h)
|
minecraft/Agent.h)
|
||||||
|
|
||||||
|
|
@ -422,8 +424,6 @@ set(SETTINGS_SOURCES
|
||||||
set(JAVA_SOURCES
|
set(JAVA_SOURCES
|
||||||
java/JavaChecker.h
|
java/JavaChecker.h
|
||||||
java/JavaChecker.cpp
|
java/JavaChecker.cpp
|
||||||
java/JavaCheckerJob.h
|
|
||||||
java/JavaCheckerJob.cpp
|
|
||||||
java/JavaInstall.h
|
java/JavaInstall.h
|
||||||
java/JavaInstall.cpp
|
java/JavaInstall.cpp
|
||||||
java/JavaInstallList.h
|
java/JavaInstallList.h
|
||||||
|
|
@ -432,6 +432,20 @@ set(JAVA_SOURCES
|
||||||
java/JavaUtils.cpp
|
java/JavaUtils.cpp
|
||||||
java/JavaVersion.h
|
java/JavaVersion.h
|
||||||
java/JavaVersion.cpp
|
java/JavaVersion.cpp
|
||||||
|
|
||||||
|
java/JavaMetadata.h
|
||||||
|
java/JavaMetadata.cpp
|
||||||
|
java/download/ArchiveDownloadTask.cpp
|
||||||
|
java/download/ArchiveDownloadTask.h
|
||||||
|
java/download/ManifestDownloadTask.cpp
|
||||||
|
java/download/ManifestDownloadTask.h
|
||||||
|
java/download/SymlinkTask.cpp
|
||||||
|
java/download/SymlinkTask.h
|
||||||
|
|
||||||
|
ui/java/InstallJavaDialog.h
|
||||||
|
ui/java/InstallJavaDialog.cpp
|
||||||
|
ui/java/VersionList.h
|
||||||
|
ui/java/VersionList.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(TRANSLATIONS_SOURCES
|
set(TRANSLATIONS_SOURCES
|
||||||
|
|
@ -453,6 +467,8 @@ set(TOOLS_SOURCES
|
||||||
tools/JVisualVM.h
|
tools/JVisualVM.h
|
||||||
tools/MCEditTool.cpp
|
tools/MCEditTool.cpp
|
||||||
tools/MCEditTool.h
|
tools/MCEditTool.h
|
||||||
|
tools/GenericProfiler.cpp
|
||||||
|
tools/GenericProfiler.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(META_SOURCES
|
set(META_SOURCES
|
||||||
|
|
@ -592,7 +608,7 @@ set(PRISMUPDATER_SOURCES
|
||||||
updater/prismupdater/UpdaterDialogs.cpp
|
updater/prismupdater/UpdaterDialogs.cpp
|
||||||
updater/prismupdater/GitHubRelease.h
|
updater/prismupdater/GitHubRelease.h
|
||||||
updater/prismupdater/GitHubRelease.cpp
|
updater/prismupdater/GitHubRelease.cpp
|
||||||
|
|
||||||
Json.h
|
Json.h
|
||||||
Json.cpp
|
Json.cpp
|
||||||
FileSystem.h
|
FileSystem.h
|
||||||
|
|
@ -609,7 +625,7 @@ set(PRISMUPDATER_SOURCES
|
||||||
# Zip
|
# Zip
|
||||||
MMCZip.h
|
MMCZip.h
|
||||||
MMCZip.cpp
|
MMCZip.cpp
|
||||||
|
|
||||||
# Time
|
# Time
|
||||||
MMCTime.h
|
MMCTime.h
|
||||||
MMCTime.cpp
|
MMCTime.cpp
|
||||||
|
|
@ -624,7 +640,6 @@ set(PRISMUPDATER_SOURCES
|
||||||
net/HttpMetaCache.h
|
net/HttpMetaCache.h
|
||||||
net/Logging.h
|
net/Logging.h
|
||||||
net/Logging.cpp
|
net/Logging.cpp
|
||||||
net/NetAction.h
|
|
||||||
net/NetRequest.cpp
|
net/NetRequest.cpp
|
||||||
net/NetRequest.h
|
net/NetRequest.h
|
||||||
net/NetJob.cpp
|
net/NetJob.cpp
|
||||||
|
|
@ -653,6 +668,22 @@ ecm_qt_declare_logging_category(CORE_SOURCES
|
||||||
EXPORT "${Launcher_Name}"
|
EXPORT "${Launcher_Name}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ecm_qt_export_logging_category(
|
||||||
|
IDENTIFIER instanceProfileC
|
||||||
|
CATEGORY_NAME "launcher.instance.profile"
|
||||||
|
DEFAULT_SEVERITY Debug
|
||||||
|
DESCRIPTION "Profile actions"
|
||||||
|
EXPORT "${Launcher_Name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
ecm_qt_export_logging_category(
|
||||||
|
IDENTIFIER instanceProfileResolveC
|
||||||
|
CATEGORY_NAME "launcher.instance.profile.resolve"
|
||||||
|
DEFAULT_SEVERITY Debug
|
||||||
|
DESCRIPTION "Profile component resolusion actions"
|
||||||
|
EXPORT "${Launcher_Name}"
|
||||||
|
)
|
||||||
|
|
||||||
ecm_qt_export_logging_category(
|
ecm_qt_export_logging_category(
|
||||||
IDENTIFIER taskLogC
|
IDENTIFIER taskLogC
|
||||||
CATEGORY_NAME "launcher.task"
|
CATEGORY_NAME "launcher.task"
|
||||||
|
|
@ -665,7 +696,7 @@ ecm_qt_export_logging_category(
|
||||||
IDENTIFIER taskNetLogC
|
IDENTIFIER taskNetLogC
|
||||||
CATEGORY_NAME "launcher.task.net"
|
CATEGORY_NAME "launcher.task.net"
|
||||||
DEFAULT_SEVERITY Debug
|
DEFAULT_SEVERITY Debug
|
||||||
DESCRIPTION "task network action"
|
DESCRIPTION "Task network action"
|
||||||
EXPORT "${Launcher_Name}"
|
EXPORT "${Launcher_Name}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -673,14 +704,14 @@ ecm_qt_export_logging_category(
|
||||||
IDENTIFIER taskDownloadLogC
|
IDENTIFIER taskDownloadLogC
|
||||||
CATEGORY_NAME "launcher.task.net.download"
|
CATEGORY_NAME "launcher.task.net.download"
|
||||||
DEFAULT_SEVERITY Debug
|
DEFAULT_SEVERITY Debug
|
||||||
DESCRIPTION "task network download actions"
|
DESCRIPTION "Task network download actions"
|
||||||
EXPORT "${Launcher_Name}"
|
EXPORT "${Launcher_Name}"
|
||||||
)
|
)
|
||||||
ecm_qt_export_logging_category(
|
ecm_qt_export_logging_category(
|
||||||
IDENTIFIER taskUploadLogC
|
IDENTIFIER taskUploadLogC
|
||||||
CATEGORY_NAME "launcher.task.net.upload"
|
CATEGORY_NAME "launcher.task.net.upload"
|
||||||
DEFAULT_SEVERITY Debug
|
DEFAULT_SEVERITY Debug
|
||||||
DESCRIPTION "task network upload actions"
|
DESCRIPTION "Task network upload actions"
|
||||||
EXPORT "${Launcher_Name}"
|
EXPORT "${Launcher_Name}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -750,6 +781,8 @@ SET(LAUNCHER_SOURCES
|
||||||
DataMigrationTask.cpp
|
DataMigrationTask.cpp
|
||||||
ApplicationMessage.h
|
ApplicationMessage.h
|
||||||
ApplicationMessage.cpp
|
ApplicationMessage.cpp
|
||||||
|
SysInfo.h
|
||||||
|
SysInfo.cpp
|
||||||
|
|
||||||
# GUI - general utilities
|
# GUI - general utilities
|
||||||
DesktopServices.h
|
DesktopServices.h
|
||||||
|
|
@ -788,16 +821,12 @@ SET(LAUNCHER_SOURCES
|
||||||
# GUI - windows
|
# GUI - windows
|
||||||
ui/GuiUtil.h
|
ui/GuiUtil.h
|
||||||
ui/GuiUtil.cpp
|
ui/GuiUtil.cpp
|
||||||
ui/ColorCache.h
|
|
||||||
ui/ColorCache.cpp
|
|
||||||
ui/MainWindow.h
|
ui/MainWindow.h
|
||||||
ui/MainWindow.cpp
|
ui/MainWindow.cpp
|
||||||
ui/InstanceWindow.h
|
ui/InstanceWindow.h
|
||||||
ui/InstanceWindow.cpp
|
ui/InstanceWindow.cpp
|
||||||
|
|
||||||
# FIXME: maybe find a better home for this.
|
# FIXME: maybe find a better home for this.
|
||||||
SkinUtils.cpp
|
|
||||||
SkinUtils.h
|
|
||||||
FileIgnoreProxy.cpp
|
FileIgnoreProxy.cpp
|
||||||
FileIgnoreProxy.h
|
FileIgnoreProxy.h
|
||||||
FastFileIconProvider.cpp
|
FastFileIconProvider.cpp
|
||||||
|
|
@ -815,6 +844,10 @@ SET(LAUNCHER_SOURCES
|
||||||
ui/setupwizard/PasteWizardPage.h
|
ui/setupwizard/PasteWizardPage.h
|
||||||
ui/setupwizard/ThemeWizardPage.cpp
|
ui/setupwizard/ThemeWizardPage.cpp
|
||||||
ui/setupwizard/ThemeWizardPage.h
|
ui/setupwizard/ThemeWizardPage.h
|
||||||
|
ui/setupwizard/AutoJavaWizardPage.cpp
|
||||||
|
ui/setupwizard/AutoJavaWizardPage.h
|
||||||
|
ui/setupwizard/LoginWizardPage.cpp
|
||||||
|
ui/setupwizard/LoginWizardPage.h
|
||||||
|
|
||||||
# GUI - themes
|
# GUI - themes
|
||||||
ui/themes/FusionTheme.cpp
|
ui/themes/FusionTheme.cpp
|
||||||
|
|
@ -827,6 +860,8 @@ SET(LAUNCHER_SOURCES
|
||||||
ui/themes/DarkTheme.h
|
ui/themes/DarkTheme.h
|
||||||
ui/themes/ITheme.cpp
|
ui/themes/ITheme.cpp
|
||||||
ui/themes/ITheme.h
|
ui/themes/ITheme.h
|
||||||
|
ui/themes/HintOverrideProxyStyle.cpp
|
||||||
|
ui/themes/HintOverrideProxyStyle.h
|
||||||
ui/themes/SystemTheme.cpp
|
ui/themes/SystemTheme.cpp
|
||||||
ui/themes/SystemTheme.h
|
ui/themes/SystemTheme.h
|
||||||
ui/themes/IconTheme.cpp
|
ui/themes/IconTheme.cpp
|
||||||
|
|
@ -873,7 +908,6 @@ SET(LAUNCHER_SOURCES
|
||||||
ui/pages/instance/NotesPage.h
|
ui/pages/instance/NotesPage.h
|
||||||
ui/pages/instance/LogPage.cpp
|
ui/pages/instance/LogPage.cpp
|
||||||
ui/pages/instance/LogPage.h
|
ui/pages/instance/LogPage.h
|
||||||
ui/pages/instance/InstanceSettingsPage.cpp
|
|
||||||
ui/pages/instance/InstanceSettingsPage.h
|
ui/pages/instance/InstanceSettingsPage.h
|
||||||
ui/pages/instance/ScreenshotsPage.cpp
|
ui/pages/instance/ScreenshotsPage.cpp
|
||||||
ui/pages/instance/ScreenshotsPage.h
|
ui/pages/instance/ScreenshotsPage.h
|
||||||
|
|
@ -887,17 +921,12 @@ SET(LAUNCHER_SOURCES
|
||||||
# GUI - global settings pages
|
# GUI - global settings pages
|
||||||
ui/pages/global/AccountListPage.cpp
|
ui/pages/global/AccountListPage.cpp
|
||||||
ui/pages/global/AccountListPage.h
|
ui/pages/global/AccountListPage.h
|
||||||
ui/pages/global/CustomCommandsPage.cpp
|
|
||||||
ui/pages/global/CustomCommandsPage.h
|
|
||||||
ui/pages/global/EnvironmentVariablesPage.cpp
|
|
||||||
ui/pages/global/EnvironmentVariablesPage.h
|
|
||||||
ui/pages/global/ExternalToolsPage.cpp
|
ui/pages/global/ExternalToolsPage.cpp
|
||||||
ui/pages/global/ExternalToolsPage.h
|
ui/pages/global/ExternalToolsPage.h
|
||||||
ui/pages/global/JavaPage.cpp
|
ui/pages/global/JavaPage.cpp
|
||||||
ui/pages/global/JavaPage.h
|
ui/pages/global/JavaPage.h
|
||||||
ui/pages/global/LanguagePage.cpp
|
ui/pages/global/LanguagePage.cpp
|
||||||
ui/pages/global/LanguagePage.h
|
ui/pages/global/LanguagePage.h
|
||||||
ui/pages/global/MinecraftPage.cpp
|
|
||||||
ui/pages/global/MinecraftPage.h
|
ui/pages/global/MinecraftPage.h
|
||||||
ui/pages/global/LauncherPage.cpp
|
ui/pages/global/LauncherPage.cpp
|
||||||
ui/pages/global/LauncherPage.h
|
ui/pages/global/LauncherPage.h
|
||||||
|
|
@ -930,6 +959,9 @@ SET(LAUNCHER_SOURCES
|
||||||
ui/pages/modplatform/ShaderPackPage.cpp
|
ui/pages/modplatform/ShaderPackPage.cpp
|
||||||
ui/pages/modplatform/ShaderPackModel.cpp
|
ui/pages/modplatform/ShaderPackModel.cpp
|
||||||
|
|
||||||
|
|
||||||
|
ui/pages/modplatform/ModpackProviderBasePage.h
|
||||||
|
|
||||||
ui/pages/modplatform/atlauncher/AtlFilterModel.cpp
|
ui/pages/modplatform/atlauncher/AtlFilterModel.cpp
|
||||||
ui/pages/modplatform/atlauncher/AtlFilterModel.h
|
ui/pages/modplatform/atlauncher/AtlFilterModel.h
|
||||||
ui/pages/modplatform/atlauncher/AtlListModel.cpp
|
ui/pages/modplatform/atlauncher/AtlListModel.cpp
|
||||||
|
|
@ -992,8 +1024,6 @@ SET(LAUNCHER_SOURCES
|
||||||
ui/dialogs/CopyInstanceDialog.h
|
ui/dialogs/CopyInstanceDialog.h
|
||||||
ui/dialogs/CustomMessageBox.cpp
|
ui/dialogs/CustomMessageBox.cpp
|
||||||
ui/dialogs/CustomMessageBox.h
|
ui/dialogs/CustomMessageBox.h
|
||||||
ui/dialogs/EditAccountDialog.cpp
|
|
||||||
ui/dialogs/EditAccountDialog.h
|
|
||||||
ui/dialogs/ExportInstanceDialog.cpp
|
ui/dialogs/ExportInstanceDialog.cpp
|
||||||
ui/dialogs/ExportInstanceDialog.h
|
ui/dialogs/ExportInstanceDialog.h
|
||||||
ui/dialogs/ExportPackDialog.cpp
|
ui/dialogs/ExportPackDialog.cpp
|
||||||
|
|
@ -1022,8 +1052,6 @@ SET(LAUNCHER_SOURCES
|
||||||
ui/dialogs/ReviewMessageBox.h
|
ui/dialogs/ReviewMessageBox.h
|
||||||
ui/dialogs/VersionSelectDialog.cpp
|
ui/dialogs/VersionSelectDialog.cpp
|
||||||
ui/dialogs/VersionSelectDialog.h
|
ui/dialogs/VersionSelectDialog.h
|
||||||
ui/dialogs/SkinUploadDialog.cpp
|
|
||||||
ui/dialogs/SkinUploadDialog.h
|
|
||||||
ui/dialogs/ResourceDownloadDialog.cpp
|
ui/dialogs/ResourceDownloadDialog.cpp
|
||||||
ui/dialogs/ResourceDownloadDialog.h
|
ui/dialogs/ResourceDownloadDialog.h
|
||||||
ui/dialogs/ScrollMessageBox.cpp
|
ui/dialogs/ScrollMessageBox.cpp
|
||||||
|
|
@ -1032,26 +1060,29 @@ SET(LAUNCHER_SOURCES
|
||||||
ui/dialogs/BlockedModsDialog.h
|
ui/dialogs/BlockedModsDialog.h
|
||||||
ui/dialogs/ChooseProviderDialog.h
|
ui/dialogs/ChooseProviderDialog.h
|
||||||
ui/dialogs/ChooseProviderDialog.cpp
|
ui/dialogs/ChooseProviderDialog.cpp
|
||||||
ui/dialogs/ModUpdateDialog.cpp
|
ui/dialogs/ResourceUpdateDialog.cpp
|
||||||
ui/dialogs/ModUpdateDialog.h
|
ui/dialogs/ResourceUpdateDialog.h
|
||||||
ui/dialogs/InstallLoaderDialog.cpp
|
ui/dialogs/InstallLoaderDialog.cpp
|
||||||
ui/dialogs/InstallLoaderDialog.h
|
ui/dialogs/InstallLoaderDialog.h
|
||||||
|
|
||||||
|
ui/dialogs/skins/SkinManageDialog.cpp
|
||||||
|
ui/dialogs/skins/SkinManageDialog.h
|
||||||
|
|
||||||
# GUI - widgets
|
# GUI - widgets
|
||||||
|
ui/widgets/CheckComboBox.cpp
|
||||||
|
ui/widgets/CheckComboBox.h
|
||||||
ui/widgets/Common.cpp
|
ui/widgets/Common.cpp
|
||||||
ui/widgets/Common.h
|
ui/widgets/Common.h
|
||||||
ui/widgets/CustomCommands.cpp
|
ui/widgets/CustomCommands.cpp
|
||||||
ui/widgets/CustomCommands.h
|
ui/widgets/CustomCommands.h
|
||||||
ui/widgets/EnvironmentVariables.cpp
|
ui/widgets/EnvironmentVariables.cpp
|
||||||
ui/widgets/EnvironmentVariables.h
|
ui/widgets/EnvironmentVariables.h
|
||||||
ui/widgets/DropLabel.cpp
|
|
||||||
ui/widgets/DropLabel.h
|
|
||||||
ui/widgets/FocusLineEdit.cpp
|
ui/widgets/FocusLineEdit.cpp
|
||||||
ui/widgets/FocusLineEdit.h
|
ui/widgets/FocusLineEdit.h
|
||||||
ui/widgets/IconLabel.cpp
|
ui/widgets/IconLabel.cpp
|
||||||
ui/widgets/IconLabel.h
|
ui/widgets/IconLabel.h
|
||||||
ui/widgets/JavaSettingsWidget.cpp
|
ui/widgets/JavaWizardWidget.cpp
|
||||||
ui/widgets/JavaSettingsWidget.h
|
ui/widgets/JavaWizardWidget.h
|
||||||
ui/widgets/LabeledToolButton.cpp
|
ui/widgets/LabeledToolButton.cpp
|
||||||
ui/widgets/LabeledToolButton.h
|
ui/widgets/LabeledToolButton.h
|
||||||
ui/widgets/LanguageSelectionWidget.cpp
|
ui/widgets/LanguageSelectionWidget.cpp
|
||||||
|
|
@ -1087,6 +1118,10 @@ SET(LAUNCHER_SOURCES
|
||||||
ui/widgets/WideBar.cpp
|
ui/widgets/WideBar.cpp
|
||||||
ui/widgets/ThemeCustomizationWidget.h
|
ui/widgets/ThemeCustomizationWidget.h
|
||||||
ui/widgets/ThemeCustomizationWidget.cpp
|
ui/widgets/ThemeCustomizationWidget.cpp
|
||||||
|
ui/widgets/MinecraftSettingsWidget.h
|
||||||
|
ui/widgets/MinecraftSettingsWidget.cpp
|
||||||
|
ui/widgets/JavaSettingsWidget.h
|
||||||
|
ui/widgets/JavaSettingsWidget.cpp
|
||||||
|
|
||||||
# GUI - instance group view
|
# GUI - instance group view
|
||||||
ui/instanceview/InstanceProxyModel.cpp
|
ui/instanceview/InstanceProxyModel.cpp
|
||||||
|
|
@ -1122,13 +1157,14 @@ endif()
|
||||||
qt_wrap_ui(LAUNCHER_UI
|
qt_wrap_ui(LAUNCHER_UI
|
||||||
ui/MainWindow.ui
|
ui/MainWindow.ui
|
||||||
ui/setupwizard/PasteWizardPage.ui
|
ui/setupwizard/PasteWizardPage.ui
|
||||||
|
ui/setupwizard/AutoJavaWizardPage.ui
|
||||||
|
ui/setupwizard/LoginWizardPage.ui
|
||||||
ui/setupwizard/ThemeWizardPage.ui
|
ui/setupwizard/ThemeWizardPage.ui
|
||||||
ui/pages/global/AccountListPage.ui
|
ui/pages/global/AccountListPage.ui
|
||||||
ui/pages/global/JavaPage.ui
|
ui/pages/global/JavaPage.ui
|
||||||
ui/pages/global/LauncherPage.ui
|
ui/pages/global/LauncherPage.ui
|
||||||
ui/pages/global/APIPage.ui
|
ui/pages/global/APIPage.ui
|
||||||
ui/pages/global/ProxyPage.ui
|
ui/pages/global/ProxyPage.ui
|
||||||
ui/pages/global/MinecraftPage.ui
|
|
||||||
ui/pages/global/ExternalToolsPage.ui
|
ui/pages/global/ExternalToolsPage.ui
|
||||||
ui/pages/instance/ExternalResourcesPage.ui
|
ui/pages/instance/ExternalResourcesPage.ui
|
||||||
ui/pages/instance/NotesPage.ui
|
ui/pages/instance/NotesPage.ui
|
||||||
|
|
@ -1136,7 +1172,6 @@ qt_wrap_ui(LAUNCHER_UI
|
||||||
ui/pages/instance/ServersPage.ui
|
ui/pages/instance/ServersPage.ui
|
||||||
ui/pages/instance/GameOptionsPage.ui
|
ui/pages/instance/GameOptionsPage.ui
|
||||||
ui/pages/instance/OtherLogsPage.ui
|
ui/pages/instance/OtherLogsPage.ui
|
||||||
ui/pages/instance/InstanceSettingsPage.ui
|
|
||||||
ui/pages/instance/VersionPage.ui
|
ui/pages/instance/VersionPage.ui
|
||||||
ui/pages/instance/ManagedPackPage.ui
|
ui/pages/instance/ManagedPackPage.ui
|
||||||
ui/pages/instance/WorldListPage.ui
|
ui/pages/instance/WorldListPage.ui
|
||||||
|
|
@ -1159,6 +1194,8 @@ qt_wrap_ui(LAUNCHER_UI
|
||||||
ui/widgets/ModFilterWidget.ui
|
ui/widgets/ModFilterWidget.ui
|
||||||
ui/widgets/SubTaskProgressBar.ui
|
ui/widgets/SubTaskProgressBar.ui
|
||||||
ui/widgets/ThemeCustomizationWidget.ui
|
ui/widgets/ThemeCustomizationWidget.ui
|
||||||
|
ui/widgets/MinecraftSettingsWidget.ui
|
||||||
|
ui/widgets/JavaSettingsWidget.ui
|
||||||
ui/dialogs/CopyInstanceDialog.ui
|
ui/dialogs/CopyInstanceDialog.ui
|
||||||
ui/dialogs/ProfileSetupDialog.ui
|
ui/dialogs/ProfileSetupDialog.ui
|
||||||
ui/dialogs/ProgressDialog.ui
|
ui/dialogs/ProgressDialog.ui
|
||||||
|
|
@ -1166,7 +1203,6 @@ qt_wrap_ui(LAUNCHER_UI
|
||||||
ui/dialogs/NewComponentDialog.ui
|
ui/dialogs/NewComponentDialog.ui
|
||||||
ui/dialogs/NewsDialog.ui
|
ui/dialogs/NewsDialog.ui
|
||||||
ui/dialogs/ProfileSelectDialog.ui
|
ui/dialogs/ProfileSelectDialog.ui
|
||||||
ui/dialogs/SkinUploadDialog.ui
|
|
||||||
ui/dialogs/ExportInstanceDialog.ui
|
ui/dialogs/ExportInstanceDialog.ui
|
||||||
ui/dialogs/ExportPackDialog.ui
|
ui/dialogs/ExportPackDialog.ui
|
||||||
ui/dialogs/ExportToModListDialog.ui
|
ui/dialogs/ExportToModListDialog.ui
|
||||||
|
|
@ -1175,11 +1211,11 @@ qt_wrap_ui(LAUNCHER_UI
|
||||||
ui/dialogs/MSALoginDialog.ui
|
ui/dialogs/MSALoginDialog.ui
|
||||||
ui/dialogs/OfflineLoginDialog.ui
|
ui/dialogs/OfflineLoginDialog.ui
|
||||||
ui/dialogs/AboutDialog.ui
|
ui/dialogs/AboutDialog.ui
|
||||||
ui/dialogs/EditAccountDialog.ui
|
|
||||||
ui/dialogs/ReviewMessageBox.ui
|
ui/dialogs/ReviewMessageBox.ui
|
||||||
ui/dialogs/ScrollMessageBox.ui
|
ui/dialogs/ScrollMessageBox.ui
|
||||||
ui/dialogs/BlockedModsDialog.ui
|
ui/dialogs/BlockedModsDialog.ui
|
||||||
ui/dialogs/ChooseProviderDialog.ui
|
ui/dialogs/ChooseProviderDialog.ui
|
||||||
|
ui/dialogs/skins/SkinManageDialog.ui
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_wrap_ui(PRISM_UPDATE_UI
|
qt_wrap_ui(PRISM_UPDATE_UI
|
||||||
|
|
@ -1221,14 +1257,10 @@ include(CompilerWarnings)
|
||||||
|
|
||||||
# Add executable
|
# Add executable
|
||||||
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES})
|
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES})
|
||||||
if(BUILD_TESTING)
|
|
||||||
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_TEST)
|
|
||||||
endif()
|
|
||||||
set_project_warnings(Launcher_logic
|
set_project_warnings(Launcher_logic
|
||||||
"${Launcher_MSVC_WARNINGS}"
|
"${Launcher_MSVC_WARNINGS}"
|
||||||
"${Launcher_CLANG_WARNINGS}"
|
"${Launcher_CLANG_WARNINGS}"
|
||||||
"${Launcher_GCC_WARNINGS}")
|
"${Launcher_GCC_WARNINGS}")
|
||||||
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
|
|
||||||
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
|
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
|
||||||
target_link_libraries(Launcher_logic
|
target_link_libraries(Launcher_logic
|
||||||
|
|
@ -1239,7 +1271,6 @@ target_link_libraries(Launcher_logic
|
||||||
tomlplusplus::tomlplusplus
|
tomlplusplus::tomlplusplus
|
||||||
qdcss
|
qdcss
|
||||||
BuildConfig
|
BuildConfig
|
||||||
Katabasis
|
|
||||||
Qt${QT_VERSION_MAJOR}::Widgets
|
Qt${QT_VERSION_MAJOR}::Widgets
|
||||||
ghcFilesystem::ghc_filesystem
|
ghcFilesystem::ghc_filesystem
|
||||||
)
|
)
|
||||||
|
|
@ -1257,6 +1288,8 @@ target_link_libraries(Launcher_logic
|
||||||
Qt${QT_VERSION_MAJOR}::Concurrent
|
Qt${QT_VERSION_MAJOR}::Concurrent
|
||||||
Qt${QT_VERSION_MAJOR}::Gui
|
Qt${QT_VERSION_MAJOR}::Gui
|
||||||
Qt${QT_VERSION_MAJOR}::Widgets
|
Qt${QT_VERSION_MAJOR}::Widgets
|
||||||
|
Qt${QT_VERSION_MAJOR}::NetworkAuth
|
||||||
|
${Launcher_QT_DBUS}
|
||||||
${Launcher_QT_LIBS}
|
${Launcher_QT_LIBS}
|
||||||
)
|
)
|
||||||
target_link_libraries(Launcher_logic
|
target_link_libraries(Launcher_logic
|
||||||
|
|
@ -1265,6 +1298,10 @@ target_link_libraries(Launcher_logic
|
||||||
LocalPeer
|
LocalPeer
|
||||||
Launcher_rainbow
|
Launcher_rainbow
|
||||||
)
|
)
|
||||||
|
if (TARGET ${Launcher_QT_DBUS})
|
||||||
|
add_compile_definitions(WITH_QTDBUS)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
set(CMAKE_MACOSX_RPATH 1)
|
set(CMAKE_MACOSX_RPATH 1)
|
||||||
set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/")
|
set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/")
|
||||||
|
|
@ -1327,13 +1364,12 @@ if(Launcher_BUILD_UPDATER)
|
||||||
Qt${QT_VERSION_MAJOR}::Network
|
Qt${QT_VERSION_MAJOR}::Network
|
||||||
${Launcher_QT_LIBS}
|
${Launcher_QT_LIBS}
|
||||||
cmark::cmark
|
cmark::cmark
|
||||||
Katabasis
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp)
|
add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp)
|
||||||
target_sources("${Launcher_Name}_updater" PRIVATE updater/prismupdater/updater.exe.manifest)
|
target_sources("${Launcher_Name}_updater" PRIVATE updater/prismupdater/updater.exe.manifest)
|
||||||
target_link_libraries("${Launcher_Name}_updater" prism_updater_logic)
|
target_link_libraries("${Launcher_Name}_updater" prism_updater_logic)
|
||||||
|
|
||||||
if(DEFINED Launcher_APP_BINARY_NAME)
|
if(DEFINED Launcher_APP_BINARY_NAME)
|
||||||
set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater")
|
set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater")
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -1492,7 +1528,6 @@ if(INSTALL_BUNDLE STREQUAL "full")
|
||||||
CONFIGURATIONS Debug RelWithDebInfo ""
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
DESTINATION ${PLUGIN_DEST_DIR}
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
COMPONENT Runtime
|
COMPONENT Runtime
|
||||||
PATTERN "*qopensslbackend*" EXCLUDE
|
|
||||||
PATTERN "*qcertonlybackend*" EXCLUDE
|
PATTERN "*qcertonlybackend*" EXCLUDE
|
||||||
)
|
)
|
||||||
install(
|
install(
|
||||||
|
|
@ -1503,10 +1538,78 @@ if(INSTALL_BUNDLE STREQUAL "full")
|
||||||
REGEX "dd\\." EXCLUDE
|
REGEX "dd\\." EXCLUDE
|
||||||
REGEX "_debug\\." EXCLUDE
|
REGEX "_debug\\." EXCLUDE
|
||||||
REGEX "\\.dSYM" EXCLUDE
|
REGEX "\\.dSYM" EXCLUDE
|
||||||
PATTERN "*qopensslbackend*" EXCLUDE
|
|
||||||
PATTERN "*qcertonlybackend*" EXCLUDE
|
PATTERN "*qcertonlybackend*" EXCLUDE
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
# Wayland support
|
||||||
|
if(EXISTS "${QT_PLUGINS_DIR}/wayland-graphics-integration-client")
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-client"
|
||||||
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-client"
|
||||||
|
CONFIGURATIONS Release MinSizeRel
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
REGEX "dd\\." EXCLUDE
|
||||||
|
REGEX "_debug\\." EXCLUDE
|
||||||
|
REGEX "\\.dSYM" EXCLUDE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
if(EXISTS "${QT_PLUGINS_DIR}/wayland-graphics-integration-server")
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-server"
|
||||||
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-server"
|
||||||
|
CONFIGURATIONS Release MinSizeRel
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
REGEX "dd\\." EXCLUDE
|
||||||
|
REGEX "_debug\\." EXCLUDE
|
||||||
|
REGEX "\\.dSYM" EXCLUDE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
if(EXISTS "${QT_PLUGINS_DIR}/wayland-decoration-client")
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/wayland-decoration-client"
|
||||||
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/wayland-decoration-client"
|
||||||
|
CONFIGURATIONS Release MinSizeRel
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
REGEX "dd\\." EXCLUDE
|
||||||
|
REGEX "_debug\\." EXCLUDE
|
||||||
|
REGEX "\\.dSYM" EXCLUDE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
if(EXISTS "${QT_PLUGINS_DIR}/wayland-shell-integration")
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/wayland-shell-integration"
|
||||||
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/wayland-shell-integration"
|
||||||
|
CONFIGURATIONS Release MinSizeRel
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
REGEX "dd\\." EXCLUDE
|
||||||
|
REGEX "_debug\\." EXCLUDE
|
||||||
|
REGEX "\\.dSYM" EXCLUDE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
configure_file(
|
configure_file(
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"
|
"${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake"
|
"${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake"
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,8 @@
|
||||||
|
|
||||||
#include <QtConcurrent>
|
#include <QtConcurrent>
|
||||||
|
|
||||||
DataMigrationTask::DataMigrationTask(QObject* parent,
|
DataMigrationTask::DataMigrationTask(const QString& sourcePath, const QString& targetPath, const IPathMatcher::Ptr pathMatcher)
|
||||||
const QString& sourcePath,
|
: Task(), m_sourcePath(sourcePath), m_targetPath(targetPath), m_pathMatcher(pathMatcher), m_copy(sourcePath, targetPath)
|
||||||
const QString& targetPath,
|
|
||||||
const IPathMatcher::Ptr pathMatcher)
|
|
||||||
: Task(parent), m_sourcePath(sourcePath), m_targetPath(targetPath), m_pathMatcher(pathMatcher), m_copy(sourcePath, targetPath)
|
|
||||||
{
|
{
|
||||||
m_copy.matcher(m_pathMatcher.get()).whitelist(true);
|
m_copy.matcher(m_pathMatcher.get()).whitelist(true);
|
||||||
}
|
}
|
||||||
|
|
@ -27,7 +24,7 @@ void DataMigrationTask::executeTask()
|
||||||
|
|
||||||
// 1. Scan
|
// 1. Scan
|
||||||
// Check how many files we gotta copy
|
// Check how many files we gotta copy
|
||||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] {
|
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] {
|
||||||
return m_copy(true); // dry run to collect amount of files
|
return m_copy(true); // dry run to collect amount of files
|
||||||
});
|
});
|
||||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::dryRunFinished);
|
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::dryRunFinished);
|
||||||
|
|
@ -60,7 +57,7 @@ void DataMigrationTask::dryRunFinished()
|
||||||
setProgress(m_copy.totalCopied(), m_toCopy);
|
setProgress(m_copy.totalCopied(), m_toCopy);
|
||||||
setStatus(tr("Copying %1…").arg(shortenedName));
|
setStatus(tr("Copying %1…").arg(shortenedName));
|
||||||
});
|
});
|
||||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] {
|
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] {
|
||||||
return m_copy(false); // actually copy now
|
return m_copy(false); // actually copy now
|
||||||
});
|
});
|
||||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::copyFinished);
|
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::copyFinished);
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
class DataMigrationTask : public Task {
|
class DataMigrationTask : public Task {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit DataMigrationTask(QObject* parent, const QString& sourcePath, const QString& targetPath, IPathMatcher::Ptr pathmatcher);
|
explicit DataMigrationTask(const QString& sourcePath, const QString& targetPath, IPathMatcher::Ptr pathmatcher);
|
||||||
~DataMigrationTask() override = default;
|
~DataMigrationTask() override = default;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
||||||
|
|
@ -37,140 +37,33 @@
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
#include "FileSystem.h"
|
||||||
/**
|
|
||||||
* This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing.
|
|
||||||
*/
|
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool IndirectOpen(T callable, qint64* pid_forked = nullptr)
|
|
||||||
{
|
|
||||||
auto pid = fork();
|
|
||||||
if (pid_forked) {
|
|
||||||
if (pid > 0)
|
|
||||||
*pid_forked = pid;
|
|
||||||
else
|
|
||||||
*pid_forked = 0;
|
|
||||||
}
|
|
||||||
if (pid == -1) {
|
|
||||||
qWarning() << "IndirectOpen failed to fork: " << errno;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// child - do the stuff
|
|
||||||
if (pid == 0) {
|
|
||||||
// unset all this garbage so it doesn't get passed to the child process
|
|
||||||
qunsetenv("LD_PRELOAD");
|
|
||||||
qunsetenv("LD_LIBRARY_PATH");
|
|
||||||
qunsetenv("LD_DEBUG");
|
|
||||||
qunsetenv("QT_PLUGIN_PATH");
|
|
||||||
qunsetenv("QT_FONTPATH");
|
|
||||||
|
|
||||||
// open the URL
|
|
||||||
auto status = callable();
|
|
||||||
|
|
||||||
// detach from the parent process group.
|
|
||||||
setsid();
|
|
||||||
|
|
||||||
// die. now. do not clean up anything, it would just hang forever.
|
|
||||||
_exit(status ? 0 : 1);
|
|
||||||
} else {
|
|
||||||
// parent - assume it worked.
|
|
||||||
int status;
|
|
||||||
while (waitpid(pid, &status, 0)) {
|
|
||||||
if (WIFEXITED(status)) {
|
|
||||||
return WEXITSTATUS(status) == 0;
|
|
||||||
}
|
|
||||||
if (WIFSIGNALED(status)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace DesktopServices {
|
namespace DesktopServices {
|
||||||
bool openDirectory(const QString& path, [[maybe_unused]] bool ensureExists)
|
bool openPath(const QFileInfo& path, bool ensureFolderPathExists)
|
||||||
{
|
{
|
||||||
qDebug() << "Opening directory" << path;
|
qDebug() << "Opening path" << path;
|
||||||
QDir parentPath;
|
if (ensureFolderPathExists) {
|
||||||
QDir dir(path);
|
FS::ensureFolderPathExists(path);
|
||||||
if (ensureExists && !dir.exists()) {
|
|
||||||
parentPath.mkpath(dir.absolutePath());
|
|
||||||
}
|
}
|
||||||
auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); };
|
return openUrl(QUrl::fromLocalFile(QFileInfo(path).absoluteFilePath()));
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
|
||||||
if (!isSandbox()) {
|
|
||||||
return IndirectOpen(f);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return f();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool openFile(const QString& path)
|
bool openPath(const QString& path, bool ensureFolderPathExists)
|
||||||
{
|
{
|
||||||
qDebug() << "Opening file" << path;
|
return openPath(QFileInfo(path), ensureFolderPathExists);
|
||||||
auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(path)); };
|
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
|
||||||
if (!isSandbox()) {
|
|
||||||
return IndirectOpen(f);
|
|
||||||
} else {
|
|
||||||
return f();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
return f();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool openFile(const QString& application, const QString& path, const QString& workingDirectory, qint64* pid)
|
|
||||||
{
|
|
||||||
qDebug() << "Opening file" << path << "using" << application;
|
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
|
||||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
|
||||||
if (!isSandbox()) {
|
|
||||||
return IndirectOpen([&]() { return QProcess::startDetached(application, QStringList() << path, workingDirectory); }, pid);
|
|
||||||
} else {
|
|
||||||
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool run(const QString& application, const QStringList& args, const QString& workingDirectory, qint64* pid)
|
bool run(const QString& application, const QStringList& args, const QString& workingDirectory, qint64* pid)
|
||||||
{
|
{
|
||||||
qDebug() << "Running" << application << "with args" << args.join(' ');
|
qDebug() << "Running" << application << "with args" << args.join(' ');
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
|
||||||
if (!isSandbox()) {
|
|
||||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
|
||||||
return IndirectOpen([&]() { return QProcess::startDetached(application, args, workingDirectory); }, pid);
|
|
||||||
} else {
|
|
||||||
return QProcess::startDetached(application, args, workingDirectory, pid);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
return QProcess::startDetached(application, args, workingDirectory, pid);
|
return QProcess::startDetached(application, args, workingDirectory, pid);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool openUrl(const QUrl& url)
|
bool openUrl(const QUrl& url)
|
||||||
{
|
{
|
||||||
qDebug() << "Opening URL" << url.toString();
|
qDebug() << "Opening URL" << url.toString();
|
||||||
auto f = [&]() { return QDesktopServices::openUrl(url); };
|
return QDesktopServices::openUrl(url);
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
|
||||||
if (!isSandbox()) {
|
|
||||||
return IndirectOpen(f);
|
|
||||||
} else {
|
|
||||||
return f();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
return f();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isFlatpak()
|
bool isFlatpak()
|
||||||
|
|
@ -191,9 +84,4 @@ bool isSnap()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSandbox()
|
|
||||||
{
|
|
||||||
return isSnap() || isFlatpak();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace DesktopServices
|
} // namespace DesktopServices
|
||||||
|
|
|
||||||
|
|
@ -3,31 +3,30 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
|
class QFileInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This wraps around QDesktopServices and adds workarounds where needed
|
* This wraps around QDesktopServices and adds workarounds where needed
|
||||||
* Use this instead of QDesktopServices!
|
* Use this instead of QDesktopServices!
|
||||||
*/
|
*/
|
||||||
namespace DesktopServices {
|
namespace DesktopServices {
|
||||||
/**
|
/**
|
||||||
* Open a file in whatever application is applicable
|
* Open a path in whatever application is applicable.
|
||||||
|
* @param ensureFolderPathExists Make sure the path exists
|
||||||
*/
|
*/
|
||||||
bool openFile(const QString& path);
|
bool openPath(const QFileInfo& path, bool ensureFolderPathExists = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open a file in the specified application
|
* Open a path in whatever application is applicable.
|
||||||
|
* @param ensureFolderPathExists Make sure the path exists
|
||||||
*/
|
*/
|
||||||
bool openFile(const QString& application, const QString& path, const QString& workingDirectory = QString(), qint64* pid = 0);
|
bool openPath(const QString& path, bool ensureFolderPathExists = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run an application
|
* Run an application
|
||||||
*/
|
*/
|
||||||
bool run(const QString& application, const QStringList& args, const QString& workingDirectory = QString(), qint64* pid = 0);
|
bool run(const QString& application, const QStringList& args, const QString& workingDirectory = QString(), qint64* pid = 0);
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a directory
|
|
||||||
*/
|
|
||||||
bool openDirectory(const QString& path, bool ensureExists = false);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the URL, most likely in a browser. Maybe.
|
* Open the URL, most likely in a browser. Maybe.
|
||||||
*/
|
*/
|
||||||
|
|
@ -42,9 +41,4 @@ bool isFlatpak();
|
||||||
* Determine whether the launcher is running in a Snap environment
|
* Determine whether the launcher is running in a Snap environment
|
||||||
*/
|
*/
|
||||||
bool isSnap();
|
bool isSnap();
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the launcher is running in a sandboxed (Flatpak or Snap) environment
|
|
||||||
*/
|
|
||||||
bool isSandbox();
|
|
||||||
} // namespace DesktopServices
|
} // namespace DesktopServices
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,37 @@
|
||||||
// Licensed under the Apache-2.0 license. See README.md for details.
|
// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2024 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|
@ -8,12 +41,12 @@
|
||||||
|
|
||||||
class Exception : public std::exception {
|
class Exception : public std::exception {
|
||||||
public:
|
public:
|
||||||
Exception(const QString& message) : std::exception(), m_message(message) { qCritical() << "Exception:" << message; }
|
Exception(const QString& message) : std::exception(), m_message(message.toUtf8()) { qCritical() << "Exception:" << message; }
|
||||||
Exception(const Exception& other) : std::exception(), m_message(other.cause()) {}
|
Exception(const Exception& other) : std::exception(), m_message(other.m_message) {}
|
||||||
virtual ~Exception() noexcept {}
|
virtual ~Exception() noexcept {}
|
||||||
const char* what() const noexcept { return m_message.toLatin1().constData(); }
|
const char* what() const noexcept { return m_message.constData(); }
|
||||||
QString cause() const { return m_message; }
|
QString cause() const { return QString::fromUtf8(m_message); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_message;
|
QByteArray m_message;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QSaveFile>
|
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QStorageInfo>
|
#include <QStorageInfo>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
|
|
@ -54,6 +53,7 @@
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
|
|
||||||
#include "DesktopServices.h"
|
#include "DesktopServices.h"
|
||||||
|
#include "PSaveFile.h"
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
|
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
|
|
@ -191,8 +191,8 @@ void ensureExists(const QDir& dir)
|
||||||
void write(const QString& filename, const QByteArray& data)
|
void write(const QString& filename, const QByteArray& data)
|
||||||
{
|
{
|
||||||
ensureExists(QFileInfo(filename).dir());
|
ensureExists(QFileInfo(filename).dir());
|
||||||
QSaveFile file(filename);
|
PSaveFile file(filename);
|
||||||
if (!file.open(QSaveFile::WriteOnly)) {
|
if (!file.open(PSaveFile::WriteOnly)) {
|
||||||
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
|
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
|
||||||
}
|
}
|
||||||
if (data.size() != file.write(data)) {
|
if (data.size() != file.write(data)) {
|
||||||
|
|
@ -213,8 +213,8 @@ void appendSafe(const QString& filename, const QByteArray& data)
|
||||||
buffer = QByteArray();
|
buffer = QByteArray();
|
||||||
}
|
}
|
||||||
buffer.append(data);
|
buffer.append(data);
|
||||||
QSaveFile file(filename);
|
PSaveFile file(filename);
|
||||||
if (!file.open(QSaveFile::WriteOnly)) {
|
if (!file.open(PSaveFile::WriteOnly)) {
|
||||||
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
|
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
|
||||||
}
|
}
|
||||||
if (buffer.size() != file.write(buffer)) {
|
if (buffer.size() != file.write(buffer)) {
|
||||||
|
|
@ -272,15 +272,22 @@ bool ensureFilePathExists(QString filenamepath)
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ensureFolderPathExists(QString foldernamepath)
|
bool ensureFolderPathExists(const QFileInfo folderPath)
|
||||||
{
|
{
|
||||||
QFileInfo a(foldernamepath);
|
|
||||||
QDir dir;
|
QDir dir;
|
||||||
QString ensuredPath = a.filePath();
|
QString ensuredPath = folderPath.filePath();
|
||||||
|
if (folderPath.exists())
|
||||||
|
return true;
|
||||||
|
|
||||||
bool success = dir.mkpath(ensuredPath);
|
bool success = dir.mkpath(ensuredPath);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ensureFolderPathExists(const QString folderPathName)
|
||||||
|
{
|
||||||
|
return ensureFolderPathExists(QFileInfo(folderPathName));
|
||||||
|
}
|
||||||
|
|
||||||
bool copyFileAttributes(QString src, QString dst)
|
bool copyFileAttributes(QString src, QString dst)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
|
|
@ -334,7 +341,7 @@ bool copy::operator()(const QString& offset, bool dryRun)
|
||||||
opt |= copy_opts::overwrite_existing;
|
opt |= copy_opts::overwrite_existing;
|
||||||
|
|
||||||
// Function that'll do the actual copying
|
// Function that'll do the actual copying
|
||||||
auto copy_file = [&](QString src_path, QString relative_dst_path) {
|
auto copy_file = [this, dryRun, src, dst, opt, &err](QString src_path, QString relative_dst_path) {
|
||||||
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
|
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -421,7 +428,7 @@ void create_link::make_link_list(const QString& offset)
|
||||||
m_recursive = true;
|
m_recursive = true;
|
||||||
|
|
||||||
// Function that'll do the actual linking
|
// Function that'll do the actual linking
|
||||||
auto link_file = [&](QString src_path, QString relative_dst_path) {
|
auto link_file = [this, dst](QString src_path, QString relative_dst_path) {
|
||||||
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist)) {
|
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist)) {
|
||||||
qDebug() << "path" << relative_dst_path << "in black list or not in whitelist";
|
qDebug() << "path" << relative_dst_path << "in black list or not in whitelist";
|
||||||
return;
|
return;
|
||||||
|
|
@ -516,7 +523,7 @@ void create_link::runPrivileged(const QString& offset)
|
||||||
|
|
||||||
QString serverName = BuildConfig.LAUNCHER_APP_BINARY_NAME + "_filelink_server" + StringUtils::getRandomAlphaNumeric();
|
QString serverName = BuildConfig.LAUNCHER_APP_BINARY_NAME + "_filelink_server" + StringUtils::getRandomAlphaNumeric();
|
||||||
|
|
||||||
connect(&m_linkServer, &QLocalServer::newConnection, this, [&]() {
|
connect(&m_linkServer, &QLocalServer::newConnection, this, [this, &gotResults]() {
|
||||||
qDebug() << "Client connected, sending out pairs";
|
qDebug() << "Client connected, sending out pairs";
|
||||||
// construct block of data to send
|
// construct block of data to send
|
||||||
QByteArray block;
|
QByteArray block;
|
||||||
|
|
@ -598,7 +605,7 @@ void create_link::runPrivileged(const QString& offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
ExternalLinkFileProcess* linkFileProcess = new ExternalLinkFileProcess(serverName, m_useHardLinks, this);
|
ExternalLinkFileProcess* linkFileProcess = new ExternalLinkFileProcess(serverName, m_useHardLinks, this);
|
||||||
connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [&]() { emit finishedPrivileged(gotResults); });
|
connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [this, gotResults]() { emit finishedPrivileged(gotResults); });
|
||||||
connect(linkFileProcess, &ExternalLinkFileProcess::finished, linkFileProcess, &QObject::deleteLater);
|
connect(linkFileProcess, &ExternalLinkFileProcess::finished, linkFileProcess, &QObject::deleteLater);
|
||||||
|
|
||||||
linkFileProcess->start();
|
linkFileProcess->start();
|
||||||
|
|
@ -643,6 +650,19 @@ void ExternalLinkFileProcess::runLinkFile()
|
||||||
qDebug() << "Process exited";
|
qDebug() << "Process exited";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool moveByCopy(const QString& source, const QString& dest)
|
||||||
|
{
|
||||||
|
if (!copy(source, dest)()) { // copy
|
||||||
|
qDebug() << "Copy of" << source << "to" << dest << "failed!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!deletePath(source)) { // remove original
|
||||||
|
qDebug() << "Deletion of" << source << "failed!";
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool move(const QString& source, const QString& dest)
|
bool move(const QString& source, const QString& dest)
|
||||||
{
|
{
|
||||||
std::error_code err;
|
std::error_code err;
|
||||||
|
|
@ -650,13 +670,14 @@ bool move(const QString& source, const QString& dest)
|
||||||
ensureFilePathExists(dest);
|
ensureFilePathExists(dest);
|
||||||
fs::rename(StringUtils::toStdString(source), StringUtils::toStdString(dest), err);
|
fs::rename(StringUtils::toStdString(source), StringUtils::toStdString(dest), err);
|
||||||
|
|
||||||
if (err) {
|
if (err.value() != 0) {
|
||||||
qWarning() << "Failed to move file:" << QString::fromStdString(err.message());
|
if (moveByCopy(source, dest))
|
||||||
qDebug() << "Source file:" << source;
|
return true;
|
||||||
qDebug() << "Destination file:" << dest;
|
qDebug() << "Move of" << source << "to" << dest << "failed!";
|
||||||
|
qWarning() << "Failed to move file:" << QString::fromStdString(err.message()) << QString::number(err.value());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
return err.value() == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool deletePath(QString path)
|
bool deletePath(QString path)
|
||||||
|
|
@ -797,16 +818,68 @@ QString NormalizePath(QString path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString badFilenameChars = "\"\\/?<>:;*|!+\r\n";
|
static const QString BAD_WIN_CHARS = "<>:\"|?*\r\n";
|
||||||
|
static const QString BAD_NTFS_CHARS = "<>:\"|?*";
|
||||||
|
static const QString BAD_HFS_CHARS = ":";
|
||||||
|
|
||||||
|
static const QString BAD_FILENAME_CHARS = BAD_WIN_CHARS + "\\/";
|
||||||
|
|
||||||
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
|
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < string.length(); i++) {
|
for (int i = 0; i < string.length(); i++)
|
||||||
if (badFilenameChars.contains(string[i])) {
|
if (string.at(i) < ' ' || BAD_FILENAME_CHARS.contains(string.at(i)))
|
||||||
string[i] = replaceWith;
|
string[i] = replaceWith;
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString RemoveInvalidPathChars(QString path, QChar replaceWith)
|
||||||
|
{
|
||||||
|
QString invalidChars;
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
invalidChars = BAD_WIN_CHARS;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// the null character is ignored in this check as it was not a problem until now
|
||||||
|
switch (statFS(path).fsType) {
|
||||||
|
case FilesystemType::FAT: // similar to NTFS
|
||||||
|
/* fallthrough */
|
||||||
|
case FilesystemType::NTFS:
|
||||||
|
/* fallthrough */
|
||||||
|
case FilesystemType::REFS: // similar to NTFS(should be available only on windows)
|
||||||
|
invalidChars += BAD_NTFS_CHARS;
|
||||||
|
break;
|
||||||
|
// case FilesystemType::EXT:
|
||||||
|
// case FilesystemType::EXT_2_OLD:
|
||||||
|
// case FilesystemType::EXT_2_3_4:
|
||||||
|
// case FilesystemType::XFS:
|
||||||
|
// case FilesystemType::BTRFS:
|
||||||
|
// case FilesystemType::NFS:
|
||||||
|
// case FilesystemType::ZFS:
|
||||||
|
case FilesystemType::APFS:
|
||||||
|
/* fallthrough */
|
||||||
|
case FilesystemType::HFS:
|
||||||
|
/* fallthrough */
|
||||||
|
case FilesystemType::HFSPLUS:
|
||||||
|
/* fallthrough */
|
||||||
|
case FilesystemType::HFSX:
|
||||||
|
invalidChars += BAD_HFS_CHARS;
|
||||||
|
break;
|
||||||
|
// case FilesystemType::FUSEBLK:
|
||||||
|
// case FilesystemType::F2FS:
|
||||||
|
// case FilesystemType::UNKNOWN:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invalidChars.size() != 0) {
|
||||||
|
for (int i = 0; i < path.length(); i++) {
|
||||||
|
if (path.at(i) < ' ' || invalidChars.contains(path.at(i))) {
|
||||||
|
path[i] = replaceWith;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return string;
|
|
||||||
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DirNameFromString(QString string, QString inDir)
|
QString DirNameFromString(QString string, QString inDir)
|
||||||
|
|
@ -848,6 +921,10 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
|
||||||
if (destination.isEmpty()) {
|
if (destination.isEmpty()) {
|
||||||
destination = PathCombine(getDesktopDir(), RemoveInvalidFilenameChars(name));
|
destination = PathCombine(getDesktopDir(), RemoveInvalidFilenameChars(name));
|
||||||
}
|
}
|
||||||
|
if (!ensureFilePathExists(destination)) {
|
||||||
|
qWarning() << "Destination path can't be created!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#if defined(Q_OS_MACOS)
|
#if defined(Q_OS_MACOS)
|
||||||
// Create the Application
|
// Create the Application
|
||||||
QDir applicationDirectory =
|
QDir applicationDirectory =
|
||||||
|
|
@ -894,8 +971,7 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
|
||||||
if (!args.empty())
|
if (!args.empty())
|
||||||
argstring = " \"" + args.join("\" \"") + "\"";
|
argstring = " \"" + args.join("\" \"") + "\"";
|
||||||
|
|
||||||
stream << "#!/bin/bash"
|
stream << "#!/bin/bash" << "\n";
|
||||||
<< "\n";
|
|
||||||
stream << "\"" << target << "\" " << argstring << "\n";
|
stream << "\"" << target << "\" " << argstring << "\n";
|
||||||
|
|
||||||
stream.flush();
|
stream.flush();
|
||||||
|
|
@ -939,12 +1015,9 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
|
||||||
if (!args.empty())
|
if (!args.empty())
|
||||||
argstring = " '" + args.join("' '") + "'";
|
argstring = " '" + args.join("' '") + "'";
|
||||||
|
|
||||||
stream << "[Desktop Entry]"
|
stream << "[Desktop Entry]" << "\n";
|
||||||
<< "\n";
|
stream << "Type=Application" << "\n";
|
||||||
stream << "Type=Application"
|
stream << "Categories=Game;ActionGame;AdventureGame;Simulation" << "\n";
|
||||||
<< "\n";
|
|
||||||
stream << "Categories=Game;ActionGame;AdventureGame;Simulation"
|
|
||||||
<< "\n";
|
|
||||||
stream << "Exec=\"" << target.toLocal8Bit() << "\"" << argstring.toLocal8Bit() << "\n";
|
stream << "Exec=\"" << target.toLocal8Bit() << "\"" << argstring.toLocal8Bit() << "\n";
|
||||||
stream << "Name=" << name.toLocal8Bit() << "\n";
|
stream << "Name=" << name.toLocal8Bit() << "\n";
|
||||||
if (!icon.isEmpty()) {
|
if (!icon.isEmpty()) {
|
||||||
|
|
@ -1222,7 +1295,7 @@ bool clone::operator()(const QString& offset, bool dryRun)
|
||||||
std::error_code err;
|
std::error_code err;
|
||||||
|
|
||||||
// Function that'll do the actual cloneing
|
// Function that'll do the actual cloneing
|
||||||
auto cloneFile = [&](QString src_path, QString relative_dst_path) {
|
auto cloneFile = [this, dryRun, dst, &err](QString src_path, QString relative_dst_path) {
|
||||||
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
|
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -1581,4 +1654,70 @@ uintmax_t hardLinkCount(const QString& path)
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
// returns 8.3 file format from long path
|
||||||
|
QString shortPathName(const QString& file)
|
||||||
|
{
|
||||||
|
auto input = file.toStdWString();
|
||||||
|
std::wstring output;
|
||||||
|
long length = GetShortPathNameW(input.c_str(), NULL, 0);
|
||||||
|
if (length == 0)
|
||||||
|
return {};
|
||||||
|
// NOTE: this resizing might seem weird...
|
||||||
|
// when GetShortPathNameW fails, it returns length including null character
|
||||||
|
// when it succeeds, it returns length excluding null character
|
||||||
|
// See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989(v=vs.85).aspx
|
||||||
|
output.resize(length);
|
||||||
|
if (GetShortPathNameW(input.c_str(), (LPWSTR)output.c_str(), length) == 0)
|
||||||
|
return {};
|
||||||
|
output.resize(length - 1);
|
||||||
|
QString ret = QString::fromStdWString(output);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the string survives roundtrip through local 8bit encoding...
|
||||||
|
bool fitsInLocal8bit(const QString& string)
|
||||||
|
{
|
||||||
|
return string == QString::fromLocal8Bit(string.toLocal8Bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString getPathNameInLocal8bit(const QString& file)
|
||||||
|
{
|
||||||
|
if (!fitsInLocal8bit(file)) {
|
||||||
|
auto path = shortPathName(file);
|
||||||
|
if (!path.isEmpty()) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
// in case shortPathName fails just return the path as is
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QString getUniqueResourceName(const QString& filePath)
|
||||||
|
{
|
||||||
|
auto newFileName = filePath;
|
||||||
|
if (!newFileName.endsWith(".disabled")) {
|
||||||
|
return newFileName; // prioritize enabled mods
|
||||||
|
}
|
||||||
|
newFileName.chop(9);
|
||||||
|
if (!QFile::exists(newFileName)) {
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
QFileInfo fileInfo(filePath);
|
||||||
|
auto baseName = fileInfo.completeBaseName();
|
||||||
|
auto path = fileInfo.absolutePath();
|
||||||
|
|
||||||
|
int counter = 1;
|
||||||
|
do {
|
||||||
|
if (counter == 1) {
|
||||||
|
newFileName = FS::PathCombine(path, baseName + ".duplicate");
|
||||||
|
} else {
|
||||||
|
newFileName = FS::PathCombine(path, baseName + ".duplicate" + QString::number(counter));
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
|
} while (QFile::exists(newFileName));
|
||||||
|
|
||||||
|
return newFileName;
|
||||||
|
}
|
||||||
} // namespace FS
|
} // namespace FS
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ void appendSafe(const QString& filename, const QByteArray& data);
|
||||||
void append(const QString& filename, const QByteArray& data);
|
void append(const QString& filename, const QByteArray& data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* read data from a file safely\
|
* read data from a file safely
|
||||||
*/
|
*/
|
||||||
QByteArray read(const QString& filename);
|
QByteArray read(const QString& filename);
|
||||||
|
|
||||||
|
|
@ -91,7 +91,13 @@ bool ensureFilePathExists(QString filenamepath);
|
||||||
* Creates all the folders in a path for the specified path
|
* Creates all the folders in a path for the specified path
|
||||||
* last segment of the path is treated as a folder name and is created!
|
* last segment of the path is treated as a folder name and is created!
|
||||||
*/
|
*/
|
||||||
bool ensureFolderPathExists(QString filenamepath);
|
bool ensureFolderPathExists(const QFileInfo folderPath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates all the folders in a path for the specified path
|
||||||
|
* last segment of the path is treated as a folder name and is created!
|
||||||
|
*/
|
||||||
|
bool ensureFolderPathExists(const QString folderPathName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copies a directory and it's contents from src to dest
|
* @brief Copies a directory and it's contents from src to dest
|
||||||
|
|
@ -234,6 +240,7 @@ class create_link : public QObject {
|
||||||
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||||
|
|
||||||
int totalLinked() { return m_linked; }
|
int totalLinked() { return m_linked; }
|
||||||
|
int totalToLink() { return static_cast<int>(m_links_to_make.size()); }
|
||||||
|
|
||||||
void runPrivileged() { runPrivileged(QString()); }
|
void runPrivileged() { runPrivileged(QString()); }
|
||||||
void runPrivileged(const QString& offset);
|
void runPrivileged(const QString& offset);
|
||||||
|
|
@ -336,6 +343,8 @@ QString NormalizePath(QString path);
|
||||||
|
|
||||||
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith = '-');
|
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith = '-');
|
||||||
|
|
||||||
|
QString RemoveInvalidPathChars(QString string, QChar replaceWith = '-');
|
||||||
|
|
||||||
QString DirNameFromString(QString string, QString inDir = ".");
|
QString DirNameFromString(QString string, QString inDir = ".");
|
||||||
|
|
||||||
/// Checks if the a given Path contains "!"
|
/// Checks if the a given Path contains "!"
|
||||||
|
|
@ -370,6 +379,7 @@ enum class FilesystemType {
|
||||||
HFSX,
|
HFSX,
|
||||||
FUSEBLK,
|
FUSEBLK,
|
||||||
F2FS,
|
F2FS,
|
||||||
|
BCACHEFS,
|
||||||
UNKNOWN
|
UNKNOWN
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -398,6 +408,7 @@ static const QMap<FilesystemType, QStringList> s_filesystem_type_names = { { Fil
|
||||||
{ FilesystemType::HFSX, { "HFSX" } },
|
{ FilesystemType::HFSX, { "HFSX" } },
|
||||||
{ FilesystemType::FUSEBLK, { "FUSEBLK" } },
|
{ FilesystemType::FUSEBLK, { "FUSEBLK" } },
|
||||||
{ FilesystemType::F2FS, { "F2FS" } },
|
{ FilesystemType::F2FS, { "F2FS" } },
|
||||||
|
{ FilesystemType::BCACHEFS, { "BCACHEFS" } },
|
||||||
{ FilesystemType::UNKNOWN, { "UNKNOWN" } } };
|
{ FilesystemType::UNKNOWN, { "UNKNOWN" } } };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -450,7 +461,7 @@ QString nearestExistentAncestor(const QString& path);
|
||||||
FilesystemInfo statFS(const QString& path);
|
FilesystemInfo statFS(const QString& path);
|
||||||
|
|
||||||
static const QList<FilesystemType> s_clone_filesystems = { FilesystemType::BTRFS, FilesystemType::APFS, FilesystemType::ZFS,
|
static const QList<FilesystemType> s_clone_filesystems = { FilesystemType::BTRFS, FilesystemType::APFS, FilesystemType::ZFS,
|
||||||
FilesystemType::XFS, FilesystemType::REFS };
|
FilesystemType::XFS, FilesystemType::REFS, FilesystemType::BCACHEFS };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief if the Filesystem is reflink/clone capable
|
* @brief if the Filesystem is reflink/clone capable
|
||||||
|
|
@ -545,4 +556,10 @@ bool canLink(const QString& src, const QString& dst);
|
||||||
|
|
||||||
uintmax_t hardLinkCount(const QString& path);
|
uintmax_t hardLinkCount(const QString& path);
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
QString getPathNameInLocal8bit(const QString& file);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QString getUniqueResourceName(const QString& filePath);
|
||||||
|
|
||||||
} // namespace FS
|
} // namespace FS
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,12 @@
|
||||||
#include "Filter.h"
|
#include "Filter.h"
|
||||||
|
|
||||||
Filter::~Filter() {}
|
|
||||||
|
|
||||||
ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern) {}
|
ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern) {}
|
||||||
ContainsFilter::~ContainsFilter() {}
|
|
||||||
bool ContainsFilter::accepts(const QString& value)
|
bool ContainsFilter::accepts(const QString& value)
|
||||||
{
|
{
|
||||||
return value.contains(pattern);
|
return value.contains(pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern) {}
|
ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern) {}
|
||||||
ExactFilter::~ExactFilter() {}
|
|
||||||
bool ExactFilter::accepts(const QString& value)
|
bool ExactFilter::accepts(const QString& value)
|
||||||
{
|
{
|
||||||
return value == pattern;
|
return value == pattern;
|
||||||
|
|
@ -27,10 +23,15 @@ RegexpFilter::RegexpFilter(const QString& regexp, bool invert) : invert(invert)
|
||||||
pattern.setPattern(regexp);
|
pattern.setPattern(regexp);
|
||||||
pattern.optimize();
|
pattern.optimize();
|
||||||
}
|
}
|
||||||
RegexpFilter::~RegexpFilter() {}
|
|
||||||
bool RegexpFilter::accepts(const QString& value)
|
bool RegexpFilter::accepts(const QString& value)
|
||||||
{
|
{
|
||||||
auto match = pattern.match(value);
|
auto match = pattern.match(value);
|
||||||
bool matched = match.hasMatch();
|
bool matched = match.hasMatch();
|
||||||
return invert ? (!matched) : (matched);
|
return invert ? (!matched) : (matched);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExactListFilter::ExactListFilter(const QStringList& pattern) : m_pattern(pattern) {}
|
||||||
|
bool ExactListFilter::accepts(const QString& value)
|
||||||
|
{
|
||||||
|
return m_pattern.isEmpty() || m_pattern.contains(value);
|
||||||
|
}
|
||||||
|
|
@ -5,14 +5,14 @@
|
||||||
|
|
||||||
class Filter {
|
class Filter {
|
||||||
public:
|
public:
|
||||||
virtual ~Filter();
|
virtual ~Filter() = default;
|
||||||
virtual bool accepts(const QString& value) = 0;
|
virtual bool accepts(const QString& value) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ContainsFilter : public Filter {
|
class ContainsFilter : public Filter {
|
||||||
public:
|
public:
|
||||||
ContainsFilter(const QString& pattern);
|
ContainsFilter(const QString& pattern);
|
||||||
virtual ~ContainsFilter();
|
virtual ~ContainsFilter() = default;
|
||||||
bool accepts(const QString& value) override;
|
bool accepts(const QString& value) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -22,7 +22,7 @@ class ContainsFilter : public Filter {
|
||||||
class ExactFilter : public Filter {
|
class ExactFilter : public Filter {
|
||||||
public:
|
public:
|
||||||
ExactFilter(const QString& pattern);
|
ExactFilter(const QString& pattern);
|
||||||
virtual ~ExactFilter();
|
virtual ~ExactFilter() = default;
|
||||||
bool accepts(const QString& value) override;
|
bool accepts(const QString& value) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -32,7 +32,7 @@ class ExactFilter : public Filter {
|
||||||
class ExactIfPresentFilter : public Filter {
|
class ExactIfPresentFilter : public Filter {
|
||||||
public:
|
public:
|
||||||
ExactIfPresentFilter(const QString& pattern);
|
ExactIfPresentFilter(const QString& pattern);
|
||||||
~ExactIfPresentFilter() override = default;
|
virtual ~ExactIfPresentFilter() override = default;
|
||||||
bool accepts(const QString& value) override;
|
bool accepts(const QString& value) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -42,10 +42,20 @@ class ExactIfPresentFilter : public Filter {
|
||||||
class RegexpFilter : public Filter {
|
class RegexpFilter : public Filter {
|
||||||
public:
|
public:
|
||||||
RegexpFilter(const QString& regexp, bool invert);
|
RegexpFilter(const QString& regexp, bool invert);
|
||||||
virtual ~RegexpFilter();
|
virtual ~RegexpFilter() = default;
|
||||||
bool accepts(const QString& value) override;
|
bool accepts(const QString& value) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QRegularExpression pattern;
|
QRegularExpression pattern;
|
||||||
bool invert = false;
|
bool invert = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ExactListFilter : public Filter {
|
||||||
|
public:
|
||||||
|
ExactListFilter(const QStringList& pattern = {});
|
||||||
|
virtual ~ExactListFilter() = default;
|
||||||
|
bool accepts(const QString& value) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QStringList m_pattern;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
#include "InstanceCopyTask.h"
|
#include "InstanceCopyTask.h"
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
|
#include <memory>
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "NullInstance.h"
|
#include "NullInstance.h"
|
||||||
#include "pathmatcher/RegexpMatcher.h"
|
#include "pathmatcher/RegexpMatcher.h"
|
||||||
#include "settings/INISettingsObject.h"
|
#include "settings/INISettingsObject.h"
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs)
|
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs)
|
||||||
{
|
{
|
||||||
|
|
@ -38,38 +40,50 @@ void InstanceCopyTask::executeTask()
|
||||||
{
|
{
|
||||||
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
|
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
|
||||||
|
|
||||||
auto copySaves = [&]() {
|
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] {
|
||||||
QFileInfo mcDir(FS::PathCombine(m_stagingPath, "minecraft"));
|
|
||||||
QFileInfo dotMCDir(FS::PathCombine(m_stagingPath, ".minecraft"));
|
|
||||||
|
|
||||||
QString staging_mc_dir;
|
|
||||||
if (mcDir.exists() && !dotMCDir.exists())
|
|
||||||
staging_mc_dir = mcDir.filePath();
|
|
||||||
else
|
|
||||||
staging_mc_dir = dotMCDir.filePath();
|
|
||||||
|
|
||||||
FS::copy savesCopy(FS::PathCombine(m_origInstance->gameRoot(), "saves"), FS::PathCombine(staging_mc_dir, "saves"));
|
|
||||||
savesCopy.followSymlinks(true);
|
|
||||||
|
|
||||||
return savesCopy();
|
|
||||||
};
|
|
||||||
|
|
||||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this, copySaves] {
|
|
||||||
if (m_useClone) {
|
if (m_useClone) {
|
||||||
FS::clone folderClone(m_origInstance->instanceRoot(), m_stagingPath);
|
FS::clone folderClone(m_origInstance->instanceRoot(), m_stagingPath);
|
||||||
folderClone.matcher(m_matcher.get());
|
folderClone.matcher(m_matcher.get());
|
||||||
|
|
||||||
|
folderClone(true);
|
||||||
|
setProgress(0, folderClone.totalCloned());
|
||||||
|
connect(&folderClone, &FS::clone::fileCloned,
|
||||||
|
[this](QString src, QString dst) { setProgress(m_progress + 1, m_progressTotal); });
|
||||||
return folderClone();
|
return folderClone();
|
||||||
} else if (m_useLinks || m_useHardLinks) {
|
}
|
||||||
|
if (m_useLinks || m_useHardLinks) {
|
||||||
|
std::unique_ptr<FS::copy> savesCopy;
|
||||||
|
if (m_copySaves) {
|
||||||
|
QFileInfo mcDir(FS::PathCombine(m_stagingPath, "minecraft"));
|
||||||
|
QFileInfo dotMCDir(FS::PathCombine(m_stagingPath, ".minecraft"));
|
||||||
|
|
||||||
|
QString staging_mc_dir;
|
||||||
|
if (dotMCDir.exists() && !mcDir.exists())
|
||||||
|
staging_mc_dir = dotMCDir.filePath();
|
||||||
|
else
|
||||||
|
staging_mc_dir = mcDir.filePath();
|
||||||
|
|
||||||
|
savesCopy = std::make_unique<FS::copy>(FS::PathCombine(m_origInstance->gameRoot(), "saves"),
|
||||||
|
FS::PathCombine(staging_mc_dir, "saves"));
|
||||||
|
savesCopy->followSymlinks(true);
|
||||||
|
(*savesCopy)(true);
|
||||||
|
setProgress(0, savesCopy->totalCopied());
|
||||||
|
connect(savesCopy.get(), &FS::copy::fileCopied, [this](QString src) { setProgress(m_progress + 1, m_progressTotal); });
|
||||||
|
}
|
||||||
FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath);
|
FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath);
|
||||||
int depth = m_linkRecursively ? -1 : 0; // we need to at least link the top level instead of the instance folder
|
int depth = m_linkRecursively ? -1 : 0; // we need to at least link the top level instead of the instance folder
|
||||||
folderLink.linkRecursively(true).setMaxDepth(depth).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
|
folderLink.linkRecursively(true).setMaxDepth(depth).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
|
||||||
|
|
||||||
|
folderLink(true);
|
||||||
|
setProgress(0, m_progressTotal + folderLink.totalToLink());
|
||||||
|
connect(&folderLink, &FS::create_link::fileLinked,
|
||||||
|
[this](QString src, QString dst) { setProgress(m_progress + 1, m_progressTotal); });
|
||||||
bool there_were_errors = false;
|
bool there_were_errors = false;
|
||||||
|
|
||||||
if (!folderLink()) {
|
if (!folderLink()) {
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
if (!m_useHardLinks) {
|
if (!m_useHardLinks) {
|
||||||
|
setProgress(0, m_progressTotal);
|
||||||
qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
|
qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
|
||||||
|
|
||||||
qDebug() << "attempting to run with privelage";
|
qDebug() << "attempting to run with privelage";
|
||||||
|
|
@ -77,7 +91,7 @@ void InstanceCopyTask::executeTask()
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
bool got_priv_results = false;
|
bool got_priv_results = false;
|
||||||
|
|
||||||
connect(&folderLink, &FS::create_link::finishedPrivileged, this, [&](bool gotResults) {
|
connect(&folderLink, &FS::create_link::finishedPrivileged, this, [&got_priv_results, &loop](bool gotResults) {
|
||||||
if (!gotResults) {
|
if (!gotResults) {
|
||||||
qDebug() << "Privileged run exited without results!";
|
qDebug() << "Privileged run exited without results!";
|
||||||
}
|
}
|
||||||
|
|
@ -94,13 +108,11 @@ void InstanceCopyTask::executeTask()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_copySaves) {
|
if (savesCopy) {
|
||||||
there_were_errors |= !copySaves();
|
there_were_errors |= !(*savesCopy)();
|
||||||
}
|
}
|
||||||
|
|
||||||
return got_priv_results && !there_were_errors;
|
return got_priv_results && !there_were_errors;
|
||||||
} else {
|
|
||||||
qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
|
qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
|
||||||
|
|
@ -108,17 +120,19 @@ void InstanceCopyTask::executeTask()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_copySaves) {
|
if (savesCopy) {
|
||||||
there_were_errors |= !copySaves();
|
there_were_errors |= !(*savesCopy)();
|
||||||
}
|
}
|
||||||
|
|
||||||
return !there_were_errors;
|
return !there_were_errors;
|
||||||
} else {
|
|
||||||
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
|
|
||||||
folderCopy.followSymlinks(false).matcher(m_matcher.get());
|
|
||||||
|
|
||||||
return folderCopy();
|
|
||||||
}
|
}
|
||||||
|
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
|
||||||
|
folderCopy.followSymlinks(false).matcher(m_matcher.get());
|
||||||
|
|
||||||
|
folderCopy(true);
|
||||||
|
setProgress(0, folderCopy.totalCopied());
|
||||||
|
connect(&folderCopy, &FS::copy::fileCopied, [this](QString src) { setProgress(m_progress + 1, m_progressTotal); });
|
||||||
|
return folderCopy();
|
||||||
});
|
});
|
||||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished);
|
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished);
|
||||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &InstanceCopyTask::copyAborted);
|
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &InstanceCopyTask::copyAborted);
|
||||||
|
|
@ -142,9 +156,8 @@ void InstanceCopyTask::copyFinished()
|
||||||
if (!m_keepPlaytime) {
|
if (!m_keepPlaytime) {
|
||||||
inst->resetTimePlayed();
|
inst->resetTimePlayed();
|
||||||
}
|
}
|
||||||
if (m_useLinks)
|
|
||||||
inst->addLinkedInstanceId(m_origInstance->id());
|
|
||||||
if (m_useLinks) {
|
if (m_useLinks) {
|
||||||
|
inst->addLinkedInstanceId(m_origInstance->id());
|
||||||
auto allowed_symlinks_file = QFileInfo(FS::PathCombine(inst->gameRoot(), "allowed_symlinks.txt"));
|
auto allowed_symlinks_file = QFileInfo(FS::PathCombine(inst->gameRoot(), "allowed_symlinks.txt"));
|
||||||
|
|
||||||
QByteArray allowed_symlinks;
|
QByteArray allowed_symlinks;
|
||||||
|
|
@ -160,7 +173,11 @@ void InstanceCopyTask::copyFinished()
|
||||||
allowed_symlinks_file
|
allowed_symlinks_file
|
||||||
.filePath()); // we dont want to modify the original. also make sure the resulting file is not itself a link.
|
.filePath()); // we dont want to modify the original. also make sure the resulting file is not itself a link.
|
||||||
|
|
||||||
FS::write(allowed_symlinks_file.filePath(), allowed_symlinks);
|
try {
|
||||||
|
FS::write(allowed_symlinks_file.filePath(), allowed_symlinks);
|
||||||
|
} catch (const FS::FileSystemException& e) {
|
||||||
|
qCritical() << "Failed to write symlink :" << e.cause();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
|
|
@ -171,3 +188,14 @@ void InstanceCopyTask::copyAborted()
|
||||||
emitFailed(tr("Instance folder copy has been aborted."));
|
emitFailed(tr("Instance folder copy has been aborted."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyTask::abort()
|
||||||
|
{
|
||||||
|
if (m_copyFutureWatcher.isRunning()) {
|
||||||
|
m_copyFutureWatcher.cancel();
|
||||||
|
// NOTE: Here we don't do `emitAborted()` because it will be done when `m_copyFutureWatcher` actually cancels, which may not occur
|
||||||
|
// immediately.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,7 @@ class InstanceCopyTask : public InstanceTask {
|
||||||
protected:
|
protected:
|
||||||
//! Entry point for tasks.
|
//! Entry point for tasks.
|
||||||
virtual void executeTask() override;
|
virtual void executeTask() override;
|
||||||
|
bool abort() override;
|
||||||
void copyFinished();
|
void copyFinished();
|
||||||
void copyAborted();
|
void copyAborted();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include "FileSystem.h"
|
||||||
InstanceCreationTask::InstanceCreationTask() = default;
|
|
||||||
|
|
||||||
void InstanceCreationTask::executeTask()
|
void InstanceCreationTask::executeTask()
|
||||||
{
|
{
|
||||||
|
|
@ -39,22 +38,29 @@ void InstanceCreationTask::executeTask()
|
||||||
// files scheduled to, and we'd better not let the user abort in the middle of it, since it'd
|
// files scheduled to, and we'd better not let the user abort in the middle of it, since it'd
|
||||||
// put the instance in an invalid state.
|
// put the instance in an invalid state.
|
||||||
if (shouldOverride()) {
|
if (shouldOverride()) {
|
||||||
|
bool deleteFailed = false;
|
||||||
|
|
||||||
setAbortable(false);
|
setAbortable(false);
|
||||||
setStatus(tr("Removing old conflicting files..."));
|
setStatus(tr("Removing old conflicting files..."));
|
||||||
qDebug() << "Removing old files";
|
qDebug() << "Removing old files";
|
||||||
|
|
||||||
for (auto path : m_files_to_remove) {
|
for (const QString& path : m_files_to_remove) {
|
||||||
if (!QFile::exists(path))
|
if (!QFile::exists(path))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
qDebug() << "Removing" << path;
|
qDebug() << "Removing" << path;
|
||||||
|
|
||||||
if (!QFile::remove(path)) {
|
if (!QFile::remove(path)) {
|
||||||
qCritical() << "Couldn't remove the old conflicting files.";
|
qCritical() << "Could not remove" << path;
|
||||||
emitFailed(tr("Failed to remove old conflicting files."));
|
deleteFailed = true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
emitSucceeded();
|
if (deleteFailed) {
|
||||||
return;
|
emitFailed(tr("Failed to remove old conflicting files."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!m_abort)
|
||||||
|
emitSucceeded();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
class InstanceCreationTask : public InstanceTask {
|
class InstanceCreationTask : public InstanceTask {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
InstanceCreationTask();
|
InstanceCreationTask() = default;
|
||||||
virtual ~InstanceCreationTask() = default;
|
virtual ~InstanceCreationTask() = default;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@
|
||||||
|
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <quazip/quazipdir.h>
|
#include <quazip/quazipdir.h>
|
||||||
|
|
||||||
|
|
@ -68,16 +69,11 @@ bool InstanceImportTask::abort()
|
||||||
if (!canAbort())
|
if (!canAbort())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (m_filesNetJob)
|
bool wasAborted = false;
|
||||||
m_filesNetJob->abort();
|
if (m_task)
|
||||||
if (m_extractFuture.isRunning()) {
|
wasAborted = m_task->abort();
|
||||||
// NOTE: The tasks created by QtConcurrent::run() can't actually get cancelled,
|
Task::abort();
|
||||||
// but we can use this call to check the state when the extraction finishes.
|
return wasAborted;
|
||||||
m_extractFuture.cancel();
|
|
||||||
m_extractFuture.waitForFinished();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task::abort();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::executeTask()
|
void InstanceImportTask::executeTask()
|
||||||
|
|
@ -89,7 +85,6 @@ void InstanceImportTask::executeTask()
|
||||||
processZipPack();
|
processZipPack();
|
||||||
} else {
|
} else {
|
||||||
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
||||||
m_downloadRequired = true;
|
|
||||||
|
|
||||||
downloadFromUrl();
|
downloadFromUrl();
|
||||||
}
|
}
|
||||||
|
|
@ -97,115 +92,133 @@ void InstanceImportTask::executeTask()
|
||||||
|
|
||||||
void InstanceImportTask::downloadFromUrl()
|
void InstanceImportTask::downloadFromUrl()
|
||||||
{
|
{
|
||||||
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
|
const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path());
|
||||||
|
|
||||||
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
||||||
entry->setStale(true);
|
entry->setStale(true);
|
||||||
m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network()));
|
|
||||||
m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry));
|
|
||||||
m_archivePath = entry->getFullPath();
|
m_archivePath = entry->getFullPath();
|
||||||
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
auto filesNetJob = makeShared<NetJob>(tr("Modpack download"), APPLICATION->network());
|
||||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry));
|
||||||
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
connect(filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::processZipPack);
|
||||||
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
|
connect(filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::setProgress);
|
||||||
m_filesNetJob->start();
|
connect(filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||||
|
connect(filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::emitFailed);
|
||||||
|
connect(filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::emitAborted);
|
||||||
|
m_task.reset(filesNetJob);
|
||||||
|
filesNetJob->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::downloadSucceeded()
|
QString InstanceImportTask::getRootFromZip(QuaZip* zip, const QString& root)
|
||||||
{
|
{
|
||||||
processZipPack();
|
if (!isRunning()) {
|
||||||
m_filesNetJob.reset();
|
return {};
|
||||||
}
|
}
|
||||||
|
QuaZipDir rootDir(zip, root);
|
||||||
|
for (auto&& fileName : rootDir.entryList(QDir::Files)) {
|
||||||
|
setDetails(fileName);
|
||||||
|
if (fileName == "instance.cfg") {
|
||||||
|
qDebug() << "MultiMC:" << true;
|
||||||
|
m_modpackType = ModpackType::MultiMC;
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
if (fileName == "manifest.json") {
|
||||||
|
qDebug() << "Flame:" << true;
|
||||||
|
m_modpackType = ModpackType::Flame;
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
void InstanceImportTask::downloadFailed(QString reason)
|
QCoreApplication::processEvents();
|
||||||
{
|
}
|
||||||
emitFailed(reason);
|
|
||||||
m_filesNetJob.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
|
// Recurse the search to non-ignored subfolders
|
||||||
{
|
for (auto&& fileName : rootDir.entryList(QDir::Dirs)) {
|
||||||
setProgress(current, total);
|
if ("overrides/" == fileName)
|
||||||
}
|
continue;
|
||||||
|
|
||||||
void InstanceImportTask::downloadAborted()
|
QString result = getRootFromZip(zip, root + fileName);
|
||||||
{
|
if (!result.isEmpty())
|
||||||
emitAborted();
|
return result;
|
||||||
m_filesNetJob.reset();
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::processZipPack()
|
void InstanceImportTask::processZipPack()
|
||||||
{
|
{
|
||||||
setStatus(tr("Extracting modpack"));
|
setStatus(tr("Attempting to determine instance type"));
|
||||||
QDir extractDir(m_stagingPath);
|
QDir extractDir(m_stagingPath);
|
||||||
qDebug() << "Attempting to create instance from" << m_archivePath;
|
qDebug() << "Attempting to create instance from" << m_archivePath;
|
||||||
|
|
||||||
// open the zip and find relevant files in it
|
// open the zip and find relevant files in it
|
||||||
m_packZip.reset(new QuaZip(m_archivePath));
|
auto packZip = std::make_shared<QuaZip>(m_archivePath);
|
||||||
if (!m_packZip->open(QuaZip::mdUnzip)) {
|
if (!packZip->open(QuaZip::mdUnzip)) {
|
||||||
emitFailed(tr("Unable to open supplied modpack zip file."));
|
emitFailed(tr("Unable to open supplied modpack zip file."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QuaZipDir packZipDir(m_packZip.get());
|
QuaZipDir packZipDir(packZip.get());
|
||||||
|
qDebug() << "Attempting to determine instance type";
|
||||||
|
|
||||||
// https://docs.modrinth.com/docs/modpacks/format_definition/#storage
|
|
||||||
bool modrinthFound = packZipDir.exists("/modrinth.index.json");
|
|
||||||
bool technicFound = packZipDir.exists("/bin/modpack.jar") || packZipDir.exists("/bin/version.json");
|
|
||||||
QString root;
|
QString root;
|
||||||
|
|
||||||
// NOTE: Prioritize modpack platforms that aren't searched for recursively.
|
// NOTE: Prioritize modpack platforms that aren't searched for recursively.
|
||||||
// Especially Flame has a very common filename for its manifest, which may appear inside overrides for example
|
// Especially Flame has a very common filename for its manifest, which may appear inside overrides for example
|
||||||
if (modrinthFound) {
|
// https://docs.modrinth.com/docs/modpacks/format_definition/#storage
|
||||||
|
if (packZipDir.exists("/modrinth.index.json")) {
|
||||||
// process as Modrinth pack
|
// process as Modrinth pack
|
||||||
qDebug() << "Modrinth:" << modrinthFound;
|
qDebug() << "Modrinth:" << true;
|
||||||
m_modpackType = ModpackType::Modrinth;
|
m_modpackType = ModpackType::Modrinth;
|
||||||
} else if (technicFound) {
|
} else if (packZipDir.exists("/bin/modpack.jar") || packZipDir.exists("/bin/version.json")) {
|
||||||
// process as Technic pack
|
// process as Technic pack
|
||||||
qDebug() << "Technic:" << technicFound;
|
qDebug() << "Technic:" << true;
|
||||||
extractDir.mkpath(".minecraft");
|
extractDir.mkpath("minecraft");
|
||||||
extractDir.cd(".minecraft");
|
extractDir.cd("minecraft");
|
||||||
m_modpackType = ModpackType::Technic;
|
m_modpackType = ModpackType::Technic;
|
||||||
} else {
|
} else {
|
||||||
QStringList paths_to_ignore{ "overrides/" };
|
root = getRootFromZip(packZip.get());
|
||||||
|
setDetails("");
|
||||||
if (QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg", paths_to_ignore); !mmcRoot.isNull()) {
|
|
||||||
// process as MultiMC instance/pack
|
|
||||||
qDebug() << "MultiMC:" << mmcRoot;
|
|
||||||
root = mmcRoot;
|
|
||||||
m_modpackType = ModpackType::MultiMC;
|
|
||||||
} else if (QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json", paths_to_ignore);
|
|
||||||
!flameRoot.isNull()) {
|
|
||||||
// process as Flame pack
|
|
||||||
qDebug() << "Flame:" << flameRoot;
|
|
||||||
root = flameRoot;
|
|
||||||
m_modpackType = ModpackType::Flame;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (m_modpackType == ModpackType::Unknown) {
|
if (m_modpackType == ModpackType::Unknown) {
|
||||||
emitFailed(tr("Archive does not contain a recognized modpack type."));
|
emitFailed(tr("Archive does not contain a recognized modpack type."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setStatus(tr("Extracting modpack"));
|
||||||
|
|
||||||
// make sure we extract just the pack
|
// make sure we extract just the pack
|
||||||
m_extractFuture =
|
auto zipTask = makeShared<MMCZip::ExtractZipTask>(packZip, extractDir, root);
|
||||||
QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
|
|
||||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
|
auto progressStep = std::make_shared<TaskStepProgress>();
|
||||||
m_extractFutureWatcher.setFuture(m_extractFuture);
|
connect(zipTask.get(), &Task::finished, this, [this, progressStep] {
|
||||||
|
progressStep->state = TaskStepState::Succeeded;
|
||||||
|
stepProgress(*progressStep);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(zipTask.get(), &Task::succeeded, this, &InstanceImportTask::extractFinished, Qt::QueuedConnection);
|
||||||
|
connect(zipTask.get(), &Task::aborted, this, &InstanceImportTask::emitAborted);
|
||||||
|
connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) {
|
||||||
|
progressStep->state = TaskStepState::Failed;
|
||||||
|
stepProgress(*progressStep);
|
||||||
|
emitFailed(reason);
|
||||||
|
});
|
||||||
|
connect(zipTask.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||||
|
|
||||||
|
connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) {
|
||||||
|
progressStep->update(current, total);
|
||||||
|
stepProgress(*progressStep);
|
||||||
|
});
|
||||||
|
connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) {
|
||||||
|
progressStep->status = status;
|
||||||
|
stepProgress(*progressStep);
|
||||||
|
});
|
||||||
|
m_task.reset(zipTask);
|
||||||
|
zipTask->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::extractFinished()
|
void InstanceImportTask::extractFinished()
|
||||||
{
|
{
|
||||||
m_packZip.reset();
|
setAbortable(false);
|
||||||
|
|
||||||
if (m_extractFuture.isCanceled())
|
|
||||||
return;
|
|
||||||
if (!m_extractFuture.result().has_value()) {
|
|
||||||
emitFailed(tr("Failed to extract modpack"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QDir extractDir(m_stagingPath);
|
QDir extractDir(m_stagingPath);
|
||||||
|
|
||||||
qDebug() << "Fixing permissions for extracted pack files...";
|
qDebug() << "Fixing permissions for extracted pack files...";
|
||||||
|
|
@ -279,8 +292,11 @@ void InstanceImportTask::processFlame()
|
||||||
inst_creation_task->setGroup(m_instGroup);
|
inst_creation_task->setGroup(m_instGroup);
|
||||||
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
|
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
|
||||||
|
|
||||||
connect(inst_creation_task.get(), &Task::succeeded, this, [this, inst_creation_task] {
|
auto weak = inst_creation_task.toWeakRef();
|
||||||
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
|
connect(inst_creation_task.get(), &Task::succeeded, this, [this, weak] {
|
||||||
|
if (auto sp = weak.lock()) {
|
||||||
|
setOverride(sp->shouldOverride(), sp->originalInstanceID());
|
||||||
|
}
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
});
|
});
|
||||||
connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed);
|
connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||||
|
|
@ -289,11 +305,12 @@ void InstanceImportTask::processFlame()
|
||||||
connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus);
|
connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus);
|
||||||
connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails);
|
connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails);
|
||||||
|
|
||||||
connect(this, &Task::aborted, inst_creation_task.get(), &InstanceCreationTask::abort);
|
|
||||||
connect(inst_creation_task.get(), &Task::aborted, this, &Task::abort);
|
connect(inst_creation_task.get(), &Task::aborted, this, &Task::abort);
|
||||||
connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
|
connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
|
||||||
|
|
||||||
inst_creation_task->start();
|
m_task.reset(inst_creation_task);
|
||||||
|
setAbortable(true);
|
||||||
|
m_task->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::processTechnic()
|
void InstanceImportTask::processTechnic()
|
||||||
|
|
@ -324,13 +341,15 @@ void InstanceImportTask::processMultiMC()
|
||||||
m_instIcon = instance.iconKey();
|
m_instIcon = instance.iconKey();
|
||||||
|
|
||||||
auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon);
|
auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon);
|
||||||
|
if (importIconPath.isNull() || !QFile::exists(importIconPath))
|
||||||
|
importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), "icon.png");
|
||||||
if (!importIconPath.isNull() && QFile::exists(importIconPath)) {
|
if (!importIconPath.isNull() && QFile::exists(importIconPath)) {
|
||||||
// import icon
|
// import icon
|
||||||
auto iconList = APPLICATION->icons();
|
auto iconList = APPLICATION->icons();
|
||||||
if (iconList->iconFileExists(m_instIcon)) {
|
if (iconList->iconFileExists(m_instIcon)) {
|
||||||
iconList->deleteIcon(m_instIcon);
|
iconList->deleteIcon(m_instIcon);
|
||||||
}
|
}
|
||||||
iconList->installIcons({ importIconPath });
|
iconList->installIcon(importIconPath, m_instIcon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
|
|
@ -338,7 +357,7 @@ void InstanceImportTask::processMultiMC()
|
||||||
|
|
||||||
void InstanceImportTask::processModrinth()
|
void InstanceImportTask::processModrinth()
|
||||||
{
|
{
|
||||||
ModrinthCreationTask* inst_creation_task = nullptr;
|
shared_qobject_ptr<ModrinthCreationTask> inst_creation_task = nullptr;
|
||||||
if (!m_extra_info.isEmpty()) {
|
if (!m_extra_info.isEmpty()) {
|
||||||
auto pack_id_it = m_extra_info.constFind("pack_id");
|
auto pack_id_it = m_extra_info.constFind("pack_id");
|
||||||
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
|
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
|
||||||
|
|
@ -355,7 +374,7 @@ void InstanceImportTask::processModrinth()
|
||||||
original_instance_id = original_instance_id_it.value();
|
original_instance_id = original_instance_id_it.value();
|
||||||
|
|
||||||
inst_creation_task =
|
inst_creation_task =
|
||||||
new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
makeShared<ModrinthCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
||||||
} else {
|
} else {
|
||||||
QString pack_id;
|
QString pack_id;
|
||||||
if (!m_sourceUrl.isEmpty()) {
|
if (!m_sourceUrl.isEmpty()) {
|
||||||
|
|
@ -364,7 +383,7 @@ void InstanceImportTask::processModrinth()
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Find a way to get the ID in directly imported ZIPs
|
// FIXME: Find a way to get the ID in directly imported ZIPs
|
||||||
inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id);
|
inst_creation_task = makeShared<ModrinthCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
inst_creation_task->setName(*this);
|
inst_creation_task->setName(*this);
|
||||||
|
|
@ -372,20 +391,23 @@ void InstanceImportTask::processModrinth()
|
||||||
inst_creation_task->setGroup(m_instGroup);
|
inst_creation_task->setGroup(m_instGroup);
|
||||||
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
|
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
|
||||||
|
|
||||||
connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] {
|
auto weak = inst_creation_task.toWeakRef();
|
||||||
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
|
connect(inst_creation_task.get(), &Task::succeeded, this, [this, weak] {
|
||||||
|
if (auto sp = weak.lock()) {
|
||||||
|
setOverride(sp->shouldOverride(), sp->originalInstanceID());
|
||||||
|
}
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
});
|
});
|
||||||
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
|
connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||||
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress);
|
connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress);
|
||||||
connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||||
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus);
|
connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus);
|
||||||
connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails);
|
connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails);
|
||||||
connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater);
|
|
||||||
|
|
||||||
connect(this, &Task::aborted, inst_creation_task, &InstanceCreationTask::abort);
|
connect(inst_creation_task.get(), &Task::aborted, this, &Task::abort);
|
||||||
connect(inst_creation_task, &Task::aborted, this, &Task::abort);
|
connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
|
||||||
connect(inst_creation_task, &Task::abortStatusChanged, this, &Task::setAbortable);
|
|
||||||
|
|
||||||
inst_creation_task->start();
|
m_task.reset(inst_creation_task);
|
||||||
|
setAbortable(true);
|
||||||
|
m_task->start();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,54 +39,35 @@
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include "InstanceTask.h"
|
#include "InstanceTask.h"
|
||||||
#include "QObjectPtr.h"
|
|
||||||
#include "modplatform/flame/PackManifest.h"
|
|
||||||
#include "net/NetJob.h"
|
|
||||||
#include "settings/SettingsObject.h"
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
class QuaZip;
|
class QuaZip;
|
||||||
namespace Flame {
|
|
||||||
class FileResolvingTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
class InstanceImportTask : public InstanceTask {
|
class InstanceImportTask : public InstanceTask {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit InstanceImportTask(const QUrl& sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {});
|
explicit InstanceImportTask(const QUrl& sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {});
|
||||||
|
virtual ~InstanceImportTask() = default;
|
||||||
bool abort() override;
|
bool abort() override;
|
||||||
const QVector<Flame::File>& getBlockedFiles() const { return m_blockedMods; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//! Entry point for tasks.
|
//! Entry point for tasks.
|
||||||
virtual void executeTask() override;
|
virtual void executeTask() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void processZipPack();
|
|
||||||
void processMultiMC();
|
void processMultiMC();
|
||||||
void processTechnic();
|
void processTechnic();
|
||||||
void processFlame();
|
void processFlame();
|
||||||
void processModrinth();
|
void processModrinth();
|
||||||
|
QString getRootFromZip(QuaZip* zip, const QString& root = "");
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void downloadSucceeded();
|
void processZipPack();
|
||||||
void downloadFailed(QString reason);
|
|
||||||
void downloadProgressChanged(qint64 current, qint64 total);
|
|
||||||
void downloadAborted();
|
|
||||||
void extractFinished();
|
void extractFinished();
|
||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
NetJob::Ptr m_filesNetJob;
|
|
||||||
shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver;
|
|
||||||
QUrl m_sourceUrl;
|
QUrl m_sourceUrl;
|
||||||
QString m_archivePath;
|
QString m_archivePath;
|
||||||
bool m_downloadRequired = false;
|
Task::Ptr m_task;
|
||||||
std::unique_ptr<QuaZip> m_packZip;
|
|
||||||
QFuture<std::optional<QStringList>> m_extractFuture;
|
|
||||||
QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher;
|
|
||||||
QVector<Flame::File> m_blockedMods;
|
|
||||||
enum class ModpackType {
|
enum class ModpackType {
|
||||||
Unknown,
|
Unknown,
|
||||||
MultiMC,
|
MultiMC,
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
#include <QFileSystemWatcher>
|
#include <QFileSystemWatcher>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
|
@ -371,13 +372,13 @@ void InstanceList::undoTrashInstance()
|
||||||
|
|
||||||
auto top = m_trashHistory.pop();
|
auto top = m_trashHistory.pop();
|
||||||
|
|
||||||
while (QDir(top.polyPath).exists()) {
|
while (QDir(top.path).exists()) {
|
||||||
top.id += "1";
|
top.id += "1";
|
||||||
top.polyPath += "1";
|
top.path += "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "Moving" << top.trashPath << "back to" << top.polyPath;
|
qDebug() << "Moving" << top.trashPath << "back to" << top.path;
|
||||||
QFile(top.trashPath).rename(top.polyPath);
|
QFile(top.trashPath).rename(top.path);
|
||||||
|
|
||||||
m_instanceGroupIndex[top.id] = top.groupName;
|
m_instanceGroupIndex[top.id] = top.groupName;
|
||||||
increaseGroupCount(top.groupName);
|
increaseGroupCount(top.groupName);
|
||||||
|
|
@ -486,7 +487,7 @@ InstanceList::InstListError InstanceList::loadList()
|
||||||
int front_bookmark = -1;
|
int front_bookmark = -1;
|
||||||
int back_bookmark = -1;
|
int back_bookmark = -1;
|
||||||
int currentItem = -1;
|
int currentItem = -1;
|
||||||
auto removeNow = [&]() {
|
auto removeNow = [this, &front_bookmark, &back_bookmark, ¤tItem]() {
|
||||||
beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark);
|
beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark);
|
||||||
m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1);
|
m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1);
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
|
|
@ -634,8 +635,8 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id)
|
||||||
|
|
||||||
QString inst_type = instanceSettings->get("InstanceType").toString();
|
QString inst_type = instanceSettings->get("InstanceType").toString();
|
||||||
|
|
||||||
// NOTE: Some PolyMC versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a OneSix
|
// NOTE: Some launcher versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a
|
||||||
// instance
|
// OneSix instance
|
||||||
if (inst_type == "OneSix" || inst_type.isEmpty()) {
|
if (inst_type == "OneSix" || inst_type.isEmpty()) {
|
||||||
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
|
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -709,6 +710,12 @@ void InstanceList::saveGroupList()
|
||||||
groupsArr.insert(name, groupObj);
|
groupsArr.insert(name, groupObj);
|
||||||
}
|
}
|
||||||
toplevel.insert("groups", groupsArr);
|
toplevel.insert("groups", groupsArr);
|
||||||
|
// empty string represents ungrouped "group"
|
||||||
|
if (m_collapsedGroups.contains("")) {
|
||||||
|
QJsonObject ungrouped;
|
||||||
|
ungrouped.insert("hidden", QJsonValue(true));
|
||||||
|
toplevel.insert("ungrouped", ungrouped);
|
||||||
|
}
|
||||||
QJsonDocument doc(toplevel);
|
QJsonDocument doc(toplevel);
|
||||||
try {
|
try {
|
||||||
FS::write(groupFileName, doc.toJson());
|
FS::write(groupFileName, doc.toJson());
|
||||||
|
|
@ -804,6 +811,16 @@ void InstanceList::loadGroupList()
|
||||||
increaseGroupCount(groupName);
|
increaseGroupCount(groupName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ungroupedHidden = false;
|
||||||
|
if (rootObj.value("ungrouped").isObject()) {
|
||||||
|
QJsonObject ungrouped = rootObj.value("ungrouped").toObject();
|
||||||
|
ungroupedHidden = ungrouped.value("hidden").toBool(false);
|
||||||
|
}
|
||||||
|
if (ungroupedHidden) {
|
||||||
|
// empty string represents ungrouped "group"
|
||||||
|
m_collapsedGroups.insert("");
|
||||||
|
}
|
||||||
m_groupsLoaded = true;
|
m_groupsLoaded = true;
|
||||||
qDebug() << "Group list loaded.";
|
qDebug() << "Group list loaded.";
|
||||||
}
|
}
|
||||||
|
|
@ -847,14 +864,16 @@ class InstanceStaging : public Task {
|
||||||
const unsigned maxBackoff = 16;
|
const unsigned maxBackoff = 16;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InstanceStaging(InstanceList* parent, InstanceTask* child, QString stagingPath, InstanceName const& instanceName, QString groupName)
|
InstanceStaging(InstanceList* parent, InstanceTask* child, SettingsObjectPtr settings)
|
||||||
: m_parent(parent)
|
: m_parent(parent), backoff(minBackoff, maxBackoff)
|
||||||
, backoff(minBackoff, maxBackoff)
|
|
||||||
, m_stagingPath(std::move(stagingPath))
|
|
||||||
, m_instance_name(std::move(instanceName))
|
|
||||||
, m_groupName(std::move(groupName))
|
|
||||||
{
|
{
|
||||||
|
m_stagingPath = parent->getStagedInstancePath();
|
||||||
|
|
||||||
m_child.reset(child);
|
m_child.reset(child);
|
||||||
|
|
||||||
|
m_child->setStagingPath(m_stagingPath);
|
||||||
|
m_child->setParentSettings(std::move(settings));
|
||||||
|
|
||||||
connect(child, &Task::succeeded, this, &InstanceStaging::childSucceeded);
|
connect(child, &Task::succeeded, this, &InstanceStaging::childSucceeded);
|
||||||
connect(child, &Task::failed, this, &InstanceStaging::childFailed);
|
connect(child, &Task::failed, this, &InstanceStaging::childFailed);
|
||||||
connect(child, &Task::aborted, this, &InstanceStaging::childAborted);
|
connect(child, &Task::aborted, this, &InstanceStaging::childAborted);
|
||||||
|
|
@ -866,7 +885,7 @@ class InstanceStaging : public Task {
|
||||||
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceeded);
|
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceeded);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~InstanceStaging(){};
|
virtual ~InstanceStaging() {}
|
||||||
|
|
||||||
// FIXME/TODO: add ability to abort during instance commit retries
|
// FIXME/TODO: add ability to abort during instance commit retries
|
||||||
bool abort() override
|
bool abort() override
|
||||||
|
|
@ -881,14 +900,22 @@ class InstanceStaging : public Task {
|
||||||
bool canAbort() const override { return (m_child && m_child->canAbort()); }
|
bool canAbort() const override { return (m_child && m_child->canAbort()); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void executeTask() override { m_child->start(); }
|
virtual void executeTask() override
|
||||||
|
{
|
||||||
|
if (m_stagingPath.isNull()) {
|
||||||
|
emitFailed(tr("Could not create staging folder"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_child->start();
|
||||||
|
}
|
||||||
QStringList warnings() const override { return m_child->warnings(); }
|
QStringList warnings() const override { return m_child->warnings(); }
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void childSucceeded()
|
void childSucceeded()
|
||||||
{
|
{
|
||||||
unsigned sleepTime = backoff();
|
unsigned sleepTime = backoff();
|
||||||
if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get())) {
|
if (m_parent->commitStagedInstance(m_stagingPath, *m_child.get(), m_child->group(), *m_child.get())) {
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -897,7 +924,7 @@ class InstanceStaging : public Task {
|
||||||
emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something."));
|
emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
qDebug() << "Failed to commit instance" << m_instance_name.name() << "Initiating backoff:" << sleepTime;
|
qDebug() << "Failed to commit instance" << m_child->name() << "Initiating backoff:" << sleepTime;
|
||||||
m_backoffTimer.start(sleepTime * 500);
|
m_backoffTimer.start(sleepTime * 500);
|
||||||
}
|
}
|
||||||
void childFailed(const QString& reason)
|
void childFailed(const QString& reason)
|
||||||
|
|
@ -906,7 +933,11 @@ class InstanceStaging : public Task {
|
||||||
emitFailed(reason);
|
emitFailed(reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
void childAborted() { emitAborted(); }
|
void childAborted()
|
||||||
|
{
|
||||||
|
m_parent->destroyStagingPath(m_stagingPath);
|
||||||
|
emitAborted();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
InstanceList* m_parent;
|
InstanceList* m_parent;
|
||||||
|
|
@ -918,34 +949,35 @@ class InstanceStaging : public Task {
|
||||||
ExponentialSeries backoff;
|
ExponentialSeries backoff;
|
||||||
QString m_stagingPath;
|
QString m_stagingPath;
|
||||||
unique_qobject_ptr<InstanceTask> m_child;
|
unique_qobject_ptr<InstanceTask> m_child;
|
||||||
InstanceName m_instance_name;
|
|
||||||
QString m_groupName;
|
|
||||||
QTimer m_backoffTimer;
|
QTimer m_backoffTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
Task* InstanceList::wrapInstanceTask(InstanceTask* task)
|
Task* InstanceList::wrapInstanceTask(InstanceTask* task)
|
||||||
{
|
{
|
||||||
auto stagingPath = getStagedInstancePath();
|
return new InstanceStaging(this, task, m_globalSettings);
|
||||||
task->setStagingPath(stagingPath);
|
|
||||||
task->setParentSettings(m_globalSettings);
|
|
||||||
return new InstanceStaging(this, task, stagingPath, *task, task->group());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString InstanceList::getStagedInstancePath()
|
QString InstanceList::getStagedInstancePath()
|
||||||
{
|
{
|
||||||
QString key = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
const QString tempRoot = FS::PathCombine(m_instDir, ".tmp");
|
||||||
QString tempDir = ".LAUNCHER_TEMP/";
|
|
||||||
QString relPath = FS::PathCombine(tempDir, key);
|
QString result;
|
||||||
QDir rootPath(m_instDir);
|
int tries = 0;
|
||||||
auto path = FS::PathCombine(m_instDir, relPath);
|
|
||||||
if (!rootPath.mkpath(relPath)) {
|
do {
|
||||||
return QString();
|
if (++tries > 256)
|
||||||
}
|
return {};
|
||||||
|
|
||||||
|
const QString key = QUuid::createUuid().toString(QUuid::Id128).left(6);
|
||||||
|
result = FS::PathCombine(tempRoot, key);
|
||||||
|
} while (QFileInfo::exists(result));
|
||||||
|
|
||||||
|
if (!QDir::current().mkpath(result))
|
||||||
|
return {};
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
auto tempPath = FS::PathCombine(m_instDir, tempDir);
|
SetFileAttributesA(tempRoot.toStdString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
|
||||||
SetFileAttributesA(tempPath.toStdString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
|
|
||||||
#endif
|
#endif
|
||||||
return path;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InstanceList::commitStagedInstance(const QString& path,
|
bool InstanceList::commitStagedInstance(const QString& path,
|
||||||
|
|
@ -956,7 +988,6 @@ bool InstanceList::commitStagedInstance(const QString& path,
|
||||||
if (groupName.isEmpty() && !groupName.isNull())
|
if (groupName.isEmpty() && !groupName.isNull())
|
||||||
groupName = QString();
|
groupName = QString();
|
||||||
|
|
||||||
QDir dir;
|
|
||||||
QString instID;
|
QString instID;
|
||||||
InstancePtr inst;
|
InstancePtr inst;
|
||||||
|
|
||||||
|
|
@ -980,7 +1011,7 @@ bool InstanceList::commitStagedInstance(const QString& path,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!dir.rename(path, destination)) {
|
if (!FS::move(path, destination)) {
|
||||||
qWarning() << "Failed to move" << path << "to" << destination;
|
qWarning() << "Failed to move" << path << "to" << destination;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ enum class GroupsState { NotLoaded, Steady, Dirty };
|
||||||
|
|
||||||
struct TrashHistoryItem {
|
struct TrashHistoryItem {
|
||||||
QString id;
|
QString id;
|
||||||
QString polyPath;
|
QString path;
|
||||||
QString trashPath;
|
QString trashPath;
|
||||||
QString groupName;
|
QString groupName;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class InstancePageProvider : protected QObject, public BasePageProvider {
|
||||||
public:
|
public:
|
||||||
explicit InstancePageProvider(InstancePtr parent) { inst = parent; }
|
explicit InstancePageProvider(InstancePtr parent) { inst = parent; }
|
||||||
|
|
||||||
virtual ~InstancePageProvider(){};
|
virtual ~InstancePageProvider() = default;
|
||||||
virtual QList<BasePage*> getPages() override
|
virtual QList<BasePage*> getPages() override
|
||||||
{
|
{
|
||||||
QList<BasePage*> values;
|
QList<BasePage*> values;
|
||||||
|
|
@ -39,11 +39,11 @@ class InstancePageProvider : protected QObject, public BasePageProvider {
|
||||||
values.append(new TexturePackPage(onesix.get(), onesix->texturePackList()));
|
values.append(new TexturePackPage(onesix.get(), onesix->texturePackList()));
|
||||||
values.append(new ShaderPackPage(onesix.get(), onesix->shaderPackList()));
|
values.append(new ShaderPackPage(onesix.get(), onesix->shaderPackList()));
|
||||||
values.append(new NotesPage(onesix.get()));
|
values.append(new NotesPage(onesix.get()));
|
||||||
values.append(new WorldListPage(onesix.get(), onesix->worldList()));
|
values.append(new WorldListPage(onesix, onesix->worldList()));
|
||||||
values.append(new ServersPage(onesix));
|
values.append(new ServersPage(onesix));
|
||||||
// values.append(new GameOptionsPage(onesix.get()));
|
// values.append(new GameOptionsPage(onesix.get()));
|
||||||
values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots")));
|
values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots")));
|
||||||
values.append(new InstanceSettingsPage(onesix.get()));
|
values.append(new InstanceSettingsPage(onesix));
|
||||||
auto logMatcher = inst->getLogFileMatcher();
|
auto logMatcher = inst->getLogFileMatcher();
|
||||||
if (logMatcher) {
|
if (logMatcher) {
|
||||||
values.append(new OtherLogsPage(inst->getLogFileRoot(), logMatcher));
|
values.append(new OtherLogsPage(inst->getLogFileRoot(), logMatcher));
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
#include "InstanceTask.h"
|
#include "InstanceTask.h"
|
||||||
|
|
||||||
|
#include "Application.h"
|
||||||
|
#include "settings/SettingsObject.h"
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
|
|
||||||
|
#include <QPushButton>
|
||||||
|
|
||||||
InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name)
|
InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name)
|
||||||
{
|
{
|
||||||
auto dialog =
|
auto dialog =
|
||||||
|
|
@ -20,6 +24,9 @@ InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& ol
|
||||||
|
|
||||||
ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name)
|
ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name)
|
||||||
{
|
{
|
||||||
|
if (APPLICATION->settings()->get("SkipModpackUpdatePrompt").toBool())
|
||||||
|
return ShouldUpdate::SkipUpdating;
|
||||||
|
|
||||||
auto info = CustomMessageBox::selectable(
|
auto info = CustomMessageBox::selectable(
|
||||||
parent, QObject::tr("Similar modpack was found!"),
|
parent, QObject::tr("Similar modpack was found!"),
|
||||||
QObject::tr(
|
QObject::tr(
|
||||||
|
|
@ -27,16 +34,15 @@ ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name)
|
||||||
"separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before "
|
"separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before "
|
||||||
"updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).")
|
"updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).")
|
||||||
.arg(original_version_name),
|
.arg(original_version_name),
|
||||||
QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort);
|
QMessageBox::Information, QMessageBox::Cancel);
|
||||||
info->setButtonText(QMessageBox::Ok, QObject::tr("Update existing instance"));
|
QAbstractButton* update = info->addButton(QObject::tr("Update existing instance"), QMessageBox::AcceptRole);
|
||||||
info->setButtonText(QMessageBox::Abort, QObject::tr("Create new instance"));
|
QAbstractButton* skip = info->addButton(QObject::tr("Create new instance"), QMessageBox::ResetRole);
|
||||||
info->setButtonText(QMessageBox::Reset, QObject::tr("Cancel"));
|
|
||||||
|
|
||||||
info->exec();
|
info->exec();
|
||||||
|
|
||||||
if (info->clickedButton() == info->button(QMessageBox::Ok))
|
if (info->clickedButton() == update)
|
||||||
return ShouldUpdate::Update;
|
return ShouldUpdate::Update;
|
||||||
if (info->clickedButton() == info->button(QMessageBox::Abort))
|
if (info->clickedButton() == skip)
|
||||||
return ShouldUpdate::SkipUpdating;
|
return ShouldUpdate::SkipUpdating;
|
||||||
return ShouldUpdate::Cancel;
|
return ShouldUpdate::Cancel;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget* parent)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JavaCommon::javaWasOk(QWidget* parent, const JavaCheckResult& result)
|
void JavaCommon::javaWasOk(QWidget* parent, const JavaChecker::Result& result)
|
||||||
{
|
{
|
||||||
QString text;
|
QString text;
|
||||||
text += QObject::tr(
|
text += QObject::tr(
|
||||||
|
|
@ -79,7 +79,7 @@ void JavaCommon::javaWasOk(QWidget* parent, const JavaCheckResult& result)
|
||||||
CustomMessageBox::selectable(parent, QObject::tr("Java test success"), text, QMessageBox::Information)->show();
|
CustomMessageBox::selectable(parent, QObject::tr("Java test success"), text, QMessageBox::Information)->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JavaCommon::javaArgsWereBad(QWidget* parent, const JavaCheckResult& result)
|
void JavaCommon::javaArgsWereBad(QWidget* parent, const JavaChecker::Result& result)
|
||||||
{
|
{
|
||||||
auto htmlError = result.errorLog;
|
auto htmlError = result.errorLog;
|
||||||
QString text;
|
QString text;
|
||||||
|
|
@ -89,7 +89,7 @@ void JavaCommon::javaArgsWereBad(QWidget* parent, const JavaCheckResult& result)
|
||||||
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
|
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JavaCommon::javaBinaryWasBad(QWidget* parent, const JavaCheckResult& result)
|
void JavaCommon::javaBinaryWasBad(QWidget* parent, const JavaChecker::Result& result)
|
||||||
{
|
{
|
||||||
QString text;
|
QString text;
|
||||||
text += QObject::tr(
|
text += QObject::tr(
|
||||||
|
|
@ -116,34 +116,26 @@ void JavaCommon::TestCheck::run()
|
||||||
emit finished();
|
emit finished();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
checker.reset(new JavaChecker());
|
checker.reset(new JavaChecker(m_path, "", 0, 0, 0, 0));
|
||||||
connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinished);
|
connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinished);
|
||||||
checker->m_path = m_path;
|
checker->start();
|
||||||
checker->performCheck();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JavaCommon::TestCheck::checkFinished(JavaCheckResult result)
|
void JavaCommon::TestCheck::checkFinished(const JavaChecker::Result& result)
|
||||||
{
|
{
|
||||||
if (result.validity != JavaCheckResult::Validity::Valid) {
|
if (result.validity != JavaChecker::Result::Validity::Valid) {
|
||||||
javaBinaryWasBad(m_parent, result);
|
javaBinaryWasBad(m_parent, result);
|
||||||
emit finished();
|
emit finished();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
checker.reset(new JavaChecker());
|
checker.reset(new JavaChecker(m_path, m_args, m_maxMem, m_maxMem, result.javaVersion.requiresPermGen() ? m_permGen : 0, 0));
|
||||||
connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinishedWithArgs);
|
connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinishedWithArgs);
|
||||||
checker->m_path = m_path;
|
checker->start();
|
||||||
checker->m_args = m_args;
|
|
||||||
checker->m_minMem = m_minMem;
|
|
||||||
checker->m_maxMem = m_maxMem;
|
|
||||||
if (result.javaVersion.requiresPermGen()) {
|
|
||||||
checker->m_permGen = m_permGen;
|
|
||||||
}
|
|
||||||
checker->performCheck();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result)
|
void JavaCommon::TestCheck::checkFinishedWithArgs(const JavaChecker::Result& result)
|
||||||
{
|
{
|
||||||
if (result.validity == JavaCheckResult::Validity::Valid) {
|
if (result.validity == JavaChecker::Result::Validity::Valid) {
|
||||||
javaWasOk(m_parent, result);
|
javaWasOk(m_parent, result);
|
||||||
emit finished();
|
emit finished();
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,11 @@ namespace JavaCommon {
|
||||||
bool checkJVMArgs(QString args, QWidget* parent);
|
bool checkJVMArgs(QString args, QWidget* parent);
|
||||||
|
|
||||||
// Show a dialog saying that the Java binary was usable
|
// Show a dialog saying that the Java binary was usable
|
||||||
void javaWasOk(QWidget* parent, const JavaCheckResult& result);
|
void javaWasOk(QWidget* parent, const JavaChecker::Result& result);
|
||||||
// Show a dialog saying that the Java binary was not usable because of bad options
|
// Show a dialog saying that the Java binary was not usable because of bad options
|
||||||
void javaArgsWereBad(QWidget* parent, const JavaCheckResult& result);
|
void javaArgsWereBad(QWidget* parent, const JavaChecker::Result& result);
|
||||||
// Show a dialog saying that the Java binary was not usable
|
// Show a dialog saying that the Java binary was not usable
|
||||||
void javaBinaryWasBad(QWidget* parent, const JavaCheckResult& result);
|
void javaBinaryWasBad(QWidget* parent, const JavaChecker::Result& result);
|
||||||
// Show a dialog if we couldn't find Java Checker
|
// Show a dialog if we couldn't find Java Checker
|
||||||
void javaCheckNotFound(QWidget* parent);
|
void javaCheckNotFound(QWidget* parent);
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ class TestCheck : public QObject {
|
||||||
TestCheck(QWidget* parent, QString path, QString args, int minMem, int maxMem, int permGen)
|
TestCheck(QWidget* parent, QString path, QString args, int minMem, int maxMem, int permGen)
|
||||||
: m_parent(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen)
|
: m_parent(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen)
|
||||||
{}
|
{}
|
||||||
virtual ~TestCheck(){};
|
virtual ~TestCheck() {};
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
|
|
@ -32,11 +32,11 @@ class TestCheck : public QObject {
|
||||||
void finished();
|
void finished();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void checkFinished(JavaCheckResult result);
|
void checkFinished(const JavaChecker::Result& result);
|
||||||
void checkFinishedWithArgs(JavaCheckResult result);
|
void checkFinishedWithArgs(const JavaChecker::Result& result);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<JavaChecker> checker;
|
JavaChecker::Ptr checker;
|
||||||
QWidget* m_parent = nullptr;
|
QWidget* m_parent = nullptr;
|
||||||
QString m_path;
|
QString m_path;
|
||||||
QString m_args;
|
QString m_args;
|
||||||
|
|
|
||||||
|
|
@ -36,13 +36,14 @@
|
||||||
|
|
||||||
#include "LaunchController.h"
|
#include "LaunchController.h"
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "launch/steps/PrintServers.h"
|
||||||
#include "minecraft/auth/AccountData.h"
|
#include "minecraft/auth/AccountData.h"
|
||||||
#include "minecraft/auth/AccountList.h"
|
#include "minecraft/auth/AccountList.h"
|
||||||
|
|
||||||
#include "ui/InstanceWindow.h"
|
#include "ui/InstanceWindow.h"
|
||||||
#include "ui/MainWindow.h"
|
#include "ui/MainWindow.h"
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
#include "ui/dialogs/EditAccountDialog.h"
|
#include "ui/dialogs/MSALoginDialog.h"
|
||||||
#include "ui/dialogs/ProfileSelectDialog.h"
|
#include "ui/dialogs/ProfileSelectDialog.h"
|
||||||
#include "ui/dialogs/ProfileSetupDialog.h"
|
#include "ui/dialogs/ProfileSetupDialog.h"
|
||||||
#include "ui/dialogs/ProgressDialog.h"
|
#include "ui/dialogs/ProgressDialog.h"
|
||||||
|
|
@ -53,15 +54,15 @@
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
#include "JavaCommon.h"
|
#include "JavaCommon.h"
|
||||||
#include "launch/steps/TextPrint.h"
|
#include "launch/steps/TextPrint.h"
|
||||||
#include "minecraft/auth/AccountTask.h"
|
|
||||||
#include "tasks/Task.h"
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
LaunchController::LaunchController(QObject* parent) : Task(parent) {}
|
LaunchController::LaunchController() : Task() {}
|
||||||
|
|
||||||
void LaunchController::executeTask()
|
void LaunchController::executeTask()
|
||||||
{
|
{
|
||||||
|
|
@ -86,7 +87,7 @@ void LaunchController::decideAccount()
|
||||||
|
|
||||||
// Find an account to use.
|
// Find an account to use.
|
||||||
auto accounts = APPLICATION->accounts();
|
auto accounts = APPLICATION->accounts();
|
||||||
if (accounts->count() <= 0) {
|
if (accounts->count() <= 0 || !accounts->anyAccountIsValid()) {
|
||||||
// Tell the user they need to log in at least one account in order to play.
|
// 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"),
|
auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"),
|
||||||
tr("In order to play Minecraft, you must have at least one Microsoft "
|
tr("In order to play Minecraft, you must have at least one Microsoft "
|
||||||
|
|
@ -130,12 +131,63 @@ void LaunchController::decideAccount()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool LaunchController::askPlayDemo()
|
||||||
|
{
|
||||||
|
QMessageBox box(m_parentWidget);
|
||||||
|
box.setWindowTitle(tr("Play demo?"));
|
||||||
|
box.setText(
|
||||||
|
tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play "
|
||||||
|
"the demo?"));
|
||||||
|
box.setIcon(QMessageBox::Warning);
|
||||||
|
auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole);
|
||||||
|
auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole);
|
||||||
|
box.setDefaultButton(cancelButton);
|
||||||
|
|
||||||
|
box.exec();
|
||||||
|
return box.clickedButton() == demoButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LaunchController::askOfflineName(QString playerName, bool demo, bool& ok)
|
||||||
|
{
|
||||||
|
// we ask the user for a player name
|
||||||
|
QString message = tr("Choose your offline mode player name.");
|
||||||
|
if (demo) {
|
||||||
|
message = tr("Choose your demo mode player name.");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString();
|
||||||
|
QString usedname = lastOfflinePlayerName.isEmpty() ? playerName : lastOfflinePlayerName;
|
||||||
|
QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok);
|
||||||
|
if (!ok)
|
||||||
|
return {};
|
||||||
|
if (name.length()) {
|
||||||
|
usedname = name;
|
||||||
|
APPLICATION->settings()->set("LastOfflinePlayerName", usedname);
|
||||||
|
}
|
||||||
|
return usedname;
|
||||||
|
}
|
||||||
|
|
||||||
void LaunchController::login()
|
void LaunchController::login()
|
||||||
{
|
{
|
||||||
decideAccount();
|
decideAccount();
|
||||||
|
|
||||||
// if no account is selected, we bail
|
|
||||||
if (!m_accountToUse) {
|
if (!m_accountToUse) {
|
||||||
|
// if no account is selected, ask about demo
|
||||||
|
if (!m_demo) {
|
||||||
|
m_demo = askPlayDemo();
|
||||||
|
}
|
||||||
|
if (m_demo) {
|
||||||
|
// we ask the user for a player name
|
||||||
|
bool ok = false;
|
||||||
|
auto name = askOfflineName("Player", m_demo, ok);
|
||||||
|
if (ok) {
|
||||||
|
m_session = std::make_shared<AuthSession>();
|
||||||
|
m_session->MakeDemo(name, MinecraftAccount::uuidFromUsername(name).toString().remove(QRegularExpression("[{}-]")));
|
||||||
|
launchInstance();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if no account is selected, we bail
|
||||||
emitFailed(tr("No account selected for launch."));
|
emitFailed(tr("No account selected for launch."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -144,6 +196,13 @@ void LaunchController::login()
|
||||||
bool tryagain = true;
|
bool tryagain = true;
|
||||||
unsigned int tries = 0;
|
unsigned int tries = 0;
|
||||||
|
|
||||||
|
if ((m_accountToUse->accountType() != AccountType::Offline && m_accountToUse->accountState() == AccountState::Offline) ||
|
||||||
|
m_accountToUse->shouldRefresh()) {
|
||||||
|
// Force account refresh on the account used to launch the instance updating the AccountState
|
||||||
|
// only on first try and if it is not meant to be offline
|
||||||
|
auto accounts = APPLICATION->accounts();
|
||||||
|
accounts->requestRefresh(m_accountToUse->internalId());
|
||||||
|
}
|
||||||
while (tryagain) {
|
while (tryagain) {
|
||||||
if (tries > 0 && tries % 3 == 0) {
|
if (tries > 0 && tries % 3 == 0) {
|
||||||
auto result =
|
auto result =
|
||||||
|
|
@ -176,24 +235,17 @@ void LaunchController::login()
|
||||||
if (!m_session->wants_online) {
|
if (!m_session->wants_online) {
|
||||||
// we ask the user for a player name
|
// we ask the user for a player name
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
QString name;
|
||||||
QString message = tr("Choose your offline mode player name.");
|
if (m_offlineName.isEmpty()) {
|
||||||
if (m_session->demo) {
|
name = askOfflineName(m_session->player_name, m_session->demo, ok);
|
||||||
message = tr("Choose your demo mode player name.");
|
if (!ok) {
|
||||||
|
tryagain = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
name = m_offlineName;
|
||||||
}
|
}
|
||||||
|
m_session->MakeOffline(name);
|
||||||
QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString();
|
|
||||||
QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName;
|
|
||||||
QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok);
|
|
||||||
if (!ok) {
|
|
||||||
tryagain = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (name.length()) {
|
|
||||||
usedname = name;
|
|
||||||
APPLICATION->settings()->set("LastOfflinePlayerName", usedname);
|
|
||||||
}
|
|
||||||
m_session->MakeOffline(usedname);
|
|
||||||
// offline flavored game from here :3
|
// offline flavored game from here :3
|
||||||
}
|
}
|
||||||
if (m_accountToUse->ownsMinecraft()) {
|
if (m_accountToUse->ownsMinecraft()) {
|
||||||
|
|
@ -213,20 +265,10 @@ void LaunchController::login()
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
// play demo ?
|
// play demo ?
|
||||||
QMessageBox box(m_parentWidget);
|
if (!m_session->demo) {
|
||||||
box.setWindowTitle(tr("Play demo?"));
|
m_session->demo = askPlayDemo();
|
||||||
box.setText(
|
}
|
||||||
tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play "
|
if (m_session->demo) { // play demo here
|
||||||
"the demo?"));
|
|
||||||
box.setIcon(QMessageBox::Warning);
|
|
||||||
auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole);
|
|
||||||
auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole);
|
|
||||||
box.setDefaultButton(cancelButton);
|
|
||||||
|
|
||||||
box.exec();
|
|
||||||
if (box.clickedButton() == demoButton) {
|
|
||||||
// play demo here
|
|
||||||
m_session->MakeDemo();
|
|
||||||
launchInstance();
|
launchInstance();
|
||||||
} else {
|
} else {
|
||||||
emitFailed(tr("Launch cancelled - account does not own Minecraft."));
|
emitFailed(tr("Launch cancelled - account does not own Minecraft."));
|
||||||
|
|
@ -250,17 +292,9 @@ void LaunchController::login()
|
||||||
progDialog.execWithTask(task.get());
|
progDialog.execWithTask(task.get());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// FIXME: this is missing - the meaning is that the account is queued for refresh and we should wait for that
|
|
||||||
/*
|
|
||||||
case AccountState::Queued: {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
case AccountState::Expired: {
|
case AccountState::Expired: {
|
||||||
auto errorString = tr("The account has expired and needs to be logged into manually again.");
|
if (reauthenticateCurrentAccount())
|
||||||
QMessageBox::warning(m_parentWidget, tr("Account refresh failed"), errorString, QMessageBox::StandardButton::Ok,
|
continue;
|
||||||
QMessageBox::StandardButton::Ok);
|
|
||||||
emitFailed(errorString);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case AccountState::Disabled: {
|
case AccountState::Disabled: {
|
||||||
|
|
@ -284,6 +318,33 @@ void LaunchController::login()
|
||||||
emitFailed(tr("Failed to launch."));
|
emitFailed(tr("Failed to launch."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool LaunchController::reauthenticateCurrentAccount()
|
||||||
|
{
|
||||||
|
auto button =
|
||||||
|
QMessageBox::warning(m_parentWidget, tr("Account refresh failed"),
|
||||||
|
tr("The account has expired and needs to be reauthenticated. Do you want to reauthenticate this account?"),
|
||||||
|
QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, QMessageBox::StandardButton::Yes);
|
||||||
|
if (button == QMessageBox::StandardButton::Yes) {
|
||||||
|
auto accounts = APPLICATION->accounts();
|
||||||
|
bool isDefault = accounts->defaultAccount() == m_accountToUse;
|
||||||
|
accounts->removeAccount(accounts->index(accounts->findAccountByProfileId(m_accountToUse->profileId())));
|
||||||
|
if (m_accountToUse->accountType() == AccountType::MSA) {
|
||||||
|
auto newAccount = MSALoginDialog::newAccount(m_parentWidget);
|
||||||
|
accounts->addAccount(newAccount);
|
||||||
|
if (isDefault) {
|
||||||
|
accounts->setDefaultAccount(newAccount);
|
||||||
|
}
|
||||||
|
m_accountToUse = nullptr;
|
||||||
|
decideAccount();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
emitFailed(tr("Account expired and re-login attempt failed"));
|
||||||
|
} else {
|
||||||
|
emitFailed(tr("The account has expired and needs to be reauthenticated"));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void LaunchController::launchInstance()
|
void LaunchController::launchInstance()
|
||||||
{
|
{
|
||||||
Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL");
|
Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL");
|
||||||
|
|
@ -295,7 +356,7 @@ void LaunchController::launchInstance()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_launcher = m_instance->createLaunchTask(m_session, m_serverToJoin);
|
m_launcher = m_instance->createLaunchTask(m_session, m_targetToJoin);
|
||||||
if (!m_launcher) {
|
if (!m_launcher) {
|
||||||
emitFailed(tr("Couldn't instantiate a launcher."));
|
emitFailed(tr("Couldn't instantiate a launcher."));
|
||||||
return;
|
return;
|
||||||
|
|
@ -317,26 +378,9 @@ void LaunchController::launchInstance()
|
||||||
online_mode = "online";
|
online_mode = "online";
|
||||||
|
|
||||||
// Prepend Server Status
|
// Prepend Server Status
|
||||||
QStringList servers = { "authserver.mojang.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" };
|
QStringList servers = { "login.microsoftonline.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" };
|
||||||
QString resolved_servers = "";
|
|
||||||
QHostInfo host_info;
|
|
||||||
|
|
||||||
for (QString server : servers) {
|
m_launcher->prependStep(makeShared<PrintServers>(m_launcher.get(), servers));
|
||||||
host_info = QHostInfo::fromName(server);
|
|
||||||
resolved_servers = resolved_servers + server + " resolves to:\n [";
|
|
||||||
if (!host_info.addresses().isEmpty()) {
|
|
||||||
for (QHostAddress address : host_info.addresses()) {
|
|
||||||
resolved_servers = resolved_servers + address.toString();
|
|
||||||
if (!host_info.addresses().endsWith(address)) {
|
|
||||||
resolved_servers = resolved_servers + ", ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resolved_servers = resolved_servers + "N/A";
|
|
||||||
}
|
|
||||||
resolved_servers = resolved_servers + "]\n\n";
|
|
||||||
}
|
|
||||||
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), resolved_servers, MessageLevel::Launcher));
|
|
||||||
} else {
|
} else {
|
||||||
online_mode = m_demo ? "demo" : "offline";
|
online_mode = m_demo ? "demo" : "offline";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "minecraft/auth/MinecraftAccount.h"
|
#include "minecraft/auth/MinecraftAccount.h"
|
||||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
#include "minecraft/launch/MinecraftTarget.h"
|
||||||
|
|
||||||
class InstanceWindow;
|
class InstanceWindow;
|
||||||
class LaunchController : public Task {
|
class LaunchController : public Task {
|
||||||
|
|
@ -47,8 +47,8 @@ class LaunchController : public Task {
|
||||||
public:
|
public:
|
||||||
void executeTask() override;
|
void executeTask() override;
|
||||||
|
|
||||||
LaunchController(QObject* parent = nullptr);
|
LaunchController();
|
||||||
virtual ~LaunchController(){};
|
virtual ~LaunchController() = default;
|
||||||
|
|
||||||
void setInstance(InstancePtr instance) { m_instance = instance; }
|
void setInstance(InstancePtr instance) { m_instance = instance; }
|
||||||
|
|
||||||
|
|
@ -56,13 +56,15 @@ class LaunchController : public Task {
|
||||||
|
|
||||||
void setOnline(bool online) { m_online = online; }
|
void setOnline(bool online) { m_online = online; }
|
||||||
|
|
||||||
|
void setOfflineName(const QString& offlineName) { m_offlineName = offlineName; }
|
||||||
|
|
||||||
void setDemo(bool demo) { m_demo = demo; }
|
void setDemo(bool demo) { m_demo = demo; }
|
||||||
|
|
||||||
void setProfiler(BaseProfilerFactory* profiler) { m_profiler = profiler; }
|
void setProfiler(BaseProfilerFactory* profiler) { m_profiler = profiler; }
|
||||||
|
|
||||||
void setParentWidget(QWidget* widget) { m_parentWidget = widget; }
|
void setParentWidget(QWidget* widget) { m_parentWidget = widget; }
|
||||||
|
|
||||||
void setServerToJoin(MinecraftServerTargetPtr serverToJoin) { m_serverToJoin = std::move(serverToJoin); }
|
void setTargetToJoin(MinecraftTarget::Ptr targetToJoin) { m_targetToJoin = std::move(targetToJoin); }
|
||||||
|
|
||||||
void setAccountToUse(MinecraftAccountPtr accountToUse) { m_accountToUse = std::move(accountToUse); }
|
void setAccountToUse(MinecraftAccountPtr accountToUse) { m_accountToUse = std::move(accountToUse); }
|
||||||
|
|
||||||
|
|
@ -74,6 +76,9 @@ class LaunchController : public Task {
|
||||||
void login();
|
void login();
|
||||||
void launchInstance();
|
void launchInstance();
|
||||||
void decideAccount();
|
void decideAccount();
|
||||||
|
bool askPlayDemo();
|
||||||
|
QString askOfflineName(QString playerName, bool demo, bool& ok);
|
||||||
|
bool reauthenticateCurrentAccount();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void readyForLaunch();
|
void readyForLaunch();
|
||||||
|
|
@ -85,6 +90,7 @@ class LaunchController : public Task {
|
||||||
private:
|
private:
|
||||||
BaseProfilerFactory* m_profiler = nullptr;
|
BaseProfilerFactory* m_profiler = nullptr;
|
||||||
bool m_online = true;
|
bool m_online = true;
|
||||||
|
QString m_offlineName;
|
||||||
bool m_demo = false;
|
bool m_demo = false;
|
||||||
InstancePtr m_instance;
|
InstancePtr m_instance;
|
||||||
QWidget* m_parentWidget = nullptr;
|
QWidget* m_parentWidget = nullptr;
|
||||||
|
|
@ -92,5 +98,5 @@ class LaunchController : public Task {
|
||||||
MinecraftAccountPtr m_accountToUse = nullptr;
|
MinecraftAccountPtr m_accountToUse = nullptr;
|
||||||
AuthSessionPtr m_session;
|
AuthSessionPtr m_session;
|
||||||
shared_qobject_ptr<LaunchTask> m_launcher;
|
shared_qobject_ptr<LaunchTask> m_launcher;
|
||||||
MinecraftServerTargetPtr m_serverToJoin;
|
MinecraftTarget::Ptr m_targetToJoin;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,16 @@ if [ "x$DEPS_LIST" = "x" ]; then
|
||||||
# Just to be sure...
|
# Just to be sure...
|
||||||
chmod +x "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}"
|
chmod +x "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}"
|
||||||
|
|
||||||
|
ARGS=("${LAUNCHER_DIR}/${LAUNCHER_NAME}" "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}")
|
||||||
|
|
||||||
|
if [ -f portable.txt ]; then
|
||||||
|
ARGS+=("-d" "${LAUNCHER_DIR}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
ARGS+=("$@")
|
||||||
|
|
||||||
# Run the launcher
|
# Run the launcher
|
||||||
exec -a "${LAUNCHER_DIR}/${LAUNCHER_NAME}" "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}" -d "${LAUNCHER_DIR}" "$@"
|
exec -a "${ARGS[@]}"
|
||||||
|
|
||||||
# Run the launcher in valgrind
|
# Run the launcher in valgrind
|
||||||
# valgrind --log-file="valgrind.log" --leak-check=full --track-origins=yes "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}" -d "${LAUNCHER_DIR}" "$@"
|
# valgrind --log-file="valgrind.log" --leak-check=full --track-origins=yes "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}" -d "${LAUNCHER_DIR}" "$@"
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,8 @@
|
||||||
#include <QTextDecoder>
|
#include <QTextDecoder>
|
||||||
#include "MessageLevel.h"
|
#include "MessageLevel.h"
|
||||||
|
|
||||||
LoggedProcess::LoggedProcess(QObject* parent) : QProcess(parent)
|
LoggedProcess::LoggedProcess(const QTextCodec* output_codec, QObject* parent)
|
||||||
|
: QProcess(parent), m_err_decoder(output_codec), m_out_decoder(output_codec)
|
||||||
{
|
{
|
||||||
// QProcess has a strange interface... let's map a lot of those into a few.
|
// QProcess has a strange interface... let's map a lot of those into a few.
|
||||||
connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut);
|
connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut);
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ class LoggedProcess : public QProcess {
|
||||||
enum State { NotRunning, Starting, FailedToStart, Running, Finished, Crashed, Aborted };
|
enum State { NotRunning, Starting, FailedToStart, Running, Finished, Crashed, Aborted };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit LoggedProcess(QObject* parent = 0);
|
explicit LoggedProcess(const QTextCodec* output_codec = QTextCodec::codecForLocale(), QObject* parent = 0);
|
||||||
virtual ~LoggedProcess();
|
virtual ~LoggedProcess();
|
||||||
|
|
||||||
State state() const;
|
State state() const;
|
||||||
|
|
@ -80,8 +80,8 @@ class LoggedProcess : public QProcess {
|
||||||
QStringList reprocess(const QByteArray& data, QTextDecoder& decoder);
|
QStringList reprocess(const QByteArray& data, QTextDecoder& decoder);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTextDecoder m_err_decoder = QTextDecoder(QTextCodec::codecForLocale());
|
QTextDecoder m_err_decoder;
|
||||||
QTextDecoder m_out_decoder = QTextDecoder(QTextCodec::codecForLocale());
|
QTextDecoder m_out_decoder;
|
||||||
QString m_leftover_line;
|
QString m_leftover_line;
|
||||||
bool m_killed = false;
|
bool m_killed = false;
|
||||||
State m_state = NotRunning;
|
State m_state = NotRunning;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -42,6 +42,7 @@
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QFileInfo>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#if defined(LAUNCHER_APPLICATION)
|
#if defined(LAUNCHER_APPLICATION)
|
||||||
|
|
@ -119,9 +120,10 @@ bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool follow
|
||||||
bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks)
|
bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks)
|
||||||
{
|
{
|
||||||
QuaZip zip(fileCompressed);
|
QuaZip zip(fileCompressed);
|
||||||
|
zip.setUtf8Enabled(true);
|
||||||
QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
|
QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
|
||||||
if (!zip.open(QuaZip::mdCreate)) {
|
if (!zip.open(QuaZip::mdCreate)) {
|
||||||
QFile::remove(fileCompressed);
|
FS::deletePath(fileCompressed);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,7 +131,7 @@ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files,
|
||||||
|
|
||||||
zip.close();
|
zip.close();
|
||||||
if (zip.getZipError() != 0) {
|
if (zip.getZipError() != 0) {
|
||||||
QFile::remove(fileCompressed);
|
FS::deletePath(fileCompressed);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,8 +143,9 @@ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files,
|
||||||
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods)
|
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods)
|
||||||
{
|
{
|
||||||
QuaZip zipOut(targetJarPath);
|
QuaZip zipOut(targetJarPath);
|
||||||
|
zipOut.setUtf8Enabled(true);
|
||||||
if (!zipOut.open(QuaZip::mdCreate)) {
|
if (!zipOut.open(QuaZip::mdCreate)) {
|
||||||
QFile::remove(targetJarPath);
|
FS::deletePath(targetJarPath);
|
||||||
qCritical() << "Failed to open the minecraft.jar for modding";
|
qCritical() << "Failed to open the minecraft.jar for modding";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -160,7 +163,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
||||||
if (mod->type() == ResourceType::ZIPFILE) {
|
if (mod->type() == ResourceType::ZIPFILE) {
|
||||||
if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles)) {
|
if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles)) {
|
||||||
zipOut.close();
|
zipOut.close();
|
||||||
QFile::remove(targetJarPath);
|
FS::deletePath(targetJarPath);
|
||||||
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
|
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -169,7 +172,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
||||||
auto filename = mod->fileinfo();
|
auto filename = mod->fileinfo();
|
||||||
if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) {
|
if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) {
|
||||||
zipOut.close();
|
zipOut.close();
|
||||||
QFile::remove(targetJarPath);
|
FS::deletePath(targetJarPath);
|
||||||
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
|
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +195,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
||||||
|
|
||||||
if (!compressDirFiles(&zipOut, parent_dir, files)) {
|
if (!compressDirFiles(&zipOut, parent_dir, files)) {
|
||||||
zipOut.close();
|
zipOut.close();
|
||||||
QFile::remove(targetJarPath);
|
FS::deletePath(targetJarPath);
|
||||||
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
|
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -200,7 +203,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
||||||
} else {
|
} else {
|
||||||
// Make sure we do not continue launching when something is missing or undefined...
|
// Make sure we do not continue launching when something is missing or undefined...
|
||||||
zipOut.close();
|
zipOut.close();
|
||||||
QFile::remove(targetJarPath);
|
FS::deletePath(targetJarPath);
|
||||||
qCritical() << "Failed to add unknown mod type" << mod->fileinfo().fileName() << "to the jar.";
|
qCritical() << "Failed to add unknown mod type" << mod->fileinfo().fileName() << "to the jar.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -208,7 +211,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
||||||
|
|
||||||
if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key) { return !key.contains("META-INF"); })) {
|
if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key) { return !key.contains("META-INF"); })) {
|
||||||
zipOut.close();
|
zipOut.close();
|
||||||
QFile::remove(targetJarPath);
|
FS::deletePath(targetJarPath);
|
||||||
qCritical() << "Failed to insert minecraft.jar contents.";
|
qCritical() << "Failed to insert minecraft.jar contents.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -216,7 +219,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
|
||||||
// Recompress the jar
|
// Recompress the jar
|
||||||
zipOut.close();
|
zipOut.close();
|
||||||
if (zipOut.getZipError() != 0) {
|
if (zipOut.getZipError() != 0) {
|
||||||
QFile::remove(targetJarPath);
|
FS::deletePath(targetJarPath);
|
||||||
qCritical() << "Failed to finalize minecraft.jar!";
|
qCritical() << "Failed to finalize minecraft.jar!";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -286,10 +289,11 @@ std::optional<QStringList> extractSubDir(QuaZip* zip, const QString& subdir, con
|
||||||
|
|
||||||
do {
|
do {
|
||||||
QString file_name = zip->getCurrentFileName();
|
QString file_name = zip->getCurrentFileName();
|
||||||
|
file_name = FS::RemoveInvalidPathChars(file_name);
|
||||||
if (!file_name.startsWith(subdir))
|
if (!file_name.startsWith(subdir))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto relative_file_name = QDir::fromNativeSeparators(file_name.remove(0, subdir.size()));
|
auto relative_file_name = QDir::fromNativeSeparators(file_name.mid(subdir.size()));
|
||||||
auto original_name = relative_file_name;
|
auto original_name = relative_file_name;
|
||||||
|
|
||||||
// Fix subdirs/files ending with a / getting transformed into absolute paths
|
// Fix subdirs/files ending with a / getting transformed into absolute paths
|
||||||
|
|
@ -327,9 +331,31 @@ std::optional<QStringList> extractSubDir(QuaZip* zip, const QString& subdir, con
|
||||||
}
|
}
|
||||||
|
|
||||||
extracted.append(target_file_path);
|
extracted.append(target_file_path);
|
||||||
QFile::setPermissions(target_file_path,
|
auto fileInfo = QFileInfo(target_file_path);
|
||||||
QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser);
|
if (fileInfo.isFile()) {
|
||||||
|
auto permissions = fileInfo.permissions();
|
||||||
|
auto maxPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser |
|
||||||
|
QFileDevice::Permission::ReadGroup | QFileDevice::Permission::ReadOther;
|
||||||
|
auto minPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
|
||||||
|
|
||||||
|
auto newPermisions = (permissions & maxPermisions) | minPermisions;
|
||||||
|
if (newPermisions != permissions) {
|
||||||
|
if (!QFile::setPermissions(target_file_path, newPermisions)) {
|
||||||
|
qWarning() << (QObject::tr("Could not fix permissions for %1").arg(target_file_path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (fileInfo.isDir()) {
|
||||||
|
// Ensure the folder has the minimal required permissions
|
||||||
|
QFile::Permissions minimalPermissions = QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | QFile::ReadGroup |
|
||||||
|
QFile::ExeGroup | QFile::ReadOther | QFile::ExeOther;
|
||||||
|
|
||||||
|
QFile::Permissions currentPermissions = fileInfo.permissions();
|
||||||
|
if ((currentPermissions & minimalPermissions) != minimalPermissions) {
|
||||||
|
if (!QFile::setPermissions(target_file_path, minimalPermissions)) {
|
||||||
|
qWarning() << (QObject::tr("Could not fix permissions for %1").arg(target_file_path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path;
|
qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path;
|
||||||
} while (zip->goToNextFile());
|
} while (zip->goToNextFile());
|
||||||
|
|
||||||
|
|
@ -352,7 +378,7 @@ std::optional<QStringList> extractDir(QString fileCompressed, QString dir)
|
||||||
if (fileInfo.size() == 22) {
|
if (fileInfo.size() == 22) {
|
||||||
return QStringList();
|
return QStringList();
|
||||||
}
|
}
|
||||||
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();
|
qWarning() << "Could not open archive for unpacking:" << fileCompressed << "Error:" << zip.getZipError();
|
||||||
;
|
;
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
@ -369,7 +395,7 @@ std::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QS
|
||||||
if (fileInfo.size() == 22) {
|
if (fileInfo.size() == 22) {
|
||||||
return QStringList();
|
return QStringList();
|
||||||
}
|
}
|
||||||
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();
|
qWarning() << "Could not open archive for unpacking:" << fileCompressed << "Error:" << zip.getZipError();
|
||||||
;
|
;
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
@ -386,7 +412,7 @@ bool extractFile(QString fileCompressed, QString file, QString target)
|
||||||
if (fileInfo.size() == 22) {
|
if (fileInfo.size() == 22) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();
|
qWarning() << "Could not open archive for unpacking:" << fileCompressed << "Error:" << zip.getZipError();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return extractRelFile(&zip, file, target);
|
return extractRelFile(&zip, file, target);
|
||||||
|
|
@ -463,7 +489,7 @@ auto ExportToZipTask::exportZip() -> ZipResult
|
||||||
|
|
||||||
auto absolute = file.absoluteFilePath();
|
auto absolute = file.absoluteFilePath();
|
||||||
auto relative = m_dir.relativeFilePath(absolute);
|
auto relative = m_dir.relativeFilePath(absolute);
|
||||||
setStatus("Compresing: " + relative);
|
setStatus("Compressing: " + relative);
|
||||||
setProgress(m_progress + 1, m_progressTotal);
|
setProgress(m_progress + 1, m_progressTotal);
|
||||||
if (m_follow_symlinks) {
|
if (m_follow_symlinks) {
|
||||||
if (file.isSymLink())
|
if (file.isSymLink())
|
||||||
|
|
@ -487,10 +513,10 @@ auto ExportToZipTask::exportZip() -> ZipResult
|
||||||
void ExportToZipTask::finish()
|
void ExportToZipTask::finish()
|
||||||
{
|
{
|
||||||
if (m_build_zip_future.isCanceled()) {
|
if (m_build_zip_future.isCanceled()) {
|
||||||
QFile::remove(m_output_path);
|
FS::deletePath(m_output_path);
|
||||||
emitAborted();
|
emitAborted();
|
||||||
} else if (auto result = m_build_zip_future.result(); result.has_value()) {
|
} else if (auto result = m_build_zip_future.result(); result.has_value()) {
|
||||||
QFile::remove(m_output_path);
|
FS::deletePath(m_output_path);
|
||||||
emitFailed(result.value());
|
emitFailed(result.value());
|
||||||
} else {
|
} else {
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
|
|
@ -507,6 +533,138 @@ bool ExportToZipTask::abort()
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
|
void ExtractZipTask::executeTask()
|
||||||
|
{
|
||||||
|
if (!m_input->isOpen() && !m_input->open(QuaZip::mdUnzip)) {
|
||||||
|
emitFailed(tr("Unable to open supplied zip file."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_zip_future = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return extractZip(); });
|
||||||
|
connect(&m_zip_watcher, &QFutureWatcher<ZipResult>::finished, this, &ExtractZipTask::finish);
|
||||||
|
m_zip_watcher.setFuture(m_zip_future);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ExtractZipTask::extractZip() -> ZipResult
|
||||||
|
{
|
||||||
|
auto target = m_output_dir.absolutePath();
|
||||||
|
auto target_top_dir = QUrl::fromLocalFile(target);
|
||||||
|
|
||||||
|
QStringList extracted;
|
||||||
|
|
||||||
|
qDebug() << "Extracting subdir" << m_subdirectory << "from" << m_input->getZipName() << "to" << target;
|
||||||
|
auto numEntries = m_input->getEntriesCount();
|
||||||
|
if (numEntries < 0) {
|
||||||
|
return ZipResult(tr("Failed to enumerate files in archive"));
|
||||||
|
}
|
||||||
|
if (numEntries == 0) {
|
||||||
|
logWarning(tr("Extracting empty archives seems odd..."));
|
||||||
|
return ZipResult();
|
||||||
|
}
|
||||||
|
if (!m_input->goToFirstFile()) {
|
||||||
|
return ZipResult(tr("Failed to seek to first file in zip"));
|
||||||
|
}
|
||||||
|
|
||||||
|
setStatus("Extracting files...");
|
||||||
|
setProgress(0, numEntries);
|
||||||
|
do {
|
||||||
|
if (m_zip_future.isCanceled())
|
||||||
|
return ZipResult();
|
||||||
|
setProgress(m_progress + 1, m_progressTotal);
|
||||||
|
QString file_name = m_input->getCurrentFileName();
|
||||||
|
if (!file_name.startsWith(m_subdirectory))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto relative_file_name = QDir::fromNativeSeparators(file_name.mid(m_subdirectory.size()));
|
||||||
|
auto original_name = relative_file_name;
|
||||||
|
setStatus("Unpacking: " + relative_file_name);
|
||||||
|
|
||||||
|
// Fix subdirs/files ending with a / getting transformed into absolute paths
|
||||||
|
if (relative_file_name.startsWith('/'))
|
||||||
|
relative_file_name = relative_file_name.mid(1);
|
||||||
|
|
||||||
|
// Fix weird "folders with a single file get squashed" thing
|
||||||
|
QString sub_path;
|
||||||
|
if (relative_file_name.contains('/') && !relative_file_name.endsWith('/')) {
|
||||||
|
sub_path = relative_file_name.section('/', 0, -2) + '/';
|
||||||
|
FS::ensureFolderPathExists(FS::PathCombine(target, sub_path));
|
||||||
|
|
||||||
|
relative_file_name = relative_file_name.split('/').last();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString target_file_path;
|
||||||
|
if (relative_file_name.isEmpty()) {
|
||||||
|
target_file_path = target + '/';
|
||||||
|
} else {
|
||||||
|
target_file_path = FS::PathCombine(target_top_dir.toLocalFile(), sub_path, relative_file_name);
|
||||||
|
if (relative_file_name.endsWith('/') && !target_file_path.endsWith('/'))
|
||||||
|
target_file_path += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!target_top_dir.isParentOf(QUrl::fromLocalFile(target_file_path))) {
|
||||||
|
return ZipResult(tr("Extracting %1 was cancelled, because it was effectively outside of the target path %2")
|
||||||
|
.arg(relative_file_name, target));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!JlCompress::extractFile(m_input.get(), "", target_file_path)) {
|
||||||
|
JlCompress::removeFile(extracted);
|
||||||
|
return ZipResult(tr("Failed to extract file %1 to %2").arg(original_name, target_file_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
extracted.append(target_file_path);
|
||||||
|
auto fileInfo = QFileInfo(target_file_path);
|
||||||
|
if (fileInfo.isFile()) {
|
||||||
|
auto permissions = fileInfo.permissions();
|
||||||
|
auto maxPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser |
|
||||||
|
QFileDevice::Permission::ReadGroup | QFileDevice::Permission::ReadOther;
|
||||||
|
auto minPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
|
||||||
|
|
||||||
|
auto newPermisions = (permissions & maxPermisions) | minPermisions;
|
||||||
|
if (newPermisions != permissions) {
|
||||||
|
if (!QFile::setPermissions(target_file_path, newPermisions)) {
|
||||||
|
logWarning(tr("Could not fix permissions for %1").arg(target_file_path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (fileInfo.isDir()) {
|
||||||
|
// Ensure the folder has the minimal required permissions
|
||||||
|
QFile::Permissions minimalPermissions = QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | QFile::ReadGroup |
|
||||||
|
QFile::ExeGroup | QFile::ReadOther | QFile::ExeOther;
|
||||||
|
|
||||||
|
QFile::Permissions currentPermissions = fileInfo.permissions();
|
||||||
|
if ((currentPermissions & minimalPermissions) != minimalPermissions) {
|
||||||
|
if (!QFile::setPermissions(target_file_path, minimalPermissions)) {
|
||||||
|
logWarning(tr("Could not fix permissions for %1").arg(target_file_path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path;
|
||||||
|
} while (m_input->goToNextFile());
|
||||||
|
|
||||||
|
return ZipResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExtractZipTask::finish()
|
||||||
|
{
|
||||||
|
if (m_zip_future.isCanceled()) {
|
||||||
|
emitAborted();
|
||||||
|
} else if (auto result = m_zip_future.result(); result.has_value()) {
|
||||||
|
emitFailed(result.value());
|
||||||
|
} else {
|
||||||
|
emitSucceeded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExtractZipTask::abort()
|
||||||
|
{
|
||||||
|
if (m_zip_future.isRunning()) {
|
||||||
|
m_zip_future.cancel();
|
||||||
|
// NOTE: Here we don't do `emitAborted()` because it will be done when `m_build_zip_future` actually cancels, which may not occur
|
||||||
|
// immediately.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
} // namespace MMCZip
|
} // namespace MMCZip
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -153,8 +153,14 @@ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, Q
|
||||||
|
|
||||||
#if defined(LAUNCHER_APPLICATION)
|
#if defined(LAUNCHER_APPLICATION)
|
||||||
class ExportToZipTask : public Task {
|
class ExportToZipTask : public Task {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false)
|
ExportToZipTask(QString outputPath,
|
||||||
|
QDir dir,
|
||||||
|
QFileInfoList files,
|
||||||
|
QString destinationPrefix = "",
|
||||||
|
bool followSymlinks = false,
|
||||||
|
bool utf8Enabled = false)
|
||||||
: m_output_path(outputPath)
|
: m_output_path(outputPath)
|
||||||
, m_output(outputPath)
|
, m_output(outputPath)
|
||||||
, m_dir(dir)
|
, m_dir(dir)
|
||||||
|
|
@ -163,9 +169,15 @@ class ExportToZipTask : public Task {
|
||||||
, m_follow_symlinks(followSymlinks)
|
, m_follow_symlinks(followSymlinks)
|
||||||
{
|
{
|
||||||
setAbortable(true);
|
setAbortable(true);
|
||||||
|
m_output.setUtf8Enabled(utf8Enabled);
|
||||||
};
|
};
|
||||||
ExportToZipTask(QString outputPath, QString dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false)
|
ExportToZipTask(QString outputPath,
|
||||||
: ExportToZipTask(outputPath, QDir(dir), files, destinationPrefix, followSymlinks){};
|
QString dir,
|
||||||
|
QFileInfoList files,
|
||||||
|
QString destinationPrefix = "",
|
||||||
|
bool followSymlinks = false,
|
||||||
|
bool utf8Enabled = false)
|
||||||
|
: ExportToZipTask(outputPath, QDir(dir), files, destinationPrefix, followSymlinks, utf8Enabled) {};
|
||||||
|
|
||||||
virtual ~ExportToZipTask() = default;
|
virtual ~ExportToZipTask() = default;
|
||||||
|
|
||||||
|
|
@ -194,5 +206,34 @@ class ExportToZipTask : public Task {
|
||||||
QFuture<ZipResult> m_build_zip_future;
|
QFuture<ZipResult> m_build_zip_future;
|
||||||
QFutureWatcher<ZipResult> m_build_zip_watcher;
|
QFutureWatcher<ZipResult> m_build_zip_watcher;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ExtractZipTask : public Task {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ExtractZipTask(QString input, QDir outputDir, QString subdirectory = "")
|
||||||
|
: ExtractZipTask(std::make_shared<QuaZip>(input), outputDir, subdirectory)
|
||||||
|
{}
|
||||||
|
ExtractZipTask(std::shared_ptr<QuaZip> input, QDir outputDir, QString subdirectory = "")
|
||||||
|
: m_input(input), m_output_dir(outputDir), m_subdirectory(subdirectory)
|
||||||
|
{}
|
||||||
|
virtual ~ExtractZipTask() = default;
|
||||||
|
|
||||||
|
using ZipResult = std::optional<QString>;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void executeTask() override;
|
||||||
|
bool abort() override;
|
||||||
|
|
||||||
|
ZipResult extractZip();
|
||||||
|
void finish();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<QuaZip> m_input;
|
||||||
|
QDir m_output_dir;
|
||||||
|
QString m_subdirectory;
|
||||||
|
|
||||||
|
QFuture<ZipResult> m_zip_future;
|
||||||
|
QFutureWatcher<ZipResult> m_zip_watcher;
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
} // namespace MMCZip
|
} // namespace MMCZip
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ class PixmapCache final : public QObject {
|
||||||
*/
|
*/
|
||||||
bool _markCacheMissByEviciton()
|
bool _markCacheMissByEviciton()
|
||||||
{
|
{
|
||||||
static constexpr uint maxInt = static_cast<uint>(std::numeric_limits<int>::max());
|
static constexpr uint maxCache = static_cast<uint>(std::numeric_limits<int>::max()) / 4;
|
||||||
static constexpr uint step = 10240;
|
static constexpr uint step = 10240;
|
||||||
static constexpr int oneSecond = 1000;
|
static constexpr int oneSecond = 1000;
|
||||||
|
|
||||||
|
|
@ -118,8 +118,8 @@ class PixmapCache final : public QObject {
|
||||||
if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) {
|
if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) {
|
||||||
// increase the cache size
|
// increase the cache size
|
||||||
uint newSize = _cacheLimit() + step;
|
uint newSize = _cacheLimit() + step;
|
||||||
if (newSize >= maxInt) { // increase it until you overflow :D
|
if (newSize >= maxCache) { // increase it until you overflow :D
|
||||||
newSize = maxInt;
|
newSize = maxCache;
|
||||||
qDebug() << m_consecutive_fast_evicitons
|
qDebug() << m_consecutive_fast_evicitons
|
||||||
<< tr("pixmap cache misses by eviction happened too fast, doing nothing as the cache size reached it's limit");
|
<< tr("pixmap cache misses by eviction happened too fast, doing nothing as the cache size reached it's limit");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,8 @@ namespace MangoHud {
|
||||||
|
|
||||||
QString getLibraryString()
|
QString getLibraryString()
|
||||||
{
|
{
|
||||||
/*
|
/**
|
||||||
* Check for vulkan layers in this order:
|
* Guess MangoHud install location by searching for vulkan layers in this order:
|
||||||
*
|
*
|
||||||
* $VK_LAYER_PATH
|
* $VK_LAYER_PATH
|
||||||
* $XDG_DATA_DIRS (/usr/local/share/:/usr/share/)
|
* $XDG_DATA_DIRS (/usr/local/share/:/usr/share/)
|
||||||
|
|
@ -49,8 +49,9 @@ QString getLibraryString()
|
||||||
* /etc
|
* /etc
|
||||||
* $XDG_CONFIG_DIRS (/etc/xdg)
|
* $XDG_CONFIG_DIRS (/etc/xdg)
|
||||||
* $XDG_CONFIG_HOME (~/.config)
|
* $XDG_CONFIG_HOME (~/.config)
|
||||||
|
*
|
||||||
|
* @returns Absolute path of libMangoHud.so if found and empty QString otherwise.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
QStringList vkLayerList;
|
QStringList vkLayerList;
|
||||||
{
|
{
|
||||||
QString home = QDir::homePath();
|
QString home = QDir::homePath();
|
||||||
|
|
@ -85,7 +86,7 @@ QString getLibraryString()
|
||||||
vkLayerList << FS::PathCombine(xdgConfigHome, "vulkan", "implicit_layer.d");
|
vkLayerList << FS::PathCombine(xdgConfigHome, "vulkan", "implicit_layer.d");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (QString vkLayer : vkLayerList) {
|
for (const QString& vkLayer : vkLayerList) {
|
||||||
// prefer to use architecture specific vulkan layers
|
// prefer to use architecture specific vulkan layers
|
||||||
QString currentArch = QSysInfo::currentCpuArchitecture();
|
QString currentArch = QSysInfo::currentCpuArchitecture();
|
||||||
|
|
||||||
|
|
@ -95,8 +96,8 @@ QString getLibraryString()
|
||||||
|
|
||||||
QStringList manifestNames = { QString("MangoHud.%1.json").arg(currentArch), "MangoHud.json" };
|
QStringList manifestNames = { QString("MangoHud.%1.json").arg(currentArch), "MangoHud.json" };
|
||||||
|
|
||||||
QString filePath = "";
|
QString filePath{};
|
||||||
for (QString manifestName : manifestNames) {
|
for (const QString& manifestName : manifestNames) {
|
||||||
QString tryPath = FS::PathCombine(vkLayer, manifestName);
|
QString tryPath = FS::PathCombine(vkLayer, manifestName);
|
||||||
if (QFile::exists(tryPath)) {
|
if (QFile::exists(tryPath)) {
|
||||||
filePath = tryPath;
|
filePath = tryPath;
|
||||||
|
|
@ -107,14 +108,34 @@ QString getLibraryString()
|
||||||
if (filePath.isEmpty()) {
|
if (filePath.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
auto conf = Json::requireDocument(filePath, vkLayer);
|
||||||
|
auto confObject = Json::requireObject(conf, vkLayer);
|
||||||
|
auto layer = Json::ensureObject(confObject, "layer");
|
||||||
|
QString libraryName = Json::ensureString(layer, "library_path");
|
||||||
|
|
||||||
auto conf = Json::requireDocument(filePath, vkLayer);
|
if (libraryName.isEmpty()) {
|
||||||
auto confObject = Json::requireObject(conf, vkLayer);
|
continue;
|
||||||
auto layer = Json::ensureObject(confObject, "layer");
|
}
|
||||||
return Json::ensureString(layer, "library_path");
|
if (QFileInfo(libraryName).isAbsolute()) {
|
||||||
|
return libraryName;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __GLIBC__
|
||||||
|
// Check whether mangohud is usable on a glibc based system
|
||||||
|
QString libraryPath = findLibrary(libraryName);
|
||||||
|
if (!libraryPath.isEmpty()) {
|
||||||
|
return libraryPath;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Without glibc return recorded shared library as-is.
|
||||||
|
return libraryName;
|
||||||
|
#endif
|
||||||
|
} catch (const Exception& e) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return QString();
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
QString findLibrary(QString libName)
|
QString findLibrary(QString libName)
|
||||||
|
|
|
||||||
|
|
@ -46,14 +46,14 @@ class NullInstance : public BaseInstance {
|
||||||
{
|
{
|
||||||
setVersionBroken(true);
|
setVersionBroken(true);
|
||||||
}
|
}
|
||||||
virtual ~NullInstance(){};
|
virtual ~NullInstance() = default;
|
||||||
void saveNow() override {}
|
void saveNow() override {}
|
||||||
void loadSpecificSettings() override { setSpecificSettingsLoaded(true); }
|
void loadSpecificSettings() override { setSpecificSettingsLoaded(true); }
|
||||||
QString getStatusbarDescription() override { return tr("Unknown instance type"); };
|
QString getStatusbarDescription() override { return tr("Unknown instance type"); };
|
||||||
QSet<QString> traits() const override { return {}; };
|
QSet<QString> traits() const override { return {}; };
|
||||||
QString instanceConfigFolder() const override { return instanceRoot(); };
|
QString instanceConfigFolder() const override { return instanceRoot(); };
|
||||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override { return nullptr; }
|
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftTarget::Ptr) override { return nullptr; }
|
||||||
shared_qobject_ptr<Task> createUpdateTask([[maybe_unused]] Net::Mode mode) override { return nullptr; }
|
QList<Task::Ptr> createUpdateTask() override { return {}; }
|
||||||
QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); }
|
QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); }
|
||||||
QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); }
|
QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); }
|
||||||
QMap<QString, QString> getVariables() override { return QMap<QString, QString>(); }
|
QMap<QString, QString> getVariables() override { return QMap<QString, QString>(); }
|
||||||
|
|
@ -64,7 +64,7 @@ class NullInstance : public BaseInstance {
|
||||||
bool canEdit() const override { return false; }
|
bool canEdit() const override { return false; }
|
||||||
bool canLaunch() const override { return false; }
|
bool canLaunch() const override { return false; }
|
||||||
void populateLaunchMenu(QMenu* menu) override {}
|
void populateLaunchMenu(QMenu* menu) override {}
|
||||||
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override
|
QStringList verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) override
|
||||||
{
|
{
|
||||||
QStringList out;
|
QStringList out;
|
||||||
out << "Null instance - placeholder.";
|
out << "Null instance - placeholder.";
|
||||||
|
|
|
||||||
71
launcher/PSaveFile.h
Normal file
71
launcher/PSaveFile.h
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QSaveFile>
|
||||||
|
#include "Application.h"
|
||||||
|
|
||||||
|
#if defined(LAUNCHER_APPLICATION)
|
||||||
|
|
||||||
|
/* PSaveFile
|
||||||
|
* A class that mimics QSaveFile for Windows.
|
||||||
|
*
|
||||||
|
* When reading resources, we need to avoid accessing temporary files
|
||||||
|
* generated by QSaveFile. If we start reading such a file, we may
|
||||||
|
* inadvertently keep it open while QSaveFile is trying to remove it,
|
||||||
|
* or we might detect the file just before it is removed, leading to
|
||||||
|
* race conditions and errors.
|
||||||
|
*
|
||||||
|
* Unfortunately, QSaveFile doesn't provide a way to retrieve the
|
||||||
|
* temporary file name or to set a specific template for the temporary
|
||||||
|
* file name it uses. By default, QSaveFile appends a `.XXXXXX` suffix
|
||||||
|
* to the original file name, where the `XXXXXX` part is dynamically
|
||||||
|
* generated to ensure uniqueness.
|
||||||
|
*
|
||||||
|
* This class acts like a lock by adding and removing the target file
|
||||||
|
* name into/from a global string set, helping to manage access to
|
||||||
|
* files during critical operations.
|
||||||
|
*
|
||||||
|
* Note: Please do not use the `setFileName` function directly, as it
|
||||||
|
* is not virtual and cannot be overridden.
|
||||||
|
*/
|
||||||
|
class PSaveFile : public QSaveFile {
|
||||||
|
public:
|
||||||
|
PSaveFile(const QString& name) : QSaveFile(name) { addPath(name); }
|
||||||
|
PSaveFile(const QString& name, QObject* parent) : QSaveFile(name, parent) { addPath(name); }
|
||||||
|
virtual ~PSaveFile()
|
||||||
|
{
|
||||||
|
if (auto app = APPLICATION_DYN) {
|
||||||
|
app->removeQSavePath(m_absoluteFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void addPath(const QString& path)
|
||||||
|
{
|
||||||
|
m_absoluteFilePath = QFileInfo(path).absoluteFilePath() + "."; // add dot for tmp files only
|
||||||
|
if (auto app = APPLICATION_DYN) {
|
||||||
|
app->addQSavePath(m_absoluteFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QString m_absoluteFilePath;
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
#define PSaveFile QSaveFile
|
||||||
|
#endif
|
||||||
|
|
@ -33,7 +33,7 @@ class shared_qobject_ptr : public QSharedPointer<T> {
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void reset() { QSharedPointer<T>::reset(); }
|
void reset() { QSharedPointer<T>::reset(); }
|
||||||
void reset(T*&& other)
|
void reset(T* other)
|
||||||
{
|
{
|
||||||
shared_qobject_ptr<T> t(other);
|
shared_qobject_ptr<T> t(other);
|
||||||
this->swap(t);
|
this->swap(t);
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,9 @@
|
||||||
#include "minecraft/mod/ModFolderModel.h"
|
#include "minecraft/mod/ModFolderModel.h"
|
||||||
#include "minecraft/mod/ResourceFolderModel.h"
|
#include "minecraft/mod/ResourceFolderModel.h"
|
||||||
|
|
||||||
|
#include "modplatform/helpers/HashUtils.h"
|
||||||
#include "net/ApiDownload.h"
|
#include "net/ApiDownload.h"
|
||||||
|
#include "net/ChecksumValidator.h"
|
||||||
|
|
||||||
ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
||||||
ModPlatform::IndexedVersion version,
|
ModPlatform::IndexedVersion version,
|
||||||
|
|
@ -33,9 +35,9 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
||||||
QString custom_target_folder)
|
QString custom_target_folder)
|
||||||
: m_pack(std::move(pack)), m_pack_version(std::move(version)), m_pack_model(packs), m_custom_target_folder(custom_target_folder)
|
: m_pack(std::move(pack)), m_pack_version(std::move(version)), m_pack_model(packs), m_custom_target_folder(custom_target_folder)
|
||||||
{
|
{
|
||||||
if (auto model = dynamic_cast<ModFolderModel*>(m_pack_model.get()); model && is_indexed) {
|
if (is_indexed) {
|
||||||
m_update_task.reset(new LocalModUpdateTask(model->indexDir(), *m_pack, m_pack_version));
|
m_update_task.reset(new LocalResourceUpdateTask(m_pack_model->indexDir(), *m_pack, m_pack_version));
|
||||||
connect(m_update_task.get(), &LocalModUpdateTask::hasOldMod, this, &ResourceDownloadTask::hasOldResource);
|
connect(m_update_task.get(), &LocalResourceUpdateTask::hasOldResource, this, &ResourceDownloadTask::hasOldResource);
|
||||||
|
|
||||||
addTask(m_update_task);
|
addTask(m_update_task);
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +55,29 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_filesNetJob->addNetAction(Net::ApiDownload::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename())));
|
auto action = Net::ApiDownload::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename()));
|
||||||
|
if (!m_pack_version.hash_type.isEmpty() && !m_pack_version.hash.isEmpty()) {
|
||||||
|
switch (Hashing::algorithmFromString(m_pack_version.hash_type)) {
|
||||||
|
case Hashing::Algorithm::Md4:
|
||||||
|
action->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Md4, m_pack_version.hash));
|
||||||
|
break;
|
||||||
|
case Hashing::Algorithm::Md5:
|
||||||
|
action->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Md5, m_pack_version.hash));
|
||||||
|
break;
|
||||||
|
case Hashing::Algorithm::Sha1:
|
||||||
|
action->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Sha1, m_pack_version.hash));
|
||||||
|
break;
|
||||||
|
case Hashing::Algorithm::Sha256:
|
||||||
|
action->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Sha256, m_pack_version.hash));
|
||||||
|
break;
|
||||||
|
case Hashing::Algorithm::Sha512:
|
||||||
|
action->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Sha512, m_pack_version.hash));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_filesNetJob->addNetAction(action);
|
||||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded);
|
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded);
|
||||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged);
|
connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged);
|
||||||
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propagateStepProgress);
|
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propagateStepProgress);
|
||||||
|
|
@ -67,12 +91,8 @@ void ResourceDownloadTask::downloadSucceeded()
|
||||||
m_filesNetJob.reset();
|
m_filesNetJob.reset();
|
||||||
auto name = std::get<0>(to_delete);
|
auto name = std::get<0>(to_delete);
|
||||||
auto filename = std::get<1>(to_delete);
|
auto filename = std::get<1>(to_delete);
|
||||||
if (!name.isEmpty() && filename != m_pack_version.fileName) {
|
if (!name.isEmpty() && filename != m_pack_version.fileName)
|
||||||
if (auto model = dynamic_cast<ModFolderModel*>(m_pack_model.get()); model)
|
m_pack_model->uninstallResource(filename, true);
|
||||||
model->uninstallMod(filename, true);
|
|
||||||
else
|
|
||||||
m_pack_model->uninstallResource(filename);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceDownloadTask::downloadFailed(QString reason)
|
void ResourceDownloadTask::downloadFailed(QString reason)
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
#include "tasks/SequentialTask.h"
|
#include "tasks/SequentialTask.h"
|
||||||
|
|
||||||
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
|
#include "minecraft/mod/tasks/LocalResourceUpdateTask.h"
|
||||||
#include "modplatform/ModIndex.h"
|
#include "modplatform/ModIndex.h"
|
||||||
|
|
||||||
class ResourceFolderModel;
|
class ResourceFolderModel;
|
||||||
|
|
@ -50,7 +50,7 @@ class ResourceDownloadTask : public SequentialTask {
|
||||||
QString m_custom_target_folder;
|
QString m_custom_target_folder;
|
||||||
|
|
||||||
NetJob::Ptr m_filesNetJob;
|
NetJob::Ptr m_filesNetJob;
|
||||||
LocalModUpdateTask::Ptr m_update_task;
|
LocalResourceUpdateTask::Ptr m_update_task;
|
||||||
|
|
||||||
void downloadProgressChanged(qint64 current, qint64 total);
|
void downloadProgressChanged(qint64 current, qint64 total);
|
||||||
void downloadFailed(QString reason);
|
void downloadFailed(QString reason);
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,13 @@
|
||||||
|
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include "SysInfo.h"
|
||||||
#include "settings/SettingsObject.h"
|
#include "settings/SettingsObject.h"
|
||||||
|
|
||||||
struct RuntimeContext {
|
struct RuntimeContext {
|
||||||
QString javaArchitecture;
|
QString javaArchitecture;
|
||||||
QString javaRealArchitecture;
|
QString javaRealArchitecture;
|
||||||
QString javaPath;
|
QString system = SysInfo::currentSystem();
|
||||||
QString system;
|
|
||||||
|
|
||||||
QString mappedJavaRealArchitecture() const
|
QString mappedJavaRealArchitecture() const
|
||||||
{
|
{
|
||||||
|
|
@ -45,8 +45,6 @@ struct RuntimeContext {
|
||||||
{
|
{
|
||||||
javaArchitecture = instanceSettings->get("JavaArchitecture").toString();
|
javaArchitecture = instanceSettings->get("JavaArchitecture").toString();
|
||||||
javaRealArchitecture = instanceSettings->get("JavaRealArchitecture").toString();
|
javaRealArchitecture = instanceSettings->get("JavaRealArchitecture").toString();
|
||||||
javaPath = instanceSettings->get("JavaPath").toString();
|
|
||||||
system = currentSystem();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString getClassifier() const { return system + "-" + mappedJavaRealArchitecture(); }
|
QString getClassifier() const { return system + "-" + mappedJavaRealArchitecture(); }
|
||||||
|
|
@ -68,21 +66,4 @@ struct RuntimeContext {
|
||||||
|
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString currentSystem()
|
|
||||||
{
|
|
||||||
#if defined(Q_OS_LINUX)
|
|
||||||
return "linux";
|
|
||||||
#elif defined(Q_OS_MACOS)
|
|
||||||
return "osx";
|
|
||||||
#elif defined(Q_OS_WINDOWS)
|
|
||||||
return "windows";
|
|
||||||
#elif defined(Q_OS_FREEBSD)
|
|
||||||
return "freebsd";
|
|
||||||
#elif defined(Q_OS_OPENBSD)
|
|
||||||
return "openbsd";
|
|
||||||
#else
|
|
||||||
return "unknown";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
/* Copyright 2013-2021 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "SkinUtils.h"
|
|
||||||
#include "Application.h"
|
|
||||||
#include "net/HttpMetaCache.h"
|
|
||||||
|
|
||||||
#include <QFile>
|
|
||||||
#include <QJsonArray>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QPainter>
|
|
||||||
|
|
||||||
namespace SkinUtils {
|
|
||||||
/*
|
|
||||||
* Given a username, return a pixmap of the cached skin (if it exists), QPixmap() otherwise
|
|
||||||
*/
|
|
||||||
QPixmap getFaceFromCache(QString username, int height, int width)
|
|
||||||
{
|
|
||||||
QFile fskin(APPLICATION->metacache()->resolveEntry("skins", username + ".png")->getFullPath());
|
|
||||||
|
|
||||||
if (fskin.exists()) {
|
|
||||||
QPixmap skinTexture(fskin.fileName());
|
|
||||||
if (!skinTexture.isNull()) {
|
|
||||||
QPixmap skin = QPixmap(8, 8);
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
|
||||||
skin.fill(QColorConstants::Transparent);
|
|
||||||
#else
|
|
||||||
skin.fill(QColor(0, 0, 0, 0));
|
|
||||||
#endif
|
|
||||||
QPainter painter(&skin);
|
|
||||||
painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8));
|
|
||||||
painter.drawPixmap(0, 0, skinTexture.copy(40, 8, 8, 8));
|
|
||||||
return skin.scaled(height, width, Qt::KeepAspectRatio);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return QPixmap();
|
|
||||||
}
|
|
||||||
} // namespace SkinUtils
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
/* Copyright 2013-2021 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QPixmap>
|
|
||||||
|
|
||||||
namespace SkinUtils {
|
|
||||||
QPixmap getFaceFromCache(QString id, int height = 64, int width = 64);
|
|
||||||
}
|
|
||||||
|
|
@ -212,3 +212,25 @@ QPair<QString, QString> StringUtils::splitFirst(const QString& s, const QRegular
|
||||||
right = s.mid(end);
|
right = s.mid(end);
|
||||||
return qMakePair(left, right);
|
return qMakePair(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const QRegularExpression ulMatcher("<\\s*/\\s*ul\\s*>");
|
||||||
|
|
||||||
|
QString StringUtils::htmlListPatch(QString htmlStr)
|
||||||
|
{
|
||||||
|
int pos = htmlStr.indexOf(ulMatcher);
|
||||||
|
int imgPos;
|
||||||
|
while (pos != -1) {
|
||||||
|
pos = htmlStr.indexOf(">", pos) + 1; // Get the size of the </ul> tag. Add one for zeroeth index
|
||||||
|
imgPos = htmlStr.indexOf("<img ", pos);
|
||||||
|
if (imgPos == -1)
|
||||||
|
break; // no image after the tag
|
||||||
|
|
||||||
|
auto textBetween = htmlStr.mid(pos, imgPos - pos).trimmed(); // trim all white spaces
|
||||||
|
|
||||||
|
if (textBetween.isEmpty())
|
||||||
|
htmlStr.insert(pos, "<br>");
|
||||||
|
|
||||||
|
pos = htmlStr.indexOf(ulMatcher, pos);
|
||||||
|
}
|
||||||
|
return htmlStr;
|
||||||
|
}
|
||||||
|
|
@ -85,4 +85,6 @@ QPair<QString, QString> splitFirst(const QString& s, const QString& sep, Qt::Cas
|
||||||
QPair<QString, QString> splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs = Qt::CaseSensitive);
|
QPair<QString, QString> splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs = Qt::CaseSensitive);
|
||||||
QPair<QString, QString> splitFirst(const QString& s, const QRegularExpression& re);
|
QPair<QString, QString> splitFirst(const QString& s, const QRegularExpression& re);
|
||||||
|
|
||||||
|
QString htmlListPatch(QString htmlStr);
|
||||||
|
|
||||||
} // namespace StringUtils
|
} // namespace StringUtils
|
||||||
|
|
|
||||||
99
launcher/SysInfo.cpp
Normal file
99
launcher/SysInfo.cpp
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QString>
|
||||||
|
#include "sys.h"
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#endif
|
||||||
|
#include <QFile>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
bool rosettaDetect()
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
size_t size = sizeof(ret);
|
||||||
|
if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return ret == 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace SysInfo {
|
||||||
|
QString currentSystem()
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_LINUX)
|
||||||
|
return "linux";
|
||||||
|
#elif defined(Q_OS_MACOS)
|
||||||
|
return "osx";
|
||||||
|
#elif defined(Q_OS_WINDOWS)
|
||||||
|
return "windows";
|
||||||
|
#elif defined(Q_OS_FREEBSD)
|
||||||
|
return "freebsd";
|
||||||
|
#elif defined(Q_OS_OPENBSD)
|
||||||
|
return "openbsd";
|
||||||
|
#else
|
||||||
|
return "unknown";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QString useQTForArch()
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_MACOS) && !defined(Q_PROCESSOR_ARM)
|
||||||
|
if (rosettaDetect()) {
|
||||||
|
return "arm64";
|
||||||
|
} else {
|
||||||
|
return "x86_64";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return QSysInfo::currentCpuArchitecture();
|
||||||
|
}
|
||||||
|
|
||||||
|
int suitableMaxMem()
|
||||||
|
{
|
||||||
|
float totalRAM = (float)Sys::getSystemRam() / (float)Sys::mebibyte;
|
||||||
|
int maxMemoryAlloc;
|
||||||
|
|
||||||
|
// If totalRAM < 6GB, use (totalRAM / 1.5), else 4GB
|
||||||
|
if (totalRAM < (4096 * 1.5))
|
||||||
|
maxMemoryAlloc = (int)(totalRAM / 1.5);
|
||||||
|
else
|
||||||
|
maxMemoryAlloc = 4096;
|
||||||
|
|
||||||
|
return maxMemoryAlloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString getSupportedJavaArchitecture()
|
||||||
|
{
|
||||||
|
auto sys = currentSystem();
|
||||||
|
auto arch = useQTForArch();
|
||||||
|
if (sys == "windows") {
|
||||||
|
if (arch == "x86_64")
|
||||||
|
return "windows-x64";
|
||||||
|
if (arch == "i386")
|
||||||
|
return "windows-x86";
|
||||||
|
// Unknown, maybe arm, appending arch
|
||||||
|
return "windows-" + arch;
|
||||||
|
}
|
||||||
|
if (sys == "osx") {
|
||||||
|
if (arch == "arm64")
|
||||||
|
return "mac-os-arm64";
|
||||||
|
if (arch.contains("64"))
|
||||||
|
return "mac-os-x64";
|
||||||
|
if (arch.contains("86"))
|
||||||
|
return "mac-os-x86";
|
||||||
|
// Unknown, maybe something new, appending arch
|
||||||
|
return "mac-os-" + arch;
|
||||||
|
} else if (sys == "linux") {
|
||||||
|
if (arch == "x86_64")
|
||||||
|
return "linux-x64";
|
||||||
|
if (arch == "i386")
|
||||||
|
return "linux-x86";
|
||||||
|
// will work for arm32 arm(64)
|
||||||
|
return "linux-" + arch;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
} // namespace SysInfo
|
||||||
8
launcher/SysInfo.h
Normal file
8
launcher/SysInfo.h
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace SysInfo {
|
||||||
|
QString currentSystem();
|
||||||
|
QString useQTForArch();
|
||||||
|
QString getSupportedJavaArchitecture();
|
||||||
|
int suitableMaxMem();
|
||||||
|
} // namespace SysInfo
|
||||||
260
launcher/Untar.cpp
Normal file
260
launcher/Untar.cpp
Normal file
|
|
@ -0,0 +1,260 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
#include "Untar.h"
|
||||||
|
#include <quagzipfile.h>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QIODevice>
|
||||||
|
#include <QString>
|
||||||
|
#include "FileSystem.h"
|
||||||
|
|
||||||
|
// adaptation of the:
|
||||||
|
// - https://github.com/madler/zlib/blob/develop/contrib/untgz/untgz.c
|
||||||
|
// - https://en.wikipedia.org/wiki/Tar_(computing)
|
||||||
|
// - https://github.com/euroelessar/cutereader/blob/master/karchive/src/ktar.cpp
|
||||||
|
|
||||||
|
#define BLOCKSIZE 512
|
||||||
|
#define SHORTNAMESIZE 100
|
||||||
|
|
||||||
|
enum class TypeFlag : char {
|
||||||
|
Regular = '0', // regular file
|
||||||
|
ARegular = 0, // regular file
|
||||||
|
Link = '1', // link
|
||||||
|
Symlink = '2', // reserved
|
||||||
|
Character = '3', // character special
|
||||||
|
Block = '4', // block special
|
||||||
|
Directory = '5', // directory
|
||||||
|
FIFO = '6', // FIFO special
|
||||||
|
Contiguous = '7', // reserved
|
||||||
|
// Posix stuff
|
||||||
|
GlobalPosixHeader = 'g',
|
||||||
|
ExtendedPosixHeader = 'x',
|
||||||
|
// 'A'– 'Z' Vendor specific extensions(POSIX .1 - 1988)
|
||||||
|
// GNU
|
||||||
|
GNULongLink = 'K', /* long link name */
|
||||||
|
GNULongName = 'L', /* long file name */
|
||||||
|
};
|
||||||
|
|
||||||
|
// struct Header { /* byte offset */
|
||||||
|
// char name[100]; /* 0 */
|
||||||
|
// char mode[8]; /* 100 */
|
||||||
|
// char uid[8]; /* 108 */
|
||||||
|
// char gid[8]; /* 116 */
|
||||||
|
// char size[12]; /* 124 */
|
||||||
|
// char mtime[12]; /* 136 */
|
||||||
|
// char chksum[8]; /* 148 */
|
||||||
|
// TypeFlag typeflag; /* 156 */
|
||||||
|
// char linkname[100]; /* 157 */
|
||||||
|
// char magic[6]; /* 257 */
|
||||||
|
// char version[2]; /* 263 */
|
||||||
|
// char uname[32]; /* 265 */
|
||||||
|
// char gname[32]; /* 297 */
|
||||||
|
// char devmajor[8]; /* 329 */
|
||||||
|
// char devminor[8]; /* 337 */
|
||||||
|
// char prefix[155]; /* 345 */
|
||||||
|
// /* 500 */
|
||||||
|
// };
|
||||||
|
|
||||||
|
bool readLonglink(QIODevice* in, qint64 size, QByteArray& longlink)
|
||||||
|
{
|
||||||
|
qint64 n = 0;
|
||||||
|
size--; // ignore trailing null
|
||||||
|
if (size < 0) {
|
||||||
|
qCritical() << "The filename size is negative";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
longlink.resize(size + (BLOCKSIZE - size % BLOCKSIZE)); // make the size divisible by BLOCKSIZE
|
||||||
|
for (qint64 offset = 0; offset < longlink.size(); offset += BLOCKSIZE) {
|
||||||
|
n = in->read(longlink.data() + offset, BLOCKSIZE);
|
||||||
|
if (n != BLOCKSIZE) {
|
||||||
|
qCritical() << "The expected blocksize was not respected for the name";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
longlink.truncate(qstrlen(longlink.constData()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getOctal(char* buffer, int maxlenght, bool* ok)
|
||||||
|
{
|
||||||
|
return QByteArray(buffer, qstrnlen(buffer, maxlenght)).toInt(ok, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString decodeName(char* name)
|
||||||
|
{
|
||||||
|
return QFile::decodeName(QByteArray(name, qstrnlen(name, 100)));
|
||||||
|
}
|
||||||
|
bool Tar::extract(QIODevice* in, QString dst)
|
||||||
|
{
|
||||||
|
char buffer[BLOCKSIZE];
|
||||||
|
QString name, symlink, firstFolderName;
|
||||||
|
bool doNotReset = false, ok;
|
||||||
|
while (true) {
|
||||||
|
auto n = in->read(buffer, BLOCKSIZE);
|
||||||
|
if (n != BLOCKSIZE) { // allways expect complete blocks
|
||||||
|
qCritical() << "The expected blocksize was not respected";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (buffer[0] == 0) { // end of archive
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int mode = getOctal(buffer + 100, 8, &ok) | QFile::ReadUser | QFile::WriteUser; // hack to ensure write and read permisions
|
||||||
|
if (!ok) {
|
||||||
|
qCritical() << "The file mode can't be read";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// there are names that are exactly 100 bytes long
|
||||||
|
// and neither longlink nor \0 terminated (bug:101472)
|
||||||
|
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
name = decodeName(buffer);
|
||||||
|
if (!firstFolderName.isEmpty() && name.startsWith(firstFolderName)) {
|
||||||
|
name = name.mid(firstFolderName.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (symlink.isEmpty())
|
||||||
|
symlink = decodeName(buffer);
|
||||||
|
qint64 size = getOctal(buffer + 124, 12, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
qCritical() << "The file size can't be read";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (TypeFlag(buffer[156])) {
|
||||||
|
case TypeFlag::Regular:
|
||||||
|
/* fallthrough */
|
||||||
|
case TypeFlag::ARegular: {
|
||||||
|
auto fileName = FS::PathCombine(dst, name);
|
||||||
|
if (!FS::ensureFilePathExists(fileName)) {
|
||||||
|
qCritical() << "Can't ensure the file path to exist: " << fileName;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QFile out(fileName);
|
||||||
|
if (!out.open(QFile::WriteOnly)) {
|
||||||
|
qCritical() << "Can't open file:" << fileName;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out.setPermissions(QFile::Permissions(mode));
|
||||||
|
while (size > 0) {
|
||||||
|
QByteArray tmp(BLOCKSIZE, 0);
|
||||||
|
n = in->read(tmp.data(), BLOCKSIZE);
|
||||||
|
if (n != BLOCKSIZE) {
|
||||||
|
qCritical() << "The expected blocksize was not respected when reading file";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tmp.truncate(qMin(qint64(BLOCKSIZE), size));
|
||||||
|
out.write(tmp);
|
||||||
|
size -= BLOCKSIZE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TypeFlag::Directory: {
|
||||||
|
if (firstFolderName.isEmpty()) {
|
||||||
|
firstFolderName = name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto folderPath = FS::PathCombine(dst, name);
|
||||||
|
if (!FS::ensureFolderPathExists(folderPath)) {
|
||||||
|
qCritical() << "Can't ensure that folder exists: " << folderPath;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TypeFlag::GNULongLink: {
|
||||||
|
doNotReset = true;
|
||||||
|
QByteArray longlink;
|
||||||
|
if (readLonglink(in, size, longlink)) {
|
||||||
|
symlink = QFile::decodeName(longlink.constData());
|
||||||
|
} else {
|
||||||
|
qCritical() << "Failed to read long link";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TypeFlag::GNULongName: {
|
||||||
|
doNotReset = true;
|
||||||
|
QByteArray longlink;
|
||||||
|
if (readLonglink(in, size, longlink)) {
|
||||||
|
name = QFile::decodeName(longlink.constData());
|
||||||
|
} else {
|
||||||
|
qCritical() << "Failed to read long name";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TypeFlag::Link:
|
||||||
|
/* fallthrough */
|
||||||
|
case TypeFlag::Symlink: {
|
||||||
|
auto fileName = FS::PathCombine(dst, name);
|
||||||
|
if (!FS::create_link(FS::PathCombine(QFileInfo(fileName).path(), symlink), fileName)()) { // do not use symlinks
|
||||||
|
qCritical() << "Can't create link for:" << fileName << " to:" << FS::PathCombine(QFileInfo(fileName).path(), symlink);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FS::ensureFilePathExists(fileName);
|
||||||
|
QFile::setPermissions(fileName, QFile::Permissions(mode));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TypeFlag::Character:
|
||||||
|
/* fallthrough */
|
||||||
|
case TypeFlag::Block:
|
||||||
|
/* fallthrough */
|
||||||
|
case TypeFlag::FIFO:
|
||||||
|
/* fallthrough */
|
||||||
|
case TypeFlag::Contiguous:
|
||||||
|
/* fallthrough */
|
||||||
|
case TypeFlag::GlobalPosixHeader:
|
||||||
|
/* fallthrough */
|
||||||
|
case TypeFlag::ExtendedPosixHeader:
|
||||||
|
/* fallthrough */
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!doNotReset) {
|
||||||
|
name.truncate(0);
|
||||||
|
symlink.truncate(0);
|
||||||
|
}
|
||||||
|
doNotReset = false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GZTar::extract(QString src, QString dst)
|
||||||
|
{
|
||||||
|
QuaGzipFile a(src);
|
||||||
|
if (!a.open(QIODevice::ReadOnly)) {
|
||||||
|
qCritical() << "Can't open tar file:" << src;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Tar::extract(&a, dst);
|
||||||
|
}
|
||||||
46
launcher/Untar.h
Normal file
46
launcher/Untar.h
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <QIODevice>
|
||||||
|
|
||||||
|
// this is a hack used for the java downloader (feel free to remove it in favor of a library)
|
||||||
|
// both extract functions will extract the first folder inside dest(disregarding the prefix)
|
||||||
|
namespace Tar {
|
||||||
|
bool extract(QIODevice* in, QString dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace GZTar {
|
||||||
|
bool extract(QString src, QString dst);
|
||||||
|
}
|
||||||
|
|
@ -79,7 +79,7 @@ void Version::parse()
|
||||||
if (m_string.isEmpty())
|
if (m_string.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto classChange = [&](QChar lastChar, QChar currentChar) {
|
auto classChange = [¤tSection](QChar lastChar, QChar currentChar) {
|
||||||
if (lastChar.isNull())
|
if (lastChar.isNull())
|
||||||
return false;
|
return false;
|
||||||
if (lastChar.isDigit() != currentChar.isDigit())
|
if (lastChar.isDigit() != currentChar.isDigit())
|
||||||
|
|
@ -123,8 +123,7 @@ QDebug operator<<(QDebug debug, const Version& v)
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug.nospace() << " ]"
|
debug.nospace() << " ]" << " }";
|
||||||
<< " }";
|
|
||||||
|
|
||||||
return debug;
|
return debug;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -114,10 +114,14 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation,
|
||||||
return tr("Branch");
|
return tr("Branch");
|
||||||
case Type:
|
case Type:
|
||||||
return tr("Type");
|
return tr("Type");
|
||||||
case Architecture:
|
case CPUArchitecture:
|
||||||
return tr("Architecture");
|
return tr("Architecture");
|
||||||
case Path:
|
case Path:
|
||||||
return tr("Path");
|
return tr("Path");
|
||||||
|
case JavaName:
|
||||||
|
return tr("Java Name");
|
||||||
|
case JavaMajor:
|
||||||
|
return tr("Major Version");
|
||||||
case Time:
|
case Time:
|
||||||
return tr("Released");
|
return tr("Released");
|
||||||
}
|
}
|
||||||
|
|
@ -131,10 +135,14 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation,
|
||||||
return tr("The version's branch");
|
return tr("The version's branch");
|
||||||
case Type:
|
case Type:
|
||||||
return tr("The version's type");
|
return tr("The version's type");
|
||||||
case Architecture:
|
case CPUArchitecture:
|
||||||
return tr("CPU Architecture");
|
return tr("CPU Architecture");
|
||||||
case Path:
|
case Path:
|
||||||
return tr("Filesystem path to this version");
|
return tr("Filesystem path to this version");
|
||||||
|
case JavaName:
|
||||||
|
return tr("The alternative name of the Java version");
|
||||||
|
case JavaMajor:
|
||||||
|
return tr("The Java major version");
|
||||||
case Time:
|
case Time:
|
||||||
return tr("Release date of this version");
|
return tr("Release date of this version");
|
||||||
}
|
}
|
||||||
|
|
@ -165,10 +173,14 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
|
||||||
return sourceModel()->data(parentIndex, BaseVersionList::BranchRole);
|
return sourceModel()->data(parentIndex, BaseVersionList::BranchRole);
|
||||||
case Type:
|
case Type:
|
||||||
return sourceModel()->data(parentIndex, BaseVersionList::TypeRole);
|
return sourceModel()->data(parentIndex, BaseVersionList::TypeRole);
|
||||||
case Architecture:
|
case CPUArchitecture:
|
||||||
return sourceModel()->data(parentIndex, BaseVersionList::ArchitectureRole);
|
return sourceModel()->data(parentIndex, BaseVersionList::CPUArchitectureRole);
|
||||||
case Path:
|
case Path:
|
||||||
return sourceModel()->data(parentIndex, BaseVersionList::PathRole);
|
return sourceModel()->data(parentIndex, BaseVersionList::PathRole);
|
||||||
|
case JavaName:
|
||||||
|
return sourceModel()->data(parentIndex, BaseVersionList::JavaNameRole);
|
||||||
|
case JavaMajor:
|
||||||
|
return sourceModel()->data(parentIndex, BaseVersionList::JavaMajorRole);
|
||||||
case Time:
|
case Time:
|
||||||
return sourceModel()->data(parentIndex, Meta::VersionList::TimeRole).toDate();
|
return sourceModel()->data(parentIndex, Meta::VersionList::TimeRole).toDate();
|
||||||
default:
|
default:
|
||||||
|
|
@ -308,12 +320,18 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw)
|
||||||
m_columns.push_back(ParentVersion);
|
m_columns.push_back(ParentVersion);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
if (roles.contains(BaseVersionList::ArchitectureRole)) {
|
if (roles.contains(BaseVersionList::CPUArchitectureRole)) {
|
||||||
m_columns.push_back(Architecture);
|
m_columns.push_back(CPUArchitecture);
|
||||||
}
|
}
|
||||||
if (roles.contains(BaseVersionList::PathRole)) {
|
if (roles.contains(BaseVersionList::PathRole)) {
|
||||||
m_columns.push_back(Path);
|
m_columns.push_back(Path);
|
||||||
}
|
}
|
||||||
|
if (roles.contains(BaseVersionList::JavaNameRole)) {
|
||||||
|
m_columns.push_back(JavaName);
|
||||||
|
}
|
||||||
|
if (roles.contains(BaseVersionList::JavaMajorRole)) {
|
||||||
|
m_columns.push_back(JavaMajor);
|
||||||
|
}
|
||||||
if (roles.contains(Meta::VersionList::TimeRole)) {
|
if (roles.contains(Meta::VersionList::TimeRole)) {
|
||||||
m_columns.push_back(Time);
|
m_columns.push_back(Time);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,12 @@ class VersionFilterModel;
|
||||||
class VersionProxyModel : public QAbstractProxyModel {
|
class VersionProxyModel : public QAbstractProxyModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
enum Column { Name, ParentVersion, Branch, Type, Architecture, Path, Time };
|
enum Column { Name, ParentVersion, Branch, Type, CPUArchitecture, Path, Time, JavaName, JavaMajor };
|
||||||
using FilterMap = QHash<BaseVersionList::ModelRoles, std::shared_ptr<Filter>>;
|
using FilterMap = QHash<BaseVersionList::ModelRoles, std::shared_ptr<Filter>>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VersionProxyModel(QObject* parent = 0);
|
VersionProxyModel(QObject* parent = 0);
|
||||||
virtual ~VersionProxyModel(){};
|
virtual ~VersionProxyModel() {};
|
||||||
|
|
||||||
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||||
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||||
|
|
|
||||||
|
|
@ -104,11 +104,11 @@ void FileLinkApp::joinServer(QString server)
|
||||||
|
|
||||||
in.setDevice(&socket);
|
in.setDevice(&socket);
|
||||||
|
|
||||||
connect(&socket, &QLocalSocket::connected, this, [&]() { qDebug() << "connected to server"; });
|
connect(&socket, &QLocalSocket::connected, this, []() { qDebug() << "connected to server"; });
|
||||||
|
|
||||||
connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs);
|
connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs);
|
||||||
|
|
||||||
connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError) {
|
connect(&socket, &QLocalSocket::errorOccurred, this, [this](QLocalSocket::LocalSocketError socketError) {
|
||||||
m_status = Failed;
|
m_status = Failed;
|
||||||
switch (socketError) {
|
switch (socketError) {
|
||||||
case QLocalSocket::ServerNotFoundError:
|
case QLocalSocket::ServerNotFoundError:
|
||||||
|
|
@ -132,7 +132,7 @@ void FileLinkApp::joinServer(QString server)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(&socket, &QLocalSocket::disconnected, this, [&]() {
|
connect(&socket, &QLocalSocket::disconnected, this, [this]() {
|
||||||
qDebug() << "disconnected from server, should exit";
|
qDebug() << "disconnected from server, should exit";
|
||||||
m_status = Succeeded;
|
m_status = Succeeded;
|
||||||
exit();
|
exit();
|
||||||
|
|
|
||||||
|
|
@ -47,24 +47,24 @@
|
||||||
|
|
||||||
#define MAX_SIZE 1024
|
#define MAX_SIZE 1024
|
||||||
|
|
||||||
IconList::IconList(const QStringList& builtinPaths, QString path, QObject* parent) : QAbstractListModel(parent)
|
IconList::IconList(const QStringList& builtinPaths, const QString& path, QObject* parent) : QAbstractListModel(parent)
|
||||||
{
|
{
|
||||||
QSet<QString> builtinNames;
|
QSet<QString> builtinNames;
|
||||||
|
|
||||||
// add builtin icons
|
// add builtin icons
|
||||||
for (auto& builtinPath : builtinPaths) {
|
for (const auto& builtinPath : builtinPaths) {
|
||||||
QDir instance_icons(builtinPath);
|
QDir instanceIcons(builtinPath);
|
||||||
auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name);
|
auto fileInfoList = instanceIcons.entryInfoList(QDir::Files, QDir::Name);
|
||||||
for (auto file_info : file_info_list) {
|
for (const auto& fileInfo : fileInfoList) {
|
||||||
builtinNames.insert(file_info.completeBaseName());
|
builtinNames.insert(fileInfo.baseName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto& builtinName : builtinNames) {
|
for (const auto& builtinName : builtinNames) {
|
||||||
addThemeIcon(builtinName);
|
addThemeIcon(builtinName);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_watcher.reset(new QFileSystemWatcher());
|
m_watcher.reset(new QFileSystemWatcher());
|
||||||
is_watching = false;
|
m_isWatching = false;
|
||||||
connect(m_watcher.get(), &QFileSystemWatcher::directoryChanged, this, &IconList::directoryChanged);
|
connect(m_watcher.get(), &QFileSystemWatcher::directoryChanged, this, &IconList::directoryChanged);
|
||||||
connect(m_watcher.get(), &QFileSystemWatcher::fileChanged, this, &IconList::fileChanged);
|
connect(m_watcher.get(), &QFileSystemWatcher::fileChanged, this, &IconList::fileChanged);
|
||||||
|
|
||||||
|
|
@ -77,91 +77,131 @@ IconList::IconList(const QStringList& builtinPaths, QString path, QObject* paren
|
||||||
void IconList::sortIconList()
|
void IconList::sortIconList()
|
||||||
{
|
{
|
||||||
qDebug() << "Sorting icon list...";
|
qDebug() << "Sorting icon list...";
|
||||||
std::sort(icons.begin(), icons.end(), [](const MMCIcon& a, const MMCIcon& b) { return a.m_key.localeAwareCompare(b.m_key) < 0; });
|
std::sort(m_icons.begin(), m_icons.end(), [](const MMCIcon& a, const MMCIcon& b) {
|
||||||
|
bool aIsSubdir = a.m_key.contains(QDir::separator());
|
||||||
|
bool bIsSubdir = b.m_key.contains(QDir::separator());
|
||||||
|
if (aIsSubdir != bIsSubdir) {
|
||||||
|
return !aIsSubdir; // root-level icons come first
|
||||||
|
}
|
||||||
|
return a.m_key.localeAwareCompare(b.m_key) < 0;
|
||||||
|
});
|
||||||
reindex();
|
reindex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to add directories recursively
|
||||||
|
bool IconList::addPathRecursively(const QString& path)
|
||||||
|
{
|
||||||
|
QDir dir(path);
|
||||||
|
if (!dir.exists())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Add the directory itself
|
||||||
|
bool watching = m_watcher->addPath(path);
|
||||||
|
|
||||||
|
// Add all subdirectories
|
||||||
|
QFileInfoList entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
for (const QFileInfo& entry : entries) {
|
||||||
|
if (addPathRecursively(entry.absoluteFilePath())) {
|
||||||
|
watching = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return watching;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList IconList::getIconFilePaths() const
|
||||||
|
{
|
||||||
|
QStringList iconFiles{};
|
||||||
|
QStringList directories{ m_dir.absolutePath() };
|
||||||
|
while (!directories.isEmpty()) {
|
||||||
|
QString first = directories.takeFirst();
|
||||||
|
QDir dir(first);
|
||||||
|
for (QFileInfo& fileInfo : dir.entryInfoList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot, QDir::Name)) {
|
||||||
|
if (fileInfo.isDir())
|
||||||
|
directories.push_back(fileInfo.absoluteFilePath());
|
||||||
|
else
|
||||||
|
iconFiles.push_back(fileInfo.absoluteFilePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iconFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString formatName(const QDir& iconsDir, const QFileInfo& iconFile)
|
||||||
|
{
|
||||||
|
if (iconFile.dir() == iconsDir)
|
||||||
|
return iconFile.baseName();
|
||||||
|
|
||||||
|
constexpr auto delimiter = " » ";
|
||||||
|
QString relativePathWithoutExtension = iconsDir.relativeFilePath(iconFile.dir().path()) + QDir::separator() + iconFile.baseName();
|
||||||
|
return relativePathWithoutExtension.replace(QDir::separator(), delimiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Split into a separate function because the preprocessing impedes readability
|
||||||
|
QSet<QString> toStringSet(const QList<QString>& list)
|
||||||
|
{
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||||
|
QSet<QString> set(list.begin(), list.end());
|
||||||
|
#else
|
||||||
|
QSet<QString> set = list.toSet();
|
||||||
|
#endif
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
void IconList::directoryChanged(const QString& path)
|
void IconList::directoryChanged(const QString& path)
|
||||||
{
|
{
|
||||||
QDir new_dir(path);
|
QDir newDir(path);
|
||||||
if (m_dir.absolutePath() != new_dir.absolutePath()) {
|
if (m_dir.absolutePath() != newDir.absolutePath()) {
|
||||||
m_dir.setPath(path);
|
if (!path.startsWith(m_dir.absolutePath()))
|
||||||
|
m_dir.setPath(path);
|
||||||
m_dir.refresh();
|
m_dir.refresh();
|
||||||
if (is_watching)
|
if (m_isWatching)
|
||||||
stopWatching();
|
stopWatching();
|
||||||
startWatching();
|
startWatching();
|
||||||
}
|
}
|
||||||
if (!m_dir.exists())
|
if (!m_dir.exists() && !FS::ensureFolderPathExists(m_dir.absolutePath()))
|
||||||
if (!FS::ensureFolderPathExists(m_dir.absolutePath()))
|
return;
|
||||||
return;
|
|
||||||
m_dir.refresh();
|
m_dir.refresh();
|
||||||
auto new_list = m_dir.entryList(QDir::Files, QDir::Name);
|
const QStringList newFileNamesList = getIconFilePaths();
|
||||||
for (auto it = new_list.begin(); it != new_list.end(); it++) {
|
const QSet<QString> newSet = toStringSet(newFileNamesList);
|
||||||
QString& foo = (*it);
|
QSet<QString> currentSet;
|
||||||
foo = m_dir.filePath(foo);
|
for (const MMCIcon& it : m_icons) {
|
||||||
}
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
|
||||||
QSet<QString> new_set(new_list.begin(), new_list.end());
|
|
||||||
#else
|
|
||||||
auto new_set = new_list.toSet();
|
|
||||||
#endif
|
|
||||||
QList<QString> current_list;
|
|
||||||
for (auto& it : icons) {
|
|
||||||
if (!it.has(IconType::FileBased))
|
if (!it.has(IconType::FileBased))
|
||||||
continue;
|
continue;
|
||||||
current_list.push_back(it.m_images[IconType::FileBased].filename);
|
currentSet.insert(it.m_images[IconType::FileBased].filename);
|
||||||
}
|
}
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
QSet<QString> toRemove = currentSet - newSet;
|
||||||
QSet<QString> current_set(current_list.begin(), current_list.end());
|
QSet<QString> toAdd = newSet - currentSet;
|
||||||
#else
|
|
||||||
QSet<QString> current_set = current_list.toSet();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QSet<QString> to_remove = current_set;
|
for (const QString& removedPath : toRemove) {
|
||||||
to_remove -= new_set;
|
qDebug() << "Removing icon " << removedPath;
|
||||||
|
QFileInfo removedFile(removedPath);
|
||||||
QSet<QString> to_add = new_set;
|
QString key = m_dir.relativeFilePath(removedFile.absoluteFilePath());
|
||||||
to_add -= current_set;
|
|
||||||
|
|
||||||
for (auto remove : to_remove) {
|
|
||||||
qDebug() << "Removing " << remove;
|
|
||||||
QFileInfo rmfile(remove);
|
|
||||||
QString key = rmfile.completeBaseName();
|
|
||||||
|
|
||||||
QString suffix = rmfile.suffix();
|
|
||||||
// The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well
|
|
||||||
if (!IconUtils::isIconSuffix(suffix))
|
|
||||||
key = rmfile.fileName();
|
|
||||||
|
|
||||||
int idx = getIconIndex(key);
|
int idx = getIconIndex(key);
|
||||||
if (idx == -1)
|
if (idx == -1)
|
||||||
continue;
|
continue;
|
||||||
icons[idx].remove(IconType::FileBased);
|
m_icons[idx].remove(FileBased);
|
||||||
if (icons[idx].type() == IconType::ToBeDeleted) {
|
if (m_icons[idx].type() == ToBeDeleted) {
|
||||||
beginRemoveRows(QModelIndex(), idx, idx);
|
beginRemoveRows(QModelIndex(), idx, idx);
|
||||||
icons.remove(idx);
|
m_icons.remove(idx);
|
||||||
reindex();
|
reindex();
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
} else {
|
} else {
|
||||||
dataChanged(index(idx), index(idx));
|
dataChanged(index(idx), index(idx));
|
||||||
}
|
}
|
||||||
m_watcher->removePath(remove);
|
m_watcher->removePath(removedPath);
|
||||||
emit iconUpdated(key);
|
emit iconUpdated(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto add : to_add) {
|
for (const QString& addedPath : toAdd) {
|
||||||
qDebug() << "Adding " << add;
|
qDebug() << "Adding icon " << addedPath;
|
||||||
|
|
||||||
QFileInfo addfile(add);
|
QFileInfo addfile(addedPath);
|
||||||
QString key = addfile.completeBaseName();
|
QString relativePath = m_dir.relativeFilePath(addfile.absoluteFilePath());
|
||||||
|
QString key = QFileInfo(relativePath).completeBaseName();
|
||||||
|
QString name = formatName(m_dir, addfile);
|
||||||
|
|
||||||
QString suffix = addfile.suffix();
|
if (addIcon(key, name, addfile.filePath(), IconType::FileBased)) {
|
||||||
// The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well
|
m_watcher->addPath(addedPath);
|
||||||
if (!IconUtils::isIconSuffix(suffix))
|
|
||||||
key = addfile.fileName();
|
|
||||||
|
|
||||||
if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased)) {
|
|
||||||
m_watcher->addPath(add);
|
|
||||||
emit iconUpdated(key);
|
emit iconUpdated(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -171,24 +211,24 @@ void IconList::directoryChanged(const QString& path)
|
||||||
|
|
||||||
void IconList::fileChanged(const QString& path)
|
void IconList::fileChanged(const QString& path)
|
||||||
{
|
{
|
||||||
qDebug() << "Checking " << path;
|
qDebug() << "Checking icon " << path;
|
||||||
QFileInfo checkfile(path);
|
QFileInfo checkfile(path);
|
||||||
if (!checkfile.exists())
|
if (!checkfile.exists())
|
||||||
return;
|
return;
|
||||||
QString key = checkfile.completeBaseName();
|
QString key = m_dir.relativeFilePath(checkfile.absoluteFilePath());
|
||||||
int idx = getIconIndex(key);
|
int idx = getIconIndex(key);
|
||||||
if (idx == -1)
|
if (idx == -1)
|
||||||
return;
|
return;
|
||||||
QIcon icon(path);
|
QIcon icon(path);
|
||||||
if (!icon.availableSizes().size())
|
if (icon.availableSizes().empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
icons[idx].m_images[IconType::FileBased].icon = icon;
|
m_icons[idx].m_images[IconType::FileBased].icon = icon;
|
||||||
dataChanged(index(idx), index(idx));
|
dataChanged(index(idx), index(idx));
|
||||||
emit iconUpdated(key);
|
emit iconUpdated(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IconList::SettingChanged(const Setting& setting, QVariant value)
|
void IconList::SettingChanged(const Setting& setting, const QVariant& value)
|
||||||
{
|
{
|
||||||
if (setting.id() != "IconsDir")
|
if (setting.id() != "IconsDir")
|
||||||
return;
|
return;
|
||||||
|
|
@ -200,8 +240,8 @@ void IconList::startWatching()
|
||||||
{
|
{
|
||||||
auto abs_path = m_dir.absolutePath();
|
auto abs_path = m_dir.absolutePath();
|
||||||
FS::ensureFolderPathExists(abs_path);
|
FS::ensureFolderPathExists(abs_path);
|
||||||
is_watching = m_watcher->addPath(abs_path);
|
m_isWatching = addPathRecursively(abs_path);
|
||||||
if (is_watching) {
|
if (m_isWatching) {
|
||||||
qDebug() << "Started watching " << abs_path;
|
qDebug() << "Started watching " << abs_path;
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Failed to start watching " << abs_path;
|
qDebug() << "Failed to start watching " << abs_path;
|
||||||
|
|
@ -212,7 +252,7 @@ void IconList::stopWatching()
|
||||||
{
|
{
|
||||||
m_watcher->removePaths(m_watcher->files());
|
m_watcher->removePaths(m_watcher->files());
|
||||||
m_watcher->removePaths(m_watcher->directories());
|
m_watcher->removePaths(m_watcher->directories());
|
||||||
is_watching = false;
|
m_isWatching = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList IconList::mimeTypes() const
|
QStringList IconList::mimeTypes() const
|
||||||
|
|
@ -242,7 +282,7 @@ bool IconList::dropMimeData(const QMimeData* data,
|
||||||
if (data->hasUrls()) {
|
if (data->hasUrls()) {
|
||||||
auto urls = data->urls();
|
auto urls = data->urls();
|
||||||
QStringList iconFiles;
|
QStringList iconFiles;
|
||||||
for (auto url : urls) {
|
for (const auto& url : urls) {
|
||||||
// only local files may be dropped...
|
// only local files may be dropped...
|
||||||
if (!url.isLocalFile())
|
if (!url.isLocalFile())
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -263,33 +303,33 @@ Qt::ItemFlags IconList::flags(const QModelIndex& index) const
|
||||||
QVariant IconList::data(const QModelIndex& index, int role) const
|
QVariant IconList::data(const QModelIndex& index, int role) const
|
||||||
{
|
{
|
||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
return QVariant();
|
return {};
|
||||||
|
|
||||||
int row = index.row();
|
int row = index.row();
|
||||||
|
|
||||||
if (row < 0 || row >= icons.size())
|
if (row < 0 || row >= m_icons.size())
|
||||||
return QVariant();
|
return {};
|
||||||
|
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case Qt::DecorationRole:
|
case Qt::DecorationRole:
|
||||||
return icons[row].icon();
|
return m_icons[row].icon();
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
return icons[row].name();
|
return m_icons[row].name();
|
||||||
case Qt::UserRole:
|
case Qt::UserRole:
|
||||||
return icons[row].m_key;
|
return m_icons[row].m_key;
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int IconList::rowCount(const QModelIndex& parent) const
|
int IconList::rowCount(const QModelIndex& parent) const
|
||||||
{
|
{
|
||||||
return parent.isValid() ? 0 : icons.size();
|
return parent.isValid() ? 0 : m_icons.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IconList::installIcons(const QStringList& iconFiles)
|
void IconList::installIcons(const QStringList& iconFiles)
|
||||||
{
|
{
|
||||||
for (QString file : iconFiles)
|
for (const QString& file : iconFiles)
|
||||||
installIcon(file, {});
|
installIcon(file, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -312,17 +352,18 @@ bool IconList::iconFileExists(const QString& key) const
|
||||||
return iconEntry && iconEntry->has(IconType::FileBased);
|
return iconEntry && iconEntry->has(IconType::FileBased);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the icon with the given key or nullptr if it doesn't exist.
|
||||||
const MMCIcon* IconList::icon(const QString& key) const
|
const MMCIcon* IconList::icon(const QString& key) const
|
||||||
{
|
{
|
||||||
int iconIdx = getIconIndex(key);
|
int iconIdx = getIconIndex(key);
|
||||||
if (iconIdx == -1)
|
if (iconIdx == -1)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return &icons[iconIdx];
|
return &m_icons[iconIdx];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IconList::deleteIcon(const QString& key)
|
bool IconList::deleteIcon(const QString& key)
|
||||||
{
|
{
|
||||||
return iconFileExists(key) && QFile::remove(icon(key)->getFilePath());
|
return iconFileExists(key) && FS::deletePath(icon(key)->getFilePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IconList::trashIcon(const QString& key)
|
bool IconList::trashIcon(const QString& key)
|
||||||
|
|
@ -332,22 +373,22 @@ bool IconList::trashIcon(const QString& key)
|
||||||
|
|
||||||
bool IconList::addThemeIcon(const QString& key)
|
bool IconList::addThemeIcon(const QString& key)
|
||||||
{
|
{
|
||||||
auto iter = name_index.find(key);
|
auto iter = m_nameIndex.find(key);
|
||||||
if (iter != name_index.end()) {
|
if (iter != m_nameIndex.end()) {
|
||||||
auto& oldOne = icons[*iter];
|
auto& oldOne = m_icons[*iter];
|
||||||
oldOne.replace(Builtin, key);
|
oldOne.replace(Builtin, key);
|
||||||
dataChanged(index(*iter), index(*iter));
|
dataChanged(index(*iter), index(*iter));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// add a new icon
|
// add a new icon
|
||||||
beginInsertRows(QModelIndex(), icons.size(), icons.size());
|
beginInsertRows(QModelIndex(), m_icons.size(), m_icons.size());
|
||||||
{
|
{
|
||||||
MMCIcon mmc_icon;
|
MMCIcon mmc_icon;
|
||||||
mmc_icon.m_name = key;
|
mmc_icon.m_name = key;
|
||||||
mmc_icon.m_key = key;
|
mmc_icon.m_key = key;
|
||||||
mmc_icon.replace(Builtin, key);
|
mmc_icon.replace(Builtin, key);
|
||||||
icons.push_back(mmc_icon);
|
m_icons.push_back(mmc_icon);
|
||||||
name_index[key] = icons.size() - 1;
|
m_nameIndex[key] = m_icons.size() - 1;
|
||||||
}
|
}
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -359,22 +400,22 @@ bool IconList::addIcon(const QString& key, const QString& name, const QString& p
|
||||||
QIcon icon(path);
|
QIcon icon(path);
|
||||||
if (icon.isNull())
|
if (icon.isNull())
|
||||||
return false;
|
return false;
|
||||||
auto iter = name_index.find(key);
|
auto iter = m_nameIndex.find(key);
|
||||||
if (iter != name_index.end()) {
|
if (iter != m_nameIndex.end()) {
|
||||||
auto& oldOne = icons[*iter];
|
auto& oldOne = m_icons[*iter];
|
||||||
oldOne.replace(type, icon, path);
|
oldOne.replace(type, icon, path);
|
||||||
dataChanged(index(*iter), index(*iter));
|
dataChanged(index(*iter), index(*iter));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// add a new icon
|
// add a new icon
|
||||||
beginInsertRows(QModelIndex(), icons.size(), icons.size());
|
beginInsertRows(QModelIndex(), m_icons.size(), m_icons.size());
|
||||||
{
|
{
|
||||||
MMCIcon mmc_icon;
|
MMCIcon mmc_icon;
|
||||||
mmc_icon.m_name = name;
|
mmc_icon.m_name = name;
|
||||||
mmc_icon.m_key = key;
|
mmc_icon.m_key = key;
|
||||||
mmc_icon.replace(type, icon, path);
|
mmc_icon.replace(type, icon, path);
|
||||||
icons.push_back(mmc_icon);
|
m_icons.push_back(mmc_icon);
|
||||||
name_index[key] = icons.size() - 1;
|
m_nameIndex[key] = m_icons.size() - 1;
|
||||||
}
|
}
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -389,33 +430,32 @@ void IconList::saveIcon(const QString& key, const QString& path, const char* for
|
||||||
|
|
||||||
void IconList::reindex()
|
void IconList::reindex()
|
||||||
{
|
{
|
||||||
name_index.clear();
|
m_nameIndex.clear();
|
||||||
int i = 0;
|
for (int i = 0; i < m_icons.size(); i++) {
|
||||||
for (auto& iter : icons) {
|
m_nameIndex[m_icons[i].m_key] = i;
|
||||||
name_index[iter.m_key] = i;
|
emit iconUpdated(m_icons[i].m_key); // prevents incorrect indices with proxy model
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QIcon IconList::getIcon(const QString& key) const
|
QIcon IconList::getIcon(const QString& key) const
|
||||||
{
|
{
|
||||||
int icon_index = getIconIndex(key);
|
int iconIndex = getIconIndex(key);
|
||||||
|
|
||||||
if (icon_index != -1)
|
if (iconIndex != -1)
|
||||||
return icons[icon_index].icon();
|
return m_icons[iconIndex].icon();
|
||||||
|
|
||||||
// Fallback for icons that don't exist.
|
// Fallback for icons that don't exist.b
|
||||||
icon_index = getIconIndex("grass");
|
iconIndex = getIconIndex("grass");
|
||||||
|
|
||||||
if (icon_index != -1)
|
if (iconIndex != -1)
|
||||||
return icons[icon_index].icon();
|
return m_icons[iconIndex].icon();
|
||||||
return QIcon();
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
int IconList::getIconIndex(const QString& key) const
|
int IconList::getIconIndex(const QString& key) const
|
||||||
{
|
{
|
||||||
auto iter = name_index.find(key == "default" ? "grass" : key);
|
auto iter = m_nameIndex.find(key == "default" ? "grass" : key);
|
||||||
if (iter != name_index.end())
|
if (iter != m_nameIndex.end())
|
||||||
return *iter;
|
return *iter;
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -425,3 +465,15 @@ QString IconList::getDirectory() const
|
||||||
{
|
{
|
||||||
return m_dir.absolutePath();
|
return m_dir.absolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the directory of the icon with the given key or the default directory if it's a builtin icon.
|
||||||
|
QString IconList::iconDirectory(const QString& key) const
|
||||||
|
{
|
||||||
|
for (const auto& mmcIcon : m_icons) {
|
||||||
|
if (mmcIcon.m_key == key && mmcIcon.has(IconType::FileBased)) {
|
||||||
|
QFileInfo iconFile(mmcIcon.getFilePath());
|
||||||
|
return iconFile.dir().path();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getDirectory();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,8 @@ class QFileSystemWatcher;
|
||||||
class IconList : public QAbstractListModel {
|
class IconList : public QAbstractListModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit IconList(const QStringList& builtinPaths, QString path, QObject* parent = 0);
|
explicit IconList(const QStringList& builtinPaths, const QString& path, QObject* parent = 0);
|
||||||
virtual ~IconList(){};
|
virtual ~IconList() {};
|
||||||
|
|
||||||
QIcon getIcon(const QString& key) const;
|
QIcon getIcon(const QString& key) const;
|
||||||
int getIconIndex(const QString& key) const;
|
int getIconIndex(const QString& key) const;
|
||||||
|
|
@ -72,6 +72,7 @@ class IconList : public QAbstractListModel {
|
||||||
bool deleteIcon(const QString& key);
|
bool deleteIcon(const QString& key);
|
||||||
bool trashIcon(const QString& key);
|
bool trashIcon(const QString& key);
|
||||||
bool iconFileExists(const QString& key) const;
|
bool iconFileExists(const QString& key) const;
|
||||||
|
QString iconDirectory(const QString& key) const;
|
||||||
|
|
||||||
void installIcons(const QStringList& iconFiles);
|
void installIcons(const QStringList& iconFiles);
|
||||||
void installIcon(const QString& file, const QString& name);
|
void installIcon(const QString& file, const QString& name);
|
||||||
|
|
@ -91,18 +92,20 @@ class IconList : public QAbstractListModel {
|
||||||
IconList& operator=(const IconList&) = delete;
|
IconList& operator=(const IconList&) = delete;
|
||||||
void reindex();
|
void reindex();
|
||||||
void sortIconList();
|
void sortIconList();
|
||||||
|
bool addPathRecursively(const QString& path);
|
||||||
|
QStringList getIconFilePaths() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void directoryChanged(const QString& path);
|
void directoryChanged(const QString& path);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void fileChanged(const QString& path);
|
void fileChanged(const QString& path);
|
||||||
void SettingChanged(const Setting& setting, QVariant value);
|
void SettingChanged(const Setting& setting, const QVariant& value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
shared_qobject_ptr<QFileSystemWatcher> m_watcher;
|
shared_qobject_ptr<QFileSystemWatcher> m_watcher;
|
||||||
bool is_watching;
|
bool m_isWatching;
|
||||||
QMap<QString, int> name_index;
|
QMap<QString, int> m_nameIndex;
|
||||||
QVector<MMCIcon> icons;
|
QVector<MMCIcon> m_icons;
|
||||||
QDir m_dir;
|
QDir m_dir;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
static const QStringList validIconExtensions = { { "svg", "png", "ico", "gif", "jpg", "jpeg" } };
|
static const QStringList validIconExtensions = { { "svg", "png", "ico", "gif", "jpg", "jpeg", "webp" } };
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace IconUtils {
|
namespace IconUtils {
|
||||||
|
|
@ -52,8 +52,7 @@ QString findBestIconIn(const QString& folder, const QString& iconKey)
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
it.next();
|
it.next();
|
||||||
auto fileInfo = it.fileInfo();
|
auto fileInfo = it.fileInfo();
|
||||||
|
if ((fileInfo.completeBaseName() == iconKey || fileInfo.fileName() == iconKey) && isIconSuffix(fileInfo.suffix()))
|
||||||
if (fileInfo.completeBaseName() == iconKey && isIconSuffix(fileInfo.suffix()))
|
|
||||||
return fileInfo.absoluteFilePath();
|
return fileInfo.absoluteFilePath();
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
set(CMAKE_MODULE_PATH "@CMAKE_MODULE_PATH@")
|
set(CMAKE_MODULE_PATH "@CMAKE_MODULE_PATH@")
|
||||||
|
|
||||||
file(GLOB_RECURSE QTPLUGINS "${CMAKE_INSTALL_PREFIX}/@PLUGIN_DEST_DIR@/*@CMAKE_SHARED_LIBRARY_SUFFIX@")
|
file(GLOB_RECURSE QTPLUGINS "${CMAKE_INSTALL_PREFIX}/@PLUGIN_DEST_DIR@/*@CMAKE_SHARED_LIBRARY_SUFFIX@")
|
||||||
function(gp_resolved_file_type_override resolved_file type_var)
|
function(gp_resolved_file_type_override resolved_file type_var)
|
||||||
if(resolved_file MATCHES "^/(usr/)?lib/libQt")
|
if(resolved_file MATCHES "^/(usr/)?lib/libQt")
|
||||||
|
|
|
||||||
|
|
@ -40,14 +40,15 @@
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
|
||||||
#include "Application.h"
|
|
||||||
#include "Commandline.h"
|
#include "Commandline.h"
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "JavaUtils.h"
|
#include "java/JavaUtils.h"
|
||||||
|
|
||||||
JavaChecker::JavaChecker(QObject* parent) : QObject(parent) {}
|
JavaChecker::JavaChecker(QString path, QString args, int minMem, int maxMem, int permGen, int id)
|
||||||
|
: Task(), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen), m_id(id)
|
||||||
|
{}
|
||||||
|
|
||||||
void JavaChecker::performCheck()
|
void JavaChecker::executeTask()
|
||||||
{
|
{
|
||||||
QString checkerJar = JavaUtils::getJavaCheckPath();
|
QString checkerJar = JavaUtils::getJavaCheckPath();
|
||||||
|
|
||||||
|
|
@ -55,6 +56,9 @@ void JavaChecker::performCheck()
|
||||||
qDebug() << "Java checker library could not be found. Please check your installation.";
|
qDebug() << "Java checker library could not be found. Please check your installation.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
checkerJar = FS::getPathNameInLocal8bit(checkerJar);
|
||||||
|
#endif
|
||||||
|
|
||||||
QStringList args;
|
QStringList args;
|
||||||
|
|
||||||
|
|
@ -69,7 +73,7 @@ void JavaChecker::performCheck()
|
||||||
if (m_maxMem != 0) {
|
if (m_maxMem != 0) {
|
||||||
args << QString("-Xmx%1m").arg(m_maxMem);
|
args << QString("-Xmx%1m").arg(m_maxMem);
|
||||||
}
|
}
|
||||||
if (m_permGen != 64) {
|
if (m_permGen != 64 && m_permGen != 0) {
|
||||||
args << QString("-XX:PermSize=%1m").arg(m_permGen);
|
args << QString("-XX:PermSize=%1m").arg(m_permGen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,11 +116,10 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||||
QProcessPtr _process = process;
|
QProcessPtr _process = process;
|
||||||
process.reset();
|
process.reset();
|
||||||
|
|
||||||
JavaCheckResult result;
|
Result result = {
|
||||||
{
|
m_path,
|
||||||
result.path = m_path;
|
m_id,
|
||||||
result.id = m_id;
|
};
|
||||||
}
|
|
||||||
result.errorLog = m_stderr;
|
result.errorLog = m_stderr;
|
||||||
result.outLog = m_stdout;
|
result.outLog = m_stdout;
|
||||||
qDebug() << "STDOUT" << m_stdout;
|
qDebug() << "STDOUT" << m_stdout;
|
||||||
|
|
@ -124,8 +127,9 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||||
qDebug() << "Java checker finished with status" << status << "exit code" << exitcode;
|
qDebug() << "Java checker finished with status" << status << "exit code" << exitcode;
|
||||||
|
|
||||||
if (status == QProcess::CrashExit || exitcode == 1) {
|
if (status == QProcess::CrashExit || exitcode == 1) {
|
||||||
result.validity = JavaCheckResult::Validity::Errored;
|
result.validity = Result::Validity::Errored;
|
||||||
emit checkFinished(result);
|
emit checkFinished(result);
|
||||||
|
emitSucceeded();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,17 +162,18 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!results.contains("os.arch") || !results.contains("java.version") || !results.contains("java.vendor") || !success) {
|
if (!results.contains("os.arch") || !results.contains("java.version") || !results.contains("java.vendor") || !success) {
|
||||||
result.validity = JavaCheckResult::Validity::ReturnedInvalidData;
|
result.validity = Result::Validity::ReturnedInvalidData;
|
||||||
emit checkFinished(result);
|
emit checkFinished(result);
|
||||||
|
emitSucceeded();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto os_arch = results["os.arch"];
|
auto os_arch = results["os.arch"];
|
||||||
auto java_version = results["java.version"];
|
auto java_version = results["java.version"];
|
||||||
auto java_vendor = results["java.vendor"];
|
auto java_vendor = results["java.vendor"];
|
||||||
bool is_64 = os_arch == "x86_64" || os_arch == "amd64" || os_arch == "aarch64" || os_arch == "arm64";
|
bool is_64 = os_arch == "x86_64" || os_arch == "amd64" || os_arch == "aarch64" || os_arch == "arm64" || os_arch == "riscv64";
|
||||||
|
|
||||||
result.validity = JavaCheckResult::Validity::Valid;
|
result.validity = Result::Validity::Valid;
|
||||||
result.is_64bit = is_64;
|
result.is_64bit = is_64;
|
||||||
result.mojangPlatform = is_64 ? "64" : "32";
|
result.mojangPlatform = is_64 ? "64" : "32";
|
||||||
result.realPlatform = os_arch;
|
result.realPlatform = os_arch;
|
||||||
|
|
@ -176,6 +181,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||||
result.javaVendor = java_vendor;
|
result.javaVendor = java_vendor;
|
||||||
qDebug() << "Java checker succeeded.";
|
qDebug() << "Java checker succeeded.";
|
||||||
emit checkFinished(result);
|
emit checkFinished(result);
|
||||||
|
emitSucceeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JavaChecker::error(QProcess::ProcessError err)
|
void JavaChecker::error(QProcess::ProcessError err)
|
||||||
|
|
@ -187,15 +193,9 @@ void JavaChecker::error(QProcess::ProcessError err)
|
||||||
qDebug() << "Native environment:";
|
qDebug() << "Native environment:";
|
||||||
qDebug() << QProcessEnvironment::systemEnvironment().toStringList();
|
qDebug() << QProcessEnvironment::systemEnvironment().toStringList();
|
||||||
killTimer.stop();
|
killTimer.stop();
|
||||||
JavaCheckResult result;
|
emit checkFinished({ m_path, m_id });
|
||||||
{
|
|
||||||
result.path = m_path;
|
|
||||||
result.id = m_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit checkFinished(result);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
emitSucceeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JavaChecker::timeout()
|
void JavaChecker::timeout()
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,52 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "QObjectPtr.h"
|
|
||||||
|
|
||||||
#include "JavaVersion.h"
|
#include "JavaVersion.h"
|
||||||
|
#include "QObjectPtr.h"
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
class JavaChecker;
|
class JavaChecker : public Task {
|
||||||
|
|
||||||
struct JavaCheckResult {
|
|
||||||
QString path;
|
|
||||||
QString mojangPlatform;
|
|
||||||
QString realPlatform;
|
|
||||||
JavaVersion javaVersion;
|
|
||||||
QString javaVendor;
|
|
||||||
QString outLog;
|
|
||||||
QString errorLog;
|
|
||||||
bool is_64bit = false;
|
|
||||||
int id;
|
|
||||||
enum class Validity { Errored, ReturnedInvalidData, Valid } validity = Validity::Errored;
|
|
||||||
};
|
|
||||||
|
|
||||||
using QProcessPtr = shared_qobject_ptr<QProcess>;
|
|
||||||
using JavaCheckerPtr = shared_qobject_ptr<JavaChecker>;
|
|
||||||
class JavaChecker : public QObject {
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit JavaChecker(QObject* parent = 0);
|
using QProcessPtr = shared_qobject_ptr<QProcess>;
|
||||||
void performCheck();
|
using Ptr = shared_qobject_ptr<JavaChecker>;
|
||||||
|
|
||||||
QString m_path;
|
struct Result {
|
||||||
QString m_args;
|
QString path;
|
||||||
int m_id = 0;
|
int id;
|
||||||
int m_minMem = 0;
|
QString mojangPlatform;
|
||||||
int m_maxMem = 0;
|
QString realPlatform;
|
||||||
int m_permGen = 64;
|
JavaVersion javaVersion;
|
||||||
|
QString javaVendor;
|
||||||
|
QString outLog;
|
||||||
|
QString errorLog;
|
||||||
|
bool is_64bit = false;
|
||||||
|
enum class Validity { Errored, ReturnedInvalidData, Valid } validity = Validity::Errored;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit JavaChecker(QString path, QString args, int minMem = 0, int maxMem = 0, int permGen = 0, int id = 0);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void checkFinished(JavaCheckResult result);
|
void checkFinished(const Result& result);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void executeTask() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QProcessPtr process;
|
QProcessPtr process;
|
||||||
QTimer killTimer;
|
QTimer killTimer;
|
||||||
QString m_stdout;
|
QString m_stdout;
|
||||||
QString m_stderr;
|
QString m_stderr;
|
||||||
public slots:
|
|
||||||
|
QString m_path;
|
||||||
|
QString m_args;
|
||||||
|
int m_minMem = 0;
|
||||||
|
int m_maxMem = 0;
|
||||||
|
int m_permGen = 64;
|
||||||
|
int m_id = 0;
|
||||||
|
|
||||||
|
private slots:
|
||||||
void timeout();
|
void timeout();
|
||||||
void finished(int exitcode, QProcess::ExitStatus);
|
void finished(int exitcode, QProcess::ExitStatus);
|
||||||
void error(QProcess::ProcessError);
|
void error(QProcess::ProcessError);
|
||||||
|
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
/* Copyright 2013-2021 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "JavaCheckerJob.h"
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
void JavaCheckerJob::partFinished(JavaCheckResult result)
|
|
||||||
{
|
|
||||||
num_finished++;
|
|
||||||
qDebug() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/" << javacheckers.size();
|
|
||||||
setProgress(num_finished, javacheckers.size());
|
|
||||||
|
|
||||||
javaresults.replace(result.id, result);
|
|
||||||
|
|
||||||
if (num_finished == javacheckers.size()) {
|
|
||||||
emitSucceeded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JavaCheckerJob::executeTask()
|
|
||||||
{
|
|
||||||
qDebug() << m_job_name.toLocal8Bit() << " started.";
|
|
||||||
for (auto iter : javacheckers) {
|
|
||||||
javaresults.append(JavaCheckResult());
|
|
||||||
connect(iter.get(), &JavaChecker::checkFinished, this, &JavaCheckerJob::partFinished);
|
|
||||||
iter->performCheck();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue